Add forward shadow receiving support

This commit is contained in:
2026-04-04 23:01:34 +08:00
parent 353d129613
commit 96a44da2cb
22 changed files with 889 additions and 89 deletions

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -143,8 +143,12 @@ private:
ComPtr<ID3D12GraphicsCommandList> m_commandList;
ComPtr<ID3D12CommandAllocator> m_commandAllocator;
ComPtr<ID3D12DescriptorHeap> m_rtvHeap;
ComPtr<ID3D12DescriptorHeap> m_shaderVisibleCbvSrvUavHeap;
ComPtr<ID3D12DescriptorHeap> m_shaderVisibleSamplerHeap;
ID3D12Device* m_device = nullptr;
CommandQueueType m_type;
uint32_t m_shaderVisibleCbvSrvUavHeapCapacity = 0;
uint32_t m_shaderVisibleSamplerHeapCapacity = 0;
std::unordered_map<ID3D12Resource*, ResourceStates> m_resourceStateMap;
std::vector<ID3D12Resource*> m_trackedResources;

View File

@@ -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; }

View File

@@ -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<OwnedDescriptorSet> 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<PipelineStateKey, RHI::RHIPipelineState*, PipelineStateKeyHash> m_pipelineStates;
std::unordered_map<DynamicDescriptorSetKey, CachedDescriptorSet, DynamicDescriptorSetKeyHash> 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;

View File

@@ -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:

View File

@@ -1,5 +1,6 @@
#pragma once
#include <XCEngine/Core/Math/Vector4.h>
#include <XCEngine/Core/Math/Vector3.h>
#include <XCEngine/Rendering/RenderCameraData.h>
#include <XCEngine/Rendering/VisibleRenderObject.h>
@@ -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 {

View File

@@ -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<DescriptorType>(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<SIZE_T>(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<UINT64>(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<D3D12PipelineLayout*>(pipelineLayout);
SetPipelineLayout(d3d12Layout);
std::vector<ID3D12DescriptorHeap*> 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<D3D12DescriptorSet*>(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<ID3D12DescriptorHeap>& heap,
uint32_t& capacity) -> bool {
if (requiredDescriptors == 0) {
return true;
}
if (heap != nullptr && capacity >= requiredDescriptors) {
return true;
}
const uint32_t newCapacity = std::max<uint32_t>(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<ID3D12DescriptorHeap> 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<ID3D12DescriptorHeap*> 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<uint32_t>(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<ID3D12DescriptorHeap*> 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<D3D12DescriptorSet*>(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<ID3D12DescriptorHeap>& heap,
uint32_t& capacity) -> bool {
if (requiredDescriptors == 0) {
return true;
}
if (heap != nullptr && capacity >= requiredDescriptors) {
return true;
}
const uint32_t newCapacity = std::max<uint32_t>(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<ID3D12DescriptorHeap> 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<ID3D12DescriptorHeap*> 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<uint32_t>(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()

View File

@@ -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 ||

View File

@@ -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<Format>(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<D3D12_RESOURCE_FLAGS>(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<Format>(desc.format));
const Format format = desc.format != 0 ? static_cast<Format>(desc.format) : texture->GetFormat();
dsvDesc.Format = ResolveD3D12DepthStencilViewFormat(format);
dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
auto heap = std::make_unique<D3D12DescriptorHeap>();
@@ -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<Format>(desc.format));
const Format format = desc.format != 0 ? static_cast<Format>(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<D3D12DescriptorHeap>();

View File

@@ -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;

View File

@@ -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<float>(plan.mapWidth),
1.0f / static_cast<float>(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;

View File

@@ -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<uint32_t>(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;
}

View File

@@ -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<uint32_t>(Format::D24_UNorm_S8_UInt);
texDesc.textureType = static_cast<uint32_t>(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<uint32_t>(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;

View File

@@ -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);

View File

@@ -6,6 +6,7 @@
#include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHIResourceView.h>
#include <XCEngine/RHI/RHITexture.h>
#include <XCEngine/Core/Math/Vector4.h>
#include <XCEngine/Rendering/CameraRenderer.h>
#include <XCEngine/Rendering/ObjectIdPass.h>
#include <XCEngine/Rendering/RenderPipelineAsset.h>
@@ -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<CameraComponent*> renderedCameras;
std::vector<RenderClearFlags> renderedClearFlags;
std::vector<XCEngine::Math::Color> 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<XCEngine::RHI::Format>(desc.format);
return new MockShadowView(
m_state,
XCEngine::RHI::ResourceViewType::ShaderResource,
static_cast<XCEngine::RHI::Format>(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) {

View File

@@ -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);