1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-11 04:28:58 +00:00

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.

This commit is contained in:
Thomas Harte 2016-12-06 07:24:07 -05:00
parent 0edc043378
commit 0fee8096c1
3 changed files with 123 additions and 63 deletions

View File

@ -136,8 +136,8 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
if(next_run) if(next_run)
{ {
source_input_position_x1() = tex_x; // source_input_position_x1() = tex_x;
source_input_position_y() = tex_y; // source_input_position_y() = tex_y;
source_output_position_x1() = (uint16_t)horizontal_flywheel_->get_current_output_position(); 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 // 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_; 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(); output_x2() = (uint16_t)horizontal_flywheel_->get_current_output_position();
} }
openGL_output_builder_.array_builder.flush( 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<TextureBuilder::WriteArea> &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) for(size_t position = 0; position < input_size; position += SourceVertexSize)
{ {
(*(uint16_t *)&input_buffer[position + SourceVertexOffsetOfOutputStart + 2]) = output_y; (*(uint16_t *)&input_buffer[position + SourceVertexOffsetOfOutputStart + 2]) = output_y;

View File

@ -39,8 +39,10 @@ static const GLenum formatForDepth(size_t depth)
TextureBuilder::TextureBuilder(size_t bytes_per_pixel, GLenum texture_unit) : TextureBuilder::TextureBuilder(size_t bytes_per_pixel, GLenum texture_unit) :
bytes_per_pixel_(bytes_per_pixel), bytes_per_pixel_(bytes_per_pixel),
next_write_x_position_(0), write_areas_start_x_(0),
next_write_y_position_(0) write_areas_start_y_(0),
is_full_(false),
did_submit_(false)
{ {
image_.resize(bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight); image_.resize(bytes_per_pixel * InputBufferBuilderWidth * InputBufferBuilderHeight);
glGenTextures(1, &texture_name_); glGenTextures(1, &texture_name_);
@ -59,81 +61,81 @@ TextureBuilder::~TextureBuilder()
glDeleteTextures(1, &texture_name_); 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) 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; starting_x = write_areas_start_x_;
starting_y = write_areas_start_y_;
if(next_write_x_position_ + required_length + 2 > InputBufferBuilderWidth) }
{ else
next_write_x_position_ = 0; {
next_write_y_position_++; 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;
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;
} }
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() bool TextureBuilder::is_full()
{ {
return (next_write_y_position_ == InputBufferBuilderHeight); return is_full_;
} }
void TextureBuilder::reduce_previous_allocation_to(size_t actual_length) 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(); WriteArea &write_area = write_areas_[number_of_write_areas_-1];
write_area.length = (uint16_t)actual_length;
// 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;
}
// book end the allocation with duplicates of the first and last pixel, to protect // book end the allocation with duplicates of the first and last pixel, to protect
// against rounding errors when this run is drawn // against rounding errors when this run is drawn
memcpy( &image_pointer[(write_target_pointer_ - 1) * bytes_per_pixel_], uint8_t *start_pointer = pointer_to_location(write_area.x, write_area.y);
&image_pointer[write_target_pointer_ * bytes_per_pixel_], memcpy( &start_pointer[-bytes_per_pixel_],
start_pointer,
bytes_per_pixel_); bytes_per_pixel_);
memcpy( &image_pointer[(write_target_pointer_ + actual_length) * bytes_per_pixel_], memcpy( &start_pointer[actual_length * bytes_per_pixel_],
&image_pointer[(write_target_pointer_ + actual_length - 1) * bytes_per_pixel_], &start_pointer[(actual_length - 1) * bytes_per_pixel_],
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() void TextureBuilder::submit()
{ {
uint16_t height = write_y_position_ + (next_write_x_position_ ? 1 : 0); uint16_t height = write_areas_start_y_ + (write_areas_start_x_ ? 1 : 0);
next_write_x_position_ = next_write_y_position_ = 0; did_submit_ = true;
glTexSubImage2D( GL_TEXTURE_2D, 0, glTexSubImage2D( GL_TEXTURE_2D, 0,
0, 0, 0, 0,
@ -141,3 +143,47 @@ void TextureBuilder::submit()
formatForDepth(bytes_per_pixel_), GL_UNSIGNED_BYTE, formatForDepth(bytes_per_pixel_), GL_UNSIGNED_BYTE,
image_.data()); image_.data());
} }
void TextureBuilder::flush(const std::function<void(const std::vector<WriteArea> &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;
}

View File

@ -10,6 +10,7 @@
#define Outputs_CRT_Internals_TextureBuilder_hpp #define Outputs_CRT_Internals_TextureBuilder_hpp
#include <cstdint> #include <cstdint>
#include <functional>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -48,16 +49,11 @@ class TextureBuilder {
void submit(); void submit();
struct WriteArea { struct WriteArea {
uint16_t x, y; uint16_t x, y, length;
size_t length;
}; };
void flush(std::function<void(const std::vector<WriteArea> &write_areas, size_t count)>); void flush(const std::function<void(const std::vector<WriteArea> &write_areas, size_t count)> &);
private: private:
// details of the most recent allocation
size_t write_target_pointer_;
size_t last_allocation_amount_;
// the buffer size // the buffer size
size_t bytes_per_pixel_; size_t bytes_per_pixel_;
@ -65,8 +61,16 @@ class TextureBuilder {
std::vector<uint8_t> image_; std::vector<uint8_t> image_;
GLuint texture_name_; GLuint texture_name_;
// the current list of write areas
std::vector<WriteArea> write_areas_; std::vector<WriteArea> write_areas_;
size_t number_of_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_; uint16_t write_areas_start_x_, write_areas_start_y_;
}; };