123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- /*global define*/
- define([
- '../Core/ComponentDatatype',
- '../Core/defaultValue',
- '../Core/defined',
- '../Core/destroyObject',
- '../Core/DeveloperError',
- '../Core/Math',
- './BufferUsage'
- ], function(
- ComponentDatatype,
- defaultValue,
- defined,
- destroyObject,
- DeveloperError,
- CesiumMath,
- BufferUsage) {
- "use strict";
- /**
- * @private
- */
- var VertexArrayFacade = function(context, attributes, sizeInVertices) {
- //>>includeStart('debug', pragmas.debug);
- if (!context) {
- throw new DeveloperError('context is required.');
- }
- if (!attributes || (attributes.length === 0)) {
- throw new DeveloperError('At least one attribute is required.');
- }
- //>>includeEnd('debug');
- var attrs = VertexArrayFacade._verifyAttributes(attributes);
- sizeInVertices = sizeInVertices || 0;
- var precreatedAttributes = [];
- var attributesByUsage = {};
- var attributesForUsage;
- var usage;
- // Bucket the attributes by usage.
- var length = attrs.length;
- for (var i = 0; i < length; ++i) {
- var attribute = attrs[i];
- // If the attribute already has a vertex buffer, we do not need
- // to manage a vertex buffer or typed array for it.
- if (attribute.vertexBuffer) {
- precreatedAttributes.push(attribute);
- continue;
- }
- usage = attribute.usage;
- attributesForUsage = attributesByUsage[usage];
- if (!defined(attributesForUsage)) {
- attributesForUsage = attributesByUsage[usage] = [];
- }
- attributesForUsage.push(attribute);
- }
- // A function to sort attributes by the size of their components. From left to right, a vertex
- // stores floats, shorts, and then bytes.
- function compare(left, right) {
- return ComponentDatatype.getSizeInBytes(right.componentDatatype) - ComponentDatatype.getSizeInBytes(left.componentDatatype);
- }
- // Create a buffer description for each usage.
- this._buffersByUsage = {};
- this._allBuffers = [];
- for (usage in attributesByUsage) {
- if (attributesByUsage.hasOwnProperty(usage)) {
- attributesForUsage = attributesByUsage[usage];
- attributesForUsage.sort(compare);
- var vertexSizeInBytes = VertexArrayFacade._vertexSizeInBytes(attributesForUsage);
- var usageEnum;
- switch (Number(usage)) {
- case BufferUsage.STATIC_DRAW:
- usageEnum = BufferUsage.STATIC_DRAW;
- break;
- case BufferUsage.STREAM_DRAW:
- usageEnum = BufferUsage.STREAM_DRAW;
- break;
- case BufferUsage.DYNAMIC_DRAW:
- usageEnum = BufferUsage.DYNAMIC_DRAW;
- break;
- }
- var buffer = {
- vertexSizeInBytes : vertexSizeInBytes,
- vertexBuffer : undefined,
- usage : usageEnum,
- needsCommit : false,
- arrayBuffer : undefined,
- arrayViews : VertexArrayFacade._createArrayViews(attributesForUsage, vertexSizeInBytes)
- };
- this._buffersByUsage[usage] = buffer;
- this._allBuffers.push(buffer);
- }
- }
- this._size = 0;
- this._precreated = precreatedAttributes;
- this._context = context;
- this.writers = undefined;
- this.va = undefined;
- this.resize(sizeInVertices);
- };
- VertexArrayFacade._verifyAttributes = function(attributes) {
- var attrs = [];
- for ( var i = 0; i < attributes.length; ++i) {
- var attribute = attributes[i];
- var attr = {
- index : defaultValue(attribute.index, i),
- enabled : defaultValue(attribute.enabled, true),
- componentsPerAttribute : attribute.componentsPerAttribute,
- componentDatatype : attribute.componentDatatype || ComponentDatatype.FLOAT,
- normalize : attribute.normalize || false,
- // There will be either a vertexBuffer or an [optional] usage.
- vertexBuffer : attribute.vertexBuffer,
- usage : attribute.usage || BufferUsage.STATIC_DRAW
- };
- attrs.push(attr);
- if ((attr.componentsPerAttribute !== 1) && (attr.componentsPerAttribute !== 2) && (attr.componentsPerAttribute !== 3) && (attr.componentsPerAttribute !== 4)) {
- throw new DeveloperError('attribute.componentsPerAttribute must be in the range [1, 4].');
- }
- var datatype = attr.componentDatatype;
- if (!ComponentDatatype.validate(datatype)) {
- throw new DeveloperError('Attribute must have a valid componentDatatype or not specify it.');
- }
- if (!BufferUsage.validate(attr.usage)) {
- throw new DeveloperError('Attribute must have a valid usage or not specify it.');
- }
- }
- // Verify all attribute names are unique.
- var uniqueIndices = new Array(attrs.length);
- for ( var j = 0; j < attrs.length; ++j) {
- var currentAttr = attrs[j];
- var index = currentAttr.index;
- if (uniqueIndices[index]) {
- throw new DeveloperError('Index ' + index + ' is used by more than one attribute.');
- }
- uniqueIndices[index] = true;
- }
- return attrs;
- };
- VertexArrayFacade._vertexSizeInBytes = function(attributes) {
- var sizeInBytes = 0;
- var length = attributes.length;
- for ( var i = 0; i < length; ++i) {
- var attribute = attributes[i];
- sizeInBytes += (attribute.componentsPerAttribute * ComponentDatatype.getSizeInBytes(attribute.componentDatatype));
- }
- var maxComponentSizeInBytes = (length > 0) ? ComponentDatatype.getSizeInBytes(attributes[0].componentDatatype) : 0; // Sorted by size
- var remainder = (maxComponentSizeInBytes > 0) ? (sizeInBytes % maxComponentSizeInBytes) : 0;
- var padding = (remainder === 0) ? 0 : (maxComponentSizeInBytes - remainder);
- sizeInBytes += padding;
- return sizeInBytes;
- };
- VertexArrayFacade._createArrayViews = function(attributes, vertexSizeInBytes) {
- var views = [];
- var offsetInBytes = 0;
- var length = attributes.length;
- for ( var i = 0; i < length; ++i) {
- var attribute = attributes[i];
- var componentDatatype = attribute.componentDatatype;
- views.push({
- index : attribute.index,
- enabled : attribute.enabled,
- componentsPerAttribute : attribute.componentsPerAttribute,
- componentDatatype : componentDatatype,
- normalize : attribute.normalize,
- offsetInBytes : offsetInBytes,
- vertexSizeInComponentType : vertexSizeInBytes / ComponentDatatype.getSizeInBytes(componentDatatype),
- view : undefined
- });
- offsetInBytes += (attribute.componentsPerAttribute * ComponentDatatype.getSizeInBytes(componentDatatype));
- }
- return views;
- };
- /**
- * Invalidates writers. Can't render again until commit is called.
- */
- VertexArrayFacade.prototype.resize = function(sizeInVertices) {
- this._size = sizeInVertices;
- var allBuffers = this._allBuffers;
- this.writers = [];
- for (var i = 0, len = allBuffers.length; i < len; ++i) {
- var buffer = allBuffers[i];
- VertexArrayFacade._resize(buffer, this._size);
- // Reserving invalidates the writers, so if client's cache them, they need to invalidate their cache.
- VertexArrayFacade._appendWriters(this.writers, buffer);
- }
- // VAs are recreated next time commit is called.
- destroyVA(this);
- };
- VertexArrayFacade._resize = function(buffer, size) {
- if (buffer.vertexSizeInBytes > 0) {
- // Create larger array buffer
- var arrayBuffer = new ArrayBuffer(size * buffer.vertexSizeInBytes);
- // Copy contents from previous array buffer
- if (defined(buffer.arrayBuffer)) {
- var destView = new Uint8Array(arrayBuffer);
- var sourceView = new Uint8Array(buffer.arrayBuffer);
- var sourceLength = sourceView.length;
- for ( var j = 0; j < sourceLength; ++j) {
- destView[j] = sourceView[j];
- }
- }
- // Create typed views into the new array buffer
- var views = buffer.arrayViews;
- var length = views.length;
- for ( var i = 0; i < length; ++i) {
- var view = views[i];
- view.view = ComponentDatatype.createArrayBufferView(view.componentDatatype, arrayBuffer, view.offsetInBytes);
- }
- buffer.arrayBuffer = arrayBuffer;
- }
- };
- var createWriters = [
- // 1 component per attribute
- function(buffer, view, vertexSizeInComponentType) {
- return function(index, attribute) {
- view[index * vertexSizeInComponentType] = attribute;
- buffer.needsCommit = true;
- };
- },
- // 2 component per attribute
- function(buffer, view, vertexSizeInComponentType) {
- return function(index, component0, component1) {
- var i = index * vertexSizeInComponentType;
- view[i] = component0;
- view[i + 1] = component1;
- buffer.needsCommit = true;
- };
- },
- // 3 component per attribute
- function(buffer, view, vertexSizeInComponentType) {
- return function(index, component0, component1, component2) {
- var i = index * vertexSizeInComponentType;
- view[i] = component0;
- view[i + 1] = component1;
- view[i + 2] = component2;
- buffer.needsCommit = true;
- };
- },
- // 4 component per attribute
- function(buffer, view, vertexSizeInComponentType) {
- return function(index, component0, component1, component2, component3) {
- var i = index * vertexSizeInComponentType;
- view[i] = component0;
- view[i + 1] = component1;
- view[i + 2] = component2;
- view[i + 3] = component3;
- buffer.needsCommit = true;
- };
- }];
- VertexArrayFacade._appendWriters = function(writers, buffer) {
- var arrayViews = buffer.arrayViews;
- var length = arrayViews.length;
- for ( var i = 0; i < length; ++i) {
- var arrayView = arrayViews[i];
- writers[arrayView.index] = createWriters[arrayView.componentsPerAttribute - 1](buffer, arrayView.view, arrayView.vertexSizeInComponentType);
- }
- };
- VertexArrayFacade.prototype.commit = function(indexBuffer) {
- var recreateVA = false;
- var allBuffers = this._allBuffers;
- var buffer;
- for (var i = 0, len = allBuffers.length; i < len; ++i) {
- buffer = allBuffers[i];
- recreateVA = commit(this, buffer) || recreateVA;
- }
- ///////////////////////////////////////////////////////////////////////
- if (recreateVA || !defined(this.va)) {
- var buffersByUsage = this._buffersByUsage;
- destroyVA(this);
- var va = this.va = [];
- var numberOfVertexArrays = Math.ceil(this._size / CesiumMath.SIXTY_FOUR_KILOBYTES);
- for ( var k = 0; k < numberOfVertexArrays; ++k) {
- var attributes = [];
- for (var usage in buffersByUsage) {
- if (buffersByUsage.hasOwnProperty(usage)) {
- buffer = buffersByUsage[usage];
- VertexArrayFacade._appendAttributes(attributes, buffer, k * (buffer.vertexSizeInBytes * CesiumMath.SIXTY_FOUR_KILOBYTES));
- }
- }
- attributes = attributes.concat(this._precreated);
- va.push({
- va : this._context.createVertexArray(attributes, indexBuffer),
- indicesCount : 1.5 * ((k !== (numberOfVertexArrays - 1)) ? CesiumMath.SIXTY_FOUR_KILOBYTES : (this._size % CesiumMath.SIXTY_FOUR_KILOBYTES))
- // TODO: not hardcode 1.5
- });
- }
- }
- };
- function commit(vertexArrayFacade, buffer) {
- if (buffer.needsCommit && (buffer.vertexSizeInBytes > 0)) {
- buffer.needsCommit = false;
- var vertexBuffer = buffer.vertexBuffer;
- var vertexBufferSizeInBytes = vertexArrayFacade._size * buffer.vertexSizeInBytes;
- var vertexBufferDefined = defined(vertexBuffer);
- if (!vertexBufferDefined || (vertexBuffer.sizeInBytes < vertexBufferSizeInBytes)) {
- if (vertexBufferDefined) {
- vertexBuffer.destroy();
- }
- buffer.vertexBuffer = vertexArrayFacade._context.createVertexBuffer(buffer.arrayBuffer, buffer.usage);
- buffer.vertexBuffer.vertexArrayDestroyable = false;
- return true; // Created new vertex buffer
- }
- buffer.vertexBuffer.copyFromArrayView(buffer.arrayBuffer);
- }
- return false; // Did not create new vertex buffer
- }
- VertexArrayFacade._appendAttributes = function(attributes, buffer, vertexBufferOffset) {
- var arrayViews = buffer.arrayViews;
- var length = arrayViews.length;
- for ( var i = 0; i < length; ++i) {
- var view = arrayViews[i];
- attributes.push({
- index : view.index,
- enabled : view.enabled,
- componentsPerAttribute : view.componentsPerAttribute,
- componentDatatype : view.componentDatatype,
- normalize : view.normalize,
- vertexBuffer : buffer.vertexBuffer,
- offsetInBytes : vertexBufferOffset + view.offsetInBytes,
- strideInBytes : buffer.vertexSizeInBytes
- });
- }
- };
- VertexArrayFacade.prototype.subCommit = function(offsetInVertices, lengthInVertices) {
- //>>includeStart('debug', pragmas.debug);
- if (offsetInVertices < 0 || offsetInVertices >= this._size) {
- throw new DeveloperError('offsetInVertices must be greater than or equal to zero and less than the vertex array size.');
- }
- if (offsetInVertices + lengthInVertices > this._size) {
- throw new DeveloperError('offsetInVertices + lengthInVertices cannot exceed the vertex array size.');
- }
- //>>includeEnd('debug');
- var allBuffers = this._allBuffers;
- for (var i = 0, len = allBuffers.length; i < len; ++i) {
- subCommit(allBuffers[i], offsetInVertices, lengthInVertices);
- }
- };
- function subCommit(buffer, offsetInVertices, lengthInVertices) {
- if (buffer.needsCommit && (buffer.vertexSizeInBytes > 0)) {
- var byteOffset = buffer.vertexSizeInBytes * offsetInVertices;
- var byteLength = buffer.vertexSizeInBytes * lengthInVertices;
- // PERFORMANCE_IDEA: If we want to get really crazy, we could consider updating
- // individual attributes instead of the entire (sub-)vertex.
- //
- // PERFORMANCE_IDEA: Does creating the typed view add too much GC overhead?
- buffer.vertexBuffer.copyFromArrayView(new Uint8Array(buffer.arrayBuffer, byteOffset, byteLength), byteOffset);
- }
- }
- VertexArrayFacade.prototype.endSubCommits = function() {
- var allBuffers = this._allBuffers;
- for (var i = 0, len = allBuffers.length; i < len; ++i) {
- allBuffers[i].needsCommit = false;
- }
- };
- function destroyVA(vertexArrayFacade) {
- var va = vertexArrayFacade.va;
- if (!defined(va)) {
- return;
- }
- var length = va.length;
- for (var i = 0; i < length; ++i) {
- va[i].va.destroy();
- }
- vertexArrayFacade.va = undefined;
- }
- VertexArrayFacade.prototype.isDestroyed = function() {
- return false;
- };
- VertexArrayFacade.prototype.destroy = function() {
- var allBuffers = this._allBuffers;
- for (var i = 0, len = allBuffers.length; i < len; ++i) {
- var buffer = allBuffers[i];
- buffer.vertexBuffer = buffer.vertexBuffer && buffer.vertexBuffer.destroy();
- }
- destroyVA(this);
- return destroyObject(this);
- };
- return VertexArrayFacade;
- });
|