1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-30 23:29:08 +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_x_position(0),
_next_write_y_position(0), _next_write_y_position(0),
_last_uploaded_line(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) void CRTInputBufferBuilder::allocate_write_area(size_t required_length)
{ {
if(!_is_full) 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_x_position = _next_write_x_position + 1;
_write_y_position = _next_write_y_position; _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; _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; if(_is_full) return false;
// 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( &buffer[(_write_target_pointer - 1) * _bytes_per_pixel], memcpy( &_image[(_write_target_pointer - 1) * _bytes_per_pixel],
&buffer[_write_target_pointer * _bytes_per_pixel], &_image[_write_target_pointer * _bytes_per_pixel],
_bytes_per_pixel); _bytes_per_pixel);
memcpy( &buffer[(_write_target_pointer + actual_length) * _bytes_per_pixel], memcpy( &_image[(_write_target_pointer + actual_length) * _bytes_per_pixel],
&buffer[(_write_target_pointer + actual_length - 1) * _bytes_per_pixel], &_image[(_write_target_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 // 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; 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 { struct CRTInputBufferBuilder {
CRTInputBufferBuilder(size_t bytes_per_pixel); CRTInputBufferBuilder(size_t bytes_per_pixel);
~CRTInputBufferBuilder();
void allocate_write_area(size_t required_length); 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 get_and_finalise_current_line();
{ uint8_t *get_image_pointer();
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;
}
inline uint8_t *get_write_target(uint8_t *buffer) uint8_t *get_write_target();
{
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];
}
inline uint16_t get_last_write_x_position() uint16_t get_last_write_x_position();
{
return _write_x_position;
}
inline uint16_t get_last_write_y_position() uint16_t get_last_write_y_position();
{
return _write_y_position % InputBufferBuilderHeight;
}
inline size_t get_bytes_per_pixel() size_t get_bytes_per_pixel();
{
return _bytes_per_pixel;
}
private: private:
// where pixel data will be put to the next time a write is requested // 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. // builder but otherwise entrusted to the CRT to update.
unsigned int _last_uploaded_line; unsigned int _last_uploaded_line;
bool _is_full; bool _is_full;
uint8_t *_image;
}; };
} }

View File

