1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-06 01:28:57 +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);
_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)
@ -133,7 +133,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
uint8_t *next_run = nullptr;
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:
@ -143,30 +143,15 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
// [0/1] 3
if(next_run)
{
if(_openGL_output_builder->get_output_device() == Monitor)
{
// set the type, initial raster position and type of this run
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);
output_tex_x(0) = output_tex_x(1) = output_tex_x(2) = tex_x;
// these things are constants across the line so just throw them out now
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;
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;
}
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
@ -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(type == Scan::Type::Data && source_divider) tex_x += next_run_length / (_time_multiplier * source_divider);
if(_openGL_output_builder->get_output_device() == Monitor)
{
// 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;
source_input_position_x(1) = tex_x;
source_output_position_x(1) = (uint16_t)_horizontal_flywheel->get_current_output_position();
_openGL_output_builder->complete_output_run(6);
}
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();
}
_openGL_output_builder->complete_source_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;
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());
uint8_t *next_run = _openGL_output_builder->get_next_output_run();
if(needs_endpoint)
{
uint8_t *next_run = _openGL_output_builder->get_next_output_run();
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);
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();
output_position_y(0) = output_position_y(1) = output_position_y(2) = (uint16_t)(_vertical_flywheel->get_current_output_position() / _vertical_flywheel_output_divider);
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);
_is_writing_composite_run ^= true;
}
_openGL_output_builder->complete_output_run(3);
_is_writing_composite_run ^= true;
}
if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace)
{
_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

View File

@ -167,13 +167,13 @@ OpenGLOutputBuilder::~OpenGLOutputBuilder()
void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int output_height, bool only_if_dirty)
{
// 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_composite_output_shader();
prepare_rgb_output_shader();
prepare_output_shader();
prepare_output_vertex_array();
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;
}
// for television, update intermediate buffers and then draw; for a monitor, just draw
if(_output_device == Television || !rgb_shader_program)
struct RenderStage {
OpenGL::TextureTarget *const target;
OpenGL::Shader *const shader;
float clear_colour[3];
};
RenderStage composite_render_stages[] =
{
// decide how much to draw
if(_drawn_source_buffer_data_pointer != _source_buffer_data_pointer)
{compositeTexture.get(), composite_input_shader_program.get(), {0.0, 0.0, 0.0}},
{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
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);
}
}
glFlushMappedBufferRange(GL_ARRAY_BUFFER, drawing_zones[c*2] / SourceVertexSize, drawing_zones[c*2 + 1] / SourceVertexSize);
}
// transfer to screen
perform_output_stage(output_width, output_height, composite_output_shader_program.get());
while(active_pipeline->target)
{
// 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
glDisable(GL_BLEND);
@ -339,46 +381,6 @@ void OpenGLOutputBuilder::perform_output_stage(unsigned int output_width, unsign
{
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
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->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);
}
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()
{
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;
if(!_rgb_shader)
{
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);
output_shader_program = OpenGL::OutputShader::make_shader("", "texture(texID, srcCoordinatesVarying).rgb", false);
output_shader_program->set_source_texture_unit(filtered_texture_unit);
}
void OpenGLOutputBuilder::prepare_output_vertex_array()
{
if(rgb_shader_program)
if(output_shader_program)
{
GLint positionAttribute = rgb_shader_program->get_attrib_location("position");
GLint textureCoordinatesAttribute = rgb_shader_program->get_attrib_location("srcCoordinates");
GLint lateralAttribute = rgb_shader_program->get_attrib_location("lateral");
GLint positionAttribute = output_shader_program->get_attrib_location("position");
GLint textureCoordinatesAttribute = output_shader_program->get_attrib_location("srcCoordinates");
GLint lateralAttribute = output_shader_program->get_attrib_location("lateral");
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;
_height_of_display = height_of_display;
_horizontal_scan_period = horizontal_scan_period;
@ -540,6 +541,7 @@ void OpenGLOutputBuilder::set_colour_space_uniforms()
void OpenGLOutputBuilder::set_timing_uniforms()
{
_output_mutex->lock();
OpenGL::IntermediateShader *intermediate_shaders[] = {
composite_input_shader_program.get(),
composite_y_filter_shader_program.get(),
@ -553,17 +555,12 @@ void OpenGLOutputBuilder::set_timing_uniforms()
extends = true;
}
OpenGL::OutputShader *output_shaders[] = {
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);
}
if(output_shader_program) output_shader_program->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;
if(composite_y_filter_shader_program) composite_y_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency - 90.0f);
if(composite_chrominance_filter_shader_program) composite_chrominance_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency * 0.5f);
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(rgb_filter_shader_program) rgb_filter_shader_program->set_filter_coefficients(_cycles_per_line, (float)_input_frequency * 0.5f);
_output_mutex->unlock();
}

View File

@ -33,6 +33,7 @@ class OpenGLOutputBuilder {
OutputDevice _output_device;
// timing information to allow reasoning about input information
unsigned int _input_frequency;
unsigned int _cycles_per_line;
unsigned int _height_of_display;
unsigned int _horizontal_scan_period;
@ -47,9 +48,9 @@ class OpenGLOutputBuilder {
char *_rgb_shader;
// Methods used by the OpenGL code
void prepare_rgb_output_shader();
void prepare_composite_output_shader();
void prepare_composite_input_shader();
void prepare_output_shader();
void prepare_rgb_input_shaders();
void prepare_composite_input_shaders();
void prepare_output_vertex_array();
void prepare_source_vertex_array();
@ -61,8 +62,9 @@ class OpenGLOutputBuilder {
// transient buffers indicating composite data not yet decoded
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> rgb_input_shader_program, rgb_filter_shader_program;
GLuint output_array_buffer, output_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_rgb_sampling_function(const char *shader);
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;
GLuint _input_texture_array;

View File

@ -141,6 +141,34 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_source_conversion_s
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()
{
return make_shader(
@ -250,6 +278,70 @@ std::unique_ptr<IntermediateShader> IntermediateShader::make_chroma_filter_shade
"}", 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)
{
bind();
@ -319,6 +411,12 @@ void IntermediateShader::set_filter_coefficients(float sampling_rate, float cuto
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)
{
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);
/*!
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,
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();
/*!
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.
*/
@ -53,6 +64,12 @@ public:
*/
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
geometry should be extended so that a complete colour cycle is included at both the beginning and end.