فهرست منبع

Universal Renderer and WebGPUBackend (#26079)

* Universal Renderer and WebGPUBackend ( WIP )

* cleanup

* Rename constants.js to Constants.js

* revisions

* cleanup

* cleanup

* Rename /universal to /common

* Update examples/jsm/renderers/common/Renderer.js

Co-authored-by: Levi Pesin <[email protected]>

* Update examples/jsm/renderers/common/Renderer.js

Co-authored-by: Levi Pesin <[email protected]>

* Update examples/jsm/renderers/common/Textures.js

Co-authored-by: Levi Pesin <[email protected]>

* Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js

Co-authored-by: Levi Pesin <[email protected]>

* Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUTextureUtils.js

Co-authored-by: Levi Pesin <[email protected]>

* Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js

Co-authored-by: Levi Pesin <[email protected]>

* Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js

Co-authored-by: Levi Pesin <[email protected]>

* Update examples/jsm/renderers/common/nodes/Nodes.js

Co-authored-by: Levi Pesin <[email protected]>

* Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUPipelineUtils.js

Co-authored-by: Levi Pesin <[email protected]>

* Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js

Co-authored-by: Levi Pesin <[email protected]>

* Update examples/jsm/renderers/webgpu/backends/webgpu/utils/WebGPUAttributeUtils.js

Co-authored-by: Levi Pesin <[email protected]>

* Simplifies paths.

* cleanup

* cleanup

* fix resize

* update tip

* update tip

* Rename: webgpu/builder to webgpu/nodes

* cleanup

---------

Co-authored-by: Levi Pesin <[email protected]>
sunag 2 سال پیش
والد
کامیت
60f307f603
77فایلهای تغییر یافته به همراه4655 افزوده شده و 3676 حذف شده
  1. 1 2
      examples/jsm/nodes/Nodes.js
  2. 19 0
      examples/jsm/nodes/core/NodeBuilder.js
  3. 3 3
      examples/jsm/nodes/display/ViewportSharedTextureNode.js
  4. 20 14
      examples/jsm/nodes/display/ViewportTextureNode.js
  5. 5 1
      examples/jsm/nodes/lighting/AnalyticLightNode.js
  6. 2 2
      examples/jsm/nodes/lighting/HemisphereLightNode.js
  7. 2 3
      examples/jsm/nodes/utils/EquirectUVNode.js
  8. 2 2
      examples/jsm/renderers/common/Animation.js
  9. 75 0
      examples/jsm/renderers/common/Attributes.js
  10. 160 0
      examples/jsm/renderers/common/Backend.js
  11. 133 0
      examples/jsm/renderers/common/Background.js
  12. 11 0
      examples/jsm/renderers/common/Binding.js
  13. 169 0
      examples/jsm/renderers/common/Bindings.js
  14. 38 0
      examples/jsm/renderers/common/Buffer.js
  15. 2 2
      examples/jsm/renderers/common/BufferUtils.js
  16. 11 5
      examples/jsm/renderers/common/ChainMap.js
  17. 17 0
      examples/jsm/renderers/common/ComputePipeline.js
  18. 14 0
      examples/jsm/renderers/common/Constants.js
  19. 54 0
      examples/jsm/renderers/common/DataMap.js
  20. 36 34
      examples/jsm/renderers/common/Geometries.js
  21. 2 2
      examples/jsm/renderers/common/Info.js
  22. 13 0
      examples/jsm/renderers/common/Pipeline.js
  23. 309 0
      examples/jsm/renderers/common/Pipelines.js
  24. 18 0
      examples/jsm/renderers/common/ProgrammableStage.js
  25. 36 0
      examples/jsm/renderers/common/RenderContext.js
  26. 38 0
      examples/jsm/renderers/common/RenderContexts.js
  27. 2 39
      examples/jsm/renderers/common/RenderList.js
  28. 38 0
      examples/jsm/renderers/common/RenderLists.js
  29. 37 6
      examples/jsm/renderers/common/RenderObject.js
  30. 92 0
      examples/jsm/renderers/common/RenderObjects.js
  31. 16 0
      examples/jsm/renderers/common/RenderPipeline.js
  32. 2 2
      examples/jsm/renderers/common/RenderTarget.js
  33. 795 0
      examples/jsm/renderers/common/Renderer.js
  34. 80 0
      examples/jsm/renderers/common/SampledTexture.js
  35. 18 0
      examples/jsm/renderers/common/Sampler.js
  36. 17 0
      examples/jsm/renderers/common/StorageBuffer.js
  37. 5 5
      examples/jsm/renderers/common/TextureRenderer.js
  38. 185 0
      examples/jsm/renderers/common/Textures.js
  39. 13 9
      examples/jsm/renderers/common/Uniform.js
  40. 15 0
      examples/jsm/renderers/common/UniformBuffer.js
  41. 13 13
      examples/jsm/renderers/common/UniformsGroup.js
  42. 3 3
      examples/jsm/renderers/common/nodes/NodeRender.js
  43. 4 4
      examples/jsm/renderers/common/nodes/NodeSampledTexture.js
  44. 3 3
      examples/jsm/renderers/common/nodes/NodeSampler.js
  45. 1 1
      examples/jsm/renderers/common/nodes/NodeUniform.js
  46. 318 0
      examples/jsm/renderers/common/nodes/Nodes.js
  47. 0 199
      examples/jsm/renderers/webgpu/WebGPUAttributes.js
  48. 821 0
      examples/jsm/renderers/webgpu/WebGPUBackend.js
  49. 0 182
      examples/jsm/renderers/webgpu/WebGPUBackground.js
  50. 0 22
      examples/jsm/renderers/webgpu/WebGPUBinding.js
  51. 0 270
      examples/jsm/renderers/webgpu/WebGPUBindings.js
  52. 0 43
      examples/jsm/renderers/webgpu/WebGPUBuffer.js
  53. 0 78
      examples/jsm/renderers/webgpu/WebGPUComputePipelines.js
  54. 0 22
      examples/jsm/renderers/webgpu/WebGPUProgrammableStage.js
  55. 0 44
      examples/jsm/renderers/webgpu/WebGPUProperties.js
  56. 0 50
      examples/jsm/renderers/webgpu/WebGPURenderObjects.js
  57. 0 274
      examples/jsm/renderers/webgpu/WebGPURenderPipelines.js
  58. 0 54
      examples/jsm/renderers/webgpu/WebGPURenderStates.js
  59. 15 1219
      examples/jsm/renderers/webgpu/WebGPURenderer.js
  60. 0 75
      examples/jsm/renderers/webgpu/WebGPUSampledTexture.js
  61. 0 29
      examples/jsm/renderers/webgpu/WebGPUSampler.js
  62. 0 20
      examples/jsm/renderers/webgpu/WebGPUStorageBuffer.js
  63. 0 18
      examples/jsm/renderers/webgpu/WebGPUUniformBuffer.js
  64. 0 88
      examples/jsm/renderers/webgpu/WebGPUUtils.js
  65. 21 28
      examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
  66. 2 2
      examples/jsm/renderers/webgpu/nodes/WGSLNodeFunction.js
  67. 1 1
      examples/jsm/renderers/webgpu/nodes/WGSLNodeParser.js
  68. 251 0
      examples/jsm/renderers/webgpu/utils/WebGPUAttributeUtils.js
  69. 145 0
      examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js
  70. 6 7
      examples/jsm/renderers/webgpu/utils/WebGPUConstants.js
  71. 148 232
      examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js
  72. 3 25
      examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js
  73. 305 532
      examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js
  74. 82 0
      examples/jsm/renderers/webgpu/utils/WebGPUUtils.js
  75. 2 2
      examples/webgpu_backdrop.html
  76. 3 2
      examples/webgpu_depth_texture.html
  77. 3 3
      examples/webgpu_rtt.html

+ 1 - 2
examples/jsm/nodes/Nodes.js

@@ -141,8 +141,7 @@ export { default as NodeObjectLoader } from './loaders/NodeObjectLoader.js';
 export { default as NodeMaterialLoader } from './loaders/NodeMaterialLoader.js';
 export { default as NodeMaterialLoader } from './loaders/NodeMaterialLoader.js';
 
 
 // parsers
 // parsers
-export { default as WGSLNodeParser } from './parsers/WGSLNodeParser.js';
-export { default as GLSLNodeParser } from './parsers/GLSLNodeParser.js';
+export { default as GLSLNodeParser } from './parsers/GLSLNodeParser.js'; // @TODO: Move to jsm/renderers/webgl.
 
 
 // materials
 // materials
 export * from './materials/Materials.js';
 export * from './materials/Materials.js';

+ 19 - 0
examples/jsm/nodes/core/NodeBuilder.js

@@ -68,6 +68,9 @@ class NodeBuilder {
 		this.flowCode = { vertex: '', fragment: '', compute: [] };
 		this.flowCode = { vertex: '', fragment: '', compute: [] };
 		this.uniforms = { vertex: [], fragment: [], compute: [], index: 0 };
 		this.uniforms = { vertex: [], fragment: [], compute: [], index: 0 };
 		this.codes = { vertex: [], fragment: [], compute: [] };
 		this.codes = { vertex: [], fragment: [], compute: [] };
+		this.bindings = { vertex: [], fragment: [], compute: [] };
+		this.bindingsOffset = { vertex: 0, fragment: 0, compute: 0 };
+		this.bindingsArray = null;
 		this.attributes = [];
 		this.attributes = [];
 		this.bufferAttributes = [];
 		this.bufferAttributes = [];
 		this.varyings = [];
 		this.varyings = [];
@@ -93,6 +96,22 @@ class NodeBuilder {
 
 
 	}
 	}
 
 
+	getBindings() {
+
+		let bindingsArray = this.bindingsArray;
+
+		if ( bindingsArray === null ) {
+
+			const bindings = this.bindings;
+
+			this.bindingsArray = bindingsArray = ( this.material !== null ) ? [ ...bindings.vertex, ...bindings.fragment ] : bindings.compute;
+
+		}
+
+		return bindingsArray;
+
+	}
+
 	setHashNode( node, hash ) {
 	setHashNode( node, hash ) {
 
 
 		this.hashNodes[ hash ] = node;
 		this.hashNodes[ hash ] = node;

+ 3 - 3
examples/jsm/nodes/display/ViewportSharedTextureNode.js

@@ -3,7 +3,7 @@ import { addNodeClass } from '../core/Node.js';
 import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
 import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
 import { viewportTopLeft } from './ViewportNode.js';
 import { viewportTopLeft } from './ViewportNode.js';
 
 
-let rtt = null;
+let sharedFramebuffer = null;
 
 
 class ViewportSharedTextureNode extends ViewportTextureNode {
 class ViewportSharedTextureNode extends ViewportTextureNode {
 
 
@@ -13,9 +13,9 @@ class ViewportSharedTextureNode extends ViewportTextureNode {
 
 
 	}
 	}
 
 
-	constructRTT( builder ) {
+	constructFramebuffer( builder ) {
 
 
-		return rtt || ( rtt = builder.getRenderTarget() );
+		return sharedFramebuffer || ( sharedFramebuffer = super.constructFramebuffer( builder ) );
 
 
 	}
 	}
 
 

+ 20 - 14
examples/jsm/nodes/display/ViewportTextureNode.js

@@ -3,9 +3,9 @@ import { NodeUpdateType } from '../core/constants.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
 import { addNodeElement, nodeProxy } from '../shadernode/ShaderNode.js';
 import { viewportTopLeft } from './ViewportNode.js';
 import { viewportTopLeft } from './ViewportNode.js';
-import { Vector2 } from 'three';
+import { Vector2, FramebufferTexture } from 'three';
 
 
-let size = new Vector2();
+const _size = new Vector2();
 
 
 class ViewportTextureNode extends TextureNode {
 class ViewportTextureNode extends TextureNode {
 
 
@@ -13,25 +13,21 @@ class ViewportTextureNode extends TextureNode {
 
 
 		super( null, uv, level );
 		super( null, uv, level );
 
 
-		this.rtt = null;
-
 		this.isOutputTextureNode = true;
 		this.isOutputTextureNode = true;
 
 
 		this.updateBeforeType = NodeUpdateType.FRAME;
 		this.updateBeforeType = NodeUpdateType.FRAME;
 
 
 	}
 	}
 
 
-	constructRTT( builder ) {
+	constructFramebuffer( /*builder*/ ) {
 
 
-		return builder.getRenderTarget();
+		return new FramebufferTexture();
 
 
 	}
 	}
 
 
 	construct( builder ) {
 	construct( builder ) {
 
 
-		if ( this.rtt === null ) this.rtt = this.constructRTT( builder );
-
-		this.value = this.rtt.texture;
+		if ( this.value === null ) this.value = this.constructFramebuffer( builder );
 
 
 		return super.construct( builder );
 		return super.construct( builder );
 
 
@@ -39,14 +35,24 @@ class ViewportTextureNode extends TextureNode {
 
 
 	updateBefore( frame ) {
 	updateBefore( frame ) {
 
 
-		const rtt = this.rtt;
-
 		const renderer = frame.renderer;
 		const renderer = frame.renderer;
-		renderer.getDrawingBufferSize( size );
+		renderer.getDrawingBufferSize( _size );
+
+		//
+
+		const framebufferTexture = this.value;
+
+		if ( framebufferTexture.image.width !== _size.width || framebufferTexture.image.height !== _size.height ) {
+
+			framebufferTexture.image.width = _size.width;
+			framebufferTexture.image.height = _size.height;
+			framebufferTexture.needsUpdate = true;
+
+		}
 
 
-		rtt.setSize( size.width, size.height );
+		//
 
 
-		renderer.copyFramebufferToRenderTarget( rtt );
+		renderer.copyFramebufferToTexture( framebufferTexture );
 
 
 	}
 	}
 
 

+ 5 - 1
examples/jsm/nodes/lighting/AnalyticLightNode.js

@@ -6,6 +6,7 @@ import { vec3 } from '../shadernode/ShaderNode.js';
 import { reference } from '../accessors/ReferenceNode.js';
 import { reference } from '../accessors/ReferenceNode.js';
 import { texture } from '../accessors/TextureNode.js';
 import { texture } from '../accessors/TextureNode.js';
 import { positionWorld } from '../accessors/PositionNode.js';
 import { positionWorld } from '../accessors/PositionNode.js';
+//import { step } from '../math/MathNode.js';
 import { cond } from '../math/CondNode.js';
 import { cond } from '../math/CondNode.js';
 import MeshBasicNodeMaterial from '../materials/MeshBasicNodeMaterial.js';
 import MeshBasicNodeMaterial from '../materials/MeshBasicNodeMaterial.js';
 
 
@@ -51,6 +52,9 @@ class AnalyticLightNode extends LightingNode {
 			const depthTexture = new DepthTexture();
 			const depthTexture = new DepthTexture();
 			depthTexture.minFilter = NearestFilter;
 			depthTexture.minFilter = NearestFilter;
 			depthTexture.magFilter = NearestFilter;
 			depthTexture.magFilter = NearestFilter;
+			depthTexture.image.width = shadow.mapSize.width;
+			depthTexture.image.height = shadow.mapSize.height;
+			//depthTexture.compareFunction = THREE.LessCompare;
 
 
 			rtt.depthTexture = depthTexture;
 			rtt.depthTexture = depthTexture;
 
 
@@ -128,7 +132,7 @@ class AnalyticLightNode extends LightingNode {
 
 
 	}
 	}
 
 
-	update( frame ) {
+	update( /*frame*/ ) {
 
 
 		const { light } = this;
 		const { light } = this;
 
 

+ 2 - 2
examples/jsm/nodes/lighting/HemisphereLightNode.js

@@ -14,8 +14,8 @@ class HemisphereLightNode extends AnalyticLightNode {
 
 
 		super( light );
 		super( light );
 
 
-		this.lightPositionNode = objectPosition;
-		this.lightDirectionNode = objectPosition.normalize();
+		this.lightPositionNode = objectPosition( light );
+		this.lightDirectionNode = this.lightPositionNode.normalize();
 
 
 		this.groundColorNode = uniform( new Color() );
 		this.groundColorNode = uniform( new Color() );
 
 

+ 2 - 3
examples/jsm/nodes/utils/EquirectUVNode.js

@@ -1,5 +1,4 @@
 import TempNode from '../core/TempNode.js';
 import TempNode from '../core/TempNode.js';
-import { negate } from '../math/MathNode.js';
 import { positionWorldDirection } from '../accessors/PositionNode.js';
 import { positionWorldDirection } from '../accessors/PositionNode.js';
 import { nodeProxy, vec2 } from '../shadernode/ShaderNode.js';
 import { nodeProxy, vec2 } from '../shadernode/ShaderNode.js';
 import { addNodeClass } from '../core/Node.js';
 import { addNodeClass } from '../core/Node.js';
@@ -16,10 +15,10 @@ class EquirectUVNode extends TempNode {
 
 
 	construct() {
 	construct() {
 
 
-		const dir = negate( this.dirNode );
+		const dir = this.dirNode;
 
 
 		const u = dir.z.atan2( dir.x ).mul( 1 / ( Math.PI * 2 ) ).add( 0.5 );
 		const u = dir.z.atan2( dir.x ).mul( 1 / ( Math.PI * 2 ) ).add( 0.5 );
-		const v = dir.y.clamp( - 1.0, 1.0 ).asin().mul( 1 / Math.PI ).add( 0.5 );
+		const v = dir.y.negate().clamp( - 1.0, 1.0 ).asin().mul( 1 / Math.PI ).add( 0.5 ); // @TODO: The use of negate() here could be an NDC issue.
 
 
 		return vec2( u, v );
 		return vec2( u, v );
 
 

+ 2 - 2
examples/jsm/renderers/webgpu/WebGPUAnimation.js → examples/jsm/renderers/common/Animation.js

@@ -1,4 +1,4 @@
-class WebGPUAnimation {
+class Animation {
 
 
 	constructor() {
 	constructor() {
 
 
@@ -55,4 +55,4 @@ class WebGPUAnimation {
 
 
 }
 }
 
 
-export default WebGPUAnimation;
+export default Animation;

+ 75 - 0
examples/jsm/renderers/common/Attributes.js

@@ -0,0 +1,75 @@
+import DataMap from './DataMap.js';
+import { AttributeType } from './Constants.js';
+import { DynamicDrawUsage } from 'three';
+
+class Attributes extends DataMap {
+
+	constructor( backend ) {
+
+		super();
+
+		this.backend = backend;
+
+	}
+
+	delete( attribute ) {
+
+		const attributeData = super.delete( attribute );
+
+		if ( attributeData !== undefined ) {
+
+			this.backend.destroyAttribute( attribute );
+
+		}
+
+	}
+
+	update( attribute, type ) {
+
+		const data = this.get( attribute );
+
+		if ( data.version === undefined ) {
+
+			if ( type === AttributeType.VERTEX ) {
+
+				this.backend.createAttribute( attribute );
+
+			} else if ( type === AttributeType.INDEX ) {
+
+				this.backend.createIndexAttribute( attribute );
+
+			} else if ( type === AttributeType.STORAGE ) {
+
+				this.backend.createStorageAttribute( attribute );
+
+			}
+
+			data.version = this._getBufferAttribute( attribute ).version;
+
+		} else {
+
+			const bufferAttribute = this._getBufferAttribute( attribute );
+
+			if ( data.version < bufferAttribute.version || bufferAttribute.usage === DynamicDrawUsage ) {
+
+				this.backend.updateAttribute( attribute );
+
+				data.version = bufferAttribute.version;
+
+			}
+
+		}
+
+	}
+
+	_getBufferAttribute( attribute ) {
+
+		if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
+
+		return attribute;
+
+	}
+
+}
+
+export default Attributes;

+ 160 - 0
examples/jsm/renderers/common/Backend.js

@@ -0,0 +1,160 @@
+let vector2 = null;
+let vector4 = null;
+
+import { Vector2, Vector4 } from 'three';
+
+class Backend {
+
+	constructor( parameters = {} ) {
+
+		this.parameters = Object.assign( {}, parameters );
+		this.data = new WeakMap();
+		this.renderer = null;
+		this.domElement = null;
+
+	}
+
+	async init( renderer ) {
+
+		this.renderer = renderer;
+
+	}
+
+	// render context
+
+	begin( renderContext ) { }
+
+	finish( renderContext ) { }
+
+	// render object
+
+	draw( renderObject, info ) { }
+
+	// program
+
+	createProgram( program ) { }
+
+	destroyProgram( program ) { }
+
+	// bindings
+
+	createBindings( renderObject ) { }
+
+	updateBindings( renderObject ) { }
+
+	// pipeline
+
+	createRenderPipeline( renderObject ) { }
+
+	createComputePipeline( computeNode, pipeline ) { }
+
+	destroyPipeline( pipeline ) { }
+
+	// cache key
+
+	needsUpdate( renderObject ) { } // return Boolean ( fast test )
+
+	getCacheKey( renderObject ) { } // return String
+
+	// node builder
+
+	createNodeBuilder( renderObject ) { } // return NodeBuilder (ADD IT)
+
+	// textures
+
+	createSampler( texture ) { }
+
+	createDefaultTexture( texture ) { }
+
+	createTexture( texture ) { }
+
+	// attributes
+
+	createAttribute( attribute ) { }
+
+	createIndexAttribute( attribute ) { }
+
+	updateAttribute( attribute ) { }
+
+	destroyAttribute( attribute ) { }
+
+	// canvas
+
+	updateSize() { }
+
+	// utils
+
+	hasFeature( name ) { } // return Boolean
+
+	getInstanceCount( renderObject ) {
+
+		const { object, geometry } = renderObject;
+
+		return geometry.isInstancedBufferGeometry ? geometry.instanceCount : ( object.isInstancedMesh ? object.count : 1 );
+
+	}
+
+	getDrawingBufferSize() {
+
+		vector2 = vector2 || new Vector2();
+
+		return this.renderer.getDrawingBufferSize( vector2 );
+
+	}
+
+	getScissor() {
+
+		vector4 = vector4 || new Vector4();
+
+		return this.renderer.getScissor( vector4 );
+
+	}
+
+	getDomElement() {
+
+		let domElement = this.domElement;
+
+		if ( domElement === null ) {
+
+			this.domElement = domElement = ( this.parameters.canvas !== undefined ) ? this.parameters.canvas : this.createCanvasElement();
+
+		}
+
+		return domElement;
+
+	}
+
+	createCanvasElement() {
+
+		const canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
+		canvas.style.display = 'block';
+		return canvas;
+
+	}
+
+	// resource properties
+
+	get( object ) {
+
+		let map = this.data.get( object );
+
+		if ( map === undefined ) {
+
+			map = {};
+			this.data.set( object, map );
+
+		}
+
+		return map;
+
+	}
+
+	delete( object ) {
+
+		this.data.delete( object );
+
+	}
+
+}
+
+export default Backend;

+ 133 - 0
examples/jsm/renderers/common/Background.js

@@ -0,0 +1,133 @@
+import DataMap from './DataMap.js';
+import { Color, Mesh, BoxGeometry, BackSide } from 'three';
+import { context, positionWorldDirection, MeshBasicNodeMaterial } from '../../nodes/Nodes.js';
+
+let _clearAlpha;
+const _clearColor = new Color();
+
+class Background extends DataMap {
+
+	constructor( renderer, nodes ) {
+
+		super();
+
+		this.renderer = renderer;
+		this.nodes = nodes;
+
+		this.boxMesh = null;
+		this.boxMeshNode = null;
+
+	}
+
+	update( scene, renderList, renderContext ) {
+
+		const renderer = this.renderer;
+		const background = ( scene.isScene === true ) ? this.nodes.getBackgroundNode( scene ) || scene.background : null;
+
+		let forceClear = false;
+
+		if ( background === null ) {
+
+			// no background settings, use clear color configuration from the renderer
+
+			_clearColor.copy( renderer._clearColor );
+			_clearAlpha = renderer._clearAlpha;
+
+		} else if ( background.isColor === true ) {
+
+			// background is an opaque color
+
+			_clearColor.copy( background );
+			_clearAlpha = 1;
+			forceClear = true;
+
+		} else if ( background.isNode === true ) {
+
+			const sceneData = this.get( scene );
+			const backgroundNode = background;
+
+			_clearColor.copy( renderer._clearColor );
+			_clearAlpha = renderer._clearAlpha;
+
+			let boxMesh = this.boxMesh;
+
+			if ( boxMesh === null ) {
+
+				this.boxMeshNode = context( backgroundNode, {
+					// @TODO: Add Texture2D support using node context
+					getUVNode: () => positionWorldDirection
+				} );
+
+				const nodeMaterial = new MeshBasicNodeMaterial();
+				nodeMaterial.colorNode = this.boxMeshNode;
+				nodeMaterial.side = BackSide;
+				nodeMaterial.depthTest = false;
+				nodeMaterial.depthWrite = false;
+				nodeMaterial.fog = false;
+
+				this.boxMesh = boxMesh = new Mesh( new BoxGeometry( 1, 1, 1 ), nodeMaterial );
+				boxMesh.frustumCulled = false;
+
+				boxMesh.onBeforeRender = function ( renderer, scene, camera ) {
+
+					const scale = camera.far;
+
+					this.matrixWorld.makeScale( scale, scale, scale ).copyPosition( camera.matrixWorld );
+
+				};
+
+			}
+
+			const backgroundCacheKey = backgroundNode.getCacheKey();
+
+			if ( sceneData.backgroundCacheKey !== backgroundCacheKey ) {
+
+				this.boxMeshNode.node = backgroundNode;
+
+				boxMesh.material.needsUpdate = true;
+
+				sceneData.backgroundCacheKey = backgroundCacheKey;
+
+			}
+
+			renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null );
+
+		} else {
+
+			console.error( 'THREE.Renderer: Unsupported background configuration.', background );
+
+		}
+
+		//
+
+		if ( renderer.autoClear === true || forceClear === true ) {
+
+			_clearColor.multiplyScalar( _clearAlpha );
+
+			const clearColorValue = renderContext.clearColorValue;
+
+			clearColorValue.r = _clearColor.r;
+			clearColorValue.g = _clearColor.g;
+			clearColorValue.b = _clearColor.b;
+			clearColorValue.a = _clearAlpha;
+
+			renderContext.depthClearValue = renderer._clearDepth;
+			renderContext.stencilClearValue = renderer._clearStencil;
+
+			renderContext.clearColor = renderer.autoClearColor === true;
+			renderContext.clearDepth = renderer.autoClearDepth === true;
+			renderContext.clearStencil = renderer.autoClearStencil === true;
+
+		} else {
+
+			renderContext.clearColor = false;
+			renderContext.clearDepth = false;
+			renderContext.clearStencil = false;
+
+		}
+
+	}
+
+}
+
+export default Background;

+ 11 - 0
examples/jsm/renderers/common/Binding.js

@@ -0,0 +1,11 @@
+class Binding {
+
+	constructor( name = '' ) {
+
+		this.name = name;
+
+	}
+
+}
+
+export default Binding;

+ 169 - 0
examples/jsm/renderers/common/Bindings.js

@@ -0,0 +1,169 @@
+import DataMap from './DataMap.js';
+import { AttributeType } from './Constants.js';
+
+class Bindings extends DataMap {
+
+	constructor( backend, nodes, textures, attributes, pipelines, info ) {
+
+		super();
+
+		this.backend = backend;
+		this.textures = textures;
+		this.pipelines = pipelines;
+		this.attributes = attributes;
+		this.nodes = nodes;
+		this.info = info;
+
+		this.pipelines.bindings = this; // assign bindings to pipelines
+
+		this.updateMap = new WeakMap();
+
+	}
+
+	getForRender( renderObject ) {
+
+		const bindings = renderObject.getBindings();
+
+		const data = this.get( renderObject );
+
+		if ( data.bindings !== bindings ) {
+
+			// each object defines an array of bindings (ubos, textures, samplers etc.)
+
+			data.bindings = bindings;
+
+			this._init( bindings );
+
+			const pipeline = this.pipelines.getForRender( renderObject );
+
+			this.backend.createBindings( bindings, pipeline );
+
+		}
+
+		return data.bindings;
+
+	}
+
+	getForCompute( computeNode ) {
+
+		const data = this.get( computeNode );
+
+		if ( data.bindings === undefined ) {
+
+			const nodeBuilder = this.nodes.getForCompute( computeNode );
+
+			const bindings = nodeBuilder.getBindings();
+
+			data.bindings = bindings;
+
+			this._init( bindings );
+
+			const pipeline = this.pipelines.getForCompute( computeNode );
+
+			this.backend.createBindings( bindings, pipeline );
+
+		}
+
+		return data.bindings;
+
+	}
+
+	updateForCompute( computeNode ) {
+
+		this._update( computeNode, this.getForCompute( computeNode ) );
+
+	}
+
+	updateForRender( renderObject ) {
+
+		this._update( renderObject, this.getForRender( renderObject ) );
+
+	}
+
+	_init( bindings ) {
+
+		for ( const binding of bindings ) {
+
+			if ( binding.isSampler || binding.isSampledTexture ) {
+
+				this.textures.updateTexture( binding.texture );
+
+			} else if ( binding.isStorageBuffer ) {
+
+				const attribute = binding.attribute;
+
+				this.attributes.update( attribute, AttributeType.STORAGE );
+
+			}
+
+		}
+
+	}
+
+	_update( object, bindings ) {
+
+		const { backend } = this;
+
+		const updateMap = this.updateMap;
+		const frame = this.info.render.frame;
+
+		let needsBindingsUpdate = false;
+
+		// iterate over all bindings and check if buffer updates or a new binding group is required
+
+		for ( const binding of bindings ) {
+
+			const isShared = binding.isShared;
+			const isUpdated = updateMap.get( binding ) === frame;
+
+			if ( isShared && isUpdated ) continue;
+
+			if ( binding.isUniformBuffer ) {
+
+				const needsUpdate = binding.update();
+
+				if ( needsUpdate ) {
+
+					backend.updateBinding( binding );
+
+				}
+
+			} else if ( binding.isSampledTexture ) {
+
+				if ( binding.needsBindingsUpdate ) needsBindingsUpdate = true;
+
+				const needsUpdate = binding.update();
+
+				if ( needsUpdate ) {
+
+					this.textures.updateTexture( binding.texture );
+
+				}
+
+			}
+
+			updateMap.set( binding, frame );
+
+		}
+
+		if ( needsBindingsUpdate === true ) {
+
+			const pipeline = this.pipelines.getForRender( object );
+
+			this.backend.updateBindings( bindings, pipeline );
+
+		}
+
+	}
+
+	dispose() {
+
+		super.dispose();
+
+		this.updateMap = new WeakMap();
+
+	}
+
+}
+
+export default Bindings;

+ 38 - 0
examples/jsm/renderers/common/Buffer.js

@@ -0,0 +1,38 @@
+import Binding from './Binding.js';
+import { getFloatLength } from './BufferUtils.js';
+
+class Buffer extends Binding {
+
+	constructor( name, buffer = null ) {
+
+		super( name );
+
+		this.isBuffer = true;
+
+		this.bytesPerElement = Float32Array.BYTES_PER_ELEMENT;
+
+		this._buffer = buffer;
+
+	}
+
+	get byteLength() {
+
+		return getFloatLength( this._buffer.byteLength );
+
+	}
+
+	get buffer() {
+
+		return this._buffer;
+
+	}
+
+	update() {
+
+		return true;
+
+	}
+
+}
+
+export default Buffer;

+ 2 - 2
examples/jsm/renderers/webgpu/WebGPUBufferUtils.js → examples/jsm/renderers/common/BufferUtils.js

@@ -1,10 +1,10 @@
-import { GPUChunkSize } from './constants.js';
+import { GPU_CHUNK_BYTES } from './Constants.js';
 
 
 function getFloatLength( floatLength ) {
 function getFloatLength( floatLength ) {
 
 
 	// ensure chunk size alignment (STD140 layout)
 	// ensure chunk size alignment (STD140 layout)
 
 
-	return floatLength + ( ( GPUChunkSize - ( floatLength % GPUChunkSize ) ) % GPUChunkSize );
+	return floatLength + ( ( GPU_CHUNK_BYTES - ( floatLength % GPU_CHUNK_BYTES ) ) % GPU_CHUNK_BYTES );
 
 
 }
 }
 
 

+ 11 - 5
examples/jsm/renderers/webgpu/WebGPUWeakMap.js → examples/jsm/renderers/common/ChainMap.js

@@ -1,8 +1,8 @@
-export default class WebGPUWeakMap extends WeakMap {
+export default class ChainMap {
 
 
 	constructor() {
 	constructor() {
 
 
-		super();
+		this.weakMap = new WeakMap();
 
 
 	}
 	}
 
 
@@ -10,7 +10,7 @@ export default class WebGPUWeakMap extends WeakMap {
 
 
 		if ( Array.isArray( keys ) ) {
 		if ( Array.isArray( keys ) ) {
 
 
-			let map = this;
+			let map = this.weakMap;
 
 
 			for ( let i = 0; i < keys.length - 1; i ++ ) {
 			for ( let i = 0; i < keys.length - 1; i ++ ) {
 
 
@@ -34,7 +34,7 @@ export default class WebGPUWeakMap extends WeakMap {
 
 
 		if ( Array.isArray( keys ) ) {
 		if ( Array.isArray( keys ) ) {
 
 
-			let map = this;
+			let map = this.weakMap;
 
 
 			for ( let i = 0; i < keys.length - 1; i ++ ) {
 			for ( let i = 0; i < keys.length - 1; i ++ ) {
 
 
@@ -60,7 +60,7 @@ export default class WebGPUWeakMap extends WeakMap {
 
 
 		if ( Array.isArray( keys ) ) {
 		if ( Array.isArray( keys ) ) {
 
 
-			let map = this;
+			let map = this.weakMap;
 
 
 			for ( let i = 0; i < keys.length - 1; i ++ ) {
 			for ( let i = 0; i < keys.length - 1; i ++ ) {
 
 
@@ -80,4 +80,10 @@ export default class WebGPUWeakMap extends WeakMap {
 
 
 	}
 	}
 
 
+	dispose() {
+
+		this.weakMap.clear();
+
+	}
+
 }
 }

+ 17 - 0
examples/jsm/renderers/common/ComputePipeline.js

@@ -0,0 +1,17 @@
+import Pipeline from './Pipeline.js';
+
+class ComputePipeline extends Pipeline {
+
+	constructor( cacheKey, computeProgram ) {
+
+		super( cacheKey );
+
+		this.computeProgram = computeProgram;
+
+		this.isComputePipeline = true;
+
+	}
+
+}
+
+export default ComputePipeline;

+ 14 - 0
examples/jsm/renderers/common/Constants.js

@@ -0,0 +1,14 @@
+export const AttributeType = {
+	VERTEX: 1,
+	INDEX: 2,
+	STORAGE: 4
+};
+
+// size of a chunk in bytes (STD140 layout)
+
+export const GPU_CHUNK_BYTES = 16;
+
+// @TODO: Move to src/constants.js
+
+export const BlendColorFactor = 211;
+export const OneMinusBlendColorFactor = 212;

+ 54 - 0
examples/jsm/renderers/common/DataMap.js

@@ -0,0 +1,54 @@
+class DataMap {
+
+	constructor() {
+
+		this.data = new WeakMap();
+
+	}
+
+	get( object ) {
+
+		let map = this.data.get( object );
+
+		if ( map === undefined ) {
+
+			map = {};
+			this.data.set( object, map );
+
+		}
+
+		return map;
+
+	}
+
+	delete( object ) {
+
+		let map;
+
+		if ( this.data.has( object ) ) {
+
+			map = this.data.get( object );
+
+			this.data.delete( object );
+
+		}
+
+		return map;
+
+	}
+
+	has( object ) {
+
+		return this.data.has( object );
+
+	}
+
+	dispose() {
+
+		this.data.clear();
+
+	}
+
+}
+
+export default DataMap;

+ 36 - 34
examples/jsm/renderers/webgpu/WebGPUGeometries.js → examples/jsm/renderers/common/Geometries.js

@@ -1,3 +1,5 @@
+import DataMap from './DataMap.js';
+import { AttributeType } from './Constants.js';
 import { Uint32BufferAttribute, Uint16BufferAttribute } from 'three';
 import { Uint32BufferAttribute, Uint16BufferAttribute } from 'three';
 
 
 function arrayNeedsUint32( array ) {
 function arrayNeedsUint32( array ) {
@@ -64,16 +66,17 @@ function getWireframeIndex( geometry ) {
 
 
 }
 }
 
 
-class WebGPUGeometries {
+class Geometries extends DataMap {
 
 
-	constructor( attributes, properties, info ) {
+	constructor( attributes, info ) {
+
+		super();
 
 
 		this.attributes = attributes;
 		this.attributes = attributes;
-		this.properties = properties;
 		this.info = info;
 		this.info = info;
 
 
 		this.wireframes = new WeakMap();
 		this.wireframes = new WeakMap();
-		this.geometryFrame = new WeakMap();
+		this.attributeFrame = new WeakMap();
 
 
 	}
 	}
 
 
@@ -81,7 +84,7 @@ class WebGPUGeometries {
 
 
 		const geometry = renderObject.geometry;
 		const geometry = renderObject.geometry;
 
 
-		return this.properties.has( geometry ) && this.properties.get( geometry ).initialized === true;
+		return super.has( geometry ) && this.get( geometry ).initialized === true;
 
 
 	}
 	}
 
 
@@ -89,18 +92,20 @@ class WebGPUGeometries {
 
 
 		if ( this.has( renderObject ) === false ) this.initGeometry( renderObject );
 		if ( this.has( renderObject ) === false ) this.initGeometry( renderObject );
 
 
-		this.updateFrameAttributes( renderObject );
+		this.updateAttributes( renderObject );
 
 
 	}
 	}
 
 
 	initGeometry( renderObject ) {
 	initGeometry( renderObject ) {
 
 
 		const geometry = renderObject.geometry;
 		const geometry = renderObject.geometry;
-		const geometryProperties = this.properties.get( geometry );
+		const geometryData = this.get( geometry );
+
+		geometryData.initialized = true;
 
 
-		geometryProperties.initialized = true;
+		this.info.memory.geometries ++;
 
 
-		const dispose = () => {
+		const onDispose = () => {
 
 
 			this.info.memory.geometries --;
 			this.info.memory.geometries --;
 
 
@@ -109,13 +114,13 @@ class WebGPUGeometries {
 
 
 			if ( index !== null ) {
 			if ( index !== null ) {
 
 
-				this.attributes.remove( index );
+				this.attributes.delete( index );
 
 
 			}
 			}
 
 
 			for ( const geometryAttribute of geometryAttributes ) {
 			for ( const geometryAttribute of geometryAttributes ) {
 
 
-				this.attributes.remove( geometryAttribute );
+				this.attributes.delete( geometryAttribute );
 
 
 			}
 			}
 
 
@@ -123,50 +128,47 @@ class WebGPUGeometries {
 
 
 			if ( wireframeAttribute !== undefined ) {
 			if ( wireframeAttribute !== undefined ) {
 
 
-				this.attributes.remove( wireframeAttribute );
+				this.attributes.delete( wireframeAttribute );
 
 
 			}
 			}
 
 
-			geometry.removeEventListener( 'dispose', dispose );
+			geometry.removeEventListener( 'dispose', onDispose );
 
 
 		};
 		};
 
 
-		this.info.memory.geometries ++;
-
-		geometry.addEventListener( 'dispose', dispose );
+		geometry.addEventListener( 'dispose', onDispose );
 
 
 	}
 	}
 
 
-	updateFrameAttributes( renderObject ) {
-
-		const frame = this.info.render.frame;
-		const geometry = renderObject.geometry;
+	updateAttributes( renderObject ) {
 
 
-		if ( this.geometryFrame.get( geometry ) !== frame ) {
+		const attributes = renderObject.getAttributes();
 
 
-			this.updateAttributes( renderObject );
+		for ( const attribute of attributes ) {
 
 
-			this.geometryFrame.set( geometry, frame );
+			this.updateAttribute( attribute, AttributeType.VERTEX );
 
 
 		}
 		}
 
 
-	}
+		const index = this.getIndex( renderObject );
 
 
-	updateAttributes( renderObject ) {
+		if ( index !== null ) {
 
 
-		const attributes = renderObject.getAttributes();
+			this.updateAttribute( index, AttributeType.INDEX );
 
 
-		for ( const attribute of attributes ) {
+		}
 
 
-			this.attributes.update( attribute );
+	}
 
 
-		}
+	updateAttribute( attribute, type ) {
 
 
-		const index = this.getIndex( renderObject );
+		const frame = this.info.render.frame;
 
 
-		if ( index !== null ) {
+		if ( this.attributeFrame.get( attribute ) !== frame ) {
+
+			this.attributes.update( attribute, type );
 
 
-			this.attributes.update( index, true );
+			this.attributeFrame.set( attribute, frame );
 
 
 		}
 		}
 
 
@@ -192,7 +194,7 @@ class WebGPUGeometries {
 
 
 			} else if ( wireframeAttribute.version !== getWireframeVersion( geometry ) ) {
 			} else if ( wireframeAttribute.version !== getWireframeVersion( geometry ) ) {
 
 
-				this.attributes.remove( wireframeAttribute );
+				this.attributes.delete( wireframeAttribute );
 
 
 				wireframeAttribute = getWireframeIndex( geometry );
 				wireframeAttribute = getWireframeIndex( geometry );
 
 
@@ -210,4 +212,4 @@ class WebGPUGeometries {
 
 
 }
 }
 
 
-export default WebGPUGeometries;
+export default Geometries;

+ 2 - 2
examples/jsm/renderers/webgpu/WebGPUInfo.js → examples/jsm/renderers/common/Info.js

@@ -1,4 +1,4 @@
-class WebGPUInfo {
+class Info {
 
 
 	constructor() {
 	constructor() {
 
 
@@ -70,4 +70,4 @@ class WebGPUInfo {
 }
 }
 
 
 
 
-export default WebGPUInfo;
+export default Info;

+ 13 - 0
examples/jsm/renderers/common/Pipeline.js

@@ -0,0 +1,13 @@
+class Pipeline {
+
+	constructor( cacheKey ) {
+
+		this.cacheKey = cacheKey;
+
+		this.usedTimes = 0;
+
+	}
+
+}
+
+export default Pipeline;

+ 309 - 0
examples/jsm/renderers/common/Pipelines.js

@@ -0,0 +1,309 @@
+import DataMap from './DataMap.js';
+import RenderPipeline from './RenderPipeline.js';
+import ComputePipeline from './ComputePipeline.js';
+import ProgrammableStage from './ProgrammableStage.js';
+
+class Pipelines extends DataMap {
+
+	constructor( backend, nodes ) {
+
+		super();
+
+		this.backend = backend;
+		this.nodes = nodes;
+
+		this.bindings = null; // set by the bindings
+
+		this.caches = new Map();
+		this.programs = {
+			vertex: new Map(),
+			fragment: new Map(),
+			compute: new Map()
+		};
+
+	}
+
+	getForCompute( computeNode ) {
+
+		const { backend } = this;
+
+		const data = this.get( computeNode );
+
+		if ( data.pipeline === undefined ) {
+
+			// release previous cache
+
+			this._releasePipeline( computeNode );
+
+			// get shader
+
+			const nodeBuilder = this.nodes.getForCompute( computeNode );
+
+			// programmable stage
+
+			let stageCompute = this.programs.compute.get( nodeBuilder.computeShader );
+
+			if ( stageCompute === undefined ) {
+
+				stageCompute = new ProgrammableStage( nodeBuilder.computeShader, 'compute' );
+				this.programs.compute.set( nodeBuilder.computeShader, stageCompute );
+
+				backend.createProgram( stageCompute );
+
+			}
+
+			// determine compute pipeline
+
+			const pipeline = this._getComputePipeline( stageCompute );
+
+			// keep track of all used times
+
+			pipeline.usedTimes ++;
+			stageCompute.usedTimes ++;
+
+			//
+
+			data.pipeline = pipeline;
+
+		}
+
+		return data.pipeline;
+
+	}
+
+	getForRender( renderObject ) {
+
+		const { backend } = this;
+
+		const data = this.get( renderObject );
+
+		if ( this._needsUpdate( renderObject ) ) {
+
+			// release previous cache
+
+			this._releasePipeline( renderObject );
+
+			// get shader
+
+			const nodeBuilder = this.nodes.getForRender( renderObject );
+
+			// programmable stages
+
+			let stageVertex = this.programs.vertex.get( nodeBuilder.vertexShader );
+
+			if ( stageVertex === undefined ) {
+
+				stageVertex = new ProgrammableStage( nodeBuilder.vertexShader, 'vertex' );
+				this.programs.vertex.set( nodeBuilder.vertexShader, stageVertex );
+
+				backend.createProgram( stageVertex );
+
+			}
+
+			let stageFragment = this.programs.fragment.get( nodeBuilder.fragmentShader );
+
+			if ( stageFragment === undefined ) {
+
+				stageFragment = new ProgrammableStage( nodeBuilder.fragmentShader, 'fragment' );
+				this.programs.fragment.set( nodeBuilder.fragmentShader, stageFragment );
+
+				backend.createProgram( stageFragment );
+
+			}
+
+			// determine render pipeline
+
+			const pipeline = this._getRenderPipeline( renderObject, stageVertex, stageFragment );
+
+			// keep track of all used times
+
+			pipeline.usedTimes ++;
+			stageVertex.usedTimes ++;
+			stageFragment.usedTimes ++;
+
+			//
+
+			data.pipeline = pipeline;
+
+		}
+
+		return data.pipeline;
+
+	}
+
+	delete( object ) {
+
+		this._releasePipeline( object );
+
+		super.delete( object );
+
+	}
+
+	dispose() {
+
+		super.dispose();
+
+		this.caches = new Map();
+		this.programs = {
+			vertex: new Map(),
+			fragment: new Map(),
+			compute: new Map()
+		};
+
+	}
+
+	_getComputePipeline( stageCompute ) {
+
+		// check for existing pipeline
+
+		const cacheKey = 'compute:' + stageCompute.id;
+
+		let pipeline = this.caches.get( cacheKey );
+
+		if ( pipeline === undefined ) {
+
+			pipeline = new ComputePipeline( cacheKey, stageCompute );
+
+			this.caches.set( cacheKey, pipeline );
+
+			this.backend.createComputePipeline( pipeline );
+
+		}
+
+		return pipeline;
+
+	}
+
+	_getRenderPipeline( renderObject, stageVertex, stageFragment ) {
+
+		// check for existing pipeline
+
+		const cacheKey = this._getRenderCacheKey( renderObject, stageVertex, stageFragment );
+
+		let pipeline = this.caches.get( cacheKey );
+
+		if ( pipeline === undefined ) {
+
+			pipeline = new RenderPipeline( cacheKey, stageVertex, stageFragment );
+
+			this.caches.set( cacheKey, pipeline );
+
+			renderObject.pipeline = pipeline;
+
+			this.backend.createRenderPipeline( renderObject );
+
+		} else {
+
+			// assign a shared pipeline to renderObject
+
+			renderObject.pipeline = pipeline;
+
+		}
+
+		return pipeline;
+
+	}
+
+	_getRenderCacheKey( renderObject, stageVertex, stageFragment ) {
+
+		const { material } = renderObject;
+
+		const parameters = [
+			stageVertex.id, stageFragment.id,
+			material.transparent, material.blending, material.premultipliedAlpha,
+			material.blendSrc, material.blendDst, material.blendEquation,
+			material.blendSrcAlpha, material.blendDstAlpha, material.blendEquationAlpha,
+			material.colorWrite,
+			material.depthWrite, material.depthTest, material.depthFunc,
+			material.stencilWrite, material.stencilFunc,
+			material.stencilFail, material.stencilZFail, material.stencilZPass,
+			material.stencilFuncMask, material.stencilWriteMask,
+			material.side,
+			this.backend.getCacheKey( renderObject )
+		];
+
+		return parameters.join();
+
+	}
+
+	_releasePipeline( object ) {
+
+		const pipeline = this.get( object ).pipeline;
+
+		//this.bindings.delete( object );
+
+		if ( pipeline && -- pipeline.usedTimes === 0 ) {
+
+			this.caches.delete( pipeline.cacheKey );
+
+			if ( pipeline.isComputePipeline ) {
+
+				this._releaseProgram( pipeline.computeProgram );
+
+			} else {
+
+				this._releaseProgram( pipeline.vertexProgram );
+				this._releaseProgram( pipeline.fragmentProgram );
+
+			}
+
+		}
+
+	}
+
+	_releaseProgram( program ) {
+
+		if ( -- program.usedTimes === 0 ) {
+
+			const code = program.code;
+			const stage = program.stage;
+
+			this.programs[ stage ].delete( code );
+
+		}
+
+	}
+
+	_needsUpdate( renderObject ) {
+
+		const data = this.get( renderObject );
+		const material = renderObject.material;
+
+		let needsUpdate = this.backend.needsUpdate( renderObject );
+
+		// check material state
+
+		if ( data.material !== material || data.materialVersion !== material.version ||
+			data.transparent !== material.transparent || data.blending !== material.blending || data.premultipliedAlpha !== material.premultipliedAlpha ||
+			data.blendSrc !== material.blendSrc || data.blendDst !== material.blendDst || data.blendEquation !== material.blendEquation ||
+			data.blendSrcAlpha !== material.blendSrcAlpha || data.blendDstAlpha !== material.blendDstAlpha || data.blendEquationAlpha !== material.blendEquationAlpha ||
+			data.colorWrite !== material.colorWrite ||
+			data.depthWrite !== material.depthWrite || data.depthTest !== material.depthTest || data.depthFunc !== material.depthFunc ||
+			data.stencilWrite !== material.stencilWrite || data.stencilFunc !== material.stencilFunc ||
+			data.stencilFail !== material.stencilFail || data.stencilZFail !== material.stencilZFail || data.stencilZPass !== material.stencilZPass ||
+			data.stencilFuncMask !== material.stencilFuncMask || data.stencilWriteMask !== material.stencilWriteMask ||
+			data.side !== material.side
+		) {
+
+			data.material = material; data.materialVersion = material.version;
+			data.transparent = material.transparent; data.blending = material.blending; data.premultipliedAlpha = material.premultipliedAlpha;
+			data.blendSrc = material.blendSrc; data.blendDst = material.blendDst; data.blendEquation = material.blendEquation;
+			data.blendSrcAlpha = material.blendSrcAlpha; data.blendDstAlpha = material.blendDstAlpha; data.blendEquationAlpha = material.blendEquationAlpha;
+			data.colorWrite = material.colorWrite;
+			data.depthWrite = material.depthWrite; data.depthTest = material.depthTest; data.depthFunc = material.depthFunc;
+			data.stencilWrite = material.stencilWrite; data.stencilFunc = material.stencilFunc;
+			data.stencilFail = material.stencilFail; data.stencilZFail = material.stencilZFail; data.stencilZPass = material.stencilZPass;
+			data.stencilFuncMask = material.stencilFuncMask; data.stencilWriteMask = material.stencilWriteMask;
+			data.side = material.side;
+
+			needsUpdate = true;
+
+		}
+
+		return needsUpdate || data.pipeline !== undefined;
+
+	}
+
+}
+
+export default Pipelines;

+ 18 - 0
examples/jsm/renderers/common/ProgrammableStage.js

@@ -0,0 +1,18 @@
+let _id = 0;
+
+class ProgrammableStage {
+
+	constructor( code, type ) {
+
+		this.id = _id ++;
+
+		this.code = code;
+		this.stage = type;
+
+		this.usedTimes = 0;
+
+	}
+
+}
+
+export default ProgrammableStage;

+ 36 - 0
examples/jsm/renderers/common/RenderContext.js

@@ -0,0 +1,36 @@
+import { Vector4 } from 'three';
+
+let id = 0;
+
+class RenderContext {
+
+	constructor() {
+
+		this.id = id ++;
+
+		this.color = true;
+		this.clearColor = true;
+		this.clearColorValue = { r: 0, g: 0, b: 0, a: 1 };
+
+		this.depth = true;
+		this.clearDepth = true;
+		this.clearDepthValue = 1;
+
+		this.stencil = true;
+		this.clearStencil = true;
+		this.clearStencilValue = 1;
+
+		this.viewport = false;
+		this.viewportValue = new Vector4();
+
+		this.scissor = false;
+		this.scissorValue = new Vector4();
+
+		this.texture = null;
+		this.depthTexture = null;
+
+	}
+
+}
+
+export default RenderContext;

+ 38 - 0
examples/jsm/renderers/common/RenderContexts.js

@@ -0,0 +1,38 @@
+import ChainMap from './ChainMap.js';
+import RenderContext from './RenderContext.js';
+
+class RenderContexts {
+
+	constructor() {
+
+		this.renderStates = new ChainMap();
+
+	}
+
+	get( scene, camera ) {
+
+		const chainKey = [ scene, camera ];
+
+		let renderState = this.renderStates.get( chainKey );
+
+		if ( renderState === undefined ) {
+
+			renderState = new RenderContext();
+
+			this.renderStates.set( chainKey, renderState );
+
+		}
+
+		return renderState;
+
+	}
+
+	dispose() {
+
+		this.renderStates = new ChainMap();
+
+	}
+
+}
+
+export default RenderContexts;

+ 2 - 39
examples/jsm/renderers/webgpu/WebGPURenderLists.js → examples/jsm/renderers/common/RenderList.js

@@ -1,4 +1,3 @@
-import WebGPUWeakMap from './WebGPUWeakMap.js';
 import { lights } from '../../nodes/Nodes.js';
 import { lights } from '../../nodes/Nodes.js';
 
 
 function painterSortStable( a, b ) {
 function painterSortStable( a, b ) {
@@ -49,7 +48,7 @@ function reversePainterSortStable( a, b ) {
 
 
 }
 }
 
 
-class WebGPURenderList {
+class RenderList {
 
 
 	constructor() {
 	constructor() {
 
 
@@ -176,40 +175,4 @@ class WebGPURenderList {
 
 
 }
 }
 
 
-class WebGPURenderLists {
-
-	constructor() {
-
-		this.lists = new WeakMap();
-		this.lists = new WebGPUWeakMap();
-
-	}
-
-	get( scene, camera ) {
-
-		const lists = this.lists;
-		const keys = [ scene, camera ];
-
-		let list = lists.get( keys );
-
-		if ( list === undefined ) {
-
-			list = new WebGPURenderList();
-			lists.set( keys, list );
-
-
-		}
-
-		return list;
-
-	}
-
-	dispose() {
-
-		this.lists = new WeakMap();
-
-	}
-
-}
-
-export default WebGPURenderLists;
+export default RenderList;

+ 38 - 0
examples/jsm/renderers/common/RenderLists.js

@@ -0,0 +1,38 @@
+import ChainMap from './ChainMap.js';
+import RenderList from './RenderList.js';
+
+class RenderLists {
+
+	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 RenderList();
+			lists.set( keys, list );
+
+		}
+
+		return list;
+
+	}
+
+	dispose() {
+
+		this.lists = new ChainMap();
+
+	}
+
+}
+
+export default RenderLists;

+ 37 - 6
examples/jsm/renderers/webgpu/WebGPURenderObject.js → examples/jsm/renderers/common/RenderObject.js

@@ -1,10 +1,15 @@
-export default class WebGPURenderObject {
+let id = 0;
 
 
-	constructor( renderer, nodes, object, material, scene, camera, lightsNode ) {
+export default class RenderObject {
 
 
-		this.renderer = renderer;
-		this.nodes = nodes;
+	constructor( nodes, geometries, renderer, object, material, scene, camera, lightsNode ) {
+
+		this._nodes = nodes;
+		this._geometries = geometries;
+
+		this.id = id ++;
 
 
+		this.renderer = renderer;
 		this.object = object;
 		this.object = object;
 		this.material = material;
 		this.material = material;
 		this.scene = scene;
 		this.scene = scene;
@@ -14,17 +19,43 @@ export default class WebGPURenderObject {
 		this.geometry = object.geometry;
 		this.geometry = object.geometry;
 
 
 		this.attributes = null;
 		this.attributes = null;
+		this.context = null;
+		this.pipeline = null;
 
 
 		this._materialVersion = - 1;
 		this._materialVersion = - 1;
 		this._materialCacheKey = '';
 		this._materialCacheKey = '';
 
 
 	}
 	}
 
 
+	getNodeBuilder() {
+
+		return this._nodes.getForRender( this );
+
+	}
+
+	getBindings() {
+
+		return this.getNodeBuilder().getBindings();
+
+	}
+
+	getIndex() {
+
+		return this._geometries.getIndex( this );
+
+	}
+
+	getChainArray() {
+
+		return [ this.object, this.material, this.scene, this.camera, this.lightsNode ];
+
+	}
+
 	getAttributes() {
 	getAttributes() {
 
 
 		if ( this.attributes !== null ) return this.attributes;
 		if ( this.attributes !== null ) return this.attributes;
 
 
-		const nodeAttributes = this.nodes.get( this ).getAttributesArray();
+		const nodeAttributes = this.getNodeBuilder().getAttributesArray();
 		const geometry = this.geometry;
 		const geometry = this.geometry;
 
 
 		const attributes = [];
 		const attributes = [];
@@ -55,7 +86,7 @@ export default class WebGPURenderObject {
 		const cacheKey = [];
 		const cacheKey = [];
 
 
 		cacheKey.push( 'material:' + this._materialCacheKey );
 		cacheKey.push( 'material:' + this._materialCacheKey );
-		cacheKey.push( 'nodes:' + this.nodes.getCacheKey( scene, lightsNode ) );
+		cacheKey.push( 'nodes:' + this._nodes.getCacheKey( scene, lightsNode ) );
 
 
 		return '{' + cacheKey.join( ',' ) + '}';
 		return '{' + cacheKey.join( ',' ) + '}';
 
 

+ 92 - 0
examples/jsm/renderers/common/RenderObjects.js

@@ -0,0 +1,92 @@
+import DataMap from './DataMap.js';
+import ChainMap from './ChainMap.js';
+import RenderObject from './RenderObject.js';
+
+class RenderObjects extends ChainMap {
+
+	constructor( renderer, nodes, geometries, pipelines, info ) {
+
+		super();
+
+		this.renderer = renderer;
+		this.nodes = nodes;
+		this.geometries = geometries;
+		this.pipelines = pipelines;
+		this.info = info;
+
+		this.dataMap = new DataMap();
+
+	}
+
+	get( object, material, scene, camera, lightsNode ) {
+
+		const chainArray = [ object, material, scene, camera, lightsNode ];
+
+		let renderObject = super.get( chainArray );
+
+		if ( renderObject === undefined ) {
+
+			renderObject = new RenderObject( this.nodes, this.geometries, this.renderer, object, material, scene, camera, lightsNode );
+
+			this._initRenderObject( renderObject );
+
+			this.set( chainArray, renderObject );
+
+		} else {
+
+			const data = this.dataMap.get( renderObject );
+			const cacheKey = renderObject.getCacheKey();
+
+			if ( data.cacheKey !== cacheKey ) {
+
+				data.cacheKey = cacheKey;
+
+				this.pipelines.delete( renderObject );
+				this.nodes.delete( renderObject );
+
+			}
+
+		}
+
+		return renderObject;
+
+	}
+
+	dispose() {
+
+		super.dispose();
+
+		this.dataMap.clear();
+
+	}
+
+	_initRenderObject( renderObject ) {
+
+		const data = this.dataMap.get( renderObject );
+
+		if ( data.initialized !== true ) {
+
+			data.initialized = true;
+			data.cacheKey = renderObject.getCacheKey();
+
+			const onDispose = () => {
+
+				renderObject.material.removeEventListener( 'dispose', onDispose );
+
+				this.pipelines.delete( renderObject );
+				this.nodes.delete( renderObject );
+
+				this.delete( renderObject.getChainArray() );
+
+			};
+
+			renderObject.material.addEventListener( 'dispose', onDispose );
+
+		}
+
+	}
+
+
+}
+
+export default RenderObjects;

+ 16 - 0
examples/jsm/renderers/common/RenderPipeline.js

@@ -0,0 +1,16 @@
+import Pipeline from './Pipeline.js';
+
+class RenderPipeline extends Pipeline {
+
+	constructor( cacheKey, vertexProgram, fragmentProgram ) {
+
+		super( cacheKey );
+
+		this.vertexProgram = vertexProgram;
+		this.fragmentProgram = fragmentProgram;
+
+	}
+
+}
+
+export default RenderPipeline;

+ 2 - 2
examples/jsm/renderers/webgpu/WebGPURenderTarget.js → examples/jsm/renderers/common/RenderTarget.js

@@ -2,7 +2,7 @@ import { WebGLRenderTarget } from 'three';
 
 
 // @TODO: Consider rename WebGLRenderTarget to just RenderTarget
 // @TODO: Consider rename WebGLRenderTarget to just RenderTarget
 
 
-class WebGPURenderTarget extends WebGLRenderTarget {
+class RenderTarget extends WebGLRenderTarget {
 
 
 	constructor( width, height, options = {} ) {
 	constructor( width, height, options = {} ) {
 
 
@@ -12,4 +12,4 @@ class WebGPURenderTarget extends WebGLRenderTarget {
 
 
 }
 }
 
 
-export default WebGPURenderTarget;
+export default RenderTarget;

+ 795 - 0
examples/jsm/renderers/common/Renderer.js

@@ -0,0 +1,795 @@
+import Animation from './Animation.js';
+import RenderObjects from './RenderObjects.js';
+import Attributes from './Attributes.js';
+import Geometries from './Geometries.js';
+import Info from './Info.js';
+import Pipelines from './Pipelines.js';
+import Bindings from './Bindings.js';
+import RenderLists from './RenderLists.js';
+import RenderContexts from './RenderContexts.js';
+import Textures from './Textures.js';
+import Background from './Background.js';
+import Nodes from './nodes/Nodes.js';
+import { Frustum, Matrix4, Vector2, Vector3, Vector4, Color, SRGBColorSpace, NoToneMapping } from 'three';
+
+const _drawingBufferSize = new Vector2();
+const _screen = new Vector4();
+const _frustum = new Frustum();
+const _projScreenMatrix = new Matrix4();
+const _vector3 = new Vector3();
+
+class Renderer {
+
+	constructor( backend ) {
+
+		this.isRenderer = true;
+
+		// public
+
+		this.domElement = backend.getDomElement();
+
+		this.backend = backend;
+
+		this.autoClear = true;
+		this.autoClearColor = true;
+		this.autoClearDepth = true;
+		this.autoClearStencil = true;
+
+		this.outputColorSpace = SRGBColorSpace;
+
+		this.toneMapping = NoToneMapping;
+		this.toneMappingExposure = 1.0;
+
+		this.sortObjects = true;
+
+		// internals
+
+		this._pixelRatio = 1;
+		this._width = this.domElement.width;
+		this._height = this.domElement.height;
+
+		this._viewport = new Vector4( 0, 0, this._width, this._height );
+		this._scissor = new Vector4( 0, 0, this._width, this._height );
+		this._scissorTest = false;
+
+		this._info = null;
+		this._properties = null;
+		this._attributes = null;
+		this._geometries = null;
+		this._nodes = null;
+		this._bindings = null;
+		this._objects = null;
+		this._pipelines = null;
+		this._renderLists = null;
+		this._renderContexts = null;
+		this._textures = null;
+		this._background = null;
+
+		this._animation = new Animation();
+
+		this._currentRenderContext = null;
+		this._lastRenderContext = null;
+
+		this._opaqueSort = null;
+		this._transparentSort = null;
+
+		this._clearAlpha = 1;
+		this._clearColor = new Color( 0x000000 );
+		this._clearDepth = 1;
+		this._clearStencil = 0;
+
+		this._renderTarget = null;
+
+		this._initialized = false;
+		this._initPromise = null;
+
+		// backwards compatibility
+
+		this.shadowMap = {
+			enabled: false,
+			type: null
+		};
+
+	}
+
+	async init() {
+
+		if ( this._initialized ) {
+
+			throw new Error( 'Renderer: Backend has already been initialized.' );
+
+		}
+
+		if ( this._initPromise !== null ) {
+
+			return this._initPromise;
+
+		}
+
+		this._initPromise = new Promise( async ( resolve, reject ) => {
+
+			const backend = this.backend;
+
+			try {
+
+				await backend.init( this );
+
+			} catch ( error ) {
+
+				reject( error );
+				return;
+
+			}
+
+			this._info = new Info();
+			this._nodes = new Nodes( this, backend );
+			this._attributes = new Attributes( backend );
+			this._background = new Background( this, this._nodes );
+			this._geometries = new Geometries( this._attributes, this._info );
+			this._textures = new Textures( backend, this._info );
+			this._pipelines = new Pipelines( backend, this._nodes );
+			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._info );
+			this._renderLists = new RenderLists();
+			this._renderContexts = new RenderContexts();
+
+			//
+
+			this._animation.setNodes( this._nodes );
+			this._animation.start();
+
+			this._initialized = true;
+
+			resolve();
+
+		} );
+
+		return this._initPromise;
+
+	}
+
+	async compile( scene, camera ) {
+
+		console.warn( 'THREE.Renderer: .compile() is not implemented yet.' );
+
+	}
+
+	async render( scene, camera ) {
+
+		if ( this._initialized === false ) await this.init();
+
+		// preserve render tree
+
+		const nodeFrame = this._nodes.nodeFrame;
+
+		const previousRenderId = nodeFrame.renderId;
+		const previousRenderState = this._currentRenderContext;
+
+		//
+
+		const renderContext = this._renderContexts.get( scene, camera );
+		const renderTarget = this._renderTarget;
+
+		this._currentRenderContext = renderContext;
+
+		nodeFrame.renderId ++;
+
+		//
+
+		if ( this._animation.isAnimating === false ) nodeFrame.update();
+
+		if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
+
+		if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
+
+		if ( this._info.autoReset === true ) this._info.reset();
+
+		this._info.render.frame ++;
+
+		//
+
+		let viewport = this._viewport;
+		let scissor = this._scissor;
+		let pixelRatio = this._pixelRatio;
+
+		if ( renderTarget !== null ) {
+
+			viewport = renderTarget.viewport;
+			scissor = renderTarget.scissor;
+			pixelRatio = 1;
+
+		}
+
+		this.getDrawingBufferSize( _drawingBufferSize );
+
+		_screen.set( 0, 0, _drawingBufferSize.width, _drawingBufferSize.height );
+
+		const minDepth = ( viewport.minDepth === undefined ) ? 0 : viewport.minDepth;
+		const maxDepth = ( viewport.maxDepth === undefined ) ? 1 : viewport.maxDepth;
+
+		renderContext.viewportValue.copy( viewport ).multiplyScalar( pixelRatio ).floor();
+		renderContext.viewportValue.minDepth = minDepth;
+		renderContext.viewportValue.maxDepth = maxDepth;
+		renderContext.viewport = renderContext.viewportValue.equals( _screen ) === false;
+
+		renderContext.scissorValue.copy( scissor ).multiplyScalar( pixelRatio ).floor();
+		renderContext.scissor = this._scissorTest && renderContext.scissorValue.equals( _screen ) === false;
+
+		//
+
+		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
+		_frustum.setFromProjectionMatrix( _projScreenMatrix );
+
+		const renderList = this._renderLists.get( scene, camera );
+		renderList.init();
+
+		this._projectObject( scene, camera, 0, renderList );
+
+		renderList.finish();
+
+		if ( this.sortObjects === true ) {
+
+			renderList.sort( this._opaqueSort, this._transparentSort );
+
+		}
+
+		//
+
+		if ( renderTarget !== null ) {
+
+			this._textures.updateRenderTarget( renderTarget );
+
+			const renderTargetData = this._textures.get( renderTarget );
+
+			renderContext.texture = renderTargetData.texture;
+			renderContext.depthTexture = renderTargetData.depthTexture;
+
+		} else {
+
+			renderContext.texture = null;
+			renderContext.depthTexture = null;
+
+		}
+
+		//
+
+		this._nodes.updateScene( scene );
+
+		//
+
+		this._background.update( scene, renderList, renderContext );
+
+		//
+
+		this.backend.beginRender( renderContext );
+
+		// process render lists
+
+		const opaqueObjects = renderList.opaque;
+		const transparentObjects = renderList.transparent;
+		const lightsNode = renderList.lightsNode;
+
+		if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, scene, lightsNode );
+		if ( transparentObjects.length > 0 ) this._renderObjects( transparentObjects, camera, scene, lightsNode );
+
+		// finish render pass
+
+		this.backend.finishRender( renderContext );
+
+		// restore render tree
+
+		nodeFrame.renderId = previousRenderId;
+		this._currentRenderContext = previousRenderState;
+
+		this._lastRenderContext = renderContext;
+
+	}
+
+	setAnimationLoop( callback ) {
+
+		if ( this._initialized === false ) this.init();
+
+		const animation = this._animation;
+
+		animation.setAnimationLoop( callback );
+
+		( callback === null ) ? animation.stop() : animation.start();
+
+	}
+
+	async getArrayBuffer( attribute ) {
+
+		return await this.backend.getArrayBuffer( attribute );
+
+	}
+
+	getContext() {
+
+		return this._context;
+
+	}
+
+	getPixelRatio() {
+
+		return this._pixelRatio;
+
+	}
+
+	getDrawingBufferSize( target ) {
+
+		return target.set( this._width * this._pixelRatio, this._height * this._pixelRatio ).floor();
+
+	}
+
+	getSize( target ) {
+
+		return target.set( this._width, this._height );
+
+	}
+
+	setPixelRatio( value = 1 ) {
+
+		this._pixelRatio = value;
+
+		this.setSize( this._width, this._height, false );
+
+	}
+
+	setDrawingBufferSize( width, height, pixelRatio ) {
+
+		this._width = width;
+		this._height = height;
+
+		this._pixelRatio = pixelRatio;
+
+		this.domElement.width = Math.floor( width * pixelRatio );
+		this.domElement.height = Math.floor( height * pixelRatio );
+
+		this.setViewport( 0, 0, width, height );
+
+		if ( this._initialized ) this.backend.updateSize();
+
+	}
+
+	setSize( width, height, updateStyle = true ) {
+
+		this._width = width;
+		this._height = height;
+
+		this.domElement.width = Math.floor( width * this._pixelRatio );
+		this.domElement.height = Math.floor( height * this._pixelRatio );
+
+		if ( updateStyle === true ) {
+
+			this.domElement.style.width = width + 'px';
+			this.domElement.style.height = height + 'px';
+
+		}
+
+		this.setViewport( 0, 0, width, height );
+
+		if ( this._initialized ) this.backend.updateSize();
+
+	}
+
+	setOpaqueSort( method ) {
+
+		this._opaqueSort = method;
+
+	}
+
+	setTransparentSort( method ) {
+
+		this._transparentSort = method;
+
+	}
+
+	getScissor( target ) {
+
+		const scissor = this._scissor;
+
+		target.x = scissor.x;
+		target.y = scissor.y;
+		target.width = scissor.width;
+		target.height = scissor.height;
+
+		return target;
+
+	}
+
+	setScissor( x, y, width, height ) {
+
+		const scissor = this._scissor;
+
+		if ( x.isVector4 ) {
+
+			scissor.copy( x );
+
+		} else {
+
+			scissor.set( x, y, width, height );
+
+		}
+
+	}
+
+	getScissorTest() {
+
+		return this._scissorTest;
+
+	}
+
+	setScissorTest( boolean ) {
+
+		this._scissorTest = boolean;
+
+	}
+
+	getViewport( target ) {
+
+		return target.copy( this._viewport );
+
+	}
+
+	setViewport( x, y, width, height, minDepth = 0, maxDepth = 1 ) {
+
+		const viewport = this._viewport;
+
+		if ( x.isVector4 ) {
+
+			viewport.copy( x );
+
+		} else {
+
+			viewport.set( x, y, width, height );
+
+		}
+
+		viewport.minDepth = minDepth;
+		viewport.maxDepth = maxDepth;
+
+	}
+
+	getClearColor( target ) {
+
+		return target.copy( this._clearColor );
+
+	}
+
+	setClearColor( color, alpha = 1 ) {
+
+		this._clearColor.set( color );
+		this._clearAlpha = alpha;
+
+	}
+
+	getClearAlpha() {
+
+		return this._clearAlpha;
+
+	}
+
+	setClearAlpha( alpha ) {
+
+		this._clearAlpha = alpha;
+
+	}
+
+	getClearDepth() {
+
+		return this._clearDepth;
+
+	}
+
+	setClearDepth( depth ) {
+
+		this._clearDepth = depth;
+
+	}
+
+	getClearStencil() {
+
+		return this._clearStencil;
+
+	}
+
+	setClearStencil( stencil ) {
+
+		this._clearStencil = stencil;
+
+	}
+
+	clear( color = true, depth = true, stencil = true ) {
+
+		const renderContext = this._currentRenderContext || this._lastRenderContext;
+
+		if ( renderContext ) this.backend.clear( renderContext, color, depth, stencil );
+
+	}
+
+	clearColor() {
+
+		this.clear( true, false, false );
+
+	}
+
+	clearDepth() {
+
+		this.clear( false, true, false );
+
+	}
+
+	clearStencil() {
+
+		this.clear( false, false, true );
+
+	}
+
+	dispose() {
+
+		this._objects.dispose();
+		this._properties.dispose();
+		this._pipelines.dispose();
+		this._nodes.dispose();
+		this._bindings.dispose();
+		this._info.dispose();
+		this._renderLists.dispose();
+		this._renderContexts.dispose();
+		this._textures.dispose();
+
+		this.setRenderTarget( null );
+		this.setAnimationLoop( null );
+
+	}
+
+	setRenderTarget( renderTarget ) {
+
+		this._renderTarget = renderTarget;
+
+	}
+
+	async compute( computeNodes ) {
+
+		if ( this._initialized === false ) await this.init();
+
+		const backend = this.backend;
+		const pipelines = this._pipelines;
+		const computeGroup = Array.isArray( computeNodes ) ? computeNodes : [ computeNodes ];
+
+		backend.beginCompute( computeGroup );
+
+		for ( const computeNode of computeGroup ) {
+
+			// onInit
+
+			if ( pipelines.has( computeNode ) === false ) {
+
+				computeNode.onInit( { renderer: this } );
+
+			}
+
+			this._nodes.updateForCompute( computeNode );
+			this._bindings.updateForCompute( computeNode );
+
+			const computePipeline = pipelines.getForCompute( computeNode );
+			const computeBindings = this._bindings.getForCompute( computeNode );
+
+			backend.compute( computeGroup, computeNode, computeBindings, computePipeline );
+
+		}
+
+		backend.finishCompute( computeGroup );
+
+	}
+
+	getRenderTarget() {
+
+		return this._renderTarget;
+
+	}
+
+	hasFeature( name ) {
+
+		return this.backend.hasFeature( name );
+
+	}
+
+	copyFramebufferToTexture( framebufferTexture ) {
+
+		const renderContext = this._currentRenderContext || this._lastRenderContext;
+
+		this._textures.updateTexture( framebufferTexture );
+
+		this.backend.copyFramebufferToTexture( framebufferTexture, renderContext );
+
+	}
+
+	_projectObject( object, camera, groupOrder, renderList ) {
+
+		if ( object.visible === false ) return;
+
+		const visible = object.layers.test( camera.layers );
+
+		if ( visible ) {
+
+			if ( object.isGroup ) {
+
+				groupOrder = object.renderOrder;
+
+			} else if ( object.isLOD ) {
+
+				if ( object.autoUpdate === true ) object.update( camera );
+
+			} else if ( object.isLight ) {
+
+				renderList.pushLight( object );
+
+			} else if ( object.isSprite ) {
+
+				if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {
+
+					if ( this.sortObjects === true ) {
+
+						_vector3.setFromMatrixPosition( object.matrixWorld ).applyMatrix4( _projScreenMatrix );
+
+					}
+
+					const geometry = object.geometry;
+					const material = object.material;
+
+					if ( material.visible ) {
+
+						renderList.push( object, geometry, material, groupOrder, _vector3.z, null );
+
+					}
+
+				}
+
+			} else if ( object.isLineLoop ) {
+
+				console.error( 'THREE.Renderer: Objects of type THREE.LineLoop are not supported. Please use THREE.Line or THREE.LineSegments.' );
+
+			} else if ( object.isMesh || object.isLine || object.isPoints ) {
+
+				if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {
+
+					const geometry = object.geometry;
+					const material = object.material;
+
+					if ( this.sortObjects === true ) {
+
+						if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
+
+						_vector3
+							.copy( geometry.boundingSphere.center )
+							.applyMatrix4( object.matrixWorld )
+							.applyMatrix4( _projScreenMatrix );
+
+					}
+
+					if ( Array.isArray( material ) ) {
+
+						const groups = geometry.groups;
+
+						for ( let i = 0, l = groups.length; i < l; i ++ ) {
+
+							const group = groups[ i ];
+							const groupMaterial = material[ group.materialIndex ];
+
+							if ( groupMaterial && groupMaterial.visible ) {
+
+								renderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );
+
+							}
+
+						}
+
+					} else if ( material.visible ) {
+
+						renderList.push( object, geometry, material, groupOrder, _vector3.z, null );
+
+					}
+
+				}
+
+			}
+
+		}
+
+		const children = object.children;
+
+		for ( let i = 0, l = children.length; i < l; i ++ ) {
+
+			this._projectObject( children[ i ], camera, groupOrder, renderList );
+
+		}
+
+	}
+
+	_renderObjects( renderList, camera, scene, lightsNode ) {
+
+		// process renderable objects
+
+		for ( let i = 0, il = renderList.length; i < il; i ++ ) {
+
+			const renderItem = renderList[ i ];
+
+			// @TODO: Add support for multiple materials per object. This will require to extract
+			// the material from the renderItem object and pass it with its group data to _renderObject().
+
+			const { object, geometry, material, group } = renderItem;
+
+			if ( camera.isArrayCamera ) {
+
+				const cameras = camera.cameras;
+
+				for ( let j = 0, jl = cameras.length; j < jl; j ++ ) {
+
+					const camera2 = cameras[ j ];
+
+					if ( object.layers.test( camera2.layers ) ) {
+
+						const vp = camera2.viewport;
+						const minDepth = ( vp.minDepth === undefined ) ? 0 : vp.minDepth;
+						const maxDepth = ( vp.maxDepth === undefined ) ? 1 : vp.maxDepth;
+
+						const viewportValue = this._currentRenderContext.viewportValue;
+						viewportValue.copy( vp ).multiplyScalar( this._pixelRatio ).floor();
+						viewportValue.minDepth = minDepth;
+						viewportValue.maxDepth = maxDepth;
+
+						this.backend.updateViewport( this._currentRenderContext );
+
+						this._renderObject( object, scene, camera2, geometry, material, group, lightsNode );
+
+					}
+
+				}
+
+			} else {
+
+				this._renderObject( object, scene, camera, geometry, material, group, lightsNode );
+
+			}
+
+		}
+
+	}
+
+	_renderObject( object, scene, camera, geometry, material, group, lightsNode ) {
+
+		material = scene.overrideMaterial !== null ? scene.overrideMaterial : material;
+
+		//
+
+		object.onBeforeRender( this, scene, camera, geometry, material, group );
+
+		//
+
+		const renderObject = this._objects.get( object, material, scene, camera, lightsNode );
+		renderObject.context = this._currentRenderContext;
+
+		//
+
+		this._nodes.updateBefore( renderObject );
+
+		//
+
+		object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
+		object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
+
+		//
+
+		this._nodes.updateForRender( renderObject );
+		this._geometries.update( renderObject );
+		this._bindings.updateForRender( renderObject );
+
+		//
+
+		this.backend.draw( renderObject, this._info );
+
+	}
+
+}
+
+export default Renderer;

+ 80 - 0
examples/jsm/renderers/common/SampledTexture.js

@@ -0,0 +1,80 @@
+import Binding from './Binding.js';
+
+let id = 0;
+
+class SampledTexture extends Binding {
+
+	constructor( name, texture ) {
+
+		super( name );
+
+		this.id = id ++;
+
+		this.texture = texture;
+		this.version = 0;
+
+		this.isSampledTexture = true;
+
+	}
+
+	get needsBindingsUpdate() {
+
+		const { texture, version } = this;
+
+		return texture.isVideoTexture ? true : version !== texture.version; // @TODO: version === 0 && texture.version > 0 ( add it just to External Textures like PNG,JPG )
+
+	}
+
+	update() {
+
+		if ( this.version !== this.texture.version ) {
+
+			this.version = this.texture.version;
+
+			return true;
+
+		}
+
+		return false;
+
+	}
+
+}
+
+class SampledArrayTexture extends SampledTexture {
+
+	constructor( name, texture ) {
+
+		super( name, texture );
+
+		this.isSampledArrayTexture = true;
+
+	}
+
+}
+
+class Sampled3DTexture extends SampledTexture {
+
+	constructor( name, texture ) {
+
+		super( name, texture );
+
+		this.isSampled3DTexture = true;
+
+	}
+
+}
+
+class SampledCubeTexture extends SampledTexture {
+
+	constructor( name, texture ) {
+
+		super( name, texture );
+
+		this.isSampledCubeTexture = true;
+
+	}
+
+}
+
+export { SampledTexture, SampledArrayTexture, Sampled3DTexture, SampledCubeTexture };

+ 18 - 0
examples/jsm/renderers/common/Sampler.js

@@ -0,0 +1,18 @@
+import Binding from './Binding.js';
+
+class Sampler extends Binding {
+
+	constructor( name, texture ) {
+
+		super( name );
+
+		this.texture = texture;
+		this.version = 0;
+
+		this.isSampler = true;
+
+	}
+
+}
+
+export default Sampler;

+ 17 - 0
examples/jsm/renderers/common/StorageBuffer.js

@@ -0,0 +1,17 @@
+import Buffer from './Buffer.js';
+
+class StorageBuffer extends Buffer {
+
+	constructor( name, attribute ) {
+
+		super( name, attribute.array );
+
+		this.attribute = attribute;
+
+		this.isStorageBuffer = true;
+
+	}
+
+}
+
+export default StorageBuffer;

+ 5 - 5
examples/jsm/renderers/webgpu/WebGPUTextureRenderer.js → examples/jsm/renderers/common/TextureRenderer.js

@@ -1,16 +1,16 @@
-import WebGPURenderTarget from './WebGPURenderTarget.js';
+import RenderTarget from './RenderTarget.js';
 
 
-class WebGPUTextureRenderer {
+class TextureRenderer {
 
 
 	constructor( renderer, options = {} ) {
 	constructor( renderer, options = {} ) {
 
 
 		this.renderer = renderer;
 		this.renderer = renderer;
 
 
-		this.renderTarget = new WebGPURenderTarget( 1, 1, options );
+		this.renderTarget = new RenderTarget( 1, 1, options );
 
 
 	}
 	}
 
 
-	getTexture() {
+	get texture() {
 
 
 		return this.renderTarget.texture;
 		return this.renderTarget.texture;
 
 
@@ -35,4 +35,4 @@ class WebGPUTextureRenderer {
 
 
 }
 }
 
 
-export default WebGPUTextureRenderer;
+export default TextureRenderer;

+ 185 - 0
examples/jsm/renderers/common/Textures.js

@@ -0,0 +1,185 @@
+import DataMap from './DataMap.js';
+import { DepthTexture, DepthStencilFormat, UnsignedInt248Type } from 'three';
+
+class Textures extends DataMap {
+
+	constructor( backend, info ) {
+
+		super();
+
+		this.backend = backend;
+		this.info = info;
+
+	}
+
+	updateRenderTarget( renderTarget ) {
+
+		const renderTargetData = this.get( renderTarget );
+
+		const texture = renderTarget.texture;
+
+		let depthTexture = renderTarget.depthTexture || renderTargetData.depthTexture;
+
+		if ( depthTexture === undefined ) {
+
+			depthTexture = new DepthTexture();
+			depthTexture.format = DepthStencilFormat;
+			depthTexture.type = UnsignedInt248Type;
+			depthTexture.image.width = texture.image.width;
+			depthTexture.image.height = texture.image.height;
+
+		}
+
+		if ( renderTargetData.width !== texture.image.width || texture.image.height !== renderTargetData.height ) {
+
+			texture.needsUpdate = true;
+			depthTexture.needsUpdate = true;
+
+			depthTexture.image.width = texture.image.width;
+			depthTexture.image.height = texture.image.height;
+
+		}
+
+		renderTargetData.width = texture.image.width;
+		renderTargetData.height = texture.image.height;
+		renderTargetData.texture = texture;
+		renderTargetData.depthTexture = depthTexture;
+
+		this.updateTexture( texture );
+		this.updateTexture( depthTexture );
+
+		// dispose handler
+
+		if ( renderTargetData.initialized !== true ) {
+
+			renderTargetData.initialized = true;
+
+			// dispose
+
+			const onDispose = () => {
+
+				renderTarget.removeEventListener( 'dispose', onDispose );
+
+				this._destroyTexture( texture );
+				this._destroyTexture( depthTexture );
+
+			};
+
+			renderTarget.addEventListener( 'dispose', onDispose );
+
+		}
+
+	}
+
+	updateTexture( texture ) {
+
+		const textureData = this.get( texture );
+		if ( textureData.initialized === true && textureData.version === texture.version ) return;
+
+		const isRenderTexture = texture.isRenderTargetTexture || texture.isDepthTexture || texture.isFramebufferTexture;
+		const backend = this.backend;
+
+		if ( isRenderTexture && textureData.initialized === true ) {
+
+			// it's an update
+
+			backend.destroySampler( texture );
+			backend.destroyTexture( texture );
+
+		}
+
+		//
+
+		if ( isRenderTexture ) {
+
+			backend.createSampler( texture );
+			backend.createTexture( texture );
+
+		} else {
+
+			const needsCreate = textureData.initialized !== true;
+
+			if ( needsCreate ) backend.createSampler( texture );
+
+			if ( texture.version > 0 ) {
+
+				const image = texture.image;
+
+				if ( image === undefined ) {
+
+					console.warn( 'THREE.Renderer: Texture marked for update but image is undefined.' );
+
+				} else if ( image.complete === false ) {
+
+					console.warn( 'THREE.Renderer: Texture marked for update but image is incomplete.' );
+
+				} else {
+
+					if ( textureData.isDefaultTexture === undefined || textureData.isDefaultTexture === true ) {
+
+						backend.createTexture( texture );
+
+						textureData.isDefaultTexture = false;
+
+					}
+
+					backend.updateTexture( texture );
+
+				}
+
+			} else {
+
+				// async update
+
+				backend.createDefaultTexture( texture );
+
+				textureData.isDefaultTexture = true;
+
+			}
+
+		}
+
+		// dispose handler
+
+		if ( textureData.initialized !== true ) {
+
+			textureData.initialized = true;
+
+			//
+
+			this.info.memory.textures ++;
+
+			// dispose
+
+			const onDispose = () => {
+
+				texture.removeEventListener( 'dispose', onDispose );
+
+				this._destroyTexture( texture );
+
+				this.info.memory.textures --;
+
+			};
+
+			texture.addEventListener( 'dispose', onDispose );
+
+		}
+
+		//
+
+		textureData.version = texture.version;
+
+	}
+
+	_destroyTexture( texture ) {
+
+		this.backend.destroySampler( texture );
+		this.backend.destroyTexture( texture );
+
+		this.delete( texture );
+
+	}
+
+}
+
+export default Textures;

+ 13 - 9
examples/jsm/renderers/webgpu/WebGPUUniform.js → examples/jsm/renderers/common/Uniform.js

@@ -1,6 +1,6 @@
 import { Color, Matrix3, Matrix4, Vector2, Vector3, Vector4 } from 'three';
 import { Color, Matrix3, Matrix4, Vector2, Vector3, Vector4 } from 'three';
 
 
-class WebGPUUniform {
+class Uniform {
 
 
 	constructor( name, value = null ) {
 	constructor( name, value = null ) {
 
 
@@ -28,7 +28,7 @@ class WebGPUUniform {
 
 
 }
 }
 
 
-class FloatUniform extends WebGPUUniform {
+class FloatUniform extends Uniform {
 
 
 	constructor( name, value = 0 ) {
 	constructor( name, value = 0 ) {
 
 
@@ -43,7 +43,7 @@ class FloatUniform extends WebGPUUniform {
 
 
 }
 }
 
 
-class Vector2Uniform extends WebGPUUniform {
+class Vector2Uniform extends Uniform {
 
 
 	constructor( name, value = new Vector2() ) {
 	constructor( name, value = new Vector2() ) {
 
 
@@ -58,7 +58,7 @@ class Vector2Uniform extends WebGPUUniform {
 
 
 }
 }
 
 
-class Vector3Uniform extends WebGPUUniform {
+class Vector3Uniform extends Uniform {
 
 
 	constructor( name, value = new Vector3() ) {
 	constructor( name, value = new Vector3() ) {
 
 
@@ -73,7 +73,7 @@ class Vector3Uniform extends WebGPUUniform {
 
 
 }
 }
 
 
-class Vector4Uniform extends WebGPUUniform {
+class Vector4Uniform extends Uniform {
 
 
 	constructor( name, value = new Vector4() ) {
 	constructor( name, value = new Vector4() ) {
 
 
@@ -88,7 +88,7 @@ class Vector4Uniform extends WebGPUUniform {
 
 
 }
 }
 
 
-class ColorUniform extends WebGPUUniform {
+class ColorUniform extends Uniform {
 
 
 	constructor( name, value = new Color() ) {
 	constructor( name, value = new Color() ) {
 
 
@@ -103,7 +103,7 @@ class ColorUniform extends WebGPUUniform {
 
 
 }
 }
 
 
-class Matrix3Uniform extends WebGPUUniform {
+class Matrix3Uniform extends Uniform {
 
 
 	constructor( name, value = new Matrix3() ) {
 	constructor( name, value = new Matrix3() ) {
 
 
@@ -118,7 +118,7 @@ class Matrix3Uniform extends WebGPUUniform {
 
 
 }
 }
 
 
-class Matrix4Uniform extends WebGPUUniform {
+class Matrix4Uniform extends Uniform {
 
 
 	constructor( name, value = new Matrix4() ) {
 	constructor( name, value = new Matrix4() ) {
 
 
@@ -133,4 +133,8 @@ class Matrix4Uniform extends WebGPUUniform {
 
 
 }
 }
 
 
-export { FloatUniform, Vector2Uniform, Vector3Uniform, Vector4Uniform, ColorUniform, Matrix3Uniform, Matrix4Uniform };
+export {
+	FloatUniform,
+	Vector2Uniform, Vector3Uniform, Vector4Uniform, ColorUniform,
+	Matrix3Uniform, Matrix4Uniform
+};

+ 15 - 0
examples/jsm/renderers/common/UniformBuffer.js

@@ -0,0 +1,15 @@
+import Buffer from './Buffer.js';
+
+class UniformBuffer extends Buffer {
+
+	constructor( name, buffer = null ) {
+
+		super( name, buffer );
+
+		this.isUniformBuffer = true;
+
+	}
+
+}
+
+export default UniformBuffer;

+ 13 - 13
examples/jsm/renderers/webgpu/WebGPUUniformsGroup.js → examples/jsm/renderers/common/UniformsGroup.js

@@ -1,7 +1,7 @@
-import WebGPUUniformBuffer from './WebGPUUniformBuffer.js';
-import { GPUChunkSize } from './constants.js';
+import UniformBuffer from './UniformBuffer.js';
+import { GPU_CHUNK_BYTES } from './Constants.js';
 
 
-class WebGPUUniformsGroup extends WebGPUUniformBuffer {
+class UniformsGroup extends UniformBuffer {
 
 
 	constructor( name ) {
 	constructor( name ) {
 
 
@@ -37,17 +37,17 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer {
 
 
 	}
 	}
 
 
-	getBuffer() {
+	get buffer() {
 
 
-		let buffer = this.buffer;
+		let buffer = this._buffer;
 
 
 		if ( buffer === null ) {
 		if ( buffer === null ) {
 
 
-			const byteLength = this.getByteLength();
+			const byteLength = this.byteLength;
 
 
 			buffer = new Float32Array( new ArrayBuffer( byteLength ) );
 			buffer = new Float32Array( new ArrayBuffer( byteLength ) );
 
 
-			this.buffer = buffer;
+			this._buffer = buffer;
 
 
 		}
 		}
 
 
@@ -55,7 +55,7 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer {
 
 
 	}
 	}
 
 
-	getByteLength() {
+	get byteLength() {
 
 
 		let offset = 0; // global buffer offset in bytes
 		let offset = 0; // global buffer offset in bytes
 
 
@@ -65,8 +65,8 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer {
 
 
 			// offset within a single chunk in bytes
 			// offset within a single chunk in bytes
 
 
-			const chunkOffset = offset % GPUChunkSize;
-			const remainingSizeInChunk = GPUChunkSize - chunkOffset;
+			const chunkOffset = offset % GPU_CHUNK_BYTES;
+			const remainingSizeInChunk = GPU_CHUNK_BYTES - chunkOffset;
 
 
 			// conformance tests
 			// conformance tests
 
 
@@ -74,7 +74,7 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer {
 
 
 				// check for chunk overflow
 				// check for chunk overflow
 
 
-				offset += ( GPUChunkSize - chunkOffset );
+				offset += ( GPU_CHUNK_BYTES - chunkOffset );
 
 
 			} else if ( chunkOffset % uniform.boundary !== 0 ) {
 			} else if ( chunkOffset % uniform.boundary !== 0 ) {
 
 
@@ -90,7 +90,7 @@ class WebGPUUniformsGroup extends WebGPUUniformBuffer {
 
 
 		}
 		}
 
 
-		return Math.ceil( offset / GPUChunkSize ) * GPUChunkSize;
+		return Math.ceil( offset / GPU_CHUNK_BYTES ) * GPU_CHUNK_BYTES;
 
 
 	}
 	}
 
 
@@ -296,4 +296,4 @@ function arraysEqual( a, b, offset ) {
 
 
 }
 }
 
 
-export default WebGPUUniformsGroup;
+export default UniformsGroup;

+ 3 - 3
examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js → examples/jsm/renderers/common/nodes/NodeRender.js

@@ -1,8 +1,8 @@
-import WebGPUNodeBuilder from './WebGPUNodeBuilder.js';
+import WebGPUNodeBuilder from '../../webgpu/backends/webgpu/builder/WGSLNodeBuilder.js';
 import { NoToneMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from 'three';
 import { NoToneMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from 'three';
 import { NodeFrame, cubeTexture, texture, rangeFog, densityFog, reference, toneMapping, positionWorld, modelWorldMatrix, transformDirection, equirectUV, viewportBottomLeft } from '../../../nodes/Nodes.js';
 import { NodeFrame, cubeTexture, texture, rangeFog, densityFog, reference, toneMapping, positionWorld, modelWorldMatrix, transformDirection, equirectUV, viewportBottomLeft } from '../../../nodes/Nodes.js';
 
 
-class WebGPUNodes {
+class NodeRender {
 
 
 	constructor( renderer, properties ) {
 	constructor( renderer, properties ) {
 
 
@@ -299,4 +299,4 @@ class WebGPUNodes {
 
 
 }
 }
 
 
-export default WebGPUNodes;
+export default NodeRender;

+ 4 - 4
examples/jsm/renderers/webgpu/nodes/WebGPUNodeSampledTexture.js → examples/jsm/renderers/common/nodes/NodeSampledTexture.js

@@ -1,6 +1,6 @@
-import { WebGPUSampledTexture, WebGPUSampledCubeTexture } from '../WebGPUSampledTexture.js';
+import { SampledTexture, SampledCubeTexture } from '../SampledTexture.js';
 
 
-class WebGPUNodeSampledTexture extends WebGPUSampledTexture {
+class NodeSampledTexture extends SampledTexture {
 
 
 	constructor( name, textureNode ) {
 	constructor( name, textureNode ) {
 
 
@@ -18,7 +18,7 @@ class WebGPUNodeSampledTexture extends WebGPUSampledTexture {
 
 
 }
 }
 
 
-class WebGPUNodeSampledCubeTexture extends WebGPUSampledCubeTexture {
+class NodeSampledCubeTexture extends SampledCubeTexture {
 
 
 	constructor( name, textureNode ) {
 	constructor( name, textureNode ) {
 
 
@@ -36,4 +36,4 @@ class WebGPUNodeSampledCubeTexture extends WebGPUSampledCubeTexture {
 
 
 }
 }
 
 
-export { WebGPUNodeSampledTexture, WebGPUNodeSampledCubeTexture };
+export { NodeSampledTexture, NodeSampledCubeTexture };

+ 3 - 3
examples/jsm/renderers/webgpu/nodes/WebGPUNodeSampler.js → examples/jsm/renderers/common/nodes/NodeSampler.js

@@ -1,6 +1,6 @@
-import WebGPUSampler from '../WebGPUSampler.js';
+import Sampler from '../Sampler.js';
 
 
-class WebGPUNodeSampler extends WebGPUSampler {
+class NodeSampler extends Sampler {
 
 
 	constructor( name, textureNode ) {
 	constructor( name, textureNode ) {
 
 
@@ -18,4 +18,4 @@ class WebGPUNodeSampler extends WebGPUSampler {
 
 
 }
 }
 
 
-export default WebGPUNodeSampler;
+export default NodeSampler;

+ 1 - 1
examples/jsm/renderers/webgpu/nodes/WebGPUNodeUniform.js → examples/jsm/renderers/common/nodes/NodeUniform.js

@@ -1,7 +1,7 @@
 import {
 import {
 	FloatUniform, Vector2Uniform, Vector3Uniform, Vector4Uniform,
 	FloatUniform, Vector2Uniform, Vector3Uniform, Vector4Uniform,
 	ColorUniform, Matrix3Uniform, Matrix4Uniform
 	ColorUniform, Matrix3Uniform, Matrix4Uniform
-} from '../WebGPUUniform.js';
+} from '../Uniform.js';
 
 
 class FloatNodeUniform extends FloatUniform {
 class FloatNodeUniform extends FloatUniform {
 
 

+ 318 - 0
examples/jsm/renderers/common/nodes/Nodes.js

@@ -0,0 +1,318 @@
+import DataMap from '../DataMap.js';
+import { NoToneMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from 'three';
+import { NodeFrame, cubeTexture, texture, rangeFog, densityFog, reference, toneMapping, positionWorld, modelWorldMatrix, transformDirection, equirectUV, viewportBottomLeft } from '../../../nodes/Nodes.js';
+
+class Nodes extends DataMap {
+
+	constructor( renderer, backend ) {
+
+		super();
+
+		this.renderer = renderer;
+		this.backend = backend;
+		this.nodeFrame = new NodeFrame();
+
+	}
+
+	getForRender( renderObject ) {
+
+		const renderObjectProperties = this.get( renderObject );
+
+		let nodeBuilder = renderObjectProperties.nodeBuilder;
+
+		if ( nodeBuilder === undefined ) {
+
+			nodeBuilder = this.backend.createNodeBuilder( renderObject.object, this.renderer );
+			nodeBuilder.material = renderObject.material;
+			nodeBuilder.lightsNode = renderObject.lightsNode;
+			nodeBuilder.environmentNode = this.getEnvironmentNode( renderObject.scene );
+			nodeBuilder.fogNode = this.getFogNode( renderObject.scene );
+			nodeBuilder.toneMappingNode = this.getToneMappingNode();
+			nodeBuilder.build();
+
+			renderObjectProperties.nodeBuilder = nodeBuilder;
+
+		}
+
+		return nodeBuilder;
+
+	}
+
+	getForCompute( computeNode ) {
+
+		const computeProperties = this.get( computeNode );
+
+		let nodeBuilder = computeProperties.nodeBuilder;
+
+		if ( nodeBuilder === undefined ) {
+
+			nodeBuilder = this.backend.createNodeBuilder( computeNode, this.renderer );
+			nodeBuilder.build();
+
+			computeProperties.nodeBuilder = nodeBuilder;
+
+		}
+
+		return nodeBuilder;
+
+	}
+
+	getEnvironmentNode( scene ) {
+
+		return scene.environmentNode || this.get( scene ).environmentNode || null;
+
+	}
+
+	getBackgroundNode( scene ) {
+
+		return scene.backgroundNode || this.get( scene ).backgroundNode || null;
+
+	}
+
+	getFogNode( scene ) {
+
+		return scene.fogNode || this.get( scene ).fogNode || null;
+
+	}
+
+	getToneMappingNode() {
+
+		return this.renderer.toneMappingNode || this.get( this.renderer ).toneMappingNode || null;
+
+	}
+
+	getCacheKey( scene, lightsNode ) {
+
+		const environmentNode = this.getEnvironmentNode( scene );
+		const fogNode = this.getFogNode( scene );
+		const toneMappingNode = this.getToneMappingNode();
+
+		const cacheKey = [];
+
+		if ( lightsNode ) cacheKey.push( 'lightsNode:' + lightsNode.getCacheKey() );
+		if ( environmentNode ) cacheKey.push( 'environmentNode:' + environmentNode.getCacheKey() );
+		if ( fogNode ) cacheKey.push( 'fogNode:' + fogNode.getCacheKey() );
+		if ( toneMappingNode ) cacheKey.push( 'toneMappingNode:' + toneMappingNode.getCacheKey() );
+
+		return '{' + cacheKey.join( ',' ) + '}';
+
+	}
+
+	updateScene( scene ) {
+
+		this.updateEnvironment( scene );
+		this.updateFog( scene );
+		this.updateBackground( scene );
+		this.updateToneMapping();
+
+	}
+
+	updateToneMapping() {
+
+		const renderer = this.renderer;
+		const rendererProperties = this.get( renderer );
+		const rendererToneMapping = renderer.toneMapping;
+
+		if ( rendererToneMapping !== NoToneMapping ) {
+
+			if ( rendererProperties.toneMapping !== rendererToneMapping ) {
+
+				rendererProperties.toneMappingNode = toneMapping( rendererToneMapping, reference( 'toneMappingExposure', 'float', renderer ) );
+				rendererProperties.toneMapping = rendererToneMapping;
+
+			}
+
+		} else {
+
+			delete rendererProperties.toneMappingNode;
+			delete rendererProperties.toneMapping;
+
+		}
+
+	}
+
+	updateBackground( scene ) {
+
+		const sceneProperties = this.get( scene );
+		const background = scene.background;
+
+		if ( background ) {
+
+			if ( sceneProperties.background !== background ) {
+
+				let backgroundNode = null;
+
+				if ( background.isCubeTexture === true ) {
+
+					backgroundNode = cubeTexture( background, transformDirection( positionWorld, modelWorldMatrix ) );
+
+				} else if ( background.isTexture === true ) {
+
+					let nodeUV = null;
+
+					if ( background.mapping === EquirectangularReflectionMapping || background.mapping === EquirectangularRefractionMapping ) {
+
+						nodeUV = equirectUV();
+
+					} else {
+
+						nodeUV = viewportBottomLeft;
+
+					}
+
+					backgroundNode = texture( background, nodeUV );
+
+				} else if ( background.isColor !== true ) {
+
+					console.error( 'WebGPUNodes: Unsupported background configuration.', background );
+
+				}
+
+				sceneProperties.backgroundNode = backgroundNode;
+				sceneProperties.background = background;
+
+			}
+
+		} else if ( sceneProperties.backgroundNode ) {
+
+			delete sceneProperties.backgroundNode;
+			delete sceneProperties.background;
+
+		}
+
+	}
+
+	updateFog( scene ) {
+
+		const sceneProperties = this.get( scene );
+		const fog = scene.fog;
+
+		if ( fog ) {
+
+			if ( sceneProperties.fog !== fog ) {
+
+				let fogNode = null;
+
+				if ( fog.isFogExp2 ) {
+
+					fogNode = densityFog( reference( 'color', 'color', fog ), reference( 'density', 'float', fog ) );
+
+				} else if ( fog.isFog ) {
+
+					fogNode = rangeFog( reference( 'color', 'color', fog ), reference( 'near', 'float', fog ), reference( 'far', 'float', fog ) );
+
+				} else {
+
+					console.error( 'WebGPUNodes: Unsupported fog configuration.', fog );
+
+				}
+
+				sceneProperties.fogNode = fogNode;
+				sceneProperties.fog = fog;
+
+			}
+
+		} else {
+
+			delete sceneProperties.fogNode;
+			delete sceneProperties.fog;
+
+		}
+
+	}
+
+	updateEnvironment( scene ) {
+
+		const sceneProperties = this.get( scene );
+		const environment = scene.environment;
+
+		if ( environment ) {
+
+			if ( sceneProperties.environment !== environment ) {
+
+				let environmentNode = null;
+
+				if ( environment.isCubeTexture === true ) {
+
+					environmentNode = cubeTexture( environment );
+
+				} else if ( environment.isTexture === true ) {
+
+					environmentNode = texture( environment );
+
+				} else {
+
+					console.error( 'Nodes: Unsupported environment configuration.', environment );
+
+				}
+
+				sceneProperties.environmentNode = environmentNode;
+				sceneProperties.environment = environment;
+
+			}
+
+		} else if ( sceneProperties.environmentNode ) {
+
+			delete sceneProperties.environmentNode;
+			delete sceneProperties.environment;
+
+		}
+
+	}
+
+	getNodeFrame( renderObject ) {
+
+		const nodeFrame = this.nodeFrame;
+		nodeFrame.scene = renderObject.scene;
+		nodeFrame.object = renderObject.object;
+		nodeFrame.camera = renderObject.camera;
+		nodeFrame.renderer = renderObject.renderer;
+		nodeFrame.material = renderObject.material;
+
+		return nodeFrame;
+
+	}
+
+	updateBefore( renderObject ) {
+
+		const nodeFrame = this.getNodeFrame( renderObject );
+		const nodeBuilder = this.getForRender( renderObject );
+
+		for ( const node of nodeBuilder.updateBeforeNodes ) {
+
+			nodeFrame.updateBeforeNode( node );
+
+		}
+
+	}
+
+	updateForCompute( computeNode ) {
+
+
+
+	}
+
+	updateForRender( renderObject ) {
+
+		const nodeFrame = this.getNodeFrame( renderObject );
+		const nodeBuilder = this.getForRender( renderObject );
+
+		for ( const node of nodeBuilder.updateNodes ) {
+
+			nodeFrame.updateNode( node );
+
+		}
+
+	}
+
+	dispose() {
+
+		super.dispose();
+
+		this.nodeFrame = new NodeFrame();
+
+	}
+
+}
+
+export default Nodes;

+ 0 - 199
examples/jsm/renderers/webgpu/WebGPUAttributes.js

@@ -1,199 +0,0 @@
-import { DynamicDrawUsage } from "three";
-
-class WebGPUAttributes {
-
-	constructor( device ) {
-
-		this.buffers = new WeakMap();
-		this.device = device;
-
-	}
-
-	get( attribute ) {
-
-		const bufferAttribute = this._getBufferAttribute( attribute );
-
-		return this.buffers.get( bufferAttribute );
-
-	}
-
-	remove( attribute ) {
-
-		const bufferAttribute = this._getBufferAttribute( attribute );
-
-		const data = this.buffers.get( bufferAttribute );
-
-		if ( data ) {
-
-			this._destroyBuffers( data );
-
-			this.buffers.delete( bufferAttribute );
-
-		}
-
-	}
-
-	update( attribute, isIndex = false, gpuUsage = null ) {
-
-		const bufferAttribute = this._getBufferAttribute( attribute );
-
-		let data = this.buffers.get( bufferAttribute );
-
-		if ( data === undefined ) {
-
-			if ( gpuUsage === null ) {
-
-				gpuUsage = ( isIndex === true ) ? GPUBufferUsage.INDEX : GPUBufferUsage.VERTEX;
-
-			}
-
-			data = this._createBuffer( bufferAttribute, gpuUsage );
-
-			this.buffers.set( bufferAttribute, data );
-
-		} else if ( gpuUsage && gpuUsage !== data.usage ) {
-
-			this._destroyBuffers( data );
-
-			data = this._createBuffer( bufferAttribute, gpuUsage );
-
-			this.buffers.set( bufferAttribute, data );
-
-		} else if ( data.version < bufferAttribute.version || bufferAttribute.usage === DynamicDrawUsage ) {
-
-			this._writeBuffer( data.buffer, bufferAttribute );
-
-			data.version = bufferAttribute.version;
-
-		}
-
-	}
-
-	async getArrayBuffer( attribute ) {
-
-		const data = this.get( attribute );
-		const device = this.device;
-
-		const gpuBuffer = data.buffer;
-		const size = gpuBuffer.size;
-
-		let gpuReadBuffer = data.readBuffer;
-		let needsUnmap = true;
-
-		if ( gpuReadBuffer === null ) {
-
-			gpuReadBuffer = device.createBuffer( {
-				label: attribute.name,
-				size,
-				usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
-			} );
-
-			needsUnmap = false;
-
-			data.readBuffer = gpuReadBuffer;
-
-		}
-
-		const cmdEncoder = device.createCommandEncoder( {} );
-
-		cmdEncoder.copyBufferToBuffer(
-			gpuBuffer,
-			0,
-			gpuReadBuffer,
-			0,
-			size
-		);
-
-		if ( needsUnmap ) gpuReadBuffer.unmap();
-
-		const gpuCommands = cmdEncoder.finish();
-		device.queue.submit( [ gpuCommands ] );
-
-		await gpuReadBuffer.mapAsync( GPUMapMode.READ );
-
-		const arrayBuffer = gpuReadBuffer.getMappedRange();
-
-		return arrayBuffer;
-
-	}
-
-	_getBufferAttribute( attribute ) {
-
-		if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
-
-		return attribute;
-
-	}
-
-	_createBuffer( bufferAttribute, usage ) {
-
-		const array = bufferAttribute.array;
-		const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441
-
-		const buffer = this.device.createBuffer( {
-			label: bufferAttribute.name,
-			size,
-			usage: usage | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
-			mappedAtCreation: true
-		} );
-
-		new array.constructor( buffer.getMappedRange() ).set( array );
-
-		buffer.unmap();
-
-		bufferAttribute.onUploadCallback();
-
-		return {
-			version: bufferAttribute.version,
-			buffer,
-			readBuffer: null,
-			usage
-		};
-
-	}
-
-	_writeBuffer( buffer, attribute ) {
-
-		const device = this.device;
-
-		const array = attribute.array;
-		const updateRange = attribute.updateRange;
-
-		if ( updateRange.count === - 1 ) {
-
-			// Not using update ranges
-
-			device.queue.writeBuffer(
-				buffer,
-				0,
-				array,
-				0
-			);
-
-		} else {
-
-			device.queue.writeBuffer(
-				buffer,
-				0,
-				array,
-				updateRange.offset * array.BYTES_PER_ELEMENT,
-				updateRange.count * array.BYTES_PER_ELEMENT
-			);
-
-			updateRange.count = - 1; // reset range
-
-		}
-
-	}
-
-	_destroyBuffers( { buffer, readBuffer } ) {
-
-		buffer.destroy();
-
-		if ( readBuffer !== null ) readBuffer.destroy();
-
-	}
-
-}
-
-export default WebGPUAttributes;

+ 821 - 0
examples/jsm/renderers/webgpu/WebGPUBackend.js

@@ -0,0 +1,821 @@
+import { GPUFeatureName, GPUTextureFormat, GPULoadOp, GPUStoreOp, GPUIndexFormat } from './utils/WebGPUConstants.js';
+
+import WebGPUNodeBuilder from './nodes/WGSLNodeBuilder.js';
+import Backend from '../common/Backend.js';
+
+import { Matrix4, Frustum, DepthFormat } from 'three';
+
+import WebGPUUtils from './utils/WebGPUUtils.js';
+import WebGPUAttributeUtils from './utils/WebGPUAttributeUtils.js';
+import WebGPUBindingUtils from './utils/WebGPUBindingUtils.js';
+import WebGPUPipelineUtils from './utils/WebGPUPipelineUtils.js';
+import WebGPUTextureUtils from './utils/WebGPUTextureUtils.js';
+
+// statics
+
+let _staticAdapter = null;
+
+if ( navigator.gpu !== undefined ) {
+
+	_staticAdapter = await navigator.gpu.requestAdapter();
+
+}
+
+// hacks
+
+let _initializedHack = false;
+
+function _initWebGPUHack() {
+
+	if ( _initializedHack ) return;
+
+	console.info( 'THREE.WebGPURenderer: Modified Matrix4.makePerspective() and Matrix4.makeOrtographic() to work with WebGPU, see https://github.com/mrdoob/three.js/issues/20276.' );
+
+	Matrix4.prototype.makePerspective = function ( left, right, top, bottom, near, far ) {
+
+		const te = this.elements;
+		const x = 2 * near / ( right - left );
+		const y = 2 * near / ( top - bottom );
+
+		const a = ( right + left ) / ( right - left );
+		const b = ( top + bottom ) / ( top - bottom );
+		const c = - far / ( far - near );
+		const d = - far * near / ( far - near );
+
+		te[ 0 ] = x;	te[ 4 ] = 0;	te[ 8 ] = a;	te[ 12 ] = 0;
+		te[ 1 ] = 0;	te[ 5 ] = y;	te[ 9 ] = b;	te[ 13 ] = 0;
+		te[ 2 ] = 0;	te[ 6 ] = 0;	te[ 10 ] = c;	te[ 14 ] = d;
+		te[ 3 ] = 0;	te[ 7 ] = 0;	te[ 11 ] = - 1;	te[ 15 ] = 0;
+
+		return this;
+
+	};
+
+	Matrix4.prototype.makeOrthographic = function ( left, right, top, bottom, near, far ) {
+
+		const te = this.elements;
+		const w = 1.0 / ( right - left );
+		const h = 1.0 / ( top - bottom );
+		const p = 1.0 / ( far - near );
+
+		const x = ( right + left ) * w;
+		const y = ( top + bottom ) * h;
+		const z = near * p;
+
+		te[ 0 ] = 2 * w;	te[ 4 ] = 0;		te[ 8 ] = 0;		te[ 12 ] = - x;
+		te[ 1 ] = 0;		te[ 5 ] = 2 * h;	te[ 9 ] = 0;		te[ 13 ] = - y;
+		te[ 2 ] = 0;		te[ 6 ] = 0;		te[ 10 ] = - 1 * p;	te[ 14 ] = - z;
+		te[ 3 ] = 0;		te[ 7 ] = 0;		te[ 11 ] = 0;		te[ 15 ] = 1;
+
+		return this;
+
+	};
+
+	Frustum.prototype.setFromProjectionMatrix = function ( m ) {
+
+		const planes = this.planes;
+		const me = m.elements;
+		const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];
+		const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];
+		const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];
+		const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];
+
+		planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();
+		planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();
+		planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();
+		planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();
+		planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();
+		planes[ 5 ].setComponents( me2, me6, me10, me14 ).normalize();
+
+		return this;
+
+	};
+
+	_initializedHack = true;
+
+}
+
+_initWebGPUHack();
+
+//
+
+class WebGPUBackend extends Backend {
+
+	constructor( parameters = {} ) {
+
+		super( parameters );
+
+		// some parameters require default values other than "undefined"
+
+		this.parameters.antialias = ( parameters.antialias === true );
+
+		if ( this.parameters.antialias === true ) {
+
+			this.parameters.sampleCount = ( parameters.sampleCount === undefined ) ? 4 : parameters.sampleCount;
+
+		} else {
+
+			this.parameters.sampleCount = 1;
+
+		}
+
+		this.parameters.requiredLimits = ( parameters.requiredLimits === undefined ) ? {} : parameters.requiredLimits;
+
+		this.adapter = null;
+		this.device = null;
+		this.context = null;
+		this.colorBuffer = null;
+		this.depthBuffer = null;
+
+		this.utils = new WebGPUUtils( this );
+		this.attributeUtils = new WebGPUAttributeUtils( this );
+		this.bindingUtils = new WebGPUBindingUtils( this );
+		this.pipelineUtils = new WebGPUPipelineUtils( this );
+		this.textureUtils = new WebGPUTextureUtils( this );
+
+	}
+
+	async init( renderer ) {
+
+		await super.init( renderer );
+
+		//
+
+		const parameters = this.parameters;
+
+		const adapterOptions = {
+			powerPreference: parameters.powerPreference
+		};
+
+		const adapter = await navigator.gpu.requestAdapter( adapterOptions );
+
+		if ( adapter === null ) {
+
+			throw new Error( 'WebGPUBackend: Unable to create WebGPU adapter.' );
+
+		}
+
+		// feature support
+
+		const features = Object.values( GPUFeatureName );
+
+		const supportedFeatures = [];
+
+		for ( const name of features ) {
+
+			if ( adapter.features.has( name ) ) {
+
+				supportedFeatures.push( name );
+
+			}
+
+		}
+
+		const deviceDescriptor = {
+			requiredFeatures: supportedFeatures,
+			requiredLimits: parameters.requiredLimits
+		};
+
+		const device = await adapter.requestDevice( deviceDescriptor );
+
+		const context = ( parameters.context !== undefined ) ? parameters.context : renderer.domElement.getContext( 'webgpu' );
+
+		this.adapter = adapter;
+		this.device = device;
+		this.context = context;
+
+		this.updateSize();
+
+	}
+
+	async getArrayBuffer( attribute ) {
+
+		return await this.attributeUtils.getArrayBuffer( attribute );
+
+	}
+
+	beginRender( renderContext ) {
+
+		const renderContextData = this.get( renderContext );
+
+		const device = this.device;
+
+		const descriptor = {
+			colorAttachments: [ {
+				view: null
+			} ],
+			depthStencilAttachment: {
+				view: null
+			}
+		};
+
+		const colorAttachment = descriptor.colorAttachments[ 0 ];
+		const depthStencilAttachment = descriptor.depthStencilAttachment;
+
+		const antialias = this.parameters.antialias;
+
+		if ( renderContext.texture !== null ) {
+
+			const textureData = this.get( renderContext.texture );
+			const depthTextureData = this.get( renderContext.depthTexture );
+
+			// @TODO: Support RenderTarget with antialiasing.
+
+			colorAttachment.view = textureData.texture.createView();
+			depthStencilAttachment.view = depthTextureData.texture.createView();
+
+			if ( renderContext.stencil && renderContext.depthTexture.format === DepthFormat ) {
+
+				renderContext.stencil = false;
+
+			}
+
+		} else {
+
+			if ( antialias === true ) {
+
+				colorAttachment.view = this.colorBuffer.createView();
+				colorAttachment.resolveTarget = this.context.getCurrentTexture().createView();
+
+			} else {
+
+				colorAttachment.view = this.context.getCurrentTexture().createView();
+				colorAttachment.resolveTarget = undefined;
+
+			}
+
+			depthStencilAttachment.view = this.depthBuffer.createView();
+
+		}
+
+		if ( renderContext.clearColor ) {
+
+			colorAttachment.clearValue = renderContext.clearColorValue;
+			colorAttachment.loadOp = GPULoadOp.Clear;
+			colorAttachment.storeOp = GPUStoreOp.Store;
+
+		} else {
+
+			colorAttachment.loadOp = GPULoadOp.Load;
+			colorAttachment.storeOp = GPUStoreOp.Store;
+
+		}
+
+		//
+
+		if ( renderContext.depth ) {
+
+			if ( renderContext.clearDepth ) {
+
+				depthStencilAttachment.depthClearValue = renderContext.clearDepthValue;
+				depthStencilAttachment.depthLoadOp = GPULoadOp.Clear;
+				depthStencilAttachment.depthStoreOp = GPUStoreOp.Store;
+
+			} else {
+
+				depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
+				depthStencilAttachment.depthStoreOp = GPUStoreOp.Store;
+
+			}
+
+		}
+
+		if ( renderContext.stencil ) {
+
+			if ( renderContext.clearStencil ) {
+
+				depthStencilAttachment.stencilClearValue = renderContext.clearStencilValue;
+				depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear;
+				depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store;
+
+			} else {
+
+				depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
+				depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store;
+
+			}
+
+		}
+
+		//
+
+		const encoder = device.createCommandEncoder( { label: 'renderContext_' + renderContext.id } );
+		const currentPass = encoder.beginRenderPass( descriptor );
+
+		//
+
+		renderContextData.descriptor = descriptor;
+		renderContextData.encoder = encoder;
+		renderContextData.currentPass = currentPass;
+
+		//
+
+		if ( renderContext.viewport ) {
+
+			this.updateViewport( renderContext );
+
+		}
+
+		if ( renderContext.scissor ) {
+
+			const { x, y, width, height } = renderContext.scissorValue;
+
+			currentPass.setScissorRect( x, y, width, height );
+
+		}
+
+	}
+
+	finishRender( renderContext ) {
+
+		const renderContextData = this.get( renderContext );
+
+		renderContextData.currentPass.end();
+
+		this.device.queue.submit( [ renderContextData.encoder.finish() ] );
+
+	}
+
+	updateViewport( renderContext ) {
+
+		const { currentPass } = this.get( renderContext );
+		const { x, y, width, height, minDepth, maxDepth } = renderContext.viewportValue;
+
+		currentPass.setViewport( x, y, width, height, minDepth, maxDepth );
+
+	}
+
+	clear( renderContext, color, depth, stencil ) {
+
+		const device = this.device;
+		const renderContextData = this.get( renderContext );
+
+		const { descriptor } = renderContextData;
+
+		depth = depth && renderContext.depth;
+		stencil = stencil && renderContext.stencil;
+
+		const colorAttachment = descriptor.colorAttachments[ 0 ];
+
+		const antialias = this.parameters.antialias;
+
+		// @TODO: Include render target in clear operation.
+		if ( antialias === true ) {
+
+			colorAttachment.view = this.colorBuffer.createView();
+			colorAttachment.resolveTarget = this.context.getCurrentTexture().createView();
+
+		} else {
+
+			colorAttachment.view = this.context.getCurrentTexture().createView();
+			colorAttachment.resolveTarget = undefined;
+
+		}
+
+		descriptor.depthStencilAttachment.view = this.depthBuffer.createView();
+
+		if ( color ) {
+
+			colorAttachment.loadOp = GPULoadOp.Clear;
+			colorAttachment.clearValue = renderContext.clearColorValue;
+
+		}
+
+		if ( depth ) {
+
+			descriptor.depthStencilAttachment.depthLoadOp = GPULoadOp.Clear;
+			descriptor.depthStencilAttachment.depthClearValue = renderContext.clearDepthValue;
+
+		}
+
+		if ( stencil ) {
+
+			descriptor.depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear;
+			descriptor.depthStencilAttachment.stencilClearValue = renderContext.clearStencilValue;
+
+		}
+
+		renderContextData.encoder = device.createCommandEncoder( {} );
+		renderContextData.currentPass = renderContextData.encoder.beginRenderPass( descriptor );
+
+		renderContextData.currentPass.end();
+
+		device.queue.submit( [ renderContextData.encoder.finish() ] );
+
+	}
+
+	// compute
+
+	beginCompute( computeGroup ) {
+
+		const groupGPU = this.get( computeGroup );
+
+		groupGPU.cmdEncoderGPU = this.device.createCommandEncoder( {} );
+		groupGPU.passEncoderGPU = groupGPU.cmdEncoderGPU.beginComputePass();
+
+	}
+
+	compute( computeGroup, computeNode, bindings, pipeline ) {
+
+		const { passEncoderGPU } = this.get( computeGroup );
+
+		// pipeline
+
+		const pipelineGPU = this.get( pipeline ).pipeline;
+		passEncoderGPU.setPipeline( pipelineGPU );
+
+		// bind group
+
+		const bindGroupGPU = this.get( bindings ).group;
+		passEncoderGPU.setBindGroup( 0, bindGroupGPU );
+
+		passEncoderGPU.dispatchWorkgroups( computeNode.dispatchCount );
+
+	}
+
+	finishCompute( computeGroup ) {
+
+		const groupData = this.get( computeGroup );
+
+		groupData.passEncoderGPU.end();
+		this.device.queue.submit( [ groupData.cmdEncoderGPU.finish() ] );
+
+	}
+
+	// render object
+
+	draw( renderObject, info ) {
+
+		const { object, geometry, context, pipeline } = renderObject;
+
+		const bindingsData = this.get( renderObject.getBindings() );
+		const contextData = this.get( context );
+		const pipelineGPU = this.get( pipeline ).pipeline;
+
+		// pipeline
+
+		const passEncoderGPU = contextData.currentPass;
+		passEncoderGPU.setPipeline( pipelineGPU );
+
+		// bind group
+
+		const bindGroupGPU = bindingsData.group;
+		passEncoderGPU.setBindGroup( 0, bindGroupGPU );
+
+		// index
+
+		const index = renderObject.getIndex();
+
+		const hasIndex = ( index !== null );
+
+		if ( hasIndex === true ) {
+
+			const buffer = this.get( index ).buffer;
+			const indexFormat = ( index.array instanceof Uint16Array ) ? GPUIndexFormat.Uint16 : GPUIndexFormat.Uint32;
+
+			passEncoderGPU.setIndexBuffer( buffer, indexFormat );
+
+		}
+
+		// vertex buffers
+
+		const attributes = renderObject.getAttributes();
+
+		for ( let i = 0, l = attributes.length; i < l; i ++ ) {
+
+			const buffer = this.get( attributes[ i ] ).buffer;
+			passEncoderGPU.setVertexBuffer( i, buffer );
+
+		}
+
+		// draw
+
+		const drawRange = geometry.drawRange;
+		const firstVertex = drawRange.start;
+
+		const instanceCount = this.getInstanceCount( renderObject );
+
+		if ( hasIndex === true ) {
+
+			const indexCount = ( drawRange.count !== Infinity ) ? drawRange.count : index.count;
+
+			passEncoderGPU.drawIndexed( indexCount, instanceCount, firstVertex, 0, 0 );
+
+			info.update( object, indexCount, instanceCount );
+
+		} else {
+
+			const positionAttribute = geometry.attributes.position;
+			const vertexCount = ( drawRange.count !== Infinity ) ? drawRange.count : positionAttribute.count;
+
+			passEncoderGPU.draw( vertexCount, instanceCount, firstVertex, 0 );
+
+			info.update( object, vertexCount, instanceCount );
+
+		}
+
+	}
+
+	// cache key
+
+	needsUpdate( renderObject ) {
+
+		const renderObjectGPU = this.get( renderObject );
+
+		const { object, material } = renderObject;
+
+		const utils = this.utils;
+
+		const sampleCount = utils.getSampleCount();
+		const colorSpace = utils.getCurrentColorSpace( renderObject.context );
+		const colorFormat = utils.getCurrentColorFormat( renderObject.context );
+		const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
+		const primitiveTopology = utils.getPrimitiveTopology( object, material );
+
+		let needsUpdate = false;
+
+		if ( renderObjectGPU.sampleCount !== sampleCount || renderObjectGPU.colorSpace !== colorSpace ||
+			renderObjectGPU.colorFormat !== colorFormat || renderObjectGPU.depthStencilFormat !== depthStencilFormat ||
+            renderObjectGPU.primitiveTopology !== primitiveTopology ) {
+
+			renderObjectGPU.sampleCount = sampleCount;
+			renderObjectGPU.colorSpace = colorSpace;
+			renderObjectGPU.colorFormat = colorFormat;
+			renderObjectGPU.depthStencilFormat = depthStencilFormat;
+			renderObjectGPU.primitiveTopology = primitiveTopology;
+
+			needsUpdate = true;
+
+		}
+
+		return needsUpdate;
+
+	}
+
+	getCacheKey( renderObject ) {
+
+		const { object, material } = renderObject;
+
+		const utils = this.utils;
+		const renderContext = renderObject.context;
+
+		return [
+			utils.getSampleCount(),
+			utils.getCurrentColorSpace( renderContext ), utils.getCurrentColorFormat( renderContext ), utils.getCurrentDepthStencilFormat( renderContext ),
+			utils.getPrimitiveTopology( object, material )
+		].join();
+
+	}
+
+	// textures
+
+	createSampler( texture ) {
+
+		this.textureUtils.createSampler( texture );
+
+	}
+
+	destroySampler( texture ) {
+
+		this.textureUtils.destroySampler( texture );
+
+	}
+
+	createDefaultTexture( texture ) {
+
+		this.textureUtils.createDefaultTexture( texture );
+
+	}
+
+	createTexture( texture ) {
+
+		this.textureUtils.createTexture( texture );
+
+	}
+
+	updateTexture( texture ) {
+
+		this.textureUtils.updateTexture( texture );
+
+	}
+
+	destroyTexture( texture ) {
+
+		this.textureUtils.destroyTexture( texture );
+
+	}
+
+	// node builder
+
+	createNodeBuilder( object, renderer ) {
+
+		return new WebGPUNodeBuilder( object, renderer );
+
+	}
+
+	// program
+
+	createProgram( program ) {
+
+		const programGPU = this.get( program );
+
+		programGPU.module = {
+			module: this.device.createShaderModule( { code: program.code, label: program.stage } ),
+			entryPoint: 'main'
+		};
+
+	}
+
+	destroyProgram( program ) {
+
+		this.delete( program );
+
+	}
+
+	// pipelines
+
+	createRenderPipeline( renderObject ) {
+
+		this.pipelineUtils.createRenderPipeline( renderObject );
+
+	}
+
+	createComputePipeline( computePipeline ) {
+
+		this.pipelineUtils.createComputePipeline( computePipeline );
+
+	}
+
+	// bindings
+
+	createBindings( bindings, pipeline ) {
+
+		this.bindingUtils.createBindings( bindings, pipeline );
+
+	}
+
+	updateBindings( bindings, pipeline ) {
+
+		this.bindingUtils.createBindings( bindings, pipeline );
+
+	}
+
+	updateBinding( binding ) {
+
+		this.bindingUtils.updateBinding( binding );
+
+	}
+
+	// attributes
+
+	createIndexAttribute( attribute ) {
+
+		this.attributeUtils.createAttribute( attribute, GPUBufferUsage.INDEX | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST );
+
+	}
+
+	createAttribute( attribute ) {
+
+		this.attributeUtils.createAttribute( attribute, GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST );
+
+	}
+
+	createStorageAttribute( attribute ) {
+
+		this.attributeUtils.createAttribute( attribute, GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST );
+
+	}
+
+	updateAttribute( attribute ) {
+
+		this.attributeUtils.updateAttribute( attribute );
+
+	}
+
+	destroyAttribute( attribute ) {
+
+		this.attributeUtils.destroyAttribute( attribute );
+
+	}
+
+	// canvas
+
+	updateSize() {
+
+		this._configureContext();
+		this._setupColorBuffer();
+		this._setupDepthBuffer();
+
+	}
+
+	// utils public
+
+	hasFeature( name ) {
+
+		const adapter = this.adapter || _staticAdapter;
+
+		//
+
+		const features = Object.values( GPUFeatureName );
+
+		if ( features.includes( name ) === false ) {
+
+			throw new Error( 'THREE.WebGPURenderer: Unknown WebGPU GPU feature: ' + name );
+
+		}
+
+		//
+
+		return adapter.features.has( name );
+
+	}
+
+	copyFramebufferToTexture( framebufferTexture, renderContext ) {
+
+		const renderContextData = this.get( renderContext );
+
+		const { encoder, descriptor } = renderContextData;
+
+		const sourceGPU = this.context.getCurrentTexture();
+		const destinationGPU = this.get( framebufferTexture ).texture;
+
+		renderContextData.currentPass.end();
+
+		encoder.copyTextureToTexture(
+			{
+			  texture: sourceGPU
+			},
+			{
+			  texture: destinationGPU
+			},
+			[
+				framebufferTexture.image.width,
+				framebufferTexture.image.height
+			]
+		);
+
+		descriptor.colorAttachments[ 0 ].loadOp = GPULoadOp.Load;
+		if ( renderContext.depth ) descriptor.depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
+		if ( renderContext.stencil ) descriptor.depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
+
+		renderContextData.currentPass = encoder.beginRenderPass( descriptor );
+
+	}
+
+	// utils
+
+	_configureContext() {
+
+		this.context.configure( {
+			device: this.device,
+			format: GPUTextureFormat.BGRA8Unorm,
+			usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
+			alphaMode: 'premultiplied'
+		} );
+
+	}
+
+	_setupColorBuffer() {
+
+		if ( this.colorBuffer ) this.colorBuffer.destroy();
+
+		const { width, height } = this.getDrawingBufferSize();
+		//const format = navigator.gpu.getPreferredCanvasFormat(); // @TODO: Move to WebGPUUtils
+
+		this.colorBuffer = this.device.createTexture( {
+			label: 'colorBuffer',
+			size: {
+				width: width,
+				height: height,
+				depthOrArrayLayers: 1
+			},
+			sampleCount: this.parameters.sampleCount,
+			format: GPUTextureFormat.BGRA8Unorm,
+			usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
+		} );
+
+	}
+
+	_setupDepthBuffer() {
+
+		if ( this.depthBuffer ) this.depthBuffer.destroy();
+
+		const { width, height } = this.getDrawingBufferSize();
+
+		this.depthBuffer = this.device.createTexture( {
+			label: 'depthBuffer',
+			size: {
+				width: width,
+				height: height,
+				depthOrArrayLayers: 1
+			},
+			sampleCount: this.parameters.sampleCount,
+			format: GPUTextureFormat.Depth24PlusStencil8,
+			usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
+		} );
+
+	}
+
+}
+
+export default WebGPUBackend;

+ 0 - 182
examples/jsm/renderers/webgpu/WebGPUBackground.js

@@ -1,182 +0,0 @@
-import { GPULoadOp, GPUStoreOp } from './constants.js';
-import { Color, Mesh, BoxGeometry, BackSide } from 'three';
-import { context, positionWorldDirection, MeshBasicNodeMaterial } from '../../nodes/Nodes.js';
-
-let _clearAlpha;
-const _clearColor = new Color();
-
-class WebGPUBackground {
-
-	constructor( renderer, properties ) {
-
-		this.renderer = renderer;
-		this.properties = properties;
-
-		this.boxMesh = null;
-		this.boxMeshNode = null;
-
-	}
-
-	update( scene, renderList, renderState ) {
-
-		const renderer = this.renderer;
-		const background = ( scene.isScene === true ) ? scene.backgroundNode || this.properties.get( scene ).backgroundNode || scene.background : null;
-
-		let forceClear = false;
-
-		if ( background === null ) {
-
-			// no background settings, use clear color configuration from the renderer
-
-			_clearColor.copy( renderer._clearColor );
-			_clearAlpha = renderer._clearAlpha;
-
-		} else if ( background.isColor === true ) {
-
-			// background is an opaque color
-
-			_clearColor.copy( background );
-			_clearAlpha = 1;
-			forceClear = true;
-
-		} else if ( background.isNode === true ) {
-
-			const sceneProperties = this.properties.get( scene );
-			const backgroundNode = background;
-
-			_clearColor.copy( renderer._clearColor );
-			_clearAlpha = renderer._clearAlpha;
-
-			let boxMesh = this.boxMesh;
-
-			if ( boxMesh === null ) {
-
-				this.boxMeshNode = context( backgroundNode, {
-					// @TODO: Add Texture2D support using node context
-					getUVNode: () => positionWorldDirection
-				} );
-
-				const nodeMaterial = new MeshBasicNodeMaterial();
-				nodeMaterial.colorNode = this.boxMeshNode;
-				nodeMaterial.side = BackSide;
-				nodeMaterial.depthTest = false;
-				nodeMaterial.depthWrite = false;
-				nodeMaterial.fog = false;
-
-				this.boxMesh = boxMesh = new Mesh( new BoxGeometry( 1, 1, 1 ), nodeMaterial );
-				boxMesh.frustumCulled = false;
-
-				boxMesh.onBeforeRender = function ( renderer, scene, camera ) {
-
-					const scale = camera.far;
-
-					this.matrixWorld.makeScale( scale, scale, scale ).copyPosition( camera.matrixWorld );
-
-				};
-
-			}
-
-			const backgroundCacheKey = backgroundNode.getCacheKey();
-
-			if ( sceneProperties.backgroundCacheKey !== backgroundCacheKey ) {
-
-				this.boxMeshNode.node = backgroundNode;
-
-				boxMesh.material.needsUpdate = true;
-
-				sceneProperties.backgroundCacheKey = backgroundCacheKey;
-
-			}
-
-			renderList.unshift( boxMesh, boxMesh.geometry, boxMesh.material, 0, 0, null );
-
-		} else {
-
-			console.error( 'THREE.WebGPURenderer: Unsupported background configuration.', background );
-
-		}
-
-		// configure render pass descriptor
-
-		const colorAttachment = renderState.descriptorGPU.colorAttachments[ 0 ];
-		const depthStencilAttachment = renderState.descriptorGPU.depthStencilAttachment;
-
-		if ( renderer.autoClear === true || forceClear === true ) {
-
-			if ( renderer.autoClearColor === true ) {
-
-				_clearColor.multiplyScalar( _clearAlpha );
-
-				colorAttachment.clearValue = { r: _clearColor.r, g: _clearColor.g, b: _clearColor.b, a: _clearAlpha };
-				colorAttachment.loadOp = GPULoadOp.Clear;
-				colorAttachment.storeOp = GPUStoreOp.Store;
-
-			} else {
-
-				colorAttachment.loadOp = GPULoadOp.Load;
-				colorAttachment.storeOp = GPUStoreOp.Store;
-
-			}
-
-			if ( renderState.depth ) {
-
-				if ( renderer.autoClearDepth === true ) {
-
-					depthStencilAttachment.depthClearValue = renderer._clearDepth;
-					depthStencilAttachment.depthLoadOp = GPULoadOp.Clear;
-					depthStencilAttachment.depthStoreOp = GPUStoreOp.Store;
-
-				} else {
-
-					depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
-					depthStencilAttachment.depthStoreOp = GPUStoreOp.Store;
-
-				}
-
-			}
-
-			if ( renderState.stencil ) {
-
-				if ( renderer.autoClearStencil === true ) {
-
-					depthStencilAttachment.stencilClearValue = renderer._clearStencil;
-					depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear;
-					depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store;
-
-				} else {
-
-					depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
-					depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store;
-
-				}
-
-			}
-
-		} else {
-
-			colorAttachment.loadOp = GPULoadOp.Load;
-			colorAttachment.storeOp = GPUStoreOp.Store;
-
-			if ( renderState.depth ) {
-
-				depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
-				depthStencilAttachment.depthStoreOp = GPUStoreOp.Store;
-
-			}
-
-			if ( renderState.stencil ) {
-
-				depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
-				depthStencilAttachment.stencilStoreOp = GPUStoreOp.Store;
-
-			}
-
-		}
-
-		this.forceClear = false;
-
-	}
-
-}
-
-export default WebGPUBackground;

+ 0 - 22
examples/jsm/renderers/webgpu/WebGPUBinding.js

@@ -1,22 +0,0 @@
-class WebGPUBinding {
-
-	constructor( name = '' ) {
-
-		this.name = name;
-		this.visibility = null;
-
-		this.type = null; // read-only
-
-		this.isShared = false;
-
-	}
-
-	setVisibility( visibility ) {
-
-		this.visibility = visibility;
-
-	}
-
-}
-
-export default WebGPUBinding;

+ 0 - 270
examples/jsm/renderers/webgpu/WebGPUBindings.js

@@ -1,270 +0,0 @@
-class WebGPUBindings {
-
-	constructor( device, info, properties, textures, renderPipelines, computePipelines, attributes, nodes ) {
-
-		this.device = device;
-		this.info = info;
-		this.properties = properties;
-		this.textures = textures;
-		this.renderPipelines = renderPipelines;
-		this.computePipelines = computePipelines;
-		this.attributes = attributes;
-		this.nodes = nodes;
-
-		this.uniformsData = new WeakMap();
-
-		this.updateMap = new WeakMap();
-
-	}
-
-	get( renderObject ) {
-
-		let data = this.uniformsData.get( renderObject );
-
-		if ( data === undefined ) {
-
-			// each object defines an array of bindings (ubos, textures, samplers etc.)
-
-			const nodeBuilder = this.nodes.get( renderObject );
-			const bindings = nodeBuilder.getBindings();
-
-			// setup (static) binding layout and (dynamic) binding group
-
-			const pipeline = this.renderPipelines.get( renderObject ).pipeline;
-
-			const bindLayout = pipeline.getBindGroupLayout( 0 );
-			const bindGroup = this._createBindGroup( bindings, bindLayout );
-
-			data = {
-				layout: bindLayout,
-				group: bindGroup,
-				bindings: bindings
-			};
-
-			this.uniformsData.set( renderObject, data );
-
-		}
-
-		return data;
-
-	}
-
-	getForCompute( computeNode ) {
-
-		let data = this.uniformsData.get( computeNode );
-
-		if ( data === undefined ) {
-
-			// each object defines an array of bindings (ubos, textures, samplers etc.)
-
-			const nodeBuilder = this.nodes.getForCompute( computeNode );
-			const bindings = nodeBuilder.getBindings();
-
-			// setup (static) binding layout and (dynamic) binding group
-
-			const pipeline = this.computePipelines.get( computeNode );
-
-			const bindLayout = pipeline.getBindGroupLayout( 0 );
-			const bindGroup = this._createBindGroup( bindings, bindLayout );
-
-			data = {
-				layout: bindLayout,
-				group: bindGroup,
-				bindings: bindings
-			};
-
-			this.uniformsData.set( computeNode, data );
-
-		}
-
-		return data;
-
-	}
-
-	remove( object ) {
-
-		this.uniformsData.delete( object );
-
-	}
-
-	update( object ) {
-
-		const textures = this.textures;
-
-		const data = this.get( object );
-		const bindings = data.bindings;
-
-		const updateMap = this.updateMap;
-		const frame = this.info.render.frame;
-
-		let needsBindGroupRefresh = false;
-
-		// iterate over all bindings and check if buffer updates or a new binding group is required
-
-		for ( const binding of bindings ) {
-
-			const isShared = binding.isShared;
-			const isUpdated = updateMap.get( binding ) === frame;
-
-			if ( isShared && isUpdated ) continue;
-
-			if ( binding.isUniformBuffer ) {
-
-				const buffer = binding.getBuffer();
-				const needsBufferWrite = binding.update();
-
-				if ( needsBufferWrite === true ) {
-
-					const bufferGPU = binding.bufferGPU;
-
-					this.device.queue.writeBuffer( bufferGPU, 0, buffer, 0 );
-
-				}
-
-			} else if ( binding.isStorageBuffer ) {
-
-				const attribute = binding.attribute;
-
-				this.attributes.update( attribute, false, binding.usage );
-
-			} else if ( binding.isSampler ) {
-
-				const texture = binding.getTexture();
-
-				textures.updateSampler( texture );
-
-				const samplerGPU = textures.getSampler( texture );
-
-				if ( binding.samplerGPU !== samplerGPU ) {
-
-					binding.samplerGPU = samplerGPU;
-					needsBindGroupRefresh = true;
-
-				}
-
-			} else if ( binding.isSampledTexture ) {
-
-				const texture = binding.getTexture();
-
-				const needsTextureRefresh = textures.updateTexture( texture );
-
-				const textureGPU = textures.getTextureGPU( texture );
-
-				if ( textureGPU !== undefined && binding.textureGPU !== textureGPU || needsTextureRefresh === true ) {
-
-					binding.textureGPU = textureGPU;
-					needsBindGroupRefresh = true;
-
-				}
-
-			}
-
-			updateMap.set( binding, frame );
-
-		}
-
-		if ( needsBindGroupRefresh === true ) {
-
-			data.group = this._createBindGroup( bindings, data.layout );
-
-		}
-
-	}
-
-	dispose() {
-
-		this.uniformsData = new WeakMap();
-		this.updateMap = new WeakMap();
-
-	}
-
-	_createBindGroup( bindings, layout ) {
-
-		let bindingPoint = 0;
-		const entries = [];
-
-		for ( const binding of bindings ) {
-
-			if ( binding.isUniformBuffer ) {
-
-				if ( binding.bufferGPU === null ) {
-
-					const byteLength = binding.getByteLength();
-
-					binding.bufferGPU = this.device.createBuffer( {
-						label: 'bindingBuffer',
-						size: byteLength,
-						usage: binding.usage
-					} );
-
-				}
-
-				entries.push( { binding: bindingPoint, resource: { buffer: binding.bufferGPU } } );
-
-			} else if ( binding.isStorageBuffer ) {
-
-				if ( binding.bufferGPU === null ) {
-
-					const attribute = binding.attribute;
-
-					this.attributes.update( attribute, false, binding.usage );
-					binding.bufferGPU = this.attributes.get( attribute ).buffer;
-
-				}
-
-				entries.push( { binding: bindingPoint, resource: { buffer: binding.bufferGPU } } );
-
-			} else if ( binding.isSampler ) {
-
-				if ( binding.samplerGPU === null ) {
-
-					binding.samplerGPU = this.textures.getDefaultSampler();
-
-				}
-
-				entries.push( { binding: bindingPoint, resource: binding.samplerGPU } );
-
-			} else if ( binding.isSampledTexture ) {
-
-				if ( binding.textureGPU === null ) {
-
-					if ( binding.isSampledCubeTexture ) {
-
-						binding.textureGPU = this.textures.getDefaultCubeTexture();
-
-					} else if ( binding.texture.isVideoTexture ) {
-
-						binding.textureGPU = this.textures.getDefaultVideoTexture();
-
-					} else if ( binding.texture.isDepthTexture ) {
-
-						binding.textureGPU = this.textures.getDefaultDepthTexture();
-
-					} else {
-
-						binding.textureGPU = this.textures.getDefaultTexture();
-
-					}
-
-				}
-
-				const resource = binding.textureGPU instanceof GPUTexture ? binding.textureGPU.createView( { aspect: binding.aspect, dimension: binding.dimension } ) : binding.textureGPU;
-
-				entries.push( { binding: bindingPoint, resource } );
-
-			}
-
-			bindingPoint ++;
-
-		}
-
-		return this.device.createBindGroup( {
-			layout,
-			entries
-		} );
-
-	}
-
-}
-
-export default WebGPUBindings;

+ 0 - 43
examples/jsm/renderers/webgpu/WebGPUBuffer.js

@@ -1,43 +0,0 @@
-import WebGPUBinding from './WebGPUBinding.js';
-import { getFloatLength } from './WebGPUBufferUtils.js';
-
-class WebGPUBuffer extends WebGPUBinding {
-
-	constructor( name, type, buffer = null ) {
-
-		super( name );
-
-		this.isBuffer = true;
-
-		this.bytesPerElement = Float32Array.BYTES_PER_ELEMENT;
-		this.type = type;
-		this.visibility = GPUShaderStage.VERTEX;
-
-		this.usage = GPUBufferUsage.COPY_DST;
-
-		this.buffer = buffer;
-		this.bufferGPU = null; // set by the renderer
-
-	}
-
-	getByteLength() {
-
-		return getFloatLength( this.buffer.byteLength );
-
-	}
-
-	getBuffer() {
-
-		return this.buffer;
-
-	}
-
-	update() {
-
-		return true;
-
-	}
-
-}
-
-export default WebGPUBuffer;

+ 0 - 78
examples/jsm/renderers/webgpu/WebGPUComputePipelines.js

@@ -1,78 +0,0 @@
-import WebGPUProgrammableStage from './WebGPUProgrammableStage.js';
-
-class WebGPUComputePipelines {
-
-	constructor( device, nodes ) {
-
-		this.device = device;
-		this.nodes = nodes;
-
-		this.pipelines = new WeakMap();
-		this.stages = {
-			compute: new WeakMap()
-		};
-
-	}
-
-	has( computeNode ) {
-
-		return this.pipelines.get( computeNode ) !== undefined;
-
-	}
-
-	get( computeNode ) {
-
-		let pipeline = this.pipelines.get( computeNode );
-
-		// @TODO: Reuse compute pipeline if possible, introduce WebGPUComputePipeline
-
-		if ( pipeline === undefined ) {
-
-			const device = this.device;
-
-			// get shader
-
-			const nodeBuilder = this.nodes.getForCompute( computeNode );
-			const computeShader = nodeBuilder.computeShader;
-
-			const shader = {
-				computeShader
-			};
-
-			// programmable stage
-
-			let stageCompute = this.stages.compute.get( shader );
-
-			if ( stageCompute === undefined ) {
-
- 				stageCompute = new WebGPUProgrammableStage( device, computeShader, 'compute' );
-
-				this.stages.compute.set( shader, stageCompute );
-
-			}
-
-			pipeline = device.createComputePipeline( {
-				compute: stageCompute.stage,
-				layout: 'auto'
-			} );
-
-			this.pipelines.set( computeNode, pipeline );
-
-		}
-
-		return pipeline;
-
-	}
-
-	dispose() {
-
-		this.pipelines = new WeakMap();
-		this.stages = {
-			compute: new WeakMap()
-		};
-
-	}
-
-}
-
-export default WebGPUComputePipelines;

+ 0 - 22
examples/jsm/renderers/webgpu/WebGPUProgrammableStage.js

@@ -1,22 +0,0 @@
-let _id = 0;
-
-class WebGPUProgrammableStage {
-
-	constructor( device, code, type ) {
-
-		this.id = _id ++;
-
-		this.code = code;
-		this.type = type;
-		this.usedTimes = 0;
-
-		this.stage = {
-			module: device.createShaderModule( { code, label: type } ),
-			entryPoint: 'main'
-		};
-
-	}
-
-}
-
-export default WebGPUProgrammableStage;

+ 0 - 44
examples/jsm/renderers/webgpu/WebGPUProperties.js

@@ -1,44 +0,0 @@
-class WebGPUProperties {
-
-	constructor() {
-
-		this.properties = new WeakMap();
-
-	}
-
-	get( object ) {
-
-		let map = this.properties.get( object );
-
-		if ( map === undefined ) {
-
-			map = {};
-			this.properties.set( object, map );
-
-		}
-
-		return map;
-
-	}
-
-	remove( object ) {
-
-		this.properties.delete( object );
-
-	}
-
-	has( object ) {
-
-		return this.properties.has( object );
-
-	}
-
-	dispose() {
-
-		this.properties = new WeakMap();
-
-	}
-
-}
-
-export default WebGPUProperties;

+ 0 - 50
examples/jsm/renderers/webgpu/WebGPURenderObjects.js

@@ -1,50 +0,0 @@
-import WebGPUWeakMap from './WebGPUWeakMap.js';
-import WebGPURenderObject from './WebGPURenderObject.js';
-
-class WebGPURenderObjects {
-
-	constructor( renderer, nodes, geometries, info ) {
-
-		this.renderer = renderer;
-		this.nodes = nodes;
-		this.geometries = geometries;
-		this.info = info;
-
-		this.cache = new WebGPUWeakMap();
-
-	}
-
-	get( object, material, scene, camera, lightsNode ) {
-
-		const chainKey = [ object, material, scene, camera, lightsNode ];
-
-		let renderObject = this.cache.get( chainKey );
-
-		if ( renderObject === undefined ) {
-
-			renderObject = new WebGPURenderObject( this.renderer, this.nodes, object, material, scene, camera, lightsNode );
-
-			this.cache.set( chainKey, renderObject );
-
-		}
-
-		return renderObject;
-
-	}
-
-	remove( object, material, scene, camera, lightsNode ) {
-
-		this.cache.delete( [ object, material, scene, camera, lightsNode ] );
-
-	}
-
-	dispose() {
-
-		this.cache = new WebGPUWeakMap();
-		this.updateMap = new WeakMap();
-
-	}
-
-}
-
-export default WebGPURenderObjects;

+ 0 - 274
examples/jsm/renderers/webgpu/WebGPURenderPipelines.js

@@ -1,274 +0,0 @@
-import WebGPURenderPipeline from './WebGPURenderPipeline.js';
-import WebGPUProgrammableStage from './WebGPUProgrammableStage.js';
-
-class WebGPURenderPipelines {
-
-	constructor( device, nodes, utils ) {
-
-		this.device = device;
-		this.nodes = nodes;
-		this.utils = utils;
-
-		this.bindings = null;
-
-		this.pipelines = [];
-		this.cache = new WeakMap();
-
-		this.stages = {
-			vertex: new Map(),
-			fragment: new Map()
-		};
-
-	}
-
-	get( renderObject ) {
-
-		const device = this.device;
-		const cache = this._getCache( renderObject );
-
-		let currentPipeline = cache.currentPipeline;
-
-		if ( this._needsUpdate( renderObject ) ) {
-
-			// release previous cache
-
-			this._releasePipeline( renderObject );
-
-			// get shader
-
-			const nodeBuilder = this.nodes.get( renderObject );
-
-			// programmable stages
-
-			let stageVertex = this.stages.vertex.get( nodeBuilder.vertexShader );
-
-			if ( stageVertex === undefined ) {
-
-				stageVertex = new WebGPUProgrammableStage( device, nodeBuilder.vertexShader, 'vertex' );
-				this.stages.vertex.set( nodeBuilder.vertexShader, stageVertex );
-
-			}
-
-			let stageFragment = this.stages.fragment.get( nodeBuilder.fragmentShader );
-
-			if ( stageFragment === undefined ) {
-
-				stageFragment = new WebGPUProgrammableStage( device, nodeBuilder.fragmentShader, 'fragment' );
-				this.stages.fragment.set( nodeBuilder.fragmentShader, stageFragment );
-
-			}
-
-			// determine render pipeline
-
-			currentPipeline = this._acquirePipeline( renderObject, stageVertex, stageFragment );
-			cache.currentPipeline = currentPipeline;
-
-			// keep track of all used times
-
-			currentPipeline.usedTimes ++;
-			stageVertex.usedTimes ++;
-			stageFragment.usedTimes ++;
-
-		}
-
-		return currentPipeline;
-
-	}
-
-	remove( renderObject ) {
-
-		this._releasePipeline( renderObject );
-
-	}
-
-	dispose() {
-
-		this.pipelines = [];
-		this.cache = new WeakMap();
-		this.shaderModules = {
-			vertex: new Map(),
-			fragment: new Map()
-		};
-
-	}
-
-	_acquirePipeline( renderObject, stageVertex, stageFragment ) {
-
-		let pipeline;
-		const pipelines = this.pipelines;
-
-		// check for existing pipeline
-
-		const cacheKey = this._computeCacheKey( renderObject, stageVertex, stageFragment );
-
-		for ( let i = 0, il = pipelines.length; i < il; i ++ ) {
-
-			const preexistingPipeline = pipelines[ i ];
-
-			if ( preexistingPipeline.cacheKey === cacheKey ) {
-
-				pipeline = preexistingPipeline;
-				break;
-
-			}
-
-		}
-
-		if ( pipeline === undefined ) {
-
-			pipeline = new WebGPURenderPipeline( this.device, this.utils );
-			pipeline.init( renderObject, cacheKey, stageVertex, stageFragment );
-
-			pipelines.push( pipeline );
-
-		}
-
-		return pipeline;
-
-	}
-
-	_computeCacheKey( renderObject, stageVertex, stageFragment ) {
-
-		const { object, material } = renderObject;
-		const utils = this.utils;
-
-		const parameters = [
-			stageVertex.id, stageFragment.id,
-			material.transparent, material.blending, material.premultipliedAlpha,
-			material.blendSrc, material.blendDst, material.blendEquation,
-			material.blendSrcAlpha, material.blendDstAlpha, material.blendEquationAlpha,
-			material.colorWrite,
-			material.depthWrite, material.depthTest, material.depthFunc,
-			material.stencilWrite, material.stencilFunc,
-			material.stencilFail, material.stencilZFail, material.stencilZPass,
-			material.stencilFuncMask, material.stencilWriteMask,
-			material.side,
-			utils.getSampleCount(),
-			utils.getCurrentColorSpace(), utils.getCurrentColorFormat(), utils.getCurrentDepthStencilFormat(),
-			utils.getPrimitiveTopology( object, material )
-		];
-
-		return parameters.join();
-
-	}
-
-	_getCache( renderObject ) {
-
-		let cache = this.cache.get( renderObject );
-
-		if ( cache === undefined ) {
-
-			cache = {};
-			this.cache.set( renderObject, cache );
-
-		}
-
-		return cache;
-
-	}
-
-	_releasePipeline( renderObject ) {
-
-		const cache = this._getCache( renderObject );
-
-		const pipeline = cache.currentPipeline;
-		delete cache.currentPipeline;
-
-		this.bindings.remove( renderObject );
-
-		if ( pipeline && -- pipeline.usedTimes === 0 ) {
-
-			const pipelines = this.pipelines;
-
-			const i = pipelines.indexOf( pipeline );
-			pipelines[ i ] = pipelines[ pipelines.length - 1 ];
-			pipelines.pop();
-
-			this._releaseStage( pipeline.stageVertex );
-			this._releaseStage( pipeline.stageFragment );
-
-		}
-
-	}
-
-	_releaseStage( stage ) {
-
-		if ( -- stage.usedTimes === 0 ) {
-
-			const code = stage.code;
-			const type = stage.type;
-
-			this.stages[ type ].delete( code );
-
-		}
-
-	}
-
-	_needsUpdate( renderObject ) {
-
-		const cache = this._getCache( renderObject );
-		const material = renderObject.material;
-
-		let needsUpdate = false;
-
-		// check pipeline state
-
-		if ( cache.currentPipeline === undefined ) needsUpdate = true;
-
-		// check material state
-
-		if ( cache.material !== material || cache.materialVersion !== material.version ||
-			cache.transparent !== material.transparent || cache.blending !== material.blending || cache.premultipliedAlpha !== material.premultipliedAlpha ||
-			cache.blendSrc !== material.blendSrc || cache.blendDst !== material.blendDst || cache.blendEquation !== material.blendEquation ||
-			cache.blendSrcAlpha !== material.blendSrcAlpha || cache.blendDstAlpha !== material.blendDstAlpha || cache.blendEquationAlpha !== material.blendEquationAlpha ||
-			cache.colorWrite !== material.colorWrite ||
-			cache.depthWrite !== material.depthWrite || cache.depthTest !== material.depthTest || cache.depthFunc !== material.depthFunc ||
-			cache.stencilWrite !== material.stencilWrite || cache.stencilFunc !== material.stencilFunc ||
-			cache.stencilFail !== material.stencilFail || cache.stencilZFail !== material.stencilZFail || cache.stencilZPass !== material.stencilZPass ||
-			cache.stencilFuncMask !== material.stencilFuncMask || cache.stencilWriteMask !== material.stencilWriteMask ||
-			cache.side !== material.side
-		) {
-
-			cache.material = material; cache.materialVersion = material.version;
-			cache.transparent = material.transparent; cache.blending = material.blending; cache.premultipliedAlpha = material.premultipliedAlpha;
-			cache.blendSrc = material.blendSrc; cache.blendDst = material.blendDst; cache.blendEquation = material.blendEquation;
-			cache.blendSrcAlpha = material.blendSrcAlpha; cache.blendDstAlpha = material.blendDstAlpha; cache.blendEquationAlpha = material.blendEquationAlpha;
-			cache.colorWrite = material.colorWrite;
-			cache.depthWrite = material.depthWrite; cache.depthTest = material.depthTest; cache.depthFunc = material.depthFunc;
-			cache.stencilWrite = material.stencilWrite; cache.stencilFunc = material.stencilFunc;
-			cache.stencilFail = material.stencilFail; cache.stencilZFail = material.stencilZFail; cache.stencilZPass = material.stencilZPass;
-			cache.stencilFuncMask = material.stencilFuncMask; cache.stencilWriteMask = material.stencilWriteMask;
-			cache.side = material.side;
-
-			needsUpdate = true;
-
-		}
-
-		// check renderer state
-
-		const utils = this.utils;
-
-		const sampleCount = utils.getSampleCount();
-		const colorSpace = utils.getCurrentColorSpace();
-		const colorFormat = utils.getCurrentColorFormat();
-		const depthStencilFormat = utils.getCurrentDepthStencilFormat();
-
-		if ( cache.sampleCount !== sampleCount || cache.colorSpace !== colorSpace ||
-			cache.colorFormat !== colorFormat || cache.depthStencilFormat !== depthStencilFormat ) {
-
-			cache.sampleCount = sampleCount;
-			cache.colorSpace = colorSpace;
-			cache.colorFormat = colorFormat;
-			cache.depthStencilFormat = depthStencilFormat;
-
-			needsUpdate = true;
-
-		}
-
-		return needsUpdate;
-
-	}
-
-}
-
-export default WebGPURenderPipelines;

+ 0 - 54
examples/jsm/renderers/webgpu/WebGPURenderStates.js

@@ -1,54 +0,0 @@
-import WebGPUWeakMap from './WebGPUWeakMap.js';
-
-class WebGPURenderState {
-
-	constructor() {
-
-		this.depth = true;
-		this.stencil = true;
-
-		// defined by renderer(backend)
-
-		this.descriptorGPU = null;
-		this.encoderGPU = null;
-		this.currentPassGPU = null;
-
-	}
-
-}
-
-class WebGPURenderStates {
-
-	constructor() {
-
-		this.renderStates = new WebGPUWeakMap();
-
-	}
-
-	get( scene, camera ) {
-
-		const chainKey = [ scene, camera ];
-
-		let renderState = this.renderStates.get( chainKey );
-
-		if ( renderState === undefined ) {
-
-			renderState = new WebGPURenderState();
-
-			this.renderStates.set( chainKey, renderState );
-
-		}
-
-		return renderState;
-
-	}
-
-	dispose() {
-
-		this.renderStates = new WebGPUWeakMap();
-
-	}
-
-}
-
-export default WebGPURenderStates;

+ 15 - 1219
examples/jsm/renderers/webgpu/WebGPURenderer.js

@@ -1,1233 +1,29 @@
-import { GPUIndexFormat, GPUTextureFormat, GPUFeatureName, GPULoadOp } from './constants.js';
-import WebGPUAnimation from './WebGPUAnimation.js';
-import WebGPURenderObjects from './WebGPURenderObjects.js';
-import WebGPUAttributes from './WebGPUAttributes.js';
-import WebGPUGeometries from './WebGPUGeometries.js';
-import WebGPUInfo from './WebGPUInfo.js';
-import WebGPUProperties from './WebGPUProperties.js';
-import WebGPURenderPipelines from './WebGPURenderPipelines.js';
-import WebGPUComputePipelines from './WebGPUComputePipelines.js';
-import WebGPUBindings from './WebGPUBindings.js';
-import WebGPURenderLists from './WebGPURenderLists.js';
-import WebGPURenderStates from './WebGPURenderStates.js';
-import WebGPUTextures from './WebGPUTextures.js';
-import WebGPUBackground from './WebGPUBackground.js';
-import WebGPUNodes from './nodes/WebGPUNodes.js';
-import WebGPUUtils from './WebGPUUtils.js';
-import { Frustum, Matrix4, Vector3, Color, SRGBColorSpace, NoToneMapping, DepthFormat } from 'three';
+import Renderer from '../common/Renderer.js';
+import WebGPUBackend from './WebGPUBackend.js';
+/*
+const debugHandler = {
 
 
-let staticAdapter = null;
+	get: function ( target, name ) {
 
 
-if ( navigator.gpu !== undefined ) {
+		// Add |update
+		if ( /^(create|destroy)/.test( name ) ) console.log( 'WebGPUBackend.' + name );
 
 
-	staticAdapter = await navigator.gpu.requestAdapter();
+		return target[ name ];
 
 
-}
-
-console.info( 'THREE.WebGPURenderer: Modified Matrix4.makePerspective() and Matrix4.makeOrtographic() to work with WebGPU, see https://github.com/mrdoob/three.js/issues/20276.' );
-
-Matrix4.prototype.makePerspective = function ( left, right, top, bottom, near, far ) {
-
-	const te = this.elements;
-	const x = 2 * near / ( right - left );
-	const y = 2 * near / ( top - bottom );
-
-	const a = ( right + left ) / ( right - left );
-	const b = ( top + bottom ) / ( top - bottom );
-	const c = - far / ( far - near );
-	const d = - far * near / ( far - near );
-
-	te[ 0 ] = x;	te[ 4 ] = 0;	te[ 8 ] = a;	te[ 12 ] = 0;
-	te[ 1 ] = 0;	te[ 5 ] = y;	te[ 9 ] = b;	te[ 13 ] = 0;
-	te[ 2 ] = 0;	te[ 6 ] = 0;	te[ 10 ] = c;	te[ 14 ] = d;
-	te[ 3 ] = 0;	te[ 7 ] = 0;	te[ 11 ] = - 1;	te[ 15 ] = 0;
-
-	return this;
-
-};
-
-Matrix4.prototype.makeOrthographic = function ( left, right, top, bottom, near, far ) {
-
-	const te = this.elements;
-	const w = 1.0 / ( right - left );
-	const h = 1.0 / ( top - bottom );
-	const p = 1.0 / ( far - near );
-
-	const x = ( right + left ) * w;
-	const y = ( top + bottom ) * h;
-	const z = near * p;
-
-	te[ 0 ] = 2 * w;	te[ 4 ] = 0;		te[ 8 ] = 0;		te[ 12 ] = - x;
-	te[ 1 ] = 0;		te[ 5 ] = 2 * h;	te[ 9 ] = 0;		te[ 13 ] = - y;
-	te[ 2 ] = 0;		te[ 6 ] = 0;		te[ 10 ] = - 1 * p;	te[ 14 ] = - z;
-	te[ 3 ] = 0;		te[ 7 ] = 0;		te[ 11 ] = 0;		te[ 15 ] = 1;
-
-	return this;
-
-};
-
-Frustum.prototype.setFromProjectionMatrix = function ( m ) {
-
-	const planes = this.planes;
-	const me = m.elements;
-	const me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];
-	const me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];
-	const me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];
-	const me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];
-
-	planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();
-	planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();
-	planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();
-	planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();
-	planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();
-	planes[ 5 ].setComponents( me2, me6, me10, me14 ).normalize();
-
-	return this;
+	}
 
 
 };
 };
-
-const _frustum = new Frustum();
-const _projScreenMatrix = new Matrix4();
-const _vector3 = new Vector3();
-
-class WebGPURenderer {
+*/
+class WebGPURenderer extends Renderer {
 
 
 	constructor( parameters = {} ) {
 	constructor( parameters = {} ) {
 
 
-		this.isWebGPURenderer = true;
-
-		// public
-
-		this.domElement = ( parameters.canvas !== undefined ) ? parameters.canvas : this._createCanvasElement();
-
-		this.autoClear = true;
-		this.autoClearColor = true;
-		this.autoClearDepth = true;
-		this.autoClearStencil = true;
-
-		this.outputColorSpace = SRGBColorSpace;
-
-		this.toneMapping = NoToneMapping;
-		this.toneMappingExposure = 1.0;
-
-		this.sortObjects = true;
-
-		// internals
-
-		this._parameters = Object.assign( {}, parameters );
-
-		this._pixelRatio = 1;
-		this._width = this.domElement.width;
-		this._height = this.domElement.height;
-
-		this._viewport = null;
-		this._scissor = null;
-
-		this._adapter = null;
-		this._device = null;
-		this._context = null;
-		this._colorBuffer = null;
-		this._depthBuffer = null;
-
-		this._info = null;
-		this._properties = null;
-		this._attributes = null;
-		this._geometries = null;
-		this._nodes = null;
-		this._bindings = null;
-		this._objects = null;
-		this._renderPipelines = null;
-		this._computePipelines = null;
-		this._renderLists = null;
-		this._renderStates = null;
-		this._textures = null;
-		this._background = null;
-
-		this._animation = new WebGPUAnimation();
-
-		this._currentRenderState = null;
-		this._lastRenderState = null;
-
-		this._opaqueSort = null;
-		this._transparentSort = null;
-
-		this._clearAlpha = 1;
-		this._clearColor = new Color( 0x000000 );
-		this._clearDepth = 1;
-		this._clearStencil = 0;
-
-		this._renderTarget = null;
-
-		this._initialized = false;
-		this._initPromise = null;
-
-		// some parameters require default values other than "undefined"
-
-		this._parameters.antialias = ( parameters.antialias === true );
-
-		if ( this._parameters.antialias === true ) {
-
-			this._parameters.sampleCount = ( parameters.sampleCount === undefined ) ? 4 : parameters.sampleCount;
-
-		} else {
-
-			this._parameters.sampleCount = 1;
-
-		}
-
-		this._parameters.requiredLimits = ( parameters.requiredLimits === undefined ) ? {} : parameters.requiredLimits;
-
-		// backwards compatibility
-
-		this.shadow = {
-			shadowMap: {}
-		};
-
-	}
-
-	async init() {
-
-		if ( this._initialized === true ) {
-
-			throw new Error( 'WebGPURenderer: Device has already been initialized.' );
-
-		}
-
-		if ( this._initPromise !== null ) {
-
-			return this._initPromise;
-
-		}
-
-		this._initPromise = new Promise( async ( resolve, reject ) => {
-
-			const parameters = this._parameters;
-
-			const adapterOptions = {
-				powerPreference: parameters.powerPreference
-			};
-
-			const adapter = await navigator.gpu.requestAdapter( adapterOptions );
-
-			if ( adapter === null ) {
-
-				reject( new Error( 'WebGPURenderer: Unable to create WebGPU adapter.' ) );
-				return;
-
-			}
-
-			// feature support
-
-			const features = Object.values( GPUFeatureName );
-
-			const supportedFeatures = [];
-
-			for ( const name of features ) {
-
-				if ( adapter.features.has( name ) ) {
-
-					supportedFeatures.push( name );
-
-				}
-
-			}
-
-			const deviceDescriptor = {
-				requiredFeatures: supportedFeatures,
-				requiredLimits: parameters.requiredLimits
-			};
-
-			const device = await adapter.requestDevice( deviceDescriptor );
-
-			const context = ( parameters.context !== undefined ) ? parameters.context : this.domElement.getContext( 'webgpu' );
-
-			this._adapter = adapter;
-			this._device = device;
-			this._context = context;
-
-			this._configureContext();
-
-			this._info = new WebGPUInfo();
-			this._properties = new WebGPUProperties();
-			this._attributes = new WebGPUAttributes( device );
-			this._geometries = new WebGPUGeometries( this._attributes, this._properties, this._info );
-			this._textures = new WebGPUTextures( device, this._properties, this._info );
-			this._utils = new WebGPUUtils( this );
-			this._nodes = new WebGPUNodes( this, this._properties );
-			this._objects = new WebGPURenderObjects( this, this._nodes, this._geometries, this._info );
-			this._computePipelines = new WebGPUComputePipelines( device, this._nodes );
-			this._renderPipelines = new WebGPURenderPipelines( device, this._nodes, this._utils );
-			this._bindings = this._renderPipelines.bindings = new WebGPUBindings( device, this._info, this._properties, this._textures, this._renderPipelines, this._computePipelines, this._attributes, this._nodes );
-			this._renderLists = new WebGPURenderLists();
-			this._renderStates = new WebGPURenderStates();
-			this._background = new WebGPUBackground( this, this._properties );
-
-			//
-
-			this._setupColorBuffer();
-			this._setupDepthBuffer();
-
-			this._animation.setNodes( this._nodes );
-			this._animation.start();
-
-			this._initialized = true;
-
-			resolve();
-
-		} );
-
-		return this._initPromise;
-
-	}
-
-	async render( scene, camera ) {
-
-		if ( this._initialized === false ) await this.init();
-
-		// preserve render tree
-
-		const nodeFrame = this._nodes.nodeFrame;
-
-		const previousRenderId = nodeFrame.renderId;
-		const previousRenderState = this._currentRenderState;
-
-		//
-
-		const renderState = this._renderStates.get( scene, camera );
-		const renderTarget = this._renderTarget;
-
-		this._currentRenderState = renderState;
-
-		nodeFrame.renderId ++;
-
-		//
-
-		if ( this._animation.isAnimating === false ) nodeFrame.update();
-
-		if ( scene.matrixWorldAutoUpdate === true ) scene.updateMatrixWorld();
-
-		if ( camera.parent === null && camera.matrixWorldAutoUpdate === true ) camera.updateMatrixWorld();
-
-		if ( this._info.autoReset === true ) this._info.reset();
-
-		this._info.render.frame ++;
-
-		_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
-		_frustum.setFromProjectionMatrix( _projScreenMatrix );
-
-		const renderList = this._renderLists.get( scene, camera );
-		renderList.init();
-
-		this._projectObject( scene, camera, 0, renderList );
-
-		renderList.finish();
-
-		if ( this.sortObjects === true ) {
-
-			renderList.sort( this._opaqueSort, this._transparentSort );
-
-		}
-
-		// prepare render pass descriptor
-
-		renderState.descriptorGPU = {
-			colorAttachments: [ {
-				view: null
-			} ],
-			depthStencilAttachment: {
-				view: null
-			}
-		};
-
-		const colorAttachment = renderState.descriptorGPU.colorAttachments[ 0 ];
-		const depthStencilAttachment = renderState.descriptorGPU.depthStencilAttachment;
-
-		if ( renderTarget !== null ) {
-
-			this._textures.initRenderTarget( renderTarget );
-
-			// @TODO: Support RenderTarget with antialiasing.
-
-			const renderTargetProperties = this._properties.get( renderTarget );
-
-			colorAttachment.view = renderTargetProperties.colorTextureGPU.createView();
-			depthStencilAttachment.view = renderTargetProperties.depthTextureGPU.createView();
-
-			renderState.stencil = renderTarget.depthTexture ? renderTarget.depthTexture.format !== DepthFormat : true;
-
-		} else {
-
-			if ( this._parameters.antialias === true ) {
-
-				colorAttachment.view = this._colorBuffer.createView();
-				colorAttachment.resolveTarget = this._context.getCurrentTexture().createView();
-
-			} else {
-
-				colorAttachment.view = this._context.getCurrentTexture().createView();
-				colorAttachment.resolveTarget = undefined;
-
-			}
+		const backend = new WebGPUBackend( parameters );
+		//const backend = new Proxy( new WebGPUBackend( parameters ), debugHandler );
 
 
-			depthStencilAttachment.view = this._depthBuffer.createView();
+		super( backend );
 
 
-		}
-
-		//
-
-		this._nodes.updateEnvironment( scene );
-		this._nodes.updateFog( scene );
-		this._nodes.updateBackground( scene );
-		this._nodes.updateToneMapping();
-
-		//
-
-		this._background.update( scene, renderList, renderState );
-
-		// start render pass
-
-		const device = this._device;
-
-		renderState.encoderGPU = device.createCommandEncoder( {} );
-		renderState.currentPassGPU = renderState.encoderGPU.beginRenderPass( renderState.descriptorGPU );
-
-		// global rasterization settings for all renderable objects
-
-		const vp = this._viewport;
-
-		if ( vp !== null ) {
-
-			const width = Math.floor( vp.width * this._pixelRatio );
-			const height = Math.floor( vp.height * this._pixelRatio );
-
-			renderState.currentPassGPU.setViewport( vp.x, vp.y, width, height, vp.minDepth, vp.maxDepth );
-
-		}
-
-		const sc = this._scissor;
-
-		if ( sc !== null ) {
-
-			const width = Math.floor( sc.width * this._pixelRatio );
-			const height = Math.floor( sc.height * this._pixelRatio );
-
-			renderState.currentPassGPU.setScissorRect( sc.x, sc.y, width, height );
-
-		}
-
-		// process render lists
-
-		const opaqueObjects = renderList.opaque;
-		const transparentObjects = renderList.transparent;
-		const lightsNode = renderList.lightsNode;
-
-		if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, scene, lightsNode, renderState );
-		if ( transparentObjects.length > 0 ) this._renderObjects( transparentObjects, camera, scene, lightsNode, renderState );
-
-		// finish render pass
-
-		renderState.currentPassGPU.end();
-
-		device.queue.submit( [ renderState.encoderGPU.finish() ] );
-
-		// restore render tree
-
-		nodeFrame.renderId = previousRenderId;
-		this._currentRenderState = previousRenderState;
-
-		this._lastRenderState = renderState;
-
-	}
-
-	setAnimationLoop( callback ) {
-
-		if ( this._initialized === false ) this.init();
-
-		const animation = this._animation;
-
-		animation.setAnimationLoop( callback );
-
-		( callback === null ) ? animation.stop() : animation.start();
-
-	}
-
-	async getArrayBuffer( attribute ) {
-
-		return await this._attributes.getArrayBuffer( attribute );
-
-	}
-
-	getContext() {
-
-		return this._context;
-
-	}
-
-	getPixelRatio() {
-
-		return this._pixelRatio;
-
-	}
-
-	getDrawingBufferSize( target ) {
-
-		return target.set( this._width * this._pixelRatio, this._height * this._pixelRatio ).floor();
-
-	}
-
-	getSize( target ) {
-
-		return target.set( this._width, this._height );
-
-	}
-
-	setPixelRatio( value = 1 ) {
-
-		this._pixelRatio = value;
-
-		this.setSize( this._width, this._height, false );
-
-	}
-
-	setDrawingBufferSize( width, height, pixelRatio ) {
-
-		this._width = width;
-		this._height = height;
-
-		this._pixelRatio = pixelRatio;
-
-		this.domElement.width = Math.floor( width * pixelRatio );
-		this.domElement.height = Math.floor( height * pixelRatio );
-
-		this._configureContext();
-		this._setupColorBuffer();
-		this._setupDepthBuffer();
-
-	}
-
-	setSize( width, height, updateStyle = true ) {
-
-		this._width = width;
-		this._height = height;
-
-		this.domElement.width = Math.floor( width * this._pixelRatio );
-		this.domElement.height = Math.floor( height * this._pixelRatio );
-
-		if ( updateStyle === true ) {
-
-			this.domElement.style.width = width + 'px';
-			this.domElement.style.height = height + 'px';
-
-		}
-
-		this._configureContext();
-		this._setupColorBuffer();
-		this._setupDepthBuffer();
-
-	}
-
-	setOpaqueSort( method ) {
-
-		this._opaqueSort = method;
-
-	}
-
-	setTransparentSort( method ) {
-
-		this._transparentSort = method;
-
-	}
-
-	getScissor( target ) {
-
-		const scissor = this._scissor;
-
-		target.x = scissor.x;
-		target.y = scissor.y;
-		target.width = scissor.width;
-		target.height = scissor.height;
-
-		return target;
-
-	}
-
-	setScissor( x, y, width, height ) {
-
-		if ( x === null ) {
-
-			this._scissor = null;
-
-		} else {
-
-			this._scissor = {
-				x: x,
-				y: y,
-				width: width,
-				height: height
-			};
-
-		}
-
-	}
-
-	copyFramebufferToRenderTarget( renderTarget ) {
-
-		const renderState = this._currentRenderState;
-		const { encoderGPU, descriptorGPU } = renderState;
-
-		const texture = renderTarget.texture;
-		texture.internalFormat = GPUTextureFormat.BGRA8Unorm;
-
-		this._textures.initRenderTarget( renderTarget );
-
-		const sourceGPU = this._context.getCurrentTexture();
-		const destinationGPU = this._textures.getTextureGPU( texture );
-
-		renderState.currentPassGPU.end();
-
-		encoderGPU.copyTextureToTexture(
-			{
-			  texture: sourceGPU
-			},
-			{
-			  texture: destinationGPU
-			},
-			[
-				texture.image.width,
-				texture.image.height
-			]
-		);
-
-		descriptorGPU.colorAttachments[ 0 ].loadOp = GPULoadOp.Load;
-		if ( renderState.depth ) descriptorGPU.depthStencilAttachment.depthLoadOp = GPULoadOp.Load;
-		if ( renderState.stencil ) descriptorGPU.depthStencilAttachment.stencilLoadOp = GPULoadOp.Load;
-
-		renderState.currentPassGPU = encoderGPU.beginRenderPass( descriptorGPU );
-
-	}
-
-	getViewport( target ) {
-
-		const viewport = this._viewport;
-
-		target.x = viewport.x;
-		target.y = viewport.y;
-		target.width = viewport.width;
-		target.height = viewport.height;
-		target.minDepth = viewport.minDepth;
-		target.maxDepth = viewport.maxDepth;
-
-		return target;
-
-	}
-
-	setViewport( x, y, width, height, minDepth = 0, maxDepth = 1 ) {
-
-		if ( x === null ) {
-
-			this._viewport = null;
-
-		} else {
-
-			this._viewport = {
-				x: x,
-				y: y,
-				width: width,
-				height: height,
-				minDepth: minDepth,
-				maxDepth: maxDepth
-			};
-
-		}
-
-	}
-
-	getClearColor( target ) {
-
-		return target.copy( this._clearColor );
-
-	}
-
-	setClearColor( color, alpha = 1 ) {
-
-		this._clearColor.set( color );
-		this._clearAlpha = alpha;
-
-	}
-
-	getClearAlpha() {
-
-		return this._clearAlpha;
-
-	}
-
-	setClearAlpha( alpha ) {
-
-		this._clearAlpha = alpha;
-
-	}
-
-	getClearDepth() {
-
-		return this._clearDepth;
-
-	}
-
-	setClearDepth( depth ) {
-
-		this._clearDepth = depth;
-
-	}
-
-	getClearStencil() {
-
-		return this._clearStencil;
-
-	}
-
-	setClearStencil( stencil ) {
-
-		this._clearStencil = stencil;
-
-	}
-
-	clear( color = true, depth = true, stencil = true ) {
-
-		const renderState = this._currentRenderState || this._lastRenderState;
-		if ( renderState === null ) return;
-
-		depth = depth && renderState.depth;
-		stencil = stencil && renderState.stencil;
-
-		const descriptorGPU = renderState.descriptorGPU;
-		const colorAttachment = descriptorGPU.colorAttachments[ 0 ];
-
-		// @TODO: Include render target in clear operation.
-		if ( this._parameters.antialias === true ) {
-
-			colorAttachment.view = this._colorBuffer.createView();
-			colorAttachment.resolveTarget = this._context.getCurrentTexture().createView();
-
-		} else {
-
-			colorAttachment.view = this._context.getCurrentTexture().createView();
-			colorAttachment.resolveTarget = undefined;
-
-		}
-
-		descriptorGPU.depthStencilAttachment.view = this._depthBuffer.createView();
-
-		if ( color ) {
-
-			colorAttachment.loadOp = GPULoadOp.Clear;
-			colorAttachment.clearValue = { r: this._clearColor.r, g: this._clearColor.g, b: this._clearColor.b, a: this._clearAlpha };
-
-		}
-
-		if ( depth ) {
-
-			descriptorGPU.depthStencilAttachment.depthLoadOp = GPULoadOp.Clear;
-			descriptorGPU.depthStencilAttachment.depthClearValue = this._clearDepth;
-
-		}
-
-		if ( stencil ) {
-
-			descriptorGPU.depthStencilAttachment.stencilLoadOp = GPULoadOp.Clear;
-			descriptorGPU.depthStencilAttachment.stencilClearValue = this._clearStencil;
-
-		}
-
-		renderState.encoderGPU = this._device.createCommandEncoder( {} );
-		renderState.currentPassGPU = renderState.encoderGPU.beginRenderPass( renderState.descriptorGPU );
-
-		renderState.currentPassGPU.end();
-
-		this._device.queue.submit( [ renderState.encoderGPU.finish() ] );
-
-	}
-
-	clearColor() {
-
-		this.clear( true, false, false );
-
-	}
-
-	clearDepth() {
-
-		this.clear( false, true, false );
-
-	}
-
-	clearStencil() {
-
-		this.clear( false, false, true );
-
-	}
-
-	dispose() {
-
-		this._objects.dispose();
-		this._properties.dispose();
-		this._renderPipelines.dispose();
-		this._computePipelines.dispose();
-		this._nodes.dispose();
-		this._bindings.dispose();
-		this._info.dispose();
-		this._renderLists.dispose();
-		this._renderStates.dispose();
-		this._textures.dispose();
-
-		this.setRenderTarget( null );
-		this.setAnimationLoop( null );
-
-	}
-
-	setRenderTarget( renderTarget ) {
-
-		this._renderTarget = renderTarget;
-
-	}
-
-	async compute( ...computeNodes ) {
-
-		if ( this._initialized === false ) await this.init();
-
-		const device = this._device;
-		const computePipelines = this._computePipelines;
-
-		const cmdEncoder = device.createCommandEncoder( {} );
-		const passEncoder = cmdEncoder.beginComputePass();
-
-		for ( const computeNode of computeNodes ) {
-
-			// onInit
-
-			if ( computePipelines.has( computeNode ) === false ) {
-
-				computeNode.onInit( { renderer: this } );
-
-			}
-
-			// pipeline
-
-			const pipeline = computePipelines.get( computeNode );
-			passEncoder.setPipeline( pipeline );
-
-			// node
-
-			//this._nodes.update( computeNode );
-
-			// bind group
-
-			const bindGroup = this._bindings.getForCompute( computeNode ).group;
-			this._bindings.update( computeNode );
-			passEncoder.setBindGroup( 0, bindGroup );
-
-			passEncoder.dispatchWorkgroups( computeNode.dispatchCount );
-
-		}
-
-		passEncoder.end();
-		device.queue.submit( [ cmdEncoder.finish() ] );
-
-	}
-
-	getRenderTarget() {
-
-		return this._renderTarget;
-
-	}
-
-	hasFeature( name ) {
-
-		const adapter = this._adapter || staticAdapter;
-
-		//
-
-		const features = Object.values( GPUFeatureName );
-
-		if ( features.includes( name ) === false ) {
-
-			throw new Error( 'THREE.WebGPURenderer: Unknown WebGPU GPU feature: ' + name );
-
-		}
-
-		//
-
-		return adapter.features.has( name );
-
-	}
-
-	_projectObject( object, camera, groupOrder, renderList ) {
-
-		if ( object.visible === false ) return;
-
-		const visible = object.layers.test( camera.layers );
-
-		if ( visible ) {
-
-			if ( object.isGroup ) {
-
-				groupOrder = object.renderOrder;
-
-			} else if ( object.isLOD ) {
-
-				if ( object.autoUpdate === true ) object.update( camera );
-
-			} else if ( object.isLight ) {
-
-				renderList.pushLight( object );
-
-			} else if ( object.isSprite ) {
-
-				if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {
-
-					if ( this.sortObjects === true ) {
-
-						_vector3.setFromMatrixPosition( object.matrixWorld ).applyMatrix4( _projScreenMatrix );
-
-					}
-
-					const geometry = object.geometry;
-					const material = object.material;
-
-					if ( material.visible ) {
-
-						renderList.push( object, geometry, material, groupOrder, _vector3.z, null );
-
-					}
-
-				}
-
-			} else if ( object.isLineLoop ) {
-
-				console.error( 'THREE.WebGPURenderer: Objects of type THREE.LineLoop are not supported. Please use THREE.Line or THREE.LineSegments.' );
-
-			} else if ( object.isMesh || object.isLine || object.isPoints ) {
-
-				if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {
-
-					const geometry = object.geometry;
-					const material = object.material;
-
-					if ( this.sortObjects === true ) {
-
-						if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();
-
-						_vector3
-							.copy( geometry.boundingSphere.center )
-							.applyMatrix4( object.matrixWorld )
-							.applyMatrix4( _projScreenMatrix );
-
-					}
-
-					if ( Array.isArray( material ) ) {
-
-						const groups = geometry.groups;
-
-						for ( let i = 0, l = groups.length; i < l; i ++ ) {
-
-							const group = groups[ i ];
-							const groupMaterial = material[ group.materialIndex ];
-
-							if ( groupMaterial && groupMaterial.visible ) {
-
-								renderList.push( object, geometry, groupMaterial, groupOrder, _vector3.z, group );
-
-							}
-
-						}
-
-					} else if ( material.visible ) {
-
-						renderList.push( object, geometry, material, groupOrder, _vector3.z, null );
-
-					}
-
-				}
-
-			}
-
-		}
-
-		const children = object.children;
-
-		for ( let i = 0, l = children.length; i < l; i ++ ) {
-
-			this._projectObject( children[ i ], camera, groupOrder, renderList );
-
-		}
-
-	}
-
-	_renderObjects( renderList, camera, scene, lightsNode ) {
-
-		// process renderable objects
-
-		for ( let i = 0, il = renderList.length; i < il; i ++ ) {
-
-			const renderItem = renderList[ i ];
-
-			// @TODO: Add support for multiple materials per object. This will require to extract
-			// the material from the renderItem object and pass it with its group data to _renderObject().
-
-			const { object, geometry, material, group } = renderItem;
-
-			if ( camera.isArrayCamera ) {
-
-				const cameras = camera.cameras;
-
-				for ( let j = 0, jl = cameras.length; j < jl; j ++ ) {
-
-					const camera2 = cameras[ j ];
-
-					if ( object.layers.test( camera2.layers ) ) {
-
-						const vp = camera2.viewport;
-						const minDepth = ( vp.minDepth === undefined ) ? 0 : vp.minDepth;
-						const maxDepth = ( vp.maxDepth === undefined ) ? 1 : vp.maxDepth;
-
-						this._currentRenderState.currentPassGPU.setViewport( vp.x, vp.y, vp.width, vp.height, minDepth, maxDepth );
-
-						this._renderObject( object, scene, camera2, geometry, material, group, lightsNode );
-
-					}
-
-				}
-
-			} else {
-
-				this._renderObject( object, scene, camera, geometry, material, group, lightsNode );
-
-			}
-
-		}
-
-	}
-
-	_renderObject( object, scene, camera, geometry, material, group, lightsNode ) {
-
-		material = scene.overrideMaterial !== null ? scene.overrideMaterial : material;
-
-		//
-
-		object.onBeforeRender( this, scene, camera, geometry, material, group );
-
-		//
-
-		const renderObject = this._getRenderObject( object, material, scene, camera, lightsNode );
-
-		//
-
-		this._nodes.updateBefore( renderObject );
-
-		//
-
-		const passEncoder = this._currentRenderState.currentPassGPU;
-		const info = this._info;
-
-		//
-
-		object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
-		object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
-
-		// updates
-
-		this._nodes.update( renderObject );
-		this._geometries.update( renderObject );
-		this._bindings.update( renderObject );
-
-		// pipeline
-
-		const renderPipeline = this._renderPipelines.get( renderObject );
-		passEncoder.setPipeline( renderPipeline.pipeline );
-
-		// bind group
-
-		const bindGroup = this._bindings.get( renderObject ).group;
-		passEncoder.setBindGroup( 0, bindGroup );
-
-		// index
-
-		const index = this._geometries.getIndex( renderObject );
-
-		const hasIndex = ( index !== null );
-
-		if ( hasIndex === true ) {
-
-			this._setupIndexBuffer( renderObject );
-
-		}
-
-		// vertex buffers
-
-		this._setupVertexBuffers( renderObject );
-
-		// draw
-
-		const drawRange = geometry.drawRange;
-		const firstVertex = drawRange.start;
-
-		const instanceCount = geometry.isInstancedBufferGeometry ? geometry.instanceCount : ( object.isInstancedMesh ? object.count : 1 );
-
-		if ( hasIndex === true ) {
-
-			const indexCount = ( drawRange.count !== Infinity ) ? drawRange.count : index.count;
-
-			passEncoder.drawIndexed( indexCount, instanceCount, firstVertex, 0, 0 );
-
-			info.update( object, indexCount, instanceCount );
-
-		} else {
-
-			const positionAttribute = geometry.attributes.position;
-			const vertexCount = ( drawRange.count !== Infinity ) ? drawRange.count : positionAttribute.count;
-
-			passEncoder.draw( vertexCount, instanceCount, firstVertex, 0 );
-
-			info.update( object, vertexCount, instanceCount );
-
-		}
-
-	}
-
-	_getRenderObject( object, material, scene, camera, lightsNode ) {
-
-		const renderObject = this._objects.get( object, material, scene, camera, lightsNode );
-		const renderObjectProperties = this._properties.get( renderObject );
-
-		if ( renderObjectProperties.initialized !== true ) {
-
-			renderObjectProperties.initialized = true;
-
-			const dispose = () => {
-
-				this._renderPipelines.remove( renderObject );
-				this._nodes.remove( renderObject );
-				this._properties.remove( renderObject );
-
-				this._objects.remove( object, material, scene, camera, lightsNode );
-
-				renderObject.material.removeEventListener( 'dispose', dispose );
-
-			};
-
-			renderObject.material.addEventListener( 'dispose', dispose );
-
-		}
-
-		const cacheKey = renderObject.getCacheKey();
-
-		if ( renderObjectProperties.cacheKey !== cacheKey ) {
-
-			renderObjectProperties.cacheKey = cacheKey;
-
-			this._renderPipelines.remove( renderObject );
-			this._nodes.remove( renderObject );
-
-		}
-
-		return renderObject;
-
-	}
-
-	_setupIndexBuffer( renderObject ) {
-
-		const index = this._geometries.getIndex( renderObject );
-		const passEncoder = this._currentRenderState.currentPassGPU;
-
-		const buffer = this._attributes.get( index ).buffer;
-		const indexFormat = ( index.array instanceof Uint16Array ) ? GPUIndexFormat.Uint16 : GPUIndexFormat.Uint32;
-
-		passEncoder.setIndexBuffer( buffer, indexFormat );
-
-	}
-
-	_setupVertexBuffers( renderObject ) {
-
-		const passEncoder = this._currentRenderState.currentPassGPU;
-		const attributes = renderObject.getAttributes();
-
-		for ( let i = 0, l = attributes.length; i < l; i ++ ) {
-
-			const buffer = this._attributes.get( attributes[ i ] ).buffer;
-			passEncoder.setVertexBuffer( i, buffer );
-
-		}
-
-	}
-
-	_setupColorBuffer() {
-
-		const device = this._device;
-
-		if ( device ) {
-
-			if ( this._colorBuffer ) this._colorBuffer.destroy();
-
-			this._colorBuffer = this._device.createTexture( {
-				label: 'colorBuffer',
-				size: {
-					width: Math.floor( this._width * this._pixelRatio ),
-					height: Math.floor( this._height * this._pixelRatio ),
-					depthOrArrayLayers: 1
-				},
-				sampleCount: this._parameters.sampleCount,
-				format: GPUTextureFormat.BGRA8Unorm,
-				usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
-			} );
-
-		}
-
-	}
-
-	_setupDepthBuffer() {
-
-		const device = this._device;
-
-		if ( device ) {
-
-			if ( this._depthBuffer ) this._depthBuffer.destroy();
-
-			this._depthBuffer = this._device.createTexture( {
-				label: 'depthBuffer',
-				size: {
-					width: Math.floor( this._width * this._pixelRatio ),
-					height: Math.floor( this._height * this._pixelRatio ),
-					depthOrArrayLayers: 1
-				},
-				sampleCount: this._parameters.sampleCount,
-				format: GPUTextureFormat.Depth24PlusStencil8,
-				usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC
-			} );
-
-		}
-
-	}
-
-	_configureContext() {
-
-		const device = this._device;
-
-		if ( device ) {
-
-			this._context.configure( {
-				device: device,
-				format: GPUTextureFormat.BGRA8Unorm,
-				usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
-				alphaMode: 'premultiplied'
-			} );
-
-		}
-
-	}
-
-	_createCanvasElement() {
-
-		const canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );
-		canvas.style.display = 'block';
-		return canvas;
+		this.isWebGPURenderer = true;
 
 
 	}
 	}
 
 

+ 0 - 75
examples/jsm/renderers/webgpu/WebGPUSampledTexture.js

@@ -1,75 +0,0 @@
-import WebGPUBinding from './WebGPUBinding.js';
-import { GPUBindingType, GPUTextureViewDimension, GPUTextureAspect } from './constants.js';
-
-class WebGPUSampledTexture extends WebGPUBinding {
-
-	constructor( name, texture ) {
-
-		super( name );
-
-		this.isSampledTexture = true;
-
-		this.texture = texture;
-
-		this.dimension = GPUTextureViewDimension.TwoD;
-
-		this.type = GPUBindingType.SampledTexture;
-		this.visibility = GPUShaderStage.FRAGMENT;
-
-		this.aspect = texture.isDepthTexture ? GPUTextureAspect.DepthOnly : GPUTextureAspect.All;
-
-		this.textureGPU = null; // set by the renderer
-
-	}
-
-	getTexture() {
-
-		return this.texture;
-
-	}
-
-}
-
-class WebGPUSampledArrayTexture extends WebGPUSampledTexture {
-
-	constructor( name, texture ) {
-
-		super( name, texture );
-
-		this.isSampledArrayTexture = true;
-
-		this.dimension = GPUTextureViewDimension.TwoDArray;
-
-	}
-
-}
-
-class WebGPUSampled3DTexture extends WebGPUSampledTexture {
-
-	constructor( name, texture ) {
-
-		super( name, texture );
-
-		this.isSampled3DTexture = true;
-
-		this.dimension = GPUTextureViewDimension.ThreeD;
-
-	}
-
-}
-
-class WebGPUSampledCubeTexture extends WebGPUSampledTexture {
-
-	constructor( name, texture ) {
-
-		super( name, texture );
-
-		this.isSampledCubeTexture = true;
-
-		this.dimension = GPUTextureViewDimension.Cube;
-
-	}
-
-}
-
-export { WebGPUSampledTexture, WebGPUSampledArrayTexture, WebGPUSampled3DTexture, WebGPUSampledCubeTexture };

+ 0 - 29
examples/jsm/renderers/webgpu/WebGPUSampler.js

@@ -1,29 +0,0 @@
-import WebGPUBinding from './WebGPUBinding.js';
-import { GPUBindingType } from './constants.js';
-
-class WebGPUSampler extends WebGPUBinding {
-
-	constructor( name, texture ) {
-
-		super( name );
-
-		this.isSampler = true;
-
-		this.texture = texture;
-
-		this.type = GPUBindingType.Sampler;
-		this.visibility = GPUShaderStage.FRAGMENT;
-
-		this.samplerGPU = null; // set by the renderer
-
-	}
-
-	getTexture() {
-
-		return this.texture;
-
-	}
-
-}
-
-export default WebGPUSampler;

+ 0 - 20
examples/jsm/renderers/webgpu/WebGPUStorageBuffer.js

@@ -1,20 +0,0 @@
-import WebGPUBuffer from './WebGPUBuffer.js';
-import { GPUBindingType } from './constants.js';
-
-class WebGPUStorageBuffer extends WebGPUBuffer {
-
-	constructor( name, attribute ) {
-
-		super( name, GPUBindingType.StorageBuffer, attribute.array );
-
-		this.isStorageBuffer = true;
-
-		this.usage |= GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE;
-
-		this.attribute = attribute;
-
-	}
-
-}
-
-export default WebGPUStorageBuffer;

+ 0 - 18
examples/jsm/renderers/webgpu/WebGPUUniformBuffer.js

@@ -1,18 +0,0 @@
-import WebGPUBuffer from './WebGPUBuffer.js';
-import { GPUBindingType } from './constants.js';
-
-class WebGPUUniformBuffer extends WebGPUBuffer {
-
-	constructor( name, buffer = null ) {
-
-		super( name, GPUBindingType.UniformBuffer, buffer );
-
-		this.isUniformBuffer = true;
-
-		this.usage |= GPUBufferUsage.UNIFORM;
-
-	}
-
-}
-
-export default WebGPUUniformBuffer;

+ 0 - 88
examples/jsm/renderers/webgpu/WebGPUUtils.js

@@ -1,88 +0,0 @@
-import { GPUPrimitiveTopology, GPUTextureFormat } from './constants.js';
-
-class WebGPUUtils {
-
-	constructor( renderer ) {
-
-		this.renderer = renderer;
-
-	}
-
-	getCurrentColorSpace() {
-
-		const renderer = this.renderer;
-
-		const renderTarget = renderer.getRenderTarget();
-
-		if ( renderTarget !== null ) {
-
-			return renderTarget.texture.colorSpace;
-
-		}
-
-		return renderer.outputColorSpace;
-
-	}
-
-	getCurrentColorFormat() {
-
-		let format;
-
-		const renderer = this.renderer;
-		const renderTarget = renderer.getRenderTarget();
-
-		if ( renderTarget !== null ) {
-
-			const renderTargetProperties = renderer._properties.get( renderTarget );
-			format = renderTargetProperties.colorTextureFormat;
-
-		} else {
-
-			format = GPUTextureFormat.BGRA8Unorm; // default context format
-
-		}
-
-		return format;
-
-	}
-
-	getCurrentDepthStencilFormat() {
-
-		let format;
-
-		const renderer = this.renderer;
-		const renderTarget = renderer.getRenderTarget();
-
-		if ( renderTarget !== null ) {
-
-			const renderTargetProperties = renderer._properties.get( renderTarget );
-			format = renderTargetProperties.depthTextureFormat;
-
-		} else {
-
-			format = GPUTextureFormat.Depth24PlusStencil8;
-
-		}
-
-		return format;
-
-	}
-
-	getPrimitiveTopology( object, material ) {
-
-		if ( object.isPoints ) return GPUPrimitiveTopology.PointList;
-		else if ( object.isLineSegments || ( object.isMesh && material.wireframe === true ) ) return GPUPrimitiveTopology.LineList;
-		else if ( object.isLine ) return GPUPrimitiveTopology.LineStrip;
-		else if ( object.isMesh ) return GPUPrimitiveTopology.TriangleList;
-
-	}
-
-	getSampleCount() {
-
-		return this.renderer._parameters.sampleCount;
-
-	}
-
-}
-
-export default WebGPUUtils;

+ 21 - 28
examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js → examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js

@@ -1,24 +1,28 @@
-import WebGPUUniformsGroup from '../WebGPUUniformsGroup.js';
+import UniformsGroup from '../../common/UniformsGroup.js';
 import {
 import {
 	FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform,
 	FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform,
 	ColorNodeUniform, Matrix3NodeUniform, Matrix4NodeUniform
 	ColorNodeUniform, Matrix3NodeUniform, Matrix4NodeUniform
-} from './WebGPUNodeUniform.js';
-import WebGPUNodeSampler from './WebGPUNodeSampler.js';
-import { WebGPUNodeSampledTexture, WebGPUNodeSampledCubeTexture } from './WebGPUNodeSampledTexture.js';
+} from '../../common/nodes/NodeUniform.js';
+import NodeSampler from '../../common/nodes/NodeSampler.js';
+import { NodeSampledTexture, NodeSampledCubeTexture } from '../../common/nodes/NodeSampledTexture.js';
 
 
-import WebGPUUniformBuffer from '../WebGPUUniformBuffer.js';
-import WebGPUStorageBuffer from '../WebGPUStorageBuffer.js';
-import { getVectorLength, getStrideLength } from '../WebGPUBufferUtils.js';
+import UniformBuffer from '../../common/UniformBuffer.js';
+import StorageBuffer from '../../common/StorageBuffer.js';
+import { getVectorLength, getStrideLength } from '../../common/BufferUtils.js';
 
 
-import WebGPURenderTarget from '../WebGPURenderTarget.js';
+import RenderTarget from '../../common/RenderTarget.js';
 
 
-import { NodeBuilder, WGSLNodeParser, CodeNode, NodeMaterial } from '../../../nodes/Nodes.js';
+import { NodeBuilder, CodeNode, NodeMaterial } from '../../../nodes/Nodes.js';
 
 
+import WGSLNodeParser from './WGSLNodeParser.js';
+
+/*
 const gpuShaderStageLib = {
 const gpuShaderStageLib = {
 	'vertex': GPUShaderStage.VERTEX,
 	'vertex': GPUShaderStage.VERTEX,
 	'fragment': GPUShaderStage.FRAGMENT,
 	'fragment': GPUShaderStage.FRAGMENT,
 	'compute': GPUShaderStage.COMPUTE
 	'compute': GPUShaderStage.COMPUTE
 };
 };
+*/
 
 
 const supports = {
 const supports = {
 	instance: true
 	instance: true
@@ -97,9 +101,6 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 
 		super( object, renderer, new WGSLNodeParser() );
 		super( object, renderer, new WGSLNodeParser() );
 
 
-		this.bindings = { vertex: [], fragment: [], compute: [] };
-		this.bindingsOffset = { vertex: 0, fragment: 0, compute: 0 };
-
 		this.uniformsGroup = {};
 		this.uniformsGroup = {};
 
 
 		this.builtins = {
 		this.builtins = {
@@ -250,14 +251,6 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 
 	}
 	}
 
 
-	getBindings() {
-
-		const bindings = this.bindings;
-
-		return this.material !== null ? [ ...bindings.vertex, ...bindings.fragment ] : bindings.compute;
-
-	}
-
 	getUniformFromNode( node, type, shaderStage ) {
 	getUniformFromNode( node, type, shaderStage ) {
 
 
 		const uniformNode = super.getUniformFromNode( node, type, shaderStage );
 		const uniformNode = super.getUniformFromNode( node, type, shaderStage );
@@ -271,17 +264,17 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 
 			if ( type === 'texture' || type === 'cubeTexture' ) {
 			if ( type === 'texture' || type === 'cubeTexture' ) {
 
 
-				const sampler = new WebGPUNodeSampler( `${uniformNode.name}_sampler`, uniformNode.node );
+				const sampler = new NodeSampler( `${uniformNode.name}_sampler`, uniformNode.node );
 
 
 				let texture = null;
 				let texture = null;
 
 
 				if ( type === 'texture' ) {
 				if ( type === 'texture' ) {
 
 
-					texture = new WebGPUNodeSampledTexture( uniformNode.name, uniformNode.node );
+					texture = new NodeSampledTexture( uniformNode.name, uniformNode.node );
 
 
 				} else if ( type === 'cubeTexture' ) {
 				} else if ( type === 'cubeTexture' ) {
 
 
-					texture = new WebGPUNodeSampledCubeTexture( uniformNode.name, uniformNode.node );
+					texture = new NodeSampledCubeTexture( uniformNode.name, uniformNode.node );
 
 
 				}
 				}
 
 
@@ -305,9 +298,9 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 
 			} else if ( type === 'buffer' || type === 'storageBuffer' ) {
 			} else if ( type === 'buffer' || type === 'storageBuffer' ) {
 
 
-				const bufferClass = type === 'storageBuffer' ? WebGPUStorageBuffer : WebGPUUniformBuffer;
+				const bufferClass = type === 'storageBuffer' ? StorageBuffer : UniformBuffer;
 				const buffer = new bufferClass( 'NodeBuffer_' + node.id, node.value );
 				const buffer = new bufferClass( 'NodeBuffer_' + node.id, node.value );
-				buffer.setVisibility( gpuShaderStageLib[ shaderStage ] );
+				//buffer.setVisibility( gpuShaderStageLib[ shaderStage ] );
 
 
 				// add first textures in sequence and group for last
 				// add first textures in sequence and group for last
 				const lastBinding = bindings[ bindings.length - 1 ];
 				const lastBinding = bindings[ bindings.length - 1 ];
@@ -323,8 +316,8 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 
 				if ( uniformsGroup === undefined ) {
 				if ( uniformsGroup === undefined ) {
 
 
-					uniformsGroup = new WebGPUUniformsGroup( 'nodeUniforms' );
-					uniformsGroup.setVisibility( gpuShaderStageLib[ shaderStage ] );
+					uniformsGroup = new UniformsGroup( 'nodeUniforms' );
+					//uniformsGroup.setVisibility( gpuShaderStageLib[ shaderStage ] );
 
 
 					this.uniformsGroup[ shaderStage ] = uniformsGroup;
 					this.uniformsGroup[ shaderStage ] = uniformsGroup;
 
 
@@ -700,7 +693,7 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 
 	getRenderTarget( width, height, options ) {
 	getRenderTarget( width, height, options ) {
 
 
-		return new WebGPURenderTarget( width, height, options );
+		return new RenderTarget( width, height, options );
 
 
 	}
 	}
 
 

+ 2 - 2
examples/jsm/nodes/parsers/WGSLNodeFunction.js → examples/jsm/renderers/webgpu/nodes/WGSLNodeFunction.js

@@ -1,5 +1,5 @@
-import NodeFunction from '../core/NodeFunction.js';
-import NodeFunctionInput from '../core/NodeFunctionInput.js';
+import NodeFunction from '../../../nodes/core/NodeFunction.js';
+import NodeFunctionInput from '../../../nodes/core/NodeFunctionInput.js';
 
 
 const declarationRegexp = /^[fn]*\s*([a-z_0-9]+)?\s*\(([\s\S]*?)\)\s*[\-\>]*\s*([a-z_0-9]+)?/i;
 const declarationRegexp = /^[fn]*\s*([a-z_0-9]+)?\s*\(([\s\S]*?)\)\s*[\-\>]*\s*([a-z_0-9]+)?/i;
 const propertiesRegexp = /[a-z_0-9]+/ig;
 const propertiesRegexp = /[a-z_0-9]+/ig;

+ 1 - 1
examples/jsm/nodes/parsers/WGSLNodeParser.js → examples/jsm/renderers/webgpu/nodes/WGSLNodeParser.js

@@ -1,4 +1,4 @@
-import NodeParser from '../core/NodeParser.js';
+import NodeParser from '../../../nodes/core/NodeParser.js';
 import WGSLNodeFunction from './WGSLNodeFunction.js';
 import WGSLNodeFunction from './WGSLNodeFunction.js';
 
 
 class WGSLNodeParser extends NodeParser {
 class WGSLNodeParser extends NodeParser {

+ 251 - 0
examples/jsm/renderers/webgpu/utils/WebGPUAttributeUtils.js

@@ -0,0 +1,251 @@
+import { Float16BufferAttribute } from 'three';
+
+const typedArraysToVertexFormatPrefix = new Map( [
+	[ Int8Array, [ 'sint8', 'snorm8' ]],
+	[ Uint8Array, [ 'uint8', 'unorm8' ]],
+	[ Int16Array, [ 'sint16', 'snorm16' ]],
+	[ Uint16Array, [ 'uint16', 'unorm16' ]],
+	[ Int32Array, [ 'sint32', 'snorm32' ]],
+	[ Uint32Array, [ 'uint32', 'unorm32' ]],
+	[ Float32Array, [ 'float32', ]],
+] );
+
+const typedAttributeToVertexFormatPrefix = new Map( [
+	[ Float16BufferAttribute, [ 'float16', ]],
+] );
+
+const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [
+	[ Int32Array, 'sint32' ],
+	[ Uint32Array, 'uint32' ],
+	[ Float32Array, 'float32' ]
+] );
+
+class WebGPUAttributeUtils {
+
+	constructor( backend ) {
+
+		this.backend = backend;
+
+	}
+
+	createAttribute( attribute, usage ) {
+
+		const bufferAttribute = this._getBufferAttribute( attribute );
+
+		const backend = this.backend;
+		const device = backend.device;
+
+		const array = bufferAttribute.array;
+		const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441
+
+		const buffer = device.createBuffer( {
+			label: bufferAttribute.name,
+			size: size,
+			usage: usage,
+			mappedAtCreation: true
+		} );
+
+		new array.constructor( buffer.getMappedRange() ).set( array );
+
+		buffer.unmap();
+
+		backend.get( attribute ).buffer = buffer;
+
+	}
+
+	updateAttribute( attribute ) {
+
+		const bufferAttribute = this._getBufferAttribute( attribute );
+
+		const backend = this.backend;
+		const device = backend.device;
+
+		const buffer = backend.get( attribute ).buffer;
+
+		const array = bufferAttribute.array;
+		const updateRange = bufferAttribute.updateRange;
+
+		if ( updateRange.count === - 1 ) {
+
+			// Not using update ranges
+
+			device.queue.writeBuffer(
+				buffer,
+				0,
+				array,
+				0
+			);
+
+		} else {
+
+			device.queue.writeBuffer(
+				buffer,
+				0,
+				array,
+				updateRange.offset * array.BYTES_PER_ELEMENT,
+				updateRange.count * array.BYTES_PER_ELEMENT
+			);
+
+			updateRange.count = - 1; // reset range
+
+		}
+
+	}
+
+	createShaderAttributes( renderObject ) {
+
+		const attributes = renderObject.getAttributes();
+		const shaderAttributes = [];
+
+		for ( let slot = 0; slot < attributes.length; slot ++ ) {
+
+			const geometryAttribute = attributes[ slot ];
+			const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT;
+
+			const format = this._getVertexFormat( geometryAttribute );
+
+			let arrayStride = geometryAttribute.itemSize * bytesPerElement;
+			let offset = 0;
+
+			if ( geometryAttribute.isInterleavedBufferAttribute === true ) {
+
+				// @TODO: It can be optimized for "vertexBuffers" on RenderPipeline
+
+				arrayStride = geometryAttribute.data.stride * bytesPerElement;
+				offset = geometryAttribute.offset * bytesPerElement;
+
+			}
+
+			shaderAttributes.push( {
+				geometryAttribute,
+				arrayStride,
+				offset,
+				format,
+				slot
+			} );
+
+		}
+
+		return shaderAttributes;
+
+	}
+
+	destroyAttribute( attribute ) {
+
+		const backend = this.backend;
+		const data = backend.get( attribute );
+
+		data.buffer.destroy();
+
+		backend.delete( attribute );
+
+	}
+
+	async getArrayBuffer( attribute ) {
+
+		const backend = this.backend;
+		const device = backend.device;
+
+		const data = backend.get( attribute );
+
+		//const bufferAttribute = this._getBufferAttribute( attribute );
+
+		const bufferGPU = data.buffer;
+		const size = bufferGPU.size;
+
+		let readBufferGPU = data.readBuffer;
+		let needsUnmap = true;
+
+		if ( readBufferGPU === undefined ) {
+
+			readBufferGPU = device.createBuffer( {
+				label: attribute.name,
+				size,
+				usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
+			} );
+
+			needsUnmap = false;
+
+			data.readBuffer = readBufferGPU;
+
+		}
+
+		const cmdEncoder = device.createCommandEncoder( {} );
+
+		cmdEncoder.copyBufferToBuffer(
+			bufferGPU,
+			0,
+			readBufferGPU,
+			0,
+			size
+		);
+
+		if ( needsUnmap ) readBufferGPU.unmap();
+
+		const gpuCommands = cmdEncoder.finish();
+		device.queue.submit( [ gpuCommands ] );
+
+		await readBufferGPU.mapAsync( GPUMapMode.READ );
+
+		const arrayBuffer = readBufferGPU.getMappedRange();
+
+		return arrayBuffer;
+
+	}
+
+	_getVertexFormat( geometryAttribute ) {
+
+		const { itemSize, normalized } = geometryAttribute;
+		const ArrayType = geometryAttribute.array.constructor;
+		const AttributeType = geometryAttribute.constructor;
+
+		let format;
+
+		if ( itemSize == 1 ) {
+
+			format = typeArraysToVertexFormatPrefixForItemSize1.get( ArrayType );
+
+		} else {
+
+			const prefixOptions = typedAttributeToVertexFormatPrefix.get( AttributeType ) || typedArraysToVertexFormatPrefix.get( ArrayType );
+			const prefix = prefixOptions[ normalized ? 1 : 0 ];
+
+			if ( prefix ) {
+
+				const bytesPerUnit = ArrayType.BYTES_PER_ELEMENT * itemSize;
+				const paddedBytesPerUnit = Math.floor( ( bytesPerUnit + 3 ) / 4 ) * 4;
+				const paddedItemSize = paddedBytesPerUnit / ArrayType.BYTES_PER_ELEMENT;
+
+				if ( paddedItemSize % 1 ) {
+
+					throw new Error( 'THREE.WebGPUAttributeUtils: Bad vertex format item size.' );
+
+				}
+
+				format = `${prefix}x${paddedItemSize}`;
+
+			}
+
+		}
+
+		if ( ! format ) {
+
+			console.error( 'THREE.WebGPUAttributeUtils: Vertex format not supported yet.' );
+
+		}
+
+		return format;
+
+	}
+
+	_getBufferAttribute( attribute ) {
+
+		if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;
+
+		return attribute;
+
+	}
+
+}
+
+export default WebGPUAttributeUtils;

+ 145 - 0
examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js

@@ -0,0 +1,145 @@
+import {
+	GPUTextureAspect, GPUTextureViewDimension
+} from './WebGPUConstants.js';
+
+class WebGPUBindingUtils {
+
+	constructor( backend ) {
+
+		this.backend = backend;
+
+	}
+
+	createBindings( bindings, pipeline ) {
+
+		const backend = this.backend;
+		const bindingsData = backend.get( bindings );
+
+		// setup (static) binding layout and (dynamic) binding group
+
+		const pipelineGPU = backend.get( pipeline ).pipeline;
+
+		const bindLayoutGPU = pipelineGPU.getBindGroupLayout( 0 );
+		const bindGroupGPU = this.createBindGroup( bindings, bindLayoutGPU );
+
+		bindingsData.layout = bindLayoutGPU;
+		bindingsData.group = bindGroupGPU;
+		bindingsData.bindings = bindings;
+
+	}
+
+	updateBinding( binding ) {
+
+		const backend = this.backend;
+		const device = backend.device;
+
+		const buffer = binding.buffer;
+		const bufferGPU = backend.get( binding ).buffer;
+
+		device.queue.writeBuffer( bufferGPU, 0, buffer, 0 );
+
+	}
+
+	createBindGroup( bindings, layoutGPU ) {
+
+		const backend = this.backend;
+		const device = backend.device;
+
+		let bindingPoint = 0;
+		const entriesGPU = [];
+
+		for ( const binding of bindings ) {
+
+			if ( binding.isUniformBuffer ) {
+
+				const bindingData = backend.get( binding );
+
+				if ( bindingData.buffer === undefined ) {
+
+					const byteLength = binding.byteLength;
+
+					const usage = GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST;
+
+					const bufferGPU = device.createBuffer( {
+						label: 'bindingBuffer',
+						size: byteLength,
+						usage: usage
+					} );
+
+					bindingData.buffer = bufferGPU;
+
+				}
+
+				entriesGPU.push( { binding: bindingPoint, resource: { buffer: bindingData.buffer } } );
+
+			} else if ( binding.isStorageBuffer ) {
+
+				const bindingData = backend.get( binding );
+
+				if ( bindingData.buffer === undefined ) {
+
+					const attribute = binding.attribute;
+					//const usage = GPUBufferUsage.STORAGE | GPUBufferUsage.VERTEX | /*GPUBufferUsage.COPY_SRC |*/ GPUBufferUsage.COPY_DST;
+
+					//backend.attributeUtils.createAttribute( attribute, usage ); // @TODO: Move it to universal renderer
+
+					bindingData.buffer = backend.get( attribute ).buffer;
+
+				}
+
+				entriesGPU.push( { binding: bindingPoint, resource: { buffer: bindingData.buffer } } );
+
+			} else if ( binding.isSampler ) {
+
+				const textureGPU = backend.get( binding.texture );
+
+				entriesGPU.push( { binding: bindingPoint, resource: textureGPU.sampler } );
+
+			} else if ( binding.isSampledTexture ) {
+
+				const textureData = backend.get( binding.texture );
+
+				let dimensionViewGPU;
+
+				if ( binding.isSampledCubeTexture ) {
+
+					dimensionViewGPU = GPUTextureViewDimension.Cube;
+
+				} else {
+
+					dimensionViewGPU = GPUTextureViewDimension.TwoD;
+
+				}
+
+				let resourceGPU;
+
+				if ( textureData.externalTexture !== undefined ) {
+
+					resourceGPU = device.importExternalTexture( { source: textureData.externalTexture } );
+
+				} else {
+
+					const aspectGPU = GPUTextureAspect.All;
+
+					resourceGPU = textureData.texture.createView( { aspect: aspectGPU, dimension: dimensionViewGPU } );
+
+				}
+
+				entriesGPU.push( { binding: bindingPoint, resource: resourceGPU } );
+
+			}
+
+			bindingPoint ++;
+
+		}
+
+		return device.createBindGroup( {
+			layout: layoutGPU,
+			entries: entriesGPU
+		} );
+
+	}
+
+}
+
+export default WebGPUBindingUtils;

+ 6 - 7
examples/jsm/renderers/webgpu/constants.js → examples/jsm/renderers/webgpu/utils/WebGPUConstants.js

@@ -214,6 +214,12 @@ export const GPUAddressMode = {
 	MirrorRepeat: 'mirror-repeat'
 	MirrorRepeat: 'mirror-repeat'
 };
 };
 
 
+export const GPUSamplerBindingType = {
+	Filtering: 'filtering',
+	NonFiltering: 'non-filtering',
+	Comparison: 'comparison'
+};
+
 export const GPUFilterMode = {
 export const GPUFilterMode = {
 	Linear: 'linear',
 	Linear: 'linear',
 	Nearest: 'nearest'
 	Nearest: 'nearest'
@@ -314,10 +320,3 @@ export const GPUFeatureName = {
 	BGRA8UNormStorage: 'bgra8unorm-storage',
 	BGRA8UNormStorage: 'bgra8unorm-storage',
 	Float32Filterable: 'float32-filterable'
 	Float32Filterable: 'float32-filterable'
 };
 };
-
-export const GPUChunkSize = 16; // size of a chunk in bytes (STD140 layout)
-
-// @TODO: Move to src/constants.js
-
-export const BlendColorFactor = 211;
-export const OneMinusBlendColorFactor = 212;

+ 148 - 232
examples/jsm/renderers/webgpu/WebGPURenderPipeline.js → examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js

@@ -1,57 +1,42 @@
-import { GPUIndexFormat, GPUCompareFunction, GPUFrontFace, GPUCullMode, GPUBlendFactor, GPUBlendOperation, BlendColorFactor, OneMinusBlendColorFactor, GPUColorWriteFlags, GPUStencilOperation, GPUInputStepMode } from './constants.js';
+import { BlendColorFactor, OneMinusBlendColorFactor, } from '../../common/Constants.js';
+
+import {
+	GPUInputStepMode, GPUFrontFace, GPUCullMode, GPUColorWriteFlags, GPUCompareFunction, GPUBlendFactor, GPUBlendOperation, GPUIndexFormat, GPUStencilOperation
+} from './WebGPUConstants.js';
+
 import {
 import {
-	Float16BufferAttribute,
 	FrontSide, BackSide, DoubleSide,
 	FrontSide, BackSide, DoubleSide,
 	NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth,
 	NeverDepth, AlwaysDepth, LessDepth, LessEqualDepth, EqualDepth, GreaterEqualDepth, GreaterDepth, NotEqualDepth,
-	NeverStencilFunc, AlwaysStencilFunc, LessStencilFunc, LessEqualStencilFunc, EqualStencilFunc, GreaterEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc,
-	KeepStencilOp, ZeroStencilOp, ReplaceStencilOp, InvertStencilOp, IncrementStencilOp, DecrementStencilOp, IncrementWrapStencilOp, DecrementWrapStencilOp,
 	NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending,
 	NoBlending, NormalBlending, AdditiveBlending, SubtractiveBlending, MultiplyBlending, CustomBlending,
+	ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstColorFactor,
+	OneMinusDstColorFactor, DstAlphaFactor, OneMinusDstAlphaFactor, SrcAlphaSaturateFactor,
 	AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation,
 	AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation,
-	ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstAlphaFactor, OneMinusDstAlphaFactor, DstColorFactor, OneMinusDstColorFactor, SrcAlphaSaturateFactor
+	KeepStencilOp, ZeroStencilOp, ReplaceStencilOp, InvertStencilOp, IncrementStencilOp, DecrementStencilOp, IncrementWrapStencilOp, DecrementWrapStencilOp,
+	NeverStencilFunc, AlwaysStencilFunc, LessStencilFunc, LessEqualStencilFunc, EqualStencilFunc, GreaterEqualStencilFunc, GreaterStencilFunc, NotEqualStencilFunc
 } from 'three';
 } from 'three';
 
 
-const typedArraysToVertexFormatPrefix = new Map( [
-	[ Int8Array, [ 'sint8', 'snorm8' ]],
-	[ Uint8Array, [ 'uint8', 'unorm8' ]],
-	[ Int16Array, [ 'sint16', 'snorm16' ]],
-	[ Uint16Array, [ 'uint16', 'unorm16' ]],
-	[ Int32Array, [ 'sint32', 'snorm32' ]],
-	[ Uint32Array, [ 'uint32', 'unorm32' ]],
-	[ Float32Array, [ 'float32', ]],
-] );
-
-const typedAttributeToVertexFormatPrefix = new Map( [
-	[ Float16BufferAttribute, [ 'float16', ]],
-] );
+class WebGPUPipelineUtils {
 
 
-const typeArraysToVertexFormatPrefixForItemSize1 = new Map( [
-	[ Int32Array, 'sint32' ],
-	[ Uint32Array, 'uint32' ],
-	[ Float32Array, 'float32' ]
-] );
+	constructor( backend ) {
 
 
-class WebGPURenderPipeline {
+		this.backend = backend;
 
 
-	constructor( device, utils ) {
-
-		this.cacheKey = null;
-		this.shaderAttributes = null;
-		this.stageVertex = null;
-		this.stageFragment = null;
-		this.usedTimes = 0;
+	}
 
 
-		this._device = device;
-		this._utils = utils;
+	createRenderPipeline( renderObject ) {
 
 
-	}
+		const { object, material, geometry, pipeline } = renderObject;
+		const { vertexProgram, fragmentProgram } = pipeline;
 
 
-	init( renderObject, cacheKey, stageVertex, stageFragment ) {
+		const backend = this.backend;
+		const device = backend.device;
+		const utils = backend.utils;
 
 
-		const { object, material, geometry } = renderObject;
+		const pipelineData = backend.get( pipeline );
 
 
 		// determine shader attributes
 		// determine shader attributes
 
 
-		const shaderAttributes = this._getShaderAttributes( renderObject );
+		const shaderAttributes = backend.attributeUtils.createShaderAttributes( renderObject );
 
 
 		// vertex buffers
 		// vertex buffers
 
 
@@ -70,11 +55,6 @@ class WebGPURenderPipeline {
 
 
 		}
 		}
 
 
-		this.cacheKey = cacheKey;
-		this.shaderAttributes = shaderAttributes;
-		this.stageVertex = stageVertex;
-		this.stageFragment = stageFragment;
-
 		// blending
 		// blending
 
 
 		let alphaBlend = {};
 		let alphaBlend = {};
@@ -104,16 +84,19 @@ class WebGPURenderPipeline {
 
 
 		//
 		//
 
 
+		const vertexModule = backend.get( vertexProgram ).module;
+		const fragmentModule = backend.get( fragmentProgram ).module;
+
 		const primitiveState = this._getPrimitiveState( object, geometry, material );
 		const primitiveState = this._getPrimitiveState( object, geometry, material );
 		const colorWriteMask = this._getColorWriteMask( material );
 		const colorWriteMask = this._getColorWriteMask( material );
 		const depthCompare = this._getDepthCompare( material );
 		const depthCompare = this._getDepthCompare( material );
-		const colorFormat = this._utils.getCurrentColorFormat();
-		const depthStencilFormat = this._utils.getCurrentDepthStencilFormat();
-		const sampleCount = this._utils.getSampleCount();
+		const colorFormat = utils.getCurrentColorFormat( renderObject.context );
+		const depthStencilFormat = utils.getCurrentDepthStencilFormat( renderObject.context );
+		const sampleCount = utils.getSampleCount();
 
 
-		this.pipeline = this._device.createRenderPipeline( {
-			vertex: Object.assign( {}, stageVertex.stage, { buffers: vertexBuffers } ),
-			fragment: Object.assign( {}, stageFragment.stage, { targets: [ {
+		pipelineData.pipeline = device.createRenderPipeline( {
+			vertex: Object.assign( {}, vertexModule, { buffers: vertexBuffers } ),
+			fragment: Object.assign( {}, fragmentModule, { targets: [ {
 				format: colorFormat,
 				format: colorFormat,
 				blend: {
 				blend: {
 					alpha: alphaBlend,
 					alpha: alphaBlend,
@@ -139,6 +122,22 @@ class WebGPURenderPipeline {
 
 
 	}
 	}
 
 
+	createComputePipeline( pipeline ) {
+
+		const backend = this.backend;
+		const device = backend.device;
+
+		const computeProgram = backend.get( pipeline.computeProgram ).module;
+
+		const pipelineGPU = backend.get( pipeline );
+
+		pipelineGPU.pipeline = device.createComputePipeline( {
+			compute: computeProgram,
+			layout: 'auto'
+		} );
+
+	}
+
 	_getAlphaBlend( material ) {
 	_getAlphaBlend( material ) {
 
 
 		const blending = material.blending;
 		const blending = material.blending;
@@ -285,7 +284,6 @@ class WebGPURenderPipeline {
 				blendFactor = GPUBlendFactor.OneMinusBlendColor;
 				blendFactor = GPUBlendFactor.OneMinusBlendColor;
 				break;
 				break;
 
 
-
 			default:
 			default:
 				console.error( 'THREE.WebGPURenderer: Blend factor not supported.', blend );
 				console.error( 'THREE.WebGPURenderer: Blend factor not supported.', blend );
 
 
@@ -295,41 +293,6 @@ class WebGPURenderPipeline {
 
 
 	}
 	}
 
 
-	_getBlendOperation( blendEquation ) {
-
-		let blendOperation;
-
-		switch ( blendEquation ) {
-
-			case AddEquation:
-				blendOperation = GPUBlendOperation.Add;
-				break;
-
-			case SubtractEquation:
-				blendOperation = GPUBlendOperation.Subtract;
-				break;
-
-			case ReverseSubtractEquation:
-				blendOperation = GPUBlendOperation.ReverseSubtract;
-				break;
-
-			case MinEquation:
-				blendOperation = GPUBlendOperation.Min;
-				break;
-
-			case MaxEquation:
-				blendOperation = GPUBlendOperation.Max;
-				break;
-
-			default:
-				console.error( 'THREE.WebGPURenderer: Blend equation not supported.', blendEquation );
-
-		}
-
-		return blendOperation;
-
-	}
-
 	_getColorBlend( material ) {
 	_getColorBlend( material ) {
 
 
 		const blending = material.blending;
 		const blending = material.blending;
@@ -382,109 +345,6 @@ class WebGPURenderPipeline {
 
 
 	}
 	}
 
 
-	_getColorWriteMask( material ) {
-
-		return ( material.colorWrite === true ) ? GPUColorWriteFlags.All : GPUColorWriteFlags.None;
-
-	}
-
-	_getDepthCompare( material ) {
-
-		let depthCompare;
-
-		if ( material.depthTest === false ) {
-
-			depthCompare = GPUCompareFunction.Always;
-
-		} else {
-
-			const depthFunc = material.depthFunc;
-
-			switch ( depthFunc ) {
-
-				case NeverDepth:
-					depthCompare = GPUCompareFunction.Never;
-					break;
-
-				case AlwaysDepth:
-					depthCompare = GPUCompareFunction.Always;
-					break;
-
-				case LessDepth:
-					depthCompare = GPUCompareFunction.Less;
-					break;
-
-				case LessEqualDepth:
-					depthCompare = GPUCompareFunction.LessEqual;
-					break;
-
-				case EqualDepth:
-					depthCompare = GPUCompareFunction.Equal;
-					break;
-
-				case GreaterEqualDepth:
-					depthCompare = GPUCompareFunction.GreaterEqual;
-					break;
-
-				case GreaterDepth:
-					depthCompare = GPUCompareFunction.Greater;
-					break;
-
-				case NotEqualDepth:
-					depthCompare = GPUCompareFunction.NotEqual;
-					break;
-
-				default:
-					console.error( 'THREE.WebGPURenderer: Invalid depth function.', depthFunc );
-
-			}
-
-		}
-
-		return depthCompare;
-
-	}
-
-	_getPrimitiveState( object, geometry, material ) {
-
-		const descriptor = {};
-
-		descriptor.topology = this._utils.getPrimitiveTopology( object, material );
-
-		if ( object.isLine === true && object.isLineSegments !== true ) {
-
-			const count = ( geometry.index ) ? geometry.index.count : geometry.attributes.position.count;
-			descriptor.stripIndexFormat = ( count > 65535 ) ? GPUIndexFormat.Uint32 : GPUIndexFormat.Uint16; // define data type for primitive restart value
-
-		}
-
-		switch ( material.side ) {
-
-			case FrontSide:
-				descriptor.frontFace = GPUFrontFace.CW;
-				descriptor.cullMode = GPUCullMode.Front;
-				break;
-
-			case BackSide:
-				descriptor.frontFace = GPUFrontFace.CW;
-				descriptor.cullMode = GPUCullMode.Back;
-				break;
-
-			case DoubleSide:
-				descriptor.frontFace = GPUFrontFace.CW;
-				descriptor.cullMode = GPUCullMode.None;
-				break;
-
-			default:
-				console.error( 'THREE.WebGPURenderer: Unknown Material.side value.', material.side );
-				break;
-
-		}
-
-		return descriptor;
-
-	}
-
 	_getStencilCompare( material ) {
 	_getStencilCompare( material ) {
 
 
 		let stencilCompare;
 		let stencilCompare;
@@ -581,89 +441,145 @@ class WebGPURenderPipeline {
 
 
 	}
 	}
 
 
-	_getVertexFormat( geometryAttribute ) {
+	_getBlendOperation( blendEquation ) {
+
+		let blendOperation;
+
+		switch ( blendEquation ) {
+
+			case AddEquation:
+				blendOperation = GPUBlendOperation.Add;
+				break;
 
 
-		const { itemSize, normalized } = geometryAttribute;
-		const ArrayType = geometryAttribute.array.constructor;
-		const AttributeType = geometryAttribute.constructor;
+			case SubtractEquation:
+				blendOperation = GPUBlendOperation.Subtract;
+				break;
 
 
-		let format;
+			case ReverseSubtractEquation:
+				blendOperation = GPUBlendOperation.ReverseSubtract;
+				break;
 
 
-		if ( itemSize == 1 ) {
+			case MinEquation:
+				blendOperation = GPUBlendOperation.Min;
+				break;
 
 
-			format = typeArraysToVertexFormatPrefixForItemSize1.get( ArrayType );
+			case MaxEquation:
+				blendOperation = GPUBlendOperation.Max;
+				break;
 
 
-		} else {
+			default:
+				console.error( 'THREE.WebGPUPipelineUtils: Blend equation not supported.', blendEquation );
 
 
-			const prefixOptions = typedAttributeToVertexFormatPrefix.get( AttributeType ) || typedArraysToVertexFormatPrefix.get( ArrayType );
-			const prefix = prefixOptions[ normalized ? 1 : 0 ];
+		}
 
 
-			if ( prefix ) {
+		return blendOperation;
 
 
-				const bytesPerUnit = ArrayType.BYTES_PER_ELEMENT * itemSize;
-				const paddedBytesPerUnit = Math.floor( ( bytesPerUnit + 3 ) / 4 ) * 4;
-				const paddedItemSize = paddedBytesPerUnit / ArrayType.BYTES_PER_ELEMENT;
+	}
 
 
-				if ( paddedItemSize % 1 ) {
+	_getPrimitiveState( object, geometry, material ) {
 
 
-					throw new Error( 'THREE.WebGPURenderer: Bad vertex format item size.' );
+		const descriptor = {};
+		const utils = this.backend.utils;
 
 
-				}
+		descriptor.topology = utils.getPrimitiveTopology( object, material );
 
 
-				format = `${prefix}x${paddedItemSize}`;
+		if ( object.isLine === true && object.isLineSegments !== true ) {
 
 
-			}
+			const count = ( geometry.index ) ? geometry.index.count : geometry.attributes.position.count;
+			descriptor.stripIndexFormat = ( count > 65535 ) ? GPUIndexFormat.Uint32 : GPUIndexFormat.Uint16; // define data type for primitive restart value
 
 
 		}
 		}
 
 
-		if ( ! format ) {
+		switch ( material.side ) {
 
 
-			console.error( 'THREE.WebGPURenderer: Vertex format not supported yet.' );
+			case FrontSide:
+				descriptor.frontFace = GPUFrontFace.CW;
+				descriptor.cullMode = GPUCullMode.Front;
+				break;
+
+			case BackSide:
+				descriptor.frontFace = GPUFrontFace.CW;
+				descriptor.cullMode = GPUCullMode.Back;
+				break;
+
+			case DoubleSide:
+				descriptor.frontFace = GPUFrontFace.CW;
+				descriptor.cullMode = GPUCullMode.None;
+				break;
+
+			default:
+				console.error( 'THREE.WebGPUPipelineUtils: Unknown material.side value.', material.side );
+				break;
 
 
 		}
 		}
 
 
-		return format;
+		return descriptor;
 
 
 	}
 	}
 
 
-	_getShaderAttributes( renderObject ) {
+	_getColorWriteMask( material ) {
 
 
-		const attributes = renderObject.getAttributes();
-		const shaderAttributes = [];
+		return ( material.colorWrite === true ) ? GPUColorWriteFlags.All : GPUColorWriteFlags.None;
 
 
-		for ( let slot = 0; slot < attributes.length; slot ++ ) {
+	}
 
 
-			const geometryAttribute = attributes[ slot ];
-			const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT;
+	_getDepthCompare( material ) {
 
 
-			const format = this._getVertexFormat( geometryAttribute );
+		let depthCompare;
 
 
-			let arrayStride = geometryAttribute.itemSize * bytesPerElement;
-			let offset = 0;
+		if ( material.depthTest === false ) {
 
 
-			if ( geometryAttribute.isInterleavedBufferAttribute === true ) {
+			depthCompare = GPUCompareFunction.Always;
 
 
-				// @TODO: It can be optimized for "vertexBuffers" on RenderPipeline
+		} else {
 
 
-				arrayStride = geometryAttribute.data.stride * bytesPerElement;
-				offset = geometryAttribute.offset * bytesPerElement;
+			const depthFunc = material.depthFunc;
 
 
-			}
+			switch ( depthFunc ) {
 
 
-			shaderAttributes.push( {
-				geometryAttribute,
-				arrayStride,
-				offset,
-				format,
-				slot
-			} );
+				case NeverDepth:
+					depthCompare = GPUCompareFunction.Never;
+					break;
+
+				case AlwaysDepth:
+					depthCompare = GPUCompareFunction.Always;
+					break;
+
+				case LessDepth:
+					depthCompare = GPUCompareFunction.Less;
+					break;
+
+				case LessEqualDepth:
+					depthCompare = GPUCompareFunction.LessEqual;
+					break;
+
+				case EqualDepth:
+					depthCompare = GPUCompareFunction.Equal;
+					break;
+
+				case GreaterEqualDepth:
+					depthCompare = GPUCompareFunction.GreaterEqual;
+					break;
+
+				case GreaterDepth:
+					depthCompare = GPUCompareFunction.Greater;
+					break;
+
+				case NotEqualDepth:
+					depthCompare = GPUCompareFunction.NotEqual;
+					break;
+
+				default:
+					console.error( 'THREE.WebGPUPipelineUtils: Invalid depth function.', depthFunc );
+
+			}
 
 
 		}
 		}
 
 
-		return shaderAttributes;
+		return depthCompare;
 
 
 	}
 	}
 
 
 }
 }
 
 
-export default WebGPURenderPipeline;
+export default WebGPUPipelineUtils;

+ 3 - 25
examples/jsm/renderers/webgpu/WebGPUTextureUtils.js → examples/jsm/renderers/webgpu/utils/WebGPUTextureMipmapUtils.js

@@ -1,28 +1,6 @@
-// Copyright 2020 Brandon Jones
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
+import { GPUTextureViewDimension, GPUIndexFormat, GPUFilterMode, GPUPrimitiveTopology, GPULoadOp, GPUStoreOp } from './WebGPUConstants.js';
 
 
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-import { GPUTextureViewDimension, GPUIndexFormat, GPUFilterMode, GPUPrimitiveTopology, GPULoadOp, GPUStoreOp } from './constants.js';
-
-// ported from https://github.com/toji/web-texture-tool/blob/master/src/webgpu-mipmap-generator.js
-
-class WebGPUTextureUtils {
+class WebGPUTextureMipmapUtils {
 
 
 	constructor( device ) {
 	constructor( device ) {
 
 
@@ -182,4 +160,4 @@ fn main( @location( 0 ) vTex : vec2<f32> ) -> @location( 0 ) vec4<f32> {
 
 
 }
 }
 
 
-export default WebGPUTextureUtils;
+export default WebGPUTextureMipmapUtils;

+ 305 - 532
examples/jsm/renderers/webgpu/WebGPUTextures.js → examples/jsm/renderers/webgpu/utils/WebGPUTextureUtils.js

@@ -1,476 +1,375 @@
-import { GPUTextureFormat, GPUAddressMode, GPUFilterMode, GPUTextureDimension, GPUFeatureName } from './constants.js';
-import { VideoTexture, CubeTexture, Texture, NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter, RepeatWrapping, MirroredRepeatWrapping, RGB_ETC2_Format, RGBA_ETC2_EAC_Format,
-	RGBAFormat, RedFormat, RGFormat, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, UnsignedByteType, FloatType, HalfFloatType, SRGBColorSpace, DepthFormat, DepthStencilFormat, DepthTexture,
+import {
+	GPUTextureFormat, GPUAddressMode, GPUFilterMode, GPUTextureDimension, GPUFeatureName
+} from './WebGPUConstants.js';
+
+import {
+	CubeTexture, Texture,
+	NearestFilter, NearestMipmapNearestFilter, NearestMipmapLinearFilter, LinearFilter,
+	RepeatWrapping, MirroredRepeatWrapping,
+	RGB_ETC2_Format, RGBA_ETC2_EAC_Format,
+	RGBAFormat, RedFormat, RGFormat, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, UnsignedByteType, FloatType, HalfFloatType, SRGBColorSpace, DepthFormat, DepthStencilFormat,
 	RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format,
 	RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_ASTC_10x5_Format,
-	RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, UnsignedIntType, UnsignedShortType, UnsignedInt248Type
+	RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_10x10_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, UnsignedIntType, UnsignedShortType, UnsignedInt248Type,
+	NeverCompare, AlwaysCompare, LessCompare, LessEqualCompare, EqualCompare, GreaterEqualCompare, GreaterCompare, NotEqualCompare
 } from 'three';
 } from 'three';
-import WebGPUTextureUtils from './WebGPUTextureUtils.js';
 
 
-class WebGPUTextures {
+import WebGPUTextureMipmapUtils from './WebGPUTextureMipmapUtils.js';
 
 
-	constructor( device, properties, info ) {
+const _compareToWebGPU = {
+	[ NeverCompare ]: 'never',
+	[ AlwaysCompare ]: 'less',
+	[ LessCompare ]: 'equal',
+	[ LessEqualCompare ]: 'less-equal',
+	[ EqualCompare ]: 'greater',
+	[ GreaterEqualCompare ]: 'not-equal',
+	[ GreaterCompare ]: 'greater-equal',
+	[ NotEqualCompare ]: 'always'
+};
 
 
-		this.device = device;
-		this.properties = properties;
-		this.info = info;
+class WebGPUTextureUtils {
+
+	constructor( backend ) {
+
+		this.backend = backend;
+
+		this.mipmapUtils = null;
 
 
 		this.defaultTexture = null;
 		this.defaultTexture = null;
-		this.depthDefaultTexture = null;
-		this.defaultVideoTexture = null;
 		this.defaultCubeTexture = null;
 		this.defaultCubeTexture = null;
-		this.defaultSampler = null;
-
-		this.samplerCache = new Map();
-		this.utils = null;
 
 
 	}
 	}
 
 
-	getDefaultSampler() {
+	createSampler( texture ) {
+
+		const backend = this.backend;
+		const device = backend.device;
+
+		const textureGPU = backend.get( texture );
+
+		const samplerDescriptorGPU = {
+			addressModeU: this._convertAddressMode( texture.wrapS ),
+			addressModeV: this._convertAddressMode( texture.wrapT ),
+			addressModeW: this._convertAddressMode( texture.wrapR ),
+			magFilter: this._convertFilterMode( texture.magFilter ),
+			minFilter: this._convertFilterMode( texture.minFilter ),
+			mipmapFilter: this._convertFilterMode( texture.minFilter ),
+			maxAnisotropy: texture.anisotropy
+		};
 
 
-		if ( this.defaultSampler === null ) {
+		if ( texture.isDepthTexture && texture.compareFunction !== null ) {
 
 
-			this.defaultSampler = this.device.createSampler( {} );
+			samplerDescriptorGPU.compare = _compareToWebGPU[ texture.compareFunction ];
 
 
 		}
 		}
 
 
-		return this.defaultSampler;
+		textureGPU.sampler = device.createSampler( samplerDescriptorGPU );
 
 
 	}
 	}
 
 
-	getDefaultDepthTexture() {
+	createDefaultTexture( texture ) {
 
 
-		if ( this.depthDefaultTexture === null ) {
+		let textureGPU;
 
 
-			const depthTexture = new DepthTexture();
-			depthTexture.image.width = 1;
-			depthTexture.image.height = 1;
+		if ( texture.isCubeTexture ) {
+
+			textureGPU = this._getDefaultCubeTextureGPU();
 
 
-			this._uploadTexture( depthTexture );
+		} else {
 
 
-			this.depthDefaultTexture = this.getTextureGPU( depthTexture );
+			textureGPU = this._getDefaultTextureGPU();
 
 
 		}
 		}
 
 
-		return this.depthDefaultTexture;
+		this.backend.get( texture ).texture = textureGPU;
 
 
 	}
 	}
 
 
-	getDefaultTexture() {
-
-		if ( this.defaultTexture === null ) {
+	createTexture( texture ) {
 
 
-			const texture = new Texture();
-			texture.minFilter = NearestFilter;
-			texture.magFilter = NearestFilter;
+		const backend = this.backend;
+		const textureData = backend.get( texture );
 
 
-			this._uploadTexture( texture );
+		if ( textureData.initialized ) {
 
 
-			this.defaultTexture = this.getTextureGPU( texture );
+			throw new Error( 'WebGPUTextureUtils: Texture already initialized.' );
 
 
 		}
 		}
 
 
-		return this.defaultTexture;
-
-	}
+		const { width, height, depth } = this._getSize( texture );
 
 
-	getDefaultVideoTexture() {
+		const needsMipmaps = this._needsMipmaps( texture );
+		const dimension = this._getDimension( texture );
+		const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps );
+		const format = texture.internalFormat || this._getFormat( texture );
+		//const sampleCount = texture.isRenderTargetTexture || texture.isDepthTexture ? backend.utils.getSampleCount() : 1;
+		const sampleCount = 1;
 
 
-		if ( this.defaultVideoTexture === null ) {
+		let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST;
 
 
-			const video = document.getElementById( 'video' );
+		if ( texture.isCompressedTexture !== true ) {
 
 
-			const texture = new VideoTexture( video );
-			texture.minFilter = NearestFilter;
-			texture.magFilter = NearestFilter;
+			usage |= GPUTextureUsage.RENDER_ATTACHMENT;
 
 
-			this._uploadVideoTexture( texture );
+		}
 
 
-			this.defaultVideoTexture = this.getTextureGPU( texture );
+		const textureDescriptorGPU = {
+			label: texture.name,
+			size: {
+				width: width,
+				height: height,
+				depthOrArrayLayers: depth,
+			},
+			mipLevelCount: mipLevelCount,
+			sampleCount: sampleCount,
+			dimension: dimension,
+			format: format,
+			usage: usage
+		};
 
 
-		}
+		// texture creation
 
 
-		return this.defaultVideoTexture;
+		if ( texture.isVideoTexture ) {
 
 
-	}
+			const video = texture.source.data;
+			const videoFrame = new VideoFrame( video );
 
 
-	getDefaultCubeTexture() {
+			textureDescriptorGPU.size.width = videoFrame.displayWidth;
+			textureDescriptorGPU.size.height = videoFrame.displayHeight;
 
 
-		if ( this.defaultCubeTexture === null ) {
+			videoFrame.close();
 
 
-			const texture = new CubeTexture();
-			texture.minFilter = NearestFilter;
-			texture.magFilter = NearestFilter;
+			textureData.externalTexture = video;
 
 
-			this._uploadTexture( texture );
+		} else {
 
 
-			this.defaultCubeTexture = this.getTextureGPU( texture );
+			textureData.texture = backend.device.createTexture( textureDescriptorGPU );
 
 
 		}
 		}
 
 
-		return this.defaultCubeTexture;
+		textureData.initialized = true;
+
+		textureData.needsMipmaps = needsMipmaps;
+		textureData.textureDescriptorGPU = textureDescriptorGPU;
 
 
 	}
 	}
 
 
-	getTextureGPU( texture ) {
+	destroyTexture( texture ) {
+
+		const backend = this.backend;
+		const textureData = backend.get( texture );
 
 
-		const textureProperties = this.properties.get( texture );
+		textureData.texture.destroy();
 
 
-		return textureProperties.textureGPU;
+		backend.delete( texture );
 
 
 	}
 	}
 
 
-	getSampler( texture ) {
+	destroySampler( texture ) {
 
 
-		const textureProperties = this.properties.get( texture );
+		const backend = this.backend;
+		const textureData = backend.get( texture );
 
 
-		return textureProperties.samplerGPU;
+		delete textureData.sampler;
 
 
 	}
 	}
 
 
 	updateTexture( texture ) {
 	updateTexture( texture ) {
 
 
-		let needsUpdate = false;
-
-		const textureProperties = this.properties.get( texture );
-
-		if ( texture.version > 0 && textureProperties.version !== texture.version ) {
+		const textureData = this.backend.get( texture );
 
 
-			const image = texture.image;
+		const { needsMipmaps, textureDescriptorGPU } = textureData;
 
 
-			if ( image === undefined ) {
-
-				console.warn( 'THREE.WebGPURenderer: Texture marked for update but image is undefined.' );
+		// transfer texture data
 
 
-			} else if ( image.complete === false ) {
+		if ( texture.isDataTexture || texture.isDataArrayTexture || texture.isData3DTexture ) {
 
 
-				console.warn( 'THREE.WebGPURenderer: Texture marked for update but image is incomplete.' );
+			this._copyBufferToTexture( texture.image, textureData.texture, textureDescriptorGPU, needsMipmaps );
 
 
-			} else {
+		} else if ( texture.isCompressedTexture ) {
 
 
-				// texture init
+			this._copyCompressedBufferToTexture( texture.mipmaps, textureData.texture, textureDescriptorGPU );
 
 
-				if ( textureProperties.initialized === undefined ) {
+		} else if ( texture.isCubeTexture ) {
 
 
-					textureProperties.initialized = true;
+			if ( texture.image.length === 6 ) {
 
 
-					const disposeCallback = onTextureDispose.bind( this );
-					textureProperties.disposeCallback = disposeCallback;
+				this._copyCubeMapToTexture( texture.image, texture, textureData.texture, textureDescriptorGPU, needsMipmaps );
 
 
-					texture.addEventListener( 'dispose', disposeCallback );
+			}
 
 
-					this.info.memory.textures ++;
+		} else if ( texture.isRenderTargetTexture ) {
 
 
-				}
+			if ( needsMipmaps === true ) this._generateMipmaps( textureData.texture, textureDescriptorGPU );
 
 
-				//
+		} else if ( texture.isVideoTexture ) {
 
 
-				if ( texture.isVideoTexture ) {
+			const video = texture.source.data;
 
 
-					needsUpdate = this._uploadVideoTexture( texture );
+			textureData.externalTexture = video;
 
 
-				} else {
+		} else if ( texture.image !== null ) {
 
 
-					needsUpdate = this._uploadTexture( texture );
+			this._copyImageToTexture( texture.image, texture, textureData.texture, textureDescriptorGPU, needsMipmaps );
 
 
-				}
+		} else {
 
 
-			}
+			console.warn( 'WebGPUTextureUtils: Unable to update texture.' );
 
 
 		}
 		}
 
 
-		// if the texture is used for RTT, it's necessary to init it once so the binding
-		// group's resource definition points to the respective GPUTexture
-
-		if ( textureProperties.initializedRTT === false ) {
-
-			textureProperties.initializedRTT = true;
-			needsUpdate = true;
-
-		}
+		//
 
 
-		return needsUpdate;
+		textureData.version = texture.version;
 
 
 	}
 	}
 
 
-	updateSampler( texture ) {
-
-		const array = [];
+	_getDefaultTextureGPU() {
 
 
-		array.push( texture.wrapS );
-		array.push( texture.wrapT );
-		array.push( texture.wrapR );
-		array.push( texture.magFilter );
-		array.push( texture.minFilter );
-		array.push( texture.anisotropy );
+		let defaultTexture = this.defaultTexture;
 
 
-		const key = array.join();
-		let samplerGPU = this.samplerCache.get( key );
+		if ( defaultTexture === null ) {
 
 
-		if ( samplerGPU === undefined ) {
+			const texture = new Texture();
+			texture.minFilter = NearestFilter;
+			texture.magFilter = NearestFilter;
 
 
-			samplerGPU = this.device.createSampler( {
-				addressModeU: this._convertAddressMode( texture.wrapS ),
-				addressModeV: this._convertAddressMode( texture.wrapT ),
-				addressModeW: this._convertAddressMode( texture.wrapR ),
-				magFilter: this._convertFilterMode( texture.magFilter ),
-				minFilter: this._convertFilterMode( texture.minFilter ),
-				mipmapFilter: this._convertFilterMode( texture.minFilter ),
-				maxAnisotropy: texture.anisotropy
-			} );
+			this.createTexture( texture );
 
 
-			this.samplerCache.set( key, samplerGPU );
+			this.defaultTexture = defaultTexture = texture;
 
 
 		}
 		}
 
 
-		const textureProperties = this.properties.get( texture );
-		textureProperties.samplerGPU = samplerGPU;
+		return this.backend.get( defaultTexture ).texture;
 
 
 	}
 	}
 
 
-	initRenderTarget( renderTarget ) {
-
-		const properties = this.properties;
-		const renderTargetProperties = properties.get( renderTarget );
-
-		if ( renderTargetProperties.initialized === undefined ) {
-
-			const device = this.device;
+	_getDefaultCubeTextureGPU() {
 
 
-			const width = renderTarget.width;
-			const height = renderTarget.height;
+		let defaultCubeTexture = this.defaultTexture;
 
 
-			const texture = renderTarget.texture;
+		if ( defaultCubeTexture === null ) {
 
 
-			const colorTextureFormat = texture.internalFormat || this._getFormat( texture );
-			const label = texture.name ? '_' + texture.name : '';
-			const needsMipmaps = this._needsMipmaps( texture );
-			const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps );
-
-			const colorTextureGPU = device.createTexture( {
-				label: 'renderTarget' + label,
-				size: {
-					width: width,
-					height: height,
-					depthOrArrayLayers: 1
-				},
-				mipLevelCount: mipLevelCount,
-				format: colorTextureFormat,
-				usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
-			} );
-
-			this.info.memory.textures ++;
-
-			renderTargetProperties.colorTextureGPU = colorTextureGPU;
-			renderTargetProperties.colorTextureFormat = colorTextureFormat;
-
-			// When the ".texture" or ".depthTexture" property of a render target is used as a map,
-			// the renderer has to find the respective GPUTexture objects to setup the bind groups.
-			// Since it's not possible to see just from a texture object whether it belongs to a render
-			// target or not, we need the initializedRTT flag.
-
-			const textureProperties = properties.get( texture );
-			textureProperties.textureGPU = colorTextureGPU;
-			textureProperties.initializedRTT = false;
-
-			if ( renderTarget.depthBuffer === true ) {
+			const texture = new CubeTexture();
+			texture.minFilter = NearestFilter;
+			texture.magFilter = NearestFilter;
 
 
-				const depthTextureFormat = renderTarget.depthTexture !== null ? this._getFormat( renderTarget.depthTexture ) : GPUTextureFormat.Depth24PlusStencil8;
+			this.createTexture( texture );
 
 
-				const depthTextureGPU = device.createTexture( {
-					label: 'renderTarget' + label + '_depthBuffer',
-					size: {
-						width: width,
-						height: height,
-						depthOrArrayLayers: 1
-					},
-					format: depthTextureFormat,
-					usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST
-				} );
+			this.defaultCubeTexture = defaultCubeTexture = texture;
 
 
-				this.info.memory.textures ++;
+		}
 
 
-				renderTargetProperties.depthTextureGPU = depthTextureGPU;
-				renderTargetProperties.depthTextureFormat = depthTextureFormat;
+		return this.backend.get( defaultCubeTexture ).texture;
 
 
-				if ( renderTarget.depthTexture !== null ) {
+	}
 
 
-					const depthTextureProperties = properties.get( renderTarget.depthTexture );
-					depthTextureProperties.textureGPU = depthTextureGPU;
-					depthTextureProperties.initializedRTT = false;
+	_copyImageToTexture( image, texture, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth ) {
 
 
-				}
+		if ( this._isHTMLImage( image ) ) {
 
 
-			}
+			this._getImageBitmapFromHTML( image, texture ).then( imageBitmap => {
 
 
-			//
+				this._copyExternalImageToTexture( imageBitmap, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth );
 
 
-			const disposeCallback = onRenderTargetDispose.bind( this );
-			renderTargetProperties.disposeCallback = disposeCallback;
+			} );
 
 
-			renderTarget.addEventListener( 'dispose', disposeCallback );
+		} else {
 
 
-			//
+			// assume ImageBitmap
 
 
-			renderTargetProperties.initialized = true;
+			this._copyExternalImageToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth );
 
 
 		}
 		}
 
 
 	}
 	}
 
 
-	dispose() {
+	_isHTMLImage( image ) {
 
 
-		this.samplerCache.clear();
+		return ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement );
 
 
 	}
 	}
 
 
-	_convertAddressMode( value ) {
-
-		let addressMode = GPUAddressMode.ClampToEdge;
-
-		if ( value === RepeatWrapping ) {
-
-			addressMode = GPUAddressMode.Repeat;
+	_copyCubeMapToTexture( images, texture, textureGPU, textureDescriptorGPU, needsMipmaps ) {
 
 
-		} else if ( value === MirroredRepeatWrapping ) {
-
-			addressMode = GPUAddressMode.MirrorRepeat;
-
-		}
+		for ( let i = 0; i < 6; i ++ ) {
 
 
-		return addressMode;
+			const image = images[ i ];
 
 
-	}
+			if ( image.isDataTexture ) {
 
 
-	_convertFilterMode( value ) {
+				this._copyBufferToTexture( image.image, textureGPU, textureDescriptorGPU, needsMipmaps, i );
 
 
-		let filterMode = GPUFilterMode.Linear;
+			} else {
 
 
-		if ( value === NearestFilter || value === NearestMipmapNearestFilter || value === NearestMipmapLinearFilter ) {
+				this._copyImageToTexture( image, texture, textureGPU, textureDescriptorGPU, needsMipmaps, i );
 
 
-			filterMode = GPUFilterMode.Nearest;
+			}
 
 
 		}
 		}
 
 
-		return filterMode;
-
 	}
 	}
 
 
-	_uploadVideoTexture( texture ) {
+	_copyExternalImageToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth = 0 ) {
 
 
-		const device = this.device;
+		const device = this.backend.device;
 
 
-		const textureProperties = this.properties.get( texture );
-
-		const textureGPU = device.importExternalTexture( {
-			source: texture.source.data
-		} );
-
-		textureProperties.textureGPU = textureGPU;
-		//textureProperties.version = texture.version; // @TODO: Force update for now, study a better solution soon using native VideoTexture.update() to fix warns
+		device.queue.copyExternalImageToTexture(
+			{
+				source: image
+			}, {
+				texture: textureGPU,
+				mipLevel: 0,
+				origin: { x: 0, y: 0, z: originDepth }
+			}, {
+				width: image.width,
+				height: image.height,
+				depthOrArrayLayers: 1
+			}
+		);
 
 
-		return true;
+		if ( needsMipmaps ) this._generateMipmaps( textureGPU, textureDescriptorGPU, originDepth );
 
 
 	}
 	}
 
 
-	_uploadTexture( texture ) {
-
-		let needsUpdate = false;
-
-		const device = this.device;
-		const image = texture.image;
-
-		const textureProperties = this.properties.get( texture );
-
-		const { width, height, depth } = this._getSize( texture );
-		const needsMipmaps = this._needsMipmaps( texture );
-		const dimension = this._getDimension( texture );
-		const mipLevelCount = this._getMipLevelCount( texture, width, height, needsMipmaps );
-		const format = texture.internalFormat || this._getFormat( texture );
-
-		let usage = GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST;
+	_generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer ) {
 
 
-		if ( needsMipmaps ) {
+		if ( this.mipmapUtils === null ) {
 
 
-			// current mipmap generation requires RENDER_ATTACHMENT
-
-			usage |= GPUTextureUsage.RENDER_ATTACHMENT;
+			this.mipmapUtils = new WebGPUTextureMipmapUtils( this.backend.device );
 
 
 		}
 		}
 
 
-		const textureGPUDescriptor = {
-			label: texture.name,
-			size: {
-				width: width,
-				height: height,
-				depthOrArrayLayers: depth,
-			},
-			mipLevelCount: mipLevelCount,
-			sampleCount: 1,
-			dimension: dimension,
-			format: format,
-			usage: usage
-		};
-
-		// texture creation
-
-		let textureGPU = textureProperties.textureGPU;
-
-		if ( textureGPU === undefined ) {
-
-			textureGPU = device.createTexture( textureGPUDescriptor );
-
-			needsUpdate = true;
-
-		}
-
-		// transfer texture data
-
-		if ( texture.isDataTexture || texture.isDataArrayTexture || texture.isData3DTexture ) {
-
-			this._copyBufferToTexture( image, textureGPU, textureGPUDescriptor, needsMipmaps );
-
-		} else if ( texture.isCompressedTexture ) {
-
-			this._copyCompressedBufferToTexture( texture.mipmaps, textureGPU, textureGPUDescriptor );
+		this.mipmapUtils.generateMipmaps( textureGPU, textureDescriptorGPU, baseArrayLayer );
 
 
-		} else if ( texture.isCubeTexture ) {
-
-			if ( image.length === 6 ) {
-
-				this._copyCubeMapToTexture( image, texture, textureGPU, textureGPUDescriptor, needsMipmaps );
-
-			}
-
-		} else if ( texture.isRenderTargetTexture ) {
-
-			if ( needsMipmaps === true ) this._generateMipmaps( textureGPU, textureGPUDescriptor );
-
-		} else if ( texture.isDepthTexture !== true && image !== null ) {
+	}
 
 
-			this._copyImageToTexture( image, texture, textureGPU, textureGPUDescriptor, needsMipmaps );
+	_getImageBitmapFromHTML( image, texture ) {
 
 
-		}
+		const width = image.width;
+		const height = image.height;
 
 
-		//
+		const options = {};
 
 
-		textureProperties.textureGPU = textureGPU;
-		textureProperties.version = texture.version;
+		options.imageOrientation = ( texture.flipY === true ) ? 'flipY' : 'none';
+		options.premultiplyAlpha = ( texture.premultiplyAlpha === true ) ? 'premultiply' : 'default';
 
 
-		return needsUpdate;
+		return createImageBitmap( image, 0, 0, width, height, options );
 
 
 	}
 	}
 
 
-	_copyBufferToTexture( image, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth = 0 ) {
+	_copyBufferToTexture( image, textureGPU, textureDescriptorGPU, needsMipmaps, originDepth = 0 ) {
 
 
 		// @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture()
 		// @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture()
 		// @TODO: Consider to support valid buffer layouts with other formats like RGB
 		// @TODO: Consider to support valid buffer layouts with other formats like RGB
 
 
+		const device = this.backend.device;
+
 		const data = image.data;
 		const data = image.data;
 
 
-		const bytesPerTexel = this._getBytesPerTexel( textureGPUDescriptor.format );
+		const bytesPerTexel = this._getBytesPerTexel( textureDescriptorGPU.format );
 		const bytesPerRow = image.width * bytesPerTexel;
 		const bytesPerRow = image.width * bytesPerTexel;
 
 
-		this.device.queue.writeTexture(
+		device.queue.writeTexture(
 			{
 			{
 				texture: textureGPU,
 				texture: textureGPU,
 				mipLevel: 0,
 				mipLevel: 0,
@@ -487,55 +386,17 @@ class WebGPUTextures {
 				depthOrArrayLayers: ( image.depth !== undefined ) ? image.depth : 1
 				depthOrArrayLayers: ( image.depth !== undefined ) ? image.depth : 1
 			} );
 			} );
 
 
-		if ( needsMipmaps === true ) this._generateMipmaps( textureGPU, textureGPUDescriptor, originDepth );
-
-	}
-
-	_copyCubeMapToTexture( images, texture, textureGPU, textureGPUDescriptor, needsMipmaps ) {
-
-		for ( let i = 0; i < 6; i ++ ) {
-
-			const image = images[ i ];
-
-			if ( image.isDataTexture ) {
-
-				this._copyBufferToTexture( image.image, textureGPU, textureGPUDescriptor, needsMipmaps, i );
-
-			} else {
-
-				this._copyImageToTexture( image, texture, textureGPU, textureGPUDescriptor, needsMipmaps, i );
-
-			}
-
-		}
+		if ( needsMipmaps === true ) this._generateMipmaps( textureGPU, textureDescriptorGPU, originDepth );
 
 
 	}
 	}
 
 
-	_copyExternalImageToTexture( image, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth = 0 ) {
-
-		this.device.queue.copyExternalImageToTexture(
-			{
-				source: image
-			}, {
-				texture: textureGPU,
-				mipLevel: 0,
-				origin: { x: 0, y: 0, z: originDepth }
-			}, {
-				width: image.width,
-				height: image.height,
-				depthOrArrayLayers: 1
-			}
-		);
-
-		if ( needsMipmaps ) this._generateMipmaps( textureGPU, textureGPUDescriptor, originDepth );
-
-	}
-
-	_copyCompressedBufferToTexture( mipmaps, textureGPU, textureGPUDescriptor ) {
+	_copyCompressedBufferToTexture( mipmaps, textureGPU, textureDescriptorGPU ) {
 
 
 		// @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture()
 		// @TODO: Consider to use GPUCommandEncoder.copyBufferToTexture()
 
 
-		const blockData = this._getBlockData( textureGPUDescriptor.format );
+		const device = this.backend.device;
+
+		const blockData = this._getBlockData( textureDescriptorGPU.format );
 
 
 		for ( let i = 0; i < mipmaps.length; i ++ ) {
 		for ( let i = 0; i < mipmaps.length; i ++ ) {
 
 
@@ -546,7 +407,7 @@ class WebGPUTextures {
 
 
 			const bytesPerRow = Math.ceil( width / blockData.width ) * blockData.byteLength;
 			const bytesPerRow = Math.ceil( width / blockData.width ) * blockData.byteLength;
 
 
-			this.device.queue.writeTexture(
+			device.queue.writeTexture(
 				{
 				{
 					texture: textureGPU,
 					texture: textureGPU,
 					mipLevel: i
 					mipLevel: i
@@ -567,18 +428,6 @@ class WebGPUTextures {
 
 
 	}
 	}
 
 
-	_generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer ) {
-
-		if ( this.utils === null ) {
-
-			this.utils = new WebGPUTextureUtils( this.device ); // only create this helper if necessary
-
-		}
-
-		this.utils.generateMipmaps( textureGPU, textureGPUDescriptor, baseArrayLayer );
-
-	}
-
 	_getBlockData( format ) {
 	_getBlockData( format ) {
 
 
 		// this method is only relevant for compressed texture formats
 		// this method is only relevant for compressed texture formats
@@ -616,6 +465,74 @@ class WebGPUTextures {
 
 
 	}
 	}
 
 
+	_convertAddressMode( value ) {
+
+		let addressMode = GPUAddressMode.ClampToEdge;
+
+		if ( value === RepeatWrapping ) {
+
+			addressMode = GPUAddressMode.Repeat;
+
+		} else if ( value === MirroredRepeatWrapping ) {
+
+			addressMode = GPUAddressMode.MirrorRepeat;
+
+		}
+
+		return addressMode;
+
+	}
+
+	_convertFilterMode( value ) {
+
+		let filterMode = GPUFilterMode.Linear;
+
+		if ( value === NearestFilter || value === NearestMipmapNearestFilter || value === NearestMipmapLinearFilter ) {
+
+			filterMode = GPUFilterMode.Nearest;
+
+		}
+
+		return filterMode;
+
+	}
+
+	_getSize( texture ) {
+
+		const image = texture.image;
+
+		let width, height, depth;
+
+		if ( texture.isCubeTexture ) {
+
+			const faceImage = image.length > 0 ? image[ 0 ].image || image[ 0 ] : null;
+
+			width = faceImage ? faceImage.width : 1;
+			height = faceImage ? faceImage.height : 1;
+			depth = 6; // one image for each side of the cube map
+
+		} else if ( image !== null ) {
+
+			width = image.width;
+			height = image.height;
+			depth = ( image.depth !== undefined ) ? image.depth : 1;
+
+		} else {
+
+			width = height = depth = 1;
+
+		}
+
+		return { width, height, depth };
+
+	}
+
+	_needsMipmaps( texture ) {
+
+		return ( texture.isCompressedTexture !== true ) && ( texture.generateMipmaps === true ) && ( texture.minFilter !== NearestFilter ) && ( texture.minFilter !== LinearFilter );
+
+	}
+
 	_getBytesPerTexel( format ) {
 	_getBytesPerTexel( format ) {
 
 
 		if ( format === GPUTextureFormat.R8Unorm ) return 1;
 		if ( format === GPUTextureFormat.R8Unorm ) return 1;
@@ -648,6 +565,28 @@ class WebGPUTextures {
 
 
 	}
 	}
 
 
+	_getMipLevelCount( texture, width, height, needsMipmaps ) {
+
+		let mipLevelCount;
+
+		if ( texture.isCompressedTexture ) {
+
+			mipLevelCount = texture.mipmaps.length;
+
+		} else if ( needsMipmaps ) {
+
+			mipLevelCount = Math.floor( Math.log2( Math.max( width, height ) ) ) + 1;
+
+		} else {
+
+			mipLevelCount = 1; // a texture without mipmaps has a base mip (mipLevel 0)
+
+		}
+
+		return mipLevelCount;
+
+	}
+
 	_getFormat( texture ) {
 	_getFormat( texture ) {
 
 
 		const format = texture.format;
 		const format = texture.format;
@@ -656,7 +595,11 @@ class WebGPUTextures {
 
 
 		let formatGPU;
 		let formatGPU;
 
 
-		if ( texture.isCompressedTexture === true ) {
+		if ( texture.isRenderTargetTexture === true || texture.isFramebufferTexture === true ) {
+
+			formatGPU = GPUTextureFormat.BGRA8Unorm;
+
+		} else if ( texture.isCompressedTexture === true ) {
 
 
 			switch ( format ) {
 			switch ( format ) {
 
 
@@ -875,176 +818,6 @@ class WebGPUTextures {
 
 
 	}
 	}
 
 
-	_isHTMLImage( image ) {
-
-		return ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) || ( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement );
-
-	}
-
-	_copyImageToTexture( image, texture, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth ) {
-
-		if ( this._isHTMLImage( image ) ) {
-
-			this._getImageBitmapFromHTML( image, texture ).then( imageBitmap => {
-
-				this._copyExternalImageToTexture( imageBitmap, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth );
-
-			} );
-
-		} else {
-
-			// assume ImageBitmap
-
-			this._copyExternalImageToTexture( image, textureGPU, textureGPUDescriptor, needsMipmaps, originDepth );
-
-		}
-
-	}
-
-	_getImageBitmapFromHTML( image, texture ) {
-
-		const width = image.width;
-		const height = image.height;
-
-		const options = {};
-
-		options.imageOrientation = ( texture.flipY === true ) ? 'flipY' : 'none';
-		options.premultiplyAlpha = ( texture.premultiplyAlpha === true ) ? 'premultiply' : 'default';
-
-		return createImageBitmap( image, 0, 0, width, height, options );
-
-	}
-
-	_getImageBitmap( image, texture ) {
-
-		const width = image.width;
-		const height = image.height;
-
-		if ( ( typeof HTMLImageElement !== 'undefined' && image instanceof HTMLImageElement ) ||
-			( typeof HTMLCanvasElement !== 'undefined' && image instanceof HTMLCanvasElement ) ) {
-
-			const options = {};
-
-			options.imageOrientation = ( texture.flipY === true ) ? 'flipY' : 'none';
-			options.premultiplyAlpha = ( texture.premultiplyAlpha === true ) ? 'premultiply' : 'default';
-
-			return createImageBitmap( image, 0, 0, width, height, options );
-
-		} else {
-
-			// assume ImageBitmap
-
-			return Promise.resolve( image );
-
-		}
-
-	}
-
-	_getMipLevelCount( texture, width, height, needsMipmaps ) {
-
-		let mipLevelCount;
-
-		if ( texture.isCompressedTexture ) {
-
-			mipLevelCount = texture.mipmaps.length;
-
-		} else if ( needsMipmaps ) {
-
-			mipLevelCount = Math.floor( Math.log2( Math.max( width, height ) ) ) + 1;
-
-		} else {
-
-			mipLevelCount = 1; // a texture without mipmaps has a base mip (mipLevel 0)
-
-		}
-
-		return mipLevelCount;
-
-	}
-
-	_getSize( texture ) {
-
-		const image = texture.image;
-
-		let width, height, depth;
-
-		if ( texture.isCubeTexture ) {
-
-			const faceImage = image.length > 0 ? image[ 0 ].image || image[ 0 ] : null;
-
-			width = faceImage ? faceImage.width : 1;
-			height = faceImage ? faceImage.height : 1;
-			depth = 6; // one image for each side of the cube map
-
-		} else if ( image !== null ) {
-
-			width = image.width;
-			height = image.height;
-			depth = ( image.depth !== undefined ) ? image.depth : 1;
-
-		} else {
-
-			width = height = depth = 1;
-
-		}
-
-		return { width, height, depth };
-
-	}
-
-	_needsMipmaps( texture ) {
-
-		return ( texture.isCompressedTexture !== true ) && ( texture.generateMipmaps === true ) && ( texture.minFilter !== NearestFilter ) && ( texture.minFilter !== LinearFilter );
-
-	}
-
-}
-
-function onRenderTargetDispose( event ) {
-
-	const renderTarget = event.target;
-	const properties = this.properties;
-
-	const renderTargetProperties = properties.get( renderTarget );
-
-	renderTarget.removeEventListener( 'dispose', renderTargetProperties.disposeCallback );
-
-	renderTargetProperties.colorTextureGPU.destroy();
-	properties.remove( renderTarget.texture );
-
-	this.info.memory.textures --;
-
-	if ( renderTarget.depthBuffer === true ) {
-
-		renderTargetProperties.depthTextureGPU.destroy();
-
-		this.info.memory.textures --;
-
-		if ( renderTarget.depthTexture !== null ) {
-
-			properties.remove( renderTarget.depthTexture );
-
-		}
-
-	}
-
-	properties.remove( renderTarget );
-
-}
-
-function onTextureDispose( event ) {
-
-	const texture = event.target;
-
-	const textureProperties = this.properties.get( texture );
-	textureProperties.textureGPU.destroy();
-
-	texture.removeEventListener( 'dispose', textureProperties.disposeCallback );
-
-	this.properties.remove( texture );
-
-	this.info.memory.textures --;
-
 }
 }
 
 
-export default WebGPUTextures;
+export default WebGPUTextureUtils;

+ 82 - 0
examples/jsm/renderers/webgpu/utils/WebGPUUtils.js

@@ -0,0 +1,82 @@
+import { GPUPrimitiveTopology, GPUTextureFormat } from './WebGPUConstants.js';
+
+class WebGPUUtils {
+
+	constructor( backend ) {
+
+		this.backend = backend;
+
+	}
+
+	getCurrentDepthStencilFormat( renderContext ) {
+
+		let format;
+
+		if ( renderContext.depthTexture !== null ) {
+
+			format = this.getTextureFormatGPU( renderContext.depthTexture );
+
+		} else {
+
+			format = GPUTextureFormat.Depth24PlusStencil8;
+
+		}
+
+		return format;
+
+	}
+
+	getTextureFormatGPU( texture ) {
+
+		return this.backend.get( texture ).texture.format;
+
+	}
+
+	getCurrentColorFormat( renderContext ) {
+
+		let format;
+
+		if ( renderContext.texture !== null ) {
+
+			format = this.getTextureFormatGPU( renderContext.texture );
+
+		} else {
+
+			format = GPUTextureFormat.BGRA8Unorm; // default context format
+
+		}
+
+		return format;
+
+	}
+
+	getCurrentColorSpace( renderContext ) {
+
+		if ( renderContext.texture !== null ) {
+
+			return renderContext.texture.colorSpace;
+
+		}
+
+		return this.backend.renderer.outputColorSpace;
+
+	}
+
+	getPrimitiveTopology( object, material ) {
+
+		if ( object.isPoints ) return GPUPrimitiveTopology.PointList;
+		else if ( object.isLineSegments || ( object.isMesh && material.wireframe === true ) ) return GPUPrimitiveTopology.LineList;
+		else if ( object.isLine ) return GPUPrimitiveTopology.LineStrip;
+		else if ( object.isMesh ) return GPUPrimitiveTopology.TriangleList;
+
+	}
+
+	getSampleCount() {
+
+		return this.backend.parameters.sampleCount;
+
+	}
+
+}
+
+export default WebGPUUtils;

+ 2 - 2
examples/webgpu_backdrop.html

@@ -83,6 +83,8 @@
 
 
 				// portals
 				// portals
 
 
+				const geometry = new THREE.SphereGeometry( .3, 32, 16 );
+
 				portals = new THREE.Group();
 				portals = new THREE.Group();
 				scene.add( portals );
 				scene.add( portals );
 
 
@@ -92,8 +94,6 @@
 					const id = portals.children.length;
 					const id = portals.children.length;
 					const rotation = THREE.MathUtils.degToRad( id * 45 );
 					const rotation = THREE.MathUtils.degToRad( id * 45 );
 
 
-					const geometry = new THREE.SphereGeometry( .3, 32, 16 );
-
 					const material = new MeshStandardNodeMaterial( { color: 0x0066ff } );
 					const material = new MeshStandardNodeMaterial( { color: 0x0066ff } );
 					material.roughnessNode = float( .2 );
 					material.roughnessNode = float( .2 );
 					material.metalnessNode = float( 0 );
 					material.metalnessNode = float( 0 );

+ 3 - 2
examples/webgpu_depth_texture.html

@@ -30,7 +30,7 @@
 
 
 			import WebGPU from 'three/addons/capabilities/WebGPU.js';
 			import WebGPU from 'three/addons/capabilities/WebGPU.js';
 			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
 			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
-			import WebGPUTextureRenderer from 'three/addons/renderers/webgpu/WebGPUTextureRenderer.js';
+			import TextureRenderer from 'three/addons/renderers/common/TextureRenderer.js';
 
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 
 
@@ -92,8 +92,9 @@
 				document.body.appendChild( renderer.domElement );
 				document.body.appendChild( renderer.domElement );
 
 
 				const depthTexture = new THREE.DepthTexture();
 				const depthTexture = new THREE.DepthTexture();
+				depthTexture.type = THREE.FloatType;
 
 
-				textureRenderer = new WebGPUTextureRenderer( renderer );
+				textureRenderer = new TextureRenderer( renderer );
 				textureRenderer.renderTarget.depthTexture = depthTexture;
 				textureRenderer.renderTarget.depthTexture = depthTexture;
 				textureRenderer.setSize( window.innerWidth * dpr, window.innerHeight * dpr );
 				textureRenderer.setSize( window.innerWidth * dpr, window.innerHeight * dpr );
 
 

+ 3 - 3
examples/webgpu_rtt.html

@@ -30,7 +30,7 @@
 
 
 			import WebGPU from 'three/addons/capabilities/WebGPU.js';
 			import WebGPU from 'three/addons/capabilities/WebGPU.js';
 			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
 			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
-			import WebGPUTextureRenderer from 'three/addons/renderers/webgpu/WebGPUTextureRenderer.js';
+			import TextureRenderer from 'three/addons/renderers/common/TextureRenderer.js';
 
 
 			let camera, scene, renderer;
 			let camera, scene, renderer;
 			const mouse = new THREE.Vector2();
 			const mouse = new THREE.Vector2();
@@ -81,7 +81,7 @@
 				renderer.setAnimationLoop( animate );
 				renderer.setAnimationLoop( animate );
 				document.body.appendChild( renderer.domElement );
 				document.body.appendChild( renderer.domElement );
 
 
-				textureRenderer = new WebGPUTextureRenderer( renderer );
+				textureRenderer = new TextureRenderer( renderer );
 				textureRenderer.setSize( window.innerWidth * dpr, window.innerHeight * dpr );
 				textureRenderer.setSize( window.innerWidth * dpr, window.innerHeight * dpr );
 
 
 				window.addEventListener( 'mousemove', onWindowMouseMove );
 				window.addEventListener( 'mousemove', onWindowMouseMove );
@@ -99,7 +99,7 @@
 				const screenFXNode = uniform( mouse ).add( vec2( 0.5, 0.5 ) );
 				const screenFXNode = uniform( mouse ).add( vec2( 0.5, 0.5 ) );
 
 
 				const materialFX = new MeshBasicNodeMaterial();
 				const materialFX = new MeshBasicNodeMaterial();
-				materialFX.colorNode = texture( textureRenderer.getTexture() ).mul( screenFXNode );
+				materialFX.colorNode = texture( textureRenderer.texture ).mul( screenFXNode );
 
 
 				const quad = new THREE.Mesh( geometryFX, materialFX );
 				const quad = new THREE.Mesh( geometryFX, materialFX );
 				sceneFX.add( quad );
 				sceneFX.add( quad );