1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-30 23:29:08 +00:00

Merge pull request #597 from TomHarte/MacRaceCondition

Splits OpenGL ScanTarget update and draw functions.
This commit is contained in:
Thomas Harte 2019-03-02 19:36:19 -05:00 committed by GitHub
commit 5b56ad0d78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 68 additions and 44 deletions

View File

@ -68,7 +68,7 @@
</AdditionalOptions> </AdditionalOptions>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableASanStackUseAfterReturn = "YES" enableASanStackUseAfterReturn = "YES"

View File

@ -198,17 +198,21 @@ class MachineDocument:
} }
// MARK: CSOpenGLViewDelegate // MARK: CSOpenGLViewDelegate
final func openGLView(_ view: CSOpenGLView, drawViewOnlyIfDirty onlyIfDirty: Bool) { final func openGLViewRedraw(_ view: CSOpenGLView, event redrawEvent: CSOpenGLViewRedrawEvent) {
bestEffortLock.lock() switch redrawEvent {
if let bestEffortUpdater = bestEffortUpdater { case .timer:
bestEffortLock.unlock() bestEffortLock.lock()
bestEffortUpdater.update() if let bestEffortUpdater = bestEffortUpdater {
if drawLock.try() { bestEffortLock.unlock()
self.machine.drawView(forPixelSize: view.backingSize, onlyIfDirty: onlyIfDirty) bestEffortUpdater.update()
drawLock.unlock() } else {
} bestEffortLock.unlock()
} else { }
bestEffortLock.unlock() self.machine.updateView(forPixelSize: view.backingSize)
fallthrough
case .appKit:
self.machine.drawView(forPixelSize: view.backingSize)
} }
} }

View File

