Align URP render graph frame data scope

This commit is contained in:
2026-04-26 20:51:16 +08:00
parent ee05558f86
commit db2d3eb414
13 changed files with 918 additions and 73 deletions

View File

@@ -6,63 +6,123 @@ already made. It is intentionally not a full map of the current implementation.
If this file conflicts with the current code, trust the code and update this
file in the same change.
## Long-Term Goal
## Next-Stage Goal
The rendering module is moving toward a Unity SRP / URP style model:
The rendering module's next stage is to close the SRP / URP architecture around
Unity's public rendering model. The goal is not merely to have similar class
names; pipeline authors should be able to reason about XCEngine rendering with
the same mental model they use for Unity SRP and URP.
Unity alignment is the primary design constraint:
- Treat `GraphicsSettings.renderPipelineAsset`, `RenderPipelineAsset`,
`ScriptableRenderPipelineAsset`, `ScriptableRenderPipeline`,
`ScriptableRenderContext`, `ScriptableRendererData`,
`ScriptableRenderer`, `ScriptableRendererFeature`,
`ScriptableRenderPass`, `RenderPassEvent`, `RenderingData`, and
`UniversalAdditionalCameraData` as the product-facing spine.
- Keep the managed API close to Unity semantics wherever the engine can support
it. If XCEngine must diverge, the divergence should be explicit, documented,
and caused by a real engine constraint rather than native convenience.
- Prefer Unity-style authoring concepts over native implementation concepts.
Public C# code should feel like URP pipeline authoring, not like remote
control over a native built-in renderer.
The pipeline selection contract must stay explicit and Unity-like:
- `GraphicsSettings.renderPipelineAsset == null` selects the built-in pipeline.
- `GraphicsSettings.renderPipelineAsset != null` selects the programmable SRP
path.
- The managed product shape should stay close to:
`ScriptableRenderPipelineAsset -> ScriptableRendererData ->
ScriptableRenderer -> ScriptableRendererFeature/ScriptableRenderPass`.
- `ScriptableRenderPipelineHost` is the native host for managed SRP execution;
it must not become a hidden fallback renderer when managed SRP planning or
recording fails.
- Managed SRP / URP owns renderer selection, stage planning, feature/pass
organization, camera policy, and optional stage requests such as shadow,
depth, post-process, and final output.
- Native rendering owns scene extraction, renderer-list drawing, render graph
execution, fullscreen primitive execution, and built-in private draw kernels.
- Public managed scene drawing should expose drawing capability, not native
built-in injection order. Keep the public surface centered on
`DrawRenderers(...)`, `DrawSkybox()`, `DrawOpaqueRenderers()`, and
- `GraphicsSettings.renderPipelineAsset != null` selects the managed SRP path.
- Runtime startup must not silently assign a project default SRP asset. A
project default can be loaded by project/editor policy, but rendering mode
changes must remain visible through `GraphicsSettings.renderPipelineAsset`.
- If a managed SRP asset cannot create a valid pipeline, recorder, or backend
bridge, creation should fail visibly and let the top-level factory choose the
built-in pipeline. `ScriptableRenderPipelineHost` must not impersonate a
successful managed SRP by drawing through a hidden fallback renderer.
The URP object model should be the center of future work:
- `ScriptableRenderPipelineAsset` owns pipeline defaults, renderer-data lists,
default renderer selection, camera policy, final-color policy, shadow policy,
and managed pipeline lifetime.
- `ScriptableRendererData` is the serialized renderer configuration. It owns
renderer feature lists, default feature creation, runtime invalidation, and
renderer instance rebuilds.
- `ScriptableRenderer` owns per-camera pass queue construction. It should
enqueue passes from renderer features, sort by `RenderPassEvent`, partition
work into URP-like renderer blocks, and record only the stage requested by
the native frame plan.
- `ScriptableRendererFeature` owns reusable renderer extensions. Features
should configure camera/frame policy and enqueue `ScriptableRenderPass`
instances without reaching into native private pass order.
- `ScriptableRenderPass` owns pass lifecycle and recording. The long-term pass
surface should be `RecordRenderGraph(...)`, renderer-list drawing, public
texture/resource declarations, and explicit pass data.
The native/managed boundary should remain strict:
- Managed SRP / URP owns renderer selection, feature/pass organization,
optional stage planning, camera clear policy, shadow/depth requests,
post-process and final-output policy, and public frame data exposed to user
passes.
- Native rendering owns scene extraction, culling and renderer-list
realization, render graph compilation/execution, RHI resource lifetime,
fullscreen primitive execution, and private built-in draw kernels.
- Managed scene drawing should expose draw capabilities, not native injection
points. Keep the public scene surface centered on `DrawRenderers(...)`,
`DrawSkybox()`, `DrawOpaqueRenderers()`, and
`DrawTransparentRenderers()`.
- Do not reintroduce implicit project-default SRP activation, hidden
`ScriptableRenderPipelineHost` fallback, or public managed wrappers for
built-in private scene injection points.
- Future URP alignment should add managed-side renderer/pass/render-graph
capability. It should not reopen native private fallback paths just to make a
feature appear to work.
- Main-scene, shadow, and depth work should be renderer-driven. Managed passes
may request those stages and call public draw APIs, while native continues to
execute renderer lists and low-level draw kernels.
Managed RenderGraph is the key capability gap for the next stage:
- Grow the current fullscreen-pass bridge into a URP-style render-graph
authoring surface with explicit texture creation/imports, read/write
declarations, pass data, frame-data/context-container access, and scheduled
renderer-list drawing.
- Post-process and final-output should become ordinary URP feature/pass work
where possible. Native fullscreen kernels may remain execution primitives,
but they should not appear as feature-specific public shortcuts.
- Prefer data-driven frame state (`CameraData`, `LightingData`, `ShadowData`,
`EnvironmentData`, `FinalColorData`, `StageColorData`) over direct scene
object inspection during pass recording.
Tests should protect Unity-facing semantics while the architecture closes:
- Prioritize contract tests for null-vs-SRP selection, managed SRP creation
failure, no hidden fallback rendering, renderer-data/default-renderer
selection, camera renderer override, feature/pass enqueue order, stage
planning, render-graph recording, and forbidden public API surface.
- Add URP integration scenes after the API and responsibility boundaries are
stable: default Universal renderer, `RenderObjectsRendererFeature`,
post-process/final-output, shadow/depth, camera override, and camera stack.
- Avoid overfitting tests to temporary native bridge details. Tests should lock
Unity SRP / URP semantics first, implementation mechanics second.
Guardrails:
- Do not reintroduce public managed wrappers for native private scene injection
points.
- Do not make `RenderPassEvent` or renderer blocks a thin alias for native
built-in pass order. They should describe URP scheduling semantics.
- Do not add managed APIs that only work by reaching into one built-in native
renderer path. Add a public renderer-list, render-graph, or pass abstraction
instead.
- A managed SRP stage that declares support and then fails to record should
fail visibly. It should not silently continue by drawing the default native
scene.
## Past Execution
- Established the top-level selection rule: explicit null means built-in,
explicit managed asset means SRP, and runtime startup must not silently assign
`ProjectDefaultRenderPipelineAsset`.
- Removed public managed native scene injection APIs such as
`ScriptableRenderContext.RecordSceneInjectionPoint(...)`,
`RecordBefore/After*Injection(...)`, and
`RecordOpaque/Skybox/TransparentScenePhase(...)`.
- Kept `RecordScene()` / `RecordScenePhase(...)` internal and made the public
managed scene surface express draw operations through `DrawRenderers(...)`,
`DrawSkybox()`, `DrawOpaqueRenderers()`, and
`DrawTransparentRenderers()`.
- Moved renderer-backed SRP stage ownership into `ScriptableRenderer`,
`ScriptableRendererFeature`, and `ScriptableRenderPass`; renderer-backed
assets clear native optional stage defaults before managed renderer/features
explicitly request the stages they need.
- Removed `CameraRenderRequestContext.hasDirectionalShadow` and
`ClearDirectionalShadow()` from the ScriptCore public surface. Core keeps only
internal bridge access, while URP policy belongs in renderer asset, renderer
data, and feature code.
- Reordered managed camera request policy so managed / URP first observes the
native directional-shadow baseline, then the final managed policy recomputes
or clears the request without native planning adding it back afterward.
- Prevented URP default renderer feature factories from auto-injecting built-in
private feature wrappers.
- Tightened SRP creation failure semantics:
`ManagedScriptableRenderPipelineAsset::CreatePipeline()` fails when managed
runtime, backend asset, or stage recorder is unavailable; factory fallback
then returns the built-in pipeline instead of letting
`ScriptableRenderPipelineHost` impersonate a successful managed SRP.
- Made the public managed
`ScriptableRenderPass.RecordRenderGraph(RenderGraph, ContextContainer)` path
receive real URP frame data. `RenderingData`, `CameraData`, `LightingData`,
`ShadowData`, `EnvironmentData`, `FinalColorData`, and `StageColorData` are
now `ContextItem`s populated before public render-graph recording.
- Moved managed public render-graph frame data ownership from individual
`ScriptableRenderPass` calls to the `ScriptableRenderer` stage recording
scope. Public `ContextContainer` state is now shared across renderer passes
for the same stage, matching URP's frame-data mental model instead of
pass-local scratch data.