diff --git a/webgpu/lessons/webgpu-optimization.md b/webgpu/lessons/webgpu-optimization.md index 4c554731..fb868b0a 100644 --- a/webgpu/lessons/webgpu-optimization.md +++ b/webgpu/lessons/webgpu-optimization.md @@ -466,12 +466,10 @@ We need a simple UI so we can adjust how many things we're drawing. ```js const settings = { numObjects: 1000, - render: true, }; const gui = new GUI(); gui.add(settings, 'numObjects', { min: 0, max: maxObjects, step: 1}); - gui.add(settings, 'render'); ``` Now we can write our render loop. @@ -643,6 +641,7 @@ A few more things left to do. Let's add in resizing then = time; + const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; ++ + // Don't set the canvas size if it's already that size as it may be slow. + if (canvas.width !== width || canvas.height !== height) { + canvas.width = width; @@ -844,7 +843,7 @@ async function main() { requestAnimationFrame(render); ``` -And finally we need to show the timing +And we need to show the timing ```js async function main() { @@ -879,6 +878,46 @@ async function main() { requestAnimationFrame(render); ``` +One more thing, just to help with better comparisons. An issue we have now +is, every visible cube has every pixel rendered or at least checked if it +needs to be rendered. Since we're not optimizing the rendering of pixels +but rather optimizing the usage of WebGPU itself, it can be useful to be +able to draw to a 1x1 pixel canvas. This effectively removes nearly all +of the time spend rasterizing triangles and instead leaves only the part +of our code that is doing math and communicating with WebGPU. + +So let's add an option to do that + +```js + const settings = { + numObjects: 1000, ++ render: true, + }; + + const gui = new GUI(); + gui.add(settings, 'numObjects', { min: 0, max: maxObjects, step: 1}); ++ gui.add(settings, 'render'); + + let depthTexture; + let then = 0; + let frameCount = 0; + + function render(time) { + time *= 0.001; // convert to seconds + const deltaTime = time - then; + then = time; + ++frameCount; + + const startTimeMs = performance.now(); + +- const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; ++ const {width, height} = settings.render ++ ? canvasToSizeMap.get(canvas) ?? canvas ++ : { width: 1, height: 1 }; +``` + +Now, if we uncheck 'render', we'll remove almost all of the um, ahh ..., rendering. + And with that, we have our first "un-optimized" example. It's following the steps listed near the top of the article, and it works. @@ -2009,6 +2048,10 @@ which is almost 60% more than we started with. {{{example url="../webgpu-optimization-step6-use-mapped-buffers.html"}}} +With rendering unchecked, the difference is even better. For me I get +9000 at 75fps with the original non-optimized example and 18000 at 75fps +in this last version. That's a 2x speed up! + Other things that *might* help * **Double buffer the large uniform buffer** diff --git a/webgpu/lessons/webgpu-timing.md b/webgpu/lessons/webgpu-timing.md index 83c9d96b..f1392160 100644 --- a/webgpu/lessons/webgpu-timing.md +++ b/webgpu/lessons/webgpu-timing.md @@ -2,11 +2,6 @@ Title: WebGPU Timing Performance Description: Timing operations in WebGPU TOC: Timing Performance -
The `'timestamp-query'` feature used in this article -should be available in Chrome 121 or 122. If it's not available you can probably -turn it on by enabling on enable-webgpu-developer-features in about:flags. -
- Let's go over various things you might want to time for performance. We'll time 3 things: @@ -825,6 +820,8 @@ async function main() { ... ``` +{{{example url="../webgpu-timing-with-timing-helper.html"}}} + A few points about the `TimingHelper` class: * You still have to manually request the `'timestamp-query'` feature when you diff --git a/webgpu/webgl-optimization-none-uniform-buffers.html b/webgpu/webgl-optimization-none-uniform-buffers.html index 48b40b67..ab103c0d 100644 --- a/webgpu/webgl-optimization-none-uniform-buffers.html +++ b/webgpu/webgl-optimization-none-uniform-buffers.html @@ -358,7 +358,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgl-optimization-none.html b/webgpu/webgl-optimization-none.html index 28ff4b10..04c05c89 100644 --- a/webgpu/webgl-optimization-none.html +++ b/webgpu/webgl-optimization-none.html @@ -355,7 +355,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-all.html b/webgpu/webgpu-optimization-all.html index ea29f48e..88471889 100644 --- a/webgpu/webgpu-optimization-all.html +++ b/webgpu/webgpu-optimization-all.html @@ -460,7 +460,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-none.html b/webgpu/webgpu-optimization-none.html index 2989ac35..77b26c65 100644 --- a/webgpu/webgpu-optimization-none.html +++ b/webgpu/webgpu-optimization-none.html @@ -432,7 +432,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-step3-global-vs-per-object-uniforms.html b/webgpu/webgpu-optimization-step3-global-vs-per-object-uniforms.html index 2c7d919e..320824df 100644 --- a/webgpu/webgpu-optimization-step3-global-vs-per-object-uniforms.html +++ b/webgpu/webgpu-optimization-step3-global-vs-per-object-uniforms.html @@ -456,7 +456,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-step4-material-uniforms.html b/webgpu/webgpu-optimization-step4-material-uniforms.html index 27c1b894..b7031037 100644 --- a/webgpu/webgpu-optimization-step4-material-uniforms.html +++ b/webgpu/webgpu-optimization-step4-material-uniforms.html @@ -465,7 +465,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-step5-double-buffer-frequently-updated-uniform-buffers-pre-submit.html b/webgpu/webgpu-optimization-step5-double-buffer-frequently-updated-uniform-buffers-pre-submit.html index 5e299854..cb167cc4 100644 --- a/webgpu/webgpu-optimization-step5-double-buffer-frequently-updated-uniform-buffers-pre-submit.html +++ b/webgpu/webgpu-optimization-step5-double-buffer-frequently-updated-uniform-buffers-pre-submit.html @@ -477,7 +477,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-step5-double-buffer-frequently-updated-uniform-buffers.html b/webgpu/webgpu-optimization-step5-double-buffer-frequently-updated-uniform-buffers.html index 5e299854..cb167cc4 100644 --- a/webgpu/webgpu-optimization-step5-double-buffer-frequently-updated-uniform-buffers.html +++ b/webgpu/webgpu-optimization-step5-double-buffer-frequently-updated-uniform-buffers.html @@ -477,7 +477,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-step5-use-buffer-offsets.html b/webgpu/webgpu-optimization-step5-use-buffer-offsets.html index 3efac7ac..bd16b55f 100644 --- a/webgpu/webgpu-optimization-step5-use-buffer-offsets.html +++ b/webgpu/webgpu-optimization-step5-use-buffer-offsets.html @@ -468,7 +468,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-step6-use-mapped-buffers-2-command-buffers.html b/webgpu/webgpu-optimization-step6-use-mapped-buffers-2-command-buffers.html index be325171..2c8809de 100644 --- a/webgpu/webgpu-optimization-step6-use-mapped-buffers-2-command-buffers.html +++ b/webgpu/webgpu-optimization-step6-use-mapped-buffers-2-command-buffers.html @@ -467,7 +467,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-step6-use-mapped-buffers-dyanmic-offsets.html b/webgpu/webgpu-optimization-step6-use-mapped-buffers-dyanmic-offsets.html index a81aa00b..1d7ca9a8 100644 --- a/webgpu/webgpu-optimization-step6-use-mapped-buffers-dyanmic-offsets.html +++ b/webgpu/webgpu-optimization-step6-use-mapped-buffers-dyanmic-offsets.html @@ -505,7 +505,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-step6-use-mapped-buffers-math-w-offsets.html b/webgpu/webgpu-optimization-step6-use-mapped-buffers-math-w-offsets.html index d1811819..ffe102cf 100644 --- a/webgpu/webgpu-optimization-step6-use-mapped-buffers-math-w-offsets.html +++ b/webgpu/webgpu-optimization-step6-use-mapped-buffers-math-w-offsets.html @@ -1027,7 +1027,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-step6-use-mapped-buffers.html b/webgpu/webgpu-optimization-step6-use-mapped-buffers.html index 9e3b8ee4..a70a8d6c 100644 --- a/webgpu/webgpu-optimization-step6-use-mapped-buffers.html +++ b/webgpu/webgpu-optimization-step6-use-mapped-buffers.html @@ -467,7 +467,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-step7-double-buffer-2-submit.html b/webgpu/webgpu-optimization-step7-double-buffer-2-submit.html index 8c64cedb..cc32e983 100644 --- a/webgpu/webgpu-optimization-step7-double-buffer-2-submit.html +++ b/webgpu/webgpu-optimization-step7-double-buffer-2-submit.html @@ -469,7 +469,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-step7-double-buffer-typedarray-set.html b/webgpu/webgpu-optimization-step7-double-buffer-typedarray-set.html index bbade6c2..89ce54ac 100644 --- a/webgpu/webgpu-optimization-step7-double-buffer-typedarray-set.html +++ b/webgpu/webgpu-optimization-step7-double-buffer-typedarray-set.html @@ -472,7 +472,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width; diff --git a/webgpu/webgpu-optimization-step7-double-buffer.html b/webgpu/webgpu-optimization-step7-double-buffer.html index 90cbee2e..45b3d7a1 100644 --- a/webgpu/webgpu-optimization-step7-double-buffer.html +++ b/webgpu/webgpu-optimization-step7-double-buffer.html @@ -469,7 +469,10 @@ const startTimeMs = performance.now(); - const {width, height} = canvasToSizeMap.get(canvas) ?? canvas; + const {width, height} = settings.render + ? canvasToSizeMap.get(canvas) ?? canvas + : { width: 1, height: 1 }; + // Don't set the canvas size if it's already that size as it may be slow. if (canvas.width !== width || canvas.height !== height) { canvas.width = width;