aboutsummaryrefslogtreecommitdiff
path: root/src/library_gl.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/library_gl.js')
-rw-r--r--src/library_gl.js837
1 files changed, 502 insertions, 335 deletions
diff --git a/src/library_gl.js b/src/library_gl.js
index afd36197..cc39b048 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -209,6 +209,105 @@ var LibraryGL = {
((height - 1) * alignedRowSize + plainRowSize);
},
+ get: function(name_, p, type) {
+ var ret = undefined;
+ switch(name_) { // Handle a few trivial GLES values
+ case 0x8DFA: // GL_SHADER_COMPILER
+ ret = 1;
+ break;
+ case 0x8DF8: // GL_SHADER_BINARY_FORMATS
+ if (type === 'Integer') {
+ // fall through, see gles2_conformance.cpp
+ } else {
+ GL.recordError(0x0500); // GL_INVALID_ENUM
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGet' + type + 'v(GL_SHADER_BINARY_FORMATS): Invalid parameter type!');
+#endif
+ return;
+ }
+ case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS
+ ret = 0;
+ break;
+ case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS
+ // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length),
+ // so implement it ourselves to allow C++ GLES2 code get the length.
+ var formats = Module.ctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/);
+ ret = formats.length;
+ break;
+ case 0x8B9A: // GL_IMPLEMENTATION_COLOR_READ_TYPE
+ ret = 0x1401; // GL_UNSIGNED_BYTE
+ break;
+ case 0x8B9B: // GL_IMPLEMENTATION_COLOR_READ_FORMAT
+ ret = 0x1908; // GL_RGBA
+ break;
+ }
+
+ if (ret === undefined) {
+ var result = Module.ctx.getParameter(name_);
+ switch (typeof(result)) {
+ case "number":
+ ret = result;
+ break;
+ case "boolean":
+ ret = result ? 1 : 0;
+ break;
+ case "string":
+ GL.recordError(0x0500); // GL_INVALID_ENUM
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGet' + type + 'v(' + name_ + ') on a name which returns a string!');
+#endif
+ return;
+ case "object":
+ if (result === null) {
+ GL.recordError(0x0500); // GL_INVALID_ENUM
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGet' + type + 'v(' + name_ + ') and it returns null!');
+#endif
+ return;
+ } else if (result instanceof Float32Array ||
+ result instanceof Uint32Array ||
+ result instanceof Int32Array ||
+ result instanceof Array) {
+ for (var i = 0; i < result.length; ++i) {
+ switch (type) {
+ case 'Integer': {{{ makeSetValue('p', 'i*4', 'result[i]', 'i32') }}}; break;
+ case 'Float': {{{ makeSetValue('p', 'i*4', 'result[i]', 'float') }}}; break;
+ case 'Boolean': {{{ makeSetValue('p', 'i', 'result[i] ? 1 : 0', 'i8') }}}; break;
+ default: throw 'internal glGet error, bad type: ' + type;
+ }
+ }
+ return;
+ } else if (result instanceof WebGLBuffer ||
+ result instanceof WebGLProgram ||
+ result instanceof WebGLFramebuffer ||
+ result instanceof WebGLRenderbuffer ||
+ result instanceof WebGLTexture) {
+ ret = result.name | 0;
+ } else {
+ GL.recordError(0x0500); // GL_INVALID_ENUM
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGet' + type + 'v: Unknown object returned from WebGL getParameter(' + name_ + ')!');
+#endif
+ return;
+ }
+ break;
+ default:
+ GL.recordError(0x0500); // GL_INVALID_ENUM
+#if GL_ASSERTIONS
+ Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Native code calling glGet' + type + 'v(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');
+#endif
+ return;
+ }
+ }
+
+ switch (type) {
+ case 'Integer': {{{ makeSetValue('p', '0', 'ret', 'i32') }}}; break;
+ case 'Float': {{{ makeSetValue('p', '0', 'ret', 'float') }}}; break;
+ case 'Boolean': {{{ makeSetValue('p', '0', 'ret ? 1 : 0', 'i8') }}}; break;
+ default: throw 'internal glGet error, bad type: ' + type;
+ }
+ },
+
getTexPixelData: function(type, format, width, height, pixels, internalFormat) {
var sizePerPixel;
switch (type) {
@@ -288,6 +387,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) {
@@ -554,214 +669,17 @@ var LibraryGL = {
glGetIntegerv__sig: 'vii',
glGetIntegerv: function(name_, p) {
- switch(name_) { // Handle a few trivial GLES values
- case 0x8DFA: // GL_SHADER_COMPILER
- {{{ makeSetValue('p', '0', '1', 'i32') }}};
- return;
- case 0x8DF8: // GL_SHADER_BINARY_FORMATS
- case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS
- {{{ makeSetValue('p', '0', '0', 'i32') }}};
- return;
- case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS
- // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length),
- // so implement it ourselves to allow C++ GLES2 code get the length.
- var formats = Module.ctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/);
- {{{ makeSetValue('p', '0', 'formats.length', 'i32') }}};
- return;
- }
- var result = Module.ctx.getParameter(name_);
- switch (typeof(result)) {
- case "number":
- {{{ makeSetValue('p', '0', 'result', 'i32') }}};
- break;
- case "boolean":
- {{{ makeSetValue('p', '0', 'result ? 1 : 0', 'i8') }}};
- break;
- case "string":
- GL.recordError(0x0500/*GL_INVALID_ENUM*/);
-#if GL_ASSERTIONS
- Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Native code calling glGetIntegerv(' + name_ + ') on a name which returns a string!');
-#endif
- return;
- case "object":
- if (result === null) {
- {{{ makeSetValue('p', '0', '0', 'i32') }}};
- } else if (result instanceof Float32Array ||
- result instanceof Uint32Array ||
- result instanceof Int32Array ||
- result instanceof Array) {
- for (var i = 0; i < result.length; ++i) {
- {{{ makeSetValue('p', 'i*4', 'result[i]', 'i32') }}};
- }
- } else if (result instanceof WebGLBuffer) {
- {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}};
- } else if (result instanceof WebGLProgram) {
- {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}};
- } else if (result instanceof WebGLFramebuffer) {
- {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}};
- } else if (result instanceof WebGLRenderbuffer) {
- {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}};
- } else if (result instanceof WebGLTexture) {
- {{{ makeSetValue('p', '0', 'result.name | 0', 'i32') }}};
- } else {
- GL.recordError(0x0500/*GL_INVALID_ENUM*/);
-#if GL_ASSERTIONS
- Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Unknown object returned from WebGL getParameter(' + name_ + ')!');
-#endif
- return;
- }
- break;
- default:
- GL.recordError(0x0500/*GL_INVALID_ENUM*/);
-#if GL_ASSERTIONS
- Module.printErr('GL_INVALID_ENUM in glGetIntegerv: Native code calling glGetIntegerv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');
-#endif
- return;
- }
+ return GL.get(name_, p, 'Integer');
},
glGetFloatv__sig: 'vii',
glGetFloatv: function(name_, p) {
- switch(name_) {
- case 0x8DFA: // GL_SHADER_COMPILER
- {{{ makeSetValue('p', '0', '1', 'float') }}};
- return;
- case 0x8DF8: // GL_SHADER_BINARY_FORMATS
- GL.recordError(0x0500/*GL_INVALID_ENUM*/);
-#if GL_ASSERTIONS
- Module.printErr('GL_INVALID_ENUM in glGetFloatv(GL_SHADER_BINARY_FORMATS): Invalid parameter type!');
-#endif
- return;
- case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS
- {{{ makeSetValue('p', '0', '0', 'float') }}};
- return;
- case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS
- // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length),
- // so implement it ourselves to allow C++ GLES2 code get the length.
- var formats = Module.ctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/);
- {{{ makeSetValue('p', '0', 'formats.length', 'float') }}};
- return;
- }
-
- var result = Module.ctx.getParameter(name_);
- switch (typeof(result)) {
- case "number":
- {{{ makeSetValue('p', '0', 'result', 'float') }}};
- break;
- case "boolean":
- {{{ makeSetValue('p', '0', 'result ? 1.0 : 0.0', 'float') }}};
- break;
- case "string":
- {{{ makeSetValue('p', '0', '0', 'float') }}};
- case "object":
- if (result === null) {
- GL.recordError(0x0500/*GL_INVALID_ENUM*/);
-#if GL_ASSERTIONS
- Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns null!');
-#endif
- return;
- } else if (result instanceof Float32Array ||
- result instanceof Uint32Array ||
- result instanceof Int32Array ||
- result instanceof Array) {
- for (var i = 0; i < result.length; ++i) {
- {{{ makeSetValue('p', 'i*4', 'result[i]', 'float') }}};
- }
- } else if (result instanceof WebGLBuffer) {
- {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}};
- } else if (result instanceof WebGLProgram) {
- {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}};
- } else if (result instanceof WebGLFramebuffer) {
- {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}};
- } else if (result instanceof WebGLRenderbuffer) {
- {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}};
- } else if (result instanceof WebGLTexture) {
- {{{ makeSetValue('p', '0', 'result.name | 0', 'float') }}};
- } else {
- GL.recordError(0x0500/*GL_INVALID_ENUM*/);
-#if GL_ASSERTIONS
- Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');
-#endif
- return;
- }
- break;
- default:
- GL.recordError(0x0500/*GL_INVALID_ENUM*/);
-#if GL_ASSERTIONS
- Module.printErr('GL_INVALID_ENUM in glGetFloatv: Native code calling glGetFloatv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');
-#endif
- return;
- }
+ return GL.get(name_, p, 'Float');
},
glGetBooleanv__sig: 'vii',
glGetBooleanv: function(name_, p) {
- switch(name_) {
- case 0x8DFA: // GL_SHADER_COMPILER
- {{{ makeSetValue('p', '0', '1', 'i8') }}};
- return;
- case 0x8DF8: // GL_SHADER_BINARY_FORMATS
- GL.recordError(0x0500/*GL_INVALID_ENUM*/);
-#if GL_ASSERTIONS
- Module.printErr('GL_INVALID_ENUM in glGetBooleanv(GL_SHADER_BINARY_FORMATS): Invalid parameter type!');
-#endif
- return;
- case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS
- {{{ makeSetValue('p', '0', '0', 'i8') }}};
- return;
- case 0x86A2: // GL_NUM_COMPRESSED_TEXTURE_FORMATS
- // WebGL doesn't have GL_NUM_COMPRESSED_TEXTURE_FORMATS (it's obsolete since GL_COMPRESSED_TEXTURE_FORMATS returns a JS array that can be queried for length),
- // so implement it ourselves to allow C++ GLES2 code get the length.
- var hasCompressedFormats = Module.ctx.getParameter(0x86A3 /*GL_COMPRESSED_TEXTURE_FORMATS*/).length > 0 ? 1 : 0;
- {{{ makeSetValue('p', '0', 'hasCompressedFormats', 'i8') }}};
- return;
- }
-
- var result = Module.ctx.getParameter(name_);
- switch (typeof(result)) {
- case "number":
- {{{ makeSetValue('p', '0', 'result != 0', 'i8') }}};
- break;
- case "boolean":
- {{{ makeSetValue('p', '0', 'result != 0', 'i8') }}};
- break;
- case "string":
- GL.recordError(0x0500/*GL_INVALID_ENUM*/);
-#if GL_ASSERTIONS
- Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Native code calling glGetBooleanv(' + name_ + ') on a name which returns a string!');
-#endif
- return;
- case "object":
- if (result === null) {
- {{{ makeSetValue('p', '0', '0', 'i8') }}};
- } else if (result instanceof Float32Array ||
- result instanceof Uint32Array ||
- result instanceof Int32Array ||
- result instanceof Array) {
- for (var i = 0; i < result.length; ++i) {
- {{{ makeSetValue('p', 'i', 'result[i] != 0', 'i8') }}};
- }
- } else if (result instanceof WebGLBuffer ||
- result instanceof WebGLProgram ||
- result instanceof WebGLFramebuffer ||
- result instanceof WebGLRenderbuffer ||
- result instanceof WebGLTexture) {
- {{{ makeSetValue('p', '0', '1', 'i8') }}}; // non-zero ID is always 1!
- } else {
- GL.recordError(0x0500/*GL_INVALID_ENUM*/);
-#if GL_ASSERTIONS
- Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Unknown object returned from WebGL getParameter(' + name_ + ')!');
-#endif
- return;
- }
- break;
- default:
- GL.recordError(0x0500/*GL_INVALID_ENUM*/);
-#if GL_ASSERTIONS
- Module.printErr('GL_INVALID_ENUM in glGetBooleanv: Native code calling glGetBooleanv(' + name_ + ') and it returns ' + result + ' of type ' + typeof(result) + '!');
-#endif
- return;
- }
+ return GL.get(name_, p, 'Boolean');
},
glGenTextures__sig: 'vii',
@@ -1808,7 +1726,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
@@ -2149,7 +2067,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);
}
@@ -2689,32 +2610,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);
};
}
@@ -3076,16 +3050,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;
}
},
@@ -3094,16 +3080,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;
}
},
@@ -3115,10 +3113,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:
@@ -3133,61 +3137,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:
@@ -3203,7 +3258,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
}
@@ -3243,26 +3301,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
@@ -3289,7 +3342,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
@@ -3305,13 +3358,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;
@@ -3320,10 +3378,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;
@@ -3349,18 +3408,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])
@@ -3374,24 +3438,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';
@@ -3524,10 +3575,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 = [];
@@ -3570,7 +3636,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
@@ -3583,7 +3651,7 @@ var LibraryGL = {
this.fogScaleLocation || this.fogDensityLocation);
},
- prepare: function() {
+ prepare: function prepare() {
// Calculate the array buffer
var arrayBuffer;
if (!GL.currArrayBuffer) {
@@ -3598,10 +3666,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 &&
@@ -3636,62 +3704,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);
+ }
+ }