Enable depth-only shadow pass execution
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,9 +129,13 @@ 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);
|
||||
attachments.reserve(hasDepthAttachment ? 2u : 1u);
|
||||
|
||||
VkAttachmentReference colorAttachmentRef = {};
|
||||
if (hasColorAttachment) {
|
||||
VkAttachmentDescription colorAttachment = {};
|
||||
colorAttachment.format = ToVulkanFormat(static_cast<Format>(m_renderTargetFormats[0]));
|
||||
colorAttachment.samples = ToVulkanSampleCount(m_sampleCount);
|
||||
@@ -138,12 +147,11 @@ bool VulkanPipelineState::CreateGraphicsPipeline(const GraphicsPipelineDesc& des
|
||||
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attachments.push_back(colorAttachment);
|
||||
|
||||
VkAttachmentReference colorAttachmentRef = {};
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user