mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-18 16:30:29 +00:00
Rewired the existing cause-an-update route from the OpenGLView through the best-effort updater.
This commit is contained in:
parent
530cf3dcbb
commit
1921a6c469
@ -11,3 +11,4 @@
|
|||||||
|
|
||||||
#import "CSOpenGLView.h"
|
#import "CSOpenGLView.h"
|
||||||
#import "CSAudioQueue.h"
|
#import "CSAudioQueue.h"
|
||||||
|
#import "CSBestEffortUpdater.h"
|
||||||
|
@ -18,7 +18,7 @@ class Atari2600Document: MachineDocument {
|
|||||||
// MARK: NSDocument overrides
|
// MARK: NSDocument overrides
|
||||||
override init() {
|
override init() {
|
||||||
super.init()
|
super.init()
|
||||||
self.intendedCyclesPerSecond = 1194720
|
self.bestEffortUpdater.clockRate = 1194720
|
||||||
}
|
}
|
||||||
|
|
||||||
override class func autosavesInPlace() -> Bool {
|
override class func autosavesInPlace() -> Bool {
|
||||||
|
@ -27,7 +27,7 @@ class ElectronDocument: MachineDocument {
|
|||||||
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
||||||
super.windowControllerDidLoadNib(aController)
|
super.windowControllerDidLoadNib(aController)
|
||||||
|
|
||||||
self.intendedCyclesPerSecond = 2000000
|
self.bestEffortUpdater.clockRate = 2000000
|
||||||
|
|
||||||
if let os = rom("os"), basic = rom("basic") {
|
if let os = rom("os"), basic = rom("basic") {
|
||||||
self.electron.setOSROM(os)
|
self.electron.setOSROM(os)
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
import AudioToolbox
|
import AudioToolbox
|
||||||
|
|
||||||
class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDelegate, NSWindowDelegate {
|
class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDelegate, CSBestEffortUpdaterDelegate, NSWindowDelegate {
|
||||||
|
|
||||||
lazy var actionLock = NSLock()
|
lazy var actionLock = NSLock()
|
||||||
lazy var drawLock = NSLock()
|
lazy var drawLock = NSLock()
|
||||||
@ -34,6 +34,11 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
var audioQueue: CSAudioQueue! = nil
|
var audioQueue: CSAudioQueue! = nil
|
||||||
|
lazy var bestEffortUpdater: CSBestEffortUpdater = {
|
||||||
|
let updater = CSBestEffortUpdater()
|
||||||
|
updater.delegate = self
|
||||||
|
return updater
|
||||||
|
}()
|
||||||
|
|
||||||
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
override func windowControllerDidLoadNib(aController: NSWindowController) {
|
||||||
super.windowControllerDidLoadNib(aController)
|
super.windowControllerDidLoadNib(aController)
|
||||||
@ -66,36 +71,39 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe
|
|||||||
super.close()
|
super.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
var intendedCyclesPerSecond: Int64 = 0
|
final func bestEffortUpdater(bestEffortUpdater: CSBestEffortUpdater!, runForCycles cycles: UInt, didSkipPreviousUpdate: Bool) {
|
||||||
private var cycleCountError: Int64 = 0
|
runForNumberOfCycles(Int32(cycles))
|
||||||
private var lastTime: CVTimeStamp?
|
|
||||||
private var skippedFrames = 0
|
|
||||||
final func openGLView(view: CSOpenGLView, didUpdateToTime time: CVTimeStamp, didSkipPreviousUpdate : Bool, frequency : Double) {
|
|
||||||
if let lastTime = lastTime {
|
|
||||||
// perform (time passed in seconds) * (intended cycles per second), converting and
|
|
||||||
// maintaining an error count to deal with underflow
|
|
||||||
let videoTimeScale64 = Int64(time.videoTimeScale)
|
|
||||||
let videoTimeCount = ((time.videoTime - lastTime.videoTime) * intendedCyclesPerSecond) + cycleCountError
|
|
||||||
cycleCountError = videoTimeCount % videoTimeScale64
|
|
||||||
var numberOfCycles = videoTimeCount / videoTimeScale64
|
|
||||||
|
|
||||||
// if the emulation has fallen behind then silently limit the request;
|
|
||||||
// some actions — e.g. the host computer waking after sleep — may give us a
|
|
||||||
// prohibitive backlog
|
|
||||||
if didSkipPreviousUpdate {
|
|
||||||
skippedFrames++
|
|
||||||
} else {
|
|
||||||
skippedFrames = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// run for at most three frames up to and until that causes overshoots in the
|
|
||||||
// permitted processing window for at least four consecutive frames, in which
|
|
||||||
// case limit to one
|
|
||||||
numberOfCycles = min(numberOfCycles, Int64(Double(intendedCyclesPerSecond) * frequency * ((skippedFrames > 4) ? 3.0 : 1.0)))
|
|
||||||
runForNumberOfCycles(Int32(numberOfCycles))
|
|
||||||
}
|
|
||||||
lastTime = time
|
|
||||||
}
|
}
|
||||||
|
// var intendedCyclesPerSecond: Int64 = 0
|
||||||
|
// private var cycleCountError: Int64 = 0
|
||||||
|
// private var lastTime: CVTimeStamp?
|
||||||
|
// private var skippedFrames = 0
|
||||||
|
// final func openGLView(view: CSOpenGLView, didUpdateToTime time: CVTimeStamp, didSkipPreviousUpdate : Bool, frequency : Double) {
|
||||||
|
// if let lastTime = lastTime {
|
||||||
|
// // perform (time passed in seconds) * (intended cycles per second), converting and
|
||||||
|
// // maintaining an error count to deal with underflow
|
||||||
|
// let videoTimeScale64 = Int64(time.videoTimeScale)
|
||||||
|
// let videoTimeCount = ((time.videoTime - lastTime.videoTime) * intendedCyclesPerSecond) + cycleCountError
|
||||||
|
// cycleCountError = videoTimeCount % videoTimeScale64
|
||||||
|
// var numberOfCycles = videoTimeCount / videoTimeScale64
|
||||||
|
//
|
||||||
|
// // if the emulation has fallen behind then silently limit the request;
|
||||||
|
// // some actions — e.g. the host computer waking after sleep — may give us a
|
||||||
|
// // prohibitive backlog
|
||||||
|
// if didSkipPreviousUpdate {
|
||||||
|
// skippedFrames++
|
||||||
|
// } else {
|
||||||
|
// skippedFrames = 0
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // run for at most three frames up to and until that causes overshoots in the
|
||||||
|
// // permitted processing window for at least four consecutive frames, in which
|
||||||
|
// // case limit to one
|
||||||
|
// numberOfCycles = min(numberOfCycles, Int64(Double(intendedCyclesPerSecond) * frequency * ((skippedFrames > 4) ? 3.0 : 1.0)))
|
||||||
|
// runForNumberOfCycles(Int32(numberOfCycles))
|
||||||
|
// }
|
||||||
|
// lastTime = time
|
||||||
|
// }
|
||||||
|
|
||||||
// MARK: Utilities for children
|
// MARK: Utilities for children
|
||||||
func dataForResource(name : String, ofType type: String, inDirectory directory: String) -> NSData? {
|
func dataForResource(name : String, ofType type: String, inDirectory directory: String) -> NSData? {
|
||||||
@ -115,6 +123,7 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func openGLView(view: CSOpenGLView, drawViewOnlyIfDirty onlyIfDirty: Bool) {
|
func openGLView(view: CSOpenGLView, drawViewOnlyIfDirty onlyIfDirty: Bool) {
|
||||||
|
bestEffortUpdater.update()
|
||||||
if drawLock.tryLock() {
|
if drawLock.tryLock() {
|
||||||
self.machine().drawViewForPixelSize(view.backingSize, onlyIfDirty: onlyIfDirty)
|
self.machine().drawViewForPixelSize(view.backingSize, onlyIfDirty: onlyIfDirty)
|
||||||
drawLock.unlock()
|
drawLock.unlock()
|
||||||
|
@ -18,7 +18,7 @@ class Vic20Document: MachineDocument {
|
|||||||
// MARK: NSDocument overrides
|
// MARK: NSDocument overrides
|
||||||
override init() {
|
override init() {
|
||||||
super.init()
|
super.init()
|
||||||
self.intendedCyclesPerSecond = 1022727
|
self.bestEffortUpdater.clockRate = 1022727
|
||||||
// TODO: or 1108405 for PAL; see http://www.antimon.org/dl/c64/code/stable.txt
|
// TODO: or 1108405 for PAL; see http://www.antimon.org/dl/c64/code/stable.txt
|
||||||
|
|
||||||
if let kernel = rom("kernel-ntsc"), basic = rom("basic"), characters = rom("characters-english") {
|
if let kernel = rom("kernel-ntsc"), basic = rom("basic"), characters = rom("characters-english") {
|
||||||
|
@ -12,14 +12,6 @@
|
|||||||
@class CSOpenGLView;
|
@class CSOpenGLView;
|
||||||
|
|
||||||
@protocol CSOpenGLViewDelegate
|
@protocol CSOpenGLViewDelegate
|
||||||
/*!
|
|
||||||
Tells the delegate that time has advanced.
|
|
||||||
@param view The view sending the message.
|
|
||||||
@param time The time to which time has advanced.
|
|
||||||
@param didSkipPreviousUpdate @c YES if the previous update that would have occurred was skipped because a didUpdateToTime: call prior to that was still ongoing; @c NO otherwise.
|
|
||||||
*/
|
|
||||||
- (void)openGLView:(nonnull CSOpenGLView *)view didUpdateToTime:(CVTimeStamp)time didSkipPreviousUpdate:(BOOL)didSkipPreviousUpdate frequency:(double)frequency;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Requests that the delegate produce an image of its current output state. May be called on
|
Requests that the delegate produce an image of its current output state. May be called on
|
||||||
any queue or thread.
|
any queue or thread.
|
||||||
|
@ -12,9 +12,6 @@
|
|||||||
|
|
||||||
@implementation CSOpenGLView {
|
@implementation CSOpenGLView {
|
||||||
CVDisplayLinkRef _displayLink;
|
CVDisplayLinkRef _displayLink;
|
||||||
uint32_t _updateIsOngoing;
|
|
||||||
BOOL _hasSkipped;
|
|
||||||
dispatch_queue_t _serialDispatchQueue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)prepareOpenGL
|
- (void)prepareOpenGL
|
||||||
@ -34,10 +31,6 @@
|
|||||||
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
|
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
|
||||||
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_displayLink, cglContext, cglPixelFormat);
|
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_displayLink, cglContext, cglPixelFormat);
|
||||||
|
|
||||||
// create a serial dispatch queue
|
|
||||||
_serialDispatchQueue = dispatch_queue_create("OpenGLView", DISPATCH_QUEUE_SERIAL);
|
|
||||||
// dispatch_set_target_queue(_serialDispatchQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
|
|
||||||
|
|
||||||
// set the clear colour
|
// set the clear colour
|
||||||
[self.openGLContext makeCurrentContext];
|
[self.openGLContext makeCurrentContext];
|
||||||
glClearColor(0.0, 0.0, 0.0, 1.0);
|
glClearColor(0.0, 0.0, 0.0, 1.0);
|
||||||
@ -55,24 +48,6 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
|||||||
|
|
||||||
- (void)drawAtTime:(const CVTimeStamp *)now frequency:(double)frequency
|
- (void)drawAtTime:(const CVTimeStamp *)now frequency:(double)frequency
|
||||||
{
|
{
|
||||||
const uint32_t processingMask = 0x01;
|
|
||||||
|
|
||||||
// Always post an -openGLView:didUpdateToTime: if a previous one isn't still ongoing. This is the hook upon which the substantial processing occurs.
|
|
||||||
if(!OSAtomicTestAndSet(processingMask, &_updateIsOngoing))
|
|
||||||
{
|
|
||||||
CVTimeStamp time = *now;
|
|
||||||
BOOL didSkip = _hasSkipped;
|
|
||||||
dispatch_async(_serialDispatchQueue, ^{
|
|
||||||
[self.delegate openGLView:self didUpdateToTime:time didSkipPreviousUpdate:didSkip frequency:frequency];
|
|
||||||
OSAtomicTestAndClear(processingMask, &_updateIsOngoing);
|
|
||||||
});
|
|
||||||
_hasSkipped = NO;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_hasSkipped = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the display now regardless of other activity.
|
// Draw the display now regardless of other activity.
|
||||||
[self drawViewOnlyIfDirty:YES];
|
[self drawViewOnlyIfDirty:YES];
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user