1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-30 23:29:08 +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:
@ -142,20 +142,6 @@ 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)
{
// 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_x(0) = tex_x;
source_input_position_y(0) = source_input_position_y(1) = tex_y; source_input_position_y(0) = source_input_position_y(1) = tex_y;
@ -167,7 +153,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
source_offset(0) = 0; source_offset(0) = 0;
source_offset(1) = 255; 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
// horizontal counter appropriately // horizontal counter appropriately
@ -188,27 +173,13 @@ 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)
{
// 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);
}
else
{
source_input_position_x(1) = tex_x; source_input_position_x(1) = tex_x;
source_output_position_x(1) = (uint16_t)_horizontal_flywheel->get_current_output_position(); 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 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; 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_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; if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event != Flywheel::SyncEvent::None) honoured_event = next_horizontal_sync_event;
@ -236,7 +207,6 @@ void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divi
{ {
_openGL_output_builder->increment_composite_output_y(); _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
if(next_run_length == time_until_vertical_sync_event && next_vertical_sync_event == Flywheel::SyncEvent::EndRetrace) if(next_run_length == time_until_vertical_sync_event && next_vertical_sync_event == Flywheel::SyncEvent::EndRetrace)

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,10 +241,30 @@ 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}},
{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) if(_drawn_source_buffer_data_pointer != _source_buffer_data_pointer)
{ {
// determine how many lines are newly reclaimed; they'll need to be cleared // determine how many lines are newly reclaimed; they'll need to be cleared
@ -268,32 +288,17 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
glFlushMappedBufferRange(GL_ARRAY_BUFFER, 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);
} }
OpenGL::TextureTarget *targets[] = { while(active_pipeline->target)
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 // switch to the initial texture
targets[stage]->bind_framebuffer(); active_pipeline->target->bind_framebuffer();
shaders[stage]->bind(); active_pipeline->shader->bind();
// clear as desired // clear as desired
if(number_of_clearing_zones) if(number_of_clearing_zones)
{ {
glEnable(GL_SCISSOR_TEST); glEnable(GL_SCISSOR_TEST);
glClearColor(clear_colours[stage][0], clear_colours[stage][1], clear_colours[stage][2], 1.0); 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++) for(int c = 0; c < number_of_clearing_zones; c++)
{ {
glScissor(0, clearing_zones[c*2], IntermediateBufferWidth, clearing_zones[c*2 + 1]); glScissor(0, clearing_zones[c*2], IntermediateBufferWidth, clearing_zones[c*2 + 1]);
@ -307,39 +312,12 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
{ {
glDrawArrays(GL_LINES, drawing_zones[c*2] / SourceVertexSize, drawing_zones[c*2 + 1] / SourceVertexSize); glDrawArrays(GL_LINES, drawing_zones[c*2] / SourceVertexSize, drawing_zones[c*2 + 1] / SourceVertexSize);
} }
active_pipeline++;
} }
} }
// transfer to screen // transfer to framebuffer
perform_output_stage(output_width, output_height, composite_output_shader_program.get());
}
else
perform_output_stage(output_width, output_height, rgb_shader_program.get());
// copy framebuffer to the intended place
glDisable(GL_BLEND);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height);
glClear(GL_COLOR_BUFFER_BIT);
framebuffer->draw((float)output_width / (float)output_height);
// drawing commands having been issued, reclaim the array buffer pointer
glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer);
_output_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, OutputVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer);
_source_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, SourceVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
_input_texture_data = (uint8_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, _input_texture_array_size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
_output_mutex->unlock();
}
void OpenGLOutputBuilder::perform_output_stage(unsigned int output_width, unsigned int output_height, OpenGL::OutputShader *const shader)
{
if(shader)
{
// bind the target
framebuffer->bind_framebuffer(); framebuffer->bind_framebuffer();
// draw all pending lines // draw all pending lines
@ -367,11 +345,11 @@ void OpenGLOutputBuilder::perform_output_stage(unsigned int output_width, unsign
// update uniforms (implicitly binding the shader) // update uniforms (implicitly binding the shader)
if(_last_output_width != output_width || _last_output_height != output_height) if(_last_output_width != output_width || _last_output_height != output_height)
{ {
shader->set_output_size(output_width, output_height, _visible_area); output_shader_program->set_output_size(output_width, output_height, _visible_area);
_last_output_width = output_width; _last_output_width = output_width;
_last_output_height = output_height; _last_output_height = output_height;
} }
shader->bind(); output_shader_program->bind();
// draw // draw
for(int c = 0; c < number_of_drawing_zones; c++) for(int c = 0; c < number_of_drawing_zones; c++)
@ -379,6 +357,30 @@ void OpenGLOutputBuilder::perform_output_stage(unsigned int output_width, unsign
glDrawArrays(GL_TRIANGLE_STRIP, drawing_zones[c*2] / OutputVertexSize, drawing_zones[c*2 + 1] / OutputVertexSize); glDrawArrays(GL_TRIANGLE_STRIP, drawing_zones[c*2] / OutputVertexSize, drawing_zones[c*2 + 1] / OutputVertexSize);
} }
} }
// copy framebuffer to the intended place
glDisable(GL_BLEND);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, (GLsizei)output_width, (GLsizei)output_height);
glClear(GL_COLOR_BUFFER_BIT);
framebuffer->draw((float)output_width / (float)output_height);
// drawing commands having been issued, reclaim the array buffer pointer
glBindBuffer(GL_ARRAY_BUFFER, output_array_buffer);
_output_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, OutputVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
glBindBuffer(GL_ARRAY_BUFFER, source_array_buffer);
_source_buffer_data = (uint8_t *)glMapBufferRange(GL_ARRAY_BUFFER, 0, SourceVertexBufferDataSize, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
_input_texture_data = (uint8_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, _input_texture_array_size, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
_output_mutex->unlock();
}
void OpenGLOutputBuilder::perform_output_stage(unsigned int output_width, unsigned int output_height, OpenGL::OutputShader *const shader)
{
if(shader)
{
} }
} }
@ -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.