mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-07-21 23:02:05 +00:00
[host] hdr: encode legacy captures as HDR10
Convert FP16 scRGB captures to BT.2020 PQ before storing them in the 10-bit wire format. Preserve linear-light downsampling, publish the real SDR white level and display metadata, and advance the format version when those properties change.
This commit is contained in:
@@ -39,6 +39,7 @@
|
||||
#include <dxgi1_3.h>
|
||||
#include <dxgi1_6.h>
|
||||
#include <d3dcommon.h>
|
||||
#include <math.h>
|
||||
|
||||
// definitions
|
||||
struct D12Interface
|
||||
@@ -49,6 +50,7 @@ struct D12Interface
|
||||
ID3D12Device3 ** device;
|
||||
|
||||
DISPLAYCONFIG_PATH_INFO displayPathInfo;
|
||||
DXGI_OUTPUT_DESC1 outputDesc;
|
||||
|
||||
ID3D12CommandQueue ** copyQueue;
|
||||
ID3D12CommandQueue ** computeQueue;
|
||||
@@ -110,6 +112,54 @@ ComScope * d12_comScope = NULL;
|
||||
|
||||
static struct D12Interface * this = NULL;
|
||||
|
||||
static bool d12_setHDRMetadata(CaptureFrame * frame,
|
||||
const DXGI_OUTPUT_DESC1 * desc)
|
||||
{
|
||||
const float coordinates[] =
|
||||
{
|
||||
desc->RedPrimary [0], desc->RedPrimary [1],
|
||||
desc->GreenPrimary[0], desc->GreenPrimary[1],
|
||||
desc->BluePrimary [0], desc->BluePrimary [1],
|
||||
desc->WhitePoint [0], desc->WhitePoint [1],
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i < ARRAY_LENGTH(coordinates); ++i)
|
||||
if (coordinates[i] < 0.0f || coordinates[i] > 1.0f)
|
||||
return false;
|
||||
|
||||
if (desc->RedPrimary[1] == 0.0f || desc->GreenPrimary[1] == 0.0f ||
|
||||
desc->BluePrimary[1] == 0.0f || desc->WhitePoint[1] == 0.0f ||
|
||||
desc->MaxLuminance <= 0.0f)
|
||||
return false;
|
||||
|
||||
frame->hdrDisplayPrimary[0][0] = (uint16_t)lroundf(
|
||||
desc->RedPrimary[0] * 50000.0f);
|
||||
frame->hdrDisplayPrimary[0][1] = (uint16_t)lroundf(
|
||||
desc->RedPrimary[1] * 50000.0f);
|
||||
frame->hdrDisplayPrimary[1][0] = (uint16_t)lroundf(
|
||||
desc->GreenPrimary[0] * 50000.0f);
|
||||
frame->hdrDisplayPrimary[1][1] = (uint16_t)lroundf(
|
||||
desc->GreenPrimary[1] * 50000.0f);
|
||||
frame->hdrDisplayPrimary[2][0] = (uint16_t)lroundf(
|
||||
desc->BluePrimary[0] * 50000.0f);
|
||||
frame->hdrDisplayPrimary[2][1] = (uint16_t)lroundf(
|
||||
desc->BluePrimary[1] * 50000.0f);
|
||||
frame->hdrWhitePoint[0] = (uint16_t)lroundf(
|
||||
desc->WhitePoint[0] * 50000.0f);
|
||||
frame->hdrWhitePoint[1] = (uint16_t)lroundf(
|
||||
desc->WhitePoint[1] * 50000.0f);
|
||||
frame->hdrMaxDisplayLuminance = (uint32_t)lroundf(desc->MaxLuminance);
|
||||
frame->hdrMinDisplayLuminance = desc->MinLuminance > 0.0f ?
|
||||
(uint32_t)lroundf(desc->MinLuminance * 10000.0f) : 0;
|
||||
|
||||
if ((uint64_t)frame->hdrMaxDisplayLuminance * 10000 <=
|
||||
frame->hdrMinDisplayLuminance)
|
||||
return false;
|
||||
|
||||
frame->hdrMetadata = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// forwards
|
||||
|
||||
static bool d12_enumerateDevices(
|
||||
@@ -298,6 +348,7 @@ static bool d12_init(void * ivshmemBase, unsigned * alignSize)
|
||||
|
||||
DXGI_OUTPUT_DESC1 desc1;
|
||||
IDXGIOutput6_GetDesc1(*output6, &desc1);
|
||||
this->outputDesc = desc1;
|
||||
if (!display_getPathInfo(desc1.Monitor, &this->displayPathInfo))
|
||||
{
|
||||
DEBUG_ERROR("Failed to get the display path info");
|
||||
@@ -546,12 +597,16 @@ static CaptureResult d12_waitFrame(unsigned frameBufferIndex,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
const D3D12_RESOURCE_DESC srcDesc = ID3D12Resource_GetDesc(*src);
|
||||
D12FrameFormat srcFormat =
|
||||
{
|
||||
.desc = ID3D12Resource_GetDesc(*src),
|
||||
.desc = srcDesc,
|
||||
.colorSpace = desc.colorSpace,
|
||||
.width = srcFormat.desc.Width,
|
||||
.height = srcFormat.desc.Height
|
||||
.width = srcDesc.Width,
|
||||
.height = srcDesc.Height,
|
||||
.hdr = desc.colorSpace ==
|
||||
DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020,
|
||||
.hdrPQ = false,
|
||||
};
|
||||
|
||||
switch(srcFormat.desc.Format)
|
||||
@@ -577,12 +632,25 @@ static CaptureResult d12_waitFrame(unsigned frameBufferIndex,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (srcFormat.hdr)
|
||||
{
|
||||
if (srcFormat.format == CAPTURE_FMT_RGBA10)
|
||||
srcFormat.hdrPQ = true;
|
||||
else if (srcFormat.format != CAPTURE_FMT_RGBA16F)
|
||||
{
|
||||
DEBUG_ERROR("HDR capture did not provide an FP16 scRGB or PQ texture");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
// if the input format changed, reconfigure the effects
|
||||
if (srcFormat.desc.Width == 0 ||
|
||||
srcFormat.desc.Width != this->captureFormat.desc.Width ||
|
||||
srcFormat.desc.Height != this->captureFormat.desc.Height ||
|
||||
srcFormat.desc.Format != this->captureFormat.desc.Format ||
|
||||
srcFormat.colorSpace != this->captureFormat.colorSpace)
|
||||
srcFormat.colorSpace != this->captureFormat.colorSpace ||
|
||||
srcFormat.hdr != this->captureFormat.hdr ||
|
||||
srcFormat.hdrPQ != this->captureFormat.hdrPQ)
|
||||
{
|
||||
DEBUG_TRACE("Capture format changed");
|
||||
|
||||
@@ -621,7 +689,9 @@ static CaptureResult d12_waitFrame(unsigned frameBufferIndex,
|
||||
dstFormat.colorSpace != this->dstFormat.colorSpace ||
|
||||
dstFormat.width != this->dstFormat.width ||
|
||||
dstFormat.height != this->dstFormat.height ||
|
||||
dstFormat.format != this->dstFormat.format)
|
||||
dstFormat.format != this->dstFormat.format ||
|
||||
dstFormat.hdr != this->dstFormat.hdr ||
|
||||
dstFormat.hdrPQ != this->dstFormat.hdrPQ)
|
||||
{
|
||||
DEBUG_TRACE("Output format changed");
|
||||
++this->formatVer;
|
||||
@@ -655,9 +725,12 @@ static CaptureResult d12_waitFrame(unsigned frameBufferIndex,
|
||||
frame->pitch = this->pitch;
|
||||
frame->stride = this->pitch / this->bpp;
|
||||
frame->format = this->dstFormat.format;
|
||||
frame->hdr = this->dstFormat.colorSpace ==
|
||||
DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
|
||||
frame->hdrPQ = false;
|
||||
frame->hdr = this->dstFormat.hdr;
|
||||
frame->hdrPQ = this->dstFormat.hdrPQ;
|
||||
frame->sdrWhiteLevel = (uint32_t)lroundf(
|
||||
display_getSDRWhiteLevel(&this->displayPathInfo));
|
||||
if (frame->hdr)
|
||||
d12_setHDRMetadata(frame, &this->outputDesc);
|
||||
frame->rotation = desc.rotation;
|
||||
|
||||
D12Effect * effect;
|
||||
|
||||
@@ -52,6 +52,8 @@ typedef struct D12FrameFormat
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace;
|
||||
unsigned width, height;
|
||||
CaptureFormat format;
|
||||
bool hdr;
|
||||
bool hdrPQ;
|
||||
}
|
||||
D12FrameFormat;
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "common/debug.h"
|
||||
#include "common/windebug.h"
|
||||
#include "common/array.h"
|
||||
#include "common/display.h"
|
||||
#include "common/option.h"
|
||||
|
||||
#include <d3dcompiler.h>
|
||||
@@ -36,17 +35,9 @@ typedef struct HDR16to10Inst
|
||||
{
|
||||
D12Effect base;
|
||||
|
||||
const DISPLAYCONFIG_PATH_INFO * displayPathInfo;
|
||||
struct
|
||||
{
|
||||
float SDRWhiteLevel;
|
||||
}
|
||||
consts;
|
||||
|
||||
ID3D12RootSignature ** rootSignature;
|
||||
ID3D12PipelineState ** pso;
|
||||
ID3D12DescriptorHeap ** descHeap;
|
||||
ID3D12Resource ** constBuffer;
|
||||
|
||||
unsigned threadsX, threadsY;
|
||||
ID3D12Resource ** dst;
|
||||
@@ -76,6 +67,8 @@ static void d12_effect_hdr16to10InitOptions(void)
|
||||
static D12EffectStatus d12_effect_hdr16to10Create(D12Effect ** instance,
|
||||
ID3D12Device3 * device, const DISPLAYCONFIG_PATH_INFO * displayPathInfo)
|
||||
{
|
||||
(void)displayPathInfo;
|
||||
|
||||
if (!option_get_bool("d12", "HDR16to10"))
|
||||
return D12_EFFECT_STATUS_BYPASS;
|
||||
|
||||
@@ -90,18 +83,9 @@ static D12EffectStatus d12_effect_hdr16to10Create(D12Effect ** instance,
|
||||
HRESULT hr;
|
||||
comRef_scopePush(10);
|
||||
|
||||
this->displayPathInfo = displayPathInfo;
|
||||
|
||||
// shader resource view
|
||||
D3D12_DESCRIPTOR_RANGE descriptorRanges[3] =
|
||||
D3D12_DESCRIPTOR_RANGE descriptorRanges[2] =
|
||||
{
|
||||
{
|
||||
.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV,
|
||||
.NumDescriptors = 1,
|
||||
.BaseShaderRegister = 0,
|
||||
.RegisterSpace = 0,
|
||||
.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND
|
||||
},
|
||||
{
|
||||
.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
|
||||
.NumDescriptors = 1,
|
||||
@@ -175,18 +159,27 @@ static D12EffectStatus d12_effect_hdr16to10Create(D12Effect ** instance,
|
||||
|
||||
// Compile the shader
|
||||
const char * testCode =
|
||||
"cbuffer Constants : register(b0)\n"
|
||||
"{\n"
|
||||
" float SDRWhiteLevel;\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"Texture2D <float4> src : register(t0);\n"
|
||||
"RWTexture2D<float4> dst : register(u0);\n"
|
||||
"static const float PQ_m1 = 0.1593017578125;\n"
|
||||
"static const float PQ_m2 = 78.84375;\n"
|
||||
"static const float PQ_c1 = 0.8359375;\n"
|
||||
"static const float PQ_c2 = 18.8515625;\n"
|
||||
"static const float PQ_c3 = 18.6875;\n"
|
||||
"\n"
|
||||
"[numthreads(" STR(THREADS) ", " STR(THREADS) ", 1)]\n"
|
||||
"void main(uint3 dt : SV_DispatchThreadID)\n"
|
||||
"{\n"
|
||||
" dst[dt.xy] = src[dt.xy] * SDRWhiteLevel;"
|
||||
" float4 color = src[dt.xy];\n"
|
||||
" float3 linear709 = color.rgb * (80.0 / 10000.0);\n"
|
||||
" float3 linear2020 = float3(\n"
|
||||
" dot(linear709, float3(0.6274039, 0.3292830, 0.0433131)),\n"
|
||||
" dot(linear709, float3(0.0690973, 0.9195404, 0.0113623)),\n"
|
||||
" dot(linear709, float3(0.0163914, 0.0880133, 0.8955953)));\n"
|
||||
" float3 p = pow(max(linear2020, 0.0), PQ_m1);\n"
|
||||
" float3 pq = pow((PQ_c1 + PQ_c2 * p) /\n"
|
||||
" (1.0 + PQ_c3 * p), PQ_m2);\n"
|
||||
" dst[dt.xy] = float4(pq, color.a);\n"
|
||||
"}\n";
|
||||
|
||||
bool debug = false;
|
||||
@@ -240,48 +233,9 @@ static D12EffectStatus d12_effect_hdr16to10Create(D12Effect ** instance,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
D3D12_HEAP_PROPERTIES constHeapProps =
|
||||
{
|
||||
.Type = D3D12_HEAP_TYPE_UPLOAD,
|
||||
.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
|
||||
.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN
|
||||
};
|
||||
|
||||
D3D12_RESOURCE_DESC constBufferDesc =
|
||||
{
|
||||
.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
|
||||
.Width = ALIGN_TO(sizeof(this->consts),
|
||||
D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT),
|
||||
.Height = 1,
|
||||
.DepthOrArraySize = 1,
|
||||
.MipLevels = 1,
|
||||
.Format = DXGI_FORMAT_UNKNOWN,
|
||||
.SampleDesc.Count = 1,
|
||||
.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
|
||||
.Flags = D3D12_RESOURCE_FLAG_NONE
|
||||
};
|
||||
|
||||
comRef_defineLocal(ID3D12Resource, constBuffer);
|
||||
hr = ID3D12Device3_CreateCommittedResource(
|
||||
device,
|
||||
&constHeapProps,
|
||||
D3D12_HEAP_FLAG_NONE,
|
||||
&constBufferDesc,
|
||||
D3D12_RESOURCE_STATE_GENERIC_READ,
|
||||
NULL,
|
||||
&IID_ID3D12Resource,
|
||||
(void **)constBuffer);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
DEBUG_WINERROR("Failed to create the constant buffer resource", hr);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
comRef_toGlobal(this->rootSignature, rootSignature);
|
||||
comRef_toGlobal(this->pso , pso );
|
||||
comRef_toGlobal(this->descHeap , descHeap );
|
||||
comRef_toGlobal(this->constBuffer , constBuffer );
|
||||
|
||||
result = D12_EFFECT_STATUS_OK;
|
||||
*instance = &this->base;
|
||||
@@ -313,7 +267,7 @@ static D12EffectStatus d12_effect_hdr16to10SetFormat(D12Effect * effect,
|
||||
HRESULT hr;
|
||||
|
||||
if (src->desc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT ||
|
||||
src->colorSpace != DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020)
|
||||
!src->hdr || src->hdrPQ)
|
||||
{
|
||||
result = D12_EFFECT_STATUS_BYPASS;
|
||||
goto exit;
|
||||
@@ -358,6 +312,8 @@ static D12EffectStatus d12_effect_hdr16to10SetFormat(D12Effect * effect,
|
||||
|
||||
dst->desc = desc;
|
||||
dst->format = CAPTURE_FMT_RGBA10;
|
||||
dst->hdr = true;
|
||||
dst->hdrPQ = true;
|
||||
result = D12_EFFECT_STATUS_OK;
|
||||
|
||||
exit:
|
||||
@@ -371,21 +327,6 @@ static ID3D12Resource * d12_effect_hdr16to10Run(D12Effect * effect,
|
||||
{
|
||||
HDR16to10Inst * this = UPCAST(HDR16to10Inst, effect);
|
||||
|
||||
float nits = 80.0f / display_getSDRWhiteLevel(this->displayPathInfo);
|
||||
if (nits != this->consts.SDRWhiteLevel)
|
||||
{
|
||||
this->consts.SDRWhiteLevel = nits;
|
||||
|
||||
void * data;
|
||||
D3D12_RANGE readRange = { 0, 0 };
|
||||
HRESULT hr = ID3D12Resource_Map(*this->constBuffer, 0, &readRange, &data);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
memcpy(data, &this->consts, sizeof(this->consts));
|
||||
ID3D12Resource_Unmap(*this->constBuffer, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// transition the destination texture to unordered access so we can write to it
|
||||
{
|
||||
D3D12_RESOURCE_BARRIER barrier =
|
||||
@@ -407,19 +348,6 @@ static ID3D12Resource * d12_effect_hdr16to10Run(D12Effect * effect,
|
||||
D3D12_CPU_DESCRIPTOR_HANDLE cpuSrvUavHandle =
|
||||
ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(*this->descHeap);
|
||||
|
||||
// descriptor for input CBV
|
||||
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc =
|
||||
{
|
||||
.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(*this->constBuffer),
|
||||
.SizeInBytes = ALIGN_TO(sizeof(this->consts),
|
||||
D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT)
|
||||
};
|
||||
ID3D12Device3_CreateConstantBufferView(device, &cbvDesc, cpuSrvUavHandle);
|
||||
|
||||
// move to the next slot
|
||||
cpuSrvUavHandle.ptr += ID3D12Device3_GetDescriptorHandleIncrementSize(
|
||||
device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
||||
|
||||
// descriptor for input SRV
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc =
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user