Skip to content

Fix/webgpu crash pixel density#8476

Open
saurabh24thakur wants to merge 2 commits intoprocessing:dev-2.0from
saurabh24thakur:fix/webgpu-crash-pixel-density
Open

Fix/webgpu crash pixel density#8476
saurabh24thakur wants to merge 2 commits intoprocessing:dev-2.0from
saurabh24thakur:fix/webgpu-crash-pixel-density

Conversation

@saurabh24thakur
Copy link

@saurabh24thakur saurabh24thakur commented Feb 1, 2026

Fixes #8456

Description

Fixes a crash when using the WebGPU renderer where calling pixelDensity() immediately after setAttributes() (or any other function that triggers an asynchronous context reset) would result in TypeError: Cannot read properties of undefined (reading 'createTexture').

The Issue

In p5.js 2.0, setAttributes() triggers an asynchronous reset of the rendering context via _resetContext(). Because core functions like pixelDensity() are synchronous, they can trigger a resize() (and subsequently _updateSize()) on a new renderer instance before its WebGPU device has been successfully initialized.

Changes

  1. RendererWebGPU: Added guard clauses to _updateSize(), clear(), and clearDepth() to return early if this.device is not yet defined. Since _initContext() calls these methods upon successful initialization, the state is eventually synchronized correctly once the device is ready.
  2. Renderer3D:
    • Updated _resetContext() to await the new renderer's contextReady promise before replacing the existing renderer on the p5 instance. This ensures that the sketch's _renderer property always points to a valid, initialized renderer.
    • Ensured the new renderer inherits the pixelDensity of the renderer it is replacing to avoid size-mismatch warnings in the console (e.g., depth-stencil attachment size mismatches).
  3. Tests: Added a new unit test in test/unit/webgpu/issue_repro.js that specifically reproduces this race condition.

Checklist

  • I have read the CONTRIBUTING document.
  • My code follows the style guidelines of this project.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • My changes generate no new warnings.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.

- Added guard clauses in RendererWebGPU to handle uninitialized devices.
- Improved Renderer3D._resetContext to wait for context initialization and inherit pixel density.
- Added regression test in test/unit/webgpu/issue_repro.js.
@saurabh24thakur saurabh24thakur changed the base branch from main to dev-2.0 February 1, 2026 05:00

// If we reach here, no immediate crash occurred.
// Let's wait for the context to finish resetting to be sure everything is stable.
await new Promise(resolve => setTimeout(resolve, 100));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Timeouts in tests are fairly brittle, we can use expect(() => { ... }).not.toThrow() or something like that as an alternative.

myp5 = new p5(function(p) {
p.setup = async function() {
try {
await p.createCanvas(100, 100, 'webgpu');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use p.WEBGPU rather than the value of the constant?

await p.createCanvas(100, 100, 'webgpu');

// This triggers an asynchronous _resetContext
p.setAttributes({ powerPreference: 'low-power' });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure that we'll do something with this, can we try changing antialias as a more normal example?

@@ -0,0 +1,51 @@
import p5 from '../../../src/app.js';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new file called issue_repro.js is not really readable for future contributors trying to understand the structure of our code and tests. Could this just be a test in the existing WebGPU renderer tests?

}

_updateSize() {
if (!this.device) return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, is this added just to methods that get called automatically when you reinitialize the context?

@davepagurek
Copy link
Contributor

Thanks for working on this! Left a few comments, let me know what you think!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants