#include "Rendering/Pipelines/BuiltinForwardPipeline.h" #include "Debug/Logger.h" #include "Core/Asset/ResourceManager.h" #include "RHI/RHICommandList.h" #include "Rendering/Detail/ShaderVariantUtils.h" #include "Rendering/Materials/RenderMaterialResolve.h" #include "Rendering/RenderSurface.h" #include "Resources/BuiltinResources.h" #include #include #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 BuiltinForwardSkyboxPass final : public RenderPass { public: explicit BuiltinForwardSkyboxPass(BuiltinForwardPipeline& pipeline) : m_pipeline(pipeline) { } const char* GetName() const override { return "BuiltinForwardSkyboxPass"; } 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.ExecuteForwardSkyboxPass(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; } const Resources::ShaderPass* FindCompatibleSkyboxPass( const Resources::Shader& shader, Resources::ShaderBackend backend) { const Resources::ShaderPass* skyboxPass = shader.FindPass("Skybox"); if (skyboxPass != nullptr && ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, skyboxPass->name, backend)) { return skyboxPass; } if (shader.GetPassCount() > 0 && ::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, shader.GetPasses()[0].name, backend)) { return &shader.GetPasses()[0]; } return nullptr; } RHI::GraphicsPipelineDesc CreateSkyboxPipelineDesc( RHI::RHIType backendType, RHI::RHIPipelineLayout* pipelineLayout, const Resources::Shader& shader, const Containers::String& passName) { RHI::GraphicsPipelineDesc pipelineDesc = {}; pipelineDesc.pipelineLayout = pipelineLayout; pipelineDesc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); pipelineDesc.renderTargetCount = 1; pipelineDesc.renderTargetFormats[0] = static_cast(RHI::Format::R8G8B8A8_UNorm); pipelineDesc.depthStencilFormat = static_cast(RHI::Format::D24_UNorm_S8_UInt); pipelineDesc.sampleCount = 1; pipelineDesc.rasterizerState.fillMode = static_cast(RHI::FillMode::Solid); pipelineDesc.rasterizerState.cullMode = static_cast(RHI::CullMode::None); pipelineDesc.rasterizerState.frontFace = static_cast(RHI::FrontFace::CounterClockwise); pipelineDesc.rasterizerState.depthClipEnable = true; pipelineDesc.blendState.blendEnable = false; pipelineDesc.blendState.colorWriteMask = static_cast(RHI::ColorWriteMask::All); pipelineDesc.depthStencilState.depthTestEnable = true; pipelineDesc.depthStencilState.depthWriteEnable = false; pipelineDesc.depthStencilState.depthFunc = static_cast(RHI::ComparisonFunc::LessEqual); const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType); if (const Resources::ShaderStageVariant* vertexVariant = shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) { ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader); } if (const Resources::ShaderStageVariant* fragmentVariant = shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) { ::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader); } return pipelineDesc; } } // namespace BuiltinForwardPipeline::BuiltinForwardPipeline() { m_passSequence.AddPass(std::make_unique(*this)); 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::HasProceduralSkybox(const RenderSceneData& sceneData) const { return sceneData.environment.HasProceduralSkybox() && sceneData.cameraData.perspectiveProjection; } bool BuiltinForwardPipeline::HasSkybox(const RenderSceneData& sceneData) const { return sceneData.environment.HasSkybox() && sceneData.cameraData.perspectiveProjection; } 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) && !HasSkybox(sceneData)) { 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::ExecuteForwardSkyboxPass(const RenderPassContext& passContext) { const RenderContext& context = passContext.renderContext; const RenderSurface& surface = passContext.surface; const RenderSceneData& sceneData = passContext.sceneData; if (!HasSkybox(sceneData)) { return true; } if (surface.GetDepthAttachment() == nullptr) { return true; } if (!EnsureSkyboxResources(context) || m_skyboxPipelineLayout == nullptr || m_skyboxPipelineState == nullptr || m_skyboxEnvironmentSet.set == nullptr || m_skyboxMaterialSet.set == nullptr || m_skyboxPanoramicTextureSet.set == nullptr || m_skyboxCubemapTextureSet.set == nullptr || m_skyboxSamplerSet.set == nullptr) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "BuiltinForwardPipeline skybox pass failed to initialize runtime skybox resources"); return false; } SkyboxConstants constants = {}; constants.topColor = sceneData.environment.skybox.topColor.ToVector4(); constants.horizonColor = sceneData.environment.skybox.horizonColor.ToVector4(); constants.bottomColor = sceneData.environment.skybox.bottomColor.ToVector4(); constants.cameraRightAndTanHalfFov = Math::Vector4( sceneData.cameraData.worldRight, std::tan(sceneData.cameraData.verticalFovRadians * 0.5f)); constants.cameraUpAndAspect = Math::Vector4( sceneData.cameraData.worldUp, sceneData.cameraData.aspectRatio); constants.cameraForwardAndUnused = Math::Vector4(sceneData.cameraData.worldForward, 0.0f); const Resources::Material* skyboxMaterial = sceneData.environment.HasMaterialSkybox() ? sceneData.environment.materialSkybox.material : nullptr; const Resources::Texture* panoramicSkyboxTexture = ResolveSkyboxPanoramicTexture(skyboxMaterial); const Resources::Texture* cubemapSkyboxTexture = ResolveSkyboxCubemapTexture(skyboxMaterial); RHI::RHIResourceView* panoramicSkyboxTextureView = ResolveTextureView(panoramicSkyboxTexture); RHI::RHIResourceView* cubemapSkyboxTextureView = ResolveTextureView(cubemapSkyboxTexture); const BuiltinSkyboxMaterialData skyboxMaterialData = BuildBuiltinSkyboxMaterialData(skyboxMaterial); BuiltinSkyboxTextureMode activeTextureMode = BuiltinSkyboxTextureMode::None; switch (skyboxMaterialData.textureMode) { case BuiltinSkyboxTextureMode::Panoramic: if (panoramicSkyboxTextureView != nullptr) { activeTextureMode = BuiltinSkyboxTextureMode::Panoramic; } else if (cubemapSkyboxTextureView != nullptr) { activeTextureMode = BuiltinSkyboxTextureMode::Cubemap; } break; case BuiltinSkyboxTextureMode::Cubemap: if (cubemapSkyboxTextureView != nullptr) { activeTextureMode = BuiltinSkyboxTextureMode::Cubemap; } else if (panoramicSkyboxTextureView != nullptr) { activeTextureMode = BuiltinSkyboxTextureMode::Panoramic; } break; case BuiltinSkyboxTextureMode::None: default: if (panoramicSkyboxTextureView != nullptr) { activeTextureMode = BuiltinSkyboxTextureMode::Panoramic; } else if (cubemapSkyboxTextureView != nullptr) { activeTextureMode = BuiltinSkyboxTextureMode::Cubemap; } break; } if (panoramicSkyboxTextureView == nullptr) { panoramicSkyboxTextureView = m_fallbackTexture2DView; } if (cubemapSkyboxTextureView == nullptr) { cubemapSkyboxTextureView = m_fallbackTextureCubeView; } SkyboxMaterialConstants materialConstants = {}; materialConstants.tintAndExposure = Math::Vector4( skyboxMaterialData.tint.x, skyboxMaterialData.tint.y, skyboxMaterialData.tint.z, std::max(0.0f, skyboxMaterialData.exposure)); materialConstants.rotationRadiansAndMode = Math::Vector4( Math::Radians(skyboxMaterialData.rotationDegrees), static_cast(activeTextureMode), 0.0f, 0.0f); m_skyboxEnvironmentSet.set->WriteConstant(0, &constants, sizeof(constants)); m_skyboxMaterialSet.set->WriteConstant(0, &materialConstants, sizeof(materialConstants)); if (m_skyboxBoundPanoramicTextureView != panoramicSkyboxTextureView) { m_skyboxPanoramicTextureSet.set->Update(0, panoramicSkyboxTextureView); m_skyboxBoundPanoramicTextureView = panoramicSkyboxTextureView; } if (m_skyboxBoundCubemapTextureView != cubemapSkyboxTextureView) { m_skyboxCubemapTextureSet.set->Update(0, cubemapSkyboxTextureView); m_skyboxBoundCubemapTextureView = cubemapSkyboxTextureView; } RHI::RHICommandList* commandList = context.commandList; commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); commandList->SetPipelineState(m_skyboxPipelineState); RHI::RHIDescriptorSet* descriptorSets[] = { m_skyboxEnvironmentSet.set, m_skyboxMaterialSet.set, m_skyboxPanoramicTextureSet.set, m_skyboxCubemapTextureSet.set, m_skyboxSamplerSet.set }; commandList->SetGraphicsDescriptorSets(0, 5, descriptorSets, m_skyboxPipelineLayout); commandList->Draw(3, 1, 0, 0); return true; } 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, sceneData, 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; } m_builtinSkyboxShader = Resources::ResourceManager::Get().Load( Resources::GetBuiltinSkyboxShaderPath()); if (!m_builtinSkyboxShader.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "BuiltinForwardPipeline failed to load builtin skybox 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 whitePixels2D[4] = { 255, 255, 255, 255 }; RHI::TextureDesc fallback2DDesc = {}; fallback2DDesc.width = 1; fallback2DDesc.height = 1; fallback2DDesc.depth = 1; fallback2DDesc.mipLevels = 1; fallback2DDesc.arraySize = 1; fallback2DDesc.format = static_cast(RHI::Format::R8G8B8A8_UNorm); fallback2DDesc.textureType = static_cast(RHI::TextureType::Texture2D); fallback2DDesc.sampleCount = 1; fallback2DDesc.sampleQuality = 0; fallback2DDesc.flags = 0; m_fallbackTexture2D = context.device->CreateTexture( fallback2DDesc, whitePixels2D, sizeof(whitePixels2D), sizeof(whitePixels2D)); if (m_fallbackTexture2D == nullptr) { return false; } RHI::ResourceViewDesc fallback2DViewDesc = {}; fallback2DViewDesc.format = static_cast(RHI::Format::R8G8B8A8_UNorm); fallback2DViewDesc.dimension = RHI::ResourceViewDimension::Texture2D; fallback2DViewDesc.mipLevel = 0; m_fallbackTexture2DView = context.device->CreateShaderResourceView(m_fallbackTexture2D, fallback2DViewDesc); if (m_fallbackTexture2DView == nullptr) { return false; } const unsigned char whitePixelsCube[6 * 4] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; RHI::TextureDesc fallbackCubeDesc = {}; fallbackCubeDesc.width = 1; fallbackCubeDesc.height = 1; fallbackCubeDesc.depth = 1; fallbackCubeDesc.mipLevels = 1; fallbackCubeDesc.arraySize = 6; fallbackCubeDesc.format = static_cast(RHI::Format::R8G8B8A8_UNorm); fallbackCubeDesc.textureType = static_cast(RHI::TextureType::TextureCube); fallbackCubeDesc.sampleCount = 1; fallbackCubeDesc.sampleQuality = 0; fallbackCubeDesc.flags = 0; m_fallbackTextureCube = context.device->CreateTexture( fallbackCubeDesc, whitePixelsCube, sizeof(whitePixelsCube), 4); if (m_fallbackTextureCube == nullptr) { return false; } RHI::ResourceViewDesc fallbackCubeViewDesc = {}; fallbackCubeViewDesc.format = static_cast(RHI::Format::R8G8B8A8_UNorm); fallbackCubeViewDesc.dimension = RHI::ResourceViewDimension::TextureCube; fallbackCubeViewDesc.mipLevel = 0; m_fallbackTextureCubeView = context.device->CreateShaderResourceView(m_fallbackTextureCube, fallbackCubeViewDesc); if (m_fallbackTextureCubeView == nullptr) { return false; } return true; } bool BuiltinForwardPipeline::EnsureSkyboxResources(const RenderContext& context) { if (m_skyboxPipelineLayout != nullptr && m_skyboxPipelineState != nullptr && m_skyboxEnvironmentSet.set != nullptr && m_skyboxMaterialSet.set != nullptr && m_skyboxPanoramicTextureSet.set != nullptr && m_skyboxCubemapTextureSet.set != nullptr && m_skyboxSamplerSet.set != nullptr) { return true; } DestroySkyboxResources(); return CreateSkyboxResources(context); } bool BuiltinForwardPipeline::CreateSkyboxResources(const RenderContext& context) { if (!context.IsValid() || !m_builtinSkyboxShader.IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "BuiltinForwardPipeline skybox resources require a valid context and loaded shader"); return false; } const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(m_backendType); const Resources::ShaderPass* skyboxPass = FindCompatibleSkyboxPass(*m_builtinSkyboxShader.Get(), backend); if (skyboxPass == nullptr) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "BuiltinForwardPipeline could not resolve a valid skybox shader pass"); return false; } RHI::DescriptorSetLayoutBinding environmentBinding = {}; environmentBinding.binding = 0; environmentBinding.type = static_cast(RHI::DescriptorType::CBV); environmentBinding.count = 1; environmentBinding.visibility = static_cast(RHI::ShaderVisibility::All); RHI::DescriptorSetLayoutBinding materialBinding = environmentBinding; RHI::DescriptorSetLayoutBinding textureBinding = {}; textureBinding.binding = 0; textureBinding.type = static_cast(RHI::DescriptorType::SRV); textureBinding.count = 1; textureBinding.visibility = static_cast(RHI::ShaderVisibility::All); RHI::DescriptorSetLayoutBinding samplerBinding = {}; samplerBinding.binding = 0; samplerBinding.type = static_cast(RHI::DescriptorType::Sampler); samplerBinding.count = 1; samplerBinding.visibility = static_cast(RHI::ShaderVisibility::All); RHI::DescriptorSetLayoutDesc environmentLayout = {}; environmentLayout.bindings = &environmentBinding; environmentLayout.bindingCount = 1; RHI::DescriptorSetLayoutDesc materialLayout = {}; materialLayout.bindings = &materialBinding; materialLayout.bindingCount = 1; RHI::DescriptorSetLayoutDesc panoramicTextureLayout = {}; panoramicTextureLayout.bindings = &textureBinding; panoramicTextureLayout.bindingCount = 1; RHI::DescriptorSetLayoutDesc cubemapTextureLayout = {}; cubemapTextureLayout.bindings = &textureBinding; cubemapTextureLayout.bindingCount = 1; RHI::DescriptorSetLayoutDesc samplerLayout = {}; samplerLayout.bindings = &samplerBinding; samplerLayout.bindingCount = 1; RHI::DescriptorSetLayoutDesc setLayouts[] = { environmentLayout, materialLayout, panoramicTextureLayout, cubemapTextureLayout, samplerLayout }; RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {}; pipelineLayoutDesc.setLayouts = setLayouts; pipelineLayoutDesc.setLayoutCount = 5; m_skyboxPipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); if (m_skyboxPipelineLayout == nullptr) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "BuiltinForwardPipeline failed to create skybox pipeline layout"); DestroySkyboxResources(); return false; } auto createSkyboxDescriptorSet = [this](const RHI::DescriptorSetLayoutDesc& layout, RHI::DescriptorHeapType heapType, bool shaderVisible, OwnedDescriptorSet& ownedSet) -> bool { RHI::DescriptorPoolDesc poolDesc = {}; poolDesc.type = heapType; poolDesc.descriptorCount = 1; poolDesc.shaderVisible = shaderVisible; ownedSet.pool = m_device->CreateDescriptorPool(poolDesc); if (ownedSet.pool == nullptr) { return false; } ownedSet.set = ownedSet.pool->AllocateSet(layout); return ownedSet.set != nullptr; }; if (!createSkyboxDescriptorSet( environmentLayout, RHI::DescriptorHeapType::CBV_SRV_UAV, false, m_skyboxEnvironmentSet) || !createSkyboxDescriptorSet( materialLayout, RHI::DescriptorHeapType::CBV_SRV_UAV, false, m_skyboxMaterialSet) || !createSkyboxDescriptorSet( panoramicTextureLayout, RHI::DescriptorHeapType::CBV_SRV_UAV, true, m_skyboxPanoramicTextureSet) || !createSkyboxDescriptorSet( cubemapTextureLayout, RHI::DescriptorHeapType::CBV_SRV_UAV, true, m_skyboxCubemapTextureSet) || !createSkyboxDescriptorSet( samplerLayout, RHI::DescriptorHeapType::Sampler, true, m_skyboxSamplerSet)) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "BuiltinForwardPipeline failed to allocate skybox descriptor sets"); DestroySkyboxResources(); return false; } m_skyboxSamplerSet.set->UpdateSampler(0, m_sampler); m_skyboxPanoramicTextureSet.set->Update(0, m_fallbackTexture2DView); m_skyboxCubemapTextureSet.set->Update(0, m_fallbackTextureCubeView); m_skyboxBoundPanoramicTextureView = m_fallbackTexture2DView; m_skyboxBoundCubemapTextureView = m_fallbackTextureCubeView; m_skyboxPipelineState = m_device->CreatePipelineState( CreateSkyboxPipelineDesc( m_backendType, m_skyboxPipelineLayout, *m_builtinSkyboxShader.Get(), skyboxPass->name)); if (m_skyboxPipelineState == nullptr || !m_skyboxPipelineState->IsValid()) { Debug::Logger::Get().Error( Debug::LogCategory::Rendering, "BuiltinForwardPipeline failed to create skybox graphics pipeline state"); DestroySkyboxResources(); return false; } return true; } void BuiltinForwardPipeline::DestroySkyboxResources() { if (m_skyboxPipelineState != nullptr) { m_skyboxPipelineState->Shutdown(); delete m_skyboxPipelineState; m_skyboxPipelineState = nullptr; } DestroyOwnedDescriptorSet(m_skyboxSamplerSet); DestroyOwnedDescriptorSet(m_skyboxCubemapTextureSet); DestroyOwnedDescriptorSet(m_skyboxPanoramicTextureSet); DestroyOwnedDescriptorSet(m_skyboxMaterialSet); DestroyOwnedDescriptorSet(m_skyboxEnvironmentSet); m_skyboxBoundPanoramicTextureView = nullptr; m_skyboxBoundCubemapTextureView = nullptr; if (m_skyboxPipelineLayout != nullptr) { m_skyboxPipelineLayout->Shutdown(); delete m_skyboxPipelineLayout; m_skyboxPipelineLayout = nullptr; } } 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(); DestroySkyboxResources(); if (m_fallbackTextureCubeView != nullptr) { m_fallbackTextureCubeView->Shutdown(); delete m_fallbackTextureCubeView; m_fallbackTextureCubeView = nullptr; } if (m_fallbackTextureCube != nullptr) { m_fallbackTextureCube->Shutdown(); delete m_fallbackTextureCube; m_fallbackTextureCube = nullptr; } if (m_fallbackTexture2DView != nullptr) { m_fallbackTexture2DView->Shutdown(); delete m_fallbackTexture2DView; m_fallbackTexture2DView = nullptr; } if (m_fallbackTexture2D != nullptr) { m_fallbackTexture2D->Shutdown(); delete m_fallbackTexture2D; m_fallbackTexture2D = 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(); m_builtinSkyboxShader.Reset(); } } // namespace Pipelines } // namespace Rendering } // namespace XCEngine