mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-26 09:29:45 +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:
parent
2e7ac0e6db
commit
f621cc8523
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user