|
|
@@ -7,37 +7,6 @@ import { FloatType, IntType, UnsignedIntType } from '../../../constants.js';
|
|
|
import { NodeAccess } from '../../../nodes/core/constants.js';
|
|
|
import { isTypedArray, error } from '../../../utils.js';
|
|
|
|
|
|
-/**
|
|
|
-* Class representing a WebGPU bind group layout.
|
|
|
-*
|
|
|
-*/
|
|
|
-class BindGroupLayout {
|
|
|
-
|
|
|
- /**
|
|
|
- * Constructs a new BindGroupLayout.
|
|
|
- *
|
|
|
- * @param {GPUBindGroupLayout} layoutGPU - A GPU Bind Group Layout.
|
|
|
- */
|
|
|
- constructor( layoutGPU ) {
|
|
|
-
|
|
|
- /**
|
|
|
- * The current GPUBindGroupLayout
|
|
|
- *
|
|
|
- * @type {GPUBindGroupLayout}
|
|
|
- */
|
|
|
- this.layoutGPU = layoutGPU;
|
|
|
-
|
|
|
- /**
|
|
|
- * The number of bind groups that use the current GPUBindGroupLayout
|
|
|
- *
|
|
|
- * @type {number}
|
|
|
- */
|
|
|
- this.usedTimes = 0;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* A WebGPU backend utility module for managing bindings.
|
|
|
*
|
|
|
@@ -65,11 +34,11 @@ class WebGPUBindingUtils {
|
|
|
this.backend = backend;
|
|
|
|
|
|
/**
|
|
|
- * A cache that maps combinations of layout entries to existing bind group layouts.
|
|
|
+ * A cache for managing bind group layouts.
|
|
|
*
|
|
|
- * @type {Map<string, BindGroupLayout>}
|
|
|
+ * @type {WeakMap<Array<Binding>,GPUBindGroupLayout>}
|
|
|
*/
|
|
|
- this.bindGroupLayoutCache = new Map();
|
|
|
+ this.bindGroupLayoutCache = new WeakMap();
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -84,33 +53,185 @@ class WebGPUBindingUtils {
|
|
|
const backend = this.backend;
|
|
|
const device = backend.device;
|
|
|
|
|
|
- const bindingsData = backend.get( bindGroup );
|
|
|
+ const entries = [];
|
|
|
|
|
|
- // When current bind group has already been assigned a layout
|
|
|
- if ( bindingsData.bindGroupLayout !== undefined ) {
|
|
|
+ let index = 0;
|
|
|
|
|
|
- return bindingsData.bindGroupLayout.layoutGPU;
|
|
|
+ for ( const binding of bindGroup.bindings ) {
|
|
|
|
|
|
- }
|
|
|
+ const bindingGPU = {
|
|
|
+ binding: index ++,
|
|
|
+ visibility: binding.visibility
|
|
|
+ };
|
|
|
|
|
|
- const entries = this._createBindingsLayoutEntries( bindGroup );
|
|
|
+ if ( binding.isUniformBuffer || binding.isStorageBuffer ) {
|
|
|
|
|
|
- const bindGroupLayoutKey = JSON.stringify( entries );
|
|
|
+ const buffer = {}; // GPUBufferBindingLayout
|
|
|
|
|
|
- let bindGroupLayout = this.bindGroupLayoutCache.get( bindGroupLayoutKey );
|
|
|
+ if ( binding.isStorageBuffer ) {
|
|
|
|
|
|
- if ( bindGroupLayout === undefined ) {
|
|
|
+ if ( binding.visibility & GPUShaderStage.COMPUTE ) {
|
|
|
|
|
|
- bindGroupLayout = new BindGroupLayout( device.createBindGroupLayout( { entries } ) );
|
|
|
- this.bindGroupLayoutCache.set( bindGroupLayoutKey, bindGroupLayout );
|
|
|
+ // compute
|
|
|
|
|
|
- }
|
|
|
+ if ( binding.access === NodeAccess.READ_WRITE || binding.access === NodeAccess.WRITE_ONLY ) {
|
|
|
+
|
|
|
+ buffer.type = GPUBufferBindingType.Storage;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ buffer.type = GPUBufferBindingType.ReadOnlyStorage;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ buffer.type = GPUBufferBindingType.ReadOnlyStorage;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ bindingGPU.buffer = buffer;
|
|
|
+
|
|
|
+ } else if ( binding.isSampledTexture && binding.store ) {
|
|
|
+
|
|
|
+ const storageTexture = {}; // GPUStorageTextureBindingLayout
|
|
|
+ storageTexture.format = this.backend.get( binding.texture ).texture.format;
|
|
|
+
|
|
|
+ const access = binding.access;
|
|
|
+
|
|
|
+ if ( access === NodeAccess.READ_WRITE ) {
|
|
|
+
|
|
|
+ storageTexture.access = GPUStorageTextureAccess.ReadWrite;
|
|
|
+
|
|
|
+ } else if ( access === NodeAccess.WRITE_ONLY ) {
|
|
|
+
|
|
|
+ storageTexture.access = GPUStorageTextureAccess.WriteOnly;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ storageTexture.access = GPUStorageTextureAccess.ReadOnly;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( binding.texture.isArrayTexture ) {
|
|
|
+
|
|
|
+ storageTexture.viewDimension = GPUTextureViewDimension.TwoDArray;
|
|
|
+
|
|
|
+ } else if ( binding.texture.is3DTexture ) {
|
|
|
+
|
|
|
+ storageTexture.viewDimension = GPUTextureViewDimension.ThreeD;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ bindingGPU.storageTexture = storageTexture;
|
|
|
+
|
|
|
+ } else if ( binding.isSampledTexture ) {
|
|
|
+
|
|
|
+ const texture = {}; // GPUTextureBindingLayout
|
|
|
+
|
|
|
+ const { primarySamples } = backend.utils.getTextureSampleData( binding.texture );
|
|
|
+
|
|
|
+ if ( primarySamples > 1 ) {
|
|
|
+
|
|
|
+ texture.multisampled = true;
|
|
|
+
|
|
|
+ if ( ! binding.texture.isDepthTexture ) {
|
|
|
+
|
|
|
+ texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( binding.texture.isDepthTexture ) {
|
|
|
+
|
|
|
+ if ( backend.compatibilityMode && binding.texture.compareFunction === null ) {
|
|
|
+
|
|
|
+ texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ texture.sampleType = GPUTextureSampleType.Depth;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if ( binding.texture.isDataTexture || binding.texture.isDataArrayTexture || binding.texture.isData3DTexture ) {
|
|
|
+
|
|
|
+ const type = binding.texture.type;
|
|
|
+
|
|
|
+ if ( type === IntType ) {
|
|
|
+
|
|
|
+ texture.sampleType = GPUTextureSampleType.SInt;
|
|
|
+
|
|
|
+ } else if ( type === UnsignedIntType ) {
|
|
|
+
|
|
|
+ texture.sampleType = GPUTextureSampleType.UInt;
|
|
|
+
|
|
|
+ } else if ( type === FloatType ) {
|
|
|
+
|
|
|
+ if ( this.backend.hasFeature( 'float32-filterable' ) ) {
|
|
|
|
|
|
- bindingsData.layout = bindGroupLayout;
|
|
|
- bindingsData.layout.usedTimes ++;
|
|
|
- bindingsData.layoutKey = bindGroupLayoutKey;
|
|
|
+ texture.sampleType = GPUTextureSampleType.Float;
|
|
|
|
|
|
- return bindGroupLayout.layoutGPU;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ if ( binding.isSampledCubeTexture ) {
|
|
|
+
|
|
|
+ texture.viewDimension = GPUTextureViewDimension.Cube;
|
|
|
+
|
|
|
+ } else if ( binding.texture.isArrayTexture || binding.texture.isDataArrayTexture || binding.texture.isCompressedArrayTexture ) {
|
|
|
+
|
|
|
+ texture.viewDimension = GPUTextureViewDimension.TwoDArray;
|
|
|
+
|
|
|
+ } else if ( binding.isSampledTexture3D ) {
|
|
|
+
|
|
|
+ texture.viewDimension = GPUTextureViewDimension.ThreeD;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ bindingGPU.texture = texture;
|
|
|
+
|
|
|
+ } else if ( binding.isSampler ) {
|
|
|
+
|
|
|
+ const sampler = {}; // GPUSamplerBindingLayout
|
|
|
+
|
|
|
+ if ( binding.texture.isDepthTexture ) {
|
|
|
+
|
|
|
+ if ( binding.texture.compareFunction !== null ) {
|
|
|
+
|
|
|
+ sampler.type = GPUSamplerBindingType.Comparison;
|
|
|
+
|
|
|
+ } else if ( backend.compatibilityMode ) {
|
|
|
+
|
|
|
+ sampler.type = GPUSamplerBindingType.NonFiltering;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ bindingGPU.sampler = sampler;
|
|
|
+
|
|
|
+ } else {
|
|
|
+
|
|
|
+ error( `WebGPUBindingUtils: Unsupported binding "${ binding }".` );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ entries.push( bindingGPU );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return device.createBindGroupLayout( { entries } );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -124,12 +245,19 @@ class WebGPUBindingUtils {
|
|
|
*/
|
|
|
createBindings( bindGroup, bindings, cacheIndex, version = 0 ) {
|
|
|
|
|
|
- const { backend } = this;
|
|
|
+ const { backend, bindGroupLayoutCache } = this;
|
|
|
const bindingsData = backend.get( bindGroup );
|
|
|
|
|
|
// setup (static) binding layout and (dynamic) binding group
|
|
|
|
|
|
- const bindLayoutGPU = this.createBindingsLayout( bindGroup );
|
|
|
+ let bindLayoutGPU = bindGroupLayoutCache.get( bindGroup.bindingsReference );
|
|
|
+
|
|
|
+ if ( bindLayoutGPU === undefined ) {
|
|
|
+
|
|
|
+ bindLayoutGPU = this.createBindingsLayout( bindGroup );
|
|
|
+ bindGroupLayoutCache.set( bindGroup.bindingsReference, bindLayoutGPU );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
let bindGroupGPU;
|
|
|
|
|
|
@@ -164,6 +292,7 @@ class WebGPUBindingUtils {
|
|
|
}
|
|
|
|
|
|
bindingsData.group = bindGroupGPU;
|
|
|
+ bindingsData.layout = bindLayoutGPU;
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -225,10 +354,10 @@ class WebGPUBindingUtils {
|
|
|
* Creates a GPU bind group for the camera index.
|
|
|
*
|
|
|
* @param {Uint32Array} data - The index data.
|
|
|
- * @param {GPUBindGroupLayout} layoutGPU - The GPU bind group layout.
|
|
|
+ * @param {GPUBindGroupLayout} layout - The GPU bind group layout.
|
|
|
* @return {GPUBindGroup} The GPU bind group.
|
|
|
*/
|
|
|
- createBindGroupIndex( data, layoutGPU ) {
|
|
|
+ createBindGroupIndex( data, layout ) {
|
|
|
|
|
|
const backend = this.backend;
|
|
|
const device = backend.device;
|
|
|
@@ -248,7 +377,7 @@ class WebGPUBindingUtils {
|
|
|
|
|
|
return device.createBindGroup( {
|
|
|
label: 'bindGroupCameraIndex_' + index,
|
|
|
- layout: layoutGPU,
|
|
|
+ layout,
|
|
|
entries
|
|
|
} );
|
|
|
|
|
|
@@ -409,242 +538,6 @@ class WebGPUBindingUtils {
|
|
|
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Creates a bind group layout entry for the given binding.
|
|
|
- *
|
|
|
- * @param {Binding} binding - The binding.
|
|
|
- * @param {number} index - The index of the bind group layout entry in the bind group layout.
|
|
|
- * @return {GPUBindGroupLayoutEntry} The bind group layout entry.
|
|
|
- */
|
|
|
- _createBindingLayoutEntry( binding, index ) {
|
|
|
-
|
|
|
- const backend = this.backend;
|
|
|
-
|
|
|
- const bindingGPU = {
|
|
|
- binding: index,
|
|
|
- visibility: binding.visibility
|
|
|
- };
|
|
|
-
|
|
|
- if ( binding.isUniformBuffer || binding.isStorageBuffer ) {
|
|
|
-
|
|
|
- const buffer = {}; // GPUBufferBindingLayout
|
|
|
-
|
|
|
- if ( binding.isStorageBuffer ) {
|
|
|
-
|
|
|
- if ( binding.visibility & GPUShaderStage.COMPUTE ) {
|
|
|
-
|
|
|
- // compute
|
|
|
-
|
|
|
- if ( binding.access === NodeAccess.READ_WRITE || binding.access === NodeAccess.WRITE_ONLY ) {
|
|
|
-
|
|
|
- buffer.type = GPUBufferBindingType.Storage;
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- buffer.type = GPUBufferBindingType.ReadOnlyStorage;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- buffer.type = GPUBufferBindingType.ReadOnlyStorage;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- bindingGPU.buffer = buffer;
|
|
|
-
|
|
|
- } else if ( binding.isSampledTexture && binding.store ) {
|
|
|
-
|
|
|
- const storageTexture = {}; // GPUStorageTextureBindingLayout
|
|
|
- storageTexture.format = this.backend.get( binding.texture ).texture.format;
|
|
|
-
|
|
|
- const access = binding.access;
|
|
|
-
|
|
|
- if ( access === NodeAccess.READ_WRITE ) {
|
|
|
-
|
|
|
- storageTexture.access = GPUStorageTextureAccess.ReadWrite;
|
|
|
-
|
|
|
- } else if ( access === NodeAccess.WRITE_ONLY ) {
|
|
|
-
|
|
|
- storageTexture.access = GPUStorageTextureAccess.WriteOnly;
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- storageTexture.access = GPUStorageTextureAccess.ReadOnly;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- if ( binding.texture.isArrayTexture ) {
|
|
|
-
|
|
|
- storageTexture.viewDimension = GPUTextureViewDimension.TwoDArray;
|
|
|
-
|
|
|
- } else if ( binding.texture.is3DTexture ) {
|
|
|
-
|
|
|
- storageTexture.viewDimension = GPUTextureViewDimension.ThreeD;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- bindingGPU.storageTexture = storageTexture;
|
|
|
-
|
|
|
- } else if ( binding.isSampledTexture ) {
|
|
|
-
|
|
|
- const texture = {}; // GPUTextureBindingLayout
|
|
|
-
|
|
|
- const { primarySamples } = backend.utils.getTextureSampleData( binding.texture );
|
|
|
-
|
|
|
- if ( primarySamples > 1 ) {
|
|
|
-
|
|
|
- texture.multisampled = true;
|
|
|
-
|
|
|
- if ( ! binding.texture.isDepthTexture ) {
|
|
|
-
|
|
|
- texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- if ( binding.texture.isDepthTexture ) {
|
|
|
-
|
|
|
- if ( backend.compatibilityMode && binding.texture.compareFunction === null ) {
|
|
|
-
|
|
|
- texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- texture.sampleType = GPUTextureSampleType.Depth;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- } else if ( binding.texture.isDataTexture || binding.texture.isDataArrayTexture || binding.texture.isData3DTexture ) {
|
|
|
-
|
|
|
- const type = binding.texture.type;
|
|
|
-
|
|
|
- if ( type === IntType ) {
|
|
|
-
|
|
|
- texture.sampleType = GPUTextureSampleType.SInt;
|
|
|
-
|
|
|
- } else if ( type === UnsignedIntType ) {
|
|
|
-
|
|
|
- texture.sampleType = GPUTextureSampleType.UInt;
|
|
|
-
|
|
|
- } else if ( type === FloatType ) {
|
|
|
-
|
|
|
- if ( this.backend.hasFeature( 'float32-filterable' ) ) {
|
|
|
-
|
|
|
- texture.sampleType = GPUTextureSampleType.Float;
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- texture.sampleType = GPUTextureSampleType.UnfilterableFloat;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- if ( binding.isSampledCubeTexture ) {
|
|
|
-
|
|
|
- texture.viewDimension = GPUTextureViewDimension.Cube;
|
|
|
-
|
|
|
- } else if ( binding.texture.isArrayTexture || binding.texture.isDataArrayTexture || binding.texture.isCompressedArrayTexture ) {
|
|
|
-
|
|
|
- texture.viewDimension = GPUTextureViewDimension.TwoDArray;
|
|
|
-
|
|
|
- } else if ( binding.isSampledTexture3D ) {
|
|
|
-
|
|
|
- texture.viewDimension = GPUTextureViewDimension.ThreeD;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- bindingGPU.texture = texture;
|
|
|
-
|
|
|
- } else if ( binding.isSampler ) {
|
|
|
-
|
|
|
- const sampler = {}; // GPUSamplerBindingLayout
|
|
|
-
|
|
|
- if ( binding.texture.isDepthTexture ) {
|
|
|
-
|
|
|
- if ( binding.texture.compareFunction !== null ) {
|
|
|
-
|
|
|
- sampler.type = GPUSamplerBindingType.Comparison;
|
|
|
-
|
|
|
- } else if ( backend.compatibilityMode ) {
|
|
|
-
|
|
|
- sampler.type = GPUSamplerBindingType.NonFiltering;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- bindingGPU.sampler = sampler;
|
|
|
-
|
|
|
- } else {
|
|
|
-
|
|
|
- error( `WebGPUBindingUtils: Unsupported binding "${ binding }".` );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return bindingGPU;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Creates a GPU bind group layout entries for the given bind group.
|
|
|
- *
|
|
|
- * @param {BindGroup} bindGroup - The bind group.
|
|
|
- * @return {Array<GPUBindGroupLayoutEntry>} The GPU bind group layout entries.
|
|
|
- */
|
|
|
- _createBindingsLayoutEntries( bindGroup ) {
|
|
|
-
|
|
|
- const entries = [];
|
|
|
- let index = 0;
|
|
|
-
|
|
|
- for ( const binding of bindGroup.bindings ) {
|
|
|
-
|
|
|
- entries.push( this._createBindingLayoutEntry( binding, index ) );
|
|
|
- index ++;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- return entries;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Delete the data associated with a bind group.
|
|
|
- *
|
|
|
- * @param {BindGroup} bindGroup - The bind group.
|
|
|
- */
|
|
|
- deleteBindGroupData( bindGroup ) {
|
|
|
-
|
|
|
- const { backend } = this;
|
|
|
-
|
|
|
- const bindingsData = backend.get( bindGroup );
|
|
|
-
|
|
|
- // Decrement the layout reference's usedTimes attribute
|
|
|
- bindingsData.layout.usedTimes --;
|
|
|
-
|
|
|
- // Remove reference from map
|
|
|
- if ( bindingsData.layout.usedTimes === 0 ) {
|
|
|
-
|
|
|
- this.bindGroupLayoutCache.delete( bindingsData.layoutKey );
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- bindingsData.layout = null;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- dispose() {
|
|
|
-
|
|
|
- this.bindGroupLayoutCache.clear();
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
}
|
|
|
|
|
|
export default WebGPUBindingUtils;
|