feat(srp): add shader pass selection for scene draws

- let DrawingSettings carry an optional shaderPassName across managed and native scene draw APIs
- allow RenderObjectsRendererFeature to author explicit ForwardLit or Unlit scene draws
- exercise the new scene draw pass selection seam in the project render pipeline probe
This commit is contained in:
2026-04-20 22:58:08 +08:00
parent 0319680954
commit cd6f9aa4cf
9 changed files with 134 additions and 17 deletions

View File

@@ -1,6 +1,7 @@
#pragma once
#include <XCEngine/Core/Asset/ResourceHandle.h>
#include <XCEngine/Core/Containers/String.h>
#include <XCEngine/Rendering/Execution/ScenePhase.h>
#include <XCEngine/Rendering/FrameData/CullingResults.h>
#include <XCEngine/Resources/Material/Material.h>
@@ -12,10 +13,15 @@ struct DrawSettings {
ScenePhase scenePhase = ScenePhase::Opaque;
RendererListDesc rendererListDesc = {};
Resources::ResourceHandle<Resources::Material> overrideMaterial = {};
Containers::String shaderPassName = {};
bool HasOverrideMaterial() const {
return overrideMaterial.IsValid();
}
bool HasShaderPassName() const {
return !shaderPassName.Empty();
}
};
} // namespace Rendering

View File

@@ -297,10 +297,12 @@ private:
void DestroyPipelineResources();
static bool TryResolveSurfacePassType(
const Resources::Material* material,
const BuiltinMaterialPass* preferredPass,
BuiltinMaterialPass& outPass);
ResolvedShaderPass ResolveSurfaceShaderPass(
const RenderSceneData& sceneData,
const Resources::Material* material) const;
const Resources::Material* material,
const BuiltinMaterialPass* preferredPass) const;
PassResourceLayout* GetOrCreatePassResourceLayout(
const RenderContext& context,
const ResolvedShaderPass& resolvedShaderPass);
@@ -308,7 +310,8 @@ private:
const RenderContext& context,
const RenderSurface& surface,
const RenderSceneData& sceneData,
const Resources::Material* material);
const Resources::Material* material,
const BuiltinMaterialPass* preferredPass);
RHI::RHIPipelineState* GetOrCreateSkyboxPipelineState(
const RenderContext& context,
const RenderSurface& surface);
@@ -364,7 +367,8 @@ private:
bool DrawVisibleItem(
const FrameExecutionContext& executionContext,
const VisibleRenderItem& visibleItem,
const Resources::Material* material);
const Resources::Material* material,
const BuiltinMaterialPass* preferredPass);
bool EnsureSkyboxResources(const RenderContext& context);
bool CreateSkyboxResources(const RenderContext& context);
void DestroySkyboxResources();

View File

