mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-13 22:32:03 +00:00
Extended window for picking output frequency, attempted to consolidate CRT OpenGL timing uniforms for approprate resetting.
This commit is contained in:
parent
082003ed0a
commit
929cfc49cb
@ -29,15 +29,20 @@ struct CRTDelegate: public Outputs::CRT::Delegate {
|
|||||||
int _frameCount;
|
int _frameCount;
|
||||||
int _hitCount;
|
int _hitCount;
|
||||||
BOOL _didDecideRegion;
|
BOOL _didDecideRegion;
|
||||||
|
int _batchesReceived;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)crt:(Outputs::CRT::CRT *)crt didEndBatchOfFrames:(unsigned int)numberOfFrames withUnexpectedVerticalSyncs:(unsigned int)numberOfUnexpectedSyncs {
|
- (void)crt:(Outputs::CRT::CRT *)crt didEndBatchOfFrames:(unsigned int)numberOfFrames withUnexpectedVerticalSyncs:(unsigned int)numberOfUnexpectedSyncs {
|
||||||
if(!_didDecideRegion)
|
if(!_didDecideRegion)
|
||||||
{
|
{
|
||||||
_didDecideRegion = YES;
|
_batchesReceived++;
|
||||||
if(numberOfUnexpectedSyncs >= numberOfFrames >> 1)
|
if(_batchesReceived == 2)
|
||||||
{
|
{
|
||||||
_atari2600.switch_region();
|
_didDecideRegion = YES;
|
||||||
|
if(numberOfUnexpectedSyncs >= numberOfFrames >> 1)
|
||||||
|
{
|
||||||
|
_atari2600.switch_region();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,6 +185,8 @@ void OpenGLOutputBuilder::draw_frame(unsigned int output_width, unsigned int out
|
|||||||
prepare_rgb_output_shader();
|
prepare_rgb_output_shader();
|
||||||
prepare_output_vertex_array();
|
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,
|
// 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
|
// 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.
|
// it works either way.
|
||||||
@ -762,17 +764,11 @@ std::unique_ptr<OpenGL::Shader> OpenGLOutputBuilder::prepare_intermediate_shader
|
|||||||
shader = std::unique_ptr<OpenGL::Shader>(new OpenGL::Shader(vertex_shader, fragment_shader, bindings));
|
shader = std::unique_ptr<OpenGL::Shader>(new OpenGL::Shader(vertex_shader, fragment_shader, bindings));
|
||||||
|
|
||||||
GLint texIDUniform = shader->get_uniform_location("texID");
|
GLint texIDUniform = shader->get_uniform_location("texID");
|
||||||
GLint phaseCyclesPerTickUniform = shader->get_uniform_location("phaseCyclesPerTick");
|
|
||||||
GLint outputTextureSizeUniform = shader->get_uniform_location("outputTextureSize");
|
GLint outputTextureSizeUniform = shader->get_uniform_location("outputTextureSize");
|
||||||
GLint extensionUniform = shader->get_uniform_location("extension");
|
|
||||||
|
|
||||||
shader->bind();
|
shader->bind();
|
||||||
glUniform1i(texIDUniform, (GLint)(texture_unit - GL_TEXTURE0));
|
glUniform1i(texIDUniform, (GLint)(texture_unit - GL_TEXTURE0));
|
||||||
glUniform2i(outputTextureSizeUniform, IntermediateBufferWidth, IntermediateBufferHeight);
|
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(vertex_shader);
|
||||||
free(fragment_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);
|
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);
|
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);
|
composite_chrominance_filter_shader_program = prepare_intermediate_shader("outputPosition", "uniform sampler2D texID;", get_chrominance_filter_fragment_shader(), filtered_y_texture_unit, true);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLOutputBuilder::prepare_source_vertex_array()
|
void OpenGLOutputBuilder::prepare_source_vertex_array()
|
||||||
@ -851,20 +834,7 @@ std::unique_ptr<OpenGL::Shader> OpenGLOutputBuilder::prepare_output_shader(char
|
|||||||
timestampBaseUniform = shader_program->get_uniform_location("timestampBase");
|
timestampBaseUniform = shader_program->get_uniform_location("timestampBase");
|
||||||
|
|
||||||
GLint texIDUniform = shader_program->get_uniform_location("texID");
|
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);
|
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);
|
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)
|
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 =
|
set_timing_uniforms();
|
||||||
// "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";
|
|
||||||
|
|
||||||
// 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
|
#pragma mark - Internal Configuration
|
||||||
// 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);";
|
|
||||||
|
|
||||||
// assumes y = [0,1], u and v = [-0.5, 0.5]; therefore u components are multiplied by 1.14678899082569, v by 0.8130081300813
|
void OpenGLOutputBuilder::set_timing_uniforms()
|
||||||
// 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);";
|
{
|
||||||
|
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 =
|
float phaseCyclesPerTick = (float)_colour_cycle_numerator / (float)(_colour_cycle_denominator * _cycles_per_line);
|
||||||
// "in vec2 srcCoordinatesVarying[4];\n"
|
glUniform1f(phaseCyclesPerTickUniform, phaseCyclesPerTick);
|
||||||
// "in float phase;\n"
|
glUniform1f(extensionUniform, extends ? ceilf(1.0f / phaseCyclesPerTick) : 0.0f);
|
||||||
// "\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"
|
extends = true;
|
||||||
// "const mat3 yiqToRGB = mat3(1.0, 1.0, 1.0, 1.1389784, -0.3240608, -1.3176884, 0.6490692, -0.6762444, 1.7799756);\n";
|
}
|
||||||
|
|
||||||
// const char *const ntscFragmentShaderBody =
|
OpenGL::Shader *output_shaders[] = {
|
||||||
// "vec4 angles = vec4(phase) + vec4(-2.35619449019234, -0.78539816339745, 0.78539816339745, 2.35619449019234);\n"
|
rgb_shader_program.get(),
|
||||||
// "vec4 samples = vec4("
|
composite_output_shader_program.get()
|
||||||
// " sample(srcCoordinatesVarying[0], angles.x),"
|
};
|
||||||
// " sample(srcCoordinatesVarying[1], angles.y),"
|
for(int c = 0; c < 2; c++)
|
||||||
// " sample(srcCoordinatesVarying[2], angles.z),"
|
{
|
||||||
// " sample(srcCoordinatesVarying[3], angles.w)"
|
if(output_shaders[c])
|
||||||
// ");\n"
|
{
|
||||||
// "\n"
|
output_shaders[c]->bind();
|
||||||
// "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";
|
|
||||||
|
|
||||||
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -97,6 +97,7 @@ class OpenGLOutputBuilder {
|
|||||||
std::unique_ptr<OpenGL::TextureTarget> filteredTexture; // receives filtered YIQ or YUV
|
std::unique_ptr<OpenGL::TextureTarget> filteredTexture; // receives filtered YIQ or YUV
|
||||||
|
|
||||||
void perform_output_stage(unsigned int output_width, unsigned int output_height, OpenGL::Shader *const shader);
|
void perform_output_stage(unsigned int output_width, unsigned int output_height, OpenGL::Shader *const shader);
|
||||||
|
void set_timing_uniforms();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
OpenGLOutputBuilder(unsigned int buffer_depth);
|
OpenGLOutputBuilder(unsigned int buffer_depth);
|
||||||
@ -207,16 +208,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);
|
||||||
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)
|
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
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *_input_texture_data;
|
uint8_t *_input_texture_data;
|
||||||
GLuint _input_texture_array;
|
GLuint _input_texture_array;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user