#include "Rendering/Pipelines/BuiltinForwardPipeline.h" #include "Debug/Logger.h" #include "Core/Asset/ResourceManager.h" #include "RHI/RHICommandList.h" #include "Rendering/Materials/RenderMaterialResolve.h" #include "Rendering/RenderSurface.h" #include "Resources/BuiltinResources.h" #include namespace XCEngine { namespace Rendering { namespace Pipelines { namespace Detail { class BuiltinForwardOpaquePass final : public RenderPass { public: explicit BuiltinForwardOpaquePass(BuiltinForwardPipeline& pipeline) : m_pipeline(pipeline) { } const char* GetName() const override { return "BuiltinForwardOpaquePass"; } bool Initialize(const RenderContext& context) override { return m_pipeline.EnsureInitialized(context); } void Shutdown() override { m_pipeline.DestroyPipelineResources(); } bool Execute(const RenderPassContext& context) override { return m_pipeline.ExecuteForwardOpaquePass(context); } private: BuiltinForwardPipeline& m_pipeline; }; class BuiltinForwardTransparentPass final : public RenderPass { public: explicit BuiltinForwardTransparentPass(BuiltinForwardPipeline& pipeline) : m_pipeline(pipeline) { } const char* GetName() const override { return "BuiltinForwardTransparentPass"; } bool Initialize(const RenderContext& context) override { return m_pipeline.EnsureInitialized(context); } void Shutdown() override { m_pipeline.DestroyPipelineResources(); } bool Execute(const RenderPassContext& context) override { return m_pipeline.ExecuteForwardTransparentPass(context); } private: BuiltinForwardPipeline& m_pipeline; }; } // namespace Detail namespace { bool IsDepthFormat(RHI::Format format) { return format == RHI::Format::D24_UNorm_S8_UInt || format == RHI::Format::D32_Float; } } // namespace BuiltinForwardPipeline::BuiltinForwardPipeline() { m_passSequence.AddPass(std::make_unique(*this)); m_passSequence.AddPass(std::make_unique(*this)); } BuiltinForwardPipeline::~BuiltinForwardPipeline() { Shutdown(); } std::unique_ptr BuiltinForwardPipelineAsset::CreatePipeline() const { return std::make_unique(); } RHI::InputLayoutDesc BuiltinForwardPipeline::BuildInputLayout() { RHI::InputLayoutDesc inputLayout = {}; RHI::InputElementDesc position = {}; position.semanticName = "POSITION"; position.semanticIndex = 0; position.format = static_cast(RHI::Format::R32G32B32_Float); position.inputSlot = 0; position.alignedByteOffset = 0; inputLayout.elements.push_back(position); RHI::InputElementDesc normal = {}; normal.semanticName = "NORMAL"; normal.semanticIndex = 0; normal.format = static_cast(RHI::Format::R32G32B32_Float); normal.inputSlot = 0; normal.alignedByteOffset = static_cast(offsetof(Resources::StaticMeshVertex, normal)); inputLayout.elements.push_back(normal); RHI::InputElementDesc texcoord = {}; texcoord.semanticName = "TEXCOORD"; texcoord.semanticIndex = 0; texcoord.format = static_cast(RHI::Format::R32G32_Float); texcoord.inputSlot = 0; texcoord.alignedByteOffset = static_cast(offsetof(Resources::StaticMeshVertex, uv0)); inputLayout.elements.push_back(texcoord); return inputLayout; } bool BuiltinForwardPipeline::Initialize(const RenderContext& context) { return m_passSequence.Initialize(context); } void BuiltinForwardPipeline::Shutdown() { m_passSequence.Shutdown(); } bool BuiltinForwardPipeline::Render( const RenderContext& context, const RenderSurface& surface, const RenderSceneData& sceneData) { if (!Initialize(context)) { return false; } const RenderPassContext passContext = { context, surface, sceneData }; return m_passSequence.Execute(passContext); } bool BuiltinForwardPipeline::BeginForwardScenePass(const RenderPassContext& passContext) { const RenderContext& context = passContext.renderContext; const RenderSurface& surface = passContext.surface; const RenderSceneData& sceneData = passContext.sceneData; const std::vector& colorAttachments = surface.GetColorAttachments(); if (colorAttachments.empty()) { return false; } RHI::RHICommandList* commandList = context.commandList; if (surface.IsAutoTransitionEnabled()) { for (RHI::RHIResourceView* renderTarget : colorAttachments) { if (renderTarget != nullptr) { commandList->TransitionBarrier( renderTarget, surface.GetColorStateBefore(), RHI::ResourceStates::RenderTarget); } } } std::vector renderTargets = colorAttachments; commandList->SetRenderTargets( static_cast(renderTargets.size()), renderTargets.data(), surface.GetDepthAttachment()); const Math::RectInt renderArea = surface.GetRenderArea(); if (renderArea.width <= 0 || renderArea.height <= 0) { return false; } const RHI::Viewport viewport = { static_cast(renderArea.x), static_cast(renderArea.y), static_cast(renderArea.width), static_cast(renderArea.height), 0.0f, 1.0f }; const RHI::Rect scissorRect = { renderArea.x, renderArea.y, renderArea.x + renderArea.width, renderArea.y + renderArea.height }; const RHI::Rect clearRects[] = { scissorRect }; commandList->SetViewport(viewport); commandList->SetScissorRect(scissorRect); const Math::Color clearColor = surface.HasClearColorOverride() ? surface.GetClearColorOverride() : sceneData.cameraData.clearColor; const float clearValues[4] = { clearColor.r, clearColor.g, clearColor.b, clearColor.a }; if (HasRenderClearFlag(sceneData.cameraData.clearFlags, RenderClearFlags::Color)) { for (RHI::RHIResourceView* renderTarget : renderTargets) { if (renderTarget != nullptr) { commandList->ClearRenderTarget(renderTarget, clearValues, 1, clearRects); } } } if (surface.GetDepthAttachment() != nullptr && HasRenderClearFlag(sceneData.cameraData.clearFlags, RenderClearFlags::Depth)) { commandList->ClearDepthStencil(surface.GetDepthAttachment(), 1.0f, 0, 1, clearRects); } commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); return true; } void BuiltinForwardPipeline::EndForwardScenePass(const RenderPassContext& passContext) { const RenderContext& context = passContext.renderContext; const RenderSurface& surface = passContext.surface; const std::vector& colorAttachments = surface.GetColorAttachments(); RHI::RHICommandList* commandList = context.commandList; if (!surface.IsAutoTransitionEnabled()) { return; } commandList->EndRenderPass(); for (RHI::RHIResourceView* renderTarget : colorAttachments) { if (renderTarget != nullptr) { commandList->TransitionBarrier( renderTarget, RHI::ResourceStates::RenderTarget, surface.GetColorStateAfter()); } } } bool BuiltinForwardPipeline::ExecuteForwardOpaquePass(const RenderPassContext& passContext) { const RenderContext& context = passContext.renderContext; const RenderSceneData& sceneData = passContext.sceneData; if (!BeginForwardScenePass(passContext)) { return false; } if (sceneData.lighting.HasMainDirectionalShadow() && IsDepthFormat(sceneData.lighting.mainDirectionalShadow.shadowMap->GetFormat())) { context.commandList->TransitionBarrier( sceneData.lighting.mainDirectionalShadow.shadowMap, RHI::ResourceStates::DepthWrite, RHI::ResourceStates::PixelShaderResource); } return DrawVisibleItems(context, sceneData, false); } bool BuiltinForwardPipeline::ExecuteForwardTransparentPass(const RenderPassContext& passContext) { const RenderContext& context = passContext.renderContext; const RenderSceneData& sceneData = passContext.sceneData; const bool drawResult = DrawVisibleItems(context, sceneData, true); if (sceneData.lighting.HasMainDirectionalShadow() && IsDepthFormat(sceneData.lighting.mainDirectionalShadow.shadowMap->GetFormat())) { context.commandList->TransitionBarrier( sceneData.lighting.mainDirectionalShadow.shadowMap, RHI::ResourceStates::PixelShaderResource, RHI::ResourceStates::DepthWrite); } EndForwardScenePass(passContext); return drawResult; } bool BuiltinForwardPipeline::DrawVisibleItems( const RenderContext& context, const RenderSceneData& sceneData, bool drawTransparentItems) { RHI::RHICommandList* commandList = context.commandList; RHI::RHIPipelineState* currentPipelineState = nullptr; for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) { const bool isTransparentItem = IsTransparentRenderQueue(visibleItem.renderQueue); if (isTransparentItem != drawTransparentItems) { continue; } const Resources::Material* material = ResolveMaterial(visibleItem); BuiltinMaterialPass pass = BuiltinMaterialPass::ForwardLit; if (!TryResolveSurfacePassType(material, pass)) { continue; } RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, material); if (pipelineState == nullptr) { continue; } if (pipelineState != currentPipelineState) { commandList->SetPipelineState(pipelineState); currentPipelineState = pipelineState; } DrawVisibleItem(context, sceneData, visibleItem); } return true; } bool BuiltinForwardPipeline::EnsureInitialized(const RenderContext& context) { if (!context.IsValid()) { return false; } if (m_initialized && m_device == context.device && m_backendType == context.backendType) { return true; } DestroyPipelineResources(); m_device = context.device; m_backendType = context.backendType; m_initialized = CreatePipelineResources(context); return m_initialized; } bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& context) { m_builtinForwardShader = Resources::ResourceManager::Get().Load( Resources::GetBuiltinForwardLitShaderPath()); if (!m_builtinForwardShader.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "BuiltinForwardPipeline failed to load builtin forward shader resource"); return false; } m_builtinUnlitShader = Resources::ResourceManager::Get().Load( Resources::GetBuiltinUnlitShaderPath()); if (!m_builtinUnlitShader.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "BuiltinForwardPipeline failed to load builtin unlit shader resource"); return false; } RHI::SamplerDesc samplerDesc = {}; samplerDesc.filter = static_cast(RHI::FilterMode::Linear); samplerDesc.addressU = static_cast(RHI::TextureAddressMode::Clamp); samplerDesc.addressV = static_cast(RHI::TextureAddressMode::Clamp); samplerDesc.addressW = static_cast(RHI::TextureAddressMode::Clamp); samplerDesc.mipLodBias = 0.0f; samplerDesc.maxAnisotropy = 1; samplerDesc.comparisonFunc = static_cast(RHI::ComparisonFunc::Always); samplerDesc.minLod = 0.0f; samplerDesc.maxLod = 1000.0f; m_sampler = context.device->CreateSampler(samplerDesc); if (m_sampler == nullptr) { return false; } RHI::SamplerDesc shadowSamplerDesc = samplerDesc; shadowSamplerDesc.filter = static_cast(RHI::FilterMode::Point); m_shadowSampler = context.device->CreateSampler(shadowSamplerDesc); if (m_shadowSampler == nullptr) { return false; } const unsigned char whitePixel[4] = { 255, 255, 255, 255 }; RHI::TextureDesc textureDesc = {}; textureDesc.width = 1; textureDesc.height = 1; textureDesc.depth = 1; textureDesc.mipLevels = 1; textureDesc.arraySize = 1; textureDesc.format = static_cast(RHI::Format::R8G8B8A8_UNorm); textureDesc.textureType = static_cast(RHI::TextureType::Texture2D); textureDesc.sampleCount = 1; textureDesc.sampleQuality = 0; textureDesc.flags = 0; m_fallbackTexture = context.device->CreateTexture(textureDesc, whitePixel, sizeof(whitePixel), 4); if (m_fallbackTexture == nullptr) { return false; } RHI::ResourceViewDesc textureViewDesc = {}; textureViewDesc.format = static_cast(RHI::Format::R8G8B8A8_UNorm); textureViewDesc.dimension = RHI::ResourceViewDimension::Texture2D; textureViewDesc.mipLevel = 0; m_fallbackTextureView = context.device->CreateShaderResourceView(m_fallbackTexture, textureViewDesc); if (m_fallbackTextureView == nullptr) { return false; } return true; } void BuiltinForwardPipeline::DestroyPipelineResources() { m_resourceCache.Shutdown(); for (auto& pipelinePair : m_pipelineStates) { if (pipelinePair.second != nullptr) { pipelinePair.second->Shutdown(); delete pipelinePair.second; } } m_pipelineStates.clear(); for (auto& descriptorSetPair : m_dynamicDescriptorSets) { DestroyOwnedDescriptorSet(descriptorSetPair.second.descriptorSet); } m_dynamicDescriptorSets.clear(); for (auto& passLayoutPair : m_passResourceLayouts) { DestroyPassResourceLayout(passLayoutPair.second); } m_passResourceLayouts.clear(); if (m_fallbackTextureView != nullptr) { m_fallbackTextureView->Shutdown(); delete m_fallbackTextureView; m_fallbackTextureView = nullptr; } if (m_fallbackTexture != nullptr) { m_fallbackTexture->Shutdown(); delete m_fallbackTexture; m_fallbackTexture = nullptr; } if (m_sampler != nullptr) { m_sampler->Shutdown(); delete m_sampler; m_sampler = nullptr; } if (m_shadowSampler != nullptr) { m_shadowSampler->Shutdown(); delete m_shadowSampler; m_shadowSampler = nullptr; } m_device = nullptr; m_initialized = false; m_builtinForwardShader.Reset(); m_builtinUnlitShader.Reset(); } } // namespace Pipelines } // namespace Rendering } // namespace XCEngine