VertexArrayFacade.js 17 KB

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