From 653cecadeb0f3fa264c9c4c8371512c6b4e6b6bf Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Sun, 19 Jul 2026 05:51:59 +1000 Subject: [PATCH] [idd] disable HDR and post-processing when in software mode --- idd/LGIdd/CD3D12Device.cpp | 19 ++- idd/LGIdd/CD3D12Device.h | 4 +- idd/LGIdd/CEdid.cpp | 43 +++--- idd/LGIdd/CEdid.h | 2 +- idd/LGIdd/CIndirectDeviceContext.cpp | 149 ++++++++++++--------- idd/LGIdd/CIndirectDeviceContext.h | 5 +- idd/LGIdd/CIndirectMonitorContext.cpp | 3 +- idd/LGIdd/CPostProcessor.cpp | 40 ++++++ idd/LGIdd/CPostProcessor.h | 2 + idd/LGIdd/CSwapChainProcessor.cpp | 8 +- idd/LGIdd/Device.cpp | 37 ++++- idd/LGIdd/effect/CColorTransformEffect.cpp | 2 +- 12 files changed, 215 insertions(+), 99 deletions(-) diff --git a/idd/LGIdd/CD3D12Device.cpp b/idd/LGIdd/CD3D12Device.cpp index 45b8868a..692ec214 100644 --- a/idd/LGIdd/CD3D12Device.cpp +++ b/idd/LGIdd/CD3D12Device.cpp @@ -60,10 +60,13 @@ static void CALLBACK _D3D12DebugCallback( description); } -CD3D12Device::InitResult CD3D12Device::Init(CIVSHMEM &ivshmem, UINT64 &alignSize) +CD3D12Device::InitResult CD3D12Device::Init(CIVSHMEM &ivshmem, + UINT64 &alignSize, bool enableCompute) { HRESULT hr; + m_computeEnabled = enableCompute; + hr = CreateDXGIFactory2(m_debug ? DXGI_CREATE_FACTORY_DEBUG : 0, IID_PPV_ARGS(&m_factory)); if (FAILED(hr)) { @@ -132,8 +135,9 @@ CD3D12Device::InitResult CD3D12Device::Init(CIVSHMEM &ivshmem, UINT64 &alignSize m_indirectCopy ? CD3D12CommandQueue::NORMAL : CD3D12CommandQueue::FAST)) return InitResult::FAILURE; - if (!m_computeQueue.Init(m_device.Get(), D3D12_COMMAND_LIST_TYPE_COMPUTE, L"Compute", - CD3D12CommandQueue::FAST)) + if (m_computeEnabled && + !m_computeQueue.Init(m_device.Get(), D3D12_COMMAND_LIST_TYPE_COMPUTE, + L"Compute", CD3D12CommandQueue::FAST)) return InitResult::FAILURE; DEBUG_INFO("Created CD3D12Device"); @@ -160,7 +164,8 @@ void CD3D12Device::WaitForIdle() for (int i = 0; i < ARRAYSIZE(m_copyQueue); ++i) drain(m_copyQueue[i]); - drain(m_computeQueue); + if (m_computeEnabled) + drain(m_computeQueue); } bool CD3D12Device::HeapTest() @@ -233,6 +238,12 @@ CD3D12CommandQueue * CD3D12Device::GetCopyQueue() CD3D12CommandQueue * CD3D12Device::GetComputeQueue() { + if (!m_computeEnabled) + { + DEBUG_ERROR("Compute queue requested while compute processing is disabled"); + return nullptr; + } + for (int c = 0; c < 100; ++c) { if (m_computeQueue.IsReady()) diff --git a/idd/LGIdd/CD3D12Device.h b/idd/LGIdd/CD3D12Device.h index 3828c07d..2aa62e3f 100644 --- a/idd/LGIdd/CD3D12Device.h +++ b/idd/LGIdd/CD3D12Device.h @@ -52,6 +52,7 @@ struct CD3D12Device CD3D12CommandQueue m_copyQueue[4]; unsigned m_copyQueueIndex = 0; CD3D12CommandQueue m_computeQueue; + bool m_computeEnabled = false; bool HeapTest(); @@ -66,7 +67,8 @@ struct CD3D12Device SUCCESS }; - InitResult Init(CIVSHMEM &ivshmem, UINT64 &alignSize); + InitResult Init(CIVSHMEM &ivshmem, UINT64 &alignSize, + bool enableCompute); void DeInit(); // Wait for all command queues to finish in-flight GPU work and run their diff --git a/idd/LGIdd/CEdid.cpp b/idd/LGIdd/CEdid.cpp index dcc699b7..5125bfec 100644 --- a/idd/LGIdd/CEdid.cpp +++ b/idd/LGIdd/CEdid.cpp @@ -45,6 +45,7 @@ static const BYTE EDID_MANUFACTURE_YEAR_2026 = 36; // 1990 + 36 = 2026 static const BYTE EDID_VERSION = 1; static const BYTE EDID_REVISION = 4; +static const BYTE EDID_VIDEO_INPUT_DIGITAL_8BPC_HDMI_A = 0xa2; static const BYTE EDID_VIDEO_INPUT_DIGITAL_10BPC_HDMI_A = 0xb2; static const BYTE EDID_HORIZONTAL_SIZE_CM = 52; static const BYTE EDID_VERTICAL_SIZE_CM = 29; @@ -316,17 +317,16 @@ static WORD EdidChromaticity(double value) return (WORD)min(1023.0, max(0.0, value * 1024.0 + 0.5)); } -static void SetChromaticityCoordinates(BYTE coordinates[10]) +static void SetChromaticityCoordinates(BYTE coordinates[10], bool hdr) { - // 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); + // Describe the wire gamut. Accelerated HDR uses the BT.2020 container; + // software rendering is SDR-only and uses the standard sRGB/BT.709 gamut. + const WORD rx = EdidChromaticity(hdr ? 0.7080 : 0.6400); + const WORD ry = EdidChromaticity(hdr ? 0.2920 : 0.3300); + const WORD gx = EdidChromaticity(hdr ? 0.1700 : 0.3000); + const WORD gy = EdidChromaticity(hdr ? 0.7970 : 0.6000); + const WORD bx = EdidChromaticity(hdr ? 0.1310 : 0.1500); + const WORD by = EdidChromaticity(hdr ? 0.0460 : 0.0600); const WORD wx = EdidChromaticity(0.3127); const WORD wy = EdidChromaticity(0.3290); @@ -344,7 +344,7 @@ static void SetChromaticityCoordinates(BYTE coordinates[10]) coordinates[9] = (BYTE)(wy >> 2); } -static void InitEdidBaseBlock(EdidBaseBlock& base) +static void InitEdidBaseBlock(EdidBaseBlock& base, bool hdr) { memcpy(base.header, EDID_HEADER, sizeof(base.header)); @@ -358,12 +358,14 @@ static void InitEdidBaseBlock(EdidBaseBlock& base) base.version = EDID_VERSION; base.revision = EDID_REVISION; - base.videoInputDefinition = EDID_VIDEO_INPUT_DIGITAL_10BPC_HDMI_A; + base.videoInputDefinition = hdr ? + EDID_VIDEO_INPUT_DIGITAL_10BPC_HDMI_A : + EDID_VIDEO_INPUT_DIGITAL_8BPC_HDMI_A; base.horizontalSizeCm = EDID_HORIZONTAL_SIZE_CM; base.verticalSizeCm = EDID_VERTICAL_SIZE_CM; base.displayGamma = EDID_DISPLAY_GAMMA_2_2; base.supportedFeatures = EDID_FEATURES_PREFERRED_TIMING_RGB; - SetChromaticityCoordinates(base.chromaticityCoordinates); + SetChromaticityCoordinates(base.chromaticityCoordinates, hdr); for (UINT i = 0; i < EDID_STANDARD_TIMING_COUNT; ++i) base.standardTimings[i] = MakeUnusedStandardTiming(); @@ -572,12 +574,12 @@ bool CEdid::WriteDetailedTiming(BYTE* dtd, const CSettings::DisplayMode& mode) return true; } -void CEdid::Build(const CSettings::DisplayModes& modes) +void CEdid::Build(const CSettings::DisplayModes& modes, bool hdr) { m_data.assign(static_cast>::size_type>(EDID_BLOCK_SIZE) * 2, 0); EdidBaseBlock baseBlock = {}; - InitEdidBaseBlock(baseBlock); + InitEdidBaseBlock(baseBlock, hdr); CSettings::DisplayModes sorted = modes; std::stable_sort(sorted.begin(), sorted.end(), @@ -625,10 +627,13 @@ void CEdid::Build(const CSettings::DisplayModes& modes) ctaBlock.revision = CTA_REVISION; UINT dataOffset = CTA_HEADER_SIZE; - AppendCtaDataBlock(cta, dataOffset, MakeCtaHdrStaticMetadataDataBlock ()); - AppendCtaDataBlock(cta, dataOffset, MakeCtaColorimetryDataBlock ()); - AppendCtaDataBlock(cta, dataOffset, MakeCtaHdmiForumVendorSpecificDataBlock()); - AppendCtaDataBlock(cta, dataOffset, MakeCtaHdmiVendorSpecificDataBlock ()); + if (hdr) + { + AppendCtaDataBlock(cta, dataOffset, MakeCtaHdrStaticMetadataDataBlock ()); + AppendCtaDataBlock(cta, dataOffset, MakeCtaColorimetryDataBlock ()); + AppendCtaDataBlock(cta, dataOffset, MakeCtaHdmiForumVendorSpecificDataBlock()); + AppendCtaDataBlock(cta, dataOffset, MakeCtaHdmiVendorSpecificDataBlock ()); + } ctaBlock.dtdOffset = (BYTE)dataOffset; ctaBlock.flags = 0x00; diff --git a/idd/LGIdd/CEdid.h b/idd/LGIdd/CEdid.h index fbff5ba2..bb7a4ec9 100644 --- a/idd/LGIdd/CEdid.h +++ b/idd/LGIdd/CEdid.h @@ -29,7 +29,7 @@ class CEdid { public: - void Build(const CSettings::DisplayModes& modes); + void Build(const CSettings::DisplayModes& modes, bool hdr); const BYTE* Data() const { return m_data.empty() ? nullptr : m_data.data(); } UINT Size() const { return (UINT)m_data.size(); } diff --git a/idd/LGIdd/CIndirectDeviceContext.cpp b/idd/LGIdd/CIndirectDeviceContext.cpp index d52da11a..9f4cf41b 100644 --- a/idd/LGIdd/CIndirectDeviceContext.cpp +++ b/idd/LGIdd/CIndirectDeviceContext.cpp @@ -27,6 +27,7 @@ #include "CDebug.h" #include "VersionInfo.h" +#include #include static const struct LGMPQueueConfig FRAME_QUEUE_CONFIG = @@ -67,6 +68,7 @@ void CIndirectDeviceContext::QueryIddCxCapabilities() if (!NT_SUCCESS(status)) { m_iddCxVersion = 0; + m_hasIddCx110DDIs = false; m_canProcessFP16 = false; DEBUG_ERROR_HR(status, "IddCxGetVersion Failed"); return; @@ -89,10 +91,15 @@ void CIndirectDeviceContext::QueryIddCxCapabilities() const bool hasIddCx110DDIs = false; #endif - m_canProcessFP16 = m_iddCxVersion >= IDDCX_VERSION_1_10 && hasIddCx110DDIs; + m_hasIddCx110DDIs = + m_iddCxVersion >= IDDCX_VERSION_1_10 && hasIddCx110DDIs; + m_canProcessFP16 = !m_softwareMode && m_hasIddCx110DDIs; DEBUG_INFO("IddCx version: 0x%04x", m_iddCxVersion); - DEBUG_INFO("IddCx 1.10 HDR/WCG DDIs: %s", m_canProcessFP16 ? "available" : "unavailable"); + DEBUG_INFO("IddCx 1.10 HDR/WCG DDIs: %s", + m_hasIddCx110DDIs ? "available" : "unavailable"); + if (m_softwareMode && m_hasIddCx110DDIs) + DEBUG_INFO("HDR/WCG disabled for software rendering"); } void CIndirectDeviceContext::PopulateDefaultModes() @@ -117,7 +124,7 @@ void CIndirectDeviceContext::InitializeEdid() { AcquireSRWLockExclusive(&m_modeLock); if (!m_edid.Size()) - m_edid.Build(m_displayModes); + m_edid.Build(m_displayModes, CanProcessFP16()); ReleaseSRWLockExclusive(&m_modeLock); } @@ -194,6 +201,68 @@ void CIndirectDeviceContext::InitAdapter() m_ivshmemOpened = true; } + // Select the render adapter before advertising capabilities. If no hardware + // adapter is available, this is a software-rendered display and must remain + // SDR-only; the software path must never depend on compute processing. + bool havePreferredRenderAdapter = false; + LUID preferredRenderAdapter = {}; + IDXGIFactory1 * factory = NULL; + HRESULT factoryStatus = CreateDXGIFactory( + __uuidof(IDXGIFactory1), (void **)&factory); + if (FAILED(factoryStatus)) + DEBUG_ERROR_HR(factoryStatus, "CreateDXGIFactory Failed"); + else + { + for (UINT i = 0;; ++i) + { + IDXGIAdapter1 * dxgiAdapter = nullptr; + HRESULT enumStatus = factory->EnumAdapters1(i, &dxgiAdapter); + if (enumStatus == DXGI_ERROR_NOT_FOUND) + break; + if (FAILED(enumStatus)) + { + DEBUG_ERROR_HR(enumStatus, "Failed to enumerate DXGI adapter %u", i); + break; + } + + DXGI_ADAPTER_DESC1 adapterDesc = {}; + HRESULT descStatus = dxgiAdapter->GetDesc1(&adapterDesc); + dxgiAdapter->Release(); + if (FAILED(descStatus)) + { + DEBUG_ERROR_HR(descStatus, "Failed to query DXGI adapter %u", i); + continue; + } + + if ((adapterDesc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) || + (adapterDesc.VendorId == 0x1414 && adapterDesc.DeviceId == 0x008c)) + { + DEBUG_INFO("Ignoring software render adapter %ls", adapterDesc.Description); + continue; + } + + if ((adapterDesc.VendorId == 0x1b36 && adapterDesc.DeviceId == 0x000d) || // QXL + (adapterDesc.VendorId == 0x1234 && adapterDesc.DeviceId == 0x1111)) // QEMU Standard VGA + { + DEBUG_INFO("Ignoring display-only adapter %ls (vendor 0x%04x, device 0x%04x)", + adapterDesc.Description, adapterDesc.VendorId, adapterDesc.DeviceId); + continue; + } + + DEBUG_INFO("Selected render adapter %ls (vendor 0x%04x, device 0x%04x)", + adapterDesc.Description, adapterDesc.VendorId, adapterDesc.DeviceId); + preferredRenderAdapter = adapterDesc.AdapterLuid; + havePreferredRenderAdapter = true; + break; + } + + factory->Release(); + } + + m_softwareMode = !havePreferredRenderAdapter; + if (m_softwareMode) + DEBUG_INFO("No hardware render adapter available; using SDR software mode"); + QueryIddCxCapabilities(); DEBUG_TRACE("Loading configured display modes"); PopulateDefaultModes(); @@ -218,7 +287,7 @@ void CIndirectDeviceContext::InitAdapter() */ caps.Flags = IDDCX_ADAPTER_FLAGS_USE_SMALLEST_MODE; #ifdef HAS_IDDCX_110 - if (CanUseIddCx110DDIs()) + if (CanProcessFP16()) caps.Flags |= IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16; #endif @@ -250,12 +319,17 @@ void CIndirectDeviceContext::InitAdapter() DEBUG_INFO("Calling IddCxAdapterInitAsync with flags 0x%08x", caps.Flags); NTSTATUS status = IddCxAdapterInitAsync(&init, &initOut); - if (!NT_SUCCESS(status) && CanUseIddCx110DDIs()) + if (!NT_SUCCESS(status) && CanProcessFP16()) { DEBUG_WARN( "IddCxAdapterInitAsync rejected FP16 adapter capabilities (0x%08x), retrying without HDR/WCG", status); m_canProcessFP16 = false; + // The monitor has not been created yet, so replace the provisional HDR + // EDID before Windows can observe it. + AcquireSRWLockExclusive(&m_modeLock); + m_edid.Build(m_displayModes, false); + ReleaseSRWLockExclusive(&m_modeLock); caps.Flags = (IDDCX_ADAPTER_FLAGS)(caps.Flags & ~IDDCX_ADAPTER_FLAGS_CAN_PROCESS_FP16); ZeroMemory(&initOut, sizeof(initOut)); status = IddCxAdapterInitAsync(&init, &initOut); @@ -281,63 +355,12 @@ void CIndirectDeviceContext::InitAdapter() // Try to co-exist with the virtual video device by telling IddCx which // hardware adapter we prefer to render on. - IDXGIFactory * factory = NULL; - HRESULT factoryStatus = CreateDXGIFactory( - __uuidof(IDXGIFactory), (void **)&factory); - if (FAILED(factoryStatus)) - DEBUG_ERROR_HR(factoryStatus, "CreateDXGIFactory Failed"); - else + if (havePreferredRenderAdapter) { - bool renderAdapterSelected = false; - for (UINT i = 0;; ++i) - { - IDXGIAdapter * dxgiAdapter = nullptr; - HRESULT enumStatus = factory->EnumAdapters(i, &dxgiAdapter); - if (enumStatus == DXGI_ERROR_NOT_FOUND) - break; - if (FAILED(enumStatus)) - { - DEBUG_ERROR_HR(enumStatus, "Failed to enumerate DXGI adapter %u", i); - break; - } - - DXGI_ADAPTER_DESC adapterDesc = {}; - HRESULT descStatus = dxgiAdapter->GetDesc(&adapterDesc); - dxgiAdapter->Release(); - if (FAILED(descStatus)) - { - DEBUG_ERROR_HR(descStatus, "Failed to query DXGI adapter %u", i); - continue; - } - - if (adapterDesc.VendorId == 0x1414 && adapterDesc.DeviceId == 0x008c) - { - DEBUG_INFO("Ignoring software render adapter %ls", adapterDesc.Description); - continue; - } - - if ((adapterDesc.VendorId == 0x1b36 && adapterDesc.DeviceId == 0x000d) || // QXL - (adapterDesc.VendorId == 0x1234 && adapterDesc.DeviceId == 0x1111)) // QEMU Standard VGA - { - DEBUG_INFO("Ignoring display-only adapter %ls (vendor 0x%04x, device 0x%04x)", - adapterDesc.Description, adapterDesc.VendorId, adapterDesc.DeviceId); - continue; - } - - DEBUG_INFO("Selecting render adapter %ls (vendor 0x%04x, device 0x%04x)", - adapterDesc.Description, adapterDesc.VendorId, adapterDesc.DeviceId); - IDARG_IN_ADAPTERSETRENDERADAPTER args = {}; - args.PreferredRenderAdapter = adapterDesc.AdapterLuid; - IddCxAdapterSetRenderAdapter(m_adapter, &args); - DEBUG_INFO("Preferred render adapter set"); - renderAdapterSelected = true; - break; - } - - if (!renderAdapterSelected) - DEBUG_INFO("No preferred hardware render adapter was selected"); - - factory->Release(); + IDARG_IN_ADAPTERSETRENDERADAPTER args = {}; + args.PreferredRenderAdapter = preferredRenderAdapter; + IddCxAdapterSetRenderAdapter(m_adapter, &args); + DEBUG_INFO("Preferred render adapter set"); } auto * wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(m_adapter); @@ -684,7 +707,7 @@ NTSTATUS CIndirectDeviceContext::ParseMonitorDescription2( mode->Size = sizeof(IDDCX_MONITOR_MODE2); mode->Origin = IDDCX_MONITOR_MODE_ORIGIN_MONITORDESCRIPTOR; FillSignalInfo(mode->MonitorVideoSignalInfo, it->width, it->height, it->refresh, true); - mode->BitsPerComponent = GetWireBitsPerComponent(CanUseIddCx110DDIs()); + mode->BitsPerComponent = GetWireBitsPerComponent(CanProcessFP16()); if (it->preferred) outArgs->PreferredMonitorModeIdx = @@ -716,7 +739,7 @@ NTSTATUS CIndirectDeviceContext::MonitorQueryTargetModes2( ZeroMemory(mode, sizeof(*mode)); mode->Size = sizeof(IDDCX_TARGET_MODE2); FillSignalInfo(mode->TargetVideoSignalInfo.targetVideoSignalInfo, it->width, it->height, it->refresh, false); - mode->BitsPerComponent = GetWireBitsPerComponent(CanUseIddCx110DDIs()); + mode->BitsPerComponent = GetWireBitsPerComponent(CanProcessFP16()); } return STATUS_SUCCESS; diff --git a/idd/LGIdd/CIndirectDeviceContext.h b/idd/LGIdd/CIndirectDeviceContext.h index 27d15f16..4e352c09 100644 --- a/idd/LGIdd/CIndirectDeviceContext.h +++ b/idd/LGIdd/CIndirectDeviceContext.h @@ -115,7 +115,9 @@ private: DXGI_FORMAT m_format = DXGI_FORMAT_UNKNOWN; FrameType m_frameType = FRAME_TYPE_INVALID; UINT m_iddCxVersion = 0; + bool m_hasIddCx110DDIs = false; bool m_canProcessFP16 = false; + bool m_softwareMode = true; // HDR state from EvtIddCxMonitorSetDefaultHdrMetadata (IddCx 1.10+) // Protected by m_hdrLock - accessed from both the IDD callback thread @@ -147,7 +149,6 @@ private: std::shared_ptr m_colorTransform; void QueryIddCxCapabilities(); - bool CanUseIddCx110DDIs() const { return m_canProcessFP16; } void ScheduleInitRetry(); void StopInitRetry(); @@ -211,7 +212,9 @@ public: size_t GetAlignSize () const { return m_alignSize ; } size_t GetMaxFrameSize() const { return m_maxFrameSize ; } + bool HasIddCx110DDIs() const { return m_hasIddCx110DDIs; } bool CanProcessFP16 () const { return m_canProcessFP16; } + bool IsSoftwareMode () const { return m_softwareMode ; } struct PreparedFrameBuffer { diff --git a/idd/LGIdd/CIndirectMonitorContext.cpp b/idd/LGIdd/CIndirectMonitorContext.cpp index 28637614..7f47a66a 100644 --- a/idd/LGIdd/CIndirectMonitorContext.cpp +++ b/idd/LGIdd/CIndirectMonitorContext.cpp @@ -64,7 +64,8 @@ void CIndirectMonitorContext::AssignSwapChain(IDDCX_SWAPCHAIN swapChain, LUID re UINT64 alignSize = CPlatformInfo::GetPageSize(); dx12Device = std::make_shared(renderAdapter); - CD3D12Device::InitResult r = dx12Device->Init(m_devContext->GetIVSHMEM(), alignSize); + CD3D12Device::InitResult r = dx12Device->Init( + m_devContext->GetIVSHMEM(), alignSize, !dx11Device->IsSoftware()); if (r == CD3D12Device::RETRY) { dx12Device.reset(); diff --git a/idd/LGIdd/CPostProcessor.cpp b/idd/LGIdd/CPostProcessor.cpp index 3e9a71b8..a1fbf9bd 100644 --- a/idd/LGIdd/CPostProcessor.cpp +++ b/idd/LGIdd/CPostProcessor.cpp @@ -30,6 +30,46 @@ #include #include +namespace +{ + bool NearlyEqual(float a, float b, float tolerance) + { + const float delta = a - b; + return delta >= -tolerance && delta <= tolerance; + } +} + +bool IsIdentityColorTransform(const D12ColorTransform& transform) +{ + static const float matrixTolerance = 1.0f / 1048576.0f; + static const float lutTolerance = 1.0f / 65535.0f; + + if (transform.matrixEnabled) + { + for (unsigned row = 0; row < 3; ++row) + for (unsigned column = 0; column < 4; ++column) + { + const float expected = row == column ? 1.0f : 0.0f; + const float effective = + transform.matrix[row][column] * transform.scalar; + if (!NearlyEqual(effective, expected, matrixTolerance)) + return false; + } + } + + if (transform.lutEnabled) + for (unsigned i = 0; i < 4096; ++i) + { + const float expected = (float)i / 4095.0f; + if (!NearlyEqual(transform.lut[i][0], expected, lutTolerance) || + !NearlyEqual(transform.lut[i][1], expected, lutTolerance) || + !NearlyEqual(transform.lut[i][2], expected, lutTolerance)) + return false; + } + + return true; +} + static void CopyHDRMetadata(D12FrameFormat& dst, const D12FrameFormat& src) { dst.hdrMetadata = src.hdrMetadata; diff --git a/idd/LGIdd/CPostProcessor.h b/idd/LGIdd/CPostProcessor.h index ef51aeaa..cba37028 100644 --- a/idd/LGIdd/CPostProcessor.h +++ b/idd/LGIdd/CPostProcessor.h @@ -52,6 +52,8 @@ struct D12ColorTransform float lut[4096][4] = {}; }; +bool IsIdentityColorTransform(const D12ColorTransform& transform); + struct D12FrameFormat { D3D12_RESOURCE_DESC desc = {}; diff --git a/idd/LGIdd/CSwapChainProcessor.cpp b/idd/LGIdd/CSwapChainProcessor.cpp index d648386b..a04cad09 100644 --- a/idd/LGIdd/CSwapChainProcessor.cpp +++ b/idd/LGIdd/CSwapChainProcessor.cpp @@ -40,7 +40,9 @@ CSwapChainProcessor::CSwapChainProcessor(CIndirectMonitorContext * monitorContex { m_resPool.Init(dx11Device, dx12Device); m_fbPool.Init(this); - if (!m_postProcessor.Init(dx12Device)) + if (m_dx11Device->IsSoftware()) + DEBUG_INFO("Software render adapter: post-processing disabled"); + else if (!m_postProcessor.Init(dx12Device)) DEBUG_ERROR("Failed to initialize post processor"); // Manual-reset: both worker threads wait on this, so it must stay signalled @@ -189,7 +191,7 @@ bool CSwapChainProcessor::SwapChainThreadCore() UINT sdrWhiteLevel = KVMFR_SDR_WHITE_LEVEL_DEFAULT; #ifdef HAS_IDDCX_110 - if (m_devContext->CanProcessFP16()) + if (m_devContext->HasIddCx110DDIs()) { IDARG_IN_RELEASEANDACQUIREBUFFER2 acquireIn = {}; acquireIn.Size = sizeof(acquireIn); @@ -780,7 +782,7 @@ bool CSwapChainProcessor::QueryHWCursor() UINT cursorWhiteLevel = m_sdrWhiteLevel.load(std::memory_order_relaxed); NTSTATUS status; #ifdef HAS_IDDCX_110 - if (m_devContext->CanProcessFP16()) + if (m_devContext->HasIddCx110DDIs()) { IDARG_OUT_QUERY_HWCURSOR3 out3 = {}; status = IddCxMonitorQueryHardwareCursor3(m_monitor, &in, &out3); diff --git a/idd/LGIdd/Device.cpp b/idd/LGIdd/Device.cpp index 765cc874..b26b6fb9 100644 --- a/idd/LGIdd/Device.cpp +++ b/idd/LGIdd/Device.cpp @@ -160,13 +160,20 @@ NTSTATUS LGIddParseMonitorDescription2(const IDARG_IN_PARSEMONITORDESCRIPTION2* NTSTATUS LGIddAdapterQueryTargetInfo(IDDCX_ADAPTER adapter, IDARG_IN_QUERYTARGET_INFO* inArgs, IDARG_OUT_QUERYTARGET_INFO* outArgs) { - UNREFERENCED_PARAMETER(adapter); UNREFERENCED_PARAMETER(inArgs); - outArgs->TargetCaps = - (IDDCX_TARGET_CAPS)(IDDCX_TARGET_CAPS_WIDE_COLOR_SPACE | IDDCX_TARGET_CAPS_HIGH_COLOR_SPACE); - outArgs->DitheringSupport.Rgb = - (IDDCX_BITS_PER_COMPONENT)(IDDCX_BITS_PER_COMPONENT_8 | IDDCX_BITS_PER_COMPONENT_10); + auto* wrapper = WdfObjectGet_CIndirectDeviceContextWrapper(adapter); + const bool hdr = wrapper && wrapper->context && + wrapper->context->CanProcessFP16(); + + outArgs->TargetCaps = hdr ? + (IDDCX_TARGET_CAPS)(IDDCX_TARGET_CAPS_WIDE_COLOR_SPACE | + IDDCX_TARGET_CAPS_HIGH_COLOR_SPACE) : + (IDDCX_TARGET_CAPS)0; + outArgs->DitheringSupport.Rgb = hdr ? + (IDDCX_BITS_PER_COMPONENT)(IDDCX_BITS_PER_COMPONENT_8 | + IDDCX_BITS_PER_COMPONENT_10) : + IDDCX_BITS_PER_COMPONENT_8; outArgs->DitheringSupport.YCbCr444 = IDDCX_BITS_PER_COMPONENT_NONE; outArgs->DitheringSupport.YCbCr422 = IDDCX_BITS_PER_COMPONENT_NONE; outArgs->DitheringSupport.YCbCr420 = IDDCX_BITS_PER_COMPONENT_NONE; @@ -187,6 +194,12 @@ NTSTATUS LGIddMonitorSetDefaultHdrMetadata(IDDCX_MONITOR monitor, auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor); auto* ctx = wrapper->context->GetDeviceContext(); + if (ctx->IsSoftwareMode()) + { + ctx->SetHDRActive(nullptr); + return STATUS_SUCCESS; + } + ctx->SetHDRActive(inArgs->Data.pHdr10); return STATUS_SUCCESS; @@ -197,6 +210,13 @@ NTSTATUS LGIddMonitorSetGammaRamp(IDDCX_MONITOR monitor, const IDARG_IN_SET_GAMM auto* wrapper = WdfObjectGet_CIndirectMonitorContextWrapper(monitor); auto* ctx = wrapper->context->GetDeviceContext(); + if (ctx->IsSoftwareMode()) + { + ctx->SetColorTransform(nullptr); + DEBUG_INFO("Ignoring display color transform in software mode"); + return STATUS_SUCCESS; + } + if (inArgs->Type == IDDCX_GAMMARAMP_TYPE_DEFAULT) { ctx->SetColorTransform(nullptr); @@ -236,6 +256,13 @@ NTSTATUS LGIddMonitorSetGammaRamp(IDDCX_MONITOR monitor, const IDARG_IN_SET_GAMM transform->lut[i][3] = 1.0f; } + if (IsIdentityColorTransform(*transform)) + { + ctx->SetColorTransform(nullptr); + DEBUG_INFO("Ignoring identity display color transform"); + return STATUS_SUCCESS; + } + ctx->SetColorTransform(std::move(transform)); DEBUG_INFO("Display color transform updated (matrix:%d lut:%d)", input->MatrixEnabled, input->LutEnabled); diff --git a/idd/LGIdd/effect/CColorTransformEffect.cpp b/idd/LGIdd/effect/CColorTransformEffect.cpp index be8fbbef..e90cf204 100644 --- a/idd/LGIdd/effect/CColorTransformEffect.cpp +++ b/idd/LGIdd/effect/CColorTransformEffect.cpp @@ -208,7 +208,7 @@ PostProcessStatus CColorTransformEffect::SetFormat( const ComPtr& device, const D12FrameFormat& src, D12FrameFormat& dst) { - if (!src.colorTransform || + if (!src.colorTransform || IsIdentityColorTransform(*src.colorTransform) || (!src.colorTransform->matrixEnabled && !src.colorTransform->lutEnabled)) return PostProcessStatus::BYPASS_EFFECT;