1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-06 01:28:57 +00:00

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.

This commit is contained in:
Thomas Harte 2016-05-04 21:27:10 -04:00
parent 2e7ac0e6db
commit f621cc8523
5 changed files with 79 additions and 114 deletions

View File

@ -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;
}

View File

@ -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;
};
}

View File

@ -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<OpenGL::TextureTarget> new_framebuffer = std::unique_ptr<OpenGL::TextureTarget>(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();
}

View File

@ -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;
};
}

View File

@ -159,7 +159,7 @@ std::unique_ptr<IntermediateShader> 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);