From fc1a67c157fc89f0efdebb80a556483120c5cb02 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 May 2016 18:45:55 -0400 Subject: [PATCH 01/27] Lateral is no longer written per vertex. It's a fixed list. --- Outputs/CRT/CRT.cpp | 4 --- Outputs/CRT/Internals/CRTConstants.hpp | 5 ++-- Outputs/CRT/Internals/CRTOpenGL.cpp | 34 +++++++++++++++++--------- Outputs/CRT/Internals/CRTOpenGL.hpp | 4 ++- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 1b6c32084..029dd3b52 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -99,7 +99,6 @@ Flywheel::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested, #define output_position_y(v) (*(uint16_t *)&next_run[OutputVertexSize*v + OutputVertexOffsetOfPosition + 2]) #define output_tex_x(v) (*(uint16_t *)&next_run[OutputVertexSize*v + OutputVertexOffsetOfTexCoord + 0]) #define output_tex_y(v) (*(uint16_t *)&next_run[OutputVertexSize*v + OutputVertexOffsetOfTexCoord + 2]) -#define output_lateral(v) next_run[OutputVertexSize*v + OutputVertexOffsetOfLateral] #define source_input_position_x(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfInputPosition + 0]) #define source_input_position_y(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfInputPosition + 2]) @@ -195,9 +194,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi output_position_y(0) = output_position_y(1) = output_position_y(2) = (uint16_t)(_vertical_flywheel->get_current_output_position() / _vertical_flywheel_output_divider); output_tex_x(0) = output_tex_x(1) = output_tex_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); output_tex_y(0) = output_tex_y(1) = output_tex_y(2) = _openGL_output_builder->get_composite_output_y(); - output_lateral(0) = 0; - output_lateral(1) = _is_writing_composite_run ? 1 : 0; - output_lateral(2) = 1; _openGL_output_builder->complete_output_run(3); _is_writing_composite_run ^= true; diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp index 63cfe99ed..b6edb5013 100644 --- a/Outputs/CRT/Internals/CRTConstants.hpp +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -19,9 +19,8 @@ namespace CRT { // or is one of the intermediate buffers that we've used to convert from composite towards RGB. const GLsizei OutputVertexOffsetOfPosition = 0; const GLsizei OutputVertexOffsetOfTexCoord = 4; -const GLsizei OutputVertexOffsetOfLateral = 8; -const GLsizei OutputVertexSize = 12; +const GLsizei OutputVertexSize = 8; // Input vertices, used only in composite mode, map from the input buffer to temporary buffer locations; such // remapping occurs to ensure a continous stream of data for each scan, giving correct out-of-bounds behaviour @@ -41,7 +40,7 @@ const GLsizei IntermediateBufferWidth = 2048; const GLsizei IntermediateBufferHeight = 2048; // Some internal buffer sizes -const GLsizeiptr OutputVertexBufferDataSize = 89856; // a multiple of 6 * OutputVertexSize +const GLsizeiptr OutputVertexBufferDataSize = 59904; // a multiple of 6 * OutputVertexSize const GLsizeiptr SourceVertexBufferDataSize = 87360; // a multiple of 2 * SourceVertexSize } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 55adaa3a4..f804dce9d 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -137,6 +137,17 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); glBufferData(GL_ARRAY_BUFFER, OutputVertexBufferDataSize, NULL, GL_STREAM_DRAW); + // create and populate a buffer for the lateral attributes + glGenBuffers(1, &lateral_array_buffer); + glBindBuffer(GL_ARRAY_BUFFER, lateral_array_buffer); + size_t number_of_vertices = OutputVertexBufferDataSize/OutputVertexSize; + uint8_t lateral_pattern[] = {0, 0, 1, 0, 1, 1}; + uint8_t *laterals = new uint8_t[number_of_vertices]; + for(size_t c = 0; c < number_of_vertices; c++) + laterals[c] = lateral_pattern[c%6]; + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)number_of_vertices, laterals, GL_STATIC_DRAW); + delete[] laterals; + // map that buffer too, for any CRT activity that may occur before the first draw _output_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, OutputVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); @@ -160,6 +171,7 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder() glDeleteBuffers(1, &_input_texture_array); glDeleteBuffers(1, &output_array_buffer); glDeleteBuffers(1, &source_array_buffer); + glDeleteBuffers(1, &lateral_array_buffer); glDeleteVertexArrays(1, &output_vertex_array); free(_composite_shader); @@ -168,6 +180,9 @@ 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(); + // establish essentials if(!output_shader_program) { @@ -190,9 +205,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out defaultFramebuffer = 0; } - // lock down any further work on the current frame - _output_mutex->lock(); - // release the mapping, giving up on trying to draw if data has been lost glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); glUnmapBuffer(GL_ARRAY_BUFFER); @@ -466,19 +478,21 @@ void OpenGLOutputBuilder::prepare_output_vertex_array() { GLint positionAttribute = output_shader_program->get_attrib_location("position"); GLint textureCoordinatesAttribute = output_shader_program->get_attrib_location("srcCoordinates"); - GLint lateralAttribute = output_shader_program->get_attrib_location("lateral"); glBindVertexArray(output_vertex_array); glEnableVertexAttribArray((GLuint)positionAttribute); glEnableVertexAttribArray((GLuint)textureCoordinatesAttribute); - glEnableVertexAttribArray((GLuint)lateralAttribute); const GLsizei vertexStride = OutputVertexSize; glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); glVertexAttribPointer((GLuint)positionAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfPosition); glVertexAttribPointer((GLuint)textureCoordinatesAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfTexCoord); - glVertexAttribPointer((GLuint)lateralAttribute, 1, GL_UNSIGNED_BYTE, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfLateral); + + GLint lateralAttribute = output_shader_program->get_attrib_location("lateral"); + glEnableVertexAttribArray((GLuint)lateralAttribute); + glBindBuffer(GL_ARRAY_BUFFER, lateral_array_buffer); + glVertexAttribPointer((GLuint)lateralAttribute, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, (void *)0); } } @@ -497,6 +511,7 @@ void OpenGLOutputBuilder::set_output_device(OutputDevice output_device) void OpenGLOutputBuilder::set_timing(unsigned int input_frequency, unsigned int cycles_per_line, unsigned int height_of_display, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider) { + _output_mutex->lock(); _input_frequency = input_frequency; _cycles_per_line = cycles_per_line; _height_of_display = height_of_display; @@ -505,13 +520,13 @@ void OpenGLOutputBuilder::set_timing(unsigned int input_frequency, unsigned int _vertical_period_divider = vertical_period_divider; set_timing_uniforms(); + _output_mutex->unlock(); } #pragma mark - Internal Configuration void OpenGLOutputBuilder::set_colour_space_uniforms() { - _output_mutex->lock(); GLfloat rgbToYUV[] = {0.299f, -0.14713f, 0.615f, 0.587f, -0.28886f, -0.51499f, 0.114f, 0.436f, -0.10001f}; GLfloat yuvToRGB[] = {1.0f, 1.0f, 1.0f, 0.0f, -0.39465f, 2.03211f, 1.13983f, -0.58060f, 0.0f}; @@ -535,13 +550,10 @@ void OpenGLOutputBuilder::set_colour_space_uniforms() if(composite_input_shader_program) composite_input_shader_program->set_colour_conversion_matrices(fromRGB, toRGB); if(composite_chrominance_filter_shader_program) composite_chrominance_filter_shader_program->set_colour_conversion_matrices(fromRGB, toRGB); - _output_mutex->unlock(); } void OpenGLOutputBuilder::set_timing_uniforms() { - _output_mutex->lock(); - OpenGL::IntermediateShader *intermediate_shaders[] = { composite_input_shader_program.get(), composite_separation_filter_program.get(), @@ -563,6 +575,4 @@ void OpenGLOutputBuilder::set_timing_uniforms() if(composite_y_filter_shader_program) composite_y_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency * 0.66f); if(composite_chrominance_filter_shader_program) composite_chrominance_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency * 0.5f); if(rgb_filter_shader_program) rgb_filter_shader_program->set_filter_coefficients(_cycles_per_line, (float)_input_frequency * 0.5f); - - _output_mutex->unlock(); } diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index 5015d2486..a8abfb24f 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -73,7 +73,7 @@ class OpenGLOutputBuilder { std::unique_ptr framebuffer; // the current pixel output - GLuint output_array_buffer, output_vertex_array; + GLuint output_array_buffer, lateral_array_buffer, output_vertex_array; GLuint source_array_buffer, source_vertex_array; unsigned int _last_output_width, _last_output_height; @@ -91,10 +91,12 @@ class OpenGLOutputBuilder { inline void set_colour_format(ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator) { + _output_mutex->lock(); _colour_space = colour_space; _colour_cycle_numerator = colour_cycle_numerator; _colour_cycle_denominator = colour_cycle_denominator; set_colour_space_uniforms(); + _output_mutex->unlock(); } inline void set_visible_area(Rect visible_area) From 254171106181f8b4d0203d4400311fa171663268 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 May 2016 20:56:47 -0400 Subject: [PATCH 02/27] Made attempts (i) to flush buffers before unmapping them; and (ii) to bring texture uploads within the new orthodoxy. --- Outputs/CRT/CRT.cpp | 8 +- Outputs/CRT/CRT.hpp | 12 -- .../CRT/Internals/CRTInputBufferBuilder.cpp | 9 +- .../CRT/Internals/CRTInputBufferBuilder.hpp | 54 ++++++--- Outputs/CRT/Internals/CRTOpenGL.cpp | 111 ++++++++---------- Outputs/CRT/Internals/CRTOpenGL.hpp | 10 +- Outputs/CRT/Internals/TextureTarget.cpp | 2 + 7 files changed, 106 insertions(+), 100 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 029dd3b52..5166e9717 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -293,8 +293,8 @@ void CRT::output_level(unsigned int number_of_cycles) Scan scan { .type = Scan::Type::Level, .number_of_cycles = number_of_cycles, - .tex_x = _openGL_output_builder->get_last_write_x_posiiton(), - .tex_y = _openGL_output_builder->get_last_write_y_posiiton() + .tex_x = _openGL_output_builder->get_last_write_x_posititon(), + .tex_y = _openGL_output_builder->get_last_write_y_posititon() }; output_scan(&scan); } @@ -316,8 +316,8 @@ void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider Scan scan { .type = Scan::Type::Data, .number_of_cycles = number_of_cycles, - .tex_x = _openGL_output_builder->get_last_write_x_posiiton(), - .tex_y = _openGL_output_builder->get_last_write_y_posiiton(), + .tex_x = _openGL_output_builder->get_last_write_x_posititon(), + .tex_y = _openGL_output_builder->get_last_write_y_posititon(), .source_divider = source_divider }; output_scan(&scan); diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index ed0852666..dd48512f2 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -240,18 +240,6 @@ class CRT { _openGL_output_builder->set_rgb_sampling_function(shader); } - /*! Optionally sets a function that will map from an input cycle count to a colour carrier phase. - - If this function is not supplied then the colour phase is determined from - the input clock rate and the the colour cycle clock rate. Machines whose per-line clock rate - is not intended exactly to match the normal line time may prefer to supply a custom function. - - @param A GLSL fragent including a function with the signature - `float phase_for_clock_cycle(int cycle)` that returns the colour phase at the beginning of - the supplied cycle. - */ -// void set_phase_function(const char *shader); - inline void set_output_device(OutputDevice output_device) { _openGL_output_builder->set_output_device(output_device); diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index 4e4ac0fdd..a277860f3 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -16,13 +16,11 @@ CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) : bytes_per_pixel(bytes_per_pixel), _next_write_x_position(0), _next_write_y_position(0), - last_uploaded_line(0), - _wraparound_sync(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0)) + last_uploaded_line(0) {} CRTInputBufferBuilder::~CRTInputBufferBuilder() { - glDeleteSync(_wraparound_sync); } void CRTInputBufferBuilder::allocate_write_area(size_t required_length) @@ -31,12 +29,13 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth) { - move_to_new_line(); + _next_write_x_position = 0; + _next_write_y_position++; } _write_x_position = _next_write_x_position + 1; _write_y_position = _next_write_y_position; - _write_target_pointer = (_write_y_position * InputBufferBuilderWidth) + _write_x_position; + _write_target_pointer = ((_write_y_position % InputBufferBuilderHeight) * InputBufferBuilderWidth) + _write_x_position; _next_write_x_position += required_length + 2; } diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp index 4e074f2bc..5e053f817 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp @@ -25,30 +25,52 @@ struct CRTInputBufferBuilder { void allocate_write_area(size_t required_length); void reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer); - // a pointer to the section of content buffer currently being - // returned and to where the next section will begin - uint16_t _next_write_x_position, _next_write_y_position; - uint16_t _write_x_position, _write_y_position; - size_t _write_target_pointer; - size_t _last_allocation_amount; - size_t bytes_per_pixel; - - // Storage for the amount of buffer uploaded so far; initialised correctly by the buffer - // builder but otherwise entrusted to the CRT to update. - unsigned int last_uploaded_line; - - GLsync _wraparound_sync; - - inline void move_to_new_line() + inline uint16_t get_and_finalise_current_line() { + uint16_t result = _write_y_position; _next_write_x_position = 0; - _next_write_y_position = (_next_write_y_position+1)%InputBufferBuilderHeight; + _next_write_y_position++; + _next_write_y_position %= InputBufferBuilderHeight; + return result; } inline uint8_t *get_write_target(uint8_t *buffer) { return &buffer[_write_target_pointer * bytes_per_pixel]; } + + inline uint16_t get_last_write_x_position() + { + return _write_x_position; + } + + inline uint16_t get_last_write_y_position() + { + return _write_y_position; + } + + inline size_t get_bytes_per_pixel() + { + return bytes_per_pixel; + } + + private: + // where pixel data will be put to the next time a write is requested + uint16_t _next_write_x_position, _next_write_y_position; + + // the most recent position returned for pixel data writing + uint16_t _write_x_position, _write_y_position; + + // details of the most recent allocation + size_t _write_target_pointer; + size_t _last_allocation_amount; + + // the buffer size + size_t bytes_per_pixel; + + // Storage for the amount of buffer uploaded so far; initialised correctly by the buffer + // builder but otherwise entrusted to the CRT to update. + unsigned int last_uploaded_line; }; } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index f804dce9d..5f917fedd 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -118,12 +118,12 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, internalFormatForDepth(_buffer_builder->bytes_per_pixel), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(_buffer_builder->bytes_per_pixel), GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormatForDepth(_buffer_builder->get_bytes_per_pixel()), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE, nullptr); // create a pixel unpack buffer glGenBuffers(1, &_input_texture_array); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array); - _input_texture_array_size = (GLsizeiptr)(InputBufferBuilderWidth * InputBufferBuilderHeight * _buffer_builder->bytes_per_pixel); + _input_texture_array_size = (GLsizeiptr)(InputBufferBuilderWidth * InputBufferBuilderHeight * _buffer_builder->get_bytes_per_pixel()); glBufferData(GL_PIXEL_UNPACK_BUFFER, _input_texture_array_size, NULL, GL_STREAM_DRAW); // map the buffer for clients @@ -205,11 +205,48 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out defaultFramebuffer = 0; } + // determine how many lines are newly reclaimed; they'll need to be cleared + GLsizei clearing_zones[4], source_drawing_zones[4]; + GLsizei output_drawing_zones[4], texture_upload_zones[4]; + int number_of_clearing_zones = getCircularRanges(_cleared_composite_output_y+1, _composite_src_output_y+1, IntermediateBufferHeight, 1, clearing_zones); + int number_of_source_drawing_zones = getCircularRanges(_drawn_source_buffer_data_pointer, _source_buffer_data_pointer, SourceVertexBufferDataSize, 2*SourceVertexSize, source_drawing_zones); + int number_of_output_drawing_zones = getCircularRanges(_drawn_output_buffer_data_pointer, _output_buffer_data_pointer, OutputVertexBufferDataSize, 6*OutputVertexSize, output_drawing_zones); + + uint16_t completed_texture_y = _buffer_builder->get_and_finalise_current_line(); + int number_of_texture_upload_zones = getCircularRanges(_uploaded_texture_y, completed_texture_y, InputBufferBuilderHeight, 1, texture_upload_zones); + + _composite_src_output_y %= IntermediateBufferHeight; + _source_buffer_data_pointer %= SourceVertexBufferDataSize; + _output_buffer_data_pointer %= OutputVertexBufferDataSize; + + _cleared_composite_output_y = _composite_src_output_y; + _drawn_source_buffer_data_pointer = _source_buffer_data_pointer; + _drawn_output_buffer_data_pointer = _output_buffer_data_pointer; + _uploaded_texture_y = completed_texture_y % InputBufferBuilderHeight; + // release the mapping, giving up on trying to draw if data has been lost glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); + for(int c = 0; c < number_of_output_drawing_zones; c++) + { + glFlushMappedBufferRange(GL_ARRAY_BUFFER, output_drawing_zones[c*2], output_drawing_zones[c*2 + 1]); + } glUnmapBuffer(GL_ARRAY_BUFFER); + + // bind and flush the source array buffer glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); + for(int c = 0; c < number_of_source_drawing_zones; c++) + { + glFlushMappedBufferRange(GL_ARRAY_BUFFER, source_drawing_zones[c*2], source_drawing_zones[c*2 + 1]); + } glUnmapBuffer(GL_ARRAY_BUFFER); + + if(number_of_texture_upload_zones) + { + for(int c = 0; c < number_of_texture_upload_zones; c++) + { + glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, (GLsizeiptr)texture_upload_zones[c*2] * InputBufferBuilderWidth * (GLsizeiptr)_buffer_builder->get_bytes_per_pixel(), (GLsizeiptr)texture_upload_zones[c*2 + 1] * InputBufferBuilderWidth * (GLsizeiptr)_buffer_builder->get_bytes_per_pixel()); + } + } glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // make sure there's a target to draw to @@ -233,26 +270,14 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array); } - // upload more source pixel data if any; we'll always resubmit the last line submitted last - // time as it may have had extra data appended to it - if(_buffer_builder->_write_y_position < _buffer_builder->last_uploaded_line) + // upload new source pixels + for(int c = 0; c < number_of_texture_upload_zones; c++) { glTexSubImage2D( GL_TEXTURE_2D, 0, - 0, (GLint)_buffer_builder->last_uploaded_line, - InputBufferBuilderWidth, (GLint)(InputBufferBuilderHeight - _buffer_builder->last_uploaded_line), - formatForDepth(_buffer_builder->bytes_per_pixel), GL_UNSIGNED_BYTE, - (void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->bytes_per_pixel)); - _buffer_builder->last_uploaded_line = 0; - } - - if(_buffer_builder->_write_y_position > _buffer_builder->last_uploaded_line) - { - glTexSubImage2D( GL_TEXTURE_2D, 0, - 0, (GLint)_buffer_builder->last_uploaded_line, - InputBufferBuilderWidth, (GLint)(1 + _buffer_builder->_next_write_y_position - _buffer_builder->last_uploaded_line), - formatForDepth(_buffer_builder->bytes_per_pixel), GL_UNSIGNED_BYTE, - (void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->bytes_per_pixel)); - _buffer_builder->last_uploaded_line = _buffer_builder->_next_write_y_position; + 0, texture_upload_zones[c*2], + InputBufferBuilderWidth, texture_upload_zones[c*2 + 1], + formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE, + (void *)((size_t)texture_upload_zones[c*2] * InputBufferBuilderWidth * _buffer_builder->get_bytes_per_pixel())); } struct RenderStage { @@ -280,29 +305,12 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out RenderStage *active_pipeline = (_output_device == Television || !rgb_input_shader_program) ? composite_render_stages : rgb_render_stages; // for television, update intermediate buffers and then draw; for a monitor, just draw - if(_drawn_source_buffer_data_pointer != _source_buffer_data_pointer) + if(number_of_source_drawing_zones) { - // determine how many lines are newly reclaimed; they'll need to be cleared - GLsizei clearing_zones[4], drawing_zones[4]; - int number_of_clearing_zones = getCircularRanges(_cleared_composite_output_y+1, _composite_src_output_y+1, IntermediateBufferHeight, 1, clearing_zones); - int number_of_drawing_zones = getCircularRanges(_drawn_source_buffer_data_pointer, _source_buffer_data_pointer, SourceVertexBufferDataSize, 2*SourceVertexSize, drawing_zones); - - _composite_src_output_y %= IntermediateBufferHeight; - _cleared_composite_output_y = _composite_src_output_y; - _source_buffer_data_pointer %= SourceVertexBufferDataSize; - _drawn_source_buffer_data_pointer = _source_buffer_data_pointer; - // all drawing will be from the source vertex array and without blending glBindVertexArray(source_vertex_array); glDisable(GL_BLEND); - // flush the source data - glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); - for(int c = 0; c < number_of_drawing_zones; c++) - { - glFlushMappedBufferRange(GL_ARRAY_BUFFER, drawing_zones[c*2] / SourceVertexSize, drawing_zones[c*2 + 1] / SourceVertexSize); - } - while(active_pipeline->target) { // switch to the initial texture @@ -323,9 +331,9 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out } // draw as desired - for(int c = 0; c < number_of_drawing_zones; c++) + for(int c = 0; c < number_of_source_drawing_zones; c++) { - glDrawArrays(GL_LINES, drawing_zones[c*2] / SourceVertexSize, drawing_zones[c*2 + 1] / SourceVertexSize); + glDrawArrays(GL_LINES, source_drawing_zones[c*2] / SourceVertexSize, source_drawing_zones[c*2 + 1] / SourceVertexSize); } active_pipeline++; @@ -335,22 +343,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out // transfer to framebuffer framebuffer->bind_framebuffer(); - // draw all pending lines - GLsizei drawing_zones[4]; - int number_of_drawing_zones = getCircularRanges(_drawn_output_buffer_data_pointer, _output_buffer_data_pointer, OutputVertexBufferDataSize, 6*OutputVertexSize, drawing_zones); - - // flush the buffer data - glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); - for(int c = 0; c < number_of_drawing_zones; c++) - { - glFlushMappedBufferRange(GL_ARRAY_BUFFER, drawing_zones[c*2] / OutputVertexSize, drawing_zones[c*2 + 1] / OutputVertexSize); - } - - _output_buffer_data_pointer %= SourceVertexBufferDataSize; - _output_buffer_data_pointer -= (_output_buffer_data_pointer%(6*OutputVertexSize)); - _drawn_output_buffer_data_pointer = _output_buffer_data_pointer; - - if(number_of_drawing_zones > 0) + if(number_of_output_drawing_zones) { glEnable(GL_BLEND); @@ -367,9 +360,9 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out output_shader_program->bind(); // draw - for(int c = 0; c < number_of_drawing_zones; c++) + for(int c = 0; c < number_of_output_drawing_zones; c++) { - glDrawArrays(GL_TRIANGLE_STRIP, drawing_zones[c*2] / OutputVertexSize, drawing_zones[c*2 + 1] / OutputVertexSize); + glDrawArrays(GL_TRIANGLE_STRIP, output_drawing_zones[c*2] / OutputVertexSize, output_drawing_zones[c*2 + 1] / OutputVertexSize); } } @@ -387,7 +380,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); _source_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, SourceVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); - _input_texture_data = (uint8_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, _input_texture_array_size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); + _input_texture_data = (uint8_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, _input_texture_array_size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT ); _output_mutex->unlock(); } diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index a8abfb24f..dd2343e46 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -157,14 +157,14 @@ class OpenGLOutputBuilder { _buffer_builder->reduce_previous_allocation_to(actual_length, _input_texture_data); } - inline uint16_t get_last_write_x_posiiton() + inline uint16_t get_last_write_x_posititon() { - return _buffer_builder->_write_x_position; + return _buffer_builder->get_last_write_x_position(); } - inline uint16_t get_last_write_y_posiiton() + inline uint16_t get_last_write_y_posititon() { - return _buffer_builder->_write_y_position; + return _buffer_builder->get_last_write_y_position(); } void draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty); @@ -186,6 +186,8 @@ class OpenGLOutputBuilder { uint8_t *_output_buffer_data; GLsizei _output_buffer_data_pointer; GLsizei _drawn_output_buffer_data_pointer; + + uint16_t _uploaded_texture_y; }; } diff --git a/Outputs/CRT/Internals/TextureTarget.cpp b/Outputs/CRT/Internals/TextureTarget.cpp index 2ea700a6f..2738a43d1 100644 --- a/Outputs/CRT/Internals/TextureTarget.cpp +++ b/Outputs/CRT/Internals/TextureTarget.cpp @@ -37,6 +37,8 @@ TextureTarget::TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit) if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) throw ErrorFramebufferIncomplete; + + glClear(GL_COLOR_BUFFER_BIT); } TextureTarget::~TextureTarget() From 759fbd89ca4866ed14427cc2bbebd36cfe72183c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 May 2016 21:26:09 -0400 Subject: [PATCH 03/27] Switched pixel unpack buffer back to explicit flushes. --- Outputs/CRT/Internals/CRTOpenGL.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 5f917fedd..8c6b5b6d7 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -266,11 +266,11 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out new_framebuffer->bind_texture(); } framebuffer = std::move(new_framebuffer); - glActiveTexture(source_data_texture_unit); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array); } // upload new source pixels + glActiveTexture(source_data_texture_unit); for(int c = 0; c < number_of_texture_upload_zones; c++) { glTexSubImage2D( GL_TEXTURE_2D, 0, @@ -380,7 +380,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); _source_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, SourceVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); - _input_texture_data = (uint8_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, _input_texture_array_size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT ); + _input_texture_data = (uint8_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, _input_texture_array_size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); _output_mutex->unlock(); } From 3684c6404f6faea71e48092428ab2ac9b58081d8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 May 2016 22:22:12 -0400 Subject: [PATCH 04/27] Proven through deliberate inefficiency: the issues are (i) texture upload; and (ii) buffer wraparound. --- Outputs/CRT/Internals/CRTConstants.hpp | 4 ++-- .../CRT/Internals/CRTInputBufferBuilder.hpp | 2 +- Outputs/CRT/Internals/CRTOpenGL.cpp | 19 ++++++++++++------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp index b6edb5013..743fa4b57 100644 --- a/Outputs/CRT/Internals/CRTConstants.hpp +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -40,8 +40,8 @@ const GLsizei IntermediateBufferWidth = 2048; const GLsizei IntermediateBufferHeight = 2048; // Some internal buffer sizes -const GLsizeiptr OutputVertexBufferDataSize = 59904; // a multiple of 6 * OutputVertexSize -const GLsizeiptr SourceVertexBufferDataSize = 87360; // a multiple of 2 * SourceVertexSize +const GLsizeiptr OutputVertexBufferDataSize = 5990400; // a multiple of 6 * OutputVertexSize +const GLsizeiptr SourceVertexBufferDataSize = 8736000; // a multiple of 2 * SourceVertexSize } } diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp index 5e053f817..a65182887 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp @@ -46,7 +46,7 @@ struct CRTInputBufferBuilder { inline uint16_t get_last_write_y_position() { - return _write_y_position; + return _write_y_position % InputBufferBuilderHeight; } inline size_t get_bytes_per_pixel() diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 8c6b5b6d7..e5ee9206c 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -270,14 +270,19 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out } // upload new source pixels - glActiveTexture(source_data_texture_unit); - for(int c = 0; c < number_of_texture_upload_zones; c++) + if(number_of_texture_upload_zones) { - glTexSubImage2D( GL_TEXTURE_2D, 0, - 0, texture_upload_zones[c*2], - InputBufferBuilderWidth, texture_upload_zones[c*2 + 1], - formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE, - (void *)((size_t)texture_upload_zones[c*2] * InputBufferBuilderWidth * _buffer_builder->get_bytes_per_pixel())); + glActiveTexture(source_data_texture_unit); + glTexImage2D(GL_TEXTURE_2D, 0, internalFormatForDepth(_buffer_builder->get_bytes_per_pixel()), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE, (void *)0); +// for(int c = 0; c < number_of_texture_upload_zones; c++) +// { +// glTexSubImage2D( GL_TEXTURE_2D, 0, +// 0, texture_upload_zones[c*2], +// InputBufferBuilderWidth, texture_upload_zones[c*2 + 1], +// formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE, +// (void *)((size_t)texture_upload_zones[c*2] * InputBufferBuilderWidth * _buffer_builder->get_bytes_per_pixel())); +// } +// glFinish(); } struct RenderStage { From 87df57195d20bccf84ec9e2c2e8e0bb83f5b529a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 4 May 2016 07:39:45 -0400 Subject: [PATCH 05/27] Started on an attempt not to treat the various buffers as free to loop within, starting with the input texture. --- Machines/Atari2600/Atari2600.cpp | 7 +- Machines/Electron/Electron.cpp | 155 ++++++++++-------- Outputs/CRT/CRT.cpp | 24 ++- Outputs/CRT/CRT.hpp | 6 +- .../CRT/Internals/CRTInputBufferBuilder.cpp | 53 +++--- .../CRT/Internals/CRTInputBufferBuilder.hpp | 20 ++- Outputs/CRT/Internals/CRTOpenGL.hpp | 4 +- 7 files changed, 151 insertions(+), 118 deletions(-) diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 4d12af989..0022cdc03 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -156,8 +156,8 @@ void Machine::get_output_pixel(uint8_t *pixel, int offset) if(playerPixels[0] || missilePixels[0]) outputColour = _playerColour[0]; } - // store colour - pixel[0] = outputColour; + // store colour, if possible + if(pixel) *pixel = outputColour; } // in imputing the knowledge that all we're dealing with is the rollover from 159 to 0, @@ -237,8 +237,7 @@ void Machine::output_pixels(unsigned int count) } if(_horizontalTimer < (_vBlankExtend ? 152 : 160)) { - if(_outputBuffer) - get_output_pixel(&_outputBuffer[_lastOutputStateDuration], 159 - _horizontalTimer); + get_output_pixel(_outputBuffer ? &_outputBuffer[_lastOutputStateDuration] : nullptr, 159 - _horizontalTimer); // increment all graphics counters increment_object_counter(0); diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index e2453dfd2..5ddb69435 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -539,7 +539,7 @@ inline void Machine::start_pixel_line() } _currentScreenAddress = _startLineAddress; _current_pixel_column = 0; - _current_output_target = nullptr; + _initial_output_target = _current_output_target = nullptr; } inline void Machine::end_pixel_line() @@ -566,7 +566,7 @@ inline void Machine::output_pixels(unsigned int number_of_cycles) case 2: case 5: divider = 8; break; } - if(!_current_output_target || divider != _current_output_divider) + if(!_initial_output_target || divider != _current_output_divider) { if(_current_output_target) _crt->output_data((unsigned int)((_current_output_target - _initial_output_target) * _current_output_divider), _current_output_divider); _current_output_divider = divider; @@ -584,97 +584,112 @@ inline void Machine::output_pixels(unsigned int number_of_cycles) switch(_screen_mode) { case 0: case 3: - while(number_of_cycles--) + if(_initial_output_target) { - get_pixel(); - *(uint32_t *)_current_output_target = _paletteTables.eighty1bpp[_last_pixel_byte]; - _current_output_target += 4; - _current_pixel_column++; - } + while(number_of_cycles--) + { + get_pixel(); + *(uint32_t *)_current_output_target = _paletteTables.eighty1bpp[_last_pixel_byte]; + _current_output_target += 4; + _current_pixel_column++; + } + } else _current_output_target += 4*number_of_cycles; break; case 1: - while(number_of_cycles--) + if(_initial_output_target) { - get_pixel(); - *(uint16_t *)_current_output_target = _paletteTables.eighty2bpp[_last_pixel_byte]; - _current_output_target += 2; - _current_pixel_column++; - } + while(number_of_cycles--) + { + get_pixel(); + *(uint16_t *)_current_output_target = _paletteTables.eighty2bpp[_last_pixel_byte]; + _current_output_target += 2; + _current_pixel_column++; + } + } else _current_output_target += 2*number_of_cycles; break; case 2: - while(number_of_cycles--) + if(_initial_output_target) { - get_pixel(); - *_current_output_target = _paletteTables.eighty4bpp[_last_pixel_byte]; - _current_output_target += 1; - _current_pixel_column++; - } + while(number_of_cycles--) + { + get_pixel(); + *_current_output_target = _paletteTables.eighty4bpp[_last_pixel_byte]; + _current_output_target += 1; + _current_pixel_column++; + } + } else _current_output_target += number_of_cycles; break; case 4: case 6: - if(_current_pixel_column&1) + if(_initial_output_target) { - _last_pixel_byte <<= 4; - *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; - _current_output_target += 2; + if(_current_pixel_column&1) + { + _last_pixel_byte <<= 4; + *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; + _current_output_target += 2; - number_of_cycles--; - _current_pixel_column++; - } - while(number_of_cycles > 1) - { - get_pixel(); - *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; - _current_output_target += 2; + number_of_cycles--; + _current_pixel_column++; + } + while(number_of_cycles > 1) + { + get_pixel(); + *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; + _current_output_target += 2; - _last_pixel_byte <<= 4; - *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; - _current_output_target += 2; + _last_pixel_byte <<= 4; + *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; + _current_output_target += 2; - number_of_cycles -= 2; - _current_pixel_column+=2; - } - if(number_of_cycles) - { - get_pixel(); - *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; - _current_output_target += 2; - _current_pixel_column++; - } + number_of_cycles -= 2; + _current_pixel_column+=2; + } + if(number_of_cycles) + { + get_pixel(); + *(uint16_t *)_current_output_target = _paletteTables.forty1bpp[_last_pixel_byte]; + _current_output_target += 2; + _current_pixel_column++; + } + } else _current_output_target += 2 * number_of_cycles; break; case 5: - if(_current_pixel_column&1) + if(_initial_output_target) { - _last_pixel_byte <<= 2; - *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; - _current_output_target += 1; + if(_current_pixel_column&1) + { + _last_pixel_byte <<= 2; + *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; + _current_output_target += 1; - number_of_cycles--; - _current_pixel_column++; - } - while(number_of_cycles > 1) - { - get_pixel(); - *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; - _current_output_target += 1; + number_of_cycles--; + _current_pixel_column++; + } + while(number_of_cycles > 1) + { + get_pixel(); + *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; + _current_output_target += 1; - _last_pixel_byte <<= 2; - *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; - _current_output_target += 1; + _last_pixel_byte <<= 2; + *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; + _current_output_target += 1; - number_of_cycles -= 2; - _current_pixel_column+=2; - } - if(number_of_cycles) - { - get_pixel(); - *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; - _current_output_target += 1; - _current_pixel_column++; - } + number_of_cycles -= 2; + _current_pixel_column+=2; + } + if(number_of_cycles) + { + get_pixel(); + *_current_output_target = _paletteTables.forty2bpp[_last_pixel_byte]; + _current_output_target += 1; + _current_pixel_column++; + } + } else _current_output_target += number_of_cycles; break; } diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 5166e9717..9a1ef52a5 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -312,15 +312,21 @@ void CRT::output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider) { - _openGL_output_builder->reduce_previous_allocation_to(number_of_cycles / source_divider); - Scan scan { - .type = Scan::Type::Data, - .number_of_cycles = number_of_cycles, - .tex_x = _openGL_output_builder->get_last_write_x_posititon(), - .tex_y = _openGL_output_builder->get_last_write_y_posititon(), - .source_divider = source_divider - }; - output_scan(&scan); + if(_openGL_output_builder->reduce_previous_allocation_to(number_of_cycles / source_divider)) + { + Scan scan { + .type = Scan::Type::Data, + .number_of_cycles = number_of_cycles, + .tex_x = _openGL_output_builder->get_last_write_x_posititon(), + .tex_y = _openGL_output_builder->get_last_write_y_posititon(), + .source_divider = source_divider + }; + output_scan(&scan); + } + else + { + output_blank(number_of_cycles); + } } Outputs::CRT::Rect CRT::get_rect_for_area(int first_line_after_sync, int number_of_lines, int first_cycle_after_sync, int number_of_cycles, float aspect_ratio) diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index dd48512f2..0794956f5 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -176,14 +176,16 @@ class CRT { */ void output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t amplitude); - /*! Ensures that the given number of output samples are allocated for writing. + /*! Attempts to allocate the given number of output samples for writing. The beginning of the most recently allocated area is used as the start of data written by a call to @c output_data; it is acceptable to write and to output less data than the amount requested but that may be less efficient. + Allocation should fail only if emulation is running significantly below real speed. + @param required_length The number of samples to allocate. - @returns A pointer to the allocated area. + @returns A pointer to the allocated area if room is available; @c nullptr otherwise. */ inline uint8_t *allocate_write_area(size_t required_length) { diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index a277860f3..1aa8f3d35 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -13,44 +13,51 @@ using namespace Outputs::CRT; CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) : - bytes_per_pixel(bytes_per_pixel), + _bytes_per_pixel(bytes_per_pixel), _next_write_x_position(0), _next_write_y_position(0), - last_uploaded_line(0) + _last_uploaded_line(0), + _is_full(false) {} -CRTInputBufferBuilder::~CRTInputBufferBuilder() -{ -} - void CRTInputBufferBuilder::allocate_write_area(size_t required_length) { - _last_allocation_amount = required_length; - - if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth) + if (!_is_full) { - _next_write_x_position = 0; - _next_write_y_position++; - } + _last_allocation_amount = required_length; - _write_x_position = _next_write_x_position + 1; - _write_y_position = _next_write_y_position; - _write_target_pointer = ((_write_y_position % InputBufferBuilderHeight) * InputBufferBuilderWidth) + _write_x_position; - _next_write_x_position += required_length + 2; + if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth) + { + _next_write_x_position = 0; + _next_write_y_position++; + + _is_full = (_next_write_y_position == _last_uploaded_line + InputBufferBuilderHeight); + if(_is_full) return; + } + + _write_x_position = _next_write_x_position + 1; + _write_y_position = _next_write_y_position; + _write_target_pointer = ((_write_y_position % InputBufferBuilderHeight) * InputBufferBuilderWidth) + _write_x_position; + _next_write_x_position += required_length + 2; + } } -void CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer) +bool CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer) { + if(_is_full) return false; + // book end the allocation with duplicates of the first and last pixel, to protect // against rounding errors when this run is drawn - memcpy( &buffer[(_write_target_pointer - 1) * bytes_per_pixel], - &buffer[_write_target_pointer * bytes_per_pixel], - bytes_per_pixel); + memcpy( &buffer[(_write_target_pointer - 1) * _bytes_per_pixel], + &buffer[_write_target_pointer * _bytes_per_pixel], + _bytes_per_pixel); - memcpy( &buffer[(_write_target_pointer + actual_length) * bytes_per_pixel], - &buffer[(_write_target_pointer + actual_length - 1) * bytes_per_pixel], - bytes_per_pixel); + memcpy( &buffer[(_write_target_pointer + actual_length) * _bytes_per_pixel], + &buffer[(_write_target_pointer + actual_length - 1) * _bytes_per_pixel], + _bytes_per_pixel); // return any allocated length that wasn't actually used to the available pool _next_write_x_position -= (_last_allocation_amount - actual_length); + + return true; } diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp index a65182887..98bfd3190 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp @@ -20,23 +20,26 @@ namespace CRT { struct CRTInputBufferBuilder { CRTInputBufferBuilder(size_t bytes_per_pixel); - ~CRTInputBufferBuilder(); void allocate_write_area(size_t required_length); - void reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer); + bool reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer); inline uint16_t get_and_finalise_current_line() { uint16_t result = _write_y_position; - _next_write_x_position = 0; - _next_write_y_position++; + if(!_is_full) + { + _next_write_x_position = 0; + _next_write_y_position++; + } _next_write_y_position %= InputBufferBuilderHeight; + _is_full = false; return result; } inline uint8_t *get_write_target(uint8_t *buffer) { - return &buffer[_write_target_pointer * bytes_per_pixel]; + return _is_full ? nullptr : &buffer[_write_target_pointer * _bytes_per_pixel]; } inline uint16_t get_last_write_x_position() @@ -51,7 +54,7 @@ struct CRTInputBufferBuilder { inline size_t get_bytes_per_pixel() { - return bytes_per_pixel; + return _bytes_per_pixel; } private: @@ -66,11 +69,12 @@ struct CRTInputBufferBuilder { size_t _last_allocation_amount; // the buffer size - size_t bytes_per_pixel; + size_t _bytes_per_pixel; // Storage for the amount of buffer uploaded so far; initialised correctly by the buffer // builder but otherwise entrusted to the CRT to update. - unsigned int last_uploaded_line; + unsigned int _last_uploaded_line; + bool _is_full; }; } diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index dd2343e46..e1f3daf08 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -152,9 +152,9 @@ class OpenGLOutputBuilder { return output; } - inline void reduce_previous_allocation_to(size_t actual_length) + inline bool reduce_previous_allocation_to(size_t actual_length) { - _buffer_builder->reduce_previous_allocation_to(actual_length, _input_texture_data); + return _buffer_builder->reduce_previous_allocation_to(actual_length, _input_texture_data); } inline uint16_t get_last_write_x_posititon() From 2e7ac0e6dbf761cf088e727c8c3e7c0f8c3fb66b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 4 May 2016 21:08:38 -0400 Subject: [PATCH 06/27] With a lot of logging also currently ongoing, fixed circular ranges for clearing and when covering an entire buffer. --- Outputs/CRT/Internals/CRTConstants.hpp | 2 +- .../CRT/Internals/CRTInputBufferBuilder.cpp | 5 +- .../CRT/Internals/CRTInputBufferBuilder.hpp | 6 ++ Outputs/CRT/Internals/CRTOpenGL.cpp | 76 ++++++++++++++----- 4 files changed, 66 insertions(+), 23 deletions(-) diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp index 743fa4b57..f17ac438b 100644 --- a/Outputs/CRT/Internals/CRTConstants.hpp +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -33,7 +33,7 @@ const GLsizei SourceVertexSize = 16; // These constants hold the size of the rolling buffer to which the CPU writes const GLsizei InputBufferBuilderWidth = 2048; -const GLsizei InputBufferBuilderHeight = 1024; +const GLsizei InputBufferBuilderHeight = 8; // This is the size of the intermediate buffers used during composite to RGB conversion const GLsizei IntermediateBufferWidth = 2048; diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index 1aa8f3d35..bbed0826d 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -22,7 +22,7 @@ CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) : void CRTInputBufferBuilder::allocate_write_area(size_t required_length) { - if (!_is_full) + if(!_is_full) { _last_allocation_amount = required_length; @@ -32,7 +32,8 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) _next_write_y_position++; _is_full = (_next_write_y_position == _last_uploaded_line + InputBufferBuilderHeight); - if(_is_full) return; + if(_is_full) + return; } _write_x_position = _next_write_x_position + 1; diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp index 98bfd3190..24ec70070 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp @@ -14,6 +14,7 @@ #include #include "CRTConstants.hpp" #include "OpenGL.hpp" +#include namespace Outputs { namespace CRT { @@ -33,12 +34,17 @@ struct CRTInputBufferBuilder { _next_write_y_position++; } _next_write_y_position %= InputBufferBuilderHeight; + _last_uploaded_line = _next_write_y_position; _is_full = false; return result; } inline uint8_t *get_write_target(uint8_t *buffer) { + if(!_is_full) + { + memset(&buffer[_write_target_pointer * _bytes_per_pixel], 0, _last_allocation_amount * _bytes_per_pixel); + } return _is_full ? nullptr : &buffer[_write_target_pointer * _bytes_per_pixel]; } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index e5ee9206c..493b94386 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -39,15 +39,12 @@ static const GLenum formatForDepth(size_t depth) static int getCircularRanges(GLsizei start, GLsizei end, GLsizei buffer_length, GLsizei granularity, GLsizei *ranges) { - GLsizei startOffset = start%granularity; - if(startOffset) - { - start -= startOffset; - } + start -= start%granularity; + end -= end%granularity; GLsizei length = end - start; if(!length) return 0; - if(length > buffer_length) + if(length >= buffer_length) { ranges[0] = 0; ranges[1] = buffer_length; @@ -56,7 +53,7 @@ static int getCircularRanges(GLsizei start, GLsizei end, GLsizei buffer_length, else { ranges[0] = start % buffer_length; - if(ranges[0]+length < buffer_length) + if(ranges[0]+length <= buffer_length) { ranges[1] = length; return 1; @@ -102,7 +99,7 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : _buffer_builder = std::unique_ptr(new CRTInputBufferBuilder(buffer_depth)); glBlendFunc(GL_SRC_ALPHA, GL_CONSTANT_COLOR); - glBlendColor(0.6f, 0.6f, 0.6f, 1.0f); + glBlendColor(0.0f, 0.0f, 0.0f, 1.0f); // Create intermediate textures and bind to slots 0, 1 and 2 compositeTexture = std::unique_ptr(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, composite_texture_unit)); @@ -208,7 +205,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out // determine how many lines are newly reclaimed; they'll need to be cleared GLsizei clearing_zones[4], source_drawing_zones[4]; GLsizei output_drawing_zones[4], texture_upload_zones[4]; - int number_of_clearing_zones = getCircularRanges(_cleared_composite_output_y+1, _composite_src_output_y+1, IntermediateBufferHeight, 1, clearing_zones); + int number_of_clearing_zones = getCircularRanges(_cleared_composite_output_y, _composite_src_output_y, IntermediateBufferHeight, 1, clearing_zones); int number_of_source_drawing_zones = getCircularRanges(_drawn_source_buffer_data_pointer, _source_buffer_data_pointer, SourceVertexBufferDataSize, 2*SourceVertexSize, source_drawing_zones); int number_of_output_drawing_zones = getCircularRanges(_drawn_output_buffer_data_pointer, _output_buffer_data_pointer, OutputVertexBufferDataSize, 6*OutputVertexSize, output_drawing_zones); @@ -224,6 +221,44 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out _drawn_output_buffer_data_pointer = _output_buffer_data_pointer; _uploaded_texture_y = completed_texture_y % InputBufferBuilderHeight; + for(int c = 0; c < number_of_source_drawing_zones; c++) + { + printf("src: + %0.0f\n", (float)source_drawing_zones[c*2 + 1] / (2.0f * SourceVertexSize)); +// for(int r = 0; r < source_drawing_zones[c*2 + 1]; r += 2*SourceVertexSize) +// { + int offset = source_drawing_zones[c*2 + 0]; + uint16_t *base = (uint16_t *)&_source_buffer_data[offset]; + printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[10], base[11]); + + offset += source_drawing_zones[c*2 + 1] - 2*SourceVertexSize; + base = (uint16_t *)&_source_buffer_data[offset]; + printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[10], base[11]); +// } + } + for(int c = 0; c < number_of_texture_upload_zones; c++) + { + printf("tx: + %d\n", texture_upload_zones[c*2 + 1]); + } + for(int c = 0; c < number_of_clearing_zones; c++) + { + printf("cl: %d + %d\n", clearing_zones[c*2], clearing_zones[c*2 + 1]); + } + for(int c = 0; c < number_of_output_drawing_zones; c++) + { + printf("o: + %0.0f\n", (float)output_drawing_zones[c*2 + 1] / (6.0f * OutputVertexSize)); +// for(int r = 0; r < output_drawing_zones[c*2 + 1]; r += 6*OutputVertexSize) +// { + int offset = output_drawing_zones[c*2 + 0]; + uint16_t *base = (uint16_t *)&_output_buffer_data[offset]; + printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[14], base[15]); + + offset += output_drawing_zones[c*2 + 1] - 6*OutputVertexSize; + base = (uint16_t *)&_output_buffer_data[offset]; + printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[14], base[15]); +// } + } + printf("\n"); + // release the mapping, giving up on trying to draw if data has been lost glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); for(int c = 0; c < number_of_output_drawing_zones; c++) @@ -323,17 +358,18 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out active_pipeline->shader->bind(); // clear as desired - if(number_of_clearing_zones) - { - glEnable(GL_SCISSOR_TEST); - glClearColor(active_pipeline->clear_colour[0], active_pipeline->clear_colour[1], active_pipeline->clear_colour[2], 1.0); - for(int c = 0; c < number_of_clearing_zones; c++) - { - glScissor(0, clearing_zones[c*2], IntermediateBufferWidth, clearing_zones[c*2 + 1]); - glClear(GL_COLOR_BUFFER_BIT); - } - glDisable(GL_SCISSOR_TEST); - } +// if(number_of_clearing_zones) +// { +// glEnable(GL_SCISSOR_TEST); +// glClearColor(active_pipeline->clear_colour[0], active_pipeline->clear_colour[1], active_pipeline->clear_colour[2], 1.0); +// for(int c = 0; c < number_of_clearing_zones; c++) +// { +// glScissor(0, clearing_zones[c*2], IntermediateBufferWidth, clearing_zones[c*2 + 1]); +// glClear(GL_COLOR_BUFFER_BIT); +// } +// glDisable(GL_SCISSOR_TEST); +// } + glClear(GL_COLOR_BUFFER_BIT); // draw as desired for(int c = 0; c < number_of_source_drawing_zones; c++) From f621cc8523999435cd467b72eae2456f91fb2eb9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 4 May 2016 21:27:10 -0400 Subject: [PATCH 07/27] Withdrew the pixel unpack buffer in order to avoid the potential for a paused machine to be undrawable. The cost is at least an extra memcpy per frame; I'm letting the driver worry about the whole process for now. --- .../CRT/Internals/CRTInputBufferBuilder.cpp | 53 ++++++++++-- .../CRT/Internals/CRTInputBufferBuilder.hpp | 44 +++------- Outputs/CRT/Internals/CRTOpenGL.cpp | 80 +++++-------------- Outputs/CRT/Internals/CRTOpenGL.hpp | 14 +--- .../Internals/Shaders/IntermediateShader.cpp | 2 +- 5 files changed, 79 insertions(+), 114 deletions(-) diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index bbed0826d..806c5b515 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -17,9 +17,15 @@ CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) : _next_write_x_position(0), _next_write_y_position(0), _last_uploaded_line(0), - _is_full(false) + _is_full(false), + _image(new uint8_t[bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight]) {} +CRTInputBufferBuilder::~CRTInputBufferBuilder() +{ + delete[] _image; +} + void CRTInputBufferBuilder::allocate_write_area(size_t required_length) { if(!_is_full) @@ -38,23 +44,23 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) _write_x_position = _next_write_x_position + 1; _write_y_position = _next_write_y_position; - _write_target_pointer = ((_write_y_position % InputBufferBuilderHeight) * InputBufferBuilderWidth) + _write_x_position; + _write_target_pointer = (_write_y_position * InputBufferBuilderWidth) + _write_x_position; _next_write_x_position += required_length + 2; } } -bool CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer) +bool CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length) { if(_is_full) return false; // book end the allocation with duplicates of the first and last pixel, to protect // against rounding errors when this run is drawn - memcpy( &buffer[(_write_target_pointer - 1) * _bytes_per_pixel], - &buffer[_write_target_pointer * _bytes_per_pixel], + memcpy( &_image[(_write_target_pointer - 1) * _bytes_per_pixel], + &_image[_write_target_pointer * _bytes_per_pixel], _bytes_per_pixel); - memcpy( &buffer[(_write_target_pointer + actual_length) * _bytes_per_pixel], - &buffer[(_write_target_pointer + actual_length - 1) * _bytes_per_pixel], + memcpy( &_image[(_write_target_pointer + actual_length) * _bytes_per_pixel], + &_image[(_write_target_pointer + actual_length - 1) * _bytes_per_pixel], _bytes_per_pixel); // return any allocated length that wasn't actually used to the available pool @@ -62,3 +68,36 @@ bool CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length, return true; } + +uint8_t *CRTInputBufferBuilder::get_image_pointer() +{ + return _image; +} + +uint16_t CRTInputBufferBuilder::get_and_finalise_current_line() +{ + uint16_t result = _write_y_position; + _next_write_x_position = _next_write_y_position = 0; + _is_full = false; + return result; +} + +uint8_t *CRTInputBufferBuilder::get_write_target() +{ + return _is_full ? nullptr : &_image[_write_target_pointer * _bytes_per_pixel]; +} + +uint16_t CRTInputBufferBuilder::get_last_write_x_position() +{ + return _write_x_position; +} + +uint16_t CRTInputBufferBuilder::get_last_write_y_position() +{ + return _write_y_position % InputBufferBuilderHeight; +} + +size_t CRTInputBufferBuilder::get_bytes_per_pixel() +{ + return _bytes_per_pixel; +} diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp index 24ec70070..32346dff0 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp @@ -21,47 +21,21 @@ namespace CRT { struct CRTInputBufferBuilder { CRTInputBufferBuilder(size_t bytes_per_pixel); + ~CRTInputBufferBuilder(); void allocate_write_area(size_t required_length); - bool reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer); + bool reduce_previous_allocation_to(size_t actual_length); - inline uint16_t get_and_finalise_current_line() - { - uint16_t result = _write_y_position; - if(!_is_full) - { - _next_write_x_position = 0; - _next_write_y_position++; - } - _next_write_y_position %= InputBufferBuilderHeight; - _last_uploaded_line = _next_write_y_position; - _is_full = false; - return result; - } + uint16_t get_and_finalise_current_line(); + uint8_t *get_image_pointer(); - inline uint8_t *get_write_target(uint8_t *buffer) - { - if(!_is_full) - { - memset(&buffer[_write_target_pointer * _bytes_per_pixel], 0, _last_allocation_amount * _bytes_per_pixel); - } - return _is_full ? nullptr : &buffer[_write_target_pointer * _bytes_per_pixel]; - } + uint8_t *get_write_target(); - inline uint16_t get_last_write_x_position() - { - return _write_x_position; - } + uint16_t get_last_write_x_position(); - inline uint16_t get_last_write_y_position() - { - return _write_y_position % InputBufferBuilderHeight; - } + uint16_t get_last_write_y_position(); - inline size_t get_bytes_per_pixel() - { - return _bytes_per_pixel; - } + size_t get_bytes_per_pixel(); private: // where pixel data will be put to the next time a write is requested @@ -81,6 +55,8 @@ struct CRTInputBufferBuilder { // builder but otherwise entrusted to the CRT to update. unsigned int _last_uploaded_line; bool _is_full; + + uint8_t *_image; }; } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 493b94386..d81893c37 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -88,7 +88,6 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : _rgb_shader(nullptr), _output_buffer_data(nullptr), _source_buffer_data(nullptr), - _input_texture_data(nullptr), _output_buffer_data_pointer(0), _drawn_output_buffer_data_pointer(0), _source_buffer_data_pointer(0), @@ -117,15 +116,6 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, internalFormatForDepth(_buffer_builder->get_bytes_per_pixel()), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE, nullptr); - // create a pixel unpack buffer - glGenBuffers(1, &_input_texture_array); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array); - _input_texture_array_size = (GLsizeiptr)(InputBufferBuilderWidth * InputBufferBuilderHeight * _buffer_builder->get_bytes_per_pixel()); - glBufferData(GL_PIXEL_UNPACK_BUFFER, _input_texture_array_size, NULL, GL_STREAM_DRAW); - - // map the buffer for clients - _input_texture_data = (uint8_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, _input_texture_array_size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); - // create the output vertex array glGenVertexArrays(1, &output_vertex_array); @@ -165,7 +155,6 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder() glUnmapBuffer(GL_ARRAY_BUFFER); glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); glDeleteTextures(1, &textureName); - glDeleteBuffers(1, &_input_texture_array); glDeleteBuffers(1, &output_array_buffer); glDeleteBuffers(1, &source_array_buffer); glDeleteBuffers(1, &lateral_array_buffer); @@ -204,13 +193,12 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out // determine how many lines are newly reclaimed; they'll need to be cleared GLsizei clearing_zones[4], source_drawing_zones[4]; - GLsizei output_drawing_zones[4], texture_upload_zones[4]; + GLsizei output_drawing_zones[4]; int number_of_clearing_zones = getCircularRanges(_cleared_composite_output_y, _composite_src_output_y, IntermediateBufferHeight, 1, clearing_zones); int number_of_source_drawing_zones = getCircularRanges(_drawn_source_buffer_data_pointer, _source_buffer_data_pointer, SourceVertexBufferDataSize, 2*SourceVertexSize, source_drawing_zones); int number_of_output_drawing_zones = getCircularRanges(_drawn_output_buffer_data_pointer, _output_buffer_data_pointer, OutputVertexBufferDataSize, 6*OutputVertexSize, output_drawing_zones); uint16_t completed_texture_y = _buffer_builder->get_and_finalise_current_line(); - int number_of_texture_upload_zones = getCircularRanges(_uploaded_texture_y, completed_texture_y, InputBufferBuilderHeight, 1, texture_upload_zones); _composite_src_output_y %= IntermediateBufferHeight; _source_buffer_data_pointer %= SourceVertexBufferDataSize; @@ -219,26 +207,19 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out _cleared_composite_output_y = _composite_src_output_y; _drawn_source_buffer_data_pointer = _source_buffer_data_pointer; _drawn_output_buffer_data_pointer = _output_buffer_data_pointer; - _uploaded_texture_y = completed_texture_y % InputBufferBuilderHeight; for(int c = 0; c < number_of_source_drawing_zones; c++) { printf("src: + %0.0f\n", (float)source_drawing_zones[c*2 + 1] / (2.0f * SourceVertexSize)); -// for(int r = 0; r < source_drawing_zones[c*2 + 1]; r += 2*SourceVertexSize) -// { - int offset = source_drawing_zones[c*2 + 0]; - uint16_t *base = (uint16_t *)&_source_buffer_data[offset]; - printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[10], base[11]); + int offset = source_drawing_zones[c*2 + 0]; + uint16_t *base = (uint16_t *)&_source_buffer_data[offset]; + printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[10], base[11]); - offset += source_drawing_zones[c*2 + 1] - 2*SourceVertexSize; - base = (uint16_t *)&_source_buffer_data[offset]; - printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[10], base[11]); -// } - } - for(int c = 0; c < number_of_texture_upload_zones; c++) - { - printf("tx: + %d\n", texture_upload_zones[c*2 + 1]); + offset += source_drawing_zones[c*2 + 1] - 2*SourceVertexSize; + base = (uint16_t *)&_source_buffer_data[offset]; + printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[10], base[11]); } + printf("tx: + %d\n", completed_texture_y); for(int c = 0; c < number_of_clearing_zones; c++) { printf("cl: %d + %d\n", clearing_zones[c*2], clearing_zones[c*2 + 1]); @@ -246,16 +227,13 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out for(int c = 0; c < number_of_output_drawing_zones; c++) { printf("o: + %0.0f\n", (float)output_drawing_zones[c*2 + 1] / (6.0f * OutputVertexSize)); -// for(int r = 0; r < output_drawing_zones[c*2 + 1]; r += 6*OutputVertexSize) -// { - int offset = output_drawing_zones[c*2 + 0]; - uint16_t *base = (uint16_t *)&_output_buffer_data[offset]; - printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[14], base[15]); + int offset = output_drawing_zones[c*2 + 0]; + uint16_t *base = (uint16_t *)&_output_buffer_data[offset]; + printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[14], base[15]); - offset += output_drawing_zones[c*2 + 1] - 6*OutputVertexSize; - base = (uint16_t *)&_output_buffer_data[offset]; - printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[14], base[15]); -// } + offset += output_drawing_zones[c*2 + 1] - 6*OutputVertexSize; + base = (uint16_t *)&_output_buffer_data[offset]; + printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[14], base[15]); } printf("\n"); @@ -275,19 +253,9 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out } glUnmapBuffer(GL_ARRAY_BUFFER); - if(number_of_texture_upload_zones) - { - for(int c = 0; c < number_of_texture_upload_zones; c++) - { - glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, (GLsizeiptr)texture_upload_zones[c*2] * InputBufferBuilderWidth * (GLsizeiptr)_buffer_builder->get_bytes_per_pixel(), (GLsizeiptr)texture_upload_zones[c*2 + 1] * InputBufferBuilderWidth * (GLsizeiptr)_buffer_builder->get_bytes_per_pixel()); - } - } - glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - // make sure there's a target to draw to if(!framebuffer || framebuffer->get_height() != output_height || framebuffer->get_width() != output_width) { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); std::unique_ptr new_framebuffer = std::unique_ptr(new OpenGL::TextureTarget((GLsizei)output_width, (GLsizei)output_height, pixel_accumulation_texture_unit)); if(framebuffer) { @@ -301,23 +269,17 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out new_framebuffer->bind_texture(); } framebuffer = std::move(new_framebuffer); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array); } // upload new source pixels - if(number_of_texture_upload_zones) + if(completed_texture_y) { glActiveTexture(source_data_texture_unit); - glTexImage2D(GL_TEXTURE_2D, 0, internalFormatForDepth(_buffer_builder->get_bytes_per_pixel()), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE, (void *)0); -// for(int c = 0; c < number_of_texture_upload_zones; c++) -// { -// glTexSubImage2D( GL_TEXTURE_2D, 0, -// 0, texture_upload_zones[c*2], -// InputBufferBuilderWidth, texture_upload_zones[c*2 + 1], -// formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE, -// (void *)((size_t)texture_upload_zones[c*2] * InputBufferBuilderWidth * _buffer_builder->get_bytes_per_pixel())); -// } -// glFinish(); + glTexSubImage2D( GL_TEXTURE_2D, 0, + 0, 0, + InputBufferBuilderWidth, completed_texture_y, + formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE, + _buffer_builder->get_image_pointer()); } struct RenderStage { @@ -421,8 +383,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); _source_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, SourceVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); - _input_texture_data = (uint8_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, _input_texture_array_size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); - _output_mutex->unlock(); } diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index e1f3daf08..8c5a5a11a 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -145,16 +145,13 @@ class OpenGLOutputBuilder { inline uint8_t *allocate_write_area(size_t required_length) { - _output_mutex->lock(); _buffer_builder->allocate_write_area(required_length); - uint8_t *output = _input_texture_data ? _buffer_builder->get_write_target(_input_texture_data) : nullptr; - _output_mutex->unlock(); - return output; + return _buffer_builder->get_write_target(); } inline bool reduce_previous_allocation_to(size_t actual_length) { - return _buffer_builder->reduce_previous_allocation_to(actual_length, _input_texture_data); + return _buffer_builder->reduce_previous_allocation_to(actual_length); } inline uint16_t get_last_write_x_posititon() @@ -174,11 +171,6 @@ class OpenGLOutputBuilder { void set_output_device(OutputDevice output_device); void set_timing(unsigned int input_frequency, unsigned int cycles_per_line, unsigned int height_of_display, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider); - uint8_t *_input_texture_data; - GLuint _input_texture_array; - GLsync _input_texture_sync; - GLsizeiptr _input_texture_array_size; - uint8_t *_source_buffer_data; GLsizei _source_buffer_data_pointer; GLsizei _drawn_source_buffer_data_pointer; @@ -186,8 +178,6 @@ class OpenGLOutputBuilder { uint8_t *_output_buffer_data; GLsizei _output_buffer_data_pointer; GLsizei _drawn_output_buffer_data_pointer; - - uint16_t _uploaded_texture_y; }; } diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp index 530d689ac..c49f93f8a 100644 --- a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp +++ b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp @@ -159,7 +159,7 @@ std::unique_ptr IntermediateShader::make_rgb_source_shader(c "void main(void)" "{" - "fragColour = rgb_sample(texID, inputPositionsVarying[5], iInputPositionVarying);" + "fragColour = vec3(0.2) + rgb_sample(texID, inputPositionsVarying[5], iInputPositionVarying);" "}" , rgb_shader); From e9a9cfb09fa52c19cb3b39f5a6025f785be4549d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 5 May 2016 07:22:49 -0400 Subject: [PATCH 08/27] Fixed off-by-one error on texture upload. --- Outputs/CRT/Internals/CRTConstants.hpp | 2 +- .../CRT/Internals/CRTInputBufferBuilder.cpp | 21 ++++++++++--------- .../CRT/Internals/CRTInputBufferBuilder.hpp | 6 +----- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp index f17ac438b..5d17b5ad3 100644 --- a/Outputs/CRT/Internals/CRTConstants.hpp +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -33,7 +33,7 @@ const GLsizei SourceVertexSize = 16; // These constants hold the size of the rolling buffer to which the CPU writes const GLsizei InputBufferBuilderWidth = 2048; -const GLsizei InputBufferBuilderHeight = 8; +const GLsizei InputBufferBuilderHeight = 8192; // This is the size of the intermediate buffers used during composite to RGB conversion const GLsizei IntermediateBufferWidth = 2048; diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index 806c5b515..c4d6784c3 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -16,8 +16,6 @@ CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) : _bytes_per_pixel(bytes_per_pixel), _next_write_x_position(0), _next_write_y_position(0), - _last_uploaded_line(0), - _is_full(false), _image(new uint8_t[bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight]) {} @@ -28,7 +26,7 @@ CRTInputBufferBuilder::~CRTInputBufferBuilder() void CRTInputBufferBuilder::allocate_write_area(size_t required_length) { - if(!_is_full) + if(_next_write_y_position != InputBufferBuilderHeight) { _last_allocation_amount = required_length; @@ -37,8 +35,7 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) _next_write_x_position = 0; _next_write_y_position++; - _is_full = (_next_write_y_position == _last_uploaded_line + InputBufferBuilderHeight); - if(_is_full) + if(_next_write_y_position == InputBufferBuilderHeight) return; } @@ -46,12 +43,17 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) _write_y_position = _next_write_y_position; _write_target_pointer = (_write_y_position * InputBufferBuilderWidth) + _write_x_position; _next_write_x_position += required_length + 2; + + if(_write_y_position == 0 && _write_x_position == 0) + { + memset(_image, 0, _bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight); + } } } bool CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length) { - if(_is_full) return false; + if(_next_write_y_position == InputBufferBuilderHeight) return false; // book end the allocation with duplicates of the first and last pixel, to protect // against rounding errors when this run is drawn @@ -76,15 +78,14 @@ uint8_t *CRTInputBufferBuilder::get_image_pointer() uint16_t CRTInputBufferBuilder::get_and_finalise_current_line() { - uint16_t result = _write_y_position; + uint16_t result = _write_y_position + (_next_write_x_position ? 1 : 0); _next_write_x_position = _next_write_y_position = 0; - _is_full = false; return result; } uint8_t *CRTInputBufferBuilder::get_write_target() { - return _is_full ? nullptr : &_image[_write_target_pointer * _bytes_per_pixel]; + return (_next_write_y_position == InputBufferBuilderHeight) ? nullptr : &_image[_write_target_pointer * _bytes_per_pixel]; } uint16_t CRTInputBufferBuilder::get_last_write_x_position() @@ -94,7 +95,7 @@ uint16_t CRTInputBufferBuilder::get_last_write_x_position() uint16_t CRTInputBufferBuilder::get_last_write_y_position() { - return _write_y_position % InputBufferBuilderHeight; + return _write_y_position; } size_t CRTInputBufferBuilder::get_bytes_per_pixel() diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp index 32346dff0..33279f7c0 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp @@ -51,11 +51,7 @@ struct CRTInputBufferBuilder { // the buffer size size_t _bytes_per_pixel; - // Storage for the amount of buffer uploaded so far; initialised correctly by the buffer - // builder but otherwise entrusted to the CRT to update. - unsigned int _last_uploaded_line; - bool _is_full; - + // the buffer uint8_t *_image; }; From 284b3100743b057e024a81401da99f537a4a8a6d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 5 May 2016 19:52:05 -0400 Subject: [PATCH 09/27] Attempted to implement end-of-buffer tests for all stages. --- OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m | 10 ++++++++-- Outputs/CRT/CRT.cpp | 15 +++++++++------ Outputs/CRT/Internals/CRTConstants.hpp | 4 ++-- Outputs/CRT/Internals/CRTInputBufferBuilder.cpp | 2 +- Outputs/CRT/Internals/CRTOpenGL.cpp | 1 + Outputs/CRT/Internals/CRTOpenGL.hpp | 7 +++++++ 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m index 8e41cc50e..240b63f22 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m @@ -43,8 +43,14 @@ 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)]; + static int d = 0; + d++; + if(d == 10) + { + d = 0; + CSOpenGLView *const view = (__bridge CSOpenGLView *)displayLinkContext; + [view drawAtTime:now frequency:CVDisplayLinkGetActualOutputVideoRefreshPeriod(displayLink)]; + } return kCVReturnSuccess; } diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 9a1ef52a5..a14f6a29d 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -130,7 +130,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi bool is_output_segment = ((is_output_run && next_run_length) && !_horizontal_flywheel->is_in_retrace() && !_vertical_flywheel->is_in_retrace()); uint8_t *next_run = nullptr; - if(is_output_segment) + if(is_output_segment && !_openGL_output_builder->composite_output_buffer_is_full()) { next_run = _openGL_output_builder->get_next_source_run(); } @@ -190,12 +190,15 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi { uint8_t *next_run = _openGL_output_builder->get_next_output_run(); - output_position_x(0) = output_position_x(1) = output_position_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); - output_position_y(0) = output_position_y(1) = output_position_y(2) = (uint16_t)(_vertical_flywheel->get_current_output_position() / _vertical_flywheel_output_divider); - output_tex_x(0) = output_tex_x(1) = output_tex_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); - output_tex_y(0) = output_tex_y(1) = output_tex_y(2) = _openGL_output_builder->get_composite_output_y(); + if(next_run) + { + output_position_x(0) = output_position_x(1) = output_position_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); + output_position_y(0) = output_position_y(1) = output_position_y(2) = (uint16_t)(_vertical_flywheel->get_current_output_position() / _vertical_flywheel_output_divider); + output_tex_x(0) = output_tex_x(1) = output_tex_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); + output_tex_y(0) = output_tex_y(1) = output_tex_y(2) = _openGL_output_builder->get_composite_output_y(); - _openGL_output_builder->complete_output_run(3); + _openGL_output_builder->complete_output_run(3); + } _is_writing_composite_run ^= true; } diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp index 5d17b5ad3..65fc45f12 100644 --- a/Outputs/CRT/Internals/CRTConstants.hpp +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -33,11 +33,11 @@ const GLsizei SourceVertexSize = 16; // These constants hold the size of the rolling buffer to which the CPU writes const GLsizei InputBufferBuilderWidth = 2048; -const GLsizei InputBufferBuilderHeight = 8192; +const GLsizei InputBufferBuilderHeight = 1024; // This is the size of the intermediate buffers used during composite to RGB conversion const GLsizei IntermediateBufferWidth = 2048; -const GLsizei IntermediateBufferHeight = 2048; +const GLsizei IntermediateBufferHeight = 1024; // Some internal buffer sizes const GLsizeiptr OutputVertexBufferDataSize = 5990400; // a multiple of 6 * OutputVertexSize diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index c4d6784c3..15ad5e617 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -30,7 +30,7 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) { _last_allocation_amount = required_length; - if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth) +// if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth) { _next_write_x_position = 0; _next_write_y_position++; diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index d81893c37..4b8afc1dd 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -375,6 +375,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height); glClear(GL_COLOR_BUFFER_BIT); framebuffer->draw((float)output_width / (float)output_height); +// compositeTexture->draw((float)output_width / (float)output_height); // drawing commands having been issued, reclaim the array buffer pointer glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index 8c5a5a11a..8812abcc0 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -106,6 +106,7 @@ class OpenGLOutputBuilder { inline uint8_t *get_next_source_run() { + if(_source_buffer_data_pointer == _drawn_source_buffer_data_pointer + SourceVertexBufferDataSize) return nullptr; _output_mutex->lock(); return &_source_buffer_data[_source_buffer_data_pointer % SourceVertexBufferDataSize]; } @@ -118,6 +119,7 @@ class OpenGLOutputBuilder { inline uint8_t *get_next_output_run() { + if(_output_buffer_data_pointer == _drawn_output_buffer_data_pointer + OutputVertexBufferDataSize) return nullptr; _output_mutex->lock(); return &_output_buffer_data[_output_buffer_data_pointer % OutputVertexBufferDataSize]; } @@ -133,6 +135,11 @@ class OpenGLOutputBuilder { return _output_device; } + inline bool composite_output_buffer_is_full() + { + return _composite_src_output_y == _cleared_composite_output_y + IntermediateBufferHeight; + } + inline uint16_t get_composite_output_y() { return _composite_src_output_y % IntermediateBufferHeight; From 8284b272ad8ce2feaa5c11db3372c16878c02e56 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 5 May 2016 20:00:28 -0400 Subject: [PATCH 10/27] Picked appropriate new-world values for various buffers. --- Outputs/CRT/Internals/CRTConstants.hpp | 6 +++--- Outputs/CRT/Internals/CRTOpenGL.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp index 65fc45f12..50096f4ec 100644 --- a/Outputs/CRT/Internals/CRTConstants.hpp +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -37,11 +37,11 @@ const GLsizei InputBufferBuilderHeight = 1024; // This is the size of the intermediate buffers used during composite to RGB conversion const GLsizei IntermediateBufferWidth = 2048; -const GLsizei IntermediateBufferHeight = 1024; +const GLsizei IntermediateBufferHeight = 2048; // Some internal buffer sizes -const GLsizeiptr OutputVertexBufferDataSize = 5990400; // a multiple of 6 * OutputVertexSize -const GLsizeiptr SourceVertexBufferDataSize = 8736000; // a multiple of 2 * SourceVertexSize +const GLsizeiptr OutputVertexBufferDataSize = 44928; // a multiple of 6 * OutputVertexSize +const GLsizeiptr SourceVertexBufferDataSize = 29952; // a multiple of 2 * SourceVertexSize } } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 4b8afc1dd..3c3154d8c 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -374,8 +374,8 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height); glClear(GL_COLOR_BUFFER_BIT); - framebuffer->draw((float)output_width / (float)output_height); -// compositeTexture->draw((float)output_width / (float)output_height); +// framebuffer->draw((float)output_width / (float)output_height); + compositeTexture->draw((float)output_width / (float)output_height); // drawing commands having been issued, reclaim the array buffer pointer glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); From a74f081aa97ad7a2e68338fd0d55d8dd5d555386 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 5 May 2016 21:21:27 -0400 Subject: [PATCH 11/27] Put lateral lookup table directly into the shader. --- Outputs/CRT/Internals/CRTOpenGL.cpp | 21 ++----------------- Outputs/CRT/Internals/CRTOpenGL.hpp | 2 +- .../CRT/Internals/Shaders/OutputShader.cpp | 4 +++- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 3c3154d8c..06362794d 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -124,17 +124,6 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); glBufferData(GL_ARRAY_BUFFER, OutputVertexBufferDataSize, NULL, GL_STREAM_DRAW); - // create and populate a buffer for the lateral attributes - glGenBuffers(1, &lateral_array_buffer); - glBindBuffer(GL_ARRAY_BUFFER, lateral_array_buffer); - size_t number_of_vertices = OutputVertexBufferDataSize/OutputVertexSize; - uint8_t lateral_pattern[] = {0, 0, 1, 0, 1, 1}; - uint8_t *laterals = new uint8_t[number_of_vertices]; - for(size_t c = 0; c < number_of_vertices; c++) - laterals[c] = lateral_pattern[c%6]; - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)number_of_vertices, laterals, GL_STATIC_DRAW); - delete[] laterals; - // map that buffer too, for any CRT activity that may occur before the first draw _output_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, OutputVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); @@ -157,7 +146,6 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder() glDeleteTextures(1, &textureName); glDeleteBuffers(1, &output_array_buffer); glDeleteBuffers(1, &source_array_buffer); - glDeleteBuffers(1, &lateral_array_buffer); glDeleteVertexArrays(1, &output_vertex_array); free(_composite_shader); @@ -374,8 +362,8 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height); glClear(GL_COLOR_BUFFER_BIT); -// framebuffer->draw((float)output_width / (float)output_height); - compositeTexture->draw((float)output_width / (float)output_height); + framebuffer->draw((float)output_width / (float)output_height); +// compositeTexture->draw((float)output_width / (float)output_height); // drawing commands having been issued, reclaim the array buffer pointer glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); @@ -483,11 +471,6 @@ void OpenGLOutputBuilder::prepare_output_vertex_array() glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); glVertexAttribPointer((GLuint)positionAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfPosition); glVertexAttribPointer((GLuint)textureCoordinatesAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)OutputVertexOffsetOfTexCoord); - - GLint lateralAttribute = output_shader_program->get_attrib_location("lateral"); - glEnableVertexAttribArray((GLuint)lateralAttribute); - glBindBuffer(GL_ARRAY_BUFFER, lateral_array_buffer); - glVertexAttribPointer((GLuint)lateralAttribute, 1, GL_UNSIGNED_BYTE, GL_FALSE, 0, (void *)0); } } diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index 8812abcc0..c41550f8c 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -73,7 +73,7 @@ class OpenGLOutputBuilder { std::unique_ptr framebuffer; // the current pixel output - GLuint output_array_buffer, lateral_array_buffer, output_vertex_array; + GLuint output_array_buffer, output_vertex_array; GLuint source_array_buffer, source_vertex_array; unsigned int _last_output_width, _last_output_height; diff --git a/Outputs/CRT/Internals/Shaders/OutputShader.cpp b/Outputs/CRT/Internals/Shaders/OutputShader.cpp index 7deb4c508..00be80a6e 100644 --- a/Outputs/CRT/Internals/Shaders/OutputShader.cpp +++ b/Outputs/CRT/Internals/Shaders/OutputShader.cpp @@ -33,7 +33,7 @@ std::unique_ptr OutputShader::make_shader(const char *fragment_met "in vec2 position;" "in vec2 srcCoordinates;" - "in float lateral;" +// "in float lateral;" "uniform vec2 boundsOrigin;" "uniform vec2 boundsSize;" @@ -47,6 +47,8 @@ std::unique_ptr OutputShader::make_shader(const char *fragment_met "void main(void)" "{" + "float laterals[] = float[](0, 0, 1, 0, 1, 1);" + "float lateral = laterals[gl_VertexID %% 6];" "lateralVarying = lateral - 0.5;" "ivec2 textureSize = textureSize(texID, 0);" From c73751b15a8fc5c32523632d778a6ab211655373 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 5 May 2016 21:27:13 -0400 Subject: [PATCH 12/27] Reverted deliberately broken segments to produce something mergeable. --- .../Mac/Clock Signal/Views/CSOpenGLView.m | 10 ++---- Outputs/CRT/Internals/CRTOpenGL.cpp | 31 +------------------ .../Internals/Shaders/IntermediateShader.cpp | 2 +- 3 files changed, 4 insertions(+), 39 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m index 240b63f22..8e41cc50e 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m @@ -43,14 +43,8 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp *now, const CVTimeStamp *outputTime, CVOptionFlags flagsIn, CVOptionFlags *flagsOut, void *displayLinkContext) { - static int d = 0; - d++; - if(d == 10) - { - d = 0; - CSOpenGLView *const view = (__bridge CSOpenGLView *)displayLinkContext; - [view drawAtTime:now frequency:CVDisplayLinkGetActualOutputVideoRefreshPeriod(displayLink)]; - } + CSOpenGLView *const view = (__bridge CSOpenGLView *)displayLinkContext; + [view drawAtTime:now frequency:CVDisplayLinkGetActualOutputVideoRefreshPeriod(displayLink)]; return kCVReturnSuccess; } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 06362794d..2c2490627 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -98,7 +98,7 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : _buffer_builder = std::unique_ptr(new CRTInputBufferBuilder(buffer_depth)); glBlendFunc(GL_SRC_ALPHA, GL_CONSTANT_COLOR); - glBlendColor(0.0f, 0.0f, 0.0f, 1.0f); + glBlendColor(0.6f, 0.6f, 0.6f, 1.0f); // Create intermediate textures and bind to slots 0, 1 and 2 compositeTexture = std::unique_ptr(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, composite_texture_unit)); @@ -196,35 +196,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out _drawn_source_buffer_data_pointer = _source_buffer_data_pointer; _drawn_output_buffer_data_pointer = _output_buffer_data_pointer; - for(int c = 0; c < number_of_source_drawing_zones; c++) - { - printf("src: + %0.0f\n", (float)source_drawing_zones[c*2 + 1] / (2.0f * SourceVertexSize)); - int offset = source_drawing_zones[c*2 + 0]; - uint16_t *base = (uint16_t *)&_source_buffer_data[offset]; - printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[10], base[11]); - - offset += source_drawing_zones[c*2 + 1] - 2*SourceVertexSize; - base = (uint16_t *)&_source_buffer_data[offset]; - printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[10], base[11]); - } - printf("tx: + %d\n", completed_texture_y); - for(int c = 0; c < number_of_clearing_zones; c++) - { - printf("cl: %d + %d\n", clearing_zones[c*2], clearing_zones[c*2 + 1]); - } - for(int c = 0; c < number_of_output_drawing_zones; c++) - { - printf("o: + %0.0f\n", (float)output_drawing_zones[c*2 + 1] / (6.0f * OutputVertexSize)); - int offset = output_drawing_zones[c*2 + 0]; - uint16_t *base = (uint16_t *)&_output_buffer_data[offset]; - printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[14], base[15]); - - offset += output_drawing_zones[c*2 + 1] - 6*OutputVertexSize; - base = (uint16_t *)&_output_buffer_data[offset]; - printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[14], base[15]); - } - printf("\n"); - // release the mapping, giving up on trying to draw if data has been lost glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); for(int c = 0; c < number_of_output_drawing_zones; c++) diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp index c49f93f8a..530d689ac 100644 --- a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp +++ b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp @@ -159,7 +159,7 @@ std::unique_ptr IntermediateShader::make_rgb_source_shader(c "void main(void)" "{" - "fragColour = vec3(0.2) + rgb_sample(texID, inputPositionsVarying[5], iInputPositionVarying);" + "fragColour = rgb_sample(texID, inputPositionsVarying[5], iInputPositionVarying);" "}" , rgb_shader); From 8e4ab3771951ae986a51824f4fa704f5e1a28461 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 7 May 2016 18:37:18 -0400 Subject: [PATCH 13/27] Things are back pretty much to where they were, but offset is eliminated as an input. --- .../Documents/MachineDocument.swift | 6 ++ Outputs/CRT/CRT.cpp | 36 +++++++---- Outputs/CRT/CRT.hpp | 2 +- Outputs/CRT/Internals/CRTConstants.hpp | 4 +- .../CRT/Internals/CRTInputBufferBuilder.cpp | 20 ++++--- .../CRT/Internals/CRTInputBufferBuilder.hpp | 3 + Outputs/CRT/Internals/CRTOpenGL.cpp | 60 +++++++++++-------- Outputs/CRT/Internals/CRTOpenGL.hpp | 23 ++++++- .../Internals/Shaders/IntermediateShader.cpp | 11 ++-- 9 files changed, 110 insertions(+), 55 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 473e81aad..19c905c68 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -36,7 +36,13 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe private var cycleCountError: Int64 = 0 private var lastTime: CVTimeStamp? private var skippedFrames = 0 +// private var frameSkip = 0 final func openGLView(view: CSOpenGLView, didUpdateToTime time: CVTimeStamp, didSkipPreviousUpdate : Bool, frequency : Double) { +// frameSkip = frameSkip + 1 +// let modFrameSkip = frameSkip % 10 +// if modFrameSkip == 0 { +// runForNumberOfCycles(Int32(intendedCyclesPerSecond / 60)) +// } if let lastTime = lastTime { // perform (time passed in seconds) * (intended cycles per second), converting and // maintaining an error count to deal with underflow diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index a14f6a29d..0f1680df1 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -104,9 +104,8 @@ Flywheel::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested, #define source_input_position_y(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfInputPosition + 2]) #define source_output_position_x(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfOutputPosition + 0]) #define source_output_position_y(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfOutputPosition + 2]) -#define source_phase(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAmplitudeAndOffset + 0] -#define source_amplitude(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAmplitudeAndOffset + 1] -#define source_offset(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAmplitudeAndOffset + 2] +#define source_phase(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAndAmplitude + 0] +#define source_amplitude(v) next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseAndAmplitude + 1] #define source_phase_time(v) (*(uint16_t *)&next_run[SourceVertexSize*v + SourceVertexOffsetOfPhaseTime]) void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divider, bool hsync_requested, bool vsync_requested, const bool vsync_charging, const Scan::Type type, uint16_t tex_x, uint16_t tex_y) @@ -142,6 +141,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi // [0/1] 3 if(next_run) { +// printf("%d -> %d", tex_y, _openGL_output_builder->get_composite_output_y()); source_input_position_x(0) = tex_x; source_input_position_y(0) = source_input_position_y(1) = tex_y; source_output_position_x(0) = (uint16_t)_horizontal_flywheel->get_current_output_position(); @@ -149,8 +149,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi source_phase(0) = source_phase(1) = _colour_burst_phase; source_amplitude(0) = source_amplitude(1) = _colour_burst_amplitude; source_phase_time(0) = source_phase_time(1) = _colour_burst_time; - source_offset(0) = 0; - source_offset(1) = 255; } // decrement the number of cycles left to run for and increment the @@ -188,16 +186,23 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi if(needs_endpoint) { - uint8_t *next_run = _openGL_output_builder->get_next_output_run(); - - if(next_run) + if( + _openGL_output_builder->composite_output_run_has_room_for_vertices(_did_start_run ? 6 : 3) && + !_openGL_output_builder->composite_output_buffer_is_full() && + _is_writing_composite_run == _did_start_run) { - output_position_x(0) = output_position_x(1) = output_position_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); - output_position_y(0) = output_position_y(1) = output_position_y(2) = (uint16_t)(_vertical_flywheel->get_current_output_position() / _vertical_flywheel_output_divider); - output_tex_x(0) = output_tex_x(1) = output_tex_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); - output_tex_y(0) = output_tex_y(1) = output_tex_y(2) = _openGL_output_builder->get_composite_output_y(); + uint8_t *next_run = _openGL_output_builder->get_next_output_run(); + if(next_run) + { +// printf("{%d -> %0.2f}", _openGL_output_builder->get_composite_output_y(), (float)_vertical_flywheel->get_current_output_position() / (float)_vertical_flywheel->get_scan_period()); + output_position_x(0) = output_position_x(1) = output_position_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); + output_position_y(0) = output_position_y(1) = output_position_y(2) = (uint16_t)(_vertical_flywheel->get_current_output_position() / _vertical_flywheel_output_divider); + output_tex_x(0) = output_tex_x(1) = output_tex_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); + output_tex_y(0) = output_tex_y(1) = output_tex_y(2) = _openGL_output_builder->get_composite_output_y(); - _openGL_output_builder->complete_output_run(3); + _openGL_output_builder->complete_output_run(3); + _did_start_run ^= true; + } } _is_writing_composite_run ^= true; } @@ -210,6 +215,10 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi // if this is vertical retrace then adcance a field if(next_run_length == time_until_vertical_sync_event && next_vertical_sync_event == Flywheel::SyncEvent::EndRetrace) { +// printf("\n!%d!\n", _openGL_output_builder->get_composite_output_y()); + // TODO: eliminate the below; it's to aid with debug, aligning the top of the + // input buffer with the top of the incoming frame. + _openGL_output_builder->release_source_buffer_write_pointer(); if(_delegate) { _frames_since_last_delegate_call++; @@ -317,6 +326,7 @@ void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider { if(_openGL_output_builder->reduce_previous_allocation_to(number_of_cycles / source_divider)) { +// printf("[%d with %d]", (_vertical_flywheel->get_current_time() * 262) / _vertical_flywheel->get_scan_period(), _openGL_output_builder->get_last_write_y_posititon()); Scan scan { .type = Scan::Type::Data, .number_of_cycles = number_of_cycles, diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index 0794956f5..3ea5858ab 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -68,7 +68,7 @@ class CRT { uint8_t _colour_burst_phase, _colour_burst_amplitude; uint16_t _colour_burst_time; - bool _is_writing_composite_run; + bool _is_writing_composite_run, _did_start_run; // the outer entry point for dispatching output_sync, output_blank, output_level and output_data void advance_cycles(unsigned int number_of_cycles, unsigned int source_divider, bool hsync_requested, bool vsync_requested, const bool vsync_charging, const Scan::Type type, uint16_t tex_x, uint16_t tex_y); diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp index 50096f4ec..f2764d9b0 100644 --- a/Outputs/CRT/Internals/CRTConstants.hpp +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -26,14 +26,14 @@ const GLsizei OutputVertexSize = 8; // remapping occurs to ensure a continous stream of data for each scan, giving correct out-of-bounds behaviour const GLsizei SourceVertexOffsetOfInputPosition = 0; const GLsizei SourceVertexOffsetOfOutputPosition = 4; -const GLsizei SourceVertexOffsetOfPhaseAmplitudeAndOffset = 8; +const GLsizei SourceVertexOffsetOfPhaseAndAmplitude = 8; const GLsizei SourceVertexOffsetOfPhaseTime = 12; const GLsizei SourceVertexSize = 16; // These constants hold the size of the rolling buffer to which the CPU writes const GLsizei InputBufferBuilderWidth = 2048; -const GLsizei InputBufferBuilderHeight = 1024; +const GLsizei InputBufferBuilderHeight = 1024; // 1024 // This is the size of the intermediate buffers used during composite to RGB conversion const GLsizei IntermediateBufferWidth = 2048; diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index 15ad5e617..605423033 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -16,7 +16,8 @@ CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) : _bytes_per_pixel(bytes_per_pixel), _next_write_x_position(0), _next_write_y_position(0), - _image(new uint8_t[bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight]) + _image(new uint8_t[bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight]), + _should_reset(false) {} CRTInputBufferBuilder::~CRTInputBufferBuilder() @@ -30,7 +31,7 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) { _last_allocation_amount = required_length; -// if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth) + if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth) { _next_write_x_position = 0; _next_write_y_position++; @@ -41,13 +42,17 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) _write_x_position = _next_write_x_position + 1; _write_y_position = _next_write_y_position; +// printf("#%d,%d#", _write_x_position, _write_y_position); _write_target_pointer = (_write_y_position * InputBufferBuilderWidth) + _write_x_position; _next_write_x_position += required_length + 2; + } +} - if(_write_y_position == 0 && _write_x_position == 0) - { - memset(_image, 0, _bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight); - } +void CRTInputBufferBuilder::release_write_pointer() +{ + if(_should_reset) + { + _next_write_x_position = _next_write_y_position = 0; } } @@ -78,8 +83,9 @@ uint8_t *CRTInputBufferBuilder::get_image_pointer() uint16_t CRTInputBufferBuilder::get_and_finalise_current_line() { + // TODO: we may have a vended allocate_write_area in play that'll be lost by this step; fix. uint16_t result = _write_y_position + (_next_write_x_position ? 1 : 0); - _next_write_x_position = _next_write_y_position = 0; + _should_reset = (_next_write_y_position == InputBufferBuilderHeight); return result; } diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp index 33279f7c0..c4488d04b 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp @@ -27,6 +27,7 @@ struct CRTInputBufferBuilder { bool reduce_previous_allocation_to(size_t actual_length); uint16_t get_and_finalise_current_line(); + void release_write_pointer(); uint8_t *get_image_pointer(); uint8_t *get_write_target(); @@ -53,6 +54,8 @@ struct CRTInputBufferBuilder { // the buffer uint8_t *_image; + + bool _should_reset; }; } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 2c2490627..90b9ecaad 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -173,10 +173,10 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out // This should return either an actual framebuffer number, if this is a target with a framebuffer intended for output, // or 0 if no framebuffer is bound, in which case 0 is also what we want to supply to bind the implied framebuffer. So // it works either way. - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&defaultFramebuffer); +// glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&defaultFramebuffer); // TODO: is this sustainable, cross-platform? If so, why store it at all? - defaultFramebuffer = 0; +// defaultFramebuffer = 0; } // determine how many lines are newly reclaimed; they'll need to be cleared @@ -186,6 +186,14 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out int number_of_source_drawing_zones = getCircularRanges(_drawn_source_buffer_data_pointer, _source_buffer_data_pointer, SourceVertexBufferDataSize, 2*SourceVertexSize, source_drawing_zones); int number_of_output_drawing_zones = getCircularRanges(_drawn_output_buffer_data_pointer, _output_buffer_data_pointer, OutputVertexBufferDataSize, 6*OutputVertexSize, output_drawing_zones); +// for(int c = 0; c < number_of_output_drawing_zones; c++) +// { +// printf("\n(%d->%d)\n", output_drawing_zones[c*2], output_drawing_zones[c*2] + output_drawing_zones[c*2 + 1]); +// } + +// if(number_of_output_drawing_zones) +// printf("\n\n"); + uint16_t completed_texture_y = _buffer_builder->get_and_finalise_current_line(); _composite_src_output_y %= IntermediateBufferHeight; @@ -279,18 +287,17 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out active_pipeline->shader->bind(); // clear as desired -// if(number_of_clearing_zones) -// { -// glEnable(GL_SCISSOR_TEST); -// glClearColor(active_pipeline->clear_colour[0], active_pipeline->clear_colour[1], active_pipeline->clear_colour[2], 1.0); -// for(int c = 0; c < number_of_clearing_zones; c++) -// { -// glScissor(0, clearing_zones[c*2], IntermediateBufferWidth, clearing_zones[c*2 + 1]); -// glClear(GL_COLOR_BUFFER_BIT); -// } -// glDisable(GL_SCISSOR_TEST); -// } - glClear(GL_COLOR_BUFFER_BIT); + if(number_of_clearing_zones) + { + glEnable(GL_SCISSOR_TEST); + glClearColor(active_pipeline->clear_colour[0], active_pipeline->clear_colour[1], active_pipeline->clear_colour[2], 1.0); + for(int c = 0; c < number_of_clearing_zones; c++) + { + glScissor(0, clearing_zones[c*2], IntermediateBufferWidth, clearing_zones[c*2 + 1]); + glClear(GL_COLOR_BUFFER_BIT); + } + glDisable(GL_SCISSOR_TEST); + } // draw as desired for(int c = 0; c < number_of_source_drawing_zones; c++) @@ -305,8 +312,11 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out // transfer to framebuffer framebuffer->bind_framebuffer(); + if(number_of_output_drawing_zones) { +// glClearColor(0.5, 0.5, 0.5, 1.0); +// glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); // Ensure we're back on the output framebuffer, drawing from the output array buffer @@ -326,6 +336,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out { glDrawArrays(GL_TRIANGLE_STRIP, output_drawing_zones[c*2] / OutputVertexSize, output_drawing_zones[c*2 + 1] / OutputVertexSize); } +// glDrawArrays(GL_TRIANGLE_STRIP, 0, OutputVertexBufferDataSize / OutputVertexSize); } // copy framebuffer to the intended place @@ -333,8 +344,9 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height); glClear(GL_COLOR_BUFFER_BIT); + framebuffer->draw((float)output_width / (float)output_height); -// compositeTexture->draw((float)output_width / (float)output_height); +// filteredYTexture->draw((float)output_width / (float)output_height); // drawing commands having been issued, reclaim the array buffer pointer glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); @@ -399,24 +411,24 @@ void OpenGLOutputBuilder::prepare_source_vertex_array() { if(composite_input_shader_program) { - GLint inputPositionAttribute = composite_input_shader_program->get_attrib_location("inputPosition"); - GLint outputPositionAttribute = composite_input_shader_program->get_attrib_location("outputPosition"); - GLint phaseAmplitudeAndOffsetAttribute = composite_input_shader_program->get_attrib_location("phaseAmplitudeAndOffset"); - GLint phaseTimeAttribute = composite_input_shader_program->get_attrib_location("phaseTime"); + GLint inputPositionAttribute = composite_input_shader_program->get_attrib_location("inputPosition"); + GLint outputPositionAttribute = composite_input_shader_program->get_attrib_location("outputPosition"); + GLint phaseAndAmplitudeAttribute = composite_input_shader_program->get_attrib_location("phaseAndAmplitude"); + GLint phaseTimeAttribute = composite_input_shader_program->get_attrib_location("phaseTime"); glBindVertexArray(source_vertex_array); glEnableVertexAttribArray((GLuint)inputPositionAttribute); glEnableVertexAttribArray((GLuint)outputPositionAttribute); - glEnableVertexAttribArray((GLuint)phaseAmplitudeAndOffsetAttribute); + glEnableVertexAttribArray((GLuint)phaseAndAmplitudeAttribute); glEnableVertexAttribArray((GLuint)phaseTimeAttribute); const GLsizei vertexStride = SourceVertexSize; glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); - glVertexAttribPointer((GLuint)inputPositionAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)SourceVertexOffsetOfInputPosition); - glVertexAttribPointer((GLuint)outputPositionAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)SourceVertexOffsetOfOutputPosition); - glVertexAttribPointer((GLuint)phaseAmplitudeAndOffsetAttribute, 3, GL_UNSIGNED_BYTE, GL_TRUE, vertexStride, (void *)SourceVertexOffsetOfPhaseAmplitudeAndOffset); - glVertexAttribPointer((GLuint)phaseTimeAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)SourceVertexOffsetOfPhaseTime); + glVertexAttribPointer((GLuint)inputPositionAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)SourceVertexOffsetOfInputPosition); + glVertexAttribPointer((GLuint)outputPositionAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)SourceVertexOffsetOfOutputPosition); + glVertexAttribPointer((GLuint)phaseAndAmplitudeAttribute, 2, GL_UNSIGNED_BYTE, GL_TRUE, vertexStride, (void *)SourceVertexOffsetOfPhaseAndAmplitude); + glVertexAttribPointer((GLuint)phaseTimeAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)SourceVertexOffsetOfPhaseTime); } } diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index c41550f8c..13c3daa20 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -117,10 +117,16 @@ class OpenGLOutputBuilder { _output_mutex->unlock(); } + inline bool composite_output_run_has_room_for_vertices(GLsizei vertices_to_write) + { + return _composite_src_output_y <= _cleared_composite_output_y + IntermediateBufferHeight - vertices_to_write * OutputVertexSize; + } + inline uint8_t *get_next_output_run() { if(_output_buffer_data_pointer == _drawn_output_buffer_data_pointer + OutputVertexBufferDataSize) return nullptr; _output_mutex->lock(); +// printf("{%ld}", _output_buffer_data_pointer % OutputVertexBufferDataSize); return &_output_buffer_data[_output_buffer_data_pointer % OutputVertexBufferDataSize]; } @@ -135,9 +141,9 @@ class OpenGLOutputBuilder { return _output_device; } - inline bool composite_output_buffer_is_full() + inline bool composite_output_buffer_has_room_for_vertices(GLsizei vertices_to_write) { - return _composite_src_output_y == _cleared_composite_output_y + IntermediateBufferHeight; + return _composite_src_output_y <= _cleared_composite_output_y + IntermediateBufferHeight - vertices_to_write * OutputVertexSize; } inline uint16_t get_composite_output_y() @@ -145,9 +151,15 @@ class OpenGLOutputBuilder { return _composite_src_output_y % IntermediateBufferHeight; } + inline bool composite_output_buffer_is_full() + { + return _composite_src_output_y == _cleared_composite_output_y + IntermediateBufferHeight; + } + inline void increment_composite_output_y() { - _composite_src_output_y++; + if(!composite_output_buffer_is_full()) + _composite_src_output_y++; } inline uint8_t *allocate_write_area(size_t required_length) @@ -171,6 +183,11 @@ class OpenGLOutputBuilder { return _buffer_builder->get_last_write_y_position(); } + inline void release_source_buffer_write_pointer() + { + _buffer_builder->release_write_pointer(); + } + void draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty); void set_openGL_context_will_change(bool should_delete_resources); void set_composite_sampling_function(const char *shader); diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp index 530d689ac..003b3e19f 100644 --- a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp +++ b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp @@ -20,7 +20,7 @@ namespace { { {"inputPosition", 0}, {"outputPosition", 1}, - {"phaseAmplitudeAndOffset", 2}, + {"phaseAndAmplitude", 2}, {"phaseTime", 3}, {nullptr} }; @@ -37,7 +37,7 @@ std::unique_ptr IntermediateShader::make_shader(const char * "in vec2 inputPosition;" "in vec2 outputPosition;" - "in vec3 phaseAmplitudeAndOffset;" + "in vec2 phaseAndAmplitude;" "in float phaseTime;" "uniform float phaseCyclesPerTick;" @@ -53,7 +53,8 @@ std::unique_ptr IntermediateShader::make_shader(const char * "void main(void)" "{" - "vec2 extensionVector = vec2(extension, 0.0) * 2.0 * (phaseAmplitudeAndOffset.z - 0.5);" + "float direction = float(gl_VertexID & 1);" + "vec2 extensionVector = vec2(extension, 0.0) * 2.0 * (direction - 0.5);" "vec2 extendedInputPosition = %s + extensionVector;" "vec2 extendedOutputPosition = outputPosition + extensionVector;" @@ -74,8 +75,8 @@ std::unique_ptr IntermediateShader::make_shader(const char * "inputPositionsVarying[10] = mappedInputPosition + (vec2(offsets[0], 0.0) / textureSize);" "delayLinePositionVarying = mappedInputPosition - vec2(0.0, 1.0);" - "phaseAndAmplitudeVarying.x = (phaseCyclesPerTick * (extendedOutputPosition.x - phaseTime) + phaseAmplitudeAndOffset.x) * 2.0 * 3.141592654;" - "phaseAndAmplitudeVarying.y = 0.33;" + "phaseAndAmplitudeVarying.x = (phaseCyclesPerTick * (extendedOutputPosition.x - phaseTime) + phaseAndAmplitude.x) * 2.0 * 3.141592654;" + "phaseAndAmplitudeVarying.y = 0.33;" // TODO: reinstate connection with phaseAndAmplitude "vec2 eyePosition = 2.0*(extendedOutputPosition / outputTextureSize) - vec2(1.0) + vec2(0.5)/textureSize;" "gl_Position = vec4(eyePosition, 0.0, 1.0);" From 1142c86811d95382a50c153e483d161b768578fe Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 7 May 2016 18:38:51 -0400 Subject: [PATCH 14/27] Slightly simplified conditional. --- Machines/Atari2600/Atari2600.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index 0022cdc03..a5113400f 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -156,8 +156,8 @@ void Machine::get_output_pixel(uint8_t *pixel, int offset) if(playerPixels[0] || missilePixels[0]) outputColour = _playerColour[0]; } - // store colour, if possible - if(pixel) *pixel = outputColour; + // store colour + *pixel = outputColour; } // in imputing the knowledge that all we're dealing with is the rollover from 159 to 0, @@ -237,7 +237,8 @@ void Machine::output_pixels(unsigned int count) } if(_horizontalTimer < (_vBlankExtend ? 152 : 160)) { - get_output_pixel(_outputBuffer ? &_outputBuffer[_lastOutputStateDuration] : nullptr, 159 - _horizontalTimer); + uint8_t throwaway_pixel; + get_output_pixel(_outputBuffer ? &_outputBuffer[_lastOutputStateDuration] : &throwaway_pixel, 159 - _horizontalTimer); // increment all graphics counters increment_object_counter(0); From 9485ef2c8cdb0ac7e7841fc8d0d878e7337d152c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 May 2016 16:07:36 -0400 Subject: [PATCH 15/27] At last a genuine bug fixed: was nudging the wrong amount in intermediate shaders. --- .../Documents/Atari2600Document.swift | 5 +++++ .../Documents/MachineDocument.swift | 8 ++++++-- .../Mac/Clock Signal/Views/CSOpenGLView.h | 2 ++ .../Mac/Clock Signal/Views/CSOpenGLView.m | 17 +++++++++-------- Outputs/CRT/CRT.cpp | 4 ---- Outputs/CRT/Internals/CRTConstants.hpp | 4 ++-- Outputs/CRT/Internals/CRTInputBufferBuilder.cpp | 3 ++- Outputs/CRT/Internals/CRTOpenGL.cpp | 13 ------------- Outputs/CRT/Internals/CRTOpenGL.hpp | 2 +- .../Internals/Shaders/IntermediateShader.cpp | 2 +- 10 files changed, 28 insertions(+), 32 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift b/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift index 6c586c360..071a44db1 100644 --- a/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift +++ b/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift @@ -77,6 +77,11 @@ class Atari2600Document: MachineDocument { atari2600.setState(true, forDigitalInput: input) } + if event.keyCode == 49 { + self.runForNumberOfCycles(Int32(self.intendedCyclesPerSecond / 60)) + self.openGLView.drawViewOnlyIfDirty(true) + } + if event.keyCode == 36 { atari2600.setResetLineEnabled(true) } diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 19c905c68..41eb71ebf 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -36,12 +36,16 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe private var cycleCountError: Int64 = 0 private var lastTime: CVTimeStamp? private var skippedFrames = 0 -// private var frameSkip = 0 + private var frameSkip = 0 final func openGLView(view: CSOpenGLView, didUpdateToTime time: CVTimeStamp, didSkipPreviousUpdate : Bool, frequency : Double) { // frameSkip = frameSkip + 1 // let modFrameSkip = frameSkip % 10 -// if modFrameSkip == 0 { +// if modFrameSkip == 0 && frameSkip < 500 { // runForNumberOfCycles(Int32(intendedCyclesPerSecond / 60)) +// view.drawViewOnlyIfDirty(true) +// view.performWithGLContext({ () -> Void in +// self.openGLView(view, drawViewOnlyIfDirty: true) +// }) // } if let lastTime = lastTime { // perform (time passed in seconds) * (intended cycles per second), converting and diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h index f5c821fe8..dad53987d 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h @@ -75,4 +75,6 @@ - (void)performWithGLContext:(nonnull dispatch_block_t)action; +- (void)drawViewOnlyIfDirty:(BOOL)onlyIfDirty; + @end diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m index 8e41cc50e..e6bf76dfd 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m @@ -60,6 +60,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt BOOL didSkip = _hasSkipped; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ [self.delegate openGLView:self didUpdateToTime:time didSkipPreviousUpdate:didSkip frequency:frequency]; + [self drawViewOnlyIfDirty:YES]; OSAtomicTestAndClear(processingMask, &_updateIsOngoing); }); _hasSkipped = NO; @@ -68,13 +69,13 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt // 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); - }); - } +// if(!OSAtomicTestAndSet(drawingMask, &_updateIsOngoing)) +// { +// dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ +// [self drawViewOnlyIfDirty:YES]; +// OSAtomicTestAndClear(drawingMask, &_updateIsOngoing); +// }); +// } } - (void)invalidate @@ -134,7 +135,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt - (void)drawRect:(NSRect)dirtyRect { - [self drawViewOnlyIfDirty:NO]; +// [self drawViewOnlyIfDirty:NO]; } - (void)drawViewOnlyIfDirty:(BOOL)onlyIfDirty diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 0f1680df1..2c5969047 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -141,7 +141,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi // [0/1] 3 if(next_run) { -// printf("%d -> %d", tex_y, _openGL_output_builder->get_composite_output_y()); source_input_position_x(0) = tex_x; source_input_position_y(0) = source_input_position_y(1) = tex_y; source_output_position_x(0) = (uint16_t)_horizontal_flywheel->get_current_output_position(); @@ -194,7 +193,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi uint8_t *next_run = _openGL_output_builder->get_next_output_run(); if(next_run) { -// printf("{%d -> %0.2f}", _openGL_output_builder->get_composite_output_y(), (float)_vertical_flywheel->get_current_output_position() / (float)_vertical_flywheel->get_scan_period()); output_position_x(0) = output_position_x(1) = output_position_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); output_position_y(0) = output_position_y(1) = output_position_y(2) = (uint16_t)(_vertical_flywheel->get_current_output_position() / _vertical_flywheel_output_divider); output_tex_x(0) = output_tex_x(1) = output_tex_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); @@ -215,7 +213,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi // if this is vertical retrace then adcance a field if(next_run_length == time_until_vertical_sync_event && next_vertical_sync_event == Flywheel::SyncEvent::EndRetrace) { -// printf("\n!%d!\n", _openGL_output_builder->get_composite_output_y()); // TODO: eliminate the below; it's to aid with debug, aligning the top of the // input buffer with the top of the incoming frame. _openGL_output_builder->release_source_buffer_write_pointer(); @@ -326,7 +323,6 @@ void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider { if(_openGL_output_builder->reduce_previous_allocation_to(number_of_cycles / source_divider)) { -// printf("[%d with %d]", (_vertical_flywheel->get_current_time() * 262) / _vertical_flywheel->get_scan_period(), _openGL_output_builder->get_last_write_y_posititon()); Scan scan { .type = Scan::Type::Data, .number_of_cycles = number_of_cycles, diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp index f2764d9b0..da9484fd9 100644 --- a/Outputs/CRT/Internals/CRTConstants.hpp +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -33,11 +33,11 @@ const GLsizei SourceVertexSize = 16; // These constants hold the size of the rolling buffer to which the CPU writes const GLsizei InputBufferBuilderWidth = 2048; -const GLsizei InputBufferBuilderHeight = 1024; // 1024 +const GLsizei InputBufferBuilderHeight = 512; // This is the size of the intermediate buffers used during composite to RGB conversion const GLsizei IntermediateBufferWidth = 2048; -const GLsizei IntermediateBufferHeight = 2048; +const GLsizei IntermediateBufferHeight = 512; // Some internal buffer sizes const GLsizeiptr OutputVertexBufferDataSize = 44928; // a multiple of 6 * OutputVertexSize diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index 605423033..920375437 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -42,7 +42,7 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) _write_x_position = _next_write_x_position + 1; _write_y_position = _next_write_y_position; -// printf("#%d,%d#", _write_x_position, _write_y_position); +// printf("#%d,%d#\n", _write_x_position, _write_y_position); _write_target_pointer = (_write_y_position * InputBufferBuilderWidth) + _write_x_position; _next_write_x_position += required_length + 2; } @@ -53,6 +53,7 @@ void CRTInputBufferBuilder::release_write_pointer() if(_should_reset) { _next_write_x_position = _next_write_y_position = 0; +// printf("#Reset!#\n"); } } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 90b9ecaad..ed6a20478 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -185,15 +185,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out int number_of_clearing_zones = getCircularRanges(_cleared_composite_output_y, _composite_src_output_y, IntermediateBufferHeight, 1, clearing_zones); int number_of_source_drawing_zones = getCircularRanges(_drawn_source_buffer_data_pointer, _source_buffer_data_pointer, SourceVertexBufferDataSize, 2*SourceVertexSize, source_drawing_zones); int number_of_output_drawing_zones = getCircularRanges(_drawn_output_buffer_data_pointer, _output_buffer_data_pointer, OutputVertexBufferDataSize, 6*OutputVertexSize, output_drawing_zones); - -// for(int c = 0; c < number_of_output_drawing_zones; c++) -// { -// printf("\n(%d->%d)\n", output_drawing_zones[c*2], output_drawing_zones[c*2] + output_drawing_zones[c*2 + 1]); -// } - -// if(number_of_output_drawing_zones) -// printf("\n\n"); - uint16_t completed_texture_y = _buffer_builder->get_and_finalise_current_line(); _composite_src_output_y %= IntermediateBufferHeight; @@ -315,8 +306,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out if(number_of_output_drawing_zones) { -// glClearColor(0.5, 0.5, 0.5, 1.0); -// glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); // Ensure we're back on the output framebuffer, drawing from the output array buffer @@ -336,7 +325,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out { glDrawArrays(GL_TRIANGLE_STRIP, output_drawing_zones[c*2] / OutputVertexSize, output_drawing_zones[c*2 + 1] / OutputVertexSize); } -// glDrawArrays(GL_TRIANGLE_STRIP, 0, OutputVertexBufferDataSize / OutputVertexSize); } // copy framebuffer to the intended place @@ -346,7 +334,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glClear(GL_COLOR_BUFFER_BIT); framebuffer->draw((float)output_width / (float)output_height); -// filteredYTexture->draw((float)output_width / (float)output_height); // drawing commands having been issued, reclaim the array buffer pointer glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index 13c3daa20..dcea80003 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -57,7 +57,7 @@ class OpenGLOutputBuilder { // the run and input data buffers std::unique_ptr _buffer_builder; - std::shared_ptr _output_mutex; + std::unique_ptr _output_mutex; // transient buffers indicating composite data not yet decoded uint16_t _composite_src_output_y, _cleared_composite_output_y; diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp index 003b3e19f..4bdbb0b9b 100644 --- a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp +++ b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp @@ -78,7 +78,7 @@ std::unique_ptr IntermediateShader::make_shader(const char * "phaseAndAmplitudeVarying.x = (phaseCyclesPerTick * (extendedOutputPosition.x - phaseTime) + phaseAndAmplitude.x) * 2.0 * 3.141592654;" "phaseAndAmplitudeVarying.y = 0.33;" // TODO: reinstate connection with phaseAndAmplitude - "vec2 eyePosition = 2.0*(extendedOutputPosition / outputTextureSize) - vec2(1.0) + vec2(0.5)/textureSize;" + "vec2 eyePosition = 2.0*(extendedOutputPosition / outputTextureSize) - vec2(1.0) + vec2(0.5)/outputTextureSize;" "gl_Position = vec4(eyePosition, 0.0, 1.0);" "}", sampler_type, input_variable); From 9b2061be983a173b4f4116e54387408497197a2f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 May 2016 16:09:39 -0400 Subject: [PATCH 16/27] Actually, that should probably be 1/outputTextureSize, as the output range is -1 to +1. --- Outputs/CRT/Internals/Shaders/IntermediateShader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp index 4bdbb0b9b..5db05c572 100644 --- a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp +++ b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp @@ -78,7 +78,7 @@ std::unique_ptr IntermediateShader::make_shader(const char * "phaseAndAmplitudeVarying.x = (phaseCyclesPerTick * (extendedOutputPosition.x - phaseTime) + phaseAndAmplitude.x) * 2.0 * 3.141592654;" "phaseAndAmplitudeVarying.y = 0.33;" // TODO: reinstate connection with phaseAndAmplitude - "vec2 eyePosition = 2.0*(extendedOutputPosition / outputTextureSize) - vec2(1.0) + vec2(0.5)/outputTextureSize;" + "vec2 eyePosition = 2.0*(extendedOutputPosition / outputTextureSize) - vec2(1.0) + vec2(1.0)/outputTextureSize;" "gl_Position = vec4(eyePosition, 0.0, 1.0);" "}", sampler_type, input_variable); From a75259ce9fe0a59cb3c05d0bbd31d1abe6d8168b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 May 2016 16:18:42 -0400 Subject: [PATCH 17/27] Removed some dead caveman debugging statements. --- Outputs/CRT/Internals/CRTInputBufferBuilder.cpp | 2 -- Outputs/CRT/Internals/CRTOpenGL.hpp | 1 - 2 files changed, 3 deletions(-) diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index 920375437..22c10030f 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -42,7 +42,6 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) _write_x_position = _next_write_x_position + 1; _write_y_position = _next_write_y_position; -// printf("#%d,%d#\n", _write_x_position, _write_y_position); _write_target_pointer = (_write_y_position * InputBufferBuilderWidth) + _write_x_position; _next_write_x_position += required_length + 2; } @@ -53,7 +52,6 @@ void CRTInputBufferBuilder::release_write_pointer() if(_should_reset) { _next_write_x_position = _next_write_y_position = 0; -// printf("#Reset!#\n"); } } diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index dcea80003..701e11e8c 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -126,7 +126,6 @@ class OpenGLOutputBuilder { { if(_output_buffer_data_pointer == _drawn_output_buffer_data_pointer + OutputVertexBufferDataSize) return nullptr; _output_mutex->lock(); -// printf("{%ld}", _output_buffer_data_pointer % OutputVertexBufferDataSize); return &_output_buffer_data[_output_buffer_data_pointer % OutputVertexBufferDataSize]; } From ccdbec3ffcb3a4ae188c389e763335895bad06cc Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 May 2016 16:43:11 -0400 Subject: [PATCH 18/27] Corrected for potential lost lines. --- Outputs/CRT/Internals/CRTConstants.hpp | 4 ++-- .../CRT/Internals/CRTInputBufferBuilder.cpp | 20 +++++++++++++------ Outputs/CRT/Internals/CRTOpenGL.cpp | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp index da9484fd9..9180d3c32 100644 --- a/Outputs/CRT/Internals/CRTConstants.hpp +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -40,8 +40,8 @@ const GLsizei IntermediateBufferWidth = 2048; const GLsizei IntermediateBufferHeight = 512; // Some internal buffer sizes -const GLsizeiptr OutputVertexBufferDataSize = 44928; // a multiple of 6 * OutputVertexSize -const GLsizeiptr SourceVertexBufferDataSize = 29952; // a multiple of 2 * SourceVertexSize +const GLsizeiptr OutputVertexBufferDataSize = 6 * 8 * 312 * 10; // a multiple of 6 * OutputVertexSize +const GLsizeiptr SourceVertexBufferDataSize = 2 * 16 * 312 * 10; // a multiple of 2 * SourceVertexSize } } diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index 22c10030f..7bb835127 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -49,16 +49,25 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) void CRTInputBufferBuilder::release_write_pointer() { - if(_should_reset) - { - _next_write_x_position = _next_write_y_position = 0; - } +// if(_should_reset) +// { +// _next_write_x_position = _next_write_y_position = 0; +// } } bool CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length) { if(_next_write_y_position == InputBufferBuilderHeight) return false; + // correct if the writing cursor was reset while a client was writing + if(_next_write_x_position == 0 && _next_write_y_position == 0 && _write_target_pointer != 1) + { + memmove(&_image[1], &_image[_write_target_pointer], actual_length); + _write_target_pointer = 1; + _last_allocation_amount = actual_length; + _next_write_x_position = (uint16_t)(actual_length + 2); + } + // book end the allocation with duplicates of the first and last pixel, to protect // against rounding errors when this run is drawn memcpy( &_image[(_write_target_pointer - 1) * _bytes_per_pixel], @@ -82,9 +91,8 @@ uint8_t *CRTInputBufferBuilder::get_image_pointer() uint16_t CRTInputBufferBuilder::get_and_finalise_current_line() { - // TODO: we may have a vended allocate_write_area in play that'll be lost by this step; fix. uint16_t result = _write_y_position + (_next_write_x_position ? 1 : 0); - _should_reset = (_next_write_y_position == InputBufferBuilderHeight); + _next_write_x_position = _next_write_y_position = 0; return result; } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index ed6a20478..cfac44ba2 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -185,7 +185,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out int number_of_clearing_zones = getCircularRanges(_cleared_composite_output_y, _composite_src_output_y, IntermediateBufferHeight, 1, clearing_zones); int number_of_source_drawing_zones = getCircularRanges(_drawn_source_buffer_data_pointer, _source_buffer_data_pointer, SourceVertexBufferDataSize, 2*SourceVertexSize, source_drawing_zones); int number_of_output_drawing_zones = getCircularRanges(_drawn_output_buffer_data_pointer, _output_buffer_data_pointer, OutputVertexBufferDataSize, 6*OutputVertexSize, output_drawing_zones); - uint16_t completed_texture_y = _buffer_builder->get_and_finalise_current_line(); + uint16_t completed_texture_y = _buffer_builder->get_and_finalise_current_line(); _composite_src_output_y %= IntermediateBufferHeight; _source_buffer_data_pointer %= SourceVertexBufferDataSize; From 8ca3dfc9c54af540499bc70641ba745c1850e52e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 May 2016 16:45:57 -0400 Subject: [PATCH 19/27] Reinstated traditional drawing logic. --- .../Mac/Clock Signal/Views/CSOpenGLView.m | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m index e6bf76dfd..8e41cc50e 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m @@ -60,7 +60,6 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt BOOL didSkip = _hasSkipped; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ [self.delegate openGLView:self didUpdateToTime:time didSkipPreviousUpdate:didSkip frequency:frequency]; - [self drawViewOnlyIfDirty:YES]; OSAtomicTestAndClear(processingMask, &_updateIsOngoing); }); _hasSkipped = NO; @@ -69,13 +68,13 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt // 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); -// }); -// } + if(!OSAtomicTestAndSet(drawingMask, &_updateIsOngoing)) + { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ + [self drawViewOnlyIfDirty:YES]; + OSAtomicTestAndClear(drawingMask, &_updateIsOngoing); + }); + } } - (void)invalidate @@ -135,7 +134,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt - (void)drawRect:(NSRect)dirtyRect { -// [self drawViewOnlyIfDirty:NO]; + [self drawViewOnlyIfDirty:NO]; } - (void)drawViewOnlyIfDirty:(BOOL)onlyIfDirty From dd469fe83a645164b9135b82dd7539eff71d5e3d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 May 2016 18:50:30 -0400 Subject: [PATCH 20/27] Switched back to normal frame drawing logic and cleaned up the circular range stuff a little, to make it clear that no errors lie there. --- .../Documents/MachineDocument.swift | 8 +-- .../Mac/Clock Signal/Views/CSOpenGLView.m | 2 +- Outputs/CRT/Internals/CRTConstants.hpp | 4 +- Outputs/CRT/Internals/CRTOpenGL.cpp | 56 +++++++++++-------- 4 files changed, 38 insertions(+), 32 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 41eb71ebf..db28c4456 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -36,16 +36,12 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe private var cycleCountError: Int64 = 0 private var lastTime: CVTimeStamp? private var skippedFrames = 0 - private var frameSkip = 0 +// private var frameSkip = 0 final func openGLView(view: CSOpenGLView, didUpdateToTime time: CVTimeStamp, didSkipPreviousUpdate : Bool, frequency : Double) { // frameSkip = frameSkip + 1 -// let modFrameSkip = frameSkip % 10 -// if modFrameSkip == 0 && frameSkip < 500 { +// if (frameSkip % 1) == 0 && frameSkip < 50000 { // runForNumberOfCycles(Int32(intendedCyclesPerSecond / 60)) // view.drawViewOnlyIfDirty(true) -// view.performWithGLContext({ () -> Void in -// self.openGLView(view, drawViewOnlyIfDirty: true) -// }) // } if let lastTime = lastTime { // perform (time passed in seconds) * (intended cycles per second), converting and diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m index 8e41cc50e..90444e057 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m @@ -134,7 +134,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt - (void)drawRect:(NSRect)dirtyRect { - [self drawViewOnlyIfDirty:NO]; +// [self drawViewOnlyIfDirty:NO]; } - (void)drawViewOnlyIfDirty:(BOOL)onlyIfDirty diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp index 9180d3c32..1b83ce9b2 100644 --- a/Outputs/CRT/Internals/CRTConstants.hpp +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -40,8 +40,8 @@ const GLsizei IntermediateBufferWidth = 2048; const GLsizei IntermediateBufferHeight = 512; // Some internal buffer sizes -const GLsizeiptr OutputVertexBufferDataSize = 6 * 8 * 312 * 10; // a multiple of 6 * OutputVertexSize -const GLsizeiptr SourceVertexBufferDataSize = 2 * 16 * 312 * 10; // a multiple of 2 * SourceVertexSize +const GLsizeiptr OutputVertexBufferDataSize = 6 * 8 * 312; // a multiple of 6 * OutputVertexSize +const GLsizeiptr SourceVertexBufferDataSize = 2 * 16 * 312; // a multiple of 2 * SourceVertexSize } } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index cfac44ba2..5e2652ac6 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -37,7 +37,11 @@ static const GLenum formatForDepth(size_t depth) } } -static int getCircularRanges(GLsizei start, GLsizei end, GLsizei buffer_length, GLsizei granularity, GLsizei *ranges) +struct Range { + GLsizei location, length; +}; + +static int getCircularRanges(GLsizei start, GLsizei end, GLsizei buffer_length, GLsizei granularity, Range *ranges) { start -= start%granularity; end -= end%granularity; @@ -46,28 +50,36 @@ static int getCircularRanges(GLsizei start, GLsizei end, GLsizei buffer_length, if(!length) return 0; if(length >= buffer_length) { - ranges[0] = 0; - ranges[1] = buffer_length; + ranges[0].location = 0; + ranges[0].length = buffer_length; return 1; } else { - ranges[0] = start % buffer_length; - if(ranges[0]+length <= buffer_length) + ranges[0].location = start % buffer_length; + if(ranges[0].location + length <= buffer_length) { - ranges[1] = length; + ranges[0].length = length; return 1; } else { - ranges[1] = buffer_length - ranges[0]; - ranges[2] = 0; - ranges[3] = length - ranges[1]; + ranges[0].length = buffer_length - ranges[0].location; + ranges[1].location = 0; + ranges[1].length = length - ranges[0].length; return 2; } } } +static inline void drawArrayRanges(GLenum mode, GLsizei vertex_size, int number_of_ranges, Range *ranges) +{ + for(int c = 0; c < number_of_ranges; c++) + { + glDrawArrays(mode, ranges[c].location / vertex_size, ranges[c].length / vertex_size); + } +} + using namespace Outputs::CRT; namespace { @@ -180,8 +192,8 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out } // determine how many lines are newly reclaimed; they'll need to be cleared - GLsizei clearing_zones[4], source_drawing_zones[4]; - GLsizei output_drawing_zones[4]; + Range clearing_zones[2], source_drawing_zones[2]; + Range output_drawing_zones[2]; int number_of_clearing_zones = getCircularRanges(_cleared_composite_output_y, _composite_src_output_y, IntermediateBufferHeight, 1, clearing_zones); int number_of_source_drawing_zones = getCircularRanges(_drawn_source_buffer_data_pointer, _source_buffer_data_pointer, SourceVertexBufferDataSize, 2*SourceVertexSize, source_drawing_zones); int number_of_output_drawing_zones = getCircularRanges(_drawn_output_buffer_data_pointer, _output_buffer_data_pointer, OutputVertexBufferDataSize, 6*OutputVertexSize, output_drawing_zones); @@ -195,11 +207,13 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out _drawn_source_buffer_data_pointer = _source_buffer_data_pointer; _drawn_output_buffer_data_pointer = _output_buffer_data_pointer; + glFinish(); + // release the mapping, giving up on trying to draw if data has been lost glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); for(int c = 0; c < number_of_output_drawing_zones; c++) { - glFlushMappedBufferRange(GL_ARRAY_BUFFER, output_drawing_zones[c*2], output_drawing_zones[c*2 + 1]); + glFlushMappedBufferRange(GL_ARRAY_BUFFER, output_drawing_zones[c].location, output_drawing_zones[c].length); } glUnmapBuffer(GL_ARRAY_BUFFER); @@ -207,7 +221,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); for(int c = 0; c < number_of_source_drawing_zones; c++) { - glFlushMappedBufferRange(GL_ARRAY_BUFFER, source_drawing_zones[c*2], source_drawing_zones[c*2 + 1]); + glFlushMappedBufferRange(GL_ARRAY_BUFFER, source_drawing_zones[c].location, source_drawing_zones[c].length); } glUnmapBuffer(GL_ARRAY_BUFFER); @@ -284,26 +298,25 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out glClearColor(active_pipeline->clear_colour[0], active_pipeline->clear_colour[1], active_pipeline->clear_colour[2], 1.0); for(int c = 0; c < number_of_clearing_zones; c++) { - glScissor(0, clearing_zones[c*2], IntermediateBufferWidth, clearing_zones[c*2 + 1]); + glScissor(0, clearing_zones[c].location, IntermediateBufferWidth, clearing_zones[c].length); glClear(GL_COLOR_BUFFER_BIT); } glDisable(GL_SCISSOR_TEST); } // draw as desired - for(int c = 0; c < number_of_source_drawing_zones; c++) - { - glDrawArrays(GL_LINES, source_drawing_zones[c*2] / SourceVertexSize, source_drawing_zones[c*2 + 1] / SourceVertexSize); - } + drawArrayRanges(GL_LINES, SourceVertexSize, number_of_source_drawing_zones, source_drawing_zones); active_pipeline++; } + + // TODO: determine why the finish below is required +// glFinish(); } // transfer to framebuffer framebuffer->bind_framebuffer(); - if(number_of_output_drawing_zones) { glEnable(GL_BLEND); @@ -321,10 +334,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out output_shader_program->bind(); // draw - for(int c = 0; c < number_of_output_drawing_zones; c++) - { - glDrawArrays(GL_TRIANGLE_STRIP, output_drawing_zones[c*2] / OutputVertexSize, output_drawing_zones[c*2 + 1] / OutputVertexSize); - } + drawArrayRanges(GL_TRIANGLE_STRIP, OutputVertexSize, number_of_output_drawing_zones, output_drawing_zones); } // copy framebuffer to the intended place From ef3b91d3aaf8470b486ab42a2bcbd4555a6d5488 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 May 2016 19:42:33 -0400 Subject: [PATCH 21/27] Made an attempt to get formal on synchronisation. --- Outputs/CRT/Internals/CRTConstants.hpp | 2 +- Outputs/CRT/Internals/CRTOpenGL.cpp | 56 ++++++++++++-------------- Outputs/CRT/Internals/CRTOpenGL.hpp | 10 +++-- 3 files changed, 33 insertions(+), 35 deletions(-) diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp index 1b83ce9b2..421274c05 100644 --- a/Outputs/CRT/Internals/CRTConstants.hpp +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -41,7 +41,7 @@ const GLsizei IntermediateBufferHeight = 512; // Some internal buffer sizes const GLsizeiptr OutputVertexBufferDataSize = 6 * 8 * 312; // a multiple of 6 * OutputVertexSize -const GLsizeiptr SourceVertexBufferDataSize = 2 * 16 * 312; // a multiple of 2 * SourceVertexSize +const GLsizeiptr SourceVertexBufferDataSize = 2 * 16 * 312; // a multiple of 2 * SourceVertexSize } } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 5e2652ac6..a336e15ac 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -80,6 +80,18 @@ static inline void drawArrayRanges(GLenum mode, GLsizei vertex_size, int number_ } } +static void submitArrayData(GLuint buffer, uint8_t *source, int number_of_ranges, Range *ranges) +{ + glBindBuffer(GL_ARRAY_BUFFER, buffer); + for(int c = 0; c < number_of_ranges; c++) + { + uint8_t *data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, ranges[c].location, ranges[c].length, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); + memcpy(data, &source[ranges[c].location], (size_t)ranges[c].length); + glFlushMappedBufferRange(GL_ARRAY_BUFFER, ranges[c].location, ranges[c].length); + glUnmapBuffer(GL_ARRAY_BUFFER); + } +} + using namespace Outputs::CRT; namespace { @@ -98,8 +110,8 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : _cleared_composite_output_y(0), _composite_shader(nullptr), _rgb_shader(nullptr), - _output_buffer_data(nullptr), - _source_buffer_data(nullptr), + _output_buffer_data(new uint8_t[OutputVertexBufferDataSize]), + _source_buffer_data(new uint8_t[SourceVertexBufferDataSize]), _output_buffer_data_pointer(0), _drawn_output_buffer_data_pointer(0), _source_buffer_data_pointer(0), @@ -136,9 +148,6 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); glBufferData(GL_ARRAY_BUFFER, OutputVertexBufferDataSize, NULL, GL_STREAM_DRAW); - // map that buffer too, for any CRT activity that may occur before the first draw - _output_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, OutputVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); - // create the source vertex array glGenVertexArrays(1, &source_vertex_array); @@ -146,15 +155,12 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : glGenBuffers(1, &source_array_buffer); glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); glBufferData(GL_ARRAY_BUFFER, SourceVertexBufferDataSize, NULL, GL_STREAM_DRAW); - - // map that buffer too, for any CRT activity that may occur before the first draw - _source_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, SourceVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); } OpenGLOutputBuilder::~OpenGLOutputBuilder() { - glUnmapBuffer(GL_ARRAY_BUFFER); - glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); +// glUnmapBuffer(GL_ARRAY_BUFFER); +// glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); glDeleteTextures(1, &textureName); glDeleteBuffers(1, &output_array_buffer); glDeleteBuffers(1, &source_array_buffer); @@ -207,23 +213,19 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out _drawn_source_buffer_data_pointer = _source_buffer_data_pointer; _drawn_output_buffer_data_pointer = _output_buffer_data_pointer; - glFinish(); + // drawing commands having been issued, reclaim the array buffer pointer +// glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); +// +// glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); +// _source_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, SourceVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); + + glClientWaitSync(_fence, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); // release the mapping, giving up on trying to draw if data has been lost - glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); - for(int c = 0; c < number_of_output_drawing_zones; c++) - { - glFlushMappedBufferRange(GL_ARRAY_BUFFER, output_drawing_zones[c].location, output_drawing_zones[c].length); - } - glUnmapBuffer(GL_ARRAY_BUFFER); + submitArrayData(output_array_buffer, _output_buffer_data.get(), number_of_output_drawing_zones, output_drawing_zones); // bind and flush the source array buffer - glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); - for(int c = 0; c < number_of_source_drawing_zones; c++) - { - glFlushMappedBufferRange(GL_ARRAY_BUFFER, source_drawing_zones[c].location, source_drawing_zones[c].length); - } - glUnmapBuffer(GL_ARRAY_BUFFER); + submitArrayData(source_array_buffer, _source_buffer_data.get(), number_of_source_drawing_zones, source_drawing_zones); // make sure there's a target to draw to if(!framebuffer || framebuffer->get_height() != output_height || framebuffer->get_width() != output_width) @@ -345,13 +347,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out framebuffer->draw((float)output_width / (float)output_height); - // drawing commands having been issued, reclaim the array buffer pointer - glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); - _output_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, OutputVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); - - glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); - _source_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, SourceVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); - + _fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); _output_mutex->unlock(); } diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index 701e11e8c..903e7c501 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -108,7 +108,7 @@ class OpenGLOutputBuilder { { if(_source_buffer_data_pointer == _drawn_source_buffer_data_pointer + SourceVertexBufferDataSize) return nullptr; _output_mutex->lock(); - return &_source_buffer_data[_source_buffer_data_pointer % SourceVertexBufferDataSize]; + return &_source_buffer_data.get()[_source_buffer_data_pointer % SourceVertexBufferDataSize]; } inline void complete_source_run() @@ -126,7 +126,7 @@ class OpenGLOutputBuilder { { if(_output_buffer_data_pointer == _drawn_output_buffer_data_pointer + OutputVertexBufferDataSize) return nullptr; _output_mutex->lock(); - return &_output_buffer_data[_output_buffer_data_pointer % OutputVertexBufferDataSize]; + return &_output_buffer_data.get()[_output_buffer_data_pointer % OutputVertexBufferDataSize]; } inline void complete_output_run(GLsizei vertices_written) @@ -194,13 +194,15 @@ class OpenGLOutputBuilder { void set_output_device(OutputDevice output_device); void set_timing(unsigned int input_frequency, unsigned int cycles_per_line, unsigned int height_of_display, unsigned int horizontal_scan_period, unsigned int vertical_scan_period, unsigned int vertical_period_divider); - uint8_t *_source_buffer_data; + std::unique_ptr _source_buffer_data; GLsizei _source_buffer_data_pointer; GLsizei _drawn_source_buffer_data_pointer; - uint8_t *_output_buffer_data; + std::unique_ptr _output_buffer_data; GLsizei _output_buffer_data_pointer; GLsizei _drawn_output_buffer_data_pointer; + + GLsync _fence; }; } From 1a214a83083baf6a0f186888c72929cb98ff866a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 May 2016 19:45:36 -0400 Subject: [PATCH 22/27] Fence sync tied up. Remaining glitches are likely off-by-one-type errors but we'll see. --- OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m | 2 +- Outputs/CRT/Internals/CRTOpenGL.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m index 90444e057..8e41cc50e 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.m @@ -134,7 +134,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt - (void)drawRect:(NSRect)dirtyRect { -// [self drawViewOnlyIfDirty:NO]; + [self drawViewOnlyIfDirty:NO]; } - (void)drawViewOnlyIfDirty:(BOOL)onlyIfDirty diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index a336e15ac..cc000f45c 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -117,7 +117,8 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : _source_buffer_data_pointer(0), _drawn_source_buffer_data_pointer(0), _last_output_width(0), - _last_output_height(0) + _last_output_height(0), + _fence(nullptr) { _buffer_builder = std::unique_ptr(new CRTInputBufferBuilder(buffer_depth)); @@ -219,7 +220,11 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out // glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); // _source_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, SourceVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); - glClientWaitSync(_fence, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); + if(_fence != nullptr) + { + glClientWaitSync(_fence, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); + glDeleteSync(_fence); + } // release the mapping, giving up on trying to draw if data has been lost submitArrayData(output_array_buffer, _output_buffer_data.get(), number_of_output_drawing_zones, output_drawing_zones); From 986019f9f951c119a016c85e9e695c53025d2395 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 May 2016 20:30:59 -0400 Subject: [PATCH 23/27] Fixed error that could lead to split output run vertex writes. --- Outputs/CRT/CRT.cpp | 9 +++++---- Outputs/CRT/Internals/CRTOpenGL.cpp | 17 ----------------- Outputs/CRT/Internals/CRTOpenGL.hpp | 2 +- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 2c5969047..7905f02b3 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -69,7 +69,8 @@ CRT::CRT(unsigned int common_output_divisor) : _common_output_divisor(common_output_divisor), _is_writing_composite_run(false), _delegate(nullptr), - _frames_since_last_delegate_call(0) {} + _frames_since_last_delegate_call(0), + _did_start_run(false) {} CRT::CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int buffer_depth) : CRT(common_output_divisor) { @@ -186,9 +187,9 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi if(needs_endpoint) { if( - _openGL_output_builder->composite_output_run_has_room_for_vertices(_did_start_run ? 6 : 3) && - !_openGL_output_builder->composite_output_buffer_is_full() && - _is_writing_composite_run == _did_start_run) + _is_writing_composite_run == _did_start_run && + _openGL_output_builder->composite_output_run_has_room_for_vertices(_did_start_run ? 3 : 6) && + !_openGL_output_builder->composite_output_buffer_is_full()) { uint8_t *next_run = _openGL_output_builder->get_next_output_run(); if(next_run) diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index cc000f45c..0dcd0e3c0 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -188,14 +188,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out set_timing_uniforms(); set_colour_space_uniforms(); - - // This should return either an actual framebuffer number, if this is a target with a framebuffer intended for output, - // or 0 if no framebuffer is bound, in which case 0 is also what we want to supply to bind the implied framebuffer. So - // it works either way. -// glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&defaultFramebuffer); - - // TODO: is this sustainable, cross-platform? If so, why store it at all? -// defaultFramebuffer = 0; } // determine how many lines are newly reclaimed; they'll need to be cleared @@ -214,12 +206,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out _drawn_source_buffer_data_pointer = _source_buffer_data_pointer; _drawn_output_buffer_data_pointer = _output_buffer_data_pointer; - // drawing commands having been issued, reclaim the array buffer pointer -// glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); -// -// glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); -// _source_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, SourceVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT); - if(_fence != nullptr) { glClientWaitSync(_fence, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); @@ -316,9 +302,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out active_pipeline++; } - - // TODO: determine why the finish below is required -// glFinish(); } // transfer to framebuffer diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index 903e7c501..ba0ec23ea 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -119,7 +119,7 @@ class OpenGLOutputBuilder { inline bool composite_output_run_has_room_for_vertices(GLsizei vertices_to_write) { - return _composite_src_output_y <= _cleared_composite_output_y + IntermediateBufferHeight - vertices_to_write * OutputVertexSize; + return _output_buffer_data_pointer <= _drawn_output_buffer_data_pointer + OutputVertexBufferDataSize - vertices_to_write * OutputVertexSize; } inline uint8_t *get_next_output_run() From 7b5982e455d4c90f693e013527dcee21d57566dd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 May 2016 20:51:28 -0400 Subject: [PATCH 24/27] Removed last mentions of 'lateral'. --- Outputs/CRT/Internals/Shaders/OutputShader.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Outputs/CRT/Internals/Shaders/OutputShader.cpp b/Outputs/CRT/Internals/Shaders/OutputShader.cpp index 00be80a6e..0b7d735e5 100644 --- a/Outputs/CRT/Internals/Shaders/OutputShader.cpp +++ b/Outputs/CRT/Internals/Shaders/OutputShader.cpp @@ -18,7 +18,6 @@ namespace { { {"position", 0}, {"srcCoordinates", 1}, - {"lateral", 2}, {nullptr} }; } @@ -33,7 +32,6 @@ std::unique_ptr OutputShader::make_shader(const char *fragment_met "in vec2 position;" "in vec2 srcCoordinates;" -// "in float lateral;" "uniform vec2 boundsOrigin;" "uniform vec2 boundsSize;" From 3084c465d6dcfa0209b0cf4d9be888d98ef67e2a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 May 2016 20:53:21 -0400 Subject: [PATCH 25/27] Removed further testing work. --- .../Mac/Clock Signal/Documents/Atari2600Document.swift | 5 ----- OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h | 2 -- 2 files changed, 7 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift b/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift index 071a44db1..6c586c360 100644 --- a/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift +++ b/OSBindings/Mac/Clock Signal/Documents/Atari2600Document.swift @@ -77,11 +77,6 @@ class Atari2600Document: MachineDocument { atari2600.setState(true, forDigitalInput: input) } - if event.keyCode == 49 { - self.runForNumberOfCycles(Int32(self.intendedCyclesPerSecond / 60)) - self.openGLView.drawViewOnlyIfDirty(true) - } - if event.keyCode == 36 { atari2600.setResetLineEnabled(true) } diff --git a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h index dad53987d..f5c821fe8 100644 --- a/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h +++ b/OSBindings/Mac/Clock Signal/Views/CSOpenGLView.h @@ -75,6 +75,4 @@ - (void)performWithGLContext:(nonnull dispatch_block_t)action; -- (void)drawViewOnlyIfDirty:(BOOL)onlyIfDirty; - @end From 1f02c5df88dce28bf8f9229cf9e88c54d278dd1e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 May 2016 20:58:18 -0400 Subject: [PATCH 26/27] Tidied up some further loose ends. --- .../Mac/Clock Signal/Documents/MachineDocument.swift | 6 ------ Outputs/CRT/Internals/TextureTarget.cpp | 7 ++++--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index db28c4456..473e81aad 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -36,13 +36,7 @@ class MachineDocument: NSDocument, CSOpenGLViewDelegate, CSOpenGLViewResponderDe private var cycleCountError: Int64 = 0 private var lastTime: CVTimeStamp? private var skippedFrames = 0 -// private var frameSkip = 0 final func openGLView(view: CSOpenGLView, didUpdateToTime time: CVTimeStamp, didSkipPreviousUpdate : Bool, frequency : Double) { -// frameSkip = frameSkip + 1 -// if (frameSkip % 1) == 0 && frameSkip < 50000 { -// runForNumberOfCycles(Int32(intendedCyclesPerSecond / 60)) -// view.drawViewOnlyIfDirty(true) -// } if let lastTime = lastTime { // perform (time passed in seconds) * (intended cycles per second), converting and // maintaining an error count to deal with underflow diff --git a/Outputs/CRT/Internals/TextureTarget.cpp b/Outputs/CRT/Internals/TextureTarget.cpp index 2738a43d1..ed0bffd84 100644 --- a/Outputs/CRT/Internals/TextureTarget.cpp +++ b/Outputs/CRT/Internals/TextureTarget.cpp @@ -8,6 +8,7 @@ #include "TextureTarget.hpp" #include +#include using namespace OpenGL; @@ -29,7 +30,9 @@ TextureTarget::TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit) glGenTextures(1, &_texture); glActiveTexture(texture_unit); glBindTexture(GL_TEXTURE_2D, _texture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)_expanded_width, (GLsizei)_expanded_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + uint8_t *blank_buffer = (uint8_t *)calloc((size_t)(_expanded_width * _expanded_height), 4); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)_expanded_width, (GLsizei)_expanded_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, blank_buffer); + free(blank_buffer); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); @@ -37,8 +40,6 @@ TextureTarget::TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit) if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) throw ErrorFramebufferIncomplete; - - glClear(GL_COLOR_BUFFER_BIT); } TextureTarget::~TextureTarget() From 47302de23a68578a0f5b7d7ba611ac1ee47a3015 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 8 May 2016 21:11:56 -0400 Subject: [PATCH 27/27] Switched to simpler storage for `_image`. --- Outputs/CRT/CRT.cpp | 3 -- .../CRT/Internals/CRTInputBufferBuilder.cpp | 32 ++++++------------- .../CRT/Internals/CRTInputBufferBuilder.hpp | 8 ++--- Outputs/CRT/Internals/CRTOpenGL.hpp | 5 --- 4 files changed, 12 insertions(+), 36 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 7905f02b3..73e2576c2 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -214,9 +214,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi // if this is vertical retrace then adcance a field if(next_run_length == time_until_vertical_sync_event && next_vertical_sync_event == Flywheel::SyncEvent::EndRetrace) { - // TODO: eliminate the below; it's to aid with debug, aligning the top of the - // input buffer with the top of the incoming frame. - _openGL_output_builder->release_source_buffer_write_pointer(); if(_delegate) { _frames_since_last_delegate_call++; diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp index 7bb835127..eecae0ee3 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.cpp @@ -16,15 +16,9 @@ CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) : _bytes_per_pixel(bytes_per_pixel), _next_write_x_position(0), _next_write_y_position(0), - _image(new uint8_t[bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight]), - _should_reset(false) + _image(new uint8_t[bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight]) {} -CRTInputBufferBuilder::~CRTInputBufferBuilder() -{ - delete[] _image; -} - void CRTInputBufferBuilder::allocate_write_area(size_t required_length) { if(_next_write_y_position != InputBufferBuilderHeight) @@ -47,22 +41,16 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length) } } -void CRTInputBufferBuilder::release_write_pointer() -{ -// if(_should_reset) -// { -// _next_write_x_position = _next_write_y_position = 0; -// } -} - bool CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length) { if(_next_write_y_position == InputBufferBuilderHeight) return false; + uint8_t *const image_pointer = _image.get(); + // correct if the writing cursor was reset while a client was writing if(_next_write_x_position == 0 && _next_write_y_position == 0 && _write_target_pointer != 1) { - memmove(&_image[1], &_image[_write_target_pointer], actual_length); + memmove(&image_pointer[1], &image_pointer[_write_target_pointer], actual_length); _write_target_pointer = 1; _last_allocation_amount = actual_length; _next_write_x_position = (uint16_t)(actual_length + 2); @@ -70,12 +58,12 @@ bool CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length) // book end the allocation with duplicates of the first and last pixel, to protect // against rounding errors when this run is drawn - memcpy( &_image[(_write_target_pointer - 1) * _bytes_per_pixel], - &_image[_write_target_pointer * _bytes_per_pixel], + memcpy( &image_pointer[(_write_target_pointer - 1) * _bytes_per_pixel], + &image_pointer[_write_target_pointer * _bytes_per_pixel], _bytes_per_pixel); - memcpy( &_image[(_write_target_pointer + actual_length) * _bytes_per_pixel], - &_image[(_write_target_pointer + actual_length - 1) * _bytes_per_pixel], + memcpy( &image_pointer[(_write_target_pointer + actual_length) * _bytes_per_pixel], + &image_pointer[(_write_target_pointer + actual_length - 1) * _bytes_per_pixel], _bytes_per_pixel); // return any allocated length that wasn't actually used to the available pool @@ -86,7 +74,7 @@ bool CRTInputBufferBuilder::reduce_previous_allocation_to(size_t actual_length) uint8_t *CRTInputBufferBuilder::get_image_pointer() { - return _image; + return _image.get(); } uint16_t CRTInputBufferBuilder::get_and_finalise_current_line() @@ -98,7 +86,7 @@ uint16_t CRTInputBufferBuilder::get_and_finalise_current_line() uint8_t *CRTInputBufferBuilder::get_write_target() { - return (_next_write_y_position == InputBufferBuilderHeight) ? nullptr : &_image[_write_target_pointer * _bytes_per_pixel]; + return (_next_write_y_position == InputBufferBuilderHeight) ? nullptr : &_image.get()[_write_target_pointer * _bytes_per_pixel]; } uint16_t CRTInputBufferBuilder::get_last_write_x_position() diff --git a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp index c4488d04b..55d3ea792 100644 --- a/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp +++ b/Outputs/CRT/Internals/CRTInputBufferBuilder.hpp @@ -14,20 +14,18 @@ #include #include "CRTConstants.hpp" #include "OpenGL.hpp" -#include +#include namespace Outputs { namespace CRT { struct CRTInputBufferBuilder { CRTInputBufferBuilder(size_t bytes_per_pixel); - ~CRTInputBufferBuilder(); void allocate_write_area(size_t required_length); bool reduce_previous_allocation_to(size_t actual_length); uint16_t get_and_finalise_current_line(); - void release_write_pointer(); uint8_t *get_image_pointer(); uint8_t *get_write_target(); @@ -53,9 +51,7 @@ struct CRTInputBufferBuilder { size_t _bytes_per_pixel; // the buffer - uint8_t *_image; - - bool _should_reset; + std::unique_ptr _image; }; } diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index ba0ec23ea..ceb286884 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -182,11 +182,6 @@ class OpenGLOutputBuilder { return _buffer_builder->get_last_write_y_position(); } - inline void release_source_buffer_write_pointer() - { - _buffer_builder->release_write_pointer(); - } - void draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty); void set_openGL_context_will_change(bool should_delete_resources); void set_composite_sampling_function(const char *shader);