From d0f99a6d2825e1ad9b62bf3b837fde033673682b Mon Sep 17 00:00:00 2001 From: piebot Date: Sat, 12 Sep 2026 00:23:31 +0100 Subject: [PATCH] feat: gamma functionality on windows64 --- 4J_Render/Windows64/CompiledShaders.h | 3 +- 4J_Render/Windows64/PS_ScreenGamma.h | 201 +++++++++++++++++++++ 4J_Render/Windows64/Renderer.h | 9 +- 4J_Render/Windows64/RendererCore.cpp | 104 ++++++++++- 4J_Render/Windows64/shaders/build.bat | 3 +- 4J_Render/Windows64/shaders/screen_PS.hlsl | 13 ++ 6 files changed, 327 insertions(+), 6 deletions(-) create mode 100644 4J_Render/Windows64/PS_ScreenGamma.h diff --git a/4J_Render/Windows64/CompiledShaders.h b/4J_Render/Windows64/CompiledShaders.h index bd16be7..35d3601 100644 --- a/4J_Render/Windows64/CompiledShaders.h +++ b/4J_Render/Windows64/CompiledShaders.h @@ -36,4 +36,5 @@ SOFTWARE. #include "PS_TextureProjection.h" #include "PS_ForceLOD.h" #include "PS_ScreenSpace.h" -#include "PS_ScreenClear.h" \ No newline at end of file +#include "PS_ScreenClear.h" +#include "PS_ScreenGamma.h" \ No newline at end of file diff --git a/4J_Render/Windows64/PS_ScreenGamma.h b/4J_Render/Windows64/PS_ScreenGamma.h new file mode 100644 index 0000000..ef80566 --- /dev/null +++ b/4J_Render/Windows64/PS_ScreenGamma.h @@ -0,0 +1,201 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer gammaBuff +// { +// +// float4 gammaParams; // Offset: 0 Size: 16 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim HLSL Bind Count +// ------------------------------ ---------- ------- ----------- -------------- ------ +// screen_sampler_s sampler NA NA s0 1 +// screen_texture texture float4 2d t0 1 +// gammaBuff cbuffer NA NA cb6 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_TARGET 0 xyzw 0 TARGET float xyzw +// +ps_4_0 +dcl_constantbuffer CB6[1], immediateIndexed +dcl_sampler s0, mode_default +dcl_resource_texture2d (float,float,float,float) t0 +dcl_input_ps linear v1.xy +dcl_output o0.xyzw +dcl_temps 1 +sample r0.xyzw, v1.xyxx, t0.xyzw, s0 +mov_sat r0.xyz, r0.xyzx +mov o0.w, r0.w +log r0.xyz, r0.xyzx +mul r0.xyz, r0.xyzx, cb6[0].xxxx +exp o0.xyz, r0.xyzx +ret +// Approximately 7 instruction slots used +#endif + +const BYTE g_main_PS_ScreenGamma[] = +{ + 68, 88, 66, 67, 53, 209, + 160, 253, 213, 124, 231, 96, + 156, 17, 77, 217, 140, 64, + 219, 192, 1, 0, 0, 0, + 84, 3, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 88, 1, 0, 0, 176, 1, + 0, 0, 228, 1, 0, 0, + 216, 2, 0, 0, 82, 68, + 69, 70, 28, 1, 0, 0, + 1, 0, 0, 0, 168, 0, + 0, 0, 3, 0, 0, 0, + 28, 0, 0, 0, 0, 4, + 255, 255, 0, 129, 0, 0, + 244, 0, 0, 0, 124, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 1, 0, 0, 0, + 141, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 1, 0, 0, 0, 13, 0, + 0, 0, 156, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 115, 99, + 114, 101, 101, 110, 95, 115, + 97, 109, 112, 108, 101, 114, + 95, 115, 0, 115, 99, 114, + 101, 101, 110, 95, 116, 101, + 120, 116, 117, 114, 101, 0, + 103, 97, 109, 109, 97, 66, + 117, 102, 102, 0, 171, 171, + 156, 0, 0, 0, 1, 0, + 0, 0, 192, 0, 0, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 216, 0, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, + 2, 0, 0, 0, 228, 0, + 0, 0, 0, 0, 0, 0, + 103, 97, 109, 109, 97, 80, + 97, 114, 97, 109, 115, 0, + 1, 0, 3, 0, 1, 0, + 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 49, 48, 46, + 49, 0, 73, 83, 71, 78, + 80, 0, 0, 0, 2, 0, + 0, 0, 8, 0, 0, 0, + 56, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 68, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 3, 3, 0, 0, + 83, 86, 95, 80, 79, 83, + 73, 84, 73, 79, 78, 0, + 84, 69, 88, 67, 79, 79, + 82, 68, 0, 171, 171, 171, + 79, 83, 71, 78, 44, 0, + 0, 0, 1, 0, 0, 0, + 8, 0, 0, 0, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 83, 86, + 95, 84, 65, 82, 71, 69, + 84, 0, 171, 171, 83, 72, + 68, 82, 236, 0, 0, 0, + 64, 0, 0, 0, 59, 0, + 0, 0, 89, 0, 0, 4, + 70, 142, 32, 0, 6, 0, + 0, 0, 1, 0, 0, 0, + 90, 0, 0, 3, 0, 96, + 16, 0, 0, 0, 0, 0, + 88, 24, 0, 4, 0, 112, + 16, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 98, 16, + 0, 3, 50, 16, 16, 0, + 1, 0, 0, 0, 101, 0, + 0, 3, 242, 32, 16, 0, + 0, 0, 0, 0, 104, 0, + 0, 2, 1, 0, 0, 0, + 69, 0, 0, 9, 242, 0, + 16, 0, 0, 0, 0, 0, + 70, 16, 16, 0, 1, 0, + 0, 0, 70, 126, 16, 0, + 0, 0, 0, 0, 0, 96, + 16, 0, 0, 0, 0, 0, + 54, 32, 0, 5, 114, 0, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 54, 0, 0, 5, + 130, 32, 16, 0, 0, 0, + 0, 0, 58, 0, 16, 0, + 0, 0, 0, 0, 47, 0, + 0, 5, 114, 0, 16, 0, + 0, 0, 0, 0, 70, 2, + 16, 0, 0, 0, 0, 0, + 56, 0, 0, 8, 114, 0, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 6, 128, 32, 0, + 6, 0, 0, 0, 0, 0, + 0, 0, 25, 0, 0, 5, + 114, 32, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 0, 0, 0, 0, 62, 0, + 0, 1, 83, 84, 65, 84, + 116, 0, 0, 0, 7, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 +}; diff --git a/4J_Render/Windows64/Renderer.h b/4J_Render/Windows64/Renderer.h index 18afc05..6ef0266 100644 --- a/4J_Render/Windows64/Renderer.h +++ b/4J_Render/Windows64/Renderer.h @@ -61,6 +61,7 @@ public: int kScreenHeight; int kScreenWidth; + void SetGamma(float gamma); void UpdateGamma(unsigned short usGamma); void MatrixMode(int type); void MatrixSetIdentity(); @@ -415,6 +416,7 @@ public: ID3D11Buffer *m_alphaTestBuffer; ID3D11Buffer *m_clearColorBuffer; ID3D11Buffer *m_forcedLODBuffer; + ID3D11Buffer *m_gammaRenderBuffer; uint64_t dynamicVertexBase; DWORD dynamicVertexOffset; ID3D11Buffer *dynamicVertexBuffer; @@ -456,6 +458,7 @@ public: ID3D11PixelShader **pixelShaderTable; ID3D11PixelShader *screenSpacePixelShader; ID3D11PixelShader *screenClearPixelShader; + ID3D11PixelShader *screenGammaPixelShader; unsigned int *vertexStrideTable; ID3D11InputLayout **inputLayoutTable; ID3D11Buffer *quadIndexBuffer; @@ -492,7 +495,11 @@ public: std::unordered_map managedRasterizerStates; bool m_bShouldScreenGrabNextFrame; bool m_bSuspended; - + unsigned short m_usPreviousGamma; + float m_usGamma; + ID3D11SamplerState *m_gammaSamplerState; + ID3D11Buffer *m_gammaFullscreenBoundsBuffer; + // @Patoke add ID3D11Texture2D *m_backBufferTexture; }; diff --git a/4J_Render/Windows64/RendererCore.cpp b/4J_Render/Windows64/RendererCore.cpp index 859cc6b..c665cc3 100644 --- a/4J_Render/Windows64/RendererCore.cpp +++ b/4J_Render/Windows64/RendererCore.cpp @@ -99,6 +99,7 @@ Renderer::Context::Context(ID3D11Device* device, ID3D11DeviceContext* deviceCont , m_alphaTestBuffer(NULL) , m_clearColorBuffer(NULL) , m_forcedLODBuffer(NULL) + , m_gammaRenderBuffer(NULL) , dynamicVertexBase(0) , dynamicVertexOffset(0) , dynamicVertexBuffer(NULL) @@ -226,6 +227,10 @@ Renderer::Context::Context(ID3D11Device* device, ID3D11DeviceContext* deviceCont device->CreateBuffer(&cbDesc, &cbData, &m_clearColorBuffer); device->CreateBuffer(&cbDesc, &cbData, &m_forcedLODBuffer); + cbDesc.ByteWidth = sizeof(zero4); + cbData.pSysMem = zero4; + device->CreateBuffer(&cbDesc, &cbData, &m_gammaRenderBuffer); + deviceContext->VSSetConstantBuffers(0, 10, &m_modelViewMatrix); deviceContext->PSSetConstantBuffers(0, 6, &m_tintColorBuffer); @@ -411,7 +416,6 @@ void Renderer::CaptureThumbnail(ImageFileBuffer *pngOut) c.m_pDeviceContext->VSSetShader(screenSpaceVertexShader, NULL, 0); c.m_pDeviceContext->IASetInputLayout(NULL); - c.m_pDeviceContext->PSSetShader(screenSpacePixelShader, NULL, 0); c.m_pDeviceContext->OMSetBlendState(blendState, NULL, -1); c.m_pDeviceContext->OMSetDepthStencilState(depthState, 0); c.m_pDeviceContext->RSSetState(rasterizerState); @@ -836,7 +840,26 @@ void Renderer::Initialise(ID3D11Device *pDevice, IDXGISwapChain *pSwapChain) m_pDevice->CreateBuffer(&fanIndexDesc, &fanIndexData, &fanIndexBuffer); delete[] fanIndices; - InitializeCriticalSection(&Renderer::totalAllocCS); + D3D11_SAMPLER_DESC gammaSamplerDesc = {}; + gammaSamplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + gammaSamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + gammaSamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + gammaSamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; + gammaSamplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; + m_pDevice->CreateSamplerState(&gammaSamplerDesc, &m_gammaSamplerState); + + const float fullscreenBounds[4] = { 0.0f, 0.0f, 2.0f, 2.0f }; + D3D11_BUFFER_DESC boundsDesc = {}; + boundsDesc.ByteWidth = sizeof(fullscreenBounds); + boundsDesc.Usage = D3D11_USAGE_IMMUTABLE; + boundsDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + D3D11_SUBRESOURCE_DATA boundsData = { fullscreenBounds }; + m_pDevice->CreateBuffer(&boundsDesc, &boundsData, &m_gammaFullscreenBoundsBuffer); + + m_usPreviousGamma = 0xFFFF; + m_usGamma = 1.0f; + + InitializeCriticalSection(&Renderer::totalAllocCS); } ID3D11DeviceContext *Renderer::InitialiseContext(bool fromPresent) @@ -877,6 +900,70 @@ bool Renderer::IsWidescreen() return (aspectRatio >= 1.56f); } +// PIEBOT: Inspired by CaptureThumbnail +void Renderer::SetGamma(float gamma) +{ + Renderer::Context &c = getContext(); + + // @Patoke fix: bind render target to a proper backbuffer texture + ID3D11Resource *actualBackBuffer = NULL; + mainRenderTargetView->GetResource(&actualBackBuffer); + + // copy the backbuffer contents + c.m_pDeviceContext->CopyResource(m_backBufferTexture, actualBackBuffer); + actualBackBuffer->Release(); + + D3D11_MAPPED_SUBRESOURCE mapped = {}; + if (SUCCEEDED(c.m_pDeviceContext->Map(c.m_gammaRenderBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped))) + { + *reinterpret_cast(mapped.pData) = DirectX::XMFLOAT4(gamma, 0.0f, 0.0f, 0.0f); + c.m_pDeviceContext->Unmap(c.m_gammaRenderBuffer, 0); + } + + ID3D11BlendState *blendState = NULL; + ID3D11RasterizerState *rasterizerState = NULL; + + D3D11_BLEND_DESC blendDesc = {}; + blendDesc.RenderTarget[0].BlendEnable = false; + blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE; + blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO; + blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + m_pDevice->CreateBlendState(&blendDesc, &blendState); + + D3D11_RASTERIZER_DESC rasterDesc = {}; + rasterDesc.FillMode = D3D11_FILL_SOLID; + rasterDesc.CullMode = D3D11_CULL_NONE; + rasterDesc.DepthClipEnable = true; + rasterDesc.MultisampleEnable = true; + m_pDevice->CreateRasterizerState(&rasterDesc, &rasterizerState); + + c.m_pDeviceContext->VSSetShader(screenSpaceVertexShader, NULL, 0); + c.m_pDeviceContext->PSSetShader(screenGammaPixelShader, NULL, 0); + c.m_pDeviceContext->IASetInputLayout(NULL); + c.m_pDeviceContext->IASetVertexBuffers(0, 0, NULL, NULL, NULL); + c.m_pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + c.m_pDeviceContext->OMSetBlendState(blendState, NULL, 0xFFFFFFFF); + c.m_pDeviceContext->RSSetState(rasterizerState); + + blendState->Release(); + rasterizerState->Release(); + + D3D11_VIEWPORT viewport = { 0.0f, 0.0f, (float)backBufferWidth, (float)backBufferHeight, 0.0f, 1.0f }; + c.m_pDeviceContext->OMSetRenderTargets(1, &mainRenderTargetView, NULL); + c.m_pDeviceContext->RSSetViewports(1, &viewport); + + c.m_pDeviceContext->PSSetShaderResources(0, 1, &mainShaderResourceView); + c.m_pDeviceContext->PSSetSamplers(0, 1, &m_gammaSamplerState); + c.m_pDeviceContext->VSSetConstantBuffers(9, 1, &m_gammaFullscreenBoundsBuffer); + c.m_pDeviceContext->PSSetConstantBuffers(6, 1, &c.m_gammaRenderBuffer); + + c.m_pDeviceContext->Draw(4, 0); +} + void Renderer::Present() { PROFILER_SCOPE("Renderer::Present", "Present", MP_MAGENTA); @@ -948,6 +1035,8 @@ void Renderer::Present() m_bShouldScreenGrabNextFrame = false; } + SetGamma(m_usGamma); + m_pSwapChain->Present(C4JRender::GetVSync(), 0); ++presentCount; } @@ -993,6 +1082,7 @@ void Renderer::SetupShaders() m_pDevice->CreatePixelShader(g_main_PS_ForceLOD, sizeof(g_main_PS_ForceLOD), NULL, &pixelShaderTable[C4JRender::PIXEL_SHADER_TYPE_FORCELOD]); m_pDevice->CreatePixelShader(g_main_PS_ScreenSpace, sizeof(g_main_PS_ScreenSpace), NULL, &screenSpacePixelShader); m_pDevice->CreatePixelShader(g_main_PS_ScreenClear, sizeof(g_main_PS_ScreenClear), NULL, &screenClearPixelShader); + m_pDevice->CreatePixelShader(g_main_PS_ScreenGamma, sizeof(g_main_PS_ScreenGamma), NULL, &screenGammaPixelShader); m_pDevice->CreateInputLayout(g_vertex_PTN_Elements_PF3_TF2_CB4_NB4_XW1, 5, g_main_VS_PF3_TF2_CB4_NB4_XW1, sizeof(g_main_VS_PF3_TF2_CB4_NB4_XW1), &inputLayoutTable[C4JRender::VERTEX_TYPE_PF3_TF2_CB4_NB4_XW1]); m_pDevice->CreateInputLayout(g_vertex_PTN_Elements_Compressed, 2, g_main_VS_Compressed, sizeof(g_main_VS_Compressed), &inputLayoutTable[C4JRender::VERTEX_TYPE_COMPRESSED]); @@ -1053,7 +1143,15 @@ bool Renderer::Suspended() return m_bSuspended; } -void Renderer::UpdateGamma(unsigned short) {} +void Renderer::UpdateGamma(unsigned short usGamma) +{ + if (usGamma != m_usPreviousGamma) + { + m_usPreviousGamma = usGamma; + + m_usGamma = 1.0f / (0.5f + (usGamma / 32768.0f) * 1.5f); + } +} Renderer::Context &Renderer::getContext() { diff --git a/4J_Render/Windows64/shaders/build.bat b/4J_Render/Windows64/shaders/build.bat index 47a8733..7ab240a 100644 --- a/4J_Render/Windows64/shaders/build.bat +++ b/4J_Render/Windows64/shaders/build.bat @@ -21,4 +21,5 @@ if not exist %OUT% mkdir %OUT% %FXC% %OPT% /T ps_4_0 /E main /D TEXTURE_PROJECTION /Fh %OUT%/PS_TextureProjection.h /Vn g_main_PS_TextureProjection main_PS.hlsl %FXC% %OPT% /T ps_4_0 /E main /D FORCE_LOD /Fh %OUT%/PS_ForceLOD.h /Vn g_main_PS_ForceLOD main_PS.hlsl %FXC% %OPT% /T ps_4_0 /E PS_ScreenSpace /Fh %OUT%/PS_ScreenSpace.h /Vn g_main_PS_ScreenSpace screen_PS.hlsl -%FXC% %OPT% /T ps_4_0 /E PS_ScreenClear /Fh %OUT%/PS_ScreenClear.h /Vn g_main_PS_ScreenClear screen_PS.hlsl \ No newline at end of file +%FXC% %OPT% /T ps_4_0 /E PS_ScreenClear /Fh %OUT%/PS_ScreenClear.h /Vn g_main_PS_ScreenClear screen_PS.hlsl +%FXC% %OPT% /T ps_4_0 /E PS_ScreenGamma /Fh %OUT%/PS_ScreenGamma.h /Vn g_main_PS_ScreenGamma screen_PS.hlsl \ No newline at end of file diff --git a/4J_Render/Windows64/shaders/screen_PS.hlsl b/4J_Render/Windows64/shaders/screen_PS.hlsl index 376f487..2083020 100644 --- a/4J_Render/Windows64/shaders/screen_PS.hlsl +++ b/4J_Render/Windows64/shaders/screen_PS.hlsl @@ -26,6 +26,11 @@ cbuffer cbuff : register(b4) float4 clearColour; }; +cbuffer gammaBuff : register(b6) +{ + float4 gammaParams; +}; + SamplerState screen_sampler_s : register(s0); Texture2D screen_texture : register(t0); @@ -37,4 +42,12 @@ float4 PS_ScreenSpace(float4 position : SV_POSITION, float2 texcoord : TEXCOORD0 float4 PS_ScreenClear(float4 position : SV_POSITION) : SV_TARGET { return clearColour; +} + +float4 PS_ScreenGamma(float4 position : SV_POSITION, float2 texcoord : TEXCOORD0) : SV_TARGET +{ + float4 colour = screen_texture.Sample(screen_sampler_s, texcoord); + float invGamma = gammaParams.x; + colour.rgb = pow(saturate(colour.rgb), invGamma); + return colour; } \ No newline at end of file