mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-07-20 14:22:00 +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"
|
||||
|
||||
#define KVMFR_MAGIC "KVMFR---"
|
||||
#define KVMFR_VERSION 22
|
||||
#define KVMFR_VERSION 23
|
||||
|
||||
// Fallback used by producers that cannot report the source display's SDR
|
||||
// white level. IDD frames override this with IDDCX_METADATA2::SdrWhiteLevel.
|
||||
@@ -54,7 +54,10 @@ enum
|
||||
CURSOR_FLAG_POSITION = 0x1,
|
||||
CURSOR_FLAG_VISIBLE = 0x2,
|
||||
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;
|
||||
@@ -135,6 +138,7 @@ typedef struct KVMFRCursor
|
||||
uint32_t width; // width of the shape
|
||||
uint32_t height; // height 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;
|
||||
|
||||
|
||||
@@ -515,8 +515,9 @@ static void sendPointer(bool newClient)
|
||||
KVMFRCursor *cursor = lgmpHostMemPtr(mem);
|
||||
cursor->x = app.pointerInfo.x;
|
||||
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.pointerInfo.visible ? CURSOR_FLAG_VISIBLE : 0);
|
||||
|
||||
@@ -524,7 +525,7 @@ static void sendPointer(bool newClient)
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t flags = 0;
|
||||
uint32_t flags = CURSOR_FLAG_VISIBLE_VALID;
|
||||
PLGMPMemory mem;
|
||||
if (app.pointerInfo.shapeUpdate)
|
||||
{
|
||||
@@ -539,6 +540,7 @@ static void sendPointer(bool newClient)
|
||||
app.pointerIndex = 0;
|
||||
}
|
||||
KVMFRCursor *cursor = lgmpHostMemPtr(mem);
|
||||
cursor->sdrWhiteLevel = KVMFR_SDR_WHITE_LEVEL_DEFAULT;
|
||||
|
||||
if (app.pointerInfo.positionUpdate || newClient)
|
||||
{
|
||||
|
||||
@@ -309,6 +309,41 @@ static EdidStandardTiming MakeUnusedStandardTiming()
|
||||
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)
|
||||
{
|
||||
memcpy(base.header, EDID_HEADER, sizeof(base.header));
|
||||
@@ -328,6 +363,7 @@ static void InitEdidBaseBlock(EdidBaseBlock& base)
|
||||
base.verticalSizeCm = EDID_VERTICAL_SIZE_CM;
|
||||
base.displayGamma = EDID_DISPLAY_GAMMA_2_2;
|
||||
base.supportedFeatures = EDID_FEATURES_PREFERRED_TIMING_RGB;
|
||||
SetChromaticityCoordinates(base.chromaticityCoordinates);
|
||||
|
||||
for (UINT i = 0; i < EDID_STANDARD_TIMING_COUNT; ++i)
|
||||
base.standardTimings[i] = MakeUnusedStandardTiming();
|
||||
@@ -612,4 +648,4 @@ void CEdid::Build(const CSettings::DisplayModes& modes)
|
||||
|
||||
SetChecksum(cta);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
cursor->sdrWhiteLevel = sdrWhiteLevel ?
|
||||
sdrWhiteLevel : KVMFR_SDR_WHITE_LEVEL_DEFAULT;
|
||||
|
||||
m_cursorVisible = info.IsCursorVisible;
|
||||
uint32_t flags = 0;
|
||||
uint32_t flags = CURSOR_FLAG_VISIBLE_VALID;
|
||||
|
||||
if (info.IsCursorVisible)
|
||||
{
|
||||
@@ -1416,7 +1419,7 @@ void CIndirectDeviceContext::ResendCursor()
|
||||
cursor->y = (int16_t)m_cursorY;
|
||||
|
||||
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);
|
||||
|
||||
LGMP_STATUS status;
|
||||
|
||||
@@ -225,7 +225,8 @@ public:
|
||||
void WriteFrameBuffer(unsigned frameIndex, void* src, size_t offset, size_t len, bool setWritePos) 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
|
||||
void SetHDRActive(const struct IDDCX_HDR10_METADATA * hdrMeta);
|
||||
|
||||
@@ -206,6 +206,7 @@ bool CSwapChainProcessor::SwapChainThreadCore()
|
||||
surface = buffer.MetaData.pSurface;
|
||||
colorSpace = buffer.MetaData.SurfaceColorSpace;
|
||||
sdrWhiteLevel = buffer.MetaData.SdrWhiteLevel;
|
||||
m_sdrWhiteLevel.store(sdrWhiteLevel, std::memory_order_relaxed);
|
||||
UpdateHDRMetadata(buffer.MetaData);
|
||||
}
|
||||
}
|
||||
@@ -776,6 +777,7 @@ bool CSwapChainProcessor::QueryHWCursor()
|
||||
in.ShapeBufferSizeInBytes = 512 * 512 * 4;
|
||||
|
||||
IDARG_OUT_QUERY_HWCURSOR out = {};
|
||||
UINT cursorWhiteLevel = m_sdrWhiteLevel.load(std::memory_order_relaxed);
|
||||
NTSTATUS status;
|
||||
#ifdef HAS_IDDCX_110
|
||||
if (m_devContext->CanProcessFP16())
|
||||
@@ -787,6 +789,8 @@ bool CSwapChainProcessor::QueryHWCursor()
|
||||
out.Y = out3.Y;
|
||||
out.IsCursorShapeUpdated = out3.IsCursorShapeUpdated;
|
||||
out.CursorShapeInfo = out3.CursorShapeInfo;
|
||||
if (out3.SdrWhiteLevel)
|
||||
cursorWhiteLevel = out3.SdrWhiteLevel;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@@ -810,7 +814,7 @@ bool CSwapChainProcessor::QueryHWCursor()
|
||||
if (out.IsCursorShapeUpdated)
|
||||
m_lastShapeId = out.CursorShapeInfo.ShapeId;
|
||||
|
||||
m_devContext->SendCursor(out, m_shapeBuffer);
|
||||
m_devContext->SendCursor(out, m_shapeBuffer, cursorWhiteLevel);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <Windows.h>
|
||||
#include <wrl.h>
|
||||
#include <IddCx.h>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
@@ -60,6 +61,7 @@ private:
|
||||
Wrappers::Event m_cursorDataEvent;
|
||||
BYTE* m_shapeBuffer;
|
||||
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
|
||||
// frame buffers alternate, so this must be copied along with the current
|
||||
|
||||
Reference in New Issue
Block a user