Skip to content

Fix invalid global-merge reads and respect camera layer masks#38

Draft
andybak wants to merge 5 commits into
wuyize25:mainfrom
icosa-mirror:pr/global-sort-fixes
Draft

Fix invalid global-merge reads and respect camera layer masks#38
andybak wants to merge 5 commits into
wuyize25:mainfrom
icosa-mirror:pr/global-sort-fixes

Conversation

@andybak

@andybak andybak commented Jul 16, 2026

Copy link
Copy Markdown

This PR fixes correctness issues in globally sorted rendering.

First, the final merge shader previously used a conditional expression that could
evaluate an out-of-range buffer access before selecting the valid result. Replacing
it with an explicit branch ensures only the selected order buffer is read.

Second, one global draw can carry only one Unity layer. The global path now falls
back to per-renderer rendering when participating splat renderers use different
layers. If all participating renderers share a layer, that layer is assigned to the
global RenderParams, so Unity camera culling masks include or exclude the merged
draw correctly.

Third, isolated splat renderers such as model or panel previews can now opt out of
the global merge through GsplatRenderer.ParticipateInGlobalSort. Participating
same-layer scene renderers can remain globally sorted while opted-out renderers use
the existing per-renderer fallback path.

Main changes

  • Replace the final merge shader's conditional buffer read with an explicit branch.
  • Detect mixed-layer participating renderer sets and disable global merging for that
    set.
  • Store the shared participating renderer layer on the global renderer.
  • Assign the shared layer to the global RenderParams draw.
  • Add the serialized, runtime-settable ParticipateInGlobalSort renderer option,
    enabled by default.
  • Partition active renderers into the global set and a per-renderer fallback set.
  • Keep opted-out renderers visible while the participating set uses the global draw.

Risk and compatibility

Existing renderers participate by default, so existing same-layer scenes retain the
global draw optimization.

Mixed layers within the participating set still force the scene back to
per-renderer rendering. A renderer explicitly opted out of global sorting is not
depth-sorted against the global set, so opt-out is intended for isolated previews,
UI, or renderers in a separate visual space. It should not be used for splats that
must blend with participating renderers in the same scene.

When global rendering is active, each opted-out renderer retains its existing
per-renderer sort and draw cost. The renderer partitions reuse their lists and do
not add per-frame managed allocations.

Integration note with pr/depth-prepass

This branch is independently correct against current main, whose global draw uses
RenderParams. The depth-prepass branch adds SRP command-buffer global draws, which
do not receive layer filtering automatically. If both PRs are accepted, rebase this
branch after pr/depth-prepass and retain the combined branch's per-camera global
eligibility check and opted-out fallback draws in the new color and depth draw
methods. This prevents the SRP global path from reintroducing the camera-mask
regression or dropping opted-out renderers.

Suggested testing

  • Render two globally sorted splat objects on the same layer.
  • Confirm a camera including that layer renders the global draw.
  • Confirm a camera excluding that layer does not render the splats.
  • Put two participating renderers on different layers and confirm the system falls
    back to per-renderer rendering.
  • Render two participating same-layer scene splats plus one opted-out preview on a
    different layer; confirm the scene splats remain globally sorted and the preview
    remains visible.
  • Confirm an opted-out renderer continues to use its per-renderer sorting path.
  • Use separate cameras that include different renderer layers.
  • Exercise merge inputs where either side of the final merge is exhausted first.

Consumer validation

The opt-out path was tested in Open Brush with two dynamically imported,
overlapping scene splats and an active model preview on the Panels layer. The
imported scene splats remained in the global set while the preview rendered through
the independent fallback path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant