Skip to content

Commit

Permalink
WebGPURenderer: RenderBundle (#28347)
Browse files Browse the repository at this point in the history
* wip

* add demo

* add gpu metrics

* fix bundeType condition

* cleanup

* refactor and cleanup

* support postprocess and multisample

* update

* cache and pbr on bundle example

* wip static mode

* update

* update

* revert shared

* ci

* circular dep

* move the logic to the renderContext

* add screenshot for ci

* cleanup

* new RenderBundle API

* TODO: Need to handle FBO too

* cleanup

* more cleanup

* fix deepscan

* fix framebuffer

* update example

* update scene too

* reuse correct scene for update matrices

* introduce renderBundle.needsUpdate and rename to private _renderBundle()

* cleanup

* improve example

* fix capsule constructor

* remove confusing gui in example

* Adding RenderBundles and Group.static

---------

Co-authored-by: sunag <[email protected]>
  • Loading branch information
RenaudRohlinger and sunag authored May 23, 2024
1 parent 2f55b35 commit 82b78e7
Show file tree
Hide file tree
Showing 10 changed files with 603 additions and 17 deletions.
3 changes: 2 additions & 1 deletion examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,8 @@
"webgpu_instancing_morph",
"webgpu_texturegrad",
"webgpu_volume_cloud",
"webgpu_volume_perlin"
"webgpu_volume_perlin",
"webgpu_renderbundle"
],
"webaudio": [
"webaudio_orientation",
Expand Down
18 changes: 18 additions & 0 deletions examples/jsm/renderers/common/RenderBundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class RenderBundle {

constructor( scene, camera ) {

this.scene = scene;
this.camera = camera;

}

clone() {

return Object.assign( new this.constructor(), this );

}

}

export default RenderBundle;
38 changes: 38 additions & 0 deletions examples/jsm/renderers/common/RenderBundles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import ChainMap from './ChainMap.js';
import RenderBundle from './RenderBundle.js';

class RenderBundles {

constructor() {

this.lists = new ChainMap();

}

get( scene, camera ) {

const lists = this.lists;
const keys = [ scene, camera ];

let list = lists.get( keys );

if ( list === undefined ) {

list = new RenderBundle( scene, camera );
lists.set( keys, list );

}

return list;

}

dispose() {

this.lists = new ChainMap();

}

}

export default RenderBundles;
9 changes: 9 additions & 0 deletions examples/jsm/renderers/common/RenderList.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class RenderList {

this.opaque = [];
this.transparent = [];
this.bundles = [];

this.lightsNode = new LightsNode( [] );
this.lightsArray = [];
Expand All @@ -71,6 +72,8 @@ class RenderList {

this.opaque.length = 0;
this.transparent.length = 0;
this.bundles.length = 0;

this.lightsArray.length = 0;

this.occlusionQueryCount = 0;
Expand Down Expand Up @@ -135,6 +138,12 @@ class RenderList {

}

pushBundle( group ) {

this.bundles.push( group );

}

pushLight( light ) {

this.lightsArray.push( light );
Expand Down
128 changes: 127 additions & 1 deletion examples/jsm/renderers/common/Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import ClippingContext from './ClippingContext.js';
import { Scene, Frustum, Matrix4, Vector2, Vector3, Vector4, DoubleSide, BackSide, FrontSide, SRGBColorSpace, NoColorSpace, NoToneMapping, LinearFilter, LinearSRGBColorSpace, RenderTarget, HalfFloatType, RGBAFormat } from 'three';
import { NodeMaterial } from '../../nodes/Nodes.js';
import QuadMesh from '../../objects/QuadMesh.js';
import RenderBundles from './RenderBundles.js';

const _scene = new Scene();
const _drawingBufferSize = new Vector2();
Expand Down Expand Up @@ -87,6 +88,7 @@ class Renderer {
this._bindings = null;
this._objects = null;
this._pipelines = null;
this._bundles = null;
this._renderLists = null;
this._renderContexts = null;
this._textures = null;
Expand All @@ -111,6 +113,7 @@ class Renderer {

this._renderObjectFunction = null;
this._currentRenderObjectFunction = null;
this._currentRenderBundle = null;

this._handleObjectFunction = this._renderObjectDirect;

Expand Down Expand Up @@ -171,6 +174,7 @@ class Renderer {
this._bindings = new Bindings( backend, this._nodes, this._textures, this._attributes, this._pipelines, this.info );
this._objects = new RenderObjects( this, this._nodes, this._geometries, this._pipelines, this._bindings, this.info );
this._renderLists = new RenderLists();
this._bundles = new RenderBundles();
this._renderContexts = new RenderContexts();

//
Expand Down Expand Up @@ -326,6 +330,81 @@ class Renderer {

}

_renderBundle( bundle, sceneRef, lightsNode ) {

const { object, camera, renderList } = bundle;

const renderContext = this._currentRenderContext;
const renderContextData = this.backend.get( renderContext );

//

const renderBundle = this._bundles.get( object, camera );

const renderBundleData = this.backend.get( renderBundle );
if ( renderBundleData.renderContexts === undefined ) renderBundleData.renderContexts = new Set();

//

const renderBundleNeedsUpdate = renderBundleData.renderContexts.has( renderContext ) === false || object.needsUpdate === true;

renderBundleData.renderContexts.add( renderContext );

if ( renderBundleNeedsUpdate ) {

if ( renderContextData.renderObjects === undefined || object.needsUpdate === true ) {

const nodeFrame = this._nodes.nodeFrame;

renderContextData.renderObjects = [];
renderContextData.renderBundles = [];
renderContextData.scene = sceneRef;
renderContextData.camera = camera;
renderContextData.renderId = nodeFrame.renderId;

renderContextData.registerBundlesPhase = true;

}

this._currentRenderBundle = renderBundle;

const opaqueObjects = renderList.opaque;

if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, sceneRef, lightsNode );

this._currentRenderBundle = null;

//

object.needsUpdate = false;

} 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 ];

this._nodes.updateBefore( renderObject );

//

renderObject.object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, renderObject.object.matrixWorld );
renderObject.object.normalMatrix.getNormalMatrix( renderObject.object.modelViewMatrix );

this._nodes.updateForRender( renderObject );
this._bindings.updateForRender( renderObject );

this.backend.draw( renderObject, this.info );

}

}

}

render( scene, camera ) {

if ( this._initialized === false ) {
Expand Down Expand Up @@ -456,7 +535,6 @@ class Renderer {

if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();


//

let viewport = this._viewport;
Expand Down Expand Up @@ -564,8 +642,10 @@ class Renderer {

const opaqueObjects = renderList.opaque;
const transparentObjects = renderList.transparent;
const bundles = renderList.bundles;
const lightsNode = renderList.lightsNode;

if ( bundles.length > 0 ) this._renderBundles( bundles, sceneRef, lightsNode );
if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, sceneRef, lightsNode );
if ( transparentObjects.length > 0 ) this._renderObjects( transparentObjects, camera, sceneRef, lightsNode );

Expand Down Expand Up @@ -1194,6 +1274,25 @@ class Renderer {

}

if ( object.static === true ) {

const baseRenderList = renderList;

// replace render list
renderList = this._renderLists.get( object, camera );

renderList.begin();

baseRenderList.pushBundle( {
object,
camera,
renderList,
} );

renderList.finish();

}

const children = object.children;

for ( let i = 0, l = children.length; i < l; i ++ ) {
Expand All @@ -1204,6 +1303,16 @@ class Renderer {

}

_renderBundles( bundles, sceneRef, lightsNode ) {

for ( const bundle of bundles ) {

this._renderBundle( bundle, sceneRef, lightsNode );

}

}

_renderObjects( renderList, camera, scene, lightsNode ) {

// process renderable objects
Expand Down Expand Up @@ -1397,8 +1506,25 @@ class Renderer {

//

if ( this._currentRenderBundle !== null && this._currentRenderBundle.needsUpdate === true ) {

const renderObjectData = this.backend.get( renderObject );

renderObjectData.bundleEncoder = undefined;
renderObjectData.lastPipelineGPU = undefined;

}

this.backend.draw( renderObject, this.info );

if ( this._currentRenderBundle !== null ) {

const renderContextData = this.backend.get( this._currentRenderContext );

renderContextData.renderObjects.push( renderObject );

}

}

_createObjectPipeline( object, material, scene, camera, lightsNode, passId ) {
Expand Down
40 changes: 38 additions & 2 deletions examples/jsm/renderers/webgpu/WebGPUBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,13 @@ class WebGPUBackend extends Backend {
const renderContextData = this.get( renderContext );
const occlusionQueryCount = renderContext.occlusionQueryCount;

if ( renderContextData.renderBundles !== undefined && renderContextData.renderBundles.length > 0 ) {

renderContextData.registerBundlesPhase = false;
renderContextData.currentPass.executeBundles( renderContextData.renderBundles );

}

if ( occlusionQueryCount > renderContextData.occlusionQueryIndex ) {

renderContextData.currentPass.endOcclusionQuery();
Expand Down Expand Up @@ -791,9 +798,22 @@ class WebGPUBackend extends Backend {
const pipelineGPU = this.get( pipeline ).pipeline;
const currentSets = contextData.currentSets;

// pipeline
const renderObjectData = this.get( renderObject );

const { bundleEncoder, renderBundle, lastPipelineGPU } = renderObjectData;

const renderContextData = this.get( context );

if ( renderContextData.registerBundlesPhase === true && bundleEncoder !== undefined && lastPipelineGPU === pipelineGPU ) {

renderContextData.renderBundles.push( renderBundle );
return;

}

const passEncoderGPU = contextData.currentPass;
const passEncoderGPU = this.renderer._currentRenderBundle ? this.createBundleEncoder( context, renderObject ) : contextData.currentPass;

// pipeline

if ( currentSets.pipeline !== pipelineGPU ) {

Expand Down Expand Up @@ -905,6 +925,16 @@ class WebGPUBackend extends Backend {

}


if ( this.renderer._currentRenderBundle ) {

const renderBundle = passEncoderGPU.finish();
renderObjectData.lastPipelineGPU = pipelineGPU;
renderObjectData.renderBundle = renderBundle;
renderObjectData.bundleEncoder = passEncoderGPU;

}

}

// cache key
Expand Down Expand Up @@ -1160,6 +1190,12 @@ class WebGPUBackend extends Backend {

}

createBundleEncoder( renderContext, renderObject ) {

return this.pipelineUtils.createBundleEncoder( renderContext, renderObject );

}

// bindings

createBindings( bindings ) {
Expand Down
Loading

0 comments on commit 82b78e7

Please sign in to comment.