1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 23:52:26 +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)
{
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<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)
{
(*(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) :
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<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
#include <cstdint>
#include <functional>
#include <memory>
#include <vector>
@ -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<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:
// 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<uint8_t> image_;
GLuint texture_name_;
// the current list of write areas
std::vector<WriteArea> 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_;
};