Enable depth-only shadow pass execution

This commit is contained in:
2026-04-04 20:35:47 +08:00
parent a548e0d0a9
commit 353d129613
8 changed files with 494 additions and 59 deletions

View File

@@ -45,6 +45,7 @@ public:
VkPipeline GetPipeline() const { return m_pipeline; }
VkPipelineLayout GetPipelineLayout() const { return m_pipelineLayout; }
VkRenderPass GetRenderPass() const { return m_renderPass; }
uint32_t GetRenderTargetCount() const { return m_renderTargetCount; }
bool HasDepthStencilAttachment() const {
return m_depthStencilFormat != 0 && static_cast<Format>(m_depthStencilFormat) != Format::Unknown;
}

View File

@@ -22,14 +22,14 @@ struct ScenePassRenderRequest {
RenderCameraData cameraDataOverride = {};
bool IsRequested() const {
return !surface.GetColorAttachments().empty();
return surface.GetDepthAttachment() != nullptr ||
!surface.GetColorAttachments().empty();
}
bool IsValid() const {
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
return !colorAttachments.empty() &&
colorAttachments[0] != nullptr &&
surface.GetDepthAttachment() != nullptr &&
return surface.GetDepthAttachment() != nullptr &&
(colorAttachments.empty() || colorAttachments[0] != nullptr) &&
surface.GetRenderAreaWidth() > 0 &&
surface.GetRenderAreaHeight() > 0;
}

View File

@@ -102,11 +102,17 @@ private:
Resources::MaterialRenderState renderState;
const Resources::Shader* shader = nullptr;
Containers::String passName;
uint32_t renderTargetCount = 0;
uint32_t renderTargetFormat = 0;
uint32_t depthStencilFormat = 0;
bool operator==(const PipelineStateKey& other) const {
return renderState == other.renderState &&
shader == other.shader &&
passName == other.passName;
passName == other.passName &&
renderTargetCount == other.renderTargetCount &&
renderTargetFormat == other.renderTargetFormat &&
depthStencilFormat == other.depthStencilFormat;
}
};
@@ -115,6 +121,9 @@ private:
size_t hash = MaterialRenderStateHash()(key.renderState);
hash ^= reinterpret_cast<size_t>(key.shader) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<Containers::String>{}(key.passName) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<uint32_t>{}(key.renderTargetCount) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<uint32_t>{}(key.renderTargetFormat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
hash ^= std::hash<uint32_t>{}(key.depthStencilFormat) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
return hash;
}
};
@@ -133,6 +142,7 @@ private:
const ResolvedShaderPass& resolvedShaderPass);
RHI::RHIPipelineState* GetOrCreatePipelineState(
const RenderContext& context,
const RenderSurface& surface,
const Resources::Material* material);
bool CreateOwnedDescriptorSet(
const BuiltinPassSetLayoutMetadata& setLayout,
@@ -145,6 +155,7 @@ private:
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
bool DrawVisibleItem(
const RenderContext& context,
const RenderSurface& surface,
const RenderSceneData& sceneData,
const VisibleRenderItem& visibleItem);

View File

@@ -1024,8 +1024,12 @@ bool VulkanCommandList::EnsureGraphicsRenderPass() {
return false;
}
const bool expectsColorAttachment = m_currentPipelineState->GetRenderTargetCount() > 0;
auto* colorView = static_cast<VulkanResourceView*>(m_currentColorTarget);
if (colorView == nullptr || colorView->GetTexture() == nullptr || colorView->GetImageView() == VK_NULL_HANDLE) {
if (expectsColorAttachment &&
(colorView == nullptr ||
colorView->GetTexture() == nullptr ||
colorView->GetImageView() == VK_NULL_HANDLE)) {
return false;
}
@@ -1036,23 +1040,42 @@ bool VulkanCommandList::EnsureGraphicsRenderPass() {
return false;
}
VulkanTexture* texture = colorView->GetTexture();
TransitionTexture(texture, ResourceStates::RenderTarget);
if (!expectsColorAttachment && !expectsDepthAttachment) {
return false;
}
VulkanTexture* colorTexture = expectsColorAttachment ? colorView->GetTexture() : nullptr;
VulkanTexture* depthTexture = expectsDepthAttachment ? depthView->GetTexture() : nullptr;
if (expectsColorAttachment) {
TransitionTexture(colorTexture, ResourceStates::RenderTarget);
}
if (expectsDepthAttachment) {
TransitionTexture(depthView->GetTexture(), ResourceStates::DepthWrite);
TransitionTexture(depthTexture, ResourceStates::DepthWrite);
}
VkFramebufferCreateInfo framebufferInfo = {};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = m_currentPipelineState->GetRenderPass();
VkImageView attachments[2] = { colorView->GetImageView(), VK_NULL_HANDLE };
framebufferInfo.attachmentCount = expectsDepthAttachment ? 2 : 1;
if (expectsDepthAttachment) {
attachments[1] = depthView->GetImageView();
VkImageView attachments[2] = { VK_NULL_HANDLE, VK_NULL_HANDLE };
uint32_t attachmentCount = 0;
uint32_t framebufferWidth = 0;
uint32_t framebufferHeight = 0;
if (expectsColorAttachment) {
attachments[attachmentCount++] = colorView->GetImageView();
framebufferWidth = colorTexture->GetWidth();
framebufferHeight = colorTexture->GetHeight();
}
if (expectsDepthAttachment) {
attachments[attachmentCount++] = depthView->GetImageView();
if (framebufferWidth == 0 || framebufferHeight == 0) {
framebufferWidth = depthTexture->GetWidth();
framebufferHeight = depthTexture->GetHeight();
}
}
framebufferInfo.attachmentCount = attachmentCount;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = texture->GetWidth();
framebufferInfo.height = texture->GetHeight();
framebufferInfo.width = framebufferWidth;
framebufferInfo.height = framebufferHeight;
framebufferInfo.layers = 1;
VkFramebuffer framebuffer = VK_NULL_HANDLE;
@@ -1066,7 +1089,7 @@ bool VulkanCommandList::EnsureGraphicsRenderPass() {
renderPassInfo.renderPass = m_currentPipelineState->GetRenderPass();
renderPassInfo.framebuffer = framebuffer;
renderPassInfo.renderArea.offset = { 0, 0 };
renderPassInfo.renderArea.extent = { texture->GetWidth(), texture->GetHeight() };
renderPassInfo.renderArea.extent = { framebufferWidth, framebufferHeight };
renderPassInfo.clearValueCount = 0;
renderPassInfo.pClearValues = nullptr;

View File

@@ -65,12 +65,17 @@ bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelin
const bool hasVertexShader = HasShaderPayload(desc.vertexShader);
const bool hasFragmentShader = HasShaderPayload(desc.fragmentShader);
const bool hasColorAttachment = m_renderTargetCount == 1 && m_renderTargetFormats[0] != 0;
const bool hasDepthAttachment = HasDepthStencilAttachment();
if (!hasVertexShader && !hasFragmentShader) {
m_isConfigured = true;
return true;
}
if (m_renderTargetCount != 1 || m_renderTargetFormats[0] == 0 || !hasVertexShader || !hasFragmentShader) {
if (m_renderTargetCount > 1 ||
(!hasColorAttachment && !hasDepthAttachment) ||
!hasVertexShader ||
!hasFragmentShader) {
Shutdown();
return false;
}
@@ -124,26 +129,29 @@ bool VulkanPipelineState::CreateGraphicsPipeline(const GraphicsPipelineDesc& des
return false;
}
const bool hasColorAttachment = m_renderTargetCount == 1 && m_renderTargetFormats[0] != 0;
const bool hasDepthAttachment = HasDepthStencilAttachment();
std::vector<VkAttachmentDescription> attachments;
attachments.reserve(2);
VkAttachmentDescription colorAttachment = {};
colorAttachment.format = ToVulkanFormat(static_cast<Format>(m_renderTargetFormats[0]));
colorAttachment.samples = ToVulkanSampleCount(m_sampleCount);
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments.push_back(colorAttachment);
attachments.reserve(hasDepthAttachment ? 2u : 1u);
VkAttachmentReference colorAttachmentRef = {};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
if (hasColorAttachment) {
VkAttachmentDescription colorAttachment = {};
colorAttachment.format = ToVulkanFormat(static_cast<Format>(m_renderTargetFormats[0]));
colorAttachment.samples = ToVulkanSampleCount(m_sampleCount);
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments.push_back(colorAttachment);
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
VkAttachmentReference depthAttachmentRef = {};
const bool hasDepthAttachment = HasDepthStencilAttachment();
if (hasDepthAttachment) {
VkAttachmentDescription depthAttachment = {};
depthAttachment.format = ToVulkanFormat(static_cast<Format>(m_depthStencilFormat));
@@ -156,14 +164,14 @@ bool VulkanPipelineState::CreateGraphicsPipeline(const GraphicsPipelineDesc& des
depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments.push_back(depthAttachment);
depthAttachmentRef.attachment = 1;
depthAttachmentRef.attachment = hasColorAttachment ? 1u : 0u;
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
subpass.colorAttachmentCount = hasColorAttachment ? 1u : 0u;
subpass.pColorAttachments = hasColorAttachment ? &colorAttachmentRef : nullptr;
if (hasDepthAttachment) {
subpass.pDepthStencilAttachment = &depthAttachmentRef;
}
@@ -272,8 +280,8 @@ bool VulkanPipelineState::CreateGraphicsPipeline(const GraphicsPipelineDesc& des
VkPipelineColorBlendStateCreateInfo colorBlending = {};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.attachmentCount = hasColorAttachment ? 1u : 0u;
colorBlending.pAttachments = hasColorAttachment ? &colorBlendAttachment : nullptr;
VkPipelineDepthStencilStateCreateInfo depthStencil = {};
depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
@@ -372,7 +380,10 @@ void VulkanPipelineState::SetComputeShader(RHIShader* shader) {
PipelineStateHash VulkanPipelineState::GetHash() const {
PipelineStateHash hash = {};
hash.topologyHash = m_topologyType;
hash.renderTargetHash = m_renderTargetCount ^ (m_renderTargetFormats[0] << 8);
hash.renderTargetHash =
m_renderTargetCount ^
(m_renderTargetFormats[0] << 8) ^
(m_depthStencilFormat << 16);
return hash;
}

View File

@@ -6,6 +6,9 @@
#include "Rendering/Pipelines/BuiltinForwardPipeline.h"
#include "Rendering/RenderPipelineAsset.h"
#include "Rendering/RenderSurface.h"
#include "RHI/RHIDevice.h"
#include "RHI/RHIResourceView.h"
#include "RHI/RHITexture.h"
#include "Scene/Scene.h"
namespace XCEngine {
@@ -13,6 +16,36 @@ namespace Rendering {
namespace {
struct OwnedShadowSurfaceResources {
RHI::RHITexture* depthTexture = nullptr;
RHI::RHIResourceView* depthView = nullptr;
RenderSurface surface = {};
OwnedShadowSurfaceResources() = default;
OwnedShadowSurfaceResources(const OwnedShadowSurfaceResources&) = delete;
OwnedShadowSurfaceResources& operator=(const OwnedShadowSurfaceResources&) = delete;
~OwnedShadowSurfaceResources() {
Reset();
}
void Reset() {
if (depthView != nullptr) {
depthView->Shutdown();
delete depthView;
depthView = nullptr;
}
if (depthTexture != nullptr) {
depthTexture->Shutdown();
delete depthTexture;
depthTexture = nullptr;
}
surface = RenderSurface();
}
};
bool InitializePassSequence(
RenderPassSequence* sequence,
const RenderContext& context,
@@ -110,6 +143,52 @@ bool ExecuteScenePassRequest(
return pass->Execute(passContext);
}
bool CreateDirectionalShadowSurface(
const RenderContext& context,
const DirectionalShadowRenderPlan& plan,
OwnedShadowSurfaceResources& outResources) {
if (!context.IsValid() || !plan.IsValid()) {
return false;
}
RHI::TextureDesc depthDesc = {};
depthDesc.width = plan.mapWidth;
depthDesc.height = plan.mapHeight;
depthDesc.depth = 1;
depthDesc.mipLevels = 1;
depthDesc.arraySize = 1;
depthDesc.format = static_cast<uint32_t>(RHI::Format::D24_UNorm_S8_UInt);
depthDesc.textureType = static_cast<uint32_t>(RHI::TextureType::Texture2D);
depthDesc.sampleCount = 1;
depthDesc.sampleQuality = 0;
depthDesc.flags = 0;
RHI::RHITexture* depthTexture = context.device->CreateTexture(depthDesc);
if (depthTexture == nullptr) {
return false;
}
RHI::ResourceViewDesc depthViewDesc = {};
depthViewDesc.format = static_cast<uint32_t>(RHI::Format::D24_UNorm_S8_UInt);
depthViewDesc.dimension = RHI::ResourceViewDimension::Texture2D;
depthViewDesc.mipLevel = 0;
RHI::RHIResourceView* depthView =
context.device->CreateDepthStencilView(depthTexture, depthViewDesc);
if (depthView == nullptr) {
depthTexture->Shutdown();
delete depthTexture;
return false;
}
outResources.Reset();
outResources.depthTexture = depthTexture;
outResources.depthView = depthView;
outResources.surface = RenderSurface(plan.mapWidth, plan.mapHeight);
outResources.surface.SetDepthAttachment(depthView);
return true;
}
} // namespace
CameraRenderer::CameraRenderer()
@@ -237,14 +316,32 @@ bool CameraRenderer::Render(
!request.depthOnly.IsValid()) {
return false;
}
if (request.shadowCaster.IsRequested() &&
!request.shadowCaster.IsValid()) {
return false;
}
if (request.objectId.IsRequested() &&
!request.objectId.IsValid()) {
return false;
}
ShadowCasterRenderRequest resolvedShadowCaster = request.shadowCaster;
OwnedShadowSurfaceResources ownedShadowSurface;
if (resolvedShadowCaster.IsRequested()) {
if (!resolvedShadowCaster.IsValid()) {
return false;
}
} else if (request.directionalShadow.IsValid()) {
if (!CreateDirectionalShadowSurface(
request.context,
request.directionalShadow,
ownedShadowSurface)) {
return false;
}
resolvedShadowCaster.surface = ownedShadowSurface.surface;
if (!resolvedShadowCaster.hasCameraDataOverride) {
resolvedShadowCaster.hasCameraDataOverride = true;
resolvedShadowCaster.cameraDataOverride = request.directionalShadow.cameraData;
}
}
RenderSceneData sceneData = m_sceneExtractor.ExtractForCamera(
*request.scene,
*request.camera,
@@ -279,7 +376,7 @@ bool CameraRenderer::Render(
if (!ExecuteScenePassRequest(
m_shadowCasterPass.get(),
request.shadowCaster,
resolvedShadowCaster,
request.context,
sceneData)) {
ShutdownPassSequence(request.preScenePasses, preScenePassesInitialized);

View File

@@ -45,19 +45,47 @@ bool IsSupportedPerObjectOnlyBindingPlan(const BuiltinPassResourceBindingPlan& b
!bindingPlan.usesSamplers;
}
uint32_t ResolveSurfaceColorAttachmentCount(const RenderSurface& surface) {
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
return (!colorAttachments.empty() && colorAttachments[0] != nullptr) ? 1u : 0u;
}
RHI::Format ResolveSurfaceColorFormat(const RenderSurface& surface) {
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
if (colorAttachments.empty() || colorAttachments[0] == nullptr) {
return RHI::Format::Unknown;
}
return colorAttachments[0]->GetFormat();
}
RHI::Format ResolveSurfaceDepthFormat(const RenderSurface& surface) {
if (RHI::RHIResourceView* depthAttachment = surface.GetDepthAttachment();
depthAttachment != nullptr) {
return depthAttachment->GetFormat();
}
return RHI::Format::Unknown;
}
RHI::GraphicsPipelineDesc CreatePipelineDesc(
RHI::RHIType backendType,
RHI::RHIPipelineLayout* pipelineLayout,
const Resources::Shader& shader,
const Containers::String& passName,
const Resources::Material* material,
const RenderSurface& surface,
const RHI::InputLayoutDesc& inputLayout) {
RHI::GraphicsPipelineDesc pipelineDesc = {};
pipelineDesc.pipelineLayout = pipelineLayout;
pipelineDesc.topologyType = static_cast<uint32_t>(RHI::PrimitiveTopologyType::Triangle);
pipelineDesc.renderTargetCount = 1;
pipelineDesc.renderTargetFormats[0] = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(RHI::Format::D24_UNorm_S8_UInt);
pipelineDesc.renderTargetCount = ResolveSurfaceColorAttachmentCount(surface);
if (pipelineDesc.renderTargetCount > 0) {
pipelineDesc.renderTargetFormats[0] =
static_cast<uint32_t>(ResolveSurfaceColorFormat(surface));
}
pipelineDesc.depthStencilFormat =
static_cast<uint32_t>(ResolveSurfaceDepthFormat(surface));
pipelineDesc.sampleCount = 1;
pipelineDesc.inputLayout = inputLayout;
ApplyMaterialRenderState(material, pipelineDesc);
@@ -138,9 +166,10 @@ bool BuiltinDepthStylePassBase::Execute(const RenderPassContext& context) {
}
const std::vector<RHI::RHIResourceView*>& colorAttachments = context.surface.GetColorAttachments();
if (colorAttachments.empty() ||
colorAttachments[0] == nullptr ||
context.surface.GetDepthAttachment() == nullptr) {
RHI::RHIResourceView* depthAttachment = context.surface.GetDepthAttachment();
RHI::RHIResourceView* renderTarget =
(!colorAttachments.empty() ? colorAttachments[0] : nullptr);
if (depthAttachment == nullptr) {
return false;
}
@@ -154,16 +183,20 @@ bool BuiltinDepthStylePassBase::Execute(const RenderPassContext& context) {
}
RHI::RHICommandList* commandList = context.renderContext.commandList;
RHI::RHIResourceView* renderTarget = colorAttachments[0];
if (context.surface.IsAutoTransitionEnabled()) {
if (context.surface.IsAutoTransitionEnabled() && renderTarget != nullptr) {
commandList->TransitionBarrier(
renderTarget,
context.surface.GetColorStateBefore(),
RHI::ResourceStates::RenderTarget);
}
commandList->SetRenderTargets(1, &renderTarget, context.surface.GetDepthAttachment());
RHI::RHIResourceView* renderTargets[] = { renderTarget };
const uint32_t renderTargetCount = renderTarget != nullptr ? 1u : 0u;
commandList->SetRenderTargets(
renderTargetCount,
renderTargetCount > 0 ? renderTargets : nullptr,
depthAttachment);
const RHI::Viewport viewport = {
static_cast<float>(renderArea.x),
@@ -185,11 +218,12 @@ bool BuiltinDepthStylePassBase::Execute(const RenderPassContext& context) {
const Math::Color clearColor = context.sceneData.cameraData.clearColor;
const float clearValues[4] = { clearColor.r, clearColor.g, clearColor.b, clearColor.a };
if (HasRenderClearFlag(context.sceneData.cameraData.clearFlags, RenderClearFlags::Color)) {
if (renderTarget != nullptr &&
HasRenderClearFlag(context.sceneData.cameraData.clearFlags, RenderClearFlags::Color)) {
commandList->ClearRenderTarget(renderTarget, clearValues, 1, clearRects);
}
if (HasRenderClearFlag(context.sceneData.cameraData.clearFlags, RenderClearFlags::Depth)) {
commandList->ClearDepthStencil(context.surface.GetDepthAttachment(), 1.0f, 0, 1, clearRects);
commandList->ClearDepthStencil(depthAttachment, 1.0f, 0, 1, clearRects);
}
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
@@ -199,12 +233,12 @@ bool BuiltinDepthStylePassBase::Execute(const RenderPassContext& context) {
continue;
}
DrawVisibleItem(context.renderContext, context.sceneData, visibleItem);
DrawVisibleItem(context.renderContext, context.surface, context.sceneData, visibleItem);
}
commandList->EndRenderPass();
if (context.surface.IsAutoTransitionEnabled()) {
if (context.surface.IsAutoTransitionEnabled() && renderTarget != nullptr) {
commandList->TransitionBarrier(
renderTarget,
RHI::ResourceStates::RenderTarget,
@@ -433,6 +467,7 @@ BuiltinDepthStylePassBase::PassResourceLayout* BuiltinDepthStylePassBase::GetOrC
RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState(
const RenderContext& context,
const RenderSurface& surface,
const Resources::Material* material) {
const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material);
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
@@ -449,6 +484,9 @@ RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState(
material != nullptr ? material->GetRenderState() : Resources::MaterialRenderState();
pipelineKey.shader = resolvedShaderPass.shader;
pipelineKey.passName = resolvedShaderPass.passName;
pipelineKey.renderTargetCount = ResolveSurfaceColorAttachmentCount(surface);
pipelineKey.renderTargetFormat = static_cast<uint32_t>(ResolveSurfaceColorFormat(surface));
pipelineKey.depthStencilFormat = static_cast<uint32_t>(ResolveSurfaceDepthFormat(surface));
const auto existing = m_pipelineStates.find(pipelineKey);
if (existing != m_pipelineStates.end()) {
@@ -461,6 +499,7 @@ RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState(
*resolvedShaderPass.shader,
resolvedShaderPass.passName,
material,
surface,
BuildCommonInputLayout());
RHI::RHIPipelineState* pipelineState = context.device->CreatePipelineState(pipelineDesc);
if (pipelineState == nullptr || !pipelineState->IsValid()) {
@@ -552,6 +591,7 @@ void BuiltinDepthStylePassBase::DestroyPassResourceLayout(PassResourceLayout& pa
bool BuiltinDepthStylePassBase::DrawVisibleItem(
const RenderContext& context,
const RenderSurface& surface,
const RenderSceneData& sceneData,
const VisibleRenderItem& visibleItem) {
if (visibleItem.mesh == nullptr || visibleItem.gameObject == nullptr) {
@@ -579,7 +619,7 @@ bool BuiltinDepthStylePassBase::DrawVisibleItem(
return false;
}
RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, material);
RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, material);
if (pipelineState == nullptr) {
return false;
}

View File

@@ -1,6 +1,11 @@
#include <gtest/gtest.h>
#include <XCEngine/Components/CameraComponent.h>
#include <XCEngine/RHI/RHICommandList.h>
#include <XCEngine/RHI/RHICommandQueue.h>
#include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHIResourceView.h>
#include <XCEngine/RHI/RHITexture.h>
#include <XCEngine/Rendering/CameraRenderer.h>
#include <XCEngine/Rendering/ObjectIdPass.h>
#include <XCEngine/Rendering/RenderPipelineAsset.h>
@@ -40,6 +45,193 @@ struct MockPipelineState {
std::vector<std::string> eventLog;
};
struct MockShadowAllocationState {
int createTextureCalls = 0;
int shutdownTextureCalls = 0;
int destroyTextureCalls = 0;
int createDepthViewCalls = 0;
int shutdownDepthViewCalls = 0;
int destroyDepthViewCalls = 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;
};
class MockShadowTexture final : public XCEngine::RHI::RHITexture {
public:
MockShadowTexture(
std::shared_ptr<MockShadowAllocationState> state,
uint32_t width,
uint32_t height,
XCEngine::RHI::Format format)
: m_state(std::move(state))
, m_width(width)
, m_height(height)
, m_format(format) {
}
~MockShadowTexture() override {
++m_state->destroyTextureCalls;
}
uint32_t GetWidth() const override { return m_width; }
uint32_t GetHeight() const override { return m_height; }
uint32_t GetDepth() const override { return 1; }
uint32_t GetMipLevels() const override { return 1; }
XCEngine::RHI::Format GetFormat() const override { return m_format; }
XCEngine::RHI::TextureType GetTextureType() const override { return XCEngine::RHI::TextureType::Texture2D; }
XCEngine::RHI::ResourceStates GetState() const override { return m_stateValue; }
void SetState(XCEngine::RHI::ResourceStates state) override { m_stateValue = state; }
void* GetNativeHandle() override { return nullptr; }
const std::string& GetName() const override { return m_name; }
void SetName(const std::string& name) override { m_name = name; }
void Shutdown() override {
++m_state->shutdownTextureCalls;
}
private:
std::shared_ptr<MockShadowAllocationState> m_state;
uint32_t m_width = 0;
uint32_t m_height = 0;
XCEngine::RHI::Format m_format = XCEngine::RHI::Format::Unknown;
XCEngine::RHI::ResourceStates m_stateValue = XCEngine::RHI::ResourceStates::DepthWrite;
std::string m_name;
};
class MockShadowView final : public XCEngine::RHI::RHIResourceView {
public:
MockShadowView(
std::shared_ptr<MockShadowAllocationState> state,
XCEngine::RHI::ResourceViewType viewType,
XCEngine::RHI::Format format,
XCEngine::RHI::ResourceViewDimension dimension)
: m_state(std::move(state))
, m_viewType(viewType)
, m_format(format)
, m_dimension(dimension) {
}
~MockShadowView() override {
++m_state->destroyDepthViewCalls;
}
void Shutdown() override {
++m_state->shutdownDepthViewCalls;
}
void* GetNativeHandle() override { return nullptr; }
bool IsValid() const override { return true; }
XCEngine::RHI::ResourceViewType GetViewType() const override { return m_viewType; }
XCEngine::RHI::ResourceViewDimension GetDimension() const override { return m_dimension; }
XCEngine::RHI::Format GetFormat() const override { return m_format; }
private:
std::shared_ptr<MockShadowAllocationState> m_state;
XCEngine::RHI::ResourceViewType m_viewType = XCEngine::RHI::ResourceViewType::DepthStencil;
XCEngine::RHI::Format m_format = XCEngine::RHI::Format::Unknown;
XCEngine::RHI::ResourceViewDimension m_dimension = XCEngine::RHI::ResourceViewDimension::Unknown;
};
class MockShadowDevice final : public XCEngine::RHI::RHIDevice {
public:
explicit MockShadowDevice(std::shared_ptr<MockShadowAllocationState> state)
: m_state(std::move(state)) {
}
bool Initialize(const XCEngine::RHI::RHIDeviceDesc&) override { return true; }
void Shutdown() override {}
XCEngine::RHI::RHIBuffer* CreateBuffer(const XCEngine::RHI::BufferDesc&) override { return nullptr; }
XCEngine::RHI::RHITexture* CreateTexture(const XCEngine::RHI::TextureDesc& desc) override {
++m_state->createTextureCalls;
m_state->lastTextureWidth = desc.width;
m_state->lastTextureHeight = desc.height;
m_state->lastTextureFormat = static_cast<XCEngine::RHI::Format>(desc.format);
return new MockShadowTexture(
m_state,
desc.width,
desc.height,
static_cast<XCEngine::RHI::Format>(desc.format));
}
XCEngine::RHI::RHITexture* CreateTexture(
const XCEngine::RHI::TextureDesc& desc,
const void*,
size_t,
uint32_t) override {
return CreateTexture(desc);
}
XCEngine::RHI::RHISwapChain* CreateSwapChain(
const XCEngine::RHI::SwapChainDesc&,
XCEngine::RHI::RHICommandQueue*) override { return nullptr; }
XCEngine::RHI::RHICommandList* CreateCommandList(const XCEngine::RHI::CommandListDesc&) override { return nullptr; }
XCEngine::RHI::RHICommandQueue* CreateCommandQueue(const XCEngine::RHI::CommandQueueDesc&) override { return nullptr; }
XCEngine::RHI::RHIShader* CreateShader(const XCEngine::RHI::ShaderCompileDesc&) override { return nullptr; }
XCEngine::RHI::RHIPipelineState* CreatePipelineState(const XCEngine::RHI::GraphicsPipelineDesc&) override { return nullptr; }
XCEngine::RHI::RHIPipelineLayout* CreatePipelineLayout(const XCEngine::RHI::RHIPipelineLayoutDesc&) override { return nullptr; }
XCEngine::RHI::RHIFence* CreateFence(const XCEngine::RHI::FenceDesc&) override { return nullptr; }
XCEngine::RHI::RHISampler* CreateSampler(const XCEngine::RHI::SamplerDesc&) override { return nullptr; }
XCEngine::RHI::RHIRenderPass* CreateRenderPass(
uint32_t,
const XCEngine::RHI::AttachmentDesc*,
const XCEngine::RHI::AttachmentDesc*) override { return nullptr; }
XCEngine::RHI::RHIFramebuffer* CreateFramebuffer(
XCEngine::RHI::RHIRenderPass*,
uint32_t,
uint32_t,
uint32_t,
XCEngine::RHI::RHIResourceView**,
XCEngine::RHI::RHIResourceView*) override { return nullptr; }
XCEngine::RHI::RHIDescriptorPool* CreateDescriptorPool(const XCEngine::RHI::DescriptorPoolDesc&) override { return nullptr; }
XCEngine::RHI::RHIDescriptorSet* CreateDescriptorSet(
XCEngine::RHI::RHIDescriptorPool*,
const XCEngine::RHI::DescriptorSetLayoutDesc&) override { return nullptr; }
XCEngine::RHI::RHIResourceView* CreateVertexBufferView(
XCEngine::RHI::RHIBuffer*,
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
XCEngine::RHI::RHIResourceView* CreateIndexBufferView(
XCEngine::RHI::RHIBuffer*,
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
XCEngine::RHI::RHIResourceView* CreateRenderTargetView(
XCEngine::RHI::RHITexture*,
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
XCEngine::RHI::RHIResourceView* CreateDepthStencilView(
XCEngine::RHI::RHITexture*,
const XCEngine::RHI::ResourceViewDesc& desc) override {
++m_state->createDepthViewCalls;
m_state->lastDepthViewFormat = static_cast<XCEngine::RHI::Format>(desc.format);
return new MockShadowView(
m_state,
XCEngine::RHI::ResourceViewType::DepthStencil,
static_cast<XCEngine::RHI::Format>(desc.format),
desc.dimension);
}
XCEngine::RHI::RHIResourceView* CreateShaderResourceView(
XCEngine::RHI::RHITexture*,
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
XCEngine::RHI::RHIResourceView* CreateUnorderedAccessView(
XCEngine::RHI::RHITexture*,
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
const XCEngine::RHI::RHICapabilities& GetCapabilities() const override { return m_capabilities; }
const XCEngine::RHI::RHIDeviceInfo& GetDeviceInfo() const override { return m_deviceInfo; }
void* GetNativeDevice() override { return nullptr; }
private:
std::shared_ptr<MockShadowAllocationState> m_state;
XCEngine::RHI::RHICapabilities m_capabilities = {};
XCEngine::RHI::RHIDeviceInfo m_deviceInfo = {};
};
struct MockPipelineAssetState {
int createCalls = 0;
std::shared_ptr<MockPipelineState> lastCreatedPipelineState;
@@ -416,13 +608,11 @@ TEST(CameraRenderer_Test, ExecutesShadowCasterAndDepthOnlyRequestsBeforeMainPipe
request.cameraDepth = camera->GetDepth();
request.shadowCaster.surface = RenderSurface(128, 64);
request.shadowCaster.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(1));
request.shadowCaster.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2));
request.shadowCaster.hasCameraDataOverride = true;
request.shadowCaster.cameraDataOverride.worldPosition = XCEngine::Math::Vector3(7.0f, 8.0f, 9.0f);
request.depthOnly.surface = RenderSurface(96, 48);
request.depthOnly.surface.SetColorAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(3));
request.depthOnly.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(4));
request.depthOnly.hasClearColorOverride = true;
request.depthOnly.clearColorOverride = XCEngine::Math::Color(0.3f, 0.2f, 0.1f, 1.0f);
@@ -451,6 +641,68 @@ TEST(CameraRenderer_Test, ExecutesShadowCasterAndDepthOnlyRequestsBeforeMainPipe
EXPECT_FLOAT_EQ(depthPassRaw->lastClearColor.a, 1.0f);
}
TEST(CameraRenderer_Test, AutoAllocatesDirectionalShadowSurfaceFromShadowPlan) {
Scene scene("CameraRendererAutoDirectionalShadowScene");
GameObject* cameraObject = scene.CreateGameObject("Camera");
auto* camera = cameraObject->AddComponent<CameraComponent>();
camera->SetPrimary(true);
camera->SetDepth(2.0f);
auto pipelineState = std::make_shared<MockPipelineState>();
auto allocationState = std::make_shared<MockShadowAllocationState>();
MockShadowDevice device(allocationState);
CameraRenderer renderer(
std::make_unique<MockPipeline>(pipelineState),
std::make_unique<MockObjectIdPass>(pipelineState));
auto shadowPass = std::make_unique<MockScenePass>(pipelineState, "shadowCaster");
MockScenePass* shadowPassRaw = shadowPass.get();
renderer.SetShadowCasterPass(std::move(shadowPass));
RenderContext context = CreateValidContext();
context.device = &device;
CameraRenderRequest request;
request.scene = &scene;
request.camera = camera;
request.context = context;
request.surface = RenderSurface(320, 180);
request.cameraDepth = camera->GetDepth();
request.directionalShadow.enabled = true;
request.directionalShadow.mapWidth = 256;
request.directionalShadow.mapHeight = 128;
request.directionalShadow.cameraData.viewportWidth = 256;
request.directionalShadow.cameraData.viewportHeight = 128;
request.directionalShadow.cameraData.clearFlags = RenderClearFlags::Depth;
request.directionalShadow.cameraData.worldPosition = XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f);
ASSERT_TRUE(renderer.Render(request));
EXPECT_EQ(
pipelineState->eventLog,
(std::vector<std::string>{
"init:shadowCaster",
"shadowCaster",
"pipeline" }));
EXPECT_EQ(shadowPassRaw->lastViewportWidth, 256u);
EXPECT_EQ(shadowPassRaw->lastViewportHeight, 128u);
EXPECT_EQ(shadowPassRaw->lastSurfaceWidth, 256u);
EXPECT_EQ(shadowPassRaw->lastSurfaceHeight, 128u);
EXPECT_EQ(shadowPassRaw->lastClearFlags, RenderClearFlags::Depth);
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->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->shutdownDepthViewCalls, 1);
EXPECT_EQ(allocationState->shutdownTextureCalls, 1);
EXPECT_EQ(allocationState->destroyDepthViewCalls, 1);
EXPECT_EQ(allocationState->destroyTextureCalls, 1);
}
TEST(CameraRenderer_Test, StopsRenderingWhenShadowCasterRequestIsInvalid) {
Scene scene("CameraRendererInvalidShadowScene");