mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-29 16:55:59 +00:00
Switches back to angular stuff at input resolution; ensures all S-Video modes work.
Now to roll back onto composite. Fingers crossed!
This commit is contained in:
parent
3d564d85fd
commit
c40d858f02
@ -15,22 +15,6 @@
|
|||||||
#include "BufferingScanTarget.hpp"
|
#include "BufferingScanTarget.hpp"
|
||||||
#include "FIRFilter.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 {
|
namespace {
|
||||||
|
|
||||||
struct Uniforms {
|
struct Uniforms {
|
||||||
@ -43,6 +27,7 @@ struct Uniforms {
|
|||||||
simd::float2 offset;
|
simd::float2 offset;
|
||||||
simd::float3 firCoefficients[8];
|
simd::float3 firCoefficients[8];
|
||||||
float radiansPerPixel;
|
float radiansPerPixel;
|
||||||
|
float cyclesMultiplier;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr size_t NumBufferedScans = 2048;
|
constexpr size_t NumBufferedScans = 2048;
|
||||||
@ -374,6 +359,14 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
|||||||
if(samplerDictionary[c].directRGB) assert([library newFunctionWithName:samplerDictionary[c].directRGB]);
|
if(samplerDictionary[c].directRGB) assert([library newFunctionWithName:samplerDictionary[c].directRGB]);
|
||||||
}
|
}
|
||||||
#endif
|
#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.
|
// Build the composition pipeline if one is in use.
|
||||||
const bool isSVideoOutput = modals.display_type == Outputs::Display::DisplayType::SVideo;
|
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);
|
_compositionRenderPass.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.5, 0.5, 1.0);
|
||||||
|
|
||||||
auto *const firCoefficients = uniforms()->firCoefficients;
|
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);
|
const float colourCyclesPerLine = float(modals.colour_cycle_numerator) / float(modals.colour_cycle_denominator);
|
||||||
|
|
||||||
if(isSVideoOutput) {
|
if(isSVideoOutput) {
|
||||||
@ -414,7 +407,7 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
|||||||
SignalProcessing::FIRFilter chrominancefilter(15, cyclesPerLine, 0.0f, colourCyclesPerLine);
|
SignalProcessing::FIRFilter chrominancefilter(15, cyclesPerLine, 0.0f, colourCyclesPerLine);
|
||||||
const auto calculatedCoefficients = chrominancefilter.get_coefficients();
|
const auto calculatedCoefficients = chrominancefilter.get_coefficients();
|
||||||
for(size_t c = 0; c < 8; ++c) {
|
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;
|
uniforms()->radiansPerPixel = (colourCyclesPerLine * 3.141592654f * 2.0f) / cyclesPerLine;
|
||||||
@ -425,7 +418,7 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
|||||||
pipelineDescriptor.vertexFunction = [library newFunctionWithName:_isUsingCompositionPipeline ? @"lineToDisplay" : @"scanToDisplay"];
|
pipelineDescriptor.vertexFunction = [library newFunctionWithName:_isUsingCompositionPipeline ? @"lineToDisplay" : @"scanToDisplay"];
|
||||||
|
|
||||||
if(_isUsingCompositionPipeline) {
|
if(_isUsingCompositionPipeline) {
|
||||||
pipelineDescriptor.fragmentFunction = [library newFunctionWithName:isSVideoOutput ? @"filterSVideoFragment" : @"filterCompositeFragment"];
|
pipelineDescriptor.fragmentFunction = [library newFunctionWithName:@"filterFragment"];
|
||||||
} else {
|
} else {
|
||||||
const bool isRGBOutput = modals.display_type == Outputs::Display::DisplayType::RGB;
|
const bool isRGBOutput = modals.display_type == Outputs::Display::DisplayType::RGB;
|
||||||
pipelineDescriptor.fragmentFunction =
|
pipelineDescriptor.fragmentFunction =
|
||||||
|
@ -37,6 +37,9 @@ struct Uniforms {
|
|||||||
|
|
||||||
// Maps from pixel offsets into the composition buffer to angular difference.
|
// Maps from pixel offsets into the composition buffer to angular difference.
|
||||||
float radiansPerPixel;
|
float radiansPerPixel;
|
||||||
|
|
||||||
|
// Applies a multiplication to all cyclesSinceRetrace values.
|
||||||
|
float cycleMultiplier;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -86,13 +89,13 @@ struct SourceInterpolator {
|
|||||||
|
|
||||||
// MARK: - Vertex shaders.
|
// MARK: - Vertex shaders.
|
||||||
|
|
||||||
float2 textureLocation(constant Line *line, float offset) {
|
float2 textureLocation(constant Line *line, float offset, constant Uniforms &uniforms) {
|
||||||
return float2(
|
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);
|
line->line);
|
||||||
}
|
}
|
||||||
|
|
||||||
float2 textureLocation(constant Scan *scan, float offset) {
|
float2 textureLocation(constant Scan *scan, float offset, constant Uniforms &) {
|
||||||
return float2(
|
return float2(
|
||||||
mix(scan->endPoints[0].dataOffset, scan->endPoints[1].dataOffset, offset),
|
mix(scan->endPoints[0].dataOffset, scan->endPoints[1].dataOffset, offset),
|
||||||
scan->dataY);
|
scan->dataY);
|
||||||
@ -143,7 +146,7 @@ template <typename Input> SourceInterpolator toDisplay(
|
|||||||
0.0f,
|
0.0f,
|
||||||
1.0f
|
1.0f
|
||||||
);
|
);
|
||||||
output.textureCoordinates = textureLocation(&inputs[instanceID], float((vertexID&2) >> 1));
|
output.textureCoordinates = textureLocation(&inputs[instanceID], float((vertexID&2) >> 1), uniforms);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@ -175,7 +178,7 @@ vertex SourceInterpolator scanToComposition( constant Uniforms &uniforms [[buffe
|
|||||||
SourceInterpolator result;
|
SourceInterpolator result;
|
||||||
|
|
||||||
// Populate result as if direct texture access were available.
|
// 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.y = scans[instanceID].line;
|
||||||
result.position.zw = float2(0.0f, 1.0f);
|
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.
|
// 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<float> texture [[texture(0)]]) {
|
float2 convertLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d<float> texture [[texture(0)]]) {
|
||||||
const auto luminancePhase = texture.sample(standardSampler, vert.textureCoordinates).rg;
|
const auto luminancePhase = texture.sample(standardSampler, vert.textureCoordinates).rg;
|
||||||
const float phaseOffset = 3.141592654 * 4.0 * luminancePhase.g;
|
const float phaseOffset = 3.141592654 * 4.0 * luminancePhase.g;
|
||||||
@ -230,8 +234,12 @@ float2 convertLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d<f
|
|||||||
return float2(luminancePhase.r, rawChroma);
|
return float2(luminancePhase.r, rawChroma);
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment float2 sampleLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d<float> texture [[texture(0)]]) {
|
fragment float4 sampleLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d<float> texture [[texture(0)]]) {
|
||||||
return convertLuminance8Phase8(vert, texture);
|
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<float> texture [[texture(0)]]) {
|
fragment float4 compositeSampleLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d<float> texture [[texture(0)]]) {
|
||||||
@ -279,7 +287,13 @@ float3 convertRed1Green1Blue1(SourceInterpolator vert, texture2d<ushort> texture
|
|||||||
\
|
\
|
||||||
fragment float4 svideoSample##name(SourceInterpolator vert [[stage_in]], texture2d<pixelType> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
fragment float4 svideoSample##name(SourceInterpolator vert [[stage_in]], texture2d<pixelType> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
||||||
const auto colour = uniforms.fromRGB * convert##name(vert, texture); \
|
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<pixelType> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
fragment float4 compositeSample##name(SourceInterpolator vert [[stage_in]], texture2d<pixelType> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
||||||
@ -344,7 +358,7 @@ fragment float4 clearFragment() {
|
|||||||
|
|
||||||
// MARK: - Conversion fragment shaders
|
// MARK: - Conversion fragment shaders
|
||||||
|
|
||||||
fragment float4 filterSVideoFragment(SourceInterpolator vert [[stage_in]], texture2d<float> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) {
|
/*fragment float4 filterSVideoFragment(SourceInterpolator vert [[stage_in]], texture2d<float> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) {
|
||||||
#define Sample(x) texture.sample(standardSampler, vert.textureCoordinates + float2(x, 0.0f))
|
#define Sample(x) texture.sample(standardSampler, vert.textureCoordinates + float2(x, 0.0f))
|
||||||
float4 rawSamples[] = {
|
float4 rawSamples[] = {
|
||||||
Sample(-7), Sample(-6), Sample(-5), Sample(-4), Sample(-3), Sample(-2), Sample(-1),
|
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(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);
|
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<float> 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
|
#undef Sample
|
||||||
|
|
||||||
return float4(uniforms.toRGB * colour, 1.0f);
|
return float4(uniforms.toRGB * colour, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment float4 filterCompositeFragment(SourceInterpolator vert [[stage_in]], texture2d<float> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) {
|
//fragment float4 filterCompositeFragment(SourceInterpolator vert [[stage_in]], texture2d<float> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) {
|
||||||
// TODO.
|
// // TODO.
|
||||||
return float4(1.0);
|
// return float4(1.0);
|
||||||
}
|
//}
|
||||||
|
@ -264,6 +264,9 @@ void BufferingScanTarget::will_change_owner() {
|
|||||||
std::lock_guard lock_guard(producer_mutex_);
|
std::lock_guard lock_guard(producer_mutex_);
|
||||||
allocation_has_failed_ = true;
|
allocation_has_failed_ = true;
|
||||||
vended_scan_ = nullptr;
|
vended_scan_ = nullptr;
|
||||||
|
#ifdef DEBUG
|
||||||
|
data_is_allocated_ = false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
const Outputs::Display::Metrics &BufferingScanTarget::display_metrics() {
|
const Outputs::Display::Metrics &BufferingScanTarget::display_metrics() {
|
||||||
|
Loading…
Reference in New Issue
Block a user