1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-23 03:32:32 +00:00

Takes a first shot at running OpenGL work throughout a frame.

Rather than en masse at the end. But it seems I've been lazy with my threading. Work to do!
This commit is contained in:
Thomas Harte 2020-02-03 21:58:29 -05:00
parent 0f2783075f
commit cf9729c74f
4 changed files with 72 additions and 19 deletions

View File

@ -177,9 +177,7 @@ class MachineDocument:
if let machine = self.machine, let openGLView = self.openGLView {
// Establish the output aspect ratio and audio.
let aspectRatio = self.aspectRatio()
openGLView.perform(glContext: {
machine.setView(openGLView, aspectRatio: Float(aspectRatio.width / aspectRatio.height))
})
machine.setView(openGLView, aspectRatio: Float(aspectRatio.width / aspectRatio.height))
// Attach an options panel if one is available.
if let optionsPanelNibName = self.machineDescription?.optionsPanelNibName {

View File

@ -33,7 +33,7 @@
#include "../../../../Outputs/OpenGL/ScanTarget.hpp"
#include "../../../../Outputs/OpenGL/Screenshot.hpp"
@interface CSMachine()
@interface CSMachine() <CSOpenGLViewDisplayLinkDelegate>
- (void)speaker:(Outputs::Speaker::Speaker *)speaker didCompleteSamples:(const int16_t *)samples length:(int)length;
- (void)speakerDidChangeInputClock:(Outputs::Speaker::Speaker *)speaker;
- (void)addLED:(NSString *)led;
@ -151,6 +151,7 @@ struct ActivityObserver: public Activity::Observer {
NSMutableArray<NSString *> *_leds;
CSHighPrecisionTimer *_timer;
CGSize _pixelSize;
std::unique_ptr<Outputs::Display::OpenGL::ScanTarget> _scanTarget;
}
@ -318,9 +319,10 @@ struct ActivityObserver: public Activity::Observer {
- (void)setView:(CSOpenGLView *)view aspectRatio:(float)aspectRatio {
_view = view;
_view.displayLinkDelegate = self;
[view performWithGLContext:^{
[self setupOutputWithAspectRatio:aspectRatio];
}];
} flushDrawable:NO];
}
- (void)setupOutputWithAspectRatio:(float)aspectRatio {
@ -329,7 +331,7 @@ struct ActivityObserver: public Activity::Observer {
}
- (void)updateViewForPixelSize:(CGSize)pixelSize {
self->_scanTarget->update((int)pixelSize.width, (int)pixelSize.height);
// _pixelSize = pixelSize;
// @synchronized(self) {
// const auto scan_status = _machine->crt_machine()->get_scan_status();
@ -699,9 +701,31 @@ struct ActivityObserver: public Activity::Observer {
#pragma mark - Timer
- (void)openGLView:(CSOpenGLView *)view didUpdateDisplayLink:(CVDisplayLinkRef)displayLink {
}
- (void)openGLViewDisplayLinkDidFire:(CSOpenGLView *)view {
@synchronized(self) {
_pixelSize = view.backingSize;
}
[self.view performWithGLContext:^{
self->_scanTarget->draw((int)self->_pixelSize.width, (int)self->_pixelSize.height);
} flushDrawable:YES];
}
- (void)start {
_timer = [[CSHighPrecisionTimer alloc] initWithTask:^{
self->_machine->crt_machine()->run_for(2500000.0 / 1000000000.0);
CGSize pixelSize;
@synchronized(self) {
self->_machine->crt_machine()->run_for(2500000.0 / 1000000000.0);
pixelSize = self->_pixelSize;
}
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
[self.view performWithGLContext:^{
self->_scanTarget->update((int)pixelSize.width, (int)pixelSize.height);
} flushDrawable:NO];
});
} interval:2500000];
}

View File

@ -101,6 +101,29 @@ typedef NS_ENUM(NSInteger, CSOpenGLViewRedrawEvent) {
@end
/*!
Although I'm still on the fence about this as a design decision, CSOpenGLView is itself responsible
for creating and destroying a CVDisplayLink. There's a practical reason for this: you'll get real synchronisation
only if a link is explicitly tied to a particular display, and the CSOpenGLView therefore owns the knowledge
necessary to decide when to create and modify them. It doesn't currently just propagate "did change screen"-type
messages because I haven't yet found a way to track that other than polling, in which case I might as well put
that into the display link callback.
*/
@protocol CSOpenGLViewDisplayLinkDelegate
/*!
Informs the delegate that from now on, the display link @c displayLink will be used for update notifications
and/or that the frequency or phase or @c displayLink has changed.
*/
- (void)openGLView:(nonnull CSOpenGLView *)view didUpdateDisplayLink:(nonnull CVDisplayLinkRef)displayLink;
/*!
Informs the delegate that the display link has fired.
*/
- (void)openGLViewDisplayLinkDidFire:(nonnull CSOpenGLView *)view;
@end
/*!
Provides an OpenGL canvas with a refresh-linked update timer that can forward a subset
of typical first-responder actions.
@ -109,6 +132,7 @@ typedef NS_ENUM(NSInteger, CSOpenGLViewRedrawEvent) {
@property (atomic, weak, nullable) id <CSOpenGLViewDelegate> delegate;
@property (nonatomic, weak, nullable) id <CSOpenGLViewResponderDelegate> responderDelegate;
@property (atomic, weak, nullable) id <CSOpenGLViewDisplayLinkDelegate> displayLinkDelegate;
/// Determines whether the view offers mouse capturing — i.e. if the user clicks on the view then
/// then the system cursor is disabled and the mouse events defined by CSOpenGLViewResponderDelegate
@ -139,6 +163,7 @@ typedef NS_ENUM(NSInteger, CSOpenGLViewRedrawEvent) {
Locks this view's OpenGL context and makes it current, performs @c action and then unlocks
the context. @c action is performed on the calling queue.
*/
- (void)performWithGLContext:(nonnull dispatch_block_t)action flushDrawable:(BOOL)flushDrawable;
- (void)performWithGLContext:(nonnull dispatch_block_t)action;
/*!

View File

@ -30,11 +30,6 @@
// Note the initial screen.
_currentScreen = self.window.screen;
// Synchronize buffer swaps with vertical refresh rate.
// TODO: discard this, once scheduling is sufficiently intelligent?
GLint swapInt = 1;
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
// set the clear colour
[self.openGLContext makeCurrentContext];
glClearColor(0.0, 0.0, 0.0, 1.0);
@ -68,6 +63,9 @@
CGLPixelFormatObj cglPixelFormat = [[self pixelFormat] CGLPixelFormatObj];
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_displayLink, cglContext, cglPixelFormat);
// Give a shout-out.
[self.displayLinkDelegate openGLView:self didUpdateDisplayLink:_displayLink];
// Activate the display link
CVDisplayLinkStart(_displayLink);
}
@ -75,7 +73,8 @@
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) {
CSOpenGLView *const view = (__bridge CSOpenGLView *)displayLinkContext;
[view drawAtTime:now frequency:CVDisplayLinkGetActualOutputVideoRefreshPeriod(displayLink)];
[view checkDisplayLink];
[view.displayLinkDelegate openGLViewDisplayLinkDidFire:view];
/*
Do not touch the display link from after this call; there's a bit of a race condition with setupDisplayLink.
Specifically: Apple provides CVDisplayLinkStop but a call to that merely prevents future calls to the callback,
@ -89,7 +88,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
return kCVReturnSuccess;
}
- (void)drawAtTime:(const CVTimeStamp *)now frequency:(double)frequency {
- (void)checkDisplayLink {
// Test now whether the screen this view is on has changed since last time it was checked.
// There's likely a callback available for this, on NSWindow if nowhere else, or an NSNotification,
// but since this method is going to be called repeatedly anyway, and the test is cheap, polling
@ -105,7 +104,9 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
// the window is actually on, and at its rate.
[self setupDisplayLink];
}
}
- (void)drawAtTime:(const CVTimeStamp *)now frequency:(double)frequency {
[self redrawWithEvent:CSOpenGLViewRedrawEventTimer];
}
@ -113,11 +114,10 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
[self redrawWithEvent:CSOpenGLViewRedrawEventAppKit];
}
- (void)redrawWithEvent:(CSOpenGLViewRedrawEvent)event {
- (void)redrawWithEvent:(CSOpenGLViewRedrawEvent)event {
[self performWithGLContext:^{
[self.delegate openGLViewRedraw:self event:event];
CGLFlushDrawable([[self openGLContext] CGLContextObj]);
}];
} flushDrawable:YES];
}
- (void)invalidate {
@ -145,7 +145,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
[self performWithGLContext:^{
CGSize viewSize = [self backingSize];
glViewport(0, 0, (GLsizei)viewSize.width, (GLsizei)viewSize.height);
}];
} flushDrawable:NO];
}
- (void)awakeFromNib {
@ -178,11 +178,17 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
[self registerForDraggedTypes:@[(__bridge NSString *)kUTTypeFileURL]];
}
- (void)performWithGLContext:(dispatch_block_t)action {
- (void)performWithGLContext:(dispatch_block_t)action flushDrawable:(BOOL)flushDrawable {
CGLLockContext([[self openGLContext] CGLContextObj]);
[self.openGLContext makeCurrentContext];
action();
CGLUnlockContext([[self openGLContext] CGLContextObj]);
if(flushDrawable) CGLFlushDrawable([[self openGLContext] CGLContextObj]);
}
- (void)performWithGLContext:(nonnull dispatch_block_t)action {
[self performWithGLContext:action flushDrawable:NO];
}
#pragma mark - NSResponder