Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebGPURenderer: Render Bundle + Shared Bind Group (WIP) #28719

Closed
wants to merge 4 commits into from

Conversation

sunag
Copy link
Collaborator

@sunag sunag commented Jun 22, 2024

Related issue: #28347, #28705

Description

Shared Bind Groups bring great results in rendering optimization, together with the Render Bundle it is beautiful to see, it was so simple when setting object3d.static=true to get this result.

Live example

no bundle bundle bundle + shared bind group
13.74ms 10.81ms 3.82ms
image image image

@sunag sunag added this to the r166 milestone Jun 22, 2024
@RenaudRohlinger
Copy link
Collaborator

Incredible. This PR marks a significant leap forward as the WebGPURenderer will now offers way better performance and a more modern API, surpassing the WebGLRenderer and aligning with the latest 3D API.

The UBO and static part will also benefit the WebGLBackend.

An interesting example could also be a split-screen camera view (3D Editor Tools UI / Mario Kart style) rendering the same scene multiple times from different camera angles.

@RenaudRohlinger
Copy link
Collaborator

RenaudRohlinger commented Jun 22, 2024

Currently, one thing we need to figure out soon is how to update a specific object within the bundle.

Using something like group.children[i].needsUpdate = true seems the most straightforward approach. This means we might need to keep a reference somewhere of all the meshes in the bundle within the Renderer so we can handle per-mesh updates and visibility state.

Additionally, we can later introduce frustum culling via indirect draw calls.

For example:

_renderBundle( bundle, sceneRef, lightsNode ) {

if ( renderBundleNeedsUpdate ) {
  ...
} else {
  
  const renderContext = this._currentRenderContext;
  const renderContextData = this.backend.get( renderContext );
  
  for ( let i = 0, l = renderContextData.renderObjects.length; i < l; i ++ ) {
  
  const renderObject = renderContextData.renderObjects[ i ];
  
  if ( renderObject.object.needsUpdate === true) {
	// TODO 1: ... update object here if needed
  }
  if ( renderObject.object.visible !== prevVisible ) {
	// TODO 2: ... handle .visible flag here and maybe layers?
  }
  if ( renderObject.object.frustumCulled === true ) {
	// TODO 3: handle frustum culling via indirectDraw call in WebGPU and CPU in WebGL
  }
  
  this._nodes.updateForRender( renderObject, renderGroup );
  this._bindings.updateForRender( renderObject, renderGroup );
  
  this.backend.draw( renderObject, this.info );
  
  }
  
}

/cc @sunag

@nkallen
Copy link
Contributor

nkallen commented Jun 24, 2024

Great progress -

Can I suggest: I prefer to avoid interfaces like this:

group.children[i].needsUpdate = true

Which are going to guarantee O(n) iterations

Write "batch" interfaces

group.setChildNeedsUpdate(i) 
group.needsUpdate(i)
group.needsUpdate(child[i])

(or whatever)

Batch operations allow for O(1) updates and more generally allow batching work performance optimizations. Of course there are many factors to balance in designing an API.

@sunag
Copy link
Collaborator Author

sunag commented Jun 24, 2024

I think that when we have sub-children it is better to define the property on the object instead of on the group, since the renderer deals with RenderObject, it seems easier to implement it as well.

I am inclined to add object class terms to handle this, like StaticGroup() or BundleGroup() instead of group.static for example. group.static seems to limit the evolution of the API a bit in the sense that we will have different optimization techniques for dynamic and static objects in this context. I think mesh.static could be better inside a BundleGroup for example. /cc @mrdoob

@mrdoob
Copy link
Owner

mrdoob commented Jun 28, 2024

BundleGroup sounds good to me 👍

@mrdoob
Copy link
Owner

mrdoob commented Jun 28, 2024

@nkallen

Write "batch" interfaces

group.setChildNeedsUpdate(i) 
group.needsUpdate(i)
group.needsUpdate(child[i])

(or whatever)

These are interesting...

How about:

group.updateChildAt(i)

Copy link

github-actions bot commented Sep 3, 2024

📦 Bundle size

Full ESM build, minified and gzipped.

Before After Diff
WebGL 685.1 kB
169.6 kB
685.1 kB
169.6 kB
+0 B
+0 B
WebGPU 822.6 kB
220.8 kB
822.7 kB
220.8 kB
+22 B
+0 B
WebGPU Nodes 822.2 kB
220.7 kB
822.2 kB
220.7 kB
+441 B
+95 B

🌳 Bundle size after tree-shaking

Minimal build including a renderer, camera, empty scene, and dependencies.

Before After Diff
WebGL 461.9 kB
111.4 kB
461.9 kB
111.4 kB
+0 B
+0 B
WebGPU 521.7 kB
140.7 kB
534.5 kB
144.5 kB
+12.79 kB
+3.85 kB
WebGPU Nodes 478.4 kB
130.5 kB
478.8 kB
130.7 kB
-42.99 kB
+224 B

@sunag sunag removed this from the r169 milestone Sep 3, 2024
@sunag
Copy link
Collaborator Author

sunag commented Sep 3, 2024

@RenaudRohlinger @aardgoose I think all related implementations have been done in separate PR. Thanks for the help 🙏

@sunag sunag closed this Sep 3, 2024
@sunag sunag deleted the dev-renderbundle branch September 3, 2024 18:36
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.

None yet

4 participants