Split builtin forward pipeline resources
This commit is contained in:
@@ -462,6 +462,7 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/SceneRenderRequestPlanner.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/SceneRenderer.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/BuiltinForwardPipelineResources.cpp
|
||||
|
||||
# Scene
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/Scene.h
|
||||
|
||||
@@ -1,21 +1,12 @@
|
||||
#include "Rendering/Pipelines/BuiltinForwardPipeline.h"
|
||||
|
||||
#include "Components/GameObject.h"
|
||||
#include "Components/MeshFilterComponent.h"
|
||||
#include "Components/MeshRendererComponent.h"
|
||||
#include "Debug/Logger.h"
|
||||
#include "Core/Asset/ResourceManager.h"
|
||||
#include "RHI/RHICommandList.h"
|
||||
#include "Rendering/Detail/ShaderVariantUtils.h"
|
||||
#include "Rendering/RenderMaterialUtility.h"
|
||||
#include "Rendering/RenderSurface.h"
|
||||
#include "Resources/BuiltinResources.h"
|
||||
#include "Resources/Material/Material.h"
|
||||
#include "Resources/Shader/Shader.h"
|
||||
#include "Resources/Texture/Texture.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
|
||||
namespace XCEngine {
|
||||
@@ -53,9 +44,6 @@ private:
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr float kForwardAmbientIntensity = 0.28f;
|
||||
constexpr float kSpotInnerAngleRatio = 0.8f;
|
||||
|
||||
bool IsDepthFormat(RHI::Format format) {
|
||||
return format == RHI::Format::D24_UNorm_S8_UInt ||
|
||||
format == RHI::Format::D32_Float;
|
||||
@@ -77,82 +65,6 @@ bool TryResolveSurfacePassType(
|
||||
return false;
|
||||
}
|
||||
|
||||
const Resources::ShaderPass* FindCompatibleSurfacePass(
|
||||
const Resources::Shader& shader,
|
||||
const Resources::Material* material,
|
||||
BuiltinMaterialPass pass,
|
||||
Resources::ShaderBackend backend) {
|
||||
if (material != nullptr && !material->GetShaderPass().Empty()) {
|
||||
const Resources::ShaderPass* explicitPass = shader.FindPass(material->GetShaderPass());
|
||||
if (explicitPass != nullptr &&
|
||||
::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, explicitPass->name, backend)) {
|
||||
return explicitPass;
|
||||
}
|
||||
}
|
||||
|
||||
for (const Resources::ShaderPass& shaderPass : shader.GetPasses()) {
|
||||
if (ShaderPassMatchesBuiltinPass(shaderPass, pass) &&
|
||||
::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, shaderPass.name, backend)) {
|
||||
return &shaderPass;
|
||||
}
|
||||
}
|
||||
|
||||
if (pass != BuiltinMaterialPass::ForwardLit) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Resources::ShaderPass* defaultPass = shader.FindPass("ForwardLit");
|
||||
if (defaultPass != nullptr &&
|
||||
::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, defaultPass->name, backend)) {
|
||||
return defaultPass;
|
||||
}
|
||||
|
||||
defaultPass = shader.FindPass("Default");
|
||||
if (defaultPass != nullptr &&
|
||||
::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, defaultPass->name, backend)) {
|
||||
return defaultPass;
|
||||
}
|
||||
|
||||
if (shader.GetPassCount() > 0 &&
|
||||
::XCEngine::Rendering::Detail::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 Resources::Material* material) {
|
||||
RHI::GraphicsPipelineDesc pipelineDesc = {};
|
||||
pipelineDesc.pipelineLayout = pipelineLayout;
|
||||
pipelineDesc.topologyType = static_cast<uint32_t>(RHI::PrimitiveTopologyType::Triangle);
|
||||
pipelineDesc.renderTargetCount = 1;
|
||||
pipelineDesc.renderTargetFormats[0] = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
|
||||
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(RHI::Format::D24_UNorm_S8_UInt);
|
||||
pipelineDesc.sampleCount = 1;
|
||||
ApplyMaterialRenderState(material, pipelineDesc);
|
||||
|
||||
pipelineDesc.inputLayout = BuiltinForwardPipeline::BuildInputLayout();
|
||||
|
||||
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
|
||||
const Resources::ShaderStageVariant* vertexVariant =
|
||||
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend);
|
||||
const Resources::ShaderStageVariant* fragmentVariant =
|
||||
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend);
|
||||
if (vertexVariant != nullptr) {
|
||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader);
|
||||
}
|
||||
if (fragmentVariant != nullptr) {
|
||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader);
|
||||
}
|
||||
|
||||
return pipelineDesc;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BuiltinForwardPipeline::BuiltinForwardPipeline() {
|
||||
@@ -482,652 +394,6 @@ void BuiltinForwardPipeline::DestroyPipelineResources() {
|
||||
m_builtinUnlitShader.Reset();
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::ResolvedShaderPass BuiltinForwardPipeline::ResolveSurfaceShaderPass(
|
||||
const Resources::Material* material) const {
|
||||
ResolvedShaderPass resolved = {};
|
||||
BuiltinMaterialPass pass = BuiltinMaterialPass::ForwardLit;
|
||||
if (!TryResolveSurfacePassType(material, pass)) {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(m_backendType);
|
||||
|
||||
if (material != nullptr && material->GetShader() != nullptr) {
|
||||
const Resources::Shader* materialShader = material->GetShader();
|
||||
if (const Resources::ShaderPass* shaderPass =
|
||||
FindCompatibleSurfacePass(*materialShader, material, pass, backend)) {
|
||||
resolved.shader = materialShader;
|
||||
resolved.pass = shaderPass;
|
||||
resolved.passName = shaderPass->name;
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
|
||||
const Resources::ResourceHandle<Resources::Shader>* builtinShaderHandle =
|
||||
pass == BuiltinMaterialPass::Unlit ? &m_builtinUnlitShader : &m_builtinForwardShader;
|
||||
if (builtinShaderHandle->IsValid()) {
|
||||
const Resources::Shader* builtinShader = builtinShaderHandle->Get();
|
||||
if (const Resources::ShaderPass* shaderPass =
|
||||
FindCompatibleSurfacePass(*builtinShader, nullptr, pass, backend)) {
|
||||
resolved.shader = builtinShader;
|
||||
resolved.pass = shaderPass;
|
||||
resolved.passName = shaderPass->name;
|
||||
}
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::PassResourceLayout* BuiltinForwardPipeline::GetOrCreatePassResourceLayout(
|
||||
const RenderContext& context,
|
||||
const ResolvedShaderPass& resolvedShaderPass) {
|
||||
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PassLayoutKey passLayoutKey = {};
|
||||
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||
passLayoutKey.passName = resolvedShaderPass.passName;
|
||||
|
||||
const auto existing = m_passResourceLayouts.find(passLayoutKey);
|
||||
if (existing != m_passResourceLayouts.end()) {
|
||||
return &existing->second;
|
||||
}
|
||||
|
||||
PassResourceLayout passLayout = {};
|
||||
auto failLayout = [this, &passLayout](const char* message) -> PassResourceLayout* {
|
||||
Debug::Logger::Get().Error(Debug::LogCategory::Rendering, message);
|
||||
DestroyPassResourceLayout(passLayout);
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
const Containers::Array<Resources::ShaderResourceBindingDesc>& resourceBindings = resolvedShaderPass.pass->resources;
|
||||
if (resourceBindings.Empty()) {
|
||||
return failLayout("BuiltinForwardPipeline requires explicit resource bindings on the resolved shader pass");
|
||||
}
|
||||
|
||||
BuiltinPassResourceBindingPlan bindingPlan = {};
|
||||
Containers::String bindingPlanError;
|
||||
if (!TryBuildBuiltinPassResourceBindingPlan(resourceBindings, bindingPlan, &bindingPlanError)) {
|
||||
const Containers::String contextualError =
|
||||
Containers::String("BuiltinForwardPipeline failed to resolve pass resource bindings for shader='") +
|
||||
resolvedShaderPass.shader->GetPath() +
|
||||
"', pass='" + resolvedShaderPass.passName +
|
||||
"': " + bindingPlanError +
|
||||
". Bindings: " + DescribeShaderResourceBindings(resourceBindings);
|
||||
return failLayout(contextualError.CStr());
|
||||
}
|
||||
|
||||
const bool hasAnyResource = !bindingPlan.bindings.Empty();
|
||||
if (hasAnyResource) {
|
||||
Containers::String setLayoutError;
|
||||
if (!TryBuildBuiltinPassSetLayouts(bindingPlan, passLayout.setLayouts, &setLayoutError)) {
|
||||
return failLayout(setLayoutError.CStr());
|
||||
}
|
||||
passLayout.staticDescriptorSets.resize(passLayout.setLayouts.size());
|
||||
passLayout.firstDescriptorSet = bindingPlan.firstDescriptorSet;
|
||||
passLayout.descriptorSetCount = bindingPlan.descriptorSetCount;
|
||||
}
|
||||
|
||||
passLayout.perObject = bindingPlan.perObject;
|
||||
passLayout.material = bindingPlan.material;
|
||||
passLayout.lighting = bindingPlan.lighting;
|
||||
passLayout.shadowReceiver = bindingPlan.shadowReceiver;
|
||||
passLayout.baseColorTexture = bindingPlan.baseColorTexture;
|
||||
passLayout.linearClampSampler = bindingPlan.linearClampSampler;
|
||||
passLayout.shadowMapTexture = bindingPlan.shadowMapTexture;
|
||||
passLayout.shadowMapSampler = bindingPlan.shadowMapSampler;
|
||||
|
||||
if (!passLayout.perObject.IsValid()) {
|
||||
return failLayout("BuiltinForwardPipeline requires a PerObject resource binding");
|
||||
}
|
||||
if (ShaderPassMatchesBuiltinPass(*resolvedShaderPass.pass, BuiltinMaterialPass::ForwardLit) &&
|
||||
!passLayout.lighting.IsValid()) {
|
||||
return failLayout("BuiltinForwardPipeline forward-lit pass requires a Lighting resource binding");
|
||||
}
|
||||
|
||||
std::vector<RHI::DescriptorSetLayoutDesc> nativeSetLayouts(passLayout.setLayouts.size());
|
||||
for (size_t i = 0; i < passLayout.setLayouts.size(); ++i) {
|
||||
nativeSetLayouts[i] = passLayout.setLayouts[i].layout;
|
||||
}
|
||||
|
||||
RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {};
|
||||
pipelineLayoutDesc.setLayouts = nativeSetLayouts.empty() ? nullptr : nativeSetLayouts.data();
|
||||
pipelineLayoutDesc.setLayoutCount = static_cast<uint32_t>(nativeSetLayouts.size());
|
||||
passLayout.pipelineLayout = context.device->CreatePipelineLayout(pipelineLayoutDesc);
|
||||
if (passLayout.pipelineLayout == nullptr) {
|
||||
return failLayout("BuiltinForwardPipeline failed to create a pipeline layout from shader pass resources");
|
||||
}
|
||||
|
||||
const auto result = m_passResourceLayouts.emplace(passLayoutKey, passLayout);
|
||||
PassResourceLayout& storedPassLayout = result.first->second;
|
||||
RefreshBuiltinPassSetLayouts(storedPassLayout.setLayouts);
|
||||
return &storedPassLayout;
|
||||
}
|
||||
|
||||
RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState(
|
||||
const RenderContext& context,
|
||||
const Resources::Material* material) {
|
||||
const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material);
|
||||
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||
Debug::Logger::Get().Error(
|
||||
Debug::LogCategory::Rendering,
|
||||
"BuiltinForwardPipeline could not resolve a valid surface shader pass");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
||||
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PipelineStateKey pipelineKey = {};
|
||||
pipelineKey.renderState =
|
||||
material != nullptr ? material->GetRenderState() : Resources::MaterialRenderState();
|
||||
pipelineKey.shader = resolvedShaderPass.shader;
|
||||
pipelineKey.passName = resolvedShaderPass.passName;
|
||||
|
||||
const auto existing = m_pipelineStates.find(pipelineKey);
|
||||
if (existing != m_pipelineStates.end()) {
|
||||
return existing->second;
|
||||
}
|
||||
|
||||
const RHI::GraphicsPipelineDesc pipelineDesc =
|
||||
CreatePipelineDesc(
|
||||
context.backendType,
|
||||
passLayout->pipelineLayout,
|
||||
*resolvedShaderPass.shader,
|
||||
resolvedShaderPass.passName,
|
||||
material);
|
||||
RHI::RHIPipelineState* pipelineState = context.device->CreatePipelineState(pipelineDesc);
|
||||
if (pipelineState == nullptr || !pipelineState->IsValid()) {
|
||||
Debug::Logger::Get().Error(
|
||||
Debug::LogCategory::Rendering,
|
||||
"BuiltinForwardPipeline failed to create pipeline state");
|
||||
if (pipelineState != nullptr) {
|
||||
pipelineState->Shutdown();
|
||||
delete pipelineState;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_pipelineStates.emplace(pipelineKey, pipelineState);
|
||||
return pipelineState;
|
||||
}
|
||||
|
||||
bool BuiltinForwardPipeline::CreateOwnedDescriptorSet(
|
||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||
OwnedDescriptorSet& descriptorSet) {
|
||||
RHI::DescriptorPoolDesc poolDesc = {};
|
||||
poolDesc.type = setLayout.heapType;
|
||||
poolDesc.descriptorCount = CountBuiltinPassHeapDescriptors(setLayout.heapType, setLayout.bindings);
|
||||
poolDesc.shaderVisible = setLayout.shaderVisible;
|
||||
|
||||
descriptorSet.pool = m_device->CreateDescriptorPool(poolDesc);
|
||||
if (descriptorSet.pool == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
descriptorSet.set = descriptorSet.pool->AllocateSet(setLayout.layout);
|
||||
if (descriptorSet.set == nullptr) {
|
||||
DestroyOwnedDescriptorSet(descriptorSet);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RHI::RHIDescriptorSet* BuiltinForwardPipeline::GetOrCreateStaticDescriptorSet(
|
||||
PassResourceLayout& passLayout,
|
||||
Core::uint32 setIndex) {
|
||||
if (setIndex >= passLayout.setLayouts.size() ||
|
||||
setIndex >= passLayout.staticDescriptorSets.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OwnedDescriptorSet& descriptorSet = passLayout.staticDescriptorSets[setIndex];
|
||||
if (descriptorSet.set == nullptr) {
|
||||
if (!CreateOwnedDescriptorSet(passLayout.setLayouts[setIndex], descriptorSet)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (passLayout.setLayouts[setIndex].usesSampler) {
|
||||
RHI::RHISampler* sampler = nullptr;
|
||||
Core::uint32 binding = 0;
|
||||
|
||||
if (passLayout.setLayouts[setIndex].usesLinearClampSampler) {
|
||||
sampler = m_sampler;
|
||||
if (!passLayout.linearClampSampler.IsValid() ||
|
||||
passLayout.linearClampSampler.set != setIndex) {
|
||||
DestroyOwnedDescriptorSet(descriptorSet);
|
||||
return nullptr;
|
||||
}
|
||||
binding = passLayout.linearClampSampler.binding;
|
||||
} else if (passLayout.setLayouts[setIndex].usesShadowMapSampler) {
|
||||
sampler = m_shadowSampler;
|
||||
if (!passLayout.shadowMapSampler.IsValid() ||
|
||||
passLayout.shadowMapSampler.set != setIndex) {
|
||||
DestroyOwnedDescriptorSet(descriptorSet);
|
||||
return nullptr;
|
||||
}
|
||||
binding = passLayout.shadowMapSampler.binding;
|
||||
}
|
||||
|
||||
if (sampler == nullptr) {
|
||||
DestroyOwnedDescriptorSet(descriptorSet);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
descriptorSet.set->UpdateSampler(binding, sampler);
|
||||
}
|
||||
}
|
||||
|
||||
return descriptorSet.set;
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::CachedDescriptorSet* BuiltinForwardPipeline::GetOrCreateDynamicDescriptorSet(
|
||||
const PassLayoutKey& passLayoutKey,
|
||||
const PassResourceLayout& passLayout,
|
||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||
Core::uint32 setIndex,
|
||||
Core::uint64 objectId,
|
||||
const Resources::Material* material,
|
||||
const MaterialConstantPayloadView& materialConstants,
|
||||
const LightingConstants& lightingConstants,
|
||||
const ShadowReceiverConstants& shadowReceiverConstants,
|
||||
RHI::RHIResourceView* baseColorTextureView,
|
||||
RHI::RHIResourceView* shadowMapTextureView) {
|
||||
DynamicDescriptorSetKey key = {};
|
||||
key.passLayout = passLayoutKey;
|
||||
key.setIndex = setIndex;
|
||||
key.objectId = objectId;
|
||||
key.material = material;
|
||||
|
||||
CachedDescriptorSet& cachedDescriptorSet = m_dynamicDescriptorSets[key];
|
||||
if (cachedDescriptorSet.descriptorSet.set == nullptr) {
|
||||
if (!CreateOwnedDescriptorSet(setLayout, cachedDescriptorSet.descriptorSet)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0;
|
||||
if (setLayout.usesMaterial) {
|
||||
if (!passLayout.material.IsValid() || passLayout.material.set != setIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!materialConstants.IsValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (cachedDescriptorSet.materialVersion != materialVersion) {
|
||||
cachedDescriptorSet.descriptorSet.set->WriteConstant(
|
||||
passLayout.material.binding,
|
||||
materialConstants.data,
|
||||
materialConstants.size);
|
||||
}
|
||||
}
|
||||
|
||||
if (setLayout.usesLighting) {
|
||||
if (!passLayout.lighting.IsValid() || passLayout.lighting.set != setIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
cachedDescriptorSet.descriptorSet.set->WriteConstant(
|
||||
passLayout.lighting.binding,
|
||||
&lightingConstants,
|
||||
sizeof(lightingConstants));
|
||||
}
|
||||
|
||||
if (setLayout.usesShadowReceiver) {
|
||||
if (!passLayout.shadowReceiver.IsValid() || passLayout.shadowReceiver.set != setIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
cachedDescriptorSet.descriptorSet.set->WriteConstant(
|
||||
passLayout.shadowReceiver.binding,
|
||||
&shadowReceiverConstants,
|
||||
sizeof(shadowReceiverConstants));
|
||||
}
|
||||
|
||||
if (setLayout.usesBaseColorTexture) {
|
||||
if (baseColorTextureView == nullptr ||
|
||||
!passLayout.baseColorTexture.IsValid() ||
|
||||
passLayout.baseColorTexture.set != setIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
if (cachedDescriptorSet.baseColorTextureView != baseColorTextureView) {
|
||||
cachedDescriptorSet.descriptorSet.set->Update(
|
||||
passLayout.baseColorTexture.binding,
|
||||
baseColorTextureView);
|
||||
}
|
||||
}
|
||||
|
||||
if (setLayout.usesShadowMapTexture) {
|
||||
if (shadowMapTextureView == nullptr ||
|
||||
!passLayout.shadowMapTexture.IsValid() ||
|
||||
passLayout.shadowMapTexture.set != setIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
if (cachedDescriptorSet.shadowMapTextureView != shadowMapTextureView) {
|
||||
cachedDescriptorSet.descriptorSet.set->Update(
|
||||
passLayout.shadowMapTexture.binding,
|
||||
shadowMapTextureView);
|
||||
}
|
||||
}
|
||||
|
||||
cachedDescriptorSet.materialVersion = materialVersion;
|
||||
cachedDescriptorSet.baseColorTextureView = baseColorTextureView;
|
||||
cachedDescriptorSet.shadowMapTextureView = shadowMapTextureView;
|
||||
|
||||
return &cachedDescriptorSet;
|
||||
}
|
||||
|
||||
void BuiltinForwardPipeline::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;
|
||||
}
|
||||
}
|
||||
|
||||
void BuiltinForwardPipeline::DestroyPassResourceLayout(PassResourceLayout& passLayout) {
|
||||
for (OwnedDescriptorSet& descriptorSet : passLayout.staticDescriptorSets) {
|
||||
DestroyOwnedDescriptorSet(descriptorSet);
|
||||
}
|
||||
passLayout.staticDescriptorSets.clear();
|
||||
|
||||
if (passLayout.pipelineLayout != nullptr) {
|
||||
passLayout.pipelineLayout->Shutdown();
|
||||
delete passLayout.pipelineLayout;
|
||||
passLayout.pipelineLayout = nullptr;
|
||||
}
|
||||
|
||||
passLayout.setLayouts.clear();
|
||||
passLayout.firstDescriptorSet = 0;
|
||||
passLayout.descriptorSetCount = 0;
|
||||
passLayout.perObject = {};
|
||||
passLayout.material = {};
|
||||
passLayout.lighting = {};
|
||||
passLayout.shadowReceiver = {};
|
||||
passLayout.baseColorTexture = {};
|
||||
passLayout.linearClampSampler = {};
|
||||
passLayout.shadowMapTexture = {};
|
||||
passLayout.shadowMapSampler = {};
|
||||
}
|
||||
|
||||
const Resources::Texture* BuiltinForwardPipeline::ResolveTexture(const Resources::Material* material) const {
|
||||
return ResolveBuiltinBaseColorTexture(material);
|
||||
}
|
||||
|
||||
RHI::RHIResourceView* BuiltinForwardPipeline::ResolveTextureView(
|
||||
const VisibleRenderItem& visibleItem) {
|
||||
const Resources::Material* material = ResolveMaterial(visibleItem);
|
||||
const Resources::Texture* texture = ResolveTexture(material);
|
||||
if (texture != nullptr) {
|
||||
const RenderResourceCache::CachedTexture* cachedTexture = m_resourceCache.GetOrCreateTexture(m_device, texture);
|
||||
if (cachedTexture != nullptr && cachedTexture->shaderResourceView != nullptr) {
|
||||
return cachedTexture->shaderResourceView;
|
||||
}
|
||||
}
|
||||
|
||||
return m_fallbackTextureView;
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::LightingConstants BuiltinForwardPipeline::BuildLightingConstants(
|
||||
const RenderLightingData& lightingData) {
|
||||
LightingConstants lightingConstants = {};
|
||||
|
||||
if (lightingData.HasMainDirectionalLight()) {
|
||||
lightingConstants.mainLightDirectionAndIntensity = Math::Vector4(
|
||||
lightingData.mainDirectionalLight.direction.x,
|
||||
lightingData.mainDirectionalLight.direction.y,
|
||||
lightingData.mainDirectionalLight.direction.z,
|
||||
lightingData.mainDirectionalLight.intensity);
|
||||
lightingConstants.mainLightColorAndFlags = Math::Vector4(
|
||||
lightingData.mainDirectionalLight.color.r,
|
||||
lightingData.mainDirectionalLight.color.g,
|
||||
lightingData.mainDirectionalLight.color.b,
|
||||
1.0f);
|
||||
}
|
||||
|
||||
const Core::uint32 additionalLightCount = std::min<Core::uint32>(
|
||||
lightingData.additionalLightCount,
|
||||
kMaxLightingAdditionalLightCount);
|
||||
lightingConstants.lightingParams = Math::Vector4(
|
||||
static_cast<float>(additionalLightCount),
|
||||
kForwardAmbientIntensity,
|
||||
0.0f,
|
||||
0.0f);
|
||||
|
||||
for (Core::uint32 index = 0; index < additionalLightCount; ++index) {
|
||||
lightingConstants.additionalLights[index] =
|
||||
BuildAdditionalLightConstants(lightingData.additionalLights[index]);
|
||||
}
|
||||
|
||||
return lightingConstants;
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::AdditionalLightConstants BuiltinForwardPipeline::BuildAdditionalLightConstants(
|
||||
const RenderAdditionalLightData& lightData) {
|
||||
AdditionalLightConstants constants = {};
|
||||
constants.colorAndIntensity = Math::Vector4(
|
||||
lightData.color.r,
|
||||
lightData.color.g,
|
||||
lightData.color.b,
|
||||
lightData.intensity);
|
||||
constants.positionAndRange = Math::Vector4(
|
||||
lightData.position.x,
|
||||
lightData.position.y,
|
||||
lightData.position.z,
|
||||
lightData.range);
|
||||
constants.directionAndType = Math::Vector4(
|
||||
lightData.direction.x,
|
||||
lightData.direction.y,
|
||||
lightData.direction.z,
|
||||
static_cast<float>(lightData.type));
|
||||
|
||||
if (lightData.type == RenderLightType::Spot) {
|
||||
const float outerHalfAngleRadians = Math::Radians(lightData.spotAngle * 0.5f);
|
||||
const float innerHalfAngleRadians = outerHalfAngleRadians * kSpotInnerAngleRatio;
|
||||
constants.spotAnglesAndFlags = Math::Vector4(
|
||||
std::cos(outerHalfAngleRadians),
|
||||
std::cos(innerHalfAngleRadians),
|
||||
lightData.castsShadows ? 1.0f : 0.0f,
|
||||
0.0f);
|
||||
} else {
|
||||
constants.spotAnglesAndFlags = Math::Vector4(
|
||||
0.0f,
|
||||
0.0f,
|
||||
lightData.castsShadows ? 1.0f : 0.0f,
|
||||
0.0f);
|
||||
}
|
||||
|
||||
return constants;
|
||||
}
|
||||
|
||||
bool BuiltinForwardPipeline::DrawVisibleItem(
|
||||
const RenderContext& context,
|
||||
const RenderSceneData& sceneData,
|
||||
const VisibleRenderItem& visibleItem) {
|
||||
const RenderResourceCache::CachedMesh* cachedMesh = m_resourceCache.GetOrCreateMesh(m_device, visibleItem.mesh);
|
||||
if (cachedMesh == nullptr || cachedMesh->vertexBufferView == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::RHICommandList* commandList = context.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 = {
|
||||
sceneData.cameraData.projection,
|
||||
sceneData.cameraData.view,
|
||||
visibleItem.localToWorld.Transpose(),
|
||||
visibleItem.localToWorld.Inverse()
|
||||
};
|
||||
const LightingConstants lightingConstants = BuildLightingConstants(sceneData.lighting);
|
||||
const ShadowReceiverConstants shadowReceiverConstants = {
|
||||
sceneData.lighting.HasMainDirectionalShadow()
|
||||
? sceneData.lighting.mainDirectionalShadow.viewProjection
|
||||
: Math::Matrix4x4::Identity(),
|
||||
sceneData.lighting.HasMainDirectionalShadow()
|
||||
? sceneData.lighting.mainDirectionalShadow.shadowParams
|
||||
: Math::Vector4::Zero(),
|
||||
sceneData.lighting.HasMainDirectionalShadow()
|
||||
? Math::Vector4(1.0f, 0.0f, 0.0f, 0.0f)
|
||||
: Math::Vector4::Zero()
|
||||
};
|
||||
|
||||
const Resources::Material* material = ResolveMaterial(visibleItem);
|
||||
const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material);
|
||||
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PassLayoutKey passLayoutKey = {};
|
||||
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||
passLayoutKey.passName = resolvedShaderPass.passName;
|
||||
|
||||
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
||||
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::RHIResourceView* baseColorTextureView = ResolveTextureView(visibleItem);
|
||||
if (passLayout->baseColorTexture.IsValid() && baseColorTextureView == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::RHIResourceView* shadowMapTextureView = sceneData.lighting.HasMainDirectionalShadow()
|
||||
? sceneData.lighting.mainDirectionalShadow.shadowMap
|
||||
: m_fallbackTextureView;
|
||||
if (passLayout->shadowMapTexture.IsValid() && shadowMapTextureView == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MaterialConstantPayloadView materialConstants = ResolveSchemaMaterialConstantPayload(material);
|
||||
FallbackPerMaterialConstants fallbackMaterialConstants = {};
|
||||
if (!materialConstants.IsValid()) {
|
||||
const BuiltinForwardMaterialData materialData = BuildBuiltinForwardMaterialData(material);
|
||||
fallbackMaterialConstants.baseColorFactor = materialData.baseColorFactor;
|
||||
static const Resources::MaterialConstantFieldDesc kFallbackMaterialConstantField = {
|
||||
Containers::String("baseColorFactor"),
|
||||
Resources::MaterialPropertyType::Float4,
|
||||
0,
|
||||
sizeof(FallbackPerMaterialConstants),
|
||||
sizeof(FallbackPerMaterialConstants)
|
||||
};
|
||||
materialConstants.data = &fallbackMaterialConstants;
|
||||
materialConstants.size = sizeof(fallbackMaterialConstants);
|
||||
materialConstants.layout = {
|
||||
&kFallbackMaterialConstantField,
|
||||
1,
|
||||
sizeof(fallbackMaterialConstants)
|
||||
};
|
||||
}
|
||||
|
||||
if (passLayout->descriptorSetCount > 0) {
|
||||
std::vector<RHI::RHIDescriptorSet*> descriptorSets(passLayout->descriptorSetCount, nullptr);
|
||||
|
||||
for (Core::uint32 descriptorOffset = 0; descriptorOffset < passLayout->descriptorSetCount; ++descriptorOffset) {
|
||||
const Core::uint32 setIndex = passLayout->firstDescriptorSet + descriptorOffset;
|
||||
if (setIndex >= passLayout->setLayouts.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
|
||||
RHI::RHIDescriptorSet* descriptorSet = nullptr;
|
||||
|
||||
if (setLayout.usesPerObject ||
|
||||
setLayout.usesLighting ||
|
||||
setLayout.usesMaterial ||
|
||||
setLayout.usesShadowReceiver ||
|
||||
setLayout.usesTexture) {
|
||||
const Core::uint64 objectId =
|
||||
(setLayout.usesPerObject && visibleItem.gameObject != nullptr)
|
||||
? visibleItem.gameObject->GetID()
|
||||
: 0;
|
||||
const Resources::Material* materialKey =
|
||||
(setLayout.usesMaterial || setLayout.usesBaseColorTexture) ? material : nullptr;
|
||||
|
||||
CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet(
|
||||
passLayoutKey,
|
||||
*passLayout,
|
||||
setLayout,
|
||||
setIndex,
|
||||
objectId,
|
||||
materialKey,
|
||||
materialConstants,
|
||||
lightingConstants,
|
||||
shadowReceiverConstants,
|
||||
baseColorTextureView,
|
||||
shadowMapTextureView);
|
||||
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
descriptorSet = cachedDescriptorSet->descriptorSet.set;
|
||||
if (setLayout.usesPerObject) {
|
||||
if (!passLayout->perObject.IsValid() || passLayout->perObject.set != setIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
descriptorSet->WriteConstant(
|
||||
passLayout->perObject.binding,
|
||||
&constants,
|
||||
sizeof(constants));
|
||||
}
|
||||
} else {
|
||||
descriptorSet = GetOrCreateStaticDescriptorSet(*passLayout, setIndex);
|
||||
if (descriptorSet == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
descriptorSets[descriptorOffset] = descriptorSet;
|
||||
}
|
||||
|
||||
commandList->SetGraphicsDescriptorSets(
|
||||
passLayout->firstDescriptorSet,
|
||||
passLayout->descriptorSetCount,
|
||||
descriptorSets.data(),
|
||||
passLayout->pipelineLayout);
|
||||
}
|
||||
|
||||
if (visibleItem.hasSection) {
|
||||
const Containers::Array<Resources::MeshSection>& sections = visibleItem.mesh->GetSections();
|
||||
if (visibleItem.sectionIndex >= sections.Size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Resources::MeshSection& section = sections[visibleItem.sectionIndex];
|
||||
if (cachedMesh->indexBufferView != nullptr && section.indexCount > 0) {
|
||||
// MeshLoader flattens section indices into a single global index buffer.
|
||||
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 Pipelines
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
|
||||
@@ -0,0 +1,766 @@
|
||||
#include "Rendering/Pipelines/BuiltinForwardPipeline.h"
|
||||
|
||||
#include "Components/GameObject.h"
|
||||
#include "Debug/Logger.h"
|
||||
#include "RHI/RHICommandList.h"
|
||||
#include "RHI/RHIDevice.h"
|
||||
#include "Rendering/Detail/ShaderVariantUtils.h"
|
||||
#include "Rendering/RenderMaterialUtility.h"
|
||||
#include "Resources/Material/Material.h"
|
||||
#include "Resources/Shader/Shader.h"
|
||||
#include "Resources/Texture/Texture.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
namespace Pipelines {
|
||||
namespace {
|
||||
|
||||
constexpr float kForwardAmbientIntensity = 0.28f;
|
||||
constexpr float kSpotInnerAngleRatio = 0.8f;
|
||||
|
||||
bool TryResolveSurfacePassType(
|
||||
const Resources::Material* material,
|
||||
BuiltinMaterialPass& outPass) {
|
||||
if (MatchesBuiltinPass(material, BuiltinMaterialPass::Unlit)) {
|
||||
outPass = BuiltinMaterialPass::Unlit;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (MatchesBuiltinPass(material, BuiltinMaterialPass::ForwardLit)) {
|
||||
outPass = BuiltinMaterialPass::ForwardLit;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const Resources::ShaderPass* FindCompatibleSurfacePass(
|
||||
const Resources::Shader& shader,
|
||||
const Resources::Material* material,
|
||||
BuiltinMaterialPass pass,
|
||||
Resources::ShaderBackend backend) {
|
||||
if (material != nullptr && !material->GetShaderPass().Empty()) {
|
||||
const Resources::ShaderPass* explicitPass = shader.FindPass(material->GetShaderPass());
|
||||
if (explicitPass != nullptr &&
|
||||
::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, explicitPass->name, backend)) {
|
||||
return explicitPass;
|
||||
}
|
||||
}
|
||||
|
||||
for (const Resources::ShaderPass& shaderPass : shader.GetPasses()) {
|
||||
if (ShaderPassMatchesBuiltinPass(shaderPass, pass) &&
|
||||
::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, shaderPass.name, backend)) {
|
||||
return &shaderPass;
|
||||
}
|
||||
}
|
||||
|
||||
if (pass != BuiltinMaterialPass::ForwardLit) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Resources::ShaderPass* defaultPass = shader.FindPass("ForwardLit");
|
||||
if (defaultPass != nullptr &&
|
||||
::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, defaultPass->name, backend)) {
|
||||
return defaultPass;
|
||||
}
|
||||
|
||||
defaultPass = shader.FindPass("Default");
|
||||
if (defaultPass != nullptr &&
|
||||
::XCEngine::Rendering::Detail::ShaderPassHasGraphicsVariants(shader, defaultPass->name, backend)) {
|
||||
return defaultPass;
|
||||
}
|
||||
|
||||
if (shader.GetPassCount() > 0 &&
|
||||
::XCEngine::Rendering::Detail::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 Resources::Material* material) {
|
||||
RHI::GraphicsPipelineDesc pipelineDesc = {};
|
||||
pipelineDesc.pipelineLayout = pipelineLayout;
|
||||
pipelineDesc.topologyType = static_cast<uint32_t>(RHI::PrimitiveTopologyType::Triangle);
|
||||
pipelineDesc.renderTargetCount = 1;
|
||||
pipelineDesc.renderTargetFormats[0] = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
|
||||
pipelineDesc.depthStencilFormat = static_cast<uint32_t>(RHI::Format::D24_UNorm_S8_UInt);
|
||||
pipelineDesc.sampleCount = 1;
|
||||
ApplyMaterialRenderState(material, pipelineDesc);
|
||||
|
||||
pipelineDesc.inputLayout = BuiltinForwardPipeline::BuildInputLayout();
|
||||
|
||||
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(backendType);
|
||||
const Resources::ShaderStageVariant* vertexVariant =
|
||||
shader.FindVariant(passName, Resources::ShaderType::Vertex, backend);
|
||||
const Resources::ShaderStageVariant* fragmentVariant =
|
||||
shader.FindVariant(passName, Resources::ShaderType::Fragment, backend);
|
||||
if (vertexVariant != nullptr) {
|
||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*vertexVariant, pipelineDesc.vertexShader);
|
||||
}
|
||||
if (fragmentVariant != nullptr) {
|
||||
::XCEngine::Rendering::Detail::ApplyShaderStageVariant(*fragmentVariant, pipelineDesc.fragmentShader);
|
||||
}
|
||||
|
||||
return pipelineDesc;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BuiltinForwardPipeline::ResolvedShaderPass BuiltinForwardPipeline::ResolveSurfaceShaderPass(
|
||||
const Resources::Material* material) const {
|
||||
ResolvedShaderPass resolved = {};
|
||||
BuiltinMaterialPass pass = BuiltinMaterialPass::ForwardLit;
|
||||
if (!TryResolveSurfacePassType(material, pass)) {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
const Resources::ShaderBackend backend = ::XCEngine::Rendering::Detail::ToShaderBackend(m_backendType);
|
||||
|
||||
if (material != nullptr && material->GetShader() != nullptr) {
|
||||
const Resources::Shader* materialShader = material->GetShader();
|
||||
if (const Resources::ShaderPass* shaderPass =
|
||||
FindCompatibleSurfacePass(*materialShader, material, pass, backend)) {
|
||||
resolved.shader = materialShader;
|
||||
resolved.pass = shaderPass;
|
||||
resolved.passName = shaderPass->name;
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
|
||||
const Resources::ResourceHandle<Resources::Shader>* builtinShaderHandle =
|
||||
pass == BuiltinMaterialPass::Unlit ? &m_builtinUnlitShader : &m_builtinForwardShader;
|
||||
if (builtinShaderHandle->IsValid()) {
|
||||
const Resources::Shader* builtinShader = builtinShaderHandle->Get();
|
||||
if (const Resources::ShaderPass* shaderPass =
|
||||
FindCompatibleSurfacePass(*builtinShader, nullptr, pass, backend)) {
|
||||
resolved.shader = builtinShader;
|
||||
resolved.pass = shaderPass;
|
||||
resolved.passName = shaderPass->name;
|
||||
}
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::PassResourceLayout* BuiltinForwardPipeline::GetOrCreatePassResourceLayout(
|
||||
const RenderContext& context,
|
||||
const ResolvedShaderPass& resolvedShaderPass) {
|
||||
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PassLayoutKey passLayoutKey = {};
|
||||
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||
passLayoutKey.passName = resolvedShaderPass.passName;
|
||||
|
||||
const auto existing = m_passResourceLayouts.find(passLayoutKey);
|
||||
if (existing != m_passResourceLayouts.end()) {
|
||||
return &existing->second;
|
||||
}
|
||||
|
||||
PassResourceLayout passLayout = {};
|
||||
auto failLayout = [this, &passLayout](const char* message) -> PassResourceLayout* {
|
||||
Debug::Logger::Get().Error(Debug::LogCategory::Rendering, message);
|
||||
DestroyPassResourceLayout(passLayout);
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
const Containers::Array<Resources::ShaderResourceBindingDesc>& resourceBindings = resolvedShaderPass.pass->resources;
|
||||
if (resourceBindings.Empty()) {
|
||||
return failLayout("BuiltinForwardPipeline requires explicit resource bindings on the resolved shader pass");
|
||||
}
|
||||
|
||||
BuiltinPassResourceBindingPlan bindingPlan = {};
|
||||
Containers::String bindingPlanError;
|
||||
if (!TryBuildBuiltinPassResourceBindingPlan(resourceBindings, bindingPlan, &bindingPlanError)) {
|
||||
const Containers::String contextualError =
|
||||
Containers::String("BuiltinForwardPipeline failed to resolve pass resource bindings for shader='") +
|
||||
resolvedShaderPass.shader->GetPath() +
|
||||
"', pass='" + resolvedShaderPass.passName +
|
||||
"': " + bindingPlanError +
|
||||
". Bindings: " + DescribeShaderResourceBindings(resourceBindings);
|
||||
return failLayout(contextualError.CStr());
|
||||
}
|
||||
|
||||
const bool hasAnyResource = !bindingPlan.bindings.Empty();
|
||||
if (hasAnyResource) {
|
||||
Containers::String setLayoutError;
|
||||
if (!TryBuildBuiltinPassSetLayouts(bindingPlan, passLayout.setLayouts, &setLayoutError)) {
|
||||
return failLayout(setLayoutError.CStr());
|
||||
}
|
||||
passLayout.staticDescriptorSets.resize(passLayout.setLayouts.size());
|
||||
passLayout.firstDescriptorSet = bindingPlan.firstDescriptorSet;
|
||||
passLayout.descriptorSetCount = bindingPlan.descriptorSetCount;
|
||||
}
|
||||
|
||||
passLayout.perObject = bindingPlan.perObject;
|
||||
passLayout.material = bindingPlan.material;
|
||||
passLayout.lighting = bindingPlan.lighting;
|
||||
passLayout.shadowReceiver = bindingPlan.shadowReceiver;
|
||||
passLayout.baseColorTexture = bindingPlan.baseColorTexture;
|
||||
passLayout.linearClampSampler = bindingPlan.linearClampSampler;
|
||||
passLayout.shadowMapTexture = bindingPlan.shadowMapTexture;
|
||||
passLayout.shadowMapSampler = bindingPlan.shadowMapSampler;
|
||||
|
||||
if (!passLayout.perObject.IsValid()) {
|
||||
return failLayout("BuiltinForwardPipeline requires a PerObject resource binding");
|
||||
}
|
||||
if (ShaderPassMatchesBuiltinPass(*resolvedShaderPass.pass, BuiltinMaterialPass::ForwardLit) &&
|
||||
!passLayout.lighting.IsValid()) {
|
||||
return failLayout("BuiltinForwardPipeline forward-lit pass requires a Lighting resource binding");
|
||||
}
|
||||
|
||||
std::vector<RHI::DescriptorSetLayoutDesc> nativeSetLayouts(passLayout.setLayouts.size());
|
||||
for (size_t i = 0; i < passLayout.setLayouts.size(); ++i) {
|
||||
nativeSetLayouts[i] = passLayout.setLayouts[i].layout;
|
||||
}
|
||||
|
||||
RHI::RHIPipelineLayoutDesc pipelineLayoutDesc = {};
|
||||
pipelineLayoutDesc.setLayouts = nativeSetLayouts.empty() ? nullptr : nativeSetLayouts.data();
|
||||
pipelineLayoutDesc.setLayoutCount = static_cast<uint32_t>(nativeSetLayouts.size());
|
||||
passLayout.pipelineLayout = context.device->CreatePipelineLayout(pipelineLayoutDesc);
|
||||
if (passLayout.pipelineLayout == nullptr) {
|
||||
return failLayout("BuiltinForwardPipeline failed to create a pipeline layout from shader pass resources");
|
||||
}
|
||||
|
||||
const auto result = m_passResourceLayouts.emplace(passLayoutKey, passLayout);
|
||||
PassResourceLayout& storedPassLayout = result.first->second;
|
||||
RefreshBuiltinPassSetLayouts(storedPassLayout.setLayouts);
|
||||
return &storedPassLayout;
|
||||
}
|
||||
|
||||
RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState(
|
||||
const RenderContext& context,
|
||||
const Resources::Material* material) {
|
||||
const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material);
|
||||
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||
Debug::Logger::Get().Error(
|
||||
Debug::LogCategory::Rendering,
|
||||
"BuiltinForwardPipeline could not resolve a valid surface shader pass");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
||||
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PipelineStateKey pipelineKey = {};
|
||||
pipelineKey.renderState =
|
||||
material != nullptr ? material->GetRenderState() : Resources::MaterialRenderState();
|
||||
pipelineKey.shader = resolvedShaderPass.shader;
|
||||
pipelineKey.passName = resolvedShaderPass.passName;
|
||||
|
||||
const auto existing = m_pipelineStates.find(pipelineKey);
|
||||
if (existing != m_pipelineStates.end()) {
|
||||
return existing->second;
|
||||
}
|
||||
|
||||
const RHI::GraphicsPipelineDesc pipelineDesc =
|
||||
CreatePipelineDesc(
|
||||
context.backendType,
|
||||
passLayout->pipelineLayout,
|
||||
*resolvedShaderPass.shader,
|
||||
resolvedShaderPass.passName,
|
||||
material);
|
||||
RHI::RHIPipelineState* pipelineState = context.device->CreatePipelineState(pipelineDesc);
|
||||
if (pipelineState == nullptr || !pipelineState->IsValid()) {
|
||||
Debug::Logger::Get().Error(
|
||||
Debug::LogCategory::Rendering,
|
||||
"BuiltinForwardPipeline failed to create pipeline state");
|
||||
if (pipelineState != nullptr) {
|
||||
pipelineState->Shutdown();
|
||||
delete pipelineState;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_pipelineStates.emplace(pipelineKey, pipelineState);
|
||||
return pipelineState;
|
||||
}
|
||||
|
||||
bool BuiltinForwardPipeline::CreateOwnedDescriptorSet(
|
||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||
OwnedDescriptorSet& descriptorSet) {
|
||||
RHI::DescriptorPoolDesc poolDesc = {};
|
||||
poolDesc.type = setLayout.heapType;
|
||||
poolDesc.descriptorCount = CountBuiltinPassHeapDescriptors(setLayout.heapType, setLayout.bindings);
|
||||
poolDesc.shaderVisible = setLayout.shaderVisible;
|
||||
|
||||
descriptorSet.pool = m_device->CreateDescriptorPool(poolDesc);
|
||||
if (descriptorSet.pool == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
descriptorSet.set = descriptorSet.pool->AllocateSet(setLayout.layout);
|
||||
if (descriptorSet.set == nullptr) {
|
||||
DestroyOwnedDescriptorSet(descriptorSet);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RHI::RHIDescriptorSet* BuiltinForwardPipeline::GetOrCreateStaticDescriptorSet(
|
||||
PassResourceLayout& passLayout,
|
||||
Core::uint32 setIndex) {
|
||||
if (setIndex >= passLayout.setLayouts.size() ||
|
||||
setIndex >= passLayout.staticDescriptorSets.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OwnedDescriptorSet& descriptorSet = passLayout.staticDescriptorSets[setIndex];
|
||||
if (descriptorSet.set == nullptr) {
|
||||
if (!CreateOwnedDescriptorSet(passLayout.setLayouts[setIndex], descriptorSet)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (passLayout.setLayouts[setIndex].usesSampler) {
|
||||
RHI::RHISampler* sampler = nullptr;
|
||||
Core::uint32 binding = 0;
|
||||
|
||||
if (passLayout.setLayouts[setIndex].usesLinearClampSampler) {
|
||||
sampler = m_sampler;
|
||||
if (!passLayout.linearClampSampler.IsValid() ||
|
||||
passLayout.linearClampSampler.set != setIndex) {
|
||||
DestroyOwnedDescriptorSet(descriptorSet);
|
||||
return nullptr;
|
||||
}
|
||||
binding = passLayout.linearClampSampler.binding;
|
||||
} else if (passLayout.setLayouts[setIndex].usesShadowMapSampler) {
|
||||
sampler = m_shadowSampler;
|
||||
if (!passLayout.shadowMapSampler.IsValid() ||
|
||||
passLayout.shadowMapSampler.set != setIndex) {
|
||||
DestroyOwnedDescriptorSet(descriptorSet);
|
||||
return nullptr;
|
||||
}
|
||||
binding = passLayout.shadowMapSampler.binding;
|
||||
}
|
||||
|
||||
if (sampler == nullptr) {
|
||||
DestroyOwnedDescriptorSet(descriptorSet);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
descriptorSet.set->UpdateSampler(binding, sampler);
|
||||
}
|
||||
}
|
||||
|
||||
return descriptorSet.set;
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::CachedDescriptorSet* BuiltinForwardPipeline::GetOrCreateDynamicDescriptorSet(
|
||||
const PassLayoutKey& passLayoutKey,
|
||||
const PassResourceLayout& passLayout,
|
||||
const BuiltinPassSetLayoutMetadata& setLayout,
|
||||
Core::uint32 setIndex,
|
||||
Core::uint64 objectId,
|
||||
const Resources::Material* material,
|
||||
const MaterialConstantPayloadView& materialConstants,
|
||||
const LightingConstants& lightingConstants,
|
||||
const ShadowReceiverConstants& shadowReceiverConstants,
|
||||
RHI::RHIResourceView* baseColorTextureView,
|
||||
RHI::RHIResourceView* shadowMapTextureView) {
|
||||
DynamicDescriptorSetKey key = {};
|
||||
key.passLayout = passLayoutKey;
|
||||
key.setIndex = setIndex;
|
||||
key.objectId = objectId;
|
||||
key.material = material;
|
||||
|
||||
CachedDescriptorSet& cachedDescriptorSet = m_dynamicDescriptorSets[key];
|
||||
if (cachedDescriptorSet.descriptorSet.set == nullptr) {
|
||||
if (!CreateOwnedDescriptorSet(setLayout, cachedDescriptorSet.descriptorSet)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0;
|
||||
if (setLayout.usesMaterial) {
|
||||
if (!passLayout.material.IsValid() || passLayout.material.set != setIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!materialConstants.IsValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (cachedDescriptorSet.materialVersion != materialVersion) {
|
||||
cachedDescriptorSet.descriptorSet.set->WriteConstant(
|
||||
passLayout.material.binding,
|
||||
materialConstants.data,
|
||||
materialConstants.size);
|
||||
}
|
||||
}
|
||||
|
||||
if (setLayout.usesLighting) {
|
||||
if (!passLayout.lighting.IsValid() || passLayout.lighting.set != setIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
cachedDescriptorSet.descriptorSet.set->WriteConstant(
|
||||
passLayout.lighting.binding,
|
||||
&lightingConstants,
|
||||
sizeof(lightingConstants));
|
||||
}
|
||||
|
||||
if (setLayout.usesShadowReceiver) {
|
||||
if (!passLayout.shadowReceiver.IsValid() || passLayout.shadowReceiver.set != setIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
cachedDescriptorSet.descriptorSet.set->WriteConstant(
|
||||
passLayout.shadowReceiver.binding,
|
||||
&shadowReceiverConstants,
|
||||
sizeof(shadowReceiverConstants));
|
||||
}
|
||||
|
||||
if (setLayout.usesBaseColorTexture) {
|
||||
if (baseColorTextureView == nullptr ||
|
||||
!passLayout.baseColorTexture.IsValid() ||
|
||||
passLayout.baseColorTexture.set != setIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
if (cachedDescriptorSet.baseColorTextureView != baseColorTextureView) {
|
||||
cachedDescriptorSet.descriptorSet.set->Update(
|
||||
passLayout.baseColorTexture.binding,
|
||||
baseColorTextureView);
|
||||
}
|
||||
}
|
||||
|
||||
if (setLayout.usesShadowMapTexture) {
|
||||
if (shadowMapTextureView == nullptr ||
|
||||
!passLayout.shadowMapTexture.IsValid() ||
|
||||
passLayout.shadowMapTexture.set != setIndex) {
|
||||
return nullptr;
|
||||
}
|
||||
if (cachedDescriptorSet.shadowMapTextureView != shadowMapTextureView) {
|
||||
cachedDescriptorSet.descriptorSet.set->Update(
|
||||
passLayout.shadowMapTexture.binding,
|
||||
shadowMapTextureView);
|
||||
}
|
||||
}
|
||||
|
||||
cachedDescriptorSet.materialVersion = materialVersion;
|
||||
cachedDescriptorSet.baseColorTextureView = baseColorTextureView;
|
||||
cachedDescriptorSet.shadowMapTextureView = shadowMapTextureView;
|
||||
|
||||
return &cachedDescriptorSet;
|
||||
}
|
||||
|
||||
void BuiltinForwardPipeline::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;
|
||||
}
|
||||
}
|
||||
|
||||
void BuiltinForwardPipeline::DestroyPassResourceLayout(PassResourceLayout& passLayout) {
|
||||
for (OwnedDescriptorSet& descriptorSet : passLayout.staticDescriptorSets) {
|
||||
DestroyOwnedDescriptorSet(descriptorSet);
|
||||
}
|
||||
passLayout.staticDescriptorSets.clear();
|
||||
|
||||
if (passLayout.pipelineLayout != nullptr) {
|
||||
passLayout.pipelineLayout->Shutdown();
|
||||
delete passLayout.pipelineLayout;
|
||||
passLayout.pipelineLayout = nullptr;
|
||||
}
|
||||
|
||||
passLayout.setLayouts.clear();
|
||||
passLayout.firstDescriptorSet = 0;
|
||||
passLayout.descriptorSetCount = 0;
|
||||
passLayout.perObject = {};
|
||||
passLayout.material = {};
|
||||
passLayout.lighting = {};
|
||||
passLayout.shadowReceiver = {};
|
||||
passLayout.baseColorTexture = {};
|
||||
passLayout.linearClampSampler = {};
|
||||
passLayout.shadowMapTexture = {};
|
||||
passLayout.shadowMapSampler = {};
|
||||
}
|
||||
|
||||
const Resources::Texture* BuiltinForwardPipeline::ResolveTexture(const Resources::Material* material) const {
|
||||
return ResolveBuiltinBaseColorTexture(material);
|
||||
}
|
||||
|
||||
RHI::RHIResourceView* BuiltinForwardPipeline::ResolveTextureView(
|
||||
const VisibleRenderItem& visibleItem) {
|
||||
const Resources::Material* material = ResolveMaterial(visibleItem);
|
||||
const Resources::Texture* texture = ResolveTexture(material);
|
||||
if (texture != nullptr) {
|
||||
const RenderResourceCache::CachedTexture* cachedTexture = m_resourceCache.GetOrCreateTexture(m_device, texture);
|
||||
if (cachedTexture != nullptr && cachedTexture->shaderResourceView != nullptr) {
|
||||
return cachedTexture->shaderResourceView;
|
||||
}
|
||||
}
|
||||
|
||||
return m_fallbackTextureView;
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::LightingConstants BuiltinForwardPipeline::BuildLightingConstants(
|
||||
const RenderLightingData& lightingData) {
|
||||
LightingConstants lightingConstants = {};
|
||||
|
||||
if (lightingData.HasMainDirectionalLight()) {
|
||||
lightingConstants.mainLightDirectionAndIntensity = Math::Vector4(
|
||||
lightingData.mainDirectionalLight.direction.x,
|
||||
lightingData.mainDirectionalLight.direction.y,
|
||||
lightingData.mainDirectionalLight.direction.z,
|
||||
lightingData.mainDirectionalLight.intensity);
|
||||
lightingConstants.mainLightColorAndFlags = Math::Vector4(
|
||||
lightingData.mainDirectionalLight.color.r,
|
||||
lightingData.mainDirectionalLight.color.g,
|
||||
lightingData.mainDirectionalLight.color.b,
|
||||
1.0f);
|
||||
}
|
||||
|
||||
const Core::uint32 additionalLightCount = std::min<Core::uint32>(
|
||||
lightingData.additionalLightCount,
|
||||
kMaxLightingAdditionalLightCount);
|
||||
lightingConstants.lightingParams = Math::Vector4(
|
||||
static_cast<float>(additionalLightCount),
|
||||
kForwardAmbientIntensity,
|
||||
0.0f,
|
||||
0.0f);
|
||||
|
||||
for (Core::uint32 index = 0; index < additionalLightCount; ++index) {
|
||||
lightingConstants.additionalLights[index] =
|
||||
BuildAdditionalLightConstants(lightingData.additionalLights[index]);
|
||||
}
|
||||
|
||||
return lightingConstants;
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::AdditionalLightConstants BuiltinForwardPipeline::BuildAdditionalLightConstants(
|
||||
const RenderAdditionalLightData& lightData) {
|
||||
AdditionalLightConstants constants = {};
|
||||
constants.colorAndIntensity = Math::Vector4(
|
||||
lightData.color.r,
|
||||
lightData.color.g,
|
||||
lightData.color.b,
|
||||
lightData.intensity);
|
||||
constants.positionAndRange = Math::Vector4(
|
||||
lightData.position.x,
|
||||
lightData.position.y,
|
||||
lightData.position.z,
|
||||
lightData.range);
|
||||
constants.directionAndType = Math::Vector4(
|
||||
lightData.direction.x,
|
||||
lightData.direction.y,
|
||||
lightData.direction.z,
|
||||
static_cast<float>(lightData.type));
|
||||
|
||||
if (lightData.type == RenderLightType::Spot) {
|
||||
const float outerHalfAngleRadians = Math::Radians(lightData.spotAngle * 0.5f);
|
||||
const float innerHalfAngleRadians = outerHalfAngleRadians * kSpotInnerAngleRatio;
|
||||
constants.spotAnglesAndFlags = Math::Vector4(
|
||||
std::cos(outerHalfAngleRadians),
|
||||
std::cos(innerHalfAngleRadians),
|
||||
lightData.castsShadows ? 1.0f : 0.0f,
|
||||
0.0f);
|
||||
} else {
|
||||
constants.spotAnglesAndFlags = Math::Vector4(
|
||||
0.0f,
|
||||
0.0f,
|
||||
lightData.castsShadows ? 1.0f : 0.0f,
|
||||
0.0f);
|
||||
}
|
||||
|
||||
return constants;
|
||||
}
|
||||
|
||||
bool BuiltinForwardPipeline::DrawVisibleItem(
|
||||
const RenderContext& context,
|
||||
const RenderSceneData& sceneData,
|
||||
const VisibleRenderItem& visibleItem) {
|
||||
const RenderResourceCache::CachedMesh* cachedMesh = m_resourceCache.GetOrCreateMesh(m_device, visibleItem.mesh);
|
||||
if (cachedMesh == nullptr || cachedMesh->vertexBufferView == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::RHICommandList* commandList = context.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 = {
|
||||
sceneData.cameraData.projection,
|
||||
sceneData.cameraData.view,
|
||||
visibleItem.localToWorld.Transpose(),
|
||||
visibleItem.localToWorld.Inverse()
|
||||
};
|
||||
const LightingConstants lightingConstants = BuildLightingConstants(sceneData.lighting);
|
||||
const ShadowReceiverConstants shadowReceiverConstants = {
|
||||
sceneData.lighting.HasMainDirectionalShadow()
|
||||
? sceneData.lighting.mainDirectionalShadow.viewProjection
|
||||
: Math::Matrix4x4::Identity(),
|
||||
sceneData.lighting.HasMainDirectionalShadow()
|
||||
? sceneData.lighting.mainDirectionalShadow.shadowParams
|
||||
: Math::Vector4::Zero(),
|
||||
sceneData.lighting.HasMainDirectionalShadow()
|
||||
? Math::Vector4(1.0f, 0.0f, 0.0f, 0.0f)
|
||||
: Math::Vector4::Zero()
|
||||
};
|
||||
|
||||
const Resources::Material* material = ResolveMaterial(visibleItem);
|
||||
const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material);
|
||||
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PassLayoutKey passLayoutKey = {};
|
||||
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||
passLayoutKey.passName = resolvedShaderPass.passName;
|
||||
|
||||
PassResourceLayout* passLayout = GetOrCreatePassResourceLayout(context, resolvedShaderPass);
|
||||
if (passLayout == nullptr || passLayout->pipelineLayout == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::RHIResourceView* baseColorTextureView = ResolveTextureView(visibleItem);
|
||||
if (passLayout->baseColorTexture.IsValid() && baseColorTextureView == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::RHIResourceView* shadowMapTextureView = sceneData.lighting.HasMainDirectionalShadow()
|
||||
? sceneData.lighting.mainDirectionalShadow.shadowMap
|
||||
: m_fallbackTextureView;
|
||||
if (passLayout->shadowMapTexture.IsValid() && shadowMapTextureView == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MaterialConstantPayloadView materialConstants = ResolveSchemaMaterialConstantPayload(material);
|
||||
FallbackPerMaterialConstants fallbackMaterialConstants = {};
|
||||
if (!materialConstants.IsValid()) {
|
||||
const BuiltinForwardMaterialData materialData = BuildBuiltinForwardMaterialData(material);
|
||||
fallbackMaterialConstants.baseColorFactor = materialData.baseColorFactor;
|
||||
static const Resources::MaterialConstantFieldDesc kFallbackMaterialConstantField = {
|
||||
Containers::String("baseColorFactor"),
|
||||
Resources::MaterialPropertyType::Float4,
|
||||
0,
|
||||
sizeof(FallbackPerMaterialConstants),
|
||||
sizeof(FallbackPerMaterialConstants)
|
||||
};
|
||||
materialConstants.data = &fallbackMaterialConstants;
|
||||
materialConstants.size = sizeof(fallbackMaterialConstants);
|
||||
materialConstants.layout = {
|
||||
&kFallbackMaterialConstantField,
|
||||
1,
|
||||
sizeof(fallbackMaterialConstants)
|
||||
};
|
||||
}
|
||||
|
||||
if (passLayout->descriptorSetCount > 0) {
|
||||
std::vector<RHI::RHIDescriptorSet*> descriptorSets(passLayout->descriptorSetCount, nullptr);
|
||||
|
||||
for (Core::uint32 descriptorOffset = 0; descriptorOffset < passLayout->descriptorSetCount; ++descriptorOffset) {
|
||||
const Core::uint32 setIndex = passLayout->firstDescriptorSet + descriptorOffset;
|
||||
if (setIndex >= passLayout->setLayouts.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
|
||||
RHI::RHIDescriptorSet* descriptorSet = nullptr;
|
||||
|
||||
if (setLayout.usesPerObject ||
|
||||
setLayout.usesLighting ||
|
||||
setLayout.usesMaterial ||
|
||||
setLayout.usesShadowReceiver ||
|
||||
setLayout.usesTexture) {
|
||||
const Core::uint64 objectId =
|
||||
(setLayout.usesPerObject && visibleItem.gameObject != nullptr)
|
||||
? visibleItem.gameObject->GetID()
|
||||
: 0;
|
||||
const Resources::Material* materialKey =
|
||||
(setLayout.usesMaterial || setLayout.usesBaseColorTexture) ? material : nullptr;
|
||||
|
||||
CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet(
|
||||
passLayoutKey,
|
||||
*passLayout,
|
||||
setLayout,
|
||||
setIndex,
|
||||
objectId,
|
||||
materialKey,
|
||||
materialConstants,
|
||||
lightingConstants,
|
||||
shadowReceiverConstants,
|
||||
baseColorTextureView,
|
||||
shadowMapTextureView);
|
||||
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
descriptorSet = cachedDescriptorSet->descriptorSet.set;
|
||||
if (setLayout.usesPerObject) {
|
||||
if (!passLayout->perObject.IsValid() || passLayout->perObject.set != setIndex) {
|
||||
return false;
|
||||
}
|
||||
|
||||
descriptorSet->WriteConstant(
|
||||
passLayout->perObject.binding,
|
||||
&constants,
|
||||
sizeof(constants));
|
||||
}
|
||||
} else {
|
||||
descriptorSet = GetOrCreateStaticDescriptorSet(*passLayout, setIndex);
|
||||
if (descriptorSet == nullptr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
descriptorSets[descriptorOffset] = descriptorSet;
|
||||
}
|
||||
|
||||
commandList->SetGraphicsDescriptorSets(
|
||||
passLayout->firstDescriptorSet,
|
||||
passLayout->descriptorSetCount,
|
||||
descriptorSets.data(),
|
||||
passLayout->pipelineLayout);
|
||||
}
|
||||
|
||||
if (visibleItem.hasSection) {
|
||||
const Containers::Array<Resources::MeshSection>& sections = visibleItem.mesh->GetSections();
|
||||
if (visibleItem.sectionIndex >= sections.Size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Resources::MeshSection& section = sections[visibleItem.sectionIndex];
|
||||
if (cachedMesh->indexBufferView != nullptr && section.indexCount > 0) {
|
||||
// MeshLoader flattens section indices into a single global index buffer.
|
||||
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 Pipelines
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
Reference in New Issue
Block a user