Webgl / clarified the buffer binding/flushing logic

The Webgl points layer renderer has also been optimized accordingly,
giving out much better performance.
This commit is contained in:
Olivier Guyot
2019-04-02 21:05:28 +02:00
parent 7c1df60d06
commit b955579a9c
2 changed files with 39 additions and 16 deletions
+3
View File
@@ -323,6 +323,9 @@ class WebGLPointsLayerRenderer extends LayerRenderer {
baseIndex + 1, baseIndex + 2, baseIndex + 3 baseIndex + 1, baseIndex + 2, baseIndex + 3
); );
}); });
this.helper_.flushBufferData(ARRAY_BUFFER, this.verticesBuffer_);
this.helper_.flushBufferData(ELEMENT_ARRAY_BUFFER, this.indicesBuffer_);
} }
// write new data // write new data
+36 -16
View File
@@ -23,8 +23,8 @@ import {getContext} from '../webgl';
/** /**
* @typedef {Object} BufferCacheEntry * @typedef {Object} BufferCacheEntry
* @property {import("./Buffer.js").default} buf * @property {import("./Buffer.js").default} buffer
* @property {WebGLBuffer} buffer * @property {WebGLBuffer} webGlBuffer
*/ */
/** /**
@@ -153,15 +153,24 @@ export const DefaultAttrib = {
* ### Binding WebGL buffers and flushing data into them: * ### Binding WebGL buffers and flushing data into them:
* *
* Data that must be passed to the GPU has to be transferred using `WebGLArrayBuffer` objects. * Data that must be passed to the GPU has to be transferred using `WebGLArrayBuffer` objects.
* A buffer has to be created only once, but must be bound everytime the data it holds is changed. Using `WebGLHelper.bindBuffer` * A buffer has to be created only once, but must be bound everytime the buffer content should be used for rendering.
* will bind the buffer and flush the new data to the GPU. * This is done using `WebGLHelper.bindBuffer`.
* When the buffer's array content has changed, the new data has to be flushed to the GPU memory; this is done using
* `WebGLHelper.flushBufferData`. Note: this operation is expensive and should be done as infrequently as possible.
* *
* For now, the `WebGLHelper` class expects {@link module:ol/webgl/Buffer~WebGLArrayBuffer} objects. * When binding a `WebGLArrayBuffer`, a `target` parameter must be given: it should be either {@link module:ol/webgl~ARRAY_BUFFER}
* (if the buffer contains vertices data) or {@link module:ol/webgl~ELEMENT_ARRAY_BUFFER} (if the buffer contains indices data).
*
* Examples below:
* ```js * ```js
* // at initialization phase * // at initialization phase
* this.verticesBuffer = new WebGLArrayBuffer([], DYNAMIC_DRAW); * this.verticesBuffer = new WebGLArrayBuffer([], DYNAMIC_DRAW);
* this.indicesBuffer = new WebGLArrayBuffer([], DYNAMIC_DRAW); * this.indicesBuffer = new WebGLArrayBuffer([], DYNAMIC_DRAW);
* *
* // when array values have changed
* this.context.flushBufferData(ARRAY_BUFFER, this.verticesBuffer);
* this.context.flushBufferData(ELEMENT_ARRAY_BUFFER, this.indicesBuffer);
*
* // at rendering phase * // at rendering phase
* this.context.bindBuffer(ARRAY_BUFFER, this.verticesBuffer); * this.context.bindBuffer(ARRAY_BUFFER, this.verticesBuffer);
* this.context.bindBuffer(ELEMENT_ARRAY_BUFFER, this.indicesBuffer); * this.context.bindBuffer(ELEMENT_ARRAY_BUFFER, this.indicesBuffer);
@@ -342,24 +351,35 @@ class WebGLHelper extends Disposable {
* Just bind the buffer if it's in the cache. Otherwise create * Just bind the buffer if it's in the cache. Otherwise create
* the WebGL buffer, bind it, populate it, and add an entry to * the WebGL buffer, bind it, populate it, and add an entry to
* the cache. * the cache.
* TODO: improve this, the logic is unclear: we want A/ to bind a buffer and B/ to flush data in it * @param {number} target Target, either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.
* @param {number} target Target. * @param {import("./Buffer").default} buffer Buffer.
* @param {import("./Buffer").default} buf Buffer.
* @api * @api
*/ */
bindBuffer(target, buf) { bindBuffer(target, buffer) {
const gl = this.getGL(); const gl = this.getGL();
const arr = buf.getArray(); const bufferKey = getUid(buffer);
const bufferKey = getUid(buf);
let bufferCache = this.bufferCache_[bufferKey]; let bufferCache = this.bufferCache_[bufferKey];
if (!bufferCache) { if (!bufferCache) {
const buffer = gl.createBuffer(); const webGlBuffer = gl.createBuffer();
bufferCache = this.bufferCache_[bufferKey] = { bufferCache = this.bufferCache_[bufferKey] = {
buf: buf, buffer: buffer,
buffer: buffer webGlBuffer: webGlBuffer
}; };
} }
gl.bindBuffer(target, bufferCache.buffer); gl.bindBuffer(target, bufferCache.webGlBuffer);
}
/**
* Update the data contained in the buffer array; this is required for the
* new data to be rendered
* @param {number} target Target, either ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER.
* @param {import("./Buffer").default} buffer Buffer.
* @api
*/
flushBufferData(target, buffer) {
const gl = this.getGL();
const arr = buffer.getArray();
this.bindBuffer(target, buffer);
let /** @type {ArrayBufferView} */ arrayBuffer; let /** @type {ArrayBufferView} */ arrayBuffer;
if (target == ARRAY_BUFFER) { if (target == ARRAY_BUFFER) {
arrayBuffer = new Float32Array(arr); arrayBuffer = new Float32Array(arr);
@@ -367,7 +387,7 @@ class WebGLHelper extends Disposable {
arrayBuffer = this.hasOESElementIndexUint ? arrayBuffer = this.hasOESElementIndexUint ?
new Uint32Array(arr) : new Uint16Array(arr); new Uint32Array(arr) : new Uint16Array(arr);
} }
gl.bufferData(target, arrayBuffer, buf.getUsage()); gl.bufferData(target, arrayBuffer, buffer.getUsage());
} }
/** /**