@ -54,7 +54,9 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
- (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize; - (void)setAudioSamplingRate:(float)samplingRate bufferSize:(NSUInteger)bufferSize;
- (void)setView:(nullable CSOpenGLView *)view aspectRatio:(float)aspectRatio; - (void)setView:(nullable CSOpenGLView *)view aspectRatio:(float)aspectRatio;
- (void)drawViewForPixelSize:(CGSize)pixelSize onlyIfDirty:(BOOL)onlyIfDirty;
- (void)updateViewForPixelSize:(CGSize)pixelSize;
- (void)drawViewForPixelSize:(CGSize)pixelSize;
- (void)setKey:(uint16_t)key characters:(nullable NSString *)characters isPressed:(BOOL)isPressed; - (void)setKey:(uint16_t)key characters:(nullable NSString *)characters isPressed:(BOOL)isPressed;
- (void)clearAllKeys; - (void)clearAllKeys;

View File

@ -240,8 +240,12 @@ struct ActivityObserver: public Activity::Observer {
_machine->crt_machine()->set_scan_target(_scanTarget.get()); _machine->crt_machine()->set_scan_target(_scanTarget.get());
} }
- (void)drawViewForPixelSize:(CGSize)pixelSize onlyIfDirty:(BOOL)onlyIfDirty { - (void)updateViewForPixelSize:(CGSize)pixelSize {
_scanTarget->draw(true, (int)pixelSize.width, (int)pixelSize.height); _scanTarget->update((int)pixelSize.width, (int)pixelSize.height);
}
- (void)drawViewForPixelSize:(CGSize)pixelSize {
_scanTarget->draw((int)pixelSize.width, (int)pixelSize.height);
} }
- (void)paste:(NSString *)paste { - (void)paste:(NSString *)paste {

View File

@ -11,15 +11,24 @@
@class CSOpenGLView; @class CSOpenGLView;
typedef NS_ENUM(NSInteger, CSOpenGLViewRedrawEvent) {
/// Indicates that AppKit requested a redraw for some reason (mostly likely, the window is being resized). So,
/// if the delegate doesn't redraw the view, the user is likely to see a graphical flaw.
CSOpenGLViewRedrawEventAppKit,
/// Indicates that the view's display-linked timer has triggered a redraw request. So, if the delegate doesn't
/// redraw the view, the user will just see the previous drawing without interruption.
CSOpenGLViewRedrawEventTimer
};
@protocol CSOpenGLViewDelegate @protocol CSOpenGLViewDelegate
/*! /*!
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.
@param view The view making the request. @param view The view making the request.
@param onlyIfDirty If @c YES then the delegate may decline to redraw if its output would be @param redrawEvent If @c YES then the delegate may decline to redraw if its output would be
identical to the previous frame. If @c NO then the delegate must draw. identical to the previous frame. If @c NO then the delegate must draw.
*/ */
- (void)openGLView:(nonnull CSOpenGLView *)view drawViewOnlyIfDirty:(BOOL)onlyIfDirty; - (void)openGLViewRedraw:(nonnull CSOpenGLView *)view event:(CSOpenGLViewRedrawEvent)redrawEvent;
/*! /*!
Announces receipt of a file by drag and drop to the delegate. Announces receipt of a file by drag and drop to the delegate.

View File

@ -53,7 +53,15 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
- (void)drawAtTime:(const CVTimeStamp *)now frequency:(double)frequency - (void)drawAtTime:(const CVTimeStamp *)now frequency:(double)frequency
{ {
// Draw the display now regardless of other activity. // Draw the display now regardless of other activity.
[self drawViewOnlyIfDirty:YES]; [self performWithGLContext:^{
[self.delegate openGLViewRedraw:self event:CSOpenGLViewRedrawEventTimer];
CGLFlushDrawable([[self openGLContext] CGLContextObj]);
}];
}
- (void)drawRect:(NSRect)dirtyRect
{
[self.delegate openGLViewRedraw:self event:CSOpenGLViewRedrawEventAppKit];
} }
- (void)invalidate - (void)invalidate
@ -119,19 +127,6 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
[self registerForDraggedTypes:@[(__bridge NSString *)kUTTypeFileURL]]; [self registerForDraggedTypes:@[(__bridge NSString *)kUTTypeFileURL]];
} }
- (void)drawRect:(NSRect)dirtyRect
{
[self drawViewOnlyIfDirty:NO];
}
- (void)drawViewOnlyIfDirty:(BOOL)onlyIfDirty
{
[self performWithGLContext:^{
[self.delegate openGLView:self drawViewOnlyIfDirty:onlyIfDirty];
CGLFlushDrawable([[self openGLContext] CGLContextObj]);
}];
}
- (void)performWithGLContext:(dispatch_block_t)action - (void)performWithGLContext:(dispatch_block_t)action
{ {
CGLLockContext([[self openGLContext] CGLContextObj]); CGLLockContext([[self openGLContext] CGLContextObj]);

View File

@ -776,7 +776,8 @@ int main(int argc, char *argv[]) {
// Display a new frame and wait for vsync. // Display a new frame and wait for vsync.
updater.update(); updater.update();
scan_target.draw(true, int(window_width), int(window_height)); scan_target.update(int(window_width), int(window_height));
scan_target.draw(int(window_width), int(window_height));
if(activity_observer) activity_observer->draw(); if(activity_observer) activity_observer->draw();
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(window);
} }

View File

@ -383,10 +383,10 @@ void ScanTarget::setup_pipeline() {
input_shader_->set_uniform("textureName", GLint(SourceDataTextureUnit - GL_TEXTURE0)); input_shader_->set_uniform("textureName", GLint(SourceDataTextureUnit - GL_TEXTURE0));
} }
void ScanTarget::draw(bool synchronous, int output_width, int output_height) { void ScanTarget::update(int output_width, int output_height) {
if(fence_ != nullptr) { if(fence_ != nullptr) {
// if the GPU is still busy, don't wait; we'll catch it next time // if the GPU is still busy, don't wait; we'll catch it next time
if(glClientWaitSync(fence_, GL_SYNC_FLUSH_COMMANDS_BIT, synchronous ? GL_TIMEOUT_IGNORED : 0) == GL_TIMEOUT_EXPIRED) { if(glClientWaitSync(fence_, GL_SYNC_FLUSH_COMMANDS_BIT, 0) == GL_TIMEOUT_EXPIRED) {
return; return;
} }
fence_ = nullptr; fence_ = nullptr;
@ -666,15 +666,6 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) {
test_gl(glDisable, GL_BLEND); test_gl(glDisable, GL_BLEND);
} }
// Copy the accumulatiion texture to the target.
test_gl(glBindFramebuffer, GL_FRAMEBUFFER, target_framebuffer_);
test_gl(glViewport, 0, 0, (GLsizei)output_width, (GLsizei)output_height);
test_gl(glClearColor, 0.0f, 0.0f, 0.0f, 0.0f);
test_gl(glClear, GL_COLOR_BUFFER_BIT);
accumulation_texture_->bind_texture();
accumulation_texture_->draw(float(output_width) / float(output_height), 4.0f / 255.0f);
// All data now having been spooled to the GPU, update the read pointers to // All data now having been spooled to the GPU, update the read pointers to
// the submit pointer location. // the submit pointer location.
read_pointers_.store(submit_pointers); read_pointers_.store(submit_pointers);
@ -684,3 +675,18 @@ void ScanTarget::draw(bool synchronous, int output_width, int output_height) {
fence_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); fence_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
is_drawing_.clear(); is_drawing_.clear();
} }
void ScanTarget::draw(int output_width, int output_height) {
while(is_drawing_.test_and_set());
// Copy the accumulation texture to the target.
test_gl(glBindFramebuffer, GL_FRAMEBUFFER, target_framebuffer_);
test_gl(glViewport, 0, 0, (GLsizei)output_width, (GLsizei)output_height);
test_gl(glClearColor, 0.0f, 0.0f, 0.0f, 0.0f);
test_gl(glClear, GL_COLOR_BUFFER_BIT);
accumulation_texture_->bind_texture();
accumulation_texture_->draw(float(output_width) / float(output_height), 4.0f / 255.0f);
is_drawing_.clear();
}

View File

@ -42,7 +42,10 @@ class ScanTarget: public Outputs::Display::ScanTarget {
void set_target_framebuffer(GLuint); void set_target_framebuffer(GLuint);
void draw(bool synchronous, int output_width, int output_height); /*! Pushes the current state of output to the target framebuffer. */
void draw(int output_width, int output_height);
/*! Processes all the latest input, at a resolution suitable for later output to a framebuffer of the specified size. */
void update(int output_width, int output_height);
private: private:
#ifndef NDEBUG #ifndef NDEBUG