diff --git a/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.glsl b/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.glsl index f31a2210..2758ee64 100644 --- a/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.glsl +++ b/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.glsl @@ -1,6 +1,7 @@ // XC_BUILTIN_FORWARD_LIT_OPENGL_PS #version 430 layout(binding = 1) uniform sampler2D uBaseColorTexture; +layout(binding = 2) uniform sampler2D uShadowMapTexture; layout(std140, binding = 1) uniform PerObjectConstants { mat4 gProjectionMatrix; @@ -15,11 +16,44 @@ layout(std140, binding = 2) uniform MaterialConstants { vec4 gBaseColorFactor; }; +layout(std140, binding = 3) uniform ShadowReceiverConstants { + mat4 gWorldToShadowMatrix; + vec4 gShadowBiasAndTexelSize; + vec4 gShadowOptions; +}; + in vec3 vNormalWS; in vec2 vTexCoord; +in vec3 vPositionWS; layout(location = 0) out vec4 fragColor; +float ComputeShadowAttenuation(vec3 positionWS) { + if (gShadowOptions.x < 0.5) { + return 1.0; + } + + vec4 shadowClip = gWorldToShadowMatrix * vec4(positionWS, 1.0); + if (shadowClip.w <= 0.0) { + return 1.0; + } + + vec3 shadowNdc = shadowClip.xyz / shadowClip.w; + vec2 shadowUv = vec2( + shadowNdc.x * 0.5 + 0.5, + shadowNdc.y * -0.5 + 0.5); + if (shadowUv.x < 0.0 || shadowUv.x > 1.0 || + shadowUv.y < 0.0 || shadowUv.y > 1.0 || + shadowNdc.z < 0.0 || shadowNdc.z > 1.0) { + return 1.0; + } + + float shadowDepth = texture(uShadowMapTexture, shadowUv).r; + float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x; + float shadowStrength = clamp(gShadowBiasAndTexelSize.w, 0.0, 1.0); + return receiverDepth <= shadowDepth ? 1.0 : (1.0 - shadowStrength); +} + void main() { vec4 baseColor = texture(uBaseColorTexture, vTexCoord) * gBaseColorFactor; if (gMainLightColorAndFlags.w < 0.5) { @@ -30,7 +64,10 @@ void main() { vec3 normalWS = normalize(vNormalWS); vec3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz); float diffuse = max(dot(normalWS, directionToLightWS), 0.0); + float shadowAttenuation = diffuse > 0.0 + ? ComputeShadowAttenuation(vPositionWS) + : 1.0; vec3 lighting = vec3(0.28) + - gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w); + gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation); fragColor = vec4(baseColor.rgb * lighting, baseColor.a); } diff --git a/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.vk.glsl b/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.vk.glsl index 88d41435..4f30a511 100644 --- a/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.vk.glsl +++ b/engine/assets/builtin/shaders/forward-lit/forward-lit.frag.vk.glsl @@ -2,6 +2,8 @@ #version 450 layout(set = 3, binding = 0) uniform texture2D uBaseColorTexture; layout(set = 4, binding = 0) uniform sampler uLinearSampler; +layout(set = 6, binding = 0) uniform texture2D uShadowMapTexture; +layout(set = 7, binding = 0) uniform sampler uShadowMapSampler; layout(set = 1, binding = 0, std140) uniform PerObjectConstants { mat4 gProjectionMatrix; @@ -16,11 +18,44 @@ layout(set = 2, binding = 0, std140) uniform MaterialConstants { vec4 gBaseColorFactor; }; +layout(set = 5, binding = 0, std140) uniform ShadowReceiverConstants { + mat4 gWorldToShadowMatrix; + vec4 gShadowBiasAndTexelSize; + vec4 gShadowOptions; +}; + layout(location = 0) in vec3 vNormalWS; layout(location = 1) in vec2 vTexCoord; +layout(location = 2) in vec3 vPositionWS; layout(location = 0) out vec4 fragColor; +float ComputeShadowAttenuation(vec3 positionWS) { + if (gShadowOptions.x < 0.5) { + return 1.0; + } + + vec4 shadowClip = gWorldToShadowMatrix * vec4(positionWS, 1.0); + if (shadowClip.w <= 0.0) { + return 1.0; + } + + vec3 shadowNdc = shadowClip.xyz / shadowClip.w; + vec2 shadowUv = vec2( + shadowNdc.x * 0.5 + 0.5, + shadowNdc.y * -0.5 + 0.5); + if (shadowUv.x < 0.0 || shadowUv.x > 1.0 || + shadowUv.y < 0.0 || shadowUv.y > 1.0 || + shadowNdc.z < 0.0 || shadowNdc.z > 1.0) { + return 1.0; + } + + float shadowDepth = texture(sampler2D(uShadowMapTexture, uShadowMapSampler), shadowUv).r; + float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x; + float shadowStrength = clamp(gShadowBiasAndTexelSize.w, 0.0, 1.0); + return receiverDepth <= shadowDepth ? 1.0 : (1.0 - shadowStrength); +} + void main() { vec4 baseColor = texture(sampler2D(uBaseColorTexture, uLinearSampler), vTexCoord) * gBaseColorFactor; if (gMainLightColorAndFlags.w < 0.5) { @@ -31,7 +66,10 @@ void main() { vec3 normalWS = normalize(vNormalWS); vec3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz); float diffuse = max(dot(normalWS, directionToLightWS), 0.0); + float shadowAttenuation = diffuse > 0.0 + ? ComputeShadowAttenuation(vPositionWS) + : 1.0; vec3 lighting = vec3(0.28) + - gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w); + gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation); fragColor = vec4(baseColor.rgb * lighting, baseColor.a); } diff --git a/engine/assets/builtin/shaders/forward-lit/forward-lit.ps.hlsl b/engine/assets/builtin/shaders/forward-lit/forward-lit.ps.hlsl index aa16ea10..6e7f7a06 100644 --- a/engine/assets/builtin/shaders/forward-lit/forward-lit.ps.hlsl +++ b/engine/assets/builtin/shaders/forward-lit/forward-lit.ps.hlsl @@ -1,6 +1,8 @@ // XC_BUILTIN_FORWARD_LIT_D3D12_PS Texture2D gBaseColorTexture : register(t1); SamplerState gLinearSampler : register(s1); +Texture2D gShadowMapTexture : register(t2); +SamplerState gShadowMapSampler : register(s2); cbuffer PerObjectConstants : register(b1) { float4x4 gProjectionMatrix; @@ -15,12 +17,45 @@ cbuffer MaterialConstants : register(b2) { float4 gBaseColorFactor; }; +cbuffer ShadowReceiverConstants : register(b3) { + float4x4 gWorldToShadowMatrix; + float4 gShadowBiasAndTexelSize; + float4 gShadowOptions; +}; + struct PSInput { float4 position : SV_POSITION; float3 normalWS : TEXCOORD0; float2 texcoord : TEXCOORD1; + float3 positionWS : TEXCOORD2; }; +float ComputeShadowAttenuation(float3 positionWS) { + if (gShadowOptions.x < 0.5f) { + return 1.0f; + } + + float4 shadowClip = mul(gWorldToShadowMatrix, float4(positionWS, 1.0f)); + if (shadowClip.w <= 0.0f) { + return 1.0f; + } + + float3 shadowNdc = shadowClip.xyz / shadowClip.w; + float2 shadowUv = float2( + shadowNdc.x * 0.5f + 0.5f, + shadowNdc.y * -0.5f + 0.5f); + if (shadowUv.x < 0.0f || shadowUv.x > 1.0f || + shadowUv.y < 0.0f || shadowUv.y > 1.0f || + shadowNdc.z < 0.0f || shadowNdc.z > 1.0f) { + return 1.0f; + } + + const float shadowDepth = gShadowMapTexture.Sample(gShadowMapSampler, shadowUv).r; + const float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x; + const float shadowStrength = saturate(gShadowBiasAndTexelSize.w); + return receiverDepth <= shadowDepth ? 1.0f : (1.0f - shadowStrength); +} + float4 MainPS(PSInput input) : SV_TARGET { float4 baseColor = gBaseColorTexture.Sample(gLinearSampler, input.texcoord) * gBaseColorFactor; if (gMainLightColorAndFlags.a < 0.5f) { @@ -30,7 +65,10 @@ float4 MainPS(PSInput input) : SV_TARGET { float3 normalWS = normalize(input.normalWS); float3 directionToLightWS = normalize(gMainLightDirectionAndIntensity.xyz); float diffuse = saturate(dot(normalWS, directionToLightWS)); + float shadowAttenuation = diffuse > 0.0f + ? ComputeShadowAttenuation(input.positionWS) + : 1.0f; float3 lighting = float3(0.28f, 0.28f, 0.28f) + - gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w); + gMainLightColorAndFlags.rgb * (diffuse * gMainLightDirectionAndIntensity.w * shadowAttenuation); return float4(baseColor.rgb * lighting, baseColor.a); } diff --git a/engine/assets/builtin/shaders/forward-lit/forward-lit.shader b/engine/assets/builtin/shaders/forward-lit/forward-lit.shader index bfae040e..b778fb0a 100644 --- a/engine/assets/builtin/shaders/forward-lit/forward-lit.shader +++ b/engine/assets/builtin/shaders/forward-lit/forward-lit.shader @@ -17,6 +17,9 @@ Shader "Builtin Forward Lit" MaterialConstants (ConstantBuffer, 2, 0) [Semantic(Material)] BaseColorTexture (Texture2D, 3, 0) [Semantic(BaseColorTexture)] LinearClampSampler (Sampler, 4, 0) [Semantic(LinearClampSampler)] + ShadowReceiverConstants (ConstantBuffer, 5, 0) [Semantic(ShadowReceiver)] + ShadowMapTexture (Texture2D, 6, 0) [Semantic(ShadowMapTexture)] + ShadowMapSampler (Sampler, 7, 0) [Semantic(ShadowMapSampler)] } HLSLPROGRAM #pragma vertex MainVS diff --git a/engine/assets/builtin/shaders/forward-lit/forward-lit.vert.glsl b/engine/assets/builtin/shaders/forward-lit/forward-lit.vert.glsl index e24c6e6c..f741ef8e 100644 --- a/engine/assets/builtin/shaders/forward-lit/forward-lit.vert.glsl +++ b/engine/assets/builtin/shaders/forward-lit/forward-lit.vert.glsl @@ -15,6 +15,7 @@ layout(std140, binding = 1) uniform PerObjectConstants { out vec3 vNormalWS; out vec2 vTexCoord; +out vec3 vPositionWS; void main() { vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0); @@ -22,4 +23,5 @@ void main() { gl_Position = gProjectionMatrix * positionVS; vNormalWS = mat3(gNormalMatrix) * aNormal; vTexCoord = aTexCoord; + vPositionWS = positionWS.xyz; } diff --git a/engine/assets/builtin/shaders/forward-lit/forward-lit.vert.vk.glsl b/engine/assets/builtin/shaders/forward-lit/forward-lit.vert.vk.glsl index 0a2dab29..273fd458 100644 --- a/engine/assets/builtin/shaders/forward-lit/forward-lit.vert.vk.glsl +++ b/engine/assets/builtin/shaders/forward-lit/forward-lit.vert.vk.glsl @@ -15,6 +15,7 @@ layout(set = 1, binding = 0, std140) uniform PerObjectConstants { layout(location = 0) out vec3 vNormalWS; layout(location = 1) out vec2 vTexCoord; +layout(location = 2) out vec3 vPositionWS; void main() { vec4 positionWS = gModelMatrix * vec4(aPosition, 1.0); @@ -22,4 +23,5 @@ void main() { gl_Position = gProjectionMatrix * positionVS; vNormalWS = mat3(gNormalMatrix) * aNormal; vTexCoord = aTexCoord; + vPositionWS = positionWS.xyz; } diff --git a/engine/assets/builtin/shaders/forward-lit/forward-lit.vs.hlsl b/engine/assets/builtin/shaders/forward-lit/forward-lit.vs.hlsl index 310eebcc..2cbb0048 100644 --- a/engine/assets/builtin/shaders/forward-lit/forward-lit.vs.hlsl +++ b/engine/assets/builtin/shaders/forward-lit/forward-lit.vs.hlsl @@ -1,7 +1,4 @@ // XC_BUILTIN_FORWARD_LIT_D3D12_VS -Texture2D gBaseColorTexture : register(t1); -SamplerState gLinearSampler : register(s1); - cbuffer PerObjectConstants : register(b1) { float4x4 gProjectionMatrix; float4x4 gViewMatrix; @@ -11,10 +8,6 @@ cbuffer PerObjectConstants : register(b1) { float4 gMainLightColorAndFlags; }; -cbuffer MaterialConstants : register(b2) { - float4 gBaseColorFactor; -}; - struct VSInput { float3 position : POSITION; float3 normal : NORMAL; @@ -25,6 +18,7 @@ struct PSInput { float4 position : SV_POSITION; float3 normalWS : TEXCOORD0; float2 texcoord : TEXCOORD1; + float3 positionWS : TEXCOORD2; }; PSInput MainVS(VSInput input) { @@ -34,5 +28,6 @@ PSInput MainVS(VSInput input) { output.position = mul(gProjectionMatrix, positionVS); output.normalWS = mul((float3x3)gNormalMatrix, input.normal); output.texcoord = input.texcoord; + output.positionWS = positionWS.xyz; return output; } diff --git a/engine/include/XCEngine/RHI/D3D12/D3D12CommandList.h b/engine/include/XCEngine/RHI/D3D12/D3D12CommandList.h index 6edd5b63..61301966 100644 --- a/engine/include/XCEngine/RHI/D3D12/D3D12CommandList.h +++ b/engine/include/XCEngine/RHI/D3D12/D3D12CommandList.h @@ -143,8 +143,12 @@ private: ComPtr m_commandList; ComPtr m_commandAllocator; ComPtr m_rtvHeap; + ComPtr m_shaderVisibleCbvSrvUavHeap; + ComPtr m_shaderVisibleSamplerHeap; ID3D12Device* m_device = nullptr; CommandQueueType m_type; + uint32_t m_shaderVisibleCbvSrvUavHeapCapacity = 0; + uint32_t m_shaderVisibleSamplerHeapCapacity = 0; std::unordered_map m_resourceStateMap; std::vector m_trackedResources; diff --git a/engine/include/XCEngine/RHI/D3D12/D3D12DescriptorSet.h b/engine/include/XCEngine/RHI/D3D12/D3D12DescriptorSet.h index b07f7c19..b30e78e8 100644 --- a/engine/include/XCEngine/RHI/D3D12/D3D12DescriptorSet.h +++ b/engine/include/XCEngine/RHI/D3D12/D3D12DescriptorSet.h @@ -42,6 +42,8 @@ public: D3D12_GPU_DESCRIPTOR_HANDLE GetGPUHandle(uint32_t index = 0) const; D3D12_GPU_DESCRIPTOR_HANDLE GetGPUHandleForBinding(uint32_t binding) const; + D3D12_CPU_DESCRIPTOR_HANDLE GetCPUHandleForBinding(uint32_t binding) const; + uint32_t GetDescriptorCountForBinding(uint32_t binding) const; uint32_t GetOffset() const { return m_offset; } uint32_t GetCount() const { return m_count; } D3D12DescriptorHeap* GetHeap() const { return m_heap; } diff --git a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h index 394dfa79..efcc77e5 100644 --- a/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h +++ b/engine/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h @@ -68,6 +68,12 @@ private: Math::Vector4 mainLightColorAndFlags = Math::Vector4::Zero(); }; + struct ShadowReceiverConstants { + Math::Matrix4x4 worldToShadow = Math::Matrix4x4::Identity(); + Math::Vector4 shadowBiasAndTexelSize = Math::Vector4::Zero(); + Math::Vector4 shadowOptions = Math::Vector4::Zero(); + }; + struct FallbackPerMaterialConstants { Math::Vector4 baseColorFactor = Math::Vector4::One(); }; @@ -97,8 +103,11 @@ private: std::vector staticDescriptorSets; PassResourceBindingLocation perObject = {}; PassResourceBindingLocation material = {}; + PassResourceBindingLocation shadowReceiver = {}; PassResourceBindingLocation baseColorTexture = {}; PassResourceBindingLocation linearClampSampler = {}; + PassResourceBindingLocation shadowMapTexture = {}; + PassResourceBindingLocation shadowMapSampler = {}; }; struct DynamicDescriptorSetKey { @@ -132,7 +141,8 @@ private: struct CachedDescriptorSet { OwnedDescriptorSet descriptorSet = {}; Core::uint64 materialVersion = 0; - RHI::RHIResourceView* textureView = nullptr; + RHI::RHIResourceView* baseColorTextureView = nullptr; + RHI::RHIResourceView* shadowMapTextureView = nullptr; }; struct ResolvedShaderPass { @@ -190,7 +200,9 @@ private: Core::uint64 objectId, const Resources::Material* material, const MaterialConstantPayloadView& materialConstants, - RHI::RHIResourceView* textureView); + const ShadowReceiverConstants& shadowReceiverConstants, + RHI::RHIResourceView* baseColorTextureView, + RHI::RHIResourceView* shadowMapTextureView); void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet); void DestroyPassResourceLayout(PassResourceLayout& passLayout); @@ -214,6 +226,7 @@ private: std::unordered_map m_pipelineStates; std::unordered_map m_dynamicDescriptorSets; RHI::RHISampler* m_sampler = nullptr; + RHI::RHISampler* m_shadowSampler = nullptr; RHI::RHITexture* m_fallbackTexture = nullptr; RHI::RHIResourceView* m_fallbackTextureView = nullptr; RenderPassSequence m_passSequence; diff --git a/engine/include/XCEngine/Rendering/RenderMaterialUtility.h b/engine/include/XCEngine/Rendering/RenderMaterialUtility.h index 0d3423fa..a24da3d5 100644 --- a/engine/include/XCEngine/Rendering/RenderMaterialUtility.h +++ b/engine/include/XCEngine/Rendering/RenderMaterialUtility.h @@ -37,8 +37,11 @@ enum class BuiltinPassResourceSemantic : Core::uint8 { Unknown = 0, PerObject, Material, + ShadowReceiver, BaseColorTexture, - LinearClampSampler + ShadowMapTexture, + LinearClampSampler, + ShadowMapSampler }; struct BuiltinPassResourceBindingDesc { @@ -57,8 +60,11 @@ struct BuiltinPassResourceBindingPlan { bool usesSamplers = false; PassResourceBindingLocation perObject = {}; PassResourceBindingLocation material = {}; + PassResourceBindingLocation shadowReceiver = {}; PassResourceBindingLocation baseColorTexture = {}; PassResourceBindingLocation linearClampSampler = {}; + PassResourceBindingLocation shadowMapTexture = {}; + PassResourceBindingLocation shadowMapSampler = {}; const BuiltinPassResourceBindingDesc* FindBinding(BuiltinPassResourceSemantic semantic) const { for (const BuiltinPassResourceBindingDesc& binding : bindings) { @@ -78,8 +84,13 @@ struct BuiltinPassSetLayoutMetadata { bool shaderVisible = false; bool usesPerObject = false; bool usesMaterial = false; + bool usesShadowReceiver = false; bool usesTexture = false; + bool usesBaseColorTexture = false; + bool usesShadowMapTexture = false; bool usesSampler = false; + bool usesLinearClampSampler = false; + bool usesShadowMapSampler = false; }; inline Containers::String NormalizeBuiltinPassMetadataValue(const Containers::String& value) { @@ -198,15 +209,30 @@ inline BuiltinPassResourceSemantic ResolveBuiltinPassResourceSemantic( return BuiltinPassResourceSemantic::Material; } + if (semantic == Containers::String("shadowreceiver") || + semantic == Containers::String("shadowreceiverconstants")) { + return BuiltinPassResourceSemantic::ShadowReceiver; + } + if (semantic == Containers::String("basecolortexture") || semantic == Containers::String("maintex")) { return BuiltinPassResourceSemantic::BaseColorTexture; } + if (semantic == Containers::String("shadowmaptexture") || + semantic == Containers::String("shadowmap")) { + return BuiltinPassResourceSemantic::ShadowMapTexture; + } + if (semantic == Containers::String("linearclampsampler")) { return BuiltinPassResourceSemantic::LinearClampSampler; } + if (semantic == Containers::String("shadowmapsampler") || + semantic == Containers::String("shadowsampler")) { + return BuiltinPassResourceSemantic::ShadowMapSampler; + } + return BuiltinPassResourceSemantic::Unknown; } @@ -286,11 +312,14 @@ inline bool IsBuiltinPassResourceTypeCompatible( switch (semantic) { case BuiltinPassResourceSemantic::PerObject: case BuiltinPassResourceSemantic::Material: + case BuiltinPassResourceSemantic::ShadowReceiver: return type == Resources::ShaderResourceType::ConstantBuffer; case BuiltinPassResourceSemantic::BaseColorTexture: + case BuiltinPassResourceSemantic::ShadowMapTexture: return type == Resources::ShaderResourceType::Texture2D || type == Resources::ShaderResourceType::TextureCube; case BuiltinPassResourceSemantic::LinearClampSampler: + case BuiltinPassResourceSemantic::ShadowMapSampler: return type == Resources::ShaderResourceType::Sampler; case BuiltinPassResourceSemantic::Unknown: default: @@ -336,12 +365,21 @@ inline bool TryBuildBuiltinPassResourceBindingPlan( case BuiltinPassResourceSemantic::Material: location = &outPlan.material; break; + case BuiltinPassResourceSemantic::ShadowReceiver: + location = &outPlan.shadowReceiver; + break; case BuiltinPassResourceSemantic::BaseColorTexture: location = &outPlan.baseColorTexture; break; case BuiltinPassResourceSemantic::LinearClampSampler: location = &outPlan.linearClampSampler; break; + case BuiltinPassResourceSemantic::ShadowMapTexture: + location = &outPlan.shadowMapTexture; + break; + case BuiltinPassResourceSemantic::ShadowMapSampler: + location = &outPlan.shadowMapSampler; + break; case BuiltinPassResourceSemantic::Unknown: default: break; @@ -520,11 +558,24 @@ inline bool TryBuildBuiltinPassSetLayouts( case BuiltinPassResourceSemantic::Material: setLayout.usesMaterial = true; break; + case BuiltinPassResourceSemantic::ShadowReceiver: + setLayout.usesShadowReceiver = true; + break; case BuiltinPassResourceSemantic::BaseColorTexture: setLayout.usesTexture = true; + setLayout.usesBaseColorTexture = true; + break; + case BuiltinPassResourceSemantic::ShadowMapTexture: + setLayout.usesTexture = true; + setLayout.usesShadowMapTexture = true; break; case BuiltinPassResourceSemantic::LinearClampSampler: setLayout.usesSampler = true; + setLayout.usesLinearClampSampler = true; + break; + case BuiltinPassResourceSemantic::ShadowMapSampler: + setLayout.usesSampler = true; + setLayout.usesShadowMapSampler = true; break; case BuiltinPassResourceSemantic::Unknown: default: diff --git a/engine/include/XCEngine/Rendering/RenderSceneExtractor.h b/engine/include/XCEngine/Rendering/RenderSceneExtractor.h index 28b9b739..52212be7 100644 --- a/engine/include/XCEngine/Rendering/RenderSceneExtractor.h +++ b/engine/include/XCEngine/Rendering/RenderSceneExtractor.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -14,6 +15,10 @@ class GameObject; class Scene; } // namespace Components +namespace RHI { +class RHIResourceView; +} // namespace RHI + namespace Rendering { struct RenderDirectionalLightData { @@ -24,12 +29,28 @@ struct RenderDirectionalLightData { Math::Color color = Math::Color::White(); }; +struct RenderDirectionalShadowData { + bool enabled = false; + Math::Matrix4x4 viewProjection = Math::Matrix4x4::Identity(); + Math::Vector4 shadowParams = Math::Vector4::Zero(); + RHI::RHIResourceView* shadowMap = nullptr; + + bool IsValid() const { + return enabled && shadowMap != nullptr; + } +}; + struct RenderLightingData { RenderDirectionalLightData mainDirectionalLight; + RenderDirectionalShadowData mainDirectionalShadow; bool HasMainDirectionalLight() const { return mainDirectionalLight.enabled; } + + bool HasMainDirectionalShadow() const { + return mainDirectionalShadow.IsValid(); + } }; struct RenderSceneData { diff --git a/engine/src/RHI/D3D12/D3D12CommandList.cpp b/engine/src/RHI/D3D12/D3D12CommandList.cpp index 8de6f324..82b10a43 100644 --- a/engine/src/RHI/D3D12/D3D12CommandList.cpp +++ b/engine/src/RHI/D3D12/D3D12CommandList.cpp @@ -26,6 +26,45 @@ bool HasSamplerBindings(const D3D12DescriptorSet* descriptorSet) { return descriptorSet != nullptr && descriptorSet->HasBindingType(DescriptorType::Sampler); } +uint32_t CountDescriptorsOfType(const D3D12DescriptorSet* descriptorSet, DescriptorType type) { + if (descriptorSet == nullptr) { + return 0; + } + + uint32_t descriptorCount = 0; + const DescriptorSetLayoutBinding* bindings = descriptorSet->GetBindings(); + for (uint32_t bindingIndex = 0; bindingIndex < descriptorSet->GetBindingCount(); ++bindingIndex) { + const DescriptorSetLayoutBinding& binding = bindings[bindingIndex]; + if (static_cast(binding.type) == type) { + descriptorCount += binding.count > 0 ? binding.count : 1u; + } + } + + return descriptorCount; +} + +D3D12_CPU_DESCRIPTOR_HANDLE OffsetCPUHandle( + ID3D12Device* device, + ID3D12DescriptorHeap* heap, + D3D12_DESCRIPTOR_HEAP_TYPE heapType, + uint32_t descriptorIndex) { + D3D12_CPU_DESCRIPTOR_HANDLE handle = heap->GetCPUDescriptorHandleForHeapStart(); + const UINT descriptorSize = device->GetDescriptorHandleIncrementSize(heapType); + handle.ptr += static_cast(descriptorIndex) * descriptorSize; + return handle; +} + +D3D12_GPU_DESCRIPTOR_HANDLE OffsetGPUHandle( + ID3D12Device* device, + ID3D12DescriptorHeap* heap, + D3D12_DESCRIPTOR_HEAP_TYPE heapType, + uint32_t descriptorIndex) { + D3D12_GPU_DESCRIPTOR_HANDLE handle = heap->GetGPUDescriptorHandleForHeapStart(); + const UINT descriptorSize = device->GetDescriptorHandleIncrementSize(heapType); + handle.ptr += static_cast(descriptorIndex) * descriptorSize; + return handle; +} + } // namespace D3D12CommandList::D3D12CommandList() @@ -84,6 +123,10 @@ bool D3D12CommandList::Initialize(ID3D12Device* device, CommandQueueType type, I void D3D12CommandList::Shutdown() { m_commandList.Reset(); m_rtvHeap.Reset(); + m_shaderVisibleCbvSrvUavHeap.Reset(); + m_shaderVisibleSamplerHeap.Reset(); + m_shaderVisibleCbvSrvUavHeapCapacity = 0; + m_shaderVisibleSamplerHeapCapacity = 0; m_resourceStateMap.clear(); m_trackedResources.clear(); m_currentShader = nullptr; @@ -186,8 +229,8 @@ void D3D12CommandList::SetGraphicsDescriptorSets( D3D12PipelineLayout* d3d12Layout = static_cast(pipelineLayout); SetPipelineLayout(d3d12Layout); - std::vector descriptorHeaps; - descriptorHeaps.reserve(2); + uint32_t requiredCbvSrvUavDescriptors = 0; + uint32_t requiredSamplerDescriptors = 0; for (uint32_t i = 0; i < count; ++i) { if (descriptorSets[i] == nullptr) { @@ -195,21 +238,73 @@ void D3D12CommandList::SetGraphicsDescriptorSets( } D3D12DescriptorSet* d3d12Set = static_cast(descriptorSets[i]); - D3D12DescriptorHeap* heap = d3d12Set->GetHeap(); - if (heap != nullptr && heap->IsShaderVisible() && - (HasDescriptorTableBindings(d3d12Set) || HasSamplerBindings(d3d12Set))) { - ID3D12DescriptorHeap* nativeHeap = heap->GetDescriptorHeap(); - if (nativeHeap != nullptr && - std::find(descriptorHeaps.begin(), descriptorHeaps.end(), nativeHeap) == descriptorHeaps.end()) { - descriptorHeaps.push_back(nativeHeap); - } + if (HasDescriptorTableBindings(d3d12Set)) { + requiredCbvSrvUavDescriptors += CountDescriptorsOfType(d3d12Set, DescriptorType::SRV); + requiredCbvSrvUavDescriptors += CountDescriptorsOfType(d3d12Set, DescriptorType::UAV); + } + if (HasSamplerBindings(d3d12Set)) { + requiredSamplerDescriptors += CountDescriptorsOfType(d3d12Set, DescriptorType::Sampler); } } + auto ensureShaderVisibleHeap = + [this](D3D12_DESCRIPTOR_HEAP_TYPE heapType, + uint32_t requiredDescriptors, + ComPtr& heap, + uint32_t& capacity) -> bool { + if (requiredDescriptors == 0) { + return true; + } + + if (heap != nullptr && capacity >= requiredDescriptors) { + return true; + } + + const uint32_t newCapacity = std::max(requiredDescriptors, capacity > 0 ? capacity * 2u : 32u); + D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; + heapDesc.Type = heapType; + heapDesc.NumDescriptors = newCapacity; + heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + heapDesc.NodeMask = 0; + + ComPtr newHeap; + if (FAILED(m_device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&newHeap)))) { + return false; + } + + heap = newHeap; + capacity = newCapacity; + return true; + }; + + if (!ensureShaderVisibleHeap( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + requiredCbvSrvUavDescriptors, + m_shaderVisibleCbvSrvUavHeap, + m_shaderVisibleCbvSrvUavHeapCapacity) || + !ensureShaderVisibleHeap( + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, + requiredSamplerDescriptors, + m_shaderVisibleSamplerHeap, + m_shaderVisibleSamplerHeapCapacity)) { + return; + } + + std::vector descriptorHeaps; + descriptorHeaps.reserve(2); + if (requiredCbvSrvUavDescriptors > 0 && m_shaderVisibleCbvSrvUavHeap != nullptr) { + descriptorHeaps.push_back(m_shaderVisibleCbvSrvUavHeap.Get()); + } + if (requiredSamplerDescriptors > 0 && m_shaderVisibleSamplerHeap != nullptr) { + descriptorHeaps.push_back(m_shaderVisibleSamplerHeap.Get()); + } if (!descriptorHeaps.empty()) { SetDescriptorHeaps(static_cast(descriptorHeaps.size()), descriptorHeaps.data()); } + uint32_t nextCbvSrvUavDescriptor = 0; + uint32_t nextSamplerDescriptor = 0; + for (uint32_t i = 0; i < count; ++i) { if (descriptorSets[i] == nullptr) { continue; @@ -251,7 +346,24 @@ void D3D12CommandList::SetGraphicsDescriptorSets( if (d3d12Set->HasBindingType(DescriptorType::SRV) && hasSrvTable) { const uint32_t srvBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::SRV); - const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(srvBinding); + const uint32_t srvDescriptorCount = CountDescriptorsOfType(d3d12Set, DescriptorType::SRV); + const D3D12_CPU_DESCRIPTOR_HANDLE srcHandle = d3d12Set->GetCPUHandleForBinding(srvBinding); + const D3D12_CPU_DESCRIPTOR_HANDLE dstHandle = OffsetCPUHandle( + m_device, + m_shaderVisibleCbvSrvUavHeap.Get(), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + nextCbvSrvUavDescriptor); + m_device->CopyDescriptorsSimple( + srvDescriptorCount, + dstHandle, + srcHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = OffsetGPUHandle( + m_device, + m_shaderVisibleCbvSrvUavHeap.Get(), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + nextCbvSrvUavDescriptor); + nextCbvSrvUavDescriptor += srvDescriptorCount; if (gpuHandle.ptr != 0) { SetGraphicsRootDescriptorTable( d3d12Layout->UsesSetLayouts() @@ -263,7 +375,24 @@ void D3D12CommandList::SetGraphicsDescriptorSets( if (d3d12Set->HasBindingType(DescriptorType::UAV) && hasUavTable) { const uint32_t uavBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::UAV); - const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(uavBinding); + const uint32_t uavDescriptorCount = CountDescriptorsOfType(d3d12Set, DescriptorType::UAV); + const D3D12_CPU_DESCRIPTOR_HANDLE srcHandle = d3d12Set->GetCPUHandleForBinding(uavBinding); + const D3D12_CPU_DESCRIPTOR_HANDLE dstHandle = OffsetCPUHandle( + m_device, + m_shaderVisibleCbvSrvUavHeap.Get(), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + nextCbvSrvUavDescriptor); + m_device->CopyDescriptorsSimple( + uavDescriptorCount, + dstHandle, + srcHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = OffsetGPUHandle( + m_device, + m_shaderVisibleCbvSrvUavHeap.Get(), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + nextCbvSrvUavDescriptor); + nextCbvSrvUavDescriptor += uavDescriptorCount; if (gpuHandle.ptr != 0) { SetGraphicsRootDescriptorTable( d3d12Layout->UsesSetLayouts() @@ -278,7 +407,24 @@ void D3D12CommandList::SetGraphicsDescriptorSets( ? d3d12Layout->HasSamplerTable(setIndex) : d3d12Layout->HasSamplerTable())) { const uint32_t samplerBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::Sampler); - const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(samplerBinding); + const uint32_t samplerDescriptorCount = CountDescriptorsOfType(d3d12Set, DescriptorType::Sampler); + const D3D12_CPU_DESCRIPTOR_HANDLE srcHandle = d3d12Set->GetCPUHandleForBinding(samplerBinding); + const D3D12_CPU_DESCRIPTOR_HANDLE dstHandle = OffsetCPUHandle( + m_device, + m_shaderVisibleSamplerHeap.Get(), + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, + nextSamplerDescriptor); + m_device->CopyDescriptorsSimple( + samplerDescriptorCount, + dstHandle, + srcHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = OffsetGPUHandle( + m_device, + m_shaderVisibleSamplerHeap.Get(), + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, + nextSamplerDescriptor); + nextSamplerDescriptor += samplerDescriptorCount; if (gpuHandle.ptr != 0) { SetGraphicsRootDescriptorTable( d3d12Layout->UsesSetLayouts() @@ -303,8 +449,8 @@ void D3D12CommandList::SetComputeDescriptorSets( SetPipelineLayout(d3d12Layout); m_commandList->SetComputeRootSignature(d3d12Layout->GetRootSignature()); - std::vector descriptorHeaps; - descriptorHeaps.reserve(2); + uint32_t requiredCbvSrvUavDescriptors = 0; + uint32_t requiredSamplerDescriptors = 0; for (uint32_t i = 0; i < count; ++i) { if (descriptorSets[i] == nullptr) { @@ -312,21 +458,73 @@ void D3D12CommandList::SetComputeDescriptorSets( } D3D12DescriptorSet* d3d12Set = static_cast(descriptorSets[i]); - D3D12DescriptorHeap* heap = d3d12Set->GetHeap(); - if (heap != nullptr && heap->IsShaderVisible() && - (HasDescriptorTableBindings(d3d12Set) || HasSamplerBindings(d3d12Set))) { - ID3D12DescriptorHeap* nativeHeap = heap->GetDescriptorHeap(); - if (nativeHeap != nullptr && - std::find(descriptorHeaps.begin(), descriptorHeaps.end(), nativeHeap) == descriptorHeaps.end()) { - descriptorHeaps.push_back(nativeHeap); - } + if (HasDescriptorTableBindings(d3d12Set)) { + requiredCbvSrvUavDescriptors += CountDescriptorsOfType(d3d12Set, DescriptorType::SRV); + requiredCbvSrvUavDescriptors += CountDescriptorsOfType(d3d12Set, DescriptorType::UAV); + } + if (HasSamplerBindings(d3d12Set)) { + requiredSamplerDescriptors += CountDescriptorsOfType(d3d12Set, DescriptorType::Sampler); } } + auto ensureShaderVisibleHeap = + [this](D3D12_DESCRIPTOR_HEAP_TYPE heapType, + uint32_t requiredDescriptors, + ComPtr& heap, + uint32_t& capacity) -> bool { + if (requiredDescriptors == 0) { + return true; + } + + if (heap != nullptr && capacity >= requiredDescriptors) { + return true; + } + + const uint32_t newCapacity = std::max(requiredDescriptors, capacity > 0 ? capacity * 2u : 32u); + D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {}; + heapDesc.Type = heapType; + heapDesc.NumDescriptors = newCapacity; + heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + heapDesc.NodeMask = 0; + + ComPtr newHeap; + if (FAILED(m_device->CreateDescriptorHeap(&heapDesc, IID_PPV_ARGS(&newHeap)))) { + return false; + } + + heap = newHeap; + capacity = newCapacity; + return true; + }; + + if (!ensureShaderVisibleHeap( + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + requiredCbvSrvUavDescriptors, + m_shaderVisibleCbvSrvUavHeap, + m_shaderVisibleCbvSrvUavHeapCapacity) || + !ensureShaderVisibleHeap( + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, + requiredSamplerDescriptors, + m_shaderVisibleSamplerHeap, + m_shaderVisibleSamplerHeapCapacity)) { + return; + } + + std::vector descriptorHeaps; + descriptorHeaps.reserve(2); + if (requiredCbvSrvUavDescriptors > 0 && m_shaderVisibleCbvSrvUavHeap != nullptr) { + descriptorHeaps.push_back(m_shaderVisibleCbvSrvUavHeap.Get()); + } + if (requiredSamplerDescriptors > 0 && m_shaderVisibleSamplerHeap != nullptr) { + descriptorHeaps.push_back(m_shaderVisibleSamplerHeap.Get()); + } if (!descriptorHeaps.empty()) { SetDescriptorHeaps(static_cast(descriptorHeaps.size()), descriptorHeaps.data()); } + uint32_t nextCbvSrvUavDescriptor = 0; + uint32_t nextSamplerDescriptor = 0; + for (uint32_t i = 0; i < count; ++i) { if (descriptorSets[i] == nullptr) { continue; @@ -368,7 +566,24 @@ void D3D12CommandList::SetComputeDescriptorSets( if (d3d12Set->HasBindingType(DescriptorType::SRV) && hasSrvTable) { const uint32_t srvBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::SRV); - const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(srvBinding); + const uint32_t srvDescriptorCount = CountDescriptorsOfType(d3d12Set, DescriptorType::SRV); + const D3D12_CPU_DESCRIPTOR_HANDLE srcHandle = d3d12Set->GetCPUHandleForBinding(srvBinding); + const D3D12_CPU_DESCRIPTOR_HANDLE dstHandle = OffsetCPUHandle( + m_device, + m_shaderVisibleCbvSrvUavHeap.Get(), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + nextCbvSrvUavDescriptor); + m_device->CopyDescriptorsSimple( + srvDescriptorCount, + dstHandle, + srcHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = OffsetGPUHandle( + m_device, + m_shaderVisibleCbvSrvUavHeap.Get(), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + nextCbvSrvUavDescriptor); + nextCbvSrvUavDescriptor += srvDescriptorCount; if (gpuHandle.ptr != 0) { m_commandList->SetComputeRootDescriptorTable( d3d12Layout->UsesSetLayouts() @@ -380,7 +595,24 @@ void D3D12CommandList::SetComputeDescriptorSets( if (d3d12Set->HasBindingType(DescriptorType::UAV) && hasUavTable) { const uint32_t uavBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::UAV); - const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(uavBinding); + const uint32_t uavDescriptorCount = CountDescriptorsOfType(d3d12Set, DescriptorType::UAV); + const D3D12_CPU_DESCRIPTOR_HANDLE srcHandle = d3d12Set->GetCPUHandleForBinding(uavBinding); + const D3D12_CPU_DESCRIPTOR_HANDLE dstHandle = OffsetCPUHandle( + m_device, + m_shaderVisibleCbvSrvUavHeap.Get(), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + nextCbvSrvUavDescriptor); + m_device->CopyDescriptorsSimple( + uavDescriptorCount, + dstHandle, + srcHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = OffsetGPUHandle( + m_device, + m_shaderVisibleCbvSrvUavHeap.Get(), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + nextCbvSrvUavDescriptor); + nextCbvSrvUavDescriptor += uavDescriptorCount; if (gpuHandle.ptr != 0) { m_commandList->SetComputeRootDescriptorTable( d3d12Layout->UsesSetLayouts() @@ -395,7 +627,24 @@ void D3D12CommandList::SetComputeDescriptorSets( ? d3d12Layout->HasSamplerTable(setIndex) : d3d12Layout->HasSamplerTable())) { const uint32_t samplerBinding = d3d12Set->GetFirstBindingOfType(DescriptorType::Sampler); - const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = d3d12Set->GetGPUHandleForBinding(samplerBinding); + const uint32_t samplerDescriptorCount = CountDescriptorsOfType(d3d12Set, DescriptorType::Sampler); + const D3D12_CPU_DESCRIPTOR_HANDLE srcHandle = d3d12Set->GetCPUHandleForBinding(samplerBinding); + const D3D12_CPU_DESCRIPTOR_HANDLE dstHandle = OffsetCPUHandle( + m_device, + m_shaderVisibleSamplerHeap.Get(), + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, + nextSamplerDescriptor); + m_device->CopyDescriptorsSimple( + samplerDescriptorCount, + dstHandle, + srcHandle, + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + const D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = OffsetGPUHandle( + m_device, + m_shaderVisibleSamplerHeap.Get(), + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, + nextSamplerDescriptor); + nextSamplerDescriptor += samplerDescriptorCount; if (gpuHandle.ptr != 0) { m_commandList->SetComputeRootDescriptorTable( d3d12Layout->UsesSetLayouts() diff --git a/engine/src/RHI/D3D12/D3D12DescriptorSet.cpp b/engine/src/RHI/D3D12/D3D12DescriptorSet.cpp index 9c75d46c..371e4b8b 100644 --- a/engine/src/RHI/D3D12/D3D12DescriptorSet.cpp +++ b/engine/src/RHI/D3D12/D3D12DescriptorSet.cpp @@ -175,6 +175,25 @@ D3D12_GPU_DESCRIPTOR_HANDLE D3D12DescriptorSet::GetGPUHandleForBinding(uint32_t return GetGPUHandle(descriptorIndex); } +D3D12_CPU_DESCRIPTOR_HANDLE D3D12DescriptorSet::GetCPUHandleForBinding(uint32_t binding) const { + const uint32_t descriptorIndex = GetDescriptorIndexForBinding(binding); + if (descriptorIndex == UINT32_MAX || m_heap == nullptr) { + return D3D12_CPU_DESCRIPTOR_HANDLE{0}; + } + + const CPUDescriptorHandle handle = m_heap->GetCPUDescriptorHandle(m_offset + descriptorIndex); + return D3D12_CPU_DESCRIPTOR_HANDLE{handle.ptr}; +} + +uint32_t D3D12DescriptorSet::GetDescriptorCountForBinding(uint32_t binding) const { + const BindingRecord* bindingRecord = FindBindingRecord(binding); + if (bindingRecord == nullptr) { + return 0; + } + + return bindingRecord->layout.count > 0 ? bindingRecord->layout.count : 1u; +} + void D3D12DescriptorSet::WriteConstant(uint32_t binding, const void* data, size_t size, size_t offset) { BindingRecord* bindingRecord = FindBindingRecord(binding); if (bindingRecord == nullptr || diff --git a/engine/src/RHI/D3D12/D3D12Device.cpp b/engine/src/RHI/D3D12/D3D12Device.cpp index af5ff35d..4c6cef1a 100644 --- a/engine/src/RHI/D3D12/D3D12Device.cpp +++ b/engine/src/RHI/D3D12/D3D12Device.cpp @@ -196,6 +196,51 @@ D3D12_RTV_DIMENSION ResolveRTVDimension(const ResourceViewDesc& desc, TextureTyp } } +bool IsDepthFormat(Format format) { + return format == Format::D16_UNorm || + format == Format::D24_UNorm_S8_UInt || + format == Format::D32_Float; +} + +DXGI_FORMAT ResolveD3D12ResourceFormat(Format format) { + switch (format) { + case Format::D16_UNorm: + return DXGI_FORMAT_R16_TYPELESS; + case Format::D24_UNorm_S8_UInt: + return DXGI_FORMAT_R24G8_TYPELESS; + case Format::D32_Float: + return DXGI_FORMAT_R32_TYPELESS; + default: + return ToD3D12(format); + } +} + +DXGI_FORMAT ResolveD3D12DepthStencilViewFormat(Format format) { + switch (format) { + case Format::D16_UNorm: + return DXGI_FORMAT_D16_UNORM; + case Format::D24_UNorm_S8_UInt: + return DXGI_FORMAT_D24_UNORM_S8_UINT; + case Format::D32_Float: + return DXGI_FORMAT_D32_FLOAT; + default: + return ToD3D12(format); + } +} + +DXGI_FORMAT ResolveD3D12ShaderResourceViewFormat(Format format) { + switch (format) { + case Format::D16_UNorm: + return DXGI_FORMAT_R16_UNORM; + case Format::D24_UNorm_S8_UInt: + return DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + case Format::D32_Float: + return DXGI_FORMAT_R32_FLOAT; + default: + return ToD3D12(format); + } +} + } // namespace D3D12Device::D3D12Device() @@ -595,16 +640,14 @@ RHITexture* D3D12Device::CreateTexture(const TextureDesc& desc) { const TextureType textureType = ResolveTextureType(desc.textureType); const Format format = static_cast(desc.format); - const bool isDepthFormat = format == Format::D24_UNorm_S8_UInt || - format == Format::D32_Float || - format == Format::D16_UNorm; + const bool isDepthFormat = IsDepthFormat(format); d3d12Desc.Dimension = ToD3D12(textureType); d3d12Desc.Width = desc.width; d3d12Desc.Height = desc.height; d3d12Desc.DepthOrArraySize = ResolveDepthOrArraySize(desc, textureType); d3d12Desc.MipLevels = desc.mipLevels > 0 ? desc.mipLevels : 1; - d3d12Desc.Format = ToD3D12(format); + d3d12Desc.Format = ResolveD3D12ResourceFormat(format); d3d12Desc.SampleDesc.Count = desc.sampleCount > 0 ? desc.sampleCount : 1; d3d12Desc.SampleDesc.Quality = desc.sampleQuality; d3d12Desc.Flags = static_cast(desc.flags); @@ -1009,7 +1052,8 @@ RHIResourceView* D3D12Device::CreateDepthStencilView(RHITexture* texture, const ID3D12Resource* resource = d3d12Texture->GetResource(); D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {}; - dsvDesc.Format = ToD3D12(static_cast(desc.format)); + const Format format = desc.format != 0 ? static_cast(desc.format) : texture->GetFormat(); + dsvDesc.Format = ResolveD3D12DepthStencilViewFormat(format); dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; auto heap = std::make_unique(); @@ -1029,10 +1073,11 @@ RHIResourceView* D3D12Device::CreateShaderResourceView(RHITexture* texture, cons ID3D12Resource* resource = d3d12Texture->GetResource(); D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; - srvDesc.Format = ToD3D12(static_cast(desc.format)); + const Format format = desc.format != 0 ? static_cast(desc.format) : texture->GetFormat(); + srvDesc.Format = ResolveD3D12ShaderResourceViewFormat(format); srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; - srvDesc.Texture2D.MipLevels = desc.mipLevel > 0 ? desc.mipLevel : 1; - srvDesc.Texture2D.MostDetailedMip = 0; + srvDesc.Texture2D.MostDetailedMip = desc.mipLevel; + srvDesc.Texture2D.MipLevels = 1; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; auto heap = std::make_unique(); diff --git a/engine/src/RHI/D3D12/D3D12Texture.cpp b/engine/src/RHI/D3D12/D3D12Texture.cpp index 374d800e..a1c3e007 100644 --- a/engine/src/RHI/D3D12/D3D12Texture.cpp +++ b/engine/src/RHI/D3D12/D3D12Texture.cpp @@ -185,6 +185,21 @@ bool D3D12Texture::InitializeFromData(ID3D12Device* device, ID3D12GraphicsComman } bool D3D12Texture::InitializeDepthStencil(ID3D12Device* device, uint32_t width, uint32_t height, DXGI_FORMAT format) { + DXGI_FORMAT resourceFormat = format; + switch (format) { + case DXGI_FORMAT_D16_UNORM: + resourceFormat = DXGI_FORMAT_R16_TYPELESS; + break; + case DXGI_FORMAT_D24_UNORM_S8_UINT: + resourceFormat = DXGI_FORMAT_R24G8_TYPELESS; + break; + case DXGI_FORMAT_D32_FLOAT: + resourceFormat = DXGI_FORMAT_R32_TYPELESS; + break; + default: + break; + } + D3D12_RESOURCE_DESC desc = {}; desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; desc.Alignment = 0; @@ -192,7 +207,7 @@ bool D3D12Texture::InitializeDepthStencil(ID3D12Device* device, uint32_t width, desc.Height = height; desc.DepthOrArraySize = 1; desc.MipLevels = 1; - desc.Format = format; + desc.Format = resourceFormat; desc.SampleDesc.Count = 1; desc.SampleDesc.Quality = 0; desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; diff --git a/engine/src/Rendering/CameraRenderer.cpp b/engine/src/Rendering/CameraRenderer.cpp index 9eda7bb4..3e787d8a 100644 --- a/engine/src/Rendering/CameraRenderer.cpp +++ b/engine/src/Rendering/CameraRenderer.cpp @@ -19,6 +19,7 @@ namespace { struct OwnedShadowSurfaceResources { RHI::RHITexture* depthTexture = nullptr; RHI::RHIResourceView* depthView = nullptr; + RHI::RHIResourceView* shaderView = nullptr; RenderSurface surface = {}; OwnedShadowSurfaceResources() = default; @@ -30,6 +31,12 @@ struct OwnedShadowSurfaceResources { } void Reset() { + if (shaderView != nullptr) { + shaderView->Shutdown(); + delete shaderView; + shaderView = nullptr; + } + if (depthView != nullptr) { depthView->Shutdown(); delete depthView; @@ -181,14 +188,48 @@ bool CreateDirectionalShadowSurface( return false; } + RHI::ResourceViewDesc shaderViewDesc = {}; + shaderViewDesc.format = depthDesc.format; + shaderViewDesc.dimension = RHI::ResourceViewDimension::Texture2D; + shaderViewDesc.mipLevel = 0; + RHI::RHIResourceView* shaderView = + context.device->CreateShaderResourceView(depthTexture, shaderViewDesc); + if (shaderView == nullptr) { + depthView->Shutdown(); + delete depthView; + depthTexture->Shutdown(); + delete depthTexture; + return false; + } + outResources.Reset(); outResources.depthTexture = depthTexture; outResources.depthView = depthView; + outResources.shaderView = shaderView; outResources.surface = RenderSurface(plan.mapWidth, plan.mapHeight); outResources.surface.SetDepthAttachment(depthView); return true; } +RenderDirectionalShadowData BuildDirectionalShadowData( + const DirectionalShadowRenderPlan& plan, + RHI::RHIResourceView* shadowMapView) { + RenderDirectionalShadowData shadowData = {}; + if (!plan.IsValid() || shadowMapView == nullptr) { + return shadowData; + } + + shadowData.enabled = true; + shadowData.viewProjection = plan.cameraData.viewProjection; + shadowData.shadowMap = shadowMapView; + shadowData.shadowParams = Math::Vector4( + 0.0015f, + 1.0f / static_cast(plan.mapWidth), + 1.0f / static_cast(plan.mapHeight), + 0.85f); + return shadowData; +} + } // namespace CameraRenderer::CameraRenderer() @@ -351,6 +392,12 @@ bool CameraRenderer::Render( return false; } + if (request.directionalShadow.IsValid()) { + RHI::RHIResourceView* shadowMapView = ownedShadowSurface.shaderView; + sceneData.lighting.mainDirectionalShadow = + BuildDirectionalShadowData(request.directionalShadow, shadowMapView); + } + sceneData.cameraData.clearFlags = request.clearFlags; if (request.hasClearColorOverride) { sceneData.cameraData.clearColor = request.clearColorOverride; diff --git a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp index cc0e8bbb..d6662ff0 100644 --- a/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp +++ b/engine/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp @@ -283,6 +283,13 @@ bool BuiltinForwardPipeline::ExecuteForwardOpaquePass(const RenderPassContext& p commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); + if (sceneData.lighting.HasMainDirectionalShadow()) { + commandList->TransitionBarrier( + sceneData.lighting.mainDirectionalShadow.shadowMap, + RHI::ResourceStates::DepthWrite, + RHI::ResourceStates::PixelShaderResource); + } + RHI::RHIPipelineState* currentPipelineState = nullptr; for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) { const Resources::Material* material = ResolveMaterial(visibleItem); @@ -303,6 +310,13 @@ bool BuiltinForwardPipeline::ExecuteForwardOpaquePass(const RenderPassContext& p DrawVisibleItem(context, sceneData, visibleItem); } + if (sceneData.lighting.HasMainDirectionalShadow()) { + commandList->TransitionBarrier( + sceneData.lighting.mainDirectionalShadow.shadowMap, + RHI::ResourceStates::PixelShaderResource, + RHI::ResourceStates::DepthWrite); + } + if (surface.IsAutoTransitionEnabled()) { commandList->EndRenderPass(); for (RHI::RHIResourceView* renderTarget : renderTargets) { @@ -370,6 +384,13 @@ bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& contex return false; } + RHI::SamplerDesc shadowSamplerDesc = samplerDesc; + shadowSamplerDesc.filter = static_cast(RHI::FilterMode::Point); + m_shadowSampler = context.device->CreateSampler(shadowSamplerDesc); + if (m_shadowSampler == nullptr) { + return false; + } + const unsigned char whitePixel[4] = { 255, 255, 255, 255 }; RHI::TextureDesc textureDesc = {}; textureDesc.width = 1; @@ -438,6 +459,12 @@ void BuiltinForwardPipeline::DestroyPipelineResources() { m_sampler = nullptr; } + if (m_shadowSampler != nullptr) { + m_shadowSampler->Shutdown(); + delete m_shadowSampler; + m_shadowSampler = nullptr; + } + m_device = nullptr; m_initialized = false; m_builtinForwardShader.Reset(); @@ -527,8 +554,11 @@ BuiltinForwardPipeline::PassResourceLayout* BuiltinForwardPipeline::GetOrCreateP passLayout.perObject = bindingPlan.perObject; passLayout.material = bindingPlan.material; + passLayout.shadowReceiver = bindingPlan.shadowReceiver; passLayout.baseColorTexture = bindingPlan.baseColorTexture; passLayout.linearClampSampler = bindingPlan.linearClampSampler; + passLayout.shadowMapTexture = bindingPlan.shadowMapTexture; + passLayout.shadowMapSampler = bindingPlan.shadowMapSampler; if (!passLayout.perObject.IsValid()) { return failLayout("BuiltinForwardPipeline requires a PerObject resource binding"); @@ -672,14 +702,33 @@ RHI::RHIDescriptorSet* BuiltinForwardPipeline::GetOrCreateStaticDescriptorSet( } if (passLayout.setLayouts[setIndex].usesSampler) { - if (m_sampler == nullptr || - !passLayout.linearClampSampler.IsValid() || - passLayout.linearClampSampler.set != setIndex) { + RHI::RHISampler* sampler = nullptr; + Core::uint32 binding = 0; + + if (passLayout.setLayouts[setIndex].usesLinearClampSampler) { + sampler = m_sampler; + if (!passLayout.linearClampSampler.IsValid() || + passLayout.linearClampSampler.set != setIndex) { + DestroyOwnedDescriptorSet(descriptorSet); + return nullptr; + } + binding = passLayout.linearClampSampler.binding; + } else if (passLayout.setLayouts[setIndex].usesShadowMapSampler) { + sampler = m_shadowSampler; + if (!passLayout.shadowMapSampler.IsValid() || + passLayout.shadowMapSampler.set != setIndex) { + DestroyOwnedDescriptorSet(descriptorSet); + return nullptr; + } + binding = passLayout.shadowMapSampler.binding; + } + + if (sampler == nullptr) { DestroyOwnedDescriptorSet(descriptorSet); return nullptr; } - descriptorSet.set->UpdateSampler(passLayout.linearClampSampler.binding, m_sampler); + descriptorSet.set->UpdateSampler(binding, sampler); } } @@ -694,7 +743,9 @@ BuiltinForwardPipeline::CachedDescriptorSet* BuiltinForwardPipeline::GetOrCreate Core::uint64 objectId, const Resources::Material* material, const MaterialConstantPayloadView& materialConstants, - RHI::RHIResourceView* textureView) { + const ShadowReceiverConstants& shadowReceiverConstants, + RHI::RHIResourceView* baseColorTextureView, + RHI::RHIResourceView* shadowMapTextureView) { DynamicDescriptorSetKey key = {}; key.passLayout = passLayoutKey; key.setIndex = setIndex; @@ -709,35 +760,61 @@ BuiltinForwardPipeline::CachedDescriptorSet* BuiltinForwardPipeline::GetOrCreate } const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0; - if ((setLayout.usesMaterial || setLayout.usesTexture) && - (cachedDescriptorSet.materialVersion != materialVersion || - cachedDescriptorSet.textureView != textureView)) { - if (setLayout.usesMaterial) { - if (!passLayout.material.IsValid() || passLayout.material.set != setIndex) { - return nullptr; - } - if (!materialConstants.IsValid()) { - return nullptr; - } + if (setLayout.usesMaterial) { + if (!passLayout.material.IsValid() || passLayout.material.set != setIndex) { + return nullptr; + } + if (!materialConstants.IsValid()) { + return nullptr; + } + if (cachedDescriptorSet.materialVersion != materialVersion) { cachedDescriptorSet.descriptorSet.set->WriteConstant( passLayout.material.binding, materialConstants.data, materialConstants.size); } - - if (setLayout.usesTexture) { - if (textureView == nullptr || - !passLayout.baseColorTexture.IsValid() || - passLayout.baseColorTexture.set != setIndex) { - return nullptr; - } - cachedDescriptorSet.descriptorSet.set->Update(passLayout.baseColorTexture.binding, textureView); - } - - cachedDescriptorSet.materialVersion = materialVersion; - cachedDescriptorSet.textureView = textureView; } + if (setLayout.usesShadowReceiver) { + if (!passLayout.shadowReceiver.IsValid() || passLayout.shadowReceiver.set != setIndex) { + return nullptr; + } + cachedDescriptorSet.descriptorSet.set->WriteConstant( + passLayout.shadowReceiver.binding, + &shadowReceiverConstants, + sizeof(shadowReceiverConstants)); + } + + if (setLayout.usesBaseColorTexture) { + if (baseColorTextureView == nullptr || + !passLayout.baseColorTexture.IsValid() || + passLayout.baseColorTexture.set != setIndex) { + return nullptr; + } + if (cachedDescriptorSet.baseColorTextureView != baseColorTextureView) { + cachedDescriptorSet.descriptorSet.set->Update( + passLayout.baseColorTexture.binding, + baseColorTextureView); + } + } + + if (setLayout.usesShadowMapTexture) { + if (shadowMapTextureView == nullptr || + !passLayout.shadowMapTexture.IsValid() || + passLayout.shadowMapTexture.set != setIndex) { + return nullptr; + } + if (cachedDescriptorSet.shadowMapTextureView != shadowMapTextureView) { + cachedDescriptorSet.descriptorSet.set->Update( + passLayout.shadowMapTexture.binding, + shadowMapTextureView); + } + } + + cachedDescriptorSet.materialVersion = materialVersion; + cachedDescriptorSet.baseColorTextureView = baseColorTextureView; + cachedDescriptorSet.shadowMapTextureView = shadowMapTextureView; + return &cachedDescriptorSet; } @@ -772,8 +849,11 @@ void BuiltinForwardPipeline::DestroyPassResourceLayout(PassResourceLayout& passL passLayout.descriptorSetCount = 0; passLayout.perObject = {}; passLayout.material = {}; + passLayout.shadowReceiver = {}; passLayout.baseColorTexture = {}; passLayout.linearClampSampler = {}; + passLayout.shadowMapTexture = {}; + passLayout.shadowMapSampler = {}; } const Resources::Texture* BuiltinForwardPipeline::ResolveTexture(const Resources::Material* material) const { @@ -833,6 +913,17 @@ bool BuiltinForwardPipeline::DrawVisibleItem( 1.0f) : Math::Vector4::Zero() }; + const ShadowReceiverConstants shadowReceiverConstants = { + sceneData.lighting.HasMainDirectionalShadow() + ? sceneData.lighting.mainDirectionalShadow.viewProjection + : Math::Matrix4x4::Identity(), + sceneData.lighting.HasMainDirectionalShadow() + ? sceneData.lighting.mainDirectionalShadow.shadowParams + : Math::Vector4::Zero(), + sceneData.lighting.HasMainDirectionalShadow() + ? Math::Vector4(1.0f, 0.0f, 0.0f, 0.0f) + : Math::Vector4::Zero() + }; const Resources::Material* material = ResolveMaterial(visibleItem); const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material); @@ -849,8 +940,15 @@ bool BuiltinForwardPipeline::DrawVisibleItem( return false; } - RHI::RHIResourceView* textureView = ResolveTextureView(visibleItem); - if (passLayout->baseColorTexture.IsValid() && textureView == nullptr) { + RHI::RHIResourceView* baseColorTextureView = ResolveTextureView(visibleItem); + if (passLayout->baseColorTexture.IsValid() && baseColorTextureView == nullptr) { + return false; + } + + RHI::RHIResourceView* shadowMapTextureView = sceneData.lighting.HasMainDirectionalShadow() + ? sceneData.lighting.mainDirectionalShadow.shadowMap + : m_fallbackTextureView; + if (passLayout->shadowMapTexture.IsValid() && shadowMapTextureView == nullptr) { return false; } @@ -887,13 +985,16 @@ bool BuiltinForwardPipeline::DrawVisibleItem( const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex]; RHI::RHIDescriptorSet* descriptorSet = nullptr; - if (setLayout.usesPerObject || setLayout.usesMaterial || setLayout.usesTexture) { + if (setLayout.usesPerObject || + setLayout.usesMaterial || + setLayout.usesShadowReceiver || + setLayout.usesTexture) { const Core::uint64 objectId = (setLayout.usesPerObject && visibleItem.gameObject != nullptr) ? visibleItem.gameObject->GetID() : 0; const Resources::Material* materialKey = - (setLayout.usesMaterial || setLayout.usesTexture) ? material : nullptr; + (setLayout.usesMaterial || setLayout.usesBaseColorTexture) ? material : nullptr; CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet( passLayoutKey, @@ -903,7 +1004,9 @@ bool BuiltinForwardPipeline::DrawVisibleItem( objectId, materialKey, materialConstants, - textureView); + shadowReceiverConstants, + baseColorTextureView, + shadowMapTextureView); if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) { return false; } diff --git a/tests/RHI/unit/test_views.cpp b/tests/RHI/unit/test_views.cpp index fd20a89e..8a0c7cf5 100644 --- a/tests/RHI/unit/test_views.cpp +++ b/tests/RHI/unit/test_views.cpp @@ -154,6 +154,38 @@ TEST_P(RHITestFixture, Device_CreateShaderResourceView_FromInitialDataTexture) { delete texture; } +TEST_P(RHITestFixture, Device_CreateShaderResourceView_FromDepthTexture) { + TextureDesc texDesc = {}; + texDesc.width = 256; + texDesc.height = 256; + texDesc.depth = 1; + texDesc.mipLevels = 1; + texDesc.arraySize = 1; + texDesc.format = static_cast(Format::D24_UNorm_S8_UInt); + texDesc.textureType = static_cast(TextureType::Texture2D); + texDesc.sampleCount = 1; + texDesc.sampleQuality = 0; + texDesc.flags = 0; + + RHITexture* texture = GetDevice()->CreateTexture(texDesc); + ASSERT_NE(texture, nullptr); + + ResourceViewDesc viewDesc = {}; + viewDesc.format = static_cast(Format::D24_UNorm_S8_UInt); + viewDesc.dimension = ResourceViewDimension::Texture2D; + viewDesc.mipLevel = 0; + + RHIResourceView* srv = GetDevice()->CreateShaderResourceView(texture, viewDesc); + ASSERT_NE(srv, nullptr); + EXPECT_TRUE(srv->IsValid()); + EXPECT_EQ(srv->GetViewType(), ResourceViewType::ShaderResource); + + srv->Shutdown(); + delete srv; + texture->Shutdown(); + delete texture; +} + TEST_P(RHITestFixture, Device_CreateVertexBufferView) { BufferDesc bufferDesc = {}; bufferDesc.size = 256; diff --git a/tests/Rendering/unit/test_builtin_forward_pipeline.cpp b/tests/Rendering/unit/test_builtin_forward_pipeline.cpp index c8fc42ef..25290d4b 100644 --- a/tests/Rendering/unit/test_builtin_forward_pipeline.cpp +++ b/tests/Rendering/unit/test_builtin_forward_pipeline.cpp @@ -55,7 +55,7 @@ TEST(BuiltinForwardPipeline_Test, BuiltinForwardShaderDeclaresExplicitForwardRes const ShaderPass* pass = shader->FindPass("ForwardLit"); ASSERT_NE(pass, nullptr); - ASSERT_EQ(pass->resources.Size(), 4u); + ASSERT_EQ(pass->resources.Size(), 7u); EXPECT_EQ(pass->resources[0].semantic, "PerObject"); EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer); @@ -77,6 +77,21 @@ TEST(BuiltinForwardPipeline_Test, BuiltinForwardShaderDeclaresExplicitForwardRes EXPECT_EQ(pass->resources[3].set, 4u); EXPECT_EQ(pass->resources[3].binding, 0u); + EXPECT_EQ(pass->resources[4].semantic, "ShadowReceiver"); + EXPECT_EQ(pass->resources[4].type, ShaderResourceType::ConstantBuffer); + EXPECT_EQ(pass->resources[4].set, 5u); + EXPECT_EQ(pass->resources[4].binding, 0u); + + EXPECT_EQ(pass->resources[5].semantic, "ShadowMapTexture"); + EXPECT_EQ(pass->resources[5].type, ShaderResourceType::Texture2D); + EXPECT_EQ(pass->resources[5].set, 6u); + EXPECT_EQ(pass->resources[5].binding, 0u); + + EXPECT_EQ(pass->resources[6].semantic, "ShadowMapSampler"); + EXPECT_EQ(pass->resources[6].type, ShaderResourceType::Sampler); + EXPECT_EQ(pass->resources[6].set, 7u); + EXPECT_EQ(pass->resources[6].binding, 0u); + delete shader; } @@ -133,10 +148,13 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromExplic EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr(); EXPECT_TRUE(plan.perObject.IsValid()); EXPECT_TRUE(plan.material.IsValid()); + EXPECT_TRUE(plan.shadowReceiver.IsValid()); EXPECT_TRUE(plan.baseColorTexture.IsValid()); EXPECT_TRUE(plan.linearClampSampler.IsValid()); + EXPECT_TRUE(plan.shadowMapTexture.IsValid()); + EXPECT_TRUE(plan.shadowMapSampler.IsValid()); EXPECT_EQ(plan.firstDescriptorSet, 1u); - EXPECT_EQ(plan.descriptorSetCount, 4u); + EXPECT_EQ(plan.descriptorSetCount, 7u); EXPECT_TRUE(plan.usesConstantBuffers); EXPECT_TRUE(plan.usesTextures); EXPECT_TRUE(plan.usesSamplers); diff --git a/tests/Rendering/unit/test_camera_scene_renderer.cpp b/tests/Rendering/unit/test_camera_scene_renderer.cpp index ba2e89e6..0278062e 100644 --- a/tests/Rendering/unit/test_camera_scene_renderer.cpp +++ b/tests/Rendering/unit/test_camera_scene_renderer.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,10 @@ struct MockPipelineState { size_t lastVisibleItemCount = 0; RenderClearFlags lastClearFlags = RenderClearFlags::All; XCEngine::Math::Color lastClearColor = XCEngine::Math::Color::Black(); + bool lastHasMainDirectionalShadow = false; + XCEngine::RHI::RHIResourceView* lastShadowMap = nullptr; + XCEngine::Math::Matrix4x4 lastShadowViewProjection = XCEngine::Math::Matrix4x4::Identity(); + XCEngine::Math::Vector4 lastShadowParams = XCEngine::Math::Vector4::Zero(); std::vector renderedCameras; std::vector renderedClearFlags; std::vector renderedClearColors; @@ -52,10 +57,14 @@ struct MockShadowAllocationState { int createDepthViewCalls = 0; int shutdownDepthViewCalls = 0; int destroyDepthViewCalls = 0; + int createShaderViewCalls = 0; + int shutdownShaderViewCalls = 0; + int destroyShaderViewCalls = 0; uint32_t lastTextureWidth = 0; uint32_t lastTextureHeight = 0; XCEngine::RHI::Format lastTextureFormat = XCEngine::RHI::Format::Unknown; XCEngine::RHI::Format lastDepthViewFormat = XCEngine::RHI::Format::Unknown; + XCEngine::RHI::Format lastShaderViewFormat = XCEngine::RHI::Format::Unknown; }; class MockShadowTexture final : public XCEngine::RHI::RHITexture { @@ -114,11 +123,19 @@ public: } ~MockShadowView() override { - ++m_state->destroyDepthViewCalls; + if (m_viewType == XCEngine::RHI::ResourceViewType::ShaderResource) { + ++m_state->destroyShaderViewCalls; + } else { + ++m_state->destroyDepthViewCalls; + } } void Shutdown() override { - ++m_state->shutdownDepthViewCalls; + if (m_viewType == XCEngine::RHI::ResourceViewType::ShaderResource) { + ++m_state->shutdownShaderViewCalls; + } else { + ++m_state->shutdownDepthViewCalls; + } } void* GetNativeHandle() override { return nullptr; } @@ -217,7 +234,15 @@ public: XCEngine::RHI::RHIResourceView* CreateShaderResourceView( XCEngine::RHI::RHITexture*, - const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; } + const XCEngine::RHI::ResourceViewDesc& desc) override { + ++m_state->createShaderViewCalls; + m_state->lastShaderViewFormat = static_cast(desc.format); + return new MockShadowView( + m_state, + XCEngine::RHI::ResourceViewType::ShaderResource, + static_cast(desc.format), + desc.dimension); + } XCEngine::RHI::RHIResourceView* CreateUnorderedAccessView( XCEngine::RHI::RHITexture*, const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; } @@ -271,6 +296,10 @@ public: m_state->lastVisibleItemCount = sceneData.visibleItems.size(); m_state->lastClearFlags = sceneData.cameraData.clearFlags; m_state->lastClearColor = sceneData.cameraData.clearColor; + m_state->lastHasMainDirectionalShadow = sceneData.lighting.HasMainDirectionalShadow(); + m_state->lastShadowMap = sceneData.lighting.mainDirectionalShadow.shadowMap; + m_state->lastShadowViewProjection = sceneData.lighting.mainDirectionalShadow.viewProjection; + m_state->lastShadowParams = sceneData.lighting.mainDirectionalShadow.shadowParams; m_state->renderedCameras.push_back(sceneData.camera); m_state->renderedClearFlags.push_back(sceneData.cameraData.clearFlags); m_state->renderedClearColors.push_back(sceneData.cameraData.clearColor); @@ -677,6 +706,8 @@ TEST(CameraRenderer_Test, AutoAllocatesDirectionalShadowSurfaceFromShadowPlan) { request.directionalShadow.cameraData.viewportHeight = 128; request.directionalShadow.cameraData.clearFlags = RenderClearFlags::Depth; request.directionalShadow.cameraData.worldPosition = XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f); + request.directionalShadow.cameraData.viewProjection = + XCEngine::Math::Matrix4x4::Translation(XCEngine::Math::Vector3(11.0f, 12.0f, 13.0f)); ASSERT_TRUE(renderer.Render(request)); EXPECT_EQ( @@ -693,14 +724,27 @@ TEST(CameraRenderer_Test, AutoAllocatesDirectionalShadowSurfaceFromShadowPlan) { EXPECT_EQ(shadowPassRaw->lastWorldPosition, XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f)); EXPECT_EQ(allocationState->createTextureCalls, 1); EXPECT_EQ(allocationState->createDepthViewCalls, 1); + EXPECT_EQ(allocationState->createShaderViewCalls, 1); EXPECT_EQ(allocationState->lastTextureWidth, 256u); EXPECT_EQ(allocationState->lastTextureHeight, 128u); EXPECT_EQ(allocationState->lastTextureFormat, XCEngine::RHI::Format::D24_UNorm_S8_UInt); EXPECT_EQ(allocationState->lastDepthViewFormat, XCEngine::RHI::Format::D24_UNorm_S8_UInt); + EXPECT_EQ(allocationState->lastShaderViewFormat, XCEngine::RHI::Format::D24_UNorm_S8_UInt); EXPECT_EQ(allocationState->shutdownDepthViewCalls, 1); + EXPECT_EQ(allocationState->shutdownShaderViewCalls, 1); EXPECT_EQ(allocationState->shutdownTextureCalls, 1); EXPECT_EQ(allocationState->destroyDepthViewCalls, 1); + EXPECT_EQ(allocationState->destroyShaderViewCalls, 1); EXPECT_EQ(allocationState->destroyTextureCalls, 1); + EXPECT_TRUE(pipelineState->lastHasMainDirectionalShadow); + EXPECT_NE(pipelineState->lastShadowMap, nullptr); + EXPECT_FLOAT_EQ(pipelineState->lastShadowViewProjection.m[0][3], 11.0f); + EXPECT_FLOAT_EQ(pipelineState->lastShadowViewProjection.m[1][3], 12.0f); + EXPECT_FLOAT_EQ(pipelineState->lastShadowViewProjection.m[2][3], 13.0f); + EXPECT_FLOAT_EQ(pipelineState->lastShadowParams.x, 0.0015f); + EXPECT_FLOAT_EQ(pipelineState->lastShadowParams.y, 1.0f / 256.0f); + EXPECT_FLOAT_EQ(pipelineState->lastShadowParams.z, 1.0f / 128.0f); + EXPECT_FLOAT_EQ(pipelineState->lastShadowParams.w, 0.85f); } TEST(CameraRenderer_Test, StopsRenderingWhenShadowCasterRequestIsInvalid) { diff --git a/tests/Resources/Shader/test_shader_loader.cpp b/tests/Resources/Shader/test_shader_loader.cpp index b478e555..b9f93ae0 100644 --- a/tests/Resources/Shader/test_shader_loader.cpp +++ b/tests/Resources/Shader/test_shader_loader.cpp @@ -137,7 +137,10 @@ TEST(ShaderLoader, LoadShaderManifestBuildsMultiPassBackendVariants) { manifest << " { \"name\": \"PerObjectConstants\", \"type\": \"ConstantBuffer\", \"set\": 1, \"binding\": 0, \"semantic\": \"PerObject\" },\n"; manifest << " { \"name\": \"MaterialConstants\", \"type\": \"ConstantBuffer\", \"set\": 2, \"binding\": 0, \"semantic\": \"Material\" },\n"; manifest << " { \"name\": \"BaseColorTexture\", \"type\": \"Texture2D\", \"set\": 3, \"binding\": 0, \"semantic\": \"BaseColorTexture\" },\n"; - manifest << " { \"name\": \"LinearClampSampler\", \"type\": \"Sampler\", \"set\": 4, \"binding\": 0, \"semantic\": \"LinearClampSampler\" }\n"; + manifest << " { \"name\": \"LinearClampSampler\", \"type\": \"Sampler\", \"set\": 4, \"binding\": 0, \"semantic\": \"LinearClampSampler\" },\n"; + manifest << " { \"name\": \"ShadowReceiverConstants\", \"type\": \"ConstantBuffer\", \"set\": 5, \"binding\": 0, \"semantic\": \"ShadowReceiver\" },\n"; + manifest << " { \"name\": \"ShadowMapTexture\", \"type\": \"Texture2D\", \"set\": 6, \"binding\": 0, \"semantic\": \"ShadowMapTexture\" },\n"; + manifest << " { \"name\": \"ShadowMapSampler\", \"type\": \"Sampler\", \"set\": 7, \"binding\": 0, \"semantic\": \"ShadowMapSampler\" }\n"; manifest << " ],\n"; manifest << " \"variants\": [\n"; manifest << " { \"stage\": \"Vertex\", \"backend\": \"D3D12\", \"language\": \"HLSL\", \"source\": \"stages/forward_lit.vs.hlsl\", \"entryPoint\": \"MainVS\", \"profile\": \"vs_5_0\" },\n"; @@ -190,7 +193,7 @@ TEST(ShaderLoader, LoadShaderManifestBuildsMultiPassBackendVariants) { const ShaderPass* forwardLitPass = shader->FindPass("ForwardLit"); ASSERT_NE(forwardLitPass, nullptr); ASSERT_EQ(forwardLitPass->tags.Size(), 2u); - ASSERT_EQ(forwardLitPass->resources.Size(), 4u); + ASSERT_EQ(forwardLitPass->resources.Size(), 7u); EXPECT_EQ(forwardLitPass->tags[0].name, "LightMode"); EXPECT_EQ(forwardLitPass->tags[0].value, "ForwardBase"); EXPECT_EQ(forwardLitPass->tags[1].name, "Queue"); @@ -274,6 +277,9 @@ TEST(ShaderLoader, LoadUnityLikeShaderAuthoringBuildsRuntimeContract) { MaterialConstants (ConstantBuffer, 2, 0) [Semantic(Material)] BaseColorTexture (Texture2D, 3, 0) [Semantic(BaseColorTexture)] LinearClampSampler (Sampler, 4, 0) [Semantic(LinearClampSampler)] + ShadowReceiverConstants (ConstantBuffer, 5, 0) [Semantic(ShadowReceiver)] + ShadowMapTexture (Texture2D, 6, 0) [Semantic(ShadowMapTexture)] + ShadowMapSampler (Sampler, 7, 0) [Semantic(ShadowMapSampler)] } HLSLPROGRAM #pragma vertex MainVS @@ -325,7 +331,7 @@ TEST(ShaderLoader, LoadUnityLikeShaderAuthoringBuildsRuntimeContract) { const ShaderPass* forwardLitPass = shader->FindPass("ForwardLit"); ASSERT_NE(forwardLitPass, nullptr); ASSERT_EQ(forwardLitPass->tags.Size(), 2u); - ASSERT_EQ(forwardLitPass->resources.Size(), 4u); + ASSERT_EQ(forwardLitPass->resources.Size(), 7u); EXPECT_EQ(forwardLitPass->tags[0].name, "Queue"); EXPECT_EQ(forwardLitPass->tags[0].value, "Geometry"); EXPECT_EQ(forwardLitPass->tags[1].name, "LightMode"); @@ -671,7 +677,7 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) { ASSERT_EQ(shader->GetProperties().Size(), 2u); ASSERT_EQ(pass->variants.Size(), 6u); ASSERT_EQ(pass->tags.Size(), 1u); - ASSERT_EQ(pass->resources.Size(), 4u); + ASSERT_EQ(pass->resources.Size(), 7u); EXPECT_EQ(pass->tags[0].name, "LightMode"); EXPECT_EQ(pass->tags[0].value, "ForwardBase"); @@ -693,6 +699,22 @@ TEST(ShaderLoader, LoadBuiltinForwardLitShaderBuildsBackendVariants) { EXPECT_EQ(perObjectBinding->binding, 0u); EXPECT_EQ(perObjectBinding->semantic, "PerObject"); + const ShaderResourceBindingDesc* shadowReceiverBinding = + shader->FindPassResourceBinding("ForwardLit", "ShadowReceiverConstants"); + ASSERT_NE(shadowReceiverBinding, nullptr); + EXPECT_EQ(shadowReceiverBinding->type, ShaderResourceType::ConstantBuffer); + EXPECT_EQ(shadowReceiverBinding->set, 5u); + EXPECT_EQ(shadowReceiverBinding->binding, 0u); + EXPECT_EQ(shadowReceiverBinding->semantic, "ShadowReceiver"); + + const ShaderResourceBindingDesc* shadowTextureBinding = + shader->FindPassResourceBinding("ForwardLit", "ShadowMapTexture"); + ASSERT_NE(shadowTextureBinding, nullptr); + EXPECT_EQ(shadowTextureBinding->type, ShaderResourceType::Texture2D); + EXPECT_EQ(shadowTextureBinding->set, 6u); + EXPECT_EQ(shadowTextureBinding->binding, 0u); + EXPECT_EQ(shadowTextureBinding->semantic, "ShadowMapTexture"); + EXPECT_NE(shader->FindVariant("ForwardLit", ShaderType::Vertex, ShaderBackend::D3D12), nullptr); EXPECT_NE(shader->FindVariant("ForwardLit", ShaderType::Fragment, ShaderBackend::D3D12), nullptr); EXPECT_NE(shader->FindVariant("ForwardLit", ShaderType::Vertex, ShaderBackend::OpenGL), nullptr);