babylon.uniformBuffer.ts 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. module BABYLON {
  2. export class UniformBuffer {
  3. private _engine: Engine;
  4. private _buffer: Nullable<WebGLBuffer>;
  5. private _data: number[];
  6. private _bufferData: Float32Array;
  7. private _dynamic?: boolean;
  8. private _uniformLocations: { [key: string]: number; };
  9. private _uniformSizes: { [key: string]: number; };
  10. private _uniformLocationPointer: number;
  11. private _needSync: boolean;
  12. private _noUBO: boolean;
  13. private _currentEffect: Effect;
  14. // Pool for avoiding memory leaks
  15. private static _MAX_UNIFORM_SIZE = 256;
  16. private static _tempBuffer = new Float32Array(UniformBuffer._MAX_UNIFORM_SIZE);
  17. /**
  18. * Wrapper for updateUniform.
  19. * @method updateMatrix3x3
  20. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  21. * @param {Float32Array} matrix
  22. */
  23. public updateMatrix3x3: (name: string, matrix: Float32Array) => void;
  24. /**
  25. * Wrapper for updateUniform.
  26. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  27. * @param {Float32Array} matrix
  28. */
  29. public updateMatrix2x2: (name: string, matrix: Float32Array) => void;
  30. /**
  31. * Wrapper for updateUniform.
  32. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  33. * @param {number} x
  34. */
  35. public updateFloat: (name: string, x: number) => void;
  36. /**
  37. * Wrapper for updateUniform.
  38. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  39. * @param {number} x
  40. * @param {number} y
  41. * @param {string} [suffix] Suffix to add to the uniform name.
  42. */
  43. public updateFloat2: (name: string, x: number, y: number, suffix?: string) => void;
  44. /**
  45. * Wrapper for updateUniform.
  46. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  47. * @param {number} x
  48. * @param {number} y
  49. * @param {number} z
  50. * @param {string} [suffix] Suffix to add to the uniform name.
  51. */
  52. public updateFloat3: (name: string, x: number, y: number, z: number, suffix?: string) => void;
  53. /**
  54. * Wrapper for updateUniform.
  55. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  56. * @param {number} x
  57. * @param {number} y
  58. * @param {number} z
  59. * @param {number} w
  60. * @param {string} [suffix] Suffix to add to the uniform name.
  61. */
  62. public updateFloat4: (name: string, x: number, y: number, z: number, w: number, suffix?: string) => void;
  63. /**
  64. * Wrapper for updateUniform.
  65. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  66. * @param {Matrix} A 4x4 matrix.
  67. */
  68. public updateMatrix: (name: string, mat: Matrix) => void;
  69. /**
  70. * Wrapper for updateUniform.
  71. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  72. * @param {Vector3} vector
  73. */
  74. public updateVector3: (name: string, vector: Vector3) => void;
  75. /**
  76. * Wrapper for updateUniform.
  77. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  78. * @param {Vector4} vector
  79. */
  80. public updateVector4: (name: string, vector: Vector4) => void;
  81. /**
  82. * Wrapper for updateUniform.
  83. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  84. * @param {Color3} color
  85. * @param {string} [suffix] Suffix to add to the uniform name.
  86. */
  87. public updateColor3: (name: string, color: Color3, suffix?: string) => void;
  88. /**
  89. * Wrapper for updateUniform.
  90. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  91. * @param {Color3} color
  92. * @param {number} alpha
  93. * @param {string} [suffix] Suffix to add to the uniform name.
  94. */
  95. public updateColor4: (name: string, color: Color3, alpha: number, suffix?: string) => void;
  96. /**
  97. * Uniform buffer objects.
  98. *
  99. * Handles blocks of uniform on the GPU.
  100. *
  101. * If WebGL 2 is not available, this class falls back on traditionnal setUniformXXX calls.
  102. *
  103. * For more information, please refer to :
  104. * https://www.khronos.org/opengl/wiki/Uniform_Buffer_Object
  105. */
  106. constructor(engine: Engine, data?: number[], dynamic?: boolean) {
  107. this._engine = engine;
  108. this._noUBO = !engine.supportsUniformBuffers;
  109. this._dynamic = dynamic;
  110. this._data = data || [];
  111. this._uniformLocations = {};
  112. this._uniformSizes = {};
  113. this._uniformLocationPointer = 0;
  114. this._needSync = false;
  115. if (this._noUBO) {
  116. this.updateMatrix3x3 = this._updateMatrix3x3ForEffect;
  117. this.updateMatrix2x2 = this._updateMatrix2x2ForEffect;
  118. this.updateFloat = this._updateFloatForEffect;
  119. this.updateFloat2 = this._updateFloat2ForEffect;
  120. this.updateFloat3 = this._updateFloat3ForEffect;
  121. this.updateFloat4 = this._updateFloat4ForEffect;
  122. this.updateMatrix = this._updateMatrixForEffect;
  123. this.updateVector3 = this._updateVector3ForEffect;
  124. this.updateVector4 = this._updateVector4ForEffect;
  125. this.updateColor3 = this._updateColor3ForEffect;
  126. this.updateColor4 = this._updateColor4ForEffect;
  127. } else {
  128. this._engine._uniformBuffers.push(this);
  129. this.updateMatrix3x3 = this._updateMatrix3x3ForUniform;
  130. this.updateMatrix2x2 = this._updateMatrix2x2ForUniform;
  131. this.updateFloat = this._updateFloatForUniform;
  132. this.updateFloat2 = this._updateFloat2ForUniform;
  133. this.updateFloat3 = this._updateFloat3ForUniform;
  134. this.updateFloat4 = this._updateFloat4ForUniform;
  135. this.updateMatrix = this._updateMatrixForUniform;
  136. this.updateVector3 = this._updateVector3ForUniform;
  137. this.updateVector4 = this._updateVector4ForUniform;
  138. this.updateColor3 = this._updateColor3ForUniform;
  139. this.updateColor4 = this._updateColor4ForUniform;
  140. }
  141. }
  142. // Properties
  143. /**
  144. * Indicates if the buffer is using the WebGL2 UBO implementation,
  145. * or just falling back on setUniformXXX calls.
  146. */
  147. public get useUbo(): boolean {
  148. return !this._noUBO;
  149. }
  150. /**
  151. * Indicates if the WebGL underlying uniform buffer is in sync
  152. * with the javascript cache data.
  153. */
  154. public get isSync(): boolean {
  155. return !this._needSync;
  156. }
  157. /**
  158. * Indicates if the WebGL underlying uniform buffer is dynamic.
  159. * Also, a dynamic UniformBuffer will disable cache verification and always
  160. * update the underlying WebGL uniform buffer to the GPU.
  161. */
  162. public isDynamic(): boolean {
  163. return this._dynamic !== undefined;
  164. }
  165. /**
  166. * The data cache on JS side.
  167. */
  168. public getData(): Float32Array {
  169. return this._bufferData;
  170. }
  171. /**
  172. * The underlying WebGL Uniform buffer.
  173. */
  174. public getBuffer(): Nullable<WebGLBuffer> {
  175. return this._buffer;
  176. }
  177. /**
  178. * std140 layout specifies how to align data within an UBO structure.
  179. * See https://khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf#page=159
  180. * for specs.
  181. */
  182. private _fillAlignment(size: number) {
  183. // This code has been simplified because we only use floats, vectors of 1, 2, 3, 4 components
  184. // and 4x4 matrices
  185. // TODO : change if other types are used
  186. var alignment;
  187. if (size <= 2) {
  188. alignment = size;
  189. } else {
  190. alignment = 4;
  191. }
  192. if ((this._uniformLocationPointer % alignment) !== 0) {
  193. var oldPointer = this._uniformLocationPointer;
  194. this._uniformLocationPointer += alignment - (this._uniformLocationPointer % alignment);
  195. var diff = this._uniformLocationPointer - oldPointer;
  196. for (var i = 0; i < diff; i++) {
  197. this._data.push(0);
  198. }
  199. }
  200. }
  201. /**
  202. * Adds an uniform in the buffer.
  203. * Warning : the subsequents calls of this function must be in the same order as declared in the shader
  204. * for the layout to be correct !
  205. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  206. * @param {number|number[]} size Data size, or data directly.
  207. */
  208. public addUniform(name: string, size: number | number[]) {
  209. if (this._noUBO) {
  210. return;
  211. }
  212. if (this._uniformLocations[name] !== undefined) {
  213. // Already existing uniform
  214. return;
  215. }
  216. // This function must be called in the order of the shader layout !
  217. // size can be the size of the uniform, or data directly
  218. var data;
  219. if (size instanceof Array) {
  220. data = size;
  221. size = data.length;
  222. } else {
  223. size = <number>size;
  224. data = [];
  225. // Fill with zeros
  226. for (var i = 0; i < size; i++) {
  227. data.push(0);
  228. }
  229. }
  230. this._fillAlignment(<number>size);
  231. this._uniformSizes[name] = <number>size;
  232. this._uniformLocations[name] = this._uniformLocationPointer;
  233. this._uniformLocationPointer += <number>size;
  234. for (var i = 0; i < size; i++) {
  235. this._data.push(data[i]);
  236. }
  237. this._needSync = true;
  238. }
  239. /**
  240. * Wrapper for addUniform.
  241. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  242. * @param {Matrix} mat A 4x4 matrix.
  243. */
  244. public addMatrix(name: string, mat: Matrix) {
  245. this.addUniform(name, Array.prototype.slice.call(mat.toArray()));
  246. }
  247. /**
  248. * Wrapper for addUniform.
  249. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  250. * @param {number} x
  251. * @param {number} y
  252. */
  253. public addFloat2(name: string, x: number, y: number) {
  254. var temp = [x, y];
  255. this.addUniform(name, temp);
  256. }
  257. /**
  258. * Wrapper for addUniform.
  259. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  260. * @param {number} x
  261. * @param {number} y
  262. * @param {number} z
  263. */
  264. public addFloat3(name: string, x: number, y: number, z: number) {
  265. var temp = [x, y, z];
  266. this.addUniform(name, temp);
  267. }
  268. /**
  269. * Wrapper for addUniform.
  270. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  271. * @param {Color3} color
  272. */
  273. public addColor3(name: string, color: Color3) {
  274. var temp = new Array<number>();
  275. color.toArray(temp);
  276. this.addUniform(name, temp);
  277. }
  278. /**
  279. * Wrapper for addUniform.
  280. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  281. * @param {Color3} color
  282. * @param {number} alpha
  283. */
  284. public addColor4(name: string, color: Color3, alpha: number) {
  285. var temp = new Array<number>();
  286. color.toArray(temp);
  287. temp.push(alpha);
  288. this.addUniform(name, temp);
  289. }
  290. /**
  291. * Wrapper for addUniform.
  292. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  293. * @param {Vector3} vector
  294. */
  295. public addVector3(name: string, vector: Vector3) {
  296. var temp = new Array<number>();
  297. vector.toArray(temp);
  298. this.addUniform(name, temp);
  299. }
  300. /**
  301. * Wrapper for addUniform.
  302. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  303. */
  304. public addMatrix3x3(name: string) {
  305. this.addUniform(name, 12);
  306. }
  307. /**
  308. * Wrapper for addUniform.
  309. * @param {string} name Name of the uniform, as used in the uniform block in the shader.
  310. */
  311. public addMatrix2x2(name: string) {
  312. this.addUniform(name, 8);
  313. }
  314. /**
  315. * Effectively creates the WebGL Uniform Buffer, once layout is completed with `addUniform`.
  316. */
  317. public create(): void {
  318. if (this._noUBO) {
  319. return;
  320. }
  321. if (this._buffer) {
  322. return; // nothing to do
  323. }
  324. // See spec, alignment must be filled as a vec4
  325. this._fillAlignment(4);
  326. this._bufferData = new Float32Array(this._data);
  327. this._rebuild();
  328. this._needSync = true;
  329. }
  330. /** @hidden */
  331. public _rebuild(): void {
  332. if (this._noUBO) {
  333. return;
  334. }
  335. if (this._dynamic) {
  336. this._buffer = this._engine.createDynamicUniformBuffer(this._bufferData);
  337. } else {
  338. this._buffer = this._engine.createUniformBuffer(this._bufferData);
  339. }
  340. }
  341. /**
  342. * Updates the WebGL Uniform Buffer on the GPU.
  343. * If the `dynamic` flag is set to true, no cache comparison is done.
  344. * Otherwise, the buffer will be updated only if the cache differs.
  345. */
  346. public update(): void {
  347. if (!this._buffer) {
  348. this.create();
  349. return;
  350. }
  351. if (!this._dynamic && !this._needSync) {
  352. return;
  353. }
  354. this._engine.updateUniformBuffer(this._buffer, this._bufferData);
  355. this._needSync = false;
  356. }
  357. /**
  358. * Updates the value of an uniform. The `update` method must be called afterwards to make it effective in the GPU.
  359. * @param {string} uniformName Name of the uniform, as used in the uniform block in the shader.
  360. * @param {number[]|Float32Array} data Flattened data
  361. * @param {number} size Size of the data.
  362. */
  363. public updateUniform(uniformName: string, data: FloatArray, size: number) {
  364. var location = this._uniformLocations[uniformName];
  365. if (location === undefined) {
  366. if (this._buffer) {
  367. // Cannot add an uniform if the buffer is already created
  368. Tools.Error("Cannot add an uniform after UBO has been created.");
  369. return;
  370. }
  371. this.addUniform(uniformName, size);
  372. location = this._uniformLocations[uniformName];
  373. }
  374. if (!this._buffer) {
  375. this.create();
  376. }
  377. if (!this._dynamic) {
  378. // Cache for static uniform buffers
  379. var changed = false;
  380. for (var i = 0; i < size; i++) {
  381. if (this._bufferData[location + i] !== data[i]) {
  382. changed = true;
  383. this._bufferData[location + i] = data[i];
  384. }
  385. }
  386. this._needSync = this._needSync || changed;
  387. } else {
  388. // No cache for dynamic
  389. for (var i = 0; i < size; i++) {
  390. this._bufferData[location + i] = data[i];
  391. }
  392. }
  393. }
  394. // Update methods
  395. private _updateMatrix3x3ForUniform(name: string, matrix: Float32Array): void {
  396. // To match std140, matrix must be realigned
  397. for (var i = 0; i < 3; i++) {
  398. UniformBuffer._tempBuffer[i * 4] = matrix[i * 3];
  399. UniformBuffer._tempBuffer[i * 4 + 1] = matrix[i * 3 + 1];
  400. UniformBuffer._tempBuffer[i * 4 + 2] = matrix[i * 3 + 2];
  401. UniformBuffer._tempBuffer[i * 4 + 3] = 0.0;
  402. }
  403. this.updateUniform(name, UniformBuffer._tempBuffer, 12);
  404. }
  405. private _updateMatrix3x3ForEffect(name: string, matrix: Float32Array): void {
  406. this._currentEffect.setMatrix3x3(name, matrix);
  407. }
  408. private _updateMatrix2x2ForEffect(name: string, matrix: Float32Array): void {
  409. this._currentEffect.setMatrix2x2(name, matrix);
  410. }
  411. private _updateMatrix2x2ForUniform(name: string, matrix: Float32Array): void {
  412. // To match std140, matrix must be realigned
  413. for (var i = 0; i < 2; i++) {
  414. UniformBuffer._tempBuffer[i * 4] = matrix[i * 2];
  415. UniformBuffer._tempBuffer[i * 4 + 1] = matrix[i * 2 + 1];
  416. UniformBuffer._tempBuffer[i * 4 + 2] = 0.0;
  417. UniformBuffer._tempBuffer[i * 4 + 3] = 0.0;
  418. }
  419. this.updateUniform(name, UniformBuffer._tempBuffer, 8);
  420. }
  421. private _updateFloatForEffect(name: string, x: number) {
  422. this._currentEffect.setFloat(name, x);
  423. }
  424. private _updateFloatForUniform(name: string, x: number) {
  425. UniformBuffer._tempBuffer[0] = x;
  426. this.updateUniform(name, UniformBuffer._tempBuffer, 1);
  427. }
  428. private _updateFloat2ForEffect(name: string, x: number, y: number, suffix = "") {
  429. this._currentEffect.setFloat2(name + suffix, x, y);
  430. }
  431. private _updateFloat2ForUniform(name: string, x: number, y: number, suffix = "") {
  432. UniformBuffer._tempBuffer[0] = x;
  433. UniformBuffer._tempBuffer[1] = y;
  434. this.updateUniform(name, UniformBuffer._tempBuffer, 2);
  435. }
  436. private _updateFloat3ForEffect(name: string, x: number, y: number, z: number, suffix = "") {
  437. this._currentEffect.setFloat3(name + suffix, x, y, z);
  438. }
  439. private _updateFloat3ForUniform(name: string, x: number, y: number, z: number, suffix = "") {
  440. UniformBuffer._tempBuffer[0] = x;
  441. UniformBuffer._tempBuffer[1] = y;
  442. UniformBuffer._tempBuffer[2] = z;
  443. this.updateUniform(name, UniformBuffer._tempBuffer, 3);
  444. }
  445. private _updateFloat4ForEffect(name: string, x: number, y: number, z: number, w: number, suffix = "") {
  446. this._currentEffect.setFloat4(name + suffix, x, y, z, w);
  447. }
  448. private _updateFloat4ForUniform(name: string, x: number, y: number, z: number, w: number, suffix = "") {
  449. UniformBuffer._tempBuffer[0] = x;
  450. UniformBuffer._tempBuffer[1] = y;
  451. UniformBuffer._tempBuffer[2] = z;
  452. UniformBuffer._tempBuffer[3] = w;
  453. this.updateUniform(name, UniformBuffer._tempBuffer, 4);
  454. }
  455. private _updateMatrixForEffect(name: string, mat: Matrix) {
  456. this._currentEffect.setMatrix(name, mat);
  457. }
  458. private _updateMatrixForUniform(name: string, mat: Matrix) {
  459. this.updateUniform(name, mat.toArray(), 16);
  460. }
  461. private _updateVector3ForEffect(name: string, vector: Vector3) {
  462. this._currentEffect.setVector3(name, vector);
  463. }
  464. private _updateVector3ForUniform(name: string, vector: Vector3) {
  465. vector.toArray(UniformBuffer._tempBuffer);
  466. this.updateUniform(name, UniformBuffer._tempBuffer, 3);
  467. }
  468. private _updateVector4ForEffect(name: string, vector: Vector4) {
  469. this._currentEffect.setVector4(name, vector);
  470. }
  471. private _updateVector4ForUniform(name: string, vector: Vector4) {
  472. vector.toArray(UniformBuffer._tempBuffer);
  473. this.updateUniform(name, UniformBuffer._tempBuffer, 4);
  474. }
  475. private _updateColor3ForEffect(name: string, color: Color3, suffix = "") {
  476. this._currentEffect.setColor3(name + suffix, color);
  477. }
  478. private _updateColor3ForUniform(name: string, color: Color3, suffix = "") {
  479. color.toArray(UniformBuffer._tempBuffer);
  480. this.updateUniform(name, UniformBuffer._tempBuffer, 3);
  481. }
  482. private _updateColor4ForEffect(name: string, color: Color3, alpha: number, suffix = "") {
  483. this._currentEffect.setColor4(name + suffix, color, alpha);
  484. }
  485. private _updateColor4ForUniform(name: string, color: Color3, alpha: number, suffix = "") {
  486. color.toArray(UniformBuffer._tempBuffer);
  487. UniformBuffer._tempBuffer[3] = alpha;
  488. this.updateUniform(name, UniformBuffer._tempBuffer, 4);
  489. }
  490. /**
  491. * Sets a sampler uniform on the effect.
  492. * @param {string} name Name of the sampler.
  493. * @param {Texture} texture
  494. */
  495. public setTexture(name: string, texture: Nullable<BaseTexture>) {
  496. this._currentEffect.setTexture(name, texture);
  497. }
  498. /**
  499. * Directly updates the value of the uniform in the cache AND on the GPU.
  500. * @param {string} uniformName Name of the uniform, as used in the uniform block in the shader.
  501. * @param {number[]|Float32Array} data Flattened data
  502. */
  503. public updateUniformDirectly(uniformName: string, data: FloatArray) {
  504. this.updateUniform(uniformName, data, data.length);
  505. this.update();
  506. }
  507. /**
  508. * Binds this uniform buffer to an effect.
  509. * @param {Effect} effect
  510. * @param {string} name Name of the uniform block in the shader.
  511. */
  512. public bindToEffect(effect: Effect, name: string): void {
  513. this._currentEffect = effect;
  514. if (this._noUBO || !this._buffer) {
  515. return;
  516. }
  517. effect.bindUniformBuffer(this._buffer, name);
  518. }
  519. /**
  520. * Disposes the uniform buffer.
  521. */
  522. public dispose(): void {
  523. if (this._noUBO) {
  524. return;
  525. }
  526. let index = this._engine._uniformBuffers.indexOf(this);
  527. if (index !== -1) {
  528. this._engine._uniformBuffers.splice(index, 1);
  529. }
  530. if (!this._buffer) {
  531. return;
  532. }
  533. if (this._engine._releaseBuffer(this._buffer)) {
  534. this._buffer = null;
  535. }
  536. }
  537. }
  538. }