1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-11 04:28:58 +00:00

Brought monitor mode inside the pipeline and added an intermediate filter.

This commit is contained in:
Thomas Harte 2016-05-02 21:05:58 -04:00
parent 4a0e1dc789
commit 29e7c96e4c
5 changed files with 303 additions and 219 deletions

View File

@ -45,7 +45,7 @@ void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_di
unsigned int real_clock_scan_period = (_cycles_per_line * height_of_display) / (_time_multiplier * _common_output_divisor); unsigned int real_clock_scan_period = (_cycles_per_line * height_of_display) / (_time_multiplier * _common_output_divisor);
_vertical_flywheel_output_divider = (uint16_t)(ceilf(real_clock_scan_period / 65536.0f) * (_time_multiplier * _common_output_divisor)); _vertical_flywheel_output_divider = (uint16_t)(ceilf(real_clock_scan_period / 65536.0f) * (_time_multiplier * _common_output_divisor));
_openGL_output_builder->set_timing(_cycles_per_line, _height_of_display, _horizontal_flywheel->get_scan_period(), _vertical_flywheel->get_scan_period(), _vertical_flywheel_output_divider); _openGL_output_builder->set_timing(cycles_per_line, _cycles_per_line, _height_of_display, _horizontal_flywheel->get_scan_period(), _vertical_flywheel->get_scan_period(), _vertical_flywheel_output_divider);
} }
void CRT::set_new_display_type(unsigned int cycles_per_line, DisplayType displayType) void CRT::set_new_display_type(unsigned int cycles_per_line, DisplayType displayType)
@ -133,7 +133,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
uint8_t *next_run = nullptr; uint8_t *next_run = nullptr;
if(is_output_segment) if(is_output_segment)
{ {
next_run = (_openGL_output_builder->get_output_device() == Monitor) ? _openGL_output_builder->get_next_output_run() : _openGL_output_builder->get_next_source_run(); next_run = _openGL_output_builder->get_next_source_run();
} }
// Vertex output is arranged for triangle strips, as: // Vertex output is arranged for triangle strips, as:
@ -143,30 +143,15 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
// [0/1] 3 // [0/1] 3
if(next_run) if(next_run)
{ {
if(_openGL_output_builder->get_output_device() == Monitor) source_input_position_x(0) = tex_x;
{ source_input_position_y(0) = source_input_position_y(1) = tex_y;
// set the type, initial raster position and type of this run source_output_position_x(0) = (uint16_t)_horizontal_flywheel->get_current_output_position();
output_position_x(0) = output_position_x(1) = output_position_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); source_output_position_y(0) = source_output_position_y(1) = _openGL_output_builder->get_composite_output_y();
output_position_y(0) = output_position_y(1) = output_position_y(2) = (uint16_t)(_vertical_flywheel->get_current_output_position() / _vertical_flywheel_output_divider); source_phase(0) = source_phase(1) = _colour_burst_phase;
output_tex_x(0) = output_tex_x(1) = output_tex_x(2) = tex_x; source_amplitude(0) = source_amplitude(1) = _colour_burst_amplitude;
source_phase_time(0) = source_phase_time(1) = _colour_burst_time;
// these things are constants across the line so just throw them out now source_offset(0) = 0;
output_tex_y(0) = output_tex_y(1) = output_tex_y(2) = output_tex_y(3) = output_tex_y(4) = output_tex_y(5) = tex_y; source_offset(1) = 255;
output_lateral(0) = output_lateral(1) = output_lateral(3) = 0;
output_lateral(2) = output_lateral(4) = output_lateral(5) = 1;
}
else
{
source_input_position_x(0) = tex_x;
source_input_position_y(0) = source_input_position_y(1) = tex_y;
source_output_position_x(0) = (uint16_t)_horizontal_flywheel->get_current_output_position();
source_output_position_y(0) = source_output_position_y(1) = _openGL_output_builder->get_composite_output_y();
source_phase(0) = source_phase(1) = _colour_burst_phase;
source_amplitude(0) = source_amplitude(1) = _colour_burst_amplitude;
source_phase_time(0) = source_phase_time(1) = _colour_burst_time;
source_offset(0) = 0;
source_offset(1) = 255;
}
} }
// decrement the number of cycles left to run for and increment the // decrement the number of cycles left to run for and increment the
@ -188,54 +173,39 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
// if this is a data run then advance the buffer pointer // if this is a data run then advance the buffer pointer
if(type == Scan::Type::Data && source_divider) tex_x += next_run_length / (_time_multiplier * source_divider); if(type == Scan::Type::Data && source_divider) tex_x += next_run_length / (_time_multiplier * source_divider);
if(_openGL_output_builder->get_output_device() == Monitor) source_input_position_x(1) = tex_x;
{ source_output_position_x(1) = (uint16_t)_horizontal_flywheel->get_current_output_position();
// store the final raster position
output_position_x(3) = output_position_x(4) = output_position_x(5) = (uint16_t)_horizontal_flywheel->get_current_output_position();
output_position_y(3) = output_position_y(4) = output_position_y(5) = (uint16_t)(_vertical_flywheel->get_current_output_position() / _vertical_flywheel_output_divider);
output_tex_x(3) = output_tex_x(4) = output_tex_x(5) = tex_x;
_openGL_output_builder->complete_output_run(6); _openGL_output_builder->complete_source_run();
}
else
{
source_input_position_x(1) = tex_x;
source_output_position_x(1) = (uint16_t)_horizontal_flywheel->get_current_output_position();
_openGL_output_builder->complete_source_run();
}
} }
// if this is horizontal retrace then advance the output line counter and bookend an output run // if this is horizontal retrace then advance the output line counter and bookend an output run
if(_openGL_output_builder->get_output_device() == Television) Flywheel::SyncEvent honoured_event = Flywheel::SyncEvent::None;
if(next_run_length == time_until_vertical_sync_event && next_vertical_sync_event != Flywheel::SyncEvent::None) honoured_event = next_vertical_sync_event;
if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event != Flywheel::SyncEvent::None) honoured_event = next_horizontal_sync_event;
bool needs_endpoint =
(honoured_event == Flywheel::SyncEvent::StartRetrace && _is_writing_composite_run) ||
(honoured_event == Flywheel::SyncEvent::EndRetrace && !_horizontal_flywheel->is_in_retrace() && !_vertical_flywheel->is_in_retrace());
if(needs_endpoint)
{ {
Flywheel::SyncEvent honoured_event = Flywheel::SyncEvent::None; uint8_t *next_run = _openGL_output_builder->get_next_output_run();
if(next_run_length == time_until_vertical_sync_event && next_vertical_sync_event != Flywheel::SyncEvent::None) honoured_event = next_vertical_sync_event;
if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event != Flywheel::SyncEvent::None) honoured_event = next_horizontal_sync_event;
bool needs_endpoint =
(honoured_event == Flywheel::SyncEvent::StartRetrace && _is_writing_composite_run) ||
(honoured_event == Flywheel::SyncEvent::EndRetrace && !_horizontal_flywheel->is_in_retrace() && !_vertical_flywheel->is_in_retrace());
if(needs_endpoint) output_position_x(0) = output_position_x(1) = output_position_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position();
{ output_position_y(0) = output_position_y(1) = output_position_y(2) = (uint16_t)(_vertical_flywheel->get_current_output_position() / _vertical_flywheel_output_divider);
uint8_t *next_run = _openGL_output_builder->get_next_output_run(); output_tex_x(0) = output_tex_x(1) = output_tex_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position();
output_tex_y(0) = output_tex_y(1) = output_tex_y(2) = _openGL_output_builder->get_composite_output_y();
output_lateral(0) = 0;
output_lateral(1) = _is_writing_composite_run ? 1 : 0;
output_lateral(2) = 1;
output_position_x(0) = output_position_x(1) = output_position_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); _openGL_output_builder->complete_output_run(3);
output_position_y(0) = output_position_y(1) = output_position_y(2) = (uint16_t)(_vertical_flywheel->get_current_output_position() / _vertical_flywheel_output_divider); _is_writing_composite_run ^= true;
output_tex_x(0) = output_tex_x(1) = output_tex_x(2) = (uint16_t)_horizontal_flywheel->get_current_output_position(); }
output_tex_y(0) = output_tex_y(1) = output_tex_y(2) = _openGL_output_builder->get_composite_output_y();
output_lateral(0) = 0;
output_lateral(1) = _is_writing_composite_run ? 1 : 0;
output_lateral(2) = 1;
_openGL_output_builder->complete_output_run(3); if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace)
_is_writing_composite_run ^= true; {
} _openGL_output_builder->increment_composite_output_y();
if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace)
{
_openGL_output_builder->increment_composite_output_y();
}
} }
// if this is vertical retrace then adcance a field // if this is vertical retrace then adcance a field

