Repository navigation
Conversation
`new_shader` compiles a program's shaders, links it and asks how each step went, and the asking waits for the driver to finish. An app that makes many shaders as it loads waits for each in turn, even where the driver could compile them all at once. WebKit is the slowest at it: 38 programs from a macroquad game (CHOMP) take 4.8 s to compile one after the other on a Mac with nothing cached, and 1.0 s when every one starts before any is checked. `RenderingBackend::start_shader` compiles and links a shader without asking how it went, and `finish_shader` waits for it and returns what `new_shader` would have. In between, `is_shader_compiling` says whether `finish_shader` would still wait, with `KHR_parallel_shader_compile` (or `ARB_parallel_shader_compile`), so an app can wait for its shaders across frames instead of stopping on one. Without the extension it says `false`. Metal compiles in `start_shader`, as in `new_shader`. WebGL only enables an extension once it's asked for, so gl.js gains `webgl_enable_extension`, which also gives `has_extension` a wasm version. `new_shader` is now `start_shader` and `finish_shader`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`glLinkProgram` read every active uniform right after linking, which waits for the link to finish, so on WebGL every program still compiled and linked one after the other: 150 ms each in WebKit with nothing cached. The table now fills on the first `glGetUniformLocation` after a link, and `glGetProgramiv` checks the program itself rather than its table, so it can ask for `COMPLETION_STATUS_KHR` without waiting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 17, 2026
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.
Why
new_shadercompiles a program's shaders, links it, then asks how each step went, and asking waits for the driver to finish. An app that makes many shaders while it loads waits for each in turn, even where the driver could compile them all at once.WebKit is the slowest at it. A macroquad game (CHOMP) makes 38 programs while it loads, and in Safari's engine on a Mac (M2 Max) with nothing cached, they take 4.8 s one after the other. Started all at once and waited for with
KHR_parallel_shader_compile, they take about 1 s, and no frame waits more than 2 ms. Safari, Chrome and Edge all have the extension.Change
RenderingBackend::start_shader(source, meta)compiles and links a shader without asking how it went, and returns aPendingShader.RenderingBackend::finish_shader(pending)waits for it, if it's still compiling, and returns whatnew_shaderwould have.RenderingBackend::is_shader_compiling(&pending)says whetherfinish_shaderwould still wait. On OpenGL, it asks forCOMPLETION_STATUS_KHRwhen the context hasGL_KHR_parallel_shader_compileorGL_ARB_parallel_shader_compile. Without either, and on Metal, it'sfalse.new_shaderis nowstart_shaderfollowed byfinish_shader, so its behaviour is unchanged. On Metal, the defaults compile instart_shader, asnew_shaderdoes.webgl_enable_extension, andhas_extensiongets a wasm version that calls it.glLinkProgramread the program's uniforms right after linking, which waits for the link, so every program still linked one after the other on WebGL: about 150 ms each in WebKit. The uniform table now fills on the firstglGetUniformLocationafter a link, andglGetProgramivchecks the program rather than its table, so asking forCOMPLETION_STATUS_KHRdoesn't wait.An app that wants its shaders ready without stopping on one can start every shader, keep drawing frames while
is_shader_compilingsays any still is, then finish each.Measurements
CHOMP starts its 38 programs on its loading screen and finishes each once
is_shader_compilingsays it's done. Its loading screen times that phase. Each "first visit" run adds a different no-op statement to every shader, so the browser's shader cache has none of them.One more Firefox run after the change took 0.70 s, and everything else in that run was slow too.
Verification
cargo checkpasses foraarch64-apple-darwin,aarch64-apple-ios,aarch64-linux-android,x86_64-unknown-linux-gnuandwasm32-unknown-unknown.