Enable depth-only shadow pass execution
This commit is contained in:
@@ -45,6 +45,7 @@ public:
|
|||||||
VkPipeline GetPipeline() const { return m_pipeline; }
|
VkPipeline GetPipeline() const { return m_pipeline; }
|
||||||
VkPipelineLayout GetPipelineLayout() const { return m_pipelineLayout; }
|
VkPipelineLayout GetPipelineLayout() const { return m_pipelineLayout; }
|
||||||
VkRenderPass GetRenderPass() const { return m_renderPass; }
|
VkRenderPass GetRenderPass() const { return m_renderPass; }
|
||||||
|
uint32_t GetRenderTargetCount() const { return m_renderTargetCount; }
|
||||||
bool HasDepthStencilAttachment() const {
|
bool HasDepthStencilAttachment() const {
|
||||||
return m_depthStencilFormat != 0 && static_cast<Format>(m_depthStencilFormat) != Format::Unknown;
|
return m_depthStencilFormat != 0 && static_cast<Format>(m_depthStencilFormat) != Format::Unknown;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,14 +22,14 @@ struct ScenePassRenderRequest {
|
|||||||
RenderCameraData cameraDataOverride = {};
|
RenderCameraData cameraDataOverride = {};
|
||||||
|
|
||||||
bool IsRequested() const {
|
bool IsRequested() const {
|
||||||
return !surface.GetColorAttachments().empty();
|
return surface.GetDepthAttachment() != nullptr ||
|
||||||
|
!surface.GetColorAttachments().empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValid() const {
|
bool IsValid() const {
|
||||||
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
|
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
|
||||||
return !colorAttachments.empty() &&
|
return surface.GetDepthAttachment() != nullptr &&
|
||||||
colorAttachments[0] != nullptr &&
|
(colorAttachments.empty() || colorAttachments[0] != nullptr) &&
|
||||||
surface.GetDepthAttachment() != nullptr &&
|
|
||||||
surface.GetRenderAreaWidth() > 0 &&
|
surface.GetRenderAreaWidth() > 0 &&
|
||||||
surface.GetRenderAreaHeight() > 0;
|
surface.GetRenderAreaHeight() > 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,11 +102,17 @@ private:
|
|||||||
Resources::MaterialRenderState renderState;
|
Resources::MaterialRenderState renderState;
|
||||||
const Resources::Shader* shader = nullptr;
|
const Resources::Shader* shader = nullptr;
|
||||||
Containers::String passName;
|
Containers::String passName;
|
||||||
|
uint32_t renderTargetCount = 0;
|
||||||
|
uint32_t renderTargetFormat = 0;
|
||||||
|
uint32_t depthStencilFormat = 0;
|
||||||
|
|
||||||
bool operator==(const PipelineStateKey& other) const {
|
bool operator==(const PipelineStateKey& other) const {
|
||||||
return renderState == other.renderState &&
|
return renderState == other.renderState &&
|
||||||
shader == other.shader &&
|
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);
|
size_t hash = MaterialRenderStateHash()(key.renderState);
|
||||||
hash ^= reinterpret_cast<size_t>(key.shader) + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
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<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;
|
return hash;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -133,6 +142,7 @@ private:
|
|||||||
const ResolvedShaderPass& resolvedShaderPass);
|
const ResolvedShaderPass& resolvedShaderPass);
|
||||||
RHI::RHIPipelineState* GetOrCreatePipelineState(
|
RHI::RHIPipelineState* GetOrCreatePipelineState(
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
|
const RenderSurface& surface,
|
||||||
const Resources::Material* material);
|
const Resources::Material* material);
|
||||||
bool CreateOwnedDescriptorSet(
|
bool CreateOwnedDescriptorSet(
|
||||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||||
@@ -145,6 +155,7 @@ private:
|
|||||||
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
|
void DestroyPassResourceLayout(PassResourceLayout& passLayout);
|
||||||
bool DrawVisibleItem(
|
bool DrawVisibleItem(
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
|
const RenderSurface& surface,
|
||||||
const RenderSceneData& sceneData,
|
const RenderSceneData& sceneData,
|
||||||
const VisibleRenderItem& visibleItem);
|
const VisibleRenderItem& visibleItem);
|
||||||
|
|
||||||
|
|||||||
@@ -1024,8 +1024,12 @@ bool VulkanCommandList::EnsureGraphicsRenderPass() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool expectsColorAttachment = m_currentPipelineState->GetRenderTargetCount() > 0;
|
||||||
auto* colorView = static_cast<VulkanResourceView*>(m_currentColorTarget);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1036,23 +1040,42 @@ bool VulkanCommandList::EnsureGraphicsRenderPass() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanTexture* texture = colorView->GetTexture();
|
if (!expectsColorAttachment && !expectsDepthAttachment) {
|
||||||
TransitionTexture(texture, ResourceStates::RenderTarget);
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanTexture* colorTexture = expectsColorAttachment ? colorView->GetTexture() : nullptr;
|
||||||
|
VulkanTexture* depthTexture = expectsDepthAttachment ? depthView->GetTexture() : nullptr;
|
||||||
|
if (expectsColorAttachment) {
|
||||||
|
TransitionTexture(colorTexture, ResourceStates::RenderTarget);
|
||||||
|
}
|
||||||
if (expectsDepthAttachment) {
|
if (expectsDepthAttachment) {
|
||||||
TransitionTexture(depthView->GetTexture(), ResourceStates::DepthWrite);
|
TransitionTexture(depthTexture, ResourceStates::DepthWrite);
|
||||||
}
|
}
|
||||||
|
|
||||||
VkFramebufferCreateInfo framebufferInfo = {};
|
VkFramebufferCreateInfo framebufferInfo = {};
|
||||||
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||||
framebufferInfo.renderPass = m_currentPipelineState->GetRenderPass();
|
framebufferInfo.renderPass = m_currentPipelineState->GetRenderPass();
|
||||||
VkImageView attachments[2] = { colorView->GetImageView(), VK_NULL_HANDLE };
|
VkImageView attachments[2] = { VK_NULL_HANDLE, VK_NULL_HANDLE };
|
||||||
framebufferInfo.attachmentCount = expectsDepthAttachment ? 2 : 1;
|
uint32_t attachmentCount = 0;
|
||||||
if (expectsDepthAttachment) {
|
uint32_t framebufferWidth = 0;
|
||||||
attachments[1] = depthView->GetImageView();
|
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.pAttachments = attachments;
|
||||||
framebufferInfo.width = texture->GetWidth();
|
framebufferInfo.width = framebufferWidth;
|
||||||
framebufferInfo.height = texture->GetHeight();
|
framebufferInfo.height = framebufferHeight;
|
||||||
framebufferInfo.layers = 1;
|
framebufferInfo.layers = 1;
|
||||||
|
|
||||||
VkFramebuffer framebuffer = VK_NULL_HANDLE;
|
VkFramebuffer framebuffer = VK_NULL_HANDLE;
|
||||||
@@ -1066,7 +1089,7 @@ bool VulkanCommandList::EnsureGraphicsRenderPass() {
|
|||||||
renderPassInfo.renderPass = m_currentPipelineState->GetRenderPass();
|
renderPassInfo.renderPass = m_currentPipelineState->GetRenderPass();
|
||||||
renderPassInfo.framebuffer = framebuffer;
|
renderPassInfo.framebuffer = framebuffer;
|
||||||
renderPassInfo.renderArea.offset = { 0, 0 };
|
renderPassInfo.renderArea.offset = { 0, 0 };
|
||||||
renderPassInfo.renderArea.extent = { texture->GetWidth(), texture->GetHeight() };
|
renderPassInfo.renderArea.extent = { framebufferWidth, framebufferHeight };
|
||||||
renderPassInfo.clearValueCount = 0;
|
renderPassInfo.clearValueCount = 0;
|
||||||
renderPassInfo.pClearValues = nullptr;
|
renderPassInfo.pClearValues = nullptr;
|
||||||
|
|
||||||
|
|||||||
@@ -65,12 +65,17 @@ bool VulkanPipelineState::Initialize(VulkanDevice* device, const GraphicsPipelin
|
|||||||
|
|
||||||
const bool hasVertexShader = HasShaderPayload(desc.vertexShader);
|
const bool hasVertexShader = HasShaderPayload(desc.vertexShader);
|
||||||
const bool hasFragmentShader = HasShaderPayload(desc.fragmentShader);
|
const bool hasFragmentShader = HasShaderPayload(desc.fragmentShader);
|
||||||
|
const bool hasColorAttachment = m_renderTargetCount == 1 && m_renderTargetFormats[0] != 0;
|
||||||
|
const bool hasDepthAttachment = HasDepthStencilAttachment();
|
||||||
if (!hasVertexShader && !hasFragmentShader) {
|
if (!hasVertexShader && !hasFragmentShader) {
|
||||||
m_isConfigured = true;
|
m_isConfigured = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_renderTargetCount != 1 || m_renderTargetFormats[0] == 0 || !hasVertexShader || !hasFragmentShader) {
|
if (m_renderTargetCount > 1 ||
|
||||||
|
(!hasColorAttachment && !hasDepthAttachment) ||
|
||||||
|
!hasVertexShader ||
|
||||||
|
!hasFragmentShader) {
|
||||||
Shutdown();
|
Shutdown();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -124,26 +129,29 @@ bool VulkanPipelineState::CreateGraphicsPipeline(const GraphicsPipelineDesc& des
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool hasColorAttachment = m_renderTargetCount == 1 && m_renderTargetFormats[0] != 0;
|
||||||
|
const bool hasDepthAttachment = HasDepthStencilAttachment();
|
||||||
std::vector<VkAttachmentDescription> attachments;
|
std::vector<VkAttachmentDescription> attachments;
|
||||||
attachments.reserve(2);
|
attachments.reserve(hasDepthAttachment ? 2u : 1u);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
VkAttachmentReference colorAttachmentRef = {};
|
VkAttachmentReference colorAttachmentRef = {};
|
||||||
colorAttachmentRef.attachment = 0;
|
if (hasColorAttachment) {
|
||||||
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
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 = {};
|
VkAttachmentReference depthAttachmentRef = {};
|
||||||
const bool hasDepthAttachment = HasDepthStencilAttachment();
|
|
||||||
if (hasDepthAttachment) {
|
if (hasDepthAttachment) {
|
||||||
VkAttachmentDescription depthAttachment = {};
|
VkAttachmentDescription depthAttachment = {};
|
||||||
depthAttachment.format = ToVulkanFormat(static_cast<Format>(m_depthStencilFormat));
|
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;
|
depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||||
attachments.push_back(depthAttachment);
|
attachments.push_back(depthAttachment);
|
||||||
|
|
||||||
depthAttachmentRef.attachment = 1;
|
depthAttachmentRef.attachment = hasColorAttachment ? 1u : 0u;
|
||||||
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSubpassDescription subpass = {};
|
VkSubpassDescription subpass = {};
|
||||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||||
subpass.colorAttachmentCount = 1;
|
subpass.colorAttachmentCount = hasColorAttachment ? 1u : 0u;
|
||||||
subpass.pColorAttachments = &colorAttachmentRef;
|
subpass.pColorAttachments = hasColorAttachment ? &colorAttachmentRef : nullptr;
|
||||||
if (hasDepthAttachment) {
|
if (hasDepthAttachment) {
|
||||||
subpass.pDepthStencilAttachment = &depthAttachmentRef;
|
subpass.pDepthStencilAttachment = &depthAttachmentRef;
|
||||||
}
|
}
|
||||||
@@ -272,8 +280,8 @@ bool VulkanPipelineState::CreateGraphicsPipeline(const GraphicsPipelineDesc& des
|
|||||||
VkPipelineColorBlendStateCreateInfo colorBlending = {};
|
VkPipelineColorBlendStateCreateInfo colorBlending = {};
|
||||||
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||||
colorBlending.logicOpEnable = VK_FALSE;
|
colorBlending.logicOpEnable = VK_FALSE;
|
||||||
colorBlending.attachmentCount = 1;
|
colorBlending.attachmentCount = hasColorAttachment ? 1u : 0u;
|
||||||
colorBlending.pAttachments = &colorBlendAttachment;
|
colorBlending.pAttachments = hasColorAttachment ? &colorBlendAttachment : nullptr;
|
||||||
|
|
||||||
VkPipelineDepthStencilStateCreateInfo depthStencil = {};
|
VkPipelineDepthStencilStateCreateInfo depthStencil = {};
|
||||||
depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
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 VulkanPipelineState::GetHash() const {
|
||||||
PipelineStateHash hash = {};
|
PipelineStateHash hash = {};
|
||||||
hash.topologyHash = m_topologyType;
|
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;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
#include "Rendering/Pipelines/BuiltinForwardPipeline.h"
|
#include "Rendering/Pipelines/BuiltinForwardPipeline.h"
|
||||||
#include "Rendering/RenderPipelineAsset.h"
|
#include "Rendering/RenderPipelineAsset.h"
|
||||||
#include "Rendering/RenderSurface.h"
|
#include "Rendering/RenderSurface.h"
|
||||||
|
#include "RHI/RHIDevice.h"
|
||||||
|
#include "RHI/RHIResourceView.h"
|
||||||
|
#include "RHI/RHITexture.h"
|
||||||
#include "Scene/Scene.h"
|
#include "Scene/Scene.h"
|
||||||
|
|
||||||
namespace XCEngine {
|
namespace XCEngine {
|
||||||
@@ -13,6 +16,36 @@ namespace Rendering {
|
|||||||
|
|
||||||
namespace {
|
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(
|
bool InitializePassSequence(
|
||||||
RenderPassSequence* sequence,
|
RenderPassSequence* sequence,
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
@@ -110,6 +143,52 @@ bool ExecuteScenePassRequest(
|
|||||||
return pass->Execute(passContext);
|
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
|
} // namespace
|
||||||
|
|
||||||
CameraRenderer::CameraRenderer()
|
CameraRenderer::CameraRenderer()
|
||||||
@@ -237,14 +316,32 @@ bool CameraRenderer::Render(
|
|||||||
!request.depthOnly.IsValid()) {
|
!request.depthOnly.IsValid()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (request.shadowCaster.IsRequested() &&
|
|
||||||
!request.shadowCaster.IsValid()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (request.objectId.IsRequested() &&
|
if (request.objectId.IsRequested() &&
|
||||||
!request.objectId.IsValid()) {
|
!request.objectId.IsValid()) {
|
||||||
return false;
|
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(
|
RenderSceneData sceneData = m_sceneExtractor.ExtractForCamera(
|
||||||
*request.scene,
|
*request.scene,
|
||||||
*request.camera,
|
*request.camera,
|
||||||
@@ -279,7 +376,7 @@ bool CameraRenderer::Render(
|
|||||||
|
|
||||||
if (!ExecuteScenePassRequest(
|
if (!ExecuteScenePassRequest(
|
||||||
m_shadowCasterPass.get(),
|
m_shadowCasterPass.get(),
|
||||||
request.shadowCaster,
|
resolvedShadowCaster,
|
||||||
request.context,
|
request.context,
|
||||||
sceneData)) {
|
sceneData)) {
|
||||||
ShutdownPassSequence(request.preScenePasses, preScenePassesInitialized);
|
ShutdownPassSequence(request.preScenePasses, preScenePassesInitialized);
|
||||||
|
|||||||
@@ -45,19 +45,47 @@ bool IsSupportedPerObjectOnlyBindingPlan(const BuiltinPassResourceBindingPlan& b
|
|||||||
!bindingPlan.usesSamplers;
|
!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::GraphicsPipelineDesc CreatePipelineDesc(
|
||||||
RHI::RHIType backendType,
|
RHI::RHIType backendType,
|
||||||
RHI::RHIPipelineLayout* pipelineLayout,
|
RHI::RHIPipelineLayout* pipelineLayout,
|
||||||
const Resources::Shader& shader,
|
const Resources::Shader& shader,
|
||||||
const Containers::String& passName,
|
const Containers::String& passName,
|
||||||
const Resources::Material* material,
|
const Resources::Material* material,
|
||||||
|
const RenderSurface& surface,
|
||||||
const RHI::InputLayoutDesc& inputLayout) {
|
const RHI::InputLayoutDesc& inputLayout) {
|
||||||
RHI::GraphicsPipelineDesc pipelineDesc = {};
|
RHI::GraphicsPipelineDesc pipelineDesc = {};
|
||||||
pipelineDesc.pipelineLayout = pipelineLayout;
|
pipelineDesc.pipelineLayout = pipelineLayout;
|
||||||
pipelineDesc.topologyType = static_cast<uint32_t>(RHI::PrimitiveTopologyType::Triangle);
|
pipelineDesc.topologyType = static_cast<uint32_t>(RHI::PrimitiveTopologyType::Triangle);
|
||||||
pipelineDesc.renderTargetCount = 1;
|
pipelineDesc.renderTargetCount = ResolveSurfaceColorAttachmentCount(surface);
|
||||||
pipelineDesc.renderTargetFormats[0] = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
|
if (pipelineDesc.renderTargetCount > 0) {
|
||||||
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(RHI::Format::D24_UNorm_S8_UInt);
|
pipelineDesc.renderTargetFormats[0] =
|
||||||
|
static_cast<uint32_t>(ResolveSurfaceColorFormat(surface));
|
||||||
|
}
|
||||||
|
pipelineDesc.depthStencilFormat =
|
||||||
|
static_cast<uint32_t>(ResolveSurfaceDepthFormat(surface));
|
||||||
pipelineDesc.sampleCount = 1;
|
pipelineDesc.sampleCount = 1;
|
||||||
pipelineDesc.inputLayout = inputLayout;
|
pipelineDesc.inputLayout = inputLayout;
|
||||||
ApplyMaterialRenderState(material, pipelineDesc);
|
ApplyMaterialRenderState(material, pipelineDesc);
|
||||||
@@ -138,9 +166,10 @@ bool BuiltinDepthStylePassBase::Execute(const RenderPassContext& context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<RHI::RHIResourceView*>& colorAttachments = context.surface.GetColorAttachments();
|
const std::vector<RHI::RHIResourceView*>& colorAttachments = context.surface.GetColorAttachments();
|
||||||
if (colorAttachments.empty() ||
|
RHI::RHIResourceView* depthAttachment = context.surface.GetDepthAttachment();
|
||||||
colorAttachments[0] == nullptr ||
|
RHI::RHIResourceView* renderTarget =
|
||||||
context.surface.GetDepthAttachment() == nullptr) {
|
(!colorAttachments.empty() ? colorAttachments[0] : nullptr);
|
||||||
|
if (depthAttachment == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,16 +183,20 @@ bool BuiltinDepthStylePassBase::Execute(const RenderPassContext& context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RHI::RHICommandList* commandList = context.renderContext.commandList;
|
RHI::RHICommandList* commandList = context.renderContext.commandList;
|
||||||
RHI::RHIResourceView* renderTarget = colorAttachments[0];
|
|
||||||
|
|
||||||
if (context.surface.IsAutoTransitionEnabled()) {
|
if (context.surface.IsAutoTransitionEnabled() && renderTarget != nullptr) {
|
||||||
commandList->TransitionBarrier(
|
commandList->TransitionBarrier(
|
||||||
renderTarget,
|
renderTarget,
|
||||||
context.surface.GetColorStateBefore(),
|
context.surface.GetColorStateBefore(),
|
||||||
RHI::ResourceStates::RenderTarget);
|
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 = {
|
const RHI::Viewport viewport = {
|
||||||
static_cast<float>(renderArea.x),
|
static_cast<float>(renderArea.x),
|
||||||
@@ -185,11 +218,12 @@ bool BuiltinDepthStylePassBase::Execute(const RenderPassContext& context) {
|
|||||||
|
|
||||||
const Math::Color clearColor = context.sceneData.cameraData.clearColor;
|
const Math::Color clearColor = context.sceneData.cameraData.clearColor;
|
||||||
const float clearValues[4] = { clearColor.r, clearColor.g, clearColor.b, clearColor.a };
|
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);
|
commandList->ClearRenderTarget(renderTarget, clearValues, 1, clearRects);
|
||||||
}
|
}
|
||||||
if (HasRenderClearFlag(context.sceneData.cameraData.clearFlags, RenderClearFlags::Depth)) {
|
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);
|
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
|
||||||
@@ -199,12 +233,12 @@ bool BuiltinDepthStylePassBase::Execute(const RenderPassContext& context) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawVisibleItem(context.renderContext, context.sceneData, visibleItem);
|
DrawVisibleItem(context.renderContext, context.surface, context.sceneData, visibleItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
commandList->EndRenderPass();
|
commandList->EndRenderPass();
|
||||||
|
|
||||||
if (context.surface.IsAutoTransitionEnabled()) {
|
if (context.surface.IsAutoTransitionEnabled() && renderTarget != nullptr) {
|
||||||
commandList->TransitionBarrier(
|
commandList->TransitionBarrier(
|
||||||
renderTarget,
|
renderTarget,
|
||||||
RHI::ResourceStates::RenderTarget,
|
RHI::ResourceStates::RenderTarget,
|
||||||
@@ -433,6 +467,7 @@ BuiltinDepthStylePassBase::PassResourceLayout* BuiltinDepthStylePassBase::GetOrC
|
|||||||
|
|
||||||
RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState(
|
RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState(
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
|
const RenderSurface& surface,
|
||||||
const Resources::Material* material) {
|
const Resources::Material* material) {
|
||||||
const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material);
|
const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material);
|
||||||
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||||
@@ -449,6 +484,9 @@ RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState(
|
|||||||
material != nullptr ? material->GetRenderState() : Resources::MaterialRenderState();
|
material != nullptr ? material->GetRenderState() : Resources::MaterialRenderState();
|
||||||
pipelineKey.shader = resolvedShaderPass.shader;
|
pipelineKey.shader = resolvedShaderPass.shader;
|
||||||
pipelineKey.passName = resolvedShaderPass.passName;
|
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);
|
const auto existing = m_pipelineStates.find(pipelineKey);
|
||||||
if (existing != m_pipelineStates.end()) {
|
if (existing != m_pipelineStates.end()) {
|
||||||
@@ -461,6 +499,7 @@ RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState(
|
|||||||
*resolvedShaderPass.shader,
|
*resolvedShaderPass.shader,
|
||||||
resolvedShaderPass.passName,
|
resolvedShaderPass.passName,
|
||||||
material,
|
material,
|
||||||
|
surface,
|
||||||
BuildCommonInputLayout());
|
BuildCommonInputLayout());
|
||||||
RHI::RHIPipelineState* pipelineState = context.device->CreatePipelineState(pipelineDesc);
|
RHI::RHIPipelineState* pipelineState = context.device->CreatePipelineState(pipelineDesc);
|
||||||
if (pipelineState == nullptr || !pipelineState->IsValid()) {
|
if (pipelineState == nullptr || !pipelineState->IsValid()) {
|
||||||
@@ -552,6 +591,7 @@ void BuiltinDepthStylePassBase::DestroyPassResourceLayout(PassResourceLayout& pa
|
|||||||
|
|
||||||
bool BuiltinDepthStylePassBase::DrawVisibleItem(
|
bool BuiltinDepthStylePassBase::DrawVisibleItem(
|
||||||
const RenderContext& context,
|
const RenderContext& context,
|
||||||
|
const RenderSurface& surface,
|
||||||
const RenderSceneData& sceneData,
|
const RenderSceneData& sceneData,
|
||||||
const VisibleRenderItem& visibleItem) {
|
const VisibleRenderItem& visibleItem) {
|
||||||
if (visibleItem.mesh == nullptr || visibleItem.gameObject == nullptr) {
|
if (visibleItem.mesh == nullptr || visibleItem.gameObject == nullptr) {
|
||||||
@@ -579,7 +619,7 @@ bool BuiltinDepthStylePassBase::DrawVisibleItem(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, material);
|
RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, material);
|
||||||
if (pipelineState == nullptr) {
|
if (pipelineState == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include <XCEngine/Components/CameraComponent.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/CameraRenderer.h>
|
||||||
#include <XCEngine/Rendering/ObjectIdPass.h>
|
#include <XCEngine/Rendering/ObjectIdPass.h>
|
||||||
#include <XCEngine/Rendering/RenderPipelineAsset.h>
|
#include <XCEngine/Rendering/RenderPipelineAsset.h>
|
||||||
@@ -40,6 +45,193 @@ struct MockPipelineState {
|
|||||||
std::vector<std::string> eventLog;
|
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 {
|
struct MockPipelineAssetState {
|
||||||
int createCalls = 0;
|
int createCalls = 0;
|
||||||
std::shared_ptr<MockPipelineState> lastCreatedPipelineState;
|
std::shared_ptr<MockPipelineState> lastCreatedPipelineState;
|
||||||
@@ -416,13 +608,11 @@ TEST(CameraRenderer_Test, ExecutesShadowCasterAndDepthOnlyRequestsBeforeMainPipe
|
|||||||
request.cameraDepth = camera->GetDepth();
|
request.cameraDepth = camera->GetDepth();
|
||||||
|
|
||||||
request.shadowCaster.surface = RenderSurface(128, 64);
|
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.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(2));
|
||||||
request.shadowCaster.hasCameraDataOverride = true;
|
request.shadowCaster.hasCameraDataOverride = true;
|
||||||
request.shadowCaster.cameraDataOverride.worldPosition = XCEngine::Math::Vector3(7.0f, 8.0f, 9.0f);
|
request.shadowCaster.cameraDataOverride.worldPosition = XCEngine::Math::Vector3(7.0f, 8.0f, 9.0f);
|
||||||
|
|
||||||
request.depthOnly.surface = RenderSurface(96, 48);
|
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.surface.SetDepthAttachment(reinterpret_cast<XCEngine::RHI::RHIResourceView*>(4));
|
||||||
request.depthOnly.hasClearColorOverride = true;
|
request.depthOnly.hasClearColorOverride = true;
|
||||||
request.depthOnly.clearColorOverride = XCEngine::Math::Color(0.3f, 0.2f, 0.1f, 1.0f);
|
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);
|
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) {
|
TEST(CameraRenderer_Test, StopsRenderingWhenShadowCasterRequestIsInvalid) {
|
||||||
Scene scene("CameraRendererInvalidShadowScene");
|
Scene scene("CameraRendererInvalidShadowScene");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user