From 697e50c0cc3af0a96cdbce022c453316bf3d97d0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 13 Feb 2016 23:50:18 -0500 Subject: [PATCH] Screen output is back, no matter how inefficiently. --- Outputs/CRT/CRT.cpp | 12 +++- Outputs/CRT/CRT.hpp | 6 +- Outputs/CRT/CRTBuilders.cpp | 2 + Outputs/CRT/CRTOpenGL.cpp | 126 +++++++++++++++++++++++++----------- Outputs/CRT/CRTOpenGL.hpp | 10 +-- 5 files changed, 109 insertions(+), 47 deletions(-) diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 24838c9bb..fef92d957 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -79,7 +79,8 @@ void CRT::set_new_display_type(unsigned int cycles_per_line, DisplayType display void CRT::allocate_buffers(unsigned int number, va_list sizes) { - for(int builder = 0; builder < sizeof(_run_builders) / sizeof(*_run_builders); builder++) + _run_builders = new CRTRunBuilder *[kCRTNumberOfFrames]; + for(int builder = 0; builder < kCRTNumberOfFrames; builder++) { _run_builders[builder] = new CRTRunBuilder(); } @@ -104,10 +105,11 @@ CRT::CRT() : CRT::~CRT() { - for(int builder = 0; builder < sizeof(_run_builders) / sizeof(*_run_builders); builder++) + for(int builder = 0; builder < kCRTNumberOfFrames; builder++) { delete _run_builders[builder]; } + delete[] _run_builders; destruct_openGL(); } @@ -171,6 +173,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi #define tex_x(v) (*(uint16_t *)&next_run[kCRTSizeOfVertex*v + kCRTVertexOffsetOfTexCoord + 0]) #define tex_y(v) (*(uint16_t *)&next_run[kCRTSizeOfVertex*v + kCRTVertexOffsetOfTexCoord + 2]) #define lateral(v) next_run[kCRTSizeOfVertex*v + kCRTVertexOffsetOfLateral] +#define timestamp(v) (*(uint32_t *)&next_run[kCRTSizeOfVertex*v + kCRTVertexOffsetOfTimestamp]) #define InternalToUInt16(v) ((v) + 32768) >> 16 #define CounterToInternal(c) (unsigned int)(((uint64_t)c->get_current_output_position() * kCRTFixedPointRange) / c->get_scan_period()) @@ -186,6 +189,8 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi position_x(1) = InternalToUInt16(kCRTFixedPointOffset + x_position - _beamWidth[lengthMask].x); position_y(1) = InternalToUInt16(kCRTFixedPointOffset + y_position - _beamWidth[lengthMask].y); + timestamp(0) = timestamp(1) = timestamp(4) = _run_builders[_run_write_pointer]->duration; + tex_x(0) = tex_x(1) = tex_x(4) = tex_x; // these things are constants across the line so just throw them out now @@ -197,6 +202,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi // decrement the number of cycles left to run for and increment the // horizontal counter appropriately number_of_cycles -= next_run_length; + _run_builders[_run_write_pointer]->duration += next_run_length; // either charge or deplete the vertical retrace capacitor (making sure it stops at 0) if (vsync_charging && !_vertical_flywheel->is_in_retrace()) @@ -219,6 +225,8 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi position_x(5) = InternalToUInt16(kCRTFixedPointOffset + x_position + _beamWidth[lengthMask].x); position_y(5) = InternalToUInt16(kCRTFixedPointOffset + y_position + _beamWidth[lengthMask].y); + timestamp(2) = timestamp(3) = timestamp(5) = _run_builders[_run_write_pointer]->duration; + // if this is a data run then advance the buffer pointer if(type == Type::Data && source_divider) tex_x += next_run_length / (_time_multiplier * source_divider); diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index d320f28aa..bfe6463ee 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -295,7 +295,7 @@ class CRT { // Storage for the length of run data uploaded so far; reset to zero by reset but otherwise // entrusted to the CRT to update. - size_t uploaded_run_data; + size_t uploaded_vertices; size_t number_of_vertices; }; @@ -326,9 +326,8 @@ class CRT { }; // the run and input data buffers - static const int kCRTNumberOfFrames = 4; std::unique_ptr _buffer_builder; - CRTRunBuilder *_run_builders[kCRTNumberOfFrames]; + CRTRunBuilder **_run_builders; int _run_write_pointer; std::shared_ptr _output_mutex; @@ -346,6 +345,7 @@ class CRT { // Methods used by the OpenGL code void prepare_shader(); + void prepare_vertex_array(); void push_size_uniforms(unsigned int output_width, unsigned int output_height); char *get_vertex_shader(); diff --git a/Outputs/CRT/CRTBuilders.cpp b/Outputs/CRT/CRTBuilders.cpp index 2ecc499b0..7eebd9433 100644 --- a/Outputs/CRT/CRTBuilders.cpp +++ b/Outputs/CRT/CRTBuilders.cpp @@ -82,6 +82,8 @@ uint8_t *CRT::CRTInputBufferBuilder::get_write_target_for_buffer(int buffer) void CRT::CRTRunBuilder::reset() { number_of_vertices = 0; + uploaded_vertices = 0; + duration = 0; } uint8_t *CRT::CRTRunBuilder::get_next_input_run() diff --git a/Outputs/CRT/CRTOpenGL.cpp b/Outputs/CRT/CRTOpenGL.cpp index 2dd3e30c2..373655ef1 100644 --- a/Outputs/CRT/CRTOpenGL.cpp +++ b/Outputs/CRT/CRTOpenGL.cpp @@ -17,15 +17,15 @@ using namespace Outputs; struct CRT::OpenGLState { std::unique_ptr shaderProgram; - GLuint arrayBuffer, vertexArray; + GLuint arrayBuffers[kCRTNumberOfFrames], vertexArrays[kCRTNumberOfFrames]; GLint positionAttribute; GLint textureCoordinatesAttribute; GLint lateralAttribute; + GLint timestampAttribute; - GLint textureSizeUniform, windowSizeUniform; + GLint windowSizeUniform, timestampBaseUniform; GLint boundsOriginUniform, boundsSizeUniform; - GLint alphaUniform; GLuint textureName, shadowMaskTextureName; @@ -36,7 +36,7 @@ struct CRT::OpenGLState { std::unique_ptr filteredTexture; // receives filtered YIQ or YUV }; -static GLenum formatForDepth(unsigned int depth) +static GLenum formatForDepth(size_t depth) { switch(depth) { @@ -76,13 +76,21 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glGenVertexArrays(1, &_openGL_state->vertexArray); - glBindVertexArray(_openGL_state->vertexArray); - glGenBuffers(1, &_openGL_state->arrayBuffer); - glBindBuffer(GL_ARRAY_BUFFER, _openGL_state->arrayBuffer); + GLenum format = formatForDepth(_buffer_builder->buffers[0].bytes_per_pixel); + glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, CRTInputBufferBuilderWidth, CRTInputBufferBuilderHeight, 0, format, GL_UNSIGNED_BYTE, _buffer_builder->buffers[0].data); + + glGenVertexArrays(kCRTNumberOfFrames, _openGL_state->vertexArrays); + glGenBuffers(kCRTNumberOfFrames, _openGL_state->arrayBuffers); prepare_shader(); + for(int c = 0; c < kCRTNumberOfFrames; c++) + { + glBindBuffer(GL_ARRAY_BUFFER, _openGL_state->arrayBuffers[c]); + glBindVertexArray(_openGL_state->vertexArrays[c]); + prepare_vertex_array(); + } + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&_openGL_state->defaultFramebuffer); // _openGL_state->compositeTexture = std::unique_ptr(new OpenGL::TextureTarget(2048, kCRTFrameIntermediateBufferHeight)); @@ -95,28 +103,56 @@ void CRT::draw_frame(unsigned int output_width, unsigned int output_height, bool // update uniforms push_size_uniforms(output_width, output_height); - glUniform1f(_openGL_state->alphaUniform, 1.0f); - // submit latest frame data if required -/* glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(current_frame->number_of_vertices * current_frame->size_per_vertex), current_frame->vertices, GL_DYNAMIC_DRAW); - - glBindTexture(GL_TEXTURE_2D, _openGL_state->textureName); - if(_openGL_state->textureSize.width != _current_frame->size.width || _openGL_state->textureSize.height != _current_frame->size.height) - { - GLenum format = formatForDepth(_current_frame->buffers[0].depth); - glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, _current_frame->size.width, _current_frame->size.height, 0, format, GL_UNSIGNED_BYTE, _current_frame->buffers[0].data); - _openGL_state->textureSize = _current_frame->size; - - if(_openGL_state->textureSizeUniform >= 0) - glUniform2f(_openGL_state->textureSizeUniform, _current_frame->size.width, _current_frame->size.height); - } - else - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _current_frame->size.width, _current_frame->dirty_size.height, formatForDepth(_current_frame->buffers[0].depth), GL_UNSIGNED_BYTE, _current_frame->buffers[0].data); - - // draw - glBindFramebuffer(GL_FRAMEBUFFER, _openGL_state->defaultFramebuffer); + // clear the buffer glClear(GL_COLOR_BUFFER_BIT); - glDrawArrays(GL_TRIANGLES, 0, (GLsizei)_current_frame->number_of_vertices);*/ + + // 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 + GLenum format = formatForDepth(_buffer_builder->buffers[0].bytes_per_pixel); +// if(_buffer_builder->_next_write_y_position > _buffer_builder->last_uploaded_line) +// { +// glTexSubImage2D(GL_TEXTURE_2D, 0, 0, (GLint)_buffer_builder->last_uploaded_line, CRTInputBufferBuilderWidth, CRTInputBufferBuilderHeight, format, GL_UNSIGNED_BYTE, &_buffer_builder->buffers[0].data[_buffer_builder->last_uploaded_line * CRTInputBufferBuilderWidth * _buffer_builder->buffers[0].bytes_per_pixel]); +// _buffer_builder->last_uploaded_line = _buffer_builder->_next_write_y_position; +// } +// else +// { + glTexImage2D(GL_TEXTURE_2D, 0, (GLint)format, CRTInputBufferBuilderWidth, CRTInputBufferBuilderHeight, 0, format, GL_UNSIGNED_BYTE, _buffer_builder->buffers[0].data); + _buffer_builder->last_uploaded_line = 0; +// } + + // draw all sitting frames + int run = _run_write_pointer; +// printf("%d: %zu v %zu\n", run, _run_builders[run]->uploaded_vertices, _run_builders[run]->number_of_vertices); + GLint total_age = 0; + for(int c = 0; c < kCRTNumberOfFrames; c++) + { + // update the total age at the start of this set of runs + total_age += _run_builders[run]->duration; + + if(_run_builders[run]->number_of_vertices > 0) + { + glUniform1f(_openGL_state->timestampBaseUniform, (GLfloat)total_age); + + // bind the vertex array + glBindVertexArray(_openGL_state->vertexArrays[run]); + + // bind this frame's array buffer + glBindBuffer(GL_ARRAY_BUFFER, _openGL_state->arrayBuffers[run]); + if(_run_builders[run]->uploaded_vertices != _run_builders[run]->number_of_vertices) + { + // buffersubdata can only replace existing data, not grow the pool, so we'll just have to take this hit + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(_run_builders[run]->number_of_vertices * kCRTSizeOfVertex), &_run_builders[run]->_input_runs[0], GL_DYNAMIC_DRAW); + _run_builders[run]->uploaded_vertices = _run_builders[run]->number_of_vertices; + } + + // draw this frame + glDrawArrays(GL_TRIANGLES, 0, (GLsizei)_run_builders[run]->number_of_vertices); + } + + // advance back in time + run = (run - 1 + kCRTNumberOfFrames) % kCRTNumberOfFrames; + } _output_mutex->unlock(); } @@ -183,14 +219,18 @@ char *CRT::get_vertex_shader() "in vec2 position;" "in vec2 srcCoordinates;" "in float lateral;" + "in float timestamp;" "uniform vec2 boundsOrigin;" "uniform vec2 boundsSize;" "out float lateralVarying;" "out vec2 shadowMaskCoordinates;" + "out float age;" "uniform vec2 textureSize;" + "uniform float timestampBase;" + "uniform float ticksPerFrame;" "const float shadowMaskMultiple = 600;" @@ -202,7 +242,8 @@ char *CRT::get_vertex_shader() "shadowMaskCoordinates = position * vec2(shadowMaskMultiple, shadowMaskMultiple * 0.85057471264368);" - "srcCoordinatesVarying = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);\n" + "srcCoordinatesVarying = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);" + "age = (timestampBase - timestamp) / ticksPerFrame;" "vec2 mappedPosition = (position - boundsOrigin) / boundsSize;" "gl_Position = vec4(mappedPosition.x * 2.0 - 1.0, 1.0 - mappedPosition.y * 2.0, 0.0, 1.0);" @@ -247,20 +288,19 @@ char *CRT::get_fragment_shader() "#version 150\n" "in float lateralVarying;" + "in float age;" "in vec2 shadowMaskCoordinates;" "out vec4 fragColour;" "uniform sampler2D texID;" "uniform sampler2D shadowMaskTexID;" - "uniform float alpha;" "in vec2 srcCoordinatesVarying;" - "in float phase;\n" - "%s\n" + "\n%s\n" "void main(void)" "{" - "fragColour = vec4(rgb_sample(srcCoordinatesVarying).rgb, alpha);" + "fragColour = vec4(rgb_sample(srcCoordinatesVarying).rgb, 1.3 - age);" "}" , _rgb_shader); } @@ -284,26 +324,36 @@ void CRT::prepare_shader() _openGL_state->positionAttribute = _openGL_state->shaderProgram->get_attrib_location("position"); _openGL_state->textureCoordinatesAttribute = _openGL_state->shaderProgram->get_attrib_location("srcCoordinates"); _openGL_state->lateralAttribute = _openGL_state->shaderProgram->get_attrib_location("lateral"); - _openGL_state->alphaUniform = _openGL_state->shaderProgram->get_uniform_location("alpha"); - _openGL_state->textureSizeUniform = _openGL_state->shaderProgram->get_uniform_location("textureSize"); + _openGL_state->timestampAttribute = _openGL_state->shaderProgram->get_attrib_location("timestamp"); + _openGL_state->windowSizeUniform = _openGL_state->shaderProgram->get_uniform_location("windowSize"); _openGL_state->boundsSizeUniform = _openGL_state->shaderProgram->get_uniform_location("boundsSize"); _openGL_state->boundsOriginUniform = _openGL_state->shaderProgram->get_uniform_location("boundsOrigin"); + _openGL_state->timestampBaseUniform = _openGL_state->shaderProgram->get_uniform_location("timestampBase"); GLint texIDUniform = _openGL_state->shaderProgram->get_uniform_location("texID"); GLint shadowMaskTexIDUniform = _openGL_state->shaderProgram->get_uniform_location("shadowMaskTexID"); + GLint textureSizeUniform = _openGL_state->shaderProgram->get_uniform_location("textureSize"); + GLint ticksPerFrameUniform = _openGL_state->shaderProgram->get_uniform_location("ticksPerFrame"); glUniform1i(texIDUniform, 0); glUniform1i(shadowMaskTexIDUniform, 1); + glUniform2f(textureSizeUniform, CRTInputBufferBuilderWidth, CRTInputBufferBuilderHeight); + glUniform1f(ticksPerFrameUniform, (GLfloat)(_cycles_per_line * _height_of_display * _time_multiplier)); +} +void CRT::prepare_vertex_array() +{ glEnableVertexAttribArray((GLuint)_openGL_state->positionAttribute); glEnableVertexAttribArray((GLuint)_openGL_state->textureCoordinatesAttribute); glEnableVertexAttribArray((GLuint)_openGL_state->lateralAttribute); + glEnableVertexAttribArray((GLuint)_openGL_state->timestampBaseUniform); const GLsizei vertexStride = kCRTSizeOfVertex; - glVertexAttribPointer((GLuint)_openGL_state->positionAttribute, 2, GL_UNSIGNED_SHORT, GL_TRUE, vertexStride, (void *)kCRTVertexOffsetOfPosition); - glVertexAttribPointer((GLuint)_openGL_state->textureCoordinatesAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)kCRTVertexOffsetOfTexCoord); - glVertexAttribPointer((GLuint)_openGL_state->lateralAttribute, 1, GL_UNSIGNED_BYTE, GL_FALSE, vertexStride, (void *)kCRTVertexOffsetOfLateral); + glVertexAttribPointer((GLuint)_openGL_state->positionAttribute, 2, GL_UNSIGNED_SHORT, GL_TRUE, vertexStride, (void *)kCRTVertexOffsetOfPosition); + glVertexAttribPointer((GLuint)_openGL_state->textureCoordinatesAttribute, 2, GL_UNSIGNED_SHORT, GL_FALSE, vertexStride, (void *)kCRTVertexOffsetOfTexCoord); + glVertexAttribPointer((GLuint)_openGL_state->timestampAttribute, 4, GL_UNSIGNED_INT, GL_FALSE, vertexStride, (void *)kCRTVertexOffsetOfTimestamp); + glVertexAttribPointer((GLuint)_openGL_state->lateralAttribute, 1, GL_UNSIGNED_BYTE, GL_FALSE, vertexStride, (void *)kCRTVertexOffsetOfLateral); } void CRT::set_output_device(OutputDevice output_device) diff --git a/Outputs/CRT/CRTOpenGL.hpp b/Outputs/CRT/CRTOpenGL.hpp index d4d1ee8e6..bbf6f3df1 100644 --- a/Outputs/CRT/CRTOpenGL.hpp +++ b/Outputs/CRT/CRTOpenGL.hpp @@ -10,11 +10,11 @@ #define CRTOpenGL_h const size_t kCRTVertexOffsetOfPosition = 0; -const size_t kCRTVertexOffsetOfTexCoord = 2; -const size_t kCRTVertexOffsetOfTimestamp = 4; -const size_t kCRTVertexOffsetOfLateral = 8; +const size_t kCRTVertexOffsetOfTexCoord = 4; +const size_t kCRTVertexOffsetOfTimestamp = 8; +const size_t kCRTVertexOffsetOfLateral = 12; -const size_t kCRTSizeOfVertex = 10; +const size_t kCRTSizeOfVertex = 16; const int CRTInputBufferBuilderWidth = 2048; const int CRTInputBufferBuilderHeight = 1024; @@ -22,4 +22,6 @@ const int CRTInputBufferBuilderHeight = 1024; const int CRTIntermediateBufferWidth = 2048; const int CRTIntermediateBufferHeight = 2048; +const int kCRTNumberOfFrames = 3; + #endif /* CRTOpenGL_h */