1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-23 03:32:32 +00:00

Made attempts (i) to flush buffers before unmapping them; and (ii) to bring texture uploads within the new orthodoxy.

This commit is contained in:
Thomas Harte 2016-05-03 20:56:47 -04:00
parent fc1a67c157
commit 2541711061
7 changed files with 106 additions and 100 deletions

View File

@ -293,8 +293,8 @@ void CRT::output_level(unsigned int number_of_cycles)
Scan scan { Scan scan {
.type = Scan::Type::Level, .type = Scan::Type::Level,
.number_of_cycles = number_of_cycles, .number_of_cycles = number_of_cycles,
.tex_x = _openGL_output_builder->get_last_write_x_posiiton(), .tex_x = _openGL_output_builder->get_last_write_x_posititon(),
.tex_y = _openGL_output_builder->get_last_write_y_posiiton() .tex_y = _openGL_output_builder->get_last_write_y_posititon()
}; };
output_scan(&scan); output_scan(&scan);
} }
@ -316,8 +316,8 @@ void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider
Scan scan { Scan scan {
.type = Scan::Type::Data, .type = Scan::Type::Data,
.number_of_cycles = number_of_cycles, .number_of_cycles = number_of_cycles,
.tex_x = _openGL_output_builder->get_last_write_x_posiiton(), .tex_x = _openGL_output_builder->get_last_write_x_posititon(),
.tex_y = _openGL_output_builder->get_last_write_y_posiiton(), .tex_y = _openGL_output_builder->get_last_write_y_posititon(),
.source_divider = source_divider .source_divider = source_divider
}; };
output_scan(&scan); output_scan(&scan);

View File

@ -240,18 +240,6 @@ class CRT {
_openGL_output_builder->set_rgb_sampling_function(shader); _openGL_output_builder->set_rgb_sampling_function(shader);
} }
/*! Optionally sets a function that will map from an input cycle count to a colour carrier phase.
If this function is not supplied then the colour phase is determined from
the input clock rate and the the colour cycle clock rate. Machines whose per-line clock rate
is not intended exactly to match the normal line time may prefer to supply a custom function.
@param A GLSL fragent including a function with the signature
`float phase_for_clock_cycle(int cycle)` that returns the colour phase at the beginning of
the supplied cycle.
*/
// void set_phase_function(const char *shader);
inline void set_output_device(OutputDevice output_device) inline void set_output_device(OutputDevice output_device)
{ {
_openGL_output_builder->set_output_device(output_device); _openGL_output_builder->set_output_device(output_device);

View File

@ -16,13 +16,11 @@ CRTInputBufferBuilder::CRTInputBufferBuilder(size_t bytes_per_pixel) :
bytes_per_pixel(bytes_per_pixel), bytes_per_pixel(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)
_wraparound_sync(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0))
{} {}
CRTInputBufferBuilder::~CRTInputBufferBuilder() CRTInputBufferBuilder::~CRTInputBufferBuilder()
{ {
glDeleteSync(_wraparound_sync);
} }
void CRTInputBufferBuilder::allocate_write_area(size_t required_length) void CRTInputBufferBuilder::allocate_write_area(size_t required_length)
@ -31,12 +29,13 @@ void CRTInputBufferBuilder::allocate_write_area(size_t required_length)
if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth) if(_next_write_x_position + required_length + 2 > InputBufferBuilderWidth)
{ {
move_to_new_line(); _next_write_x_position = 0;
_next_write_y_position++;
} }
_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 * InputBufferBuilderWidth) + _write_x_position; _write_target_pointer = ((_write_y_position % InputBufferBuilderHeight) * InputBufferBuilderWidth) + _write_x_position;
_next_write_x_position += required_length + 2; _next_write_x_position += required_length + 2;
} }

View File

@ -25,30 +25,52 @@ struct CRTInputBufferBuilder {
void allocate_write_area(size_t required_length); void allocate_write_area(size_t required_length);
void reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer); void reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer);
// a pointer to the section of content buffer currently being inline uint16_t get_and_finalise_current_line()
// returned and to where the next section will begin
uint16_t _next_write_x_position, _next_write_y_position;
uint16_t _write_x_position, _write_y_position;
size_t _write_target_pointer;
size_t _last_allocation_amount;
size_t bytes_per_pixel;
// Storage for the amount of buffer uploaded so far; initialised correctly by the buffer
// builder but otherwise entrusted to the CRT to update.
unsigned int last_uploaded_line;
GLsync _wraparound_sync;
inline void move_to_new_line()
{ {
uint16_t result = _write_y_position;
_next_write_x_position = 0; _next_write_x_position = 0;
_next_write_y_position = (_next_write_y_position+1)%InputBufferBuilderHeight; _next_write_y_position++;
_next_write_y_position %= InputBufferBuilderHeight;
return result;
} }
inline uint8_t *get_write_target(uint8_t *buffer) inline uint8_t *get_write_target(uint8_t *buffer)
{ {
return &buffer[_write_target_pointer * bytes_per_pixel]; return &buffer[_write_target_pointer * bytes_per_pixel];
} }
inline uint16_t get_last_write_x_position()
{
return _write_x_position;
}
inline uint16_t get_last_write_y_position()
{
return _write_y_position;
}
inline size_t get_bytes_per_pixel()
{
return bytes_per_pixel;
}
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;
// the most recent position returned for pixel data writing
uint16_t _write_x_position, _write_y_position;
// details of the most recent allocation
size_t _write_target_pointer;
size_t _last_allocation_amount;
// the buffer size
size_t bytes_per_pixel;
// Storage for the amount of buffer uploaded so far; initialised correctly by the buffer
// builder but otherwise entrusted to the CRT to update.
unsigned int last_uploaded_line;
}; };
} }

