From a569398109163a6c139e11e5b4996a79a1637b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jukka=20Jyl=C3=A4nki?= Date: Wed, 26 Aug 2020 10:45:40 +0300 Subject: [PATCH] Improve tests/minimal_webgl for teaching purposes. Improve WebGL validation MIN_WEBGL_VERSION=2 mode. --- src/library_webgl.js | 81 +++++++++++++++++++-------- tests/minimal_webgl/CMakeLists.txt | 89 ++++++++++++++++++++++++++++++ tests/minimal_webgl/webgl.c | 10 ++-- 3 files changed, 151 insertions(+), 29 deletions(-) create mode 100644 tests/minimal_webgl/CMakeLists.txt diff --git a/src/library_webgl.js b/src/library_webgl.js index b6fba3799b5d4..cb08019474eb9 100644 --- a/src/library_webgl.js +++ b/src/library_webgl.js @@ -531,7 +531,7 @@ var LibraryGL = { var realf = 'real_' + f; glCtx[realf] = glCtx[f]; var numArgs = GL.webGLFunctionLengths[f]; // On Firefox & Chrome, could do "glCtx[realf].length", but that doesn't work on Edge, which always reports 0. - if (numArgs === undefined) throw 'Unexpected WebGL function ' + f; + if (numArgs === undefined) console.warn('Unexpected WebGL function ' + f + ' when binding TRACE_WEBGL_CALLS'); var contextHandle = glCtx.canvas.GLctxObject.handle; var threadId = (typeof _pthread_self !== 'undefined') ? _pthread_self : function() { return 1; }; // Accessing 'arguments' is super slow, so to avoid overhead, statically reason the number of arguments. @@ -548,7 +548,7 @@ var LibraryGL = { case 9: glCtx[f] = function webgl_9(a1, a2, a3, a4, a5, a6, a7, a8, a9) { var ret = glCtx[realf](a1, a2, a3, a4, a5, a6, a7, a8, a9); console.error('[Thread ' + threadId() + ', GL ctx: ' + contextHandle + ']: ' + f + '('+a1+', ' + a2 +', ' + a3 +', ' + a4 +', ' + a5 +', ' + a6 +', ' + a7 +', ' + a8 +', ' + a9 +') -> ' + ret); return ret; }; break; case 10: glCtx[f] = function webgl_10(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) { var ret = glCtx[realf](a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); console.error('[Thread ' + threadId() + ', GL ctx: ' + contextHandle + ']: ' + f + '('+a1+', ' + a2 +', ' + a3 +', ' + a4 +', ' + a5 +', ' + a6 +', ' + a7 +', ' + a8 +', ' + a9 +', ' + a10 +') -> ' + ret); return ret; }; break; case 11: glCtx[f] = function webgl_11(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) { var ret = glCtx[realf](a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); console.error('[Thread ' + threadId() + ', GL ctx: ' + contextHandle + ']: ' + f + '('+a1+', ' + a2 +', ' + a3 +', ' + a4 +', ' + a5 +', ' + a6 +', ' + a7 +', ' + a8 +', ' + a9 +', ' + a10 +', ' + a11 +') -> ' + ret); return ret; }; break; - default: throw 'hookWebGL failed! Unexpected length ' + glCtx[realf].length; + default: console.warn('hookWebGL failed! Unexpected length ' + glCtx[realf].length); } }, @@ -2246,7 +2246,10 @@ var LibraryGL = { assert((value & 3) == 0, 'Pointer to integer data passed to glUniform1iv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION == 2 +#if MIN_WEBGL_VERSION >= 2 +#if GL_ASSERTIONS + assert(GL.currentContext.version >= 2); +#endif GLctx.uniform1iv(GL.uniforms[location], HEAP32, value>>2, count); #else @@ -2273,7 +2276,7 @@ var LibraryGL = { #endif } GLctx.uniform1iv(GL.uniforms[location], view); -#endif // MIN_WEBGL_VERSION == 2 +#endif // MIN_WEBGL_VERSION >= 2 }, glUniform2iv__sig: 'viii', @@ -2286,7 +2289,10 @@ var LibraryGL = { assert((value & 3) == 0, 'Pointer to integer data passed to glUniform2iv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION == 2 +#if MIN_WEBGL_VERSION >= 2 +#if GL_ASSERTIONS + assert(GL.currentContext.version >= 2); +#endif GLctx.uniform2iv(GL.uniforms[location], HEAP32, value>>2, count*2); #else @@ -2314,7 +2320,7 @@ var LibraryGL = { #endif } GLctx.uniform2iv(GL.uniforms[location], view); -#endif // MIN_WEBGL_VERSION == 2 +#endif // MIN_WEBGL_VERSION >= 2 }, glUniform3iv__sig: 'viii', @@ -2327,7 +2333,10 @@ var LibraryGL = { assert((value & 3) == 0, 'Pointer to integer data passed to glUniform3iv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION == 2 +#if MIN_WEBGL_VERSION >= 2 +#if GL_ASSERTIONS + assert(GL.currentContext.version >= 2); +#endif GLctx.uniform3iv(GL.uniforms[location], HEAP32, value>>2, count*3); #else @@ -2356,7 +2365,7 @@ var LibraryGL = { #endif } GLctx.uniform3iv(GL.uniforms[location], view); -#endif // MIN_WEBGL_VERSION == 2 +#endif // MIN_WEBGL_VERSION >= 2 }, glUniform4iv__sig: 'viii', @@ -2369,7 +2378,10 @@ var LibraryGL = { assert((value & 3) == 0, 'Pointer to integer data passed to glUniform4iv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION == 2 +#if MIN_WEBGL_VERSION >= 2 +#if GL_ASSERTIONS + assert(GL.currentContext.version >= 2); +#endif GLctx.uniform4iv(GL.uniforms[location], HEAP32, value>>2, count*4); #else @@ -2399,7 +2411,7 @@ var LibraryGL = { #endif } GLctx.uniform4iv(GL.uniforms[location], view); -#endif // MIN_WEBGL_VERSION == 2 +#endif // MIN_WEBGL_VERSION >= 2 }, glUniform1fv__sig: 'viii', @@ -2412,7 +2424,10 @@ var LibraryGL = { assert((value & 3) == 0, 'Pointer to float data passed to glUniform1fv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION == 2 +#if MIN_WEBGL_VERSION >= 2 +#if GL_ASSERTIONS + assert(GL.currentContext.version >= 2); +#endif GLctx.uniform1fv(GL.uniforms[location], HEAPF32, value>>2, count); #else @@ -2439,7 +2454,7 @@ var LibraryGL = { #endif } GLctx.uniform1fv(GL.uniforms[location], view); -#endif // MIN_WEBGL_VERSION == 2 +#endif // MIN_WEBGL_VERSION >= 2 }, glUniform2fv__sig: 'viii', @@ -2452,7 +2467,10 @@ var LibraryGL = { assert((value & 3) == 0, 'Pointer to float data passed to glUniform2fv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION == 2 +#if MIN_WEBGL_VERSION >= 2 +#if GL_ASSERTIONS + assert(GL.currentContext.version >= 2); +#endif GLctx.uniform2fv(GL.uniforms[location], HEAPF32, value>>2, count*2); #else @@ -2480,7 +2498,7 @@ var LibraryGL = { #endif } GLctx.uniform2fv(GL.uniforms[location], view); -#endif // MIN_WEBGL_VERSION == 2 +#endif // MIN_WEBGL_VERSION >= 2 }, glUniform3fv__sig: 'viii', @@ -2493,7 +2511,10 @@ var LibraryGL = { assert((value & 3) == 0, 'Pointer to float data passed to glUniform3fv must be aligned to four bytes!' + value); #endif -#if MIN_WEBGL_VERSION == 2 +#if MIN_WEBGL_VERSION >= 2 +#if GL_ASSERTIONS + assert(GL.currentContext.version >= 2); +#endif GLctx.uniform3fv(GL.uniforms[location], HEAPF32, value>>2, count*3); #else @@ -2522,7 +2543,7 @@ var LibraryGL = { #endif } GLctx.uniform3fv(GL.uniforms[location], view); -#endif // MIN_WEBGL_VERSION == 2 +#endif // MIN_WEBGL_VERSION >= 2 }, glUniform4fv__sig: 'viii', @@ -2535,7 +2556,10 @@ var LibraryGL = { assert((value & 3) == 0, 'Pointer to float data passed to glUniform4fv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION == 2 +#if MIN_WEBGL_VERSION >= 2 +#if GL_ASSERTIONS + assert(GL.currentContext.version >= 2); +#endif GLctx.uniform4fv(GL.uniforms[location], HEAPF32, value>>2, count*4); #else @@ -2569,7 +2593,7 @@ var LibraryGL = { #endif } GLctx.uniform4fv(GL.uniforms[location], view); -#endif // MIN_WEBGL_VERSION == 2 +#endif // MIN_WEBGL_VERSION >= 2 }, glUniformMatrix2fv__sig: 'viiii', @@ -2582,7 +2606,10 @@ var LibraryGL = { assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix2fv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION == 2 +#if MIN_WEBGL_VERSION >= 2 +#if GL_ASSERTIONS + assert(GL.currentContext.version >= 2); +#endif GLctx.uniformMatrix2fv(GL.uniforms[location], !!transpose, HEAPF32, value>>2, count*4); #else @@ -2612,7 +2639,7 @@ var LibraryGL = { #endif } GLctx.uniformMatrix2fv(GL.uniforms[location], !!transpose, view); -#endif // MIN_WEBGL_VERSION == 2 +#endif // MIN_WEBGL_VERSION >= 2 }, glUniformMatrix3fv__sig: 'viiii', @@ -2625,7 +2652,10 @@ var LibraryGL = { assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix3fv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION == 2 +#if MIN_WEBGL_VERSION >= 2 +#if GL_ASSERTIONS + assert(GL.currentContext.version >= 2); +#endif GLctx.uniformMatrix3fv(GL.uniforms[location], !!transpose, HEAPF32, value>>2, count*9); #else @@ -2660,7 +2690,7 @@ var LibraryGL = { #endif } GLctx.uniformMatrix3fv(GL.uniforms[location], !!transpose, view); -#endif // MIN_WEBGL_VERSION == 2 +#endif // MIN_WEBGL_VERSION >= 2 }, glUniformMatrix4fv__sig: 'viiii', @@ -2673,7 +2703,10 @@ var LibraryGL = { assert((value & 3) == 0, 'Pointer to float data passed to glUniformMatrix4fv must be aligned to four bytes!'); #endif -#if MIN_WEBGL_VERSION == 2 +#if MIN_WEBGL_VERSION >= 2 +#if GL_ASSERTIONS + assert(GL.currentContext.version >= 2); +#endif GLctx.uniformMatrix4fv(GL.uniforms[location], !!transpose, HEAPF32, value>>2, count*16); #else @@ -2719,7 +2752,7 @@ var LibraryGL = { #endif } GLctx.uniformMatrix4fv(GL.uniforms[location], !!transpose, view); -#endif // MIN_WEBGL_VERSION == 2 +#endif // MIN_WEBGL_VERSION >= 2 }, glBindBuffer__sig: 'vii', diff --git a/tests/minimal_webgl/CMakeLists.txt b/tests/minimal_webgl/CMakeLists.txt new file mode 100644 index 0000000000000..38767d41a6a33 --- /dev/null +++ b/tests/minimal_webgl/CMakeLists.txt @@ -0,0 +1,89 @@ +cmake_minimum_required(VERSION 2.8) + +# Default to release build if not specified +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) +endif() + +project(minimal_webgl) + +macro(append_linker_flags FLAGS) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAGS}") +endmacro() + +if (EMSCRIPTEN) + set(CMAKE_EXECUTABLE_SUFFIX ".html") + + # Link in the JS library file for support code + append_linker_flags("--js-library ${CMAKE_CURRENT_LIST_DIR}/library_js.js") + + # Link in to WebGL/GLES system library + append_linker_flags("-lGL") + + # Enable Closure compiler for aggressive JS size minification + append_linker_flags("--closure 1") + + # When marshalling C UTF-8 strings across the JS<->Wasm language boundary, favor smallest generated code size + # rather than performance + append_linker_flags("-s TEXTDECODER=2") + + # Enable aggressive MINIMAL_RUNTIME mode. + append_linker_flags("-s MINIMAL_RUNTIME=2") + + # Require WebGL 2 support in target browser, for smallest generated code size. (pass -s MIN_WEBGL_VERSION=1 to dual-target WebGL 1 and WebGL 2) + append_linker_flags("-s MIN_WEBGL_VERSION=2 -s MAX_WEBGL_VERSION=2") + + # Tell the example code in webgl.c that we are only targeting WebGL 2. + add_definitions(-DMAX_WEBGL_VERSION=2) + + # The generated build output is only to be expected to be run in a web browser, never in a native CLI shell, or in a web worker. + append_linker_flags("-s ENVIRONMENT=web") + + # Choose the oldest browser versions that should be supported. The higher minimum bar you choose, the less + # emulation code may be present for old browser quirks. + append_linker_flags("-s MIN_FIREFOX_VERSION=70") + append_linker_flags("-s MIN_SAFARI_VERSION=130000") + append_linker_flags("-s MIN_IE_VERSION=0x7FFFFFFF") # Do not support Internet Explorer at all (this is the Emscripten default, shown here for posterity) + append_linker_flags("-s MIN_EDGE_VERSION=79") # Require Chromium-based Edge browser + append_linker_flags("-s MIN_CHROME_VERSION=80") + + # Fine tuning for code size: do not generate code to abort program execution on malloc() failures, that will + # not be interesting here. + append_linker_flags("-s ABORTING_MALLOC=0") + + # Reduce WebGL code size: We do not need GLES2 emulation for automatic GL extension enabling + append_linker_flags("-s GL_SUPPORT_AUTOMATIC_ENABLE_EXTENSIONS=0") + + # Reduce WebGL code size: We do not need GLES2 emulation for GL extension names + append_linker_flags("-s GL_EXTENSIONS_IN_PREFIXED_FORMAT=0") + + # Reduce WebGL code size: No need to specify the GL_VENDOR/GL_RENDERER etc. fields in format required by GLES2 spec. + append_linker_flags("-s GL_EMULATE_GLES_VERSION_STRING_FORMAT=0") + + # Reduce WebGL code size at the expense of performance (this only has an effect in WebGL 1, practically a no-op here) + append_linker_flags("-s GL_POOL_TEMP_BUFFERS=0") + + # Reduce WebGL code size: WebGL bindings layer should not keep track of certain WebGL + # errors that are only meaningful for C/C++ applications. (good to enable for release when glGetError() is not used, but disable in debug) + append_linker_flags("-s GL_TRACK_ERRORS=0") + + # Reduce WebGL code size: do not emit code for extensions that we might not need. + append_linker_flags("-s GL_SUPPORT_SIMPLE_ENABLE_EXTENSIONS=0") + + # Optimization flag to optimize aggressively for size. (other options -Os, -O3, -O2, -O1, -O0) + append_linker_flags("-Oz") + + # Reduce code size: We do not need libc errno field support in our build output. + append_linker_flags("-s SUPPORT_ERRNO=0") + + # Reduce code size: We do not need native POSIX filesystem emulation support (Emscripten FS/MEMFS) + append_linker_flags("-s FILESYSTEM=0") +endif() + +file(GLOB_RECURSE sources *.cpp *.c *.h) +add_executable(minimal_webgl ${sources}) + +file(GLOB_RECURSE assets *.png) +foreach(asset ${assets}) + file(COPY "${asset}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/") +endforeach() diff --git a/tests/minimal_webgl/webgl.c b/tests/minimal_webgl/webgl.c index 30cd2fa792537..c5a638763e5df 100644 --- a/tests/minimal_webgl/webgl.c +++ b/tests/minimal_webgl/webgl.c @@ -1,8 +1,8 @@ -#include -#include -#include -#include -#include +#include // For emscripten_get_device_pixel_ratio() +#include // For Emscripten HTML5 WebGL context creation API +#include // For Emscripten WebGL API headers (see also webgl/webgl1_ext.h and webgl/webgl2.h) +#include // For NULL and strcmp() +#include // For assert() void upload_unicode_char_to_texture(int unicodeChar, int charSize, int applyShadow); void load_texture_from_url(GLuint texture, const char *url, int *outWidth, int *outHeight);