Skip to content

fix(renderers): always sort the emissive layer's quads - #2191

Open
duzos wants to merge 1 commit into
mainfrom
fix/emissive-draw-order
Open

duzos wants to merge 1 commit into
mainfrom
fix/emissive-draw-order

Conversation

@duzos

@duzos duzos commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

About the PR

Fixes the rendering regression reported against beta 2816: console monitor
animations missing on Renaissance and Toyota, and emissions showing through the
panel around the power switch.

Reverts the one change in #2186 that caused it, and keeps the reasoning in the
code so it does not get dropped again for the same wrong reason.

Why / Balance

No balance impact. This restores the pre-#2186 look of every emissive draw.

Costs back the roughly 0.6 ms the sort was measured to save on Copper's
emission pass. On the numbers from #2186 that is about 117 fps to about 109 in
the Copper interior scenario, against 39 fps on the pre-#2186 baseline, so the
bulk of that PR's gain is unaffected.

Technical details

AITRenderLayers.emissive builds the layer with:

.cull(DISABLE_CULLING)
.transparency(TRANSLUCENT_TRANSPARENCY)
.writeMaskState(COLOR_MASK)          // depth TEST, no depth WRITE
.depthTest(LEQUAL_DEPTH_TEST)

#2186 dropped the translucent flag on this layer, which is what hands
BufferBuilder a sorter, to save the per-quad primitive centres and the
explicit index buffer.

The justification given was that 106 of the mod's 113 emission textures are
strictly binary alpha, where SRC_ALPHA blending is order-independent, so only
textures with real partial alpha needed the sorted layer.

That is wrong in one specific way. Binary alpha makes the blend
order-independent, not the occlusion.
Because the layer writes no depth,
nothing in the batch occludes anything else in it, and because back face culling
is disabled, both faces of every part are submitted. Whichever quad is drawn
last simply wins. So submission order decides what ends up on top regardless of
alpha: a part's own back face, or a light sitting behind a panel, paints over
the front of it.

The same mechanism was already written down correctly in this file, in the
javadoc for the sorted accessor, about the eye of harmony star: "with culling
disabled so both faces of both shells are submitted; unsorted, the opaque core
would land last and paint over the shell that is supposed to veil it." It simply
was not applied to the console layer.

Symptom mapping:

  • Emissions through the panel is the direct case. powerlights2 is one of
    Toyota's animated bones, and it is behind the panel it now paints over.
  • Renaissance and Toyota monitor animations are the same cause. What
    animates on those monitors is emissive: Renaissance keyframes symbol, a
    child of monitor, plus lamp1 through lamp6, rotorlight and three
    blinkers; Toyota keyframes gallifreyan2, gallifreyan3, uppertimepiece,
    powerlights2 and the rest. Those are small moving glow quads, and with the
    ordering wrong they are covered and stop reading as animated.
  • Renaissance and Toyota, specifically, because they are the only console models
    that both carry a monitor and are affected in the overlapping-glow way this
    produces. Hudolin shares their root transform but has no monitor at all.

With unsorted gone there is only one emissive layer, so the two accessors are
merged. That also removes the extra flush the sorted accessor's own javadoc
described, where an exterior emission texture reached both layers in one frame
and Immediate keys buffers on layer identity.

Not fixed here

The third item in the report, the Copper monitor text looking wrong, is a
separate change (ed57ab87c, monitor text from drawWithOutline to a shadowed
draw on POLYGON_OFFSET). Copper is the only model that was converted, which is
why only its text differs. It is cosmetic and wants a look at the intended
appearance rather than a blind revert, so it is left out of this PR.

Media

Needs a before and after on a Renaissance or Toyota console with the power on.

Requirements

None.

Breaking changes

AITRenderLayers.tardisEmissiveCullZOffsetSorted(Identifier) is removed. There
is one emissive layer now and tardisEmissiveCullZOffset(Identifier) returns it,
which is what the three callers were updated to.

Changelog

🆑

  • fix: console monitor animations are visible again on Renaissance and Toyota
  • fix: emissive lights no longer draw through the panels in front of them

The layer writes no depth and disables back face culling, so nothing in the
batch occludes anything else and both faces of every part are submitted.
Submission order is then the only thing deciding what is on top, which put
lights through panels and stopped the animated monitor glow on Renaissance and
Toyota reading as animated.

Binary alpha was the reason given for dropping the sorter. It makes the blend
order independent, not the occlusion.

Costs back the 0.6 ms the sort was measured to save on Copper. One layer per
emission texture now, so the exterior no longer flushes twice when both
accessors saw the same texture in a frame.
@duzos
duzos requested a review from a team as a code owner September 8, 2026 18:04
@github-actions github-actions Bot added size/M Denotes a PR that changes 100-999 lines. S: Untriaged Status: Indicates an item has not been triaged and doesn't have appropriate labels. S: Needs Review Status: Requires additional reviews before being fully accepted. labels Sep 8, 2026
@GSMPBot
GSMPBot requested a review from drtheodor September 8, 2026 18:04
@duzos duzos self-assigned this Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S: Needs Review Status: Requires additional reviews before being fully accepted. S: Untriaged Status: Indicates an item has not been triaged and doesn't have appropriate labels. size/M Denotes a PR that changes 100-999 lines.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant