From f621cc8523999435cd467b72eae2456f91fb2eb9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 4 May 2016 21:27:10 -0400 Subject: [PATCH] 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);