diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index be274da01..260a1d0c5 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -533,7 +533,7 @@ void OpenGLOutputBuilder::set_timing_uniforms() } 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 * 0.5f); + if(composite_y_filter_shader_program) composite_y_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency - 90.0f); if(composite_chrominance_filter_shader_program) composite_chrominance_filter_shader_program->set_filter_coefficients(_cycles_per_line, colour_subcarrier_frequency * 0.5f); _output_mutex->unlock(); } diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp index e7e4153f2..006ca60be 100644 --- a/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp +++ b/Outputs/CRT/Internals/Shaders/IntermediateShader.cpp @@ -44,6 +44,7 @@ std::unique_ptr IntermediateShader::make_shader(const char * "uniform ivec2 outputTextureSize;" "uniform float extension;" "uniform %s texID;" + "uniform float offsets[5];" "out vec2 phaseAndAmplitudeVarying;" "out vec2 inputPositionsVarying[11];" @@ -60,17 +61,17 @@ std::unique_ptr IntermediateShader::make_shader(const char * "iInputPositionVarying = extendedInputPosition;" "vec2 mappedInputPosition = (extendedInputPosition + vec2(0.0, 0.5)) / textureSize;" - "inputPositionsVarying[0] = mappedInputPosition - (vec2(10.0, 0.0) / textureSize);" - "inputPositionsVarying[1] = mappedInputPosition - (vec2(8.0, 0.0) / textureSize);" - "inputPositionsVarying[2] = mappedInputPosition - (vec2(6.0, 0.0) / textureSize);" - "inputPositionsVarying[3] = mappedInputPosition - (vec2(4.0, 0.0) / textureSize);" - "inputPositionsVarying[4] = mappedInputPosition - (vec2(2.0, 0.0) / textureSize);" + "inputPositionsVarying[0] = mappedInputPosition - (vec2(offsets[4], 0.0) / textureSize);" + "inputPositionsVarying[1] = mappedInputPosition - (vec2(offsets[3], 0.0) / textureSize);" + "inputPositionsVarying[2] = mappedInputPosition - (vec2(offsets[2], 0.0) / textureSize);" + "inputPositionsVarying[3] = mappedInputPosition - (vec2(offsets[1], 0.0) / textureSize);" + "inputPositionsVarying[4] = mappedInputPosition - (vec2(offsets[0], 0.0) / textureSize);" "inputPositionsVarying[5] = mappedInputPosition;" - "inputPositionsVarying[6] = mappedInputPosition + (vec2(2.0, 0.0) / textureSize);" - "inputPositionsVarying[7] = mappedInputPosition + (vec2(4.0, 0.0) / textureSize);" - "inputPositionsVarying[8] = mappedInputPosition + (vec2(6.0, 0.0) / textureSize);" - "inputPositionsVarying[9] = mappedInputPosition + (vec2(8.0, 0.0) / textureSize);" - "inputPositionsVarying[10] = mappedInputPosition + (vec2(10.0, 0.0) / textureSize);" + "inputPositionsVarying[6] = mappedInputPosition + (vec2(offsets[0], 0.0) / textureSize);" + "inputPositionsVarying[7] = mappedInputPosition + (vec2(offsets[1], 0.0) / textureSize);" + "inputPositionsVarying[8] = mappedInputPosition + (vec2(offsets[2], 0.0) / textureSize);" + "inputPositionsVarying[9] = mappedInputPosition + (vec2(offsets[3], 0.0) / textureSize);" + "inputPositionsVarying[10] = mappedInputPosition + (vec2(offsets[4], 0.0) / textureSize);" "delayLinePositionVarying = mappedInputPosition - vec2(0.0, 1.0);" "phaseAndAmplitudeVarying.x = (phaseCyclesPerTick * (extendedOutputPosition.x - phaseTime) + phaseAmplitudeAndOffset.x) * 2.0 * 3.141592654;" @@ -90,6 +91,7 @@ std::unique_ptr IntermediateShader::make_shader(const char * shader->weightsUniform = shader->get_uniform_location("weights"); shader->rgbToLumaChromaUniform = shader->get_uniform_location("rgbToLumaChroma"); shader->lumaChromaToRGBUniform = shader->get_uniform_location("lumaChromaToRGB"); + shader->offsetsUniform = shader->get_uniform_location("offsets"); return shader; } @@ -264,14 +266,54 @@ void IntermediateShader::set_filter_coefficients(float sampling_rate, float cuto { bind(); - sampling_rate *= 0.5f; - cutoff_frequency *= 0.5f; - + // The process below: the source texture will have bilinear filtering enabled; so by + // sampling at non-integral offsets from the centre the shader can get a weighted sum + // of two source pixels, then scale that once, to do two taps per sample. However + // that works only if the two coefficients being joined have the same sign. So the + // number of usable taps is between 11 and 21 depending on the values that come out. + // Perform a linear search for the highest number of taps we can use with 11 samples. float weights[12]; - weights[11] = 0.0f; - SignalProcessing::FIRFilter luminance_filter(11, sampling_rate, 0.0f, cutoff_frequency, SignalProcessing::FIRFilter::DefaultAttenuation); - luminance_filter.get_coefficients(weights); + float offsets[5]; + unsigned int taps = 21; + while(1) + { + float coefficients[21]; + SignalProcessing::FIRFilter luminance_filter(taps, sampling_rate, 0.0f, cutoff_frequency, SignalProcessing::FIRFilter::DefaultAttenuation); + luminance_filter.get_coefficients(coefficients); + + int sample = 0; + int c = 0; + memset(weights, 0, sizeof(float)*12); + while(c < (taps >> 1) && sample < 5) + { + if((coefficients[c] < 0.0f) == (coefficients[c+1] < 0.0f) && c+1 < (taps >> 1)) + { + weights[sample] = coefficients[c] + coefficients[c+1]; + offsets[sample] = (float)c + 1.0f + (coefficients[c+1] / weights[sample]); + c += 2; + } + else + { + offsets[sample] = (float)c + 1.0f; + weights[sample] = coefficients[c]; + c++; + } + sample ++; + } + if(c == (taps >> 1)) + { + weights[sample] = coefficients[c]; + for(int c = 0; c < sample; c++) + { + weights[sample+c+1] = weights[sample-c-1]; + } + break; + } + taps -= 2; + } + glUniform4fv(weightsUniform, 3, weights); + glUniform1fv(offsetsUniform, 5, offsets); } void IntermediateShader::set_phase_cycles_per_sample(float phase_cycles_per_sample, bool extend_runs_to_full_cycle) diff --git a/Outputs/CRT/Internals/Shaders/IntermediateShader.hpp b/Outputs/CRT/Internals/Shaders/IntermediateShader.hpp index a955045ae..8b75a51df 100644 --- a/Outputs/CRT/Internals/Shaders/IntermediateShader.hpp +++ b/Outputs/CRT/Internals/Shaders/IntermediateShader.hpp @@ -74,6 +74,7 @@ private: GLint extensionUniform; GLint rgbToLumaChromaUniform; GLint lumaChromaToRGBUniform; + GLint offsetsUniform; }; } diff --git a/Outputs/CRT/Internals/TextureTarget.cpp b/Outputs/CRT/Internals/TextureTarget.cpp index a57eaa26c..f575b3d23 100644 --- a/Outputs/CRT/Internals/TextureTarget.cpp +++ b/Outputs/CRT/Internals/TextureTarget.cpp @@ -18,8 +18,8 @@ TextureTarget::TextureTarget(GLsizei width, GLsizei height) : _width(width), _he glGenTextures(1, &_texture); glBindTexture(GL_TEXTURE_2D, _texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);