Skip to content

Commit

Permalink
WebGPURenderer: Read/Write Only Storage Buffer Creation (#28435)
Browse files Browse the repository at this point in the history
* add writable

* sketch

* changes

* Working solution

* run linter

* syntax fix

* fixed formatting

* add writable

* sketch

* changes

* Working solution

* run linter

* syntax fix

* fixed formatting

* re-add storageImmutable to compute_geometry

* switch .readOnly to .access in StorageBufferNode and NodeStorageBuffer

* Made storage buffer code consistent with storage texture code and applied storageReadOnly to examples as reference in appropriate locations

* Fixed unusued import in webgpu_compute_audio

* Remove unused import

* Removed un-needed references to 'storageReadOnlyBuffer' in WGSLNodeBuilder, added considerations for hypothetical write-only buffer access mode mentioned in PR conversation, added readOnly and writeOnly commands for creating a storageObject

* fixed missing access

* remove comments

* fix missing import

* revision

* cleanup

---------

Co-authored-by: sunag <sunagbrasil@gmail.com>
  • Loading branch information
cmhhelgeson and sunag authored Jun 25, 2024
1 parent eac2954 commit 0d5e6d6
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 28 deletions.
2 changes: 1 addition & 1 deletion examples/jsm/nodes/Nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export { default as SceneNode, backgroundBlurriness, backgroundIntensity } from
export { default as StorageBufferNode, storage, storageObject } from './accessors/StorageBufferNode.js';
export * from './accessors/TangentNode.js';
export { default as TextureNode, texture, textureLoad, /*textureLevel,*/ sampler } from './accessors/TextureNode.js';
export { default as StorageTextureNode, storageTexture, textureStore, storageTextureReadOnly, storageTextureReadWrite } from './accessors/StorageTextureNode.js';
export { default as StorageTextureNode, storageTexture, textureStore } from './accessors/StorageTextureNode.js';
export { default as Texture3DNode, texture3D } from './accessors/Texture3DNode.js';
export * from './accessors/UVNode.js';
export { default as UserDataNode, userData } from './accessors/UserDataNode.js';
Expand Down
24 changes: 23 additions & 1 deletion examples/jsm/nodes/accessors/StorageBufferNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { addNodeClass } from '../core/Node.js';
import { nodeObject } from '../shadernode/ShaderNode.js';
import { varying } from '../core/VaryingNode.js';
import { storageElement } from '../utils/StorageArrayElementNode.js';
import { GPUBufferBindingType } from '../../renderers/webgpu/utils/WebGPUConstants.js';

class StorageBufferNode extends BufferNode {

Expand All @@ -13,6 +14,8 @@ class StorageBufferNode extends BufferNode {

this.isStorageBufferNode = true;

this.access = GPUBufferBindingType.Storage;

this.bufferObject = false;
this.bufferCount = bufferCount;

Expand Down Expand Up @@ -76,9 +79,27 @@ class StorageBufferNode extends BufferNode {

}

setAccess( value ) {

this.access = value;

return this;

}

toReadOnly() {

return this.setAccess( GPUBufferBindingType.ReadOnlyStorage );

}

generate( builder ) {

if ( builder.isAvailable( 'storageBuffer' ) ) return super.generate( builder );
if ( builder.isAvailable( 'storageBuffer' ) ) {

return super.generate( builder );

}

const nodeType = this.getNodeType( builder );

Expand All @@ -102,6 +123,7 @@ class StorageBufferNode extends BufferNode {

export default StorageBufferNode;

// Read-Write Storage
export const storage = ( value, type, count ) => nodeObject( new StorageBufferNode( value, type, count ) );
export const storageObject = ( value, type, count ) => nodeObject( new StorageBufferNode( value, type, count ).setBufferObject( true ) );

Expand Down
15 changes: 12 additions & 3 deletions examples/jsm/nodes/accessors/StorageTextureNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@ class StorageTextureNode extends TextureNode {

}

toReadOnly() {

return this.setAccess( GPUStorageTextureAccess.ReadOnly );

}

toWriteOnly() {

return this.setAccess( GPUStorageTextureAccess.WriteOnly );

}

generateStore( builder ) {

const properties = builder.getNodeProperties( this );
Expand All @@ -79,9 +91,6 @@ export default StorageTextureNode;

export const storageTexture = nodeProxy( StorageTextureNode );

export const storageTextureReadOnly = ( value, uvNode, storeNode ) => storageTexture( value, uvNode, storeNode ).setAccess( 'read-only' );
export const storageTextureReadWrite = ( value, uvNode, storeNode ) => storageTexture( value, uvNode, storeNode ).setAccess( 'read-write' );

export const textureStore = ( value, uvNode, storeNode ) => {

const node = storageTexture( value, uvNode, storeNode );
Expand Down
1 change: 1 addition & 0 deletions examples/jsm/renderers/common/Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,7 @@ class Renderer {
const pipelines = this._pipelines;
const bindings = this._bindings;
const nodes = this._nodes;

const computeList = Array.isArray( computeNodes ) ? computeNodes : [ computeNodes ];

if ( computeList[ 0 ] === undefined || computeList[ 0 ].isComputeNode !== true ) {
Expand Down
3 changes: 3 additions & 0 deletions examples/jsm/renderers/common/nodes/NodeStorageBuffer.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import StorageBuffer from '../StorageBuffer.js';
import { GPUBufferBindingType } from '../../webgpu/utils/WebGPUConstants.js';

let _id = 0;

Expand All @@ -9,8 +10,10 @@ class NodeStorageBuffer extends StorageBuffer {
super( 'StorageBuffer_' + _id ++, nodeUniform ? nodeUniform.value : null );

this.nodeUniform = nodeUniform;
this.access = nodeUniform ? nodeUniform.access : GPUBufferBindingType.Storage
this.groupNode = groupNode;


}

get buffer() {
Expand Down
38 changes: 24 additions & 14 deletions examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ import { NodeBuilder, CodeNode } from '../../../nodes/Nodes.js';
import { getFormat } from '../utils/WebGPUTextureUtils.js';

import WGSLNodeParser from './WGSLNodeParser.js';
import { GPUStorageTextureAccess } from '../utils/WebGPUConstants.js';

import { GPUBufferBindingType, GPUStorageTextureAccess } from '../utils/WebGPUConstants.js';

// GPUShaderStage is not defined in browsers not supporting WebGPU
const GPUShaderStage = self.GPUShaderStage;
Expand All @@ -26,6 +25,7 @@ const gpuShaderStageLib = {
};

const supports = {
instance: true,
swizzleAssign: false,
storageBuffer: true
};
Expand Down Expand Up @@ -411,30 +411,38 @@ class WGSLNodeBuilder extends NodeBuilder {

switch ( node.access ) {

case GPUStorageTextureAccess.ReadOnly: {
case GPUStorageTextureAccess.ReadOnly:

return 'read';

}

case GPUStorageTextureAccess.WriteOnly: {
case GPUStorageTextureAccess.WriteOnly:

return 'write';

}

default: {
default:

return 'read_write';

}

}

} else {

// @TODO: Account for future read-only storage buffer pull request
return 'read_write';
switch ( node.access ) {

case GPUBufferBindingType.Storage:

return 'read_write';


case GPUBufferBindingType.ReadOnlyStorage:

return 'read';

default:

return 'write';

}

}

Expand Down Expand Up @@ -714,6 +722,7 @@ ${ flowData.code }
snippet += this.getStructMembers( struct );
snippet += '\n}';


snippets.push( snippet );

snippets.push( `\nvar<private> output : ${ name };\n\n` );
Expand Down Expand Up @@ -880,7 +889,7 @@ ${ flowData.code }

const bufferCountSnippet = bufferCount > 0 ? ', ' + bufferCount : '';
const bufferSnippet = `\t${ uniform.name } : array< ${ bufferType }${ bufferCountSnippet } >\n`;
const bufferAccessMode = bufferNode.isStorageBufferNode ? 'storage,read_write' : 'uniform';
const bufferAccessMode = bufferNode.isStorageBufferNode ? `storage, ${ this.getStorageAccess( bufferNode ) }` : 'uniform';

bufferSnippets.push( this._getWGSLStructBinding( 'NodeBuffer_' + bufferNode.id, bufferSnippet, bufferAccessMode, uniformIndexes.binding ++, uniformIndexes.group ) );

Expand Down Expand Up @@ -997,6 +1006,7 @@ ${ flowData.code }

stageData.flow = flow;


}

if ( this.material !== null ) {
Expand Down
4 changes: 2 additions & 2 deletions examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
GPUTextureAspect, GPUTextureViewDimension, GPUBufferBindingType, GPUTextureSampleType
GPUTextureAspect, GPUTextureViewDimension, GPUTextureSampleType
} from './WebGPUConstants.js';
import { FloatType, IntType, UnsignedIntType } from 'three';

Expand Down Expand Up @@ -33,7 +33,7 @@ class WebGPUBindingUtils {

if ( binding.isStorageBuffer ) {

buffer.type = GPUBufferBindingType.Storage;
buffer.type = binding.access;

}

Expand Down
6 changes: 2 additions & 4 deletions examples/webgpu_compute_audio.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<script type="module">

import * as THREE from 'three';
import { tslFn, uniform, storage, storageObject, instanceIndex, float, texture, viewportTopLeft, color } from 'three/nodes';
import { tslFn, uniform, storage, instanceIndex, float, texture, viewportTopLeft, color } from 'three/nodes';

import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

Expand Down Expand Up @@ -112,11 +112,9 @@

const waveStorageNode = storage( waveGPUBuffer, 'float', waveBuffer.length );


// read-only buffer

const waveNode = storageObject( new StorageInstancedBufferAttribute( waveBuffer, 1 ), 'float', waveBuffer.length );

const waveNode = storage( new StorageInstancedBufferAttribute( waveBuffer, 1 ), 'float', waveBuffer.length ).toReadOnly();

// params

Expand Down
4 changes: 2 additions & 2 deletions examples/webgpu_compute_geometry.html
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@

const computeFn = tslFn( () => {

const positionAttribute = storage( positionBaseAttribute, 'vec3', positionBaseAttribute.count );
const normalAttribute = storage( normalBaseAttribute, 'vec3', normalBaseAttribute.count );
const positionAttribute = storage( positionBaseAttribute, 'vec3', positionBaseAttribute.count ).toReadOnly();
const normalAttribute = storage( normalBaseAttribute, 'vec3', normalBaseAttribute.count ).toReadOnly();

const positionStorageAttribute = storage( positionStorageBufferAttribute, 'vec4', positionStorageBufferAttribute.count );
const normalStorageAttribute = storage( normalStorageBufferAttribute, 'vec4', normalStorageBufferAttribute.count );
Expand Down
2 changes: 1 addition & 1 deletion examples/webgpu_compute_texture.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
const g = v.add( Math.PI ).sin();
const b = v.add( Math.PI ).sub( 0.5 ).sin();

textureStore( storageTexture, indexUV, vec4( r, g, b, 1 ) );
textureStore( storageTexture, indexUV, vec4( r, g, b, 1 ) ).toWriteOnly();

} );

Expand Down

0 comments on commit 0d5e6d6

Please sign in to comment.