diff --git a/Extension/UI/Overlay/overlay_render.cpp b/Extension/UI/Overlay/overlay_render.cpp index 9631d0d4..7169615c 100644 --- a/Extension/UI/Overlay/overlay_render.cpp +++ b/Extension/UI/Overlay/overlay_render.cpp @@ -401,6 +401,9 @@ bool setup_graphics() { // Font upload uses the exact swapchain queue, and this pinned backend waits // for its own upload fence before returning from device-object creation. const bool objects = s.dx12_ready && ImGui_ImplDX12_CreateDeviceObjects(); + if (objects && !ImGui_ImplDX12_HasOutputTransfer()) + dingosdk::logging::write(dingosdk::logging::Level::warning, dingosdk::logging::Channel::graphics, + "Overlay output: this system could not build the HDR-aware shader; drawing in SDR, so an HDR game shows the overlay off-colour."); ImGui::SetCurrentContext(previous); if (!objects) { dingosdk::logging::printf(dingosdk::logging::Level::error, dingosdk::logging::Channel::graphics, "Graphics setup failed in ImGui initialization: win32=%d dx12=%d objects=%d", diff --git a/External/imgui/backends/imgui_impl_dx12.cpp b/External/imgui/backends/imgui_impl_dx12.cpp index 63d8b474..c6eae106 100644 --- a/External/imgui/backends/imgui_impl_dx12.cpp +++ b/External/imgui/backends/imgui_impl_dx12.cpp @@ -121,6 +121,11 @@ void ImGui_ImplDX12_SetOutputTransfer(int mode, float white_nits) g_ReSkateOutputTransfer[0] = (float)mode; g_ReSkateOutputTransfer[1] = white_nits; } +static bool g_ReSkateOutputTransferAvailable = false; +bool ImGui_ImplDX12_HasOutputTransfer() +{ + return g_ReSkateOutputTransferAvailable; +} // Functions static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* command_list, ImGui_ImplDX12_RenderBuffers* fr) @@ -536,7 +541,6 @@ bool ImGui_ImplDX12_CreateDeviceObjects() psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE; ID3DBlob* vertexShaderBlob; - ID3DBlob* pixelShaderBlob; // Create the vertex shader { @@ -582,55 +586,6 @@ bool ImGui_ImplDX12_CreateDeviceObjects() psoDesc.InputLayout = { local_layout, 3 }; } - // Create the pixel shader - { - static const char* pixelShader = - "struct PS_INPUT\ - {\ - float4 pos : SV_POSITION;\ - float4 col : COLOR0;\ - float2 uv : TEXCOORD0;\ - };\ - cbuffer vertexBuffer : register(b0) \ - {\ - float4x4 ProjectionMatrix; \ - float4 OutputTransfer; \ - };\ - SamplerState sampler0 : register(s0);\ - Texture2D texture0 : register(t0);\ - \ - float3 srgb_to_linear(float3 c)\ - {\ - return lerp(c / 12.92, pow(abs((c + 0.055) / 1.055), 2.4), step(0.04045, c)); \ - }\ - float4 main(PS_INPUT input) : SV_Target\ - {\ - float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \ - if (OutputTransfer.x > 0.5) \ - {\ - float3 lin = srgb_to_linear(saturate(out_col.rgb)); \ - if (OutputTransfer.x < 1.5) \ - {\ - out_col.rgb = lin * (OutputTransfer.y / 80.0); \ - }\ - else \ - {\ - float3 wide = float3(dot(lin, float3(0.6274, 0.3293, 0.0433)), dot(lin, float3(0.0691, 0.9195, 0.0114)), dot(lin, float3(0.0164, 0.0880, 0.8956))); \ - float3 y = pow(saturate(wide * (OutputTransfer.y / 10000.0)), 0.1593017578125); \ - out_col.rgb = pow((0.8359375 + 18.8515625 * y) / (1.0 + 18.6875 * y), 78.84375); \ - }\ - }\ - return out_col; \ - }"; - - if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), nullptr, nullptr, nullptr, "main", "ps_5_0", 0, 0, &pixelShaderBlob, nullptr))) - { - vertexShaderBlob->Release(); - return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - } - psoDesc.PS = { pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize() }; - } - // Create the blending setup { D3D12_BLEND_DESC& desc = psoDesc.BlendState; @@ -673,10 +628,80 @@ bool ImGui_ImplDX12_CreateDeviceObjects() desc.BackFace = desc.FrontFace; } - HRESULT result_pipeline_state = bd->pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineState)); + // Create the pixel shader and the pipeline + // [ReSkate] The HDR-aware shader first, then ImGui's stock one. Some D3DCompile()s cannot build the HDR one (Wine's + // builtin d3dcompiler, as under Apple's Game Porting Toolkit), and an SDR-only window beats no window at all. + { + static const char* pixelShaders[] = + { + "struct PS_INPUT\ + {\ + float4 pos : SV_POSITION;\ + float4 col : COLOR0;\ + float2 uv : TEXCOORD0;\ + };\ + cbuffer vertexBuffer : register(b0) \ + {\ + float4x4 ProjectionMatrix; \ + float4 OutputTransfer; \ + };\ + SamplerState sampler0 : register(s0);\ + Texture2D texture0 : register(t0);\ + \ + float3 srgb_to_linear(float3 c)\ + {\ + return lerp(c / 12.92, pow(abs((c + 0.055) / 1.055), 2.4), step(0.04045, c)); \ + }\ + float4 main(PS_INPUT input) : SV_Target\ + {\ + float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \ + if (OutputTransfer.x > 0.5) \ + {\ + float3 lin = srgb_to_linear(saturate(out_col.rgb)); \ + if (OutputTransfer.x < 1.5) \ + {\ + out_col.rgb = lin * (OutputTransfer.y / 80.0); \ + }\ + else \ + {\ + float3 wide = float3(dot(lin, float3(0.6274, 0.3293, 0.0433)), dot(lin, float3(0.0691, 0.9195, 0.0114)), dot(lin, float3(0.0164, 0.0880, 0.8956))); \ + float3 y = pow(saturate(wide * (OutputTransfer.y / 10000.0)), 0.1593017578125); \ + out_col.rgb = pow((0.8359375 + 18.8515625 * y) / (1.0 + 18.6875 * y), 78.84375); \ + }\ + }\ + return out_col; \ + }", + "struct PS_INPUT\ + {\ + float4 pos : SV_POSITION;\ + float4 col : COLOR0;\ + float2 uv : TEXCOORD0;\ + };\ + SamplerState sampler0 : register(s0);\ + Texture2D texture0 : register(t0);\ + \ + float4 main(PS_INPUT input) : SV_Target\ + {\ + float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \ + return out_col; \ + }", + }; + + g_ReSkateOutputTransferAvailable = false; + for (int i = 0; i < IM_ARRAYSIZE(pixelShaders) && bd->pPipelineState == nullptr; i++) + { + ID3DBlob* pixelShaderBlob = nullptr; + if (FAILED(D3DCompile(pixelShaders[i], strlen(pixelShaders[i]), nullptr, nullptr, nullptr, "main", "ps_5_0", 0, 0, &pixelShaderBlob, nullptr))) + continue; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! + psoDesc.PS = { pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize() }; + if (bd->pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineState)) != S_OK) + bd->pPipelineState = nullptr; + pixelShaderBlob->Release(); + g_ReSkateOutputTransferAvailable = bd->pPipelineState != nullptr && i == 0; + } + } vertexShaderBlob->Release(); - pixelShaderBlob->Release(); - if (result_pipeline_state != S_OK) + if (bd->pPipelineState == nullptr) return false; return ImGui_ImplDX12_CreateFontsTexture(); diff --git a/External/imgui/backends/imgui_impl_dx12.h b/External/imgui/backends/imgui_impl_dx12.h index 0a2c7bd1..129072bc 100644 --- a/External/imgui/backends/imgui_impl_dx12.h +++ b/External/imgui/backends/imgui_impl_dx12.h @@ -56,6 +56,9 @@ IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3 // primaries with the ST.2084 (PQ) curve (a 10-bit HDR target). `white_nits`: how bright white // is drawn in 1 and 2. Read at each ImGui_ImplDX12_RenderDrawData. IMGUI_IMPL_API void ImGui_ImplDX12_SetOutputTransfer(int mode, float white_nits); +// [ReSkate] Whether the pipeline made by the last ImGui_ImplDX12_CreateDeviceObjects() applies that transfer. False when +// D3DCompile() or the driver could not build the HDR-aware shader and ImGui's stock (SDR) one is drawing instead. +IMGUI_IMPL_API bool ImGui_ImplDX12_HasOutputTransfer(); #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Legacy initialization API Obsoleted in 1.91.5 diff --git a/Launcher/gui_renderer.cpp b/Launcher/gui_renderer.cpp index e3214cc2..3505160f 100644 --- a/Launcher/gui_renderer.cpp +++ b/Launcher/gui_renderer.cpp @@ -127,6 +127,9 @@ bool Renderer::init(HWND window) { "Launcher renderer: this driver cannot create the Direct3D 12 pipeline the window draws with."); return false; } + if (!ImGui_ImplDX12_HasOutputTransfer()) + logging::write(logging::Level::info, logging::Channel::launcher, + "Launcher renderer: drawing with ImGui's stock shader; this system could not build the HDR-aware one."); return true; }