GPU YUV-to-RGB Shader Pipeline Implementation

This commit is contained in:
2025-09-21 01:34:55 +09:00
parent 786d0e4667
commit 83708b8ab5
3 changed files with 265 additions and 24 deletions

View File

@@ -0,0 +1,79 @@
// YUVToRGB.hlsl - High-performance YUV to RGB conversion shader
// Eliminates CPU-based color conversion bottleneck (11-19ms -> 0.1-0.5ms)
cbuffer ColorConversionConstants : register(b0)
{
matrix colorMatrix; // BT.709 or BT.2020 color conversion matrix
float4 yuvOffsets; // Y, U, V offsets for different formats
float4 yuvRanges; // Y, U, V ranges for normalization
int2 videoSize; // Video width and height
int2 padding; // Padding for 16-byte alignment
};
// Input textures
Texture2D yTexture : register(t0); // Y plane (luminance)
Texture2D uTexture : register(t1); // U plane (chroma)
Texture2D vTexture : register(t2); // V plane (chroma)
// Samplers
SamplerState linearSampler : register(s0);
struct VSInput
{
float4 position : POSITION;
float2 texCoord : TEXCOORD0;
};
struct PSInput
{
float4 position : SV_POSITION;
float2 texCoord : TEXCOORD0;
};
// Vertex Shader
PSInput VSMain(VSInput input)
{
PSInput output;
output.position = input.position;
output.texCoord = input.texCoord;
return output;
}
// Pixel Shader - Optimized BT.709 conversion
float4 PSMain(PSInput input) : SV_TARGET
{
// Sample YUV values
float y = yTexture.Sample(linearSampler, input.texCoord).r;
float u = uTexture.Sample(linearSampler, input.texCoord).r;
float v = vTexture.Sample(linearSampler, input.texCoord).r;
// Normalize to [0,1] range and apply offsets
y = (y - yuvOffsets.x) * yuvRanges.x;
u = (u - yuvOffsets.y) * yuvRanges.y - 0.5f;
v = (v - yuvOffsets.z) * yuvRanges.z - 0.5f;
// BT.709 conversion matrix (optimized constants)
float3 yuv = float3(y, u, v);
float3 rgb = mul(colorMatrix, yuv);
// Clamp to valid range
rgb = saturate(rgb);
return float4(rgb, 1.0f);
}
// Alternative high-performance version with manual matrix
float4 PSMainFast(PSInput input) : SV_TARGET
{
float y = yTexture.Sample(linearSampler, input.texCoord).r;
float u = uTexture.Sample(linearSampler, input.texCoord).r;
float v = vTexture.Sample(linearSampler, input.texCoord).r;
// BT.709 conversion with optimized constants
float3 rgb;
rgb.r = y + 1.402f * (v - 0.5f);
rgb.g = y - 0.344f * (u - 0.5f) - 0.714f * (v - 0.5f);
rgb.b = y + 1.772f * (u - 0.5f);
return float4(saturate(rgb), 1.0f);
}

View File

