mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-16 05:27:43 +00:00
Completes the set of with/without gamma, and ensures correct alpha selection.
Also culls some other repetitive TODOs.
This commit is contained in:
@@ -25,8 +25,6 @@
|
|||||||
Source data is converted to 32bpp RGB or to composite directly from its input, at output resolution.
|
Source data is converted to 32bpp RGB or to composite directly from its input, at output resolution.
|
||||||
Gamma correction is applied unless the inputs are 1bpp (e.g. Macintosh-style black/white, TTL-style RGB).
|
Gamma correction is applied unless the inputs are 1bpp (e.g. Macintosh-style black/white, TTL-style RGB).
|
||||||
|
|
||||||
TODO: filtering when the output size is 'small'.
|
|
||||||
|
|
||||||
S-Video
|
S-Video
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@@ -364,8 +362,6 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (void)updateSizeBuffersToSize:(CGSize)size {
|
- (void)updateSizeBuffersToSize:(CGSize)size {
|
||||||
// TODO: consider multisampling here? But it seems like you'd need another level of indirection
|
|
||||||
// in order to maintain an ongoing buffer that supersamples only at the end.
|
|
||||||
const NSUInteger frameBufferWidth = NSUInteger(size.width * _view.layer.contentsScale);
|
const NSUInteger frameBufferWidth = NSUInteger(size.width * _view.layer.contentsScale);
|
||||||
const NSUInteger frameBufferHeight = NSUInteger(size.height * _view.layer.contentsScale);
|
const NSUInteger frameBufferHeight = NSUInteger(size.height * _view.layer.contentsScale);
|
||||||
|
|
||||||
@@ -619,7 +615,6 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
|||||||
_pipeline = isSVideoOutput ? Pipeline::SVideo : Pipeline::CompositeColour;
|
_pipeline = isSVideoOutput ? Pipeline::SVideo : Pipeline::CompositeColour;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: factor in gamma, which may or may not be a factor (it isn't for 1-bit formats).
|
|
||||||
struct FragmentSamplerDictionary {
|
struct FragmentSamplerDictionary {
|
||||||
/// Fragment shader that outputs to the composition buffer for composite processing.
|
/// Fragment shader that outputs to the composition buffer for composite processing.
|
||||||
NSString *const compositionComposite;
|
NSString *const compositionComposite;
|
||||||
@@ -636,29 +631,38 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
|||||||
NSString *const directRGBWithGamma;
|
NSString *const directRGBWithGamma;
|
||||||
};
|
};
|
||||||
const FragmentSamplerDictionary samplerDictionary[8] = {
|
const FragmentSamplerDictionary samplerDictionary[8] = {
|
||||||
{@"compositeSampleLuminance1", nullptr, @"sampleLuminance1", @"sampleLuminance1", nullptr, nullptr},
|
// Composite formats.
|
||||||
{@"compositeSampleLuminance8", nullptr, @"sampleLuminance8", nullptr},
|
{@"compositeSampleLuminance1", nil, @"sampleLuminance1", @"sampleLuminance1"},
|
||||||
{@"compositeSamplePhaseLinkedLuminance8", nullptr, @"samplePhaseLinkedLuminance8", nullptr},
|
{@"compositeSampleLuminance8", nil, @"sampleLuminance8", @"sampleLuminance8WithGamma"},
|
||||||
{@"compositeSampleLuminance8Phase8", @"sampleLuminance8Phase8", @"compositeSampleLuminance8Phase8", nullptr, nullptr, nullptr},
|
{@"compositeSamplePhaseLinkedLuminance8", nil, @"samplePhaseLinkedLuminance8", @"samplePhaseLinkedLuminance8WithGamma"},
|
||||||
{@"compositeSampleRed1Green1Blue1", @"svideoSampleRed1Green1Blue1", @"compositeSampleRed1Green1Blue1", nullptr, @"sampleRed1Green1Blue1", nullptr},
|
|
||||||
{@"compositeSampleRed2Green2Blue2", @"svideoSampleRed2Green2Blue2", @"compositeSampleRed2Green2Blue2", nullptr, @"sampleRed2Green2Blue2", nullptr},
|
// S-Video formats.
|
||||||
{@"compositeSampleRed4Green4Blue4", @"svideoSampleRed4Green4Blue4", @"compositeSampleRed4Green4Blue4", nullptr, @"sampleRed4Green4Blue4", nullptr},
|
{@"compositeSampleLuminance8Phase8", @"sampleLuminance8Phase8", @"directCompositeSampleLuminance8Phase8", @"directCompositeSampleLuminance8Phase8WithGamma"},
|
||||||
{@"compositeSampleRed8Green8Blue8", @"svideoSampleRed8Green8Blue8", @"compositeSampleRed8Green8Blue8", nullptr, @"sampleRed8Green8Blue8", nullptr},
|
|
||||||
|
// RGB formats.
|
||||||
|
{@"compositeSampleRed1Green1Blue1", @"svideoSampleRed1Green1Blue1", @"directCompositeSampleRed1Green1Blue1", @"directCompositeSampleRed1Green1Blue1WithGamma", @"sampleRed1Green1Blue1", @"sampleRed1Green1Blue1"},
|
||||||
|
{@"compositeSampleRed2Green2Blue2", @"svideoSampleRed2Green2Blue2", @"directCompositeSampleRed2Green2Blue2", @"directCompositeSampleRed2Green2Blue2WithGamma", @"sampleRed2Green2Blue2", @"sampleRed2Green2Blue2WithGamma"},
|
||||||
|
{@"compositeSampleRed4Green4Blue4", @"svideoSampleRed4Green4Blue4", @"directCompositeSampleRed4Green4Blue4", @"directCompositeSampleRed4Green4Blue4WithGamma", @"sampleRed4Green4Blue4", @"sampleRed4Green4Blue4WithGamma"},
|
||||||
|
{@"compositeSampleRed8Green8Blue8", @"svideoSampleRed8Green8Blue8", @"directCompositeSampleRed8Green8Blue8", @"directCompositeSampleRed8Green8Blue8WithGamma", @"sampleRed8Green8Blue8", @"sampleRed8Green8Blue8WithGamma"},
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
// Do a quick check of the names entered above. I don't think this is possible at compile time.
|
// Do a quick check that all the shaders named above are defined in the Metal code. I don't think this is possible at compile time.
|
||||||
// for(int c = 0; c < 8; ++c) {
|
for(int c = 0; c < 8; ++c) {
|
||||||
// if(samplerDictionary[c].compositionComposite) assert([library newFunctionWithName:samplerDictionary[c].compositionComposite]);
|
#define Test(x) if(samplerDictionary[c].x) assert([library newFunctionWithName:samplerDictionary[c].x]);
|
||||||
// if(samplerDictionary[c].compositionSVideo) assert([library newFunctionWithName:samplerDictionary[c].compositionSVideo]);
|
Test(compositionComposite);
|
||||||
// if(samplerDictionary[c].directComposite) assert([library newFunctionWithName:samplerDictionary[c].directComposite]);
|
Test(compositionSVideo);
|
||||||
// if(samplerDictionary[c].directRGB) assert([library newFunctionWithName:samplerDictionary[c].directRGB]);
|
Test(directComposite);
|
||||||
// }
|
Test(directCompositeWithGamma);
|
||||||
|
Test(directRGB);
|
||||||
|
Test(directRGBWithGamma);
|
||||||
|
#undef Test
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uniforms()->cyclesMultiplier = 1.0f;
|
uniforms()->cyclesMultiplier = 1.0f;
|
||||||
if(_pipeline != Pipeline::DirectToDisplay) {
|
if(_pipeline != Pipeline::DirectToDisplay) {
|
||||||
// Pick a suitable cycle multiplier. TODO: can I reduce this from a multiple of 4?
|
// Pick a suitable cycle multiplier.
|
||||||
const float minimumSize = 4.0f * float(modals.colour_cycle_numerator) / float(modals.colour_cycle_denominator);
|
const float minimumSize = 4.0f * float(modals.colour_cycle_numerator) / float(modals.colour_cycle_denominator);
|
||||||
while(uniforms()->cyclesMultiplier * modals.cycles_per_line < minimumSize) {
|
while(uniforms()->cyclesMultiplier * modals.cycles_per_line < minimumSize) {
|
||||||
uniforms()->cyclesMultiplier += 1.0f;
|
uniforms()->cyclesMultiplier += 1.0f;
|
||||||
@@ -678,7 +682,6 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
|||||||
|
|
||||||
// Generate the chrominance filter.
|
// Generate the chrominance filter.
|
||||||
{
|
{
|
||||||
// auto *const firCoefficients = uniforms()->chromaKernel;
|
|
||||||
simd::float3 firCoefficients[8];
|
simd::float3 firCoefficients[8];
|
||||||
const auto chromaCoefficients = boxCoefficients(radiansPerPixel, 3.141592654f);
|
const auto chromaCoefficients = boxCoefficients(radiansPerPixel, 3.141592654f);
|
||||||
_chromaKernelSize = 15;
|
_chromaKernelSize = 15;
|
||||||
@@ -757,8 +760,14 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
|||||||
pipelineDescriptor.fragmentFunction = [library newFunctionWithName:@"interpolateFragment"];
|
pipelineDescriptor.fragmentFunction = [library newFunctionWithName:@"interpolateFragment"];
|
||||||
} else {
|
} else {
|
||||||
const bool isRGBOutput = modals.display_type == Outputs::Display::DisplayType::RGB;
|
const bool isRGBOutput = modals.display_type == Outputs::Display::DisplayType::RGB;
|
||||||
pipelineDescriptor.fragmentFunction =
|
|
||||||
[library newFunctionWithName:isRGBOutput ? samplerDictionary[int(modals.input_data_type)].directRGB : samplerDictionary[int(modals.input_data_type)].directComposite];
|
NSString *shaderName;
|
||||||
|
if(isRGBOutput) {
|
||||||
|
shaderName = [self shouldApplyGamma] ? samplerDictionary[int(modals.input_data_type)].directRGBWithGamma : samplerDictionary[int(modals.input_data_type)].directRGB;
|
||||||
|
} else {
|
||||||
|
shaderName = [self shouldApplyGamma] ? samplerDictionary[int(modals.input_data_type)].directCompositeWithGamma : samplerDictionary[int(modals.input_data_type)].directComposite;
|
||||||
|
}
|
||||||
|
pipelineDescriptor.fragmentFunction = [library newFunctionWithName:shaderName];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable blending.
|
// Enable blending.
|
||||||
@@ -818,6 +827,7 @@ using BufferingScanTarget = Outputs::Display::BufferingScanTarget;
|
|||||||
|
|
||||||
[encoder setVertexTexture:_frameBuffer atIndex:0];
|
[encoder setVertexTexture:_frameBuffer atIndex:0];
|
||||||
[encoder setFragmentTexture:_frameBuffer atIndex:0];
|
[encoder setFragmentTexture:_frameBuffer atIndex:0];
|
||||||
|
[encoder setFragmentBuffer:_uniformsBuffer offset:0 atIndex:0];
|
||||||
|
|
||||||
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
[encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
|
||||||
[encoder endEncoding];
|
[encoder endEncoding];
|
||||||
|
@@ -10,9 +10,6 @@
|
|||||||
|
|
||||||
using namespace metal;
|
using namespace metal;
|
||||||
|
|
||||||
// TODO: I'm being very loose, so far, in use of alpha. Sometimes it's 0.64, somtimes its 1.0.
|
|
||||||
// Apply some rigour, for crying out loud.
|
|
||||||
|
|
||||||
struct Uniforms {
|
struct Uniforms {
|
||||||
// This is used to scale scan positions, i.e. it provides the range
|
// This is used to scale scan positions, i.e. it provides the range
|
||||||
// for mapping from scan-style integer positions into eye space.
|
// for mapping from scan-style integer positions into eye space.
|
||||||
@@ -262,35 +259,42 @@ half4 composite(half level, half2 quadrature, half amplitude) {
|
|||||||
// composite format used for composition. Direct sampling is always for final output, so the two
|
// composite format used for composition. Direct sampling is always for final output, so the two
|
||||||
// 8-bit formats also provide a gamma option.
|
// 8-bit formats also provide a gamma option.
|
||||||
|
|
||||||
fragment half4 sampleLuminance1(SourceInterpolator vert [[stage_in]], texture2d<ushort> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) {
|
half convertLuminance1(SourceInterpolator vert [[stage_in]], texture2d<ushort> texture [[texture(0)]]) {
|
||||||
const half luminance = clamp(half(texture.sample(standardSampler, vert.textureCoordinates).r), half(0.0f), half(1.0f)) * uniforms.outputMultiplier;
|
return clamp(half(texture.sample(standardSampler, vert.textureCoordinates).r), half(0.0f), half(1.0f));
|
||||||
return half4(half3(luminance), uniforms.outputAlpha);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment half4 compositeSampleLuminance1(SourceInterpolator vert [[stage_in]], texture2d<ushort> texture [[texture(0)]]) {
|
half convertLuminance8(SourceInterpolator vert [[stage_in]], texture2d<half> texture [[texture(0)]]) {
|
||||||
return composite(texture.sample(standardSampler, vert.textureCoordinates).r, quadrature(vert.colourPhase), vert.colourAmplitude);
|
return texture.sample(standardSampler, vert.textureCoordinates).r;
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment half4 sampleLuminance8(SourceInterpolator vert [[stage_in]], texture2d<half> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) {
|
half convertPhaseLinkedLuminance8(SourceInterpolator vert [[stage_in]], texture2d<half> texture [[texture(0)]]) {
|
||||||
return half4(texture.sample(standardSampler, vert.textureCoordinates).rrr * uniforms.outputMultiplier, uniforms.outputAlpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment half4 compositeSampleLuminance8(SourceInterpolator vert [[stage_in]], texture2d<half> texture [[texture(0)]]) {
|
|
||||||
return composite(texture.sample(standardSampler, vert.textureCoordinates).r, quadrature(vert.colourPhase), vert.colourAmplitude);
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment half4 samplePhaseLinkedLuminance8(SourceInterpolator vert [[stage_in]], texture2d<half> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) {
|
|
||||||
const int offset = int(vert.unitColourPhase * 4.0f) & 3;
|
const int offset = int(vert.unitColourPhase * 4.0f) & 3;
|
||||||
auto sample = texture.sample(standardSampler, vert.textureCoordinates);
|
auto sample = texture.sample(standardSampler, vert.textureCoordinates);
|
||||||
return half4(half3(sample[offset]), uniforms.outputAlpha);
|
return sample[offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment half4 compositeSamplePhaseLinkedLuminance8(SourceInterpolator vert [[stage_in]], texture2d<half> texture [[texture(0)]]) {
|
|
||||||
const int offset = int(vert.unitColourPhase * 4.0f) & 3;
|
#define CompositeSet(name, type) \
|
||||||
const float snappedColourPhase = float(offset) * (0.5f * 3.141592654f); // TODO: plus machine-supplied offset.
|
fragment half4 sample##name(SourceInterpolator vert [[stage_in]], texture2d<type> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
||||||
auto sample = texture.sample(standardSampler, vert.textureCoordinates);
|
const half luminance = convert##name(vert, texture) * uniforms.outputMultiplier; \
|
||||||
return composite(sample[offset], quadrature(snappedColourPhase), vert.colourAmplitude);
|
return half4(half3(luminance), uniforms.outputAlpha); \
|
||||||
}
|
} \
|
||||||
|
\
|
||||||
|
fragment half4 sample##name##WithGamma(SourceInterpolator vert [[stage_in]], texture2d<type> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
||||||
|
const half luminance = pow(convert##name(vert, texture) * uniforms.outputMultiplier, uniforms.outputGamma); \
|
||||||
|
return half4(half3(luminance), uniforms.outputAlpha); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
fragment half4 compositeSample##name(SourceInterpolator vert [[stage_in]], texture2d<type> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
||||||
|
const half luminance = convert##name(vert, texture) * uniforms.outputMultiplier; \
|
||||||
|
return composite(luminance, quadrature(vert.colourPhase), vert.colourAmplitude); \
|
||||||
|
}
|
||||||
|
|
||||||
|
CompositeSet(Luminance1, ushort);
|
||||||
|
CompositeSet(Luminance8, half);
|
||||||
|
CompositeSet(PhaseLinkedLuminance8, half);
|
||||||
|
|
||||||
|
#undef CompositeSet
|
||||||
|
|
||||||
// The luminance/phase format can produce either composite or S-Video.
|
// The luminance/phase format can produce either composite or S-Video.
|
||||||
|
|
||||||
@@ -302,54 +306,65 @@ half2 convertLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d<ha
|
|||||||
return half2(luminancePhase.r, rawChroma);
|
return half2(luminancePhase.r, rawChroma);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fragment half4 compositeSampleLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d<half> texture [[texture(0)]]) {
|
||||||
|
const half2 luminanceChroma = convertLuminance8Phase8(vert, texture);
|
||||||
|
const half luminance = mix(luminanceChroma.r, luminanceChroma.g, vert.colourAmplitude);
|
||||||
|
return composite(luminance, quadrature(vert.colourPhase), vert.colourAmplitude);
|
||||||
|
}
|
||||||
|
|
||||||
fragment half4 sampleLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d<half> texture [[texture(0)]]) {
|
fragment half4 sampleLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d<half> texture [[texture(0)]]) {
|
||||||
const half2 luminanceChroma = convertLuminance8Phase8(vert, texture);
|
const half2 luminanceChroma = convertLuminance8Phase8(vert, texture);
|
||||||
const half2 qam = quadrature(vert.colourPhase) * 0.5f;
|
const half2 qam = quadrature(vert.colourPhase) * half(0.5f);
|
||||||
return half4(luminanceChroma.r,
|
return half4(luminanceChroma.r,
|
||||||
half2(0.5f) + luminanceChroma.g*qam,
|
half2(0.5f) + luminanceChroma.g*qam,
|
||||||
half(1.0f));
|
half(1.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment half4 compositeSampleLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d<half> texture [[texture(0)]]) {
|
fragment half4 directCompositeSampleLuminance8Phase8(SourceInterpolator vert [[stage_in]], texture2d<half> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) {
|
||||||
const half2 luminanceChroma = convertLuminance8Phase8(vert, texture);
|
const half2 luminanceChroma = convertLuminance8Phase8(vert, texture);
|
||||||
const half level = mix(luminanceChroma.r, luminanceChroma.g, half(vert.colourAmplitude));
|
const half luminance = mix(luminanceChroma.r * uniforms.outputMultiplier, luminanceChroma.g, vert.colourAmplitude);
|
||||||
return composite(level, quadrature(vert.colourPhase), vert.colourAmplitude);
|
return half4(half3(luminance), uniforms.outputAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
// All the RGB formats can produce RGB, composite or S-Video.
|
fragment half4 directCompositeSampleLuminance8Phase8WithGamma(SourceInterpolator vert [[stage_in]], texture2d<half> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) {
|
||||||
//
|
const half2 luminanceChroma = convertLuminance8Phase8(vert, texture);
|
||||||
// Note on the below: in Metal you may not call a fragment function (so e.g. svideoSampleX can't just cann sampleX).
|
const half luminance = mix(pow(luminanceChroma.r * uniforms.outputMultiplier, uniforms.outputGamma), luminanceChroma.g, vert.colourAmplitude);
|
||||||
// Also I can find no functioning way to offer a templated fragment function. So I don't currently know how
|
return half4(half3(luminance), uniforms.outputAlpha);
|
||||||
// I could avoid the macro mess below.
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// All the RGB formats can produce RGB, composite or S-Video.
|
||||||
|
|
||||||
// TODO: is the calling convention here causing `vert` and `texture` to be copied?
|
|
||||||
half3 convertRed8Green8Blue8(SourceInterpolator vert, texture2d<half> texture) {
|
half3 convertRed8Green8Blue8(SourceInterpolator vert, texture2d<half> texture) {
|
||||||
return texture.sample(standardSampler, vert.textureCoordinates).rgb;
|
return texture.sample(standardSampler, vert.textureCoordinates).rgb;
|
||||||
}
|
}
|
||||||
|
|
||||||
half3 convertRed4Green4Blue4(SourceInterpolator vert, texture2d<ushort> texture) {
|
half3 convertRed4Green4Blue4(SourceInterpolator vert, texture2d<ushort> texture) {
|
||||||
const auto sample = texture.sample(standardSampler, vert.textureCoordinates).rg;
|
const auto sample = texture.sample(standardSampler, vert.textureCoordinates).rg;
|
||||||
return half3(sample.r&15, (sample.g >> 4)&15, sample.g&15);
|
return clamp(half3(sample.r&15, (sample.g >> 4)&15, sample.g&15), half(0.0f), half(1.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
half3 convertRed2Green2Blue2(SourceInterpolator vert, texture2d<ushort> texture) {
|
half3 convertRed2Green2Blue2(SourceInterpolator vert, texture2d<ushort> texture) {
|
||||||
const auto sample = texture.sample(standardSampler, vert.textureCoordinates).r;
|
const auto sample = texture.sample(standardSampler, vert.textureCoordinates).r;
|
||||||
return half3((sample >> 4)&3, (sample >> 2)&3, sample&3);
|
return clamp(half3((sample >> 4)&3, (sample >> 2)&3, sample&3), half(0.0f), half(1.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
half3 convertRed1Green1Blue1(SourceInterpolator vert, texture2d<ushort> texture) {
|
half3 convertRed1Green1Blue1(SourceInterpolator vert, texture2d<ushort> texture) {
|
||||||
const auto sample = texture.sample(standardSampler, vert.textureCoordinates).r;
|
const auto sample = texture.sample(standardSampler, vert.textureCoordinates).r;
|
||||||
return half3(sample&4, sample&2, sample&1);
|
return clamp(half3(sample&4, sample&2, sample&1), half(0.0f), half(1.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: don't hard code the 0.64 in sample##name.
|
|
||||||
#define DeclareShaders(name, pixelType) \
|
#define DeclareShaders(name, pixelType) \
|
||||||
fragment half4 sample##name(SourceInterpolator vert [[stage_in]], texture2d<pixelType> texture [[texture(0)]]) { \
|
fragment half4 sample##name(SourceInterpolator vert [[stage_in]], texture2d<pixelType> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
||||||
return half4(convert##name(vert, texture), 0.64); \
|
return half4(convert##name(vert, texture), uniforms.outputAlpha); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
fragment half4 sample##name##WithGamma(SourceInterpolator vert [[stage_in]], texture2d<pixelType> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
||||||
|
return half4(pow(convert##name(vert, texture), uniforms.outputGamma), uniforms.outputAlpha); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
fragment half4 svideoSample##name(SourceInterpolator vert [[stage_in]], texture2d<pixelType> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
fragment half4 svideoSample##name(SourceInterpolator vert [[stage_in]], texture2d<pixelType> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
||||||
const auto colour = uniforms.fromRGB * clamp(convert##name(vert, texture), half(0.0f), half(1.0f)); \
|
const auto colour = uniforms.fromRGB * convert##name(vert, texture); \
|
||||||
const half2 qam = quadrature(vert.colourPhase); \
|
const half2 qam = quadrature(vert.colourPhase); \
|
||||||
const half chroma = dot(colour.gb, qam); \
|
const half chroma = dot(colour.gb, qam); \
|
||||||
return half4( \
|
return half4( \
|
||||||
@@ -359,11 +374,24 @@ half3 convertRed1Green1Blue1(SourceInterpolator vert, texture2d<ushort> texture)
|
|||||||
); \
|
); \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
|
half composite##name(SourceInterpolator vert, texture2d<pixelType> texture, constant Uniforms &uniforms, half2 colourSubcarrier) { \
|
||||||
|
const auto colour = uniforms.fromRGB * convert##name(vert, texture); \
|
||||||
|
return mix(colour.r, dot(colour.gb, colourSubcarrier), half(vert.colourAmplitude)); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
fragment half4 compositeSample##name(SourceInterpolator vert [[stage_in]], texture2d<pixelType> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
fragment half4 compositeSample##name(SourceInterpolator vert [[stage_in]], texture2d<pixelType> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
||||||
const auto colour = uniforms.fromRGB * clamp(convert##name(vert, texture), half3(0.0f), half3(1.0f)); \
|
|
||||||
const half2 colourSubcarrier = quadrature(vert.colourPhase); \
|
const half2 colourSubcarrier = quadrature(vert.colourPhase); \
|
||||||
const half level = mix(colour.r, dot(colour.gb, colourSubcarrier), half(vert.colourAmplitude)); \
|
return composite(composite##name(vert, texture, uniforms, colourSubcarrier), colourSubcarrier, vert.colourAmplitude); \
|
||||||
return composite(level, colourSubcarrier, vert.colourAmplitude); \
|
} \
|
||||||
|
\
|
||||||
|
fragment half4 directCompositeSample##name(SourceInterpolator vert [[stage_in]], texture2d<pixelType> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
||||||
|
const half level = composite##name(vert, texture, uniforms, quadrature(vert.colourPhase)); \
|
||||||
|
return half4(half3(level), uniforms.outputAlpha); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
fragment half4 directCompositeSample##name##WithGamma(SourceInterpolator vert [[stage_in]], texture2d<pixelType> texture [[texture(0)]], constant Uniforms &uniforms [[buffer(0)]]) { \
|
||||||
|
const half level = pow(composite##name(vert, texture, uniforms, quadrature(vert.colourPhase)), uniforms.outputGamma); \
|
||||||
|
return half4(half3(level), uniforms.outputAlpha); \
|
||||||
}
|
}
|
||||||
|
|
||||||
DeclareShaders(Red8Green8Blue8, half)
|
DeclareShaders(Red8Green8Blue8, half)
|
||||||
@@ -379,8 +407,8 @@ fragment half4 interpolateFragment(CopyInterpolator vert [[stage_in]], texture2d
|
|||||||
return texture.sample(linearSampler, vert.textureCoordinates);
|
return texture.sample(linearSampler, vert.textureCoordinates);
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment half4 clearFragment() {
|
fragment half4 clearFragment(constant Uniforms &uniforms [[buffer(0)]]) {
|
||||||
return half4(0.0, 0.0, 0.0, 0.64);
|
return half4(0.0, 0.0, 0.0, uniforms.outputAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Compute kernels
|
// MARK: - Compute kernels
|
||||||
|
Reference in New Issue
Block a user