VertexArrayFacade.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. import Check from '../Core/Check.js';
  2. import ComponentDatatype from '../Core/ComponentDatatype.js';
  3. import defaultValue from '../Core/defaultValue.js';
  4. import defined from '../Core/defined.js';
  5. import destroyObject from '../Core/destroyObject.js';
  6. import DeveloperError from '../Core/DeveloperError.js';
  7. import CesiumMath from '../Core/Math.js';
  8. import Buffer from './Buffer.js';
  9. import BufferUsage from './BufferUsage.js';
  10. import VertexArray from './VertexArray.js';
  11. /**
  12. * @private
  13. */
  14. function VertexArrayFacade(context, attributes, sizeInVertices, instanced) {
  15. //>>includeStart('debug', pragmas.debug);
  16. Check.defined('context', context);
  17. if (!attributes || (attributes.length === 0)) {
  18. throw new DeveloperError('At least one attribute is required.');
  19. }
  20. //>>includeEnd('debug');
  21. var attrs = VertexArrayFacade._verifyAttributes(attributes);
  22. sizeInVertices = defaultValue(sizeInVertices, 0);
  23. var precreatedAttributes = [];
  24. var attributesByUsage = {};
  25. var attributesForUsage;
  26. var usage;
  27. // Bucket the attributes by usage.
  28. var length = attrs.length;
  29. for (var i = 0; i < length; ++i) {
  30. var attribute = attrs[i];
  31. // If the attribute already has a vertex buffer, we do not need
  32. // to manage a vertex buffer or typed array for it.
  33. if (attribute.vertexBuffer) {
  34. precreatedAttributes.push(attribute);
  35. continue;
  36. }
  37. usage = attribute.usage;
  38. attributesForUsage = attributesByUsage[usage];
  39. if (!defined(attributesForUsage)) {
  40. attributesForUsage = attributesByUsage[usage] = [];
  41. }
  42. attributesForUsage.push(attribute);
  43. }
  44. // A function to sort attributes by the size of their components. From left to right, a vertex
  45. // stores floats, shorts, and then bytes.
  46. function compare(left, right) {
  47. return ComponentDatatype.getSizeInBytes(right.componentDatatype) - ComponentDatatype.getSizeInBytes(left.componentDatatype);
  48. }
  49. this._allBuffers = [];
  50. for (usage in attributesByUsage) {
  51. if (attributesByUsage.hasOwnProperty(usage)) {
  52. attributesForUsage = attributesByUsage[usage];
  53. attributesForUsage.sort(compare);
  54. var vertexSizeInBytes = VertexArrayFacade._vertexSizeInBytes(attributesForUsage);
  55. var bufferUsage = attributesForUsage[0].usage;
  56. var buffer = {
  57. vertexSizeInBytes : vertexSizeInBytes,
  58. vertexBuffer : undefined,
  59. usage : bufferUsage,
  60. needsCommit : false,
  61. arrayBuffer : undefined,
  62. arrayViews : VertexArrayFacade._createArrayViews(attributesForUsage, vertexSizeInBytes)
  63. };
  64. this._allBuffers.push(buffer);
  65. }
  66. }
  67. this._size = 0;
  68. this._instanced = defaultValue(instanced, false);
  69. this._precreated = precreatedAttributes;
  70. this._context = context;
  71. this.writers = undefined;
  72. this.va = undefined;
  73. this.resize(sizeInVertices);
  74. }
  75. VertexArrayFacade._verifyAttributes = function(attributes) {
  76. var attrs = [];
  77. for ( var i = 0; i < attributes.length; ++i) {
  78. var attribute = attributes[i];
  79. var attr = {
  80. index : defaultValue(attribute.index, i),
  81. enabled : defaultValue(attribute.enabled, true),
  82. componentsPerAttribute : attribute.componentsPerAttribute,
  83. componentDatatype : defaultValue(attribute.componentDatatype, ComponentDatatype.FLOAT),
  84. normalize : defaultValue(attribute.normalize, false),
  85. // There will be either a vertexBuffer or an [optional] usage.
  86. vertexBuffer : attribute.vertexBuffer,
  87. usage : defaultValue(attribute.usage, BufferUsage.STATIC_DRAW)
  88. };
  89. attrs.push(attr);
  90. //>>includeStart('debug', pragmas.debug);
  91. if ((attr.componentsPerAttribute !== 1) && (attr.componentsPerAttribute !== 2) && (attr.componentsPerAttribute !== 3) && (attr.componentsPerAttribute !== 4)) {
  92. throw new DeveloperError('attribute.componentsPerAttribute must be in the range [1, 4].');
  93. }
  94. var datatype = attr.componentDatatype;
  95. if (!ComponentDatatype.validate(datatype)) {
  96. throw new DeveloperError('Attribute must have a valid componentDatatype or not specify it.');
  97. }
  98. if (!BufferUsage.validate(attr.usage)) {
  99. throw new DeveloperError('Attribute must have a valid usage or not specify it.');
  100. }
  101. //>>includeEnd('debug');
  102. }
  103. // Verify all attribute names are unique.
  104. var uniqueIndices = new Array(attrs.length);
  105. for ( var j = 0; j < attrs.length; ++j) {
  106. var currentAttr = attrs[j];
  107. var index = currentAttr.index;
  108. //>>includeStart('debug', pragmas.debug);
  109. if (uniqueIndices[index]) {
  110. throw new DeveloperError('Index ' + index + ' is used by more than one attribute.');
  111. }
  112. //>>includeEnd('debug');
  113. uniqueIndices[index] = true;
  114. }
  115. return attrs;
  116. };
  117. VertexArrayFacade._vertexSizeInBytes = function(attributes) {
  118. var sizeInBytes = 0;
  119. var length = attributes.length;
  120. for ( var i = 0; i < length; ++i) {
  121. var attribute = attributes[i];
  122. sizeInBytes += (attribute.componentsPerAttribute * ComponentDatatype.getSizeInBytes(attribute.componentDatatype));
  123. }
  124. var maxComponentSizeInBytes = (length > 0) ? ComponentDatatype.getSizeInBytes(attributes[0].componentDatatype) : 0; // Sorted by size
  125. var remainder = (maxComponentSizeInBytes > 0) ? (sizeInBytes % maxComponentSizeInBytes) : 0;
  126. var padding = (remainder === 0) ? 0 : (maxComponentSizeInBytes - remainder);
  127. sizeInBytes += padding;
  128. return sizeInBytes;
  129. };
  130. VertexArrayFacade._createArrayViews = function(attributes, vertexSizeInBytes) {
  131. var views = [];
  132. var offsetInBytes = 0;
  133. var length = attributes.length;
  134. for ( var i = 0; i < length; ++i) {
  135. var attribute = attributes[i];
  136. var componentDatatype = attribute.componentDatatype;
  137. views.push({
  138. index : attribute.index,
  139. enabled : attribute.enabled,
  140. componentsPerAttribute : attribute.componentsPerAttribute,
  141. componentDatatype : componentDatatype,
  142. normalize : attribute.normalize,
  143. offsetInBytes : offsetInBytes,
  144. vertexSizeInComponentType : vertexSizeInBytes / ComponentDatatype.getSizeInBytes(componentDatatype),
  145. view : undefined
  146. });
  147. offsetInBytes += (attribute.componentsPerAttribute * ComponentDatatype.getSizeInBytes(componentDatatype));
  148. }
  149. return views;
  150. };
  151. /**
  152. * Invalidates writers. Can't render again until commit is called.
  153. */
  154. VertexArrayFacade.prototype.resize = function(sizeInVertices) {
  155. this._size = sizeInVertices;
  156. var allBuffers = this._allBuffers;
  157. this.writers = [];
  158. for (var i = 0, len = allBuffers.length; i < len; ++i) {
  159. var buffer = allBuffers[i];
  160. VertexArrayFacade._resize(buffer, this._size);
  161. // Reserving invalidates the writers, so if client's cache them, they need to invalidate their cache.
  162. VertexArrayFacade._appendWriters(this.writers, buffer);
  163. }
  164. // VAs are recreated next time commit is called.
  165. destroyVA(this);
  166. };
  167. VertexArrayFacade._resize = function(buffer, size) {
  168. if (buffer.vertexSizeInBytes > 0) {
  169. // Create larger array buffer
  170. var arrayBuffer = new ArrayBuffer(size * buffer.vertexSizeInBytes);
  171. // Copy contents from previous array buffer
  172. if (defined(buffer.arrayBuffer)) {
  173. var destView = new Uint8Array(arrayBuffer);
  174. var sourceView = new Uint8Array(buffer.arrayBuffer);
  175. var sourceLength = sourceView.length;
  176. for ( var j = 0; j < sourceLength; ++j) {
  177. destView[j] = sourceView[j];
  178. }
  179. }
  180. // Create typed views into the new array buffer
  181. var views = buffer.arrayViews;
  182. var length = views.length;
  183. for ( var i = 0; i < length; ++i) {
  184. var view = views[i];
  185. view.view = ComponentDatatype.createArrayBufferView(view.componentDatatype, arrayBuffer, view.offsetInBytes);
  186. }
  187. buffer.arrayBuffer = arrayBuffer;
  188. }
  189. };
  190. var createWriters = [
  191. // 1 component per attribute
  192. function(buffer, view, vertexSizeInComponentType) {
  193. return function(index, attribute) {
  194. view[index * vertexSizeInComponentType] = attribute;
  195. buffer.needsCommit = true;
  196. };
  197. },
  198. // 2 component per attribute
  199. function(buffer, view, vertexSizeInComponentType) {
  200. return function(index, component0, component1) {
  201. var i = index * vertexSizeInComponentType;
  202. view[i] = component0;
  203. view[i + 1] = component1;
  204. buffer.needsCommit = true;
  205. };
  206. },
  207. // 3 component per attribute
  208. function(buffer, view, vertexSizeInComponentType) {
  209. return function(index, component0, component1, component2) {
  210. var i = index * vertexSizeInComponentType;
  211. view[i] = component0;
  212. view[i + 1] = component1;
  213. view[i + 2] = component2;
  214. buffer.needsCommit = true;
  215. };
  216. },
  217. // 4 component per attribute
  218. function(buffer, view, vertexSizeInComponentType) {
  219. return function(index, component0, component1, component2, component3) {
  220. var i = index * vertexSizeInComponentType;
  221. view[i] = component0;
  222. view[i + 1] = component1;
  223. view[i + 2] = component2;
  224. view[i + 3] = component3;
  225. buffer.needsCommit = true;
  226. };
  227. }];
  228. VertexArrayFacade._appendWriters = function(writers, buffer) {
  229. var arrayViews = buffer.arrayViews;
  230. var length = arrayViews.length;
  231. for ( var i = 0; i < length; ++i) {
  232. var arrayView = arrayViews[i];
  233. writers[arrayView.index] = createWriters[arrayView.componentsPerAttribute - 1](buffer, arrayView.view, arrayView.vertexSizeInComponentType);
  234. }
  235. };
  236. VertexArrayFacade.prototype.commit = function(indexBuffer) {
  237. var recreateVA = false;
  238. var allBuffers = this._allBuffers;
  239. var buffer;
  240. var i;
  241. var length;
  242. for (i = 0, length = allBuffers.length; i < length; ++i) {
  243. buffer = allBuffers[i];
  244. recreateVA = commit(this, buffer) || recreateVA;
  245. }
  246. ///////////////////////////////////////////////////////////////////////
  247. if (recreateVA || !defined(this.va)) {
  248. destroyVA(this);
  249. var va = this.va = [];
  250. var chunkSize = CesiumMath.SIXTY_FOUR_KILOBYTES - 4; // The 65535 index is reserved for primitive restart. Reserve the last 4 indices so that billboard quads are not broken up.
  251. var numberOfVertexArrays = (defined(indexBuffer) && !this._instanced) ? Math.ceil(this._size / chunkSize) : 1;
  252. for ( var k = 0; k < numberOfVertexArrays; ++k) {
  253. var attributes = [];
  254. for (i = 0, length = allBuffers.length; i < length; ++i) {
  255. buffer = allBuffers[i];
  256. var offset = k * (buffer.vertexSizeInBytes * chunkSize);
  257. VertexArrayFacade._appendAttributes(attributes, buffer, offset, this._instanced);
  258. }
  259. attributes = attributes.concat(this._precreated);
  260. va.push({
  261. va : new VertexArray({
  262. context : this._context,
  263. attributes : attributes,
  264. indexBuffer : indexBuffer
  265. }),
  266. indicesCount : 1.5 * ((k !== (numberOfVertexArrays - 1)) ? chunkSize : (this._size % chunkSize))
  267. // TODO: not hardcode 1.5, this assumes 6 indices per 4 vertices (as for Billboard quads).
  268. });
  269. }
  270. }
  271. };
  272. function commit(vertexArrayFacade, buffer) {
  273. if (buffer.needsCommit && (buffer.vertexSizeInBytes > 0)) {
  274. buffer.needsCommit = false;
  275. var vertexBuffer = buffer.vertexBuffer;
  276. var vertexBufferSizeInBytes = vertexArrayFacade._size * buffer.vertexSizeInBytes;
  277. var vertexBufferDefined = defined(vertexBuffer);
  278. if (!vertexBufferDefined || (vertexBuffer.sizeInBytes < vertexBufferSizeInBytes)) {
  279. if (vertexBufferDefined) {
  280. vertexBuffer.destroy();
  281. }
  282. buffer.vertexBuffer = Buffer.createVertexBuffer({
  283. context : vertexArrayFacade._context,
  284. typedArray : buffer.arrayBuffer,
  285. usage : buffer.usage
  286. });
  287. buffer.vertexBuffer.vertexArrayDestroyable = false;
  288. return true; // Created new vertex buffer
  289. }
  290. buffer.vertexBuffer.copyFromArrayView(buffer.arrayBuffer);
  291. }
  292. return false; // Did not create new vertex buffer
  293. }
  294. VertexArrayFacade._appendAttributes = function(attributes, buffer, vertexBufferOffset, instanced) {
  295. var arrayViews = buffer.arrayViews;
  296. var length = arrayViews.length;
  297. for ( var i = 0; i < length; ++i) {
  298. var view = arrayViews[i];
  299. attributes.push({
  300. index : view.index,
  301. enabled : view.enabled,
  302. componentsPerAttribute : view.componentsPerAttribute,
  303. componentDatatype : view.componentDatatype,
  304. normalize : view.normalize,
  305. vertexBuffer : buffer.vertexBuffer,
  306. offsetInBytes : vertexBufferOffset + view.offsetInBytes,
  307. strideInBytes : buffer.vertexSizeInBytes,
  308. instanceDivisor : instanced ? 1 : 0
  309. });
  310. }
  311. };
  312. VertexArrayFacade.prototype.subCommit = function(offsetInVertices, lengthInVertices) {
  313. //>>includeStart('debug', pragmas.debug);
  314. if (offsetInVertices < 0 || offsetInVertices >= this._size) {
  315. throw new DeveloperError('offsetInVertices must be greater than or equal to zero and less than the vertex array size.');
  316. }
  317. if (offsetInVertices + lengthInVertices > this._size) {
  318. throw new DeveloperError('offsetInVertices + lengthInVertices cannot exceed the vertex array size.');
  319. }
  320. //>>includeEnd('debug');
  321. var allBuffers = this._allBuffers;
  322. for (var i = 0, len = allBuffers.length; i < len; ++i) {
  323. subCommit(allBuffers[i], offsetInVertices, lengthInVertices);
  324. }
  325. };
  326. function subCommit(buffer, offsetInVertices, lengthInVertices) {
  327. if (buffer.needsCommit && (buffer.vertexSizeInBytes > 0)) {
  328. var byteOffset = buffer.vertexSizeInBytes * offsetInVertices;
  329. var byteLength = buffer.vertexSizeInBytes * lengthInVertices;
  330. // PERFORMANCE_IDEA: If we want to get really crazy, we could consider updating
  331. // individual attributes instead of the entire (sub-)vertex.
  332. //
  333. // PERFORMANCE_IDEA: Does creating the typed view add too much GC overhead?
  334. buffer.vertexBuffer.copyFromArrayView(new Uint8Array(buffer.arrayBuffer, byteOffset, byteLength), byteOffset);
  335. }
  336. }
  337. VertexArrayFacade.prototype.endSubCommits = function() {
  338. var allBuffers = this._allBuffers;
  339. for (var i = 0, len = allBuffers.length; i < len; ++i) {
  340. allBuffers[i].needsCommit = false;
  341. }
  342. };
  343. function destroyVA(vertexArrayFacade) {
  344. var va = vertexArrayFacade.va;
  345. if (!defined(va)) {
  346. return;
  347. }
  348. var length = va.length;
  349. for (var i = 0; i < length; ++i) {
  350. va[i].va.destroy();
  351. }
  352. vertexArrayFacade.va = undefined;
  353. }
  354. VertexArrayFacade.prototype.isDestroyed = function() {
  355. return false;
  356. };
  357. VertexArrayFacade.prototype.destroy = function() {
  358. var allBuffers = this._allBuffers;
  359. for (var i = 0, len = allBuffers.length; i < len; ++i) {
  360. var buffer = allBuffers[i];
  361. buffer.vertexBuffer = buffer.vertexBuffer && buffer.vertexBuffer.destroy();
  362. }
  363. destroyVA(this);
  364. return destroyObject(this);
  365. };
  366. export default VertexArrayFacade;