Repository navigation
Conversation
Version bumped in both manifests, because `release.ts` asserts they match and that check runs after `npm publish` rather than before it, so a mismatch strands a release half done. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGvtaUNATVCVxD2qcbiY4t
Two stalls found by profiling INSIDE the frames that stuttered on a 3D example, rather than around them. Together they were 86% of the time spent in those frames, and the larger one was not graphics work at all. Teardown probed every GL object with an `is*` query before deleting it. The probe is there for a real reason: deleting a handle whose context has gone raises INVALID_OPERATION, and `is*` reports false for exactly that case while being error-free to call. It is also SYNCHRONOUS, blocking until the driver answers, and teardown runs per object, so a scene dropping a batch of retained meshes paid a round trip for every buffer and vertex array in it. But the probe can only ever have something to say about a context that has been lost at least once; any other context still owns every handle made against it. It is now skipped until that happens, which for most games is never. `buffer/deletable.js` holds the rule, and the context-lost handler is what turns the probe back on. A first attempt replaced the probes with `isContextLost()`. Two context-loss specs caught it: after a RESTORE the context is live again while the old handles are dead, which is the one case the probe exists for and that check cannot see. Second, a `ShaderEffect` hosted on a `Mesh` was compiled on the frame it first drew, even when its asset had been preloaded. Preloading builds the standalone program, but a mesh host splices the body into the mesh shader, which is a different program again and was not in the warmed set. So a game that declared every effect in its manifest still stuttered the first time each one appeared, which is the opposite of what declaring them looks like it buys. `prewarm` now builds the spliced variant for every preloaded effect, in both fog states, alongside the variants it already warmed. Measured on the same scene, profiling inside the stuttering frames: five freeze windows became one, and neither `isBuffer` nor `getShaderParameter` appears in the remaining profile at all. The one that is left is a different call again, in render target creation, and is untouched here. Both are pinned by `tests/prewarm-effects.spec.js` and verified by mutation: removing the warming loop, making the probe unconditional, and dropping the probe entirely each fail a test. Full suite green at 8032. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGvtaUNATVCVxD2qcbiY4t
The spec was written and formatted after the last build, so the lint gate never saw it and four `arrow-body-style` errors reached CI. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGvtaUNATVCVxD2qcbiY4t
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found by profiling inside the frames that stuttered on a 3D example
rather than around them. Together these were 86% of the time spent in those
frames, and the larger one turned out not to be graphics work at all.
The teardown probes
Releasing a GL object asked an
is*query first. The probe is there for areal reason: deleting a handle whose context has gone raises
INVALID_OPERATION, andis*reports false for exactly that case whilebeing error-free to call.
It is also synchronous, blocking until the driver answers, and teardown
runs per object. A scene dropping a batch of retained meshes paid a round
trip for every buffer and vertex array in it. In the profile it was 70.4%
of the time inside the stuttering frames, ahead of shader compilation.
The probe can only ever say something about a context that has been lost at
least once. Any other context still owns every handle made against it. So it
is skipped until that happens, which for most games is never.
buffer/deletable.jsholds the rule and thewebglcontextlosthandler turnsthe probe back on.
The mesh-hosted effect
A
ShaderEffecton aMeshwas compiled on the frame it first drew, evenwhen its asset had been preloaded. Preloading builds the standalone program,
but a mesh host splices the body into the mesh shader, which is a different
program again and was not in the warmed set.
So a game that declared every effect in its manifest still stuttered the
first time each one appeared, which is the opposite of what declaring them
looks like it buys.
prewarmnow builds the spliced variant for everypreloaded effect in both fog states, alongside the variants it already
warmed.
Measured
Same scene, same harness, profiling inside the stuttering frames:
Neither
isBuffernorgetShaderParameterappears in the remaining profileat all. What is left is
checkFramebufferStatusduring render targetcreation, a different call in a different place, and is untouched here.
Verification
tests/prewarm-effects.spec.jspins both, and each was mutation-tested —removing the warming loop, making the probe unconditional, and dropping the
probe entirely each fail a test, and the suite returns to green when
reverted. Full engine suite green: 329 files, 8032 passed.
Also opens 20.10.0, since the fixes need a section to live in. Both manifests
are bumped together because
release.tsasserts they match and that checkruns after
npm publish.🤖 Generated with Claude Code
https://claude.ai/code/session_01NGvtaUNATVCVxD2qcbiY4t