diff --git a/features/IBL/Shaders/IBL/IBL.hlsli b/features/IBL/Shaders/IBL/IBL.hlsli index e5dcb0f787..26878c2e72 100644 --- a/features/IBL/Shaders/IBL/IBL.hlsli +++ b/features/IBL/Shaders/IBL/IBL.hlsli @@ -10,8 +10,8 @@ namespace ImageBasedLighting { #if defined(IBL_DEFERRED) - Texture2D EnvIBLTexture : register(t14); - Texture2D SkyIBLTexture : register(t15); + Texture2D EnvIBLTexture : register(t15); + Texture2D SkyIBLTexture : register(t16); #else Texture2D EnvIBLTexture : register(t76); Texture2D SkyIBLTexture : register(t77); diff --git a/package/Shaders/Common/GBuffer.hlsli b/package/Shaders/Common/GBuffer.hlsli index 8cca6bc7c0..5d308f5d09 100644 --- a/package/Shaders/Common/GBuffer.hlsli +++ b/package/Shaders/Common/GBuffer.hlsli @@ -1,40 +1,24 @@ #ifndef __GBUFFER_DEPENDENCY_HLSL__ #define __GBUFFER_DEPENDENCY_HLSL__ -// https://knarkowicz.wordpress.com/2014/04/16/octahedron-normal-vector-encoding/ - namespace GBuffer { - half2 OctWrap(half2 v) + float2 EncodeNormal(float3 n) { - return (1.0h - abs(v.yx)) * (v.xy >= 0.0h ? 1.0h : -1.0h); - } - - half2 EncodeNormal(half3 n) - { - n = -n; - n /= (abs(n.x) + abs(n.y) + abs(n.z)); - n.xy = n.z >= 0.0h ? n.xy : OctWrap(n.xy); - n.xy = n.xy * 0.5h + 0.5h; - return n.xy; - } - - half3 DecodeNormal(half2 f) - { - f = f * 2.0h - 1.0h; - // https://twitter.com/Stubbesaurus/status/937994790553227264 - half3 n = half3(f.x, f.y, 1.0h - abs(f.x) - abs(f.y)); - half t = saturate(-n.z); - n.xy += n.xy >= 0.0h ? -t : t; - return -normalize(n); + n.z = max(0.001, sqrt(8.0 - 8.0 * n.z)); + n.xy /= n.z; + return n.xy + 0.5; } - half2 EncodeNormalVanilla(half3 n) + float3 DecodeNormal(float2 enc) { - n.z = max(1.0h / 1000.0h, sqrt(8.0h + -8.0h * n.z)); - n.xy /= n.z; - return n.xy + 0.5h; + float2 fenc = enc * 4.0 - 2.0; + float f = dot(fenc, fenc); + float3 n; + n.xy = fenc * sqrt(1.0 - f / 4.0); + n.z = f / 2.0 - 1.0; + return n; } } diff --git a/package/Shaders/DeferredCompositeCS.hlsl b/package/Shaders/DeferredCompositePS.hlsl similarity index 80% rename from package/Shaders/DeferredCompositeCS.hlsl rename to package/Shaders/DeferredCompositePS.hlsl index e666f48a37..16cb52600e 100644 --- a/package/Shaders/DeferredCompositeCS.hlsl +++ b/package/Shaders/DeferredCompositePS.hlsl @@ -3,31 +3,34 @@ #include "Common/Color.hlsli" #include "Common/FrameBuffer.hlsli" #include "Common/GBuffer.hlsli" -#include "Common/MotionBlur.hlsli" #include "Common/Shading.hlsli" #include "Common/SharedData.hlsli" #include "Common/Spherical Harmonics/SphericalHarmonics.hlsli" #include "Common/VR.hlsli" -Texture2D SpecularTexture : register(t0); -Texture2D AlbedoTexture : register(t1); +Texture2D MainInputTexture : register(t0); +Texture2D SpecularTexture : register(t1); + +#if defined(SSGI) || defined(DYNAMIC_CUBEMAPS) || defined(DEBUG) Texture2D NormalRoughnessTexture : register(t2); -Texture2D MasksTexture : register(t3); +#endif -RWTexture2D MainRW : register(u0); -RWTexture2D NormalTAAMaskSpecularMaskRW : register(u1); -RWTexture2D MotionVectorsRW : register(u2); -Texture2D DepthTexture : register(t4); +#if defined(SSGI) || defined(DYNAMIC_CUBEMAPS) +Texture2D DepthTexture : register(t3); +#endif + +#if defined(SSGI) || defined(DEBUG) +Texture2D AlbedoTexture : register(t4); +#endif -#if defined(VR_STEREO_OPT) -# include "VRStereoOptimizations/modes.hlsli" -Texture2D StereoOptModeTexture : register(t16); +#if defined(SSGI) +Texture2D MasksTexture : register(t5); #endif #if defined(DYNAMIC_CUBEMAPS) -Texture2D ReflectanceTexture : register(t5); -TextureCube EnvTexture : register(t6); -TextureCube EnvReflectionsTexture : register(t7); +Texture2D ReflectanceTexture : register(t6); +TextureCube EnvTexture : register(t7); +TextureCube EnvReflectionsTexture : register(t8); SamplerState LinearSampler : register(s0); #endif @@ -35,16 +38,15 @@ SamplerState LinearSampler : register(s0); #if defined(SKYLIGHTING) # include "Skylighting/Skylighting.hlsli" -Texture3D SkylightingProbeArray : register(t8); -Texture2DArray stbn_vec3_2Dx1D_128x128x64 : register(t9); - +Texture3D SkylightingProbeArray : register(t9); +Texture2DArray stbn_vec3_2Dx1D_128x128x64 : register(t10); #endif #if defined(SSGI) -Texture2D SsgiAoTexture : register(t10); -Texture2D SsgiYTexture : register(t11); -Texture2D SsgiCoCgTexture : register(t12); -Texture2D SsgiSpecularTexture : register(t13); +Texture2D SsgiAoTexture : register(t11); +Texture2D SsgiYTexture : register(t12); +Texture2D SsgiCoCgTexture : register(t13); +Texture2D SsgiSpecularTexture : register(t14); void SampleSSGI(uint2 pixCoord, float3 normalWS, out float ao, out float3 il) { @@ -88,52 +90,54 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, inout float ao, out float3 il, # endif #endif -[numthreads(8, 8, 1)] void main(uint3 dispatchID : SV_DispatchThreadID) { - // Early exit if dispatch thread is outside screen bounds - if (any(dispatchID.xy >= uint2(SharedData::BufferDim.xy))) - return; +struct PS_INPUT +{ + float4 Position: SV_Position; + float2 TexCoord: TEXCOORD0; +}; + +struct PS_OUTPUT +{ + float4 Main: SV_Target0; + float4 NormalRoughness: SV_Target1; +}; - float2 uv = float2(dispatchID.xy + 0.5) * SharedData::BufferDim.zw; +PS_OUTPUT main(PS_INPUT input) +{ + uint2 pixCoord = uint2(input.Position.xy); + + float2 uv = float2(pixCoord + 0.5) * SharedData::BufferDim.zw; uv *= FrameBuffer::DynamicResolutionParams2.xy; // adjust for dynamic res uint eyeIndex = Stereo::GetEyeIndexFromTexCoord(uv); -#if defined(VR_STEREO_OPT) - if (eyeIndex == 1) { - uint mode = StereoOptModeTexture[uint2(dispatchID.xy)] & 0x0F; - if (mode == MODE_MAIN) { // stencil-culled in Eye 1, filled by ReprojectionCS - return; - } - } -#endif - uv = Stereo::ConvertFromStereoUV(uv, eyeIndex); - float3 normalGlossiness = NormalRoughnessTexture[dispatchID.xy]; - float3 normalVS = GBuffer::DecodeNormal(normalGlossiness.xy); + float3 diffuseColor = MainInputTexture[pixCoord].xyz; + float3 specularColor = SpecularTexture[pixCoord]; + float3 linDiffuseColor = Color::IrradianceToLinear(diffuseColor); - float3 diffuseColor = MainRW[dispatchID.xy].xyz; - float3 specularColor = SpecularTexture[dispatchID.xy]; - float3 albedo = AlbedoTexture[dispatchID.xy]; +#if defined(SSGI) || defined(DYNAMIC_CUBEMAPS) + float3 normalGlossiness = NormalRoughnessTexture[pixCoord]; + float3 normalVS = GBuffer::DecodeNormal(normalGlossiness.xy); + float3 normalWS = normalize(mul(FrameBuffer::CameraViewInverse[eyeIndex], float4(normalVS, 0)).xyz); - float depth = DepthTexture[dispatchID.xy]; + float depth = DepthTexture[pixCoord]; float4 positionWS = float4(2 * float2(uv.x, -uv.y + 1) - 1, depth, 1); positionWS = mul(FrameBuffer::CameraViewProjInverse[eyeIndex], positionWS); positionWS.xyz = positionWS.xyz / positionWS.w; +#endif - if (depth == 1.0) - MotionVectorsRW[dispatchID.xy] = MotionBlur::GetSSMotionVector(positionWS, positionWS, eyeIndex); // Apply sky motion vectors - +#if defined(DYNAMIC_CUBEMAPS) float glossiness = normalGlossiness.z; - - float3 linDiffuseColor = Color::IrradianceToLinear(diffuseColor); - float3 normalWS = normalize(mul(FrameBuffer::CameraViewInverse[eyeIndex], float4(normalVS, 0)).xyz); +#endif #if defined(SSGI) + float3 albedo = AlbedoTexture[pixCoord]; float ssgiAo; float3 ssgiIl; - SampleSSGI(dispatchID.xy, normalWS, ssgiAo, ssgiIl); + SampleSSGI(pixCoord, normalWS, ssgiAo, ssgiIl); float3 linAlbedo = Color::IrradianceToLinear(albedo / Color::PBRLightingScale); float3 multiBounceSSGIAo = MultiBounceAO(linAlbedo, ssgiAo); @@ -150,7 +154,7 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, inout float ao, out float3 il, # else float3 positionMS = positionWS.xyz; # endif - sh2 skylightingSH = Skylighting::sample(SharedData::skylightingSettings, SkylightingProbeArray, stbn_vec3_2Dx1D_128x128x64, dispatchID.xy, positionMS.xyz, normalWS); + sh2 skylightingSH = Skylighting::sample(SharedData::skylightingSettings, SkylightingProbeArray, stbn_vec3_2Dx1D_128x128x64, pixCoord, positionMS.xyz, normalWS); float skylightingDiffuse = SphericalHarmonics::FuncProductIntegral(skylightingSH, SphericalHarmonics::EvaluateCosineLobe(normalWS)) / Math::PI; skylightingDiffuse = saturate(skylightingDiffuse); skylightingDiffuse = Skylighting::mixDiffuse(SharedData::skylightingSettings, skylightingDiffuse); @@ -160,7 +164,7 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, inout float ao, out float3 il, # endif directionalAmbientColor = Color::RGBToYCoCg(directionalAmbientColor); - directionalAmbientColor.x = MasksTexture[dispatchID.xy].z; + directionalAmbientColor.x = MasksTexture[pixCoord].z; directionalAmbientColor = Color::YCoCgToRGB(directionalAmbientColor); directionalAmbientColor = max(0, directionalAmbientColor); } else @@ -170,7 +174,7 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, inout float ao, out float3 il, directionalAmbientColor *= albedo; directionalAmbientColor = Color::RGBToYCoCg(directionalAmbientColor); - directionalAmbientColor.x = MasksTexture[dispatchID.xy].z; + directionalAmbientColor.x = MasksTexture[pixCoord].z; directionalAmbientColor = Color::YCoCgToRGB(directionalAmbientColor); directionalAmbientColor = max(0, directionalAmbientColor); } @@ -200,7 +204,7 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, inout float ao, out float3 il, #if defined(DYNAMIC_CUBEMAPS) - float3 reflectance = ReflectanceTexture[dispatchID.xy]; + float3 reflectance = ReflectanceTexture[pixCoord]; if (any(reflectance > 0.0)) { float3 V = -normalize(positionWS.xyz); @@ -222,7 +226,7 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, inout float ao, out float3 il, float3 positionMS = positionWS.xyz; # endif - sh2 skylighting = Skylighting::sample(SharedData::skylightingSettings, SkylightingProbeArray, stbn_vec3_2Dx1D_128x128x64, dispatchID.xy, positionMS.xyz, R); + sh2 skylighting = Skylighting::sample(SharedData::skylightingSettings, SkylightingProbeArray, stbn_vec3_2Dx1D_128x128x64, pixCoord, positionMS.xyz, R); float skylightingSpecular = SphericalHarmonics::FuncProductIntegral(skylighting, specularLobe); skylightingSpecular = saturate(skylightingSpecular); @@ -295,7 +299,7 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, inout float ao, out float3 il, # if defined(SSGI) float3 ssgiIlSpecular; - SampleSSGISpecular(dispatchID.xy, specularLobe, ssgiAo, ssgiIlSpecular, normalWS, V, roughness); + SampleSSGISpecular(pixCoord, specularLobe, ssgiAo, ssgiIlSpecular, normalWS, V, roughness); finalIrradiance = (finalIrradiance * ssgiAo); @@ -314,9 +318,22 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, inout float ao, out float3 il, #if defined(DEBUG) +# if !defined(SSGI) && !defined(DYNAMIC_CUBEMAPS) + float3 normalGlossiness = NormalRoughnessTexture[pixCoord]; + float3 normalVS = GBuffer::DecodeNormal(normalGlossiness.xy); +# endif + +# if !defined(SSGI) + float3 albedo = AlbedoTexture[pixCoord]; +# endif + +# if !defined(DYNAMIC_CUBEMAPS) + float glossiness = normalGlossiness.z; +# endif + # if defined(VR) uv.x += (eyeIndex ? 0.1 : -0.1); -# endif // VR +# endif if (uv.x < 0.5 && uv.y < 0.5) { color = color; @@ -330,6 +347,8 @@ void SampleSSGISpecular(uint2 pixCoord, sh2 lobe, inout float ao, out float3 il, #endif - MainRW[dispatchID.xy] = float4(color, 1.0); - NormalTAAMaskSpecularMaskRW[dispatchID.xy] = float4(GBuffer::EncodeNormalVanilla(normalVS), 0.0, 0.0); -} \ No newline at end of file + PS_OUTPUT output; + output.Main = float4(color, 1.0); + output.NormalRoughness = 0; + return output; +} diff --git a/package/Shaders/DeferredCompositeVS.hlsl b/package/Shaders/DeferredCompositeVS.hlsl new file mode 100644 index 0000000000..0fbf098b89 --- /dev/null +++ b/package/Shaders/DeferredCompositeVS.hlsl @@ -0,0 +1,14 @@ +struct VS_OUTPUT +{ + float4 Position: SV_Position; + float2 TexCoord: TEXCOORD0; +}; + +VS_OUTPUT main(uint vertexID : SV_VertexID) +{ + VS_OUTPUT output; + float2 uv = float2((vertexID << 1) & 2, vertexID & 2); + output.Position = float4(uv * float2(2, -2) + float2(-1, 1), 1, 1); + output.TexCoord = uv; + return output; +} diff --git a/package/Shaders/Tests/TestGBuffer.hlsl b/package/Shaders/Tests/TestGBuffer.hlsl index abdfb8d587..df4294272c 100644 --- a/package/Shaders/Tests/TestGBuffer.hlsl +++ b/package/Shaders/Tests/TestGBuffer.hlsl @@ -1,111 +1,55 @@ // HLSL Unit Tests for Common/GBuffer.hlsli -// Note: GBuffer uses half types - we use half throughout to avoid conversion warnings #include "/Shaders/Common/GBuffer.hlsli" #include "/Test/STF/ShaderTestFramework.hlsli" /// @tags gbuffer, normal, encoding [numthreads(1, 1, 1)] void TestNormalEncodingRoundtrip() { - // Test that encoding and decoding normals is reversible - half3 testNormals[6] = { - half3(0.0h, 0.0h, 1.0h), // Up - half3(0.0h, 0.0h, -1.0h), // Down - half3(1.0h, 0.0h, 0.0h), // Right - half3(-1.0h, 0.0h, 0.0h), // Left - half3(0.0h, 1.0h, 0.0h), // Forward - half3(0.0h, -1.0h, 0.0h) // Back + float3 testNormals[5] = { + float3(0.3, 0.0, 1.0), // near +Z + float3(1.0, 0.0, 0.0), + float3(-1.0, 0.0, 0.0), + float3(0.0, 1.0, 0.0), + float3(0.0, -1.0, 0.0) }; - for (int i = 0; i < 6; i++) { - half3 original = normalize(testNormals[i]); - half2 encoded = GBuffer::EncodeNormal(original); - half3 decoded = GBuffer::DecodeNormal(encoded); + for (int i = 0; i < 5; i++) { + float3 original = normalize(testNormals[i]); + float2 encoded = GBuffer::EncodeNormal(original); + float3 decoded = GBuffer::DecodeNormal(encoded); - // Check that decoded normal is close to original - ASSERT(IsTrue, abs(decoded.x - original.x) < 0.01h); - ASSERT(IsTrue, abs(decoded.y - original.y) < 0.01h); - ASSERT(IsTrue, abs(decoded.z - original.z) < 0.01h); - - // Encoded values should be in [0, 1] range - ASSERT(IsTrue, encoded.x >= 0.0h && encoded.x <= 1.0h); - ASSERT(IsTrue, encoded.y >= 0.0h && encoded.y <= 1.0h); + ASSERT(IsTrue, abs(decoded.x - original.x) < 0.05); + ASSERT(IsTrue, abs(decoded.y - original.y) < 0.05); + ASSERT(IsTrue, abs(decoded.z - original.z) < 0.05); } } /// @tags gbuffer, normal, encoding [numthreads(1, 1, 1)] void TestNormalEncodingAngledNormals() { - // Test behavioral properties of octahedral encoding (not exact numerical accuracy) - // Half precision + quantization means we check: valid output, normalized, reasonable direction - half3 testNormals[4] = { - normalize(half3(1.0h, 1.0h, 1.0h)), - normalize(half3(-1.0h, 1.0h, 1.0h)), - normalize(half3(1.0h, -1.0h, 1.0h)), - normalize(half3(1.0h, 1.0h, -1.0h)) + float3 testNormals[4] = { + normalize(float3(1.0, 1.0, 1.0)), + normalize(float3(-1.0, 1.0, 1.0)), + normalize(float3(1.0, -1.0, 1.0)), + normalize(float3(1.0, 1.0, -1.0)) }; for (int i = 0; i < 4; i++) { - half3 original = testNormals[i]; - half2 encoded = GBuffer::EncodeNormal(original); - half3 decoded = GBuffer::DecodeNormal(encoded); - - // Check behavioral properties (relaxed for half precision quantization): - // 1. Encoded values are in valid range [0, 1] - ASSERT(IsTrue, encoded.x >= 0.0h && encoded.x <= 1.0h); - ASSERT(IsTrue, encoded.y >= 0.0h && encoded.y <= 1.0h); + float3 original = testNormals[i]; + float2 encoded = GBuffer::EncodeNormal(original); + float3 decoded = GBuffer::DecodeNormal(encoded); - // 2. Decoded normal is normalized (unit length) - half length = sqrt(decoded.x * decoded.x + decoded.y * decoded.y + decoded.z * decoded.z); - ASSERT(IsTrue, abs(length - 1.0h) < 0.02h); // Relaxed tolerance for half precision + float length = sqrt(decoded.x * decoded.x + decoded.y * decoded.y + decoded.z * decoded.z); + ASSERT(IsTrue, abs(length - 1.0) < 0.05); } } /// @tags gbuffer, normal, encoding -[numthreads(1, 1, 1)] void TestOctWrap() { - // Test behavioral properties of OctWrap (not exact numerical values) - // Half precision ternary operators have quantization, so check valid output ranges - - // Test 1: Positive inputs should produce outputs in valid range - half2 v1 = half2(0.5h, 0.5h); - half2 wrapped1 = GBuffer::OctWrap(v1); - ASSERT(IsTrue, wrapped1.x >= 0.0h && wrapped1.x <= 1.0h); - ASSERT(IsTrue, wrapped1.y >= 0.0h && wrapped1.y <= 1.0h); - - // Test 2: Negative inputs should produce outputs in valid range - half2 v2 = half2(-0.3h, 0.7h); - half2 wrapped2 = GBuffer::OctWrap(v2); - ASSERT(IsTrue, wrapped2.x >= -1.0h && wrapped2.x <= 1.0h); - ASSERT(IsTrue, wrapped2.y >= -1.0h && wrapped2.y <= 1.0h); - - // Test 3: Mixed signs should produce outputs in valid range - half2 v3 = half2(0.2h, -0.8h); - half2 wrapped3 = GBuffer::OctWrap(v3); - ASSERT(IsTrue, wrapped3.x >= -1.0h && wrapped3.x <= 1.0h); - ASSERT(IsTrue, wrapped3.y >= -1.0h && wrapped3.y <= 1.0h); -} - - /// @tags gbuffer, normal, encoding - [numthreads(1, 1, 1)] void TestVanillaNormalEncoding() -{ - // Test vanilla normal encoding with known normals - half3 upNormal = half3(0.0h, 0.0h, 1.0h); - half2 encoded = GBuffer::EncodeNormalVanilla(upNormal); - - // For up normal (0,0,1): z = sqrt(8 + -8*1) = sqrt(0) ≈ tiny value - // Result should be near (0.5, 0.5) due to the +0.5 offset - ASSERT(IsTrue, abs(encoded.x - 0.5h) < 0.2h); - ASSERT(IsTrue, abs(encoded.y - 0.5h) < 0.2h); - - // Test that encoding produces values in reasonable range - half3 testNormals[3] = { - normalize(half3(1.0h, 0.0h, 0.0h)), - normalize(half3(0.0h, 1.0h, 0.0h)), - normalize(half3(1.0h, 1.0h, 0.0h)) - }; - - for (int i = 0; i < 3; i++) { - half2 enc = GBuffer::EncodeNormalVanilla(testNormals[i]); - // Encoded values should be in a reasonable range (not infinite or NaN) - ASSERT(IsTrue, enc.x >= -10.0h && enc.x <= 10.0h); - ASSERT(IsTrue, enc.y >= -10.0h && enc.y <= 10.0h); - } +[numthreads(1, 1, 1)] void TestNormalEncodingEquator() { + float3 equatorNormal = float3(1.0, 0.0, 0.0); + float2 encoded = GBuffer::EncodeNormal(equatorNormal); + float3 decoded = GBuffer::DecodeNormal(encoded); + + ASSERT(IsTrue, abs(decoded.x - 1.0) < 0.01); + ASSERT(IsTrue, abs(decoded.y) < 0.01); + ASSERT(IsTrue, abs(decoded.z) < 0.01); } diff --git a/src/Deferred.cpp b/src/Deferred.cpp index 8f5433de4d..4749feeaae 100644 --- a/src/Deferred.cpp +++ b/src/Deferred.cpp @@ -17,6 +17,7 @@ #include "Features/WeatherEditor.h" #include "Hooks.h" +#include "Utils/D3DStateBackup.h" struct DepthStates { @@ -106,7 +107,7 @@ void Deferred::SetupResources() // Reflectance SetupRenderTarget(REFLECTANCE, texDesc, srvDesc, rtvDesc, uavDesc, DXGI_FORMAT_R11G11B10_FLOAT, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE); // Normal + Roughness - SetupRenderTarget(NORMALROUGHNESS, texDesc, srvDesc, rtvDesc, uavDesc, DXGI_FORMAT_R10G10B10A2_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE); + SetupRenderTarget(normalRoughnessRT, texDesc, srvDesc, rtvDesc, uavDesc, DXGI_FORMAT_R10G10B10A2_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE); // Masks SetupRenderTarget(MASKS, texDesc, srvDesc, rtvDesc, uavDesc, DXGI_FORMAT_R11G11B10_FLOAT, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE); @@ -133,25 +134,40 @@ void Deferred::SetupResources() } { - D3D11_TEXTURE2D_DESC texDesc; - auto mainTex = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN]; - mainTex.texture->GetDesc(&texDesc); - - texDesc.Format = DXGI_FORMAT_R11G11B10_FLOAT; - texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS; - - D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = { - .Format = texDesc.Format, - .ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D, - .Texture2D = { - .MostDetailedMip = 0, - .MipLevels = 1 } - }; - D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = { - .Format = texDesc.Format, - .ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D, - .Texture2D = { .MipSlice = 0 } - }; + auto device = globals::d3d::device; + + D3D11_BLEND_DESC blendDesc{}; + blendDesc.IndependentBlendEnable = TRUE; + blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + blendDesc.RenderTarget[1].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_BLUE; + DX::ThrowIfFailed(device->CreateBlendState(&blendDesc, compositeBlendState.put())); + + D3D11_DEPTH_STENCIL_DESC dsDesc{}; + dsDesc.DepthEnable = TRUE; + dsDesc.DepthFunc = D3D11_COMPARISON_GREATER; + dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + dsDesc.StencilEnable = FALSE; + DX::ThrowIfFailed(device->CreateDepthStencilState(&dsDesc, compositeDepthStencilState.put())); + + D3D11_DEPTH_STENCIL_DESC stencilDsDesc{}; + stencilDsDesc.DepthEnable = TRUE; + stencilDsDesc.DepthFunc = D3D11_COMPARISON_GREATER; + stencilDsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO; + stencilDsDesc.StencilEnable = TRUE; + stencilDsDesc.StencilReadMask = 0xFF; + stencilDsDesc.StencilWriteMask = 0x00; + stencilDsDesc.FrontFace.StencilFunc = D3D11_COMPARISON_NOT_EQUAL; + stencilDsDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + stencilDsDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; + stencilDsDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + stencilDsDesc.BackFace = stencilDsDesc.FrontFace; + DX::ThrowIfFailed(device->CreateDepthStencilState(&stencilDsDesc, compositeStencilDSState.put())); + + D3D11_RASTERIZER_DESC rsDesc{}; + rsDesc.FillMode = D3D11_FILL_SOLID; + rsDesc.CullMode = D3D11_CULL_NONE; + rsDesc.DepthClipEnable = FALSE; + DX::ThrowIfFailed(device->CreateRasterizerState(&rsDesc, compositeRasterizerState.put())); } } @@ -237,10 +253,12 @@ void Deferred::StartDeferred() forwardRenderTargets[i] = renderTargets[i]; } + normalRoughnessRT = forwardRenderTargets[2]; + RE::RENDER_TARGET targets[8]{ RE::RENDER_TARGET::kMAIN, RE::RENDER_TARGET::kMOTION_VECTOR, - NORMALROUGHNESS, + normalRoughnessRT, ALBEDO, SPECULAR, REFLECTANCE, @@ -273,9 +291,12 @@ void Deferred::StartDeferred() vrBuffer = *VRValues.get(); } if (vrBuffer) { + context->PSSetConstantBuffers(12, 1, buffers); + context->PSSetConstantBuffers(13, 1, &vrBuffer); context->CSSetConstantBuffers(12, 1, buffers); context->CSSetConstantBuffers(13, 1, &vrBuffer); } else { + context->PSSetConstantBuffers(12, 1, buffers); context->CSSetConstantBuffers(12, 1, buffers); } } @@ -299,34 +320,15 @@ void Deferred::DeferredPasses() auto renderer = globals::game::renderer; auto context = globals::d3d::context; - { - ID3D11Buffer* buffers[1] = { *globals::game::perFrame }; - ID3D11Buffer* vrBuffer = nullptr; - - if (REL::Module::IsVR()) { - static REL::Relocation VRValues{ REL::Offset(0x3180688) }; - vrBuffer = *VRValues.get(); - } - if (vrBuffer) { - context->CSSetConstantBuffers(12, 1, buffers); - context->CSSetConstantBuffers(13, 1, &vrBuffer); - } else { - context->CSSetConstantBuffers(12, 1, buffers); - } - } - auto specular = renderer->GetRuntimeData().renderTargets[SPECULAR]; auto albedo = renderer->GetRuntimeData().renderTargets[ALBEDO]; - auto normalRoughness = renderer->GetRuntimeData().renderTargets[NORMALROUGHNESS]; + auto normalRoughness = renderer->GetRuntimeData().renderTargets[normalRoughnessRT]; auto masks = renderer->GetRuntimeData().renderTargets[MASKS]; auto main = renderer->GetRuntimeData().renderTargets[forwardRenderTargets[0]]; - auto normals = renderer->GetRuntimeData().renderTargets[forwardRenderTargets[2]]; auto depth = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kMAIN]; auto reflectance = renderer->GetRuntimeData().renderTargets[REFLECTANCE]; - auto motionVectors = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMOTION_VECTOR]; - bool interior = Util::IsInterior(); auto& skylighting = globals::features::skylighting; @@ -337,8 +339,6 @@ void Deferred::DeferredPasses() auto [ssgi_ao, ssgi_y, ssgi_cocg, ssgi_gi_spec] = ssgi.GetOutputTextures(); bool ssgi_hq_spec = ssgi.settings.EnableExperimentalSpecularGI; - auto dispatchCount = Util::GetScreenDispatchCount(true); - auto& sss = globals::features::subsurfaceScattering; if (sss.loaded) sss.DrawSSS(); @@ -353,49 +353,83 @@ void Deferred::DeferredPasses() { TracyD3D11Zone(globals::state->tracyCtx, "Deferred Composite"); - ID3D11ShaderResourceView* srvs[16]{ - specular.SRV, - albedo.SRV, - normalRoughness.SRV, - masks.SRV, - dynamicCubemaps.loaded || REL::Module::IsVR() ? Util::GetCurrentSceneDepthSRV(true) : nullptr, - dynamicCubemaps.loaded ? reflectance.SRV : nullptr, - dynamicCubemaps.loaded ? dynamicCubemaps.envTexture->srv.get() : nullptr, - dynamicCubemaps.loaded ? dynamicCubemaps.envReflectionsTexture->srv.get() : nullptr, - dynamicCubemaps.loaded && skylighting.loaded ? skylighting.texProbeArray->srv.get() : nullptr, - dynamicCubemaps.loaded && skylighting.loaded ? skylighting.stbn_vec3_2Dx1D_128x128x64.get() : nullptr, - ssgi_ao, - ssgi_hq_spec ? nullptr : ssgi_y, - ssgi_hq_spec ? nullptr : ssgi_cocg, - ssgi_hq_spec ? ssgi_gi_spec : nullptr, - ibl.loaded ? ibl.envIBLTexture->srv.get() : nullptr, - ibl.loaded ? ibl.skyIBLTexture->srv.get() : nullptr, - }; - - if (dynamicCubemaps.loaded) - context->CSSetSamplers(0, 1, &linearSampler); - - context->CSSetShaderResources(0, ARRAYSIZE(srvs), srvs); - - // Bind VRStereoOptimizations mode texture for Eye 1 skip. - // Bind null when disabled so stale mode data doesn't cause incorrect early-exits - // in DeferredCompositeCS (null SRV reads return 0 = MODE_DISOCCLUDED, all pixels composite normally). - auto& vrStereoOpt = globals::features::vr.stereoOpt; - bool stereoCullingReady = globals::features::vr.IsStereoOptimizationCullingReady(); - ID3D11ShaderResourceView* modeSRV = stereoCullingReady ? vrStereoOpt.GetModeTextureSRV() : nullptr; - context->CSSetShaderResources(16, 1, &modeSRV); - - ID3D11UnorderedAccessView* uavs[3]{ main.UAV, normals.UAV, motionVectors.UAV }; - context->CSSetUnorderedAccessViews(0, ARRAYSIZE(uavs), uavs, nullptr); + Util::D3DStateBackup stateBackup; + stateBackup.Backup(context); + + auto& mainCopy = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN_COPY]; + auto normalRoughnessCopyRT = (normalRoughnessRT == RE::RENDER_TARGETS::kNORMAL_TAAMASK_SSRMASK) ? RE::RENDER_TARGETS::kNORMAL_TAAMASK_SSRMASK_SWAP : RE::RENDER_TARGETS::kNORMAL_TAAMASK_SSRMASK; + auto& normalRoughnessCopy = renderer->GetRuntimeData().renderTargets[normalRoughnessCopyRT]; + float2 resolution = Util::ConvertToDynamic(globals::state->screenSize); + D3D11_BOX srcBox = { 0, 0, 0, (UINT)resolution.x, (UINT)resolution.y, 1 }; + context->CopySubresourceRegion(mainCopy.texture, 0, 0, 0, 0, main.texture, 0, &srcBox); + context->CopySubresourceRegion(normalRoughnessCopy.texture, 0, 0, 0, 0, normalRoughness.texture, 0, &srcBox); + + // Constant buffers + { + ID3D11Buffer* buffers[1] = { *globals::game::perFrame }; + context->PSSetConstantBuffers(12, 1, buffers); + + if (REL::Module::IsVR()) { + static REL::Relocation VRValues{ REL::Offset(0x3180688) }; + ID3D11Buffer* vrBuffer = *VRValues.get(); + if (vrBuffer) + context->PSSetConstantBuffers(13, 1, &vrBuffer); + } + } - auto shader = interior ? GetComputeMainCompositeInterior() : GetComputeMainComposite(); - context->CSSetShader(shader, nullptr, 0); + // SRVs + ID3D11ShaderResourceView* srvs[17]{ + mainCopy.SRV, // t0 MainInputTexture + specular.SRV, // t1 SpecularTexture + normalRoughnessCopy.SRV, // t2 NormalRoughnessTexture + dynamicCubemaps.loaded || REL::Module::IsVR() ? Util::GetCurrentSceneDepthSRV(true) : nullptr, // t3 DepthTexture + albedo.SRV, // t4 AlbedoTexture + masks.SRV, // t5 MasksTexture + dynamicCubemaps.loaded ? reflectance.SRV : nullptr, // t6 ReflectanceTexture + dynamicCubemaps.loaded ? dynamicCubemaps.envTexture->srv.get() : nullptr, // t7 EnvTexture + dynamicCubemaps.loaded ? dynamicCubemaps.envReflectionsTexture->srv.get() : nullptr, // t8 EnvReflectionsTexture + dynamicCubemaps.loaded && skylighting.loaded ? skylighting.texProbeArray->srv.get() : nullptr, // t9 SkylightingProbeArray + dynamicCubemaps.loaded && skylighting.loaded ? skylighting.stbn_vec3_2Dx1D_128x128x64.get() : nullptr, // t10 stbn + ssgi_ao, // t11 SsgiAoTexture + ssgi_hq_spec ? nullptr : ssgi_y, // t12 SsgiYTexture + ssgi_hq_spec ? nullptr : ssgi_cocg, // t13 SsgiCoCgTexture + ssgi_hq_spec ? ssgi_gi_spec : nullptr, // t14 SsgiSpecularTexture + ibl.loaded ? ibl.envIBLTexture->srv.get() : nullptr, // t15 EnvIBLTexture + ibl.loaded ? ibl.skyIBLTexture->srv.get() : nullptr, // t16 SkyIBLTexture + }; - context->Dispatch(dispatchCount.x, dispatchCount.y, 1); + context->PSSetShaderResources(0, ARRAYSIZE(srvs), srvs); - // Unbind mode texture SRV - ID3D11ShaderResourceView* nullSRV = nullptr; - context->CSSetShaderResources(16, 1, &nullSRV); + if (dynamicCubemaps.loaded) + context->PSSetSamplers(0, 1, &linearSampler); + + // Render targets + stencil test for VR stereo culling + bool useStencil = globals::game::isVR && globals::features::vr.stereoOpt.IsStencilActive(); + ID3D11RenderTargetView* rtvs[2]{ main.RTV, normalRoughness.RTV }; + context->OMSetRenderTargets(ARRAYSIZE(rtvs), rtvs, depth.views[0]); + context->OMSetBlendState(compositeBlendState.get(), nullptr, 0xFFFFFFFF); + context->OMSetDepthStencilState(useStencil ? compositeStencilDSState.get() : compositeDepthStencilState.get(), 1); + + // Viewport + D3D11_VIEWPORT vp{}; + vp.Width = resolution.x; + vp.Height = resolution.y; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + context->RSSetViewports(1, &vp); + context->RSSetState(compositeRasterizerState.get()); + + // Shaders and draw + context->VSSetShader(GetCompositeVS(), nullptr, 0); + context->PSSetShader(GetCompositePS(interior), nullptr, 0); + context->GSSetShader(nullptr, nullptr, 0); + + context->IASetInputLayout(nullptr); + context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + context->Draw(3, 0); + + stateBackup.Restore(context); } // VR: Deactivate stencil culling now that geometry rendering is complete. @@ -413,20 +447,6 @@ void Deferred::DeferredPasses() globals::features::vr.DrawStereoBlend(); } - // Clear - { - ID3D11ShaderResourceView* views[16]{ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; - context->CSSetShaderResources(0, ARRAYSIZE(views), views); - - ID3D11UnorderedAccessView* uavs[3]{ nullptr, nullptr, nullptr }; - context->CSSetUnorderedAccessViews(0, ARRAYSIZE(uavs), uavs, nullptr); - - ID3D11Buffer* buffers[1] = { nullptr }; - context->CSSetConstantBuffers(12, 1, buffers); - - context->CSSetShader(nullptr, nullptr, 0); - } - if (dynamicCubemaps.loaded) dynamicCubemaps.PostDeferred(); } @@ -554,57 +574,48 @@ void Deferred::ResetBlendStates() void Deferred::ClearShaderCache() { - if (mainCompositeCS) { - mainCompositeCS->Release(); - mainCompositeCS = nullptr; + if (compositePS) { + compositePS->Release(); + compositePS = nullptr; + } + if (compositePSInterior) { + compositePSInterior->Release(); + compositePSInterior = nullptr; } - if (mainCompositeInteriorCS) { - mainCompositeInteriorCS->Release(); - mainCompositeInteriorCS = nullptr; + if (compositeVS) { + compositeVS->Release(); + compositeVS = nullptr; } } -ID3D11ComputeShader* Deferred::GetComputeMainComposite() +ID3D11VertexShader* Deferred::GetCompositeVS() { - if (!mainCompositeCS) { - logger::debug("Compiling DeferredCompositeCS"); + if (!compositeVS) { + logger::debug("Compiling DeferredCompositeVS"); std::vector> defines; - - if (globals::features::dynamicCubemaps.loaded) - defines.push_back({ "DYNAMIC_CUBEMAPS", nullptr }); - - if (globals::features::skylighting.loaded) - defines.push_back({ "SKYLIGHTING", nullptr }); - - if (globals::features::screenSpaceGI.loaded) - defines.push_back({ "SSGI", nullptr }); - - if (globals::features::ibl.loaded) - defines.push_back({ "IBL", nullptr }); - - if (REL::Module::IsVR()) - defines.push_back({ "FRAMEBUFFER", nullptr }); - - if (REL::Module::IsVR()) - defines.push_back({ "VR_STEREO_OPT", nullptr }); - - mainCompositeCS = static_cast(Util::CompileShader(L"Data\\Shaders\\DeferredCompositeCS.hlsl", defines, "cs_5_0")); + compositeVS = static_cast(Util::CompileShader(L"Data\\Shaders\\DeferredCompositeVS.hlsl", defines, "vs_5_0")); } - return mainCompositeCS; + return compositeVS; } -ID3D11ComputeShader* Deferred::GetComputeMainCompositeInterior() +ID3D11PixelShader* Deferred::GetCompositePS(bool interior) { - if (!mainCompositeInteriorCS) { - logger::debug("Compiling DeferredCompositeCS INTERIOR"); + auto& cached = interior ? compositePSInterior : compositePS; + if (!cached) { + logger::debug("Compiling DeferredCompositePS {}", interior ? "INTERIOR" : ""); std::vector> defines; - defines.push_back({ "INTERIOR", nullptr }); + + if (interior) + defines.push_back({ "INTERIOR", nullptr }); if (globals::features::dynamicCubemaps.loaded) defines.push_back({ "DYNAMIC_CUBEMAPS", nullptr }); + if (!interior && globals::features::skylighting.loaded) + defines.push_back({ "SKYLIGHTING", nullptr }); + if (globals::features::screenSpaceGI.loaded) defines.push_back({ "SSGI", nullptr }); @@ -614,12 +625,9 @@ ID3D11ComputeShader* Deferred::GetComputeMainCompositeInterior() if (REL::Module::IsVR()) defines.push_back({ "FRAMEBUFFER", nullptr }); - if (REL::Module::IsVR()) - defines.push_back({ "VR_STEREO_OPT", nullptr }); - - mainCompositeInteriorCS = static_cast(Util::CompileShader(L"Data\\Shaders\\DeferredCompositeCS.hlsl", defines, "cs_5_0")); + cached = static_cast(Util::CompileShader(L"Data\\Shaders\\DeferredCompositePS.hlsl", defines, "ps_5_0")); } - return mainCompositeInteriorCS; + return cached; } void Deferred::Hooks::Main_RenderShadowMaps::thunk() diff --git a/src/Deferred.h b/src/Deferred.h index 3adb455b76..e7cad8bfc5 100644 --- a/src/Deferred.h +++ b/src/Deferred.h @@ -1,11 +1,11 @@ #pragma once #include "Buffer.h" +#include #define ALBEDO RE::RENDER_TARGETS::kINDIRECT #define SPECULAR RE::RENDER_TARGETS::kINDIRECT_DOWNSCALED #define REFLECTANCE RE::RENDER_TARGETS::kRAWINDIRECT -#define NORMALROUGHNESS RE::RENDER_TARGETS::kRAWINDIRECT_DOWNSCALED #define MASKS RE::RENDER_TARGETS::kRAWINDIRECT_PREVIOUS #define MASKS2 RE::RENDER_TARGETS::kRAWINDIRECT_PREVIOUS_DOWNSCALED @@ -31,16 +31,24 @@ class Deferred void ClearShaderCache(); - ID3D11ComputeShader* GetComputeMainComposite(); - ID3D11ComputeShader* GetComputeMainCompositeInterior(); + ID3D11PixelShader* GetCompositePS(bool interior); + ID3D11VertexShader* GetCompositeVS(); ID3D11BlendState* deferredBlendStates[7][2][13][2]; ID3D11BlendState* forwardBlendStates[7][2][13][2]; RE::RENDER_TARGET forwardRenderTargets[4]; - ID3D11ComputeShader* mainCompositeCS = nullptr; - ID3D11ComputeShader* mainCompositeInteriorCS = nullptr; + ID3D11PixelShader* compositePS = nullptr; + ID3D11PixelShader* compositePSInterior = nullptr; + ID3D11VertexShader* compositeVS = nullptr; + + winrt::com_ptr compositeBlendState; + winrt::com_ptr compositeDepthStencilState; + winrt::com_ptr compositeStencilDSState; + winrt::com_ptr compositeRasterizerState; + + RE::RENDER_TARGET normalRoughnessRT = RE::RENDER_TARGETS::kNORMAL_TAAMASK_SSRMASK; bool deferredPass = false; diff --git a/src/Features/ScreenSpaceGI.cpp b/src/Features/ScreenSpaceGI.cpp index 09f8156dba..bd8ac299dd 100644 --- a/src/Features/ScreenSpaceGI.cpp +++ b/src/Features/ScreenSpaceGI.cpp @@ -773,7 +773,7 @@ void ScreenSpaceGI::DrawSSGI() resetViews(); srvs.at(0) = rts[deferred->forwardRenderTargets[0]].SRV; srvs.at(1) = texWorkingDepth->srv.get(); - srvs.at(2) = rts[NORMALROUGHNESS].SRV; + srvs.at(2) = rts[globals::deferred->normalRoughnessRT].SRV; srvs.at(3) = texPrevGeo->srv.get(); srvs.at(4) = rts[RE::RENDER_TARGET::kMOTION_VECTOR].SRV; srvs.at(5) = texAccumFrames[lastFrameAccumTexIdx]->srv.get(); @@ -826,7 +826,7 @@ void ScreenSpaceGI::DrawSSGI() resetViews(); srvs.at(0) = texWorkingDepth->srv.get(); - srvs.at(1) = rts[NORMALROUGHNESS].SRV; + srvs.at(1) = rts[globals::deferred->normalRoughnessRT].SRV; srvs.at(2) = texRadiance->srv.get(); srvs.at(3) = texNoise->srv.get(); srvs.at(4) = texAccumFrames[lastFrameAccumTexIdx]->srv.get(); @@ -858,7 +858,7 @@ void ScreenSpaceGI::DrawSSGI() resetViews(); srvs.at(0) = texWorkingDepth->srv.get(); - srvs.at(1) = rts[NORMALROUGHNESS].SRV; + srvs.at(1) = rts[globals::deferred->normalRoughnessRT].SRV; srvs.at(2) = texAccumFrames[lastFrameAccumTexIdx]->srv.get(); srvs.at(3) = texIlY[inputGITexIdx]->srv.get(); srvs.at(4) = texIlCoCg[inputGITexIdx]->srv.get(); diff --git a/src/Features/SubsurfaceScattering.cpp b/src/Features/SubsurfaceScattering.cpp index cfa1179070..1b14934e08 100644 --- a/src/Features/SubsurfaceScattering.cpp +++ b/src/Features/SubsurfaceScattering.cpp @@ -233,7 +233,7 @@ void SubsurfaceScattering::DrawSSS() auto mask = renderer->GetRuntimeData().renderTargets[MASKS]; auto albedo = renderer->GetRuntimeData().renderTargets[ALBEDO]; - auto normal = renderer->GetRuntimeData().renderTargets[NORMALROUGHNESS]; + auto normal = renderer->GetRuntimeData().renderTargets[globals::deferred->normalRoughnessRT]; ID3D11UnorderedAccessView* uav = blurHorizontalTemp->uav.get(); context->CSSetUnorderedAccessViews(0, 1, &uav, nullptr); diff --git a/src/Features/VRStereoOptimizations.h b/src/Features/VRStereoOptimizations.h index 4f324395ce..2fa3dfcddb 100644 --- a/src/Features/VRStereoOptimizations.h +++ b/src/Features/VRStereoOptimizations.h @@ -171,7 +171,7 @@ struct VRStereoOptimizations /// Deactivate stencil culling (called from Deferred after geometry rendering completes) void DeactivateStencil(); - /// Get mode texture SRV for external consumers (e.g., DeferredCompositeCS Eye 1 skip) + /// Get mode texture SRV for external consumers (e.g., DeferredCompositePS Eye 1 skip) ID3D11ShaderResourceView* GetModeTextureSRV() const { return texPerPixelMode ? texPerPixelMode->srv.get() : nullptr; } /// Get POM offset texture SRV for StereoBlendCS (reads per-pixel parallax depth offset) diff --git a/src/Utils/D3DStateBackup.h b/src/Utils/D3DStateBackup.h new file mode 100644 index 0000000000..7144ce265a --- /dev/null +++ b/src/Utils/D3DStateBackup.h @@ -0,0 +1,143 @@ +#pragma once + +#include + +namespace Util +{ + struct D3DStateBackup + { + static constexpr UINT kNumSRVSlots = 20; + static constexpr UINT kNumSamplerSlots = 2; + static constexpr UINT kNumCBSlots = 14; + + ID3D11InputLayout* iaInputLayout = nullptr; + D3D11_PRIMITIVE_TOPOLOGY iaTopology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; + + ID3D11VertexShader* vs = nullptr; + ID3D11Buffer* vsCBs[kNumCBSlots] = {}; + + ID3D11RasterizerState* rsState = nullptr; + UINT rsNumViewports = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; + D3D11_VIEWPORT rsViewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE] = {}; + + ID3D11PixelShader* ps = nullptr; + ID3D11ShaderResourceView* psSRVs[kNumSRVSlots] = {}; + ID3D11SamplerState* psSamplers[kNumSamplerSlots] = {}; + ID3D11Buffer* psCBs[kNumCBSlots] = {}; + + ID3D11RenderTargetView* omRTVs[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = {}; + ID3D11DepthStencilView* omDSV = nullptr; + ID3D11BlendState* omBlendState = nullptr; + FLOAT omBlendFactor[4] = {}; + UINT omSampleMask = 0; + ID3D11DepthStencilState* omDSState = nullptr; + UINT omStencilRef = 0; + + void Backup(ID3D11DeviceContext* context) + { + context->IAGetInputLayout(&iaInputLayout); + context->IAGetPrimitiveTopology(&iaTopology); + + context->VSGetShader(&vs, nullptr, nullptr); + context->VSGetConstantBuffers(0, kNumCBSlots, vsCBs); + + context->RSGetState(&rsState); + rsNumViewports = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; + context->RSGetViewports(&rsNumViewports, rsViewports); + + context->PSGetShader(&ps, nullptr, nullptr); + context->PSGetShaderResources(0, kNumSRVSlots, psSRVs); + context->PSGetSamplers(0, kNumSamplerSlots, psSamplers); + context->PSGetConstantBuffers(0, kNumCBSlots, psCBs); + + context->OMGetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, omRTVs, &omDSV); + context->OMGetBlendState(&omBlendState, omBlendFactor, &omSampleMask); + context->OMGetDepthStencilState(&omDSState, &omStencilRef); + } + + void Restore(ID3D11DeviceContext* context) + { + context->IASetInputLayout(iaInputLayout); + context->IASetPrimitiveTopology(iaTopology); + + context->VSSetShader(vs, nullptr, 0); + context->VSSetConstantBuffers(0, kNumCBSlots, vsCBs); + + context->RSSetState(rsState); + context->RSSetViewports(rsNumViewports, rsViewports); + + context->PSSetShader(ps, nullptr, 0); + context->PSSetShaderResources(0, kNumSRVSlots, psSRVs); + context->PSSetSamplers(0, kNumSamplerSlots, psSamplers); + context->PSSetConstantBuffers(0, kNumCBSlots, psCBs); + + context->OMSetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, omRTVs, omDSV); + context->OMSetBlendState(omBlendState, omBlendFactor, omSampleMask); + context->OMSetDepthStencilState(omDSState, omStencilRef); + + Release(); + } + + void Release() + { + if (iaInputLayout) { + iaInputLayout->Release(); + iaInputLayout = nullptr; + } + if (vs) { + vs->Release(); + vs = nullptr; + } + for (auto& cb : vsCBs) { + if (cb) { + cb->Release(); + cb = nullptr; + } + } + if (rsState) { + rsState->Release(); + rsState = nullptr; + } + if (ps) { + ps->Release(); + ps = nullptr; + } + for (auto& srv : psSRVs) { + if (srv) { + srv->Release(); + srv = nullptr; + } + } + for (auto& s : psSamplers) { + if (s) { + s->Release(); + s = nullptr; + } + } + for (auto& cb : psCBs) { + if (cb) { + cb->Release(); + cb = nullptr; + } + } + for (auto& rtv : omRTVs) { + if (rtv) { + rtv->Release(); + rtv = nullptr; + } + } + if (omDSV) { + omDSV->Release(); + omDSV = nullptr; + } + if (omBlendState) { + omBlendState->Release(); + omBlendState = nullptr; + } + if (omDSState) { + omDSState->Release(); + omDSState = nullptr; + } + } + }; +}