mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-07-20 22:32:01 +00:00
[idd] hdr: complete HDR display and cursor metadata
Publish BT.2020 chromaticities in the virtual monitor EDID so Windows receives a valid HDR colour volume. Extend cursor messages with their IddCx SDR white level and an explicit visibility-valid flag. Consumers can then calibrate cursors without treating colour-transform-only messages as cursor hides.
This commit is contained in:
@@ -28,7 +28,7 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define KVMFR_MAGIC "KVMFR---"
|
#define KVMFR_MAGIC "KVMFR---"
|
||||||
#define KVMFR_VERSION 22
|
#define KVMFR_VERSION 23
|
||||||
|
|
||||||
// Fallback used by producers that cannot report the source display's SDR
|
// Fallback used by producers that cannot report the source display's SDR
|
||||||
// white level. IDD frames override this with IDDCX_METADATA2::SdrWhiteLevel.
|
// white level. IDD frames override this with IDDCX_METADATA2::SdrWhiteLevel.
|
||||||
@@ -54,7 +54,10 @@ enum
|
|||||||
CURSOR_FLAG_POSITION = 0x1,
|
CURSOR_FLAG_POSITION = 0x1,
|
||||||
CURSOR_FLAG_VISIBLE = 0x2,
|
CURSOR_FLAG_VISIBLE = 0x2,
|
||||||
CURSOR_FLAG_SHAPE = 0x4,
|
CURSOR_FLAG_SHAPE = 0x4,
|
||||||
CURSOR_FLAG_COLOR_TRANSFORM = 0x8
|
CURSOR_FLAG_COLOR_TRANSFORM = 0x8,
|
||||||
|
// CURSOR_FLAG_VISIBLE contains a new visibility state. This distinguishes
|
||||||
|
// an invisible cursor update from messages carrying unrelated cursor data.
|
||||||
|
CURSOR_FLAG_VISIBLE_VALID = 0x10
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef uint32_t KVMFRCursorFlags;
|
typedef uint32_t KVMFRCursorFlags;
|
||||||
@@ -135,6 +138,7 @@ typedef struct KVMFRCursor
|
|||||||
uint32_t width; // width of the shape
|
uint32_t width; // width of the shape
|
||||||
uint32_t height; // height of the shape
|
uint32_t height; // height of the shape
|
||||||
uint32_t pitch; // row length in bytes of the shape
|
uint32_t pitch; // row length in bytes of the shape
|
||||||
|
uint32_t sdrWhiteLevel; // cursor white level in nits for HDR composition
|
||||||
}
|
}
|
||||||
KVMFRCursor;
|
KVMFRCursor;
|
||||||
|
|
||||||
|
|||||||
@@ -515,8 +515,9 @@ static void sendPointer(bool newClient)
|
|||||||
KVMFRCursor *cursor = lgmpHostMemPtr(mem);
|
KVMFRCursor *cursor = lgmpHostMemPtr(mem);
|
||||||
cursor->x = app.pointerInfo.x;
|
cursor->x = app.pointerInfo.x;
|
||||||
cursor->y = app.pointerInfo.y;
|
cursor->y = app.pointerInfo.y;
|
||||||
|
cursor->sdrWhiteLevel = KVMFR_SDR_WHITE_LEVEL_DEFAULT;
|
||||||
|
|
||||||
const uint32_t flags = CURSOR_FLAG_POSITION |
|
const uint32_t flags = CURSOR_FLAG_POSITION | CURSOR_FLAG_VISIBLE_VALID |
|
||||||
(app.pointerShapeValid ? CURSOR_FLAG_SHAPE : 0) |
|
(app.pointerShapeValid ? CURSOR_FLAG_SHAPE : 0) |
|
||||||
(app.pointerInfo.visible ? CURSOR_FLAG_VISIBLE : 0);
|
(app.pointerInfo.visible ? CURSOR_FLAG_VISIBLE : 0);
|
||||||
|
|
||||||
@@ -524,7 +525,7 @@ static void sendPointer(bool newClient)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t flags = 0;
|
uint32_t flags = CURSOR_FLAG_VISIBLE_VALID;
|
||||||
PLGMPMemory mem;
|
PLGMPMemory mem;
|
||||||
if (app.pointerInfo.shapeUpdate)
|
if (app.pointerInfo.shapeUpdate)
|
||||||
{
|
{
|
||||||
@@ -539,6 +540,7 @@ static void sendPointer(bool newClient)
|
|||||||
app.pointerIndex = 0;
|
app.pointerIndex = 0;
|
||||||
}
|
}
|
||||||
KVMFRCursor *cursor = lgmpHostMemPtr(mem);
|
KVMFRCursor *cursor = lgmpHostMemPtr(mem);
|
||||||
|
cursor->sdrWhiteLevel = KVMFR_SDR_WHITE_LEVEL_DEFAULT;
|
||||||
|
|
||||||
if (app.pointerInfo.positionUpdate || newClient)
|
if (app.pointerInfo.positionUpdate || newClient)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -309,6 +309,41 @@ static EdidStandardTiming MakeUnusedStandardTiming()
|
|||||||
return timing;
|
return timing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <algorithm> // Ensure this header is included for std::min and std::max
|
||||||
|
|
||||||
|
static WORD EdidChromaticity(double value)
|
||||||
|
{
|
||||||
|
return (WORD)min(1023.0, max(0.0, value * 1024.0 + 0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetChromaticityCoordinates(BYTE coordinates[10])
|
||||||
|
{
|
||||||
|
// The virtual display transports HDR in the BT.2020 container. Describe
|
||||||
|
// that full container gamut and its D65 white point rather than leaving an
|
||||||
|
// invalid all-zero base-block colour volume.
|
||||||
|
const WORD rx = EdidChromaticity(0.7080);
|
||||||
|
const WORD ry = EdidChromaticity(0.2920);
|
||||||
|
const WORD gx = EdidChromaticity(0.1700);
|
||||||
|
const WORD gy = EdidChromaticity(0.7970);
|
||||||
|
const WORD bx = EdidChromaticity(0.1310);
|
||||||
|
const WORD by = EdidChromaticity(0.0460);
|
||||||
|
const WORD wx = EdidChromaticity(0.3127);
|
||||||
|
const WORD wy = EdidChromaticity(0.3290);
|
||||||
|
|
||||||
|
coordinates[0] = (BYTE)(((rx & 3) << 6) | ((ry & 3) << 4) |
|
||||||
|
((gx & 3) << 2) | (gy & 3));
|
||||||
|
coordinates[1] = (BYTE)(((bx & 3) << 6) | ((by & 3) << 4) |
|
||||||
|
((wx & 3) << 2) | (wy & 3));
|
||||||
|
coordinates[2] = (BYTE)(rx >> 2);
|
||||||
|
coordinates[3] = (BYTE)(ry >> 2);
|
||||||
|
coordinates[4] = (BYTE)(gx >> 2);
|
||||||
|
coordinates[5] = (BYTE)(gy >> 2);
|
||||||
|
coordinates[6] = (BYTE)(bx >> 2);
|
||||||
|
coordinates[7] = (BYTE)(by >> 2);
|
||||||
|
coordinates[8] = (BYTE)(wx >> 2);
|
||||||
|
coordinates[9] = (BYTE)(wy >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
static void InitEdidBaseBlock(EdidBaseBlock& base)
|
static void InitEdidBaseBlock(EdidBaseBlock& base)
|
||||||
{
|
{
|
||||||
memcpy(base.header, EDID_HEADER, sizeof(base.header));
|
memcpy(base.header, EDID_HEADER, sizeof(base.header));
|
||||||
@@ -328,6 +363,7 @@ static void InitEdidBaseBlock(EdidBaseBlock& base)
|
|||||||
base.verticalSizeCm = EDID_VERTICAL_SIZE_CM;
|
base.verticalSizeCm = EDID_VERTICAL_SIZE_CM;
|
||||||
base.displayGamma = EDID_DISPLAY_GAMMA_2_2;
|
base.displayGamma = EDID_DISPLAY_GAMMA_2_2;
|
||||||
base.supportedFeatures = EDID_FEATURES_PREFERRED_TIMING_RGB;
|
base.supportedFeatures = EDID_FEATURES_PREFERRED_TIMING_RGB;
|
||||||
|
SetChromaticityCoordinates(base.chromaticityCoordinates);
|
||||||
|
|
||||||
for (UINT i = 0; i < EDID_STANDARD_TIMING_COUNT; ++i)
|
for (UINT i = 0; i < EDID_STANDARD_TIMING_COUNT; ++i)
|
||||||
base.standardTimings[i] = MakeUnusedStandardTiming();
|
base.standardTimings[i] = MakeUnusedStandardTiming();
|
||||||
@@ -612,4 +648,4 @@ void CEdid::Build(const CSettings::DisplayModes& modes)
|
|||||||
|
|
||||||
SetChecksum(cta);
|
SetChecksum(cta);
|
||||||
memcpy(m_data.data() + sizeof(baseBlock), &ctaBlock, sizeof(ctaBlock));
|
memcpy(m_data.data() + sizeof(baseBlock), &ctaBlock, sizeof(ctaBlock));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1222,7 +1222,8 @@ void CIndirectDeviceContext::FinalizeFrameBuffer(unsigned frameIndex) const
|
|||||||
fb->wp = m_height * m_pitch;
|
fb->wp = m_height * m_pitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CIndirectDeviceContext::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, const BYTE * data)
|
void CIndirectDeviceContext::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info,
|
||||||
|
const BYTE * data, UINT sdrWhiteLevel)
|
||||||
{
|
{
|
||||||
PLGMPMemory mem;
|
PLGMPMemory mem;
|
||||||
if (info.CursorShapeInfo.CursorType == IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
|
if (info.CursorShapeInfo.CursorType == IDDCX_CURSOR_SHAPE_TYPE_UNINITIALIZED)
|
||||||
@@ -1239,9 +1240,11 @@ void CIndirectDeviceContext::SendCursor(const IDARG_OUT_QUERY_HWCURSOR& info, co
|
|||||||
}
|
}
|
||||||
|
|
||||||
KVMFRCursor * cursor = (KVMFRCursor *)lgmpHostMemPtr(mem);
|
KVMFRCursor * cursor = (KVMFRCursor *)lgmpHostMemPtr(mem);
|
||||||
|
cursor->sdrWhiteLevel = sdrWhiteLevel ?
|
||||||
|
sdrWhiteLevel : KVMFR_SDR_WHITE_LEVEL_DEFAULT;
|
||||||
|
|
||||||
m_cursorVisible = info.IsCursorVisible;
|
m_cursorVisible = info.IsCursorVisible;
|
||||||
uint32_t flags = 0;
|
uint32_t flags = CURSOR_FLAG_VISIBLE_VALID;
|
||||||
|
|
||||||
if (info.IsCursorVisible)
|
if (info.IsCursorVisible)
|
||||||
{
|
{
|
||||||
@@ -1416,7 +1419,7 @@ void CIndirectDeviceContext::ResendCursor()
|
|||||||
cursor->y = (int16_t)m_cursorY;
|
cursor->y = (int16_t)m_cursorY;
|
||||||
|
|
||||||
const uint32_t flags =
|
const uint32_t flags =
|
||||||
CURSOR_FLAG_POSITION | CURSOR_FLAG_SHAPE |
|
CURSOR_FLAG_POSITION | CURSOR_FLAG_SHAPE | CURSOR_FLAG_VISIBLE_VALID |
|
||||||
(m_cursorVisible ? CURSOR_FLAG_VISIBLE : 0);
|
(m_cursorVisible ? CURSOR_FLAG_VISIBLE : 0);
|
||||||
|
|
||||||
LGMP_STATUS status;
|
LGMP_STATUS status;
|
||||||
|
|||||||
@@ -225,7 +225,8 @@ public:
|
|||||||
void WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) const;
|
void WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) const;
|
||||||
void FinalizeFrameBuffer(unsigned frameIndex) const;
|
void FinalizeFrameBuffer(unsigned frameIndex) const;
|
||||||
|
|
||||||
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR & info, const BYTE * data);
|
void SendCursor(const IDARG_OUT_QUERY_HWCURSOR & info, const BYTE * data,
|
||||||
|
UINT sdrWhiteLevel);
|
||||||
|
|
||||||
// Tracks HDR state from EvtIddCxMonitorSetDefaultHdrMetadata
|
// Tracks HDR state from EvtIddCxMonitorSetDefaultHdrMetadata
|
||||||
void SetHDRActive(const struct IDDCX_HDR10_METADATA * hdrMeta);
|
void SetHDRActive(const struct IDDCX_HDR10_METADATA * hdrMeta);
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ bool CSwapChainProcessor::SwapChainThreadCore()
|
|||||||
surface = buffer.MetaData.pSurface;
|
surface = buffer.MetaData.pSurface;
|
||||||
colorSpace = buffer.MetaData.SurfaceColorSpace;
|
colorSpace = buffer.MetaData.SurfaceColorSpace;
|
||||||
sdrWhiteLevel = buffer.MetaData.SdrWhiteLevel;
|
sdrWhiteLevel = buffer.MetaData.SdrWhiteLevel;
|
||||||
|
m_sdrWhiteLevel.store(sdrWhiteLevel, std::memory_order_relaxed);
|
||||||
UpdateHDRMetadata(buffer.MetaData);
|
UpdateHDRMetadata(buffer.MetaData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -776,6 +777,7 @@ bool CSwapChainProcessor::QueryHWCursor()
|
|||||||
in.ShapeBufferSizeInBytes = 512 * 512 * 4;
|
in.ShapeBufferSizeInBytes = 512 * 512 * 4;
|
||||||
|
|
||||||
IDARG_OUT_QUERY_HWCURSOR out = {};
|
IDARG_OUT_QUERY_HWCURSOR out = {};
|
||||||
|
UINT cursorWhiteLevel = m_sdrWhiteLevel.load(std::memory_order_relaxed);
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
#ifdef HAS_IDDCX_110
|
#ifdef HAS_IDDCX_110
|
||||||
if (m_devContext->CanProcessFP16())
|
if (m_devContext->CanProcessFP16())
|
||||||
@@ -787,6 +789,8 @@ bool CSwapChainProcessor::QueryHWCursor()
|
|||||||
out.Y = out3.Y;
|
out.Y = out3.Y;
|
||||||
out.IsCursorShapeUpdated = out3.IsCursorShapeUpdated;
|
out.IsCursorShapeUpdated = out3.IsCursorShapeUpdated;
|
||||||
out.CursorShapeInfo = out3.CursorShapeInfo;
|
out.CursorShapeInfo = out3.CursorShapeInfo;
|
||||||
|
if (out3.SdrWhiteLevel)
|
||||||
|
cursorWhiteLevel = out3.SdrWhiteLevel;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
@@ -810,7 +814,7 @@ bool CSwapChainProcessor::QueryHWCursor()
|
|||||||
if (out.IsCursorShapeUpdated)
|
if (out.IsCursorShapeUpdated)
|
||||||
m_lastShapeId = out.CursorShapeInfo.ShapeId;
|
m_lastShapeId = out.CursorShapeInfo.ShapeId;
|
||||||
|
|
||||||
m_devContext->SendCursor(out, m_shapeBuffer);
|
m_devContext->SendCursor(out, m_shapeBuffer, cursorWhiteLevel);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <wrl.h>
|
#include <wrl.h>
|
||||||
#include <IddCx.h>
|
#include <IddCx.h>
|
||||||
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
using namespace Microsoft::WRL;
|
using namespace Microsoft::WRL;
|
||||||
@@ -60,6 +61,7 @@ private:
|
|||||||
Wrappers::Event m_cursorDataEvent;
|
Wrappers::Event m_cursorDataEvent;
|
||||||
BYTE* m_shapeBuffer;
|
BYTE* m_shapeBuffer;
|
||||||
DWORD m_lastShapeId = 0;
|
DWORD m_lastShapeId = 0;
|
||||||
|
std::atomic<UINT> m_sdrWhiteLevel { KVMFR_SDR_WHITE_LEVEL_DEFAULT };
|
||||||
|
|
||||||
// Output-space damage from the previous published frame. The shared-memory
|
// Output-space damage from the previous published frame. The shared-memory
|
||||||
// frame buffers alternate, so this must be copied along with the current
|
// frame buffers alternate, so this must be copied along with the current
|
||||||
|
|||||||
Reference in New Issue
Block a user