From 254171106181f8b4d0203d4400311fa171663268 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 3 May 2016 20:56:47 -0400 Subject: [PATCH] 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()