StorageBufferNode.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. import BufferNode from './BufferNode.js';
  2. import { bufferAttribute } from './BufferAttributeNode.js';
  3. import { nodeObject, varying } from '../tsl/TSLBase.js';
  4. import { storageElement } from '../utils/StorageArrayElementNode.js';
  5. import { NodeAccess } from '../core/constants.js';
  6. import { getTypeFromLength } from '../core/NodeUtils.js';
  7. /**
  8. * This node is used in context of compute shaders and allows to define a
  9. * storage buffer for data. A typical workflow is to create instances of
  10. * this node with the convenience functions `attributeArray()` or `instancedArray()`,
  11. * setup up a compute shader that writes into the buffers and then convert
  12. * the storage buffers to attribute nodes for rendering.
  13. *
  14. * ```js
  15. * const positionBuffer = instancedArray( particleCount, 'vec3' ); // the storage buffer node
  16. *
  17. * const computeInit = Fn( () => { // the compute shader
  18. *
  19. * const position = positionBuffer.element( instanceIndex );
  20. *
  21. * // compute position data
  22. *
  23. * position.x = 1;
  24. * position.y = 1;
  25. * position.z = 1;
  26. *
  27. * } )().compute( particleCount );
  28. *
  29. * const particleMaterial = new THREE.SpriteNodeMaterial();
  30. * particleMaterial.positionNode = positionBuffer.toAttribute();
  31. *
  32. * renderer.computeAsync( computeInit );
  33. *
  34. * ```
  35. *
  36. * @augments BufferNode
  37. */
  38. class StorageBufferNode extends BufferNode {
  39. static get type() {
  40. return 'StorageBufferNode';
  41. }
  42. /**
  43. * Constructs a new storage buffer node.
  44. *
  45. * @param {StorageBufferAttribute|StorageInstancedBufferAttribute|BufferAttribute} value - The buffer data.
  46. * @param {(String|Struct)?} [bufferType=null] - The buffer type (e.g. `'vec3'`).
  47. * @param {Number} [bufferCount=0] - The buffer count.
  48. */
  49. constructor( value, bufferType = null, bufferCount = 0 ) {
  50. let nodeType, structTypeNode = null;
  51. if ( bufferType && bufferType.isStruct ) {
  52. nodeType = 'struct';
  53. structTypeNode = bufferType.layout;
  54. } else if ( bufferType === null && ( value.isStorageBufferAttribute || value.isStorageInstancedBufferAttribute ) ) {
  55. nodeType = getTypeFromLength( value.itemSize );
  56. bufferCount = value.count;
  57. } else {
  58. nodeType = bufferType;
  59. }
  60. super( value, nodeType, bufferCount );
  61. /**
  62. * This flag can be used for type testing.
  63. *
  64. * @type {Boolean}
  65. * @readonly
  66. * @default true
  67. */
  68. this.isStorageBufferNode = true;
  69. /**
  70. * The buffer struct type.
  71. *
  72. * @type {structTypeNode?}
  73. * @default null
  74. */
  75. this.structTypeNode = structTypeNode;
  76. /**
  77. * The access type of the texture node.
  78. *
  79. * @type {String}
  80. * @default 'readWrite'
  81. */
  82. this.access = NodeAccess.READ_WRITE;
  83. /**
  84. * Whether the node is atomic or not.
  85. *
  86. * @type {Boolean}
  87. * @default false
  88. */
  89. this.isAtomic = false;
  90. /**
  91. * Whether the node represents a PBO or not.
  92. * Only relevant for WebGL.
  93. *
  94. * @type {Boolean}
  95. * @default false
  96. */
  97. this.isPBO = false;
  98. /**
  99. * A reference to the internal buffer attribute node.
  100. *
  101. * @type {BufferAttributeNode?}
  102. * @default null
  103. */
  104. this._attribute = null;
  105. /**
  106. * A reference to the internal varying node.
  107. *
  108. * @type {VaryingNode?}
  109. * @default null
  110. */
  111. this._varying = null;
  112. /**
  113. * `StorageBufferNode` sets this property to `true` by default.
  114. *
  115. * @type {Boolean}
  116. * @default true
  117. */
  118. this.global = true;
  119. if ( value.isStorageBufferAttribute !== true && value.isStorageInstancedBufferAttribute !== true ) {
  120. // TODO: Improve it, possibly adding a new property to the BufferAttribute to identify it as a storage buffer read-only attribute in Renderer
  121. if ( value.isInstancedBufferAttribute ) value.isStorageInstancedBufferAttribute = true;
  122. else value.isStorageBufferAttribute = true;
  123. }
  124. }
  125. /**
  126. * This method is overwritten since the buffer data might be shared
  127. * and thus the hash should be shared as well.
  128. *
  129. * @param {NodeBuilder} builder - The current node builder.
  130. * @return {String} The hash.
  131. */
  132. getHash( builder ) {
  133. if ( this.bufferCount === 0 ) {
  134. let bufferData = builder.globalCache.getData( this.value );
  135. if ( bufferData === undefined ) {
  136. bufferData = {
  137. node: this
  138. };
  139. builder.globalCache.setData( this.value, bufferData );
  140. }
  141. return bufferData.node.uuid;
  142. }
  143. return this.uuid;
  144. }
  145. /**
  146. * Overwrites the default implementation to return a fixed value `'indirectStorageBuffer'` or `'storageBuffer'`.
  147. *
  148. * @param {NodeBuilder} builder - The current node builder.
  149. * @return {String} The input type.
  150. */
  151. getInputType( /*builder*/ ) {
  152. return this.value.isIndirectStorageBufferAttribute ? 'indirectStorageBuffer' : 'storageBuffer';
  153. }
  154. /**
  155. * Enables element access with the given index node.
  156. *
  157. * @param {IndexNode} indexNode - The index node.
  158. * @return {StorageArrayElementNode} A node representing the element access.
  159. */
  160. element( indexNode ) {
  161. return storageElement( this, indexNode );
  162. }
  163. /**
  164. * Defines whether this node is a PBO or not. Only relevant for WebGL.
  165. *
  166. * @param {Boolean} value - The value so set.
  167. * @return {StorageBufferNode} A reference to this node.
  168. */
  169. setPBO( value ) {
  170. this.isPBO = value;
  171. return this;
  172. }
  173. /**
  174. * Returns the `isPBO` value.
  175. *
  176. * @return {Boolean} Whether the node represents a PBO or not.
  177. */
  178. getPBO() {
  179. return this.isPBO;
  180. }
  181. /**
  182. * Defines the node access.
  183. *
  184. * @param {String} value - The node access.
  185. * @return {StorageBufferNode} A reference to this node.
  186. */
  187. setAccess( value ) {
  188. this.access = value;
  189. return this;
  190. }
  191. /**
  192. * Convenience method for configuring a read-only node access.
  193. *
  194. * @return {StorageBufferNode} A reference to this node.
  195. */
  196. toReadOnly() {
  197. return this.setAccess( NodeAccess.READ_ONLY );
  198. }
  199. /**
  200. * Defines whether the node is atomic or not.
  201. *
  202. * @param {Boolean} value - The atomic flag.
  203. * @return {StorageBufferNode} A reference to this node.
  204. */
  205. setAtomic( value ) {
  206. this.isAtomic = value;
  207. return this;
  208. }
  209. /**
  210. * Convenience method for making this node atomic.
  211. *
  212. * @return {StorageBufferNode} A reference to this node.
  213. */
  214. toAtomic() {
  215. return this.setAtomic( true );
  216. }
  217. /**
  218. * Returns attribute data for this storage buffer node.
  219. *
  220. * @return {{attribute: BufferAttributeNode, varying: VaryingNode}} The attribute data.
  221. */
  222. getAttributeData() {
  223. if ( this._attribute === null ) {
  224. this._attribute = bufferAttribute( this.value );
  225. this._varying = varying( this._attribute );
  226. }
  227. return {
  228. attribute: this._attribute,
  229. varying: this._varying
  230. };
  231. }
  232. /**
  233. * This method is overwritten since the node type from the availability of storage buffers
  234. * and the attribute data.
  235. *
  236. * @param {NodeBuilder} builder - The current node builder.
  237. * @return {String} The node type.
  238. */
  239. getNodeType( builder ) {
  240. if ( this.structTypeNode !== null ) {
  241. return this.structTypeNode.getNodeType( builder );
  242. }
  243. if ( builder.isAvailable( 'storageBuffer' ) || builder.isAvailable( 'indirectStorageBuffer' ) ) {
  244. return super.getNodeType( builder );
  245. }
  246. const { attribute } = this.getAttributeData();
  247. return attribute.getNodeType( builder );
  248. }
  249. /**
  250. * Generates the code snippet of the storage buffer node.
  251. *
  252. * @param {NodeBuilder} builder - The current node builder.
  253. * @return {String} The generated code snippet.
  254. */
  255. generate( builder ) {
  256. if ( this.structTypeNode !== null ) this.structTypeNode.build( builder );
  257. if ( builder.isAvailable( 'storageBuffer' ) || builder.isAvailable( 'indirectStorageBuffer' ) ) {
  258. return super.generate( builder );
  259. }
  260. const { attribute, varying } = this.getAttributeData();
  261. const output = varying.build( builder );
  262. builder.registerTransform( output, attribute );
  263. return output;
  264. }
  265. }
  266. export default StorageBufferNode;
  267. /**
  268. * TSL function for creating a storage buffer node.
  269. *
  270. * @tsl
  271. * @function
  272. * @param {StorageBufferAttribute|StorageInstancedBufferAttribute|BufferAttribute} value - The buffer data.
  273. * @param {(String|Struct)?} [type=null] - The buffer type (e.g. `'vec3'`).
  274. * @param {Number} [count=0] - The buffer count.
  275. * @returns {StorageBufferNode}
  276. */
  277. export const storage = ( value, type = null, count = 0 ) => nodeObject( new StorageBufferNode( value, type, count ) );
  278. /**
  279. * @tsl
  280. * @function
  281. * @deprecated since r171. Use `storage().setPBO( true )` instead.
  282. *
  283. * @param {StorageBufferAttribute|StorageInstancedBufferAttribute|BufferAttribute} value - The buffer data.
  284. * @param {String?} type - The buffer type (e.g. `'vec3'`).
  285. * @param {Number} count - The buffer count.
  286. * @returns {StorageBufferNode}
  287. */
  288. export const storageObject = ( value, type, count ) => { // @deprecated, r171
  289. console.warn( 'THREE.TSL: "storageObject()" is deprecated. Use "storage().setPBO( true )" instead.' );
  290. return storage( value, type, count ).setPBO( true );
  291. };
粤ICP备19079148号