mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-07-16 04:12:03 +00:00
[idd] add support to return HDR metadata to clients
This commit is contained in:
@@ -162,6 +162,15 @@ typedef struct KVMFRFrame
|
||||
uint32_t damageRectsCount; // the number of damage rectangles (zero for full-frame damage)
|
||||
FrameDamageRect damageRects[KVMFR_MAX_DAMAGE_RECTS];
|
||||
KVMFRFrameFlags flags; // bit field combination of FRAME_FLAG_*
|
||||
|
||||
// HDR static metadata (valid when FRAME_FLAG_HDR is set)
|
||||
// Display color primaries in 0.00002 units (SMPTE ST 2086 format)
|
||||
uint16_t hdrDisplayPrimary[3][2]; // Rx,Ry, Gx,Gy, Bx,By
|
||||
uint16_t hdrWhitePoint[2]; // Wx, Wy
|
||||
uint32_t hdrMaxDisplayLuminance; // Max mastering display luminance (0.0001 cd/m²)
|
||||
uint32_t hdrMinDisplayLuminance; // Min mastering display luminance (0.0001 cd/m²)
|
||||
uint32_t hdrMaxContentLightLevel; // MaxCLL (cd/m²)
|
||||
uint32_t hdrMaxFrameAverageLightLevel; // MaxFALL (cd/m²)
|
||||
}
|
||||
KVMFRFrame;
|
||||
|
||||
|
||||
@@ -52,7 +52,8 @@ static inline IDDCX_WIRE_BITS_PER_COMPONENT GetWireBitsPerComponent(bool hdr)
|
||||
IDDCX_WIRE_BITS_PER_COMPONENT bits = {};
|
||||
bits.Rgb = IDDCX_BITS_PER_COMPONENT_8;
|
||||
if (hdr)
|
||||
bits.Rgb = (IDDCX_BITS_PER_COMPONENT)(bits.Rgb | IDDCX_BITS_PER_COMPONENT_10);
|
||||
bits.Rgb = (IDDCX_BITS_PER_COMPONENT)(bits.Rgb |
|
||||
IDDCX_BITS_PER_COMPONENT_10 | IDDCX_BITS_PER_COMPONENT_16);
|
||||
bits.YCbCr444 = IDDCX_BITS_PER_COMPONENT_NONE;
|
||||
bits.YCbCr422 = IDDCX_BITS_PER_COMPONENT_NONE;
|
||||
bits.YCbCr420 = IDDCX_BITS_PER_COMPONENT_NONE;
|
||||
@@ -188,7 +189,7 @@ void CIndirectDeviceContext::InitAdapter()
|
||||
dxgiAdapter->Release();
|
||||
|
||||
if ((adapterDesc.VendorId == 0x1414 && adapterDesc.DeviceId == 0x008c) || // Microsoft Basic Render Driver
|
||||
(adapterDesc.VendorId == 0x1b36 && adapterDesc.DeviceId == 0x000d) || // QXL
|
||||
(adapterDesc.VendorId == 0x1b36 && adapterDesc.DeviceId == 0x000d) || // QXL
|
||||
(adapterDesc.VendorId == 0x1234 && adapterDesc.DeviceId == 0x1111)) // QEMU Standard VGA
|
||||
continue;
|
||||
|
||||
@@ -829,6 +830,35 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
||||
++m_formatVer;
|
||||
}
|
||||
|
||||
// Detect HDR metadata changes that require a format version bump
|
||||
// so the client knows to re-apply the HDR image description.
|
||||
if (srcFormat.hdr)
|
||||
{
|
||||
if (!m_lastHDRActive ||
|
||||
memcmp(m_lastHDRDisplayPrimary, srcFormat.displayPrimary, sizeof(m_lastHDRDisplayPrimary)) != 0 ||
|
||||
memcmp(m_lastHDRWhitePoint , srcFormat.whitePoint , sizeof(m_lastHDRWhitePoint )) != 0 ||
|
||||
m_lastHDRMaxDisplayLuminance != srcFormat.maxDisplayLuminance ||
|
||||
m_lastHDRMinDisplayLuminance != srcFormat.minDisplayLuminance ||
|
||||
m_lastHDRMaxContentLightLevel != srcFormat.maxContentLightLevel ||
|
||||
m_lastHDRMaxFrameAverageLightLevel != srcFormat.maxFrameAverageLightLevel)
|
||||
{
|
||||
++m_formatVer;
|
||||
}
|
||||
}
|
||||
else if (m_lastHDRActive)
|
||||
{
|
||||
// HDR was turned off
|
||||
++m_formatVer;
|
||||
}
|
||||
|
||||
m_lastHDRActive = srcFormat.hdr;
|
||||
memcpy(m_lastHDRDisplayPrimary, srcFormat.displayPrimary, sizeof(m_lastHDRDisplayPrimary));
|
||||
memcpy(m_lastHDRWhitePoint , srcFormat.whitePoint , sizeof(m_lastHDRWhitePoint ));
|
||||
m_lastHDRMaxDisplayLuminance = srcFormat.maxDisplayLuminance;
|
||||
m_lastHDRMinDisplayLuminance = srcFormat.minDisplayLuminance;
|
||||
m_lastHDRMaxContentLightLevel = srcFormat.maxContentLightLevel;
|
||||
m_lastHDRMaxFrameAverageLightLevel = srcFormat.maxFrameAverageLightLevel;
|
||||
|
||||
if (++m_frameIndex == LGMP_Q_FRAME_LEN)
|
||||
m_frameIndex = 0;
|
||||
|
||||
@@ -850,9 +880,6 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
||||
(dstFormat.hdr ? FRAME_FLAG_HDR : 0) |
|
||||
(dstFormat.hdrPQ ? FRAME_FLAG_HDR_PQ : 0);
|
||||
|
||||
if (dstFormat.format == FRAME_TYPE_RGBA16F)
|
||||
flags |= FRAME_FLAG_HDR;
|
||||
|
||||
if (maxRows < dstFormat.desc.Height)
|
||||
flags |= FRAME_FLAG_TRUNCATED;
|
||||
|
||||
@@ -870,6 +897,18 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
||||
fi->flags = flags;
|
||||
fi->rotation = FRAME_ROT_0;
|
||||
fi->type = dstFormat.format;
|
||||
|
||||
// Populate HDR metadata if the frame is HDR
|
||||
if (flags & FRAME_FLAG_HDR)
|
||||
{
|
||||
memcpy(fi->hdrDisplayPrimary, srcFormat.displayPrimary, sizeof(fi->hdrDisplayPrimary));
|
||||
memcpy(fi->hdrWhitePoint , srcFormat.whitePoint , sizeof(fi->hdrWhitePoint));
|
||||
fi->hdrMaxDisplayLuminance = srcFormat.maxDisplayLuminance;
|
||||
fi->hdrMinDisplayLuminance = srcFormat.minDisplayLuminance;
|
||||
fi->hdrMaxContentLightLevel = srcFormat.maxContentLightLevel;
|
||||
fi->hdrMaxFrameAverageLightLevel = srcFormat.maxFrameAverageLightLevel;
|
||||
}
|
||||
|
||||
fi->damageRectsCount = 0;
|
||||
if (nbDirtyRects <= ARRAYSIZE(fi->damageRects))
|
||||
{
|
||||
@@ -984,6 +1023,59 @@ void CIndirectDeviceContext::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, co
|
||||
}
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::SetHDRActive(const struct IDDCX_HDR_METADATA * hdrMeta)
|
||||
{
|
||||
AcquireSRWLockExclusive(&m_hdrLock);
|
||||
|
||||
if (!hdrMeta)
|
||||
{
|
||||
m_hdrActive = false;
|
||||
ReleaseSRWLockExclusive(&m_hdrLock);
|
||||
return;
|
||||
}
|
||||
|
||||
auto& hdr10 = hdrMeta->Hdr10;
|
||||
|
||||
m_hdrActive = true;
|
||||
|
||||
m_hdrDisplayPrimary[0][0] = hdr10.RedPrimary [0];
|
||||
m_hdrDisplayPrimary[0][1] = hdr10.RedPrimary [1];
|
||||
m_hdrDisplayPrimary[1][0] = hdr10.GreenPrimary[0];
|
||||
m_hdrDisplayPrimary[1][1] = hdr10.GreenPrimary[1];
|
||||
m_hdrDisplayPrimary[2][0] = hdr10.BluePrimary [0];
|
||||
m_hdrDisplayPrimary[2][1] = hdr10.BluePrimary [1];
|
||||
m_hdrWhitePoint [0] = hdr10.WhitePoint [0];
|
||||
m_hdrWhitePoint [1] = hdr10.WhitePoint [1];
|
||||
|
||||
m_hdrMaxDisplayLuminance = hdr10.MaxMasteringLuminance;
|
||||
m_hdrMinDisplayLuminance = hdr10.MinMasteringLuminance;
|
||||
m_hdrMaxContentLightLevel = hdr10.MaxContentLightLevel;
|
||||
m_hdrMaxFrameAverageLightLevel = hdr10.MaxFrameAverageLightLevel;
|
||||
|
||||
ReleaseSRWLockExclusive(&m_hdrLock);
|
||||
}
|
||||
|
||||
bool CIndirectDeviceContext::GetHDRMetadata(D12FrameFormat & format) const
|
||||
{
|
||||
AcquireSRWLockShared(&m_hdrLock);
|
||||
|
||||
if (!m_hdrActive)
|
||||
{
|
||||
ReleaseSRWLockShared(&m_hdrLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(format.displayPrimary, m_hdrDisplayPrimary, sizeof(format.displayPrimary));
|
||||
memcpy(format.whitePoint , m_hdrWhitePoint , sizeof(format.whitePoint ));
|
||||
format.maxDisplayLuminance = m_hdrMaxDisplayLuminance;
|
||||
format.minDisplayLuminance = m_hdrMinDisplayLuminance;
|
||||
format.maxContentLightLevel = m_hdrMaxContentLightLevel;
|
||||
format.maxFrameAverageLightLevel = m_hdrMaxFrameAverageLightLevel;
|
||||
|
||||
ReleaseSRWLockShared(&m_hdrLock);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CIndirectDeviceContext::ResendCursor() const
|
||||
{
|
||||
PLGMPMemory mem = m_pointerShape;
|
||||
|
||||
@@ -90,6 +90,27 @@ private:
|
||||
UINT m_iddCxVersion = 0;
|
||||
bool m_canProcessFP16 = false;
|
||||
|
||||
// HDR state from EvtIddCxMonitorSetDefaultHdrMetadata (IddCx 1.10+)
|
||||
// Protected by m_hdrLock - accessed from both the IDD callback thread
|
||||
// (SetHDRActive) and the swap-chain thread (PrepareFrameBuffer, GetHDRMetadata).
|
||||
mutable SRWLOCK m_hdrLock = SRWLOCK_INIT;
|
||||
bool m_hdrActive = false;
|
||||
uint16_t m_hdrDisplayPrimary[3][2] = {};
|
||||
uint16_t m_hdrWhitePoint[2] = {};
|
||||
uint32_t m_hdrMaxDisplayLuminance = 0;
|
||||
uint32_t m_hdrMinDisplayLuminance = 0;
|
||||
uint32_t m_hdrMaxContentLightLevel = 0;
|
||||
uint32_t m_hdrMaxFrameAverageLightLevel = 0;
|
||||
|
||||
// Previous HDR metadata used to detect changes for formatVer bumps
|
||||
uint16_t m_lastHDRDisplayPrimary[3][2] = {};
|
||||
uint16_t m_lastHDRWhitePoint[2] = {};
|
||||
uint32_t m_lastHDRMaxDisplayLuminance = 0;
|
||||
uint32_t m_lastHDRMinDisplayLuminance = 0;
|
||||
uint32_t m_lastHDRMaxContentLightLevel = 0;
|
||||
uint32_t m_lastHDRMaxFrameAverageLightLevel = 0;
|
||||
bool m_lastHDRActive = false;
|
||||
|
||||
void QueryIddCxCapabilities();
|
||||
bool CanUseIddCx110DDIs() const { return m_canProcessFP16; }
|
||||
|
||||
@@ -156,6 +177,22 @@ public:
|
||||
|
||||
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR & info, const BYTE * data);
|
||||
|
||||
// Tracks HDR state from EvtIddCxMonitorSetDefaultHdrMetadata
|
||||
void SetHDRActive(const struct IDDCX_HDR_METADATA * hdrMeta);
|
||||
|
||||
// Returns true if the display is currently in HDR mode
|
||||
bool IsHDRActive() const
|
||||
{
|
||||
AcquireSRWLockShared(&m_hdrLock);
|
||||
bool result = m_hdrActive;
|
||||
ReleaseSRWLockShared(&m_hdrLock);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Copies current HDR metadata into the provided D12FrameFormat.
|
||||
// Returns true if HDR is active and metadata was copied.
|
||||
bool GetHDRMetadata(D12FrameFormat & format) const;
|
||||
|
||||
CIVSHMEM &GetIVSHMEM() { return m_ivshmem; }
|
||||
};
|
||||
|
||||
|
||||
@@ -41,6 +41,19 @@ struct D12FrameFormat
|
||||
FrameType format = FRAME_TYPE_INVALID;
|
||||
bool hdr = false;
|
||||
bool hdrPQ = false;
|
||||
|
||||
// HDR static metadata (SMPTE ST 2086)
|
||||
// Display color primaries in 0.00002 units (xy coordinates)
|
||||
uint16_t displayPrimary[3][2];
|
||||
// White point in 0.00002 units
|
||||
uint16_t whitePoint[2];
|
||||
// Max display luminance in 0.0001 cd/m² units
|
||||
uint32_t maxDisplayLuminance;
|
||||
// Min display luminance in 0.0001 cd/m² units
|
||||
uint32_t minDisplayLuminance;
|
||||
// MaxCLL and MaxFALL in cd/m²
|
||||
uint32_t maxContentLightLevel;
|
||||
uint32_t maxFrameAverageLightLevel;
|
||||
};
|
||||
|
||||
class CPostProcessEffect
|
||||
|
||||
@@ -366,8 +366,51 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
||||
srcFormat.width = (unsigned)srcDesc.Width;
|
||||
srcFormat.height = srcDesc.Height;
|
||||
srcFormat.format = GetFrameType(srcDesc.Format);
|
||||
srcFormat.hdr = srcDesc.Format == DXGI_FORMAT_R16G16B16A16_FLOAT;
|
||||
srcFormat.hdrPQ = false;
|
||||
|
||||
// Determine HDR status from both format and color space.
|
||||
// HDR metadata loading is handled inside each branch so the logic
|
||||
// is self-contained: first identify the content type, then load the
|
||||
// matching metadata.
|
||||
if (srcDesc.Format == DXGI_FORMAT_R16G16B16A16_FLOAT)
|
||||
{
|
||||
// FP16 is HDR content (scRGB / linear, not PQ-curve).
|
||||
// FP16 always carries HDR color data regardless of OS HDR mode,
|
||||
// but metadata (primaries, luminances) may be unavailable.
|
||||
srcFormat.hdr = true;
|
||||
srcFormat.hdrPQ = false;
|
||||
if (!m_devContext->GetHDRMetadata(srcFormat))
|
||||
{
|
||||
// No HDR metadata from the OS; provide reasonable defaults
|
||||
// so downstream consumers have valid primaries and luminances.
|
||||
// BT.709/sRGB primaries (in 0.00002 units):
|
||||
srcFormat.displayPrimary[0][0] = 13250; // Rx
|
||||
srcFormat.displayPrimary[0][1] = 34500; // Ry
|
||||
srcFormat.displayPrimary[1][0] = 7500; // Gx
|
||||
srcFormat.displayPrimary[1][1] = 30000; // Gy
|
||||
srcFormat.displayPrimary[2][0] = 34000; // Bx
|
||||
srcFormat.displayPrimary[2][1] = 16000; // By
|
||||
// D65 white point (in 0.00002 units):
|
||||
srcFormat.whitePoint[0] = 15635;
|
||||
srcFormat.whitePoint[1] = 16450;
|
||||
// 80 cd/m² display, 0.005 cd/m² black (in 0.0001 cd/m² units):
|
||||
srcFormat.maxDisplayLuminance = 800000;
|
||||
srcFormat.minDisplayLuminance = 50;
|
||||
// Content light levels unknown:
|
||||
srcFormat.maxContentLightLevel = 0;
|
||||
srcFormat.maxFrameAverageLightLevel = 0;
|
||||
}
|
||||
}
|
||||
else if (m_devContext->CanProcessFP16() && m_devContext->IsHDRActive())
|
||||
{
|
||||
// Non-FP16 format (e.g., RGBA10) with OS HDR mode active.
|
||||
// Windows applies the PQ (ST.2084) transfer function for non-FP16 HDR.
|
||||
srcFormat.hdr = true;
|
||||
srcFormat.hdrPQ = true;
|
||||
|
||||
// Load HDR metadata; if none is available the frame is not HDR.
|
||||
if (!m_devContext->GetHDRMetadata(srcFormat))
|
||||
srcFormat.hdr = false;
|
||||
}
|
||||
|
||||
bool postProcessFormatChanged = false;
|
||||
if (!m_postProcessor.Configure(srcFormat, &postProcessFormatChanged))
|
||||
|
||||
@@ -165,8 +165,11 @@ NTSTATUS LGIddAdapterCommitModes2(IDDCX_ADAPTER adapter, const IDARG_IN_COMMITMO
|
||||
NTSTATUS LGIddMonitorSetDefaultHdrMetadata(IDDCX_MONITOR monitor,
|
||||
const IDARG_IN_MONITOR_SET_DEFAULT_HDR_METADATA* inArgs)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(monitor);
|
||||
UNREFERENCED_PARAMETER(inArgs);
|
||||
auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor);
|
||||
auto* ctx = wrapper->context->GetDeviceContext();
|
||||
|
||||
ctx->SetHDRActive(inArgs->pHdrMetaData);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,14 +36,24 @@ bool CHDR16to10Effect::Init(const ComPtr<ID3D12Device3>& device)
|
||||
const char * shader =
|
||||
"cbuffer Constants : register(b0)\n"
|
||||
"{\n"
|
||||
" float SDRWhiteLevel;\n"
|
||||
" float ReferenceWhiteNits;\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"
|
||||
"[numthreads(" POST_PROCESS_THREADS_STR ", " POST_PROCESS_THREADS_STR ", 1)]\n"
|
||||
"void main(uint3 dt : SV_DispatchThreadID)\n"
|
||||
"{\n"
|
||||
" dst[dt.xy] = float4(src[dt.xy].rgb * SDRWhiteLevel, src[dt.xy].a);\n"
|
||||
" float3 linear = src[dt.xy].rgb * ReferenceWhiteNits;\n"
|
||||
" // scRGB to PQ (ST.2084)\n"
|
||||
" float3 Y = linear / 10000.0;\n"
|
||||
" float3 Ym1 = pow(max(Y, 0.0), PQ_m1);\n"
|
||||
" float3 pq = pow((PQ_c1 + PQ_c2 * Ym1) / (1.0 + PQ_c3 * Ym1), PQ_m2);\n"
|
||||
" dst[dt.xy] = float4(pq, src[dt.xy].a);\n"
|
||||
"}\n";
|
||||
|
||||
if (!InitCompute(device, ranges, ARRAYSIZE(ranges), nullptr, 0, shader))
|
||||
@@ -99,10 +109,20 @@ PostProcessStatus CHDR16to10Effect::SetFormat(
|
||||
m_threadsX = ((unsigned)desc.Width + (Threads - 1)) / Threads;
|
||||
m_threadsY = ((unsigned)desc.Height + (Threads - 1)) / Threads;
|
||||
|
||||
dst.desc = desc;
|
||||
dst.desc = desc;
|
||||
dst.format = FRAME_TYPE_RGBA10;
|
||||
dst.hdr = true;
|
||||
dst.hdrPQ = false;
|
||||
dst.hdr = true;
|
||||
dst.hdrPQ = true;
|
||||
|
||||
// Explicitly propagate HDR static metadata so it survives post-processing.
|
||||
// Do not rely on the caller's copy semantics in CPostProcessor::Configure().
|
||||
memcpy(dst.displayPrimary, src.displayPrimary, sizeof(dst.displayPrimary));
|
||||
memcpy(dst.whitePoint , src.whitePoint , sizeof(dst.whitePoint ));
|
||||
dst.maxDisplayLuminance = src.maxDisplayLuminance;
|
||||
dst.minDisplayLuminance = src.minDisplayLuminance;
|
||||
dst.maxContentLightLevel = src.maxContentLightLevel;
|
||||
dst.maxFrameAverageLightLevel = src.maxFrameAverageLightLevel;
|
||||
|
||||
return PostProcessStatus::SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ class CHDR16to10Effect : public CComputeEffect
|
||||
private:
|
||||
struct Consts
|
||||
{
|
||||
float SDRWhiteLevel;
|
||||
} m_consts = { 1.0f };
|
||||
float ReferenceWhiteNits; // scRGB reference white in nits (typically 80)
|
||||
} m_consts = { 80.0f };
|
||||
ComPtr<ID3D12Resource> m_constBuffer;
|
||||
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user