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:
parent
4a0e1dc789
commit
29e7c96e4c
@ -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)
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user