Browse Source

WebGPURenderPipelines: Add pipeline cache. (#21741)

* WebGPURenderPipelines: Determine pipeline based on parameters.

* Add WebGPURenderPipeline.
Michael Herzog 4 years ago
parent
commit
4ad824343e

+ 4 - 4
examples/jsm/renderers/webgpu/WebGPUBindings.js

@@ -1,12 +1,12 @@
 class WebGPUBindings {
 class WebGPUBindings {
 
 
-	constructor( device, info, properties, textures, pipelines, computePipelines, attributes, nodes ) {
+	constructor( device, info, properties, textures, renderPipelines, computePipelines, attributes, nodes ) {
 
 
 		this.device = device;
 		this.device = device;
 		this.info = info;
 		this.info = info;
 		this.properties = properties;
 		this.properties = properties;
 		this.textures = textures;
 		this.textures = textures;
-		this.pipelines = pipelines;
+		this.renderPipelines = renderPipelines;
 		this.computePipelines = computePipelines;
 		this.computePipelines = computePipelines;
 		this.attributes = attributes;
 		this.attributes = attributes;
 		this.nodes = nodes;
 		this.nodes = nodes;
@@ -23,7 +23,7 @@ class WebGPUBindings {
 
 
 		if ( data === undefined ) {
 		if ( data === undefined ) {
 
 
-			const pipeline = this.pipelines.get( object );
+			const renderPipelines = this.renderPipelines.get( object );
 
 
 			// each material defines an array of bindings (ubos, textures, samplers etc.)
 			// each material defines an array of bindings (ubos, textures, samplers etc.)
 
 
@@ -33,7 +33,7 @@ class WebGPUBindings {
 
 
 			// setup (static) binding layout and (dynamic) binding group
 			// setup (static) binding layout and (dynamic) binding group
 
 
-			const bindLayout = pipeline.getBindGroupLayout( 0 );
+			const bindLayout = renderPipelines.pipeline.getBindGroupLayout( 0 );
 			const bindGroup = this._createBindGroup( bindings, bindLayout );
 			const bindGroup = this._createBindGroup( bindings, bindLayout );
 
 
 			data = {
 			data = {

+ 644 - 0
examples/jsm/renderers/webgpu/WebGPURenderPipeline.js

@@ -0,0 +1,644 @@
+import { GPUPrimitiveTopology, GPUIndexFormat, GPUCompareFunction, GPUFrontFace, GPUCullMode, GPUVertexFormat, GPUBlendFactor, GPUBlendOperation, BlendColorFactor, OneMinusBlendColorFactor, GPUColorWriteFlags, GPUStencilOperation, GPUInputStepMode } from './constants.js';
+import {
+	FrontSide, BackSide, DoubleSide,
+	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,
+	AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation,
+	ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstAlphaFactor, OneMinusDstAlphaFactor, DstColorFactor, OneMinusDstColorFactor, SrcAlphaSaturateFactor
+} from 'three';
+
+class WebGPURenderPipeline {
+
+	constructor( cacheKey, device, renderer, sampleCount ) {
+
+		this.cacheKey = cacheKey;
+		this.shaderAttributes = null;
+		this.usedTimes = 0;
+
+		this._device = device;
+		this._renderer = renderer;
+		this._sampleCount = sampleCount;
+
+	}
+
+	init( moduleVertex, moduleFragment, object, nodeBuilder ) {
+
+		const material = object.material;
+		const geometry = object.geometry;
+
+		// determine shader attributes
+
+		const shaderAttributes = this._parseShaderAttributes( nodeBuilder.vertexShader );
+
+		// vertex buffers
+
+		const vertexBuffers = [];
+
+		for ( const attribute of shaderAttributes ) {
+
+			const name = attribute.name;
+			const geometryAttribute = geometry.getAttribute( name );
+			const stepMode = ( geometryAttribute !== undefined && geometryAttribute.isInstancedBufferAttribute ) ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
+
+			vertexBuffers.push( {
+				arrayStride: attribute.arrayStride,
+				attributes: [ { shaderLocation: attribute.slot, offset: 0, format: attribute.format } ],
+				stepMode: stepMode
+			} );
+
+		}
+
+		this.shaderAttributes = shaderAttributes;
+
+		// blending
+
+		let alphaBlend = {};
+		let colorBlend = {};
+
+		if ( material.transparent === true && material.blending !== NoBlending ) {
+
+			alphaBlend = this._getAlphaBlend( material );
+			colorBlend = this._getColorBlend( material );
+
+		}
+
+		// stencil
+
+		let stencilFront = {};
+
+		if ( material.stencilWrite === true ) {
+
+			stencilFront = {
+				compare: this._getStencilCompare( material ),
+				failOp: this._getStencilOperation( material.stencilFail ),
+				depthFailOp: this._getStencilOperation( material.stencilZFail ),
+				passOp: this._getStencilOperation( material.stencilZPass )
+			};
+
+		}
+
+		//
+
+		const primitiveState = this._getPrimitiveState( object, material );
+		const colorWriteMask = this._getColorWriteMask( material );
+		const depthCompare = this._getDepthCompare( material );
+		const colorFormat = this._renderer.getCurrentColorFormat();
+		const depthStencilFormat = this._renderer.getCurrentDepthStencilFormat();
+
+		this.pipeline = this._device.createRenderPipeline( {
+			vertex: Object.assign( {}, moduleVertex, { buffers: vertexBuffers } ),
+			fragment: Object.assign( {}, moduleFragment, { targets: [ {
+				format: colorFormat,
+				blend: {
+					alpha: alphaBlend,
+					color: colorBlend
+				},
+				writeMask: colorWriteMask
+			} ] } ),
+			primitive: primitiveState,
+			depthStencil: {
+				format: depthStencilFormat,
+				depthWriteEnabled: material.depthWrite,
+				depthCompare: depthCompare,
+				stencilFront: stencilFront,
+				stencilBack: {}, // three.js does not provide an API to configure the back function (gl.stencilFuncSeparate() was never used)
+				stencilReadMask: material.stencilFuncMask,
+				stencilWriteMask: material.stencilWriteMask
+			},
+			multisample: {
+				count: this._sampleCount
+			}
+		} );
+
+	}
+
+	_getArrayStride( type ) {
+
+		// @TODO: This code is GLSL specific. We need to update when we switch to WGSL.
+
+		if ( type === 'float' ) return 4;
+		if ( type === 'vec2' ) return 8;
+		if ( type === 'vec3' ) return 12;
+		if ( type === 'vec4' ) return 16;
+
+		if ( type === 'int' ) return 4;
+		if ( type === 'ivec2' ) return 8;
+		if ( type === 'ivec3' ) return 12;
+		if ( type === 'ivec4' ) return 16;
+
+		if ( type === 'uint' ) return 4;
+		if ( type === 'uvec2' ) return 8;
+		if ( type === 'uvec3' ) return 12;
+		if ( type === 'uvec4' ) return 16;
+
+		console.error( 'THREE.WebGPURenderer: Shader variable type not supported yet.', type );
+
+	}
+
+	_getAlphaBlend( material ) {
+
+		const blending = material.blending;
+		const premultipliedAlpha = material.premultipliedAlpha;
+
+		let alphaBlend = undefined;
+
+		switch ( blending ) {
+
+			case NormalBlending:
+
+				if ( premultipliedAlpha === false ) {
+
+					alphaBlend = {
+						srcFactor: GPUBlendFactor.One,
+						dstFactor: GPUBlendFactor.OneMinusSrcAlpha,
+						operation: GPUBlendOperation.Add
+					};
+
+				}
+
+				break;
+
+			case AdditiveBlending:
+				// no alphaBlend settings
+				break;
+
+			case SubtractiveBlending:
+
+				if ( premultipliedAlpha === true ) {
+
+					alphaBlend = {
+						srcFactor: GPUBlendFactor.OneMinusSrcColor,
+						dstFactor: GPUBlendFactor.OneMinusSrcAlpha,
+						operation: GPUBlendOperation.Add
+					};
+
+				}
+
+				break;
+
+			case MultiplyBlending:
+				if ( premultipliedAlpha === true ) {
+
+					alphaBlend = {
+						srcFactor: GPUBlendFactor.Zero,
+						dstFactor: GPUBlendFactor.SrcAlpha,
+						operation: GPUBlendOperation.Add
+					};
+
+				}
+
+				break;
+
+			case CustomBlending:
+
+				const blendSrcAlpha = material.blendSrcAlpha;
+				const blendDstAlpha = material.blendDstAlpha;
+				const blendEquationAlpha = material.blendEquationAlpha;
+
+				if ( blendSrcAlpha !== null && blendDstAlpha !== null && blendEquationAlpha !== null ) {
+
+					alphaBlend = {
+						srcFactor: this._getBlendFactor( blendSrcAlpha ),
+						dstFactor: this._getBlendFactor( blendDstAlpha ),
+						operation: this._getBlendOperation( blendEquationAlpha )
+					};
+
+				}
+
+				break;
+
+			default:
+				console.error( 'THREE.WebGPURenderer: Blending not supported.', blending );
+
+		}
+
+		return alphaBlend;
+
+	}
+
+	_getBlendFactor( blend ) {
+
+		let blendFactor;
+
+		switch ( blend ) {
+
+			case ZeroFactor:
+				blendFactor = GPUBlendFactor.Zero;
+				break;
+
+			case OneFactor:
+				blendFactor = GPUBlendFactor.One;
+				break;
+
+			case SrcColorFactor:
+				blendFactor = GPUBlendFactor.SrcColor;
+				break;
+
+			case OneMinusSrcColorFactor:
+				blendFactor = GPUBlendFactor.OneMinusSrcColor;
+				break;
+
+			case SrcAlphaFactor:
+				blendFactor = GPUBlendFactor.SrcAlpha;
+				break;
+
+			case OneMinusSrcAlphaFactor:
+				blendFactor = GPUBlendFactor.OneMinusSrcAlpha;
+				break;
+
+			case DstColorFactor:
+				blendFactor = GPUBlendFactor.DstColor;
+				break;
+
+			case OneMinusDstColorFactor:
+				blendFactor = GPUBlendFactor.OneMinusDstColor;
+				break;
+
+			case DstAlphaFactor:
+				blendFactor = GPUBlendFactor.DstAlpha;
+				break;
+
+			case OneMinusDstAlphaFactor:
+				blendFactor = GPUBlendFactor.OneMinusDstAlpha;
+				break;
+
+			case SrcAlphaSaturateFactor:
+				blendFactor = GPUBlendFactor.SrcAlphaSaturated;
+				break;
+
+			case BlendColorFactor:
+				blendFactor = GPUBlendFactor.BlendColor;
+				break;
+
+			case OneMinusBlendColorFactor:
+				blendFactor = GPUBlendFactor.OneMinusBlendColor;
+				break;
+
+
+			default:
+				console.error( 'THREE.WebGPURenderer: Blend factor not supported.', blend );
+
+		}
+
+		return blendFactor;
+
+	}
+
+	_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 ) {
+
+		const blending = material.blending;
+		const premultipliedAlpha = material.premultipliedAlpha;
+
+		const colorBlend = {
+			srcFactor: null,
+			dstFactor: null,
+			operation: null
+		};
+
+		switch ( blending ) {
+
+			case NormalBlending:
+
+				colorBlend.srcFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.One : GPUBlendFactor.SrcAlpha;
+				colorBlend.dstFactor = GPUBlendFactor.OneMinusSrcAlpha;
+				colorBlend.operation = GPUBlendOperation.Add;
+				break;
+
+			case AdditiveBlending:
+				colorBlend.srcFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.One : GPUBlendFactor.SrcAlpha;
+				colorBlend.operation = GPUBlendOperation.Add;
+				break;
+
+			case SubtractiveBlending:
+				colorBlend.srcFactor = GPUBlendFactor.Zero;
+				colorBlend.dstFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.Zero : GPUBlendFactor.OneMinusSrcColor;
+				colorBlend.operation = GPUBlendOperation.Add;
+				break;
+
+			case MultiplyBlending:
+				colorBlend.srcFactor = GPUBlendFactor.Zero;
+				colorBlend.dstFactor = GPUBlendFactor.SrcColor;
+				colorBlend.operation = GPUBlendOperation.Add;
+				break;
+
+			case CustomBlending:
+				colorBlend.srcFactor = this._getBlendFactor( material.blendSrc );
+				colorBlend.dstFactor = this._getBlendFactor( material.blendDst );
+				colorBlend.operation = this._getBlendOperation( material.blendEquation );
+				break;
+
+			default:
+				console.error( 'THREE.WebGPURenderer: Blending not supported.', blending );
+
+		}
+
+		return colorBlend;
+
+	}
+
+	_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, material ) {
+
+		const descriptor = {};
+
+		descriptor.topology = this._getPrimitiveTopology( object );
+
+		if ( object.isLine === true && object.isLineSegments !== true ) {
+
+			const geometry = object.geometry;
+			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.CCW;
+				descriptor.cullMode = GPUCullMode.Back;
+				break;
+
+			case BackSide:
+				descriptor.frontFace = GPUFrontFace.CW;
+				descriptor.cullMode = GPUCullMode.Back;
+				break;
+
+			case DoubleSide:
+				descriptor.frontFace = GPUFrontFace.CCW;
+				descriptor.cullMode = GPUCullMode.None;
+				break;
+
+			default:
+				console.error( 'THREE.WebGPURenderer: Unknown Material.side value.', material.side );
+				break;
+
+		}
+
+		return descriptor;
+
+	}
+
+	_getPrimitiveTopology( object ) {
+
+		if ( object.isMesh ) return GPUPrimitiveTopology.TriangleList;
+		else if ( object.isPoints ) return GPUPrimitiveTopology.PointList;
+		else if ( object.isLineSegments ) return GPUPrimitiveTopology.LineList;
+		else if ( object.isLine ) return GPUPrimitiveTopology.LineStrip;
+
+	}
+
+	_getStencilCompare( material ) {
+
+		let stencilCompare;
+
+		const stencilFunc = material.stencilFunc;
+
+		switch ( stencilFunc ) {
+
+			case NeverStencilFunc:
+				stencilCompare = GPUCompareFunction.Never;
+				break;
+
+			case AlwaysStencilFunc:
+				stencilCompare = GPUCompareFunction.Always;
+				break;
+
+			case LessStencilFunc:
+				stencilCompare = GPUCompareFunction.Less;
+				break;
+
+			case LessEqualStencilFunc:
+				stencilCompare = GPUCompareFunction.LessEqual;
+				break;
+
+			case EqualStencilFunc:
+				stencilCompare = GPUCompareFunction.Equal;
+				break;
+
+			case GreaterEqualStencilFunc:
+				stencilCompare = GPUCompareFunction.GreaterEqual;
+				break;
+
+			case GreaterStencilFunc:
+				stencilCompare = GPUCompareFunction.Greater;
+				break;
+
+			case NotEqualStencilFunc:
+				stencilCompare = GPUCompareFunction.NotEqual;
+				break;
+
+			default:
+				console.error( 'THREE.WebGPURenderer: Invalid stencil function.', stencilFunc );
+
+		}
+
+		return stencilCompare;
+
+	}
+
+	_getStencilOperation( op ) {
+
+		let stencilOperation;
+
+		switch ( op ) {
+
+			case KeepStencilOp:
+				stencilOperation = GPUStencilOperation.Keep;
+				break;
+
+			case ZeroStencilOp:
+				stencilOperation = GPUStencilOperation.Zero;
+				break;
+
+			case ReplaceStencilOp:
+				stencilOperation = GPUStencilOperation.Replace;
+				break;
+
+			case InvertStencilOp:
+				stencilOperation = GPUStencilOperation.Invert;
+				break;
+
+			case IncrementStencilOp:
+				stencilOperation = GPUStencilOperation.IncrementClamp;
+				break;
+
+			case DecrementStencilOp:
+				stencilOperation = GPUStencilOperation.DecrementClamp;
+				break;
+
+			case IncrementWrapStencilOp:
+				stencilOperation = GPUStencilOperation.IncrementWrap;
+				break;
+
+			case DecrementWrapStencilOp:
+				stencilOperation = GPUStencilOperation.DecrementWrap;
+				break;
+
+			default:
+				console.error( 'THREE.WebGPURenderer: Invalid stencil operation.', stencilOperation );
+
+		}
+
+		return stencilOperation;
+
+	}
+
+	_getVertexFormat( type ) {
+
+		// @TODO: This code is GLSL specific. We need to update when we switch to WGSL.
+
+		if ( type === 'float' ) return GPUVertexFormat.Float32;
+		if ( type === 'vec2' ) return GPUVertexFormat.Float32x2;
+		if ( type === 'vec3' ) return GPUVertexFormat.Float32x3;
+		if ( type === 'vec4' ) return GPUVertexFormat.Float32x4;
+
+		if ( type === 'int' ) return GPUVertexFormat.Sint32;
+		if ( type === 'ivec2' ) return GPUVertexFormat.Sint32x2;
+		if ( type === 'ivec3' ) return GPUVertexFormat.Sint32x3;
+		if ( type === 'ivec4' ) return GPUVertexFormat.Sint32x4;
+
+		if ( type === 'uint' ) return GPUVertexFormat.Uint32;
+		if ( type === 'uvec2' ) return GPUVertexFormat.Uint32x2;
+		if ( type === 'uvec3' ) return GPUVertexFormat.Uint32x3;
+		if ( type === 'uvec4' ) return GPUVertexFormat.Uint32x4;
+
+		console.error( 'THREE.WebGPURenderer: Shader variable type not supported yet.', type );
+
+	}
+
+	_parseShaderAttributes( shader ) {
+
+		// find "layout (location = num) in type name" in vertex shader
+
+		const regex = /\s*layout\s*\(\s*location\s*=\s*(?<location>[0-9]+)\s*\)\s*in\s+(?<type>\w+)\s+(?<name>\w+)\s*;/gmi;
+		let shaderAttribute = null;
+
+		const attributes = [];
+
+		while ( shaderAttribute = regex.exec( shader ) ) {
+
+			const shaderLocation = parseInt( shaderAttribute.groups.location );
+			const arrayStride = this._getArrayStride( shaderAttribute.groups.type );
+			const vertexFormat = this._getVertexFormat( shaderAttribute.groups.type );
+
+			attributes.push( {
+				name: shaderAttribute.groups.name,
+				arrayStride: arrayStride,
+				slot: shaderLocation,
+				format: vertexFormat
+			} );
+
+		}
+
+		// the sort ensures to setup vertex buffers in the correct order
+
+		return attributes.sort( function ( a, b ) {
+
+			return a.slot - b.slot;
+
+		} );
+
+	}
+
+}
+
+export default WebGPURenderPipeline;

+ 136 - 636
examples/jsm/renderers/webgpu/WebGPURenderPipelines.js

@@ -1,13 +1,6 @@
-import { GPUPrimitiveTopology, GPUIndexFormat, GPUTextureFormat, GPUCompareFunction, GPUFrontFace, GPUCullMode, GPUVertexFormat, GPUBlendFactor, GPUBlendOperation, BlendColorFactor, OneMinusBlendColorFactor, GPUColorWriteFlags, GPUStencilOperation, GPUInputStepMode } from './constants.js';
-import {
-	FrontSide, BackSide, DoubleSide,
-	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,
-	AddEquation, SubtractEquation, ReverseSubtractEquation, MinEquation, MaxEquation,
-	ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstAlphaFactor, OneMinusDstAlphaFactor, DstColorFactor, OneMinusDstColorFactor, SrcAlphaSaturateFactor
-} from 'three';
+import WebGPURenderPipeline from './WebGPURenderPipeline.js';
+
+let _id = 0;
 
 
 class WebGPURenderPipelines {
 class WebGPURenderPipelines {
 
 
@@ -20,8 +13,8 @@ class WebGPURenderPipelines {
 		this.sampleCount = sampleCount;
 		this.sampleCount = sampleCount;
 		this.nodes = nodes;
 		this.nodes = nodes;
 
 
-		this.pipelines = new WeakMap();
-		this.shaderAttributes = new WeakMap();
+		this.pipelines = [];
+		this.objectCache = new WeakMap();
 
 
 		this.shaderModules = {
 		this.shaderModules = {
 			vertex: new Map(),
 			vertex: new Map(),
@@ -32,28 +25,17 @@ class WebGPURenderPipelines {
 
 
 	get( object ) {
 	get( object ) {
 
 
-		// @TODO: Avoid a 1:1 relationship between pipelines and objects. It's necessary
-		// to check various conditions in order to request an appropriate pipeline.
-		//
-		// - material's version and node configuration
-		// - environment map (material)
-		// - fog and environment (scene)
-		// - output encoding (renderer)
-		// - light state
-		// - clipping planes
-		//
-		// The renderer needs to manage multiple pipelines per object so
-		// GPUDevice.createRenderPipeline() is only called when no pipeline exists for the
-		// current configuration.
-
-		let pipeline = this.pipelines.get( object );
+		const device = this.device;
+		const properties = this.properties;
 
 
-		if ( pipeline === undefined ) {
+		const material = object.material;
 
 
-			const device = this.device;
-			const properties = this.properties;
+		const materialProperties = properties.get( material );
+		const objectProperties = properties.get( object );
 
 
-			const material = object.material;
+		let currentPipeline = objectProperties.currentPipeline;
+
+		if ( this._needsUpdate( object ) ) {
 
 
 			// get shader
 			// get shader
 
 
@@ -71,7 +53,8 @@ class WebGPURenderPipelines {
 
 
 				moduleVertex = {
 				moduleVertex = {
 					module: device.createShaderModule( { code: byteCodeVertex } ),
 					module: device.createShaderModule( { code: byteCodeVertex } ),
-					entryPoint: 'main'
+					entryPoint: 'main',
+					id: _id ++
 				};
 				};
 
 
 				this.shaderModules.vertex.set( nodeBuilder.vertexShader, moduleVertex );
 				this.shaderModules.vertex.set( nodeBuilder.vertexShader, moduleVertex );
@@ -86,128 +69,58 @@ class WebGPURenderPipelines {
 
 
 				moduleFragment = {
 				moduleFragment = {
 					module: device.createShaderModule( { code: byteCodeFragment } ),
 					module: device.createShaderModule( { code: byteCodeFragment } ),
-					entryPoint: 'main'
+					entryPoint: 'main',
+					id: _id ++
 				};
 				};
 
 
 				this.shaderModules.fragment.set( nodeBuilder.fragmentShader, moduleFragment );
 				this.shaderModules.fragment.set( nodeBuilder.fragmentShader, moduleFragment );
 
 
 			}
 			}
 
 
-			// dispose material
-
-			const materialProperties = properties.get( material );
-
-			if ( materialProperties.disposeCallback === undefined ) {
-
-				const disposeCallback = onMaterialDispose.bind( this );
-				materialProperties.disposeCallback = disposeCallback;
-
-				material.addEventListener( 'dispose', disposeCallback );
-
-			}
-
-			// determine shader attributes
+			// determine render pipeline
 
 
-			const shaderAttributes = this._parseShaderAttributes( nodeBuilder.vertexShader );
+			currentPipeline = this._acquirePipeline( moduleVertex, moduleFragment, object, nodeBuilder );
+			objectProperties.currentPipeline = currentPipeline;
 
 
-			// vertex buffers
+			// keep track of all pipelines which are used by a material
 
 
-			const vertexBuffers = [];
-			const geometry = object.geometry;
+			let materialPipelines = materialProperties.pipelines;
 
 
-			for ( const attribute of shaderAttributes ) {
+			if ( materialPipelines === undefined ) {
 
 
-				const name = attribute.name;
-				const geometryAttribute = geometry.getAttribute( name );
-				const stepMode = ( geometryAttribute !== undefined && geometryAttribute.isInstancedBufferAttribute ) ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
-
-				vertexBuffers.push( {
-					arrayStride: attribute.arrayStride,
-					attributes: [ { shaderLocation: attribute.slot, offset: 0, format: attribute.format } ],
-					stepMode: stepMode
-				} );
+				materialPipelines = new Set();
+				materialProperties.pipelines = materialPipelines;
 
 
 			}
 			}
 
 
-			//
-
-			let alphaBlend = {};
-			let colorBlend = {};
+			if ( materialPipelines.has( currentPipeline ) === false ) {
 
 
-			if ( material.transparent === true && material.blending !== NoBlending ) {
-
-				alphaBlend = this._getAlphaBlend( material );
-				colorBlend = this._getColorBlend( material );
+				materialPipelines.add( currentPipeline );
+				currentPipeline.usedTimes ++;
 
 
 			}
 			}
 
 
-			//
+			// dispose
 
 
-			let stencilFront = {};
+			if ( materialProperties.disposeCallback === undefined ) {
 
 
-			if ( material.stencilWrite === true ) {
+				const disposeCallback = onMaterialDispose.bind( this );
+				materialProperties.disposeCallback = disposeCallback;
 
 
-				stencilFront = {
-					compare: this._getStencilCompare( material ),
-					failOp: this._getStencilOperation( material.stencilFail ),
-					depthFailOp: this._getStencilOperation( material.stencilZFail ),
-					passOp: this._getStencilOperation( material.stencilZPass )
-				};
+				material.addEventListener( 'dispose', disposeCallback );
 
 
 			}
 			}
 
 
-			// pipeline
-
-			const primitiveState = this._getPrimitiveState( object, material );
-			const colorWriteMask = this._getColorWriteMask( material );
-			const depthCompare = this._getDepthCompare( material );
-			const colorFormat = this._getColorFormat( this.renderer );
-			const depthStencilFormat = this._getDepthStencilFormat( this.renderer );
-
-			pipeline = device.createRenderPipeline( {
-				vertex: Object.assign( {}, moduleVertex, { buffers: vertexBuffers } ),
-				fragment: Object.assign( {}, moduleFragment, { targets: [ {
-					format: colorFormat,
-					blend: {
-						alpha: alphaBlend,
-						color: colorBlend
-					},
-					writeMask: colorWriteMask
-				} ] } ),
-				primitive: primitiveState,
-				depthStencil: {
-					format: depthStencilFormat,
-					depthWriteEnabled: material.depthWrite,
-					depthCompare: depthCompare,
-					stencilFront: stencilFront,
-					stencilBack: {}, // three.js does not provide an API to configure the back function (gl.stencilFuncSeparate() was never used)
-					stencilReadMask: material.stencilFuncMask,
-					stencilWriteMask: material.stencilWriteMask
-				},
-				multisample: {
-					count: this.sampleCount
-				}
-			} );
-
-			this.pipelines.set( object, pipeline );
-			this.shaderAttributes.set( pipeline, shaderAttributes );
-
 		}
 		}
 
 
-		return pipeline;
-
-	}
-
-	getShaderAttributes( pipeline ) {
-
-		return this.shaderAttributes.get( pipeline );
+		return currentPipeline;
 
 
 	}
 	}
 
 
 	dispose() {
 	dispose() {
 
 
-		this.pipelines = new WeakMap();
-		this.shaderAttributes = new WeakMap();
+		this.pipelines = [];
+		this.objectCache = new WeakMap();
 		this.shaderModules = {
 		this.shaderModules = {
 			vertex: new Map(),
 			vertex: new Map(),
 			fragment: new Map()
 			fragment: new Map()
@@ -215,587 +128,174 @@ class WebGPURenderPipelines {
 
 
 	}
 	}
 
 
-	_getArrayStride( type ) {
-
-		// @TODO: This code is GLSL specific. We need to update when we switch to WGSL.
-
-		if ( type === 'float' ) return 4;
-		if ( type === 'vec2' ) return 8;
-		if ( type === 'vec3' ) return 12;
-		if ( type === 'vec4' ) return 16;
-
-		if ( type === 'int' ) return 4;
-		if ( type === 'ivec2' ) return 8;
-		if ( type === 'ivec3' ) return 12;
-		if ( type === 'ivec4' ) return 16;
-
-		if ( type === 'uint' ) return 4;
-		if ( type === 'uvec2' ) return 8;
-		if ( type === 'uvec3' ) return 12;
-		if ( type === 'uvec4' ) return 16;
-
-		console.error( 'THREE.WebGPURenderer: Shader variable type not supported yet.', type );
-
-	}
-
-	_getAlphaBlend( material ) {
-
-		const blending = material.blending;
-		const premultipliedAlpha = material.premultipliedAlpha;
-
-		let alphaBlend = undefined;
-
-		switch ( blending ) {
-
-			case NormalBlending:
-
-				if ( premultipliedAlpha === false ) {
-
-					alphaBlend = {
-						srcFactor: GPUBlendFactor.One,
-						dstFactor: GPUBlendFactor.OneMinusSrcAlpha,
-						operation: GPUBlendOperation.Add
-					};
-
-				}
-
-				break;
-
-			case AdditiveBlending:
-				// no alphaBlend settings
-				break;
-
-			case SubtractiveBlending:
-
-				if ( premultipliedAlpha === true ) {
-
-					alphaBlend = {
-						srcFactor: GPUBlendFactor.OneMinusSrcColor,
-						dstFactor: GPUBlendFactor.OneMinusSrcAlpha,
-						operation: GPUBlendOperation.Add
-					};
-
-				}
-
-				break;
-
-			case MultiplyBlending:
-				if ( premultipliedAlpha === true ) {
-
-					alphaBlend = {
-						srcFactor: GPUBlendFactor.Zero,
-						dstFactor: GPUBlendFactor.SrcAlpha,
-						operation: GPUBlendOperation.Add
-					};
-
-				}
-
-				break;
-
-			case CustomBlending:
-
-				const blendSrcAlpha = material.blendSrcAlpha;
-				const blendDstAlpha = material.blendDstAlpha;
-				const blendEquationAlpha = material.blendEquationAlpha;
-
-				if ( blendSrcAlpha !== null && blendDstAlpha !== null && blendEquationAlpha !== null ) {
-
-					alphaBlend = {
-						srcFactor: this._getBlendFactor( blendSrcAlpha ),
-						dstFactor: this._getBlendFactor( blendDstAlpha ),
-						operation: this._getBlendOperation( blendEquationAlpha )
-					};
-
-				}
-
-				break;
-
-			default:
-				console.error( 'THREE.WebGPURenderer: Blending not supported.', blending );
-
-		}
-
-		return alphaBlend;
-
-	}
-
-	_getBlendFactor( blend ) {
-
-		let blendFactor;
-
-		switch ( blend ) {
-
-			case ZeroFactor:
-				blendFactor = GPUBlendFactor.Zero;
-				break;
-
-			case OneFactor:
-				blendFactor = GPUBlendFactor.One;
-				break;
-
-			case SrcColorFactor:
-				blendFactor = GPUBlendFactor.SrcColor;
-				break;
-
-			case OneMinusSrcColorFactor:
-				blendFactor = GPUBlendFactor.OneMinusSrcColor;
-				break;
-
-			case SrcAlphaFactor:
-				blendFactor = GPUBlendFactor.SrcAlpha;
-				break;
-
-			case OneMinusSrcAlphaFactor:
-				blendFactor = GPUBlendFactor.OneMinusSrcAlpha;
-				break;
-
-			case DstColorFactor:
-				blendFactor = GPUBlendFactor.DstColor;
-				break;
-
-			case OneMinusDstColorFactor:
-				blendFactor = GPUBlendFactor.OneMinusDstColor;
-				break;
-
-			case DstAlphaFactor:
-				blendFactor = GPUBlendFactor.DstAlpha;
-				break;
-
-			case OneMinusDstAlphaFactor:
-				blendFactor = GPUBlendFactor.OneMinusDstAlpha;
-				break;
-
-			case SrcAlphaSaturateFactor:
-				blendFactor = GPUBlendFactor.SrcAlphaSaturated;
-				break;
-
-			case BlendColorFactor:
-				blendFactor = GPUBlendFactor.BlendColor;
-				break;
-
-			case OneMinusBlendColorFactor:
-				blendFactor = GPUBlendFactor.OneMinusBlendColor;
-				break;
-
-
-			default:
-				console.error( 'THREE.WebGPURenderer: Blend factor not supported.', blend );
-
-		}
-
-		return blendFactor;
-
-	}
-
-	_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;
+	_acquirePipeline( moduleVertex, moduleFragment, object, nodeBuilder ) {
 
 
-	}
-
-	_getColorBlend( material ) {
+		let pipeline;
+		const pipelines = this.pipelines;
 
 
-		const blending = material.blending;
-		const premultipliedAlpha = material.premultipliedAlpha;
+		// check for existing pipeline
 
 
-		const colorBlend = {
-			srcFactor: null,
-			dstFactor: null,
-			operation: null
-		};
+		const cacheKey = this._computeCacheKey( moduleVertex, moduleFragment, object );
 
 
-		switch ( blending ) {
+		for ( let i = 0, il = pipelines.length; i < il; i ++ ) {
 
 
-			case NormalBlending:
+			const preexistingPipeline = pipelines[ i ];
 
 
-				colorBlend.srcFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.One : GPUBlendFactor.SrcAlpha;
-				colorBlend.dstFactor = GPUBlendFactor.OneMinusSrcAlpha;
-				colorBlend.operation = GPUBlendOperation.Add;
-				break;
+			if ( preexistingPipeline.cacheKey === cacheKey ) {
 
 
-			case AdditiveBlending:
-				colorBlend.srcFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.One : GPUBlendFactor.SrcAlpha;
-				colorBlend.operation = GPUBlendOperation.Add;
+				pipeline = preexistingPipeline;
 				break;
 				break;
 
 
-			case SubtractiveBlending:
-				colorBlend.srcFactor = GPUBlendFactor.Zero;
-				colorBlend.dstFactor = ( premultipliedAlpha === true ) ? GPUBlendFactor.Zero : GPUBlendFactor.OneMinusSrcColor;
-				colorBlend.operation = GPUBlendOperation.Add;
-				break;
-
-			case MultiplyBlending:
-				colorBlend.srcFactor = GPUBlendFactor.Zero;
-				colorBlend.dstFactor = GPUBlendFactor.SrcColor;
-				colorBlend.operation = GPUBlendOperation.Add;
-				break;
-
-			case CustomBlending:
-				colorBlend.srcFactor = this._getBlendFactor( material.blendSrc );
-				colorBlend.dstFactor = this._getBlendFactor( material.blendDst );
-				colorBlend.operation = this._getBlendOperation( material.blendEquation );
-				break;
-
-			default:
-				console.error( 'THREE.WebGPURenderer: Blending not supported.', blending );
+			}
 
 
 		}
 		}
 
 
-		return colorBlend;
-
-	}
-
-	_getColorFormat( renderer ) {
-
-		let format;
-
-		const renderTarget = renderer.getRenderTarget();
-
-		if ( renderTarget !== null ) {
-
-			const renderTargetProperties = this.properties.get( renderTarget );
-			format = renderTargetProperties.colorTextureFormat;
-
-		} else {
+		if ( pipeline === undefined ) {
 
 
-			format = GPUTextureFormat.BRGA8Unorm; // default swap chain format
+			pipeline = new WebGPURenderPipeline( cacheKey, this.device, this.renderer, this.sampleCount );
+			pipeline.init( moduleVertex, moduleFragment, object, nodeBuilder );
+			pipelines.push( pipeline );
 
 
 		}
 		}
 
 
-		return format;
-
-	}
-
-	_getColorWriteMask( material ) {
-
-		return ( material.colorWrite === true ) ? GPUColorWriteFlags.All : GPUColorWriteFlags.None;
+		return pipeline;
 
 
 	}
 	}
 
 
-	_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;
+	_computeCacheKey( moduleVertex, moduleFragment, object ) {
 
 
-				case GreaterEqualDepth:
-					depthCompare = GPUCompareFunction.GreaterEqual;
-					break;
+		const material = object.material;
+		const renderer = this.renderer;
 
 
-				case GreaterDepth:
-					depthCompare = GPUCompareFunction.Greater;
-					break;
+		const parameters = [
+			moduleVertex.id, moduleFragment.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.sampleCount,
+			renderer.getCurrentEncoding(), renderer.getCurrentColorFormat(), renderer.getCurrentDepthStencilFormat()
+		];
 
 
-				case NotEqualDepth:
-					depthCompare = GPUCompareFunction.NotEqual;
-					break;
-
-				default:
-					console.error( 'THREE.WebGPURenderer: Invalid depth function.', depthFunc );
-
-			}
-
-		}
-
-		return depthCompare;
+		return parameters.join();
 
 
 	}
 	}
 
 
-	_getDepthStencilFormat( renderer ) {
-
-		let format;
-
-		const renderTarget = renderer.getRenderTarget();
+	_releasePipeline( pipeline ) {
 
 
-		if ( renderTarget !== null ) {
+		if ( -- pipeline.usedTimes === 0 ) {
 
 
-			const renderTargetProperties = this.properties.get( renderTarget );
-			format = renderTargetProperties.depthTextureFormat;
+			const pipelines = this.pipelines;
 
 
-		} else {
-
-			format = GPUTextureFormat.Depth24PlusStencil8;
+			const i = pipelines.indexOf( pipeline );
+			pipelines[ i ] = pipelines[ pipelines.length - 1 ];
+			pipelines.pop();
 
 
 		}
 		}
 
 
-		return format;
-
 	}
 	}
 
 
-	_getPrimitiveState( object, material ) {
+	_needsUpdate( object ) {
 
 
-		const descriptor = {};
+		let cache = this.objectCache.get( object );
 
 
-		descriptor.topology = this._getPrimitiveTopology( object );
+		if ( cache === undefined ) {
 
 
-		if ( object.isLine === true && object.isLineSegments !== true ) {
-
-			const geometry = object.geometry;
-			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
+			cache = {};
+			this.objectCache.set( object, cache );
 
 
 		}
 		}
 
 
-		switch ( material.side ) {
-
-			case FrontSide:
-				descriptor.frontFace = GPUFrontFace.CCW;
-				descriptor.cullMode = GPUCullMode.Back;
-				break;
-
-			case BackSide:
-				descriptor.frontFace = GPUFrontFace.CW;
-				descriptor.cullMode = GPUCullMode.Back;
-				break;
-
-			case DoubleSide:
-				descriptor.frontFace = GPUFrontFace.CCW;
-				descriptor.cullMode = GPUCullMode.None;
-				break;
-
-			default:
-				console.error( 'THREE.WebGPURenderer: Unknown Material.side value.', material.side );
-				break;
+		const material = object.material;
+
+		let needsUpdate = false;
+
+		// 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;
 
 
 		}
 		}
 
 
-		return descriptor;
-
-	}
-
-	_getPrimitiveTopology( object ) {
-
-		if ( object.isMesh ) return GPUPrimitiveTopology.TriangleList;
-		else if ( object.isPoints ) return GPUPrimitiveTopology.PointList;
-		else if ( object.isLineSegments ) return GPUPrimitiveTopology.LineList;
-		else if ( object.isLine ) return GPUPrimitiveTopology.LineStrip;
-
-	}
-
-	_getStencilCompare( material ) {
-
-		let stencilCompare;
-
-		const stencilFunc = material.stencilFunc;
+		// check renderer state
 
 
-		switch ( stencilFunc ) {
+		const renderer = this.renderer;
 
 
-			case NeverStencilFunc:
-				stencilCompare = GPUCompareFunction.Never;
-				break;
+		const encoding = renderer.getCurrentEncoding();
+		const colorFormat = renderer.getCurrentColorFormat();
+		const depthStencilFormat = renderer.getCurrentDepthStencilFormat();
 
 
-			case AlwaysStencilFunc:
-				stencilCompare = GPUCompareFunction.Always;
-				break;
+		if ( cache.sampleCount !== this.sampleCount || cache.encoding !== encoding ||
+			cache.colorFormat !== colorFormat || cache.depthStencilFormat !== depthStencilFormat ) {
 
 
-			case LessStencilFunc:
-				stencilCompare = GPUCompareFunction.Less;
-				break;
+			cache.sampleCount = this.sampleCount;
+			cache.encoding = encoding;
+			cache.colorFormat = colorFormat;
+			cache.depthStencilFormat = depthStencilFormat;
 
 
-			case LessEqualStencilFunc:
-				stencilCompare = GPUCompareFunction.LessEqual;
-				break;
-
-			case EqualStencilFunc:
-				stencilCompare = GPUCompareFunction.Equal;
-				break;
-
-			case GreaterEqualStencilFunc:
-				stencilCompare = GPUCompareFunction.GreaterEqual;
-				break;
-
-			case GreaterStencilFunc:
-				stencilCompare = GPUCompareFunction.Greater;
-				break;
-
-			case NotEqualStencilFunc:
-				stencilCompare = GPUCompareFunction.NotEqual;
-				break;
-
-			default:
-				console.error( 'THREE.WebGPURenderer: Invalid stencil function.', stencilFunc );
+			needsUpdate = true;
 
 
 		}
 		}
 
 
-		return stencilCompare;
+		return needsUpdate;
 
 
 	}
 	}
 
 
-	_getStencilOperation( op ) {
-
-		let stencilOperation;
-
-		switch ( op ) {
-
-			case KeepStencilOp:
-				stencilOperation = GPUStencilOperation.Keep;
-				break;
-
-			case ZeroStencilOp:
-				stencilOperation = GPUStencilOperation.Zero;
-				break;
-
-			case ReplaceStencilOp:
-				stencilOperation = GPUStencilOperation.Replace;
-				break;
-
-			case InvertStencilOp:
-				stencilOperation = GPUStencilOperation.Invert;
-				break;
-
-			case IncrementStencilOp:
-				stencilOperation = GPUStencilOperation.IncrementClamp;
-				break;
-
-			case DecrementStencilOp:
-				stencilOperation = GPUStencilOperation.DecrementClamp;
-				break;
-
-			case IncrementWrapStencilOp:
-				stencilOperation = GPUStencilOperation.IncrementWrap;
-				break;
-
-			case DecrementWrapStencilOp:
-				stencilOperation = GPUStencilOperation.DecrementWrap;
-				break;
-
-			default:
-				console.error( 'THREE.WebGPURenderer: Invalid stencil operation.', stencilOperation );
-
-		}
-
-		return stencilOperation;
-
-	}
-
-	_getVertexFormat( type ) {
-
-		// @TODO: This code is GLSL specific. We need to update when we switch to WGSL.
-
-		if ( type === 'float' ) return GPUVertexFormat.Float32;
-		if ( type === 'vec2' ) return GPUVertexFormat.Float32x2;
-		if ( type === 'vec3' ) return GPUVertexFormat.Float32x3;
-		if ( type === 'vec4' ) return GPUVertexFormat.Float32x4;
-
-		if ( type === 'int' ) return GPUVertexFormat.Sint32;
-		if ( type === 'ivec2' ) return GPUVertexFormat.Sint32x2;
-		if ( type === 'ivec3' ) return GPUVertexFormat.Sint32x3;
-		if ( type === 'ivec4' ) return GPUVertexFormat.Sint32x4;
-
-		if ( type === 'uint' ) return GPUVertexFormat.Uint32;
-		if ( type === 'uvec2' ) return GPUVertexFormat.Uint32x2;
-		if ( type === 'uvec3' ) return GPUVertexFormat.Uint32x3;
-		if ( type === 'uvec4' ) return GPUVertexFormat.Uint32x4;
-
-		console.error( 'THREE.WebGPURenderer: Shader variable type not supported yet.', type );
-
-	}
-
-	_parseShaderAttributes( shader ) {
+}
 
 
-		// find "layout (location = num) in type name" in vertex shader
+function onMaterialDispose( event ) {
 
 
-		const regex = /\s*layout\s*\(\s*location\s*=\s*(?<location>[0-9]+)\s*\)\s*in\s+(?<type>\w+)\s+(?<name>\w+)\s*;/gmi;
-		let shaderAttribute = null;
+	const properties = this.properties;
 
 
-		const attributes = [];
+	const material = event.target;
+	const materialProperties = properties.get( material );
 
 
-		while ( shaderAttribute = regex.exec( shader ) ) {
+	material.removeEventListener( 'dispose', materialProperties.disposeCallback );
 
 
-			const shaderLocation = parseInt( shaderAttribute.groups.location );
-			const arrayStride = this._getArrayStride( shaderAttribute.groups.type );
-			const vertexFormat = this._getVertexFormat( shaderAttribute.groups.type );
+	properties.remove( material );
 
 
-			attributes.push( {
-				name: shaderAttribute.groups.name,
-				arrayStride: arrayStride,
-				slot: shaderLocation,
-				format: vertexFormat
-			} );
+	// remove references to pipelines
 
 
-		}
+	const pipelines = properties.get( material ).pipelines;
 
 
-		// the sort ensures to setup vertex buffers in the correct order
+	if ( pipelines !== undefined ) {
 
 
-		return attributes.sort( function ( a, b ) {
+		pipelines.forEach( function ( pipeline ) {
 
 
-			return a.slot - b.slot;
+			this._releasePipeline( pipeline );
 
 
 		} );
 		} );
 
 
 	}
 	}
 
 
-}
-
-function onMaterialDispose( event ) {
-
-	const properties = this.properties;
-
-	const material = event.target;
-	const materialProperties = properties.get( material );
-
-	material.removeEventListener( 'dispose', materialProperties.disposeCallback );
-
-	properties.remove( material );
-
-	// @TODO: still needed remove nodes, bindings and pipeline
+	// @TODO: still need remove nodes and bindings
 
 
 }
 }
 
 

+ 57 - 6
examples/jsm/renderers/webgpu/WebGPURenderer.js

@@ -14,7 +14,7 @@ import WebGPUNodes from './nodes/WebGPUNodes.js';
 
 
 import glslang from '../../libs/glslang.js';
 import glslang from '../../libs/glslang.js';
 
 
-import { Frustum, Matrix4, Vector3, Color } from 'three';
+import { Frustum, Matrix4, Vector3, Color, LinearEncoding } from 'three';
 
 
 console.info( 'THREE.WebGPURenderer: Modified Matrix4.makePerspective() and Matrix4.makeOrtographic() to work with WebGPU, see https://github.com/mrdoob/three.js/issues/20276.' );
 console.info( 'THREE.WebGPURenderer: Modified Matrix4.makePerspective() and Matrix4.makeOrtographic() to work with WebGPU, see https://github.com/mrdoob/three.js/issues/20276.' );
 
 
@@ -76,6 +76,8 @@ class WebGPURenderer {
 		this.autoClearDepth = true;
 		this.autoClearDepth = true;
 		this.autoClearStencil = true;
 		this.autoClearStencil = true;
 
 
+		this.outputEncoding = LinearEncoding;
+
 		this.sortObjects = true;
 		this.sortObjects = true;
 
 
 		// internals
 		// internals
@@ -463,6 +465,55 @@ class WebGPURenderer {
 
 
 	}
 	}
 
 
+	getCurrentEncoding() {
+
+		const renderTarget = this.getRenderTarget();
+		return ( renderTarget !== null ) ? renderTarget.texture.encoding : this.outputEncoding;
+
+	}
+
+	getCurrentColorFormat() {
+
+		let format;
+
+		const renderTarget = this.getRenderTarget();
+
+		if ( renderTarget !== null ) {
+
+			const renderTargetProperties = this._properties.get( renderTarget );
+			format = renderTargetProperties.colorTextureFormat;
+
+		} else {
+
+			format = GPUTextureFormat.BRGA8Unorm; // default swap chain format
+
+		}
+
+		return format;
+
+	}
+
+	getCurrentDepthStencilFormat() {
+
+		let format;
+
+		const renderTarget = this.getRenderTarget();
+
+		if ( renderTarget !== null ) {
+
+			const renderTargetProperties = this._properties.get( renderTarget );
+			format = renderTargetProperties.depthTextureFormat;
+
+		} else {
+
+			format = GPUTextureFormat.Depth24PlusStencil8;
+
+		}
+
+		return format;
+
+	}
+
 	getClearColor( target ) {
 	getClearColor( target ) {
 
 
 		return target.copy( this._clearColor );
 		return target.copy( this._clearColor );
@@ -757,8 +808,8 @@ class WebGPURenderer {
 
 
 		// pipeline
 		// pipeline
 
 
-		const pipeline = this._renderPipelines.get( object );
-		passEncoder.setPipeline( pipeline );
+		const renderPipeline = this._renderPipelines.get( object );
+		passEncoder.setPipeline( renderPipeline.pipeline );
 
 
 		// bind group
 		// bind group
 
 
@@ -780,7 +831,7 @@ class WebGPURenderer {
 
 
 		// vertex buffers
 		// vertex buffers
 
 
-		this._setupVertexBuffers( geometry.attributes, passEncoder, pipeline );
+		this._setupVertexBuffers( geometry.attributes, passEncoder, renderPipeline );
 
 
 		// draw
 		// draw
 
 
@@ -818,9 +869,9 @@ class WebGPURenderer {
 
 
 	}
 	}
 
 
-	_setupVertexBuffers( geometryAttributes, encoder, pipeline ) {
+	_setupVertexBuffers( geometryAttributes, encoder, renderPipeline ) {
 
 
-		const shaderAttributes = this._renderPipelines.getShaderAttributes( pipeline );
+		const shaderAttributes = renderPipeline.shaderAttributes;
 
 
 		for ( const shaderAttribute of shaderAttributes ) {
 		for ( const shaderAttribute of shaderAttributes ) {