[WIP] feat: Asynchronous pipeline creation#2604
Conversation
|
pkg.pr.new packages benchmark commit |
📊 Bundle Size Comparison
👀 Notable resultsStatic test results:No major changes. Dynamic test results:No major changes. 📋 All resultsClick to reveal the results table (355 entries).
If you wish to run a comparison for other, slower bundlers, run the 'Tree-shake test' from the GitHub Actions menu. |
Resolution Time Benchmark---
config:
themeVariables:
xyChart:
plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
title "Random Branching (🔴 PR | 🔵 main | 🟢 release)"
x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
y-axis "time (ms)"
line [0.79, 1.62, 3.42, 5.58, 6.56, 8.98, 19.26, 18.94]
line [0.86, 1.68, 3.45, 5.10, 6.24, 10.09, 18.93, 21.71]
line [0.81, 1.62, 3.80, 5.16, 6.11, 9.50, 19.05, 22.42]
---
config:
themeVariables:
xyChart:
plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
title "Linear Recursion (🔴 PR | 🔵 main | 🟢 release)"
x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
y-axis "time (ms)"
line [0.29, 0.52, 0.64, 0.74, 1.15, 1.12, 1.23, 1.37]
line [0.32, 0.49, 0.60, 0.73, 1.04, 1.13, 1.32, 1.46]
line [0.27, 0.44, 0.61, 0.73, 1.00, 1.05, 1.25, 1.39]
---
config:
themeVariables:
xyChart:
plotColorPalette: "#E63946, #3B82F6, #059669"
---
xychart
title "Full Tree (🔴 PR | 🔵 main | 🟢 release)"
x-axis "max depth" [1, 2, 3, 4, 5, 6, 7, 8]
y-axis "time (ms)"
line [0.71, 1.77, 3.31, 5.66, 11.05, 22.50, 46.89, 99.53]
line [0.77, 1.90, 3.72, 5.52, 10.44, 22.01, 48.49, 98.32]
line [0.91, 1.98, 3.99, 6.13, 10.98, 22.25, 48.14, 99.25]
|
9fe9b72 to
de777f1
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces explicit pipeline initialization APIs for compute pipelines, enabling asynchronous compilation to reduce first-dispatch stalls, and updates docs/examples/tests to exercise the new behavior.
Changes:
- Refactors
ComputePipelineCoreand addsinitAsync()/initSync()to compute pipelines. - Updates multiple docs examples to eagerly initialize compute pipelines (primarily via
initAsync()). - Adds unit tests and updates docs/example snapshots to reflect the new initialization flow.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/typegpu/src/core/pipeline/computePipeline.ts | Adds initAsync/initSync, refactors memoization/initialization flow, and moves perf tracking into a helper. |
| packages/typegpu/tests/pipelineInit.test.ts | Adds tests covering sync/async initialization, caching, and unwrap behavior during async init. |
| packages/typegpu-testing-utility/src/extendedIt.ts | Extends the GPU device mock with createComputePipelineAsync for new tests. |
| apps/typegpu-docs/src/examples/simulation/stable-fluid/index.ts | Eagerly initializes all compute pipelines with initAsync() to avoid first-frame stalls. |
| apps/typegpu-docs/src/examples/simulation/gravity/index.ts | Eagerly initializes compute pipelines with initAsync(). |
| apps/typegpu-docs/src/examples/rendering/function-visualizer/index.ts | Eagerly initializes dynamically-created guarded compute pipelines with initAsync(). |
| apps/typegpu-docs/src/examples/image-processing/background-segmentation/index.ts | Eagerly initializes guarded compute pipelines via initSync() before use. |
| apps/typegpu-docs/src/examples/algorithms/mnist-inference/index.ts | Eagerly initializes compute pipelines via initAsync() (including optional subgroup pipeline). |
| apps/typegpu-docs/src/examples/algorithms/matrix-next/index.ts | Eagerly initializes both compute strategies via initAsync(). |
| apps/typegpu-docs/tests/individual-example-tests/stable-fluid.test.ts | Updates expected shader-module call count and snapshot output. |
| apps/typegpu-docs/tests/individual-example-tests/mnist-inference.test.ts | Updates shader snapshot output ordering/content. |
| apps/typegpu-docs/tests/individual-example-tests/matrix-next.test.ts | Updates expected shader-module call count and snapshot output. |
| apps/typegpu-docs/src/content/docs/apis/pipelines.mdx | Documents pipeline initialization and shows initSync/initAsync usage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Pipeline initialization involves resolving the pipeline code, creating the shader module, and creating the underlying WebGPU pipeline. | ||
| This happens automatically the first time the pipeline is executed via `.draw`, `.dispatchWorkgroups`, or a similar method. | ||
| The `initSync` and `initAsync` methods let you perform this work ahead of time, avoiding a stall on first execution. |
There was a problem hiding this comment.
I'm missing the explanation of when one should use initSync as opposed to initAsync and vice versa. On the other hand, it's non-trivial to explain it briefly 🤔
Maybe:
| Pipeline initialization involves resolving the pipeline code, creating the shader module, and creating the underlying WebGPU pipeline. | |
| This happens automatically the first time the pipeline is executed via `.draw`, `.dispatchWorkgroups`, or a similar method. | |
| The `initSync` and `initAsync` methods let you perform this work ahead of time, avoiding a stall on first execution. | |
| Pipeline initialization involves resolving the pipeline code, creating the shader module, and creating the underlying WebGPU pipeline. | |
| This happens automatically the first time the pipeline is executed via `.draw`, `.dispatchWorkgroups`, or a similar method. | |
| The `initSync` function lets you jump-start that initialization on the WebGPU process early. To await until the initialization actually finishes and avoid a stall on first execution, you can use the `initAsync` function instead. |
There was a problem hiding this comment.
I updated this paragraph and added commented snippets, tell me what you think
| }, | ||
| }); | ||
|
|
||
| await Promise.all([computeCollisionsPipeline.initAsync(), computeGravityPipeline.initAsync()]); |
There was a problem hiding this comment.
I'd prewarm these promises early:
| await Promise.all([computeCollisionsPipeline.initAsync(), computeGravityPipeline.initAsync()]); | |
| // Initializing all used pipelines | |
| const pipelinePromises = [ | |
| computeCollisionsPipeline.initAsync(), | |
| computeGravityPipeline.initAsync(), | |
| skyBoxPipeline.initAsync(), | |
| renderPipeline.initAsync(), | |
| ]; |
And await them right before requesting the first frame:
// Only requesting the first frame after all pipelines have been initialized
await Promise.all(pipelinePromises);
requestAnimationFrame(frame);On another note, why aren't we preinitializing the render pipelines?
There was a problem hiding this comment.
we aren't because they do not yethave initAsync/initSync methods - as I mentioned, I wanted to wait with them for an initial review so as not to do the same fixes twice
reczkok
left a comment
There was a problem hiding this comment.
Looks nice, I like the performance tracking approach
| ## Initialization | ||
|
|
||
| Pipeline initialization involves resolving the pipeline code, creating the shader module, and creating the underlying WebGPU pipeline. | ||
| This happens automatically the first time the pipeline is executed via `.draw`, `.dispatchWorkgroups`, or a similar method. | ||
| The `initSync` method lets you start the initialization early. | ||
| To wait until initialization actually finishes on the device (fully avoiding a stall on first execution), use `initAsync` instead. |
There was a problem hiding this comment.
I would stress here (and especially in the AI Skill) that this is not that important for small/mid sized shaders - only makes sense when you have a bigger application or shaders that are very big or have very many of them) - AI is prone to overusing solutions like those.
|
|
||
| let isComputing = false; | ||
|
|
||
| await Promise.all(pipelinePromises); |
There was a problem hiding this comment.
I feel like we can await it even later (might make no difference tho)
Changes:
ComputePipelineCore,initAsyncandinitSync,initAsync,The PERF is set to true only in browser tests, which currently do not work.
Also, I think that the measurements are just incorrect.
Still, I opted to leave them as is in a separate class. I tested manually the results before and after on Stable Fluids (7 resolves), and they were similar, so I don't think any logic changed.
For 9 pipelines in StableFluids, the async initialization actually seems slower (0.015s for the await) than sync (0.008s).
Please, @reczkok @iwoplaza @cieplypolar let me know if this looks alright, then I'll update skills and render pipeline (probably 1:1 changes from compute).