1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-24 12:30:17 +00:00

Screen output is back, no matter how inefficiently.

This commit is contained in:
Thomas Harte 2016-02-13 23:50:18 -05:00
parent d0b2d840da
commit 697e50c0cc
5 changed files with 109 additions and 47 deletions

View File

@ -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);

View File

@ -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<CRTInputBufferBuilder> _buffer_builder;
CRTRunBuilder *_run_builders[kCRTNumberOfFrames];
CRTRunBuilder **_run_builders;
int _run_write_pointer;
std::shared_ptr<std::mutex> _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();

View File

@ -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()

View File

@ -17,15 +17,15 @@ using namespace Outputs;
struct CRT::OpenGLState {
std::unique_ptr<OpenGL::Shader> 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<OpenGL::TextureTarget> 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<OpenGL::TextureTarget>(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)

View File

@ -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 */