From 9da7716c72e8a34026d01b1ac2c92c628e967482 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 19 Mar 2016 22:46:17 -0400 Subject: [PATCH] Attempted to simplify threading, thereby allowing machines to be constructed within a valid GL context, and started adding appropriate GL syncs. Which all oddly drops everything to a negligible FPS. Investigation will follow. --- .../Documents/ElectronDocument.swift | 30 ++- .../Mac/Clock Signal/Views/CSOpenGLView.h | 3 + .../Mac/Clock Signal/Views/CSOpenGLView.m | 63 ++---- .../Mac/Clock Signal/Wrappers/CSAtari2600.mm | 6 - .../Mac/Clock Signal/Wrappers/CSElectron.mm | 182 ++++++++++-------- .../Wrappers/CSMachine+Subclassing.h | 6 +- .../Mac/Clock Signal/Wrappers/CSMachine.h | 1 - .../Mac/Clock Signal/Wrappers/CSMachine.mm | 39 +--- .../CRT/Internals/CRTInputBufferBuilder.cpp | 13 +- .../CRT/Internals/CRTInputBufferBuilder.hpp | 10 + Outputs/CRT/Internals/CRTOpenGL.hpp | 1 + 11 files changed, 168 insertions(+), 186 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift b/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift index 9c2a83d61..a815e50d7 100644 --- a/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/ElectronDocument.swift @@ -11,23 +11,21 @@ import AudioToolbox class ElectronDocument: MachineDocument { - private var electron: CSElectron! = CSElectron() - override init() { - super.init() - self.intendedCyclesPerSecond = 2000000 - - if let osPath = NSBundle.mainBundle().pathForResource("os", ofType: "rom") { - electron.setOSROM(NSData(contentsOfFile: osPath)!) - } - if let basicPath = NSBundle.mainBundle().pathForResource("basic", ofType: "rom") { - electron.setBASICROM(NSData(contentsOfFile: basicPath)!) - } - } + private lazy var electron = CSElectron() override func windowControllerDidLoadNib(aController: NSWindowController) { super.windowControllerDidLoadNib(aController) - electron.view = openGLView - electron.audioQueue = self.audioQueue + self.intendedCyclesPerSecond = 2000000 + openGLView.performWithGLContext({ + if let osPath = NSBundle.mainBundle().pathForResource("os", ofType: "rom") { + self.electron.setOSROM(NSData(contentsOfFile: osPath)!) + } + if let basicPath = NSBundle.mainBundle().pathForResource("basic", ofType: "rom") { + self.electron.setBASICROM(NSData(contentsOfFile: basicPath)!) + } + self.electron.view = self.openGLView + self.electron.audioQueue = self.audioQueue + }) } override var windowNibName: String? { @@ -62,10 +60,8 @@ class ElectronDocument: MachineDocument { lazy var actionLock = NSLock() override func close() { actionLock.lock() - electron.sync() openGLView.invalidate() openGLView.openGLContext!.makeCurrentContext() - electron = nil actionLock.unlock() super.close() @@ -74,7 +70,7 @@ class ElectronDocument: MachineDocument { // MARK: CSOpenGLViewDelegate override func runForNumberOfCycles(numberOfCycles: Int32) { if actionLock.tryLock() { - electron?.runForNumberOfCycles(numberOfCycles) + electron.runForNumberOfCycles(numberOfCycles) actionLock.unlock() } } diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h index 003308243..250689c1b 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h @@ -28,6 +28,7 @@ identical to the previous frame. If @c NO then the delegate must draw. */ - (void)openGLView:(nonnull CSOpenGLView *)view drawViewOnlyIfDirty:(BOOL)onlyIfDirty; + @end @protocol CSOpenGLViewResponderDelegate @@ -72,4 +73,6 @@ /// The size in pixels of the OpenGL canvas, factoring in screen pixel density and view size in points. @property (nonatomic, readonly) CGSize backingSize; +- (void)performWithGLContext:(nonnull dispatch_block_t)action; + @end diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m index 0e9b133ac..edc816354 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m @@ -10,15 +10,8 @@ @import CoreVideo; @import GLKit; -typedef NS_ENUM(NSInteger, CSOpenGLViewCondition) { - CSOpenGLViewConditionReadyForUpdate, - CSOpenGLViewConditionUpdating -}; - @implementation CSOpenGLView { CVDisplayLinkRef _displayLink; - NSConditionLock *_runningLock; - dispatch_queue_t _dispatchQueue; } - (void)prepareOpenGL @@ -33,10 +26,6 @@ typedef NS_ENUM(NSInteger, CSOpenGLViewCondition) { // Set the renderer output callback function CVDisplayLinkSetOutputCallback(_displayLink, DisplayLinkCallback, (__bridge void * __nullable)(self)); - // Create a queue and a condition lock for dispatching to it - _runningLock = [[NSConditionLock alloc] initWithCondition:CSOpenGLViewConditionReadyForUpdate]; - _dispatchQueue = dispatch_queue_create("com.thomasharte.clocksignal.GL", DISPATCH_QUEUE_SERIAL); - // Set the display link for the current renderer CGLContextObj cglContext = [[self openGLContext] CGLContextObj]; CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj]; @@ -62,24 +51,14 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt - (void)drawAtTime:(const CVTimeStamp *)now { - if([_runningLock tryLockWhenCondition:CSOpenGLViewConditionReadyForUpdate]) - { - CVTimeStamp timeStamp = *now; - dispatch_async(_dispatchQueue, ^{ - [_runningLock lockWhenCondition:CSOpenGLViewConditionUpdating]; - [self.delegate openGLView:self didUpdateToTime:timeStamp]; - [self drawViewOnlyIfDirty:YES]; - [_runningLock unlockWithCondition:CSOpenGLViewConditionReadyForUpdate]; - }); - [_runningLock unlockWithCondition:CSOpenGLViewConditionUpdating]; - } + NSLog(@"%0.4f", (double)now->videoTime / (double)now->videoTimeScale); + [self.delegate openGLView:self didUpdateToTime:*now]; + [self drawViewOnlyIfDirty:YES]; } - (void)invalidate { CVDisplayLinkStop(_displayLink); - [_runningLock lockWhenCondition:CSOpenGLViewConditionReadyForUpdate]; - [_runningLock unlock]; } - (void)dealloc @@ -97,13 +76,10 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt { [super reshape]; - [self.openGLContext makeCurrentContext]; - CGLLockContext([[self openGLContext] CGLContextObj]); - - CGSize viewSize = [self backingSize]; - glViewport(0, 0, (GLsizei)viewSize.width, (GLsizei)viewSize.height); - - CGLUnlockContext([[self openGLContext] CGLContextObj]); + [self performWithGLContext:^{ + CGSize viewSize = [self backingSize]; + glViewport(0, 0, (GLsizei)viewSize.width, (GLsizei)viewSize.height); + }]; } - (void)awakeFromNib @@ -141,13 +117,18 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt } - (void)drawViewOnlyIfDirty:(BOOL)onlyIfDirty +{ + [self performWithGLContext:^{ + [self.delegate openGLView:self drawViewOnlyIfDirty:onlyIfDirty]; + CGLFlushDrawable([[self openGLContext] CGLContextObj]); + }]; +} + +- (void)performWithGLContext:(dispatch_block_t)action { [self.openGLContext makeCurrentContext]; CGLLockContext([[self openGLContext] CGLContextObj]); - - [self.delegate openGLView:self drawViewOnlyIfDirty:onlyIfDirty]; - - CGLFlushDrawable([[self openGLContext] CGLContextObj]); + action(); CGLUnlockContext([[self openGLContext] CGLContextObj]); } @@ -160,23 +141,17 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt - (void)keyDown:(NSEvent *)theEvent { - dispatch_async(_dispatchQueue, ^{ - [self.responderDelegate keyDown:theEvent]; - }); + [self.responderDelegate keyDown:theEvent]; } - (void)keyUp:(NSEvent *)theEvent { - dispatch_async(_dispatchQueue, ^{ - [self.responderDelegate keyUp:theEvent]; - }); + [self.responderDelegate keyUp:theEvent]; } - (void)flagsChanged:(NSEvent *)theEvent { - dispatch_async(_dispatchQueue, ^{ - [self.responderDelegate flagsChanged:theEvent]; - }); + [self.responderDelegate flagsChanged:theEvent]; } @end diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.mm b/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.mm index 8239d90df..2fc2ca00a 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.mm +++ b/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.mm @@ -52,21 +52,15 @@ } - (void)setROM:(NSData *)rom { - [self perform:^{ _atari2600.set_rom(rom.length, (const uint8_t *)rom.bytes); - }]; } - (void)setState:(BOOL)state forDigitalInput:(Atari2600DigitalInput)digitalInput { - [self perform:^{ _atari2600.set_digital_input(digitalInput, state ? true : false); - }]; } - (void)setResetLineEnabled:(BOOL)enabled { - [self perform:^{ _atari2600.set_reset_line(enabled ? true : false); - }]; } @end diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm index 36cd681b9..608aa4e73 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm +++ b/OSBindings/Mac/Clock Signal/Wrappers/CSElectron.mm @@ -16,124 +16,142 @@ Electron::Machine _electron; } -- (void)doRunForNumberOfCycles:(int)numberOfCycles { - _electron.run_for_cycles(numberOfCycles); - _electron.update_output(); +- (void)runForNumberOfCycles:(int)numberOfCycles { + @synchronized(self) { + _electron.run_for_cycles(numberOfCycles); + _electron.update_output(); + } } - (void)setOSROM:(nonnull NSData *)rom { - _electron.set_rom(Electron::ROMSlotOS, rom.length, (const uint8_t *)rom.bytes); + @synchronized(self) { + _electron.set_rom(Electron::ROMSlotOS, rom.length, (const uint8_t *)rom.bytes); + } } - (void)setBASICROM:(nonnull NSData *)rom { - _electron.set_rom(Electron::ROMSlotBASIC, rom.length, (const uint8_t *)rom.bytes); + @synchronized(self) { + _electron.set_rom(Electron::ROMSlotBASIC, rom.length, (const uint8_t *)rom.bytes); + } } - (void)setROM:(nonnull NSData *)rom slot:(int)slot { - _electron.set_rom((Electron::ROMSlot)slot, rom.length, (const uint8_t *)rom.bytes); + @synchronized(self) { + _electron.set_rom((Electron::ROMSlot)slot, rom.length, (const uint8_t *)rom.bytes); + } } - (void)drawViewForPixelSize:(CGSize)pixelSize onlyIfDirty:(BOOL)onlyIfDirty { - _electron.get_crt()->draw_frame((unsigned int)pixelSize.width, (unsigned int)pixelSize.height, onlyIfDirty ? true : false); + @synchronized(self) { + _electron.get_crt()->draw_frame((unsigned int)pixelSize.width, (unsigned int)pixelSize.height, onlyIfDirty ? true : false); + } } - (BOOL)openUEFAtURL:(NSURL *)URL { - try { - std::shared_ptr tape(new Storage::UEF([URL fileSystemRepresentation])); - _electron.set_tape(tape); - return YES; - } catch(int exception) { - return NO; + @synchronized(self) { + try { + std::shared_ptr tape(new Storage::UEF([URL fileSystemRepresentation])); + _electron.set_tape(tape); + return YES; + } catch(int exception) { + return NO; + } } } - (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(int)sampleRate { - _electron.get_speaker()->set_output_rate(sampleRate, 256); - _electron.get_speaker()->set_output_quality(15); - _electron.get_speaker()->set_delegate(delegate); - return YES; + @synchronized(self) { + _electron.get_speaker()->set_output_rate(sampleRate, 256); + _electron.get_speaker()->set_output_quality(15); + _electron.get_speaker()->set_delegate(delegate); + return YES; + } } - (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed { - switch(key) - { - case kVK_ANSI_0: _electron.set_key_state(Electron::Key::Key0, isPressed); break; - case kVK_ANSI_1: _electron.set_key_state(Electron::Key::Key1, isPressed); break; - case kVK_ANSI_2: _electron.set_key_state(Electron::Key::Key2, isPressed); break; - case kVK_ANSI_3: _electron.set_key_state(Electron::Key::Key3, isPressed); break; - case kVK_ANSI_4: _electron.set_key_state(Electron::Key::Key4, isPressed); break; - case kVK_ANSI_5: _electron.set_key_state(Electron::Key::Key5, isPressed); break; - case kVK_ANSI_6: _electron.set_key_state(Electron::Key::Key6, isPressed); break; - case kVK_ANSI_7: _electron.set_key_state(Electron::Key::Key7, isPressed); break; - case kVK_ANSI_8: _electron.set_key_state(Electron::Key::Key8, isPressed); break; - case kVK_ANSI_9: _electron.set_key_state(Electron::Key::Key9, isPressed); break; + @synchronized(self) { + switch(key) + { + case kVK_ANSI_0: _electron.set_key_state(Electron::Key::Key0, isPressed); break; + case kVK_ANSI_1: _electron.set_key_state(Electron::Key::Key1, isPressed); break; + case kVK_ANSI_2: _electron.set_key_state(Electron::Key::Key2, isPressed); break; + case kVK_ANSI_3: _electron.set_key_state(Electron::Key::Key3, isPressed); break; + case kVK_ANSI_4: _electron.set_key_state(Electron::Key::Key4, isPressed); break; + case kVK_ANSI_5: _electron.set_key_state(Electron::Key::Key5, isPressed); break; + case kVK_ANSI_6: _electron.set_key_state(Electron::Key::Key6, isPressed); break; + case kVK_ANSI_7: _electron.set_key_state(Electron::Key::Key7, isPressed); break; + case kVK_ANSI_8: _electron.set_key_state(Electron::Key::Key8, isPressed); break; + case kVK_ANSI_9: _electron.set_key_state(Electron::Key::Key9, isPressed); break; - case kVK_ANSI_Q: _electron.set_key_state(Electron::Key::KeyQ, isPressed); break; - case kVK_ANSI_W: _electron.set_key_state(Electron::Key::KeyW, isPressed); break; - case kVK_ANSI_E: _electron.set_key_state(Electron::Key::KeyE, isPressed); break; - case kVK_ANSI_R: _electron.set_key_state(Electron::Key::KeyR, isPressed); break; - case kVK_ANSI_T: _electron.set_key_state(Electron::Key::KeyT, isPressed); break; - case kVK_ANSI_Y: _electron.set_key_state(Electron::Key::KeyY, isPressed); break; - case kVK_ANSI_U: _electron.set_key_state(Electron::Key::KeyU, isPressed); break; - case kVK_ANSI_I: _electron.set_key_state(Electron::Key::KeyI, isPressed); break; - case kVK_ANSI_O: _electron.set_key_state(Electron::Key::KeyO, isPressed); break; - case kVK_ANSI_P: _electron.set_key_state(Electron::Key::KeyP, isPressed); break; - case kVK_ANSI_A: _electron.set_key_state(Electron::Key::KeyA, isPressed); break; - case kVK_ANSI_S: _electron.set_key_state(Electron::Key::KeyS, isPressed); break; - case kVK_ANSI_D: _electron.set_key_state(Electron::Key::KeyD, isPressed); break; - case kVK_ANSI_F: _electron.set_key_state(Electron::Key::KeyF, isPressed); break; - case kVK_ANSI_G: _electron.set_key_state(Electron::Key::KeyG, isPressed); break; - case kVK_ANSI_H: _electron.set_key_state(Electron::Key::KeyH, isPressed); break; - case kVK_ANSI_J: _electron.set_key_state(Electron::Key::KeyJ, isPressed); break; - case kVK_ANSI_K: _electron.set_key_state(Electron::Key::KeyK, isPressed); break; - case kVK_ANSI_L: _electron.set_key_state(Electron::Key::KeyL, isPressed); break; - case kVK_ANSI_Z: _electron.set_key_state(Electron::Key::KeyZ, isPressed); break; - case kVK_ANSI_X: _electron.set_key_state(Electron::Key::KeyX, isPressed); break; - case kVK_ANSI_C: _electron.set_key_state(Electron::Key::KeyC, isPressed); break; - case kVK_ANSI_V: _electron.set_key_state(Electron::Key::KeyV, isPressed); break; - case kVK_ANSI_B: _electron.set_key_state(Electron::Key::KeyB, isPressed); break; - case kVK_ANSI_N: _electron.set_key_state(Electron::Key::KeyN, isPressed); break; - case kVK_ANSI_M: _electron.set_key_state(Electron::Key::KeyM, isPressed); break; + case kVK_ANSI_Q: _electron.set_key_state(Electron::Key::KeyQ, isPressed); break; + case kVK_ANSI_W: _electron.set_key_state(Electron::Key::KeyW, isPressed); break; + case kVK_ANSI_E: _electron.set_key_state(Electron::Key::KeyE, isPressed); break; + case kVK_ANSI_R: _electron.set_key_state(Electron::Key::KeyR, isPressed); break; + case kVK_ANSI_T: _electron.set_key_state(Electron::Key::KeyT, isPressed); break; + case kVK_ANSI_Y: _electron.set_key_state(Electron::Key::KeyY, isPressed); break; + case kVK_ANSI_U: _electron.set_key_state(Electron::Key::KeyU, isPressed); break; + case kVK_ANSI_I: _electron.set_key_state(Electron::Key::KeyI, isPressed); break; + case kVK_ANSI_O: _electron.set_key_state(Electron::Key::KeyO, isPressed); break; + case kVK_ANSI_P: _electron.set_key_state(Electron::Key::KeyP, isPressed); break; + case kVK_ANSI_A: _electron.set_key_state(Electron::Key::KeyA, isPressed); break; + case kVK_ANSI_S: _electron.set_key_state(Electron::Key::KeyS, isPressed); break; + case kVK_ANSI_D: _electron.set_key_state(Electron::Key::KeyD, isPressed); break; + case kVK_ANSI_F: _electron.set_key_state(Electron::Key::KeyF, isPressed); break; + case kVK_ANSI_G: _electron.set_key_state(Electron::Key::KeyG, isPressed); break; + case kVK_ANSI_H: _electron.set_key_state(Electron::Key::KeyH, isPressed); break; + case kVK_ANSI_J: _electron.set_key_state(Electron::Key::KeyJ, isPressed); break; + case kVK_ANSI_K: _electron.set_key_state(Electron::Key::KeyK, isPressed); break; + case kVK_ANSI_L: _electron.set_key_state(Electron::Key::KeyL, isPressed); break; + case kVK_ANSI_Z: _electron.set_key_state(Electron::Key::KeyZ, isPressed); break; + case kVK_ANSI_X: _electron.set_key_state(Electron::Key::KeyX, isPressed); break; + case kVK_ANSI_C: _electron.set_key_state(Electron::Key::KeyC, isPressed); break; + case kVK_ANSI_V: _electron.set_key_state(Electron::Key::KeyV, isPressed); break; + case kVK_ANSI_B: _electron.set_key_state(Electron::Key::KeyB, isPressed); break; + case kVK_ANSI_N: _electron.set_key_state(Electron::Key::KeyN, isPressed); break; + case kVK_ANSI_M: _electron.set_key_state(Electron::Key::KeyM, isPressed); break; - case kVK_Space: _electron.set_key_state(Electron::Key::KeySpace, isPressed); break; - case kVK_ANSI_Grave: - case kVK_ANSI_Backslash: - _electron.set_key_state(Electron::Key::KeyCopy, isPressed); break; - case kVK_Return: _electron.set_key_state(Electron::Key::KeyReturn, isPressed); break; - case kVK_ANSI_Minus: _electron.set_key_state(Electron::Key::KeyMinus, isPressed); break; + case kVK_Space: _electron.set_key_state(Electron::Key::KeySpace, isPressed); break; + case kVK_ANSI_Grave: + case kVK_ANSI_Backslash: + _electron.set_key_state(Electron::Key::KeyCopy, isPressed); break; + case kVK_Return: _electron.set_key_state(Electron::Key::KeyReturn, isPressed); break; + case kVK_ANSI_Minus: _electron.set_key_state(Electron::Key::KeyMinus, isPressed); break; - case kVK_RightArrow: _electron.set_key_state(Electron::Key::KeyRight, isPressed); break; - case kVK_LeftArrow: _electron.set_key_state(Electron::Key::KeyLeft, isPressed); break; - case kVK_DownArrow: _electron.set_key_state(Electron::Key::KeyDown, isPressed); break; - case kVK_UpArrow: _electron.set_key_state(Electron::Key::KeyUp, isPressed); break; + case kVK_RightArrow: _electron.set_key_state(Electron::Key::KeyRight, isPressed); break; + case kVK_LeftArrow: _electron.set_key_state(Electron::Key::KeyLeft, isPressed); break; + case kVK_DownArrow: _electron.set_key_state(Electron::Key::KeyDown, isPressed); break; + case kVK_UpArrow: _electron.set_key_state(Electron::Key::KeyUp, isPressed); break; - case kVK_Delete: _electron.set_key_state(Electron::Key::KeyDelete, isPressed); break; - case kVK_Escape: _electron.set_key_state(Electron::Key::KeyEscape, isPressed); break; + case kVK_Delete: _electron.set_key_state(Electron::Key::KeyDelete, isPressed); break; + case kVK_Escape: _electron.set_key_state(Electron::Key::KeyEscape, isPressed); break; - case kVK_ANSI_Comma: _electron.set_key_state(Electron::Key::KeyComma, isPressed); break; - case kVK_ANSI_Period: _electron.set_key_state(Electron::Key::KeyFullStop, isPressed); break; + case kVK_ANSI_Comma: _electron.set_key_state(Electron::Key::KeyComma, isPressed); break; + case kVK_ANSI_Period: _electron.set_key_state(Electron::Key::KeyFullStop, isPressed); break; - case kVK_ANSI_Semicolon: - _electron.set_key_state(Electron::Key::KeySemiColon, isPressed); break; - case kVK_ANSI_Quote: _electron.set_key_state(Electron::Key::KeyColon, isPressed); break; + case kVK_ANSI_Semicolon: + _electron.set_key_state(Electron::Key::KeySemiColon, isPressed); break; + case kVK_ANSI_Quote: _electron.set_key_state(Electron::Key::KeyColon, isPressed); break; - case kVK_ANSI_Slash: _electron.set_key_state(Electron::Key::KeySlash, isPressed); break; + case kVK_ANSI_Slash: _electron.set_key_state(Electron::Key::KeySlash, isPressed); break; - case kVK_Shift: _electron.set_key_state(Electron::Key::KeyShift, isPressed); break; - case kVK_Control: _electron.set_key_state(Electron::Key::KeyControl, isPressed); break; - case kVK_Command: _electron.set_key_state(Electron::Key::KeyFunc, isPressed); break; + case kVK_Shift: _electron.set_key_state(Electron::Key::KeyShift, isPressed); break; + case kVK_Control: _electron.set_key_state(Electron::Key::KeyControl, isPressed); break; + case kVK_Command: _electron.set_key_state(Electron::Key::KeyFunc, isPressed); break; - case kVK_F12: _electron.set_key_state(Electron::Key::KeyBreak, isPressed); break; + case kVK_F12: _electron.set_key_state(Electron::Key::KeyBreak, isPressed); break; - default: -// printf("%02x\n", key); - break; + default: +// printf("%02x\n", key); + break; + } } } - (void)setUseFastLoadingHack:(BOOL)useFastLoadingHack { - _useFastLoadingHack = useFastLoadingHack; - _electron.set_use_fast_tape_hack(useFastLoadingHack ? true : false); + @synchronized(self) { + _useFastLoadingHack = useFastLoadingHack; + _electron.set_use_fast_tape_hack(useFastLoadingHack ? true : false); + } } @end diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSMachine+Subclassing.h b/OSBindings/Mac/Clock Signal/Wrappers/CSMachine+Subclassing.h index 0f991da4b..788d1423b 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/CSMachine+Subclassing.h +++ b/OSBindings/Mac/Clock Signal/Wrappers/CSMachine+Subclassing.h @@ -13,10 +13,8 @@ @interface CSMachine (Subclassing) - (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(int)sampleRate; - -- (void)doRunForNumberOfCycles:(int)numberOfCycles; -- (void)perform:(dispatch_block_t)action; - - (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length; +- (void)performAsync:(dispatch_block_t)action; +- (void)performSync:(dispatch_block_t)action; @end diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSMachine.h b/OSBindings/Mac/Clock Signal/Wrappers/CSMachine.h index f2f50fd26..ee4b6759a 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Wrappers/CSMachine.h @@ -13,7 +13,6 @@ @interface CSMachine : NSObject - (void)runForNumberOfCycles:(int)numberOfCycles; -- (void)sync; @property (nonatomic, weak) CSOpenGLView *view; @property (nonatomic, weak) AudioQueue *audioQueue; diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSMachine.mm b/OSBindings/Mac/Clock Signal/Wrappers/CSMachine.mm index 79a8fcb4e..8f15b603b 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Wrappers/CSMachine.mm @@ -16,48 +16,20 @@ struct SpeakerDelegate: public Outputs::Speaker::Delegate { } }; -typedef NS_ENUM(NSInteger, CSAtari2600RunningState) { - CSMachineRunningStateRunning, - CSMachineRunningStateStopped -}; - @implementation CSMachine { SpeakerDelegate _speakerDelegate; - dispatch_queue_t _serialDispatchQueue; - NSConditionLock *_runningLock; -} - -- (void)perform:(dispatch_block_t)action { - dispatch_async(_serialDispatchQueue, action); } - (void)speaker:(Outputs::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length { [self.audioQueue enqueueAudioBuffer:samples numberOfSamples:(unsigned int)length]; } -- (void)runForNumberOfCycles:(int)cycles { - if([_runningLock tryLockWhenCondition:CSMachineRunningStateStopped]) { - [_runningLock unlockWithCondition:CSMachineRunningStateRunning]; - dispatch_async(_serialDispatchQueue, ^{ - [_runningLock lockWhenCondition:CSMachineRunningStateRunning]; - [self doRunForNumberOfCycles:cycles]; - [_runningLock unlockWithCondition:CSMachineRunningStateStopped]; - }); - } -} - -- (void)sync { - dispatch_sync(_serialDispatchQueue, ^{}); -} - - (instancetype)init { self = [super init]; if (self) { _serialDispatchQueue = dispatch_queue_create("Machine queue", DISPATCH_QUEUE_SERIAL); - _runningLock = [[NSConditionLock alloc] initWithCondition:CSMachineRunningStateStopped]; - _speakerDelegate.machine = self; [self setSpeakerDelegate:&_speakerDelegate sampleRate:44100]; } @@ -68,6 +40,15 @@ typedef NS_ENUM(NSInteger, CSAtari2600RunningState) { - (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(int)sampleRate { return NO; } -- (void)doRunForNumberOfCycles:(int)numberOfCycles {} + +- (void)runForNumberOfCycles:(int)numberOfCycles {} + +- (void)performSync:(dispatch_block_t)action { + dispatch_sync(_serialDispatchQueue, action); +} + +- (void)performAsync:(dispatch_block_t)action { + dispatch_async(_serialDispatchQueue, action); +} @end diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index 6fdaf5ba8..4e4ac0fdd 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -12,10 +12,17 @@ using namespace Outputs::CRT; -CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) : bytes_per_pixel(bytes_per_pixel) +CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) : + bytes_per_pixel(bytes_per_pixel), + _next_write_x_position(0), + _next_write_y_position(0), + last_uploaded_line(0), + _wraparound_sync(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)) +{} + +CRTInputBufferBuilder::~CRTInputBufferBuilder() { - _next_write_x_position = _next_write_y_position = 0; - last_uploaded_line = 0; + glDeleteSync(_wraparound_sync); } void CRTInputBufferBuilder::allocate_write_area(size_t required_length) diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp index 78e665a6b..440ab178d 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp @@ -13,12 +13,14 @@ #include #include #include "CRTConstants.hpp" +#include "OpenGL.hpp" namespace Outputs { namespace CRT { struct CRTInputBufferBuilder { CRTInputBufferBuilder(size_t bytes_per_pixel); + ~CRTInputBufferBuilder(); void allocate_write_area(size_t required_length); void reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer); @@ -35,10 +37,18 @@ struct CRTInputBufferBuilder { // builder but otherwise entrusted to the CRT to update. unsigned int last_uploaded_line; + GLsync _wraparound_sync; + inline void move_to_new_line() { _next_write_x_position = 0; _next_write_y_position = (_next_write_y_position+1)%InputBufferBuilderHeight; + if(!_next_write_y_position) + { + glClientWaitSync(_wraparound_sync, 0, ~(GLuint64)0); + glDeleteSync(_wraparound_sync); + _wraparound_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } } inline uint8_t *get_write_target(uint8_t *buffer) diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index 0d48a9ed3..52ebc154a 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -206,6 +206,7 @@ class OpenGLOutputBuilder { uint8_t *_input_texture_data; GLuint _input_texture_array; + GLsync _input_texture_sync; uint8_t *_output_buffer_data; size_t _output_buffer_data_pointer;