diff --git a/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.mm b/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.mm index 2ded5ede0..8a48379f3 100644 --- a/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.mm +++ b/OSBindings/Mac/Clock Signal/Wrappers/CSAtari2600.mm @@ -29,15 +29,20 @@ struct CRTDelegate: public Outputs::CRT::Delegate { int _frameCount; int _hitCount; BOOL _didDecideRegion; + int _batchesReceived; } - (void)crt:(Outputs::CRT::CRT *)crt didEndBatchOfFrames:(unsigned int)numberOfFrames withUnexpectedVerticalSyncs:(unsigned int)numberOfUnexpectedSyncs { if(!_didDecideRegion) { - _didDecideRegion = YES; - if(numberOfUnexpectedSyncs >= numberOfFrames >> 1) + _batchesReceived++; + if(_batchesReceived == 2) { - _atari2600.switch_region(); + _didDecideRegion = YES; + if(numberOfUnexpectedSyncs >= numberOfFrames >> 1) + { + _atari2600.switch_region(); + } } } } diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index b577d0d23..0514f4448 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -185,6 +185,8 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out prepare_rgb_output_shader(); prepare_output_vertex_array(); + set_timing_uniforms(); + // This should return either an actual framebuffer number, if this is a target with a framebuffer intended for output, // or 0 if no framebuffer is bound, in which case 0 is also what we want to supply to bind the implied framebuffer. So // it works either way. @@ -762,17 +764,11 @@ std::unique_ptr OpenGLOutputBuilder::prepare_intermediate_shader shader = std::unique_ptr(new OpenGL::Shader(vertex_shader, fragment_shader, bindings)); GLint texIDUniform = shader->get_uniform_location("texID"); - GLint phaseCyclesPerTickUniform = shader->get_uniform_location("phaseCyclesPerTick"); GLint outputTextureSizeUniform = shader->get_uniform_location("outputTextureSize"); - GLint extensionUniform = shader->get_uniform_location("extension"); shader->bind(); glUniform1i(texIDUniform, (GLint)(texture_unit - GL_TEXTURE0)); glUniform2i(outputTextureSizeUniform, IntermediateBufferWidth, IntermediateBufferHeight); - - float phaseCyclesPerTick = (float)_colour_cycle_numerator / (float)(_colour_cycle_denominator * _cycles_per_line); - glUniform1f(phaseCyclesPerTickUniform, phaseCyclesPerTick); - glUniform1f(extensionUniform, extends ? ceilf(1.0f / phaseCyclesPerTick) : 0.0f); } free(vertex_shader); free(fragment_shader); @@ -784,23 +780,10 @@ void OpenGLOutputBuilder::prepare_composite_input_shader() { composite_input_shader_program = prepare_intermediate_shader("inputPosition", "uniform usampler2D texID;", get_input_fragment_shader(), source_data_texture_unit, false); - float colour_subcarrier_frequency = (float)_colour_cycle_numerator / (float)_colour_cycle_denominator; - GLint weightsUniform; - float weights[12]; composite_y_filter_shader_program = prepare_intermediate_shader("outputPosition", "uniform sampler2D texID;", get_y_filter_fragment_shader(), composite_texture_unit, true); - SignalProcessing::FIRFilter luminance_filter(11, _cycles_per_line * 0.5f, 0.0f, colour_subcarrier_frequency * 0.5f, SignalProcessing::FIRFilter::DefaultAttenuation); - composite_y_filter_shader_program->bind(); - weightsUniform = composite_y_filter_shader_program->get_uniform_location("weights"); - luminance_filter.get_coefficients(weights); - glUniform4fv(weightsUniform, 3, weights); - composite_chrominance_filter_shader_program = prepare_intermediate_shader("outputPosition", "uniform sampler2D texID;", get_chrominance_filter_fragment_shader(), filtered_y_texture_unit, false); - SignalProcessing::FIRFilter chrominance_filter(11, _cycles_per_line * 0.5f, 0.0f, colour_subcarrier_frequency * 0.5f, SignalProcessing::FIRFilter::DefaultAttenuation); - composite_chrominance_filter_shader_program->bind(); - weightsUniform = composite_chrominance_filter_shader_program->get_uniform_location("weights"); - chrominance_filter.get_coefficients(weights); - glUniform4fv(weightsUniform, 3, weights); + composite_chrominance_filter_shader_program = prepare_intermediate_shader("outputPosition", "uniform sampler2D texID;", get_chrominance_filter_fragment_shader(), filtered_y_texture_unit, true); } void OpenGLOutputBuilder::prepare_source_vertex_array() @@ -851,20 +834,7 @@ std::unique_ptr OpenGLOutputBuilder::prepare_output_shader(char timestampBaseUniform = shader_program->get_uniform_location("timestampBase"); GLint texIDUniform = shader_program->get_uniform_location("texID"); - GLint ticksPerFrameUniform = shader_program->get_uniform_location("ticksPerFrame"); - GLint scanNormalUniform = shader_program->get_uniform_location("scanNormal"); - GLint positionConversionUniform = shader_program->get_uniform_location("positionConversion"); - glUniform1i(texIDUniform, source_texture_unit - GL_TEXTURE0); - glUniform1f(ticksPerFrameUniform, (GLfloat)(_cycles_per_line * _height_of_display)); - glUniform2f(positionConversionUniform, _horizontal_scan_period, _vertical_scan_period / (unsigned int)_vertical_period_divider); - - float scan_angle = atan2f(1.0f / (float)_height_of_display, 1.0f); - float scan_normal[] = { -sinf(scan_angle), cosf(scan_angle)}; - float multiplier = (float)_cycles_per_line / ((float)_height_of_display * (float)_horizontal_scan_period); - scan_normal[0] *= multiplier; - scan_normal[1] *= multiplier; - glUniform2f(scanNormalUniform, scan_normal[0], scan_normal[1]); } free(vertex_shader); @@ -908,7 +878,7 @@ void OpenGLOutputBuilder::prepare_output_vertex_array() } } -#pragma mark - Configuration +#pragma mark - Public Configuration void OpenGLOutputBuilder::set_output_device(OutputDevice output_device) { @@ -925,50 +895,87 @@ 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) +{ + _cycles_per_line = cycles_per_line; + _height_of_display = height_of_display; + _horizontal_scan_period = horizontal_scan_period; + _vertical_scan_period = vertical_scan_period; + _vertical_period_divider = vertical_period_divider; -// const char *const ntscVertexShaderGlobals = -// "out vec2 srcCoordinatesVarying[4];\n" -// "out float phase;\n"; -// -// const char *const ntscVertexShaderBody = -// "phase = srcCoordinates.x * 6.283185308;\n" -// "\n" -// "srcCoordinatesVarying[0] = vec2(srcCoordinates.x / textureSize.x, (srcCoordinates.y + 0.5) / textureSize.y);\n" -// "srcCoordinatesVarying[3] = srcCoordinatesVarying[0] + vec2(0.375 / textureSize.x, 0.0);\n" -// "srcCoordinatesVarying[2] = srcCoordinatesVarying[0] + vec2(0.125 / textureSize.x, 0.0);\n" -// "srcCoordinatesVarying[1] = srcCoordinatesVarying[0] - vec2(0.125 / textureSize.x, 0.0);\n" -// "srcCoordinatesVarying[0] = srcCoordinatesVarying[0] - vec2(0.325 / textureSize.x, 0.0);\n"; + set_timing_uniforms(); +} - // assumes y = [0, 1], i and q = [-0.5, 0.5]; therefore i components are multiplied by 1.1914 versus standard matrices, q by 1.0452 -// const char *const yiqToRGB = "const mat3 yiqToRGB = mat3(1.0, 1.0, 1.0, 1.1389784, -0.3240608, -1.3176884, 0.6490692, -0.6762444, 1.7799756);"; +#pragma mark - Internal Configuration - // assumes y = [0,1], u and v = [-0.5, 0.5]; therefore u components are multiplied by 1.14678899082569, v by 0.8130081300813 -// const char *const yuvToRGB = "const mat3 yiqToRGB = mat3(1.0, 1.0, 1.0, 0.0, -0.75213899082569, 2.33040137614679, 0.92669105691057, -0.4720325203252, 0.0);"; +void OpenGLOutputBuilder::set_timing_uniforms() +{ + OpenGL::Shader *intermediate_shaders[] = { + composite_input_shader_program.get(), + composite_y_filter_shader_program.get(), + composite_chrominance_filter_shader_program.get() + }; + bool extends = false; + for(int c = 0; c < 3; c++) + { + if(intermediate_shaders[c]) + { + intermediate_shaders[c]->bind(); + GLint phaseCyclesPerTickUniform = intermediate_shaders[c]->get_uniform_location("phaseCyclesPerTick"); + GLint extensionUniform = intermediate_shaders[c]->get_uniform_location("extension"); -// const char *const ntscFragmentShaderGlobals = -// "in vec2 srcCoordinatesVarying[4];\n" -// "in float phase;\n" -// "\n" -// "// for conversion from i and q are in the range [-0.5, 0.5] (so i needs to be multiplied by 1.1914 and q by 1.0452)\n" -// "const mat3 yiqToRGB = mat3(1.0, 1.0, 1.0, 1.1389784, -0.3240608, -1.3176884, 0.6490692, -0.6762444, 1.7799756);\n"; + float phaseCyclesPerTick = (float)_colour_cycle_numerator / (float)(_colour_cycle_denominator * _cycles_per_line); + glUniform1f(phaseCyclesPerTickUniform, phaseCyclesPerTick); + glUniform1f(extensionUniform, extends ? ceilf(1.0f / phaseCyclesPerTick) : 0.0f); + } + extends = true; + } -// const char *const ntscFragmentShaderBody = -// "vec4 angles = vec4(phase) + vec4(-2.35619449019234, -0.78539816339745, 0.78539816339745, 2.35619449019234);\n" -// "vec4 samples = vec4(" -// " sample(srcCoordinatesVarying[0], angles.x)," -// " sample(srcCoordinatesVarying[1], angles.y)," -// " sample(srcCoordinatesVarying[2], angles.z)," -// " sample(srcCoordinatesVarying[3], angles.w)" -// ");\n" -// "\n" -// "float y = dot(vec4(0.25), samples);\n" -// "samples -= vec4(y);\n" -// "\n" -// "float i = dot(cos(angles), samples);\n" -// "float q = dot(sin(angles), samples);\n" -// "\n" -// "fragColour = 5.0 * texture(shadowMaskTexID, shadowMaskCoordinates) * vec4(yiqToRGB * vec3(y, i, q), 1.0);//sin(lateralVarying));\n"; + OpenGL::Shader *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]->bind(); -// dot(vec3(1.0/6.0, 2.0/3.0, 1.0/6.0), vec3(sample(srcCoordinatesVarying[0]), sample(srcCoordinatesVarying[0]), sample(srcCoordinatesVarying[0])));//sin(lateralVarying));\n"; -//} + GLint ticksPerFrameUniform = output_shaders[c]->get_uniform_location("ticksPerFrame"); + GLint scanNormalUniform = output_shaders[c]->get_uniform_location("scanNormal"); + GLint positionConversionUniform = output_shaders[c]->get_uniform_location("positionConversion"); + glUniform1f(ticksPerFrameUniform, (GLfloat)(_cycles_per_line * _height_of_display)); + float scan_angle = atan2f(1.0f / (float)_height_of_display, 1.0f); + float scan_normal[] = { -sinf(scan_angle), cosf(scan_angle)}; + float multiplier = (float)_cycles_per_line / ((float)_height_of_display * (float)_horizontal_scan_period); + scan_normal[0] *= multiplier; + scan_normal[1] *= multiplier; + glUniform2f(scanNormalUniform, scan_normal[0], scan_normal[1]); + + glUniform2f(positionConversionUniform, _horizontal_scan_period, _vertical_scan_period / (unsigned int)_vertical_period_divider); + } + } + + float colour_subcarrier_frequency = (float)_colour_cycle_numerator / (float)_colour_cycle_denominator; + GLint weightsUniform; + float weights[12]; + + if(composite_y_filter_shader_program) + { + SignalProcessing::FIRFilter luminance_filter(11, _cycles_per_line * 0.5f, 0.0f, colour_subcarrier_frequency * 0.5f, SignalProcessing::FIRFilter::DefaultAttenuation); + composite_y_filter_shader_program->bind(); + weightsUniform = composite_y_filter_shader_program->get_uniform_location("weights"); + luminance_filter.get_coefficients(weights); + glUniform4fv(weightsUniform, 3, weights); + } + + if(composite_chrominance_filter_shader_program) + { + SignalProcessing::FIRFilter chrominance_filter(11, _cycles_per_line * 0.5f, 0.0f, colour_subcarrier_frequency * 0.5f, SignalProcessing::FIRFilter::DefaultAttenuation); + composite_chrominance_filter_shader_program->bind(); + weightsUniform = composite_chrominance_filter_shader_program->get_uniform_location("weights"); + chrominance_filter.get_coefficients(weights); + glUniform4fv(weightsUniform, 3, weights); + } +} diff --git a/Outputs/CRT/Internals/CRTOpenGL.hpp b/Outputs/CRT/Internals/CRTOpenGL.hpp index 52c0bfa2e..dc250b15b 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.hpp +++ b/Outputs/CRT/Internals/CRTOpenGL.hpp @@ -97,6 +97,7 @@ class OpenGLOutputBuilder { std::unique_ptr filteredTexture; // receives filtered YIQ or YUV void perform_output_stage(unsigned int output_width, unsigned int output_height, OpenGL::Shader *const shader); + void set_timing_uniforms(); public: OpenGLOutputBuilder(unsigned int buffer_depth); @@ -207,16 +208,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); - inline 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) - { - _cycles_per_line = cycles_per_line; - _height_of_display = height_of_display; - _horizontal_scan_period = horizontal_scan_period; - _vertical_scan_period = vertical_scan_period; - _vertical_period_divider = vertical_period_divider; - - // TODO: update related uniforms - } + 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); uint8_t *_input_texture_data; GLuint _input_texture_array;