@ -88,7 +88,6 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
_rgb_shader(nullptr), _rgb_shader(nullptr),
_output_buffer_data(nullptr), _output_buffer_data(nullptr),
_source_buffer_data(nullptr), _source_buffer_data(nullptr),
_input_texture_data(nullptr),
_output_buffer_data_pointer(0), _output_buffer_data_pointer(0),
_drawn_output_buffer_data_pointer(0), _drawn_output_buffer_data_pointer(0),
_source_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); 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); 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 // create the output vertex array
glGenVertexArrays(1, &output_vertex_array); glGenVertexArrays(1, &output_vertex_array);
@ -165,7 +155,6 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder()
glUnmapBuffer(GL_ARRAY_BUFFER); glUnmapBuffer(GL_ARRAY_BUFFER);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glDeleteTextures(1, &textureName); glDeleteTextures(1, &textureName);
glDeleteBuffers(1, &_input_texture_array);
glDeleteBuffers(1, &output_array_buffer); glDeleteBuffers(1, &output_array_buffer);
glDeleteBuffers(1, &source_array_buffer); glDeleteBuffers(1, &source_array_buffer);
glDeleteBuffers(1, &lateral_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 // determine how many lines are newly reclaimed; they'll need to be cleared
GLsizei clearing_zones[4], source_drawing_zones[4]; 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_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_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); 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(); 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; _composite_src_output_y %= IntermediateBufferHeight;
_source_buffer_data_pointer %= SourceVertexBufferDataSize; _source_buffer_data_pointer %= SourceVertexBufferDataSize;
@ -219,13 +207,10 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
_cleared_composite_output_y = _composite_src_output_y; _cleared_composite_output_y = _composite_src_output_y;
_drawn_source_buffer_data_pointer = _source_buffer_data_pointer; _drawn_source_buffer_data_pointer = _source_buffer_data_pointer;
_drawn_output_buffer_data_pointer = _output_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++) 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)); 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]; int offset = source_drawing_zones[c*2 + 0];
uint16_t *base = (uint16_t *)&_source_buffer_data[offset]; uint16_t *base = (uint16_t *)&_source_buffer_data[offset];
printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[10], base[11]); printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[10], base[11]);
@ -233,12 +218,8 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
offset += source_drawing_zones[c*2 + 1] - 2*SourceVertexSize; offset += source_drawing_zones[c*2 + 1] - 2*SourceVertexSize;
base = (uint16_t *)&_source_buffer_data[offset]; base = (uint16_t *)&_source_buffer_data[offset];
printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[10], base[11]); 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]);
} }
printf("tx: + %d\n", completed_texture_y);
for(int c = 0; c < number_of_clearing_zones; c++) for(int c = 0; c < number_of_clearing_zones; c++)
{ {
printf("cl: %d + %d\n", clearing_zones[c*2], clearing_zones[c*2 + 1]); printf("cl: %d + %d\n", clearing_zones[c*2], clearing_zones[c*2 + 1]);
@ -246,8 +227,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
for(int c = 0; c < number_of_output_drawing_zones; c++) 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)); 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]; int offset = output_drawing_zones[c*2 + 0];
uint16_t *base = (uint16_t *)&_output_buffer_data[offset]; uint16_t *base = (uint16_t *)&_output_buffer_data[offset];
printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[14], base[15]); printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[14], base[15]);
@ -255,7 +234,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
offset += output_drawing_zones[c*2 + 1] - 6*OutputVertexSize; offset += output_drawing_zones[c*2 + 1] - 6*OutputVertexSize;
base = (uint16_t *)&_output_buffer_data[offset]; base = (uint16_t *)&_output_buffer_data[offset];
printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[14], base[15]); printf("(%d/%d) -> (%d/%d)\n", base[2], base[3], base[14], base[15]);
// }
} }
printf("\n"); printf("\n");
@ -275,19 +253,9 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
} }
glUnmapBuffer(GL_ARRAY_BUFFER); 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 // make sure there's a target to draw to
if(!framebuffer || framebuffer->get_height() != output_height || framebuffer->get_width() != output_width) 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)); 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) if(framebuffer)
{ {
@ -301,23 +269,17 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
new_framebuffer->bind_texture(); new_framebuffer->bind_texture();
} }
framebuffer = std::move(new_framebuffer); framebuffer = std::move(new_framebuffer);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array);
} }
// upload new source pixels // upload new source pixels
if(number_of_texture_upload_zones) if(completed_texture_y)
{ {
glActiveTexture(source_data_texture_unit); 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); glTexSubImage2D( GL_TEXTURE_2D, 0,
// for(int c = 0; c < number_of_texture_upload_zones; c++) 0, 0,
// { InputBufferBuilderWidth, completed_texture_y,
// glTexSubImage2D( GL_TEXTURE_2D, 0, formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE,
// 0, texture_upload_zones[c*2], _buffer_builder->get_image_pointer());
// 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();
} }
struct RenderStage { struct RenderStage {
@ -421,8 +383,6 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); 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); _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(); _output_mutex->unlock();
} }

View File

@ -145,16 +145,13 @@ class OpenGLOutputBuilder {
inline uint8_t *allocate_write_area(size_t required_length) inline uint8_t *allocate_write_area(size_t required_length)
{ {
_output_mutex->lock();
_buffer_builder->allocate_write_area(required_length); _buffer_builder->allocate_write_area(required_length);
uint8_t *output = _input_texture_data ? _buffer_builder->get_write_target(_input_texture_data) : nullptr; return _buffer_builder->get_write_target();
_output_mutex->unlock();
return output;
} }
inline bool reduce_previous_allocation_to(size_t actual_length) 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() inline uint16_t get_last_write_x_posititon()
@ -174,11 +171,6 @@ class OpenGLOutputBuilder {
void set_output_device(OutputDevice output_device); 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); 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; uint8_t *_source_buffer_data;
GLsizei _source_buffer_data_pointer; GLsizei _source_buffer_data_pointer;
GLsizei _drawn_source_buffer_data_pointer; GLsizei _drawn_source_buffer_data_pointer;
@ -186,8 +178,6 @@ class OpenGLOutputBuilder {
uint8_t *_output_buffer_data; uint8_t *_output_buffer_data;
GLsizei _output_buffer_data_pointer; GLsizei _output_buffer_data_pointer;
GLsizei _drawn_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)" "void main(void)"
"{" "{"
"fragColour = rgb_sample(texID, inputPositionsVarying[5], iInputPositionVarying);" "fragColour = vec3(0.2) + rgb_sample(texID, inputPositionsVarying[5], iInputPositionVarying);"
"}" "}"
, rgb_shader); , rgb_shader);