diff --git a/eslint.config.js b/eslint.config.js
index 686364408bf7e0..f2893072bd2ba1 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -63,6 +63,7 @@ export default [
GPUTexture: 'readonly',
GPUMapMode: 'readonly',
QUnit: 'readonly',
+ Ammo: 'readonly',
XRRigidTransform: 'readonly',
XRMediaBinding: 'readonly',
CodeMirror: 'readonly',
diff --git a/examples/files.json b/examples/files.json
index 08469c9ad5891f..9f99a3c4f776f3 100644
--- a/examples/files.json
+++ b/examples/files.json
@@ -552,6 +552,12 @@
"games_fps"
],
"physics": [
+ "physics_ammo_break",
+ "physics_ammo_cloth",
+ "physics_ammo_instancing",
+ "physics_ammo_rope",
+ "physics_ammo_terrain",
+ "physics_ammo_volume",
"physics_jolt_instancing",
"physics_rapier_basic",
"physics_rapier_instancing",
diff --git a/examples/jsm/Addons.js b/examples/jsm/Addons.js
index 4edc716cdca569..67c4f66382e585 100644
--- a/examples/jsm/Addons.js
+++ b/examples/jsm/Addons.js
@@ -171,6 +171,7 @@ export * from './objects/Sky.js';
export * from './objects/Water.js';
export { Water as Water2 } from './objects/Water2.js';
+export * from './physics/AmmoPhysics.js';
export * from './physics/JoltPhysics.js';
export * from './physics/RapierPhysics.js';
diff --git a/examples/jsm/physics/AmmoPhysics.js b/examples/jsm/physics/AmmoPhysics.js
new file mode 100644
index 00000000000000..87664185034ed5
--- /dev/null
+++ b/examples/jsm/physics/AmmoPhysics.js
@@ -0,0 +1,366 @@
+const AMMO_PATH = 'https://cdn.jsdelivr.net/gh/kripken/ammo.js@79190a1f03845794b1bba1777f30037349967658/builds/ammo.wasm.js';
+
+/**
+ * @classdesc Can be used to include Ammo.js as a Physics engine into
+ * `three.js` apps. The API can be initialized via:
+ * ```js
+ * const physics = await AmmoPhysics();
+ * ```
+ * The component automatically imports Ammo.js from a CDN so make sure
+ * to use the component with an active Internet connection.
+ *
+ * @name AmmoPhysics
+ * @class
+ * @hideconstructor
+ * @three_import import { AmmoPhysics } from 'three/addons/physics/AmmoPhysics.js';
+ */
+async function AmmoPhysics() {
+
+ if ( typeof Ammo === 'undefined' ) {
+
+ await new Promise( ( resolve, reject ) => {
+
+ const script = document.createElement( 'script' );
+ script.src = AMMO_PATH;
+ script.onload = resolve;
+ script.onerror = reject;
+ document.head.appendChild( script );
+
+ } );
+
+ }
+
+ const AmmoLib = await Ammo();
+
+ const frameRate = 60;
+
+ const collisionConfiguration = new AmmoLib.btDefaultCollisionConfiguration();
+ const dispatcher = new AmmoLib.btCollisionDispatcher( collisionConfiguration );
+ const broadphase = new AmmoLib.btDbvtBroadphase();
+ const solver = new AmmoLib.btSequentialImpulseConstraintSolver();
+ const world = new AmmoLib.btDiscreteDynamicsWorld( dispatcher, broadphase, solver, collisionConfiguration );
+ world.setGravity( new AmmoLib.btVector3( 0, - 9.8, 0 ) );
+
+ const worldTransform = new AmmoLib.btTransform();
+
+ //
+
+ function getShape( geometry ) {
+
+ const parameters = geometry.parameters;
+
+ // TODO change type to is*
+
+ if ( geometry.type === 'BoxGeometry' ) {
+
+ const sx = parameters.width !== undefined ? parameters.width / 2 : 0.5;
+ const sy = parameters.height !== undefined ? parameters.height / 2 : 0.5;
+ const sz = parameters.depth !== undefined ? parameters.depth / 2 : 0.5;
+
+ const shape = new AmmoLib.btBoxShape( new AmmoLib.btVector3( sx, sy, sz ) );
+ shape.setMargin( 0.05 );
+
+ return shape;
+
+ } else if ( geometry.type === 'SphereGeometry' || geometry.type === 'IcosahedronGeometry' ) {
+
+ const radius = parameters.radius !== undefined ? parameters.radius : 1;
+
+ const shape = new AmmoLib.btSphereShape( radius );
+ shape.setMargin( 0.05 );
+
+ return shape;
+
+ }
+
+ console.error( 'AmmoPhysics: Unsupported geometry type:', geometry.type );
+
+ return null;
+
+ }
+
+ const meshes = [];
+ const meshMap = new WeakMap();
+
+ function addScene( scene ) {
+
+ scene.traverse( function ( child ) {
+
+ if ( child.isMesh ) {
+
+ const physics = child.userData.physics;
+
+ if ( physics ) {
+
+ addMesh( child, physics.mass, physics.restitution );
+
+ }
+
+ }
+
+ } );
+
+ }
+
+ function addMesh( mesh, mass = 0, restitution = 0 ) {
+
+ const shape = getShape( mesh.geometry );
+
+ if ( shape !== null ) {
+
+ if ( mesh.isInstancedMesh ) {
+
+ handleInstancedMesh( mesh, shape, mass, restitution );
+
+ } else if ( mesh.isMesh ) {
+
+ handleMesh( mesh, shape, mass, restitution );
+
+ }
+
+ }
+
+ }
+
+ function handleMesh( mesh, shape, mass, restitution ) {
+
+ const position = mesh.position;
+ const quaternion = mesh.quaternion;
+
+ const transform = new AmmoLib.btTransform();
+ transform.setIdentity();
+ transform.setOrigin( new AmmoLib.btVector3( position.x, position.y, position.z ) );
+ transform.setRotation( new AmmoLib.btQuaternion( quaternion.x, quaternion.y, quaternion.z, quaternion.w ) );
+
+ const motionState = new AmmoLib.btDefaultMotionState( transform );
+
+ const localInertia = new AmmoLib.btVector3( 0, 0, 0 );
+ shape.calculateLocalInertia( mass, localInertia );
+
+ const rbInfo = new AmmoLib.btRigidBodyConstructionInfo( mass, motionState, shape, localInertia );
+ rbInfo.set_m_restitution( restitution );
+
+ const body = new AmmoLib.btRigidBody( rbInfo );
+ // body.setFriction( 4 );
+ world.addRigidBody( body );
+
+ if ( mass > 0 ) {
+
+ meshes.push( mesh );
+ meshMap.set( mesh, body );
+
+ }
+
+
+ }
+
+ function handleInstancedMesh( mesh, shape, mass, restitution ) {
+
+ const array = mesh.instanceMatrix.array;
+
+ const bodies = [];
+
+ for ( let i = 0; i < mesh.count; i ++ ) {
+
+ const index = i * 16;
+
+ const transform = new AmmoLib.btTransform();
+ transform.setFromOpenGLMatrix( array.slice( index, index + 16 ) );
+
+ const motionState = new AmmoLib.btDefaultMotionState( transform );
+
+ const localInertia = new AmmoLib.btVector3( 0, 0, 0 );
+ shape.calculateLocalInertia( mass, localInertia );
+
+ const rbInfo = new AmmoLib.btRigidBodyConstructionInfo( mass, motionState, shape, localInertia );
+ rbInfo.set_m_restitution( restitution );
+
+ const body = new AmmoLib.btRigidBody( rbInfo );
+ world.addRigidBody( body );
+
+ bodies.push( body );
+
+ }
+
+ if ( mass > 0 ) {
+
+ meshes.push( mesh );
+
+ meshMap.set( mesh, bodies );
+
+ }
+
+ }
+
+ //
+
+ function setMeshPosition( mesh, position, index = 0 ) {
+
+ if ( mesh.isInstancedMesh ) {
+
+ const bodies = meshMap.get( mesh );
+ const body = bodies[ index ];
+
+ body.setAngularVelocity( new AmmoLib.btVector3( 0, 0, 0 ) );
+ body.setLinearVelocity( new AmmoLib.btVector3( 0, 0, 0 ) );
+
+ worldTransform.setIdentity();
+ worldTransform.setOrigin( new AmmoLib.btVector3( position.x, position.y, position.z ) );
+ body.setWorldTransform( worldTransform );
+
+ } else if ( mesh.isMesh ) {
+
+ const body = meshMap.get( mesh );
+
+ body.setAngularVelocity( new AmmoLib.btVector3( 0, 0, 0 ) );
+ body.setLinearVelocity( new AmmoLib.btVector3( 0, 0, 0 ) );
+
+ worldTransform.setIdentity();
+ worldTransform.setOrigin( new AmmoLib.btVector3( position.x, position.y, position.z ) );
+ body.setWorldTransform( worldTransform );
+
+ }
+
+ }
+
+ //
+
+ let lastTime = 0;
+
+ function step() {
+
+ const time = performance.now();
+
+ if ( lastTime > 0 ) {
+
+ const delta = ( time - lastTime ) / 1000;
+
+ world.stepSimulation( delta, 10 );
+
+ //
+
+ for ( let i = 0, l = meshes.length; i < l; i ++ ) {
+
+ const mesh = meshes[ i ];
+
+ if ( mesh.isInstancedMesh ) {
+
+ const array = mesh.instanceMatrix.array;
+ const bodies = meshMap.get( mesh );
+
+ for ( let j = 0; j < bodies.length; j ++ ) {
+
+ const body = bodies[ j ];
+
+ const motionState = body.getMotionState();
+ motionState.getWorldTransform( worldTransform );
+
+ const position = worldTransform.getOrigin();
+ const quaternion = worldTransform.getRotation();
+
+ compose( position, quaternion, array, j * 16 );
+
+ }
+
+ mesh.instanceMatrix.needsUpdate = true;
+ mesh.computeBoundingSphere();
+
+ } else if ( mesh.isMesh ) {
+
+ const body = meshMap.get( mesh );
+
+ const motionState = body.getMotionState();
+ motionState.getWorldTransform( worldTransform );
+
+ const position = worldTransform.getOrigin();
+ const quaternion = worldTransform.getRotation();
+ mesh.position.set( position.x(), position.y(), position.z() );
+ mesh.quaternion.set( quaternion.x(), quaternion.y(), quaternion.z(), quaternion.w() );
+
+ }
+
+ }
+
+ }
+
+ lastTime = time;
+
+ }
+
+ // animate
+
+ setInterval( step, 1000 / frameRate );
+
+ return {
+ /**
+ * Adds the given scene to this physics simulation. Only meshes with a
+ * `physics` object in their {@link Object3D#userData} field will be honored.
+ * The object can be used to store the mass of the mesh. E.g.:
+ * ```js
+ * box.userData.physics = { mass: 1 };
+ * ```
+ *
+ * @method
+ * @name AmmoPhysics#addScene
+ * @param {Object3D} scene The scene or any type of 3D object to add.
+ */
+ addScene: addScene,
+
+ /**
+ * Adds the given mesh to this physics simulation.
+ *
+ * @method
+ * @name AmmoPhysics#addMesh
+ * @param {Mesh} mesh The mesh to add.
+ * @param {number} [mass=0] The mass in kg of the mesh.
+ * @param {number} [restitution=0] The restitution of the mesh, usually from 0 to 1. Represents how "bouncy" objects are when they collide with each other.
+ */
+ addMesh: addMesh,
+
+ /**
+ * Set the position of the given mesh which is part of the physics simulation. Calling this
+ * method will reset the current simulated velocity of the mesh.
+ *
+ * @method
+ * @name AmmoPhysics#setMeshPosition
+ * @param {Mesh} mesh The mesh to update the position for.
+ * @param {Vector3} position - The new position.
+ * @param {number} [index=0] - If the mesh is instanced, the index represents the instanced ID.
+ */
+ setMeshPosition: setMeshPosition
+ // addCompoundMesh
+ };
+
+}
+
+function compose( position, quaternion, array, index ) {
+
+ const x = quaternion.x(), y = quaternion.y(), z = quaternion.z(), w = quaternion.w();
+ const x2 = x + x, y2 = y + y, z2 = z + z;
+ const xx = x * x2, xy = x * y2, xz = x * z2;
+ const yy = y * y2, yz = y * z2, zz = z * z2;
+ const wx = w * x2, wy = w * y2, wz = w * z2;
+
+ array[ index + 0 ] = ( 1 - ( yy + zz ) );
+ array[ index + 1 ] = ( xy + wz );
+ array[ index + 2 ] = ( xz - wy );
+ array[ index + 3 ] = 0;
+
+ array[ index + 4 ] = ( xy - wz );
+ array[ index + 5 ] = ( 1 - ( xx + zz ) );
+ array[ index + 6 ] = ( yz + wx );
+ array[ index + 7 ] = 0;
+
+ array[ index + 8 ] = ( xz + wy );
+ array[ index + 9 ] = ( yz - wx );
+ array[ index + 10 ] = ( 1 - ( xx + yy ) );
+ array[ index + 11 ] = 0;
+
+ array[ index + 12 ] = position.x();
+ array[ index + 13 ] = position.y();
+ array[ index + 14 ] = position.z();
+ array[ index + 15 ] = 1;
+
+}
+
+export { AmmoPhysics };
diff --git a/examples/physics_ammo_break.html b/examples/physics_ammo_break.html
new file mode 100644
index 00000000000000..f50f4a0283abd1
--- /dev/null
+++ b/examples/physics_ammo_break.html
@@ -0,0 +1,606 @@
+
+
+ Convex object breaking example
+
+
+
+
+
+
+
+
+
+
+
+ Physics threejs demo with convex objects breaking in real time
Press mouse to throw balls and move the camera.
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/physics_ammo_cloth.html b/examples/physics_ammo_cloth.html
new file mode 100644
index 00000000000000..3125769f9cb725
--- /dev/null
+++ b/examples/physics_ammo_cloth.html
@@ -0,0 +1,473 @@
+
+
+ Ammo.js softbody cloth demo
+
+
+
+
+
+
+
+
+
+
+ Ammo.js physics soft body cloth demo
Press Q or A to move the arm.
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/physics_ammo_instancing.html b/examples/physics_ammo_instancing.html
new file mode 100644
index 00000000000000..8fdf5d16adc6a9
--- /dev/null
+++ b/examples/physics_ammo_instancing.html
@@ -0,0 +1,175 @@
+
+
+
+ three.js physics - ammo.js instancing
+
+
+
+
+
+
+
+
+
+
+
+
three.js physics - ammo.js instancing
+
+
+
+
+
+
+
diff --git a/examples/physics_ammo_rope.html b/examples/physics_ammo_rope.html
new file mode 100644
index 00000000000000..ab4f2a21eb4913
--- /dev/null
+++ b/examples/physics_ammo_rope.html
@@ -0,0 +1,494 @@
+
+
+ Amjs softbody rope demo
+
+
+
+
+
+
+
+
+
+
+ Ammo.js physics soft body rope demo
Press Q or A to move the arm.
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/physics_ammo_terrain.html b/examples/physics_ammo_terrain.html
new file mode 100644
index 00000000000000..677d2cd9f0c04f
--- /dev/null
+++ b/examples/physics_ammo_terrain.html
@@ -0,0 +1,452 @@
+
+
+
+ Ammo.js terrain heightfield demo
+
+
+
+
+
+
+
+
+
+
+
+ Ammo.js physics terrain heightfield demo
+
+
+
+
+
+
+
+
+
diff --git a/examples/physics_ammo_volume.html b/examples/physics_ammo_volume.html
new file mode 100644
index 00000000000000..e14c6b15d773d9
--- /dev/null
+++ b/examples/physics_ammo_volume.html
@@ -0,0 +1,515 @@
+
+
+ Ammo.js softbody volume demo
+
+
+
+
+
+
+
+
+
+
+
+ Ammo.js physics soft body volume demo
+ Click to throw a ball
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/screenshots/physics_ammo_break.jpg b/examples/screenshots/physics_ammo_break.jpg
new file mode 100644
index 00000000000000..6ac9575f5fbc11
Binary files /dev/null and b/examples/screenshots/physics_ammo_break.jpg differ
diff --git a/examples/screenshots/physics_ammo_cloth.jpg b/examples/screenshots/physics_ammo_cloth.jpg
new file mode 100644
index 00000000000000..b083eb4ac8116d
Binary files /dev/null and b/examples/screenshots/physics_ammo_cloth.jpg differ
diff --git a/examples/screenshots/physics_ammo_instancing.jpg b/examples/screenshots/physics_ammo_instancing.jpg
new file mode 100644
index 00000000000000..14c92bc3923cf1
Binary files /dev/null and b/examples/screenshots/physics_ammo_instancing.jpg differ
diff --git a/examples/screenshots/physics_ammo_rope.jpg b/examples/screenshots/physics_ammo_rope.jpg
new file mode 100644
index 00000000000000..806a29a149768e
Binary files /dev/null and b/examples/screenshots/physics_ammo_rope.jpg differ
diff --git a/examples/screenshots/physics_ammo_terrain.jpg b/examples/screenshots/physics_ammo_terrain.jpg
new file mode 100644
index 00000000000000..892922b2399ab1
Binary files /dev/null and b/examples/screenshots/physics_ammo_terrain.jpg differ
diff --git a/examples/screenshots/physics_ammo_volume.jpg b/examples/screenshots/physics_ammo_volume.jpg
new file mode 100644
index 00000000000000..45a42ed55ec0a2
Binary files /dev/null and b/examples/screenshots/physics_ammo_volume.jpg differ
diff --git a/examples/tags.json b/examples/tags.json
index 69befd8bba43a3..ce2b7ab331430e 100644
--- a/examples/tags.json
+++ b/examples/tags.json
@@ -7,6 +7,12 @@
"misc_controls_transform": [ "scale", "rotate", "translate" ],
"misc_exporter_gcode": [ "community" ],
"misc_raycaster_helper": [ "community" ],
+ "physics_ammo_break": [ "community" ],
+ "physics_ammo_cloth": [ "integration", "community" ],
+ "physics_ammo_instancing": [ "community" ],
+ "physics_ammo_rope": [ "community" ],
+ "physics_ammo_terrain": [ "community" ],
+ "physics_ammo_volume": [ "community" ],
"physics_jolt_instancing": [ "community" ],
"physics_rapier_instancing": [ "community" ],
"physics_rapier_basic": [ "community" ],
diff --git a/manual/en/physics.html b/manual/en/physics.html
index 49a933efa0a0b2..ec85322ec00932 100644
--- a/manual/en/physics.html
+++ b/manual/en/physics.html
@@ -71,6 +71,7 @@ 1. Using Three.js Physics Addons
+ - AmmoPhysics: A wrapper for Ammo.js (Bullet Physics).
- JoltPhysics: A wrapper for Jolt Physics.
- RapierPhysics: A wrapper for Rapier.
@@ -84,6 +85,7 @@
Examples
@@ -132,6 +134,17 @@ 3. Importing WASM-based Engines
to handle the WASM memory management and interaction with the physics API directly.
+
+ Examples
+
+
+
Projects
diff --git a/manual/zh/physics.html b/manual/zh/physics.html
index 885372dc817889..a9a4e53eeb1bff 100644
--- a/manual/zh/physics.html
+++ b/manual/zh/physics.html
@@ -70,6 +70,7 @@ 1. 使用 three.js 物理插件
+ - AmmoPhysics:Ammo.js(Bullet 物理)的封装。
- JoltPhysics:Jolt Physics 的封装。
- RapierPhysics:Rapier 的封装。
@@ -82,6 +83,7 @@
示例
@@ -129,6 +131,17 @@ 3. 引入基于 WASM 的引擎
需要处理 WASM 内存管理及与物理 API 的直接交互。
+
+ 示例
+
+
+
项目
diff --git a/src/nodes/lighting/ShadowNode.js b/src/nodes/lighting/ShadowNode.js
index 211bce7a63ffe2..78a3056d6c6f08 100644
--- a/src/nodes/lighting/ShadowNode.js
+++ b/src/nodes/lighting/ShadowNode.js
@@ -48,7 +48,7 @@ export const getShadowRenderObjectFunction = ( renderer, shadow, shadowType, use
if ( renderObjectFunction === undefined || ( renderObjectFunction.shadowType !== shadowType || renderObjectFunction.useVelocity !== useVelocity ) ) {
- renderObjectFunction = ( object, scene, _camera, geometry, material, group, ...params ) => {
+ renderObjectFunction = ( object, scene, _camera, geometry, material, group, lightsNode, clippingContext, passId ) => {
if ( object.castShadow === true || ( object.receiveShadow && shadowType === VSMShadowMap ) ) {
@@ -60,7 +60,7 @@ export const getShadowRenderObjectFunction = ( renderer, shadow, shadowType, use
object.onBeforeShadow( renderer, object, _camera, shadow.camera, geometry, scene.overrideMaterial, group );
- renderer.renderObject( object, scene, _camera, geometry, material, group, ...params );
+ renderer.renderObject( object, scene, _camera, geometry, material, group, lightsNode, clippingContext, passId );
object.onAfterShadow( renderer, object, _camera, shadow.camera, geometry, scene.overrideMaterial, group );
diff --git a/src/renderers/common/nodes/NodeManager.js b/src/renderers/common/nodes/NodeManager.js
index 64322c444d7e45..f6d4f0b54e848b 100644
--- a/src/renderers/common/nodes/NodeManager.js
+++ b/src/renderers/common/nodes/NodeManager.js
@@ -3,7 +3,7 @@ import ChainMap from '../ChainMap.js';
import NodeBuilderState from './NodeBuilderState.js';
import NodeMaterial from '../../../materials/nodes/NodeMaterial.js';
import { cubeMapNode } from '../../../nodes/utils/CubeMapNode.js';
-import { NodeFrame, StackTrace } from '../../../nodes/Nodes.js';
+import { NodeFrame, NodeUpdateType, StackTrace } from '../../../nodes/Nodes.js';
import { renderGroup, cubeTexture, texture, fog, rangeFogFactor, densityFogFactor, reference, pmremTexture, screenUV, uniform } from '../../../nodes/TSL.js';
import { builtin } from '../../../nodes/accessors/BuiltinNode.js';
@@ -115,6 +115,12 @@ class NodeManager extends DataMap {
const groupNode = nodeUniformsGroup.groupNode;
+ // groups that are updated per object always require an update so no further checks are needed
+
+ if ( groupNode.updateType === NodeUpdateType.OBJECT ) return true;
+
+ // check for update
+
_chainKeys[ 0 ] = groupNode;
_chainKeys[ 1 ] = nodeUniformsGroup;
diff --git a/src/renderers/webgpu/WebGPUBackend.js b/src/renderers/webgpu/WebGPUBackend.js
index 777288f9102f33..1c14545e4e3ead 100644
--- a/src/renderers/webgpu/WebGPUBackend.js
+++ b/src/renderers/webgpu/WebGPUBackend.js
@@ -1578,6 +1578,7 @@ class WebGPUBackend extends Backend {
groupGPU.cmdEncoderGPU = this.device.createCommandEncoder( _commandEncoderDescriptor );
groupGPU.passEncoderGPU = groupGPU.cmdEncoderGPU.beginComputePass( _computePassDescriptor );
+ groupGPU.currentPipeline = null;
_commandEncoderDescriptor.reset();
_computePassDescriptor.reset();
@@ -1599,13 +1600,19 @@ class WebGPUBackend extends Backend {
compute( computeGroup, computeNode, bindings, pipeline, dispatchSize = null ) {
const computeNodeData = this.get( computeNode );
- const { passEncoderGPU } = this.get( computeGroup );
+ const groupGPU = this.get( computeGroup );
+ const { passEncoderGPU } = groupGPU;
// pipeline
const pipelineGPU = this.get( pipeline ).pipeline;
- this.pipelineUtils.setPipeline( passEncoderGPU, pipelineGPU );
+ if ( groupGPU.currentPipeline !== pipelineGPU ) {
+
+ passEncoderGPU.setPipeline( pipelineGPU );
+ groupGPU.currentPipeline = pipelineGPU;
+
+ }
// bind groups
@@ -1727,17 +1734,26 @@ class WebGPUBackend extends Backend {
const hasIndex = ( index !== null );
// pipeline
- this.pipelineUtils.setPipeline( passEncoderGPU, pipelineGPU );
- currentSets.pipeline = pipelineGPU;
+
+ if ( currentSets.pipeline !== pipelineGPU ) {
+
+ passEncoderGPU.setPipeline( pipelineGPU );
+ currentSets.pipeline = pipelineGPU;
+
+ }
// bind groups
+
const currentBindingGroups = currentSets.bindingGroups;
+
for ( let i = 0, l = bindings.length; i < l; i ++ ) {
const bindGroup = bindings[ i ];
- const bindingsData = this.get( bindGroup );
+
if ( currentBindingGroups[ i ] !== bindGroup.id ) {
+ const bindingsData = this.get( bindGroup );
+
passEncoderGPU.setBindGroup( i, bindingsData.group );
currentBindingGroups[ i ] = bindGroup.id;
diff --git a/src/renderers/webgpu/utils/WebGPUPipelineUtils.js b/src/renderers/webgpu/utils/WebGPUPipelineUtils.js
index 4ed6702b0a176c..851059692556d1 100644
--- a/src/renderers/webgpu/utils/WebGPUPipelineUtils.js
+++ b/src/renderers/webgpu/utils/WebGPUPipelineUtils.js
@@ -48,35 +48,6 @@ class WebGPUPipelineUtils {
*/
this.backend = backend;
- /**
- * A Weak Map that tracks the active pipeline for render or compute passes.
- *
- * @private
- * @type {WeakMap<(GPURenderPassEncoder|GPUComputePassEncoder),(GPURenderPipeline|GPUComputePipeline)>}
- */
- this._activePipelines = new WeakMap();
-
- }
-
- /**
- * Sets the given pipeline for the given pass. The method makes sure to only set the
- * pipeline when necessary.
- *
- * @param {(GPURenderPassEncoder|GPUComputePassEncoder)} pass - The pass encoder.
- * @param {(GPURenderPipeline|GPUComputePipeline)} pipeline - The pipeline.
- */
- setPipeline( pass, pipeline ) {
-
- const currentPipeline = this._activePipelines.get( pass );
-
- if ( currentPipeline !== pipeline ) {
-
- pass.setPipeline( pipeline );
-
- this._activePipelines.set( pass, pipeline );
-
- }
-
}
/**
@@ -314,10 +285,11 @@ class WebGPUPipelineUtils {
try {
let asyncError = null;
+ let pipelinePromise = null;
try {
- pipelineData.pipeline = await device.createRenderPipelineAsync( _renderPipelineDescriptor );
+ pipelinePromise = device.createRenderPipelineAsync( _renderPipelineDescriptor );
} catch ( err ) {
@@ -325,6 +297,22 @@ class WebGPUPipelineUtils {
}
+ _renderPipelineDescriptor.reset();
+
+ if ( pipelinePromise !== null ) {
+
+ try {
+
+ pipelineData.pipeline = await pipelinePromise;
+
+ } catch ( err ) {
+
+ asyncError = err;
+
+ }
+
+ }
+
const errorScope = await device.popErrorScope();
if ( errorScope !== null || asyncError !== null ) {
@@ -340,8 +328,6 @@ class WebGPUPipelineUtils {
} finally {
- _renderPipelineDescriptor.reset();
-
// Guarantee resolution so `compileAsync`'s Promise.all cannot hang on an
// unexpected throw from any await above.
resolve();
diff --git a/src/renderers/webgpu/utils/WebGPUUtils.js b/src/renderers/webgpu/utils/WebGPUUtils.js
index 6c80c8b96cf033..9fbc2af1ad975d 100644
--- a/src/renderers/webgpu/utils/WebGPUUtils.js
+++ b/src/renderers/webgpu/utils/WebGPUUtils.js
@@ -24,6 +24,15 @@ class WebGPUUtils {
*/
this.backend = backend;
+ /**
+ * Caches the preferred canvas format.
+ *
+ * @private
+ * @type {?string}
+ * @default null
+ */
+ this._preferredCanvasFormat = null;
+
}
/**
@@ -248,7 +257,13 @@ class WebGPUUtils {
if ( bufferType === undefined ) {
- return navigator.gpu.getPreferredCanvasFormat();
+ if ( this._preferredCanvasFormat === null ) {
+
+ this._preferredCanvasFormat = navigator.gpu.getPreferredCanvasFormat();
+
+ }
+
+ return this._preferredCanvasFormat;
} else if ( bufferType === UnsignedByteType ) {