diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 683eb8f32..cc6fa648f 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -13,30 +13,6 @@ #include "../../../SignalProcessing/FIRFilter.hpp" #include "Shaders/OutputShader.hpp" -static const GLint internalFormatForDepth(size_t depth) -{ - switch(depth) - { - default: return GL_FALSE; - case 1: return GL_R8UI; - case 2: return GL_RG8UI; - case 3: return GL_RGB8UI; - case 4: return GL_RGBA8UI; - } -} - -static const GLenum formatForDepth(size_t depth) -{ - switch(depth) - { - default: return GL_FALSE; - case 1: return GL_RED_INTEGER; - case 2: return GL_RG_INTEGER; - case 3: return GL_RGB_INTEGER; - case 4: return GL_RGBA_INTEGER; - } -} - struct Range { GLsizei location, length; }; @@ -78,8 +54,7 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : _rgb_shader(nullptr), _last_output_width(0), _last_output_height(0), - _fence(nullptr), - _texture_builder(new TextureBuilder(buffer_depth)) + _fence(nullptr) { _output_buffer.data.resize(OutputVertexBufferDataSize); _source_buffer.data.resize(SourceVertexBufferDataSize); @@ -94,14 +69,8 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : filteredTexture.reset(new OpenGL::TextureTarget(IntermediateBufferWidth, IntermediateBufferHeight, filtered_texture_unit)); // create the surce texture - glGenTextures(1, &textureName); glActiveTexture(source_data_texture_unit); - glBindTexture(GL_TEXTURE_2D, textureName); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - 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(_texture_builder->get_bytes_per_pixel()), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(_texture_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE, nullptr); + _texture_builder.reset(new TextureBuilder(buffer_depth)); // create the output vertex array glGenVertexArrays(1, &output_vertex_array); @@ -122,7 +91,6 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) : OpenGLOutputBuilder::~OpenGLOutputBuilder() { - glDeleteTextures(1, &textureName); glDeleteBuffers(1, &output_array_buffer); glDeleteBuffers(1, &source_array_buffer); glDeleteVertexArrays(1, &output_vertex_array); @@ -190,16 +158,8 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out GLsizei submitted_source_data = submitArrayData(source_array_buffer, _source_buffer.data.data(), &_source_buffer.pointer); // upload new source pixels, if any - uint16_t completed_texture_y = _texture_builder->get_and_finalise_current_line(); - if(completed_texture_y) - { - glActiveTexture(source_data_texture_unit); - glTexSubImage2D( GL_TEXTURE_2D, 0, - 0, 0, - InputBufferBuilderWidth, completed_texture_y, - formatForDepth(_texture_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE, - _texture_builder->get_image_pointer()); - } + glActiveTexture(source_data_texture_unit); + _texture_builder->submit(); // buffer usage restart from 0 for the next time around _composite_src_output_y = 0; diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index 590e6bf18..6f4a7fa00 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -80,7 +80,7 @@ class OpenGLOutputBuilder { unsigned int _last_output_width, _last_output_height; - GLuint textureName, shadowMaskTextureName; + GLuint shadowMaskTextureName; GLuint defaultFramebuffer; diff --git a/Outputs/CRT/Internals/TextureBuilder.cpp b/Outputs/CRT/Internals/TextureBuilder.cpp index c55df69d8..a728bccc1 100644 --- a/Outputs/CRT/Internals/TextureBuilder.cpp +++ b/Outputs/CRT/Internals/TextureBuilder.cpp @@ -8,102 +8,134 @@ #include "TextureBuilder.hpp" #include "CRTOpenGL.hpp" +#include "OpenGL.hpp" #include using namespace Outputs::CRT; -TextureBuilder::TextureBuilder(size_t bytes_per_pixel) : - _bytes_per_pixel(bytes_per_pixel), - _next_write_x_position(0), - _next_write_y_position(0) +static const GLint internalFormatForDepth(size_t depth) { - _image.resize(bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight); + switch(depth) + { + default: return GL_FALSE; + case 1: return GL_R8UI; + case 2: return GL_RG8UI; + case 3: return GL_RGB8UI; + case 4: return GL_RGBA8UI; + } +} + +static const GLenum formatForDepth(size_t depth) +{ + switch(depth) + { + default: return GL_FALSE; + case 1: return GL_RED_INTEGER; + case 2: return GL_RG_INTEGER; + case 3: return GL_RGB_INTEGER; + case 4: return GL_RGBA_INTEGER; + } +} + +TextureBuilder::TextureBuilder(size_t bytes_per_pixel) : + bytes_per_pixel_(bytes_per_pixel), + next_write_x_position_(0), + next_write_y_position_(0) +{ + image_.resize(bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight); + glGenTextures(1, &texture_name_); + glBindTexture(GL_TEXTURE_2D, texture_name_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + 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(bytes_per_pixel), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(bytes_per_pixel), GL_UNSIGNED_BYTE, nullptr); +} + +TextureBuilder::~TextureBuilder() +{ + glDeleteTextures(1, &texture_name_); } uint8_t *TextureBuilder::allocate_write_area(size_t required_length) { - if(_next_write_y_position != InputBufferBuilderHeight) + if(next_write_y_position_ != InputBufferBuilderHeight) { - _last_allocation_amount = 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++; + next_write_x_position_ = 0; + next_write_y_position_++; - if(_next_write_y_position == InputBufferBuilderHeight) + 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; + 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; } else return nullptr; - return &_image[_write_target_pointer * _bytes_per_pixel]; + return &image_[write_target_pointer_ * bytes_per_pixel_]; } bool TextureBuilder::is_full() { - return (_next_write_y_position == InputBufferBuilderHeight); + return (next_write_y_position_ == InputBufferBuilderHeight); } void TextureBuilder::reduce_previous_allocation_to(size_t actual_length) { - if(_next_write_y_position == InputBufferBuilderHeight) return; + if(next_write_y_position_ == InputBufferBuilderHeight) return; - uint8_t *const image_pointer = _image.data(); + 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) + 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; + 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; } // 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], - _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_pointer[(_write_target_pointer + actual_length) * _bytes_per_pixel], - &image_pointer[(_write_target_pointer + actual_length - 1) * _bytes_per_pixel], - _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 - _next_write_x_position -= (_last_allocation_amount - actual_length); -} - -uint8_t *TextureBuilder::get_image_pointer() -{ - return _image.data(); -} - -uint16_t TextureBuilder::get_and_finalise_current_line() -{ - uint16_t result = _write_y_position + (_next_write_x_position ? 1 : 0); - _next_write_x_position = _next_write_y_position = 0; - return result; + next_write_x_position_ -= (last_allocation_amount_ - actual_length); } uint16_t TextureBuilder::get_last_write_x_position() { - return _write_x_position; + return write_x_position_; } uint16_t TextureBuilder::get_last_write_y_position() { - return _write_y_position; + return write_y_position_; } -size_t TextureBuilder::get_bytes_per_pixel() +void TextureBuilder::submit() { - return _bytes_per_pixel; + uint16_t height = write_y_position_ + (next_write_x_position_ ? 1 : 0); + next_write_x_position_ = next_write_y_position_ = 0; + + glTexSubImage2D( GL_TEXTURE_2D, 0, + 0, 0, + InputBufferBuilderWidth, height, + formatForDepth(bytes_per_pixel_), GL_UNSIGNED_BYTE, + image_.data()); } diff --git a/Outputs/CRT/Internals/TextureBuilder.hpp b/Outputs/CRT/Internals/TextureBuilder.hpp index 26ad25b51..d00f648ab 100644 --- a/Outputs/CRT/Internals/TextureBuilder.hpp +++ b/Outputs/CRT/Internals/TextureBuilder.hpp @@ -26,8 +26,10 @@ namespace CRT { */ class TextureBuilder { public: - /// Constructs an instance of InputTextureBuilder that contains a texture of colour depth @c bytes_per_pixel. + /// Constructs an instance of InputTextureBuilder that contains a texture of colour depth @c bytes_per_pixel; + /// this creates a new texture and binds it to the current active texture unit. TextureBuilder(size_t bytes_per_pixel); + virtual ~TextureBuilder(); /// Finds the first available space of at least @c required_length pixels in size. Calls must be paired off /// with calls to @c reduce_previous_allocation_to. @@ -38,42 +40,36 @@ class TextureBuilder { /// and indicates that its actual final size was @c actual_length. void reduce_previous_allocation_to(size_t actual_length); - /// @returns the row that was the final one to receive data; also resets the builder to restart filling of - /// the texture from row 0. - uint16_t get_and_finalise_current_line(); - - /// @returns a pointer to the image data for this texture. - uint8_t *get_image_pointer(); - /// @returns the start column for the most recent allocated write area. uint16_t get_last_write_x_position(); /// @returns the row of the most recent allocated write area. uint16_t get_last_write_y_position(); - /// @returns the number of bytes per pixel as supplied to the constructor. - size_t get_bytes_per_pixel(); - /// @returns @c true if all future calls to @c allocate_write_area will fail on account of the input texture /// being full; @c false if calls may succeed. bool is_full(); + /// Updates the currently-bound texture with all new data provided since the last @c submit. + void submit(); + 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; + 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; + uint16_t write_x_position_, write_y_position_; // details of the most recent allocation - size_t _write_target_pointer; - size_t _last_allocation_amount; + size_t write_target_pointer_; + size_t last_allocation_amount_; // the buffer size - size_t _bytes_per_pixel; + size_t bytes_per_pixel_; // the buffer - std::vector _image; + std::vector image_; + GLuint texture_name_; }; }