View File

@ -167,13 +167,13 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder()
void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty) void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty)
{ {
// establish essentials // establish essentials
if(!composite_input_shader_program && !rgb_shader_program) if(!output_shader_program)
{ {
prepare_composite_input_shader(); prepare_composite_input_shaders();
prepare_rgb_input_shaders();
prepare_source_vertex_array(); prepare_source_vertex_array();
prepare_composite_output_shader(); prepare_output_shader();
prepare_rgb_output_shader();
prepare_output_vertex_array(); prepare_output_vertex_array();
set_timing_uniforms(); set_timing_uniforms();
@ -241,80 +241,122 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
_buffer_builder->last_uploaded_line = _buffer_builder->_next_write_y_position; _buffer_builder->last_uploaded_line = _buffer_builder->_next_write_y_position;
} }
// for television, update intermediate buffers and then draw; for a monitor, just draw struct RenderStage {
if(_output_device == Television || !rgb_shader_program) OpenGL::TextureTarget *const target;
OpenGL::Shader *const shader;
float clear_colour[3];
};
RenderStage composite_render_stages[] =
{ {
// decide how much to draw {compositeTexture.get(), composite_input_shader_program.get(), {0.0, 0.0, 0.0}},
if(_drawn_source_buffer_data_pointer != _source_buffer_data_pointer) {filteredYTexture.get(), composite_y_filter_shader_program.get(), {0.0, 0.5, 0.5}},
{filteredTexture.get(), composite_chrominance_filter_shader_program.get(), {0.0, 0.0, 0.0}},
{nullptr}
};
RenderStage rgb_render_stages[] =
{
{compositeTexture.get(), rgb_input_shader_program.get(), {0.0, 0.0, 0.0}},
{filteredTexture.get(), rgb_filter_shader_program.get(), {0.0, 0.0, 0.0}},
{nullptr}
};
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)
{
// 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++)
{ {
// determine how many lines are newly reclaimed; they'll need to be cleared glFlushMappedBufferRange(GL_ARRAY_BUFFER, drawing_zones[c*2] / SourceVertexSize, drawing_zones[c*2 + 1] / SourceVertexSize);
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);
}
OpenGL::TextureTarget *targets[] = {
compositeTexture.get(),
filteredYTexture.get(),
filteredTexture.get()
};
OpenGL::Shader *shaders[] = {
composite_input_shader_program.get(),
composite_y_filter_shader_program.get(),
composite_chrominance_filter_shader_program.get()
};
float clear_colours[][3] = {
{0.0, 0.0, 0.0},
{0.0, 0.5, 0.5},
{0.0, 0.0, 0.0}
};
for(int stage = 0; stage < 3; stage++)
{
// switch to the initial texture
targets[stage]->bind_framebuffer();
shaders[stage]->bind();
// clear as desired
if(number_of_clearing_zones)
{
glEnable(GL_SCISSOR_TEST);
glClearColor(clear_colours[stage][0], clear_colours[stage][1], clear_colours[stage][2], 1.0);
for(int c = 0; c < number_of_clearing_zones; c++)
{
glScissor(0, clearing_zones[c*2], IntermediateBufferWidth, clearing_zones[c*2 + 1]);
glClear(GL_COLOR_BUFFER_BIT);
}
glDisable(GL_SCISSOR_TEST);
}
// draw as desired
for(int c = 0; c < number_of_drawing_zones; c++)
{
glDrawArrays(GL_LINES, drawing_zones[c*2] / SourceVertexSize, drawing_zones[c*2 + 1] / SourceVertexSize);
}
}
} }
// transfer to screen while(active_pipeline->target)
perform_output_stage(output_width, output_height, composite_output_shader_program.get()); {
// switch to the initial texture
active_pipeline->target->bind_framebuffer();
active_pipeline->shader->bind();
// clear as desired
if(number_of_clearing_zones)
{
glEnable(GL_SCISSOR_TEST);
glClearColor(active_pipeline->clear_colour[0], active_pipeline->clear_colour[1], active_pipeline->clear_colour[2], 1.0);
for(int c = 0; c < number_of_clearing_zones; c++)
{
glScissor(0, clearing_zones[c*2], IntermediateBufferWidth, clearing_zones[c*2 + 1]);
glClear(GL_COLOR_BUFFER_BIT);
}
glDisable(GL_SCISSOR_TEST);
}
// draw as desired
for(int c = 0; c < number_of_drawing_zones; c++)
{
glDrawArrays(GL_LINES, drawing_zones[c*2] / SourceVertexSize, drawing_zones[c*2 + 1] / SourceVertexSize);
}
active_pipeline++;
}
}
// 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)
{
glEnable(GL_BLEND);
// Ensure we're back on the output framebuffer, drawing from the output array buffer
glBindVertexArray(output_vertex_array);
// update uniforms (implicitly binding the shader)
if(_last_output_width != output_width || _last_output_height != output_height)
{
output_shader_program->set_output_size(output_width, output_height, _visible_area);
_last_output_width = output_width;
_last_output_height = output_height;
}
output_shader_program->bind();
// draw
for(int c = 0; c < number_of_drawing_zones; c++)
{
glDrawArrays(GL_TRIANGLE_STRIP, drawing_zones[c*2] / OutputVertexSize, drawing_zones[c*2 + 1] / OutputVertexSize);
}
} }
else
perform_output_stage(output_width, output_height, rgb_shader_program.get());
// copy framebuffer to the intended place // copy framebuffer to the intended place
glDisable(GL_BLEND); glDisable(GL_BLEND);
@ -339,46 +381,6 @@ void OpenGLOutputBuilder::perform_output_stage(unsigned int output_width, unsign
{ {
if(shader) if(shader)
{ {
// bind the target
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)
{
glEnable(GL_BLEND);
// Ensure we're back on the output framebuffer, drawing from the output array buffer
glBindVertexArray(output_vertex_array);
// update uniforms (implicitly binding the shader)
if(_last_output_width != output_width || _last_output_height != output_height)
{
shader->set_output_size(output_width, output_height, _visible_area);
_last_output_width = output_width;
_last_output_height = output_height;
}
shader->bind();
// draw
for(int c = 0; c < number_of_drawing_zones; c++)
{
glDrawArrays(GL_TRIANGLE_STRIP, drawing_zones[c*2] / OutputVertexSize, drawing_zones[c*2 + 1] / OutputVertexSize);
}
}
} }
} }
@ -398,7 +400,7 @@ void OpenGLOutputBuilder::set_rgb_sampling_function(const char *shader)
#pragma mark - Program compilation #pragma mark - Program compilation
void OpenGLOutputBuilder::prepare_composite_input_shader() void OpenGLOutputBuilder::prepare_composite_input_shaders()
{ {
composite_input_shader_program = OpenGL::IntermediateShader::make_source_conversion_shader(_composite_shader, _rgb_shader); composite_input_shader_program = OpenGL::IntermediateShader::make_source_conversion_shader(_composite_shader, _rgb_shader);
composite_input_shader_program->set_source_texture_unit(source_data_texture_unit); composite_input_shader_program->set_source_texture_unit(source_data_texture_unit);
@ -413,6 +415,20 @@ void OpenGLOutputBuilder::prepare_composite_input_shader()
composite_chrominance_filter_shader_program->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight); composite_chrominance_filter_shader_program->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
} }
void OpenGLOutputBuilder::prepare_rgb_input_shaders()
{
if(_rgb_shader)
{
rgb_input_shader_program = OpenGL::IntermediateShader::make_rgb_source_shader(_rgb_shader);
rgb_input_shader_program->set_source_texture_unit(source_data_texture_unit);
rgb_input_shader_program->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
rgb_filter_shader_program = OpenGL::IntermediateShader::make_rgb_filter_shader();
rgb_filter_shader_program->set_source_texture_unit(composite_texture_unit);
rgb_filter_shader_program->set_output_size(IntermediateBufferWidth, IntermediateBufferHeight);
}
}
void OpenGLOutputBuilder::prepare_source_vertex_array() void OpenGLOutputBuilder::prepare_source_vertex_array()
{ {
if(composite_input_shader_program) if(composite_input_shader_program)
@ -438,35 +454,19 @@ void OpenGLOutputBuilder::prepare_source_vertex_array()
} }
} }
void OpenGLOutputBuilder::prepare_rgb_output_shader() void OpenGLOutputBuilder::prepare_output_shader()
{ {
const char *rgb_shader = _rgb_shader; output_shader_program = OpenGL::OutputShader::make_shader("", "texture(texID, srcCoordinatesVarying).rgb", false);
if(!_rgb_shader) output_shader_program->set_source_texture_unit(filtered_texture_unit);
{
rgb_shader =
"vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)"
"{"
"return texture(sampler, coordinate).rgb / vec3(255.0);"
"}";
}
rgb_shader_program = OpenGL::OutputShader::make_shader(rgb_shader, "rgb_sample(texID, srcCoordinatesVarying, iSrcCoordinatesVarying)", true);
rgb_shader_program->set_source_texture_unit(source_data_texture_unit);
}
void OpenGLOutputBuilder::prepare_composite_output_shader()
{
composite_output_shader_program = OpenGL::OutputShader::make_shader("", "texture(texID, srcCoordinatesVarying).rgb", false);
composite_output_shader_program->set_source_texture_unit(filtered_texture_unit);
} }
void OpenGLOutputBuilder::prepare_output_vertex_array() void OpenGLOutputBuilder::prepare_output_vertex_array()
{ {
if(rgb_shader_program) if(output_shader_program)
{ {
GLint positionAttribute = rgb_shader_program->get_attrib_location("position"); GLint positionAttribute = output_shader_program->get_attrib_location("position");
GLint textureCoordinatesAttribute = rgb_shader_program->get_attrib_location("srcCoordinates"); GLint textureCoordinatesAttribute = output_shader_program->get_attrib_location("srcCoordinates");
GLint lateralAttribute = rgb_shader_program->get_attrib_location("lateral"); GLint lateralAttribute = output_shader_program->get_attrib_location("lateral");
glBindVertexArray(output_vertex_array); glBindVertexArray(output_vertex_array);
@ -495,8 +495,9 @@ void OpenGLOutputBuilder::set_output_device(OutputDevice output_device)
} }
} }
void OpenGLOutputBuilder::set_timing(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) void OpenGLOutputBuilder::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)
{ {
_input_frequency = input_frequency;
_cycles_per_line = cycles_per_line; _cycles_per_line = cycles_per_line;
_height_of_display = height_of_display; _height_of_display = height_of_display;
_horizontal_scan_period = horizontal_scan_period; _horizontal_scan_period = horizontal_scan_period;
@ -540,6 +541,7 @@ void OpenGLOutputBuilder::set_colour_space_uniforms()
void OpenGLOutputBuilder::set_timing_uniforms() void OpenGLOutputBuilder::set_timing_uniforms()
{ {
_output_mutex->lock(); _output_mutex->lock();
OpenGL::IntermediateShader *intermediate_shaders[] = { OpenGL::IntermediateShader *intermediate_shaders[] = {
composite_input_shader_program.get(), composite_input_shader_program.get(),
composite_y_filter_shader_program.get(), composite_y_filter_shader_program.get(),
@ -553,17 +555,12 @@ void OpenGLOutputBuilder::set_timing_uniforms()
extends = true; extends = true;
} }
OpenGL::OutputShader *output_shaders[] = { if(output_shader_program) output_shader_program->set_timing(_height_of_display, _cycles_per_line, _horizontal_scan_period, _vertical_scan_period, _vertical_period_divider);
rgb_shader_program.get(),
composite_output_shader_program.get()
};
for(int c = 0; c < 2; c++)
{
if(output_shaders[c]) output_shaders[c]->set_timing(_height_of_display, _cycles_per_line, _horizontal_scan_period, _vertical_scan_period, _vertical_period_divider);
}
float colour_subcarrier_frequency = (float)_colour_cycle_numerator / (float)_colour_cycle_denominator; float colour_subcarrier_frequency = (float)_colour_cycle_numerator / (float)_colour_cycle_denominator;
if(composite_y_filter_shader_program) composite_y_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency - 90.0f); if(composite_y_filter_shader_program) composite_y_filter_shader_program->set_separation_frequency(_cycles_per_line, colour_subcarrier_frequency);
if(composite_chrominance_filter_shader_program) composite_chrominance_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency * 0.5f); if(composite_chrominance_filter_shader_program) composite_chrominance_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency * 0.5f);
if(rgb_filter_shader_program) rgb_filter_shader_program->set_filter_coefficients(_cycles_per_line, (float)_input_frequency * 0.5f);
_output_mutex->unlock(); _output_mutex->unlock();
} }

