StorageBufferNode.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. if ( value.isStorageBufferAttribute || value.isStorageInstancedBufferAttribute ) {
  55. bufferCount = value.count;
  56. }
  57. } else if ( bufferType === null && ( value.isStorageBufferAttribute || value.isStorageInstancedBufferAttribute ) ) {
  58. nodeType = getTypeFromLength( value.itemSize );
  59. bufferCount = value.count;
  60. } else {
  61. nodeType = bufferType;
  62. }
  63. super( value, nodeType, bufferCount );
  64. /**
  65. * This flag can be used for type testing.
  66. *
  67. * @type {boolean}
  68. * @readonly
  69. * @default true
  70. */
  71. this.isStorageBufferNode = true;
  72. /**
  73. * The buffer struct type.
  74. *
  75. * @type {?StructTypeNode}
  76. * @default null
  77. */
  78. this.structTypeNode = structTypeNode;
  79. /**
  80. * The access type of the texture node.
  81. *
  82. * @type {string}
  83. * @default 'readWrite'
  84. */
  85. this.access = NodeAccess.READ_WRITE;
  86. /**
  87. * Whether the node is atomic or not.
  88. *
  89. * @type {boolean}
  90. * @default false
  91. */
  92. this.isAtomic = false;
  93. /**
  94. * Whether the node represents a PBO or not.
  95. * Only relevant for WebGL.
  96. *
  97. * @type {boolean}
  98. * @default false
  99. */
  100. this.isPBO = false;
  101. /**
  102. * A reference to the internal buffer attribute node.
  103. *
  104. * @type {?BufferAttributeNode}
  105. * @default null
  106. */
  107. this._attribute = null;
  108. /**
  109. * A reference to the internal varying node.
  110. *
  111. * @type {?VaryingNode}
  112. * @default null
  113. */
  114. this._varying = null;
  115. /**
  116. * `StorageBufferNode` sets this property to `true` by default.
  117. *
  118. * @type {boolean}
  119. * @default true
  120. */
  121. this.global = true;
  122. if ( value.isStorageBufferAttribute !== true && value.isStorageInstancedBufferAttribute !== true ) {
  123. // TODO: Improve it, possibly adding a new property to the BufferAttribute to identify it as a storage buffer read-only attribute in Renderer
  124. if ( value.isInstancedBufferAttribute ) value.isStorageInstancedBufferAttribute = true;
  125. else value.isStorageBufferAttribute = true;
  126. }
  127. }
  128. /**
  129. * This method is overwritten since the buffer data might be shared
  130. * and thus the hash should be shared as well.
  131. *
  132. * @param {NodeBuilder} builder - The current node builder.
  133. * @return {string} The hash.
  134. */
  135. getHash( builder ) {
  136. if ( this.bufferCount === 0 ) {
  137. let bufferData = builder.globalCache.getData( this.value );
  138. if ( bufferData === undefined ) {
  139. bufferData = {
  140. node: this
  141. };
  142. builder.globalCache.setData( this.value, bufferData );
  143. }
  144. return bufferData.node.uuid;
  145. }
  146. return this.uuid;
  147. }
  148. /**
  149. * Overwrites the default implementation to return a fixed value `'indirectStorageBuffer'` or `'storageBuffer'`.
  150. *
  151. * @param {NodeBuilder} builder - The current node builder.
  152. * @return {string} The input type.
  153. */
  154. getInputType( /*builder*/ ) {
  155. return this.value.isIndirectStorageBufferAttribute ? 'indirectStorageBuffer' : 'storageBuffer';
  156. }
  157. /**
  158. * Enables element access with the given index node.
  159. *
  160. * @param {IndexNode} indexNode - The index node.
  161. * @return {StorageArrayElementNode} A node representing the element access.
  162. */
  163. element( indexNode ) {
  164. return storageElement( this, indexNode );
  165. }
  166. /**
  167. * Defines whether this node is a PBO or not. Only relevant for WebGL.
  168. *
  169. * @param {boolean} value - The value so set.
  170. * @return {StorageBufferNode} A reference to this node.
  171. */
  172. setPBO( value ) {
  173. this.isPBO = value;
  174. return this;
  175. }
  176. /**
  177. * Returns the `isPBO` value.
  178. *
  179. * @return {boolean} Whether the node represents a PBO or not.
  180. */
  181. getPBO() {
  182. return this.isPBO;
  183. }
  184. /**
  185. * Defines the node access.
  186. *
  187. * @param {string} value - The node access.
  188. * @return {StorageBufferNode} A reference to this node.
  189. */
  190. setAccess( value ) {
  191. this.access = value;
  192. return this;
  193. }
  194. /**
  195. * Convenience method for configuring a read-only node access.
  196. *
  197. * @return {StorageBufferNode} A reference to this node.
  198. */
  199. toReadOnly() {
  200. return this.setAccess( NodeAccess.READ_ONLY );
  201. }
  202. /**
  203. * Defines whether the node is atomic or not.
  204. *
  205. * @param {boolean} value - The atomic flag.
  206. * @return {StorageBufferNode} A reference to this node.
  207. */
  208. setAtomic( value ) {
  209. this.isAtomic = value;
  210. return this;
  211. }
  212. /**
  213. * Convenience method for making this node atomic.
  214. *
  215. * @return {StorageBufferNode} A reference to this node.
  216. */
  217. toAtomic() {
  218. return this.setAtomic( true );
  219. }
  220. /**
  221. * Returns attribute data for this storage buffer node.
  222. *
  223. * @return {{attribute: BufferAttributeNode, varying: VaryingNode}} The attribute data.
  224. */
  225. getAttributeData() {
  226. if ( this._attribute === null ) {
  227. this._attribute = bufferAttribute( this.value );
  228. this._varying = varying( this._attribute );
  229. }
  230. return {
  231. attribute: this._attribute,
  232. varying: this._varying
  233. };
  234. }
  235. /**
  236. * This method is overwritten since the node type from the availability of storage buffers
  237. * and the attribute data.
  238. *
  239. * @param {NodeBuilder} builder - The current node builder.
  240. * @return {string} The node type.
  241. */
  242. getNodeType( builder ) {
  243. if ( this.structTypeNode !== null ) {
  244. return this.structTypeNode.getNodeType( builder );
  245. }
  246. if ( builder.isAvailable( 'storageBuffer' ) || builder.isAvailable( 'indirectStorageBuffer' ) ) {
  247. return super.getNodeType( builder );
  248. }
  249. const { attribute } = this.getAttributeData();
  250. return attribute.getNodeType( builder );
  251. }
  252. /**
  253. * Returns the type of a member of the struct.
  254. *
  255. * @param {NodeBuilder} builder - The current node builder.
  256. * @param {string} name - The name of the member.
  257. * @return {string} The type of the member.
  258. */
  259. getMemberType( builder, name ) {
  260. if ( this.structTypeNode !== null ) {
  261. return this.structTypeNode.getMemberType( builder, name );
  262. }
  263. return 'void';
  264. }
  265. /**
  266. * Generates the code snippet of the storage buffer node.
  267. *
  268. * @param {NodeBuilder} builder - The current node builder.
  269. * @return {string} The generated code snippet.
  270. */
  271. generate( builder ) {
  272. if ( this.structTypeNode !== null ) this.structTypeNode.build( builder );
  273. if ( builder.isAvailable( 'storageBuffer' ) || builder.isAvailable( 'indirectStorageBuffer' ) ) {
  274. return super.generate( builder );
  275. }
  276. const { attribute, varying } = this.getAttributeData();
  277. const output = varying.build( builder );
  278. builder.registerTransform( output, attribute );
  279. return output;
  280. }
  281. }
  282. export default StorageBufferNode;
  283. /**
  284. * TSL function for creating a storage buffer node.
  285. *
  286. * @tsl
  287. * @function
  288. * @param {StorageBufferAttribute|StorageInstancedBufferAttribute|BufferAttribute} value - The buffer data.
  289. * @param {?(string|Struct)} [type=null] - The buffer type (e.g. `'vec3'`).
  290. * @param {number} [count=0] - The buffer count.
  291. * @returns {StorageBufferNode}
  292. */
  293. export const storage = ( value, type = null, count = 0 ) => nodeObject( new StorageBufferNode( value, type, count ) );
  294. /**
  295. * @tsl
  296. * @function
  297. * @deprecated since r171. Use `storage().setPBO( true )` instead.
  298. *
  299. * @param {StorageBufferAttribute|StorageInstancedBufferAttribute|BufferAttribute} value - The buffer data.
  300. * @param {?string} type - The buffer type (e.g. `'vec3'`).
  301. * @param {number} count - The buffer count.
  302. * @returns {StorageBufferNode}
  303. */
  304. export const storageObject = ( value, type, count ) => { // @deprecated, r171
  305. console.warn( 'THREE.TSL: "storageObject()" is deprecated. Use "storage().setPBO( true )" instead.' );
  306. return storage( value, type, count ).setPBO( true );
  307. };
粤ICP备19079148号