From 68a8851c52ff558f192080bf13f4284f7b9ef9ee Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 2 Jun 2016 22:29:09 -0400 Subject: [PATCH] Increased parallelism, allowing a simplification in the GL view. --- .../Documents/MachineDocument.swift | 7 +-- .../Mac/Clock Signal/Views/CSOpenGLView.m | 15 +------ Outputs/CRT/Internals/CRTOpenGL.cpp | 43 +++++++++++-------- Outputs/CRT/Internals/CRTOpenGL.hpp | 1 + 4 files changed, 32 insertions(+), 34 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 491941257..e752f032f 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -86,9 +86,10 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe skippedFrames = 0 } - if skippedFrames > 4 { - numberOfCycles = min(numberOfCycles, Int64(Double(intendedCyclesPerSecond) * frequency)) - } + // 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 diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m index 2645c368b..ce4a19046 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m @@ -56,7 +56,6 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt - (void)drawAtTime:(const CVTimeStamp *)now frequency:(double)frequency { const uint32_t processingMask = 0x01; - const uint32_t drawingMask = 0x02; // 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)) @@ -68,24 +67,14 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt OSAtomicTestAndClear(processingMask, &_updateIsOngoing); }); _hasSkipped = NO; - NSLog(@"+"); } else { _hasSkipped = YES; - NSLog(@"-"); } - // Draw the display only if a previous draw is not still ongoing. -drawViewOnlyIfDirty: is guaranteed - // to be safe to call concurrently with -openGLView:updateToTime: so there's no need to worry about - // the above interrupting the below or vice versa. - if(!OSAtomicTestAndSet(drawingMask, &_updateIsOngoing)) - { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ - [self drawViewOnlyIfDirty:YES]; - OSAtomicTestAndClear(drawingMask, &_updateIsOngoing); - }); - } + // Draw the display now regardless of other activity. + [self drawViewOnlyIfDirty:YES]; } - (void)invalidate diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 94df2e3b6..e01ea78a5 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -108,6 +108,7 @@ namespace { OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : _output_mutex(new std::mutex), + _draw_mutex(new std::mutex), _visible_area(Rect(0, 0, 1, 1)), _composite_src_output_y(0), _cleared_composite_output_y(0), @@ -172,8 +173,8 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder() void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty) { - // lock down any further work on the current frame - _output_mutex->lock(); + // lock down any other draw_frames + _draw_mutex->lock(); // establish essentials if(!output_shader_program) @@ -195,21 +196,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glDeleteSync(_fence); } - // release the mapping, giving up on trying to draw if data has been lost - GLsizei submitted_output_data = submitArrayData(output_array_buffer, _output_buffer_data.get(), &_output_buffer_data_pointer); - - // bind and flush the source array buffer - GLsizei submitted_source_data = submitArrayData(source_array_buffer, _source_buffer_data.get(), &_source_buffer_data_pointer); - - // determine how many lines are newly reclaimed; they'll need to be cleared - Range clearing_zones[2]; - - // the clearing zones for the composite output Y are calculated with a fixed offset of '1' which has the effect of clearing - // one ahead of the expected drawing area this frame; that's because the current _composite_src_output_y may or may not have been - // written to during the last update, so we want it to have been cleared during the last update. - int number_of_clearing_zones = getCircularRanges(&_cleared_composite_output_y, &_composite_src_output_y, IntermediateBufferHeight, 1, 1, clearing_zones); - uint16_t completed_texture_y = _buffer_builder->get_and_finalise_current_line(); - // make sure there's a target to draw to if(!framebuffer || framebuffer->get_height() != output_height || framebuffer->get_width() != output_width) { @@ -228,6 +214,24 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out framebuffer = std::move(new_framebuffer); } + // lock out the machine emulation until data is copied + _output_mutex->lock(); + + // release the mapping, giving up on trying to draw if data has been lost + GLsizei submitted_output_data = submitArrayData(output_array_buffer, _output_buffer_data.get(), &_output_buffer_data_pointer); + + // bind and flush the source array buffer + GLsizei submitted_source_data = submitArrayData(source_array_buffer, _source_buffer_data.get(), &_source_buffer_data_pointer); + + // determine how many lines are newly reclaimed; they'll need to be cleared + Range clearing_zones[2]; + + // the clearing zones for the composite output Y are calculated with a fixed offset of '1' which has the effect of clearing + // one ahead of the expected drawing area this frame; that's because the current _composite_src_output_y may or may not have been + // written to during the last update, so we want it to have been cleared during the last update. + int number_of_clearing_zones = getCircularRanges(&_cleared_composite_output_y, &_composite_src_output_y, IntermediateBufferHeight, 1, 1, clearing_zones); + uint16_t completed_texture_y = _buffer_builder->get_and_finalise_current_line(); + // upload new source pixels if(completed_texture_y) { @@ -239,6 +243,9 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out _buffer_builder->get_image_pointer()); } + // data having been grabbed, allow the machine to continue + _output_mutex->unlock(); + struct RenderStage { OpenGL::TextureTarget *const target; OpenGL::Shader *const shader; @@ -330,7 +337,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out framebuffer->draw((float)output_width / (float)output_height); _fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - _output_mutex->unlock(); + _draw_mutex->unlock(); } void OpenGLOutputBuilder::reset_all_OpenGL_state() diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index e562767a8..4f0a79a32 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -58,6 +58,7 @@ class OpenGLOutputBuilder { // the run and input data buffers std::unique_ptr _buffer_builder; std::unique_ptr _output_mutex; + std::unique_ptr _draw_mutex; // transient buffers indicating composite data not yet decoded GLsizei _composite_src_output_y, _cleared_composite_output_y;