aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/library_gl.js138
-rw-r--r--tests/gles2_uniform_arrays.cpp121
-rw-r--r--tests/test_browser.py4
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'])