import BufferNode from './BufferNode.js';
import { bufferAttribute } from './BufferAttributeNode.js';
import { nodeObject, varying } from '../tsl/TSLBase.js';
import { storageElement } from '../utils/StorageArrayElementNode.js';
import { NodeAccess } from '../core/constants.js';
import { getTypeFromLength } from '../core/NodeUtils.js';
/** @module StorageBufferNode **/
/**
* This node is used in context of compute shaders and allows to define a
* storage buffer for data. A typical workflow is to create instances of
* this node with the convenience functions `attributeArray()` or `instancedArray()`,
* setup up a compute shader that writes into the buffers and then convert
* the storage buffers to attribute nodes for rendering.
*
* ```js
* const positionBuffer = instancedArray( particleCount, 'vec3' ); // the storage buffer node
*
* const computeInit = Fn( () => { // the compute shader
*
* const position = positionBuffer.element( instanceIndex );
*
* // compute position data
*
* position.x = 1;
* position.y = 1;
* position.z = 1;
*
* } )().compute( particleCount );
*
* const particleMaterial = new THREE.SpriteNodeMaterial();
* particleMaterial.positionNode = positionBuffer.toAttribute();
*
* renderer.computeAsync( computeInit );
*
* ```
*
* @augments BufferNode
*/
class StorageBufferNode extends BufferNode {
static get type() {
return 'StorageBufferNode';
}
/**
* Constructs a new storage buffer node.
*
* @param {StorageBufferAttribute|StorageInstancedBufferAttribute|BufferAttribute} value - The buffer data.
* @param {(String|Struct)?} [bufferType=null] - The buffer type (e.g. `'vec3'`).
* @param {Number} [bufferCount=0] - The buffer count.
*/
constructor( value, bufferType = null, bufferCount = 0 ) {
let nodeType, structTypeNode = null;
if ( bufferType && bufferType.isStruct ) {
nodeType = 'struct';
structTypeNode = bufferType.layout;
} else if ( bufferType === null && ( value.isStorageBufferAttribute || value.isStorageInstancedBufferAttribute ) ) {
nodeType = getTypeFromLength( value.itemSize );
bufferCount = value.count;
} else {
nodeType = bufferType;
}
super( value, nodeType, bufferCount );
/**
* This flag can be used for type testing.
*
* @type {Boolean}
* @readonly
* @default true
*/
this.isStorageBufferNode = true;
/**
* The buffer struct type.
*
* @type {structTypeNode?}
* @default null
*/
this.structTypeNode = structTypeNode;
/**
* The access type of the texture node.
*
* @type {String}
* @default 'readWrite'
*/
this.access = NodeAccess.READ_WRITE;
/**
* Whether the node is atomic or not.
*
* @type {Boolean}
* @default false
*/
this.isAtomic = false;
/**
* Whether the node represents a PBO or not.
* Only relevant for WebGL.
*
* @type {Boolean}
* @default false
*/
this.isPBO = false;
/**
* A reference to the internal buffer attribute node.
*
* @type {BufferAttributeNode?}
* @default null
*/
this._attribute = null;
/**
* A reference to the internal varying node.
*
* @type {VaryingNode?}
* @default null
*/
this._varying = null;
/**
* `StorageBufferNode` sets this property to `true` by default.
*
* @type {Boolean}
* @default true
*/
this.global = true;
if ( value.isStorageBufferAttribute !== true && value.isStorageInstancedBufferAttribute !== true ) {
// TODO: Improve it, possibly adding a new property to the BufferAttribute to identify it as a storage buffer read-only attribute in Renderer
if ( value.isInstancedBufferAttribute ) value.isStorageInstancedBufferAttribute = true;
else value.isStorageBufferAttribute = true;
}
}
/**
* This method is overwritten since the buffer data might be shared
* and thus the hash should be shared as well.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {String} The hash.
*/
getHash( builder ) {
if ( this.bufferCount === 0 ) {
let bufferData = builder.globalCache.getData( this.value );
if ( bufferData === undefined ) {
bufferData = {
node: this
};
builder.globalCache.setData( this.value, bufferData );
}
return bufferData.node.uuid;
}
return this.uuid;
}
/**
* Overwrites the default implementation to return a fixed value `'indirectStorageBuffer'` or `'storageBuffer'`.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {String} The input type.
*/
getInputType( /*builder*/ ) {
return this.value.isIndirectStorageBufferAttribute ? 'indirectStorageBuffer' : 'storageBuffer';
}
/**
* Enables element access with the given index node.
*
* @param {IndexNode} indexNode - The index node.
* @return {StorageArrayElementNode} A node representing the element access.
*/
element( indexNode ) {
return storageElement( this, indexNode );
}
/**
* Defines whether this node is a PBO or not. Only relevant for WebGL.
*
* @param {Boolean} value - The value so set.
* @return {StorageBufferNode} A reference to this node.
*/
setPBO( value ) {
this.isPBO = value;
return this;
}
/**
* Returns the `isPBO` value.
*
* @return {Boolean} Whether the node represents a PBO or not.
*/
getPBO() {
return this.isPBO;
}
/**
* Defines the node access.
*
* @param {String} value - The node access.
* @return {StorageBufferNode} A reference to this node.
*/
setAccess( value ) {
this.access = value;
return this;
}
/**
* Convenience method for configuring a read-only node access.
*
* @return {StorageBufferNode} A reference to this node.
*/
toReadOnly() {
return this.setAccess( NodeAccess.READ_ONLY );
}
/**
* Defines whether the node is atomic or not.
*
* @param {Boolean} value - The atomic flag.
* @return {StorageBufferNode} A reference to this node.
*/
setAtomic( value ) {
this.isAtomic = value;
return this;
}
/**
* Convenience method for making this node atomic.
*
* @return {StorageBufferNode} A reference to this node.
*/
toAtomic() {
return this.setAtomic( true );
}
/**
* Returns attribute data for this storage buffer node.
*
* @return {{attribute: BufferAttributeNode, varying: VaryingNode}} The attribute data.
*/
getAttributeData() {
if ( this._attribute === null ) {
this._attribute = bufferAttribute( this.value );
this._varying = varying( this._attribute );
}
return {
attribute: this._attribute,
varying: this._varying
};
}
/**
* This method is overwritten since the node type from the availability of storage buffers
* and the attribute data.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {String} The node type.
*/
getNodeType( builder ) {
if ( this.structTypeNode !== null ) {
return this.structTypeNode.getNodeType( builder );
}
if ( builder.isAvailable( 'storageBuffer' ) || builder.isAvailable( 'indirectStorageBuffer' ) ) {
return super.getNodeType( builder );
}
const { attribute } = this.getAttributeData();
return attribute.getNodeType( builder );
}
/**
* Generates the code snippet of the storage buffer node.
*
* @param {NodeBuilder} builder - The current node builder.
* @return {String} The generated code snippet.
*/
generate( builder ) {
if ( this.structTypeNode !== null ) this.structTypeNode.build( builder );
if ( builder.isAvailable( 'storageBuffer' ) || builder.isAvailable( 'indirectStorageBuffer' ) ) {
return super.generate( builder );
}
const { attribute, varying } = this.getAttributeData();
const output = varying.build( builder );
builder.registerTransform( output, attribute );
return output;
}
}
export default StorageBufferNode;
/**
* TSL function for creating a storage buffer node.
*
* @function
* @param {StorageBufferAttribute|StorageInstancedBufferAttribute|BufferAttribute} value - The buffer data.
* @param {(String|Struct)?} [type=null] - The buffer type (e.g. `'vec3'`).
* @param {Number} [count=0] - The buffer count.
* @returns {StorageBufferNode}
*/
export const storage = ( value, type = null, count = 0 ) => nodeObject( new StorageBufferNode( value, type, count ) );
/**
* @function
* @deprecated since r171. Use `storage().setPBO( true )` instead.
*
* @param {StorageBufferAttribute|StorageInstancedBufferAttribute|BufferAttribute} value - The buffer data.
* @param {String?} type - The buffer type (e.g. `'vec3'`).
* @param {Number} count - The buffer count.
* @returns {StorageBufferNode}
*/
export const storageObject = ( value, type, count ) => { // @deprecated, r171
console.warn( 'THREE.TSL: "storageObject()" is deprecated. Use "storage().setPBO( true )" instead.' );
return storage( value, type, count ).setPBO( true );
};