diff --git a/editor/src/Viewport/Passes/SceneViewportGridPass.cpp b/editor/src/Viewport/Passes/SceneViewportGridPass.cpp index 8c234dd8..e17a24f6 100644 --- a/editor/src/Viewport/Passes/SceneViewportGridPass.cpp +++ b/editor/src/Viewport/Passes/SceneViewportGridPass.cpp @@ -2,11 +2,216 @@ #include "Viewport/SceneViewportShaderPaths.h" +#include "Rendering/Internal/RenderSurfacePipelineUtils.h" +#include "Rendering/Internal/ShaderVariantUtils.h" +#include "Rendering/Materials/RenderMaterialStateUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + namespace XCEngine { namespace Editor { namespace { +constexpr float kCameraHeightScaleFactor = 0.50f; +constexpr float kTransitionStart = 0.65f; +constexpr float kTransitionEnd = 0.95f; +constexpr float kMinimumVerticalViewComponent = 0.15f; + +struct GridConstants { + Math::Matrix4x4 viewProjection = Math::Matrix4x4::Identity(); + Math::Vector4 cameraPositionAndScale = Math::Vector4::Zero(); + Math::Vector4 cameraRightAndFade = Math::Vector4::Zero(); + Math::Vector4 cameraUpAndTanHalfFov = Math::Vector4::Zero(); + Math::Vector4 cameraForwardAndAspect = Math::Vector4::Zero(); + Math::Vector4 viewportNearFar = Math::Vector4::Zero(); + Math::Vector4 gridTransition = Math::Vector4::Zero(); +}; + +const Resources::ShaderPass* FindInfiniteGridCompatiblePass( + const Resources::Shader& shader, + Resources::ShaderBackend backend) { + const Resources::ShaderPass* gridPass = shader.FindPass("InfiniteGrid"); + if (gridPass != nullptr && + Rendering::Internal::ShaderPassHasGraphicsVariants( + shader, + gridPass->name, + backend)) { + return gridPass; + } + + if (shader.GetPassCount() > 0 && + Rendering::Internal::ShaderPassHasGraphicsVariants( + shader, + shader.GetPasses()[0].name, + backend)) { + return &shader.GetPasses()[0]; + } + + return nullptr; +} + +RHI::GraphicsPipelineDesc CreatePipelineDesc( + RHI::RHIType backendType, + RHI::RHIPipelineLayout* pipelineLayout, + const Resources::Shader& shader, + const Containers::String& passName, + const Rendering::RenderSurface& surface) { + RHI::GraphicsPipelineDesc pipelineDesc = {}; + pipelineDesc.pipelineLayout = pipelineLayout; + pipelineDesc.topologyType = + static_cast(RHI::PrimitiveTopologyType::Triangle); + Rendering::Internal::ApplySingleColorAttachmentPropertiesToGraphicsPipelineDesc( + surface, + pipelineDesc); + pipelineDesc.depthStencilFormat = + static_cast( + Rendering::Internal::ResolveSurfaceDepthFormat(surface)); + + const Resources::ShaderPass* shaderPass = shader.FindPass(passName); + if (shaderPass != nullptr && shaderPass->hasFixedFunctionState) { + Rendering::ApplyRenderState( + shaderPass->fixedFunctionState, + pipelineDesc); + } else { + Resources::MaterialRenderState fallbackState = {}; + fallbackState.cullMode = Resources::MaterialCullMode::None; + fallbackState.depthWriteEnable = false; + fallbackState.depthTestEnable = true; + fallbackState.depthFunc = Resources::MaterialComparisonFunc::LessEqual; + fallbackState.blendEnable = true; + fallbackState.srcBlend = Resources::MaterialBlendFactor::SrcAlpha; + fallbackState.dstBlend = Resources::MaterialBlendFactor::InvSrcAlpha; + fallbackState.srcBlendAlpha = Resources::MaterialBlendFactor::One; + fallbackState.dstBlendAlpha = + Resources::MaterialBlendFactor::InvSrcAlpha; + fallbackState.blendOp = Resources::MaterialBlendOp::Add; + fallbackState.blendOpAlpha = Resources::MaterialBlendOp::Add; + fallbackState.colorWriteMask = + static_cast(RHI::ColorWriteMask::All); + Rendering::ApplyRenderState(fallbackState, pipelineDesc); + } + + const Resources::ShaderBackend backend = + Rendering::Internal::ToShaderBackend(backendType); + if (const Resources::ShaderStageVariant* vertexVariant = + shader.FindVariant( + passName, + Resources::ShaderType::Vertex, + backend)) { + if (shaderPass != nullptr) { + Rendering::Internal::ApplyShaderStageVariant( + shader.GetPath(), + *shaderPass, + backend, + *vertexVariant, + pipelineDesc.vertexShader); + } + } + if (const Resources::ShaderStageVariant* fragmentVariant = + shader.FindVariant( + passName, + Resources::ShaderType::Fragment, + backend)) { + if (shaderPass != nullptr) { + Rendering::Internal::ApplyShaderStageVariant( + shader.GetPath(), + *shaderPass, + backend, + *fragmentVariant, + pipelineDesc.fragmentShader); + } + } + + return pipelineDesc; +} + +float SnapGridSpacing(float targetSpacing) { + const float clampedTarget = (std::max)(targetSpacing, 0.02f); + const float exponent = std::floor(std::log10(clampedTarget)); + return std::pow(10.0f, exponent); +} + +float ComputeTransitionBlend(float targetSpacing, float baseScale) { + const float normalizedSpacing = + (std::max)(targetSpacing / (std::max)(baseScale, 1e-4f), 1.0f); + const float transitionPosition = std::log10(normalizedSpacing); + const float t = + (transitionPosition - kTransitionStart) / + (kTransitionEnd - kTransitionStart); + const float saturated = (std::clamp)(t, 0.0f, 1.0f); + return saturated * saturated * (3.0f - 2.0f * saturated); +} + +float ComputeViewDistanceToGridPlane(const SceneViewportGridPassData& data) { + const float cameraHeight = std::abs(data.cameraPosition.y); + const Math::Vector3 forward = data.cameraForward.Normalized(); + + const bool lookingTowardGrid = + (data.cameraPosition.y >= 0.0f && forward.y < 0.0f) || + (data.cameraPosition.y < 0.0f && forward.y > 0.0f); + if (!lookingTowardGrid) { + return cameraHeight; + } + + const float verticalViewComponent = + (std::max)(std::abs(forward.y), kMinimumVerticalViewComponent); + return cameraHeight / verticalViewComponent; +} + +Math::Matrix4x4 BuildInfiniteGridViewMatrix( + const SceneViewportGridPassData& data) { + const Math::Vector3 right = data.cameraRight.Normalized(); + const Math::Vector3 up = data.cameraUp.Normalized(); + const Math::Vector3 forward = data.cameraForward.Normalized(); + + Math::Matrix4x4 view = Math::Matrix4x4::Identity(); + view.m[0][0] = right.x; + view.m[0][1] = right.y; + view.m[0][2] = right.z; + view.m[0][3] = -Math::Vector3::Dot(right, data.cameraPosition); + + view.m[1][0] = up.x; + view.m[1][1] = up.y; + view.m[1][2] = up.z; + view.m[1][3] = -Math::Vector3::Dot(up, data.cameraPosition); + + view.m[2][0] = forward.x; + view.m[2][1] = forward.y; + view.m[2][2] = forward.z; + view.m[2][3] = -Math::Vector3::Dot(forward, data.cameraPosition); + return view; +} + +Math::Matrix4x4 BuildInfiniteGridProjectionMatrix( + const SceneViewportGridPassData& data, + float viewportWidth, + float viewportHeight) { + const float aspect = viewportHeight > 0.0f + ? viewportWidth / viewportHeight + : 1.0f; + return Math::Matrix4x4::Perspective( + data.verticalFovDegrees * Math::DEG_TO_RAD, + aspect, + data.nearClipPlane, + data.farClipPlane); +} + class SceneViewportGridPass final : public Rendering::RenderPass { public: SceneViewportGridPass( @@ -34,22 +239,351 @@ private: } // namespace -SceneViewportGridPassRenderer::SceneViewportGridPassRenderer() - : m_gridPass(GetSceneViewportInfiniteGridShaderPath()) { +InfiniteGridParameters BuildInfiniteGridParameters( + const SceneViewportGridPassData& data) { + InfiniteGridParameters parameters = {}; + if (!data.valid) { + return parameters; + } + + const float cameraHeight = std::abs(data.cameraPosition.y); + const float viewDistance = ComputeViewDistanceToGridPlane(data); + const float targetSpacing = + (std::max)(cameraHeight * kCameraHeightScaleFactor, 0.1f); + + parameters.valid = true; + parameters.baseScale = SnapGridSpacing(targetSpacing); + parameters.transitionBlend = + ComputeTransitionBlend(targetSpacing, parameters.baseScale); + parameters.fadeDistance = + (std::max)(parameters.baseScale * 320.0f, viewDistance * 80.0f); + return parameters; } +class SceneViewportGridPassRenderer::Impl { +public: + Impl() + : m_shaderPath(GetSceneViewportInfiniteGridShaderPath()) { + } + + void Shutdown() { + DestroyResources(); + } + + bool Render( + const Rendering::RenderContext& renderContext, + const Rendering::RenderSurface& surface, + const SceneViewportGridPassData& data) { + if (!data.valid || !renderContext.IsValid()) { + return false; + } + + const std::vector& colorAttachments = + surface.GetColorAttachments(); + if (!Rendering::Internal::HasSingleColorAttachment(surface) || + colorAttachments.empty() || + colorAttachments[0] == nullptr || + surface.GetDepthAttachment() == nullptr) { + return false; + } + + const Math::RectInt renderArea = surface.GetRenderArea(); + if (renderArea.width <= 0 || renderArea.height <= 0) { + return false; + } + + if (!EnsureInitialized(renderContext, surface)) { + return false; + } + + const InfiniteGridParameters parameters = + BuildInfiniteGridParameters(data); + if (!parameters.valid) { + return false; + } + + const Math::Matrix4x4 viewProjection = + BuildInfiniteGridProjectionMatrix( + data, + static_cast(renderArea.width), + static_cast(renderArea.height)) * + BuildInfiniteGridViewMatrix(data); + + const float aspect = renderArea.height > 0 + ? static_cast(renderArea.width) / + static_cast(renderArea.height) + : 1.0f; + + GridConstants constants = {}; + constants.viewProjection = viewProjection.Transpose(); + constants.cameraPositionAndScale = + Math::Vector4(data.cameraPosition, parameters.baseScale); + constants.cameraRightAndFade = + Math::Vector4(data.cameraRight, parameters.fadeDistance); + constants.cameraUpAndTanHalfFov = Math::Vector4( + data.cameraUp, + std::tan(data.verticalFovDegrees * Math::DEG_TO_RAD * 0.5f)); + constants.cameraForwardAndAspect = + Math::Vector4(data.cameraForward, aspect); + constants.viewportNearFar = Math::Vector4( + static_cast(surface.GetWidth()), + static_cast(surface.GetHeight()), + data.nearClipPlane, + data.farClipPlane); + constants.gridTransition = + Math::Vector4(parameters.transitionBlend, 0.0f, 0.0f, 0.0f); + + m_constantSet->WriteConstant(0, &constants, sizeof(constants)); + + RHI::RHICommandList* commandList = renderContext.commandList; + RHI::RHIResourceView* renderTarget = colorAttachments[0]; + RHI::RHIResourceView* depthAttachment = surface.GetDepthAttachment(); + if (surface.IsAutoTransitionEnabled()) { + commandList->TransitionBarrier( + renderTarget, + surface.GetColorStateAfter(), + RHI::ResourceStates::RenderTarget); + commandList->TransitionBarrier( + depthAttachment, + surface.GetDepthStateAfter(), + RHI::ResourceStates::DepthWrite); + } + + commandList->SetRenderTargets(1, &renderTarget, depthAttachment); + + 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 + }; + + commandList->SetViewport(viewport); + commandList->SetScissorRect(scissorRect); + commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); + commandList->SetPipelineState(m_pipelineState); + + RHI::RHIDescriptorSet* descriptorSets[] = { m_constantSet }; + commandList->SetGraphicsDescriptorSets( + 0, + 1, + descriptorSets, + m_pipelineLayout); + commandList->Draw(3, 1, 0, 0); + commandList->EndRenderPass(); + + if (surface.IsAutoTransitionEnabled()) { + commandList->TransitionBarrier( + renderTarget, + RHI::ResourceStates::RenderTarget, + surface.GetColorStateAfter()); + commandList->TransitionBarrier( + depthAttachment, + RHI::ResourceStates::DepthWrite, + surface.GetDepthStateAfter()); + } + + return true; + } + +private: + bool EnsureInitialized( + const Rendering::RenderContext& renderContext, + const Rendering::RenderSurface& surface) { + const RHI::Format renderTargetFormat = + Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u); + const RHI::Format depthFormat = + Rendering::Internal::ResolveSurfaceDepthFormat(surface); + const uint32_t renderTargetSampleCount = + Rendering::Internal::ResolveSurfaceSampleCount(surface); + if (m_pipelineState != nullptr && + m_pipelineLayout != nullptr && + m_constantPool != nullptr && + m_constantSet != nullptr && + m_device == renderContext.device && + m_backendType == renderContext.backendType && + m_renderTargetFormat == renderTargetFormat && + m_depthStencilFormat == depthFormat && + m_renderTargetSampleCount == renderTargetSampleCount) { + return true; + } + + DestroyResources(); + return CreateResources(renderContext, surface); + } + + bool CreateResources( + const Rendering::RenderContext& renderContext, + const Rendering::RenderSurface& surface) { + if (!renderContext.IsValid()) { + return false; + } + + if (!Rendering::Internal::HasSingleColorAttachment(surface) || + Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u) == + RHI::Format::Unknown || + Rendering::Internal::ResolveSurfaceDepthFormat(surface) == + RHI::Format::Unknown) { + return false; + } + + if (m_shaderPath.Empty()) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "SceneViewportGridPassRenderer requires a valid shader path"); + return false; + } + + m_device = renderContext.device; + m_backendType = renderContext.backendType; + m_shader = Resources::ResourceManager::Get().Load( + m_shaderPath); + if (!m_shader.IsValid()) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "SceneViewportGridPassRenderer failed to load infinite-grid shader"); + DestroyResources(); + return false; + } + + const Resources::ShaderBackend backend = + Rendering::Internal::ToShaderBackend(m_backendType); + const Resources::ShaderPass* infiniteGridPass = + FindInfiniteGridCompatiblePass(*m_shader.Get(), backend); + if (infiniteGridPass == nullptr) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "SceneViewportGridPassRenderer could not resolve InfiniteGrid pass"); + DestroyResources(); + return false; + } + + RHI::DescriptorSetLayoutBinding constantBinding = {}; + constantBinding.binding = 0; + constantBinding.type = + static_cast(RHI::DescriptorType::CBV); + constantBinding.count = 1; + + RHI::DescriptorSetLayoutDesc constantLayout = {}; + constantLayout.bindings = &constantBinding; + constantLayout.bindingCount = 1; + + RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {}; + pipelineLayoutDesc.setLayouts = &constantLayout; + pipelineLayoutDesc.setLayoutCount = 1; + m_pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); + if (m_pipelineLayout == nullptr) { + DestroyResources(); + return false; + } + + RHI::DescriptorPoolDesc constantPoolDesc = {}; + constantPoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV; + constantPoolDesc.descriptorCount = 1; + constantPoolDesc.shaderVisible = false; + m_constantPool = m_device->CreateDescriptorPool(constantPoolDesc); + if (m_constantPool == nullptr) { + DestroyResources(); + return false; + } + + m_constantSet = m_constantPool->AllocateSet(constantLayout); + if (m_constantSet == nullptr) { + DestroyResources(); + return false; + } + + const RHI::GraphicsPipelineDesc pipelineDesc = + CreatePipelineDesc( + m_backendType, + m_pipelineLayout, + *m_shader.Get(), + infiniteGridPass->name, + surface); + m_pipelineState = m_device->CreatePipelineState(pipelineDesc); + if (m_pipelineState == nullptr || !m_pipelineState->IsValid()) { + DestroyResources(); + return false; + } + + m_renderTargetFormat = + Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u); + m_depthStencilFormat = + Rendering::Internal::ResolveSurfaceDepthFormat(surface); + m_renderTargetSampleCount = + Rendering::Internal::ResolveSurfaceSampleCount(surface); + return true; + } + + void DestroyResources() { + if (m_pipelineState != nullptr) { + m_pipelineState->Shutdown(); + delete m_pipelineState; + m_pipelineState = nullptr; + } + + if (m_constantSet != nullptr) { + m_constantSet->Shutdown(); + delete m_constantSet; + m_constantSet = nullptr; + } + + if (m_constantPool != nullptr) { + m_constantPool->Shutdown(); + delete m_constantPool; + m_constantPool = nullptr; + } + + if (m_pipelineLayout != nullptr) { + m_pipelineLayout->Shutdown(); + delete m_pipelineLayout; + m_pipelineLayout = nullptr; + } + + m_device = nullptr; + m_backendType = RHI::RHIType::D3D12; + m_shader.Reset(); + m_renderTargetFormat = RHI::Format::Unknown; + m_depthStencilFormat = RHI::Format::Unknown; + m_renderTargetSampleCount = 1u; + } + + RHI::RHIDevice* m_device = nullptr; + RHI::RHIType m_backendType = RHI::RHIType::D3D12; + RHI::RHIPipelineLayout* m_pipelineLayout = nullptr; + RHI::RHIPipelineState* m_pipelineState = nullptr; + RHI::RHIDescriptorPool* m_constantPool = nullptr; + RHI::RHIDescriptorSet* m_constantSet = nullptr; + Containers::String m_shaderPath = {}; + Resources::ResourceHandle m_shader = {}; + RHI::Format m_renderTargetFormat = RHI::Format::Unknown; + RHI::Format m_depthStencilFormat = RHI::Format::Unknown; + uint32_t m_renderTargetSampleCount = 1u; +}; + +SceneViewportGridPassRenderer::SceneViewportGridPassRenderer() + : m_impl(std::make_unique()) { +} + +SceneViewportGridPassRenderer::~SceneViewportGridPassRenderer() = default; + void SceneViewportGridPassRenderer::Shutdown() { - m_gridPass.Shutdown(); + m_impl->Shutdown(); } bool SceneViewportGridPassRenderer::Render( const Rendering::RenderContext& renderContext, const Rendering::RenderSurface& surface, const SceneViewportGridPassData& data) { - return m_gridPass.Render( - renderContext, - surface, - ToBuiltinSceneViewportGridPassData(data)); + return m_impl->Render(renderContext, surface, data); } std::unique_ptr CreateSceneViewportGridPass( diff --git a/editor/src/Viewport/Passes/SceneViewportGridPass.h b/editor/src/Viewport/Passes/SceneViewportGridPass.h index f6bc2150..f409ee88 100644 --- a/editor/src/Viewport/Passes/SceneViewportGridPass.h +++ b/editor/src/Viewport/Passes/SceneViewportGridPass.h @@ -2,7 +2,6 @@ #include "Viewport/SceneViewportPassSpecs.h" -#include #include #include #include @@ -15,7 +14,11 @@ namespace Editor { class SceneViewportGridPassRenderer { public: SceneViewportGridPassRenderer(); - ~SceneViewportGridPassRenderer() = default; + ~SceneViewportGridPassRenderer(); + SceneViewportGridPassRenderer(const SceneViewportGridPassRenderer&) = delete; + SceneViewportGridPassRenderer& operator=(const SceneViewportGridPassRenderer&) = delete; + SceneViewportGridPassRenderer(SceneViewportGridPassRenderer&&) = delete; + SceneViewportGridPassRenderer& operator=(SceneViewportGridPassRenderer&&) = delete; void Shutdown(); @@ -25,7 +28,8 @@ public: const SceneViewportGridPassData& data); private: - Rendering::Passes::BuiltinInfiniteGridPass m_gridPass; + class Impl; + std::unique_ptr m_impl; }; std::unique_ptr CreateSceneViewportGridPass( diff --git a/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp b/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp index 2af7a5ec..1c852d57 100644 --- a/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp +++ b/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.cpp @@ -2,11 +2,210 @@ #include "Viewport/ViewportHostRenderTargets.h" +#include "Rendering/Internal/RenderSurfacePipelineUtils.h" +#include "Rendering/Internal/ShaderVariantUtils.h" +#include "Rendering/Materials/RenderMaterialStateUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + namespace XCEngine { namespace Editor { namespace { +class SceneViewportSelectionMaskPass final + : public Rendering::Passes::BuiltinDepthStylePassBase { +public: + SceneViewportSelectionMaskPass() + : BuiltinDepthStylePassBase( + Rendering::BuiltinMaterialPass::SelectionMask, + Resources::GetBuiltinSelectionMaskShaderPath()) { + } + + const char* GetName() const override { + return "SceneViewportSelectionMaskPass"; + } + + bool Render( + const Rendering::RenderContext& context, + const Rendering::RenderSurface& surface, + const Rendering::RenderSceneData& sceneData, + const std::vector& selectedObjectIds) { + m_selectedObjectIds.clear(); + m_selectedObjectIds.reserve(selectedObjectIds.size()); + for (uint64_t selectedObjectId : selectedObjectIds) { + Rendering::RenderObjectId renderObjectId = + Rendering::kInvalidRenderObjectId; + if (Rendering::TryConvertRuntimeObjectIdToRenderObjectId( + selectedObjectId, + renderObjectId)) { + m_selectedObjectIds.push_back(renderObjectId); + } + } + + if (m_selectedObjectIds.empty()) { + return false; + } + + Rendering::RenderSceneData selectionMaskSceneData = sceneData; + selectionMaskSceneData.cameraData.clearFlags = + Rendering::RenderClearFlags::Color; + selectionMaskSceneData.cameraData.clearColor = Math::Color::Black(); + + const Rendering::RenderPassContext passContext = { + context, + surface, + selectionMaskSceneData, + nullptr, + nullptr, + RHI::ResourceStates::Common + }; + return Execute(passContext); + } + +protected: + bool ShouldRenderVisibleItem( + const Rendering::VisibleRenderItem& visibleItem) const override { + if (!Rendering::IsValidRenderObjectId(visibleItem.renderObjectId)) { + return false; + } + + return std::find( + m_selectedObjectIds.begin(), + m_selectedObjectIds.end(), + visibleItem.renderObjectId) != m_selectedObjectIds.end(); + } + +private: + std::vector m_selectedObjectIds = {}; +}; + +const Resources::ShaderPass* FindSelectionOutlineCompatiblePass( + const Resources::Shader& shader, + Resources::ShaderBackend backend) { + const Resources::ShaderPass* outlinePass = + shader.FindPass("SelectionOutline"); + if (outlinePass != nullptr && + Rendering::Internal::ShaderPassHasGraphicsVariants( + shader, + outlinePass->name, + backend)) { + return outlinePass; + } + + const Resources::ShaderPass* editorOutlinePass = + shader.FindPass("EditorSelectionOutline"); + if (editorOutlinePass != nullptr && + Rendering::Internal::ShaderPassHasGraphicsVariants( + shader, + editorOutlinePass->name, + backend)) { + return editorOutlinePass; + } + + if (shader.GetPassCount() > 0 && + Rendering::Internal::ShaderPassHasGraphicsVariants( + shader, + shader.GetPasses()[0].name, + backend)) { + return &shader.GetPasses()[0]; + } + + return nullptr; +} + +RHI::GraphicsPipelineDesc CreatePipelineDesc( + RHI::RHIType backendType, + RHI::RHIPipelineLayout* pipelineLayout, + const Resources::Shader& shader, + const Containers::String& passName, + const Rendering::RenderSurface& surface) { + RHI::GraphicsPipelineDesc pipelineDesc = {}; + pipelineDesc.pipelineLayout = pipelineLayout; + pipelineDesc.topologyType = + static_cast(RHI::PrimitiveTopologyType::Triangle); + Rendering::Internal::ApplySingleColorAttachmentPropertiesToGraphicsPipelineDesc( + surface, + pipelineDesc); + pipelineDesc.depthStencilFormat = + static_cast(RHI::Format::Unknown); + + const Resources::ShaderPass* shaderPass = shader.FindPass(passName); + if (shaderPass != nullptr && shaderPass->hasFixedFunctionState) { + Rendering::ApplyRenderState( + shaderPass->fixedFunctionState, + pipelineDesc); + } else { + Resources::MaterialRenderState fallbackState = {}; + fallbackState.cullMode = Resources::MaterialCullMode::None; + fallbackState.depthWriteEnable = false; + fallbackState.depthTestEnable = false; + fallbackState.depthFunc = Resources::MaterialComparisonFunc::Always; + fallbackState.blendEnable = true; + fallbackState.srcBlend = Resources::MaterialBlendFactor::SrcAlpha; + fallbackState.dstBlend = Resources::MaterialBlendFactor::InvSrcAlpha; + fallbackState.srcBlendAlpha = Resources::MaterialBlendFactor::One; + fallbackState.dstBlendAlpha = + Resources::MaterialBlendFactor::InvSrcAlpha; + fallbackState.blendOp = Resources::MaterialBlendOp::Add; + fallbackState.blendOpAlpha = Resources::MaterialBlendOp::Add; + fallbackState.colorWriteMask = + static_cast(RHI::ColorWriteMask::All); + Rendering::ApplyRenderState(fallbackState, pipelineDesc); + } + + const Resources::ShaderBackend backend = + Rendering::Internal::ToShaderBackend(backendType); + if (const Resources::ShaderStageVariant* vertexVariant = + shader.FindVariant( + passName, + Resources::ShaderType::Vertex, + backend)) { + if (shaderPass != nullptr) { + Rendering::Internal::ApplyShaderStageVariant( + shader.GetPath(), + *shaderPass, + backend, + *vertexVariant, + pipelineDesc.vertexShader); + } + } + if (const Resources::ShaderStageVariant* fragmentVariant = + shader.FindVariant( + passName, + Resources::ShaderType::Fragment, + backend)) { + if (shaderPass != nullptr) { + Rendering::Internal::ApplyShaderStageVariant( + shader.GetPath(), + *shaderPass, + backend, + *fragmentVariant, + pipelineDesc.fragmentShader); + } + } + + return pipelineDesc; +} + class SceneViewportSelectionOutlinePass final : public Rendering::RenderPass { public: SceneViewportSelectionOutlinePass( @@ -43,14 +242,435 @@ private: } // namespace +class SceneViewportSelectionOutlinePassRenderer::Impl { +public: + struct OutlineConstants { + Math::Vector4 viewportSizeAndTexelSize = Math::Vector4::Zero(); + Math::Vector4 outlineColor = Math::Vector4::Zero(); + Math::Vector4 outlineInfo = Math::Vector4::Zero(); + Math::Vector4 depthParams = Math::Vector4::Zero(); + }; + + Impl() + : m_selectionMaskPass(std::make_unique()) + , m_shaderPath(Resources::GetBuiltinSelectionOutlineShaderPath()) { + } + + void Shutdown() { + if (m_selectionMaskPass != nullptr) { + m_selectionMaskPass->Shutdown(); + } + DestroyResources(); + } + + bool Render( + const Rendering::RenderContext& renderContext, + const Rendering::RenderSurface& surface, + const Rendering::RenderSceneData& sceneData, + ViewportRenderTargets& targets, + const std::vector& selectedObjectIds, + const SceneViewportSelectionOutlineStyle& style) { + Rendering::RenderSurface selectionMaskSurface = + BuildViewportSelectionMaskSurface(targets); + selectionMaskSurface.SetRenderArea(surface.GetRenderArea()); + + if (!m_selectionMaskPass->Render( + renderContext, + selectionMaskSurface, + sceneData, + selectedObjectIds)) { + return false; + } + + targets.selectionMaskState = RHI::ResourceStates::PixelShaderResource; + return RenderOutline( + renderContext, + surface, + targets.selectionMaskShaderView, + targets.selectionMaskState, + targets.depthShaderView, + surface.GetDepthStateAfter(), + style); + } + +private: + bool RenderOutline( + const Rendering::RenderContext& renderContext, + const Rendering::RenderSurface& surface, + RHI::RHIResourceView* selectionMaskTextureView, + RHI::ResourceStates selectionMaskState, + RHI::RHIResourceView* depthTextureView, + RHI::ResourceStates depthTextureState, + const SceneViewportSelectionOutlineStyle& style) { + if (!renderContext.IsValid() || + selectionMaskTextureView == nullptr || + depthTextureView == nullptr) { + return false; + } + + const std::vector& colorAttachments = + surface.GetColorAttachments(); + if (!Rendering::Internal::HasSingleColorAttachment(surface) || + colorAttachments.empty() || + colorAttachments[0] == nullptr) { + return false; + } + + const Math::RectInt renderArea = surface.GetRenderArea(); + if (renderArea.width <= 0 || renderArea.height <= 0) { + return false; + } + + if (!EnsureInitialized(renderContext, surface)) { + return false; + } + + OutlineConstants constants = {}; + constants.viewportSizeAndTexelSize = Math::Vector4( + static_cast(surface.GetWidth()), + static_cast(surface.GetHeight()), + surface.GetWidth() > 0 + ? 1.0f / static_cast(surface.GetWidth()) + : 0.0f, + surface.GetHeight() > 0 + ? 1.0f / static_cast(surface.GetHeight()) + : 0.0f); + constants.outlineColor = Math::Vector4( + style.outlineColor.r, + style.outlineColor.g, + style.outlineColor.b, + style.outlineColor.a); + constants.outlineInfo = Math::Vector4( + style.debugSelectionMask ? 1.0f : 0.0f, + style.outlineWidthPixels, + 0.0f, + 0.0f); + constants.depthParams = Math::Vector4(1.0e-5f, 0.0f, 0.0f, 0.0f); + + m_constantSet->WriteConstant(0, &constants, sizeof(constants)); + m_textureSet->Update(0, selectionMaskTextureView); + m_textureSet->Update(1, depthTextureView); + + RHI::RHICommandList* commandList = renderContext.commandList; + RHI::RHIResourceView* renderTarget = colorAttachments[0]; + if (surface.IsAutoTransitionEnabled()) { + commandList->TransitionBarrier( + renderTarget, + surface.GetColorStateAfter(), + RHI::ResourceStates::RenderTarget); + commandList->TransitionBarrier( + selectionMaskTextureView, + selectionMaskState, + RHI::ResourceStates::PixelShaderResource); + commandList->TransitionBarrier( + depthTextureView, + depthTextureState, + RHI::ResourceStates::PixelShaderResource); + } + commandList->SetRenderTargets(1, &renderTarget, nullptr); + + 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 + }; + + commandList->SetViewport(viewport); + commandList->SetScissorRect(scissorRect); + commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); + commandList->SetPipelineState(m_pipelineState); + + RHI::RHIDescriptorSet* descriptorSets[] = { + m_constantSet, + m_textureSet + }; + commandList->SetGraphicsDescriptorSets( + 0, + 2, + descriptorSets, + m_pipelineLayout); + commandList->Draw(3, 1, 0, 0); + commandList->EndRenderPass(); + + if (surface.IsAutoTransitionEnabled()) { + commandList->TransitionBarrier( + renderTarget, + RHI::ResourceStates::RenderTarget, + surface.GetColorStateAfter()); + commandList->TransitionBarrier( + selectionMaskTextureView, + RHI::ResourceStates::PixelShaderResource, + selectionMaskState); + commandList->TransitionBarrier( + depthTextureView, + RHI::ResourceStates::PixelShaderResource, + depthTextureState); + } + return true; + } + + bool EnsureInitialized( + const Rendering::RenderContext& renderContext, + const Rendering::RenderSurface& surface) { + const RHI::Format renderTargetFormat = + Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u); + const uint32_t renderTargetSampleCount = + Rendering::Internal::ResolveSurfaceSampleCount(surface); + if (m_pipelineLayout != nullptr && + m_pipelineState != nullptr && + m_constantPool != nullptr && + m_constantSet != nullptr && + m_texturePool != nullptr && + m_textureSet != nullptr && + m_device == renderContext.device && + m_backendType == renderContext.backendType && + m_renderTargetFormat == renderTargetFormat && + m_renderTargetSampleCount == renderTargetSampleCount) { + return true; + } + + if (HasCreatedResources()) { + DestroyResources(); + } + return CreateResources(renderContext, surface); + } + + bool CreateResources( + const Rendering::RenderContext& renderContext, + const Rendering::RenderSurface& surface) { + if (!renderContext.IsValid()) { + return false; + } + + if (!Rendering::Internal::HasSingleColorAttachment(surface) || + Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u) == + RHI::Format::Unknown) { + return false; + } + + if (m_shaderPath.Empty()) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "SceneViewportSelectionOutlinePassRenderer requires a valid shader path"); + return false; + } + + Resources::ResourceHandle shader = + Resources::ResourceManager::Get().Load( + m_shaderPath); + if (!shader.IsValid()) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "SceneViewportSelectionOutlinePassRenderer failed to load selection-outline shader"); + ResetState(); + return false; + } + + m_device = renderContext.device; + m_backendType = renderContext.backendType; + m_shader.emplace(std::move(shader)); + + const Resources::ShaderBackend backend = + Rendering::Internal::ToShaderBackend(m_backendType); + const Resources::ShaderPass* outlinePass = + FindSelectionOutlineCompatiblePass(*m_shader->Get(), backend); + if (outlinePass == nullptr) { + Debug::Logger::Get().Error( + Debug::LogCategory::Rendering, + "SceneViewportSelectionOutlinePassRenderer could not resolve SelectionOutline pass"); + DestroyResources(); + return false; + } + + RHI::DescriptorSetLayoutBinding constantBinding = {}; + constantBinding.binding = 0; + constantBinding.type = + static_cast(RHI::DescriptorType::CBV); + constantBinding.count = 1; + + RHI::DescriptorSetLayoutBinding textureBindings[2] = {}; + textureBindings[0].binding = 0; + textureBindings[0].type = + static_cast(RHI::DescriptorType::SRV); + textureBindings[0].count = 1; + textureBindings[1].binding = 1; + textureBindings[1].type = + static_cast(RHI::DescriptorType::SRV); + textureBindings[1].count = 1; + + RHI::DescriptorSetLayoutDesc constantLayout = {}; + constantLayout.bindings = &constantBinding; + constantLayout.bindingCount = 1; + + RHI::DescriptorSetLayoutDesc textureLayout = {}; + textureLayout.bindings = textureBindings; + textureLayout.bindingCount = 2; + + RHI::DescriptorSetLayoutDesc setLayouts[2] = {}; + setLayouts[0] = constantLayout; + setLayouts[1] = textureLayout; + + RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {}; + pipelineLayoutDesc.setLayouts = setLayouts; + pipelineLayoutDesc.setLayoutCount = 2; + m_pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); + if (m_pipelineLayout == nullptr) { + DestroyResources(); + return false; + } + + RHI::DescriptorPoolDesc constantPoolDesc = {}; + constantPoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV; + constantPoolDesc.descriptorCount = 1; + constantPoolDesc.shaderVisible = false; + m_constantPool = m_device->CreateDescriptorPool(constantPoolDesc); + if (m_constantPool == nullptr) { + DestroyResources(); + return false; + } + + m_constantSet = m_constantPool->AllocateSet(constantLayout); + if (m_constantSet == nullptr) { + DestroyResources(); + return false; + } + + RHI::DescriptorPoolDesc texturePoolDesc = {}; + texturePoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV; + texturePoolDesc.descriptorCount = 2; + texturePoolDesc.shaderVisible = true; + m_texturePool = m_device->CreateDescriptorPool(texturePoolDesc); + if (m_texturePool == nullptr) { + DestroyResources(); + return false; + } + + m_textureSet = m_texturePool->AllocateSet(textureLayout); + if (m_textureSet == nullptr) { + DestroyResources(); + return false; + } + + m_pipelineState = m_device->CreatePipelineState( + CreatePipelineDesc( + m_backendType, + m_pipelineLayout, + *m_shader->Get(), + outlinePass->name, + surface)); + if (m_pipelineState == nullptr || !m_pipelineState->IsValid()) { + DestroyResources(); + return false; + } + + m_renderTargetFormat = + Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u); + m_renderTargetSampleCount = + Rendering::Internal::ResolveSurfaceSampleCount(surface); + return true; + } + + bool HasCreatedResources() const { + return m_device != nullptr || + m_pipelineLayout != nullptr || + m_pipelineState != nullptr || + m_constantPool != nullptr || + m_constantSet != nullptr || + m_texturePool != nullptr || + m_textureSet != nullptr || + m_shader.has_value(); + } + + void DestroyResources() { + if (m_pipelineState != nullptr) { + m_pipelineState->Shutdown(); + delete m_pipelineState; + m_pipelineState = nullptr; + } + + if (m_textureSet != nullptr) { + m_textureSet->Shutdown(); + delete m_textureSet; + m_textureSet = nullptr; + } + + if (m_texturePool != nullptr) { + m_texturePool->Shutdown(); + delete m_texturePool; + m_texturePool = nullptr; + } + + if (m_constantSet != nullptr) { + m_constantSet->Shutdown(); + delete m_constantSet; + m_constantSet = nullptr; + } + + if (m_constantPool != nullptr) { + m_constantPool->Shutdown(); + delete m_constantPool; + m_constantPool = nullptr; + } + + if (m_pipelineLayout != nullptr) { + m_pipelineLayout->Shutdown(); + delete m_pipelineLayout; + m_pipelineLayout = nullptr; + } + + if (m_shader.has_value()) { + m_shader.reset(); + } + + ResetState(); + } + + void ResetState() { + m_device = nullptr; + m_backendType = RHI::RHIType::D3D12; + m_pipelineLayout = nullptr; + m_pipelineState = nullptr; + m_constantPool = nullptr; + m_constantSet = nullptr; + m_texturePool = nullptr; + m_textureSet = nullptr; + m_shader.reset(); + m_renderTargetFormat = RHI::Format::Unknown; + m_renderTargetSampleCount = 1u; + } + + std::unique_ptr m_selectionMaskPass; + RHI::RHIDevice* m_device = nullptr; + RHI::RHIType m_backendType = RHI::RHIType::D3D12; + RHI::RHIPipelineLayout* m_pipelineLayout = nullptr; + RHI::RHIPipelineState* m_pipelineState = nullptr; + RHI::RHIDescriptorPool* m_constantPool = nullptr; + RHI::RHIDescriptorSet* m_constantSet = nullptr; + RHI::RHIDescriptorPool* m_texturePool = nullptr; + RHI::RHIDescriptorSet* m_textureSet = nullptr; + Containers::String m_shaderPath = {}; + std::optional> m_shader = {}; + RHI::Format m_renderTargetFormat = RHI::Format::Unknown; + uint32_t m_renderTargetSampleCount = 1u; +}; + SceneViewportSelectionOutlinePassRenderer::SceneViewportSelectionOutlinePassRenderer() - : m_selectionMaskPass() - , m_outlinePass() { + : m_impl(std::make_unique()) { } +SceneViewportSelectionOutlinePassRenderer::~SceneViewportSelectionOutlinePassRenderer() = default; + void SceneViewportSelectionOutlinePassRenderer::Shutdown() { - m_selectionMaskPass.Shutdown(); - m_outlinePass.Shutdown(); + m_impl->Shutdown(); } bool SceneViewportSelectionOutlinePassRenderer::Render( @@ -60,29 +680,13 @@ bool SceneViewportSelectionOutlinePassRenderer::Render( ViewportRenderTargets& targets, const std::vector& selectedObjectIds, const SceneViewportSelectionOutlineStyle& style) { - Rendering::RenderSurface selectionMaskSurface = BuildViewportSelectionMaskSurface(targets); - selectionMaskSurface.SetRenderArea(surface.GetRenderArea()); - - if (!m_selectionMaskPass.Render( - renderContext, - selectionMaskSurface, - sceneData, - selectedObjectIds)) { - return false; - } - - targets.selectionMaskState = RHI::ResourceStates::PixelShaderResource; - - return m_outlinePass.Render( + return m_impl->Render( renderContext, surface, - { - targets.selectionMaskShaderView, - targets.selectionMaskState, - targets.depthShaderView, - surface.GetDepthStateAfter() - }, - ToBuiltinSceneViewportSelectionOutlineStyle(style)); + sceneData, + targets, + selectedObjectIds, + style); } std::unique_ptr CreateSceneViewportSelectionOutlinePass( diff --git a/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.h b/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.h index 7cfe9ad9..9a7c9cd0 100644 --- a/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.h +++ b/editor/src/Viewport/Passes/SceneViewportSelectionOutlinePass.h @@ -2,8 +2,6 @@ #include "Viewport/SceneViewportPassSpecs.h" -#include -#include #include #include #include @@ -20,7 +18,11 @@ struct ViewportRenderTargets; class SceneViewportSelectionOutlinePassRenderer { public: SceneViewportSelectionOutlinePassRenderer(); - ~SceneViewportSelectionOutlinePassRenderer() = default; + ~SceneViewportSelectionOutlinePassRenderer(); + SceneViewportSelectionOutlinePassRenderer(const SceneViewportSelectionOutlinePassRenderer&) = delete; + SceneViewportSelectionOutlinePassRenderer& operator=(const SceneViewportSelectionOutlinePassRenderer&) = delete; + SceneViewportSelectionOutlinePassRenderer(SceneViewportSelectionOutlinePassRenderer&&) = delete; + SceneViewportSelectionOutlinePassRenderer& operator=(SceneViewportSelectionOutlinePassRenderer&&) = delete; void Shutdown(); @@ -33,8 +35,8 @@ public: const SceneViewportSelectionOutlineStyle& style); private: - Rendering::Passes::BuiltinSelectionMaskPass m_selectionMaskPass; - Rendering::Passes::BuiltinSelectionOutlinePass m_outlinePass; + class Impl; + std::unique_ptr m_impl; }; std::unique_ptr CreateSceneViewportSelectionOutlinePass( diff --git a/editor/src/Viewport/SceneViewportPassSpecs.h b/editor/src/Viewport/SceneViewportPassSpecs.h index 22a0e907..34212f33 100644 --- a/editor/src/Viewport/SceneViewportPassSpecs.h +++ b/editor/src/Viewport/SceneViewportPassSpecs.h @@ -2,8 +2,6 @@ #include #include -#include -#include namespace XCEngine { namespace Editor { @@ -20,20 +18,14 @@ struct SceneViewportGridPassData { float orbitDistance = 6.0f; }; -inline Rendering::Passes::InfiniteGridPassData ToBuiltinSceneViewportGridPassData( - const SceneViewportGridPassData& data) { - Rendering::Passes::InfiniteGridPassData builtinData = {}; - builtinData.valid = data.valid; - builtinData.cameraPosition = data.cameraPosition; - builtinData.cameraForward = data.cameraForward; - builtinData.cameraRight = data.cameraRight; - builtinData.cameraUp = data.cameraUp; - builtinData.verticalFovDegrees = data.verticalFovDegrees; - builtinData.nearClipPlane = data.nearClipPlane; - builtinData.farClipPlane = data.farClipPlane; - builtinData.orbitDistance = data.orbitDistance; - return builtinData; -} +struct InfiniteGridParameters { + bool valid = false; + float baseScale = 1.0f; + float transitionBlend = 0.0f; + float fadeDistance = 500.0f; +}; + +InfiniteGridParameters BuildInfiniteGridParameters(const SceneViewportGridPassData& data); struct SceneViewportSelectionOutlineStyle { Math::Color outlineColor = Math::Color(1.0f, 0.4f, 0.0f, 1.0f); @@ -41,14 +33,5 @@ struct SceneViewportSelectionOutlineStyle { bool debugSelectionMask = false; }; -inline Rendering::Passes::SelectionOutlineStyle ToBuiltinSceneViewportSelectionOutlineStyle( - const SceneViewportSelectionOutlineStyle& style) { - Rendering::Passes::SelectionOutlineStyle builtinStyle = {}; - builtinStyle.outlineColor = style.outlineColor; - builtinStyle.outlineWidthPixels = style.outlineWidthPixels; - builtinStyle.debugSelectionMask = style.debugSelectionMask; - return builtinStyle; -} - } // namespace Editor } // namespace XCEngine diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index ad7bf2f6..328db6b2 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -548,11 +548,8 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinDepthOnlyPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h - ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinSelectionMaskPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinColorScalePostProcessPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinFinalColorPass.h - ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h - ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinSelectionOutlinePass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Features/BuiltinGaussianSplatPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Features/BuiltinVolumetricPass.h ${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Pipelines/BuiltinForwardPipeline.h @@ -592,11 +589,8 @@ add_library(XCEngine STATIC ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinShadowCasterPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPassResources.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinSelectionMaskPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinColorScalePostProcessPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinFinalColorPass.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinInfiniteGridPass.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinSelectionOutlinePass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Features/BuiltinGaussianSplatPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Features/BuiltinVolumetricPass.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Features/Internal/BuiltinGaussianSplatPassResources.cpp diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h b/engine/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h deleted file mode 100644 index 97316ce6..00000000 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinInfiniteGridPass.h +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace XCEngine { -namespace Rendering { -namespace Passes { - -struct InfiniteGridPassData { - bool valid = false; - Math::Vector3 cameraPosition = Math::Vector3::Zero(); - Math::Vector3 cameraForward = Math::Vector3::Forward(); - Math::Vector3 cameraRight = Math::Vector3::Right(); - Math::Vector3 cameraUp = Math::Vector3::Up(); - float verticalFovDegrees = 60.0f; - float nearClipPlane = 0.03f; - float farClipPlane = 2000.0f; - float orbitDistance = 6.0f; -}; - -struct InfiniteGridParameters { - bool valid = false; - float baseScale = 1.0f; - float transitionBlend = 0.0f; - float fadeDistance = 500.0f; -}; - -InfiniteGridParameters BuildInfiniteGridParameters(const InfiniteGridPassData& data); - -class BuiltinInfiniteGridPass { -public: - explicit BuiltinInfiniteGridPass(Containers::String shaderPath = Containers::String()); - ~BuiltinInfiniteGridPass() = default; - - void SetShaderPath(const Containers::String& shaderPath); - const Containers::String& GetShaderPath() const; - - void Shutdown(); - - bool Render( - const RenderContext& renderContext, - const RenderSurface& surface, - const InfiniteGridPassData& data); - -private: - bool EnsureInitialized(const RenderContext& renderContext, const RenderSurface& surface); - bool CreateResources(const RenderContext& renderContext, const RenderSurface& surface); - void DestroyResources(); - - RHI::RHIDevice* m_device = nullptr; - RHI::RHIType m_backendType = RHI::RHIType::D3D12; - RHI::RHIPipelineLayout* m_pipelineLayout = nullptr; - RHI::RHIPipelineState* m_pipelineState = nullptr; - RHI::RHIDescriptorPool* m_constantPool = nullptr; - RHI::RHIDescriptorSet* m_constantSet = nullptr; - Containers::String m_shaderPath; - Resources::ResourceHandle m_builtinInfiniteGridShader; - RHI::Format m_renderTargetFormat = RHI::Format::Unknown; - RHI::Format m_depthStencilFormat = RHI::Format::Unknown; - uint32_t m_renderTargetSampleCount = 1u; -}; - -} // namespace Passes -} // namespace Rendering -} // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinSelectionMaskPass.h b/engine/include/XCEngine/Rendering/Passes/BuiltinSelectionMaskPass.h deleted file mode 100644 index 65e63cc8..00000000 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinSelectionMaskPass.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -namespace XCEngine { -namespace Rendering { - -struct RenderSceneData; -struct VisibleRenderItem; - -namespace Passes { - -class BuiltinSelectionMaskPass final : public BuiltinDepthStylePassBase { -public: - BuiltinSelectionMaskPass(); - ~BuiltinSelectionMaskPass() override = default; - - static RHI::InputLayoutDesc BuildInputLayout(); - - const char* GetName() const override; - - bool Render( - const RenderContext& context, - const RenderSurface& surface, - const RenderSceneData& sceneData, - const std::vector& selectedObjectIds); - -protected: - bool ShouldRenderVisibleItem(const VisibleRenderItem& visibleItem) const override; - -private: - std::vector m_selectedObjectIds = {}; -}; - -} // namespace Passes -} // namespace Rendering -} // namespace XCEngine diff --git a/engine/include/XCEngine/Rendering/Passes/BuiltinSelectionOutlinePass.h b/engine/include/XCEngine/Rendering/Passes/BuiltinSelectionOutlinePass.h deleted file mode 100644 index 3ab9da6a..00000000 --- a/engine/include/XCEngine/Rendering/Passes/BuiltinSelectionOutlinePass.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace XCEngine { -namespace Rendering { -namespace Passes { - -struct SelectionOutlineStyle { - Math::Color outlineColor = Math::Color(1.0f, 0.4f, 0.0f, 1.0f); - float outlineWidthPixels = 2.0f; - bool debugSelectionMask = false; -}; - -struct SelectionOutlinePassInputs { - RHI::RHIResourceView* selectionMaskTextureView = nullptr; - RHI::ResourceStates selectionMaskState = RHI::ResourceStates::PixelShaderResource; - RHI::RHIResourceView* depthTextureView = nullptr; - RHI::ResourceStates depthTextureState = RHI::ResourceStates::DepthWrite; -}; - -class BuiltinSelectionOutlinePass { -public: - explicit BuiltinSelectionOutlinePass(Containers::String shaderPath = Containers::String()); - ~BuiltinSelectionOutlinePass() = default; - BuiltinSelectionOutlinePass(const BuiltinSelectionOutlinePass&) = delete; - BuiltinSelectionOutlinePass& operator=(const BuiltinSelectionOutlinePass&) = delete; - BuiltinSelectionOutlinePass(BuiltinSelectionOutlinePass&&) = delete; - BuiltinSelectionOutlinePass& operator=(BuiltinSelectionOutlinePass&&) = delete; - - void SetShaderPath(const Containers::String& shaderPath); - const Containers::String& GetShaderPath() const; - - void Shutdown(); - - bool Render( - const RenderContext& renderContext, - const RenderSurface& surface, - const SelectionOutlinePassInputs& inputs, - const SelectionOutlineStyle& style = {}); - -private: - struct OutlineConstants { - Math::Vector4 viewportSizeAndTexelSize = Math::Vector4::Zero(); - Math::Vector4 outlineColor = Math::Vector4::Zero(); - Math::Vector4 outlineInfo = Math::Vector4::Zero(); - Math::Vector4 depthParams = Math::Vector4::Zero(); - }; - - bool EnsureInitialized(const RenderContext& renderContext, const RenderSurface& surface); - bool CreateResources(const RenderContext& renderContext, const RenderSurface& surface); - void DestroyResources(); - bool HasCreatedResources() const; - void ResetState(); - - RHI::RHIDevice* m_device = nullptr; - RHI::RHIType m_backendType = RHI::RHIType::D3D12; - RHI::RHIPipelineLayout* m_pipelineLayout = nullptr; - RHI::RHIPipelineState* m_pipelineState = nullptr; - RHI::RHIDescriptorPool* m_constantPool = nullptr; - RHI::RHIDescriptorSet* m_constantSet = nullptr; - RHI::RHIDescriptorPool* m_texturePool = nullptr; - RHI::RHIDescriptorSet* m_textureSet = nullptr; - Containers::String m_shaderPath; - std::optional> m_builtinSelectionOutlineShader; - RHI::Format m_renderTargetFormat = RHI::Format::Unknown; - uint32_t m_renderTargetSampleCount = 1u; -}; - -} // namespace Passes -} // namespace Rendering -} // namespace XCEngine diff --git a/engine/src/Rendering/Passes/BuiltinInfiniteGridPass.cpp b/engine/src/Rendering/Passes/BuiltinInfiniteGridPass.cpp deleted file mode 100644 index 6dbb21af..00000000 --- a/engine/src/Rendering/Passes/BuiltinInfiniteGridPass.cpp +++ /dev/null @@ -1,499 +0,0 @@ -#include "Rendering/Passes/BuiltinInfiniteGridPass.h" - -#include -#include -#include -#include -#include -#include - -#include "Rendering/Internal/RenderSurfacePipelineUtils.h" -#include "Rendering/Materials/RenderMaterialStateUtils.h" -#include "Rendering/Internal/ShaderVariantUtils.h" - -#include -#include -#include - -namespace XCEngine { -namespace Rendering { -namespace Passes { - -namespace { - -constexpr float kCameraHeightScaleFactor = 0.50f; -constexpr float kTransitionStart = 0.65f; -constexpr float kTransitionEnd = 0.95f; -constexpr float kMinimumVerticalViewComponent = 0.15f; - -struct GridConstants { - Math::Matrix4x4 viewProjection = Math::Matrix4x4::Identity(); - Math::Vector4 cameraPositionAndScale = Math::Vector4::Zero(); - Math::Vector4 cameraRightAndFade = Math::Vector4::Zero(); - Math::Vector4 cameraUpAndTanHalfFov = Math::Vector4::Zero(); - Math::Vector4 cameraForwardAndAspect = Math::Vector4::Zero(); - Math::Vector4 viewportNearFar = Math::Vector4::Zero(); - Math::Vector4 gridTransition = Math::Vector4::Zero(); -}; - -const Resources::ShaderPass* FindInfiniteGridCompatiblePass( - const Resources::Shader& shader, - Resources::ShaderBackend backend) { - const Resources::ShaderPass* gridPass = shader.FindPass("InfiniteGrid"); - if (gridPass != nullptr && - ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants(shader, gridPass->name, backend)) { - return gridPass; - } - - if (shader.GetPassCount() > 0 && - ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants(shader, shader.GetPasses()[0].name, backend)) { - return &shader.GetPasses()[0]; - } - - return nullptr; -} - -RHI::GraphicsPipelineDesc CreatePipelineDesc( - RHI::RHIType backendType, - RHI::RHIPipelineLayout* pipelineLayout, - const Resources::Shader& shader, - const Containers::String& passName, - const RenderSurface& surface) { - RHI::GraphicsPipelineDesc pipelineDesc = {}; - pipelineDesc.pipelineLayout = pipelineLayout; - pipelineDesc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); - ::XCEngine::Rendering::Internal::ApplySingleColorAttachmentPropertiesToGraphicsPipelineDesc( - surface, - pipelineDesc); - pipelineDesc.depthStencilFormat = - static_cast(::XCEngine::Rendering::Internal::ResolveSurfaceDepthFormat(surface)); - - const Resources::ShaderPass* shaderPass = shader.FindPass(passName); - if (shaderPass != nullptr && shaderPass->hasFixedFunctionState) { - ::XCEngine::Rendering::ApplyRenderState(shaderPass->fixedFunctionState, pipelineDesc); - } else { - Resources::MaterialRenderState fallbackState = {}; - fallbackState.cullMode = Resources::MaterialCullMode::None; - fallbackState.depthWriteEnable = false; - fallbackState.depthTestEnable = true; - fallbackState.depthFunc = Resources::MaterialComparisonFunc::LessEqual; - fallbackState.blendEnable = true; - fallbackState.srcBlend = Resources::MaterialBlendFactor::SrcAlpha; - fallbackState.dstBlend = Resources::MaterialBlendFactor::InvSrcAlpha; - fallbackState.srcBlendAlpha = Resources::MaterialBlendFactor::One; - fallbackState.dstBlendAlpha = Resources::MaterialBlendFactor::InvSrcAlpha; - fallbackState.blendOp = Resources::MaterialBlendOp::Add; - fallbackState.blendOpAlpha = Resources::MaterialBlendOp::Add; - fallbackState.colorWriteMask = static_cast(RHI::ColorWriteMask::All); - ::XCEngine::Rendering::ApplyRenderState(fallbackState, pipelineDesc); - } - - const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(backendType); - if (const Resources::ShaderStageVariant* vertexVariant = shader.FindVariant( - passName, - Resources::ShaderType::Vertex, - backend)) { - if (shaderPass != nullptr) { - ::XCEngine::Rendering::Internal::ApplyShaderStageVariant( - shader.GetPath(), - *shaderPass, - backend, - *vertexVariant, - pipelineDesc.vertexShader); - } - } - if (const Resources::ShaderStageVariant* fragmentVariant = shader.FindVariant( - passName, - Resources::ShaderType::Fragment, - backend)) { - if (shaderPass != nullptr) { - ::XCEngine::Rendering::Internal::ApplyShaderStageVariant( - shader.GetPath(), - *shaderPass, - backend, - *fragmentVariant, - pipelineDesc.fragmentShader); - } - } - - return pipelineDesc; -} - -float SnapGridSpacing(float targetSpacing) { - const float clampedTarget = (std::max)(targetSpacing, 0.02f); - const float exponent = std::floor(std::log10(clampedTarget)); - return std::pow(10.0f, exponent); -} - -float ComputeTransitionBlend(float targetSpacing, float baseScale) { - const float normalizedSpacing = (std::max)(targetSpacing / (std::max)(baseScale, 1e-4f), 1.0f); - const float transitionPosition = std::log10(normalizedSpacing); - const float t = - (transitionPosition - kTransitionStart) / - (kTransitionEnd - kTransitionStart); - const float saturated = (std::clamp)(t, 0.0f, 1.0f); - return saturated * saturated * (3.0f - 2.0f * saturated); -} - -float ComputeViewDistanceToGridPlane(const InfiniteGridPassData& data) { - const float cameraHeight = std::abs(data.cameraPosition.y); - const Math::Vector3 forward = data.cameraForward.Normalized(); - - const bool lookingTowardGrid = - (data.cameraPosition.y >= 0.0f && forward.y < 0.0f) || - (data.cameraPosition.y < 0.0f && forward.y > 0.0f); - if (!lookingTowardGrid) { - return cameraHeight; - } - - const float verticalViewComponent = (std::max)(std::abs(forward.y), kMinimumVerticalViewComponent); - return cameraHeight / verticalViewComponent; -} - -Math::Matrix4x4 BuildInfiniteGridViewMatrix(const InfiniteGridPassData& data) { - const Math::Vector3 right = data.cameraRight.Normalized(); - const Math::Vector3 up = data.cameraUp.Normalized(); - const Math::Vector3 forward = data.cameraForward.Normalized(); - - Math::Matrix4x4 view = Math::Matrix4x4::Identity(); - view.m[0][0] = right.x; - view.m[0][1] = right.y; - view.m[0][2] = right.z; - view.m[0][3] = -Math::Vector3::Dot(right, data.cameraPosition); - - view.m[1][0] = up.x; - view.m[1][1] = up.y; - view.m[1][2] = up.z; - view.m[1][3] = -Math::Vector3::Dot(up, data.cameraPosition); - - view.m[2][0] = forward.x; - view.m[2][1] = forward.y; - view.m[2][2] = forward.z; - view.m[2][3] = -Math::Vector3::Dot(forward, data.cameraPosition); - return view; -} - -Math::Matrix4x4 BuildInfiniteGridProjectionMatrix( - const InfiniteGridPassData& data, - float viewportWidth, - float viewportHeight) { - const float aspect = viewportHeight > 0.0f - ? viewportWidth / viewportHeight - : 1.0f; - return Math::Matrix4x4::Perspective( - data.verticalFovDegrees * Math::DEG_TO_RAD, - aspect, - data.nearClipPlane, - data.farClipPlane); -} - -} // namespace - -InfiniteGridParameters BuildInfiniteGridParameters(const InfiniteGridPassData& data) { - InfiniteGridParameters parameters = {}; - if (!data.valid) { - return parameters; - } - - const float cameraHeight = std::abs(data.cameraPosition.y); - const float viewDistance = ComputeViewDistanceToGridPlane(data); - const float targetSpacing = (std::max)(cameraHeight * kCameraHeightScaleFactor, 0.1f); - - parameters.valid = true; - parameters.baseScale = SnapGridSpacing(targetSpacing); - parameters.transitionBlend = ComputeTransitionBlend(targetSpacing, parameters.baseScale); - parameters.fadeDistance = (std::max)( - parameters.baseScale * 320.0f, - viewDistance * 80.0f); - return parameters; -} - -BuiltinInfiniteGridPass::BuiltinInfiniteGridPass(Containers::String shaderPath) - : m_shaderPath(std::move(shaderPath)) { -} - -void BuiltinInfiniteGridPass::SetShaderPath(const Containers::String& shaderPath) { - if (m_shaderPath == shaderPath) { - return; - } - - DestroyResources(); - m_shaderPath = shaderPath; -} - -const Containers::String& BuiltinInfiniteGridPass::GetShaderPath() const { - return m_shaderPath; -} - -void BuiltinInfiniteGridPass::Shutdown() { - DestroyResources(); -} - -bool BuiltinInfiniteGridPass::Render( - const RenderContext& renderContext, - const RenderSurface& surface, - const InfiniteGridPassData& data) { - if (!data.valid || !renderContext.IsValid()) { - return false; - } - - const std::vector& colorAttachments = surface.GetColorAttachments(); - if (!::XCEngine::Rendering::Internal::HasSingleColorAttachment(surface) || - colorAttachments.empty() || - colorAttachments[0] == nullptr || - surface.GetDepthAttachment() == nullptr) { - return false; - } - - const Math::RectInt renderArea = surface.GetRenderArea(); - if (renderArea.width <= 0 || renderArea.height <= 0) { - return false; - } - - if (!EnsureInitialized(renderContext, surface)) { - return false; - } - - const InfiniteGridParameters parameters = BuildInfiniteGridParameters(data); - if (!parameters.valid) { - return false; - } - - const Math::Matrix4x4 viewProjection = - BuildInfiniteGridProjectionMatrix( - data, - static_cast(renderArea.width), - static_cast(renderArea.height)) * - BuildInfiniteGridViewMatrix(data); - - const float aspect = renderArea.height > 0 - ? static_cast(renderArea.width) / static_cast(renderArea.height) - : 1.0f; - - GridConstants constants = {}; - constants.viewProjection = viewProjection.Transpose(); - constants.cameraPositionAndScale = Math::Vector4(data.cameraPosition, parameters.baseScale); - constants.cameraRightAndFade = Math::Vector4(data.cameraRight, parameters.fadeDistance); - constants.cameraUpAndTanHalfFov = Math::Vector4( - data.cameraUp, - std::tan(data.verticalFovDegrees * Math::DEG_TO_RAD * 0.5f)); - constants.cameraForwardAndAspect = Math::Vector4(data.cameraForward, aspect); - constants.viewportNearFar = Math::Vector4( - static_cast(surface.GetWidth()), - static_cast(surface.GetHeight()), - data.nearClipPlane, - data.farClipPlane); - constants.gridTransition = Math::Vector4(parameters.transitionBlend, 0.0f, 0.0f, 0.0f); - - m_constantSet->WriteConstant(0, &constants, sizeof(constants)); - - RHI::RHICommandList* commandList = renderContext.commandList; - RHI::RHIResourceView* renderTarget = colorAttachments[0]; - RHI::RHIResourceView* depthAttachment = surface.GetDepthAttachment(); - if (surface.IsAutoTransitionEnabled()) { - commandList->TransitionBarrier( - renderTarget, - surface.GetColorStateAfter(), - RHI::ResourceStates::RenderTarget); - commandList->TransitionBarrier( - depthAttachment, - surface.GetDepthStateAfter(), - RHI::ResourceStates::DepthWrite); - } - - commandList->SetRenderTargets(1, &renderTarget, depthAttachment); - - 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 - }; - - commandList->SetViewport(viewport); - commandList->SetScissorRect(scissorRect); - commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); - commandList->SetPipelineState(m_pipelineState); - - RHI::RHIDescriptorSet* descriptorSets[] = { m_constantSet }; - commandList->SetGraphicsDescriptorSets(0, 1, descriptorSets, m_pipelineLayout); - commandList->Draw(3, 1, 0, 0); - commandList->EndRenderPass(); - - if (surface.IsAutoTransitionEnabled()) { - commandList->TransitionBarrier( - renderTarget, - RHI::ResourceStates::RenderTarget, - surface.GetColorStateAfter()); - commandList->TransitionBarrier( - depthAttachment, - RHI::ResourceStates::DepthWrite, - surface.GetDepthStateAfter()); - } - - return true; -} - -bool BuiltinInfiniteGridPass::EnsureInitialized(const RenderContext& renderContext, const RenderSurface& surface) { - const RHI::Format renderTargetFormat = - ::XCEngine::Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u); - const RHI::Format depthFormat = - ::XCEngine::Rendering::Internal::ResolveSurfaceDepthFormat(surface); - const uint32_t renderTargetSampleCount = - ::XCEngine::Rendering::Internal::ResolveSurfaceSampleCount(surface); - if (m_pipelineState != nullptr && - m_pipelineLayout != nullptr && - m_constantPool != nullptr && - m_constantSet != nullptr && - m_device == renderContext.device && - m_backendType == renderContext.backendType && - m_renderTargetFormat == renderTargetFormat && - m_depthStencilFormat == depthFormat && - m_renderTargetSampleCount == renderTargetSampleCount) { - return true; - } - - DestroyResources(); - return CreateResources(renderContext, surface); -} - -bool BuiltinInfiniteGridPass::CreateResources(const RenderContext& renderContext, const RenderSurface& surface) { - if (!renderContext.IsValid()) { - return false; - } - - if (!::XCEngine::Rendering::Internal::HasSingleColorAttachment(surface) || - ::XCEngine::Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u) == RHI::Format::Unknown || - ::XCEngine::Rendering::Internal::ResolveSurfaceDepthFormat(surface) == RHI::Format::Unknown) { - return false; - } - - if (m_shaderPath.Empty()) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinInfiniteGridPass requires an injected shader path before resource creation"); - return false; - } - - m_device = renderContext.device; - m_backendType = renderContext.backendType; - m_builtinInfiniteGridShader = Resources::ResourceManager::Get().Load( - m_shaderPath); - if (!m_builtinInfiniteGridShader.IsValid()) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinInfiniteGridPass failed to load configured infinite-grid shader resource"); - DestroyResources(); - return false; - } - - const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(m_backendType); - const Resources::ShaderPass* infiniteGridPass = - FindInfiniteGridCompatiblePass(*m_builtinInfiniteGridShader.Get(), backend); - if (infiniteGridPass == nullptr) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinInfiniteGridPass could not resolve a valid InfiniteGrid shader pass"); - DestroyResources(); - return false; - } - - RHI::DescriptorSetLayoutBinding constantBinding = {}; - constantBinding.binding = 0; - constantBinding.type = static_cast(RHI::DescriptorType::CBV); - constantBinding.count = 1; - - RHI::DescriptorSetLayoutDesc constantLayout = {}; - constantLayout.bindings = &constantBinding; - constantLayout.bindingCount = 1; - - RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {}; - pipelineLayoutDesc.setLayouts = &constantLayout; - pipelineLayoutDesc.setLayoutCount = 1; - m_pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); - if (m_pipelineLayout == nullptr) { - DestroyResources(); - return false; - } - - RHI::DescriptorPoolDesc constantPoolDesc = {}; - constantPoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV; - constantPoolDesc.descriptorCount = 1; - constantPoolDesc.shaderVisible = false; - m_constantPool = m_device->CreateDescriptorPool(constantPoolDesc); - if (m_constantPool == nullptr) { - DestroyResources(); - return false; - } - - m_constantSet = m_constantPool->AllocateSet(constantLayout); - if (m_constantSet == nullptr) { - DestroyResources(); - return false; - } - - RHI::GraphicsPipelineDesc pipelineDesc = {}; - pipelineDesc = CreatePipelineDesc( - m_backendType, - m_pipelineLayout, - *m_builtinInfiniteGridShader.Get(), - infiniteGridPass->name, - surface); - - m_pipelineState = m_device->CreatePipelineState(pipelineDesc); - if (m_pipelineState == nullptr || !m_pipelineState->IsValid()) { - DestroyResources(); - return false; - } - - m_renderTargetFormat = ::XCEngine::Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u); - m_depthStencilFormat = ::XCEngine::Rendering::Internal::ResolveSurfaceDepthFormat(surface); - m_renderTargetSampleCount = ::XCEngine::Rendering::Internal::ResolveSurfaceSampleCount(surface); - - return true; -} - -void BuiltinInfiniteGridPass::DestroyResources() { - if (m_pipelineState != nullptr) { - m_pipelineState->Shutdown(); - delete m_pipelineState; - m_pipelineState = nullptr; - } - - if (m_constantSet != nullptr) { - m_constantSet->Shutdown(); - delete m_constantSet; - m_constantSet = nullptr; - } - - if (m_constantPool != nullptr) { - m_constantPool->Shutdown(); - delete m_constantPool; - m_constantPool = nullptr; - } - - if (m_pipelineLayout != nullptr) { - m_pipelineLayout->Shutdown(); - delete m_pipelineLayout; - m_pipelineLayout = nullptr; - } - - m_device = nullptr; - m_backendType = RHI::RHIType::D3D12; - m_builtinInfiniteGridShader.Reset(); - m_renderTargetFormat = RHI::Format::Unknown; - m_depthStencilFormat = RHI::Format::Unknown; - m_renderTargetSampleCount = 1u; -} - -} // namespace Passes -} // namespace Rendering -} // namespace XCEngine diff --git a/engine/src/Rendering/Passes/BuiltinSelectionMaskPass.cpp b/engine/src/Rendering/Passes/BuiltinSelectionMaskPass.cpp deleted file mode 100644 index dbd5465c..00000000 --- a/engine/src/Rendering/Passes/BuiltinSelectionMaskPass.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "Rendering/Passes/BuiltinSelectionMaskPass.h" - -#include "Components/GameObject.h" -#include "Resources/BuiltinResources.h" - -#include -#include - -#include - -namespace XCEngine { -namespace Rendering { -namespace Passes { - -BuiltinSelectionMaskPass::BuiltinSelectionMaskPass() - : BuiltinDepthStylePassBase( - BuiltinMaterialPass::SelectionMask, - Resources::GetBuiltinSelectionMaskShaderPath()) { -} - -RHI::InputLayoutDesc BuiltinSelectionMaskPass::BuildInputLayout() { - return BuildCommonInputLayout(); -} - -const char* BuiltinSelectionMaskPass::GetName() const { - return "BuiltinSelectionMaskPass"; -} - -bool BuiltinSelectionMaskPass::Render( - const RenderContext& context, - const RenderSurface& surface, - const RenderSceneData& sceneData, - const std::vector& selectedObjectIds) { - m_selectedObjectIds.clear(); - m_selectedObjectIds.reserve(selectedObjectIds.size()); - for (uint64_t selectedObjectId : selectedObjectIds) { - RenderObjectId renderObjectId = kInvalidRenderObjectId; - if (TryConvertRuntimeObjectIdToRenderObjectId(selectedObjectId, renderObjectId)) { - m_selectedObjectIds.push_back(renderObjectId); - } - } - - if (m_selectedObjectIds.empty()) { - return false; - } - - RenderSceneData selectionMaskSceneData = sceneData; - selectionMaskSceneData.cameraData.clearFlags = RenderClearFlags::Color; - selectionMaskSceneData.cameraData.clearColor = Math::Color::Black(); - - const RenderPassContext passContext = { - context, - surface, - selectionMaskSceneData, - nullptr, - nullptr, - RHI::ResourceStates::Common - }; - return Execute(passContext); -} - -bool BuiltinSelectionMaskPass::ShouldRenderVisibleItem(const VisibleRenderItem& visibleItem) const { - if (!IsValidRenderObjectId(visibleItem.renderObjectId)) { - return false; - } - - return std::find(m_selectedObjectIds.begin(), m_selectedObjectIds.end(), visibleItem.renderObjectId) != - m_selectedObjectIds.end(); -} - -} // namespace Passes -} // namespace Rendering -} // namespace XCEngine diff --git a/engine/src/Rendering/Passes/BuiltinSelectionOutlinePass.cpp b/engine/src/Rendering/Passes/BuiltinSelectionOutlinePass.cpp deleted file mode 100644 index f7dc8820..00000000 --- a/engine/src/Rendering/Passes/BuiltinSelectionOutlinePass.cpp +++ /dev/null @@ -1,465 +0,0 @@ -#include "Rendering/Passes/BuiltinSelectionOutlinePass.h" - -#include "Core/Asset/ResourceManager.h" -#include "Debug/Logger.h" -#include "Rendering/Internal/RenderSurfacePipelineUtils.h" -#include "Rendering/Internal/ShaderVariantUtils.h" -#include "Rendering/Materials/RenderMaterialStateUtils.h" -#include "RHI/RHICommandList.h" -#include "RHI/RHIDevice.h" - -#include - -#include - -namespace XCEngine { -namespace Rendering { -namespace Passes { - -namespace { - -const Resources::ShaderPass* FindSelectionOutlineCompatiblePass( - const Resources::Shader& shader, - Resources::ShaderBackend backend) { - const Resources::ShaderPass* outlinePass = shader.FindPass("SelectionOutline"); - if (outlinePass != nullptr && - ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants(shader, outlinePass->name, backend)) { - return outlinePass; - } - - const Resources::ShaderPass* editorOutlinePass = shader.FindPass("EditorSelectionOutline"); - if (editorOutlinePass != nullptr && - ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants( - shader, - editorOutlinePass->name, - backend)) { - return editorOutlinePass; - } - - if (shader.GetPassCount() > 0 && - ::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants( - shader, - shader.GetPasses()[0].name, - backend)) { - return &shader.GetPasses()[0]; - } - - return nullptr; -} - -RHI::GraphicsPipelineDesc CreatePipelineDesc( - RHI::RHIType backendType, - RHI::RHIPipelineLayout* pipelineLayout, - const Resources::Shader& shader, - const Containers::String& passName, - const RenderSurface& surface) { - RHI::GraphicsPipelineDesc pipelineDesc = {}; - pipelineDesc.pipelineLayout = pipelineLayout; - pipelineDesc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); - ::XCEngine::Rendering::Internal::ApplySingleColorAttachmentPropertiesToGraphicsPipelineDesc( - surface, - pipelineDesc); - pipelineDesc.depthStencilFormat = static_cast(RHI::Format::Unknown); - - const Resources::ShaderPass* shaderPass = shader.FindPass(passName); - if (shaderPass != nullptr && shaderPass->hasFixedFunctionState) { - ::XCEngine::Rendering::ApplyRenderState(shaderPass->fixedFunctionState, pipelineDesc); - } else { - Resources::MaterialRenderState fallbackState = {}; - fallbackState.cullMode = Resources::MaterialCullMode::None; - fallbackState.depthWriteEnable = false; - fallbackState.depthTestEnable = false; - fallbackState.depthFunc = Resources::MaterialComparisonFunc::Always; - fallbackState.blendEnable = true; - fallbackState.srcBlend = Resources::MaterialBlendFactor::SrcAlpha; - fallbackState.dstBlend = Resources::MaterialBlendFactor::InvSrcAlpha; - fallbackState.srcBlendAlpha = Resources::MaterialBlendFactor::One; - fallbackState.dstBlendAlpha = Resources::MaterialBlendFactor::InvSrcAlpha; - fallbackState.blendOp = Resources::MaterialBlendOp::Add; - fallbackState.blendOpAlpha = Resources::MaterialBlendOp::Add; - fallbackState.colorWriteMask = static_cast(RHI::ColorWriteMask::All); - ::XCEngine::Rendering::ApplyRenderState(fallbackState, pipelineDesc); - } - - const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(backendType); - if (const Resources::ShaderStageVariant* vertexVariant = - shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) { - if (shaderPass != nullptr) { - ::XCEngine::Rendering::Internal::ApplyShaderStageVariant( - shader.GetPath(), - *shaderPass, - backend, - *vertexVariant, - pipelineDesc.vertexShader); - } - } - if (const Resources::ShaderStageVariant* fragmentVariant = - shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) { - if (shaderPass != nullptr) { - ::XCEngine::Rendering::Internal::ApplyShaderStageVariant( - shader.GetPath(), - *shaderPass, - backend, - *fragmentVariant, - pipelineDesc.fragmentShader); - } - } - - return pipelineDesc; -} - -} // namespace - -BuiltinSelectionOutlinePass::BuiltinSelectionOutlinePass(Containers::String shaderPath) - : m_shaderPath(shaderPath.Empty() ? Resources::GetBuiltinSelectionOutlineShaderPath() : std::move(shaderPath)) { - ResetState(); -} - -void BuiltinSelectionOutlinePass::SetShaderPath(const Containers::String& shaderPath) { - if (m_shaderPath == shaderPath) { - return; - } - - DestroyResources(); - m_shaderPath = shaderPath; -} - -const Containers::String& BuiltinSelectionOutlinePass::GetShaderPath() const { - return m_shaderPath; -} - -void BuiltinSelectionOutlinePass::Shutdown() { - DestroyResources(); -} - -bool BuiltinSelectionOutlinePass::Render( - const RenderContext& renderContext, - const RenderSurface& surface, - const SelectionOutlinePassInputs& inputs, - const SelectionOutlineStyle& style) { - if (!renderContext.IsValid() || - inputs.selectionMaskTextureView == nullptr || - inputs.depthTextureView == nullptr) { - return false; - } - - const std::vector& colorAttachments = surface.GetColorAttachments(); - if (!::XCEngine::Rendering::Internal::HasSingleColorAttachment(surface) || - colorAttachments.empty() || - colorAttachments[0] == nullptr) { - return false; - } - - const Math::RectInt renderArea = surface.GetRenderArea(); - if (renderArea.width <= 0 || renderArea.height <= 0) { - return false; - } - - if (!EnsureInitialized(renderContext, surface)) { - return false; - } - - OutlineConstants constants = {}; - constants.viewportSizeAndTexelSize = Math::Vector4( - static_cast(surface.GetWidth()), - static_cast(surface.GetHeight()), - surface.GetWidth() > 0 ? 1.0f / static_cast(surface.GetWidth()) : 0.0f, - surface.GetHeight() > 0 ? 1.0f / static_cast(surface.GetHeight()) : 0.0f); - constants.outlineColor = Math::Vector4( - style.outlineColor.r, - style.outlineColor.g, - style.outlineColor.b, - style.outlineColor.a); - constants.outlineInfo = Math::Vector4( - style.debugSelectionMask ? 1.0f : 0.0f, - style.outlineWidthPixels, - 0.0f, - 0.0f); - constants.depthParams = Math::Vector4(1.0e-5f, 0.0f, 0.0f, 0.0f); - - m_constantSet->WriteConstant(0, &constants, sizeof(constants)); - m_textureSet->Update(0, inputs.selectionMaskTextureView); - m_textureSet->Update(1, inputs.depthTextureView); - - RHI::RHICommandList* commandList = renderContext.commandList; - RHI::RHIResourceView* renderTarget = colorAttachments[0]; - if (surface.IsAutoTransitionEnabled()) { - commandList->TransitionBarrier( - renderTarget, - surface.GetColorStateAfter(), - RHI::ResourceStates::RenderTarget); - commandList->TransitionBarrier( - inputs.selectionMaskTextureView, - inputs.selectionMaskState, - RHI::ResourceStates::PixelShaderResource); - commandList->TransitionBarrier( - inputs.depthTextureView, - inputs.depthTextureState, - RHI::ResourceStates::PixelShaderResource); - } - commandList->SetRenderTargets(1, &renderTarget, nullptr); - - 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 - }; - - commandList->SetViewport(viewport); - commandList->SetScissorRect(scissorRect); - commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); - commandList->SetPipelineState(m_pipelineState); - - RHI::RHIDescriptorSet* descriptorSets[] = { m_constantSet, m_textureSet }; - commandList->SetGraphicsDescriptorSets(0, 2, descriptorSets, m_pipelineLayout); - commandList->Draw(3, 1, 0, 0); - commandList->EndRenderPass(); - - if (surface.IsAutoTransitionEnabled()) { - commandList->TransitionBarrier( - renderTarget, - RHI::ResourceStates::RenderTarget, - surface.GetColorStateAfter()); - commandList->TransitionBarrier( - inputs.selectionMaskTextureView, - RHI::ResourceStates::PixelShaderResource, - inputs.selectionMaskState); - commandList->TransitionBarrier( - inputs.depthTextureView, - RHI::ResourceStates::PixelShaderResource, - inputs.depthTextureState); - } - return true; -} - -bool BuiltinSelectionOutlinePass::EnsureInitialized(const RenderContext& renderContext, const RenderSurface& surface) { - const RHI::Format renderTargetFormat = - ::XCEngine::Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u); - const uint32_t renderTargetSampleCount = - ::XCEngine::Rendering::Internal::ResolveSurfaceSampleCount(surface); - if (m_pipelineLayout != nullptr && - m_pipelineState != nullptr && - m_constantPool != nullptr && - m_constantSet != nullptr && - m_texturePool != nullptr && - m_textureSet != nullptr && - m_device == renderContext.device && - m_backendType == renderContext.backendType && - m_renderTargetFormat == renderTargetFormat && - m_renderTargetSampleCount == renderTargetSampleCount) { - return true; - } - - if (HasCreatedResources()) { - DestroyResources(); - } - return CreateResources(renderContext, surface); -} - -bool BuiltinSelectionOutlinePass::CreateResources(const RenderContext& renderContext, const RenderSurface& surface) { - if (!renderContext.IsValid()) { - return false; - } - - if (!::XCEngine::Rendering::Internal::HasSingleColorAttachment(surface) || - ::XCEngine::Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u) == RHI::Format::Unknown) { - return false; - } - - if (m_shaderPath.Empty()) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinSelectionOutlinePass requires an injected shader path before resource creation"); - return false; - } - - Resources::ResourceHandle shader = Resources::ResourceManager::Get().Load( - m_shaderPath); - if (!shader.IsValid()) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinSelectionOutlinePass failed to load configured selection-outline shader resource"); - ResetState(); - return false; - } - - m_device = renderContext.device; - m_backendType = renderContext.backendType; - m_builtinSelectionOutlineShader.emplace(std::move(shader)); - - const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(m_backendType); - const Resources::ShaderPass* outlinePass = - FindSelectionOutlineCompatiblePass(*m_builtinSelectionOutlineShader->Get(), backend); - if (outlinePass == nullptr) { - Debug::Logger::Get().Error( - Debug::LogCategory::Rendering, - "BuiltinSelectionOutlinePass could not resolve a valid SelectionOutline shader pass"); - DestroyResources(); - return false; - } - - RHI::DescriptorSetLayoutBinding constantBinding = {}; - constantBinding.binding = 0; - constantBinding.type = static_cast(RHI::DescriptorType::CBV); - constantBinding.count = 1; - - RHI::DescriptorSetLayoutBinding textureBindings[2] = {}; - textureBindings[0].binding = 0; - textureBindings[0].type = static_cast(RHI::DescriptorType::SRV); - textureBindings[0].count = 1; - textureBindings[1].binding = 1; - textureBindings[1].type = static_cast(RHI::DescriptorType::SRV); - textureBindings[1].count = 1; - - RHI::DescriptorSetLayoutDesc constantLayout = {}; - constantLayout.bindings = &constantBinding; - constantLayout.bindingCount = 1; - - RHI::DescriptorSetLayoutDesc textureLayout = {}; - textureLayout.bindings = textureBindings; - textureLayout.bindingCount = 2; - - RHI::DescriptorSetLayoutDesc setLayouts[2] = {}; - setLayouts[0] = constantLayout; - setLayouts[1] = textureLayout; - - RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {}; - pipelineLayoutDesc.setLayouts = setLayouts; - pipelineLayoutDesc.setLayoutCount = 2; - m_pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); - if (m_pipelineLayout == nullptr) { - DestroyResources(); - return false; - } - - RHI::DescriptorPoolDesc constantPoolDesc = {}; - constantPoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV; - constantPoolDesc.descriptorCount = 1; - constantPoolDesc.shaderVisible = false; - m_constantPool = m_device->CreateDescriptorPool(constantPoolDesc); - if (m_constantPool == nullptr) { - DestroyResources(); - return false; - } - - m_constantSet = m_constantPool->AllocateSet(constantLayout); - if (m_constantSet == nullptr) { - DestroyResources(); - return false; - } - - RHI::DescriptorPoolDesc texturePoolDesc = {}; - texturePoolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV; - texturePoolDesc.descriptorCount = 2; - texturePoolDesc.shaderVisible = true; - m_texturePool = m_device->CreateDescriptorPool(texturePoolDesc); - if (m_texturePool == nullptr) { - DestroyResources(); - return false; - } - - m_textureSet = m_texturePool->AllocateSet(textureLayout); - if (m_textureSet == nullptr) { - DestroyResources(); - return false; - } - - m_pipelineState = m_device->CreatePipelineState( - CreatePipelineDesc( - m_backendType, - m_pipelineLayout, - *m_builtinSelectionOutlineShader->Get(), - outlinePass->name, - surface)); - if (m_pipelineState == nullptr || !m_pipelineState->IsValid()) { - DestroyResources(); - return false; - } - - m_renderTargetFormat = ::XCEngine::Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u); - m_renderTargetSampleCount = ::XCEngine::Rendering::Internal::ResolveSurfaceSampleCount(surface); - - return true; -} - -bool BuiltinSelectionOutlinePass::HasCreatedResources() const { - return m_device != nullptr || - m_pipelineLayout != nullptr || - m_pipelineState != nullptr || - m_constantPool != nullptr || - m_constantSet != nullptr || - m_texturePool != nullptr || - m_textureSet != nullptr || - m_builtinSelectionOutlineShader.has_value(); -} - -void BuiltinSelectionOutlinePass::DestroyResources() { - if (m_pipelineState != nullptr) { - m_pipelineState->Shutdown(); - delete m_pipelineState; - m_pipelineState = nullptr; - } - - if (m_textureSet != nullptr) { - m_textureSet->Shutdown(); - delete m_textureSet; - m_textureSet = nullptr; - } - - if (m_texturePool != nullptr) { - m_texturePool->Shutdown(); - delete m_texturePool; - m_texturePool = nullptr; - } - - if (m_constantSet != nullptr) { - m_constantSet->Shutdown(); - delete m_constantSet; - m_constantSet = nullptr; - } - - if (m_constantPool != nullptr) { - m_constantPool->Shutdown(); - delete m_constantPool; - m_constantPool = nullptr; - } - - if (m_pipelineLayout != nullptr) { - m_pipelineLayout->Shutdown(); - delete m_pipelineLayout; - m_pipelineLayout = nullptr; - } - - if (m_builtinSelectionOutlineShader.has_value()) { - m_builtinSelectionOutlineShader.reset(); - } - - ResetState(); -} - -void BuiltinSelectionOutlinePass::ResetState() { - m_device = nullptr; - m_backendType = RHI::RHIType::D3D12; - m_pipelineLayout = nullptr; - m_pipelineState = nullptr; - m_constantPool = nullptr; - m_constantSet = nullptr; - m_texturePool = nullptr; - m_textureSet = nullptr; - m_builtinSelectionOutlineShader.reset(); - m_renderTargetFormat = RHI::Format::Unknown; - m_renderTargetSampleCount = 1u; -} - -} // namespace Passes -} // namespace Rendering -} // namespace XCEngine