2026-04-01 17:47:49 +08:00
|
|
|
#include "Rendering/Passes/BuiltinObjectIdOutlinePass.h"
|
2026-03-29 15:12:38 +08:00
|
|
|
|
2026-04-02 20:18:39 +08:00
|
|
|
#include "Core/Asset/ResourceManager.h"
|
|
|
|
|
#include "Debug/Logger.h"
|
2026-04-10 01:57:15 +08:00
|
|
|
#include "Rendering/Internal/RenderSurfacePipelineUtils.h"
|
|
|
|
|
#include "Rendering/Internal/ShaderVariantUtils.h"
|
2026-04-08 16:09:15 +08:00
|
|
|
#include "Rendering/Materials/RenderMaterialStateUtils.h"
|
2026-04-10 01:57:15 +08:00
|
|
|
#include "Rendering/Picking/ObjectIdCodec.h"
|
2026-04-01 17:47:49 +08:00
|
|
|
#include "RHI/RHICommandList.h"
|
|
|
|
|
#include "RHI/RHIDevice.h"
|
2026-04-08 16:09:15 +08:00
|
|
|
#include <XCEngine/Resources/BuiltinResources.h>
|
2026-03-29 15:12:38 +08:00
|
|
|
|
2026-04-01 17:33:07 +08:00
|
|
|
#include <algorithm>
|
2026-04-02 20:45:51 +08:00
|
|
|
#include <utility>
|
2026-03-29 15:12:38 +08:00
|
|
|
|
|
|
|
|
namespace XCEngine {
|
2026-04-01 17:47:49 +08:00
|
|
|
namespace Rendering {
|
|
|
|
|
namespace Passes {
|
2026-03-29 15:12:38 +08:00
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
2026-04-02 20:18:39 +08:00
|
|
|
const Resources::ShaderPass* FindObjectIdOutlineCompatiblePass(
|
|
|
|
|
const Resources::Shader& shader,
|
|
|
|
|
Resources::ShaderBackend backend) {
|
|
|
|
|
const Resources::ShaderPass* outlinePass = shader.FindPass("ObjectIdOutline");
|
|
|
|
|
if (outlinePass != nullptr &&
|
2026-04-10 01:57:15 +08:00
|
|
|
::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants(shader, outlinePass->name, backend)) {
|
2026-04-02 20:18:39 +08:00
|
|
|
return outlinePass;
|
|
|
|
|
}
|
2026-04-01 17:33:07 +08:00
|
|
|
|
2026-04-02 20:18:39 +08:00
|
|
|
const Resources::ShaderPass* editorOutlinePass = shader.FindPass("EditorObjectIdOutline");
|
|
|
|
|
if (editorOutlinePass != nullptr &&
|
2026-04-10 01:57:15 +08:00
|
|
|
::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants(shader, editorOutlinePass->name, backend)) {
|
2026-04-02 20:18:39 +08:00
|
|
|
return editorOutlinePass;
|
2026-04-01 17:33:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:18:39 +08:00
|
|
|
if (shader.GetPassCount() > 0 &&
|
2026-04-10 01:57:15 +08:00
|
|
|
::XCEngine::Rendering::Internal::ShaderPassHasGraphicsVariants(shader, shader.GetPasses()[0].name, backend)) {
|
2026-04-02 20:18:39 +08:00
|
|
|
return &shader.GetPasses()[0];
|
2026-04-01 17:33:07 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:18:39 +08:00
|
|
|
return nullptr;
|
2026-03-29 15:12:38 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:18:39 +08:00
|
|
|
RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
|
|
|
|
RHI::RHIType backendType,
|
|
|
|
|
RHI::RHIPipelineLayout* pipelineLayout,
|
|
|
|
|
const Resources::Shader& shader,
|
2026-04-10 01:57:15 +08:00
|
|
|
const Containers::String& passName,
|
|
|
|
|
const RenderSurface& surface) {
|
2026-04-02 20:18:39 +08:00
|
|
|
RHI::GraphicsPipelineDesc pipelineDesc = {};
|
|
|
|
|
pipelineDesc.pipelineLayout = pipelineLayout;
|
|
|
|
|
pipelineDesc.topologyType = static_cast<uint32_t>(RHI::PrimitiveTopologyType::Triangle);
|
2026-04-10 01:57:15 +08:00
|
|
|
::XCEngine::Rendering::Internal::ApplySingleColorAttachmentPropertiesToGraphicsPipelineDesc(
|
|
|
|
|
surface,
|
|
|
|
|
pipelineDesc);
|
2026-04-02 20:18:39 +08:00
|
|
|
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(RHI::Format::Unknown);
|
2026-03-29 15:12:38 +08:00
|
|
|
|
2026-04-08 16:09:15 +08:00
|
|
|
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<uint8_t>(RHI::ColorWriteMask::All);
|
|
|
|
|
::XCEngine::Rendering::ApplyRenderState(fallbackState, pipelineDesc);
|
|
|
|
|
}
|
2026-03-29 15:12:38 +08:00
|
|
|
|
2026-04-10 01:57:15 +08:00
|
|
|
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(backendType);
|
2026-04-02 20:18:39 +08:00
|
|
|
if (const Resources::ShaderStageVariant* vertexVariant =
|
|
|
|
|
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend)) {
|
2026-04-08 16:09:15 +08:00
|
|
|
if (shaderPass != nullptr) {
|
2026-04-10 01:57:15 +08:00
|
|
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
2026-04-09 02:59:36 +08:00
|
|
|
shader.GetPath(),
|
2026-04-08 16:09:15 +08:00
|
|
|
*shaderPass,
|
|
|
|
|
backend,
|
|
|
|
|
*vertexVariant,
|
|
|
|
|
pipelineDesc.vertexShader);
|
|
|
|
|
}
|
2026-04-02 20:18:39 +08:00
|
|
|
}
|
|
|
|
|
if (const Resources::ShaderStageVariant* fragmentVariant =
|
|
|
|
|
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend)) {
|
2026-04-08 16:09:15 +08:00
|
|
|
if (shaderPass != nullptr) {
|
2026-04-10 01:57:15 +08:00
|
|
|
::XCEngine::Rendering::Internal::ApplyShaderStageVariant(
|
2026-04-09 02:59:36 +08:00
|
|
|
shader.GetPath(),
|
2026-04-08 16:09:15 +08:00
|
|
|
*shaderPass,
|
|
|
|
|
backend,
|
|
|
|
|
*fragmentVariant,
|
|
|
|
|
pipelineDesc.fragmentShader);
|
|
|
|
|
}
|
2026-03-29 15:12:38 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:18:39 +08:00
|
|
|
return pipelineDesc;
|
2026-03-29 15:12:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
2026-04-03 15:43:21 +08:00
|
|
|
BuiltinObjectIdOutlinePass::BuiltinObjectIdOutlinePass(Containers::String shaderPath)
|
2026-04-08 16:09:15 +08:00
|
|
|
: m_shaderPath(shaderPath.Empty() ? Resources::GetBuiltinObjectIdOutlineShaderPath() : std::move(shaderPath)) {
|
2026-04-02 20:45:51 +08:00
|
|
|
ResetState();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 15:43:21 +08:00
|
|
|
void BuiltinObjectIdOutlinePass::SetShaderPath(const Containers::String& shaderPath) {
|
|
|
|
|
if (m_shaderPath == shaderPath) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DestroyResources();
|
|
|
|
|
m_shaderPath = shaderPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Containers::String& BuiltinObjectIdOutlinePass::GetShaderPath() const {
|
|
|
|
|
return m_shaderPath;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-01 17:47:49 +08:00
|
|
|
void BuiltinObjectIdOutlinePass::Shutdown() {
|
2026-03-29 15:12:38 +08:00
|
|
|
DestroyResources();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-01 17:47:49 +08:00
|
|
|
bool BuiltinObjectIdOutlinePass::Render(
|
|
|
|
|
const RenderContext& renderContext,
|
|
|
|
|
const RenderSurface& surface,
|
2026-04-10 01:57:15 +08:00
|
|
|
const ObjectIdOutlinePassInputs& inputs,
|
2026-04-01 17:33:07 +08:00
|
|
|
const std::vector<uint64_t>& selectedObjectIds,
|
2026-04-01 17:47:49 +08:00
|
|
|
const ObjectIdOutlineStyle& style) {
|
2026-03-29 15:12:38 +08:00
|
|
|
if (!renderContext.IsValid() ||
|
2026-04-10 01:57:15 +08:00
|
|
|
inputs.objectIdTextureView == nullptr ||
|
2026-04-01 17:33:07 +08:00
|
|
|
selectedObjectIds.empty()) {
|
2026-03-29 15:12:38 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 01:57:15 +08:00
|
|
|
const std::vector<RHI::RHIResourceView*>& colorAttachments = surface.GetColorAttachments();
|
|
|
|
|
if (!::XCEngine::Rendering::Internal::HasSingleColorAttachment(surface) ||
|
|
|
|
|
colorAttachments.empty() ||
|
|
|
|
|
colorAttachments[0] == nullptr) {
|
2026-03-29 15:12:38 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 01:57:15 +08:00
|
|
|
const Math::RectInt renderArea = surface.GetRenderArea();
|
|
|
|
|
if (renderArea.width <= 0 || renderArea.height <= 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!EnsureInitialized(renderContext, surface)) {
|
2026-03-29 15:12:38 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutlineConstants constants = {};
|
|
|
|
|
constants.viewportSizeAndTexelSize = Math::Vector4(
|
|
|
|
|
static_cast<float>(surface.GetWidth()),
|
|
|
|
|
static_cast<float>(surface.GetHeight()),
|
|
|
|
|
surface.GetWidth() > 0 ? 1.0f / static_cast<float>(surface.GetWidth()) : 0.0f,
|
|
|
|
|
surface.GetHeight() > 0 ? 1.0f / static_cast<float>(surface.GetHeight()) : 0.0f);
|
2026-04-01 17:47:49 +08:00
|
|
|
constants.outlineColor = Math::Vector4(
|
|
|
|
|
style.outlineColor.r,
|
|
|
|
|
style.outlineColor.g,
|
|
|
|
|
style.outlineColor.b,
|
|
|
|
|
style.outlineColor.a);
|
2026-04-01 17:33:07 +08:00
|
|
|
|
2026-04-10 01:57:15 +08:00
|
|
|
uint32_t selectedCount = 0u;
|
|
|
|
|
for (uint64_t selectedObjectId : selectedObjectIds) {
|
|
|
|
|
if (selectedCount >= kMaxSelectedObjectCount) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RenderObjectId renderObjectId = kInvalidRenderObjectId;
|
|
|
|
|
if (!TryConvertRuntimeObjectIdToRenderObjectId(selectedObjectId, renderObjectId)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constants.selectedObjectColors[selectedCount] =
|
|
|
|
|
EncodeRenderObjectIdToColor(renderObjectId);
|
|
|
|
|
++selectedCount;
|
|
|
|
|
}
|
|
|
|
|
if (selectedCount == 0u) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-01 17:33:07 +08:00
|
|
|
constants.selectedInfo = Math::Vector4(
|
|
|
|
|
static_cast<float>(selectedCount),
|
2026-04-01 17:47:49 +08:00
|
|
|
style.debugSelectionMask ? 1.0f : 0.0f,
|
|
|
|
|
style.outlineWidthPixels,
|
2026-04-01 17:33:07 +08:00
|
|
|
0.0f);
|
|
|
|
|
|
2026-03-29 15:12:38 +08:00
|
|
|
m_constantSet->WriteConstant(0, &constants, sizeof(constants));
|
2026-04-10 01:57:15 +08:00
|
|
|
m_textureSet->Update(0, inputs.objectIdTextureView);
|
2026-03-29 15:12:38 +08:00
|
|
|
|
|
|
|
|
RHI::RHICommandList* commandList = renderContext.commandList;
|
|
|
|
|
RHI::RHIResourceView* renderTarget = colorAttachments[0];
|
2026-04-10 01:57:15 +08:00
|
|
|
if (surface.IsAutoTransitionEnabled()) {
|
|
|
|
|
commandList->TransitionBarrier(
|
|
|
|
|
renderTarget,
|
|
|
|
|
surface.GetColorStateAfter(),
|
|
|
|
|
RHI::ResourceStates::RenderTarget);
|
|
|
|
|
commandList->TransitionBarrier(
|
|
|
|
|
inputs.objectIdTextureView,
|
|
|
|
|
inputs.objectIdTextureState,
|
|
|
|
|
RHI::ResourceStates::PixelShaderResource);
|
|
|
|
|
}
|
2026-03-29 15:12:38 +08:00
|
|
|
commandList->SetRenderTargets(1, &renderTarget, nullptr);
|
|
|
|
|
|
|
|
|
|
const RHI::Viewport viewport = {
|
2026-04-10 01:57:15 +08:00
|
|
|
static_cast<float>(renderArea.x),
|
|
|
|
|
static_cast<float>(renderArea.y),
|
|
|
|
|
static_cast<float>(renderArea.width),
|
|
|
|
|
static_cast<float>(renderArea.height),
|
2026-03-29 15:12:38 +08:00
|
|
|
0.0f,
|
|
|
|
|
1.0f
|
|
|
|
|
};
|
|
|
|
|
const RHI::Rect scissorRect = {
|
2026-04-10 01:57:15 +08:00
|
|
|
renderArea.x,
|
|
|
|
|
renderArea.y,
|
|
|
|
|
renderArea.x + renderArea.width,
|
|
|
|
|
renderArea.y + renderArea.height
|
2026-03-29 15:12:38 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
commandList->SetViewport(viewport);
|
|
|
|
|
commandList->SetScissorRect(scissorRect);
|
|
|
|
|
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
|
|
|
|
|
commandList->SetPipelineState(m_pipelineState);
|
|
|
|
|
|
2026-04-01 17:33:07 +08:00
|
|
|
RHI::RHIDescriptorSet* descriptorSets[] = { m_constantSet, m_textureSet };
|
|
|
|
|
commandList->SetGraphicsDescriptorSets(0, 2, descriptorSets, m_pipelineLayout);
|
2026-03-29 15:12:38 +08:00
|
|
|
commandList->Draw(3, 1, 0, 0);
|
2026-04-10 01:57:15 +08:00
|
|
|
commandList->EndRenderPass();
|
|
|
|
|
|
|
|
|
|
if (surface.IsAutoTransitionEnabled()) {
|
|
|
|
|
commandList->TransitionBarrier(
|
|
|
|
|
renderTarget,
|
|
|
|
|
RHI::ResourceStates::RenderTarget,
|
|
|
|
|
surface.GetColorStateAfter());
|
|
|
|
|
commandList->TransitionBarrier(
|
|
|
|
|
inputs.objectIdTextureView,
|
|
|
|
|
RHI::ResourceStates::PixelShaderResource,
|
|
|
|
|
inputs.objectIdTextureState);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 15:12:38 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 01:57:15 +08:00
|
|
|
bool BuiltinObjectIdOutlinePass::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);
|
2026-03-29 15:12:38 +08:00
|
|
|
if (m_pipelineLayout != nullptr &&
|
|
|
|
|
m_pipelineState != nullptr &&
|
|
|
|
|
m_constantPool != nullptr &&
|
|
|
|
|
m_constantSet != nullptr &&
|
|
|
|
|
m_texturePool != nullptr &&
|
|
|
|
|
m_textureSet != nullptr &&
|
|
|
|
|
m_device == renderContext.device &&
|
2026-04-10 01:57:15 +08:00
|
|
|
m_backendType == renderContext.backendType &&
|
|
|
|
|
m_renderTargetFormat == renderTargetFormat &&
|
|
|
|
|
m_renderTargetSampleCount == renderTargetSampleCount) {
|
2026-03-29 15:12:38 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:45:51 +08:00
|
|
|
if (HasCreatedResources()) {
|
|
|
|
|
DestroyResources();
|
|
|
|
|
}
|
2026-04-10 01:57:15 +08:00
|
|
|
return CreateResources(renderContext, surface);
|
2026-03-29 15:12:38 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-10 01:57:15 +08:00
|
|
|
bool BuiltinObjectIdOutlinePass::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) {
|
2026-03-29 15:12:38 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-03 15:43:21 +08:00
|
|
|
if (m_shaderPath.Empty()) {
|
|
|
|
|
Debug::Logger::Get().Error(
|
|
|
|
|
Debug::LogCategory::Rendering,
|
|
|
|
|
"BuiltinObjectIdOutlinePass requires an injected shader path before resource creation");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:45:51 +08:00
|
|
|
Resources::ResourceHandle<Resources::Shader> shader = Resources::ResourceManager::Get().Load<Resources::Shader>(
|
2026-04-03 15:43:21 +08:00
|
|
|
m_shaderPath);
|
2026-04-02 20:45:51 +08:00
|
|
|
if (!shader.IsValid()) {
|
2026-04-02 20:18:39 +08:00
|
|
|
Debug::Logger::Get().Error(
|
|
|
|
|
Debug::LogCategory::Rendering,
|
2026-04-03 15:43:21 +08:00
|
|
|
"BuiltinObjectIdOutlinePass failed to load configured object-id-outline shader resource");
|
2026-04-02 20:45:51 +08:00
|
|
|
ResetState();
|
2026-04-02 20:18:39 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:45:51 +08:00
|
|
|
m_device = renderContext.device;
|
|
|
|
|
m_backendType = renderContext.backendType;
|
|
|
|
|
m_builtinObjectIdOutlineShader.emplace(std::move(shader));
|
|
|
|
|
|
2026-04-10 01:57:15 +08:00
|
|
|
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Internal::ToShaderBackend(m_backendType);
|
2026-04-02 20:18:39 +08:00
|
|
|
const Resources::ShaderPass* outlinePass =
|
2026-04-02 20:45:51 +08:00
|
|
|
FindObjectIdOutlineCompatiblePass(*m_builtinObjectIdOutlineShader->Get(), backend);
|
2026-04-02 20:18:39 +08:00
|
|
|
if (outlinePass == nullptr) {
|
|
|
|
|
Debug::Logger::Get().Error(
|
|
|
|
|
Debug::LogCategory::Rendering,
|
|
|
|
|
"BuiltinObjectIdOutlinePass could not resolve a valid ObjectIdOutline shader pass");
|
|
|
|
|
DestroyResources();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2026-03-29 15:12:38 +08:00
|
|
|
|
2026-04-01 17:33:07 +08:00
|
|
|
RHI::DescriptorSetLayoutBinding setBindings[2] = {};
|
2026-03-29 15:12:38 +08:00
|
|
|
setBindings[0].binding = 0;
|
|
|
|
|
setBindings[0].type = static_cast<uint32_t>(RHI::DescriptorType::CBV);
|
|
|
|
|
setBindings[0].count = 1;
|
|
|
|
|
setBindings[1].binding = 0;
|
|
|
|
|
setBindings[1].type = static_cast<uint32_t>(RHI::DescriptorType::SRV);
|
|
|
|
|
setBindings[1].count = 1;
|
|
|
|
|
|
|
|
|
|
RHI::DescriptorSetLayoutDesc constantLayout = {};
|
|
|
|
|
constantLayout.bindings = &setBindings[0];
|
|
|
|
|
constantLayout.bindingCount = 1;
|
|
|
|
|
|
|
|
|
|
RHI::DescriptorSetLayoutDesc textureLayout = {};
|
|
|
|
|
textureLayout.bindings = &setBindings[1];
|
|
|
|
|
textureLayout.bindingCount = 1;
|
|
|
|
|
|
2026-04-01 17:33:07 +08:00
|
|
|
RHI::DescriptorSetLayoutDesc setLayouts[2] = {};
|
2026-03-29 15:12:38 +08:00
|
|
|
setLayouts[0] = constantLayout;
|
|
|
|
|
setLayouts[1] = textureLayout;
|
|
|
|
|
|
|
|
|
|
RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {};
|
|
|
|
|
pipelineLayoutDesc.setLayouts = setLayouts;
|
2026-04-01 17:33:07 +08:00
|
|
|
pipelineLayoutDesc.setLayoutCount = 2;
|
2026-03-29 15:12:38 +08:00
|
|
|
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 = 1;
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:18:39 +08:00
|
|
|
m_pipelineState = m_device->CreatePipelineState(
|
|
|
|
|
CreatePipelineDesc(
|
2026-04-10 01:57:15 +08:00
|
|
|
m_backendType,
|
|
|
|
|
m_pipelineLayout,
|
|
|
|
|
*m_builtinObjectIdOutlineShader->Get(),
|
|
|
|
|
outlinePass->name,
|
|
|
|
|
surface));
|
2026-03-29 15:12:38 +08:00
|
|
|
if (m_pipelineState == nullptr || !m_pipelineState->IsValid()) {
|
|
|
|
|
DestroyResources();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 01:57:15 +08:00
|
|
|
m_renderTargetFormat = ::XCEngine::Rendering::Internal::ResolveSurfaceColorFormat(surface, 0u);
|
|
|
|
|
m_renderTargetSampleCount = ::XCEngine::Rendering::Internal::ResolveSurfaceSampleCount(surface);
|
|
|
|
|
|
2026-03-29 15:12:38 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:45:51 +08:00
|
|
|
bool BuiltinObjectIdOutlinePass::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_builtinObjectIdOutlineShader.has_value();
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-01 17:47:49 +08:00
|
|
|
void BuiltinObjectIdOutlinePass::DestroyResources() {
|
2026-03-29 15:12:38 +08:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 20:45:51 +08:00
|
|
|
if (m_builtinObjectIdOutlineShader.has_value()) {
|
|
|
|
|
m_builtinObjectIdOutlineShader.reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ResetState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BuiltinObjectIdOutlinePass::ResetState() {
|
2026-03-29 15:12:38 +08:00
|
|
|
m_device = nullptr;
|
|
|
|
|
m_backendType = RHI::RHIType::D3D12;
|
2026-04-02 20:45:51 +08:00
|
|
|
m_pipelineLayout = nullptr;
|
|
|
|
|
m_pipelineState = nullptr;
|
|
|
|
|
m_constantPool = nullptr;
|
|
|
|
|
m_constantSet = nullptr;
|
|
|
|
|
m_texturePool = nullptr;
|
|
|
|
|
m_textureSet = nullptr;
|
|
|
|
|
m_builtinObjectIdOutlineShader.reset();
|
2026-04-10 01:57:15 +08:00
|
|
|
m_renderTargetFormat = RHI::Format::Unknown;
|
|
|
|
|
m_renderTargetSampleCount = 1u;
|
2026-03-29 15:12:38 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-01 17:47:49 +08:00
|
|
|
} // namespace Passes
|
|
|
|
|
} // namespace Rendering
|
2026-03-29 15:12:38 +08:00
|
|
|
} // namespace XCEngine
|