Repository navigation
A bug-fix release: the canvas is now sized before renderer.ready resolves.
The bug
Until the Renderer's first ResizeObserver callback, the WebGPU canvas kept the browser's default backing size of 300×150. That callback is asynchronous. It often arrives while an app is still awaiting assets, but it can also land after init has finished. Anything that read canvas.width / canvas.height in that window got the wrong size:
- Camera aspect. The
renderbundleexample builds its camera fromcanvas.width / canvas.heightstraight afterready, and listens only towindowresize, which never fires at boot. On 0.20.0 its camera aspect is 2.0 (300/150) on a 1280×720 canvas, so the scene is squashed by 11% until you resize the window. On 0.20.1 the aspect is 1.778. - Render targets and first frames. Targets sized from the canvas during init were allocated at 300×150, and the first frames rendered at that size until a resize corrected them. Anything temporal (feedback, accumulation) then carried those frames forward. Whether it happened depended on load timing, so the same page could render differently from one load to the next.
- The
setTimeoutworkaround. 19 examples calledhandleResizea second time fromsetTimeout(…, 150–350)to catch the real size. That is a wall-clock race: on a slow load the timer fires before the observer, and on a fast one after.
The fix
Renderer.initsizes the canvas and the depth texture beforereadyresolves. The size isgetBoundingClientRect()×devicePixelRatio, with thedprcap, rounded the same way as the resize path. (devicePixelContentBoxSizeonly exists on an observer entry.)handleResizeskips an entry whose computed size matches the current one. The observer's first callback, which now normally repeats the pre-sized dimensions, no longer remakes the depth texture or re-fires resize handlers.- The examples size themselves with
renderer.addResizeHandler(this.handleResize)plus one directthis.handleResize()call, replacingwindowresizeand the timers.
Behaviour change
addResizeHandler callbacks now fire only when the canvas backing size actually changes. The canvas is already correctly sized when ready resolves, so a handler added after ready isn't called until a real resize. If your app relied on the first observer callback to run its handler, call the handler once yourself after subscribing:
await renderer.ready;
// … build the scene …
renderer.addResizeHandler(this.handleResize);
this.handleResize(); // size from the current canvasApps that already call their handler directly, or still use a setTimeout fallback, keep working unchanged.
Full changelog: CHANGELOG.md