mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29:42 +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:
parent
fc1a67c157
commit
2541711061
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user