From 0fee8096c1af53c23f19edcd1f3b8a56873648d8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 6 Dec 2016 07:24:07 -0500 Subject: [PATCH] Made an attempt to shuffle the texture builder to a similar flush/submit pattern as the input builder. Don't care about thread safety yet, as it's obvious I'm going to need to move that back up to the CRT. --- Outputs/CRT/CRT.cpp | 16 ++- Outputs/CRT/Internals/TextureBuilder.cpp | 152 +++++++++++++++-------- Outputs/CRT/Internals/TextureBuilder.hpp | 18 +-- 3 files changed, 123 insertions(+), 63 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index ec09e64db..e1a75b3f5 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -136,8 +136,8 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi if(next_run) { - source_input_position_x1() = tex_x; - source_input_position_y() = tex_y; +// source_input_position_x1() = tex_x; +// source_input_position_y() = tex_y; source_output_position_x1() = (uint16_t)horizontal_flywheel_->get_current_output_position(); // Don't write output_y now, write it later; we won't necessarily know what it is outside of the locked region source_phase() = colour_burst_phase_; @@ -204,8 +204,18 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi output_x2() = (uint16_t)horizontal_flywheel_->get_current_output_position(); } openGL_output_builder_.array_builder.flush( - [output_y] (uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size) + [output_y, this] (uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size) { + openGL_output_builder_.texture_builder.flush( + [output_y, input_buffer] (const std::vector &write_areas, size_t number_of_write_areas) + { + for(size_t run = 0; run < number_of_write_areas; run++) + { + *(uint16_t *)&input_buffer[run * SourceVertexSize + SourceVertexOffsetOfInputStart + 0] = write_areas[run].x; + *(uint16_t *)&input_buffer[run * SourceVertexSize + SourceVertexOffsetOfInputStart + 2] = write_areas[run].y; + *(uint16_t *)&input_buffer[run * SourceVertexSize + SourceVertexOffsetOfEnds + 0] = write_areas[run].x + write_areas[run].length; + } + }); for(size_t position = 0; position < input_size; position += SourceVertexSize) { (*(uint16_t *)&input_buffer[position + SourceVertexOffsetOfOutputStart + 2]) = output_y; diff --git a/Outputs/CRT/Internals/TextureBuilder.cpp b/Outputs/CRT/Internals/TextureBuilder.cpp index 1c7bf8f1f..28ed44857 100644 --- a/Outputs/CRT/Internals/TextureBuilder.cpp +++ b/Outputs/CRT/Internals/TextureBuilder.cpp @@ -39,8 +39,10 @@ static const GLenum formatForDepth(size_t depth) TextureBuilder::TextureBuilder(size_t bytes_per_pixel, GLenum texture_unit) : bytes_per_pixel_(bytes_per_pixel), - next_write_x_position_(0), - next_write_y_position_(0) + write_areas_start_x_(0), + write_areas_start_y_(0), + is_full_(false), + did_submit_(false) { image_.resize(bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight); glGenTextures(1, &texture_name_); @@ -59,81 +61,81 @@ TextureBuilder::~TextureBuilder() glDeleteTextures(1, &texture_name_); } +inline uint8_t *TextureBuilder::pointer_to_location(uint16_t x, uint16_t y) +{ + return &image_[((y * InputBufferBuilderWidth) + x) * bytes_per_pixel_]; +} + uint8_t *TextureBuilder::allocate_write_area(size_t required_length) { - if(next_write_y_position_ != InputBufferBuilderHeight) + if(is_full_) return nullptr; + + uint16_t starting_x, starting_y; + + if(!number_of_write_areas_) { - last_allocation_amount_ = required_length; - - if(next_write_x_position_ + required_length + 2 > InputBufferBuilderWidth) - { - next_write_x_position_ = 0; - next_write_y_position_++; - - if(next_write_y_position_ == InputBufferBuilderHeight) - return nullptr; - } - - 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_; - next_write_x_position_ += required_length + 2; + starting_x = write_areas_start_x_; + starting_y = write_areas_start_y_; + } + else + { + starting_x = write_areas_[number_of_write_areas_ - 1].x + write_areas_[number_of_write_areas_ - 1].length + 1; + starting_y = write_areas_[number_of_write_areas_ - 1].y; } - else return nullptr; - return &image_[write_target_pointer_ * bytes_per_pixel_]; + WriteArea next_write_area; + if(starting_x + required_length + 2 > InputBufferBuilderWidth) + { + starting_x = 0; + starting_y++; + + if(starting_y == InputBufferBuilderHeight) + { + is_full_ = true; + return nullptr; + } + } + + next_write_area.x = starting_x + 1; + next_write_area.y = starting_y; + next_write_area.length = (uint16_t)required_length; + if(number_of_write_areas_ < write_areas_.size()) + write_areas_[number_of_write_areas_] = next_write_area; + else + write_areas_.push_back(next_write_area); + number_of_write_areas_++; + + return pointer_to_location(next_write_area.x, next_write_area.y); } bool TextureBuilder::is_full() { - return (next_write_y_position_ == InputBufferBuilderHeight); + return is_full_; } void TextureBuilder::reduce_previous_allocation_to(size_t actual_length) { - if(next_write_y_position_ == InputBufferBuilderHeight) return; + if(is_full_) return; - uint8_t *const image_pointer = image_.data(); - - // correct if the writing cursor was reset while a client was writing - if(next_write_x_position_ == 0 && next_write_y_position_ == 0) - { - memmove(&image_pointer[bytes_per_pixel_], &image_pointer[write_target_pointer_ * bytes_per_pixel_], actual_length * bytes_per_pixel_); - write_target_pointer_ = 1; - last_allocation_amount_ = actual_length; - next_write_x_position_ = (uint16_t)(actual_length + 2); - write_x_position_ = 1; - write_y_position_ = 0; - } + WriteArea &write_area = write_areas_[number_of_write_areas_-1]; + write_area.length = (uint16_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_pointer[(write_target_pointer_ - 1) * bytes_per_pixel_], - &image_pointer[write_target_pointer_ * bytes_per_pixel_], + uint8_t *start_pointer = pointer_to_location(write_area.x, write_area.y); + memcpy( &start_pointer[-bytes_per_pixel_], + start_pointer, 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_], + memcpy( &start_pointer[actual_length * bytes_per_pixel_], + &start_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); -} - -uint16_t TextureBuilder::get_last_write_x_position() -{ - return write_x_position_; -} - -uint16_t TextureBuilder::get_last_write_y_position() -{ - return write_y_position_; } void TextureBuilder::submit() { - uint16_t height = write_y_position_ + (next_write_x_position_ ? 1 : 0); - next_write_x_position_ = next_write_y_position_ = 0; + uint16_t height = write_areas_start_y_ + (write_areas_start_x_ ? 1 : 0); + did_submit_ = true; glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, @@ -141,3 +143,47 @@ void TextureBuilder::submit() formatForDepth(bytes_per_pixel_), GL_UNSIGNED_BYTE, image_.data()); } + +void TextureBuilder::flush(const std::function &write_areas, size_t count)> &function) +{ + bool was_full = is_full_; + if(did_submit_) + { + write_areas_start_y_ = write_areas_start_x_ = 0; + is_full_ = false; + } + + if(number_of_write_areas_ && !was_full) + { + if(write_areas_[0].x != write_areas_start_x_ || write_areas_[0].y != write_areas_start_y_) + { + for(auto write_area : write_areas_) + { + if(write_areas_start_x_ + write_area.length + 2 > InputBufferBuilderWidth) + { + write_areas_start_x_ = 0; + write_areas_start_y_ ++; + + if(write_areas_start_y_ == InputBufferBuilderHeight) + { + is_full_ = true; + break; + } + } + + memmove( + pointer_to_location(write_areas_start_x_, write_areas_start_y_), + pointer_to_location(write_area.x - 1, write_area.y), + (write_area.length + 2) * bytes_per_pixel_); + write_area.x = write_areas_start_x_ + 1; + write_area.y = write_areas_start_y_; + } + } + + if(!is_full_) + function(write_areas_, number_of_write_areas_); + } + + did_submit_ = false; + number_of_write_areas_ = 0; +} diff --git a/Outputs/CRT/Internals/TextureBuilder.hpp b/Outputs/CRT/Internals/TextureBuilder.hpp index 211caedde..43b4a7b3a 100644 --- a/Outputs/CRT/Internals/TextureBuilder.hpp +++ b/Outputs/CRT/Internals/TextureBuilder.hpp @@ -10,6 +10,7 @@ #define Outputs_CRT_Internals_TextureBuilder_hpp #include +#include #include #include @@ -48,16 +49,11 @@ class TextureBuilder { void submit(); struct WriteArea { - uint16_t x, y; - size_t length; + uint16_t x, y, length; }; - void flush(std::function &write_areas, size_t count)>); + void flush(const std::function &write_areas, size_t count)> &); private: - // details of the most recent allocation - size_t write_target_pointer_; - size_t last_allocation_amount_; - // the buffer size size_t bytes_per_pixel_; @@ -65,8 +61,16 @@ class TextureBuilder { std::vector image_; GLuint texture_name_; + // the current list of write areas std::vector write_areas_; size_t number_of_write_areas_; + bool is_full_; + bool did_submit_; + inline uint8_t *pointer_to_location(uint16_t x, uint16_t y); + + // Usually: the start position for the current batch of write areas. + // Caveat: reset to the origin upon a submit. So used in comparison by flush to + // determine whether the current batch of write areas needs to be relocated. uint16_t write_areas_start_x_, write_areas_start_y_; };