From 21cd380cad13c0c286de862b031be4db2f9a698e Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Sun, 29 Oct 2023 20:26:26 +1100 Subject: [PATCH] [host] dxgi: seperate out and implement a post processor chain --- .../Windows/capture/DXGI/CMakeLists.txt | 2 + host/platform/Windows/capture/DXGI/src/dxgi.c | 349 ++++------------ .../Windows/capture/DXGI/src/dxgi_capture.h | 14 +- host/platform/Windows/capture/DXGI/src/pp.h | 54 +++ .../capture/DXGI/src/pp/sdrwhitelevel.c | 377 ++++++++++++++++++ 5 files changed, 526 insertions(+), 270 deletions(-) create mode 100644 host/platform/Windows/capture/DXGI/src/pp.h create mode 100644 host/platform/Windows/capture/DXGI/src/pp/sdrwhitelevel.c diff --git a/host/platform/Windows/capture/DXGI/CMakeLists.txt b/host/platform/Windows/capture/DXGI/CMakeLists.txt index b3ca42f4..6b78bbcd 100644 --- a/host/platform/Windows/capture/DXGI/CMakeLists.txt +++ b/host/platform/Windows/capture/DXGI/CMakeLists.txt @@ -8,6 +8,8 @@ add_library(capture_DXGI STATIC src/ods_capture.c src/util.c src/com_ref.c + + src/pp/sdrwhitelevel.c ) add_definitions("-DCOBJMACROS -DINITGUID") diff --git a/host/platform/Windows/capture/DXGI/src/dxgi.c b/host/platform/Windows/capture/DXGI/src/dxgi.c index 5fb7aa2b..d92fe888 100644 --- a/host/platform/Windows/capture/DXGI/src/dxgi.c +++ b/host/platform/Windows/capture/DXGI/src/dxgi.c @@ -49,6 +49,16 @@ #define LOCKED(...) INTERLOCKED_SECTION(this->deviceContextLock, __VA_ARGS__) +//post processers +extern const DXGIPostProcess DXGIPP_SDRWhiteLevel; + +typedef struct +{ + const DXGIPostProcess * pp; + void * opaque; +} +PostProcessInstance; + typedef struct { unsigned int id; @@ -61,12 +71,6 @@ DownsampleRule; static Vector downsampleRules = {0}; -struct ShaderConsts -{ - float sdrWhiteLevel; -} -__attribute__((aligned(16))); - // locals static struct DXGIInterface * this = NULL; @@ -82,6 +86,10 @@ static struct DXGICopyBackend * backends[] = { static bool dxgi_deinit(void); static CaptureResult dxgi_releaseFrame(void); +static bool ppInit(const DXGIPostProcess * pp, bool shareable); +static ID3D11Texture2D * ppRun(Texture * tex, ID3D11Texture2D * src); +static void ppFreeAll(void); + // implementation static const char * dxgi_getName(void) @@ -626,18 +634,10 @@ static bool dxgi_init(void) IDXGIOutput6_GetDesc1(*output6, &desc1); this->dxgiColorSpace = desc1.ColorSpace; - if (!getDisplayPathInfo(desc1.Monitor, &this->displayPathInfo)) - { - DEBUG_ERROR("Failed to get the display path info"); - goto fail; - } - this->sdrWhiteLevel = getSDRWhiteLevel(&this->displayPathInfo); - DEBUG_INFO("Bits Per Color : %u" , desc1.BitsPerColor); DEBUG_INFO("Color Space : %s" , getDXGIColorSpaceTypeStr(this->dxgiColorSpace)); DEBUG_INFO("Min/Max Luminance : %f/%f", desc1.MinLuminance, desc1.MaxLuminance); DEBUG_INFO("Frame Luminance : %f" , desc1.MaxFullFrameLuminance); - DEBUG_INFO("SDR White Level : %f" , this->sdrWhiteLevel); } } @@ -753,185 +753,26 @@ static bool dxgi_init(void) for (int i = 0; i < this->maxTextures; ++i) { this->texture[i].texDamageCount = -1; - if (!this->hdr) - continue; - - D3D11_TEXTURE2D_DESC hdrTexDesc = - { - .Width = this->width, - .Height = this->height, - .MipLevels = 1, - .ArraySize = 1, - .SampleDesc.Count = 1, - .SampleDesc.Quality = 0, - .Usage = D3D11_USAGE_DEFAULT, - .Format = DXGI_FORMAT_R10G10B10A2_UNORM, - .BindFlags = D3D11_BIND_RENDER_TARGET | - D3D11_BIND_SHADER_RESOURCE, - .CPUAccessFlags = 0, - .MiscFlags = 0 - }; - - // allow texture sharing with other backends - if (this->backend != ©BackendD3D11) - hdrTexDesc.MiscFlags |= - D3D11_RESOURCE_MISC_SHARED | - D3D11_RESOURCE_MISC_SHARED_NTHANDLE; - - status = ID3D11Device_CreateTexture2D(*this->device, &hdrTexDesc, NULL, - (ID3D11Texture2D **)comRef_newGlobal(&this->texture[i].hdrTex)); - - if (FAILED(status)) - { - DEBUG_WINERROR("Failed to create HDR texture", status); - goto fail; - } - - status = ID3D11Device_CreateRenderTargetView(*this->device, - (ID3D11Resource *)*this->texture[i].hdrTex, NULL, - (ID3D11RenderTargetView **)comRef_newGlobal( - &this->texture[i].renderTarget)); - - if (FAILED(status)) - { - DEBUG_WINERROR("Failed to create HDR target view", status); - goto fail; - } + vector_create(&this->texture[i].pp, sizeof(PostProcessInstance), 0); } - if (this->hdr) + const D3D11_VIEWPORT vp = { - const D3D11_VIEWPORT vp = - { - .TopLeftX = 0.0f, - .TopLeftY = 0.0f, - .Width = this->width, - .Height = this->height, - .MinDepth = 0.0f, - .MaxDepth = 1.0f, - }; - ID3D11DeviceContext_RSSetViewports(*this->deviceContext, 1, &vp); + .TopLeftX = 0.0f, + .TopLeftY = 0.0f, + .Width = this->width, + .Height = this->height, + .MinDepth = 0.0f, + .MaxDepth = 1.0f, + }; + ID3D11DeviceContext_RSSetViewports(*this->deviceContext, 1, &vp); - static const char * vshader = - "void main(\n" - " in uint vertexID : SV_VERTEXID,\n" - " out float4 position : SV_POSITION,\n" - " out float2 texCoord : TEXCOORD0)\n" - "{\n" - " float2 positions[4] =\n" - " {\n" - " float2(-1.0, 1.0),\n" - " float2( 1.0, 1.0),\n" - " float2(-1.0, -1.0),\n" - " float2( 1.0, -1.0)\n" - " };\n" - "\n" - " float2 texCoords[4] =\n" - " {\n" - " float2(0.0, 0.0),\n" - " float2(1.0, 0.0),\n" - " float2(0.0, 1.0),\n" - " float2(1.0, 1.0)\n" - " };\n" - "\n" - " position = float4(positions[vertexID], 0.0, 1.0);\n" - " texCoord = texCoords[vertexID];\n" - "}"; - - static const char * pshader = - "Texture2D gInputTexture : register(t0);\n" - "SamplerState gSamplerState : register(s0);\n" - "cbuffer gConsts : register(b0)\n" - "{\n" - " float SDRWhiteLevel;" - "};\n" - "\n" - "float4 main(\n" - " float4 position : SV_POSITION,\n" - " float2 texCoord : TEXCOORD0) : SV_TARGET" - "{\n" - " float4 color = gInputTexture.Sample(gSamplerState, texCoord);\n" - " color.rgb *= SDRWhiteLevel;\n" - " return color;\n" - "}\n"; - - comRef_defineLocal(ID3DBlob, byteCode); - if (!compileShader(byteCode, "main", "vs_5_0", vshader)) - goto fail; - - status = ID3D11Device_CreateVertexShader( - *this->device, - ID3D10Blob_GetBufferPointer(*byteCode), - ID3D10Blob_GetBufferSize (*byteCode), - NULL, - (ID3D11VertexShader **)comRef_newGlobal(&this->vertexShader)); - - if (FAILED(status)) - { - DEBUG_WINERROR("Failed to create the vertex shader", status); - goto fail; - } - - comRef_release(byteCode); - if (!compileShader(byteCode, "main", "ps_5_0", pshader)) - goto fail; - - status = ID3D11Device_CreatePixelShader( - *this->device, - ID3D10Blob_GetBufferPointer(*byteCode), - ID3D10Blob_GetBufferSize (*byteCode), - NULL, - (ID3D11PixelShader **)comRef_newGlobal(&this->pixelShader)); - - if (FAILED(status)) - { - DEBUG_WINERROR("Failed to create the pixel shader", status); - goto fail; - } - - const D3D11_SAMPLER_DESC samplerDesc = - { - .Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR, - .AddressU = D3D11_TEXTURE_ADDRESS_WRAP, - .AddressV = D3D11_TEXTURE_ADDRESS_WRAP, - .AddressW = D3D11_TEXTURE_ADDRESS_WRAP, - .ComparisonFunc = D3D11_COMPARISON_NEVER, - .MinLOD = 0, - .MaxLOD = D3D11_FLOAT32_MAX - }; - - status = ID3D11Device_CreateSamplerState( - *this->device, &samplerDesc, - (ID3D11SamplerState **)comRef_newGlobal(&this->samplerState)); - - if (FAILED(status)) - { - DEBUG_WINERROR("Failed to create the sampler state", status); - goto fail; - } - - struct ShaderConsts consts = - { - .sdrWhiteLevel = 80.0f / this->sdrWhiteLevel - }; - - D3D11_BUFFER_DESC bufferDesc = - { - .ByteWidth = sizeof(consts), - .Usage = D3D11_USAGE_DEFAULT, - .BindFlags = D3D11_BIND_CONSTANT_BUFFER, - }; - - D3D11_SUBRESOURCE_DATA initData = { .pSysMem = &consts }; - status = ID3D11Device_CreateBuffer( - *this->device, &bufferDesc, &initData, - (ID3D11Buffer **)comRef_newGlobal(&this->constBuffer)); - - if (FAILED(status)) - { - DEBUG_WINERROR("Failed to create the constant buffer", status); - goto fail; - } + // if HDR add the SDRWhiteLevel post processor to correct the output + if (this->hdr && !ppInit(&DXGIPP_SDRWhiteLevel, + this->backend != ©BackendD3D11)) + { + DEBUG_ERROR("Failed to initialize the SDRWhiteLevel post processor"); + goto fail; } for (int i = 0; i < LGMP_Q_FRAME_LEN; ++i) @@ -961,19 +802,20 @@ static bool dxgi_deinit(void) for (int i = 0; i < this->maxTextures; ++i) { - if (this->texture[i].map) - { - this->backend->unmapTexture(this->texture + i); - this->texture[i].map = NULL; - } + Texture * tex = &this->texture[i]; + if (!tex->map) + continue; + this->backend->unmapTexture(tex); + tex->map = NULL; } if (this->dup && *this->dup) dxgi_releaseFrame(); - // this MUST run before backend->free(). + // this MUST run before backend->free() & ppFreeAll. comRef_free(); + ppFreeAll(); if (this->backend) { this->backend->free(); @@ -1223,76 +1065,14 @@ static CaptureResult dxgi_capture(void) computeFrameDamage(tex); computeTexDamage(tex); - if (this->hdr) - { - // setup the pixel shader input resource view - comRef_defineLocal(ID3D11ShaderResourceView, inputSRV); - { - const D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = - { - .Format = this->dxgiSrcFormat, - .ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D, - .Texture2D.MipLevels = 1 - }; - status = ID3D11Device_CreateShaderResourceView( - *this->device, (ID3D11Resource *)*src, &srvDesc, inputSRV); - if (FAILED(status)) - { - DEBUG_WINERROR("Failed to create the source resource view", status); - result = CAPTURE_RESULT_ERROR; - goto exit; - } - } - - float nits = getSDRWhiteLevel(&this->displayPathInfo); - if (nits != this->sdrWhiteLevel) - { - this->sdrWhiteLevel = nits; - - struct ShaderConsts consts = - { - .sdrWhiteLevel = 80.0f / nits - }; - - ID3D11DeviceContext_UpdateSubresource( - *this->deviceContext, (ID3D11Resource*)*this->constBuffer, - 0, NULL, &consts, 0, 0); - } - - ID3D11DeviceContext_VSSetShader( - *this->deviceContext, *this->vertexShader, NULL, 0); - - ID3D11DeviceContext_PSSetShader( - *this->deviceContext, *this->pixelShader, NULL, 0); - ID3D11DeviceContext_PSSetShaderResources( - *this->deviceContext, 0, 1, inputSRV); - ID3D11DeviceContext_PSSetSamplers( - *this->deviceContext, 0, 1, this->samplerState); - ID3D11DeviceContext_PSSetConstantBuffers( - *this->deviceContext, 0, 1, this->constBuffer); - - ID3D11DeviceContext_OMSetRenderTargets( - *this->deviceContext, 1, tex->renderTarget, NULL); - - ID3D11DeviceContext_IASetPrimitiveTopology( - *this->deviceContext, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - - ID3D11DeviceContext_Draw(*this->deviceContext, 4, 0); + ID3D11Texture2D * dst = ppRun(tex, *src); + if (dst != *src) ID3D11DeviceContext_Flush(*this->deviceContext); - if (!this->backend->copyFrame(tex, *tex->hdrTex)) - { - result = CAPTURE_RESULT_ERROR; - goto exit; - } - } - else + if (!this->backend->copyFrame(tex, dst)) { - if (!this->backend->copyFrame(tex, *src)) - { - result = CAPTURE_RESULT_ERROR; - goto exit; - } + result = CAPTURE_RESULT_ERROR; + goto exit; } for (int i = 0; i < this->maxTextures; ++i) @@ -1535,6 +1315,51 @@ static CaptureResult dxgi_releaseFrame(void) return CAPTURE_RESULT_OK; } +static bool ppInit(const DXGIPostProcess * pp, bool shareable) +{ + if (!pp->setup(this->device, this->deviceContext, this->output)) + return false; + + for(int i = 0; i < this->maxTextures; ++i) + { + PostProcessInstance inst = { .pp = pp }; + if (!pp->init(&inst.opaque, this->width, this->height, shareable)) + return false; + + vector_push(&this->texture[i].pp, &inst); + } + + return true; +} + +static ID3D11Texture2D * ppRun(Texture * tex, ID3D11Texture2D * src) +{ + PostProcessInstance * inst; + vector_forEachRef(inst, &tex->pp) + src = inst->pp->run(inst->opaque, src); + + return src; +} + +static void ppFreeAll(void) +{ + for(int i = 0; i < this->maxTextures; ++i) + { + Texture * tex = &this->texture[i]; + if (!tex->pp.data) + continue; + + PostProcessInstance * inst; + vector_forEachRef(inst, &tex->pp) + { + inst->pp->free(inst->opaque); + if (i == this->maxTextures - 1) + inst->pp->finish(); + } + vector_destroy(&tex->pp); + } +} + struct CaptureInterface Capture_DXGI = { .shortName = "DXGI", diff --git a/host/platform/Windows/capture/DXGI/src/dxgi_capture.h b/host/platform/Windows/capture/DXGI/src/dxgi_capture.h index 225dc8c6..9e8d06ac 100644 --- a/host/platform/Windows/capture/DXGI/src/dxgi_capture.h +++ b/host/platform/Windows/capture/DXGI/src/dxgi_capture.h @@ -18,6 +18,8 @@ * Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "pp.h" + #include #include #include @@ -28,6 +30,7 @@ #include "common/event.h" #include "common/locking.h" #include "common/types.h" +#include "common/vector.h" #include "interface/capture.h" enum TextureState @@ -47,8 +50,9 @@ typedef struct Texture FrameDamageRect damageRects[KVMFR_MAX_DAMAGE_RECTS]; int32_t texDamageCount; FrameDamageRect texDamageRects[KVMFR_MAX_DAMAGE_RECTS]; - ID3D11RenderTargetView ** renderTarget; - ID3D11Texture2D ** hdrTex; + + // post processing + Vector pp; void * impl; } @@ -89,13 +93,7 @@ struct DXGIInterface bool needsRelease; DXGI_FORMAT dxgiSrcFormat, dxgiFormat; bool hdr; - DISPLAYCONFIG_PATH_INFO displayPathInfo; DXGI_COLOR_SPACE_TYPE dxgiColorSpace; - float sdrWhiteLevel; - ID3D11Buffer ** constBuffer; - ID3D11PixelShader ** pixelShader; - ID3D11VertexShader ** vertexShader; - ID3D11SamplerState ** samplerState; struct DXGICopyBackend * backend; CaptureGetPointerBuffer getPointerBufferFn; diff --git a/host/platform/Windows/capture/DXGI/src/pp.h b/host/platform/Windows/capture/DXGI/src/pp.h new file mode 100644 index 00000000..df055ffb --- /dev/null +++ b/host/platform/Windows/capture/DXGI/src/pp.h @@ -0,0 +1,54 @@ +/** + * Looking Glass + * Copyright © 2017-2023 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 +#include + +typedef struct +{ + /* the friendly name of the processor for debugging */ + const char * name; + + /* early initialization for registering options */ + void (*earlyInit)(void); + + /* common setup */ + bool (*setup)( + ID3D11Device ** device, + ID3D11DeviceContext ** context, + IDXGIOutput ** output); + + /* instance initialization */ + bool (*init)( + void ** opaque, + int width, + int height, + bool shareable); + + /* perform the processing */ + ID3D11Texture2D * (*run)(void * opaque, ID3D11Texture2D * src); + + /* instance destruction */ + void (*free)(void * opaque); + + /* cleanup */ + void (*finish)(void); +} +DXGIPostProcess; diff --git a/host/platform/Windows/capture/DXGI/src/pp/sdrwhitelevel.c b/host/platform/Windows/capture/DXGI/src/pp/sdrwhitelevel.c new file mode 100644 index 00000000..82ca7476 --- /dev/null +++ b/host/platform/Windows/capture/DXGI/src/pp/sdrwhitelevel.c @@ -0,0 +1,377 @@ +/** + * Looking Glass + * Copyright © 2017-2023 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 "pp.h" +#include "com_ref.h" +#include "util.h" + +#include "common/debug.h" +#include "common/windebug.h" + +#include + +typedef struct SDRWhiteLevel +{ + ID3D11Device ** device; + ID3D11DeviceContext ** context; + + ID3D11VertexShader ** vshader; + ID3D11PixelShader ** pshader; + ID3D11SamplerState ** sampler; + ID3D11Buffer ** buffer; + + DISPLAYCONFIG_PATH_INFO displayPathInfo; + float sdrWhiteLevel; +} +SDRWhiteLevel; +SDRWhiteLevel this = {0}; + +typedef struct +{ + ID3D11Texture2D ** tex; + ID3D11RenderTargetView ** target; +} +SDRWhiteLevelInst; + +struct ShaderConsts +{ + float sdrWhiteLevel; +} +__attribute__((aligned(16))); + +static const char * vshader = + "void main(\n" + " in uint vertexID : SV_VERTEXID,\n" + " out float4 position : SV_POSITION,\n" + " out float2 texCoord : TEXCOORD0)\n" + "{\n" + " float2 positions[4] =\n" + " {\n" + " float2(-1.0, 1.0),\n" + " float2( 1.0, 1.0),\n" + " float2(-1.0, -1.0),\n" + " float2( 1.0, -1.0)\n" + " };\n" + "\n" + " float2 texCoords[4] =\n" + " {\n" + " float2(0.0, 0.0),\n" + " float2(1.0, 0.0),\n" + " float2(0.0, 1.0),\n" + " float2(1.0, 1.0)\n" + " };\n" + "\n" + " position = float4(positions[vertexID], 0.0, 1.0);\n" + " texCoord = texCoords[vertexID];\n" + "}"; + +static const char * pshader = + "Texture2D gInputTexture : register(t0);\n" + "SamplerState gSamplerState : register(s0);\n" + "cbuffer gConsts : register(b0)\n" + "{\n" + " float SDRWhiteLevel;" + "};\n" + "\n" + "float4 main(\n" + " float4 position : SV_POSITION,\n" + " float2 texCoord : TEXCOORD0) : SV_TARGET" + "{\n" + " float4 color = gInputTexture.Sample(gSamplerState, texCoord);\n" + " color.rgb *= SDRWhiteLevel;\n" + " return color;\n" + "}\n"; + +static void updateConsts(void); + +static bool sdrWhiteLevel_setup( + ID3D11Device ** device, + ID3D11DeviceContext ** context, + IDXGIOutput ** output +) +{ + bool result = false; + comRef_scopePush(); + HRESULT status; + + this.device = device; + this.context = context; + + comRef_defineLocal(IDXGIOutput6, output6); + status = IDXGIOutput_QueryInterface( + *output, &IID_IDXGIOutput6, (void **)output6); + + if (!SUCCEEDED(status)) + { + DEBUG_ERROR("Failed to get the IDXGIOutput6 interface"); + goto exit; + } + + DXGI_OUTPUT_DESC1 desc1; + IDXGIOutput6_GetDesc1(*output6, &desc1); + if (!getDisplayPathInfo(desc1.Monitor, &this.displayPathInfo)) + { + DEBUG_ERROR("Failed to get the display path info"); + goto exit; + } + + // compile and create the vertex shader + comRef_defineLocal(ID3DBlob, byteCode); + if (!compileShader(byteCode, "main", "vs_5_0", vshader)) + goto exit; + + status = ID3D11Device_CreateVertexShader( + *this.device, + ID3D10Blob_GetBufferPointer(*byteCode), + ID3D10Blob_GetBufferSize (*byteCode), + NULL, + (ID3D11VertexShader **)comRef_newGlobal(&this.vshader)); + + if (FAILED(status)) + { + DEBUG_WINERROR("Failed to create the vertex shader", status); + goto exit; + } + + comRef_release(byteCode); + if (!compileShader(byteCode, "main", "ps_5_0", pshader)) + goto exit; + + status = ID3D11Device_CreatePixelShader( + *this.device, + ID3D10Blob_GetBufferPointer(*byteCode), + ID3D10Blob_GetBufferSize (*byteCode), + NULL, + (ID3D11PixelShader **)comRef_newGlobal(&this.pshader)); + + if (FAILED(status)) + { + DEBUG_WINERROR("Failed to create the pixel shader", status); + goto exit; + } + + const D3D11_SAMPLER_DESC samplerDesc = + { + .Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR, + .AddressU = D3D11_TEXTURE_ADDRESS_WRAP, + .AddressV = D3D11_TEXTURE_ADDRESS_WRAP, + .AddressW = D3D11_TEXTURE_ADDRESS_WRAP, + .ComparisonFunc = D3D11_COMPARISON_NEVER, + .MinLOD = 0, + .MaxLOD = D3D11_FLOAT32_MAX + }; + + status = ID3D11Device_CreateSamplerState( + *this.device, &samplerDesc, + (ID3D11SamplerState **)comRef_newGlobal(&this.sampler)); + + if (FAILED(status)) + { + DEBUG_WINERROR("Failed to create the sampler state", status); + goto exit; + } + + D3D11_BUFFER_DESC bufferDesc = + { + .ByteWidth = sizeof(struct ShaderConsts), + .Usage = D3D11_USAGE_DEFAULT, + .BindFlags = D3D11_BIND_CONSTANT_BUFFER, + }; + + status = ID3D11Device_CreateBuffer( + *this.device, &bufferDesc, NULL, + (ID3D11Buffer **)comRef_newGlobal(&this.buffer)); + + if (FAILED(status)) + { + DEBUG_WINERROR("Failed to create the constant buffer", status); + goto exit; + } + + updateConsts(); + DEBUG_INFO("SDR White Level : %f" , this.sdrWhiteLevel); + + result = true; + +exit: + comRef_scopePop(); + return result; +} + +static void sdrWhiteLevel_finish(void) +{ + memset(&this, 0, sizeof(this)); +} + +static bool sdrWhiteLevel_init( + void ** opaque, + int width, + int height, + bool shareable) +{ + SDRWhiteLevelInst * inst = (SDRWhiteLevelInst *)calloc(1, sizeof(*inst)); + if (!inst) + { + DEBUG_ERROR("Failed to allocate memory"); + return false; + } + + comRef_scopePush(); + + // create the output texture + D3D11_TEXTURE2D_DESC texDesc = + { + .Width = width, + .Height = height, + .MipLevels = 1, + .ArraySize = 1, + .SampleDesc.Count = 1, + .SampleDesc.Quality = 0, + .Usage = D3D11_USAGE_DEFAULT, + .Format = DXGI_FORMAT_R10G10B10A2_UNORM, + .BindFlags = D3D11_BIND_RENDER_TARGET | + D3D11_BIND_SHADER_RESOURCE, + .CPUAccessFlags = 0, + .MiscFlags = 0 + }; + + // allow texture sharing with other backends + if (shareable) + texDesc.MiscFlags |= + D3D11_RESOURCE_MISC_SHARED | + D3D11_RESOURCE_MISC_SHARED_NTHANDLE; + + comRef_defineLocal(ID3D11Texture2D, tex); + HRESULT status = ID3D11Device_CreateTexture2D( + *this.device, &texDesc, NULL, tex); + + if (FAILED(status)) + { + DEBUG_WINERROR("Failed to create the output texture", status); + goto fail; + } + + comRef_defineLocal(ID3D11RenderTargetView, target); + status = ID3D11Device_CreateRenderTargetView( + *this.device, *(ID3D11Resource **)tex, NULL, target); + + if (FAILED(status)) + { + DEBUG_WINERROR("Failed to create the render target view", status); + goto fail; + } + + *opaque = inst; + comRef_toGlobal(inst->tex , tex ); + comRef_toGlobal(inst->target, target); + + comRef_scopePop(); + return true; + +fail: + comRef_scopePop(); + free(inst); + return false; +} + +static void sdrWhiteLevel_free(void * opaque) +{ + SDRWhiteLevelInst * inst = (SDRWhiteLevelInst *)opaque; + comRef_release(inst->target); + comRef_release(inst->tex ); + free(inst); +} + +static void updateConsts(void) +{ + float nits = getSDRWhiteLevel(&this.displayPathInfo); + if (nits == this.sdrWhiteLevel) + return; + + this.sdrWhiteLevel = nits; + + struct ShaderConsts consts = { .sdrWhiteLevel = 80.0f / nits }; + ID3D11DeviceContext_UpdateSubresource( + *this.context, *(ID3D11Resource**)this.buffer, + 0, NULL, &consts, 0, 0); +} + +static ID3D11Texture2D * sdrWhiteLevel_run(void * opaque, ID3D11Texture2D * src) +{ + comRef_scopePush(); + ID3D11Texture2D * result = NULL; + SDRWhiteLevelInst * inst = (SDRWhiteLevelInst *)opaque; + HRESULT status; + + updateConsts(); + + // setup the pixel shader input resource view + comRef_defineLocal(ID3D11ShaderResourceView, inputSRV); + D3D11_TEXTURE2D_DESC desc; + ID3D11Texture2D_GetDesc(src, &desc); + + const D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = + { + .Format = desc.Format, + .ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D, + .Texture2D.MipLevels = 1 + }; + status = ID3D11Device_CreateShaderResourceView( + *this.device, (ID3D11Resource *)src, &srvDesc, inputSRV); + if (FAILED(status)) + { + DEBUG_WINERROR("Failed to create the source resource view", status); + goto exit; + } + + // set the vertex and pixel shader + ID3D11DeviceContext_VSSetShader(*this.context, *this.vshader, NULL, 0); + ID3D11DeviceContext_PSSetShader(*this.context, *this.pshader, NULL, 0); + + // set the pixel shader resources + ID3D11DeviceContext_PSSetShaderResources(*this.context, 0, 1, inputSRV ); + ID3D11DeviceContext_PSSetSamplers (*this.context, 0, 1, this.sampler); + ID3D11DeviceContext_PSSetConstantBuffers(*this.context, 0, 1, this.buffer ); + + // set the render target + ID3D11DeviceContext_OMSetRenderTargets(*this.context, 1, inst->target, NULL); + + ID3D11DeviceContext_IASetPrimitiveTopology( + *this.context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + + ID3D11DeviceContext_Draw(*this.context, 4, 0); + + result = *inst->tex; + +exit: + comRef_scopePop(); + return result; +} + +DXGIPostProcess DXGIPP_SDRWhiteLevel = +{ + .name = "SDRWhiteLevel", + .earlyInit = NULL, + .setup = sdrWhiteLevel_setup, + .init = sdrWhiteLevel_init, + .free = sdrWhiteLevel_free, + .run = sdrWhiteLevel_run, + .finish = sdrWhiteLevel_finish +};