Switch Metal to the shared functions for CSC matrix generation and chroma co-siting

This commit is contained in:
Cameron Gutman
2025-11-05 23:22:15 -06:00
parent 7f7cc89e61
commit 7fab5007a8
+23 -104
View File
@@ -24,55 +24,21 @@ extern "C" {
struct CscParams struct CscParams
{ {
vector_float3 matrix[3]; simd_float3 matrix[3];
vector_float3 offsets; simd_float3 offsets;
}; };
struct ParamBuffer struct ParamBuffer
{ {
CscParams cscParams; CscParams cscParams;
vector_float2 chromaOffset; simd_float2 chromaOffset;
float bitnessScaleFactor; float bitnessScaleFactor;
}; };
static const CscParams k_CscParams_Bt601 = {
// CSC Matrix
{
{ 1.0f, 0.0f, 1.4020f },
{ 1.0f, -0.3441f, -0.7141f },
{ 1.0f, 1.7720f, 0.0f },
},
// Zero-initialized YUV offsets
{ 0.0f, 0.0f, 0.0f },
};
static const CscParams k_CscParams_Bt709 = {
// CSC Matrix
{
{ 1.0f, 0.0f, 1.5748f },
{ 1.0f, -0.1873f, -0.4681f },
{ 1.0f, 1.8556f, 0.0f },
},
// Zero-initialized YUV offsets
{ 0.0f, 0.0f, 0.0f },
};
static const CscParams k_CscParams_Bt2020 = {
// CSC Matrix
{
{ 1.0f, 0.0f, 1.4746f },
{ 1.0f, -0.1646f, -0.5714f },
{ 1.0f, 1.8814f, 0.0f },
},
// Zero-initialized YUV offsets
{ 0.0f, 0.0f, 0.0f },
};
struct Vertex struct Vertex
{ {
vector_float4 position; simd_float4 position;
vector_float2 texCoord; simd_float2 texCoord;
}; };
#define MAX_VIDEO_PLANES 3 #define MAX_VIDEO_PLANES 3
@@ -109,8 +75,6 @@ public:
m_CommandQueue(nullptr), m_CommandQueue(nullptr),
m_SwMappingTextures{}, m_SwMappingTextures{},
m_MetalView(nullptr), m_MetalView(nullptr),
m_LastColorSpace(-1),
m_LastFullRange(false),
m_LastFrameWidth(-1), m_LastFrameWidth(-1),
m_LastFrameHeight(-1), m_LastFrameHeight(-1),
m_LastDrawableWidth(-1), m_LastDrawableWidth(-1),
@@ -259,12 +223,13 @@ public:
bool updateColorSpaceForFrame(AVFrame* frame) bool updateColorSpaceForFrame(AVFrame* frame)
{ {
if (!hasFrameFormatChanged(frame) && !m_HdrMetadataChanged) {
return true;
}
int colorspace = getFrameColorspace(frame); int colorspace = getFrameColorspace(frame);
bool fullRange = isFrameFullRange(frame);
if (colorspace != m_LastColorSpace || fullRange != m_LastFullRange || m_HdrMetadataChanged) {
CGColorSpaceRef newColorSpace; CGColorSpaceRef newColorSpace;
ParamBuffer paramBuffer; ParamBuffer paramBuffer;
int bits = (m_VideoFormat & VIDEO_FORMAT_MASK_10BIT) ? 10 : 8;
// Stop the display link before changing the Metal layer // Stop the display link before changing the Metal layer
stopDisplayLink(); stopDisplayLink();
@@ -273,7 +238,6 @@ public:
case COLORSPACE_REC_709: case COLORSPACE_REC_709:
m_MetalLayer.colorspace = newColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709); m_MetalLayer.colorspace = newColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_709);
m_MetalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; m_MetalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
paramBuffer.cscParams = k_CscParams_Bt709;
break; break;
case COLORSPACE_REC_2020: case COLORSPACE_REC_2020:
m_MetalLayer.pixelFormat = MTLPixelFormatBGR10A2Unorm; m_MetalLayer.pixelFormat = MTLPixelFormatBGR10A2Unorm;
@@ -284,69 +248,32 @@ public:
else { else {
m_MetalLayer.colorspace = newColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020); m_MetalLayer.colorspace = newColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceITUR_2020);
} }
paramBuffer.cscParams = k_CscParams_Bt2020;
break; break;
default: default:
case COLORSPACE_REC_601: case COLORSPACE_REC_601:
m_MetalLayer.colorspace = newColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); m_MetalLayer.colorspace = newColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
m_MetalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; m_MetalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm;
paramBuffer.cscParams = k_CscParams_Bt601;
break; break;
} }
int range = (1 << bits); std::array<float, 9> cscMatrix;
double yMin = (fullRange ? 0 : (16 << (bits - 8))); std::array<float, 3> yuvOffsets;
double yMax = (fullRange ? (range - 1) : (235 << (bits - 8))); std::array<float, 2> chromaOffset;
double yScale = (range - 1) / (yMax - yMin); getFramePremultipliedCscConstants(frame, cscMatrix, yuvOffsets);
double uvMin = (fullRange ? 0 : (16 << (bits - 8))); getFrameChromaCositingOffsets(frame, chromaOffset);
double uvMax = (fullRange ? (range - 1) : (240 << (bits - 8)));
double uvScale = (range - 1) / (uvMax - uvMin);
// Calculate YUV offsets // Copy the row-major CSC matrix into column-major for Metal
paramBuffer.cscParams.offsets[0] = yMin / (double)(range - 1);
paramBuffer.cscParams.offsets[1] = (range / 2) / (double)(range - 1);
paramBuffer.cscParams.offsets[2] = (range / 2) / (double)(range - 1);
// Scale the color matrix according to the color range
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
paramBuffer.cscParams.matrix[i][0] *= yScale; paramBuffer.cscParams.matrix[i] = simd_make_float3(cscMatrix[0 + i],
paramBuffer.cscParams.matrix[i][1] *= uvScale; cscMatrix[3 + i],
paramBuffer.cscParams.matrix[i][2] *= uvScale; cscMatrix[6 + i]);
} }
switch (frame->chroma_location) { paramBuffer.cscParams.offsets = simd_make_float3(yuvOffsets[0],
default: yuvOffsets[1],
case AVCHROMA_LOC_LEFT: yuvOffsets[2]);
paramBuffer.chromaOffset[0] = 0.5; paramBuffer.chromaOffset = simd_make_float2(chromaOffset[0],
paramBuffer.chromaOffset[1] = 0; chromaOffset[1]);
break;
case AVCHROMA_LOC_CENTER:
paramBuffer.chromaOffset[0] = 0;
paramBuffer.chromaOffset[1] = 0;
break;
case AVCHROMA_LOC_TOPLEFT:
paramBuffer.chromaOffset[0] = 0.5;
paramBuffer.chromaOffset[1] = 0.5;
break;
case AVCHROMA_LOC_TOP:
paramBuffer.chromaOffset[0] = 0;
paramBuffer.chromaOffset[1] = 0.5;
break;
case AVCHROMA_LOC_BOTTOMLEFT:
paramBuffer.chromaOffset[0] = 0.5;
paramBuffer.chromaOffset[1] = -0.5;
break;
case AVCHROMA_LOC_BOTTOM:
paramBuffer.chromaOffset[0] = 0;
paramBuffer.chromaOffset[1] = -0.5;
break;
}
if (m_VideoFormat & VIDEO_FORMAT_MASK_YUV444) {
// 4:4:4 has no subsampling
paramBuffer.chromaOffset[0] = 0;
paramBuffer.chromaOffset[1] = 0;
}
// Set the EDR metadata for HDR10 to enable OS tonemapping // Set the EDR metadata for HDR10 to enable OS tonemapping
if (frame->color_trc == AVCOL_TRC_SMPTE2084 && m_MasteringDisplayColorVolume != nullptr) { if (frame->color_trc == AVCOL_TRC_SMPTE2084 && m_MasteringDisplayColorVolume != nullptr) {
@@ -407,11 +334,7 @@ public:
return false; return false;
} }
m_LastColorSpace = colorspace;
m_LastFullRange = fullRange;
m_HdrMetadataChanged = false; m_HdrMetadataChanged = false;
}
return true; return true;
} }
@@ -711,7 +634,6 @@ public:
m_Window = params->window; m_Window = params->window;
m_FrameRateRange = CAFrameRateRangeMake(params->frameRate, params->frameRate, params->frameRate); m_FrameRateRange = CAFrameRateRangeMake(params->frameRate, params->frameRate, params->frameRate);
m_VideoFormat = params->videoFormat;
id<MTLDevice> device = getMetalDevice(); id<MTLDevice> device = getMetalDevice();
if (!device) { if (!device) {
@@ -1008,9 +930,6 @@ private:
id<MTLCommandQueue> m_CommandQueue; id<MTLCommandQueue> m_CommandQueue;
id<MTLTexture> m_SwMappingTextures[MAX_VIDEO_PLANES]; id<MTLTexture> m_SwMappingTextures[MAX_VIDEO_PLANES];
SDL_MetalView m_MetalView; SDL_MetalView m_MetalView;
int m_VideoFormat;
int m_LastColorSpace;
bool m_LastFullRange;
int m_LastFrameWidth; int m_LastFrameWidth;
int m_LastFrameHeight; int m_LastFrameHeight;
int m_LastDrawableWidth; int m_LastDrawableWidth;