BatchTable.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. import Cartesian2 from "../Core/Cartesian2.js";
  2. import Cartesian3 from "../Core/Cartesian3.js";
  3. import Cartesian4 from "../Core/Cartesian4.js";
  4. import combine from "../Core/combine.js";
  5. import ComponentDatatype from "../Core/ComponentDatatype.js";
  6. import defined from "../Core/defined.js";
  7. import destroyObject from "../Core/destroyObject.js";
  8. import DeveloperError from "../Core/DeveloperError.js";
  9. import PixelFormat from "../Core/PixelFormat.js";
  10. import ContextLimits from "../Renderer/ContextLimits.js";
  11. import PixelDatatype from "../Renderer/PixelDatatype.js";
  12. import Sampler from "../Renderer/Sampler.js";
  13. import Texture from "../Renderer/Texture.js";
  14. /**
  15. * Creates a texture to look up per instance attributes for batched primitives. For example, store each primitive's pick color in the texture.
  16. *
  17. * @alias BatchTable
  18. * @constructor
  19. * @private
  20. *
  21. * @param {Context} context The context in which the batch table is created.
  22. * @param {Object[]} attributes An array of objects describing a per instance attribute. Each object contains a datatype, components per attributes, whether it is normalized and a function name
  23. * to retrieve the value in the vertex shader.
  24. * @param {Number} numberOfInstances The number of instances in a batch table.
  25. *
  26. * @example
  27. * // create the batch table
  28. * var attributes = [{
  29. * functionName : 'getShow',
  30. * componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
  31. * componentsPerAttribute : 1
  32. * }, {
  33. * functionName : 'getPickColor',
  34. * componentDatatype : ComponentDatatype.UNSIGNED_BYTE,
  35. * componentsPerAttribute : 4,
  36. * normalize : true
  37. * }];
  38. * var batchTable = new BatchTable(context, attributes, 5);
  39. *
  40. * // when creating the draw commands, update the uniform map and the vertex shader
  41. * vertexShaderSource = batchTable.getVertexShaderCallback()(vertexShaderSource);
  42. * var shaderProgram = ShaderProgram.fromCache({
  43. * // ...
  44. * vertexShaderSource : vertexShaderSource,
  45. * });
  46. *
  47. * drawCommand.shaderProgram = shaderProgram;
  48. * drawCommand.uniformMap = batchTable.getUniformMapCallback()(uniformMap);
  49. *
  50. * // use the attribute function names in the shader to retrieve the instance values
  51. * // ...
  52. * attribute float batchId;
  53. *
  54. * void main() {
  55. * // ...
  56. * float show = getShow(batchId);
  57. * vec3 pickColor = getPickColor(batchId);
  58. * // ...
  59. * }
  60. */
  61. function BatchTable(context, attributes, numberOfInstances) {
  62. //>>includeStart('debug', pragmas.debug);
  63. if (!defined(context)) {
  64. throw new DeveloperError("context is required");
  65. }
  66. if (!defined(attributes)) {
  67. throw new DeveloperError("attributes is required");
  68. }
  69. if (!defined(numberOfInstances)) {
  70. throw new DeveloperError("numberOfInstances is required");
  71. }
  72. //>>includeEnd('debug');
  73. this._attributes = attributes;
  74. this._numberOfInstances = numberOfInstances;
  75. if (attributes.length === 0) {
  76. return;
  77. }
  78. // PERFORMANCE_IDEA: We may be able to arrange the attributes so they can be packing into fewer texels.
  79. // Right now, an attribute with one component uses an entire texel when 4 single component attributes can
  80. // be packed into a texel.
  81. //
  82. // Packing floats into unsigned byte textures makes the problem worse. A single component float attribute
  83. // will be packed into a single texel leaving 3 texels unused. 4 texels are reserved for each float attribute
  84. // regardless of how many components it has.
  85. var pixelDatatype = getDatatype(attributes);
  86. var textureFloatSupported = context.floatingPointTexture;
  87. var packFloats =
  88. pixelDatatype === PixelDatatype.FLOAT && !textureFloatSupported;
  89. var offsets = createOffsets(attributes, packFloats);
  90. var stride = getStride(offsets, attributes, packFloats);
  91. var maxNumberOfInstancesPerRow = Math.floor(
  92. ContextLimits.maximumTextureSize / stride
  93. );
  94. var instancesPerWidth = Math.min(
  95. numberOfInstances,
  96. maxNumberOfInstancesPerRow
  97. );
  98. var width = stride * instancesPerWidth;
  99. var height = Math.ceil(numberOfInstances / instancesPerWidth);
  100. var stepX = 1.0 / width;
  101. var centerX = stepX * 0.5;
  102. var stepY = 1.0 / height;
  103. var centerY = stepY * 0.5;
  104. this._textureDimensions = new Cartesian2(width, height);
  105. this._textureStep = new Cartesian4(stepX, centerX, stepY, centerY);
  106. this._pixelDatatype = !packFloats
  107. ? pixelDatatype
  108. : PixelDatatype.UNSIGNED_BYTE;
  109. this._packFloats = packFloats;
  110. this._offsets = offsets;
  111. this._stride = stride;
  112. this._texture = undefined;
  113. var batchLength = 4 * width * height;
  114. this._batchValues =
  115. pixelDatatype === PixelDatatype.FLOAT && !packFloats
  116. ? new Float32Array(batchLength)
  117. : new Uint8Array(batchLength);
  118. this._batchValuesDirty = false;
  119. }
  120. Object.defineProperties(BatchTable.prototype, {
  121. /**
  122. * The attribute descriptions.
  123. * @memberOf BatchTable.prototype
  124. * @type {Object[]}
  125. * @readonly
  126. */
  127. attributes: {
  128. get: function () {
  129. return this._attributes;
  130. },
  131. },
  132. /**
  133. * The number of instances.
  134. * @memberOf BatchTable.prototype
  135. * @type {Number}
  136. * @readonly
  137. */
  138. numberOfInstances: {
  139. get: function () {
  140. return this._numberOfInstances;
  141. },
  142. },
  143. });
  144. function getDatatype(attributes) {
  145. var foundFloatDatatype = false;
  146. var length = attributes.length;
  147. for (var i = 0; i < length; ++i) {
  148. if (attributes[i].componentDatatype !== ComponentDatatype.UNSIGNED_BYTE) {
  149. foundFloatDatatype = true;
  150. break;
  151. }
  152. }
  153. return foundFloatDatatype ? PixelDatatype.FLOAT : PixelDatatype.UNSIGNED_BYTE;
  154. }
  155. function getAttributeType(attributes, attributeIndex) {
  156. var componentsPerAttribute =
  157. attributes[attributeIndex].componentsPerAttribute;
  158. if (componentsPerAttribute === 2) {
  159. return Cartesian2;
  160. } else if (componentsPerAttribute === 3) {
  161. return Cartesian3;
  162. } else if (componentsPerAttribute === 4) {
  163. return Cartesian4;
  164. }
  165. return Number;
  166. }
  167. function createOffsets(attributes, packFloats) {
  168. var offsets = new Array(attributes.length);
  169. var currentOffset = 0;
  170. var attributesLength = attributes.length;
  171. for (var i = 0; i < attributesLength; ++i) {
  172. var attribute = attributes[i];
  173. var componentDatatype = attribute.componentDatatype;
  174. offsets[i] = currentOffset;
  175. if (componentDatatype !== ComponentDatatype.UNSIGNED_BYTE && packFloats) {
  176. currentOffset += 4;
  177. } else {
  178. ++currentOffset;
  179. }
  180. }
  181. return offsets;
  182. }
  183. function getStride(offsets, attributes, packFloats) {
  184. var length = offsets.length;
  185. var lastOffset = offsets[length - 1];
  186. var lastAttribute = attributes[length - 1];
  187. var componentDatatype = lastAttribute.componentDatatype;
  188. if (componentDatatype !== ComponentDatatype.UNSIGNED_BYTE && packFloats) {
  189. return lastOffset + 4;
  190. }
  191. return lastOffset + 1;
  192. }
  193. var scratchPackedFloatCartesian4 = new Cartesian4();
  194. function getPackedFloat(array, index, result) {
  195. var packed = Cartesian4.unpack(array, index, scratchPackedFloatCartesian4);
  196. var x = Cartesian4.unpackFloat(packed);
  197. packed = Cartesian4.unpack(array, index + 4, scratchPackedFloatCartesian4);
  198. var y = Cartesian4.unpackFloat(packed);
  199. packed = Cartesian4.unpack(array, index + 8, scratchPackedFloatCartesian4);
  200. var z = Cartesian4.unpackFloat(packed);
  201. packed = Cartesian4.unpack(array, index + 12, scratchPackedFloatCartesian4);
  202. var w = Cartesian4.unpackFloat(packed);
  203. return Cartesian4.fromElements(x, y, z, w, result);
  204. }
  205. function setPackedAttribute(value, array, index) {
  206. var packed = Cartesian4.packFloat(value.x, scratchPackedFloatCartesian4);
  207. Cartesian4.pack(packed, array, index);
  208. packed = Cartesian4.packFloat(value.y, packed);
  209. Cartesian4.pack(packed, array, index + 4);
  210. packed = Cartesian4.packFloat(value.z, packed);
  211. Cartesian4.pack(packed, array, index + 8);
  212. packed = Cartesian4.packFloat(value.w, packed);
  213. Cartesian4.pack(packed, array, index + 12);
  214. }
  215. var scratchGetAttributeCartesian4 = new Cartesian4();
  216. /**
  217. * Gets the value of an attribute in the table.
  218. *
  219. * @param {Number} instanceIndex The index of the instance.
  220. * @param {Number} attributeIndex The index of the attribute.
  221. * @param {undefined|Cartesian2|Cartesian3|Cartesian4} [result] The object onto which to store the result. The type is dependent on the attribute's number of components.
  222. * @returns {Number|Cartesian2|Cartesian3|Cartesian4} The attribute value stored for the instance.
  223. *
  224. * @exception {DeveloperError} instanceIndex is out of range.
  225. * @exception {DeveloperError} attributeIndex is out of range.
  226. */
  227. BatchTable.prototype.getBatchedAttribute = function (
  228. instanceIndex,
  229. attributeIndex,
  230. result
  231. ) {
  232. //>>includeStart('debug', pragmas.debug);
  233. if (instanceIndex < 0 || instanceIndex >= this._numberOfInstances) {
  234. throw new DeveloperError("instanceIndex is out of range.");
  235. }
  236. if (attributeIndex < 0 || attributeIndex >= this._attributes.length) {
  237. throw new DeveloperError("attributeIndex is out of range");
  238. }
  239. //>>includeEnd('debug');
  240. var attributes = this._attributes;
  241. var offset = this._offsets[attributeIndex];
  242. var stride = this._stride;
  243. var index = 4 * stride * instanceIndex + 4 * offset;
  244. var value;
  245. if (
  246. this._packFloats &&
  247. attributes[attributeIndex].componentDatatype !== PixelDatatype.UNSIGNED_BYTE
  248. ) {
  249. value = getPackedFloat(
  250. this._batchValues,
  251. index,
  252. scratchGetAttributeCartesian4
  253. );
  254. } else {
  255. value = Cartesian4.unpack(
  256. this._batchValues,
  257. index,
  258. scratchGetAttributeCartesian4
  259. );
  260. }
  261. var attributeType = getAttributeType(attributes, attributeIndex);
  262. if (defined(attributeType.fromCartesian4)) {
  263. return attributeType.fromCartesian4(value, result);
  264. } else if (defined(attributeType.clone)) {
  265. return attributeType.clone(value, result);
  266. }
  267. return value.x;
  268. };
  269. var setAttributeScratchValues = [
  270. undefined,
  271. undefined,
  272. new Cartesian2(),
  273. new Cartesian3(),
  274. new Cartesian4(),
  275. ];
  276. var setAttributeScratchCartesian4 = new Cartesian4();
  277. /**
  278. * Sets the value of an attribute in the table.
  279. *
  280. * @param {Number} instanceIndex The index of the instance.
  281. * @param {Number} attributeIndex The index of the attribute.
  282. * @param {Number|Cartesian2|Cartesian3|Cartesian4} value The value to be stored in the table. The type of value will depend on the number of components of the attribute.
  283. *
  284. * @exception {DeveloperError} instanceIndex is out of range.
  285. * @exception {DeveloperError} attributeIndex is out of range.
  286. */
  287. BatchTable.prototype.setBatchedAttribute = function (
  288. instanceIndex,
  289. attributeIndex,
  290. value
  291. ) {
  292. //>>includeStart('debug', pragmas.debug);
  293. if (instanceIndex < 0 || instanceIndex >= this._numberOfInstances) {
  294. throw new DeveloperError("instanceIndex is out of range.");
  295. }
  296. if (attributeIndex < 0 || attributeIndex >= this._attributes.length) {
  297. throw new DeveloperError("attributeIndex is out of range");
  298. }
  299. if (!defined(value)) {
  300. throw new DeveloperError("value is required.");
  301. }
  302. //>>includeEnd('debug');
  303. var attributes = this._attributes;
  304. var result =
  305. setAttributeScratchValues[
  306. attributes[attributeIndex].componentsPerAttribute
  307. ];
  308. var currentAttribute = this.getBatchedAttribute(
  309. instanceIndex,
  310. attributeIndex,
  311. result
  312. );
  313. var attributeType = getAttributeType(this._attributes, attributeIndex);
  314. var entriesEqual = defined(attributeType.equals)
  315. ? attributeType.equals(currentAttribute, value)
  316. : currentAttribute === value;
  317. if (entriesEqual) {
  318. return;
  319. }
  320. var attributeValue = setAttributeScratchCartesian4;
  321. attributeValue.x = defined(value.x) ? value.x : value;
  322. attributeValue.y = defined(value.y) ? value.y : 0.0;
  323. attributeValue.z = defined(value.z) ? value.z : 0.0;
  324. attributeValue.w = defined(value.w) ? value.w : 0.0;
  325. var offset = this._offsets[attributeIndex];
  326. var stride = this._stride;
  327. var index = 4 * stride * instanceIndex + 4 * offset;
  328. if (
  329. this._packFloats &&
  330. attributes[attributeIndex].componentDatatype !== PixelDatatype.UNSIGNED_BYTE
  331. ) {
  332. setPackedAttribute(attributeValue, this._batchValues, index);
  333. } else {
  334. Cartesian4.pack(attributeValue, this._batchValues, index);
  335. }
  336. this._batchValuesDirty = true;
  337. };
  338. function createTexture(batchTable, context) {
  339. var dimensions = batchTable._textureDimensions;
  340. batchTable._texture = new Texture({
  341. context: context,
  342. pixelFormat: PixelFormat.RGBA,
  343. pixelDatatype: batchTable._pixelDatatype,
  344. width: dimensions.x,
  345. height: dimensions.y,
  346. sampler: Sampler.NEAREST,
  347. flipY: false,
  348. });
  349. }
  350. function updateTexture(batchTable) {
  351. var dimensions = batchTable._textureDimensions;
  352. batchTable._texture.copyFrom({
  353. width: dimensions.x,
  354. height: dimensions.y,
  355. arrayBufferView: batchTable._batchValues,
  356. });
  357. }
  358. /**
  359. * Creates/updates the batch table texture.
  360. * @param {FrameState} frameState The frame state.
  361. *
  362. * @exception {RuntimeError} The floating point texture extension is required but not supported.
  363. */
  364. BatchTable.prototype.update = function (frameState) {
  365. if (
  366. (defined(this._texture) && !this._batchValuesDirty) ||
  367. this._attributes.length === 0
  368. ) {
  369. return;
  370. }
  371. this._batchValuesDirty = false;
  372. if (!defined(this._texture)) {
  373. createTexture(this, frameState.context);
  374. }
  375. updateTexture(this);
  376. };
  377. /**
  378. * Gets a function that will update a uniform map to contain values for looking up values in the batch table.
  379. *
  380. * @returns {BatchTable.updateUniformMapCallback} A callback for updating uniform maps.
  381. */
  382. BatchTable.prototype.getUniformMapCallback = function () {
  383. var that = this;
  384. return function (uniformMap) {
  385. if (that._attributes.length === 0) {
  386. return uniformMap;
  387. }
  388. var batchUniformMap = {
  389. batchTexture: function () {
  390. return that._texture;
  391. },
  392. batchTextureDimensions: function () {
  393. return that._textureDimensions;
  394. },
  395. batchTextureStep: function () {
  396. return that._textureStep;
  397. },
  398. };
  399. return combine(uniformMap, batchUniformMap);
  400. };
  401. };
  402. function getGlslComputeSt(batchTable) {
  403. var stride = batchTable._stride;
  404. // GLSL batchId is zero-based: [0, numberOfInstances - 1]
  405. if (batchTable._textureDimensions.y === 1) {
  406. return (
  407. "uniform vec4 batchTextureStep; \n" +
  408. "vec2 computeSt(float batchId) \n" +
  409. "{ \n" +
  410. " float stepX = batchTextureStep.x; \n" +
  411. " float centerX = batchTextureStep.y; \n" +
  412. " float numberOfAttributes = float(" +
  413. stride +
  414. "); \n" +
  415. " return vec2(centerX + (batchId * numberOfAttributes * stepX), 0.5); \n" +
  416. "} \n"
  417. );
  418. }
  419. return (
  420. "uniform vec4 batchTextureStep; \n" +
  421. "uniform vec2 batchTextureDimensions; \n" +
  422. "vec2 computeSt(float batchId) \n" +
  423. "{ \n" +
  424. " float stepX = batchTextureStep.x; \n" +
  425. " float centerX = batchTextureStep.y; \n" +
  426. " float stepY = batchTextureStep.z; \n" +
  427. " float centerY = batchTextureStep.w; \n" +
  428. " float numberOfAttributes = float(" +
  429. stride +
  430. "); \n" +
  431. " float xId = mod(batchId * numberOfAttributes, batchTextureDimensions.x); \n" +
  432. " float yId = floor(batchId * numberOfAttributes / batchTextureDimensions.x); \n" +
  433. " return vec2(centerX + (xId * stepX), centerY + (yId * stepY)); \n" +
  434. "} \n"
  435. );
  436. }
  437. function getComponentType(componentsPerAttribute) {
  438. if (componentsPerAttribute === 1) {
  439. return "float";
  440. }
  441. return "vec" + componentsPerAttribute;
  442. }
  443. function getComponentSwizzle(componentsPerAttribute) {
  444. if (componentsPerAttribute === 1) {
  445. return ".x";
  446. } else if (componentsPerAttribute === 2) {
  447. return ".xy";
  448. } else if (componentsPerAttribute === 3) {
  449. return ".xyz";
  450. }
  451. return "";
  452. }
  453. function getGlslAttributeFunction(batchTable, attributeIndex) {
  454. var attributes = batchTable._attributes;
  455. var attribute = attributes[attributeIndex];
  456. var componentsPerAttribute = attribute.componentsPerAttribute;
  457. var functionName = attribute.functionName;
  458. var functionReturnType = getComponentType(componentsPerAttribute);
  459. var functionReturnValue = getComponentSwizzle(componentsPerAttribute);
  460. var offset = batchTable._offsets[attributeIndex];
  461. var glslFunction =
  462. functionReturnType +
  463. " " +
  464. functionName +
  465. "(float batchId) \n" +
  466. "{ \n" +
  467. " vec2 st = computeSt(batchId); \n" +
  468. " st.x += batchTextureStep.x * float(" +
  469. offset +
  470. "); \n";
  471. if (
  472. batchTable._packFloats &&
  473. attribute.componentDatatype !== PixelDatatype.UNSIGNED_BYTE
  474. ) {
  475. glslFunction +=
  476. "vec4 textureValue; \n" +
  477. "textureValue.x = czm_unpackFloat(texture2D(batchTexture, st)); \n" +
  478. "textureValue.y = czm_unpackFloat(texture2D(batchTexture, st + vec2(batchTextureStep.x, 0.0))); \n" +
  479. "textureValue.z = czm_unpackFloat(texture2D(batchTexture, st + vec2(batchTextureStep.x * 2.0, 0.0))); \n" +
  480. "textureValue.w = czm_unpackFloat(texture2D(batchTexture, st + vec2(batchTextureStep.x * 3.0, 0.0))); \n";
  481. } else {
  482. glslFunction += " vec4 textureValue = texture2D(batchTexture, st); \n";
  483. }
  484. glslFunction +=
  485. " " +
  486. functionReturnType +
  487. " value = textureValue" +
  488. functionReturnValue +
  489. "; \n";
  490. if (
  491. batchTable._pixelDatatype === PixelDatatype.UNSIGNED_BYTE &&
  492. attribute.componentDatatype === ComponentDatatype.UNSIGNED_BYTE &&
  493. !attribute.normalize
  494. ) {
  495. glslFunction += "value *= 255.0; \n";
  496. } else if (
  497. batchTable._pixelDatatype === PixelDatatype.FLOAT &&
  498. attribute.componentDatatype === ComponentDatatype.UNSIGNED_BYTE &&
  499. attribute.normalize
  500. ) {
  501. glslFunction += "value /= 255.0; \n";
  502. }
  503. glslFunction += " return value; \n" + "} \n";
  504. return glslFunction;
  505. }
  506. /**
  507. * Gets a function that will update a vertex shader to contain functions for looking up values in the batch table.
  508. *
  509. * @returns {BatchTable.updateVertexShaderSourceCallback} A callback for updating a vertex shader source.
  510. */
  511. BatchTable.prototype.getVertexShaderCallback = function () {
  512. var attributes = this._attributes;
  513. if (attributes.length === 0) {
  514. return function (source) {
  515. return source;
  516. };
  517. }
  518. var batchTableShader = "uniform highp sampler2D batchTexture; \n";
  519. batchTableShader += getGlslComputeSt(this) + "\n";
  520. var length = attributes.length;
  521. for (var i = 0; i < length; ++i) {
  522. batchTableShader += getGlslAttributeFunction(this, i);
  523. }
  524. return function (source) {
  525. var mainIndex = source.indexOf("void main");
  526. var beforeMain = source.substring(0, mainIndex);
  527. var afterMain = source.substring(mainIndex);
  528. return beforeMain + "\n" + batchTableShader + "\n" + afterMain;
  529. };
  530. };
  531. /**
  532. * Returns true if this object was destroyed; otherwise, false.
  533. * <br /><br />
  534. * If this object was destroyed, it should not be used; calling any function other than
  535. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  536. *
  537. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  538. *
  539. * @see BatchTable#destroy
  540. */
  541. BatchTable.prototype.isDestroyed = function () {
  542. return false;
  543. };
  544. /**
  545. * Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
  546. * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
  547. * <br /><br />
  548. * Once an object is destroyed, it should not be used; calling any function other than
  549. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  550. * assign the return value (<code>undefined</code>) to the object as done in the example.
  551. *
  552. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  553. *
  554. * @see BatchTable#isDestroyed
  555. */
  556. BatchTable.prototype.destroy = function () {
  557. this._texture = this._texture && this._texture.destroy();
  558. return destroyObject(this);
  559. };
  560. /**
  561. * A callback for updating uniform maps.
  562. * @callback BatchTable.updateUniformMapCallback
  563. *
  564. * @param {Object} uniformMap The uniform map.
  565. * @returns {Object} The new uniform map with properties for retrieving values from the batch table.
  566. */
  567. /**
  568. * A callback for updating a vertex shader source.
  569. * @callback BatchTable.updateVertexShaderSourceCallback
  570. *
  571. * @param {String} vertexShaderSource The vertex shader source.
  572. * @returns {String} The new vertex shader source with the functions for retrieving batch table values injected.
  573. */
  574. export default BatchTable;