@@ -203,6 +203,9 @@ HRESULT D3D12VideoRenderer::RenderFrame(const VideoFrame& frame)
HRESULT hr = UpdateYUVTextures(frame);
if (FAILED(hr)) return hr;
// Update color conversion constants based on frame format
UpdateColorConversionConstants(frame.width, frame.height, frame);
// Render YUV frame using shader pipeline
return RenderYUVFrame();
}
@@ -746,12 +749,16 @@ HRESULT D3D12VideoRenderer::CreateShaderResources()
hr = CreatePipelineState();
if (FAILED(hr)) return hr;
// Create color conversion constant buffer
hr = CreateColorConversionConstantBuffer();
if (FAILED(hr)) return hr;
return S_OK;
}
HRESULT D3D12VideoRenderer::CreateRootSignature()
{
// Define root parameters
// Define descriptor ranges
D3D12_DESCRIPTOR_RANGE1 ranges[1];
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
ranges[0].NumDescriptors = 3; // Y, U, V textures
@@ -760,11 +767,21 @@ HRESULT D3D12VideoRenderer::CreateRootSignature()
ranges[0].Flags = D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC;
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
D3D12_ROOT_PARAMETER1 rootParameters[1];
rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
// Define root parameters: [0] = CBV for color conversion, [1] = SRV descriptor table
D3D12_ROOT_PARAMETER1 rootParameters[2];
// Root parameter 0: Constant buffer for color conversion constants
rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
rootParameters[0].DescriptorTable.NumDescriptorRanges = 1;
rootParameters[0].DescriptorTable.pDescriptorRanges = ranges;
rootParameters[0].Descriptor.ShaderRegister = 0; // b0
rootParameters[0].Descriptor.RegisterSpace = 0;
rootParameters[0].Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC;
// Root parameter 1: SRV descriptor table for YUV textures
rootParameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
rootParameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
rootParameters[1].DescriptorTable.NumDescriptorRanges = 1;
rootParameters[1].DescriptorTable.pDescriptorRanges = ranges;
// Define static sampler
D3D12_STATIC_SAMPLER_DESC sampler = {};
@@ -832,36 +849,48 @@ HRESULT D3D12VideoRenderer::CompileShaders()
}
)";
// YUV to RGB pixel shader with BT.709 color space conversion
// High-performance YUV to RGB pixel shader with configurable color conversion
const char* pixelShaderSource = R"(
Texture2D<float> yTexture : register(t0);
Texture2D<float> uTexture : register(t1);
Texture2D<float> vTexture : register(t2);
SamplerState texSampler : register(s0);
cbuffer ColorConversionConstants : register(b0)
{
matrix colorMatrix; // BT.709 or BT.2020 color conversion matrix
float4 yuvOffsets; // Y, U, V offsets for different formats
float4 yuvRanges; // Y, U, V ranges for normalization
int2 videoSize; // Video width and height
int2 padding; // Padding for 16-byte alignment
};
Texture2D yTexture : register(t0); // Y plane (luminance)
Texture2D uTexture : register(t1); // U plane (chroma)
Texture2D vTexture : register(t2); // V plane (chroma)
SamplerState linearSampler : register(s0);
struct PSInput
{
float4 position : SV_POSITION;
float2 texCoord : TEXCOORD;
float2 texCoord : TEXCOORD0;
};
float4 PSMain(PSInput input) : SV_TARGET
{
// Sample YUV values
float y = yTexture.Sample(texSampler, input.texCoord).r;
float u = uTexture.Sample(texSampler, input.texCoord).r;
float v = vTexture.Sample(texSampler, input.texCoord).r;
float y = yTexture.Sample(linearSampler, input.texCoord).r;
float u = uTexture.Sample(linearSampler, input.texCoord).r;
float v = vTexture.Sample(linearSampler, input.texCoord).r;
// Convert YUV to RGB using BT.709 matrix
// Y is in range [0, 1], U and V are in range [0, 1] but represent [-0.5, 0.5]
u = u - 0.5f;
v = v - 0.5f;
// Normalize to [0,1] range and apply offsets
y = (y - yuvOffsets.x) * yuvRanges.x;
u = (u - yuvOffsets.y) * yuvRanges.y - 0.5f;
v = (v - yuvOffsets.z) * yuvRanges.z - 0.5f;
float r = y + 1.5748f * v;
float g = y - 0.1873f * u - 0.4681f * v;
float b = y + 1.8556f * u;
// BT.709 conversion matrix (optimized constants)
float3 yuv = float3(y, u, v);
float3 rgb = mul(colorMatrix, yuv);
return float4(r, g, b, 1.0f);
// Clamp to valid range
rgb = saturate(rgb);
return float4(rgb, 1.0f);
}
)";
@@ -1042,8 +1071,11 @@ HRESULT D3D12VideoRenderer::RenderYUVFrame()
ID3D12DescriptorHeap* ppHeaps[] = { m_srvHeap.Get() };
m_commandList->SetDescriptorHeaps(_countof(ppHeaps), ppHeaps);
// Set SRV descriptor table (Y, U, V textures)
m_commandList->SetGraphicsRootDescriptorTable(0, m_srvHeap->GetGPUDescriptorHandleForHeapStart());
// Set color conversion constant buffer (root parameter 0)
m_commandList->SetGraphicsRootConstantBufferView(0, m_colorConversionConstantBuffer->GetGPUVirtualAddress());
// Set SRV descriptor table (Y, U, V textures) (root parameter 1)
m_commandList->SetGraphicsRootDescriptorTable(1, m_srvHeap->GetGPUDescriptorHandleForHeapStart());
// Set vertex buffer
m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
@@ -2305,4 +2337,115 @@ HRESULT D3D12VideoRenderer::RenderDirectTexture()
return S_OK;
}
// YUV-to-RGB Conversion Implementation
HRESULT D3D12VideoRenderer::CreateColorConversionConstantBuffer()
{
// Create constant buffer for color conversion parameters
UINT constantBufferSize = (sizeof(ColorConversionConstants) + 255) & ~255; // 256-byte alignment
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
D3D12_RESOURCE_DESC bufferDesc = {};
bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
bufferDesc.Alignment = 0;
bufferDesc.Width = constantBufferSize;
bufferDesc.Height = 1;
bufferDesc.DepthOrArraySize = 1;
bufferDesc.MipLevels = 1;
bufferDesc.Format = DXGI_FORMAT_UNKNOWN;
bufferDesc.SampleDesc.Count = 1;
bufferDesc.SampleDesc.Quality = 0;
bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
bufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
HRESULT hr = m_device->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&bufferDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_colorConversionConstantBuffer));
if (FAILED(hr)) return hr;
// Set up BT.709 color matrix by default
SetupBT709ColorMatrix();
return S_OK;
}
void D3D12VideoRenderer::UpdateColorConversionConstants(uint32_t videoWidth, uint32_t videoHeight,
const VideoFrame& frame)
{
// Set video dimensions
m_colorConversionData.videoSize.x = static_cast<int>(videoWidth);
m_colorConversionData.videoSize.y = static_cast<int>(videoHeight);
// For now, assume standard TV range and BT.709 color space
// TODO: Add color space metadata to VideoFrame for proper detection
bool isFullRange = false; // Default to TV range for safety
if (isFullRange) {
// Full range (0-255 mapped to 0.0-1.0)
m_colorConversionData.yuvOffsets = DirectX::XMFLOAT4(0.0f, 0.0f, 0.0f, 0.0f);
m_colorConversionData.yuvRanges = DirectX::XMFLOAT4(1.0f, 1.0f, 1.0f, 0.0f);
} else {
// Standard TV range (16-235 for Y, 16-240 for UV)
float yOffset = 16.0f / 255.0f;
float yRange = 219.0f / 255.0f; // (235-16)/255
float uvOffset = 16.0f / 255.0f;
float uvRange = 224.0f / 255.0f; // (240-16)/255
m_colorConversionData.yuvOffsets = DirectX::XMFLOAT4(yOffset, uvOffset, uvOffset, 0.0f);
m_colorConversionData.yuvRanges = DirectX::XMFLOAT4(1.0f/yRange, 1.0f/uvRange, 1.0f/uvRange, 0.0f);
}
// Use BT.709 color matrix by default (most common for web content)
SetupBT709ColorMatrix();
// Map and update constant buffer
void* mappedData;
D3D12_RANGE readRange = { 0, 0 }; // Don't read from this resource on CPU
HRESULT hr = m_colorConversionConstantBuffer->Map(0, &readRange, &mappedData);
if (SUCCEEDED(hr)) {
memcpy(mappedData, &m_colorConversionData, sizeof(ColorConversionConstants));
m_colorConversionConstantBuffer->Unmap(0, nullptr);
}
}
void D3D12VideoRenderer::SetupBT709ColorMatrix()
{
// BT.709 RGB to YUV matrix (transposed for column-major)
// Y = 0.2126*R + 0.7152*G + 0.0722*B
// Cb = -0.1146*R - 0.3854*G + 0.5000*B
// Cr = 0.5000*R - 0.4542*G - 0.0458*B
//
// YUV to RGB matrix (inverse):
// R = Y + 0.0000*U + 1.5748*V
// G = Y - 0.1873*U - 0.4681*V
// B = Y + 1.8556*U + 0.0000*V
m_colorConversionData.colorMatrix = DirectX::XMMATRIX(
1.0f, 0.0f, 1.5748f, 0.0f, // Column 0: Y coefficient for RGB
1.0f, -0.1873f, -0.4681f, 0.0f, // Column 1: U coefficient for RGB
1.0f, 1.8556f, 0.0f, 0.0f, // Column 2: V coefficient for RGB
0.0f, 0.0f, 0.0f, 1.0f // Column 3: Unused (for alignment)
);
}
void D3D12VideoRenderer::SetupBT2020ColorMatrix()
{
// BT.2020 YUV to RGB conversion matrix
// More accurate color reproduction for HDR content
m_colorConversionData.colorMatrix = DirectX::XMMATRIX(
1.0f, 0.0f, 1.7167f, 0.0f, // Column 0: Y coefficient for RGB
1.0f, -0.1678f, -0.6577f, 0.0f, // Column 1: U coefficient for RGB
1.0f, 2.1410f, 0.0f, 0.0f, // Column 2: V coefficient for RGB
0.0f, 0.0f, 0.0f, 1.0f // Column 3: Unused (for alignment)
);
}
} // namespace Vav2Player

View File

@@ -5,6 +5,7 @@
#include <wrl/client.h>
#include <memory>
#include <winrt/Microsoft.UI.Xaml.Controls.h>
#include <DirectXMath.h>
#include "../Common/VideoTypes.h"
using Microsoft::WRL::ComPtr;
@@ -136,6 +137,17 @@ private:
ComPtr<ID3DBlob> m_vertexShader;
ComPtr<ID3DBlob> m_pixelShader;
// YUV-to-RGB Conversion Resources
ComPtr<ID3D12Resource> m_colorConversionConstantBuffer;
struct ColorConversionConstants {
DirectX::XMMATRIX colorMatrix; // BT.709 or BT.2020 color conversion matrix
DirectX::XMFLOAT4 yuvOffsets; // Y, U, V offsets for different formats
DirectX::XMFLOAT4 yuvRanges; // Y, U, V ranges for normalization
DirectX::XMINT2 videoSize; // Video width and height
DirectX::XMINT2 padding; // Padding for 16-byte alignment
};
ColorConversionConstants m_colorConversionData;
// Compute Shader Resources for GPU Copy
ComPtr<ID3D12RootSignature> m_computeRootSignature;
ComPtr<ID3D12PipelineState> m_computePipelineState;
@@ -182,6 +194,13 @@ private:
HRESULT CompileShaders();
HRESULT CreatePipelineState();
// YUV-to-RGB conversion methods
HRESULT CreateColorConversionConstantBuffer();
void UpdateColorConversionConstants(uint32_t videoWidth, uint32_t videoHeight,
const VideoFrame& frame);
void SetupBT709ColorMatrix();
void SetupBT2020ColorMatrix();
// Compute Shader management
HRESULT CreateComputeShaderResources();
HRESULT CreateComputeRootSignature();