Skip to content

WebGPURenderer: Material initialization is extremely slow compared to WebGLRenderer #33821

Description

@gkjohnson

Description

Related to #33797

For the three-edge-projection I'm running a visibility culling pass to filter meshes that aren't visible. This means rendering every object with a unique id - with a CAD model this can be tens of thousands of meshes. I'm seeing that generally WebGLRenderer is significantly faster than WebGPURenderer for this initial render. As a result I'm having to use a WebGL context to perform the visibility culling while using a WebGPU context to do the actual edge projection.

I'm trying two different methods to render the Ids:

Method 1

Add every mesh to a scene with a dedicated material with "id" uniform and render everything together. I've created sample fiddles showing a basic representation with 10,000 meshes:

WebGPURenderer WebGLRenderer
fiddle https://jsfiddle.net/bLsjfy8c/ https://jsfiddle.net/w1kmp5h4/2/
timing ~2100ms ~131ms

So here WebGPURenderer is ~16x slower to initialize and render the ids.

Method 2

Render each mesh individually and reuse a single material and modify the id uniform in place.

WebGPURenderer WebGLRenderer
fiddle https://jsfiddle.net/pzcsu6jt/1/ https://jsfiddle.net/e0s7rzkq/1/
timing ~1029ms ~28ms

And here WebGPURenderer is ~36x slower to initialize and render the ids.

--

I'm not fully clear on how the internals of the node material building works but given how the materials are structured it seems like it's necessary to rebuild / analyze the full shader graph for every material given how uniform nodes are embedded in the node graph. I'm wondering if architecturally there would be value in decoupling the uniform values from the actual node graph with uniform "slots" used to define the shader?

In my mind, once the node graph is used to define a shader it should be able to reused independently of what uniforms are being defined by the user. There's be a lot to consider for a change like this but at a high level something like this comes to mind:

// define the node graph with *named* uniforms
const shader = new MeshBasicNodeShader();
shader.colorNode = uniform( 'vec4', '_id' );

// create a material constructor that references that
const IDMaterial = Material.fromShader( shader );

for ( let i = 0; i < 10000; i ++ ) {

  // named uniforms are exposed for easy modification, no need to re-traverse the node graph
  const material = new IDMaterial();
  material.uniforms._id = i;
  const mesh = new Mesh( geometry );

}

Reproduction steps

  1. Render custom materials in WebGPURenderer and WebGLRenderer
  2. See that WebGPURenderer is slower

Code

See fiddles

Live example

See main description

Screenshots

No response

Version

dev

Device

Macbook Pro M1 Pro
Windows 10 Intel i7-7700K, NVidia RTX 2070 Super

Browser

Chrome, Safari, Firefox (OSX)
Chrome (Windows)

OS

Tahoe 26.5
Windows 10

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions