From 0e918912f67e09bfbb23ee420c57e4fd9ed25f57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Tue, 6 Nov 2018 16:44:53 +0200 Subject: [PATCH] Add -s WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG=1 option. --- emcc.py | 7 ++ src/library_gl.js | 44 +++++++ src/settings.js | 7 ++ tests/test_browser.py | 5 + .../webgl_draw_triangle_with_uniform_color.c | 118 ++++++++++++++++++ 5 files changed, 181 insertions(+) create mode 100644 tests/webgl_draw_triangle_with_uniform_color.c diff --git a/emcc.py b/emcc.py index 14edaeaaa25e4..cf39016db4ebe 100755 --- a/emcc.py +++ b/emcc.py @@ -1112,6 +1112,13 @@ def check(input_file): assert not shared.Settings.WASM, 'LEGACY_VM_SUPPORT is only supported for asm.js, and not wasm. Build with -s WASM=0' shared.Settings.POLYFILL_OLD_MATH_FUNCTIONS = 1 shared.Settings.WORKAROUND_IOS_9_RIGHT_SHIFT_BUG = 1 + shared.Settings.WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG = 1 + + # Silently drop any individual backwards compatibility emulation flags that are known never to occur on browsers that support WebAssembly. + if shared.Settings.WASM: + shared.Settings.POLYFILL_OLD_MATH_FUNCTIONS = 0 + shared.Settings.WORKAROUND_IOS_9_RIGHT_SHIFT_BUG = 0 + shared.Settings.WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG = 0 if shared.Settings.SPLIT_MEMORY: if shared.Settings.WASM: diff --git a/src/library_gl.js b/src/library_gl.js index d12b40680bd9a..6da6b26d477a1 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -719,6 +719,29 @@ var LibraryGL = { context.supportsWebGL2EntryPoints = (context.version >= 2) && (getChromeVersion() === false || getChromeVersion() >= 58); #endif +#if WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG + context.cannotHandleOffsetsInUniformArrayViews = (function(g) { + try { + var p = g.createProgram(); // Note: we do not delete this program so it stays part of the context we created, but that is ok - it does not do anything and we want to keep this detection size minimal. + function b(c, t) { + var s = g.createShader(t); + g.shaderSource(s, c); + g.compileShader(s); + return s; + } + g.attachShader(p, b("attribute vec4 p;void main(){gl_Position=p;}", g.VERTEX_SHADER)); + g.attachShader(p, b("precision lowp float;uniform vec4 u;void main(){gl_FragColor=u;}", g.FRAGMENT_SHADER)); + g.linkProgram(p); + var h = new Float32Array(8); + h[4] = 1; + g.useProgram(p); + var l = g.getUniformLocation(p, "u"); + g.uniform4fv(l, h.subarray(4, 8)); // Uploading a 4-vector GL uniform from last four elements of array [0,0,0,0,1,0,0,0], i.e. uploading vec4=(1,0,0,0) at offset=4. + return !g.getUniform(p, l)[0]; // in proper WebGL we expect to read back the vector we just uploaded: (1,0,0,0). On buggy browser would instead have uploaded offset=0 of above array, i.e. vec4=(0,0,0,0) + } catch(e) { return false; } // If we get an exception, we assume we got some other error, and do not trigger this workaround. + })(); +#endif + // Store the created context object so that we can access the context given a canvas without having to pass the parameters again. if (ctx.canvas) ctx.canvas.GLctxObject = context; GL.contexts[handle] = context; @@ -3191,6 +3214,9 @@ var LibraryGL = { } } else { view = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; +#if WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG + if (GL.currentContext.cannotHandleOffsetsInUniformArrayViews) view = new Float32Array(view); +#endif } GLctx.uniform1fv(GL.uniforms[location], view); }, @@ -3219,6 +3245,9 @@ var LibraryGL = { } } else { view = {{{ makeHEAPView('F32', 'value', 'value+count*8') }}}; +#if WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG + if (GL.currentContext.cannotHandleOffsetsInUniformArrayViews) view = new Float32Array(view); +#endif } GLctx.uniform2fv(GL.uniforms[location], view); }, @@ -3248,6 +3277,9 @@ var LibraryGL = { } } else { view = {{{ makeHEAPView('F32', 'value', 'value+count*12') }}}; +#if WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG + if (GL.currentContext.cannotHandleOffsetsInUniformArrayViews) view = new Float32Array(view); +#endif } GLctx.uniform3fv(GL.uniforms[location], view); }, @@ -3278,6 +3310,9 @@ var LibraryGL = { } } else { view = {{{ makeHEAPView('F32', 'value', 'value+count*16') }}}; +#if WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG + if (GL.currentContext.cannotHandleOffsetsInUniformArrayViews) view = new Float32Array(view); +#endif } GLctx.uniform4fv(GL.uniforms[location], view); }, @@ -3394,6 +3429,9 @@ var LibraryGL = { } } else { view = {{{ makeHEAPView('F32', 'value', 'value+count*16') }}}; +#if WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG + if (GL.currentContext.cannotHandleOffsetsInUniformArrayViews) view = new Float32Array(view); +#endif } GLctx.uniformMatrix2fv(GL.uniforms[location], !!transpose, view); }, @@ -3429,6 +3467,9 @@ var LibraryGL = { } } else { view = {{{ makeHEAPView('F32', 'value', 'value+count*36') }}}; +#if WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG + if (GL.currentContext.cannotHandleOffsetsInUniformArrayViews) view = new Float32Array(view); +#endif } GLctx.uniformMatrix3fv(GL.uniforms[location], !!transpose, view); }, @@ -3471,6 +3512,9 @@ var LibraryGL = { } } else { view = {{{ makeHEAPView('F32', 'value', 'value+count*64') }}}; +#if WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG + if (GL.currentContext.cannotHandleOffsetsInUniformArrayViews) view = new Float32Array(view); +#endif } GLctx.uniformMatrix4fv(GL.uniforms[location], !!transpose, view); }, diff --git a/src/settings.js b/src/settings.js index 7b8f618c6fbf8..143549471dd38 100644 --- a/src/settings.js +++ b/src/settings.js @@ -394,6 +394,12 @@ var FULL_ES2 = 0; // from the output. var GL_EMULATE_GLES_VERSION_STRING_FORMAT = 1; +// Some old Android WeChat (Chromium 37?) browser has a WebGL bug that it ignores +// the offset of a typed array view pointing to an ArrayBuffer. Set this to +// 1 to enable a polyfill that works around the issue when it appears. This +// bug is only relevant to WebGL 1, the affected browsers do not support WebGL 2. +var WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG = 0; + // Enables WebGL2 native functions. This mode will also create a WebGL2 // context by default if no version is specified. var USE_WEBGL2 = 0; @@ -452,6 +458,7 @@ var POLYFILL_OLD_MATH_FUNCTIONS = 0; // browsers and shell environments. Specifically: // * Add polyfilling for Math.clz32, Math.trunc, Math.imul, Math.fround. (-s POLYFILL_OLD_MATH_FUNCTIONS=1) // * Work around iOS 9 right shift bug (-s WORKAROUND_IOS_9_RIGHT_SHIFT_BUG=1) +// * Work around old Chromium WebGL 1 bug (-s WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG=1) // * Disable WebAssembly. (Must be paired with -s WASM=0) // You can also configure the above options individually. var LEGACY_VM_SUPPORT = 0; diff --git a/tests/test_browser.py b/tests/test_browser.py index 9a365e4b4306a..3db97fdc43028 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -3864,6 +3864,11 @@ def test_webgl_from_client_side_memory_without_default_enabled_extensions(self): def test_webgl_offscreen_framebuffer(self): self.btest('webgl_draw_triangle.c', '0', args=['-lGL', '-s', 'OFFSCREEN_FRAMEBUFFER=1', '-DEXPLICIT_SWAP=1']) + # Tests that -s WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG=1 rendering works. + @requires_graphics_hardware + def test_webgl_workaround_webgl_uniform_upload_bug(self): + self.btest('webgl_draw_triangle_with_uniform_color.c', '0', args=['-lGL', '-s', 'WORKAROUND_OLD_WEBGL_UNIFORM_UPLOAD_IGNORED_OFFSET_BUG=1']) + # Tests the feature that shell html page can preallocate the typed array and place it to Module.buffer before loading the script page. # In this build mode, the -s TOTAL_MEMORY=xxx option will be ignored. # Preallocating the buffer in this was is asm.js only (wasm needs a Memory). diff --git a/tests/webgl_draw_triangle_with_uniform_color.c b/tests/webgl_draw_triangle_with_uniform_color.c new file mode 100644 index 0000000000000..ff89223cb951f --- /dev/null +++ b/tests/webgl_draw_triangle_with_uniform_color.c @@ -0,0 +1,118 @@ +/* + * Copyright 2018 The Emscripten Authors. All rights reserved. + * Emscripten is available under two separate licenses, the MIT license and the + * University of Illinois/NCSA Open Source License. Both these licenses can be + * found in the LICENSE file. + */ + +#include +#include +#include +#include +#include + +GLuint compile_shader(GLenum shaderType, const char *src) +{ + GLuint shader = glCreateShader(shaderType); + glShaderSource(shader, 1, &src, NULL); + glCompileShader(shader); + + GLint isCompiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled); + if (!isCompiled) + { + GLint maxLength = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength); + char *buf = (char*)malloc(maxLength+1); + glGetShaderInfoLog(shader, maxLength, &maxLength, buf); + printf("%s\n", buf); + free(buf); + return 0; + } + + return shader; +} + +GLuint create_program(GLuint vertexShader, GLuint fragmentShader) +{ + GLuint program = glCreateProgram(); + glAttachShader(program, vertexShader); + glAttachShader(program, fragmentShader); + glBindAttribLocation(program, 0, "apos"); + glBindAttribLocation(program, 1, "acolor"); + glLinkProgram(program); + return program; +} + +int main() +{ + EmscriptenWebGLContextAttributes attr; + emscripten_webgl_init_context_attributes(&attr); +#ifdef EXPLICIT_SWAP + attr.explicitSwapControl = 1; +#endif +#ifdef DRAW_FROM_CLIENT_MEMORY + // This test verifies that drawing from client-side memory when enableExtensionsByDefault==false works. + attr.enableExtensionsByDefault = 0; +#endif + + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context("#canvas", &attr); + emscripten_webgl_make_context_current(ctx); + + static const char vertex_shader[] = + "attribute vec4 apos;" + "attribute vec4 acolor;" + "varying vec4 color;" + "void main() {" + "color = acolor;" + "gl_Position = apos;" + "}"; + GLuint vs = compile_shader(GL_VERTEX_SHADER, vertex_shader); + + static const char fragment_shader[] = + "precision lowp float;" + "varying vec4 color;" + "uniform vec4 color2;" + "void main() {" + "gl_FragColor = color*color2;" + "}"; + GLuint fs = compile_shader(GL_FRAGMENT_SHADER, fragment_shader); + + GLuint program = create_program(vs, fs); + glUseProgram(program); + + static const float pos_and_color[] = { + // x, y, r, g, b + -0.6f, -0.6f, 1, 0, 0, + 0.6f, -0.6f, 0, 1, 0, + 0.f, 0.6f, 0, 0, 1, + }; + +#ifdef DRAW_FROM_CLIENT_MEMORY + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 20, pos_and_color); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 20, (void*)(pos_and_color+2)); +#else + GLuint vbo; + glGenBuffers(1, &vbo); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(pos_and_color), pos_and_color, GL_STATIC_DRAW); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 20, 0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 20, (void*)8); +#endif + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + + float color2[4] = { 0.0f, 1.f, 0.0f, 1.0f }; + glUniform4fv(glGetUniformLocation(program, "color2"), 1, color2); + glClearColor(0.3f,0.3f,0.3f,1); + glClear(GL_COLOR_BUFFER_BIT); + glDrawArrays(GL_TRIANGLES, 0, 3); + +#ifdef EXPLICIT_SWAP + emscripten_webgl_commit_frame(); +#endif + +#ifdef REPORT_RESULT + REPORT_RESULT(0); +#endif +}