From c40d858f02de29136a5a8c11c365e3cad8a46173 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Aug 2020 20:54:46 -0400 Subject: [PATCH] Switches back to angular stuff at input resolution; ensures all S-Video modes work. Now to roll back onto composite. Fingers crossed! --- .../Clock Signal/ScanTarget/CSScanTarget.mm | 31 ++++----- .../Clock Signal/ScanTarget/ScanTarget.metal | 63 +++++++++++++++---- Outputs/ScanTargets/BufferingScanTarget.cpp | 3 + 3 files changed, 65 insertions(+), 32 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm b/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm index 71e3c04d9..9baa9b9fe 100644 --- a/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm +++ b/OSBindings/Mac/Clock Signal/ScanTarget/CSScanTarget.mm @@ -15,22 +15,6 @@ #include "BufferingScanTarget.hpp" #include "FIRFilter.hpp" -/* - Pipelines in use: - - RGB input -> RGB display: - just output it. - - RGB input -> angular: - Composition in the display colour space (YIQ or YUV), conversion to and from S-Video or composite per output pixel. - - Luminance/Phase -> angular: - Composition, conversion per output pixel. - - Luminance -> composite: - Composition, conversion per input pixel. -*/ - namespace { struct Uniforms { @@ -43,6 +27,7 @@ struct Uniforms { simd::float2 offset; simd::float3 firCoefficients[8]; float radiansPerPixel; + float cyclesMultiplier; }; constexpr size_t NumBufferedScans = 2048; @@ -374,6 +359,14 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget; if(samplerDictionary[c].directRGB) assert([library newFunctionWithName:samplerDictionary[c].directRGB]); } #endif + // Pick a suitable cycle multiplier. + uniforms()->cyclesMultiplier = 1.0f; + if(_isUsingCompositionPipeline) { + const float minimumSize = 4.0f * float(modals.colour_cycle_numerator) / float(modals.colour_cycle_denominator); + while(uniforms()->cyclesMultiplier * modals.cycles_per_line < minimumSize) { + uniforms()->cyclesMultiplier += 1.0f; + } + } // Build the composition pipeline if one is in use. const bool isSVideoOutput = modals.display_type == Outputs::Display::DisplayType::SVideo; @@ -392,7 +385,7 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget; _compositionRenderPass.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.5, 0.5, 1.0); auto *const firCoefficients = uniforms()->firCoefficients; - const float cyclesPerLine = float(modals.cycles_per_line); + const float cyclesPerLine = float(modals.cycles_per_line) * uniforms()->cyclesMultiplier; const float colourCyclesPerLine = float(modals.colour_cycle_numerator) / float(modals.colour_cycle_denominator); if(isSVideoOutput) { @@ -414,7 +407,7 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget; SignalProcessing::FIRFilter chrominancefilter(15, cyclesPerLine, 0.0f, colourCyclesPerLine); const auto calculatedCoefficients = chrominancefilter.get_coefficients(); for(size_t c = 0; c < 8; ++c) { - firCoefficients[c].y = firCoefficients[c].z = calculatedCoefficients[c] * (isSVideoOutput ? 2.0f : 1.0f); + firCoefficients[c].y = firCoefficients[c].z = calculatedCoefficients[c] * (isSVideoOutput ? 4.0f : 1.0f); } uniforms()->radiansPerPixel = (colourCyclesPerLine * 3.141592654f * 2.0f) / cyclesPerLine; @@ -425,7 +418,7 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget; pipelineDescriptor.vertexFunction = [library newFunctionWithName:_isUsingCompositionPipeline ? @"lineToDisplay" : @"scanToDisplay"]; if(_isUsingCompositionPipeline) { - pipelineDescriptor.fragmentFunction = [library newFunctionWithName:isSVideoOutput ? @"filterSVideoFragment" : @"filterCompositeFragment"]; + pipelineDescriptor.fragmentFunction = [library newFunctionWithName:@"filterFragment"]; } else { const bool isRGBOutput = modals.display_type == Outputs::Display::DisplayType::RGB; pipelineDescriptor.fragmentFunction = diff --git a/OSBindings/Mac/Clock Signal/ScanTarget/ScanTarget.metal b/OSBindings/Mac/Clock Signal/ScanTarget/ScanTarget.metal index 6fcbeb812..490d6be9d 100644 --- a/OSBindings/Mac/Clock Signal/ScanTarget/ScanTarget.metal +++ b/OSBindings/Mac/Clock Signal/ScanTarget/ScanTarget.metal @@ -37,6 +37,9 @@ struct Uniforms { // Maps from pixel offsets into the composition buffer to angular difference. float radiansPerPixel; + + // Applies a multiplication to all cyclesSinceRetrace values. + float cycleMultiplier; }; namespace { @@ -86,13 +89,13 @@ struct SourceInterpolator { // MARK: - Vertex shaders. -float2 textureLocation(constant Line *line, float offset) { +float2 textureLocation(constant Line *line, float offset, constant Uniforms &uniforms) { return float2( - mix(line->endPoints[0].cyclesSinceRetrace, line->endPoints[1].cyclesSinceRetrace, offset), + uniforms.cycleMultiplier * mix(line->endPoints[0].cyclesSinceRetrace, line->endPoints[1].cyclesSinceRetrace, offset), line->line); } -float2 textureLocation(constant Scan *scan, float offset) { +float2 textureLocation(constant Scan *scan, float offset, constant Uniforms &) { return float2( mix(scan->endPoints[0].dataOffset, scan->endPoints[1].dataOffset, offset), scan->dataY); @@ -143,7 +146,7 @@ template SourceInterpolator toDisplay( 0.0f, 1.0f ); - output.textureCoordinates = textureLocation(&inputs[instanceID], float((vertexID&2) >> 1)); + output.textureCoordinates = textureLocation(&inputs[instanceID], float((vertexID&2) >> 1), uniforms); return output; } @@ -175,7 +178,7 @@ vertex SourceInterpolator scanToComposition( constant Uniforms &uniforms [[buffe SourceInterpolator result; // Populate result as if direct texture access were available. - result.position.x = mix(scans[instanceID].endPoints[0].cyclesSinceRetrace, scans[instanceID].endPoints[1].cyclesSinceRetrace, float(vertexID)); + result.position.x = uniforms.cycleMultiplier * mix(scans[instanceID].endPoints[0].cyclesSinceRetrace, scans[instanceID].endPoints[1].cyclesSinceRetrace, float(vertexID)); result.position.y = scans[instanceID].line; result.position.zw = float2(0.0f, 1.0f); @@ -223,6 +226,7 @@ fragment float4 samplePhaseLinkedLuminance8(SourceInterpolator vert [[stage_in]] // The luminance/phase format can produce either composite or S-Video. +/// @returns A 2d vector comprised where .x = luminance; .y = chroma. float2 convertLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d texture [[texture(0)]]) { const auto luminancePhase = texture.sample(standardSampler, vert.textureCoordinates).rg; const float phaseOffset = 3.141592654 * 4.0 * luminancePhase.g; @@ -230,8 +234,12 @@ float2 convertLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d texture [[texture(0)]]) { - return convertLuminance8Phase8(vert, texture); +fragment float4 sampleLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d texture [[texture(0)]]) { + const float2 luminanceChroma = convertLuminance8Phase8(vert, texture); + const float2 qam = quadrature(vert.colourPhase) * 0.5f; + return float4(luminanceChroma.r, + float2(0.5f) + luminanceChroma.g*qam, + 1.0); } fragment float4 compositeSampleLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d texture [[texture(0)]]) { @@ -279,7 +287,13 @@ float3 convertRed1Green1Blue1(SourceInterpolator vert, texture2d texture \ fragment float4 svideoSample##name(SourceInterpolator vert [[stage_in]], texture2d texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \ const auto colour = uniforms.fromRGB * convert##name(vert, texture); \ - return float4(colour, 1.0); \ + const float2 qam = quadrature(vert.colourPhase); \ + const float chroma = dot(colour.gb, qam); \ + return float4( \ + colour.r, \ + float2(0.5f) + chroma*qam*0.5f, \ + 1.0f \ + ); \ } \ \ fragment float4 compositeSample##name(SourceInterpolator vert [[stage_in]], texture2d texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \ @@ -344,7 +358,7 @@ fragment float4 clearFragment() { // MARK: - Conversion fragment shaders -fragment float4 filterSVideoFragment(SourceInterpolator vert [[stage_in]], texture2d texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { +/*fragment float4 filterSVideoFragment(SourceInterpolator vert [[stage_in]], texture2d texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { #define Sample(x) texture.sample(standardSampler, vert.textureCoordinates + float2(x, 0.0f)) float4 rawSamples[] = { Sample(-7), Sample(-6), Sample(-5), Sample(-4), Sample(-3), Sample(-2), Sample(-1), @@ -382,12 +396,35 @@ fragment float4 filterSVideoFragment(SourceInterpolator vert [[stage_in]], textu Sample(6, 8, 1) + Sample(5, 9, 2) + Sample(4, 10, 3) + Sample(3, 11, 4) + Sample(2, 12, 5) + Sample(1, 13, 6) + Sample(0, 14, 7); +#undef Sample + + return float4(uniforms.toRGB * colour, 1.0f); +}*/ + +fragment float4 filterFragment(SourceInterpolator vert [[stage_in]], texture2d texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { +#define Sample(x) texture.sample(standardSampler, vert.textureCoordinates + float2(x, 0.0f)) - float4(0.0f, 0.5f, 0.5f, 0.0f) + float4 rawSamples[] = { + Sample(-7), Sample(-6), Sample(-5), Sample(-4), Sample(-3), Sample(-2), Sample(-1), + Sample(0), + Sample(1), Sample(2), Sample(3), Sample(4), Sample(5), Sample(6), Sample(7), + }; +#undef Sample + +#define Sample(c, o, a) uniforms.firCoefficients[c] * rawSamples[o].rgb + + const float3 colour = + Sample(0, 0, -7) + Sample(1, 1, -6) + Sample(2, 2, -5) + Sample(3, 3, -4) + + Sample(4, 4, -3) + Sample(5, 5, -2) + Sample(6, 6, -1) + + Sample(7, 7, 0) + + Sample(6, 8, 1) + Sample(5, 9, 2) + Sample(4, 10, 3) + + Sample(3, 11, 4) + Sample(2, 12, 5) + Sample(1, 13, 6) + Sample(0, 14, 7); + #undef Sample return float4(uniforms.toRGB * colour, 1.0f); } -fragment float4 filterCompositeFragment(SourceInterpolator vert [[stage_in]], texture2d texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { - // TODO. - return float4(1.0); -} +//fragment float4 filterCompositeFragment(SourceInterpolator vert [[stage_in]], texture2d texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { +// // TODO. +// return float4(1.0); +//} diff --git a/Outputs/ScanTargets/BufferingScanTarget.cpp b/Outputs/ScanTargets/BufferingScanTarget.cpp index 5c8d688e2..3ee12e682 100644 --- a/Outputs/ScanTargets/BufferingScanTarget.cpp +++ b/Outputs/ScanTargets/BufferingScanTarget.cpp @@ -264,6 +264,9 @@ void BufferingScanTarget::will_change_owner() { std::lock_guard lock_guard(producer_mutex_); allocation_has_failed_ = true; vended_scan_ = nullptr; +#ifdef DEBUG + data_is_allocated_ = false; +#endif } const Outputs::Display::Metrics &BufferingScanTarget::display_metrics() {