From 777d2ee03504443790b36fdcba42cec0d79c087b Mon Sep 17 00:00:00 2001 From: sunag Date: Thu, 23 May 2024 20:10:24 -0300 Subject: [PATCH] Adding RenderBundles and Group.static --- .../renderers/{ => common}/RenderBundle.js | 3 +- .../jsm/renderers/common/RenderBundles.js | 38 +++++ examples/jsm/renderers/common/RenderList.js | 9 ++ examples/jsm/renderers/common/Renderer.js | 131 ++++++++++-------- examples/webgpu_renderbundle.html | 29 +--- 5 files changed, 125 insertions(+), 85 deletions(-) rename examples/jsm/renderers/{ => common}/RenderBundle.js (73%) create mode 100644 examples/jsm/renderers/common/RenderBundles.js diff --git a/examples/jsm/renderers/RenderBundle.js b/examples/jsm/renderers/common/RenderBundle.js similarity index 73% rename from examples/jsm/renderers/RenderBundle.js rename to examples/jsm/renderers/common/RenderBundle.js index 199ffad3b2c46..e84c0ad0de8d5 100644 --- a/examples/jsm/renderers/RenderBundle.js +++ b/examples/jsm/renderers/common/RenderBundle.js @@ -1,8 +1,7 @@ class RenderBundle { - constructor( scene, camera, name = '' ) { + constructor( scene, camera ) { - this.name = name; this.scene = scene; this.camera = camera; diff --git a/examples/jsm/renderers/common/RenderBundles.js b/examples/jsm/renderers/common/RenderBundles.js new file mode 100644 index 0000000000000..66045184139de --- /dev/null +++ b/examples/jsm/renderers/common/RenderBundles.js @@ -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; diff --git a/examples/jsm/renderers/common/RenderList.js b/examples/jsm/renderers/common/RenderList.js index e34f736c7e7c6..29c185225e99a 100644 --- a/examples/jsm/renderers/common/RenderList.js +++ b/examples/jsm/renderers/common/RenderList.js @@ -57,6 +57,7 @@ class RenderList { this.opaque = []; this.transparent = []; + this.bundles = []; this.lightsNode = new LightsNode( [] ); this.lightsArray = []; @@ -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; @@ -135,6 +138,12 @@ class RenderList { } + pushBundle( group ) { + + this.bundles.push( group ); + + } + pushLight( light ) { this.lightsArray.push( light ); diff --git a/examples/jsm/renderers/common/Renderer.js b/examples/jsm/renderers/common/Renderer.js index 5e04aa248195e..421bf1ca6da38 100644 --- a/examples/jsm/renderers/common/Renderer.js +++ b/examples/jsm/renderers/common/Renderer.js @@ -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(); @@ -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; @@ -172,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(); // @@ -327,68 +330,74 @@ class Renderer { } - _renderBundle( renderBundle ) { + _renderBundle( bundle, sceneRef, lightsNode ) { - const { scene, camera } = renderBundle; + const { object, camera, renderList } = bundle; - const renderBundleData = this.backend.get( renderBundle ); + const renderContext = this._currentRenderContext; + const renderContextData = this.backend.get( renderContext ); - const renderBundleNeedsUpdate = renderBundleData.renderContexts === undefined || renderBundle.needsUpdate === true; + // - if ( renderBundleNeedsUpdate ) { + const renderBundle = this._bundles.get( object, camera ); - renderBundleData.renderContexts = undefined; + const renderBundleData = this.backend.get( renderBundle ); + if ( renderBundleData.renderContexts === undefined ) renderBundleData.renderContexts = new Set(); - this._currentRenderBundle = renderBundle; + // - this.render( scene, camera ); + const renderBundleNeedsUpdate = renderBundleData.renderContexts.has( renderContext ) === false || object.needsUpdate === true; - this._currentRenderBundle = null; + renderBundleData.renderContexts.add( renderContext ); - renderBundle.needsUpdate = false; + if ( renderBundleNeedsUpdate ) { - } + if ( renderContextData.renderObjects === undefined || object.needsUpdate === true ) { + const nodeFrame = this._nodes.nodeFrame; - if ( renderBundleData.renderContexts !== undefined ) { + renderContextData.renderObjects = []; + renderContextData.renderBundles = []; + renderContextData.scene = sceneRef; + renderContextData.camera = camera; + renderContextData.renderId = nodeFrame.renderId; - for ( let i = 0; i < renderBundleData.renderContexts.length; i ++ ) { + renderContextData.registerBundlesPhase = true; - const renderContext = renderBundleData.renderContexts[ i ]; - const renderContextData = this.backend.get( renderContext ); + } - const scene = renderContextData.scene; - const camera = renderContextData.camera; + this._currentRenderBundle = renderBundle; - this._nodes.nodeFrame.renderId = renderContextData.renderId; - // + const opaqueObjects = renderList.opaque; - if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld(); + if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, sceneRef, lightsNode ); - if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); + this._currentRenderBundle = null; + // - this.backend.beginRender( renderContext ); + object.needsUpdate = false; - for ( let i = 0, l = renderContextData.renderObjects.length; i < l; i ++ ) { + } else { - const renderObject = renderContextData.renderObjects[ i ]; + const renderContext = this._currentRenderContext; + const renderContextData = this.backend.get( renderContext ); - this._nodes.updateBefore( renderObject ); + for ( let i = 0, l = renderContextData.renderObjects.length; i < l; i ++ ) { - // + const renderObject = renderContextData.renderObjects[ i ]; - renderObject.object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, renderObject.object.matrixWorld ); - renderObject.object.normalMatrix.getNormalMatrix( renderObject.object.modelViewMatrix ); + this._nodes.updateBefore( renderObject ); - this._nodes.updateForRender( renderObject ); - this._bindings.updateForRender( renderObject ); + // - this.backend.draw( renderObject, this.info ); + 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.finishRender( renderContext ); + this.backend.draw( renderObject, this.info ); } @@ -526,31 +535,6 @@ class Renderer { if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld(); - - if ( this._currentRenderBundle !== null ) { - - const renderBundleData = this.backend.get( this._currentRenderBundle ); - - if ( renderBundleData.renderContexts === undefined ) renderBundleData.renderContexts = []; - - renderBundleData.renderContexts.push( renderContext ); - - const renderContextData = this.backend.get( renderContext ); - - if ( renderContextData.renderObjects === undefined || this._currentRenderBundle.needsUpdate === true ) { - - renderContextData.renderObjects = []; - renderContextData.renderBundles = []; - renderContextData.scene = scene; - renderContextData.camera = camera; - renderContextData.renderId = nodeFrame.renderId; - - renderContextData.registerBundlesPhase = true; - - } - - } - // let viewport = this._viewport; @@ -658,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 ); @@ -1288,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 ++ ) { @@ -1298,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 diff --git a/examples/webgpu_renderbundle.html b/examples/webgpu_renderbundle.html index 568d6c90364b1..90658f2bae6a4 100644 --- a/examples/webgpu_renderbundle.html +++ b/examples/webgpu_renderbundle.html @@ -43,10 +43,9 @@ import { GUI } from 'three/addons/libs/lil-gui.module.min.js'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js'; - import RenderBundle from 'three/addons/renderers/RenderBundle.js'; import { MeshToonNodeMaterial } from 'three/nodes'; - let camera, scene, renderer, renderBundle; + let camera, scene, renderer; let controls, stats; let gui; let geometries, group; @@ -155,6 +154,7 @@ function initRegularMesh( count ) { group = new THREE.Group(); + group.static = api.renderBundle; for ( let i = 0; i < count; i ++ ) { @@ -209,7 +209,6 @@ // scene scene = new THREE.Scene(); - scene.background = new THREE.Color( 0xc1c1c1 ); const light = new THREE.DirectionalLight( 0xffffff, 3.4 ); @@ -223,14 +222,6 @@ initGeometries(); initMesh( count ); - - if ( api.renderBundle ) { - - renderBundle = new RenderBundle( scene, camera ); - - } - - controls = new OrbitControls( camera, renderer.domElement ); controls.autoRotate = true; controls.autoRotateSpeed = 1.0; @@ -264,9 +255,6 @@ window.addEventListener( 'resize', onWindowResize ); - - - function onWindowResize() { const width = window.innerWidth; @@ -276,11 +264,10 @@ camera.updateProjectionMatrix(); renderer.setSize( width, height ); - renderBundle.needsUpdate = true; + group.needsUpdate = true; } - async function animate() { animateMeshes(); @@ -288,15 +275,7 @@ controls.update(); const renderTimeAverage = performance.now(); - if ( api.renderBundle ) { - - renderer._renderBundle( renderBundle ); - - } else { - - renderer.render( scene, camera ); - - } + renderer.render( scene, camera ); // push only the last 60 render times renderTimeAverages.push( performance.now() - renderTimeAverage );