@@ -106,6 +106,32 @@ bool UsesDynamicSurfaceDescriptorSet(const BuiltinPassSetLayoutMetadata& setLayo
setLayout.usesMaterialBuffers;
}
bool TryResolveRequestedSurfacePassType(
const DrawSettings& drawSettings,
BuiltinMaterialPass& outPass,
bool& outHasRequestedPass) {
outHasRequestedPass = false;
if (!drawSettings.HasShaderPassName()) {
return true;
}
outHasRequestedPass = true;
const Containers::String normalizedPassName =
NormalizeBuiltinPassMetadataValue(drawSettings.shaderPassName);
if (normalizedPassName == Containers::String("forward") ||
MatchesBuiltinPassName(normalizedPassName, BuiltinMaterialPass::ForwardLit)) {
outPass = BuiltinMaterialPass::ForwardLit;
return true;
}
if (MatchesBuiltinPassName(normalizedPassName, BuiltinMaterialPass::Unlit)) {
outPass = BuiltinMaterialPass::Unlit;
return true;
}
return false;
}
} // namespace
RHI::InputLayoutDesc BuiltinForwardPipeline::BuildInputLayout() {
@@ -172,7 +198,17 @@ RHI::InputLayoutDesc BuiltinForwardPipeline::BuildInputLayout() {
bool BuiltinForwardPipeline::TryResolveSurfacePassType(
const Resources::Material* material,
const BuiltinMaterialPass* preferredPass,
BuiltinMaterialPass& outPass) {
if (preferredPass != nullptr) {
if (MatchesBuiltinPass(material, *preferredPass)) {
outPass = *preferredPass;
return true;
}
return false;
}
if (MatchesBuiltinPass(material, BuiltinMaterialPass::Unlit)) {
outPass = BuiltinMaterialPass::Unlit;
return true;
@@ -188,10 +224,14 @@ bool BuiltinForwardPipeline::TryResolveSurfacePassType(
BuiltinForwardPipeline::ResolvedShaderPass BuiltinForwardPipeline::ResolveSurfaceShaderPass(
const RenderSceneData& sceneData,
const Resources::Material* material) const {
const Resources::Material* material,
const BuiltinMaterialPass* preferredPass) const {
ResolvedShaderPass resolved = {};
BuiltinMaterialPass pass = BuiltinMaterialPass::ForwardLit;
if (!TryResolveSurfacePassType(material, pass)) {
BuiltinMaterialPass pass =
preferredPass != nullptr
? *preferredPass
: BuiltinMaterialPass::ForwardLit;
if (!TryResolveSurfacePassType(material, preferredPass, pass)) {
return resolved;
}
@@ -227,9 +267,14 @@ RHI::RHIPipelineState* BuiltinForwardPipeline::GetOrCreatePipelineState(
const RenderContext& context,
const RenderSurface& surface,
const RenderSceneData& sceneData,
const Resources::Material* material) {
const Resources::Material* material,
const BuiltinMaterialPass* preferredPass) {
const Resources::ShaderKeywordSet keywordSet = ResolvePassKeywordSet(sceneData, material);
const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(sceneData, material);
const ResolvedShaderPass resolvedShaderPass =
ResolveSurfaceShaderPass(
sceneData,
material,
preferredPass);
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
@@ -402,7 +447,8 @@ bool BuiltinForwardPipeline::ExecuteForwardTransparentPass(
bool BuiltinForwardPipeline::DrawVisibleItem(
const FrameExecutionContext& executionContext,
const VisibleRenderItem& visibleItem,
const Resources::Material* material) {
const Resources::Material* material,
const BuiltinMaterialPass* preferredPass) {
const RenderContext& context = executionContext.renderContext;
const RenderSceneData& sceneData = executionContext.sceneData;
@@ -435,7 +481,11 @@ bool BuiltinForwardPipeline::DrawVisibleItem(
shadowReceiverConstants.shadowSampling = sceneData.lighting.mainDirectionalShadow.sampling;
}
const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(sceneData, material);
const ResolvedShaderPass resolvedShaderPass =
ResolveSurfaceShaderPass(
sceneData,
material,
preferredPass);
if (resolvedShaderPass.shader == nullptr || resolvedShaderPass.pass == nullptr) {
return false;
}
@@ -578,6 +628,25 @@ bool BuiltinForwardPipeline::DrawVisibleItems(
RHI::RHICommandList* commandList = context.commandList;
RHI::RHIPipelineState* currentPipelineState = nullptr;
bool drawFailed = false;
BuiltinMaterialPass requestedPass = BuiltinMaterialPass::ForwardLit;
bool hasRequestedPass = false;
if (!TryResolveRequestedSurfacePassType(
drawSettings,
requestedPass,
hasRequestedPass)) {
Debug::Logger::Get().Error(
Debug::LogCategory::Rendering,
(Containers::String(
"BuiltinForwardPipeline only supports explicit shaderPassName values of ForwardLit or Unlit for scene draws, received: ") +
drawSettings.shaderPassName)
.CStr());
return false;
}
const BuiltinMaterialPass* preferredPass =
hasRequestedPass
? &requestedPass
: nullptr;
auto drawVisibleItem = [&](const VisibleRenderItem& visibleItem) {
if (drawFailed) {
@@ -588,12 +657,24 @@ bool BuiltinForwardPipeline::DrawVisibleItems(
drawSettings.HasOverrideMaterial()
? drawSettings.overrideMaterial.Get()
: ResolveMaterial(visibleItem);
BuiltinMaterialPass pass = BuiltinMaterialPass::ForwardLit;
if (!TryResolveSurfacePassType(material, pass)) {
BuiltinMaterialPass pass =
preferredPass != nullptr
? *preferredPass
: BuiltinMaterialPass::ForwardLit;
if (!TryResolveSurfacePassType(
material,
preferredPass,
pass)) {
return;
}
RHI::RHIPipelineState* pipelineState = GetOrCreatePipelineState(context, surface, sceneData, material);
RHI::RHIPipelineState* pipelineState =
GetOrCreatePipelineState(
context,
surface,
sceneData,
material,
preferredPass);
if (pipelineState == nullptr) {
drawFailed = true;
return;
@@ -603,7 +684,11 @@ bool BuiltinForwardPipeline::DrawVisibleItems(
currentPipelineState = pipelineState;
}
if (!DrawVisibleItem(executionContext, visibleItem, material)) {
if (!DrawVisibleItem(
executionContext,
visibleItem,
material,
preferredPass)) {
drawFailed = true;
}
};

View File

@@ -4592,7 +4592,8 @@ InternalCall_Rendering_ScriptableRenderContext_DrawRenderersByDesc(
uint64_t nativeHandle,
int32_t scenePhase,
ManagedRendererListDescData* rendererListDescData,
MonoString* overrideMaterialPath) {
MonoString* overrideMaterialPath,
MonoString* shaderPassName) {
ManagedScriptableRenderContextState* const state =
FindManagedScriptableRenderContextState(nativeHandle);
if (state == nullptr ||
@@ -4626,6 +4627,13 @@ InternalCall_Rendering_ScriptableRenderContext_DrawRenderersByDesc(
}
}
const std::string shaderPassNameUtf8 =
MonoStringToUtf8(shaderPassName);
if (!shaderPassNameUtf8.empty()) {
drawSettings.shaderPassName =
Containers::String(shaderPassNameUtf8.c_str());
}
return state->sceneRecorder->RecordSceneDrawSettings(
drawSettings)
? 1

View File

@@ -20,6 +20,7 @@ namespace XCEngine.Rendering.Universal
public bool overrideRenderLayerMask;
public uint renderLayerMask = uint.MaxValue;
public string overrideMaterialPath = string.Empty;
public string shaderPassName = string.Empty;
public bool overrideFilteringSettings;
public FilteringSettings filteringSettings;
public bool overrideSortingSettings;
@@ -111,6 +112,8 @@ namespace XCEngine.Rendering.Universal
DrawingSettings.CreateDefault();
drawingSettings.overrideMaterialPath =
overrideMaterialPath ?? string.Empty;
drawingSettings.shaderPassName =
shaderPassName ?? string.Empty;
return drawingSettings;
}
}

View File

@@ -782,7 +782,8 @@ namespace XCEngine
ulong nativeHandle,
int scenePhase,
ref Rendering.RendererListDesc rendererListDesc,
string overrideMaterialPath);
string overrideMaterialPath,
string shaderPassName);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool

View File

@@ -3,16 +3,22 @@ namespace XCEngine.Rendering
public struct DrawingSettings
{
public string overrideMaterialPath;
public string shaderPassName;
public bool hasOverrideMaterial =>
!string.IsNullOrEmpty(
overrideMaterialPath);
public bool hasShaderPassName =>
!string.IsNullOrEmpty(
shaderPassName);
public static DrawingSettings CreateDefault()
{
return new DrawingSettings
{
overrideMaterialPath = string.Empty
overrideMaterialPath = string.Empty,
shaderPassName = string.Empty
};
}
}

View File

@@ -107,6 +107,8 @@ namespace XCEngine.Rendering
(int)scenePhase,
ref rendererListDesc,
drawingSettings.overrideMaterialPath ??
string.Empty,
drawingSettings.shaderPassName ??
string.Empty);
}

View File

@@ -139,6 +139,8 @@ namespace ProjectScripts
RenderPassEvent.RenderOpaques,
scenePhase =
SceneRenderPhase.Opaque,
shaderPassName =
"Unlit",
overrideMaterialPath =
"Assets/New Material.mat",
overrideRenderQueueRange = true,