aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2013-11-27 11:42:08 -0800
committerAlon Zakai <alonzakai@gmail.com>2013-11-27 11:42:08 -0800
commit86ffa7883101af26ae61e08248816490f81e392d (patch)
tree86906fbcdc0c5467225d6c2874a93e20f5e864a9 /src
parent17f8a4b6286b789a2962cd9016923d8aa8d02d85 (diff)
parent310f39494060a44eb61fdf3e30856aa80c53492b (diff)
Merge pull request #1863 from juj/gl_emulation_opts_rebased
Optimize GL emulation layer.
Diffstat (limited to 'src')
-rw-r--r--src/library_gl.js527
1 files changed, 396 insertions, 131 deletions
diff --git a/src/library_gl.js b/src/library_gl.js
index a3d954e0..95da8f09 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -288,6 +288,22 @@ var LibraryGL = {
}
},
+#if GL_FFP_ONLY
+ enabledClientAttribIndices: [],
+ enableVertexAttribArray: function enableVertexAttribArray(index) {
+ if (!GL.enabledClientAttribIndices[index]) {
+ GL.enabledClientAttribIndices[index] = true;
+ Module.ctx.enableVertexAttribArray(index);
+ }
+ },
+ disableVertexAttribArray: function disableVertexAttribArray(index) {
+ if (GL.enabledClientAttribIndices[index]) {
+ GL.enabledClientAttribIndices[index] = false;
+ Module.ctx.disableVertexAttribArray(index);
+ }
+ },
+#endif
+
#if FULL_ES2
calcBufLength: function calcBufLength(size, type, stride, count) {
if (stride > 0) {
@@ -1820,7 +1836,7 @@ var LibraryGL = {
// Add some emulation workarounds
Module.printErr('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work.');
-#if GL_UNSAFE_OPTS == 0
+#if GL_UNSAFE_OPTS == 1
Module.printErr('WARNING: using emscripten GL emulation unsafe opts. If weirdness happens, try -s GL_UNSAFE_OPTS=0');
#endif
@@ -2161,7 +2177,10 @@ var LibraryGL = {
}
}
#endif
- GL.currProgram = program;
+ if (GL.currProgram != program) {
+ GL.currentRenderer = null; // This changes the FFP emulation shader program, need to recompute that.
+ GL.currProgram = program;
+ }
glUseProgram(program);
}
@@ -2701,32 +2720,85 @@ var LibraryGL = {
GL_SRC_ALPHA
];
- this.traverseState = function CTexEnv_traverseState(keyView) {
- keyView.next(this.mode);
- keyView.next(this.colorCombiner);
- keyView.next(this.alphaCombiner);
- keyView.next(this.colorCombiner);
- keyView.next(this.alphaScale);
- keyView.next(this.envColor[0]);
- keyView.next(this.envColor[1]);
- keyView.next(this.envColor[2]);
- keyView.next(this.envColor[3]);
-
- keyView.next(this.colorSrc[0]);
- keyView.next(this.colorSrc[1]);
- keyView.next(this.colorSrc[2]);
-
- keyView.next(this.alphaSrc[0]);
- keyView.next(this.alphaSrc[1]);
- keyView.next(this.alphaSrc[2]);
-
- keyView.next(this.colorOp[0]);
- keyView.next(this.colorOp[1]);
- keyView.next(this.colorOp[2]);
-
- keyView.next(this.alphaOp[0]);
- keyView.next(this.alphaOp[1]);
- keyView.next(this.alphaOp[2]);
+ // Map GLenums to small values to efficiently pack the enums to bits for tighter access.
+ this.traverseKey = {
+ // mode
+ 0x1E01 /* GL_REPLACE */: 0,
+ 0x2100 /* GL_MODULATE */: 1,
+ 0x0104 /* GL_ADD */: 2,
+ 0x0BE2 /* GL_BLEND */: 3,
+ 0x2101 /* GL_DECAL */: 4,
+ 0x8570 /* GL_COMBINE */: 5,
+
+ // additional color and alpha combiners
+ 0x84E7 /* GL_SUBTRACT */: 3,
+ 0x8575 /* GL_INTERPOLATE */: 4,
+
+ // color and alpha src
+ 0x1702 /* GL_TEXTURE */: 0,
+ 0x8576 /* GL_CONSTANT */: 1,
+ 0x8577 /* GL_PRIMARY_COLOR */: 2,
+ 0x8578 /* GL_PREVIOUS */: 3,
+
+ // color and alpha op
+ 0x0300 /* GL_SRC_COLOR */: 0,
+ 0x0301 /* GL_ONE_MINUS_SRC_COLOR */: 1,
+ 0x0302 /* GL_SRC_ALPHA */: 2,
+ 0x0300 /* GL_ONE_MINUS_SRC_ALPHA */: 3
+ };
+
+ // The tuple (key0,key1,key2) uniquely identifies the state of the variables in CTexEnv.
+ // -1 on key0 denotes 'the whole cached key is dirty'
+ this.key0 = -1;
+ this.key1 = 0;
+ this.key2 = 0;
+
+ this.computeKey0 = function() {
+ var k = this.traverseKey;
+ var key = k[this.mode] * 1638400; // 6 distinct values.
+ key += k[this.colorCombiner] * 327680; // 5 distinct values.
+ key += k[this.alphaCombiner] * 65536; // 5 distinct values.
+ // The above three fields have 6*5*5=150 distinct values -> 8 bits.
+ key += (this.colorScale-1) * 16384; // 10 bits used.
+ key += (this.alphaScale-1) * 4096; // 12 bits used.
+ key += k[this.colorSrc[0]] * 1024; // 14
+ key += k[this.colorSrc[1]] * 256; // 16
+ key += k[this.colorSrc[2]] * 64; // 18
+ key += k[this.alphaSrc[0]] * 16; // 20
+ key += k[this.alphaSrc[1]] * 4; // 22
+ key += k[this.alphaSrc[2]]; // 24 bits used total.
+ return key;
+ }
+ this.computeKey1 = function() {
+ var k = this.traverseKey;
+ key = k[this.colorOp[0]] * 4096;
+ key += k[this.colorOp[1]] * 1024;
+ key += k[this.colorOp[2]] * 256;
+ key += k[this.alphaOp[0]] * 16;
+ key += k[this.alphaOp[1]] * 4;
+ key += k[this.alphaOp[2]];
+ return key;
+ }
+ // TODO: remove this. The color should not be part of the key!
+ this.computeKey2 = function() {
+ return this.envColor[0] * 16777216 + this.envColor[1] * 65536 + this.envColor[2] * 256 + 1 + this.envColor[3];
+ }
+ this.recomputeKey = function() {
+ this.key0 = this.computeKey0();
+ this.key1 = this.computeKey1();
+ this.key2 = this.computeKey2();
+ }
+ this.invalidateKey = function() {
+ this.key0 = -1; // The key of this texture unit must be recomputed when rendering the next time.
+ GL.immediate.currentRenderer = null; // The currently used renderer must be re-evaluated at next render.
+ }
+ this.traverseState = function(keyView) {
+ if (this.key0 == -1) {
+ this.recomputeKey();
+ }
+ keyView.next(this.key0);
+ keyView.next(this.key1);
+ keyView.next(this.key2);
};
}
@@ -3088,16 +3160,28 @@ var LibraryGL = {
var cur = getCurTexUnit();
switch (cap) {
case GL_TEXTURE_1D:
- cur.enabled_tex1D = true;
+ if (!cur.enabled_tex1D) {
+ GL.immediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again.
+ cur.enabled_tex1D = true;
+ }
break;
case GL_TEXTURE_2D:
- cur.enabled_tex2D = true;
+ if (!cur.enabled_tex2D) {
+ GL.immediate.currentRenderer = null;
+ cur.enabled_tex2D = true;
+ }
break;
case GL_TEXTURE_3D:
- cur.enabled_tex3D = true;
+ if (!cur.enabled_tex3D) {
+ GL.immediate.currentRenderer = null;
+ cur.enabled_tex3D = true;
+ }
break;
case GL_TEXTURE_CUBE_MAP:
- cur.enabled_texCube = true;
+ if (!cur.enabled_texCube) {
+ GL.immediate.currentRenderer = null;
+ cur.enabled_texCube = true;
+ }
break;
}
},
@@ -3106,16 +3190,28 @@ var LibraryGL = {
var cur = getCurTexUnit();
switch (cap) {
case GL_TEXTURE_1D:
- cur.enabled_tex1D = false;
+ if (cur.enabled_tex1D) {
+ GL.immediate.currentRenderer = null; // Renderer state changed, and must be recreated or looked up again.
+ cur.enabled_tex1D = false;
+ }
break;
case GL_TEXTURE_2D:
- cur.enabled_tex2D = false;
+ if (cur.enabled_tex2D) {
+ GL.immediate.currentRenderer = null;
+ cur.enabled_tex2D = false;
+ }
break;
case GL_TEXTURE_3D:
- cur.enabled_tex3D = false;
+ if (cur.enabled_tex3D) {
+ GL.immediate.currentRenderer = null;
+ cur.enabled_tex3D = false;
+ }
break;
case GL_TEXTURE_CUBE_MAP:
- cur.enabled_texCube = false;
+ if (cur.enabled_texCube) {
+ GL.immediate.currentRenderer = null;
+ cur.enabled_texCube = false;
+ }
break;
}
},
@@ -3127,10 +3223,16 @@ var LibraryGL = {
var env = getCurTexUnit().env;
switch (pname) {
case GL_RGB_SCALE:
- env.colorScale = param;
+ if (env.colorScale != param) {
+ env.invalidateKey(); // We changed FFP emulation renderer state.
+ env.colorScale = param;
+ }
break;
case GL_ALPHA_SCALE:
- env.alphaScale = param;
+ if (env.alphaScale != param) {
+ env.invalidateKey();
+ env.alphaScale = param;
+ }
break;
default:
@@ -3145,61 +3247,112 @@ var LibraryGL = {
var env = getCurTexUnit().env;
switch (pname) {
case GL_TEXTURE_ENV_MODE:
- env.mode = param;
+ if (env.mode != param) {
+ env.invalidateKey(); // We changed FFP emulation renderer state.
+ env.mode = param;
+ }
break;
case GL_COMBINE_RGB:
- env.colorCombiner = param;
+ if (env.colorCombiner != param) {
+ env.invalidateKey();
+ env.colorCombiner = param;
+ }
break;
case GL_COMBINE_ALPHA:
- env.alphaCombiner = param;
+ if (env.alphaCombiner != param) {
+ env.invalidateKey();
+ env.alphaCombiner = param;
+ }
break;
case GL_SRC0_RGB:
- env.colorSrc[0] = param;
+ if (env.colorSrc[0] != param) {
+ env.invalidateKey();
+ env.colorSrc[0] = param;
+ }
break;
case GL_SRC1_RGB:
- env.colorSrc[1] = param;
+ if (env.colorSrc[1] != param) {
+ env.invalidateKey();
+ env.colorSrc[1] = param;
+ }
break;
case GL_SRC2_RGB:
- env.colorSrc[2] = param;
+ if (env.colorSrc[2] != param) {
+ env.invalidateKey();
+ env.colorSrc[2] = param;
+ }
break;
case GL_SRC0_ALPHA:
- env.alphaSrc[0] = param;
+ if (env.alphaSrc[0] != param) {
+ env.invalidateKey();
+ env.alphaSrc[0] = param;
+ }
break;
case GL_SRC1_ALPHA:
- env.alphaSrc[1] = param;
+ if (env.alphaSrc[1] != param) {
+ env.invalidateKey();
+ env.alphaSrc[1] = param;
+ }
break;
case GL_SRC2_ALPHA:
- env.alphaSrc[2] = param;
+ if (env.alphaSrc[2] != param) {
+ env.invalidateKey();
+ env.alphaSrc[2] = param;
+ }
break;
case GL_OPERAND0_RGB:
- env.colorOp[0] = param;
+ if (env.colorOp[0] != param) {
+ env.invalidateKey();
+ env.colorOp[0] = param;
+ }
break;
case GL_OPERAND1_RGB:
- env.colorOp[1] = param;
+ if (env.colorOp[1] != param) {
+ env.invalidateKey();
+ env.colorOp[1] = param;
+ }
break;
case GL_OPERAND2_RGB:
- env.colorOp[2] = param;
+ if (env.colorOp[2] != param) {
+ env.invalidateKey();
+ env.colorOp[2] = param;
+ }
break;
case GL_OPERAND0_ALPHA:
- env.alphaOp[0] = param;
+ if (env.alphaOp[0] != param) {
+ env.invalidateKey();
+ env.alphaOp[0] = param;
+ }
break;
case GL_OPERAND1_ALPHA:
- env.alphaOp[1] = param;
+ if (env.alphaOp[1] != param) {
+ env.invalidateKey();
+ env.alphaOp[1] = param;
+ }
break;
case GL_OPERAND2_ALPHA:
- env.alphaOp[2] = param;
+ if (env.alphaOp[2] != param) {
+ env.invalidateKey();
+ env.alphaOp[2] = param;
+ }
break;
case GL_RGB_SCALE:
- env.colorScale = param;
+ if (env.colorScale != param) {
+ env.invalidateKey();
+ env.colorScale = param;
+ }
break;
case GL_ALPHA_SCALE:
- env.alphaScale = param;
+ if (env.alphaScale != param) {
+ env.invalidateKey();
+ env.alphaScale = param;
+ }
break;
default:
@@ -3215,7 +3368,10 @@ var LibraryGL = {
case GL_TEXTURE_ENV_COLOR: {
for (var i = 0; i < 4; i++) {
var param = {{{ makeGetValue('params', 'i*4', 'float') }}};
- env.envColor[i] = param;
+ if (env.envColor[i] != param) {
+ env.invalidateKey(); // We changed FFP emulation renderer state.
+ env.envColor[i] = param;
+ }
}
break
}
@@ -3255,26 +3411,21 @@ var LibraryGL = {
NORMAL: 1,
COLOR: 2,
TEXTURE0: 3,
- TEXTURE1: 4,
- TEXTURE2: 5,
- TEXTURE3: 6,
- TEXTURE4: 7,
- TEXTURE5: 8,
- TEXTURE6: 9,
- NUM_ATTRIBUTES: 10, // Overwritten in init().
- MAX_TEXTURES: 7, // Overwritten in init().
+ NUM_ATTRIBUTES: -1, // Initialized in GL emulation init().
+ MAX_TEXTURES: -1, // Initialized in GL emulation init().
totalEnabledClientAttributes: 0,
enabledClientAttributes: [0, 0],
clientAttributes: [], // raw data, including possible unneeded ones
liveClientAttributes: [], // the ones actually alive in the current computation, sorted
+ currentRenderer: null, // Caches the currently active FFP emulation renderer, so that it does not have to be re-looked up unless relevant state changes.
modifiedClientAttributes: false,
clientActiveTexture: 0,
clientColor: null,
usedTexUnitList: [],
fixedFunctionProgram: null,
- setClientAttribute: function(name, size, type, stride, pointer) {
+ setClientAttribute: function setClientAttribute(name, size, type, stride, pointer) {
var attrib = this.clientAttributes[name];
if (!attrib) {
for (var i = 0; i <= name; i++) { // keep flat
@@ -3301,7 +3452,7 @@ var LibraryGL = {
},
// Renderers
- addRendererComponent: function(name, size, type) {
+ addRendererComponent: function addRendererComponent(name, size, type) {
if (!this.rendererComponents[name]) {
this.rendererComponents[name] = 1;
#if ASSERTIONS
@@ -3317,13 +3468,18 @@ var LibraryGL = {
}
},
- disableBeginEndClientAttributes: function() {
+ disableBeginEndClientAttributes: function disableBeginEndClientAttributes() {
for (var i = 0; i < this.NUM_ATTRIBUTES; i++) {
if (this.rendererComponents[i]) this.enabledClientAttributes[i] = false;
}
},
- getRenderer: function() {
+ getRenderer: function getRenderer() {
+ // If no FFP state has changed that would have forced to re-evaluate which FFP emulation shader to use,
+ // we have the currently used renderer in cache, and can immediately return that.
+ if (this.currentRenderer) {
+ return this.currentRenderer;
+ }
// return a renderer object given the liveClientAttributes
// we maintain a cache of renderers, optimized to not generate garbage
var attributes = GL.immediate.liveClientAttributes;
@@ -3332,10 +3488,11 @@ var LibraryGL = {
var keyView = cacheMap.getStaticKeyView().reset();
// By attrib state:
+ var enabledAttributesKey = 0;
for (var i = 0; i < attributes.length; i++) {
- var attribute = attributes[i];
- keyView.next(attribute.name).next(attribute.size).next(attribute.type);
+ enabledAttributesKey |= 1 << attributes[i].name;
}
+ keyView.next(enabledAttributesKey);
// By fog state:
var fogParam = 0;
@@ -3361,18 +3518,23 @@ var LibraryGL = {
}
// If we don't already have it, create it.
- if (!keyView.get()) {
+ var renderer = keyView.get();
+ if (!renderer) {
#if GL_DEBUG
Module.printErr('generating renderer for ' + JSON.stringify(attributes));
#endif
- keyView.set(this.createRenderer());
+ renderer = this.createRenderer();
+ this.currentRenderer = renderer;
+ keyView.set(renderer);
+ return renderer;
}
- return keyView.get();
+ this.currentRenderer = renderer; // Cache the currently used renderer, so later lookups without state changes can get this fast.
+ return renderer;
},
- createRenderer: function(renderer) {
+ createRenderer: function createRenderer(renderer) {
var useCurrProgram = !!GL.currProgram;
- var hasTextures = false, textureSizes = [], textureTypes = [];
+ var hasTextures = false;
for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) {
var texAttribName = GL.immediate.TEXTURE0 + i;
if (!GL.immediate.enabledClientAttributes[texAttribName])
@@ -3386,24 +3548,11 @@ var LibraryGL = {
}
#endif
- textureSizes[i] = GL.immediate.clientAttributes[texAttribName].size;
- textureTypes[i] = GL.immediate.clientAttributes[texAttribName].type;
hasTextures = true;
}
- var positionSize = GL.immediate.clientAttributes[GL.immediate.VERTEX].size;
- var positionType = GL.immediate.clientAttributes[GL.immediate.VERTEX].type;
- var colorSize = 0, colorType;
- if (GL.immediate.enabledClientAttributes[GL.immediate.COLOR]) {
- colorSize = GL.immediate.clientAttributes[GL.immediate.COLOR].size;
- colorType = GL.immediate.clientAttributes[GL.immediate.COLOR].type;
- }
- var normalSize = 0, normalType;
- if (GL.immediate.enabledClientAttributes[GL.immediate.NORMAL]) {
- normalSize = GL.immediate.clientAttributes[GL.immediate.NORMAL].size;
- normalType = GL.immediate.clientAttributes[GL.immediate.NORMAL].type;
- }
+
var ret = {
- init: function() {
+ init: function init() {
// For fixed-function shader generation.
var uTexUnitPrefix = 'u_texUnit';
var aTexCoordPrefix = 'a_texCoord';
@@ -3536,10 +3685,25 @@ var LibraryGL = {
this.program = Module.ctx.createProgram();
Module.ctx.attachShader(this.program, this.vertexShader);
Module.ctx.attachShader(this.program, this.fragmentShader);
- Module.ctx.bindAttribLocation(this.program, 0, 'a_position');
+
+ // As optimization, bind all attributes to prespecified locations, so that the FFP emulation
+ // code can submit attributes to any generated FFP shader without having to examine each shader in turn.
+ // These prespecified locations are only assumed if GL_FFP_ONLY is specified, since user could also create their
+ // own shaders that didn't have attributes in the same locations.
+ Module.ctx.bindAttribLocation(this.program, GL.immediate.VERTEX, 'a_position');
+ Module.ctx.bindAttribLocation(this.program, GL.immediate.COLOR, 'a_color');
+ Module.ctx.bindAttribLocation(this.program, GL.immediate.NORMAL, 'a_normal');
+ for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) {
+ Module.ctx.bindAttribLocation(this.program, GL.immediate.TEXTURE0 + i, 'a_texCoord'+i);
+ Module.ctx.bindAttribLocation(this.program, GL.immediate.TEXTURE0 + i, aTexCoordPrefix+i);
+ }
Module.ctx.linkProgram(this.program);
}
+ // Stores a map that remembers which matrix uniforms are up-to-date in this FFP renderer, so they don't need to be resubmitted
+ // each time we render with this program.
+ this.textureMatrixVersion = {};
+
this.positionLocation = Module.ctx.getAttribLocation(this.program, 'a_position');
this.texCoordLocations = [];
@@ -3582,7 +3746,9 @@ var LibraryGL = {
this.projectionLocation = Module.ctx.getUniformLocation(this.program, 'u_projection');
this.hasTextures = hasTextures;
- this.hasNormal = normalSize > 0 && this.normalLocation >= 0;
+ this.hasNormal = GL.immediate.enabledClientAttributes[GL.immediate.NORMAL] &&
+ GL.immediate.clientAttributes[GL.immediate.NORMAL].size > 0 &&
+ this.normalLocation >= 0;
this.hasColor = (this.colorLocation === 0) || this.colorLocation > 0;
this.floatType = Module.ctx.FLOAT; // minor optimization
@@ -3595,7 +3761,7 @@ var LibraryGL = {
this.fogScaleLocation || this.fogDensityLocation);
},
- prepare: function() {
+ prepare: function prepare() {
// Calculate the array buffer
var arrayBuffer;
if (!GL.currArrayBuffer) {
@@ -3610,10 +3776,10 @@ var LibraryGL = {
arrayBuffer = GL.currArrayBuffer;
}
+#if GL_UNSAFE_OPTS
// If the array buffer is unchanged and the renderer as well, then we can avoid all the work here
// XXX We use some heuristics here, and this may not work in all cases. Try disabling GL_UNSAFE_OPTS if you
// have odd glitches
-#if GL_UNSAFE_OPTS
var lastRenderer = GL.immediate.lastRenderer;
var canSkip = this == lastRenderer &&
arrayBuffer == GL.immediate.lastArrayBuffer &&
@@ -3648,62 +3814,105 @@ var LibraryGL = {
GL.immediate.fixedFunctionProgram = this.program;
}
- if (this.modelViewLocation) Module.ctx.uniformMatrix4fv(this.modelViewLocation, false, GL.immediate.matrix['m']);
- if (this.projectionLocation) Module.ctx.uniformMatrix4fv(this.projectionLocation, false, GL.immediate.matrix['p']);
+ if (this.modelViewLocation && this.modelViewMatrixVersion != GL.immediate.matrixVersion['m']) {
+ this.modelViewMatrixVersion = GL.immediate.matrixVersion['m'];
+ Module.ctx.uniformMatrix4fv(this.modelViewLocation, false, GL.immediate.matrix['m']);
+ }
+ if (this.projectionLocation && this.projectionMatrixVersion != GL.immediate.matrixVersion['p']) {
+ this.projectionMatrixVersion = GL.immediate.matrixVersion['p'];
+ Module.ctx.uniformMatrix4fv(this.projectionLocation, false, GL.immediate.matrix['p']);
+ }
var clientAttributes = GL.immediate.clientAttributes;
+ var posAttr = clientAttributes[GL.immediate.VERTEX];
#if GL_ASSERTIONS
- GL.validateVertexAttribPointer(positionSize, positionType, GL.immediate.stride, clientAttributes[GL.immediate.VERTEX].offset);
+ GL.validateVertexAttribPointer(posAttr.size, posAttr.type, GL.immediate.stride, clientAttributes[GL.immediate.VERTEX].offset);
#endif
- Module.ctx.vertexAttribPointer(this.positionLocation, positionSize, positionType, false,
- GL.immediate.stride, clientAttributes[GL.immediate.VERTEX].offset);
+
+#if GL_FFP_ONLY
+ if (!GL.currArrayBuffer) {
+ Module.ctx.vertexAttribPointer(GL.immediate.VERTEX, posAttr.size, posAttr.type, false, GL.immediate.stride, posAttr.offset);
+ GL.enableVertexAttribArray(GL.immediate.VERTEX);
+ if (this.hasNormal) {
+ var normalAttr = clientAttributes[GL.immediate.NORMAL];
+ Module.ctx.vertexAttribPointer(GL.immediate.NORMAL, normalAttr.size, normalAttr.type, true, GL.immediate.stride, normalAttr.offset);
+ GL.enableVertexAttribArray(GL.immediate.NORMAL);
+ }
+ }
+#else
+ Module.ctx.vertexAttribPointer(this.positionLocation, posAttr.size, posAttr.type, false, GL.immediate.stride, posAttr.offset);
Module.ctx.enableVertexAttribArray(this.positionLocation);
+ if (this.hasNormal) {
+ var normalAttr = clientAttributes[GL.immediate.NORMAL];
+#if GL_ASSERTIONS
+ GL.validateVertexAttribPointer(normalAttr.size, normalAttr.type, GL.immediate.stride, normalAttr.offset);
+#endif
+ Module.ctx.vertexAttribPointer(this.normalLocation, normalAttr.size, normalAttr.type, true, GL.immediate.stride, normalAttr.offset);
+ Module.ctx.enableVertexAttribArray(this.normalLocation);
+ }
+#endif
if (this.hasTextures) {
- //for (var i = 0; i < this.usedTexUnitList.length; i++) {
- // var texUnitID = this.usedTexUnitList[i];
for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) {
- var texUnitID = i;
- var attribLoc = this.texCoordLocations[texUnitID];
+#if GL_FFP_ONLY
+ if (!GL.currArrayBuffer) {
+ var attribLoc = GL.immediate.TEXTURE0+i;
+ var texAttr = clientAttributes[attribLoc];
+ if (texAttr.size) {
+ Module.ctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GL.immediate.stride, texAttr.offset);
+ GL.enableVertexAttribArray(attribLoc);
+ } else {
+ // These two might be dangerous, but let's try them.
+ Module.ctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1);
+ GL.disableVertexAttribArray(attribLoc);
+ }
+ }
+#else
+ var attribLoc = this.texCoordLocations[i];
if (attribLoc === undefined || attribLoc < 0) continue;
+ var texAttr = clientAttributes[GL.immediate.TEXTURE0+i];
- if (texUnitID < textureSizes.length && textureSizes[texUnitID]) {
+ if (texAttr.size) {
#if GL_ASSERTIONS
- GL.validateVertexAttribPointer(textureSizes[texUnitID], textureTypes[texUnitID], GL.immediate.stride, GL.immediate.clientAttributes[GL.immediate.TEXTURE0 + texUnitID].offset);
+ GL.validateVertexAttribPointer(texAttr.size, texAttr.type, GL.immediate.stride, texAttr.offset);
#endif
- Module.ctx.vertexAttribPointer(attribLoc, textureSizes[texUnitID], textureTypes[texUnitID], false,
- GL.immediate.stride, GL.immediate.clientAttributes[GL.immediate.TEXTURE0 + texUnitID].offset);
+ Module.ctx.vertexAttribPointer(attribLoc, texAttr.size, texAttr.type, false, GL.immediate.stride, texAttr.offset);
Module.ctx.enableVertexAttribArray(attribLoc);
} else {
// These two might be dangerous, but let's try them.
Module.ctx.vertexAttrib4f(attribLoc, 0, 0, 0, 1);
Module.ctx.disableVertexAttribArray(attribLoc);
}
- }
- for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) {
- if (this.textureMatrixLocations[i]) { // XXX might we need this even without the condition we are currently in?
- Module.ctx.uniformMatrix4fv(this.textureMatrixLocations[i], false, GL.immediate.matrix['t' + i]);
+#endif
+ var t = 't'+i;
+ if (this.textureMatrixLocations[i] && this.textureMatrixVersion[t] != GL.immediate.matrixVersion[t]) { // XXX might we need this even without the condition we are currently in?
+ this.textureMatrixVersion[t] = GL.immediate.matrixVersion[t];
+ Module.ctx.uniformMatrix4fv(this.textureMatrixLocations[i], false, GL.immediate.matrix[t]);
}
}
}
- if (colorSize) {
+ if (GL.immediate.enabledClientAttributes[GL.immediate.COLOR]) {
+ var colorAttr = clientAttributes[GL.immediate.COLOR];
#if GL_ASSERTIONS
- GL.validateVertexAttribPointer(colorSize, colorType, GL.immediate.stride, clientAttributes[GL.immediate.COLOR].offset);
+ GL.validateVertexAttribPointer(colorAttr.size, colorAttr.type, GL.immediate.stride, colorAttr.offset);
#endif
- Module.ctx.vertexAttribPointer(this.colorLocation, colorSize, colorType, true,
- GL.immediate.stride, clientAttributes[GL.immediate.COLOR].offset);
+#if GL_FFP_ONLY
+ if (!GL.currArrayBuffer) {
+ Module.ctx.vertexAttribPointer(GL.immediate.COLOR, colorAttr.size, colorAttr.type, true, GL.immediate.stride, colorAttr.offset);
+ GL.enableVertexAttribArray(GL.immediate.COLOR);
+ }
+#else
+ Module.ctx.vertexAttribPointer(this.colorLocation, colorAttr.size, colorAttr.type, true, GL.immediate.stride, colorAttr.offset);
Module.ctx.enableVertexAttribArray(this.colorLocation);
+#endif
} else if (this.hasColor) {
+#if GL_FFP_ONLY
+ GL.disableVertexAttribArray(GL.immediate.COLOR);
+ Module.ctx.vertexAttrib4fv(GL.immediate.COLOR, GL.immediate.clientColor);
+#else
Module.ctx.disableVertexAttribArray(this.colorLocation);
Module.ctx.vertexAttrib4fv(this.colorLocation, GL.immediate.clientColor);
- }
- if (this.hasNormal) {
-#if GL_ASSERTIONS
- GL.validateVertexAttribPointer(normalSize, normalType, GL.immediate.stride, clientAttributes[GL.immediate.NORMAL].offset);
#endif
- Module.ctx.vertexAttribPointer(this.normalLocation, normalSize, normalType, true,
- GL.immediate.stride, clientAttributes[GL.immediate.NORMAL].offset);
- Module.ctx.enableVertexAttribArray(this.normalLocation);
}
if (this.hasFog) {
if (this.fogColorLocation) Module.ctx.uniform4fv(this.fogColorLocation, GLEmulation.fogColor);
@@ -3713,11 +3922,12 @@ var LibraryGL = {
}
},
- cleanup: function() {
+ cleanup: function cleanup() {
+#if !GL_FFP_ONLY
Module.ctx.disableVertexAttribArray(this.positionLocation);
if (this.hasTextures) {
- for (var i = 0; i < textureSizes.length; i++) {
- if (textureSizes[i] && this.texCoordLocations[i] >= 0) {
+ for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) {
+ if (GL.immediate.enabledClientAttributes[GL.immediate.TEXTURE0+i] && this.texCoordLocations[i] >= 0) {
Module.ctx.disableVertexAttribArray(this.texCoordLocations[i]);
}
}
@@ -3741,6 +3951,7 @@ var LibraryGL = {
GL.immediate.lastProgram = null;
#endif
GL.immediate.matricesModified = true;
+#endif
}
};
ret.init();
@@ -3870,11 +4081,15 @@ var LibraryGL = {
this.TexEnvJIT.init(Module.ctx);
- GL.immediate.MAX_TEXTURES = Module.ctx.getParameter(Module.ctx.MAX_TEXTURE_IMAGE_UNITS);
- GL.immediate.NUM_ATTRIBUTES = GL.immediate.TEXTURE0 + GL.immediate.MAX_TEXTURES;
+ // User can override the maximum number of texture units that we emulate. Using fewer texture units increases runtime performance
+ // slightly, so it is advantageous to choose as small value as needed.
+ GL.immediate.MAX_TEXTURES = Module['GL_MAX_TEXTURE_IMAGE_UNITS'] || Module.ctx.getParameter(Module.ctx.MAX_TEXTURE_IMAGE_UNITS);
+ GL.immediate.NUM_ATTRIBUTES = 3 /*pos+normal+color attributes*/ + GL.immediate.MAX_TEXTURES;
GL.immediate.clientAttributes = [];
+ GLEmulation.enabledClientAttribIndices = [];
for (var i = 0; i < GL.immediate.NUM_ATTRIBUTES; i++) {
GL.immediate.clientAttributes.push({});
+ GLEmulation.enabledClientAttribIndices.push(false);
}
this.matrixStack['m'] = [];
@@ -3884,13 +4099,18 @@ var LibraryGL = {
}
// Initialize matrix library
-
+ // When user sets a matrix, increment a 'version number' on the new data, and when rendering, submit
+ // the matrices to the shader program only if they have an old version of the data.
+ GL.immediate.matrixVersion = {};
GL.immediate.matrix['m'] = GL.immediate.matrix.lib.mat4.create();
+ GL.immediate.matrixVersion['m'] = 0;
GL.immediate.matrix.lib.mat4.identity(GL.immediate.matrix['m']);
GL.immediate.matrix['p'] = GL.immediate.matrix.lib.mat4.create();
+ GL.immediate.matrixVersion['p'] = 0;
GL.immediate.matrix.lib.mat4.identity(GL.immediate.matrix['p']);
for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) {
GL.immediate.matrix['t' + i] = GL.immediate.matrix.lib.mat4.create();
+ GL.immediate.matrixVersion['t' + i] = 0;
}
// Renderer cache
@@ -3911,7 +4131,7 @@ var LibraryGL = {
// Modifies liveClientAttributes, stride, vertexPointer, vertexCounter
// count: number of elements we will draw
// beginEnd: whether we are drawing the results of a begin/end block
- prepareClientAttributes: function(count, beginEnd) {
+ prepareClientAttributes: function prepareClientAttributes(count, beginEnd) {
// If no client attributes were modified since we were last called, do nothing. Note that this
// does not work for glBegin/End, where we generate renderer components dynamically and then
// disable them ourselves, but it does help with glDrawElements/Arrays.
@@ -4009,7 +4229,7 @@ var LibraryGL = {
}
},
- flush: function(numProvidedIndexes, startIndex, ptr) {
+ flush: function flush(numProvidedIndexes, startIndex, ptr) {
#if ASSERTIONS
assert(numProvidedIndexes >= 0 || !numProvidedIndexes);
#endif
@@ -4082,7 +4302,7 @@ var LibraryGL = {
Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GL.currElementArrayBuffer] || null);
}
-#if GL_UNSAFE_OPTS == 0
+#if GL_UNSAFE_OPTS == 0 && !GL_FFP_ONLY
renderer.cleanup();
#endif
}
@@ -4330,10 +4550,12 @@ var LibraryGL = {
if (disable && GL.immediate.enabledClientAttributes[attrib]) {
GL.immediate.enabledClientAttributes[attrib] = false;
GL.immediate.totalEnabledClientAttributes--;
+ this.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed.
if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledClientStates[cap];
} else if (!disable && !GL.immediate.enabledClientAttributes[attrib]) {
GL.immediate.enabledClientAttributes[attrib] = true;
GL.immediate.totalEnabledClientAttributes++;
+ this.currentRenderer = null; // Will need to change current renderer, since the set of active vertex pointers changed.
if (GLEmulation.currentVao) GLEmulation.currentVao.enabledClientStates[cap] = 1;
}
GL.immediate.modifiedClientAttributes = true;
@@ -4345,15 +4567,40 @@ var LibraryGL = {
glVertexPointer__deps: ['$GLEmulation'], // if any pointers are used, glVertexPointer must be, and if it is, then we need emulation
glVertexPointer: function(size, type, stride, pointer) {
GL.immediate.setClientAttribute(GL.immediate.VERTEX, size, type, stride, pointer);
+#if GL_FFP_ONLY
+ if (GL.currArrayBuffer) {
+ Module.ctx.vertexAttribPointer(GL.immediate.VERTEX, size, type, false, stride, pointer);
+ GL.enableVertexAttribArray(GL.immediate.VERTEX);
+ }
+#endif
},
glTexCoordPointer: function(size, type, stride, pointer) {
GL.immediate.setClientAttribute(GL.immediate.TEXTURE0 + GL.immediate.clientActiveTexture, size, type, stride, pointer);
+#if GL_FFP_ONLY
+ if (GL.currArrayBuffer) {
+ var loc = GL.immediate.TEXTURE0 + GL.immediate.clientActiveTexture;
+ Module.ctx.vertexAttribPointer(loc, size, type, false, stride, pointer);
+ GL.enableVertexAttribArray(loc);
+ }
+#endif
},
glNormalPointer: function(type, stride, pointer) {
GL.immediate.setClientAttribute(GL.immediate.NORMAL, 3, type, stride, pointer);
+#if GL_FFP_ONLY
+ if (GL.currArrayBuffer) {
+ Module.ctx.vertexAttribPointer(GL.immediate.NORMAL, size, type, true, stride, pointer);
+ GL.enableVertexAttribArray(GL.immediate.NORMAL);
+ }
+#endif
},
glColorPointer: function(size, type, stride, pointer) {
GL.immediate.setClientAttribute(GL.immediate.COLOR, size, type, stride, pointer);
+#if GL_FFP_ONLY
+ if (GL.currArrayBuffer) {
+ Module.ctx.vertexAttribPointer(GL.immediate.COLOR, size, type, true, stride, pointer);
+ GL.enableVertexAttribArray(GL.immediate.COLOR);
+ }
+#endif
},
glClientActiveTexture__sig: 'vi',
@@ -4436,23 +4683,27 @@ var LibraryGL = {
glPushMatrix: function() {
GL.immediate.matricesModified = true;
+ GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0;
GL.immediate.matrixStack[GL.immediate.currentMatrix].push(
Array.prototype.slice.call(GL.immediate.matrix[GL.immediate.currentMatrix]));
},
glPopMatrix: function() {
GL.immediate.matricesModified = true;
+ GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0;
GL.immediate.matrix[GL.immediate.currentMatrix] = GL.immediate.matrixStack[GL.immediate.currentMatrix].pop();
},
glLoadIdentity__deps: ['$GL', '$GLImmediateSetup'],
glLoadIdentity: function() {
GL.immediate.matricesModified = true;
+ GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0;
GL.immediate.matrix.lib.mat4.identity(GL.immediate.matrix[GL.immediate.currentMatrix]);
},
glLoadMatrixd: function(matrix) {
GL.immediate.matricesModified = true;
+ GL.immediate.matrixVersion[GL.immediate.currentMatrix] = (GL.immediate.matrixVersion[GL.immediate.currentMatrix] + 1)|0;
GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F64', 'matrix', 'matrix+' + (16*8)) }}}, GL.immediate.matrix[GL.immediate.currentMatrix]);
},
@@ -4461,35 +4712,41 @@ var LibraryGL = {
if (GL.debug) Module.printErr('glLoadMatrixf receiving: ' + Array.prototype.s