diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 75cdf591..2a443691 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -79,7 +79,6 @@ add_executable(${PROJECT_NAME} WIN32 src/Viewport/SceneViewportScaleGizmo.cpp src/Viewport/SceneViewportGrid.cpp src/Viewport/SceneViewportInfiniteGridPass.cpp - src/Viewport/SceneViewportSelectionMaskPass.cpp src/Viewport/SceneViewportSelectionOutlinePass.cpp src/Viewport/SceneViewportOrientationGizmo.cpp src/Viewport/SceneViewportOverlayRenderer.cpp diff --git a/editor/src/Viewport/SceneViewportPostPassPlan.h b/editor/src/Viewport/SceneViewportPostPassPlan.h index 2fc53f40..7fb0718c 100644 --- a/editor/src/Viewport/SceneViewportPostPassPlan.h +++ b/editor/src/Viewport/SceneViewportPostPassPlan.h @@ -7,7 +7,6 @@ namespace XCEngine { namespace Editor { enum class SceneViewportPostPassStep : uint8_t { - SelectionMask, ColorToRenderTarget, InfiniteGrid, SelectionOutline, @@ -19,14 +18,11 @@ struct SceneViewportPostPassPlanInput { bool overlayValid = false; bool hasSelection = false; bool debugSelectionMask = false; - bool hasSelectionMaskRenderTarget = false; - bool hasSelectionMaskShaderView = false; + bool hasObjectIdShaderView = false; }; struct SceneViewportPostPassPlan { bool valid = false; - bool usesSelectionMaskSurface = false; - bool usesSelectionMaskShaderView = false; std::vector steps; }; @@ -39,34 +35,26 @@ inline SceneViewportPostPassPlan BuildSceneViewportPostPassPlan( plan.valid = true; - if (input.hasSelection && input.debugSelectionMask) { - plan.steps = { - SceneViewportPostPassStep::ColorToRenderTarget, - SceneViewportPostPassStep::SelectionMaskDebug, - SceneViewportPostPassStep::ColorToShaderResource - }; + if (input.debugSelectionMask) { + plan.steps.push_back(SceneViewportPostPassStep::ColorToRenderTarget); + if (input.hasSelection && input.hasObjectIdShaderView) { + plan.steps.push_back(SceneViewportPostPassStep::SelectionMaskDebug); + } + plan.steps.push_back(SceneViewportPostPassStep::ColorToShaderResource); return plan; } - const bool canRenderSelectionMask = - input.hasSelection && - input.hasSelectionMaskRenderTarget && - input.hasSelectionMaskShaderView; - const bool canRenderSelectionOutline = - canRenderSelectionMask && - input.hasSelectionMaskShaderView; - - if (canRenderSelectionMask) { - plan.usesSelectionMaskSurface = true; - plan.usesSelectionMaskShaderView = true; - plan.steps.push_back(SceneViewportPostPassStep::SelectionMask); - } - plan.steps.push_back(SceneViewportPostPassStep::ColorToRenderTarget); plan.steps.push_back(SceneViewportPostPassStep::InfiniteGrid); - if (canRenderSelectionOutline) { - plan.steps.push_back(SceneViewportPostPassStep::SelectionOutline); + if (input.hasSelection && input.hasObjectIdShaderView) { + plan.steps = { + SceneViewportPostPassStep::ColorToRenderTarget, + SceneViewportPostPassStep::InfiniteGrid, + SceneViewportPostPassStep::SelectionOutline, + SceneViewportPostPassStep::ColorToShaderResource + }; + return plan; } plan.steps.push_back(SceneViewportPostPassStep::ColorToShaderResource); diff --git a/editor/src/Viewport/SceneViewportSelectionMaskPass.cpp b/editor/src/Viewport/SceneViewportSelectionMaskPass.cpp deleted file mode 100644 index 394acf2d..00000000 --- a/editor/src/Viewport/SceneViewportSelectionMaskPass.cpp +++ /dev/null @@ -1,341 +0,0 @@ -#include "SceneViewportSelectionMaskPass.h" - -#include -#include -#include -#include - -#include -#include - -namespace XCEngine { -namespace Editor { - -namespace { - -const char kSelectionMaskHlsl[] = R"( -cbuffer PerObjectConstants : register(b0) { - float4x4 gProjectionMatrix; - float4x4 gViewMatrix; - float4x4 gModelMatrix; -}; - -struct VSInput { - float3 position : POSITION; -}; - -struct PSInput { - float4 position : SV_POSITION; -}; - -PSInput MainVS(VSInput input) { - PSInput output; - float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0)); - float4 positionVS = mul(gViewMatrix, positionWS); - output.position = mul(gProjectionMatrix, positionVS); - return output; -} - -float4 MainPS(PSInput input) : SV_TARGET { - return float4(1.0, 1.0, 1.0, 1.0); -} -)"; - -RHI::InputLayoutDesc 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 = static_cast(offsetof(Resources::StaticMeshVertex, position)); - inputLayout.elements.push_back(position); - return inputLayout; -} - -RHI::GraphicsPipelineDesc CreatePipelineDesc(RHI::RHIPipelineLayout* pipelineLayout) { - 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.inputLayout = BuildInputLayout(); - - 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); - - pipelineDesc.vertexShader.source.assign( - kSelectionMaskHlsl, - kSelectionMaskHlsl + std::strlen(kSelectionMaskHlsl)); - pipelineDesc.vertexShader.sourceLanguage = RHI::ShaderLanguage::HLSL; - pipelineDesc.vertexShader.entryPoint = L"MainVS"; - pipelineDesc.vertexShader.profile = L"vs_5_0"; - - pipelineDesc.fragmentShader.source.assign( - kSelectionMaskHlsl, - kSelectionMaskHlsl + std::strlen(kSelectionMaskHlsl)); - pipelineDesc.fragmentShader.sourceLanguage = RHI::ShaderLanguage::HLSL; - pipelineDesc.fragmentShader.entryPoint = L"MainPS"; - pipelineDesc.fragmentShader.profile = L"ps_5_0"; - return pipelineDesc; -} - -} // namespace - -void SceneViewportSelectionMaskPass::Shutdown() { - DestroyResources(); -} - -bool SceneViewportSelectionMaskPass::Render( - const Rendering::RenderContext& renderContext, - const Rendering::RenderSurface& surface, - const Rendering::RenderCameraData& cameraData, - const std::vector& renderables, - bool debugColor) { - (void)debugColor; - if (!renderContext.IsValid() || - renderContext.backendType != RHI::RHIType::D3D12 || - renderables.empty()) { - return false; - } - - if (!EnsureInitialized(renderContext)) { - return false; - } - - const std::vector& colorAttachments = surface.GetColorAttachments(); - if (colorAttachments.empty() || colorAttachments[0] == nullptr || surface.GetDepthAttachment() == nullptr) { - return false; - } - - RHI::RHICommandList* commandList = renderContext.commandList; - RHI::RHIResourceView* renderTarget = colorAttachments[0]; - commandList->SetRenderTargets(1, &renderTarget, surface.GetDepthAttachment()); - - const RHI::Viewport viewport = { - 0.0f, - 0.0f, - static_cast(surface.GetWidth()), - static_cast(surface.GetHeight()), - 0.0f, - 1.0f - }; - const RHI::Rect scissorRect = { - 0, - 0, - static_cast(surface.GetWidth()), - static_cast(surface.GetHeight()) - }; - - commandList->SetViewport(viewport); - commandList->SetScissorRect(scissorRect); - commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); - commandList->SetPipelineState(m_pipelineState); - - bool drewAnything = false; - for (const Rendering::VisibleRenderItem& renderable : renderables) { - drewAnything = DrawRenderable(renderContext, cameraData, renderable) || drewAnything; - } - - return drewAnything; -} - -bool SceneViewportSelectionMaskPass::EnsureInitialized(const Rendering::RenderContext& renderContext) { - if (m_pipelineLayout != nullptr && - m_pipelineState != nullptr && - m_device == renderContext.device && - m_backendType == renderContext.backendType) { - return true; - } - - DestroyResources(); - return CreateResources(renderContext); -} - -bool SceneViewportSelectionMaskPass::CreateResources(const Rendering::RenderContext& renderContext) { - if (!renderContext.IsValid() || renderContext.backendType != RHI::RHIType::D3D12) { - return false; - } - - m_device = renderContext.device; - m_backendType = renderContext.backendType; - - 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; - } - - m_pipelineState = m_device->CreatePipelineState(CreatePipelineDesc(m_pipelineLayout)); - if (m_pipelineState == nullptr || !m_pipelineState->IsValid()) { - if (m_pipelineState != nullptr) { - m_pipelineState->Shutdown(); - delete m_pipelineState; - m_pipelineState = nullptr; - } - DestroyResources(); - return false; - } - - return true; -} - -void SceneViewportSelectionMaskPass::DestroyResources() { - m_resourceCache.Shutdown(); - - for (auto& descriptorSetEntry : m_perObjectSets) { - DestroyOwnedDescriptorSet(descriptorSetEntry.second); - } - m_perObjectSets.clear(); - - if (m_pipelineState != nullptr) { - m_pipelineState->Shutdown(); - delete m_pipelineState; - m_pipelineState = nullptr; - } - - if (m_pipelineLayout != nullptr) { - m_pipelineLayout->Shutdown(); - delete m_pipelineLayout; - m_pipelineLayout = nullptr; - } - - m_device = nullptr; - m_backendType = RHI::RHIType::D3D12; -} - -RHI::RHIDescriptorSet* SceneViewportSelectionMaskPass::GetOrCreatePerObjectSet(uint64_t objectId) { - const auto existing = m_perObjectSets.find(objectId); - if (existing != m_perObjectSets.end()) { - return existing->second.set; - } - - RHI::DescriptorPoolDesc poolDesc = {}; - poolDesc.type = RHI::DescriptorHeapType::CBV_SRV_UAV; - poolDesc.descriptorCount = 1; - poolDesc.shaderVisible = false; - - OwnedDescriptorSet descriptorSet = {}; - descriptorSet.pool = m_device->CreateDescriptorPool(poolDesc); - if (descriptorSet.pool == nullptr) { - return nullptr; - } - - RHI::DescriptorSetLayoutBinding binding = {}; - binding.binding = 0; - binding.type = static_cast(RHI::DescriptorType::CBV); - binding.count = 1; - - RHI::DescriptorSetLayoutDesc layout = {}; - layout.bindings = &binding; - layout.bindingCount = 1; - descriptorSet.set = descriptorSet.pool->AllocateSet(layout); - if (descriptorSet.set == nullptr) { - DestroyOwnedDescriptorSet(descriptorSet); - return nullptr; - } - - const auto result = m_perObjectSets.emplace(objectId, descriptorSet); - return result.first->second.set; -} - -void SceneViewportSelectionMaskPass::DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet) { - if (descriptorSet.set != nullptr) { - descriptorSet.set->Shutdown(); - delete descriptorSet.set; - descriptorSet.set = nullptr; - } - - if (descriptorSet.pool != nullptr) { - descriptorSet.pool->Shutdown(); - delete descriptorSet.pool; - descriptorSet.pool = nullptr; - } -} - -bool SceneViewportSelectionMaskPass::DrawRenderable( - const Rendering::RenderContext& renderContext, - const Rendering::RenderCameraData& cameraData, - const Rendering::VisibleRenderItem& renderable) { - const Rendering::RenderResourceCache::CachedMesh* cachedMesh = - m_resourceCache.GetOrCreateMesh(m_device, renderable.mesh); - if (cachedMesh == nullptr || cachedMesh->vertexBufferView == nullptr) { - return false; - } - - RHI::RHICommandList* commandList = renderContext.commandList; - - RHI::RHIResourceView* vertexBuffers[] = { cachedMesh->vertexBufferView }; - const uint64_t offsets[] = { 0 }; - const uint32_t strides[] = { cachedMesh->vertexStride }; - commandList->SetVertexBuffers(0, 1, vertexBuffers, offsets, strides); - if (cachedMesh->indexBufferView != nullptr) { - commandList->SetIndexBuffer(cachedMesh->indexBufferView, 0); - } - - const PerObjectConstants constants = { - cameraData.projection, - cameraData.view, - renderable.localToWorld.Transpose() - }; - - const uint64_t objectId = renderable.gameObject != nullptr ? renderable.gameObject->GetID() : 0u; - RHI::RHIDescriptorSet* constantSet = GetOrCreatePerObjectSet(objectId); - if (constantSet == nullptr) { - return false; - } - - constantSet->WriteConstant(0, &constants, sizeof(constants)); - RHI::RHIDescriptorSet* descriptorSets[] = { constantSet }; - commandList->SetGraphicsDescriptorSets(0, 1, descriptorSets, m_pipelineLayout); - - if (renderable.hasSection) { - const auto& sections = renderable.mesh->GetSections(); - if (renderable.sectionIndex >= sections.Size()) { - return false; - } - - const Resources::MeshSection& section = sections[renderable.sectionIndex]; - if (cachedMesh->indexBufferView != nullptr && section.indexCount > 0) { - commandList->DrawIndexed(section.indexCount, 1, section.startIndex, 0, 0); - } else if (section.vertexCount > 0) { - commandList->Draw(section.vertexCount, 1, section.baseVertex, 0); - } - return true; - } - - if (cachedMesh->indexBufferView != nullptr && cachedMesh->indexCount > 0) { - commandList->DrawIndexed(cachedMesh->indexCount, 1, 0, 0, 0); - } else if (cachedMesh->vertexCount > 0) { - commandList->Draw(cachedMesh->vertexCount, 1, 0, 0); - } - - return true; -} - -} // namespace Editor -} // namespace XCEngine diff --git a/editor/src/Viewport/SceneViewportSelectionMaskPass.h b/editor/src/Viewport/SceneViewportSelectionMaskPass.h deleted file mode 100644 index 58f681a0..00000000 --- a/editor/src/Viewport/SceneViewportSelectionMaskPass.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace XCEngine { -namespace Editor { - -class SceneViewportSelectionMaskPass { -public: - ~SceneViewportSelectionMaskPass() = default; - - void Shutdown(); - - bool Render( - const Rendering::RenderContext& renderContext, - const Rendering::RenderSurface& surface, - const Rendering::RenderCameraData& cameraData, - const std::vector& renderables, - bool debugColor = false); - -private: - struct OwnedDescriptorSet { - RHI::RHIDescriptorPool* pool = nullptr; - RHI::RHIDescriptorSet* set = nullptr; - }; - - struct PerObjectConstants { - Math::Matrix4x4 projection = Math::Matrix4x4::Identity(); - Math::Matrix4x4 view = Math::Matrix4x4::Identity(); - Math::Matrix4x4 model = Math::Matrix4x4::Identity(); - }; - - bool EnsureInitialized(const Rendering::RenderContext& renderContext); - bool CreateResources(const Rendering::RenderContext& renderContext); - void DestroyResources(); - RHI::RHIDescriptorSet* GetOrCreatePerObjectSet(uint64_t objectId); - void DestroyOwnedDescriptorSet(OwnedDescriptorSet& descriptorSet); - bool DrawRenderable( - const Rendering::RenderContext& renderContext, - const Rendering::RenderCameraData& cameraData, - const Rendering::VisibleRenderItem& renderable); - - RHI::RHIDevice* m_device = nullptr; - RHI::RHIType m_backendType = RHI::RHIType::D3D12; - RHI::RHIPipelineLayout* m_pipelineLayout = nullptr; - RHI::RHIPipelineState* m_pipelineState = nullptr; - std::unordered_map m_perObjectSets; - Rendering::RenderResourceCache m_resourceCache; -}; - -} // namespace Editor -} // namespace XCEngine diff --git a/editor/src/Viewport/SceneViewportSelectionOutlinePass.cpp b/editor/src/Viewport/SceneViewportSelectionOutlinePass.cpp index 2247218b..a981a4d7 100644 --- a/editor/src/Viewport/SceneViewportSelectionOutlinePass.cpp +++ b/editor/src/Viewport/SceneViewportSelectionOutlinePass.cpp @@ -1,8 +1,10 @@ #include "SceneViewportSelectionOutlinePass.h" +#include #include #include +#include #include namespace XCEngine { @@ -16,10 +18,11 @@ const char kSelectionOutlineCompositeHlsl[] = R"( cbuffer OutlineConstants : register(b0) { float4 gViewportSizeAndTexelSize; float4 gOutlineColorAndWidth; + float4 gSelectedInfo; + float4 gSelectedObjectColors[256]; }; -Texture2D gSelectionMask : register(t0); -SamplerState gMaskSampler : register(s0); +Texture2D gObjectIdTexture : register(t0); struct VSOutput { float4 position : SV_POSITION; @@ -37,25 +40,56 @@ VSOutput MainVS(uint vertexId : SV_VertexID) { return output; } -float SampleMask(float2 uv) { - return gSelectionMask.SampleLevel(gMaskSampler, uv, 0).r; +int2 ClampPixelCoord(int2 pixelCoord) { + const int2 maxCoord = int2( + max((int)gViewportSizeAndTexelSize.x - 1, 0), + max((int)gViewportSizeAndTexelSize.y - 1, 0)); + return clamp(pixelCoord, int2(0, 0), maxCoord); +} + +float4 LoadObjectId(int2 pixelCoord) { + return gObjectIdTexture.Load(int3(ClampPixelCoord(pixelCoord), 0)); +} + +bool IsSelectedObject(float4 objectIdColor) { + if (objectIdColor.a <= 0.0) { + return false; + } + + const int selectedCount = min((int)gSelectedInfo.x, 256); + [loop] + for (int i = 0; i < selectedCount; ++i) { + const float4 selectedColor = gSelectedObjectColors[i]; + if (all(abs(objectIdColor - selectedColor) <= float4( + 0.0025, + 0.0025, + 0.0025, + 0.0025))) { + return true; + } + } + + return false; } float4 MainPS(VSOutput input) : SV_TARGET { - const float2 viewportSize = max(gViewportSizeAndTexelSize.xy, float2(1.0, 1.0)); - const float2 texelSize = max(gViewportSizeAndTexelSize.zw, float2(1e-6, 1e-6)); - const float outlineWidth = max(gOutlineColorAndWidth.w, 1.0); + const int2 pixelCoord = int2(input.position.xy); + const bool debugSelectionMask = gSelectedInfo.y > 0.5; + const bool centerSelected = IsSelectedObject(LoadObjectId(pixelCoord)); - const float2 uv = input.position.xy / viewportSize; - const float centerMask = SampleMask(uv); - if (centerMask > 0.001) { + if (debugSelectionMask) { + return centerSelected ? float4(1.0, 1.0, 1.0, 1.0) : float4(0.0, 0.0, 0.0, 1.0); + } + + if (centerSelected) { discard; } + const int outlineWidth = max((int)gOutlineColorAndWidth.w, 1); float outline = 0.0; - [unroll] + [loop] for (int y = -2; y <= 2; ++y) { - [unroll] + [loop] for (int x = -2; x <= 2; ++x) { if (x == 0 && y == 0) { continue; @@ -66,9 +100,12 @@ float4 MainPS(VSOutput input) : SV_TARGET { continue; } - const float sampleMask = SampleMask(uv + float2((float)x, (float)y) * texelSize); - const float weight = saturate(1.0 - ((distancePixels - 1.0) / max(outlineWidth, 1.0))); - outline = max(outline, sampleMask * weight); + if (!IsSelectedObject(LoadObjectId(pixelCoord + int2(x, y)))) { + continue; + } + + const float weight = saturate(1.0 - ((distancePixels - 1.0) / max((float)outlineWidth, 1.0))); + outline = max(outline, weight); } } @@ -76,7 +113,7 @@ float4 MainPS(VSOutput input) : SV_TARGET { discard; } - return float4(gOutlineColorAndWidth.rgb, gOutlineColorAndWidth.a * outline); + return float4(gOutlineColorAndWidth.rgb, outline); } )"; @@ -89,10 +126,13 @@ void SceneViewportSelectionOutlinePass::Shutdown() { bool SceneViewportSelectionOutlinePass::Render( const Rendering::RenderContext& renderContext, const Rendering::RenderSurface& surface, - RHI::RHIResourceView* maskTextureView) { + RHI::RHIResourceView* objectIdTextureView, + const std::vector& selectedObjectIds, + bool debugSelectionMask) { if (!renderContext.IsValid() || renderContext.backendType != RHI::RHIType::D3D12 || - maskTextureView == nullptr) { + objectIdTextureView == nullptr || + selectedObjectIds.empty()) { return false; } @@ -112,8 +152,22 @@ bool SceneViewportSelectionOutlinePass::Render( surface.GetWidth() > 0 ? 1.0f / static_cast(surface.GetWidth()) : 0.0f, surface.GetHeight() > 0 ? 1.0f / static_cast(surface.GetHeight()) : 0.0f); constants.outlineColorAndWidth = Math::Vector4(1.0f, 0.4f, 0.0f, kOutlineWidthPixels); + + const uint32_t selectedCount = (std::min)( + static_cast(selectedObjectIds.size()), + kMaxSelectedObjectCount); + constants.selectedInfo = Math::Vector4( + static_cast(selectedCount), + debugSelectionMask ? 1.0f : 0.0f, + 0.0f, + 0.0f); + for (uint32_t index = 0; index < selectedCount; ++index) { + constants.selectedObjectColors[index] = + Rendering::EncodeObjectIdToColor(selectedObjectIds[index]); + } + m_constantSet->WriteConstant(0, &constants, sizeof(constants)); - m_textureSet->Update(0, maskTextureView); + m_textureSet->Update(0, objectIdTextureView); RHI::RHICommandList* commandList = renderContext.commandList; RHI::RHIResourceView* renderTarget = colorAttachments[0]; @@ -139,8 +193,8 @@ bool SceneViewportSelectionOutlinePass::Render( commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList); commandList->SetPipelineState(m_pipelineState); - RHI::RHIDescriptorSet* descriptorSets[] = { m_constantSet, m_textureSet, m_samplerSet }; - commandList->SetGraphicsDescriptorSets(0, 3, descriptorSets, m_pipelineLayout); + RHI::RHIDescriptorSet* descriptorSets[] = { m_constantSet, m_textureSet }; + commandList->SetGraphicsDescriptorSets(0, 2, descriptorSets, m_pipelineLayout); commandList->Draw(3, 1, 0, 0); return true; } @@ -152,9 +206,6 @@ bool SceneViewportSelectionOutlinePass::EnsureInitialized(const Rendering::Rende m_constantSet != nullptr && m_texturePool != nullptr && m_textureSet != nullptr && - m_samplerPool != nullptr && - m_samplerSet != nullptr && - m_sampler != nullptr && m_device == renderContext.device && m_backendType == renderContext.backendType) { return true; @@ -172,16 +223,13 @@ bool SceneViewportSelectionOutlinePass::CreateResources(const Rendering::RenderC m_device = renderContext.device; m_backendType = renderContext.backendType; - RHI::DescriptorSetLayoutBinding setBindings[3] = {}; + RHI::DescriptorSetLayoutBinding setBindings[2] = {}; setBindings[0].binding = 0; setBindings[0].type = static_cast(RHI::DescriptorType::CBV); setBindings[0].count = 1; setBindings[1].binding = 0; setBindings[1].type = static_cast(RHI::DescriptorType::SRV); setBindings[1].count = 1; - setBindings[2].binding = 0; - setBindings[2].type = static_cast(RHI::DescriptorType::Sampler); - setBindings[2].count = 1; RHI::DescriptorSetLayoutDesc constantLayout = {}; constantLayout.bindings = &setBindings[0]; @@ -191,18 +239,13 @@ bool SceneViewportSelectionOutlinePass::CreateResources(const Rendering::RenderC textureLayout.bindings = &setBindings[1]; textureLayout.bindingCount = 1; - RHI::DescriptorSetLayoutDesc samplerLayout = {}; - samplerLayout.bindings = &setBindings[2]; - samplerLayout.bindingCount = 1; - - RHI::DescriptorSetLayoutDesc setLayouts[3] = {}; + RHI::DescriptorSetLayoutDesc setLayouts[2] = {}; setLayouts[0] = constantLayout; setLayouts[1] = textureLayout; - setLayouts[2] = samplerLayout; RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {}; pipelineLayoutDesc.setLayouts = setLayouts; - pipelineLayoutDesc.setLayoutCount = 3; + pipelineLayoutDesc.setLayoutCount = 2; m_pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc); if (m_pipelineLayout == nullptr) { DestroyResources(); @@ -241,39 +284,6 @@ bool SceneViewportSelectionOutlinePass::CreateResources(const Rendering::RenderC return false; } - RHI::DescriptorPoolDesc samplerPoolDesc = {}; - samplerPoolDesc.type = RHI::DescriptorHeapType::Sampler; - samplerPoolDesc.descriptorCount = 1; - samplerPoolDesc.shaderVisible = true; - m_samplerPool = m_device->CreateDescriptorPool(samplerPoolDesc); - if (m_samplerPool == nullptr) { - DestroyResources(); - return false; - } - - m_samplerSet = m_samplerPool->AllocateSet(samplerLayout); - if (m_samplerSet == nullptr) { - DestroyResources(); - 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 = 1.0f; - m_sampler = m_device->CreateSampler(samplerDesc); - if (m_sampler == nullptr) { - DestroyResources(); - return false; - } - m_samplerSet->UpdateSampler(0, m_sampler); - RHI::GraphicsPipelineDesc pipelineDesc = {}; pipelineDesc.pipelineLayout = m_pipelineLayout; pipelineDesc.topologyType = static_cast(RHI::PrimitiveTopologyType::Triangle); @@ -330,24 +340,6 @@ void SceneViewportSelectionOutlinePass::DestroyResources() { m_pipelineState = nullptr; } - if (m_sampler != nullptr) { - m_sampler->Shutdown(); - delete m_sampler; - m_sampler = nullptr; - } - - if (m_samplerSet != nullptr) { - m_samplerSet->Shutdown(); - delete m_samplerSet; - m_samplerSet = nullptr; - } - - if (m_samplerPool != nullptr) { - m_samplerPool->Shutdown(); - delete m_samplerPool; - m_samplerPool = nullptr; - } - if (m_textureSet != nullptr) { m_textureSet->Shutdown(); delete m_textureSet; diff --git a/editor/src/Viewport/SceneViewportSelectionOutlinePass.h b/editor/src/Viewport/SceneViewportSelectionOutlinePass.h index 48a6d088..ac3ec470 100644 --- a/editor/src/Viewport/SceneViewportSelectionOutlinePass.h +++ b/editor/src/Viewport/SceneViewportSelectionOutlinePass.h @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -12,9 +11,10 @@ #include #include #include -#include +#include #include +#include namespace XCEngine { namespace Editor { @@ -23,17 +23,23 @@ class SceneViewportSelectionOutlinePass { public: ~SceneViewportSelectionOutlinePass() = default; + static constexpr uint32_t kMaxSelectedObjectCount = 256u; + void Shutdown(); bool Render( const Rendering::RenderContext& renderContext, const Rendering::RenderSurface& surface, - RHI::RHIResourceView* maskTextureView); + RHI::RHIResourceView* objectIdTextureView, + const std::vector& selectedObjectIds, + bool debugSelectionMask = false); private: struct OutlineConstants { Math::Vector4 viewportSizeAndTexelSize = Math::Vector4::Zero(); Math::Vector4 outlineColorAndWidth = Math::Vector4::Zero(); + Math::Vector4 selectedInfo = Math::Vector4::Zero(); + std::array selectedObjectColors = {}; }; bool EnsureInitialized(const Rendering::RenderContext& renderContext); @@ -48,9 +54,6 @@ private: RHI::RHIDescriptorSet* m_constantSet = nullptr; RHI::RHIDescriptorPool* m_texturePool = nullptr; RHI::RHIDescriptorSet* m_textureSet = nullptr; - RHI::RHIDescriptorPool* m_samplerPool = nullptr; - RHI::RHIDescriptorSet* m_samplerSet = nullptr; - RHI::RHISampler* m_sampler = nullptr; }; } // namespace Editor diff --git a/editor/src/Viewport/ViewportHostService.h b/editor/src/Viewport/ViewportHostService.h index 5ddaf1d0..9b3e3534 100644 --- a/editor/src/Viewport/ViewportHostService.h +++ b/editor/src/Viewport/ViewportHostService.h @@ -8,9 +8,7 @@ #include "SceneViewportCameraController.h" #include "SceneViewportInfiniteGridPass.h" #include "SceneViewportPostPassPlan.h" -#include "SceneViewportSelectionMaskPass.h" #include "SceneViewportSelectionOutlinePass.h" -#include "SceneViewportSelectionUtils.h" #include "UI/ImGuiBackendBridge.h" #include @@ -128,7 +126,6 @@ public: m_device = nullptr; m_backend = nullptr; m_sceneGridPass.Shutdown(); - m_sceneSelectionMaskPass.Shutdown(); m_sceneSelectionOutlinePass.Shutdown(); m_sceneRenderer.reset(); } @@ -309,16 +306,13 @@ private: RHI::RHIResourceView* colorView = nullptr; RHI::RHITexture* depthTexture = nullptr; RHI::RHIResourceView* depthView = nullptr; - RHI::RHITexture* selectionMaskTexture = nullptr; - RHI::RHIResourceView* selectionMaskView = nullptr; - RHI::RHIResourceView* selectionMaskShaderView = nullptr; RHI::RHITexture* objectIdTexture = nullptr; RHI::RHIResourceView* objectIdView = nullptr; + RHI::RHIResourceView* objectIdShaderView = nullptr; D3D12_CPU_DESCRIPTOR_HANDLE imguiCpuHandle = {}; D3D12_GPU_DESCRIPTOR_HANDLE imguiGpuHandle = {}; ImTextureID textureId = {}; RHI::ResourceStates colorState = RHI::ResourceStates::Common; - RHI::ResourceStates selectionMaskState = RHI::ResourceStates::Common; RHI::ResourceStates objectIdState = RHI::ResourceStates::Common; bool hasValidObjectIdFrame = false; std::string statusText; @@ -333,8 +327,7 @@ private: struct SceneViewportRenderState { SceneViewportOverlayData overlay = {}; Rendering::RenderPassSequence postPasses; - Rendering::RenderCameraData cameraData = {}; - std::vector selectionRenderables; + std::vector selectedObjectIds; }; ViewportEntry& GetEntry(EditorViewportKind kind) { @@ -465,29 +458,6 @@ private: return entry.depthView != nullptr; } - bool CreateSceneViewportSelectionMaskResources(ViewportEntry& entry) { - const RHI::TextureDesc selectionMaskDesc = - BuildViewportTextureDesc(entry.width, entry.height, RHI::Format::R8G8B8A8_UNorm); - entry.selectionMaskTexture = m_device->CreateTexture(selectionMaskDesc); - if (entry.selectionMaskTexture == nullptr) { - return false; - } - - const RHI::ResourceViewDesc selectionMaskViewDesc = - BuildViewportTextureViewDesc(RHI::Format::R8G8B8A8_UNorm); - entry.selectionMaskView = m_device->CreateRenderTargetView( - entry.selectionMaskTexture, - selectionMaskViewDesc); - if (entry.selectionMaskView == nullptr) { - return false; - } - - entry.selectionMaskShaderView = m_device->CreateShaderResourceView( - entry.selectionMaskTexture, - selectionMaskViewDesc); - return entry.selectionMaskShaderView != nullptr; - } - bool CreateSceneViewportObjectIdResources(ViewportEntry& entry) { const RHI::TextureDesc objectIdDesc = BuildViewportTextureDesc(entry.width, entry.height, RHI::Format::R8G8B8A8_UNorm); @@ -501,7 +471,14 @@ private: entry.objectIdView = m_device->CreateRenderTargetView( entry.objectIdTexture, objectIdViewDesc); - return entry.objectIdView != nullptr; + if (entry.objectIdView == nullptr) { + return false; + } + + entry.objectIdShaderView = m_device->CreateShaderResourceView( + entry.objectIdTexture, + objectIdViewDesc); + return entry.objectIdShaderView != nullptr; } bool CreateViewportTextureDescriptor(ViewportEntry& entry) { @@ -537,11 +514,9 @@ private: entry.depthTexture != nullptr && entry.depthView != nullptr && (entry.kind != EditorViewportKind::Scene || - (entry.selectionMaskTexture != nullptr && - entry.selectionMaskView != nullptr && - entry.selectionMaskShaderView != nullptr && - entry.objectIdTexture != nullptr && - entry.objectIdView != nullptr)) && + (entry.objectIdTexture != nullptr && + entry.objectIdView != nullptr && + entry.objectIdShaderView != nullptr)) && entry.textureId != ImTextureID{}) { return true; } @@ -561,12 +536,6 @@ private: return false; } - if (entry.kind == EditorViewportKind::Scene && - !CreateSceneViewportSelectionMaskResources(entry)) { - DestroyViewportResources(entry); - return false; - } - if (entry.kind == EditorViewportKind::Scene && !CreateSceneViewportObjectIdResources(entry)) { DestroyViewportResources(entry); @@ -578,7 +547,6 @@ private: return false; } entry.colorState = RHI::ResourceStates::Common; - entry.selectionMaskState = RHI::ResourceStates::Common; entry.objectIdState = RHI::ResourceStates::Common; entry.hasValidObjectIdFrame = false; return true; @@ -593,15 +561,6 @@ private: return surface; } - Rendering::RenderSurface BuildSelectionMaskSurface(const ViewportEntry& entry) const { - Rendering::RenderSurface surface(entry.width, entry.height); - surface.SetColorAttachment(entry.selectionMaskView); - surface.SetDepthAttachment(entry.depthView); - surface.SetColorStateBefore(entry.selectionMaskState); - surface.SetColorStateAfter(RHI::ResourceStates::PixelShaderResource); - return surface; - } - Rendering::RenderSurface BuildObjectIdSurface(const ViewportEntry& entry) const { Rendering::RenderSurface surface(entry.width, entry.height); surface.SetColorAttachment(entry.objectIdView); @@ -611,45 +570,6 @@ private: return surface; } - void AddSceneSelectionMaskPass( - ViewportEntry& entry, - const Rendering::RenderSurface& selectionMaskSurface, - const Rendering::RenderCameraData& cameraData, - const std::vector& selectionRenderables, - Rendering::RenderPassSequence& outPostPasses) { - outPostPasses.AddPass(MakeLambdaRenderPass( - "SceneSelectionMask", - [this, &entry, selectionMaskSurface, &cameraData, &selectionRenderables]( - const Rendering::RenderPassContext& context) mutable { - context.renderContext.commandList->TransitionBarrier( - entry.selectionMaskView, - entry.selectionMaskState, - RHI::ResourceStates::RenderTarget); - entry.selectionMaskState = RHI::ResourceStates::RenderTarget; - - const float maskClearColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; - RHI::RHIResourceView* maskView = entry.selectionMaskView; - context.renderContext.commandList->SetRenderTargets(1, &maskView, entry.depthView); - context.renderContext.commandList->ClearRenderTarget(maskView, maskClearColor); - - const bool rendered = m_sceneSelectionMaskPass.Render( - context.renderContext, - selectionMaskSurface, - cameraData, - selectionRenderables); - if (!rendered) { - SetViewportStatusIfEmpty(entry.statusText, "Scene selection mask pass failed"); - } - - context.renderContext.commandList->TransitionBarrier( - entry.selectionMaskView, - entry.selectionMaskState, - RHI::ResourceStates::PixelShaderResource); - entry.selectionMaskState = RHI::ResourceStates::PixelShaderResource; - return rendered; - })); - } - void AddSceneColorToRenderTargetPass( ViewportEntry& entry, Rendering::RenderPassSequence& outPostPasses) { @@ -685,14 +605,17 @@ private: void AddSceneSelectionOutlinePass( ViewportEntry& entry, + const std::vector& selectedObjectIds, Rendering::RenderPassSequence& outPostPasses) { outPostPasses.AddPass(MakeLambdaRenderPass( "SceneSelectionOutline", - [this, &entry](const Rendering::RenderPassContext& context) { + [this, &entry, selectedObjectIds](const Rendering::RenderPassContext& context) { const bool rendered = m_sceneSelectionOutlinePass.Render( context.renderContext, context.surface, - entry.selectionMaskShaderView); + entry.objectIdShaderView, + selectedObjectIds, + false); if (!rendered) { SetViewportStatusIfEmpty(entry.statusText, "Scene selection outline pass failed"); } @@ -717,23 +640,23 @@ private: void AddSceneSelectionMaskDebugPass( ViewportEntry& entry, - const Rendering::RenderCameraData& cameraData, - const std::vector& selectionRenderables, + const std::vector& selectedObjectIds, Rendering::RenderPassSequence& outPostPasses) { outPostPasses.AddPass(MakeLambdaRenderPass( "SceneSelectionMaskDebug", - [this, &entry, &cameraData, &selectionRenderables]( + [this, &entry, selectedObjectIds]( const Rendering::RenderPassContext& context) { const float debugClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; RHI::RHIResourceView* colorView = entry.colorView; context.renderContext.commandList->SetRenderTargets(1, &colorView, entry.depthView); context.renderContext.commandList->ClearRenderTarget(colorView, debugClearColor); - const bool rendered = m_sceneSelectionMaskPass.Render( + const bool rendered = m_sceneSelectionOutlinePass.Render( context.renderContext, context.surface, - cameraData, - selectionRenderables); + entry.objectIdShaderView, + selectedObjectIds, + true); if (!rendered) { SetViewportStatusIfEmpty(entry.statusText, "Scene selection mask debug pass failed"); } @@ -745,19 +668,9 @@ private: SceneViewportPostPassStep step, ViewportEntry& entry, const SceneViewportOverlayData& overlay, - const Rendering::RenderSurface& selectionMaskSurface, - const Rendering::RenderCameraData& cameraData, - const std::vector& selectionRenderables, + const std::vector& selectedObjectIds, Rendering::RenderPassSequence& outPostPasses) { switch (step) { - case SceneViewportPostPassStep::SelectionMask: - AddSceneSelectionMaskPass( - entry, - selectionMaskSurface, - cameraData, - selectionRenderables, - outPostPasses); - break; case SceneViewportPostPassStep::ColorToRenderTarget: AddSceneColorToRenderTargetPass(entry, outPostPasses); break; @@ -765,7 +678,7 @@ private: AddSceneInfiniteGridPass(entry, overlay, outPostPasses); break; case SceneViewportPostPassStep::SelectionOutline: - AddSceneSelectionOutlinePass(entry, outPostPasses); + AddSceneSelectionOutlinePass(entry, selectedObjectIds, outPostPasses); break; case SceneViewportPostPassStep::ColorToShaderResource: AddSceneColorToShaderResourcePass(entry, outPostPasses); @@ -773,8 +686,7 @@ private: case SceneViewportPostPassStep::SelectionMaskDebug: AddSceneSelectionMaskDebugPass( entry, - cameraData, - selectionRenderables, + selectedObjectIds, outPostPasses); break; default: @@ -785,43 +697,33 @@ private: bool BuildSceneViewPostPassSequence( ViewportEntry& entry, const SceneViewportOverlayData& overlay, - const Rendering::RenderCameraData& cameraData, - const std::vector& selectionRenderables, + const std::vector& selectedObjectIds, Rendering::RenderPassSequence& outPostPasses) { - const bool hasSelection = !selectionRenderables.empty(); - const bool hasSelectionMaskRenderTarget = entry.selectionMaskView != nullptr; - const bool hasSelectionMaskShaderView = entry.selectionMaskShaderView != nullptr; + const bool hasSelection = !selectedObjectIds.empty(); + const bool hasObjectIdShaderView = entry.objectIdShaderView != nullptr; if (hasSelection && !kDebugSceneSelectionMask && - (!hasSelectionMaskRenderTarget || !hasSelectionMaskShaderView)) { - SetViewportStatusIfEmpty(entry.statusText, "Scene selection mask target is unavailable"); + !hasObjectIdShaderView) { + SetViewportStatusIfEmpty(entry.statusText, "Scene object id shader view is unavailable"); } const SceneViewportPostPassPlan plan = BuildSceneViewportPostPassPlan({ overlay.valid, hasSelection, kDebugSceneSelectionMask, - hasSelectionMaskRenderTarget, - hasSelectionMaskShaderView + hasObjectIdShaderView }); if (!plan.valid) { return false; } - Rendering::RenderSurface selectionMaskSurface = {}; - if (plan.usesSelectionMaskSurface) { - selectionMaskSurface = BuildSelectionMaskSurface(entry); - } - for (const SceneViewportPostPassStep step : plan.steps) { AddSceneViewPostPassStep( step, entry, overlay, - selectionMaskSurface, - cameraData, - selectionRenderables, + selectedObjectIds, outPostPasses); } @@ -833,27 +735,18 @@ private: IEditorContext& context, const Components::Scene& scene, SceneViewportRenderState& outState) { + (void)scene; outState.overlay = GetSceneViewOverlayData(); if (!outState.overlay.valid) { return; } - outState.selectionRenderables = CollectSceneViewportSelectionRenderables( - scene, - context.GetSelectionManager().GetSelectedEntities(), - outState.overlay.cameraPosition); - if (!outState.selectionRenderables.empty()) { - outState.cameraData = BuildSceneViewportCameraData( - *m_sceneViewCamera.camera, - entry.width, - entry.height); - } + outState.selectedObjectIds = context.GetSelectionManager().GetSelectedEntities(); BuildSceneViewPostPassSequence( entry, outState.overlay, - outState.cameraData, - outState.selectionRenderables, + outState.selectedObjectIds, outState.postPasses); } @@ -1046,30 +939,18 @@ private: m_backend->FreeTextureDescriptor(entry.imguiCpuHandle, entry.imguiGpuHandle); } - if (entry.selectionMaskShaderView != nullptr) { - entry.selectionMaskShaderView->Shutdown(); - delete entry.selectionMaskShaderView; - entry.selectionMaskShaderView = nullptr; - } - - if (entry.selectionMaskView != nullptr) { - entry.selectionMaskView->Shutdown(); - delete entry.selectionMaskView; - entry.selectionMaskView = nullptr; - } - - if (entry.selectionMaskTexture != nullptr) { - entry.selectionMaskTexture->Shutdown(); - delete entry.selectionMaskTexture; - entry.selectionMaskTexture = nullptr; - } - if (entry.objectIdView != nullptr) { entry.objectIdView->Shutdown(); delete entry.objectIdView; entry.objectIdView = nullptr; } + if (entry.objectIdShaderView != nullptr) { + entry.objectIdShaderView->Shutdown(); + delete entry.objectIdShaderView; + entry.objectIdShaderView = nullptr; + } + if (entry.objectIdTexture != nullptr) { entry.objectIdTexture->Shutdown(); delete entry.objectIdTexture; @@ -1106,7 +987,6 @@ private: entry.imguiGpuHandle = {}; entry.textureId = {}; entry.colorState = RHI::ResourceStates::Common; - entry.selectionMaskState = RHI::ResourceStates::Common; entry.objectIdState = RHI::ResourceStates::Common; entry.hasValidObjectIdFrame = false; } @@ -1118,7 +998,6 @@ private: std::array m_entries = {}; SceneViewCameraState m_sceneViewCamera; SceneViewportInfiniteGridPass m_sceneGridPass; - SceneViewportSelectionMaskPass m_sceneSelectionMaskPass; SceneViewportSelectionOutlinePass m_sceneSelectionOutlinePass; }; diff --git a/tests/editor/test_scene_viewport_post_pass_plan.cpp b/tests/editor/test_scene_viewport_post_pass_plan.cpp index b4a279e5..e38b1a5f 100644 --- a/tests/editor/test_scene_viewport_post_pass_plan.cpp +++ b/tests/editor/test_scene_viewport_post_pass_plan.cpp @@ -15,8 +15,6 @@ TEST(SceneViewportPostPassPlan_Test, ReturnsInvalidPlanWhenOverlayIsUnavailable) EXPECT_FALSE(plan.valid); EXPECT_TRUE(plan.steps.empty()); - EXPECT_FALSE(plan.usesSelectionMaskSurface); - EXPECT_FALSE(plan.usesSelectionMaskShaderView); } TEST(SceneViewportPostPassPlan_Test, BuildsGridOnlyPlanWhenNothingIsSelected) { @@ -33,16 +31,13 @@ TEST(SceneViewportPostPassPlan_Test, BuildsGridOnlyPlanWhenNothingIsSelected) { SceneViewportPostPassStep::InfiniteGrid, SceneViewportPostPassStep::ColorToShaderResource })); - EXPECT_FALSE(plan.usesSelectionMaskSurface); - EXPECT_FALSE(plan.usesSelectionMaskShaderView); } -TEST(SceneViewportPostPassPlan_Test, BuildsSelectionOutlinePlanWhenSelectionResourcesExist) { +TEST(SceneViewportPostPassPlan_Test, BuildsSelectionOutlinePlanWhenObjectIdShaderViewExists) { SceneViewportPostPassPlanInput input = {}; input.overlayValid = true; input.hasSelection = true; - input.hasSelectionMaskRenderTarget = true; - input.hasSelectionMaskShaderView = true; + input.hasObjectIdShaderView = true; const auto plan = BuildSceneViewportPostPassPlan(input); @@ -50,22 +45,17 @@ TEST(SceneViewportPostPassPlan_Test, BuildsSelectionOutlinePlanWhenSelectionReso EXPECT_EQ( plan.steps, (std::vector{ - SceneViewportPostPassStep::SelectionMask, SceneViewportPostPassStep::ColorToRenderTarget, SceneViewportPostPassStep::InfiniteGrid, SceneViewportPostPassStep::SelectionOutline, SceneViewportPostPassStep::ColorToShaderResource })); - EXPECT_TRUE(plan.usesSelectionMaskSurface); - EXPECT_TRUE(plan.usesSelectionMaskShaderView); } -TEST(SceneViewportPostPassPlan_Test, SkipsSelectionSpecificPassesWhenMaskResourcesAreMissing) { +TEST(SceneViewportPostPassPlan_Test, SkipsSelectionOutlineWhenObjectIdShaderViewIsMissing) { SceneViewportPostPassPlanInput input = {}; input.overlayValid = true; input.hasSelection = true; - input.hasSelectionMaskRenderTarget = true; - input.hasSelectionMaskShaderView = false; const auto plan = BuildSceneViewportPostPassPlan(input); @@ -77,11 +67,28 @@ TEST(SceneViewportPostPassPlan_Test, SkipsSelectionSpecificPassesWhenMaskResourc SceneViewportPostPassStep::InfiniteGrid, SceneViewportPostPassStep::ColorToShaderResource })); - EXPECT_FALSE(plan.usesSelectionMaskSurface); - EXPECT_FALSE(plan.usesSelectionMaskShaderView); } -TEST(SceneViewportPostPassPlan_Test, BuildsDebugMaskPlanWithoutSelectionMaskTarget) { +TEST(SceneViewportPostPassPlan_Test, BuildsDebugMaskPlanFromObjectIdShaderView) { + SceneViewportPostPassPlanInput input = {}; + input.overlayValid = true; + input.hasSelection = true; + input.debugSelectionMask = true; + input.hasObjectIdShaderView = true; + + const auto plan = BuildSceneViewportPostPassPlan(input); + + ASSERT_TRUE(plan.valid); + EXPECT_EQ( + plan.steps, + (std::vector{ + SceneViewportPostPassStep::ColorToRenderTarget, + SceneViewportPostPassStep::SelectionMaskDebug, + SceneViewportPostPassStep::ColorToShaderResource + })); +} + +TEST(SceneViewportPostPassPlan_Test, DebugMaskPlanFallsBackWhenObjectIdShaderViewIsMissing) { SceneViewportPostPassPlanInput input = {}; input.overlayValid = true; input.hasSelection = true; @@ -94,11 +101,8 @@ TEST(SceneViewportPostPassPlan_Test, BuildsDebugMaskPlanWithoutSelectionMaskTarg plan.steps, (std::vector{ SceneViewportPostPassStep::ColorToRenderTarget, - SceneViewportPostPassStep::SelectionMaskDebug, SceneViewportPostPassStep::ColorToShaderResource })); - EXPECT_FALSE(plan.usesSelectionMaskSurface); - EXPECT_FALSE(plan.usesSelectionMaskShaderView); } } // namespace