diff --git a/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm b/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm index 40dc5fb53..eaa34017b 100644 --- a/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm +++ b/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm @@ -98,7 +98,8 @@ struct Uniforms { simd::float3x3 fromRGB; float zoom; simd::float2 offset; - simd::float3 firCoefficients[8]; + simd::float3 chromaCoefficients[8]; + float lumaCoefficients[8]; float radiansPerPixel; float cyclesMultiplier; }; @@ -560,32 +561,30 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget; _compositionRenderPass.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.5, 0.5, 1.0); // Create suitable FIR filters. - auto *const firCoefficients = uniforms()->firCoefficients; _lineBufferPixelsPerLine = NSUInteger(modals.cycles_per_line) * NSUInteger(uniforms()->cyclesMultiplier); const float colourCyclesPerLine = float(modals.colour_cycle_numerator) / float(modals.colour_cycle_denominator); - if(isSVideoOutput) { - // In S-Video, don't filter luminance. - for(size_t c = 0; c < 7; ++c) { - firCoefficients[c].x = 0.0f; + // Generate the chrominance filter. + { + auto *const chromaCoefficients = uniforms()->chromaCoefficients; + SignalProcessing::FIRFilter chrominancefilter(15, float(_lineBufferPixelsPerLine), 0.0f, colourCyclesPerLine * (isSVideoOutput ? 1.0f : 1.0f)); + const auto calculatedCoefficients = chrominancefilter.get_coefficients(); + for(size_t c = 0; c < 8; ++c) { + chromaCoefficients[c].y = chromaCoefficients[c].z = calculatedCoefficients[c] * (isSVideoOutput ? 4.0f : 8.0f); + chromaCoefficients[c].x = 0.0f; } - firCoefficients[7].x = 1.0f; - } else { - // In composite, filter luminance gently. + chromaCoefficients[7].x = 1.0f; + } + + // Generate the luminance filter. + { + auto *const luminanceCoefficients = uniforms()->lumaCoefficients; SignalProcessing::FIRFilter luminancefilter(15, float(_lineBufferPixelsPerLine), 0.0f, colourCyclesPerLine * 0.5f); const auto calculatedCoefficients = luminancefilter.get_coefficients(); - for(size_t c = 0; c < 8; ++c) { - firCoefficients[c].x = calculatedCoefficients[c]; - } - } - - // Whether S-Video or composite, apply the same relatively strong filter to colour channels. - SignalProcessing::FIRFilter chrominancefilter(15, float(_lineBufferPixelsPerLine), 0.0f, colourCyclesPerLine * (isSVideoOutput ? 1.0f : 0.25f)); - const auto calculatedCoefficients = chrominancefilter.get_coefficients(); - for(size_t c = 0; c < 8; ++c) { - firCoefficients[c].y = firCoefficients[c].z = calculatedCoefficients[c] * (isSVideoOutput ? 4.0f : 4.0f); + memcpy(luminanceCoefficients, calculatedCoefficients.data(), sizeof(float)*8); } + // Store radians per pixel. TODO: is this now orphaned? Should I keep it anyway? uniforms()->radiansPerPixel = (colourCyclesPerLine * 3.141592654f * 2.0f) / float(_lineBufferPixelsPerLine); } diff --git a/OSBindings/Mac/Clock Signal/ScanTarget/ScanTarget.metal b/OSBindings/Mac/Clock Signal/ScanTarget/ScanTarget.metal index af86c6318..5e5020a61 100644 --- a/OSBindings/Mac/Clock Signal/ScanTarget/ScanTarget.metal +++ b/OSBindings/Mac/Clock Signal/ScanTarget/ScanTarget.metal @@ -32,9 +32,13 @@ struct Uniforms { float zoom; float2 offset; - // Describes the FIR filter in use; it'll be 15 coefficients but they're + // Describes the FIR filter in use for chroma filtering; it'll be + // 15 coefficients but they're symmetrical around the centre. + float3 chromaCoefficients[8]; + + // Describes the FIR filter in use for luma filtering; also 15 coefficients // symmetrical around the centre. - float3 firCoefficients[8]; + float lumaCoefficients[8]; // Maps from pixel offsets into the composition buffer to angular difference. float radiansPerPixel; @@ -386,7 +390,7 @@ kernel void filterChromaKernel( texture2d inTexture [[textu inTexture.read(gid + uint2(14, offset)) - moveToZero, }; -#define Sample(x, y) uniforms.firCoefficients[y] * rawSamples[x].rgb +#define Sample(x, y) uniforms.chromaCoefficients[y] * rawSamples[x].rgb const float3 colour = Sample(0, 0) + Sample(1, 1) + Sample(2, 2) + Sample(3, 3) + Sample(4, 4) + Sample(5, 5) + Sample(6, 6) + Sample(7, 7) + @@ -411,32 +415,36 @@ kernel void separateLumaKernel( texture2d inTexture [[textu uint2 gid [[thread_position_in_grid]], constant Uniforms &uniforms [[buffer(0)]], constant int &offset [[buffer(1)]]) { - // TODO! - constexpr float4 moveToZero = float4(0.0f, 0.5f, 0.5f, 0.0f); - const float4 rawSamples[] = { - inTexture.read(gid + uint2(0, offset)) - moveToZero, - inTexture.read(gid + uint2(1, offset)) - moveToZero, - inTexture.read(gid + uint2(2, offset)) - moveToZero, - inTexture.read(gid + uint2(3, offset)) - moveToZero, - inTexture.read(gid + uint2(4, offset)) - moveToZero, - inTexture.read(gid + uint2(5, offset)) - moveToZero, - inTexture.read(gid + uint2(6, offset)) - moveToZero, - inTexture.read(gid + uint2(7, offset)) - moveToZero, - inTexture.read(gid + uint2(8, offset)) - moveToZero, - inTexture.read(gid + uint2(9, offset)) - moveToZero, - inTexture.read(gid + uint2(10, offset)) - moveToZero, - inTexture.read(gid + uint2(11, offset)) - moveToZero, - inTexture.read(gid + uint2(12, offset)) - moveToZero, - inTexture.read(gid + uint2(13, offset)) - moveToZero, - inTexture.read(gid + uint2(14, offset)) - moveToZero, + const float4 centreSample = inTexture.read(gid + uint2(7, offset)); + const float rawSamples[] = { + inTexture.read(gid + uint2(0, offset)).r, + inTexture.read(gid + uint2(1, offset)).r, + inTexture.read(gid + uint2(2, offset)).r, + inTexture.read(gid + uint2(3, offset)).r, + inTexture.read(gid + uint2(4, offset)).r, + inTexture.read(gid + uint2(5, offset)).r, + inTexture.read(gid + uint2(6, offset)).r, + centreSample.r, + inTexture.read(gid + uint2(8, offset)).r, + inTexture.read(gid + uint2(9, offset)).r, + inTexture.read(gid + uint2(10, offset)).r, + inTexture.read(gid + uint2(11, offset)).r, + inTexture.read(gid + uint2(12, offset)).r, + inTexture.read(gid + uint2(13, offset)).r, + inTexture.read(gid + uint2(14, offset)).r, }; -#define Sample(x, y) uniforms.firCoefficients[y] * rawSamples[x].rgb - const float3 colour = +#define Sample(x, y) uniforms.lumaCoefficients[y] * rawSamples[x] + const float luminance = Sample(0, 0) + Sample(1, 1) + Sample(2, 2) + Sample(3, 3) + Sample(4, 4) + Sample(5, 5) + Sample(6, 6) + Sample(7, 7) + Sample(8, 6) + Sample(9, 5) + Sample(10, 4) + Sample(11, 3) + Sample(12, 2) + Sample(13, 1) + Sample(14, 0); #undef Sample - outTexture.write(float4(uniforms.toRGB * colour, 1.0f), gid + uint2(7, offset)); + outTexture.write(float4( + luminance, + (centreSample.gb - float2(0.5f)) * (centreSample.r - luminance) + float2(0.5f), + 1.0f + ), + gid + uint2(7, offset)); }