1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 23:52:26 +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 {
.type = Scan::Type::Level,
.number_of_cycles = number_of_cycles,
.tex_x = _openGL_output_builder->get_last_write_x_posiiton(),
.tex_y = _openGL_output_builder->get_last_write_y_posiiton()
.tex_x = _openGL_output_builder->get_last_write_x_posititon(),
.tex_y = _openGL_output_builder->get_last_write_y_posititon()
};
output_scan(&scan);
}
@ -316,8 +316,8 @@ void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider
Scan scan {
.type = Scan::Type::Data,
.number_of_cycles = number_of_cycles,
.tex_x = _openGL_output_builder->get_last_write_x_posiiton(),
.tex_y = _openGL_output_builder->get_last_write_y_posiiton(),
.tex_x = _openGL_output_builder->get_last_write_x_posititon(),
.tex_y = _openGL_output_builder->get_last_write_y_posititon(),
.source_divider = source_divider
};
output_scan(&scan);

View File

@ -240,18 +240,6 @@ class CRT {
_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)
{
_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),
_next_write_x_position(0),
_next_write_y_position(0),
last_uploaded_line(0),
_wraparound_sync(glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0))
last_uploaded_line(0)
{}
CRTInputBufferBuilder::~CRTInputBufferBuilder()
{
glDeleteSync(_wraparound_sync);
}
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)
{
move_to_new_line();
_next_write_x_position = 0;
_next_write_y_position++;
}
_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;
_write_target_pointer = ((_write_y_position % InputBufferBuilderHeight) * InputBufferBuilderWidth) + _write_x_position;
_next_write_x_position += required_length + 2;
}

View File

@ -25,30 +25,52 @@ struct CRTInputBufferBuilder {
void allocate_write_area(size_t required_length);
void reduce_previous_allocation_to(size_t actual_length, uint8_t *buffer);
// a pointer to the section of content buffer currently being
// 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()
inline uint16_t get_and_finalise_current_line()
{
uint16_t result = _write_y_position;
_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)
{
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_MAG_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
glGenBuffers(1, &_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);
// map the buffer for clients
@ -205,11 +205,48 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
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
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);
// bind and flush the 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);
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
@ -233,26 +270,14 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _input_texture_array);
}
// upload more source pixel data if any; we'll always resubmit the last line submitted last
// time as it may have had extra data appended to it
if(_buffer_builder->_write_y_position < _buffer_builder->last_uploaded_line)
// upload new source pixels
for(int c = 0; c < number_of_texture_upload_zones; c++)
{
glTexSubImage2D( GL_TEXTURE_2D, 0,
0, (GLint)_buffer_builder->last_uploaded_line,
InputBufferBuilderWidth, (GLint)(InputBufferBuilderHeight - _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 = 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;
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()));
}
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;
// 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
glBindVertexArray(source_vertex_array);
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)
{
// switch to the initial texture
@ -323,9 +331,9 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
}
// 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++;
@ -335,22 +343,7 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
// transfer to framebuffer
framebuffer->bind_framebuffer();
// draw all pending lines
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)
if(number_of_output_drawing_zones)
{
glEnable(GL_BLEND);
@ -367,9 +360,9 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
output_shader_program->bind();
// 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);
_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();
}

View File

@ -157,14 +157,14 @@ class OpenGLOutputBuilder {
_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);
@ -186,6 +186,8 @@ 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

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