mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-07-20 14:22:00 +00:00
[idd] hdr: propagate per-frame HDR metadata
Handle DEFAULT, NEW, and UNCHANGED IDDCX HDR10 metadata states and publish valid mastering metadata through KVMFR. Preserve the metadata across post-processing without reallocating resources for metadata-only changes.
This commit is contained in:
@@ -143,7 +143,8 @@ enum
|
||||
FRAME_FLAG_REQUEST_ACTIVATION = 0x2 ,
|
||||
FRAME_FLAG_TRUNCATED = 0x4 , // ivshmem was too small for the frame
|
||||
FRAME_FLAG_HDR = 0x8 , // RGBA10 may not be HDR
|
||||
FRAME_FLAG_HDR_PQ = 0x10 // HDR PQ has been applied to the frame
|
||||
FRAME_FLAG_HDR_PQ = 0x10, // HDR PQ has been applied to the frame
|
||||
FRAME_FLAG_HDR_METADATA = 0x20 // HDR static metadata fields are valid
|
||||
};
|
||||
|
||||
typedef uint32_t KVMFRFrameFlags;
|
||||
@@ -167,7 +168,7 @@ typedef struct KVMFRFrame
|
||||
FrameDamageRect damageRects[KVMFR_MAX_DAMAGE_RECTS];
|
||||
KVMFRFrameFlags flags; // bit field combination of FRAME_FLAG_*
|
||||
|
||||
// HDR static metadata (valid when FRAME_FLAG_HDR is set)
|
||||
// HDR static metadata (valid when FRAME_FLAG_HDR_METADATA 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
|
||||
|
||||
@@ -1052,24 +1052,21 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
||||
// Detect HDR metadata changes that require a format version bump
|
||||
// so the client knows to re-apply the HDR image description.
|
||||
//
|
||||
// Use dstFormat: post-processing (CHDR16to10Effect) can rotate the gamut
|
||||
// (BT.709 scRGB -> BT.2020 PQ), so the metadata describing the *emitted*
|
||||
// frame lives in dstFormat, not srcFormat. Forwarding srcFormat here would
|
||||
// tag PQ/BT.2020 pixels with the source's BT.709 primaries and mislead
|
||||
// consumers that derive their colour conversion from these values.
|
||||
// Use dstFormat so post-processing can propagate any metadata adjustments.
|
||||
if (dstFormat.hdr)
|
||||
{
|
||||
if (!m_lastHDRActive ||
|
||||
memcmp(m_lastHDRDisplayPrimary, dstFormat.displayPrimary, sizeof(m_lastHDRDisplayPrimary)) != 0 ||
|
||||
const bool metadataChanged =
|
||||
m_lastHDRMetadata != dstFormat.hdrMetadata ||
|
||||
(dstFormat.hdrMetadata &&
|
||||
(memcmp(m_lastHDRDisplayPrimary, dstFormat.displayPrimary, sizeof(m_lastHDRDisplayPrimary)) != 0 ||
|
||||
memcmp(m_lastHDRWhitePoint , dstFormat.whitePoint , sizeof(m_lastHDRWhitePoint )) != 0 ||
|
||||
m_lastHDRMaxDisplayLuminance != dstFormat.maxDisplayLuminance ||
|
||||
m_lastHDRMinDisplayLuminance != dstFormat.minDisplayLuminance ||
|
||||
m_lastHDRMaxContentLightLevel != dstFormat.maxContentLightLevel ||
|
||||
m_lastHDRMaxFrameAverageLightLevel != dstFormat.maxFrameAverageLightLevel ||
|
||||
m_lastSDRWhiteLevel != dstFormat.sdrWhiteLevel)
|
||||
{
|
||||
m_lastHDRMaxFrameAverageLightLevel != dstFormat.maxFrameAverageLightLevel));
|
||||
|
||||
if (!m_lastHDRActive || metadataChanged || m_lastSDRWhiteLevel != dstFormat.sdrWhiteLevel)
|
||||
++m_formatVer;
|
||||
}
|
||||
}
|
||||
else if (m_lastHDRActive)
|
||||
{
|
||||
@@ -1077,7 +1074,8 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
||||
++m_formatVer;
|
||||
}
|
||||
|
||||
m_lastHDRActive = dstFormat.hdr;
|
||||
m_lastHDRActive = dstFormat.hdr;
|
||||
m_lastHDRMetadata = dstFormat.hdrMetadata;
|
||||
memcpy(m_lastHDRDisplayPrimary, dstFormat.displayPrimary, sizeof(m_lastHDRDisplayPrimary));
|
||||
memcpy(m_lastHDRWhitePoint , dstFormat.whitePoint , sizeof(m_lastHDRWhitePoint ));
|
||||
m_lastHDRMaxDisplayLuminance = dstFormat.maxDisplayLuminance;
|
||||
@@ -1100,8 +1098,9 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
||||
const unsigned maxRows = (unsigned)(m_maxFrameSize / pitch);
|
||||
const int bpp = dstFormat.format == FRAME_TYPE_RGBA16F ? 8 : 4;
|
||||
KVMFRFrameFlags flags =
|
||||
(dstFormat.hdr ? FRAME_FLAG_HDR : 0) |
|
||||
(dstFormat.hdrPQ ? FRAME_FLAG_HDR_PQ : 0);
|
||||
(dstFormat.hdr ? FRAME_FLAG_HDR : 0) |
|
||||
(dstFormat.hdrPQ ? FRAME_FLAG_HDR_PQ : 0) |
|
||||
(dstFormat.hdrMetadata ? FRAME_FLAG_HDR_METADATA : 0);
|
||||
|
||||
if (maxRows < dstFormat.desc.Height)
|
||||
flags |= FRAME_FLAG_TRUNCATED;
|
||||
@@ -1122,10 +1121,7 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
||||
fi->rotation = FRAME_ROT_0;
|
||||
fi->type = dstFormat.format;
|
||||
|
||||
// Populate HDR metadata if the frame is HDR. Use dstFormat so the primaries
|
||||
// describe the emitted frame after any gamut conversion in post-processing
|
||||
// (see the metadata-change detection above).
|
||||
if (flags & FRAME_FLAG_HDR)
|
||||
if (flags & FRAME_FLAG_HDR_METADATA)
|
||||
{
|
||||
memcpy(fi->hdrDisplayPrimary, dstFormat.displayPrimary, sizeof(fi->hdrDisplayPrimary));
|
||||
memcpy(fi->hdrWhitePoint , dstFormat.whitePoint , sizeof(fi->hdrWhitePoint));
|
||||
@@ -1134,6 +1130,15 @@ CIndirectDeviceContext::PreparedFrameBuffer CIndirectDeviceContext::PrepareFrame
|
||||
fi->hdrMaxContentLightLevel = dstFormat.maxContentLightLevel;
|
||||
fi->hdrMaxFrameAverageLightLevel = dstFormat.maxFrameAverageLightLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(fi->hdrDisplayPrimary, 0, sizeof(fi->hdrDisplayPrimary));
|
||||
memset(fi->hdrWhitePoint , 0, sizeof(fi->hdrWhitePoint ));
|
||||
fi->hdrMaxDisplayLuminance = 0;
|
||||
fi->hdrMinDisplayLuminance = 0;
|
||||
fi->hdrMaxContentLightLevel = 0;
|
||||
fi->hdrMaxFrameAverageLightLevel = 0;
|
||||
}
|
||||
|
||||
fi->damageRectsCount = 0;
|
||||
if (nbDirtyRects <= ARRAYSIZE(fi->damageRects))
|
||||
|
||||
@@ -135,6 +135,7 @@ private:
|
||||
uint32_t m_lastHDRMaxFrameAverageLightLevel = 0;
|
||||
uint32_t m_lastSDRWhiteLevel = 0;
|
||||
bool m_lastHDRActive = false;
|
||||
bool m_lastHDRMetadata = false;
|
||||
|
||||
void QueryIddCxCapabilities();
|
||||
bool CanUseIddCx110DDIs() const { return m_canProcessFP16; }
|
||||
|
||||
@@ -17,8 +17,21 @@
|
||||
#include "effect/CHDR16to10Effect.h"
|
||||
#include "effect/CRGB24Effect.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
static void CopyHDRMetadata(D12FrameFormat& dst, const D12FrameFormat& src)
|
||||
{
|
||||
dst.hdrMetadata = src.hdrMetadata;
|
||||
dst.sdrWhiteLevel = src.sdrWhiteLevel;
|
||||
std::memcpy(dst.displayPrimary, src.displayPrimary, sizeof(dst.displayPrimary));
|
||||
std::memcpy(dst.whitePoint, src.whitePoint, sizeof(dst.whitePoint));
|
||||
dst.maxDisplayLuminance = src.maxDisplayLuminance;
|
||||
dst.minDisplayLuminance = src.minDisplayLuminance;
|
||||
dst.maxContentLightLevel = src.maxContentLightLevel;
|
||||
dst.maxFrameAverageLightLevel = src.maxFrameAverageLightLevel;
|
||||
}
|
||||
|
||||
bool CPostProcessor::Init(std::shared_ptr<CD3D12Device> dx12Device)
|
||||
{
|
||||
m_dx12Device = dx12Device;
|
||||
@@ -73,9 +86,14 @@ bool CPostProcessor::Configure(const D12FrameFormat& srcFormat, bool * formatCha
|
||||
srcFormat.width == m_srcFormat.width &&
|
||||
srcFormat.height == m_srcFormat.height &&
|
||||
srcFormat.hdr == m_srcFormat.hdr &&
|
||||
srcFormat.hdrPQ == m_srcFormat.hdrPQ &&
|
||||
srcFormat.sdrWhiteLevel == m_srcFormat.sdrWhiteLevel)
|
||||
srcFormat.hdrPQ == m_srcFormat.hdrPQ)
|
||||
{
|
||||
// Static HDR metadata may change independently of the resource format.
|
||||
// Propagate it without recreating textures or post-processing state.
|
||||
CopyHDRMetadata(m_srcFormat, srcFormat);
|
||||
CopyHDRMetadata(m_dstFormat, srcFormat);
|
||||
return true;
|
||||
}
|
||||
|
||||
D12FrameFormat oldDst = m_dstFormat;
|
||||
D12FrameFormat cur = srcFormat;
|
||||
|
||||
@@ -42,6 +42,7 @@ struct D12FrameFormat
|
||||
FrameType format = FRAME_TYPE_INVALID;
|
||||
bool hdr = false;
|
||||
bool hdrPQ = false;
|
||||
bool hdrMetadata = false;
|
||||
uint32_t sdrWhiteLevel = KVMFR_SDR_WHITE_LEVEL_DEFAULT;
|
||||
|
||||
// HDR static metadata (SMPTE ST 2086)
|
||||
@@ -49,9 +50,9 @@ struct D12FrameFormat
|
||||
uint16_t displayPrimary[3][2];
|
||||
// White point in 0.00002 units
|
||||
uint16_t whitePoint[2];
|
||||
// Max display luminance in 0.0001 cd/m² units
|
||||
// Max mastering display luminance in whole cd/m²
|
||||
uint32_t maxDisplayLuminance;
|
||||
// Min display luminance in 0.0001 cd/m² units
|
||||
// Min mastering display luminance in 0.0001 cd/m² units
|
||||
uint32_t minDisplayLuminance;
|
||||
// MaxCLL and MaxFALL in cd/m²
|
||||
uint32_t maxContentLightLevel;
|
||||
|
||||
@@ -206,6 +206,7 @@ bool CSwapChainProcessor::SwapChainThreadCore()
|
||||
surface = buffer.MetaData.pSurface;
|
||||
colorSpace = buffer.MetaData.SurfaceColorSpace;
|
||||
sdrWhiteLevel = buffer.MetaData.SdrWhiteLevel;
|
||||
UpdateHDRMetadata(buffer.MetaData);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -398,6 +399,72 @@ void CSwapChainProcessor::AccumulateFrameDamage(
|
||||
m_nbPendingDirtyRects += nbDirtyRects;
|
||||
}
|
||||
|
||||
#ifdef HAS_IDDCX_110
|
||||
void CSwapChainProcessor::UpdateHDRMetadata(const IDDCX_METADATA2& metadata)
|
||||
{
|
||||
if (!(metadata.ValidFlags & IDDCX_METADATA2_VALID_FLAGS_HDR10METADATA))
|
||||
return;
|
||||
|
||||
const IDDCX_HDR10_FRAME_METADATA& frame = metadata.Hdr10FrameMetaData;
|
||||
switch (frame.Type)
|
||||
{
|
||||
case IDDCX_HDR10_FRAME_METADATA_TYPE_DEFAULT:
|
||||
if (!m_useDefaultHDRMetadata)
|
||||
DEBUG_TRACE("HDR10 frame metadata switched to the monitor default");
|
||||
m_useDefaultHDRMetadata = true;
|
||||
m_hasNewHDRMetadata = false;
|
||||
break;
|
||||
|
||||
case IDDCX_HDR10_FRAME_METADATA_TYPE_UNCHANGED:
|
||||
break;
|
||||
|
||||
case IDDCX_HDR10_FRAME_METADATA_TYPE_NEW:
|
||||
if (!m_hasNewHDRMetadata ||
|
||||
memcmp(&m_newHDRMetadata, &frame.NewMetaData,
|
||||
sizeof(m_newHDRMetadata)) != 0)
|
||||
DEBUG_TRACE("Received new HDR10 frame metadata");
|
||||
m_newHDRMetadata = frame.NewMetaData;
|
||||
m_useDefaultHDRMetadata = false;
|
||||
m_hasNewHDRMetadata = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_WARN("Invalid HDR10 frame metadata type %u",
|
||||
static_cast<unsigned>(frame.Type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool CSwapChainProcessor::GetHDRMetadata(D12FrameFormat& format) const
|
||||
{
|
||||
#ifdef HAS_IDDCX_110
|
||||
if (m_useDefaultHDRMetadata)
|
||||
return m_devContext->GetHDRMetadata(format);
|
||||
|
||||
if (!m_hasNewHDRMetadata)
|
||||
return false;
|
||||
|
||||
const IDDCX_HDR10_METADATA& metadata = m_newHDRMetadata;
|
||||
format.displayPrimary[0][0] = metadata.RedPrimary [0];
|
||||
format.displayPrimary[0][1] = metadata.RedPrimary [1];
|
||||
format.displayPrimary[1][0] = metadata.GreenPrimary[0];
|
||||
format.displayPrimary[1][1] = metadata.GreenPrimary[1];
|
||||
format.displayPrimary[2][0] = metadata.BluePrimary [0];
|
||||
format.displayPrimary[2][1] = metadata.BluePrimary [1];
|
||||
format.whitePoint [0] = metadata.WhitePoint [0];
|
||||
format.whitePoint [1] = metadata.WhitePoint [1];
|
||||
format.maxDisplayLuminance = metadata.MaxMasteringLuminance;
|
||||
format.minDisplayLuminance = metadata.MinMasteringLuminance;
|
||||
format.maxContentLightLevel = metadata.MaxContentLightLevel;
|
||||
format.maxFrameAverageLightLevel = metadata.MaxFrameAverageLightLevel;
|
||||
return true;
|
||||
#else
|
||||
UNREFERENCED_PARAMETER(format);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer, unsigned dirtyRectCount,
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel)
|
||||
{
|
||||
@@ -480,7 +547,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
||||
// already applied to the pixel data.
|
||||
srcFormat.hdr = true;
|
||||
srcFormat.hdrPQ = true;
|
||||
if (!m_devContext->GetHDRMetadata(srcFormat))
|
||||
if (!GetHDRMetadata(srcFormat))
|
||||
{
|
||||
// HDR is active but the OS has not delivered static metadata yet
|
||||
// (e.g. a brief window during a mode switch). The pixels are still
|
||||
@@ -504,6 +571,8 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
||||
srcFormat.maxContentLightLevel = 0;
|
||||
srcFormat.maxFrameAverageLightLevel = 0;
|
||||
}
|
||||
else
|
||||
srcFormat.hdrMetadata = true;
|
||||
break;
|
||||
|
||||
case DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709:
|
||||
@@ -511,7 +580,7 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
||||
// curve has not been applied.
|
||||
srcFormat.hdr = true;
|
||||
srcFormat.hdrPQ = false;
|
||||
if (!m_devContext->GetHDRMetadata(srcFormat))
|
||||
if (!GetHDRMetadata(srcFormat))
|
||||
{
|
||||
// No HDR metadata from the OS; provide reasonable defaults
|
||||
// so downstream consumers have valid primaries and luminances.
|
||||
@@ -533,6 +602,8 @@ bool CSwapChainProcessor::SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer
|
||||
srcFormat.maxContentLightLevel = 0;
|
||||
srcFormat.maxFrameAverageLightLevel = 0;
|
||||
}
|
||||
else
|
||||
srcFormat.hdrMetadata = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -75,6 +75,14 @@ private:
|
||||
unsigned m_nbPendingDirtyRects = 0;
|
||||
bool m_hasPendingDamage = true;
|
||||
|
||||
#ifdef HAS_IDDCX_110
|
||||
// The per-frame metadata stream can select the monitor default, provide a
|
||||
// replacement block, or retain the selection from the previous frame.
|
||||
bool m_useDefaultHDRMetadata = true;
|
||||
bool m_hasNewHDRMetadata = false;
|
||||
IDDCX_HDR10_METADATA m_newHDRMetadata = {};
|
||||
#endif
|
||||
|
||||
static DWORD CALLBACK _SwapChainThread(LPVOID arg);
|
||||
void SwapChainThread();
|
||||
bool SwapChainThreadCore();
|
||||
@@ -87,6 +95,10 @@ private:
|
||||
CD3D12CommandQueue * queue, bool result, void * param1, void * param2);
|
||||
void AccumulateFrameDamage(const RECT * dirtyRects, unsigned nbDirtyRects);
|
||||
void SetFullPendingDamage();
|
||||
#ifdef HAS_IDDCX_110
|
||||
void UpdateHDRMetadata(const IDDCX_METADATA2& metadata);
|
||||
#endif
|
||||
bool GetHDRMetadata(D12FrameFormat& format) const;
|
||||
bool SwapChainNewFrame(ComPtr<IDXGIResource> acquiredBuffer, unsigned dirtyRectCount,
|
||||
DXGI_COLOR_SPACE_TYPE colorSpace, UINT sdrWhiteLevel);
|
||||
|
||||
|
||||
@@ -123,18 +123,10 @@ PostProcessStatus CHDR16to10Effect::SetFormat(
|
||||
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().
|
||||
// The shader rotates the gamut from BT.709 to BT.2020, so advertise BT.2020
|
||||
// mastering primaries here rather than forwarding the source's BT.709 set
|
||||
// (in 0.00002 units) - otherwise consumers see PQ/BT.2020 content tagged
|
||||
// with BT.709 primaries.
|
||||
dst.displayPrimary[0][0] = 35400; // Rx
|
||||
dst.displayPrimary[0][1] = 14600; // Ry
|
||||
dst.displayPrimary[1][0] = 8500; // Gx
|
||||
dst.displayPrimary[1][1] = 39850; // Gy
|
||||
dst.displayPrimary[2][0] = 6550; // Bx
|
||||
dst.displayPrimary[2][1] = 2300; // By
|
||||
// Gamut conversion changes the signal's container primaries to BT.2020, but
|
||||
// does not change the mastering display chromaticities described by ST 2086.
|
||||
dst.hdrMetadata = src.hdrMetadata;
|
||||
memcpy(dst.displayPrimary, src.displayPrimary, sizeof(dst.displayPrimary));
|
||||
memcpy(dst.whitePoint , src.whitePoint , sizeof(dst.whitePoint ));
|
||||
dst.maxDisplayLuminance = src.maxDisplayLuminance;
|
||||
dst.minDisplayLuminance = src.minDisplayLuminance;
|
||||
|
||||
Reference in New Issue
Block a user