diff --git a/doc/errors/index.md b/doc/errors/index.md index 687d9efbf1..6aedfcb807 100644 --- a/doc/errors/index.md +++ b/doc/errors/index.md @@ -228,3 +228,7 @@ Missing or invalid `size`. ### 61 Cannot determine IIIF Image API version from provided image information JSON. + +### 62 + +A `WebGLArrayBuffer` must either be of type `ELEMENT_ARRAY_BUFFER` or `ARRAY_BUFFER`. diff --git a/src/ol/webgl/Buffer.js b/src/ol/webgl/Buffer.js index 21e5b25c8c..dc07e9c861 100644 --- a/src/ol/webgl/Buffer.js +++ b/src/ol/webgl/Buffer.js @@ -2,6 +2,9 @@ * @module ol/webgl/Buffer */ import {STATIC_DRAW, STREAM_DRAW, DYNAMIC_DRAW} from '../webgl.js'; +import {includes} from '../array.js'; +import {ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, EXTENSIONS as WEBGL_EXTENSIONS} from '../webgl.js'; +import {assert} from '../asserts.js'; /** * Used to describe the intended usage for the data: `STATIC_DRAW`, `STREAM_DRAW` @@ -17,43 +20,110 @@ export const BufferUsage = { /** * @classdesc * Object used to store an array of data as well as usage information for that data. - * See the documentation of [WebGLRenderingContext.bufferData](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bufferData) for more info. + * Stores typed arrays internally, either Float32Array or Uint16/32Array depending on + * the buffer type (ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER) and available extensions. + * + * To populate the array, you can either use: + * * A size using `#ofSize(buffer)` + * * An `ArrayBuffer` object using `#fromArrayBuffer(buffer)` + * * A plain array using `#fromArray(array)` + * + * Note: + * See the documentation of [WebGLRenderingContext.bufferData](https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/bufferData) + * for more info on buffer usage. * @api */ class WebGLArrayBuffer { /** - * @param {Array=} opt_arr Array. - * @param {number=} opt_usage Usage, either `STATIC_DRAW`, `STREAM_DRAW` or `DYNAMIC_DRAW`. Default is `DYNAMIC_DRAW`. + * @param {number} type Buffer type, either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER. + * @param {number=} opt_usage Intended usage, either `STATIC_DRAW`, `STREAM_DRAW` or `DYNAMIC_DRAW`. + * Default is `DYNAMIC_DRAW`. */ - constructor(opt_arr, opt_usage) { + constructor(type, opt_usage) { /** * @private - * @type {Array} + * @type {Float32Array|Uint32Array|Uint16Array} */ - this.arr_ = opt_arr !== undefined ? opt_arr : []; + this.array = null; /** * @private * @type {number} */ - this.usage_ = opt_usage !== undefined ? opt_usage : BufferUsage.STATIC_DRAW; + this.type = type; + assert(type === ARRAY_BUFFER || type === ELEMENT_ARRAY_BUFFER, 62); + + /** + * @private + * @type {number} + */ + this.usage = opt_usage !== undefined ? opt_usage : BufferUsage.STATIC_DRAW; } /** - * @return {Array} Array. + * Populates the buffer with an array of the given size (all values will be zeroes). + * @param {number} size Array size + */ + ofSize(size) { + this.array = new (getArrayClassForType(this.type))(size); + } + + /** + * Populates the buffer with an array of the given size (all values will be zeroes). + * @param {Array} array Numerical array + */ + fromArray(array) { + this.array = (getArrayClassForType(this.type)).from(array); + } + + /** + * Populates the buffer with a raw binary array buffer. + * @param {ArrayBuffer} buffer Raw binary buffer to populate the array with. Note that this buffer must have been + * initialized for the same typed array class. + */ + fromArrayBuffer(buffer) { + this.array = new (getArrayClassForType(this.type))(buffer); + } + + /** + * @return {number} Buffer type. + */ + getType() { + return this.type; + } + + /** + * @return {Float32Array|Uint32Array|Uint16Array} Array. */ getArray() { - return this.arr_; + return this.array; } /** * @return {number} Usage. */ getUsage() { - return this.usage_; + return this.usage; + } +} + +/** + * Returns a typed array constructor based on the given buffer type + * @param {number} type Buffer type, either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER. + * @returns {Float32ArrayConstructor|Uint16ArrayConstructor|Uint32ArrayConstructor} The typed array class to use for this buffer. + */ +export function getArrayClassForType(type) { + switch (type) { + case ARRAY_BUFFER: + return Float32Array; + case ELEMENT_ARRAY_BUFFER: + const hasExtension = includes(WEBGL_EXTENSIONS, 'OES_element_index_uint'); + return hasExtension ? Uint32Array : Uint16Array; + default: + return Float32Array; } } diff --git a/test/spec/ol/webgl/buffer.test.js b/test/spec/ol/webgl/buffer.test.js index af590da0e4..8c1102b5ea 100644 --- a/test/spec/ol/webgl/buffer.test.js +++ b/test/spec/ol/webgl/buffer.test.js @@ -1,51 +1,82 @@ -import WebGLArrayBuffer from '../../../../src/ol/webgl/Buffer.js'; +import WebGLArrayBuffer, {getArrayClassForType} from '../../../../src/ol/webgl/Buffer.js'; +import { + ARRAY_BUFFER, + ELEMENT_ARRAY_BUFFER, + EXTENSIONS as WEBGL_EXTENSIONS, + STATIC_DRAW, + STREAM_DRAW +} from '../../../../src/ol/webgl.js'; describe('ol.webgl.Buffer', function() { describe('constructor', function() { - describe('without an argument', function() { - - let b; - beforeEach(function() { - b = new WebGLArrayBuffer(); - }); - - it('constructs an empty instance', function() { - expect(b.getArray()).to.be.empty(); - }); - + it('sets the default usage when not specified', function() { + const b = new WebGLArrayBuffer(ARRAY_BUFFER); + expect(b.getUsage()).to.be(STATIC_DRAW); }); - describe('with a single array argument', function() { - - let b; - beforeEach(function() { - b = new WebGLArrayBuffer([0, 1, 2, 3]); - }); - - it('constructs a populated instance', function() { - expect(b.getArray()).to.eql([0, 1, 2, 3]); - }); - + it('sets the given usage when specified', function() { + const b = new WebGLArrayBuffer(ARRAY_BUFFER, STREAM_DRAW); + expect(b.getUsage()).to.be(STREAM_DRAW); }); + it('raises an error if an incorrect type is used', function(done) { + try { + new WebGLArrayBuffer(1234); + } catch (e) { + done(); + } + done(true); + }); }); - describe('with an empty instance', function() { + describe('#getArrayClassForType', function() { + it('returns the correct typed array constructor', function() { + expect(getArrayClassForType(ARRAY_BUFFER)).to.be(Float32Array); + expect(getArrayClassForType(ELEMENT_ARRAY_BUFFER)).to.be(Uint32Array); + }); + + it('returns the correct typed array constructor (without OES uint extension)', function() { + WEBGL_EXTENSIONS.length = 0; + expect(getArrayClassForType(ELEMENT_ARRAY_BUFFER)).to.be(Uint16Array); + }); + }); + + describe('populate methods', function() { let b; beforeEach(function() { - b = new WebGLArrayBuffer(); + b = new WebGLArrayBuffer(ARRAY_BUFFER); }); - describe('getArray', function() { + it('initializes the array using a size', function() { + b.ofSize(12); + expect(b.getArray().length).to.be(12); + expect(b.getArray()[0]).to.be(0); + expect(b.getArray()[11]).to.be(0); + }); - it('returns an empty array', function() { - expect(b.getArray()).to.be.empty(); - }); + it('initializes the array using an array', function() { + b.fromArray([1, 2, 3, 4, 5]); + expect(b.getArray().length).to.be(5); + expect(b.getArray()[0]).to.be(1); + expect(b.getArray()[1]).to.be(2); + expect(b.getArray()[2]).to.be(3); + expect(b.getArray()[3]).to.be(4); + expect(b.getArray()[4]).to.be(5); + }); + it('initializes the array using a size', function() { + const a = Float32Array.of(1, 2, 3, 4, 5); + b.fromArrayBuffer(a.buffer); + expect(b.getArray().length).to.be(5); + expect(b.getArray()[0]).to.be(1); + expect(b.getArray()[1]).to.be(2); + expect(b.getArray()[2]).to.be(3); + expect(b.getArray()[3]).to.be(4); + expect(b.getArray()[4]).to.be(5); }); });