View File

@ -33,6 +33,7 @@ class OpenGLOutputBuilder {
OutputDevice _output_device; OutputDevice _output_device;
// timing information to allow reasoning about input information // timing information to allow reasoning about input information
unsigned int _input_frequency;
unsigned int _cycles_per_line; unsigned int _cycles_per_line;
unsigned int _height_of_display; unsigned int _height_of_display;
unsigned int _horizontal_scan_period; unsigned int _horizontal_scan_period;
@ -47,9 +48,9 @@ class OpenGLOutputBuilder {
char *_rgb_shader; char *_rgb_shader;
// Methods used by the OpenGL code // Methods used by the OpenGL code
void prepare_rgb_output_shader(); void prepare_output_shader();
void prepare_composite_output_shader(); void prepare_rgb_input_shaders();
void prepare_composite_input_shader(); void prepare_composite_input_shaders();
void prepare_output_vertex_array(); void prepare_output_vertex_array();
void prepare_source_vertex_array(); void prepare_source_vertex_array();
@ -61,8 +62,9 @@ class OpenGLOutputBuilder {
// transient buffers indicating composite data not yet decoded // transient buffers indicating composite data not yet decoded
uint16_t _composite_src_output_y, _cleared_composite_output_y; uint16_t _composite_src_output_y, _cleared_composite_output_y;
std::unique_ptr<OpenGL::OutputShader> rgb_shader_program, composite_output_shader_program; std::unique_ptr<OpenGL::OutputShader> output_shader_program;
std::unique_ptr<OpenGL::IntermediateShader> composite_input_shader_program, composite_y_filter_shader_program, composite_chrominance_filter_shader_program; std::unique_ptr<OpenGL::IntermediateShader> composite_input_shader_program, composite_y_filter_shader_program, composite_chrominance_filter_shader_program;
std::unique_ptr<OpenGL::IntermediateShader> rgb_input_shader_program, rgb_filter_shader_program;
GLuint output_array_buffer, output_vertex_array; GLuint output_array_buffer, output_vertex_array;
GLuint source_array_buffer, source_vertex_array; GLuint source_array_buffer, source_vertex_array;
@ -168,7 +170,7 @@ class OpenGLOutputBuilder {
void set_composite_sampling_function(const char *shader); void set_composite_sampling_function(const char *shader);
void set_rgb_sampling_function(const char *shader); void set_rgb_sampling_function(const char *shader);
void set_output_device(OutputDevice output_device); void set_output_device(OutputDevice output_device);
void set_timing(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); 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; uint8_t *_input_texture_data;
GLuint _input_texture_array; GLuint _input_texture_array;

View File

@ -141,6 +141,34 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_source_conversion_s
return shader; return shader;
} }
std::unique_ptr<IntermediateShader> IntermediateShader::make_rgb_source_shader(const char *rgb_shader)
{
char *fragment_shader;
asprintf(&fragment_shader,
"#version 150\n"
"in vec2 inputPositionsVarying[11];"
"in vec2 iInputPositionVarying;"
"in vec2 phaseAndAmplitudeVarying;"
"out vec3 fragColour;"
"uniform usampler2D texID;"
"\n%s\n"
"void main(void)"
"{"
"fragColour = rgb_sample(texID, inputPositionsVarying[5], iInputPositionVarying);"
"}"
, rgb_shader);
std::unique_ptr<IntermediateShader> shader = make_shader(fragment_shader, true, true);
free(fragment_shader);
return shader;
}
std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_luma_separation_shader() std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_luma_separation_shader()
{ {
return make_shader( return make_shader(
@ -250,6 +278,70 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_filter_shade
"}", false, false); "}", false, false);
} }
std::unique_ptr<IntermediateShader> IntermediateShader::make_rgb_filter_shader()
{
return make_shader(
"#version 150\n"
"in vec2 inputPositionsVarying[11];"
"uniform vec4 weights[3];"
"out vec3 fragColour;"
"uniform sampler2D texID;"
"void main(void)"
"{"
"vec3 samples[] = vec3[]("
"texture(texID, inputPositionsVarying[0]).rgb,"
"texture(texID, inputPositionsVarying[1]).rgb,"
"texture(texID, inputPositionsVarying[2]).rgb,"
"texture(texID, inputPositionsVarying[3]).rgb,"
"texture(texID, inputPositionsVarying[4]).rgb,"
"texture(texID, inputPositionsVarying[5]).rgb,"
"texture(texID, inputPositionsVarying[6]).rgb,"
"texture(texID, inputPositionsVarying[7]).rgb,"
"texture(texID, inputPositionsVarying[8]).rgb,"
"texture(texID, inputPositionsVarying[9]).rgb,"
"texture(texID, inputPositionsVarying[10]).rgb"
");"
"vec4 channel1[] = vec4[]("
"vec4(samples[0].r, samples[1].r, samples[2].r, samples[3].r),"
"vec4(samples[4].r, samples[5].r, samples[6].r, samples[7].r),"
"vec4(samples[8].r, samples[9].r, samples[10].r, 0.0)"
");"
"vec4 channel2[] = vec4[]("
"vec4(samples[0].g, samples[1].g, samples[2].g, samples[3].g),"
"vec4(samples[4].g, samples[5].g, samples[6].g, samples[7].g),"
"vec4(samples[8].g, samples[9].g, samples[10].g, 0.0)"
");"
"vec4 channel3[] = vec4[]("
"vec4(samples[0].b, samples[1].b, samples[2].b, samples[3].b),"
"vec4(samples[4].b, samples[5].b, samples[6].b, samples[7].b),"
"vec4(samples[8].b, samples[9].b, samples[10].b, 0.0)"
");"
"fragColour = vec3("
"dot(vec3("
"dot(channel1[0], weights[0]),"
"dot(channel1[1], weights[1]),"
"dot(channel1[2], weights[2])"
"), vec3(1.0)),"
"dot(vec3("
"dot(channel2[0], weights[0]),"
"dot(channel2[1], weights[1]),"
"dot(channel2[2], weights[2])"
"), vec3(1.0)),"
"dot(vec3("
"dot(channel3[0], weights[0]),"
"dot(channel3[1], weights[1]),"
"dot(channel3[2], weights[2])"
"), vec3(1.0))"
");"
"}", false, false);
}
void IntermediateShader::set_output_size(unsigned int output_width, unsigned int output_height) void IntermediateShader::set_output_size(unsigned int output_width, unsigned int output_height)
{ {
bind(); bind();
@ -319,6 +411,12 @@ void IntermediateShader::set_filter_coefficients(float sampling_rate, float cuto
glUniform1fv(offsetsUniform, 5, offsets); glUniform1fv(offsetsUniform, 5, offsets);
} }
void IntermediateShader::set_separation_frequency(float sampling_rate, float colour_burst_frequency)
{
// TODO: apply separately-formed filters for luminance and chrominance
set_filter_coefficients(sampling_rate, colour_burst_frequency - 50.0f);
}
void IntermediateShader::set_phase_cycles_per_sample(float phase_cycles_per_sample, bool extend_runs_to_full_cycle) void IntermediateShader::set_phase_cycles_per_sample(float phase_cycles_per_sample, bool extend_runs_to_full_cycle)
{ {
bind(); bind();

View File

@ -27,6 +27,12 @@ public:
*/ */
static std::unique_ptr<IntermediateShader> make_source_conversion_shader(const char *composite_shader, const char *rgb_shader); static std::unique_ptr<IntermediateShader> make_source_conversion_shader(const char *composite_shader, const char *rgb_shader);
/*!
Constructs and returns an intermediate shader that will take runs from the inputPositions,
converting them to RGB values using @c rgb_shader.
*/
static std::unique_ptr<IntermediateShader> make_rgb_source_shader(const char *rgb_shader);
/*! /*!
Constructs and returns an intermediate shader that will read composite samples from the R channel, Constructs and returns an intermediate shader that will read composite samples from the R channel,
filter then to obtain luminance, stored to R, and to separate out unfiltered chrominance, store to G and B. filter then to obtain luminance, stored to R, and to separate out unfiltered chrominance, store to G and B.
@ -38,6 +44,11 @@ public:
*/ */
static std::unique_ptr<IntermediateShader> make_chroma_filter_shader(); static std::unique_ptr<IntermediateShader> make_chroma_filter_shader();
/*!
Constructs and returns an intermediate shader that will filter R, G and B.
*/
static std::unique_ptr<IntermediateShader> make_rgb_filter_shader();
/*! /*!
Binds this shader and configures it for output to an area of `output_width` and `output_height` pixels. Binds this shader and configures it for output to an area of `output_width` and `output_height` pixels.
*/ */
@ -53,6 +64,12 @@ public:
*/ */
void set_filter_coefficients(float sampling_rate, float cutoff_frequency); void set_filter_coefficients(float sampling_rate, float cutoff_frequency);
/*!
Binds this shader and configures filtering to separate luminance and chrominance based on a colour
subcarrier of the given frequency.
*/
void set_separation_frequency(float sampling_rate, float colour_burst_frequency);
/*! /*!
Binds this shader and sets the number of colour phase cycles per sample, indicating whether output Binds this shader and sets the number of colour phase cycles per sample, indicating whether output
geometry should be extended so that a complete colour cycle is included at both the beginning and end. geometry should be extended so that a complete colour cycle is included at both the beginning and end.