26 KiB
渲染模块 Agent 指南
本文是修改 engine/include/XCEngine/Rendering、engine/src/Rendering,以及
managed/ 下配套 managed SRP/URP 表面的工作地图。它不能替代阅读实现代码。
如果本文和代码冲突,以代码为准,并在同一次改动里更新本文。
【未来目标】
方向
渲染器有意参考 Unity SRP 和 URP 设计。后续工作在概念适配本引擎时,应优先采用 Unity 兼容的公开命名、对象所有权和扩展点。
- 保持 managed 公开表面接近 Unity:
GraphicsSettings.renderPipelineAsset、RenderPipelineAsset、ScriptableRenderPipelineAsset、ScriptableRenderPipeline、ScriptableRenderContext、UniversalRenderPipelineAsset、UniversalRendererData、ScriptableRenderer、ScriptableRendererFeature、ScriptableRenderPass、RenderPassEvent、ContextContainer和RenderGraph。 - 在发明新的公开词汇前,优先对齐行为。如果某个 Unity 概念在这里只实现了一部分, 要诚实暴露已实现的子集,把缺失行为留在内部代码或测试里。
- 保留 Unity 风格的
RenderPassEvent数值顺序。新增或移动事件时,必须一起更新ScriptableRenderPass.TryResolveRendererBlock、RendererBlocks、probes 和测试。 - 管线自定义优先使用 managed URP features 和 passes。Native 路径应提供后端执行、 场景提取、fallback passes 和工具支持。
- 渲染器代码保持后端无关。D3D12、OpenGL 和 Vulkan 细节属于
engine/include/XCEngine/RHI/**、engine/src/RHI/**或 editor host rendering, 不应进入 SRP/URP 策略层。
近期目标
当前优先级是完成 SRP/URP 架构切分,并在每条边界上补目标明确的测试。近期产品目标 是让 URP 成为渲染决策层,让 native rendering 成为执行层。
- 将
BuiltinForwardPipeline从策略中心降级为默认 native scene draw backend。它可以 继续作为 backend/fallback 实现存在,但不应决定 URP pass 顺序、feature 参与方式或 stage 策略。 - 让
ScriptableRenderer及其 active pass queue 成为 URP stage planning 和 recording 的权威。 Opaque、skybox、transparent、depth、shadow、post 和 final-output 流程应来自 URP blocks 和 passes。 - 保持
DrawObjectsPass和DrawSkyboxPass作为 managed URP pass declarations,并通过 renderer-list/native scene-recorder backends 执行。 - 收紧 stage 支持规则。如果 fullscreen 或 main-scene stage 需要 managed graph recording, 必须要求所选 renderer/pipeline 支持并记录该 stage。
- 只在 Unity 形态的 API 后面扩展 managed RenderGraph:resource declarations、frame data、 renderer-list drawing、pass data 和 command recording。避免暴露 native 私有 graph 或 scene-recorder 细节。
- 在切分架构时补 contract tests:URP selection、renderer data selection、renderer override、 pass event ordering、renderer feature invalidation、stage planning、RenderGraph recording, 以及 no hidden built-in fallback。
- 等 SRP/URP 所有权边界稳定后,再做大范围测试体系重组。过早重组有可能固化当前的过渡行为。
修改规则
- 行为变更要从 pipeline asset/request/plan 路径开始。app/editor 代码中的临时 pass execution 通常表示层级位置不对。
- Native headers 保持小而后端无关。可行时 forward declare RHI 类型,只在后端代码里 include 具体后端头。
- 新增 camera-stack、renderer-index、shadow、depth、post-process 或 final output 行为时,不要绕过
CameraFramePlanBuilder或SceneRenderRequestPlanner。 - 不要在 native 长生命周期对象里保存 raw managed object pointers 或 managed context handles。 需要持久化时,通过现有 external managed object handle 系统 retain。
- Managed URP public APIs 不要依赖
CameraFrameGraph或NativeSceneRecorder这类 native 实现名。 - Dirty/invalidation 路径必须显式。
ScriptableRendererFeature.SetActive、 featureComputeRuntimeStateHash、renderer data feature collection hashes 和 asset runtime resource versions 必须保持一致。 - 修改 renderer-index 行为时,要一起更新
CameraRenderRequest.rendererIndex、stage support contexts、RendererRecordingContext、managed probes 和 Mono bridge calls。 - 修改 URP stage planning、stage support 或 recording 行为时,要保持
CameraFramePlan.framePlanId、RenderPipelineStageSupportContext.framePlanId、RenderPipelineStageRenderGraphContext.framePlanId、ScriptableRenderPipelinePlanningContext.framePlanId和ScriptableRenderContext.framePlanId同步。framePlanId是 per-camera-frame plan 的托管快照键,不是 renderer 或 camera 的长期身份。 - 新增 public managed rendering type 时,将它加入
managed/CMakeLists.txt,并在对应 managed probe 里覆盖 API presence。
【当下事实】
架构基线
这些事实是当前架构基线。除非有明确的架构切分和测试,不要削弱它们。
- SRP/URP 架构已经阶段性收口到可以继续扩展。公开对象模型、managed asset selection、 URP renderer data/features/passes、native frame planning、stage recording 和 RenderGraph execution 现在组成了一条路径。
- 这还不是完整 Unity URP 兼容。公开形态有意接近 Unity,但执行 feature set 仍小于 Unity URP。
UniversalRenderPipelineAsset可以被选为 active managed SRP asset,所以用户可以把渲染路由到 URP package。把它视为受支持能力,但仍处于过渡状态。- 当前 URP package 还不是强意义上对 built-in renderer 的完整替代。URP 拥有 selection、planning、 renderer feature organization、pass ordering 和 stage recording;native code 仍提供 scene extraction、 renderer-list realization、RenderGraph execution、RHI resources 和默认 scene draw backend。
- Native execution 本身不是问题。Unity 也通过 native engine 执行。风险在于 native code 继续保留 built-in forward pipeline policy,而不是作为 URP-declared work 的 backend executor。
- 目标所有权模型是:URP 决定渲染什么、何时渲染、哪些 renderer lists/passes/features 激活,以及 哪些 stages 存在;native 只执行这些声明,不夹带 built-in pipeline policy。
- Hidden fallback 很危险。如果 managed URP stage 声明支持但无法 record,失败必须可见。不要静默用 default built-in path 画同一个 stage,然后称之为 URP。
- Camera-frame graph dispatch 必须询问所选 pipeline 是否允许 legacy stage fallback。Managed
ScriptableRenderPipelineHost在 stage recorder 或 managed runtime 权威时不允许 fallback;此时缺少 recorder、sequence 或 standalone pass 要在 graph recording 阶段失败,而不是排入 built-in fallback。 - 测试体系已经有有价值的覆盖,但还不足以宣称 SRP/URP stack 完全锁定。随着架构边界收口,应补高价值 contract tests;现阶段不要为了大范围测试体系重写而暂停架构工作。
管线选择
GraphicsSettings.renderPipelineAsset == null表示 renderer 使用 engine default native pipeline selection。GraphicsSettings.renderPipelineAsset != null表示 renderer 使用 render-pipeline asset reference selection path。AssetRef是长期选择身份;managed descriptor/handle 只是运行时 materialization cache 和过渡 fallback。- Runtime startup 不应静默指定 project default SRP asset。Project 或 editor policy 可以显式选择一个,
但 active rendering mode 必须能通过
GraphicsSettings.renderPipelineAsset观察到。 GraphicsSettingsState同时保存 configured render-pipeline assetAssetRef和ManagedRenderPipelineAssetDescriptorruntime cache。只要AssetRef存在,它就是选择根;后续 managed materialization 只能更新 descriptor cache,不得清掉或替换该 asset reference。- 如果 managed asset 无法创建有效 pipeline、recorder 或 backend bridge,应显式失败,并让 top-level factory
选择 fallback path。不要让
ScriptableRenderPipelineHost看起来像成功的 managed SRP,同时在背后用 hidden native path 渲染不受支持的 managed stage。 - 保持已删除的 public bridge types 不再出现:
RendererBackedRenderPipelineAsset、RendererBackedRenderPipeline和RendererDrivenRenderPipeline。
当前形态
Native renderer 现在是 camera frame planner 加 RenderGraph executor,managed SRP/URP recording 叠在 native scene draw backend 之上。
SceneRenderer是 scene-level convenience entry point。它通过SceneRenderRequestPlanner收集 camera requests,通过RenderPipelineHost构建 frame plans,然后渲染排序后的 camera plans。RenderPipelineHost拥有一个CameraFramePlanBuilder和一个CameraRenderer。它是从CameraRenderRequest到CameraFramePlan的常规 native bridge。CameraRenderer拥有所选RenderPipeline,提取RenderSceneData,解析 directional shadow execution, 把全部 frame stages 记录进 nativeRenderGraph,编译 graph 并执行。RenderPipelineAsset是 native asset contract。它创建 pipeline,配置 request policy,配置 frame plan policy,并提供默认 final color settings。RenderPipeline、RenderPipelineBackend和RenderPipelineStageRecorder将 backend rendering 与 graph recording 分开。Managed SRP 通常提供 stage recorder,native backend 提供 scene drawing。
帧阶段
CameraFrameStage 是权威 frame-stage model。保持所有 native 和 managed stage enum 同步。
- 当前有序 stages 是
PreScenePasses、ShadowCaster、DepthOnly、MainScene、PostProcess、FinalOutput、ObjectId、PostScenePasses和OverlayPasses。 ShadowCaster、DepthOnly和ObjectId是 standalone-pass stages。MainScene是主 pipeline stage。PostProcess和FinalOutput是 fullscreen sequence stages;没有 legacyRenderPassSequence时, 它们可以由 pipeline 进行 graph recording。- 使用
CameraFramePlanAPIs,例如RequestFullscreenStage、ClearFullscreenStage、RequestShadowCasterStage和RequestDepthOnlyStage。除非在 plan implementation 内部,否则不要直接 改 plan stage fields。 - Graph-managed color flow 是显式的。
CameraFrameColorSource、UsesGraphManagedSceneColor和UsesGraphManagedOutputColor决定 stage 是读取 main scene、post process 还是 explicit surface。 - 新增 stage 时,更新
CameraFrameStage.h、managedCameraFrameStage.cs、CameraFramePlan、 camera-frame graph state/policy、stage dispatch/contract tests、managed probes,以及所有 Mono internal-call conversion code。
RenderGraph
Native RenderGraph 是 camera frames 的执行主干。
- 所有 graph resource usage 都通过
RenderGraphBuilder和RenderGraphPassBuilder记录;不要依赖隐藏的 RHI side effects。 - Imported textures 需要有效 descriptions。如果
graphOwnsTransitions为 true,imported view 必须暴露 texture,以便 executor 创建 barriers。 - Transient textures 需要有效 width、height、format、type 和 sample description。无效 transient reads 是 compile errors。
- 使用
RenderGraphRecordingContexthelpers 在RenderPass、SceneRenderFeaturePass和 pipeline-stage recorders 之间 clone 或 adapt pass contexts。 RenderGraphBlackboard携带 per-frame shared data,例如CameraFrameRenderGraphFrameData;优先使用 typed blackboard entries,不要用 global 或 static frame state。- 当前 graph execution 已有 dependency sorting、lifetime tracking、transient allocation 和 resource transitions。 除非实现并测试,否则不要假设存在 pass culling 或 transient aliasing。
Native Passes
Native passes 仍用于 backend fallback、工具和 built-in rendering。
- 新 native passes 应尽可能实现
RecordRenderGraph。只有 legacy execution 仍需要时,才把Execute保留为 fallback path。 - 对 fallback-compatible raster passes,使用
RenderPassGraphContract和RecordRenderGraphSurfaceIOhelpers。 SceneRenderFeaturePass用于在 scene phases 周围注入 native scene feature。保持SceneRenderInjectionPoint中的 injection points、ScenePhase中的 scene phases 与SceneRenderSequence对齐。- Object-id rendering 是 tooling/editor support。只有启用
XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT时,CameraRenderer才安装 top-levelBuiltinObjectIdPass。不要把 object-id ownership 移入 scene backend。 - Built-in fullscreen passes 位于
Passes/,planning factories 位于Planning/。保持 stage planning 与 pass implementation 分离。
Managed Boundary
Managed SRP assets 通过 GraphicsSettings.renderPipelineAsset 选择,并通过
ManagedScriptableRenderPipelineAsset bridge。
ManagedRenderPipelineAssetDescriptor标识 managed asset assembly、namespace、class、retained managed object handle 和可选AssetRef。它不再是 configured pipeline 的唯一身份;GraphicsSettingsState中的 configuredAssetRef才是 Unity 风格 asset selection root。- Descriptor-only selection 仍保留给测试、legacy runtime fallback 和尚未接入 asset serialization 的路径。
一旦已有 configured
AssetRef,不要把 managed object handle 或 descriptor 当作长期 asset identity。 ManagedScriptableRenderPipelineAsset解析ManagedRenderPipelineAssetRuntime,创建ScriptableRenderPipelineHost,并把 request/plan/final-color policy calls 转发给 managed code。ScriptableRenderPipelineHost组合 native backend asset 和可选 managed stage recorder。当 managed recorder 是权威时,这些 stages 的 direct legacyRenderfallback 会被刻意禁用。- Mono 创建
MonoManagedRenderPipelineAssetRuntime和MonoManagedRenderPipelineStageRecorder。 Scriptable context、planning context、camera request context、scene setup context 和 shadow execution context handles 都是临时 native registry entries。不要在调用之外保存 managed context objects。 - 当前 Mono-backed SRP assets 显式使用
DefaultNativeBackend做 scene drawing。ManagedScriptableRenderContext.DrawRenderers和DrawSkybox委托给NativeSceneRecorder以及 nativeSceneDrawBackend。 - Managed resource/version invalidation 是正确性的一部分。如果 managed asset、renderer data 或 feature
修改 runtime state,调用
SetDirty或本地 invalidation helper,确保 native runtime caches 被释放。
URP Model
URP package 位于 managed/XCEngine.RenderPipelines.Universal。它应被视为主要面向用户的 render pipeline
package。
UniversalRenderPipelineAsset拥有rendererDataList、defaultRendererIndex、UniversalShadowSettings和UniversalFinalColorSettings。它从UniversalAdditionalCameraData解析 renderer index,然后委托给 renderer data。ScriptableRendererData拥有持久 renderer instance 和 renderer feature collection。Renderer data dirty state 会释放 renderer setup cache,并递增 runtime state version。ScriptableRenderer按 stage 构建m_activePassQueue。它依次调用 featureSetupRenderPasses、featureAddRenderPasses,再调用 renderer-ownedAddRenderPasses;passes 按RenderPassEvent顺序插入,并归组到RendererBlocks。- URP stage planning 以
ScriptableRenderer的 active pass queue 为最终事实源。ConfigureCameraFramePlan仍是兼容和高级策略 hook,但它不能单独声明 shadow、depth、post 或 final-output stage;没有被 pass queue 覆盖到的 side/fullscreen stage 必须在最终 plan 中清掉。 ConfigurePassQueueCameraFramePlanInstance必须在 planning 阶段为当前 renderer 和framePlanId生成一次 per-stage pass queue 快照,并用该快照派生 stage manifest。SupportsStageRenderGraph和RecordStageRenderGraph在framePlanId != 0时必须消费这个快照,不得重新运行 featureSetupRenderPasses、featureAddRenderPasses或 renderer-ownedAddRenderPasses来重建队列。 找不到匹配framePlanId和 renderer index 的快照时必须返回不支持或 record 失败,让上层暴露错误; 不要退回 legacy queue rebuild 或 built-in fallback。RendererBlock将 pass events 映射到 camera stages:shadow caster、depth prepass、main opaque、main skybox、main transparent、post process 和 final output。UniversalRenderer拥有具体 blocks:UniversalShadowCasterBlock、UniversalDepthPrepassBlock、UniversalMainSceneBlock、UniversalPostProcessBlock和UniversalFinalOutputBlock。- Built-in URP passes 包括
DrawObjectsPass、DrawSkyboxPass和内部BuiltinFinalColorPass。当前 feature implementations 包括RenderObjectsRendererFeature、ColorScalePostProcessRendererFeature和DisableDirectionalShadowRendererFeature。 - 新增 URP feature 时,保持 serializable settings、runtime hash、
Create、ConfigureCameraRenderRequest、ConfigureRenderSceneSetup、AddRenderPasses和 stage gate behavior 一致。 如果 feature 需要 stage,必须 enqueue 一个RenderPassEvent匹配该 stage 的 pass;不要只依赖ConfigureCameraFramePlan请求 stage。
Managed RenderGraph
Managed RenderGraph 目前有意保持较小,但应维持 Unity 的现代形态。
- Public managed authoring 通过
XCEngine.Rendering.RenderGraphModule.RenderGraph.AddRasterPass和RenderGraphRasterPassBuilder进行。 - Public passes 应声明 reads、bindings、color attachments、depth attachment 和 render func,然后
Commit。 - Internal fullscreen helpers,例如 color scale、shader vector 和 final color,是 URP implementation details。
不要把它们作为 public
ScriptableRenderContextshortcuts 暴露。 - Managed
SetRenderFunc是 deferred RenderGraph execution callback。Mono bridge 必须 retain delegate, 在 native graph pass 执行时创建 pass-scopedCommandBuffer,并把已支持的 managed command stream flush 到 当前RHICommandList。当前公开且已测试的命令子集是CommandBuffer.ClearRenderTarget(Color),写向 pass 的 primary color attachment;没有 native command stream 和 executor 测试的命令不要暴露成 public API。 - Public RenderGraph frame data 由同一个 renderer stage 内记录的所有 passes 共享。保持
ContextContainer作为 stage frame data,而不是 pass-local scratch data。 - Fullscreen internal passes 需要有效
sourceColorTexture和primaryColorTarget。Multi-pass fullscreen chains 应在每个 pass 后更新 current source color。
Scene Data
Scene data 每个 camera frame 提取一次,然后由 pipeline 调整。
RenderSceneExtractor从 scene 和 camera 构建RenderSceneData。Component traversal 和 render item extraction 应留在这里,或放在Extraction/RenderSceneUtility。- Pipeline scene setup 属于
RenderPipeline::ConfigureRenderSceneData或 managedConfigureRenderSceneSetup,不属于单个 draw pass。 - Main-light shadow data 通过
DirectionalShadowPlanningSettings规划,并通过DirectionalShadowRuntime执行。当前实现是单个 main directional shadow map,不是 cascaded shadows。 - Final color policy 由 pipeline defaults 加 camera overrides 解析。只有需要 final color processing 或 feature
显式要求时,才 request
FinalOutput。 - Global shader keywords 是规范化的。Keyword changes 应集中在 scene setup policy,不要散落在 draw passes 中。
Materials And Shaders
- Built-in material pass names 和 resource semantics 位于
Builtin/。一起更新BuiltinPassTypes、BuiltinPassMetadataUtils和BuiltinPassLayoutUtils。 - Material-state resolution 属于
Materials/。避免在 passes 中重复 render state conversion logic。 - Shader variant helpers 分为 public forwarding headers 和
Internal/;保持 public includes 对 engine users 稳定。 - URP shader package paths 应遵循现有
Packages/com.xcengine.render-pipelines.universal/...约定。
测试体系
使用覆盖变更边界的最窄测试集合;当改动跨 native/managed 或 RenderGraph execution 时,再扩大范围。
- Native Rendering 改动后构建 engine:
cmake --build <build-dir> --config Debug --target XCEngine - ScriptCore 或 URP 改动后构建 managed assemblies:
cmake --build <build-dir> --config Debug --target xcengine_managed_assemblies - 构建 native rendering unit tests:
cmake --build <build-dir> --config Debug --target rendering_unit_tests - 直接运行 native rendering tests:
<build-dir>/tests/Rendering/unit/<config>/rendering_unit_tests - 或运行 native rendering CTest 子集:
ctest -C Debug -R "RenderGraph|CameraFrame|SceneRender|RenderPass|BuiltinForward|FullscreenPass" --output-on-failure - 构建 scripting bridge tests:
cmake --build <build-dir> --config Debug --target scripting_tests - 直接运行 scripting bridge tests:
<build-dir>/tests/scripting/<config>/scripting_tests - 或运行 SRP/URP scripting CTest 子集:
ctest -C Debug -R "MonoScriptRuntimeTest|ProjectScriptAssemblyTest" --output-on-failure - 当 SRP/URP phase ordering、renderer features 或 editor viewport rendering 改动时,运行 renderer phase
regression suite:
cmake --build <build-dir> --config Debug --target rendering_phase_regression - Managed API 改动常用 probe files:
managed/GameScripts/RenderPipelineApiProbe.cs、managed/GameScripts/ScriptableRenderContextApiSurfaceProbe.cs和project/Assets/Scripts/ProjectRenderPipelineProbe.cs。 - 重要 native test files:
tests/Rendering/unit/test_render_graph.cpp、tests/Rendering/unit/test_camera_frame_graph_stage_policy.cpp、tests/Rendering/unit/test_camera_frame_graph_stage_contract.cpp、tests/Rendering/unit/test_camera_scene_renderer.cpp、tests/Rendering/unit/test_scene_render_request_planner.cpp和tests/scripting/test_mono_script_runtime.cpp。
当前债务
- Managed SRP 仍是 SRP v1 surface。Main scene drawing 通过 managed calls 记录,但由 native scene draw backends 执行。
- Managed
CommandBuffer已能随 publicSetRenderFunc延迟到 RenderGraph 执行期运行,但当前只收口了ClearRenderTarget(Color)这一条受测命令。它不是完整 UnityCommandBuffer;新增命令必须一起补 native command stream、RHI flush 和 scripting bridge tests。 - Managed RenderGraph 当前暴露 raster authoring。Native graph 有 compute pass support,但 managed compute authoring 还未公开。
UniversalPostProcessBlock仍保留 post-process source promotion helper;实际 post-process stage 由 active pass queue 中的 features/passes 声明。- Render-pipeline selection 已切到
AssetRef作为根身份,但UniversalRendererData、features 和ScriptableObject字段/子资产仍是 code-created objects,还不是完整 Unity 风格 serialized asset pipeline。 - 当前 shadow support 是单个 main directional shadow path,没有 cascades。
- Graph compiler/executor 当前没有实现 pass culling 或 transient aliasing。
【过去进展】
已完成的架构切分
本节记录已经完成的架构切分。未来改动关闭或移动所有权边界时,继续把它作为流水账维护。
GraphicsSettings.renderPipelineAsset已通过ManagedScriptableRenderPipelineAsset和GraphicsSettingsState选择 managed SRP assets。- Render-pipeline asset selection 已从 descriptor/managed handle 切到
AssetRef根身份;descriptor 保留为 assembly/type/handle runtime cache,managed materialization 更新 cache 时保留 configured asset reference。 - Managed SRP execution 由
ScriptableRenderPipelineHost承载,它组合 native backend 和可选 managed stage recorder。 - Mono-backed SRP assets 使用
DefaultNativeBackend做 scene drawing,并把 managed stages 记录到 native RenderGraph。 - URP 现在已有 renderer data、renderer features、renderer pass queueing、renderer blocks、renderer-index resolution 和 per-stage recording。
- URP stage planning 已收口到 renderer active pass queue 派生的 per-
framePlanId快照和 stage manifest。 Stage support 和 stage recording 现在消费 planning 阶段保存的快照,关闭了 feature planning hook、 support probe 和 recording 各自重建 pass queue 的重复事实源。 - Public managed RenderGraph raster authoring 已存在;internal fullscreen kernels 仍是 URP implementation details。
- Public managed
SetRenderFunc已从 recording-time 调用改为 RenderGraph execution-time 调用; native 通过 retained managed delegate 和 pass-scopedCommandBufferbridge 执行已支持的 command stream, 当前受测命令为CommandBuffer.ClearRenderTarget(Color)。 - Public
ScriptableRenderPass.RecordRenderGraph(RenderGraph, ContextContainer)通过RenderingData、CameraData、LightingData、ShadowData、EnvironmentData、FinalColorData和StageColorData接收真实 URP frame data。 - Public managed
SceneRenderInjectionPoint已移除。Managed SRP/URP authors 应使用RenderPassEvent、 renderer blocks、renderer-list drawing 和 RenderGraph declarations,而不是 native private injection points。 - Main scene、post process、final output、object id、depth-only 和 shadow-caster stages 已集中到
CameraFrameStage和CameraFramePlan。 - Final color processing 表示为 policy 和 final output stage,而不是 implicit swapchain behavior。
- Object-id rendering 是 top-level tooling pass,并由
XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORTguard。 - Camera-frame graph dispatch 已收紧 legacy fallback gate:只有所选 pipeline 明确允许时,未被 render-graph recorder、pass sequence 或 standalone pass 处理的 stage 才能进入 fallback raster pass。 Managed SRP/URP host 不允许 hidden built-in fallback。