mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-04 13:31:26 +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:
parent
aa8a192c7e
commit
9da7716c72
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -13,7 +13,6 @@
|
||||
@interface CSMachine : NSObject
|
||||
|
||||
- (void)runForNumberOfCycles:(int)numberOfCycles;
|
||||
- (void)sync;
|
||||
|
||||
@property (nonatomic, weak) CSOpenGLView *view;
|
||||
@property (nonatomic, weak) AudioQueue *audioQueue;
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user