Skip to content

Commit bed563f

Browse files
authored
Merge pull request #15 from gabrieloc/feature/sktexture
Replace UIImage with SKTexture
2 parents 248090c + ac55397 commit bed563f

File tree

9 files changed

+144
-48
lines changed

9 files changed

+144
-48
lines changed

gambatte

gambatte_watchOS/GameCore.h

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ static int const kScreenHeight = 144;
7070

7171
- (void)loadFromSlot:(NSInteger)slot;
7272
- (void)saveToSlot:(NSInteger)slot;
73+
- (void)saveSavedata;
7374

7475
@end
7576

gambatte_watchOS/GameCore.mm

+21-5
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,11 @@ - (void)runGameLoop
106106

107107
OESetThreadRealtime(1. / (1. * frameInterval), .007, .03);
108108

109-
while (!_paused) { @autoreleasepool {
109+
while (!gameCoreThread.isCancelled) { @autoreleasepool {
110+
if (!gameCoreThread) {
111+
return;
112+
}
113+
110114
size_t samples = 2064;
111115

112116
while (gb.runFor((gambatte::uint_least32_t *)videoBuffer, kScreenWidth,
@@ -140,13 +144,15 @@ - (void)stopEmulation
140144
}
141145

142146
gb.saveSavedata();
143-
free(videoBuffer);
144-
free(unusedBuffer);
145-
146-
// [updateTimer invalidate];
147147
_paused = YES;
148148
[gameCoreThread cancel];
149149
gameCoreThread = nil;
150+
151+
free(videoBuffer);
152+
free(unusedBuffer);
153+
154+
videoBuffer = nil;
155+
unusedBuffer = nil;
150156
}
151157

152158
- (void)resetEmulation
@@ -156,6 +162,11 @@ - (void)resetEmulation
156162
[self startEmulation];
157163
}
158164

165+
- (void)saveSavedata
166+
{
167+
gb.saveSavedata();
168+
}
169+
159170
#pragma mark - Input
160171

161172
- (oneway void)updateInput:(GameInput)input selected:(BOOL)selected
@@ -174,6 +185,11 @@ - (uint32_t *)activeInput
174185
return activeInput;
175186
}
176187

188+
- (void)saveData
189+
{
190+
gb.saveSavedata();
191+
}
192+
177193
- (void)saveToSlot:(NSInteger)slot
178194
{
179195
gb.saveSavedata();

gambatte_watchOS/GameCoreSnapshots.swift

+47-10
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,24 @@
3333

3434
import CoreGraphics
3535
import Gambatte_watchOS
36+
import SpriteKit
3637

37-
extension GameCore {
38-
38+
extension GameplayController {
39+
3940
static let colorSpace = CGColorSpaceCreateDeviceRGB()
41+
static let screenWidth = Int(kScreenWidth)
42+
static let screenHeight = Int(kScreenHeight)
4043

4144
public func createSnapshot(from buffer: UnsafeMutablePointer<UInt32>) -> UIImage? {
42-
43-
let width = Int(kScreenWidth)
44-
let height = Int(kScreenHeight)
45-
45+
4646
guard
4747
let bitmapContext = CGContext(
4848
data: UnsafeMutableRawPointer(buffer),
49-
width: width,
50-
height: height,
49+
width: GameplayController.screenWidth,
50+
height: GameplayController.screenHeight,
5151
bitsPerComponent: 8,
52-
bytesPerRow: 4 * width,
53-
space: GameCore.colorSpace,
52+
bytesPerRow: MemoryLayout<UInt32>.size * GameplayController.screenWidth,
53+
space: GameplayController.colorSpace,
5454
bitmapInfo: CGImageByteOrderInfo.order32Little.rawValue | CGImageAlphaInfo.noneSkipFirst.rawValue),
5555

5656
let cgImage = bitmapContext.makeImage()
@@ -60,4 +60,41 @@ extension GameCore {
6060
}
6161
return UIImage(cgImage: cgImage)
6262
}
63+
64+
public func createTexture(from buffer: UnsafeMutablePointer<UInt32>) -> SKTexture {
65+
66+
let size = CGSize(width: GameplayController.screenWidth,
67+
height: GameplayController.screenHeight)
68+
let data = createData(from: buffer)
69+
let texture = SKTexture(data: data, size: size, flipped: true)
70+
texture.filteringMode = .nearest
71+
return texture
72+
}
73+
74+
fileprivate func createData(from buffer: UnsafeMutablePointer<UInt32>) -> Data {
75+
76+
let count = Int(kScreenHeight * kScreenWidth) * MemoryLayout<UInt32>.size
77+
let bufferPointer = UnsafeBufferPointer(start: buffer, count: count)
78+
return Data(buffer: bufferPointer)
79+
}
80+
81+
// Use this for debugging. Will output a file to the app's documents directory.
82+
83+
func logBuffer(_ buffer: UnsafeMutablePointer<UInt32>) {
84+
85+
let data = createData(from: buffer)
86+
let count = data.count
87+
var bytes = [UInt8](repeating: 0, count: count)
88+
data.copyBytes(to: &bytes, count: count)
89+
90+
let formatter = DateFormatter()
91+
formatter.dateStyle = .full
92+
let url = GameLoader.shared.cacheURL!.appendingPathComponent(formatter.string(from: Date()))
93+
94+
do {
95+
try data.write(to: url)
96+
} catch {
97+
}
98+
print("Wrote data to \(url)")
99+
}
63100
}

giovanni WatchKit Extension/GameplayController.swift

+46-22
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
//
3333

3434
import WatchKit
35+
import SpriteKit
3536
import Gambatte_watchOS
3637

3738
extension CGPoint {
@@ -41,7 +42,8 @@ extension CGPoint {
4142
}
4243

4344
class GameplayController: WKInterfaceController {
44-
@IBOutlet var image: WKInterfaceImage!
45+
@IBOutlet var scene: WKInterfaceSKScene!
46+
let spriteNode = SKSpriteNode(imageNamed: "loading")
4547

4648
var panOrigin: CGPoint?
4749
let deadzone: CGFloat = 1
@@ -83,6 +85,22 @@ class GameplayController: WKInterfaceController {
8385
@IBAction func selectSelected() { pressInputOnce(.select) }
8486
@IBAction func BSelected() { pressInputOnce(.B) }
8587

88+
@IBAction func loadSelected() {
89+
guard let core = loader.core else {
90+
return
91+
}
92+
core.resetEmulation()
93+
core.load(fromSlot: 0)
94+
}
95+
96+
@IBAction func saveSelected() {
97+
guard let core = loader.core else {
98+
return
99+
}
100+
core.resetEmulation()
101+
loader.core?.save(toSlot: 0)
102+
}
103+
86104
@IBAction func resetSelected() {
87105
loader.core?.resetEmulation()
88106
}
@@ -128,7 +146,7 @@ class GameplayController: WKInterfaceController {
128146
let loader = GameLoader.shared
129147

130148
var tick = 0
131-
let refreshRate = 20;
149+
let refreshRate = 5;
132150

133151
override func awake(withContext context: Any?) {
134152
super.awake(withContext: context)
@@ -138,6 +156,16 @@ class GameplayController: WKInterfaceController {
138156
return
139157
}
140158

159+
var size = contentFrame.size
160+
size.height *= 0.8
161+
let scene = SKScene(size: size)
162+
163+
spriteNode.size = size
164+
spriteNode.position = CGPoint(x: size.width * 0.5,
165+
y: size.height * 0.5)
166+
scene.addChild(spriteNode)
167+
self.scene.presentScene(scene)
168+
141169
let success: ((GameCore) -> Void) = { [unowned self] (core) in
142170
core.didRender = { [weak self] buffer in
143171
guard let s = self else {
@@ -169,44 +197,40 @@ class GameplayController: WKInterfaceController {
169197
crownSequencer.delegate = self
170198
crownSequencer.focus()
171199

172-
if let core = loader.core {
173-
core.load(fromSlot: 0)
174-
// core.paused = false
175-
}
200+
// Too buggy
201+
// if let core = loader.core, core.isLoaded {
202+
// core.load(fromSlot: 0)
203+
// }
176204
}
177205

178206
override func didDeactivate() {
179207
super.didDeactivate()
180208

181209
crownSequencer.delegate = nil
182210
crownSequencer.resignFocus()
211+
212+
loader.core?.saveSavedata()
183213

184-
if let core = loader.core {
185-
core.save(toSlot: 0)
186-
// core.paused = true;
187-
}
214+
// Too buggy
215+
// if let core = loader.core, core.isLoaded {
216+
// core.save(toSlot: 0)
217+
// }
188218
}
189219

190220
var lastSnapshot: UIImage?
191221

192222
func updateSnapshotIfNeeded(with buffer: UnsafeMutablePointer<UInt32>) {
193223

194224
tick += 1
195-
if tick > refreshRate || loader.core == nil {
225+
if tick < refreshRate || loader.core == nil {
196226
return
197227
}
198-
199-
let snapshot = loader.core!.createSnapshot(from: buffer)
200-
201-
// compare before updating. Not sure if faster.
202-
// if let lhs = lastSnapshot, let rhs = snapshot, lhs.equalPixels(to: rhs) {
203-
// return
204-
// }
205-
// lastSnapshot = snapshot
206-
207-
DispatchQueue.main.async {
208-
self.image.setImage(snapshot)
228+
229+
let texture = createTexture(from: buffer)
230+
texture.preload {
231+
self.spriteNode.texture = texture
209232
}
233+
210234
tick = 0
211235
}
212236

giovanni WatchKit Extension/LibraryController.swift

+8
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ class LibraryController: WKInterfaceController {
5555

5656
reloadGames()
5757
}
58+
59+
override func didAppear() {
60+
super.didAppear()
61+
62+
if GameLoader.shared.core != nil {
63+
GameLoader.shared.core = nil
64+
}
65+
}
5866

5967
override func willActivate() {
6068
super.willActivate()

giovanni_watchOS/Base.lproj/Interface.storyboard

+20-10
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="12113" systemVersion="16D32" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="7hZ-R3-GZe">
2+
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="12118" systemVersion="16E195" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="7hZ-R3-GZe">
33
<device id="watch42" orientation="portrait">
44
<adaptation id="fullscreen"/>
55
</device>
66
<dependencies>
7-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12078"/>
8-
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="12027"/>
7+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12086"/>
8+
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="12029"/>
99
</dependencies>
1010
<scenes>
1111
<!--GamePlay-->
1212
<scene sceneID="aou-V4-d1y">
1313
<objects>
1414
<controller identifier="GamePlay" id="AgC-eL-Hgc" customClass="GameplayController" customModule="giovanni_WatchKit_Extension">
1515
<items>
16-
<imageView width="1" height="0.80000000000000004" alignment="left" image="loading" contentMode="scaleAspectFill" id="aJl-Oj-bNg">
16+
<spriteKitScene height="0.80000000000000004" alignment="left" id="ELq-fe-WSa">
1717
<gestureRecognizers>
18-
<panGestureRecognizer id="0MQ-NG-tvP">
18+
<panGestureRecognizer id="fON-GL-rsc">
1919
<connections>
20-
<action selector="panUpdated:" destination="AgC-eL-Hgc" id="CA6-VH-l1R"/>
20+
<action selector="panUpdated:" destination="AgC-eL-Hgc" id="ZVb-df-Eb3"/>
2121
</connections>
2222
</panGestureRecognizer>
23-
<tapGestureRecognizer id="eXU-ai-JEc">
23+
<tapGestureRecognizer id="dCs-LC-B2n">
2424
<connections>
25-
<action selector="tapUpdated:" destination="AgC-eL-Hgc" id="qqH-hx-H41"/>
25+
<action selector="tapUpdated:" destination="AgC-eL-Hgc" id="Mdf-ba-AUm"/>
2626
</connections>
2727
</tapGestureRecognizer>
2828
</gestureRecognizers>
29-
</imageView>
29+
</spriteKitScene>
3030
<group width="1" height="0.20000000000000001" alignment="left" id="OfT-pQ-dwT">
3131
<items>
3232
<group width="0.10000000000000001" height="1" alignment="left" layout="vertical" spacing="0.0" id="jHt-DP-AmP">
@@ -67,6 +67,16 @@ SL</string>
6767
</items>
6868
<menu key="menu" id="nn1-cq-hAR">
6969
<items>
70+
<menuItem title="Load" icon="resume" id="BpF-sx-863">
71+
<connections>
72+
<action selector="loadSelected" destination="AgC-eL-Hgc" id="fsa-bi-ocK"/>
73+
</connections>
74+
</menuItem>
75+
<menuItem title="Save" icon="accept" id="pIq-Uh-5hH">
76+
<connections>
77+
<action selector="saveSelected" destination="AgC-eL-Hgc" id="T4K-MP-SxT"/>
78+
</connections>
79+
</menuItem>
7080
<menuItem title="Reset" icon="repeat" id="JZ8-a9-1cp">
7181
<connections>
7282
<action selector="resetSelected" destination="AgC-eL-Hgc" id="ZNK-Kz-hgl"/>
@@ -77,7 +87,7 @@ SL</string>
7787
<connections>
7888
<outlet property="ALabel" destination="e8e-tB-5Zv" id="gmP-Xj-kCc"/>
7989
<outlet property="DPadLabel" destination="cVh-ps-Gkz" id="oed-H8-hIw"/>
80-
<outlet property="image" destination="aJl-Oj-bNg" id="AY5-GI-9Ll"/>
90+
<outlet property="scene" destination="ELq-fe-WSa" id="8Pd-b5-QQL"/>
8191
</connections>
8292
</controller>
8393
</objects>

0 commit comments

Comments
 (0)