diff options
-rw-r--r-- | src/library_gl.js | 138 | ||||
-rw-r--r-- | tests/gles2_uniform_arrays.cpp | 121 | ||||
-rw-r--r-- | tests/test_browser.py | 4 |
3 files changed, 254 insertions, 9 deletions
diff --git a/src/library_gl.js b/src/library_gl.js index 83e68777..713763d2 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -410,6 +410,51 @@ var LibraryGL = { GL.depthTextureExt = Module.ctx.getExtension("WEBGL_depth_texture") || Module.ctx.getExtension("MOZ_WEBGL_depth_texture") || Module.ctx.getExtension("WEBKIT_WEBGL_depth_texture"); + }, + + // In WebGL, uniforms in a shader program are accessed through an opaque object type 'WebGLUniformLocation'. + // In GLES2, uniforms are accessed via indices. Therefore we must generate a mapping of indices -> WebGLUniformLocations + // to provide the client code the API that uses indices. + // This function takes a linked GL program and generates a mapping table for the program. + // NOTE: Populating the uniform table is performed eagerly at glLinkProgram time, so glLinkProgram should be considered + // to be a slow/costly function call. Calling glGetUniformLocation is relatively fast, since it is always a read-only + // lookup to the table populated in this function call. + populateUniformTable: function(program) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.programs, program, 'populateUniformTable', 'program'); +#endif + var p = GL.programs[program]; + GL.uniformTable[program] = {}; + var ptable = GL.uniformTable[program]; + // A program's uniformTable maps the string name of an uniform to an integer location of that uniform. + // The global GL.uniforms map maps integer locations to WebGLUniformLocations. + var numUniforms = Module.ctx.getProgramParameter(p, Module.ctx.ACTIVE_UNIFORMS); + for(var i = 0; i < numUniforms; ++i) { + var u = Module.ctx.getActiveUniform(p, i); + + var name = u.name; + // Strip off any trailing array specifier we might have got, e.g. "[0]". + if (name.indexOf(']', name.length-1) !== -1) { + var ls = name.lastIndexOf('['); + name = name.slice(0, ls); + } + + // Optimize memory usage slightly: If we have an array of uniforms, e.g. 'vec3 colors[3];', then + // only store the string 'colors' in ptable, and 'colors[0]', 'colors[1]' and 'colors[2]' will be parsed as 'colors'+i. + // Note that for the GL.uniforms table, we still need to fetch the all WebGLUniformLocations for all the indices. + var loc = Module.ctx.getUniformLocation(p, name); + var id = GL.getNewId(GL.uniforms); + ptable[name] = [u.size, id]; + GL.uniforms[id] = loc; + + for(var j = 1; j < u.size; ++j) { + var n = name + '['+j+']'; + loc = Module.ctx.getUniformLocation(p, n); + id = GL.getNewId(GL.uniforms); + + GL.uniforms[id] = loc; + } + } } }, @@ -811,6 +856,7 @@ var LibraryGL = { glGetUniformfv: function(program, location, params) { #if GL_ASSERTIONS GL.validateGLObjectID(GL.programs, program, 'glGetUniformfv', 'program'); + GL.validateGLObjectID(GL.uniforms, location, 'glGetUniformfv', 'location'); #endif var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]); if (typeof data == 'number') { @@ -826,6 +872,7 @@ var LibraryGL = { glGetUniformiv: function(program, location, params) { #if GL_ASSERTIONS GL.validateGLObjectID(GL.programs, program, 'glGetUniformiv', 'program'); + GL.validateGLObjectID(GL.uniforms, location, 'glGetUniformiv', 'location'); #endif var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]); if (typeof data == 'number' || typeof data == 'boolean') { @@ -843,16 +890,31 @@ var LibraryGL = { GL.validateGLObjectID(GL.programs, program, 'glGetUniformLocation', 'program'); #endif name = Pointer_stringify(name); + + var arrayOffset = 0; + // If user passed an array accessor "[index]", parse the array index off the accessor. + if (name.indexOf(']', name.length-1) !== -1) { + var ls = name.lastIndexOf('['); + var arrayIndex = name.slice(ls+1, -1); + if (arrayIndex.length > 0) { + arrayOffset = parseInt(arrayIndex); + if (arrayOffset < 0) { + return -1; + } + } + name = name.slice(0, ls); + } + var ptable = GL.uniformTable[program]; - if (!ptable) ptable = GL.uniformTable[program] = {}; - var id = ptable[name]; - if (id) return id; - var loc = Module.ctx.getUniformLocation(GL.programs[program], name); - if (!loc) return -1; - id = GL.getNewId(GL.uniforms); - GL.uniforms[id] = loc; - ptable[name] = id; - return id; + if (!ptable) { + return -1; + } + var uniformInfo = ptable[name]; // returns pair [ dimension_of_uniform_array, uniform_location ] + if (uniformInfo && arrayOffset < uniformInfo[0]) { // Check if user asked for an out-of-bounds element, i.e. for 'vec4 colors[3];' user could ask for 'colors[10]' which should return -1. + return uniformInfo[1]+arrayOffset; + } else { + return -1; + } }, glGetVertexAttribfv__sig: 'viii', @@ -923,54 +985,81 @@ var LibraryGL = { glUniform1f__sig: 'vif', glUniform1f: function(location, v0) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform1f', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform1f(location, v0); }, glUniform2f__sig: 'viff', glUniform2f: function(location, v0, v1) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform2f', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform2f(location, v0, v1); }, glUniform3f__sig: 'vifff', glUniform3f: function(location, v0, v1, v2) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform3f', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform3f(location, v0, v1, v2); }, glUniform4f__sig: 'viffff', glUniform4f: function(location, v0, v1, v2, v3) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform4f', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform4f(location, v0, v1, v2, v3); }, glUniform1i__sig: 'vii', glUniform1i: function(location, v0) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform1i', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform1i(location, v0); }, glUniform2i__sig: 'viii', glUniform2i: function(location, v0, v1) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform2i', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform2i(location, v0, v1); }, glUniform3i__sig: 'viiii', glUniform3i: function(location, v0, v1, v2) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform3i', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform3i(location, v0, v1, v2); }, glUniform4i__sig: 'viiiii', glUniform4i: function(location, v0, v1, v2, v3) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform4i', 'location'); +#endif location = GL.uniforms[location]; Module.ctx.uniform4i(location, v0, v1, v2, v3); }, glUniform1iv__sig: 'viii', glUniform1iv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform1iv', 'location'); +#endif location = GL.uniforms[location]; value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}}; Module.ctx.uniform1iv(location, value); @@ -978,6 +1067,9 @@ var LibraryGL = { glUniform2iv__sig: 'viii', glUniform2iv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform2iv', 'location'); +#endif location = GL.uniforms[location]; count *= 2; value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}}; @@ -986,6 +1078,9 @@ var LibraryGL = { glUniform3iv__sig: 'viii', glUniform3iv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform3iv', 'location'); +#endif location = GL.uniforms[location]; count *= 3; value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}}; @@ -994,6 +1089,9 @@ var LibraryGL = { glUniform4iv__sig: 'viii', glUniform4iv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform4iv', 'location'); +#endif location = GL.uniforms[location]; count *= 4; value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}}; @@ -1002,6 +1100,9 @@ var LibraryGL = { glUniform1fv__sig: 'viii', glUniform1fv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform1fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1016,6 +1117,9 @@ var LibraryGL = { glUniform2fv__sig: 'viii', glUniform2fv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform2fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1031,6 +1135,9 @@ var LibraryGL = { glUniform3fv__sig: 'viii', glUniform3fv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform3fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1047,6 +1154,9 @@ var LibraryGL = { glUniform4fv__sig: 'viii', glUniform4fv: function(location, count, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniform4fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1064,6 +1174,9 @@ var LibraryGL = { glUniformMatrix2fv__sig: 'viiii', glUniformMatrix2fv: function(location, count, transpose, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniformMatrix2fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1080,6 +1193,9 @@ var LibraryGL = { glUniformMatrix3fv__sig: 'viiii', glUniformMatrix3fv: function(location, count, transpose, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniformMatrix3fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1096,6 +1212,9 @@ var LibraryGL = { glUniformMatrix4fv__sig: 'viiii', glUniformMatrix4fv: function(location, count, transpose, value) { +#if GL_ASSERTIONS + GL.validateGLObjectID(GL.uniforms, location, 'glUniformMatrix4fv', 'location'); +#endif location = GL.uniforms[location]; var view; if (count == 1) { @@ -1340,6 +1459,7 @@ var LibraryGL = { #endif Module.ctx.linkProgram(GL.programs[program]); GL.uniformTable[program] = {}; // uniforms no longer keep the same names after linking + GL.populateUniformTable(program); }, glGetProgramInfoLog__sig: 'viiii', diff --git a/tests/gles2_uniform_arrays.cpp b/tests/gles2_uniform_arrays.cpp new file mode 100644 index 00000000..84e394dc --- /dev/null +++ b/tests/gles2_uniform_arrays.cpp @@ -0,0 +1,121 @@ +#include "SDL/SDL_opengl.h" +#include "SDL/SDL.h" + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +void RunTest(int testVariant) +{ + GLuint vs = 0; + + const char *vsCode = "#version 100\n" + "attribute vec4 pos; void main() { gl_Position = pos; }"; + + vs = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vs, 1, &vsCode, NULL); + glCompileShader(vs); + + GLuint ps = 0; + + const char *psCode = "#version 100\n" + "precision lowp float;\n" + "uniform vec3 color;\n" + "uniform vec3 colors[3];\n" + "void main() { gl_FragColor = vec4(color,1) + vec4(colors[0].r, colors[1].g, colors[2].b, 1); }"; + + ps = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(ps, 1, &psCode, NULL); + glCompileShader(ps); + + GLuint program = 0; + program = glCreateProgram(); + glAttachShader(program, vs); + glAttachShader(program, ps); + glBindAttribLocation(program, 0, "pos"); + glLinkProgram(program); + + int color_loc = glGetUniformLocation(program, "color"); + assert(color_loc != -1); + + glUseProgram(program); + float col[3] = { 0.2f, 0.2f, 0.2f }; + glUniform3fv(color_loc, 1, col); + + int loc = glGetUniformLocation(program, "colors"); + assert(loc != -1); + // In previous Emscripten GL layer code, calling glGetUniformLocation would do extra caching operations that interacts how glUniform** after that will work, + // so to exhibit extra issues in old code (and to keep new code from regressing), must test both with and without excess glGetUniformLocation calls. + if ((testVariant&1) != 0) + { + // Deliberately check in odd order to make sure any kind of lazy operations won't affect the indices we get. + assert(glGetUniformLocation(program, "colors[2]") == loc+2); + assert(glGetUniformLocation(program, "colors[0]") == loc); + assert(glGetUniformLocation(program, "colors[3]") == -1); + assert(glGetUniformLocation(program, "colors[1]") == loc+1); + assert(glGetUniformLocation(program, "colors[]") == loc); + assert(glGetUniformLocation(program, "colors[-100]") == -1); + assert(glGetUniformLocation(program, "colors[bleh]") == -1); + } + + float colors[4*3] = { 1,0,0, 0,0.5,0, 0,0,0.2, 1,1,1 }; + + if ((testVariant&2)!=0) + { + glUniform3fv(loc+1, 3, colors+3); // Pass the actual colors (testing a nonzero location offset), but do a mistake by setting one index too many. Spec says this should be gracefully handled, and that excess elements are ignored. + assert(glGetError() == GL_NO_ERROR); + glUniform3fv(loc, 1, colors); // Set the first index as well. + assert(glGetError() == GL_NO_ERROR); + } + else + { + glUniform3fv(loc, 4, colors); // Just directly set the full array. + assert(glGetError() == GL_NO_ERROR); + } + + assert(glGetError() == GL_NO_ERROR); + + GLuint vbo = 0; + const GLfloat v[] = { -1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1 }; + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(v), v, GL_STATIC_DRAW); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), 0); + glEnableVertexAttribArray(0); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + unsigned char pixel[4]; + glReadPixels(1,1,1,1,GL_RGBA,GL_UNSIGNED_BYTE, pixel); + //printf("%d,%d,%d,%d\n", pixel[0], pixel[1], pixel[2], pixel[3]); + assert(pixel[0] == 255); + assert(pixel[1] == 178); + assert(pixel[2] == 102); + assert(pixel[3] == 255); + + printf("OK: Case %d passed.\n", testVariant); + // Lazy, don't clean up afterwards. +} + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + + // Slightly different SDL initialization + if ( SDL_Init(SDL_INIT_VIDEO) != 0 ) { + printf("Unable to initialize SDL: %s\n", SDL_GetError()); + return 1; + } + + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + for(int i = 0; i < 4; ++i) + RunTest(i); + + return 0; +} diff --git a/tests/test_browser.py b/tests/test_browser.py index 799759a1..24113511 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -1308,6 +1308,10 @@ keydown(100);keyup(100); // trigger the end def test_gl_vertex_buffer(self): self.btest('gl_vertex_buffer.c', reference='gl_vertex_buffer.png', args=['-s', 'GL_UNSAFE_OPTS=0', '-s', 'LEGACY_GL_EMULATION=1'], reference_slack=1) + # Does not pass due to https://bugzilla.mozilla.org/show_bug.cgi?id=924264 so disabled for now. + # def test_gles2_uniform_arrays(self): + # self.btest('gles2_uniform_arrays.cpp', args=['-s', 'GL_ASSERTIONS=1'], expected=['1']) + def test_matrix_identity(self): self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840'], args=['-s', 'LEGACY_GL_EMULATION=1']) |