mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-29 12:50:28 +00:00
Increased parallelism, allowing a simplification in the GL view.
This commit is contained in:
parent
e3b95b8d2b
commit
68a8851c52
@ -86,9 +86,10 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe
|
|||||||
skippedFrames = 0
|
skippedFrames = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if skippedFrames > 4 {
|
// run for at most three frames up to and until that causes overshoots in the
|
||||||
numberOfCycles = min(numberOfCycles, Int64(Double(intendedCyclesPerSecond) * frequency))
|
// 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))
|
runForNumberOfCycles(Int32(numberOfCycles))
|
||||||
}
|
}
|
||||||
lastTime = time
|
lastTime = time
|
||||||
|
@ -56,7 +56,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;
|
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.
|
// 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))
|
if(!OSAtomicTestAndSet(processingMask, &_updateIsOngoing))
|
||||||
@ -68,24 +67,14 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
|
|||||||
OSAtomicTestAndClear(processingMask, &_updateIsOngoing);
|
OSAtomicTestAndClear(processingMask, &_updateIsOngoing);
|
||||||
});
|
});
|
||||||
_hasSkipped = NO;
|
_hasSkipped = NO;
|
||||||
NSLog(@"+");
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_hasSkipped = YES;
|
_hasSkipped = YES;
|
||||||
NSLog(@"-");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the display only if a previous draw is not still ongoing. -drawViewOnlyIfDirty: is guaranteed
|
// Draw the display now regardless of other activity.
|
||||||
// to be safe to call concurrently with -openGLView:updateToTime: so there's no need to worry about
|
[self drawViewOnlyIfDirty:YES];
|
||||||
// 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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)invalidate
|
- (void)invalidate
|
||||||
|
@ -108,6 +108,7 @@ namespace {
|
|||||||
|
|
||||||
OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
|
OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
|
||||||
_output_mutex(new std::mutex),
|
_output_mutex(new std::mutex),
|
||||||
|
_draw_mutex(new std::mutex),
|
||||||
_visible_area(Rect(0, 0, 1, 1)),
|
_visible_area(Rect(0, 0, 1, 1)),
|
||||||
_composite_src_output_y(0),
|
_composite_src_output_y(0),
|
||||||
_cleared_composite_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)
|
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
|
// lock down any other draw_frames
|
||||||
_output_mutex->lock();
|
_draw_mutex->lock();
|
||||||
|
|
||||||
// establish essentials
|
// establish essentials
|
||||||
if(!output_shader_program)
|
if(!output_shader_program)
|
||||||
@ -195,21 +196,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
|
|||||||
glDeleteSync(_fence);
|
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
|
// make sure there's a target to draw to
|
||||||
if(!framebuffer || framebuffer->get_height() != output_height || framebuffer->get_width() != output_width)
|
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);
|
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
|
// upload new source pixels
|
||||||
if(completed_texture_y)
|
if(completed_texture_y)
|
||||||
{
|
{
|
||||||
@ -239,6 +243,9 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
|
|||||||
_buffer_builder->get_image_pointer());
|
_buffer_builder->get_image_pointer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// data having been grabbed, allow the machine to continue
|
||||||
|
_output_mutex->unlock();
|
||||||
|
|
||||||
struct RenderStage {
|
struct RenderStage {
|
||||||
OpenGL::TextureTarget *const target;
|
OpenGL::TextureTarget *const target;
|
||||||
OpenGL::Shader *const shader;
|
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);
|
framebuffer->draw((float)output_width / (float)output_height);
|
||||||
|
|
||||||
_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
_output_mutex->unlock();
|
_draw_mutex->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLOutputBuilder::reset_all_OpenGL_state()
|
void OpenGLOutputBuilder::reset_all_OpenGL_state()
|
||||||
|
@ -58,6 +58,7 @@ class OpenGLOutputBuilder {
|
|||||||
// the run and input data buffers
|
// the run and input data buffers
|
||||||
std::unique_ptr<CRTInputBufferBuilder> _buffer_builder;
|
std::unique_ptr<CRTInputBufferBuilder> _buffer_builder;
|
||||||
std::unique_ptr<std::mutex> _output_mutex;
|
std::unique_ptr<std::mutex> _output_mutex;
|
||||||
|
std::unique_ptr<std::mutex> _draw_mutex;
|
||||||
|
|
||||||
// transient buffers indicating composite data not yet decoded
|
// transient buffers indicating composite data not yet decoded
|
||||||
GLsizei _composite_src_output_y, _cleared_composite_output_y;
|
GLsizei _composite_src_output_y, _cleared_composite_output_y;
|
||||||
|
Loading…
Reference in New Issue
Block a user