Adopt binding plan for builtin object-id pass

This commit is contained in:
2026-04-03 17:05:38 +08:00
parent 5d24fd0337
commit 27014e613e
7 changed files with 132 additions and 9 deletions

View File

@@ -6,6 +6,15 @@
"tags": {
"LightMode": "ObjectId"
},
"resources": [
{
"name": "PerObjectConstants",
"type": "ConstantBuffer",
"set": 0,
"binding": 0,
"semantic": "PerObject"
}
],
"variants": [
{
"stage": "Vertex",

View File

@@ -2,6 +2,7 @@
#include <XCEngine/Rendering/ObjectIdPass.h>
#include <XCEngine/Rendering/ObjectIdEncoding.h>
#include <XCEngine/Rendering/RenderMaterialUtility.h>
#include <XCEngine/Rendering/RenderResourceCache.h>
#include <XCEngine/Core/Asset/ResourceHandle.h>
#include <XCEngine/RHI/RHIPipelineState.h>
@@ -54,6 +55,8 @@ private:
RHI::RHIType m_backendType = RHI::RHIType::D3D12;
RHI::RHIPipelineLayout* m_pipelineLayout = nullptr;
RHI::RHIPipelineState* m_pipelineState = nullptr;
PassResourceBindingLocation m_perObjectBinding = {};
Core::uint32 m_firstDescriptorSet = 0;
Resources::ResourceHandle<Resources::Shader> m_builtinObjectIdShader;
RenderResourceCache m_resourceCache;
std::unordered_map<uint64_t, OwnedDescriptorSet> m_perObjectSets;

View File

@@ -229,6 +229,19 @@ inline Containers::Array<Resources::ShaderResourceBindingDesc> BuildLegacyBuilti
return bindings;
}
inline Containers::Array<Resources::ShaderResourceBindingDesc> BuildLegacyBuiltinObjectIdPassResourceBindings() {
Containers::Array<Resources::ShaderResourceBindingDesc> bindings;
bindings.Resize(1);
bindings[0].name = "PerObjectConstants";
bindings[0].type = Resources::ShaderResourceType::ConstantBuffer;
bindings[0].set = 0;
bindings[0].binding = 0;
bindings[0].semantic = "PerObject";
return bindings;
}
inline bool IsBuiltinPassResourceTypeCompatible(
BuiltinPassResourceSemantic semantic,
Resources::ShaderResourceType type) {

View File

@@ -12,6 +12,7 @@
#include "Resources/Mesh/Mesh.h"
#include <cstddef>
#include <vector>
namespace XCEngine {
namespace Rendering {
@@ -222,18 +223,55 @@ bool BuiltinObjectIdPass::CreateResources(const RenderContext& context) {
return false;
}
Containers::Array<Resources::ShaderResourceBindingDesc> resourceBindings = objectIdPass->resources;
if (resourceBindings.Empty()) {
resourceBindings = BuildLegacyBuiltinObjectIdPassResourceBindings();
}
BuiltinPassResourceBindingPlan bindingPlan = {};
Containers::String bindingPlanError;
if (!TryBuildBuiltinPassResourceBindingPlan(resourceBindings, bindingPlan, &bindingPlanError)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String("BuiltinObjectIdPass failed to resolve pass resource bindings: ") + bindingPlanError).CStr());
DestroyResources();
return false;
}
if (!bindingPlan.perObject.IsValid() ||
bindingPlan.bindings.Size() != 1u ||
bindingPlan.descriptorSetCount != 1u ||
!bindingPlan.usesConstantBuffers ||
bindingPlan.usesTextures ||
bindingPlan.usesSamplers) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
"BuiltinObjectIdPass requires exactly one PerObject constant-buffer binding");
DestroyResources();
return false;
}
m_perObjectBinding = bindingPlan.perObject;
m_firstDescriptorSet = bindingPlan.firstDescriptorSet;
std::vector<std::vector<RHI::DescriptorSetLayoutBinding>> setBindingStorage(
static_cast<size_t>(bindingPlan.maxSetIndex) + 1u);
RHI::DescriptorSetLayoutBinding constantBinding = {};
constantBinding.binding = 0;
constantBinding.binding = m_perObjectBinding.binding;
constantBinding.type = static_cast<uint32_t>(RHI::DescriptorType::CBV);
constantBinding.count = 1;
setBindingStorage[m_perObjectBinding.set].push_back(constantBinding);
RHI::DescriptorSetLayoutDesc constantLayout = {};
constantLayout.bindings = &constantBinding;
constantLayout.bindingCount = 1;
std::vector<RHI::DescriptorSetLayoutDesc> setLayouts(setBindingStorage.size());
for (size_t setIndex = 0; setIndex < setBindingStorage.size(); ++setIndex) {
setLayouts[setIndex].bindings =
setBindingStorage[setIndex].empty() ? nullptr : setBindingStorage[setIndex].data();
setLayouts[setIndex].bindingCount = static_cast<uint32_t>(setBindingStorage[setIndex].size());
}
RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {};
pipelineLayoutDesc.setLayouts = &constantLayout;
pipelineLayoutDesc.setLayoutCount = 1;
pipelineLayoutDesc.setLayouts = setLayouts.empty() ? nullptr : setLayouts.data();
pipelineLayoutDesc.setLayoutCount = static_cast<uint32_t>(setLayouts.size());
m_pipelineLayout = m_device->CreatePipelineLayout(pipelineLayoutDesc);
if (m_pipelineLayout == nullptr) {
DestroyResources();
@@ -281,10 +319,16 @@ void BuiltinObjectIdPass::DestroyResources() {
m_device = nullptr;
m_backendType = RHI::RHIType::D3D12;
m_perObjectBinding = {};
m_firstDescriptorSet = 0;
m_builtinObjectIdShader.Reset();
}
RHI::RHIDescriptorSet* BuiltinObjectIdPass::GetOrCreatePerObjectSet(uint64_t objectId) {
if (m_perObjectBinding.IsValid() == false) {
return nullptr;
}
const auto existing = m_perObjectSets.find(objectId);
if (existing != m_perObjectSets.end()) {
return existing->second.set;
@@ -302,7 +346,7 @@ RHI::RHIDescriptorSet* BuiltinObjectIdPass::GetOrCreatePerObjectSet(uint64_t obj
}
RHI::DescriptorSetLayoutBinding binding = {};
binding.binding = 0;
binding.binding = m_perObjectBinding.binding;
binding.type = static_cast<uint32_t>(RHI::DescriptorType::CBV);
binding.count = 1;
@@ -369,10 +413,10 @@ bool BuiltinObjectIdPass::DrawVisibleItem(
visibleItem.localToWorld.Transpose(),
EncodeObjectIdToColor(objectId)
};
constantSet->WriteConstant(0, &constants, sizeof(constants));
constantSet->WriteConstant(m_perObjectBinding.binding, &constants, sizeof(constants));
RHI::RHIDescriptorSet* descriptorSets[] = { constantSet };
commandList->SetGraphicsDescriptorSets(0, 1, descriptorSets, m_pipelineLayout);
commandList->SetGraphicsDescriptorSets(m_firstDescriptorSet, 1, descriptorSets, m_pipelineLayout);
if (visibleItem.hasSection) {
const Containers::Array<Resources::MeshSection>& sections = visibleItem.mesh->GetSections();