1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-10-01 13:58:20 +00:00

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.

This commit is contained in:
Thomas Harte 2016-03-19 22:46:17 -04:00
parent aa8a192c7e
commit 9da7716c72
11 changed files with 168 additions and 186 deletions

View File

@ -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()
}
}

View File

@ -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 <NSObject>
@ -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

View File

@ -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

View File

@ -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

View File

@ -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<Storage::UEF> tape(new Storage::UEF([URL fileSystemRepresentation]));
_electron.set_tape(tape);
return YES;
} catch(int exception) {
return NO;
@synchronized(self) {
try {
std::shared_ptr<Storage::UEF> 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

View File

@ -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

View File

@ -13,7 +13,6 @@
@interface CSMachine : NSObject
- (void)runForNumberOfCycles:(int)numberOfCycles;
- (void)sync;
@property (nonatomic, weak) CSOpenGLView *view;
@property (nonatomic, weak) AudioQueue *audioQueue;

View File

@ -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

View File

@ -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)

View File

@ -13,12 +13,14 @@
#include <stdarg.h>
#include <stddef.h>
#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)

View File

@ -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;