Skip to content

Commit

Permalink
Use VAO to save on vertex specifying commands when drawing
Browse files Browse the repository at this point in the history
The new behavior becomes active if WebGL on the client system supports
OES_vertex_array_object. The VAO reference is stored on the geometry
chunk. If the interleaved buffer is dirtied and the VAO references it, it
is reconstructed based on the regular buffers. The VAO is also reset if
the chunk is rebuilt. VAOs are not used if morph geometry is present.

OES_vertex_array_object is a widely supported WebGL extension, around
75% of clients have it according to webglstats. In large scenes on
CPU-constrained systems that support the extension this patch can
increase FPS as much as 15%.
  • Loading branch information
Olli Etuaho committed Jun 24, 2014
1 parent 59279e9 commit 22f9555
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 38 deletions.
6 changes: 5 additions & 1 deletion src/core/display/chunks/chunkFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ SceneJS_ChunkFactory.prototype.putChunk = function (chunk) {

if (--chunk.useCount <= 0) { // Release shared core if use count now zero

if (chunk.recycle) {
chunk.recycle();
}

this._chunks[chunk.id] = null;

var freeChunks = SceneJS_ChunkFactory._freeChunks[chunk.type];
Expand All @@ -121,7 +125,7 @@ SceneJS_ChunkFactory.prototype.putChunk = function (chunk) {
};

/**
* Re-cache shader variable locations for each active chunk
* Re-cache shader variable locations for each active chunk and reset VAOs if any
*/
SceneJS_ChunkFactory.prototype.webglRestored = function () {

Expand Down
107 changes: 71 additions & 36 deletions src/core/display/chunks/geometryChunk.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ SceneJS_ChunkFactory.createChunkType({
this._aUV2Draw = draw.getAttribute("SCENEJS_aUVCoord2");
this._aColorDraw = draw.getAttribute("SCENEJS_aVertexColor");

this.VAO = null;
this.VAOHasInterleavedBuf = false;

var pick = this.program.pick;

this._aVertexPick = pick.getAttribute("SCENEJS_aVertex");
Expand All @@ -33,57 +36,89 @@ SceneJS_ChunkFactory.createChunkType({
this._aColorPick = pick.getAttribute("SCENEJS_aVertexColor");
},

recycle:function () {
if (this.VAO) {
// Guarantee that the old VAO is deleted immediately when recycling the object.
var VAOExt = this.program.gl.getExtension("OES_vertex_array_object");
VAOExt.deleteVertexArrayOES(this.VAO);
}
},

draw:function (ctx) {

var gl = this.program.gl;

if (ctx.geoChunkId != this.id) { // HACK until we have distinct state chunks for VBOs and draw call

if (this.core.interleavedBuf && !this.core.interleavedBuf.dirty) {
this.core.interleavedBuf.bind();
if (this._aVertexDraw && !ctx.vertexBuf) {
this._aVertexDraw.bindInterleavedFloatArrayBuffer(3, this.core.interleavedStride, this.core.interleavedPositionOffset);
}
if (this._aNormalDraw && !ctx.normalBuf) {
this._aNormalDraw.bindInterleavedFloatArrayBuffer(3, this.core.interleavedStride, this.core.interleavedNormalOffset);
}
if (this._aUVDraw && !ctx.uvBuf) {
this._aUVDraw.bindInterleavedFloatArrayBuffer(2, this.core.interleavedStride, this.core.interleavedUVOffset);
}
if (this._aUV2Draw && !ctx.uv2Buf) {
this._aUV2Draw.bindInterleavedFloatArrayBuffer(2, this.core.interleavedStride, this.core.interleavedUV2Offset);
}
if (this._aColorDraw && !ctx.colorBuf) {
this._aColorDraw.bindInterleavedFloatArrayBuffer(4, this.core.interleavedStride, this.core.interleavedColorOffset);
}
var ctxBufsActive = ctx.vertexBuf || ctx.normalBuf || ctx.uvBuf || ctx.uvBuf2 || ctx.colorBuf;
if (this.VAO && (ctxBufsActive ||
this.core.interleavedBuf && this.core.interleavedBuf.dirty && this.VAOHasInterleavedBuf)) {
// Need to recreate VAO to refer to separate buffers, or can't use VAO due to buffers
// specified outside.
ctx.VAO.deleteVertexArrayOES(this.VAO);
this.VAO = null;
}
if (this.VAO) {
ctx.VAO.bindVertexArrayOES(this.VAO);
} else {
if (this._aVertexDraw && !ctx.vertexBuf) {
this._aVertexDraw.bindFloatArrayBuffer(this.core.vertexBuf);
var useInterleavedBuf = (this.core.interleavedBuf && !this.core.interleavedBuf.dirty);
if (ctx.VAO && !ctxBufsActive) {
this.VAO = ctx.VAO.createVertexArrayOES();
ctx.VAO.bindVertexArrayOES(this.VAO);
this.VAOHasInterleavedBuf = useInterleavedBuf;
}

if (this._aNormalDraw && !ctx.normalBuf) {
this._aNormalDraw.bindFloatArrayBuffer(this.core.normalBuf);
if (useInterleavedBuf) {
this.core.interleavedBuf.bind();
if (this._aVertexDraw && !ctx.vertexBuf) {
this._aVertexDraw.bindInterleavedFloatArrayBuffer(3, this.core.interleavedStride, this.core.interleavedPositionOffset);
}
if (this._aNormalDraw && !ctx.normalBuf) {
this._aNormalDraw.bindInterleavedFloatArrayBuffer(3, this.core.interleavedStride, this.core.interleavedNormalOffset);
}
if (this._aUVDraw && !ctx.uvBuf) {
this._aUVDraw.bindInterleavedFloatArrayBuffer(2, this.core.interleavedStride, this.core.interleavedUVOffset);
}
if (this._aUV2Draw && !ctx.uv2Buf) {
this._aUV2Draw.bindInterleavedFloatArrayBuffer(2, this.core.interleavedStride, this.core.interleavedUV2Offset);
}
if (this._aColorDraw && !ctx.colorBuf) {
this._aColorDraw.bindInterleavedFloatArrayBuffer(4, this.core.interleavedStride, this.core.interleavedColorOffset);
}
} else {
if (this._aVertexDraw && !ctx.vertexBuf) {
this._aVertexDraw.bindFloatArrayBuffer(this.core.vertexBuf);
}

if (this._aNormalDraw && !ctx.normalBuf) {
this._aNormalDraw.bindFloatArrayBuffer(this.core.normalBuf);
}

if (this._aUVDraw && !ctx.uvBuf) {
this._aUVDraw.bindFloatArrayBuffer(this.core.uvBuf);
}

if (this._aUV2Draw && !ctx.uvBuf2) {
this._aUV2Draw.bindFloatArrayBuffer(this.core.uvBuf2);
}

if (this._aColorDraw && !ctx.colorBuf) {
this._aColorDraw.bindFloatArrayBuffer(this.core.colorBuf);
}
}

if (this._aUVDraw && !ctx.uvBuf) {
this._aUVDraw.bindFloatArrayBuffer(this.core.uvBuf);
}

if (this._aUV2Draw && !ctx.uvBuf2) {
this._aUV2Draw.bindFloatArrayBuffer(this.core.uvBuf2);
}

if (this._aColorDraw && !ctx.colorBuf) {
this._aColorDraw.bindFloatArrayBuffer(this.core.colorBuf);
}
this.core.indexBuf.bind();
}
}

this.core.indexBuf.bind();
gl.drawElements(this.core.primitive, this.core.indexBuf.numItems, gl.UNSIGNED_SHORT, 0);

if (this.VAO) {
// We don't want following nodes that don't use their own VAOs to muck up
// this node's VAO, so we need to unbind it.
ctx.VAO.bindVertexArrayOES(null);
} else {
ctx.geoChunkId = this.id;
}

gl.drawElements(this.core.primitive, this.core.indexBuf.numItems, gl.UNSIGNED_SHORT, 0);
},

pick:function (ctx) {
Expand Down
10 changes: 9 additions & 1 deletion src/core/display/display.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ var SceneJS_Display = function (cfg) {
*/
this._frameCtx = {
pickNames:[], // Pick names of objects hit during pick render
canvas:this._canvas // The canvas
canvas:this._canvas, // The canvas
VAO:null // Vertex array object extension
};

/* The frame context has this facade which is given to scene node "rendered" listeners
Expand Down Expand Up @@ -924,6 +925,13 @@ SceneJS_Display.prototype._doDrawList = function (pick, rayPick) {

frameCtx.transparencyPass = false;

// The extension needs to be re-queried in case the context was lost and
// has been recreated.
var VAO = gl.getExtension("OES_vertex_array_object");
if (VAO) {
frameCtx.VAO = VAO;
}

gl.viewport(0, 0, this._canvas.canvas.width, this._canvas.canvas.height);
gl.clearColor(this._ambientColor[0], this._ambientColor[1], this._ambientColor[2], this._ambientColor[3]);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
Expand Down

0 comments on commit 22f9555

Please sign in to comment.