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:
parent
0f2783075f
commit
cf9729c74f
@ -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 {
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
/*!
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user