Skip to content

Depth-splice rigged attachment alpha into world alpha#5927

Open
Viscerous wants to merge 5 commits into
secondlife:developfrom
Viscerous:feature/interleaved-alpha
Open

Depth-splice rigged attachment alpha into world alpha#5927
Viscerous wants to merge 5 commits into
secondlife:developfrom
Viscerous:feature/interleaved-alpha

Conversation

@Viscerous

@Viscerous Viscerous commented Jun 15, 2026

Copy link
Copy Markdown

Currently, alpha sorting happens in two passes: rigged first, ordered by raw avatar pointer, an arbitrary order unrelated to depth that differs from viewer to viewer, and then a back-to-front walk for unrigged. Since the passes are separate, rigged (which includes almost all avatars) interacts poorly with the rest of the world (almost entirely unrigged), resulting in long-standing issues such as avatars' hair in the foreground rejecting trees in the background. Because the pointer order differs per client, two people can also see the same overlapping avatars layered differently.

Since we already deterministically sort rigged intra-avatar attachments via the attachment slots, I propose interleaving whole avatars and standalone animesh by their animation extents together with the unrigged walk in one unified pass, giving both a proper order. Hair will no longer reject alpha objects behind it, or vice versa.

An avatar's unrigged alpha attachments join the same block: every non-HUD attachment bridge is stamped, and in the interleaved sort a stamped attachment's groups key at the wearer's avatar depth. On depth ties rigged draws first, so each avatar drains as one contiguous run, rigged in attachment order, then its unrigged attachment groups back-to-front by their own bounds, preserving the two-pass behaviour that has been authored against up until now.

The avatar depth lives in a dedicated sort key (mAvatarDepth) rather than overwriting the group's mDepth, which stays bounds-derived to order an ensemble's groups among themselves and to sort unstamped world alpha. Groups of one attachment, which tie on every stamp field, fall back to bounds depth as a final tie-breaker, so their internal depth-writing order is back-to-front and frame-stable, where upstream leaves it to std::sort's unspecified tie order. Avatar pointers compare via std::less (raw < on unrelated pointers is unspecified), and the rigged/merged pass selection is an explicit EAlphaStream enum.

Since this merges two passes into one, net is likely slightly less work on the CPU side and neutral on the GPU save for the overdraw of the previously undrawn pixels. Switching RenderInterleavedAlpha (default on) off restores the exact legacy two-pass behavior for A/B comparison.

QA note: With RenderInterleavedAlpha off, stamps now fan out from the bridge to every group of a linkset, where upstream stamped only the root drawable's group. Multi-group rigged alpha attachments will sort differently (arguably correctly for the first time) even with the setting disabled. Stamps on unrigged groups are inert when the setting is off, the legacy world sort ignores them.

2026-06-15.18-07-48.mp4

@Viscerous Viscerous force-pushed the feature/interleaved-alpha branch from 948780d to ae98514 Compare July 9, 2026 16:14
@Ansariel

Ansariel commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

@Geenz any opinion on this?

@Geenz

Geenz commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

@Ansariel @Viscerous I will be taking a closer look at this tomorrow. Hopefully prior to the open source meeting. I have a few concerns with the technique, but I just need to squint at it in realtime before I make a judgement here.

@Viscerous

Viscerous commented Jul 10, 2026

Copy link
Copy Markdown
Author

@Geenz I had some time to test this a bit more thoroughly as well, the main regression would've been animesh which would've always drawn over other alpha blend, and its own alpha ordering would've been compromised as well. To handle this case, I factored out the the avatar depth calculation to LLVOAvatar::calcRiggedAlphaDepth() and is now stamped onto the spatial bridges of standalone animesh in LLControlAvatar::idleUpdate() (worn attachments and worn animesh are already handled via the wearer's attachment loop).

Additionally, since scalars are simply not accurate enough for sorting within an avatar I restored what is essentially upstream behaviour within an ensemble (like an avatar): rigged first then unrigged which is what's been authored against for years.

Remaining weak point: alpha groups inside of alpha groups (e.g. avatar inside building with windows) don't have the granularity to resolve deterministically, will depend on angle; still upgrades from "never correct" to "correct sometimes"

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants