From ad7ac6540fe4af1637fd6aae2fbee2dbc6c512cc Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Wed, 28 Feb 2024 11:59:58 +1100 Subject: [PATCH] [host] d12: implement hdr16 to hdr10 conversion --- common/include/common/display.h | 32 ++ common/src/platform/windows/CMakeLists.txt | 1 + common/src/platform/windows/display.c | 120 +++++ .../Windows/capture/D12/CMakeLists.txt | 1 + host/platform/Windows/capture/D12/d12.c | 60 ++- host/platform/Windows/capture/D12/effect.h | 9 +- .../Windows/capture/D12/effect/hdr16to10.c | 454 ++++++++++++++++++ .../Windows/capture/D12/effect/rgb24.c | 3 +- .../capture/DXGI/src/pp/sdrwhitelevel.c | 5 +- host/platform/Windows/capture/DXGI/src/util.c | 98 ---- host/platform/Windows/capture/DXGI/src/util.h | 4 - 11 files changed, 670 insertions(+), 117 deletions(-) create mode 100644 common/include/common/display.h create mode 100644 common/src/platform/windows/display.c create mode 100644 host/platform/Windows/capture/D12/effect/hdr16to10.c diff --git a/common/include/common/display.h b/common/include/common/display.h new file mode 100644 index 00000000..bfef143b --- /dev/null +++ b/common/include/common/display.h @@ -0,0 +1,32 @@ +/** + * Looking Glass + * Copyright © 2017-2024 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _H_COMMON_DISPLAY_ +#define _H_COMMON_DISPLAY_ + +#if defined(_WIN32) +#include +#include + +bool display_getPathInfo(HMONITOR monitor, DISPLAYCONFIG_PATH_INFO * info); +float display_getSDRWhiteLevel(const DISPLAYCONFIG_PATH_INFO * displayPathInfo); +#endif + +#endif diff --git a/common/src/platform/windows/CMakeLists.txt b/common/src/platform/windows/CMakeLists.txt index 0e303a80..e237f2e5 100644 --- a/common/src/platform/windows/CMakeLists.txt +++ b/common/src/platform/windows/CMakeLists.txt @@ -39,6 +39,7 @@ add_library(lg_common_platform_code STATIC ivshmem.c time.c cpuinfo.c + display.c ) target_link_libraries(lg_common_platform_code diff --git a/common/src/platform/windows/display.c b/common/src/platform/windows/display.c new file mode 100644 index 00000000..2a38a7e4 --- /dev/null +++ b/common/src/platform/windows/display.c @@ -0,0 +1,120 @@ +/** + * Looking Glass + * Copyright © 2017-2024 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common/display.h" +#include "common/debug.h" + +bool display_getPathInfo(HMONITOR monitor, DISPLAYCONFIG_PATH_INFO * info) +{ + bool result = false; + UINT32 numPath, numMode; + + MONITORINFOEXW viewInfo = { .cbSize = sizeof(viewInfo) }; + if (!GetMonitorInfoW(monitor, (MONITORINFO*)&viewInfo)) + { + DEBUG_ERROR("Failed to get the monitor info"); + goto err; + } + +err_retry: + if (FAILED(GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPath, &numMode))) + goto err; + + DISPLAYCONFIG_PATH_INFO * pathInfo = calloc(sizeof(*pathInfo), numPath); + if (!pathInfo) + goto err_mem_pathInfo; + + DISPLAYCONFIG_MODE_INFO * modeInfo = calloc(sizeof(*modeInfo), numMode); + if (!modeInfo) + goto err_mem_modeInfo; + + LONG status = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, + &numPath, pathInfo, + &numMode, modeInfo, + NULL); + + if (status != ERROR_SUCCESS) + { + if (status == ERROR_INSUFFICIENT_BUFFER) + { + free(modeInfo); + free(pathInfo); + goto err_retry; + } + + DEBUG_ERROR("QueryDisplayConfig failed with 0x%lx", status); + goto err_queryDisplay; + } + + for(unsigned i = 0; i < numPath; ++i) + { + DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = + { + .header = + { + .type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME, + .size = sizeof(sourceName), + .adapterId = pathInfo[i].sourceInfo.adapterId, + .id = pathInfo[i].sourceInfo.id, + } + }; + + if (FAILED(DisplayConfigGetDeviceInfo(&sourceName.header))) + continue; + + if (wcscmp(viewInfo.szDevice, sourceName.viewGdiDeviceName) != 0) + continue; + + *info = pathInfo[i]; + result = true; + break; + } + +err_queryDisplay: + free(modeInfo); + +err_mem_modeInfo: + free(pathInfo); + +err_mem_pathInfo: + +err: + return result; +} + +float display_getSDRWhiteLevel(const DISPLAYCONFIG_PATH_INFO * displayPathInfo) +{ + float nits = 80.0f; + DISPLAYCONFIG_SDR_WHITE_LEVEL level = + { + .header = + { + .type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL, + .size = sizeof(level), + .adapterId = displayPathInfo->targetInfo.adapterId, + .id = displayPathInfo->targetInfo.id, + } + }; + + if (SUCCEEDED(DisplayConfigGetDeviceInfo(&level.header))) + nits = level.SDRWhiteLevel / 1000.0f * 80.0f; + + return nits; +} diff --git a/host/platform/Windows/capture/D12/CMakeLists.txt b/host/platform/Windows/capture/D12/CMakeLists.txt index 3d1e820c..bfd8587d 100644 --- a/host/platform/Windows/capture/D12/CMakeLists.txt +++ b/host/platform/Windows/capture/D12/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(capture_D12 STATIC command_group.c backend/dd.c effect/rgb24.c + effect/hdr16to10.c ) target_link_libraries(capture_D12 diff --git a/host/platform/Windows/capture/D12/d12.c b/host/platform/Windows/capture/D12/d12.c index a0b5bcff..76ef9c9f 100644 --- a/host/platform/Windows/capture/D12/d12.c +++ b/host/platform/Windows/capture/D12/d12.c @@ -28,6 +28,7 @@ #include "common/option.h" #include "common/rects.h" #include "common/vector.h" +#include "common/display.h" #include "com_ref.h" #include "backend.h" @@ -36,6 +37,7 @@ #include #include +#include #include // definitions @@ -46,6 +48,8 @@ struct D12Interface IDXGIFactory2 ** factory; ID3D12Device3 ** device; + DISPLAYCONFIG_PATH_INFO displayPathInfo; + ID3D12CommandQueue ** copyQueue; ID3D12CommandQueue ** computeQueue; D12CommandGroup copyCommand; @@ -76,6 +80,7 @@ struct D12Interface bool debug; bool trackDamage; bool allowRGB24; + bool hdr16to10; unsigned frameBufferCount; // must be last @@ -154,6 +159,14 @@ static void d12_initOptions(void) .type = OPTION_TYPE_BOOL, .value.x_bool = false }, + { + .module = "d12", + .name = "HDR16to10", + .description = + "Convert HDR16/8bpp to HDR10/4bpp (saves bandwidth)", + .type = OPTION_TYPE_BOOL, + .value.x_bool = true + }, { .module = "d12", .name = "debug", @@ -180,9 +193,10 @@ static bool d12_create( return false; } - this->debug = option_get_bool("d12", "debug" ); - this->trackDamage = option_get_bool("d12", "trackDamage"); - this->allowRGB24 = option_get_bool("d12", "allowRGB24" ); + this->debug = option_get_bool("d12", "debug" ); + this->trackDamage = option_get_bool("d12", "trackDamage" ); + this->allowRGB24 = option_get_bool("d12", "allowRGB24" ); + this->hdr16to10 = option_get_bool("d12", "HDR16to10" ); DEBUG_INFO( "debug:%d trackDamage:%d allowRGB24:%d", @@ -264,6 +278,23 @@ static bool d12_init(void * ivshmemBase, unsigned * alignSize) ID3D12Debug1_SetEnableSynchronizedCommandQueueValidation(*debug, TRUE); } + // get the display path info + comRef_defineLocal(IDXGIOutput6, output6); + hr = IDXGIOutput_QueryInterface(*output, &IID_IDXGIOutput6, (void **)output6); + if (FAILED(hr)) + { + DEBUG_WINERROR("Failed to obtain the IDXGIOutput6 interface", hr); + goto exit; + } + + DXGI_OUTPUT_DESC1 desc1; + IDXGIOutput6_GetDesc1(*output6, &desc1); + if (!display_getPathInfo(desc1.Monitor, &this->displayPathInfo)) + { + DEBUG_ERROR("Failed to get the display path info"); + goto exit; + } + // create the D3D12 device comRef_defineLocal(ID3D12Device3, device); hr = DX12.D3D12CreateDevice( @@ -353,13 +384,22 @@ retryCreateCommandQueue: // create the vector of effects vector_create(&this->effects, sizeof(D12Effect *), 0); + D12Effect * effect; + + if (this->hdr16to10) + { + if (!d12_effectCreate(&D12Effect_HDR16to10, &effect, *device, + &this->displayPathInfo)) + goto exit; + vector_push(&this->effects, &effect); + } /* if RGB24 conversion is enabled add the effect to the list NOTE: THIS MUST BE THE LAST EFFECT */ if (this->allowRGB24) { - D12Effect * effect; - if (!d12_effectCreate(&D12Effect_RGB24, &effect, *device)) + if (!d12_effectCreate(&D12Effect_RGB24, &effect, *device, + &this->displayPathInfo)) goto exit; vector_push(&this->effects, &effect); } @@ -494,12 +534,14 @@ static CaptureResult d12_waitFrame(unsigned frameBufferIndex, this->captureFormat = srcFormat; D12Effect * effect; + D12FrameFormat curFormat = srcFormat; vector_forEach(effect, &this->effects) { - dstFormat = srcFormat; - switch(d12_effectSetFormat(effect, *this->device, &srcFormat, &dstFormat)) + dstFormat = curFormat; + switch(d12_effectSetFormat(effect, *this->device, &curFormat, &dstFormat)) { case D12_EFFECT_STATUS_OK: + curFormat = dstFormat; effect->enabled = true; break; @@ -542,8 +584,8 @@ static CaptureResult d12_waitFrame(unsigned frameBufferIndex, const unsigned int maxRows = maxFrameSize / layout.Footprint.RowPitch; frame->formatVer = this->formatVer; - frame->screenWidth = srcFormat.desc.Width; - frame->screenHeight = srcFormat.desc.Height; + frame->screenWidth = srcFormat.width; + frame->screenHeight = srcFormat.height; frame->dataWidth = dstFormat.desc.Width; frame->dataHeight = min(maxRows, dstFormat.desc.Height); frame->frameWidth = dstFormat.width; diff --git a/host/platform/Windows/capture/D12/effect.h b/host/platform/Windows/capture/D12/effect.h index 39fdd5ad..2034a609 100644 --- a/host/platform/Windows/capture/D12/effect.h +++ b/host/platform/Windows/capture/D12/effect.h @@ -42,7 +42,8 @@ struct D12Effect bool enabled; - bool (*create)(D12Effect ** instance, ID3D12Device3 * device); + bool (*create)(D12Effect ** instance, ID3D12Device3 * device, + const DISPLAYCONFIG_PATH_INFO * displayPathInfo); void (*free)(D12Effect ** instance); @@ -61,9 +62,10 @@ struct D12Effect }; static inline bool d12_effectCreate(const D12Effect * effect, - D12Effect ** instance, ID3D12Device3 * device) + D12Effect ** instance, ID3D12Device3 * device, + const DISPLAYCONFIG_PATH_INFO * displayPathInfo) { - if (!effect->create(instance, device)) + if (!effect->create(instance, device, displayPathInfo)) return false; memcpy(*instance, effect, sizeof(*effect)); return true; @@ -94,5 +96,6 @@ static inline ID3D12Resource * d12_effectRun(D12Effect * effect, // effect defines extern const D12Effect D12Effect_RGB24; +extern const D12Effect D12Effect_HDR16to10; #endif diff --git a/host/platform/Windows/capture/D12/effect/hdr16to10.c b/host/platform/Windows/capture/D12/effect/hdr16to10.c new file mode 100644 index 00000000..697d488e --- /dev/null +++ b/host/platform/Windows/capture/D12/effect/hdr16to10.c @@ -0,0 +1,454 @@ +#include "effect.h" + +#include "d12.h" +#include "command_group.h" + +#include "com_ref.h" +#include "common/debug.h" +#include "common/windebug.h" +#include "common/array.h" +#include "common/display.h" + +#include + +typedef struct HDR16to10Inst +{ + D12Effect base; + + const DISPLAYCONFIG_PATH_INFO * displayPathInfo; + struct + { + float SDRWhiteLevel; + } + consts; + + ID3D12RootSignature ** rootSignature; + ID3D12PipelineState ** pso; + ID3D12DescriptorHeap ** descHeap; + ID3D12Resource ** constBuffer; + + unsigned threadsX, threadsY; + ID3D12Resource ** dst; +} +HDR16to10Inst; + +#define THREADS 8 + +static bool d12_effect_hdr16to10Create(D12Effect ** instance, + ID3D12Device3 * device, const DISPLAYCONFIG_PATH_INFO * displayPathInfo) +{ + HDR16to10Inst * this = calloc(1, sizeof(*this)); + if (!this) + { + DEBUG_ERROR("out of memory"); + return false; + } + + bool result = false; + HRESULT hr; + comRef_scopePush(10); + + this->displayPathInfo = displayPathInfo; + + // shader resource view + D3D12_DESCRIPTOR_RANGE descriptorRanges[3] = + { + { + .RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV, + .NumDescriptors = 1, + .BaseShaderRegister = 0, + .RegisterSpace = 0, + .OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND + }, + { + .RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV, + .NumDescriptors = 1, + .BaseShaderRegister = 0, + .RegisterSpace = 0, + .OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND + }, + { + .RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV, + .NumDescriptors = 1, + .BaseShaderRegister = 0, + .RegisterSpace = 0, + .OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND + } + }; + + // descriptor table + D3D12_ROOT_PARAMETER rootParams[1] = + { + { + .ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE, + .ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL, + .DescriptorTable = + { + .NumDescriptorRanges = ARRAY_LENGTH(descriptorRanges), + .pDescriptorRanges = descriptorRanges + } + } + }; + + // root signature + D3D12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc = + { + .Version = D3D_ROOT_SIGNATURE_VERSION_1, + .Desc_1_0 = + { + .NumParameters = ARRAY_LENGTH(rootParams), + .pParameters = rootParams, + .NumStaticSamplers = 0, + .pStaticSamplers = NULL, + .Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE + } + }; + + // Serialize the root signature + comRef_defineLocal(ID3DBlob, blob ); + comRef_defineLocal(ID3DBlob, error); + hr = DX12.D3D12SerializeVersionedRootSignature( + &rootSignatureDesc, blob, error); + if (FAILED(hr)) + { + DEBUG_WINERROR("Failed to serialize the root signature", hr); + DEBUG_ERROR("%s", (const char *)ID3DBlob_GetBufferPointer(*error)); + goto exit; + } + + // Create the root signature + comRef_defineLocal(ID3D12RootSignature, rootSignature); + hr = ID3D12Device_CreateRootSignature( + device, + 0, + ID3DBlob_GetBufferPointer(*blob), + ID3DBlob_GetBufferSize(*blob), + &IID_ID3D12RootSignature, + (void **)rootSignature); + if (FAILED(hr)) + { + DEBUG_WINERROR("Failed to create the root signature", hr); + goto exit; + } + + // Compile the shader + const char * testCode = + "cbuffer Constants : register(b0)\n" + "{\n" + " float SDRWhiteLevel;\n" + "};\n" + "\n" + "Texture2D src : register(t0);\n" + "RWTexture2D dst : register(u0);\n" + "\n" + "[numthreads(" STR(THREADS) ", " STR(THREADS) ", 1)]\n" + "void main(uint3 dt : SV_DispatchThreadID)\n" + "{\n" + " dst[dt.xy] = src[dt.xy] * SDRWhiteLevel;" + "}\n"; + + bool debug = false; + hr = D3DCompile( + testCode, strlen(testCode), + NULL, NULL, NULL, "main", "cs_5_0", + debug ? (D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION) : 0, + 0, blob, error); + if (FAILED(hr)) + { + DEBUG_ERROR("Failed to compile the shader"); + DEBUG_ERROR("%s", (const char *)ID3DBlob_GetBufferPointer(*error)); + goto exit; + } + + // Create the PSO + D3D12_COMPUTE_PIPELINE_STATE_DESC psoDesc = + { + .pRootSignature = *rootSignature, + .CS = + { + .pShaderBytecode = ID3DBlob_GetBufferPointer(*blob), + .BytecodeLength = ID3DBlob_GetBufferSize (*blob) + } + }; + + comRef_defineLocal(ID3D12PipelineState, pso); + hr = ID3D12Device3_CreateComputePipelineState( + device, &psoDesc, &IID_ID3D12PipelineState, (void **)pso); + if (FAILED(hr)) + { + DEBUG_WINERROR("Failed to create the PSO", hr); + goto exit; + } + + // Create the descriptor heap + D3D12_DESCRIPTOR_HEAP_DESC descHeapDesc = + { + .Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + .NumDescriptors = ARRAY_LENGTH(descriptorRanges), + .Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, + .NodeMask = 0 + }; + + comRef_defineLocal(ID3D12DescriptorHeap, descHeap); + hr = ID3D12Device3_CreateDescriptorHeap( + device, &descHeapDesc, &IID_ID3D12DescriptorHeap, (void **)descHeap); + if (FAILED(hr)) + { + DEBUG_WINERROR("Failed to create the parameter heap", hr); + goto exit; + } + + D3D12_HEAP_PROPERTIES constHeapProps = + { + .Type = D3D12_HEAP_TYPE_UPLOAD, + .CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN, + .MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN + }; + + D3D12_RESOURCE_DESC constBufferDesc = + { + .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER, + .Width = ALIGN_TO(sizeof(this->consts), + D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT), + .Height = 1, + .DepthOrArraySize = 1, + .MipLevels = 1, + .Format = DXGI_FORMAT_UNKNOWN, + .SampleDesc.Count = 1, + .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR, + .Flags = D3D12_RESOURCE_FLAG_NONE + }; + + comRef_defineLocal(ID3D12Resource, constBuffer); + hr = ID3D12Device3_CreateCommittedResource( + device, + &constHeapProps, + D3D12_HEAP_FLAG_NONE, + &constBufferDesc, + D3D12_RESOURCE_STATE_GENERIC_READ, + NULL, + &IID_ID3D12Resource, + (void **)constBuffer); + + if (FAILED(hr)) + { + DEBUG_WINERROR("Failed to create the constant buffer resource", hr); + goto exit; + } + + comRef_toGlobal(this->rootSignature, rootSignature); + comRef_toGlobal(this->pso , pso ); + comRef_toGlobal(this->descHeap , descHeap ); + comRef_toGlobal(this->constBuffer , constBuffer ); + + result = true; + +exit: + if (result) + *instance = &this->base; + else + free(this); + + comRef_scopePop(); + return result; +} + +static void d12_effect_hdr16to10Free(D12Effect ** instance) +{ + HDR16to10Inst * this = UPCAST(HDR16to10Inst, *instance); + + free(this); +} + +static D12EffectStatus d12_effect_hdr16to10SetFormat(D12Effect * effect, + ID3D12Device3 * device, + const D12FrameFormat * src, + D12FrameFormat * dst) +{ + HDR16to10Inst * this = UPCAST(HDR16to10Inst, effect); + comRef_scopePush(1); + + D12EffectStatus result = D12_EFFECT_STATUS_ERROR; + HRESULT hr; + + if (src->desc.Format != DXGI_FORMAT_R16G16B16A16_FLOAT || + src->colorSpace != DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) + { + result = D12_EFFECT_STATUS_BYPASS; + goto exit; + } + + D3D12_HEAP_PROPERTIES heapProps = + { + .Type = D3D12_HEAP_TYPE_DEFAULT, + .CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN, + .MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN, + .CreationNodeMask = 1, + .VisibleNodeMask = 1 + }; + + D3D12_RESOURCE_DESC desc = + { + .Format = DXGI_FORMAT_R10G10B10A2_UNORM, + .Width = src->desc.Width, + .Height = src->desc.Height, + .Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D, + .Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, + .MipLevels = 1, + .DepthOrArraySize = 1, + .SampleDesc.Count = 1 + }; + + comRef_defineLocal(ID3D12Resource, res); + hr = ID3D12Device3_CreateCommittedResource( + device, &heapProps, D3D12_HEAP_FLAG_CREATE_NOT_ZEROED, &desc, + D3D12_RESOURCE_STATE_COPY_SOURCE, NULL, &IID_ID3D12Resource, + (void **)res); + + if (FAILED(hr)) + { + DEBUG_ERROR("Failed to create the destination texture"); + goto exit; + } + + comRef_toGlobal(this->dst, res); + this->threadsX = (desc.Width + (THREADS-1)) / THREADS; + this->threadsY = (desc.Height + (THREADS-1)) / THREADS; + + dst->desc = desc; + dst->format = CAPTURE_FMT_RGBA10; + result = D12_EFFECT_STATUS_OK; + +exit: + comRef_scopePop(); + return result; +} + +static ID3D12Resource * d12_effect_hdr16to10Run(D12Effect * effect, + ID3D12Device3 * device, ID3D12GraphicsCommandList * commandList, + ID3D12Resource * src, RECT dirtyRects[], unsigned * nbDirtyRects) +{ + HDR16to10Inst * this = UPCAST(HDR16to10Inst, effect); + + float nits = 80.0f / display_getSDRWhiteLevel(this->displayPathInfo); + if (nits != this->consts.SDRWhiteLevel) + { + this->consts.SDRWhiteLevel = nits; + + void * data; + D3D12_RANGE readRange = { 0, 0 }; + HRESULT hr = ID3D12Resource_Map(*this->constBuffer, 0, &readRange, &data); + if (SUCCEEDED(hr)) + { + memcpy(data, &this->consts, sizeof(this->consts)); + ID3D12Resource_Unmap(*this->constBuffer, 0, NULL); + } + } + + // transition the destination texture to unordered access so we can write to it + { + D3D12_RESOURCE_BARRIER barrier = + { + .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE, + .Transition = + { + .pResource = *this->dst, + .StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE, + .StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS, + .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES + } + }; + ID3D12GraphicsCommandList_ResourceBarrier(commandList, 1, &barrier); + } + + // get the heap handle + D3D12_CPU_DESCRIPTOR_HANDLE cpuSrvUavHandle = + ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(*this->descHeap); + + // descriptor for input CBV + D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = + { + .BufferLocation = ID3D12Resource_GetGPUVirtualAddress(*this->constBuffer), + .SizeInBytes = ALIGN_TO(sizeof(this->consts), + D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT) + }; + ID3D12Device3_CreateConstantBufferView(device, &cbvDesc, cpuSrvUavHandle); + + // move to the next slot + cpuSrvUavHandle.ptr += ID3D12Device3_GetDescriptorHandleIncrementSize( + device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + + // descriptor for input SRV + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = + { + .Format = DXGI_FORMAT_R16G16B16A16_FLOAT, + .ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D, + .Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, + .Texture2D.MipLevels = 1 + }; + ID3D12Device3_CreateShaderResourceView(device, src, &srvDesc, cpuSrvUavHandle); + + // move to the next slot + cpuSrvUavHandle.ptr += ID3D12Device3_GetDescriptorHandleIncrementSize( + device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + + // descriptor for the output UAV + D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = + { + .Format = DXGI_FORMAT_R10G10B10A2_UNORM, + .ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D + }; + ID3D12Device3_CreateUnorderedAccessView( + device, *this->dst, NULL, &uavDesc, cpuSrvUavHandle); + + // bind the descriptor heap to the pipeline + ID3D12GraphicsCommandList_SetDescriptorHeaps(commandList, 1, this->descHeap); + + // set the pipeline state + ID3D12GraphicsCommandList_SetPipelineState(commandList, *this->pso); + + // set the root signature on the command list + ID3D12GraphicsCommandList_SetComputeRootSignature( + commandList, *this->rootSignature); + + // get the GPU side handle for our heap + D3D12_GPU_DESCRIPTOR_HANDLE gpuSrvUavHandle = + ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(*this->descHeap); + + // bind the descriptor tables to the root signature + ID3D12GraphicsCommandList_SetComputeRootDescriptorTable( + commandList, 0, gpuSrvUavHandle); + + ID3D12GraphicsCommandList_Dispatch( + commandList, this->threadsX, this->threadsY, 1); + + // transition the destination texture to a copy source for the next stage + { + D3D12_RESOURCE_BARRIER barrier = + { + .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE, + .Transition = + { + .pResource = *this->dst, + .StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS, + .StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE, + .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES + } + }; + ID3D12GraphicsCommandList_ResourceBarrier(commandList, 1, &barrier); + } + + // return the output buffer + return *this->dst; +} + +const D12Effect D12Effect_HDR16to10 = +{ + .name = "HDR16to10", + .create = d12_effect_hdr16to10Create, + .free = d12_effect_hdr16to10Free, + .setFormat = d12_effect_hdr16to10SetFormat, + .run = d12_effect_hdr16to10Run +}; diff --git a/host/platform/Windows/capture/D12/effect/rgb24.c b/host/platform/Windows/capture/D12/effect/rgb24.c index 13352c2f..501d0ee3 100644 --- a/host/platform/Windows/capture/D12/effect/rgb24.c +++ b/host/platform/Windows/capture/D12/effect/rgb24.c @@ -25,7 +25,8 @@ TestInstance; #define THREADS 8 -static bool d12_effect_rgb24Create(D12Effect ** instance, ID3D12Device3 * device) +static bool d12_effect_rgb24Create(D12Effect ** instance, ID3D12Device3 * device, + const DISPLAYCONFIG_PATH_INFO * displayPathInfo) { TestInstance * this = calloc(1, sizeof(*this)); if (!this) diff --git a/host/platform/Windows/capture/DXGI/src/pp/sdrwhitelevel.c b/host/platform/Windows/capture/DXGI/src/pp/sdrwhitelevel.c index 7d146e47..731e3ff3 100644 --- a/host/platform/Windows/capture/DXGI/src/pp/sdrwhitelevel.c +++ b/host/platform/Windows/capture/DXGI/src/pp/sdrwhitelevel.c @@ -24,6 +24,7 @@ #include "common/debug.h" #include "common/windebug.h" +#include "common/display.h" #include @@ -92,7 +93,7 @@ static bool sdrWhiteLevel_setup( DXGI_OUTPUT_DESC1 desc1; IDXGIOutput6_GetDesc1(*output6, &desc1); - if (!getDisplayPathInfo(desc1.Monitor, &this.displayPathInfo)) + if (!display_getPathInfo(desc1.Monitor, &this.displayPathInfo)) { DEBUG_ERROR("Failed to get the display path info"); goto exit; @@ -214,7 +215,7 @@ static void sdrWhiteLevel_free(void * opaque) static void updateConsts(void) { - float nits = getSDRWhiteLevel(&this.displayPathInfo); + float nits = display_getSDRWhiteLevel(&this.displayPathInfo); if (nits == this.sdrWhiteLevel) return; diff --git a/host/platform/Windows/capture/DXGI/src/util.c b/host/platform/Windows/capture/DXGI/src/util.c index 08fcaeb2..aa55d952 100644 --- a/host/platform/Windows/capture/DXGI/src/util.c +++ b/host/platform/Windows/capture/DXGI/src/util.c @@ -224,104 +224,6 @@ bool compileShader(ID3DBlob ** dst, const char * entry, const char * target, return true; } -bool getDisplayPathInfo(HMONITOR monitor, DISPLAYCONFIG_PATH_INFO * info) -{ - bool result = false; - UINT32 numPath, numMode; - - MONITORINFOEXW viewInfo = { .cbSize = sizeof(viewInfo) }; - if (!GetMonitorInfoW(monitor, (MONITORINFO*)&viewInfo)) - { - DEBUG_ERROR("Failed to get the monitor info"); - goto err; - } - -err_retry: - if (FAILED(GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &numPath, &numMode))) - goto err; - - DISPLAYCONFIG_PATH_INFO * pathInfo = calloc(sizeof(*pathInfo), numPath); - if (!pathInfo) - goto err_mem_pathInfo; - - DISPLAYCONFIG_MODE_INFO * modeInfo = calloc(sizeof(*modeInfo), numMode); - if (!modeInfo) - goto err_mem_modeInfo; - - LONG status = QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, - &numPath, pathInfo, - &numMode, modeInfo, - NULL); - - if (status != ERROR_SUCCESS) - { - if (status == ERROR_INSUFFICIENT_BUFFER) - { - free(modeInfo); - free(pathInfo); - goto err_retry; - } - - DEBUG_ERROR("QueryDisplayConfig failed with 0x%lx", status); - goto err_queryDisplay; - } - - for(unsigned i = 0; i < numPath; ++i) - { - DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = - { - .header = - { - .type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME, - .size = sizeof(sourceName), - .adapterId = pathInfo[i].sourceInfo.adapterId, - .id = pathInfo[i].sourceInfo.id, - } - }; - - if (FAILED(DisplayConfigGetDeviceInfo(&sourceName.header))) - continue; - - if (wcscmp(viewInfo.szDevice, sourceName.viewGdiDeviceName) != 0) - continue; - - *info = pathInfo[i]; - result = true; - break; - } - -err_queryDisplay: - free(modeInfo); - -err_mem_modeInfo: - free(pathInfo); - -err_mem_pathInfo: - -err: - return result; -} - -float getSDRWhiteLevel(const DISPLAYCONFIG_PATH_INFO * displayPathInfo) -{ - float nits = 80.0f; - DISPLAYCONFIG_SDR_WHITE_LEVEL level = - { - .header = - { - .type = DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL, - .size = sizeof(level), - .adapterId = displayPathInfo->targetInfo.adapterId, - .id = displayPathInfo->targetInfo.id, - } - }; - - if (SUCCEEDED(DisplayConfigGetDeviceInfo(&level.header))) - nits = level.SDRWhiteLevel / 1000.0f * 80.0f; - - return nits; -} - DXGI_FORMAT getDXGIFormat(CaptureFormat format) { switch(format) diff --git a/host/platform/Windows/capture/DXGI/src/util.h b/host/platform/Windows/capture/DXGI/src/util.h index 609702fb..ab8ddc9b 100644 --- a/host/platform/Windows/capture/DXGI/src/util.h +++ b/host/platform/Windows/capture/DXGI/src/util.h @@ -30,8 +30,4 @@ const char * getDXGIColorSpaceTypeStr(DXGI_COLOR_SPACE_TYPE type); bool compileShader(ID3DBlob ** dst, const char * entry, const char * target, const char * code, const D3D_SHADER_MACRO * defines); -bool getDisplayPathInfo(HMONITOR monitor, DISPLAYCONFIG_PATH_INFO * info); - -float getSDRWhiteLevel(const DISPLAYCONFIG_PATH_INFO * displayPathInfo); - DXGI_FORMAT getDXGIFormat(CaptureFormat format);