View File

@ -118,12 +118,12 @@ OpenGLOutputBuilder::OpenGLOutputBuilder(unsigned int buffer_depth) :
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 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_MAG_FILTER, GL_NEAREST);
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->bytes_per_pixel), InputBufferBuilderWidth, InputBufferBuilderHeight, 0, formatForDepth(_buffer_builder->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 // create a pixel unpack buffer
glGenBuffers(1, &_input_texture_array); glGenBuffers(1, &_input_texture_array);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array);
_input_texture_array_size = (GLsizeiptr)(InputBufferBuilderWidth * InputBufferBuilderHeight * _buffer_builder->bytes_per_pixel); _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); glBufferData(GL_PIXEL_UNPACK_BUFFER, _input_texture_array_size, NULL, GL_STREAM_DRAW);
// map the buffer for clients // map the buffer for clients
@ -205,11 +205,48 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
defaultFramebuffer = 0; defaultFramebuffer = 0;
} }
// 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];
int number_of_clearing_zones = getCircularRanges(_cleared_composite_output_y+1, _composite_src_output_y+1, 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;
_output_buffer_data_pointer %= OutputVertexBufferDataSize;
_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;
// release the mapping, giving up on trying to draw if data has been lost // release the mapping, giving up on trying to draw if data has been lost
glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer); glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer);
for(int c = 0; c < number_of_output_drawing_zones; c++)
{
glFlushMappedBufferRange(GL_ARRAY_BUFFER, output_drawing_zones[c*2], output_drawing_zones[c*2 + 1]);
}
glUnmapBuffer(GL_ARRAY_BUFFER); glUnmapBuffer(GL_ARRAY_BUFFER);
// bind and flush the source array buffer
glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer); glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer);
for(int c = 0; c < number_of_source_drawing_zones; c++)
{
glFlushMappedBufferRange(GL_ARRAY_BUFFER, source_drawing_zones[c*2], source_drawing_zones[c*2 + 1]);
}
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); glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
// make sure there's a target to draw to // make sure there's a target to draw to
@ -233,26 +270,14 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array);
} }
// upload more source pixel data if any; we'll always resubmit the last line submitted last // upload new source pixels
// time as it may have had extra data appended to it for(int c = 0; c < number_of_texture_upload_zones; c++)
if(_buffer_builder->_write_y_position < _buffer_builder->last_uploaded_line)
{ {
glTexSubImage2D( GL_TEXTURE_2D, 0, glTexSubImage2D( GL_TEXTURE_2D, 0,
0, (GLint)_buffer_builder->last_uploaded_line, 0, texture_upload_zones[c*2],
InputBufferBuilderWidth, (GLint)(InputBufferBuilderHeight - _buffer_builder->last_uploaded_line), InputBufferBuilderWidth, texture_upload_zones[c*2 + 1],
formatForDepth(_buffer_builder->bytes_per_pixel), GL_UNSIGNED_BYTE, formatForDepth(_buffer_builder->get_bytes_per_pixel()), GL_UNSIGNED_BYTE,
(void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->bytes_per_pixel)); (void *)((size_t)texture_upload_zones[c*2] * InputBufferBuilderWidth * _buffer_builder->get_bytes_per_pixel()));
_buffer_builder->last_uploaded_line = 0;
}
if(_buffer_builder->_write_y_position > _buffer_builder->last_uploaded_line)
{
glTexSubImage2D( GL_TEXTURE_2D, 0,
0, (GLint)_buffer_builder->last_uploaded_line,
InputBufferBuilderWidth, (GLint)(1 + _buffer_builder->_next_write_y_position - _buffer_builder->last_uploaded_line),
formatForDepth(_buffer_builder->bytes_per_pixel), GL_UNSIGNED_BYTE,
(void *)(_buffer_builder->last_uploaded_line * InputBufferBuilderWidth * _buffer_builder->bytes_per_pixel));
_buffer_builder->last_uploaded_line = _buffer_builder->_next_write_y_position;
} }
struct RenderStage { struct RenderStage {
@ -280,29 +305,12 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
RenderStage *active_pipeline = (_output_device == Television || !rgb_input_shader_program) ? composite_render_stages : rgb_render_stages; RenderStage *active_pipeline = (_output_device == Television || !rgb_input_shader_program) ? composite_render_stages : rgb_render_stages;
// for television, update intermediate buffers and then draw; for a monitor, just draw // for television, update intermediate buffers and then draw; for a monitor, just draw
if(_drawn_source_buffer_data_pointer != _source_buffer_data_pointer) if(number_of_source_drawing_zones)
{ {
// determine how many lines are newly reclaimed; they'll need to be cleared
GLsizei clearing_zones[4], drawing_zones[4];
int number_of_clearing_zones = getCircularRanges(_cleared_composite_output_y+1, _composite_src_output_y+1, IntermediateBufferHeight, 1, clearing_zones);
int number_of_drawing_zones = getCircularRanges(_drawn_source_buffer_data_pointer, _source_buffer_data_pointer, SourceVertexBufferDataSize, 2*SourceVertexSize, drawing_zones);
_composite_src_output_y %= IntermediateBufferHeight;
_cleared_composite_output_y = _composite_src_output_y;
_source_buffer_data_pointer %= SourceVertexBufferDataSize;
_drawn_source_buffer_data_pointer = _source_buffer_data_pointer;
// all drawing will be from the source vertex array and without blending // all drawing will be from the source vertex array and without blending
glBindVertexArray(source_vertex_array); glBindVertexArray(source_vertex_array);
glDisable(GL_BLEND); glDisable(GL_BLEND);
// flush the source data
glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer);
for(int c = 0; c < number_of_drawing_zones; c++)
{
glFlushMappedBufferRange(GL_ARRAY_BUFFER, drawing_zones[c*2] / SourceVertexSize, drawing_zones[c*2 + 1] / SourceVertexSize);
}
while(active_pipeline->target) while(active_pipeline->target)
{ {
// switch to the initial texture // switch to the initial texture
@ -323,9 +331,9 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
} }
// draw as desired // draw as desired
for(int c = 0; c < number_of_drawing_zones; c++) for(int c = 0; c < number_of_source_drawing_zones; c++)
{ {
glDrawArrays(GL_LINES, drawing_zones[c*2] / SourceVertexSize, drawing_zones[c*2 + 1] / SourceVertexSize); glDrawArrays(GL_LINES, source_drawing_zones[c*2] / SourceVertexSize, source_drawing_zones[c*2 + 1] / SourceVertexSize);
} }
active_pipeline++; active_pipeline++;
@ -335,22 +343,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
// transfer to framebuffer // transfer to framebuffer
framebuffer->bind_framebuffer(); framebuffer->bind_framebuffer();
// draw all pending lines if(number_of_output_drawing_zones)
GLsizei drawing_zones[4];
int number_of_drawing_zones = getCircularRanges(_drawn_output_buffer_data_pointer, _output_buffer_data_pointer, OutputVertexBufferDataSize, 6*OutputVertexSize, drawing_zones);
// flush the buffer data
glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer);
for(int c = 0; c < number_of_drawing_zones; c++)
{
glFlushMappedBufferRange(GL_ARRAY_BUFFER, drawing_zones[c*2] / OutputVertexSize, drawing_zones[c*2 + 1] / OutputVertexSize);
}
_output_buffer_data_pointer %= SourceVertexBufferDataSize;
_output_buffer_data_pointer -= (_output_buffer_data_pointer%(6*OutputVertexSize));
_drawn_output_buffer_data_pointer = _output_buffer_data_pointer;
if(number_of_drawing_zones > 0)
{ {
glEnable(GL_BLEND); glEnable(GL_BLEND);
@ -367,9 +360,9 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
output_shader_program->bind(); output_shader_program->bind();
// draw // draw
for(int c = 0; c < number_of_drawing_zones; c++) for(int c = 0; c < number_of_output_drawing_zones; c++)
{ {
glDrawArrays(GL_TRIANGLE_STRIP, drawing_zones[c*2] / OutputVertexSize, drawing_zones[c*2 + 1] / OutputVertexSize); glDrawArrays(GL_TRIANGLE_STRIP, output_drawing_zones[c*2] / OutputVertexSize, output_drawing_zones[c*2 + 1] / OutputVertexSize);
} }
} }
@ -387,7 +380,7 @@ 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); _input_texture_data = (uint8_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, _input_texture_array_size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT );
_output_mutex->unlock(); _output_mutex->unlock();
} }

View File

@ -157,14 +157,14 @@ class OpenGLOutputBuilder {
_buffer_builder->reduce_previous_allocation_to(actual_length, _input_texture_data); _buffer_builder->reduce_previous_allocation_to(actual_length, _input_texture_data);
} }
inline uint16_t get_last_write_x_posiiton() inline uint16_t get_last_write_x_posititon()
{ {
return _buffer_builder->_write_x_position; return _buffer_builder->get_last_write_x_position();
} }
inline uint16_t get_last_write_y_posiiton() inline uint16_t get_last_write_y_posititon()
{ {
return _buffer_builder->_write_y_position; return _buffer_builder->get_last_write_y_position();
} }
void draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty); void draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty);
@ -186,6 +186,8 @@ 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

@ -37,6 +37,8 @@ TextureTarget::TextureTarget(GLsizei width, GLsizei height, GLenum texture_unit)
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
throw ErrorFramebufferIncomplete; throw ErrorFramebufferIncomplete;
glClear(GL_COLOR_BUFFER_BIT);
} }
TextureTarget::~TextureTarget() TextureTarget::~TextureTarget()