[Perf] (MG_State): drop the parsed ASTs as soon as the SPIR-V job has generated its modules

This commit is contained in:
2026-08-10 06:37:49 -04:00
parent 847ec74f48
commit d03b72267a
2 changed files with 19 additions and 1 deletions
@@ -90,6 +90,14 @@ namespace MobileGL::MG_State::GLState {
// the TShader's own intermediate rather than a copy. These used to die when
// RunBody() returned, which was safe only because nothing called getIntermediate()
// afterwards. GlslangToSpv does exactly that, so phase B has to own them.
//
// MEMORY NOTE: this is the one thing the split makes live LONGER than it used to -
// a glslang arena per stage, megabytes for a shaderpack, now alive from the end of
// phase A until phase B has generated its SPIR-V instead of dying with the link
// body. Phase B clears this vector as soon as GlslangToSpv returns, but a deep
// phase-B backlog still holds one arena per queued program. If peak RSS ever
// becomes the binding constraint on a pack load, THIS is the field to attack (by
// bounding the backlog, or by moving GlslangToSpv back into phase A).
Vector<SharedPtr<glslang::TShader>> shaders;
// GL enum per entry of `in.shaders`, in the same order (GetSpirvBinaryFromProgram
// walks it to pick the intermediates).
@@ -90,7 +90,11 @@ namespace MobileGL::MG_State::GLState {
} const phaseAReleaser{m_phaseA};
if (!m_phaseA) return;
const ProgramLinkTask::SpirvHandoff& handoff = m_phaseA->spirvHandoff;
// Non-const: the TShaders are dropped below, the moment GlslangToSpv is finished with
// them. This is safe by ownership rather than by locking - phase A is terminal and
// therefore immutable to everyone else, the GL-thread join touches only `artifacts`
// and `diagnostics`, and this node is the sole reader of the handoff.
ProgramLinkTask::SpirvHandoff& handoff = m_phaseA->spirvHandoff;
const Uint externalIndex = m_phaseA->in.externalIndex;
if (!handoff.ready || !handoff.reflection.program) {
// Phase A did not reach its tail (it failed the link, or was cancelled mid-body).
@@ -105,6 +109,12 @@ namespace MobileGL::MG_State::GLState {
MGLOG_D("ProgramObject %u: Starting SPIR-V generation", externalIndex);
GenerateSpirv(handoff, externalIndex);
// GlslangToSpv was the only consumer of the parsed ASTs, and they are by far the
// largest thing this node keeps alive (one glslang arena per stage, megabytes for a
// shaderpack). Everything after this point works on the SPIR-V and on the TProgram's
// own self-contained reflection pool, so drop them here rather than at the end of the
// body - spirv-opt plus routing is ~87% of this node's runtime.
handoff.shaders.clear();
MGLOG_D("ProgramObject %u: Building global-UBO routing tables", externalIndex);
{