Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 68 additions & 9 deletions QuickView/ComputeEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,24 @@ cbuffer ToneMapParams : register(b0)
float SplineQc;
uint IsHdrOutput;
float RealHardwarePeakScRgb;
float Padding1;
uint TransferFunction;

float4x4 ColorMatrix;
};

// HLG EOTF inverse (HLG to Linear)
float3 HLGToLinear(float3 v)
{
float3 e;
e.r = v.r <= 0.5 ? (v.r * v.r / 3.0) : (exp((v.r - 0.17883277) / 0.28466892) + 0.02372241);
e.g = v.g <= 0.5 ? (v.g * v.g / 3.0) : (exp((v.g - 0.17883277) / 0.28466892) + 0.02372241);
e.b = v.b <= 0.5 ? (v.b * v.b / 3.0) : (exp((v.b - 0.17883277) / 0.28466892) + 0.02372241);
float L_S = 0.2627 * e.r + 0.6780 * e.g + 0.0593 * e.b;
float gamma = 1.2;
float3 L_D = e * pow(max(L_S, 0.0), gamma - 1.0);
return L_D * 12.5;
}

float LinearToPQ(float L)
{
float L_norm = max(0.0, L) / 125.0;
Expand Down Expand Up @@ -132,6 +147,21 @@ void CSToneMap(uint3 id : SV_DispatchThreadID)
color.rgb = max(color.rgb, float3(0.0f, 0.0f, 0.0f));
color.a = saturate(color.a);

// EOTF Inverse
if (TransferFunction == 3) { // PQ
color.r = PQToLinear(color.r);
color.g = PQToLinear(color.g);
color.b = PQToLinear(color.b);
} else if (TransferFunction == 4) { // HLG
color.rgb = HLGToLinear(color.rgb);
} else if (TransferFunction == 1) { // SRGB
// Approximate sRGB to linear
color.rgb = pow(color.rgb, float3(2.2f, 2.2f, 2.2f));
}

// Color Matrix (e.g. Rec.2020 to ScRGB)
color.rgb = mul((float3x3)ColorMatrix, color.rgb);

float paperWhite = max(PaperWhiteScRgb, 1.0);
float displayPeak = max(DisplayPeakScRgb, paperWhite);

Expand Down Expand Up @@ -199,9 +229,24 @@ cbuffer ToneMapParams : register(b0)
float SplineQc;
uint IsHdrOutput;
float RealHardwarePeakScRgb;
float Padding1;
uint TransferFunction;

float4x4 ColorMatrix;
};

// HLG EOTF inverse (HLG to Linear)
float3 HLGToLinear(float3 v)
{
float3 e;
e.r = v.r <= 0.5 ? (v.r * v.r / 3.0) : (exp((v.r - 0.17883277) / 0.28466892) + 0.02372241);
e.g = v.g <= 0.5 ? (v.g * v.g / 3.0) : (exp((v.g - 0.17883277) / 0.28466892) + 0.02372241);
e.b = v.b <= 0.5 ? (v.b * v.b / 3.0) : (exp((v.b - 0.17883277) / 0.28466892) + 0.02372241);
float L_S = 0.2627 * e.r + 0.6780 * e.g + 0.0593 * e.b;
float gamma = 1.2;
float3 L_D = e * pow(max(L_S, 0.0), gamma - 1.0);
return L_D * 12.5;
}

float LinearToPQ(float L)
{
float L_norm = max(0.0, L) / 125.0;
Expand Down Expand Up @@ -282,10 +327,24 @@ void CSToneMapHDR(uint3 id : SV_DispatchThreadID)
color.rgb = max(color.rgb, float3(0.0f, 0.0f, 0.0f));
color.a = saturate(color.a);

// EOTF Inverse
if (TransferFunction == 3) { // PQ
color.r = PQToLinear(color.r);
color.g = PQToLinear(color.g);
color.b = PQToLinear(color.b);
} else if (TransferFunction == 4) { // HLG
color.rgb = HLGToLinear(color.rgb);
} else if (TransferFunction == 1) { // SRGB
color.rgb = pow(color.rgb, float3(2.2f, 2.2f, 2.2f));
}

// Color Matrix
color.rgb = mul((float3x3)ColorMatrix, color.rgb);

float contentPeak = max(ContentPeakScRgb, 1.0f);
float displayPeak = max(DisplayPeakScRgb, 1.0f);

if (contentPeak <= displayPeak && Exposure >= 0.999f && Exposure <= 1.001f && ExposureGain >= 0.999f && ExposureGain <= 1.001f) {
if (contentPeak <= displayPeak && Exposure >= 0.999f && Exposure <= 1.001f && ExposureGain >= 0.999f && ExposureGain <= 1.001f && TransferFunction == 2) {
DstTex[id.xy] = color;
return;
}
Expand Down Expand Up @@ -528,14 +587,14 @@ HRESULT ComputeEngine::UploadAndConvert(const uint8_t* srcPixels, int width, int
srcDesc.Height = height;
srcDesc.MipLevels = 1;
srcDesc.ArraySize = 1;
srcDesc.Format = (srcFormat == PixelFormat::R32G32B32A32_FLOAT) ? DXGI_FORMAT_R32G32B32A32_FLOAT : DXGI_FORMAT_R8G8B8A8_UNORM;
srcDesc.Format = (srcFormat == PixelFormat::R32G32B32A32_FLOAT) ? DXGI_FORMAT_R32G32B32A32_FLOAT : ((srcFormat == PixelFormat::R16G16B16A16_UNORM) ? DXGI_FORMAT_R16G16B16A16_UNORM : DXGI_FORMAT_R8G8B8A8_UNORM);
srcDesc.SampleDesc.Count = 1;
srcDesc.Usage = D3D11_USAGE_IMMUTABLE;
srcDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;

D3D11_SUBRESOURCE_DATA initData = {};
initData.pSysMem = srcPixels;
initData.SysMemPitch = width * (srcFormat == PixelFormat::R32G32B32A32_FLOAT ? 16 : 4);
initData.SysMemPitch = width * ((srcFormat == PixelFormat::R32G32B32A32_FLOAT) ? 16 : ((srcFormat == PixelFormat::R16G16B16A16_UNORM) ? 8 : 4));

ComPtr<ID3D11Texture2D> pSrc;
HRESULT hr = m_d3dDevice->CreateTexture2D(&srcDesc, &initData, &pSrc);
Expand Down Expand Up @@ -800,15 +859,15 @@ HRESULT ComputeEngine::DispatchGamutMaskLut(
return ReadbackMaskTexture(maskTexture.Get(), outReadback);
}

HRESULT ComputeEngine::ToneMapHdrToSdr(const uint8_t* srcPixels, int width, int height, int stride, const ToneMapSettings& settings, ID3D11Texture2D** outTexture) {
HRESULT ComputeEngine::ToneMapHdrToSdr(const uint8_t* srcPixels, int width, int height, int stride, const ToneMapSettings& settings, ID3D11Texture2D** outTexture, PixelFormat srcFormat) {
if (!m_valid || !srcPixels || width <= 0 || height <= 0 || !outTexture) return E_INVALIDARG;

D3D11_TEXTURE2D_DESC srcDesc = {};
srcDesc.Width = static_cast<UINT>(width);
srcDesc.Height = static_cast<UINT>(height);
srcDesc.MipLevels = 1;
srcDesc.ArraySize = 1;
srcDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
srcDesc.Format = srcFormat == PixelFormat::R16G16B16A16_UNORM ? DXGI_FORMAT_R16G16B16A16_UNORM : DXGI_FORMAT_R32G32B32A32_FLOAT;
srcDesc.SampleDesc.Count = 1;
srcDesc.Usage = D3D11_USAGE_IMMUTABLE;
srcDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
Expand Down Expand Up @@ -872,15 +931,15 @@ HRESULT ComputeEngine::ToneMapHdrToSdr(const uint8_t* srcPixels, int width, int
}


HRESULT ComputeEngine::ToneMapHdrToHdr(const uint8_t* srcPixels, int width, int height, int stride, const ToneMapSettings& settings, ID3D11Texture2D** outTexture) {
HRESULT ComputeEngine::ToneMapHdrToHdr(const uint8_t* srcPixels, int width, int height, int stride, const ToneMapSettings& settings, ID3D11Texture2D** outTexture, PixelFormat srcFormat) {
if (!m_valid || !srcPixels || width <= 0 || height <= 0 || !outTexture) return E_INVALIDARG;

D3D11_TEXTURE2D_DESC srcDesc = {};
srcDesc.Width = static_cast<UINT>(width);
srcDesc.Height = static_cast<UINT>(height);
srcDesc.MipLevels = 1;
srcDesc.ArraySize = 1;
srcDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
srcDesc.Format = srcFormat == PixelFormat::R16G16B16A16_UNORM ? DXGI_FORMAT_R16G16B16A16_UNORM : DXGI_FORMAT_R32G32B32A32_FLOAT;
srcDesc.SampleDesc.Count = 1;
srcDesc.Usage = D3D11_USAGE_IMMUTABLE;
srcDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
Expand Down
8 changes: 5 additions & 3 deletions QuickView/ComputeEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ struct alignas(16) ToneMapSettings {
float splineQc;
uint32_t isHdrOutput; // 1: HDR/Sim, 0: SDR
float realHardwarePeakScRgb; // Actual display peak in ScRGB
float padding1; // 16-byte alignment
uint32_t transferFunction; // Enum QuickView::TransferFunction

float colorMatrix[12]; // 3x4 layout for structured float3x3 mapping (requires 48 bytes)
};
static_assert(sizeof(ToneMapSettings) % 16 == 0, "CB size must be multiple of 16 bytes");

Expand Down Expand Up @@ -94,11 +96,11 @@ class ComputeEngine {
/// </summary>
HRESULT ToneMapHdrToHdr(const uint8_t* srcPixels, int width, int height,
int stride, const ToneMapSettings& settings,
ID3D11Texture2D** outTexture);
ID3D11Texture2D** outTexture, PixelFormat srcFormat = PixelFormat::R32G32B32A32_FLOAT);

HRESULT ToneMapHdrToSdr(const uint8_t* srcPixels, int width, int height,
int stride, const ToneMapSettings& settings,
ID3D11Texture2D** outTexture);
ID3D11Texture2D** outTexture, PixelFormat srcFormat = PixelFormat::R32G32B32A32_FLOAT);

/// <summary>
/// GPU-side Gain Map composition (ISO 21496-1).
Expand Down
Loading
Loading