diff options
34 files changed, 1536 insertions, 192 deletions
diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..cca1b833 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,373 @@ +This document describes changes between tagged Emscripten SDK versions. + +Note that in the compiler, version numbering is used as the mechanism to invalidate internal compiler caches, +so version numbers do not necessarily reflect the amount of changes between versions. + +To browse or download snapshots of old tagged versions, visit https://github.com/kripken/emscripten/releases . + +Not all changes are documented here. In particular, new features, user-oriented fixes, options, command-line parameters, usage changes, deprecations, significant internal modifications and optimizations etc. generally deserve a mention. To examine the full set of changes between versions, visit the link to full changeset diff at the end of each section. + +v1.7.1: 10/24/2013 +------------------ + - Remove old call to Runtime.warn in file packager code + - Fix bug with parsing of empty types. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.7.0...1.7.1 + +v1.7.0: 10/23/2013 +------------------ + - Adds mouse wheel events support in GLUT library. + - Adds support for a new link parameter -s CASE_INSENSITIVE_VFS=1 to enable Emscripten virtual filesystem to search files ignoring case. + - *Numerous* optimizations in both compilation and runtime stages. + - Remove unnecessary whitespace, compact postSets function, and other optimizations in compilation output to save on generated file size. + - Fixes float parsing from negative zero. + - Removes the -s EMIT_GENERATED_FUNCTIONS link parameter as unneeded. + - Fixes an issue where updating subranges of GL uniform arrays was not possible. + - asm.js heap size (-s TOTAL_MEMORY=x) no longer needs to be a power of 2. As a relaxed rule, choosing any multiple of 16MB is now possible. + - O1 optimization no longer runs the 'simplifyExpressions' optimization pass. This is to improve build iteration times when using -O1. Use -O2 to run that pass. + - EM_ASM() can now be used even when compiling to asm.js. + - All currently specified non-debugging-related WebGL 1 extensions are now enabled by default on startup, no need to ctx.getExtension() manually to enable them. + - Improve readability of uncaught JavaScript exceptions that are thrown all the way up to the web console by printing out the stack trace of where the throw occurred. + - Fix an issue when renaming a directory to a subdirectory. + - Several compiler stability fixes. + - Adds a JavaScript implementation of cxa_demangle function for demangling call stack traces at runtime for easier debugging. + - GL context MSAA antialising is now DISABLED by default, to make the GL behavior consistent with desktop usage. + - Added support to SDL, GLUT and GLFW libraries to specify MSAA on/off at startup. + - Implemented glColor4ubv in GL emulation mode. + - Fix an issue with LLVM keyword __attribute__ ((__constructor__)) (#1155). + - Fix an issue with va_args and -s UNALIGNED_MEMORY=1 (#1705). + - Add initial support code for LLVM SIMD constructs and a JavaScript SIMD polyfill implementation from https://github.com/johnmccutchan/ecmascript_simd/ . + - Fixed support for node.js native filesystem API NODEFS on Windows. + - Optimize application startup times of Emscripten-compiled programs by enabling the virtual filesystem XHR and asm.js compilation to proceed in parallel when opening a page. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.6.4...1.7.0 + +v1.6.4: 9/30/2013 +------------------ + - Implements a new preprocessor tool for preparsing C struct definitions (#1554), useful for Emscripten support library implementors. + - Fix parsing issue with sscanf (#1668). + - Improved the responsiveness of compiler print output on Windows. + - Improved compilation times at link stage. + - Added support for new "NODEFS" filesystem that directly accesses files on the native filesystem. Only usable with node.js when compiling to JS. + - Added support for new IDBFS filesystem for accessing files in IndexedDB storage (#1601. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.6.3...1.6.4 + +v1.6.3: 9/26/2013 +------------------ + - Emscripten CMake toolchain now generates archive files with .a suffix when project target type is static library, instead of generatic .bc files (#1648). + - Adds iconv library from the musl project to implement wide functions in C library (#1670). + - Full list of changes: https://github.com/kripken/emscripten/compare/1.6.2...1.6.3 + +v1.6.2: 9/25/2013 +------------------ + - Added support for dprintf() function (#1250). + - Fixes several compiler stability issues (#1637, #1166, #1661, #1651 and more). + - Enables support for WEBGL_depth_texture. + - Adds support for new link flag -s GL_ASSERTIONS=1 which can be used to add extra validation layer to the Emscripten GL library to catch code issues. + - Adds support to Web Audio API in SDL audio backend so that SDL audio now works in Chrome and new Opera as well. + - Fixes an alpha blending issue with SDL_SetAlpha. + - Implemented locale-related code in C library. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.6.1...1.6.2 + +v1.6.1: 9/22/2013 +------------------ + - Several optimizations to compiler link stage. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.6.0...1.6.1 + +v1.6.0: 9/21/2013 +------------------ + - Enable support for %[] pattern in scanf. + - Added dependency tracking support to linked .js files in CMake toolchain. + - The hex prefix 0x is now properly handled in sscanf (#1632). + - Simplify internal compiler operations by removing the internal framework.js. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.5.9...1.6.0 + +v1.5.9: 9/15/2013 +------------------ + - Add support for SDL_Delay in web workers. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.5.8...1.5.9 + +v1.5.8: 9/14/2013 +------------------ + - Add support for the GCC -E compiler flag. + - Update Emscripten libc headers to musl-0.9.13. + - Added new utility function emscripten_async_load_script() to asynchronously load a new .js script URL. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.5.7...1.5.8 + +v1.5.7: 8/30/2013 +------------------ + - The script tag in default shell.html is now marked 'async', which enables loading the JS script code asynchronously in Firefox without making the main thread unresponsive. + - Implemented new utility function emscripten_get_canvas_size() which returns the current Module <canvas> element size in pixels. + - Optimize code size in compiled side modules. + - Optimize startup memory usage by avoiding unnecessary copying of VFS data at startup. + - Add support for SDL_WM_ToggleFullScreen(). + - Add support for emscripten_get_now() when running in SpiderMonkey shell. + - Added new environment variable EM_BUILD_VERBOSE=0,1,2,3 to set an extra compiler output verbosity level for debugging. + - Added better support for dlopen() to simulate dynamic library loading in JavaScript. + - Improved support for BSD sockets and networking. + - Added new SOCKFS filesystem, which reads files via a network connection. + - Avoid issues with long command line limitations in CMake toolchain by using response files. + - Fix issues with client-side vertex data rendering in GL emulation mode. + - Improved precision of clock_gettime(). + - Improve function outlining support. + - Added support for using NMake generator with CMake toolchain. + - Improved support for flexible arrays in structs (#1602). + - Added ability to marshal UTF16 and UTF32 strings between C++ <-> JS code. + - Added a new commandline tool validate_asms.py to help automating asm.js validation testing. + - Improved stability with inline asm() syntax. + - Updated libc headers to new version. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.5.6...1.5.7 + +v1.5.6: 8/17/2013 +------------------ + - Improved BSD sockets support. + - Added touch events support to GLUT library. + - Added new --js-opts=0/1 command line option to control whether JS optimizer is run or not. + - Improved OpenAL support. + - Added new command line tool tools/find_bigvars.py which can be used on a output file to detect large functions and needs for outlining. + - Merged link flags -s FORCE_GL_EMULATION and -s DISABLE_GL_EMULATION to a single opt-in flag -s LEGACY_GL_EMULATION=0/1 to control whether GL emulation is active. + - Improved SDL input support. + - Several stability-related compiler fixes. + - Fixed source mapping generation support on Windows. + - Added back the EMSCRIPTEN_KEEPALIVE attribute qualifier to help prevent inlining and to retain symbols in output without dead code elimination occurring. + - Fix issues when marshalling UTF8 strings between C<->JS. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.5.5...1.5.6 + +v1.5.5: 8/9/2013 +------------------ + - Update libcxx to revision 187959, 2013-08-08. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.5.4...1.5.5 + +v1.5.4: 8/9/2013 +------------------ + - Fixed multiple issues with C stdlib support. + - Fix audio buffer queueing issues with OpenAL. + - Improved BSD sockets support. + - Added a new compile+link time command line option -Wno-warn-absolute-paths to hide the emscripten compiler warning when absolute paths are passed into the compiler. + - Added new link flag -s STB_IMAGE=0/1 and integrate it to SDL image loading to enable synchronous image loading support with SDL. + - Several improvements on function outlining support. + - Fix issues with GLES2 interop support. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.5.3...1.5.4 + +v1.5.3: 6/28/2013 +------------------ + - Added new optimization level --llvm-lto 3 to run even more aggressive LTO optimizations. + - Improve optimizations for libc and other libraries. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.5.2...1.5.3 + +v1.5.2: 6/27/2013 +------------------ + - Added support for generating source maps along the built application when -g is specified. This lets the browser show original .cpp sources when debugging. + - GLUT and SDL improvements. + - Added new link option -g<level> where level=0-4, which allows controlling various levels of debuggability added to the output. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.5.1...1.5.2 + +v1.5.1: 6/22/2013 +------------------ + - File packager now skips all directories and files starting with '.', and hidden files on Windows. + - Fix issues with strnlen, memmove, LDBL_ constants, va_args, float.h, and others. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.5.0...1.5.1 + +v1.5.0: 6/17/2013 +------------------ + - Several compiler optimizations. + - Improve SDL key events support. + - Increase debug logging when specifying emcc -v. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.4.9...1.5.0 + +v1.4.9: 6/8/2013 +------------------ + - Several compiler optimizations. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.4.8...1.4.9 + +v1.4.8: 6/6/2013 +------------------ + - Add support for webrtc-based sockets. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.4.7...1.4.8 + +v1.4.7: 6/2/2013 +------------------ + - Remove more unneeded break and continue statements in relooper. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.4.6...1.4.7 + +v1.4.6: 6/2/2013 +------------------ + - Improve relooper code. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.4.5...1.4.6 + +v1.4.5: 6/1/2013 +------------------ + - Improve relooper code. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.4.4...1.4.5 + +v1.4.4: 6/1/2013 +------------------ + - Add support for symlinks in source files. + - Fix various issues with SDL. + - Added -s FORCE_ALIGNED_MEMORY=0/1 link time flag to control whether all loads and stores are assumed to be aligned. + - Fix file packager to work with closure. + - Major improvements to embind support, and optimizations. + - Improve GL emulation. + - Optimize VFS usage. + - Allow emscripten to compile .m and .mm files. + - Added new syntax --preload-file src@dst to file packager command line to allow placing source files to custom destinations in the FS. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.4.3...1.4.4 + +v1.4.3: 5/8/2013 +------------------ + - Fix issue with strcat. + - Major embind improvements. + - Switch to le32-unknown-nacl LLVM target triple as default build option instead of the old i386-pc-linux-gnu target triple. + - Improve compiler logging behavior. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.4.2...1.4.3 + +v1.4.2: 5/3/2013 +------------------ + - Fix issues with le32-unknown-nacl LLVM target triple. + - Add some GLEW support. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.4.1...1.4.2 + +v1.4.1: 4/28/2013 +------------------ + - Implement support for le32-unknown-nacl LLVM target triple. + - Added new cmdline option -s ERROR_ON_UNDEFINED_SYMBOLS=0/1 to give compile-time error on undefined symbols at link time. Default off. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.3.8...1.4.1 + +v1.3.8: 4/29/2013 +------------------ + - Improved 64-bit integer ops codegen. + - Added Indexed DB support to vfs. + - Improve warning message on dangerous function pointer casts when compiling in asm.js mode. + - Added --use-preload-cache command line option to emcc, to be used with the file packager. + - Fixes to libcextra. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.3.7...1.3.8 + +v1.3.7: 4/24/2013 +------------------ + - Merge IMVU implementation of embind to emscripten trunk. Embind allows high-level C++ <-> JS types interop. + - Enable asm.js compilation in -O1 and higher by default. Fix issues when compiling to asm.js. + - Improve libc support with Emscripten with the musl libc headers. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.3.6...1.3.7 + +v1.3.6: 4/2/2013 +------------------ + - Fix hang issue with strtof. + - Update libcxx to upstream r178253 from March 29, 2013. + - Fix issues with GL emulation. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.3.5...1.3.6 + +v1.3.5: 3/25/2013 +------------------ + - Get exceptions working as they did before. + - Remove symbol removing hack. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.3.4...1.3.5 + +v1.3.4: 3/24/2013 +------------------ + - Update to new libcxx and libcxxabi versions from upstream. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.3.3...1.3.4 + +v1.3.3: 3/23/2013 +------------------ + - Remove unneeded check from relooper. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.3.2...1.3.3 + +v1.3.2: 3/22/2013 +------------------ + - Fix issues with fgets. + - Add support for non-fullscreen pointer lock. + - Improve OpenAL support. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.3.1...1.3.2 + +v1.3.1: 3/19/2013 +------------------ + - Improve SDL audio and mixer support. + - Add GLES2 emulation features when -s FULL_ES2=1 is specified. + - Add support for OpenAL. + - Add new -s OPENAL_DEBUG=0/1 link command line option. + - Fixed an issue with mouse coordinate being offset with canvas. + - Removed -s UTF_STRING_SUPPORT=0 parameter, this is now always on. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.3.0...1.3.1 + +v1.3.0: 3/11/2013 +------------------ + - Improve GLES2 emulation with -s FULL_ES2=1. + - Deprecated -s USE_TYPED_ARRAYS=1 and -s QUANTUM_SIZE=1. + - Implement a minifier infrastructure when compiling for asm.js. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.2.9...1.3.0 + +v1.2.9: 3/7/2013 +------------------ + - Improved canvas behavior when transitioning between fullscreen. + - Added support for getopt(). + - Fixed several libc issues. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.2.8...1.2.9 + +v1.2.8: 3/6/2013 +------------------ + - Remove unnecessary recursion in relooper RemoveUnneededFlows. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.2.7...1.2.8 + +v1.2.7: 3/6/2013 +------------------ + - Added SDL_Mixer support. + - Implemented stubs for several Unix and threading-related functions. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.2.6...1.2.7 + +v1.2.6: 3/5/2013 +------------------ + - Relooper updates. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.2.5...1.2.6 + +v1.2.5: 3/5/2013 +------------------ + - Greatly improve GL emulation support. + - Handle %c in sscanf. + - Improve compilation times by optimizing parallel execution in the linker. + - Improve several compiler stability issues detected from fuzzing tests. + - Implemented emscripten_jcache_printf. + - Allow running emscripten.py outside emcc itself. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.2.4...1.2.5 + +v1.2.4: 2/2/2013 +------------------ + - Work on adding support for asm.js compilation. + - Improve EGL support. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.2.3...1.2.4 + +v1.2.3: 1/9/2013 +------------------ + - Work on adding support for asm.js compilation. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.2.2...1.2.3 + +v1.2.2: 1/8/2013 +------------------ + - Work on adding support for asm.js compilation. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.2.1...1.2.2 + +v1.2.1: 1/8/2013 +------------------ + - Improvements to GLUT, SDL and BSD sockets headers. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.2.0...1.2.1 + +v1.2.0: 1/1/2013 +------------------ + - Work on adding support for asm.js compilation. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.1.0...1.2.0 + +v1.1.0: 12/12/2012 +------------------ + - Fix several issues with Windows support. + - Added a standalone toolchain for CMake. + - Added emscripten_run_script_string(). + - Optimize compilation times via threading. + - Update to requiring Clang 3.2. Older versions may no longer work. + - Several improvements to emscripten library support headers. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.0.1a...1.1.0 + +v1.0.1a: 11/11/2012 +------------------ + - Add relooper code to repository. + - Full list of changes: https://github.com/kripken/emscripten/compare/1.0.1...1.0.1a + +v1.0.1: 11/11/2012 +------------------ + - First commit that introduced versioning to the Emscripten compiler. @@ -397,7 +397,8 @@ Options that are modified or new in %s include: --shell-file <path> The path name to a skeleton HTML file used when generating HTML output. The shell file used needs to have this token inside it: - {{{ SCRIPT_CODE }}} + {{{ SCRIPT }}} + (see src/shell.html for an example) Note that this argument is ignored if a target other than HTML is specified using the -o option. @@ -501,7 +502,8 @@ The target file, if specified (-o <target>), defines what will be generated: <name>.js JavaScript - <name>.html HTML with embedded JavaScript + <name>.html HTML + side JavaScript file (<name>.js) + (JS on the side improves page load time) <name>.bc LLVM bitcode (default) <name>.o LLVM bitcode (same as .bc) @@ -542,7 +544,7 @@ There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR P exit(0) elif len(sys.argv) == 2 and sys.argv[1] == '-v': # -v with no inputs - print 'emcc (Emscripten GCC-like replacement + linker emulating GNU ld ) 2.0' + print 'emcc (Emscripten GCC-like replacement + linker emulating GNU ld ) %s' % shared.EMSCRIPTEN_VERSION exit(subprocess.call([shared.CLANG, '-v'])) def is_minus_s_for_emcc(newargs,i): @@ -1818,22 +1820,21 @@ try: if final_suffix == 'html': logging.debug('generating HTML') shell = open(shell_path).read() + assert '{{{ SCRIPT }}}' in shell, 'HTML shell must contain {{{ SCRIPT }}} , see src/shell.html for an example' html = open(target, 'w') + js_target = unsuffixed(target) + '.js' + base_js_target = os.path.basename(js_target) if proxy_to_worker: - html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(shared.path_from_root('src', 'proxyClient.js')).read().replace('{{{ filename }}}', target_basename))) - js_target = unsuffixed(target) + '.js' - shutil.copyfile(final, js_target) + html.write(shell.replace('{{{ SCRIPT }}}', '<script>' + open(shared.path_from_root('src', 'proxyClient.js')).read().replace('{{{ filename }}}', target_basename) + '</script>')) + shutil.move(final, js_target) elif not Compression.on: if debug_level >= 4: - match = re.match('.*?<script[^>]*>{{{ SCRIPT_CODE }}}</script>', shell, - re.DOTALL) - if match is None: - raise RuntimeError('''Could not find script insertion point - make sure you have <script type='text/javascript'>{{{ SCRIPT_CODE }}}</script> in your HTML file (with no newlines)''') - generate_source_map(target, match.group().count('\n')) - html.write(shell.replace('{{{ SCRIPT_CODE }}}', open(final).read())) + generate_source_map(target) + shutil.move(final, js_target) + script_tag = '''<script async type="text/javascript" src="%s"></script>''' % base_js_target + html.write(shell.replace('{{{ SCRIPT }}}', script_tag)) else: # Compress the main code - js_target = unsuffixed(target) + '.js' shutil.move(final, js_target) Compression.compress(js_target) @@ -1881,8 +1882,8 @@ try: }); }; compiledCodeXHR.send(null); -''' % Compression.compressed_name(js_target) - html.write(shell.replace('{{{ SCRIPT_CODE }}}', decoding)) +''' % Compression.compressed_name(base_js_target) + html.write(shell.replace('{{{ SCRIPT }}}', '<script>' + decoding + '</script>')) # Add decompressor with web worker glue code decompressor = open('decompress.js', 'w') diff --git a/src/intertyper.js b/src/intertyper.js index 96db6966..d3640889 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -672,12 +672,14 @@ function intertyper(lines, sidePass, baseLineNums) { assert((item.tokens[5].text.match(/=/g) || []).length <= 1, 'we only support at most 1 exported variable from inline js: ' + item.ident); var i = 0; var params = [], args = []; - splitTokenList(tokensLeft[3].tokens).map(function(element) { - var ident = toNiceIdent(element[1].text); - var type = element[0].text; - params.push('$' + (i++)); - args.push(ident); - }); + if (tokensLeft[3].tokens) { + splitTokenList(tokensLeft[3].tokens).map(function(element) { + var ident = toNiceIdent(element[1].text); + var type = element[0].text; + params.push('$' + (i++)); + args.push(ident); + }); + } if (item.assignTo) item.ident = 'return ' + item.ident; item.ident = '(function(' + params + ') { ' + item.ident + ' })(' + args + ');'; return { forward: null, ret: item, item: item }; diff --git a/src/library_egl.js b/src/library_egl.js index c25dc8ef..cc702fec 100644 --- a/src/library_egl.js +++ b/src/library_egl.js @@ -10,6 +10,8 @@ var LibraryEGL = { $EGL: { // This variable tracks the success status of the most recently invoked EGL function call. eglErrorCode: 0x3000 /* EGL_SUCCESS */, + + stringCache: {}, setErrorCode: function(code) { EGL.eglErrorCode = code; @@ -416,15 +418,19 @@ var LibraryEGL = { } //\todo An EGL_NOT_INITIALIZED error is generated if EGL is not initialized for dpy. EGL.setErrorCode(0x3000 /* EGL_SUCCESS */); + if (EGL.stringCache[name]) return EGL.stringCache[name]; + var ret; switch(name) { - case 0x3053 /* EGL_VENDOR */: return allocate(intArrayFromString("Emscripten"), 'i8', ALLOC_NORMAL); - case 0x3054 /* EGL_VERSION */: return allocate(intArrayFromString("1.4 Emscripten EGL"), 'i8', ALLOC_NORMAL); - case 0x3055 /* EGL_EXTENSIONS */: return allocate(intArrayFromString(""), 'i8', ALLOC_NORMAL); // Currently not supporting any EGL extensions. - case 0x308D /* EGL_CLIENT_APIS */: return allocate(intArrayFromString("OpenGL_ES"), 'i8', ALLOC_NORMAL); + case 0x3053 /* EGL_VENDOR */: ret = allocate(intArrayFromString("Emscripten"), 'i8', ALLOC_NORMAL); break; + case 0x3054 /* EGL_VERSION */: ret = allocate(intArrayFromString("1.4 Emscripten EGL"), 'i8', ALLOC_NORMAL); break; + case 0x3055 /* EGL_EXTENSIONS */: ret = allocate(intArrayFromString(""), 'i8', ALLOC_NORMAL); break; // Currently not supporting any EGL extensions. + case 0x308D /* EGL_CLIENT_APIS */: ret = allocate(intArrayFromString("OpenGL_ES"), 'i8', ALLOC_NORMAL); break; default: EGL.setErrorCode(0x300C /* EGL_BAD_PARAMETER */); return 0; } + EGL.stringCache[name] = ret; + return ret; }, // EGLAPI EGLBoolean EGLAPIENTRY eglBindAPI(EGLenum api); diff --git a/src/library_fs.js b/src/library_fs.js index bd1522a8..aece2664 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -28,6 +28,7 @@ mergeInto(LibraryManager.library, { ignorePermissions: true, ErrnoError: null, // set during init + genericErrors: {}, handleFSError: function(e) { if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + stackTrace(); @@ -62,7 +63,7 @@ mergeInto(LibraryManager.library, { } current = FS.lookupNode(current, parts[i]); - current_path = PATH.join(current_path, parts[i]); + current_path = PATH.join2(current_path, parts[i]); // jump to the mount's root node if this is a mountpoint if (FS.isMountpoint(current)) { @@ -94,9 +95,11 @@ mergeInto(LibraryManager.library, { var path; while (true) { if (FS.isRoot(node)) { - return path ? PATH.join(node.mount.mountpoint, path) : node.mount.mountpoint; + var mount = node.mount.mountpoint; + if (!path) return mount; + return mount[mount.length-1] !== '/' ? mount + '/' + path : mount + path; } - path = path ? PATH.join(node.name, path) : node.name; + path = path ? node.name + '/' + path : node.name; node = node.parent; } }, @@ -158,44 +161,50 @@ mergeInto(LibraryManager.library, { return FS.lookup(parent, name); }, createNode: function(parent, name, mode, rdev) { - var node = { - id: FS.nextInode++, - name: name, - mode: mode, - node_ops: {}, - stream_ops: {}, - rdev: rdev, - parent: null, - mount: null - }; - if (!parent) { - parent = node; // root node sets parent to itself - } - node.parent = parent; - node.mount = parent.mount; - // compatibility - var readMode = {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}}; - var writeMode = {{{ cDefine('S_IWUGO') }}}; - // NOTE we must use Object.defineProperties instead of individual calls to - // Object.defineProperty in order to make closure compiler happy - Object.defineProperties(node, { - read: { - get: function() { return (node.mode & readMode) === readMode; }, - set: function(val) { val ? node.mode |= readMode : node.mode &= ~readMode; } - }, - write: { - get: function() { return (node.mode & writeMode) === writeMode; }, - set: function(val) { val ? node.mode |= writeMode : node.mode &= ~writeMode; } - }, - isFolder: { - get: function() { return FS.isDir(node.mode); }, - }, - isDevice: { - get: function() { return FS.isChrdev(node.mode); }, - }, - }); - FS.hashAddNode(node); - return node; + if (!FS.FSNode) { + FS.FSNode = function(parent, name, mode, rdev) { + this.id = FS.nextInode++; + this.name = name; + this.mode = mode; + this.node_ops = {}; + this.stream_ops = {}; + this.rdev = rdev; + this.parent = null; + this.mount = null; + if (!parent) { + parent = this; // root node sets parent to itself + } + this.parent = parent; + this.mount = parent.mount; + FS.hashAddNode(this); + }; + + // compatibility + var readMode = {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}}; + var writeMode = {{{ cDefine('S_IWUGO') }}}; + + FS.FSNode.prototype = {}; + + // NOTE we must use Object.defineProperties instead of individual calls to + // Object.defineProperty in order to make closure compiler happy + Object.defineProperties(FS.FSNode.prototype, { + read: { + get: function() { return (this.mode & readMode) === readMode; }, + set: function(val) { val ? this.mode |= readMode : this.mode &= ~readMode; } + }, + write: { + get: function() { return (this.mode & writeMode) === writeMode; }, + set: function(val) { val ? this.mode |= writeMode : this.mode &= ~writeMode; } + }, + isFolder: { + get: function() { return FS.isDir(this.mode); }, + }, + isDevice: { + get: function() { return FS.isChrdev(this.mode); }, + }, + }); + } + return new FS.FSNode(parent, name, mode, rdev); }, destroyNode: function(node) { FS.hashRemoveNode(node); @@ -351,24 +360,28 @@ mergeInto(LibraryManager.library, { // object isn't directly passed in. not possible until // SOCKFS is completed. createStream: function(stream, fd_start, fd_end) { + if (!FS.FSStream) { + FS.FSStream = {}; + // compatibility + Object.defineProperties(FS.FSStream, { + object: { + get: function() { return this.node; }, + set: function(val) { this.node = val; } + }, + isRead: { + get: function() { return (this.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_WRONLY') }}}; } + }, + isWrite: { + get: function() { return (this.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_RDONLY') }}}; } + }, + isAppend: { + get: function() { return (this.flags & {{{ cDefine('O_APPEND') }}}); } + } + }); + } + stream.prototype = FS.FSStream; var fd = FS.nextfd(fd_start, fd_end); stream.fd = fd; - // compatibility - Object.defineProperties(stream, { - object: { - get: function() { return stream.node; }, - set: function(val) { stream.node = val; } - }, - isRead: { - get: function() { return (stream.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_WRONLY') }}}; } - }, - isWrite: { - get: function() { return (stream.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_RDONLY') }}}; } - }, - isAppend: { - get: function() { return (stream.flags & {{{ cDefine('O_APPEND') }}}); } - } - }); FS.streams[fd] = stream; return stream; }, @@ -771,7 +784,6 @@ mergeInto(LibraryManager.library, { }); }, open: function(path, flags, mode, fd_start, fd_end) { - path = PATH.normalize(path); flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags; mode = typeof mode === 'undefined' ? 0666 : mode; if ((flags & {{{ cDefine('O_CREAT') }}})) { @@ -780,13 +792,18 @@ mergeInto(LibraryManager.library, { mode = 0; } var node; - try { - var lookup = FS.lookupPath(path, { - follow: !(flags & {{{ cDefine('O_NOFOLLOW') }}}) - }); - node = lookup.node; - } catch (e) { - // ignore + if (typeof path === 'object') { + node = path; + } else { + path = PATH.normalize(path); + try { + var lookup = FS.lookupPath(path, { + follow: !(flags & {{{ cDefine('O_NOFOLLOW') }}}) + }); + node = lookup.node; + } catch (e) { + // ignore + } } // perhaps we need to create the node if ((flags & {{{ cDefine('O_CREAT') }}})) { @@ -1079,6 +1096,11 @@ mergeInto(LibraryManager.library, { }; FS.ErrnoError.prototype = new Error(); FS.ErrnoError.prototype.constructor = FS.ErrnoError; + // Some errors may happen quite a bit, to avoid overhead we reuse them (and suffer a lack of stack info) + [ERRNO_CODES.ENOENT].forEach(function(code) { + FS.genericErrors[code] = new FS.ErrnoError(code); + FS.genericErrors[code].stack = '<generic error, no stack>'; + }); }, staticInit: function() { FS.ensureErrnoError(); @@ -1173,7 +1195,7 @@ mergeInto(LibraryManager.library, { return ret; }, createFolder: function(parent, name, canRead, canWrite) { - var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); var mode = FS.getMode(canRead, canWrite); return FS.mkdir(path, mode); }, @@ -1183,7 +1205,7 @@ mergeInto(LibraryManager.library, { while (parts.length) { var part = parts.pop(); if (!part) continue; - var current = PATH.join(parent, part); + var current = PATH.join2(parent, part); try { FS.mkdir(current); } catch (e) { @@ -1194,12 +1216,12 @@ mergeInto(LibraryManager.library, { return current; }, createFile: function(parent, name, properties, canRead, canWrite) { - var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); var mode = FS.getMode(canRead, canWrite); return FS.create(path, mode); }, createDataFile: function(parent, name, data, canRead, canWrite, canOwn) { - var path = name ? PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent; + var path = name ? PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name) : parent; var mode = FS.getMode(canRead, canWrite); var node = FS.create(path, mode); if (data) { @@ -1209,16 +1231,16 @@ mergeInto(LibraryManager.library, { data = arr; } // make sure we can write to the file - FS.chmod(path, mode | {{{ cDefine('S_IWUGO') }}}); - var stream = FS.open(path, 'w'); + FS.chmod(node, mode | {{{ cDefine('S_IWUGO') }}}); + var stream = FS.open(node, 'w'); FS.write(stream, data, 0, data.length, 0, canOwn); FS.close(stream); - FS.chmod(path, mode); + FS.chmod(node, mode); } return node; }, createDevice: function(parent, name, input, output) { - var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); var mode = FS.getMode(!!input, !!output); if (!FS.createDevice.major) FS.createDevice.major = 64; var dev = FS.makedev(FS.createDevice.major++, 0); @@ -1272,7 +1294,7 @@ mergeInto(LibraryManager.library, { return FS.mkdev(path, mode, dev); }, createLink: function(parent, name, target, canRead, canWrite) { - var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var path = PATH.join2(typeof parent === 'string' ? parent : FS.getPath(parent), name); return FS.symlink(target, path); }, // Makes sure a file's contents are loaded. Returns whether the file has @@ -1462,7 +1484,7 @@ mergeInto(LibraryManager.library, { Browser.init(); // TODO we should allow people to just pass in a complete filename instead // of parent and name being that we just join them anyways - var fullname = name ? PATH.resolve(PATH.join(parent, name)) : parent; + var fullname = name ? PATH.resolve(PATH.join2(parent, name)) : parent; function processData(byteArray) { function finish(byteArray) { if (!dontCreateFile) { diff --git a/src/library_gl.js b/src/library_gl.js index 1ea8efc2..544a33db 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -42,6 +42,8 @@ var LibraryGL = { uniformTable: {}, // name => uniform ID. the uID must be identical until relinking, cannot create a new uID each call to glGetUniformLocation + stringCache: {}, + packAlignment: 4, // default alignment is 4 bytes unpackAlignment: 4, // default alignment is 4 bytes @@ -497,11 +499,14 @@ var LibraryGL = { glGetString__sig: 'ii', glGetString: function(name_) { + if (GL.stringCache[name_]) return GL.stringCache[name_]; + var ret; switch(name_) { case 0x1F00 /* GL_VENDOR */: case 0x1F01 /* GL_RENDERER */: case 0x1F02 /* GL_VERSION */: - return allocate(intArrayFromString(Module.ctx.getParameter(name_)), 'i8', ALLOC_NORMAL); + ret = allocate(intArrayFromString(Module.ctx.getParameter(name_)), 'i8', ALLOC_NORMAL); + break; case 0x1F03 /* GL_EXTENSIONS */: var exts = Module.ctx.getSupportedExtensions(); var gl_exts = []; @@ -509,12 +514,16 @@ var LibraryGL = { gl_exts.push(exts[i]); gl_exts.push("GL_" + exts[i]); } - return allocate(intArrayFromString(gl_exts.join(' ')), 'i8', ALLOC_NORMAL); // XXX this leaks! TODO: Cache all results like this in library_gl.js to be clean and nice and avoid leaking. + ret = allocate(intArrayFromString(gl_exts.join(' ')), 'i8', ALLOC_NORMAL); + break; case 0x8B8C /* GL_SHADING_LANGUAGE_VERSION */: - return allocate(intArrayFromString('OpenGL ES GLSL 1.00 (WebGL)'), 'i8', ALLOC_NORMAL); + ret = allocate(intArrayFromString('OpenGL ES GLSL 1.00 (WebGL)'), 'i8', ALLOC_NORMAL); + break; default: throw 'Failure: Invalid glGetString value: ' + name_; } + GL.stringCache[name_] = ret; + return ret; }, glGetIntegerv__sig: 'vii', @@ -1794,13 +1803,16 @@ var LibraryGL = { var glGetString = _glGetString; _glGetString = function(name_) { + if (GL.stringCache[name_]) return GL.stringCache[name_]; switch(name_) { case 0x1F03 /* GL_EXTENSIONS */: // Add various extensions that we can support - return allocate(intArrayFromString(Module.ctx.getSupportedExtensions().join(' ') + + var ret = allocate(intArrayFromString(Module.ctx.getSupportedExtensions().join(' ') + ' GL_EXT_texture_env_combine GL_ARB_texture_env_crossbar GL_ATI_texture_env_combine3 GL_NV_texture_env_combine4 GL_EXT_texture_env_dot3 GL_ARB_multitexture GL_ARB_vertex_buffer_object GL_EXT_framebuffer_object GL_ARB_vertex_program GL_ARB_fragment_program GL_ARB_shading_language_100 GL_ARB_shader_objects GL_ARB_vertex_shader GL_ARB_fragment_shader GL_ARB_texture_cube_map GL_EXT_draw_range_elements' + (GL.compressionExt ? ' GL_ARB_texture_compression GL_EXT_texture_compression_s3tc' : '') + (GL.anisotropicExt ? ' GL_EXT_texture_filter_anisotropic' : '') ), 'i8', ALLOC_NORMAL); + GL.stringCache[name_] = ret; + return ret; } return glGetString(name_); }; diff --git a/src/library_glfw.js b/src/library_glfw.js index b0519e39..647d4bb6 100644 --- a/src/library_glfw.js +++ b/src/library_glfw.js @@ -355,7 +355,9 @@ var LibraryGLFW = { } var contextAttributes = { - antialias: (GLFW.params[0x00020013] > 1) //GLFW_FSAA_SAMPLES + antialias: (GLFW.params[0x00020013] > 1), //GLFW_FSAA_SAMPLES + depth: (GLFW.params[0x00020009] > 0), //GLFW_DEPTH_BITS + stencil: (GLFW.params[0x0002000A] > 0) //GLFW_STENCIL_BITS } Module.ctx = Browser.createContext(Module['canvas'], true, true, contextAttributes); return 1; //GL_TRUE diff --git a/src/library_glut.js b/src/library_glut.js index 722ea85c..fefe7bd3 100644 --- a/src/library_glut.js +++ b/src/library_glut.js @@ -427,7 +427,9 @@ var LibraryGLUT = { glutCreateWindow__deps: ['$Browser'], glutCreateWindow: function(name) { var contextAttributes = { - antialias: ((GLUT.initDisplayMode & 0x0080 /*GLUT_MULTISAMPLE*/) != 0) + antialias: ((GLUT.initDisplayMode & 0x0080 /*GLUT_MULTISAMPLE*/) != 0), + depth: ((GLUT.initDisplayMode & 0x0010 /*GLUT_DEPTH*/) != 0), + stencil: ((GLUT.initDisplayMode & 0x0020 /*GLUT_STENCIL*/) != 0) }; Module.ctx = Browser.createContext(Module['canvas'], true, true, contextAttributes); return Module.ctx ? 1 /* a new GLUT window ID for the created context */ : 0 /* failure */; diff --git a/src/library_idbfs.js b/src/library_idbfs.js index 9031bad8..ab55673f 100644 --- a/src/library_idbfs.js +++ b/src/library_idbfs.js @@ -130,7 +130,7 @@ mergeInto(LibraryManager.library, { }; var toAbsolute = function(root) { return function(p) { - return PATH.join(root, p); + return PATH.join2(root, p); } }; diff --git a/src/library_memfs.js b/src/library_memfs.js index 94fd767e..588bbabd 100644 --- a/src/library_memfs.js +++ b/src/library_memfs.js @@ -1,6 +1,8 @@ mergeInto(LibraryManager.library, { $MEMFS__deps: ['$FS'], $MEMFS: { + ops_table: null, + // content modes CONTENT_OWNING: 1, // contains a subarray into the heap, and we own it, without copying (note: someone else needs to free() it, if that is necessary) CONTENT_FLEXIBLE: 2, // has been modified or never set to anything, and is a flexible js array that can grow/shrink @@ -13,51 +15,71 @@ mergeInto(LibraryManager.library, { // no supported throw new FS.ErrnoError(ERRNO_CODES.EPERM); } + if (!MEMFS.ops_table) { + MEMFS.ops_table = { + dir: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr, + lookup: MEMFS.node_ops.lookup, + mknod: MEMFS.node_ops.mknod, + mknod: MEMFS.node_ops.mknod, + rename: MEMFS.node_ops.rename, + unlink: MEMFS.node_ops.unlink, + rmdir: MEMFS.node_ops.rmdir, + readdir: MEMFS.node_ops.readdir, + symlink: MEMFS.node_ops.symlink + }, + stream: { + llseek: MEMFS.stream_ops.llseek + } + }, + file: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr + }, + stream: { + llseek: MEMFS.stream_ops.llseek, + read: MEMFS.stream_ops.read, + write: MEMFS.stream_ops.write, + allocate: MEMFS.stream_ops.allocate, + mmap: MEMFS.stream_ops.mmap + } + }, + link: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr, + readlink: MEMFS.node_ops.readlink + }, + stream: {} + }, + chrdev: { + node: { + getattr: MEMFS.node_ops.getattr, + setattr: MEMFS.node_ops.setattr + }, + stream: FS.chrdev_stream_ops + }, + }; + } var node = FS.createNode(parent, name, mode, dev); if (FS.isDir(node.mode)) { - node.node_ops = { - getattr: MEMFS.node_ops.getattr, - setattr: MEMFS.node_ops.setattr, - lookup: MEMFS.node_ops.lookup, - mknod: MEMFS.node_ops.mknod, - mknod: MEMFS.node_ops.mknod, - rename: MEMFS.node_ops.rename, - unlink: MEMFS.node_ops.unlink, - rmdir: MEMFS.node_ops.rmdir, - readdir: MEMFS.node_ops.readdir, - symlink: MEMFS.node_ops.symlink - }; - node.stream_ops = { - llseek: MEMFS.stream_ops.llseek - }; + node.node_ops = MEMFS.ops_table.dir.node; + node.stream_ops = MEMFS.ops_table.dir.stream; node.contents = {}; } else if (FS.isFile(node.mode)) { - node.node_ops = { - getattr: MEMFS.node_ops.getattr, - setattr: MEMFS.node_ops.setattr - }; - node.stream_ops = { - llseek: MEMFS.stream_ops.llseek, - read: MEMFS.stream_ops.read, - write: MEMFS.stream_ops.write, - allocate: MEMFS.stream_ops.allocate, - mmap: MEMFS.stream_ops.mmap - }; + node.node_ops = MEMFS.ops_table.file.node; + node.stream_ops = MEMFS.ops_table.file.stream; node.contents = []; node.contentMode = MEMFS.CONTENT_FLEXIBLE; } else if (FS.isLink(node.mode)) { - node.node_ops = { - getattr: MEMFS.node_ops.getattr, - setattr: MEMFS.node_ops.setattr, - readlink: MEMFS.node_ops.readlink - }; - node.stream_ops = {}; + node.node_ops = MEMFS.ops_table.link.node; + node.stream_ops = MEMFS.ops_table.link.stream; } else if (FS.isChrdev(node.mode)) { - node.node_ops = { - getattr: MEMFS.node_ops.getattr, - setattr: MEMFS.node_ops.setattr - }; - node.stream_ops = FS.chrdev_stream_ops; + node.node_ops = MEMFS.ops_table.chrdev.node; + node.stream_ops = MEMFS.ops_table.chrdev.stream; } node.timestamp = Date.now(); // add the new node to the parent @@ -117,7 +139,7 @@ mergeInto(LibraryManager.library, { } }, lookup: function(parent, name) { - throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + throw FS.genericErrors[ERRNO_CODES.ENOENT]; }, mknod: function(parent, name, mode, dev) { return MEMFS.createNode(parent, name, mode, dev); diff --git a/src/library_nodefs.js b/src/library_nodefs.js index 2be54076..7686f3f2 100644 --- a/src/library_nodefs.js +++ b/src/library_nodefs.js @@ -134,7 +134,7 @@ mergeInto(LibraryManager.library, { } }, lookup: function (parent, name) { - var path = PATH.join(NODEFS.realPath(parent), name); + var path = PATH.join2(NODEFS.realPath(parent), name); var mode = NODEFS.getMode(path); return NODEFS.createNode(parent, name, mode); }, @@ -156,7 +156,7 @@ mergeInto(LibraryManager.library, { }, rename: function (oldNode, newDir, newName) { var oldPath = NODEFS.realPath(oldNode); - var newPath = PATH.join(NODEFS.realPath(newDir), newName); + var newPath = PATH.join2(NODEFS.realPath(newDir), newName); try { fs.renameSync(oldPath, newPath); } catch (e) { @@ -165,7 +165,7 @@ mergeInto(LibraryManager.library, { } }, unlink: function(parent, name) { - var path = PATH.join(NODEFS.realPath(parent), name); + var path = PATH.join2(NODEFS.realPath(parent), name); try { fs.unlinkSync(path); } catch (e) { @@ -174,7 +174,7 @@ mergeInto(LibraryManager.library, { } }, rmdir: function(parent, name) { - var path = PATH.join(NODEFS.realPath(parent), name); + var path = PATH.join2(NODEFS.realPath(parent), name); try { fs.rmdirSync(path); } catch (e) { @@ -192,7 +192,7 @@ mergeInto(LibraryManager.library, { } }, symlink: function(parent, newName, oldPath) { - var newPath = PATH.join(NODEFS.realPath(parent), newName); + var newPath = PATH.join2(NODEFS.realPath(parent), newName); try { fs.symlinkSync(oldPath, newPath); } catch (e) { @@ -283,4 +283,4 @@ mergeInto(LibraryManager.library, { } } } -});
\ No newline at end of file +}); diff --git a/src/library_path.js b/src/library_path.js index 09808acd..f00a7586 100644 --- a/src/library_path.js +++ b/src/library_path.js @@ -59,26 +59,22 @@ mergeInto(LibraryManager.library, { } return root + dir; }, - basename: function(path, ext) { + basename: function(path) { // EMSCRIPTEN return '/'' for '/', not an empty string if (path === '/') return '/'; - var f = PATH.splitPath(path)[2]; - if (ext && f.substr(-1 * ext.length) === ext) { - f = f.substr(0, f.length - ext.length); - } - return f; + var lastSlash = path.lastIndexOf('/'); + if (lastSlash === -1) return path; + return path.substr(lastSlash+1); }, extname: function(path) { return PATH.splitPath(path)[3]; }, join: function() { var paths = Array.prototype.slice.call(arguments, 0); - return PATH.normalize(paths.filter(function(p, index) { - if (typeof p !== 'string') { - throw new TypeError('Arguments to path.join must be strings'); - } - return p; - }).join('/')); + return PATH.normalize(paths.join('/')); + }, + join2: function(l, r) { + return PATH.normalize(l + '/' + r); }, resolve: function() { var resolvedPath = '', @@ -134,4 +130,4 @@ mergeInto(LibraryManager.library, { return outputParts.join('/'); } } -});
\ No newline at end of file +}); diff --git a/src/library_sdl.js b/src/library_sdl.js index a0689343..e652dfc8 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -254,9 +254,13 @@ var LibrarySDL = { } var webGLContextAttributes = { - antialias: ((SDL.glAttributes[13 /*SDL_GL_MULTISAMPLEBUFFERS*/] != 0) && (SDL.glAttributes[14 /*SDL_GL_MULTISAMPLESAMPLES*/] > 1)) + antialias: ((SDL.glAttributes[13 /*SDL_GL_MULTISAMPLEBUFFERS*/] != 0) && (SDL.glAttributes[14 /*SDL_GL_MULTISAMPLESAMPLES*/] > 1)), + depth: (SDL.glAttributes[6 /*SDL_GL_DEPTH_SIZE*/] > 0), + stencil: (SDL.glAttributes[7 /*SDL_GL_STENCIL_SIZE*/] > 0) }; + var ctx = Browser.createContext(canvas, useWebGL, usePageCanvas, webGLContextAttributes); + SDL.surfaces[surf] = { width: width, height: height, @@ -786,6 +790,14 @@ var LibrarySDL = { ['mousedown', 'mouseup', 'mousemove', 'DOMMouseScroll', 'mousewheel', 'mouseout'].forEach(function(event) { Module['canvas'].addEventListener(event, SDL.receiveEvent, true); }); + + // (0,0) means 'use fullscreen' in native; in Emscripten, use the current canvas size. + if (width == 0 && height == 0) { + var canvas = Module['canvas']; + width = canvas.width; + height = canvas.height; + } + Browser.setCanvasSize(width, height, true); // Free the old surface first. if (SDL.screen) { diff --git a/src/preamble.js b/src/preamble.js index ee273f6a..9e72e7b8 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -1107,19 +1107,21 @@ var Math_min = Math.min; // it happens right before run - run will be postponed until // the dependencies are met. var runDependencies = 0; -var runDependencyTracking = {}; var runDependencyWatcher = null; var dependenciesFulfilled = null; // overridden to take different actions when all run dependencies are fulfilled +#if ASSERTIONS +var runDependencyTracking = {}; +#endif function addRunDependency(id) { runDependencies++; if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies); } +#if ASSERTIONS if (id) { assert(!runDependencyTracking[id]); runDependencyTracking[id] = 1; -#if ASSERTIONS if (runDependencyWatcher === null && typeof setInterval !== 'undefined') { // Check for missing dependencies every few seconds runDependencyWatcher = setInterval(function() { @@ -1136,10 +1138,10 @@ function addRunDependency(id) { } }, 10000); } -#endif } else { Module.printErr('warning: run dependency added without ID'); } +#endif } Module['addRunDependency'] = addRunDependency; function removeRunDependency(id) { @@ -1147,12 +1149,14 @@ function removeRunDependency(id) { if (Module['monitorRunDependencies']) { Module['monitorRunDependencies'](runDependencies); } +#if ASSERTIONS if (id) { assert(runDependencyTracking[id]); delete runDependencyTracking[id]; } else { Module.printErr('warning: run dependency removed without ID'); } +#endif if (runDependencies == 0) { if (runDependencyWatcher !== null) { clearInterval(runDependencyWatcher); diff --git a/src/runtime.js b/src/runtime.js index fa127fe7..5d5cb43b 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -224,9 +224,16 @@ var Runtime = { // bN, large number field, like a [N x i8] size = field.substr(1)|0; alignSize = 1; - } else { - assert(field[0] === '<', field); // assumed to be a vector type, if none of the above + } else if (field[0] === '<') { + // vector type size = alignSize = Types.types[field].flatSize; // fully aligned + } else if (field[0] === 'i') { + // illegal integer field, that could not be legalized because it is an internal structure field + // it is ok to have such fields, if we just use them as markers of field size and nothing more complex + size = alignSize = parseInt(field.substr(1))/8; + assert(size % 1 === 0, 'cannot handle non-byte-size field ' + field); + } else { + assert(false, 'invalid type for calculateStructAlignment'); } if (type.packed) alignSize = 1; type.alignSize = Math.max(type.alignSize, alignSize); diff --git a/src/shell.html b/src/shell.html index ff5f6e35..53a4fffb 100644 --- a/src/shell.html +++ b/src/shell.html @@ -63,8 +63,11 @@ }, canvas: document.getElementById('canvas'), setStatus: function(text) { - if (Module.setStatus.interval) clearInterval(Module.setStatus.interval); + if (!Module.setStatus.last) Module.setStatus.last = { time: Date.now(), text: '' }; + if (text === Module.setStatus.text) return; var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); + var now = Date.now(); + if (m && now - Date.now() < 30) return; // if this is a progress update, skip it if too soon var statusElement = document.getElementById('status'); var progressElement = document.getElementById('progress'); if (m) { @@ -87,6 +90,6 @@ }; Module.setStatus('Downloading...'); </script> - <script async type='text/javascript'>{{{ SCRIPT_CODE }}}</script> + {{{ SCRIPT }}} </body> </html> diff --git a/system/include/compat/ctype.h b/system/include/compat/ctype.h index 891006d9..3f504946 100644 --- a/system/include/compat/ctype.h +++ b/system/include/compat/ctype.h @@ -14,4 +14,21 @@ #include_next <ctype.h> +/* We undef these until libcxx is fixed. Without this, + some things can fail to compile correctly, like + Boost. Issue #1716. */ + +#undef isalpha +#undef isblank +#undef iscntrl +#undef isdigit +#undef isgraph +#undef islower +#undef isprint +#undef ispunct +#undef isspace +#undef isupper +#undef isxdigit +#undef isctype + #endif /* _COMPAT_CTYPE_H_ */ diff --git a/system/include/compat/wchar.h b/system/include/compat/wchar.h new file mode 100644 index 00000000..42f0bcee --- /dev/null +++ b/system/include/compat/wchar.h @@ -0,0 +1,23 @@ +#ifndef _COMPAT_WCHAR_H_ +#define _COMPAT_WCHAR_H_ + +#include_next <wchar.h> + +/* We undef these until libcxx is fixed. Without this, + some things can fail to compile correctly, like + Boost. Issue #1716. */ + +#undef iswalpha +#undef iswblank +#undef iswcntrl +#undef iswdigit +#undef iswgraph +#undef iswlower +#undef iswprint +#undef iswpunct +#undef iswspace +#undef iswupper +#undef iswxdigit +#undef iswctype + +#endif /* _COMPAT_WCHAR_H_ */ diff --git a/system/include/compat/wctype.h b/system/include/compat/wctype.h new file mode 100644 index 00000000..0eeaab8b --- /dev/null +++ b/system/include/compat/wctype.h @@ -0,0 +1,23 @@ +#ifndef _COMPAT_WCTYPE_H_ +#define _COMPAT_WCTYPE_H_ + +#include_next <wctype.h> + +/* We undef these until libcxx is fixed. Without this, + some things can fail to compile correctly, like + Boost. Issue #1716. */ + +#undef iswalpha +#undef iswblank +#undef iswcntrl +#undef iswdigit +#undef iswgraph +#undef iswlower +#undef iswprint +#undef iswpunct +#undef iswspace +#undef iswupper +#undef iswxdigit +#undef iswctype + +#endif /* _COMPAT_WCTYPE_H_ */ diff --git a/tests/aniso.c b/tests/aniso.c index e8d7bd3f..f1674cad 100644 --- a/tests/aniso.c +++ b/tests/aniso.c @@ -66,6 +66,9 @@ int main(int argc, char *argv[]) const char *exts = (const char *)glGetString(GL_EXTENSIONS); assert(hasext(exts, "GL_EXT_texture_filter_anisotropic")); + const char *vendor = (const char *)glGetString(GL_VENDOR); + printf("vendor: %s\n", vendor); + GLint aniso; glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &aniso); printf("Max anisotropy: %d (using that)\n", aniso); diff --git a/tests/cases/2xi40.ll b/tests/cases/2xi40.ll new file mode 100644 index 00000000..592f1ba4 --- /dev/null +++ b/tests/cases/2xi40.ll @@ -0,0 +1,42 @@ +; ModuleID = '/tmp/tmpe4Pk1F/a.out.bc' +target datalayout = "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32" +target triple = "le32-unknown-nacl" + +%struct.pair = type { [5 x i8], [5 x i8] } + +@.str = private unnamed_addr constant [6 x i8] c"|%d|\0A\00", align 1 +@.str1 = private unnamed_addr constant [7 x i8] c"%s,%s\0A\00", align 1 + +define i32 @main() { + %1 = alloca i32, align 4 + %pp = alloca [2 x i40], align 8 + %p = bitcast [2 x i40]* %pp to %struct.pair* + store i32 0, i32* %1 + %2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i32 10) + %3 = bitcast %struct.pair* %p to i8* + call void @llvm.memset.p0i8.i32(i8* %3, i8 120, i32 10, i32 1, i1 false) + %4 = getelementptr inbounds [2 x i40]* %pp, i32 0, i32 0 + %b4 = bitcast i40* %4 to [5 x i8]* + %5 = getelementptr inbounds [5 x i8]* %b4, i32 0, i32 2 + store i8 97, i8* %5, align 1 + %6 = getelementptr inbounds %struct.pair* %p, i32 0, i32 0 + %7 = getelementptr inbounds [5 x i8]* %6, i32 0, i32 4 + store i8 0, i8* %7, align 1 + %8 = getelementptr inbounds %struct.pair* %p, i32 0, i32 1 + %9 = getelementptr inbounds [5 x i8]* %8, i32 0, i32 3 + store i8 98, i8* %9, align 1 + %10 = getelementptr inbounds [2 x i40]* %pp, i32 0, i32 1 + %b10 = bitcast i40* %10 to [5 x i8]* + %11 = getelementptr inbounds [5 x i8]* %b10, i32 0, i32 4 + store i8 0, i8* %11, align 1 + %12 = getelementptr inbounds %struct.pair* %p, i32 0, i32 0 + %13 = getelementptr inbounds [5 x i8]* %12, i32 0, i32 0 + %14 = getelementptr inbounds %struct.pair* %p, i32 0, i32 1 + %15 = getelementptr inbounds [5 x i8]* %14, i32 0, i32 0 + %16 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([7 x i8]* @.str1, i32 0, i32 0), i8* %13, i8* %15) + ret i32 0 +} + +declare i32 @printf(i8*, ...) + +declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) nounwind diff --git a/tests/cases/2xi40.txt b/tests/cases/2xi40.txt new file mode 100644 index 00000000..59a1104e --- /dev/null +++ b/tests/cases/2xi40.txt @@ -0,0 +1 @@ +xxax,xxxb diff --git a/tests/cases/emptyasm_le32.ll b/tests/cases/emptyasm_le32.ll new file mode 100644 index 00000000..e123d3d5 --- /dev/null +++ b/tests/cases/emptyasm_le32.ll @@ -0,0 +1,16 @@ +; ModuleID = 'tests/hello_world.bc' + +@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] + +; [#uses=0] +define i32 @main() { +entry: + %retval = alloca i32, align 4 ; [#uses=1 type=i32*] + store i32 0, i32* %retval + call void asm sideeffect "", "~{memory}"() nounwind, !srcloc !0 + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32] + ret i32 1 +} + +; [#uses=1] +declare i32 @printf(i8*, ...) diff --git a/tests/embind/shell.html b/tests/embind/shell.html index c3655e03..f0ee10f8 100644 --- a/tests/embind/shell.html +++ b/tests/embind/shell.html @@ -85,6 +85,6 @@ }; Module.setStatus('Downloading...'); </script> - <script type='text/javascript'>{{{ SCRIPT_CODE }}}</script> + {{{ SCRIPT }}} </body> </html> diff --git a/tests/hello_world_gles_shell.html b/tests/hello_world_gles_shell.html index 2459d755..22ecee7b 100644 --- a/tests/hello_world_gles_shell.html +++ b/tests/hello_world_gles_shell.html @@ -49,7 +49,7 @@ Module.postRun = doTest; </script> - <script>{{{ SCRIPT_CODE }}}</script> + {{{ SCRIPT }}} </body> </html> diff --git a/tests/sdl_canvas_size.c b/tests/sdl_canvas_size.c new file mode 100644 index 00000000..923a9014 --- /dev/null +++ b/tests/sdl_canvas_size.c @@ -0,0 +1,191 @@ +/******************************************************************* + * * + * Using SDL With OpenGL * + * * + * Tutorial by Kyle Foley (sdw) * + * * + * http://gpwiki.org/index.php/SDL:Tutorials:Using_SDL_with_OpenGL * + * * + *******************************************************************/ + +/* +THIS WORK, INCLUDING THE SOURCE CODE, DOCUMENTATION +AND RELATED MEDIA AND DATA, IS PLACED INTO THE PUBLIC DOMAIN. + +THE ORIGINAL AUTHOR IS KYLE FOLEY. + +THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY +OF ANY KIND, NOT EVEN THE IMPLIED WARRANTY OF +MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, +ASSUMES _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE +RESULTING FROM THE USE, MODIFICATION, OR +REDISTRIBUTION OF THIS SOFTWARE. +*/ + +#include "SDL/SDL.h" +#include "SDL/SDL_image.h" +#include "SDL/SDL_opengl.h" + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#ifdef EMSCRIPTEN +#include <emscripten.h> +#endif + +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; + } + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new* + +#ifdef EMSCRIPTEN + // Test 1: Check that initializing video mode with size (0,0) will use the size from the <canvas> element. + screen = SDL_SetVideoMode( 0, 0, 16, SDL_OPENGL ); // *changed* + + // Test 2: Check that getting current canvas size works. + int w, h, fs; + emscripten_get_canvas_size(&w, &h, &fs); + printf("w:%d,h:%d\n", w,h); + assert(w == 700); + assert(h == 200); + + // Test 3: Check that resizing the canvas works as well. + emscripten_set_canvas_size(640, 480); + // Set the OpenGL state after creating the context with SDL_SetVideoMode +#else + screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL ); // *changed* +#endif + + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + glClearColor( 0, 0, 0, 0 ); + + glEnable( GL_TEXTURE_2D ); // Needed when we're using the fixed-function pipeline. + + glViewport( 0, 0, 640, 480 ); + + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); // just for testing + glLoadIdentity(); + + glOrtho( 0, 640, 480, 0, -1, 1 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Load the OpenGL texture + + GLuint texture; // Texture object handle + SDL_Surface *surface; // Gives us the information to make the texture + + if ( (surface = IMG_Load("screenshot.png")) ) { + + // Check that the image's width is a power of 2 + if ( (surface->w & (surface->w - 1)) != 0 ) { + printf("warning: image.bmp's width is not a power of 2\n"); + } + + // Also check if the height is a power of 2 + if ( (surface->h & (surface->h - 1)) != 0 ) { + printf("warning: image.bmp's height is not a power of 2\n"); + } + + // Have OpenGL generate a texture object handle for us + glGenTextures( 1, &texture ); + + // Bind the texture object + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set the texture's stretching properties + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + //SDL_LockSurface(surface); + + // Add some greyness + memset(surface->pixels, 0x66, surface->w*surface->h); + + // Edit the texture object's image data using the information SDL_Surface gives us + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, surface->w, surface->h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels ); + + //SDL_UnlockSurface(surface); + } + else { + printf("SDL could not load image.bmp: %s\n", SDL_GetError()); + SDL_Quit(); + return 1; + } + + // Free the SDL_Surface only if it was successfully created + if ( surface ) { + SDL_FreeSurface( surface ); + } + + // Clear the screen before drawing + glClear( GL_COLOR_BUFFER_BIT ); + + // Bind the texture to which subsequent calls refer to + glBindTexture( GL_TEXTURE_2D, texture ); + + glBegin( GL_QUADS ); + glTexCoord2i( 0, 0 ); glVertex3f( 10, 10, 0 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 10, 0 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 128, 0 ); + glTexCoord2i( 0, 1 ); glVertex3f( 10, 128, 0 ); + + glTexCoord2f( 0, 0.5 ); glVertex3f( 410, 10, 0 ); + glTexCoord2f( 1, 0.5 ); glVertex3f( 600, 10, 0 ); + glTexCoord2f( 1, 1 ); glVertex3f( 630, 200, 0 ); + glTexCoord2f( 0.5, 1 ); glVertex3f( 310, 250, 0 ); + glEnd(); + + glBegin( GL_TRIANGLE_STRIP ); + glTexCoord2i( 0, 0 ); glVertex3f( 100, 300, 0 ); + glTexCoord2i( 1, 0 ); glVertex3f( 300, 300, 0 ); + glTexCoord2i( 1, 1 ); glVertex3f( 300, 400, 0 ); + glTexCoord2i( 0, 1 ); glVertex3f( 500, 410, 0 ); + glEnd(); + + glDisable(GL_TEXTURE_2D); + + glColor3ub(90, 255, 255); + glBegin( GL_QUADS ); + glVertex3f( 10, 410, 0 ); + glVertex3f( 300, 410, 0 ); + glVertex3f( 300, 480, 0 ); + glVertex3f( 10, 470, 0 ); + glEnd(); + + glBegin( GL_QUADS ); + glColor3f(1.0, 0, 1.0); glVertex3f( 410, 410, 0 ); + glColor3f(0, 1.0, 0); glVertex3f( 600, 410, 0 ); + glColor3f(0, 0, 1.0); glVertex3f( 600, 480, 0 ); + glColor3f(1.0, 1.0, 1.0); glVertex3f( 410, 470, 0 ); + glEnd(); + + SDL_GL_SwapBuffers(); + +#if !EMSCRIPTEN + // Wait for 3 seconds to give us a chance to see the image + SDL_Delay(3000); +#endif + + // Now we can delete the OpenGL texture and close down SDL + glDeleteTextures( 1, &texture ); + + SDL_Quit(); + + return 0; +} diff --git a/tests/sdl_canvas_size.html b/tests/sdl_canvas_size.html new file mode 100644 index 00000000..50495049 --- /dev/null +++ b/tests/sdl_canvas_size.html @@ -0,0 +1,93 @@ +<!doctype html> +<html lang="en-us"> + <head> + <meta charset="utf-8"> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <title>Emscripten-Generated Code</title> + <style> + .emscripten { padding-right: 0; margin-left: auto; margin-right: auto; display: block; } + textarea.emscripten { font-family: monospace; width: 80%; } + div.emscripten { text-align: center; } + div.emscripten_border { border: 1px solid black; } + /* the canvas *must not* have any border or padding, or mouse coords will be wrong */ + canvas.emscripten { border: 0px none; } + </style> + </head> + <body> + <hr/> + <div class="emscripten" id="status">Downloading...</div> + <div class="emscripten"> + <progress value="0" max="100" id="progress" hidden=1></progress> + </div> + <div class="emscripten_border"> + <!-- Pass custom width/height to test that SDL_SetVideoMode(0,0, ...) will use these. --> + <canvas class="emscripten" id="canvas" width="700" height="200" oncontextmenu="event.preventDefault()"></canvas> + </div> + <hr/> + <div class="emscripten"> + <input type="checkbox" id="resize">Resize canvas + <input type="checkbox" id="pointerLock" checked>Lock/hide mouse pointer + + <input type="button" value="Fullscreen" onclick="Module.requestFullScreen(document.getElementById('pointerLock').checked, + document.getElementById('resize').checked)"> + </div> + + <hr/> + <textarea class="emscripten" id="output" rows="8"></textarea> + <hr> + <script type='text/javascript'> + // connect to canvas + var Module = { + preRun: [], + postRun: [], + print: (function() { + var element = document.getElementById('output'); + element.value = ''; // clear browser cache + return function(text) { + text = Array.prototype.slice.call(arguments).join(' '); + // These replacements are necessary if you render to raw HTML + //text = text.replace(/&/g, "&"); + //text = text.replace(/</g, "<"); + //text = text.replace(/>/g, ">"); + //text = text.replace('\n', '<br>', 'g'); + element.value += text + "\n"; + element.scrollTop = 99999; // focus on bottom + }; + })(), + printErr: function(text) { + text = Array.prototype.slice.call(arguments).join(' '); + if (0) { // XXX disabled for safety typeof dump == 'function') { + dump(text + '\n'); // fast, straight to the real console + } else { + console.log(text); + } + }, + canvas: document.getElementById('canvas'), + setStatus: function(text) { + if (Module.setStatus.interval) clearInterval(Module.setStatus.interval); + var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); + var statusElement = document.getElementById('status'); + var progressElement = document.getElementById('progress'); + if (m) { + text = m[1]; + progressElement.value = parseInt(m[2])*100; + progressElement.max = parseInt(m[4])*100; + progressElement.hidden = false; + } else { + progressElement.value = null; + progressElement.max = null; + progressElement.hidden = true; + } + statusElement.innerHTML = text; + }, + totalDependencies: 0, + monitorRunDependencies: function(left) { + this.totalDependencies = Math.max(this.totalDependencies, left); + Module.setStatus(left ? 'Preparing... (' + (this.totalDependencies-left) + '/' + this.totalDependencies + ')' : 'All downloads complete.'); + } + }; + Module.setStatus('Downloading...'); + </script> + {{{ SCRIPT }}} + </body> +</html> diff --git a/tests/test_browser.py b/tests/test_browser.py index ecd331fd..2ff9106b 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -869,6 +869,48 @@ keydown(100);keyup(100); // trigger the end def test_glut_wheelevents(self): self.btest('glut_wheelevents.c', '1') + def test_webgl_context_attributes(self): + # Javascript code to check the attributes support we want to test in the WebGL implementation + # (request the attribute, create a context and check its value afterwards in the context attributes). + # Tests will succeed when an attribute is not supported. + open(os.path.join(self.get_dir(), 'check_webgl_attributes_support.js'), 'w').write(''' + mergeInto(LibraryManager.library, { + webglAntialiasSupported: function() { + canvas = document.createElement('canvas'); + context = canvas.getContext('experimental-webgl', {antialias: true}); + attributes = context.getContextAttributes(); + return attributes.antialias; + }, + webglDepthSupported: function() { + canvas = document.createElement('canvas'); + context = canvas.getContext('experimental-webgl', {depth: true}); + attributes = context.getContextAttributes(); + return attributes.depth; + }, + webglStencilSupported: function() { + canvas = document.createElement('canvas'); + context = canvas.getContext('experimental-webgl', {stencil: true}); + attributes = context.getContextAttributes(); + return attributes.stencil; + } + }); + ''') + + # Copy common code file to temporary directory + filepath = path_from_root('tests/test_webgl_context_attributes_common.c') + temp_filepath = os.path.join(self.get_dir(), os.path.basename(filepath)) + shutil.copyfile(filepath, temp_filepath) + + # perform tests with attributes activated + self.btest('test_webgl_context_attributes_glut.c', '1', args=['--js-library', 'check_webgl_attributes_support.js', '-DAA_ACTIVATED', '-DDEPTH_ACTIVATED', '-DSTENCIL_ACTIVATED']) + self.btest('test_webgl_context_attributes_sdl.c', '1', args=['--js-library', 'check_webgl_attributes_support.js', '-DAA_ACTIVATED', '-DDEPTH_ACTIVATED', '-DSTENCIL_ACTIVATED']) + self.btest('test_webgl_context_attributes_glfw.c', '1', args=['--js-library', 'check_webgl_attributes_support.js', '-DAA_ACTIVATED', '-DDEPTH_ACTIVATED', '-DSTENCIL_ACTIVATED']) + + # perform tests with attributes desactivated + self.btest('test_webgl_context_attributes_glut.c', '1', args=['--js-library', 'check_webgl_attributes_support.js']) + self.btest('test_webgl_context_attributes_sdl.c', '1', args=['--js-library', 'check_webgl_attributes_support.js']) + self.btest('test_webgl_context_attributes_glfw.c', '1', args=['--js-library', 'check_webgl_attributes_support.js']) + def test_emscripten_get_now(self): self.btest('emscripten_get_now.cpp', '1') @@ -942,6 +984,11 @@ keydown(100);keyup(100); // trigger the end Popen([PYTHON, EMCC, '-O2', '--closure', '1', '--minify', '0', os.path.join(self.get_dir(), 'sdl_audio_beep.cpp'), '-s', 'DISABLE_EXCEPTION_CATCHING=0', '-o', 'page.html']).communicate() self.run_browser('page.html', '', '/report_result?1') + def test_sdl_canvas_size(self): + self.btest('sdl_canvas_size.c', reference='screenshot-gray-purple.png', reference_slack=1, + args=['-O2', '--minify', '0', '--shell-file', path_from_root('tests', 'sdl_canvas_size.html'), '--preload-file', path_from_root('tests', 'screenshot.png') + '@/', '-s', 'LEGACY_GL_EMULATION=1'], + message='You should see an image with gray at the top.') + def test_sdl_gl_read(self): # SDL, OpenGL, readPixels open(os.path.join(self.get_dir(), 'sdl_gl_read.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_gl_read.c')).read())) diff --git a/tests/test_core.py b/tests/test_core.py index 69fb31f3..b766ac74 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -2949,6 +2949,23 @@ Exiting setjmp function, level: 0, prev_jmp: -1 ''' self.do_run(src, '3.14159') + def test_iswdigit(self): + if self.emcc_args is None: return self.skip('no libcxx inclusion without emcc') + + src = ''' + #include <stdio.h> + #include <cctype> + #include <cwctype> + + int main() { + using namespace std; + printf("%d ", isdigit('0')); + printf("%d ", iswdigit(L'0')); + return 0; + } + ''' + self.do_run(src, '1 1') + def test_polymorph(self): if self.emcc_args is None: return self.skip('requires emcc') src = ''' @@ -9044,6 +9061,9 @@ def process(filename): if '_noasm' in shortname and Settings.ASM_JS: print self.skip('case "%s" not relevant for asm.js' % shortname) continue + if '_le32' in shortname and not self.is_le32(): + print self.skip('case "%s" not relevant for non-le32 target' % shortname) + continue self.emcc_args = emcc_args if os.path.exists(shortname + '.emcc'): if not self.emcc_args: continue diff --git a/tests/test_webgl_context_attributes_common.c b/tests/test_webgl_context_attributes_common.c new file mode 100644 index 00000000..7131203b --- /dev/null +++ b/tests/test_webgl_context_attributes_common.c @@ -0,0 +1,262 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> + +#include <emscripten.h> + +#define BUFFER_OFFSET(i) ((char *)NULL + (i)) + +static const int WINDOWS_SIZE = 500; + +static GLfloat vertices[] = { 0.0f, 250.f, 0.0f, + -250.f, -250.f, 0.0f, + 250.f, -250.f, 0.0f }; + +static GLfloat vertices2[] = { 0.0f, 250.f, -1.0f, + -250.f, -250.f, -1.0f, + 250.f, -250.f, -1.0f }; + +static GLuint shaderProgram = 0; +static GLuint verticesVBO = 0; +static GLuint verticesVBO2 = 0; + +static unsigned char backgroundColor[4] = {255, 255, 255, 255}; +static unsigned char triangleColor[4] = {255, 0, 0, 255}; +static unsigned char triangleColor2[4] = {0, 255, 0, 255}; + +static char vertexShaderSrc[] = + "precision highp float;" + "precision highp int;" + + "uniform mat4 u_mvpMatrix;" + "uniform vec4 u_color;" + + "attribute vec3 a_position;" + + "varying vec4 v_color;" + + "void main() {" + " gl_Position = u_mvpMatrix * vec4(a_position, 1.0);" + " v_color = u_color;" + "}" + ; + +static char fragmentShaderSrc[] = + "precision highp float;" + "precision highp int;" + + "varying vec4 v_color;" + + "void main() {" + " gl_FragColor = v_color;" + "}" + ; + +static GLuint createShader(const char *source, int type) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, (const GLchar**)(&source), NULL); + glCompileShader(shader); + return shader; +} + +static GLuint createShaderProgram(const char *vertexShaderSrc, const char *fragmentShaderSrc) { + GLuint program = glCreateProgram(); + glAttachShader(program, createShader(vertexShaderSrc, GL_VERTEX_SHADER)); + glAttachShader(program, createShader(fragmentShaderSrc, GL_FRAGMENT_SHADER)); + glLinkProgram(program); + return program; +} + +void ortho(float left, float right, float bottom, float top, float nearVal, float farVal, GLfloat *projMatrix) { + float tx = -(right+left)/(right-left); + float ty = -(top+bottom)/(top-bottom); + float tz = -(farVal+nearVal)/(farVal-nearVal); + memset(projMatrix, 0, 16 * sizeof(GLfloat)); + projMatrix[0] = 2.0f / (right-left); + projMatrix[3] = tx; + projMatrix[1*4+1] = 2.0f / (top-bottom); + projMatrix[1*4+3] = ty; + projMatrix[2*4+2] = -2.0f / (farVal-nearVal); + projMatrix[2*4+3] = tz; + projMatrix[3*4+3] = 1.0f; +} + +static void initGlObjects() { + glGenBuffers(1, &verticesVBO); + glBindBuffer(GL_ARRAY_BUFFER, verticesVBO); + glBufferData(GL_ARRAY_BUFFER, 9*sizeof(float), vertices, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glGenBuffers(1, &verticesVBO2); + glBindBuffer(GL_ARRAY_BUFFER, verticesVBO2); + glBufferData(GL_ARRAY_BUFFER, 9*sizeof(float), vertices2, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + shaderProgram = createShaderProgram(vertexShaderSrc, fragmentShaderSrc); +} + +static void drawTriangle(GLuint verticesVBO, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + glUseProgram(shaderProgram); + GLuint posLoc = glGetAttribLocation(shaderProgram, "a_position"); + GLuint mvpLoc = glGetUniformLocation(shaderProgram, "u_mvpMatrix"); + GLuint colorLoc = glGetUniformLocation(shaderProgram, "u_color"); + + GLfloat mvpMat[16]; + ortho(-WINDOWS_SIZE/2, WINDOWS_SIZE/2, -WINDOWS_SIZE/2, WINDOWS_SIZE/2, -100, 100, mvpMat); + + glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, mvpMat); + glUniform4f(colorLoc, r/255.f, g/255.f, b/255.f, a/255.f); + + glBindBuffer(GL_ARRAY_BUFFER, verticesVBO); + glEnableVertexAttribArray(posLoc); + glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), BUFFER_OFFSET(0)); + + glDrawArrays(GL_TRIANGLES, 0, 3); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glUseProgram(0); +} + +// Draw a red triangle on a white background. If antialiasing is disabled, resulting pixels +// will only have white and red colors. If antialiasing is enabled, there will be pixels +// whose color is different from red and white. +static int testAntiAliasing(bool activated) { + glViewport(0, 0, WINDOWS_SIZE, WINDOWS_SIZE); + glClearColor(backgroundColor[0]/255.f, backgroundColor[1]/255.f, backgroundColor[2]/255.f, backgroundColor[3]/255.f); + glClear(GL_COLOR_BUFFER_BIT); + + drawTriangle(verticesVBO, triangleColor[0], triangleColor[1], triangleColor[2], triangleColor[3]); + + bool antialiased = false; + + unsigned char buffer[(WINDOWS_SIZE*WINDOWS_SIZE)*4]; + glReadPixels(0, 0, WINDOWS_SIZE, WINDOWS_SIZE, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); + glFinish(); + for (unsigned int i = 0 ; i < WINDOWS_SIZE ; ++i) { + for (unsigned int j = 0 ; j < WINDOWS_SIZE ; ++j) { + unsigned char r = buffer[4*(i*WINDOWS_SIZE+j)]; + unsigned char g = buffer[4*(i*WINDOWS_SIZE+j)+1]; + unsigned char b = buffer[4*(i*WINDOWS_SIZE+j)+2]; + unsigned char a = buffer[4*(i*WINDOWS_SIZE+j)+3]; + if ((r == backgroundColor[0] && g == backgroundColor[1] && b == backgroundColor[2] && a == backgroundColor[3]) || + (r == triangleColor[0] && g == triangleColor[1] && b == triangleColor[2] && a == triangleColor[3])) { + continue; + } else { + antialiased = true; + break; + } + } + } + + return (activated && antialiased) || (!activated && !antialiased); +} + +// Draw a red triangle with depth equals to 0 then a green triangle whose depth equals -1. +// If there is an attached depth buffer, the resulting image will be a red triangle. If not, +// the resulting image will be a green triangle. +static int testDepth(bool activated) { + glViewport(0, 0, WINDOWS_SIZE, WINDOWS_SIZE); + glClearColor(backgroundColor[0]/255.f, backgroundColor[1]/255.f, backgroundColor[2]/255.f, backgroundColor[3]/255.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + + drawTriangle(verticesVBO, triangleColor[0], triangleColor[1], triangleColor[2], triangleColor[3]); + drawTriangle(verticesVBO2, triangleColor2[0], triangleColor2[1], triangleColor2[2], triangleColor2[3]); + + glDisable(GL_DEPTH_TEST); + + // read the pixel at the center of the resulting image. + unsigned char buffer[4]; + glReadPixels(WINDOWS_SIZE/2, WINDOWS_SIZE/2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); + + bool frontTriangleColor = (buffer[0] == triangleColor[0] && buffer[1] == triangleColor[1] && + buffer[2] == triangleColor[2] && buffer[3] == triangleColor[3]); + + bool backTriangleColor = (buffer[0] == triangleColor2[0] && buffer[1] == triangleColor2[1] && + buffer[2] == triangleColor2[2] && buffer[3] == triangleColor2[3]); + + return (activated && frontTriangleColor) || (!activated && backTriangleColor); +} + +// The stencil function is set to GL_LEQUAL so fragments will be written to the +// back buffer only if the ref value is less or equal than the one in the stencil buffer. +// The content of the stencil buffer is initialized to 0xFF. +// First draw a red triangle whose stencil ref value is 0x1. +// Then draw a green triangle whose stencil ref value is 0xFF. +// If there is an attached stencil buffer, the resulting image will be a red triangle. If not, +// the resulting image will be a green triangle. +static int testStencil(bool activated) { + glViewport(0, 0, WINDOWS_SIZE, WINDOWS_SIZE); + glClearColor(backgroundColor[0]/255.f, backgroundColor[1]/255.f, backgroundColor[2]/255.f, backgroundColor[3]/255.f); + glClearStencil(0xFF); + glStencilOp(GL_KEEP,GL_KEEP,GL_REPLACE); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + glEnable(GL_STENCIL_TEST); + + glStencilFunc(GL_LEQUAL, 0x1, 0xFF); + drawTriangle(verticesVBO, triangleColor[0], triangleColor[1], triangleColor[2], triangleColor[3]); + + glStencilFunc(GL_LEQUAL, 0xFF, 0xFF); + drawTriangle(verticesVBO, triangleColor2[0], triangleColor2[1], triangleColor2[2], triangleColor2[3]); + + glDisable(GL_STENCIL_TEST); + + unsigned char buffer[4]; + glReadPixels(WINDOWS_SIZE/2, WINDOWS_SIZE/2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); + + bool firstTriangleColor = (buffer[0] == triangleColor[0] && buffer[1] == triangleColor[1] && + buffer[2] == triangleColor[2] && buffer[3] == triangleColor[3]); + + bool secondTriangleColor = (buffer[0] == triangleColor2[0] && buffer[1] == triangleColor2[1] && + buffer[2] == triangleColor2[2] && buffer[3] == triangleColor2[3]); + + return (activated && firstTriangleColor) || (!activated && secondTriangleColor); +} + +static bool antiAliasingActivated = false; +static bool depthActivated = false; +static bool stencilActivated = false; + +static int result = 0; +static int resultAA = 0; +static int resultDepth = 0; +static int resultStencil = 0; + +static void draw() { + + if (!resultAA) resultAA = testAntiAliasing(antiAliasingActivated); + + if (!resultDepth) resultDepth = testDepth(depthActivated); + + if (!resultStencil) resultStencil = testStencil(stencilActivated); + + result = resultAA && resultDepth && resultStencil; + +} + +extern int webglAntialiasSupported(); +extern int webglDepthSupported(); +extern int webglStencilSupported(); + +// Check attributes support in the WebGL implementation (see test_webgl_context_attributes function in test_browser.py) +// Tests will succeed if they are not. +static void checkContextAttributesSupport() { + if (!webglAntialiasSupported()) { + resultAA = 1; + EM_ASM(alert('warning: no antialiasing\n')); + } + if (!webglDepthSupported()) { + resultDepth = 1; + EM_ASM(alert('warning: no depth\n')); + } + if (!webglStencilSupported()) { + resultStencil = 1; + EM_ASM(alert('warning: no stencil\n')); + } +} + + diff --git a/tests/test_webgl_context_attributes_glfw.c b/tests/test_webgl_context_attributes_glfw.c new file mode 100644 index 00000000..694236d0 --- /dev/null +++ b/tests/test_webgl_context_attributes_glfw.c @@ -0,0 +1,47 @@ +#include <GL/glew.h> +#include <GL/glfw.h> +#include <emscripten.h> + +#include "test_webgl_context_attributes_common.c" + +int nbSamples = 0; +int nbDepthBits = 0; +int nbStencilBits = 0; + +int main() { + + checkContextAttributesSupport(); + + glfwInit(); + +#ifdef AA_ACTIVATED + antiAliasingActivated = true; + nbSamples = 4; +#endif + +#ifdef DEPTH_ACTIVATED + depthActivated = true; + nbDepthBits = 16; +#endif + +#ifdef STENCIL_ACTIVATED + stencilActivated = true; + nbStencilBits = 8; +#endif + + glfwOpenWindowHint(GLFW_FSAA_SAMPLES, nbSamples); + glfwOpenWindow(WINDOWS_SIZE, WINDOWS_SIZE, 8, 8, 8, 8, nbDepthBits, nbStencilBits, GLFW_WINDOW); + + glewInit(); + initGlObjects(); + + draw(); + + glfwTerminate(); + + REPORT_RESULT(); + + return 0; + +} +
\ No newline at end of file diff --git a/tests/test_webgl_context_attributes_glut.c b/tests/test_webgl_context_attributes_glut.c new file mode 100644 index 00000000..3255fc9a --- /dev/null +++ b/tests/test_webgl_context_attributes_glut.c @@ -0,0 +1,42 @@ +#include <GL/glew.h> +#include <GL/glut.h> +#include <emscripten.h> + +#include "test_webgl_context_attributes_common.c" + +int main(int argc, char *argv[]) { + + checkContextAttributesSupport(); + + unsigned int glutDisplayMode = GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA; + +#ifdef AA_ACTIVATED + antiAliasingActivated = true; + glutDisplayMode |= GLUT_MULTISAMPLE; +#endif + +#ifdef DEPTH_ACTIVATED + depthActivated = true; + glutDisplayMode |= GLUT_DEPTH; +#endif + +#ifdef STENCIL_ACTIVATED + stencilActivated = true; + glutDisplayMode |= GLUT_STENCIL; +#endif + + glutInit(&argc, argv); + glutInitWindowSize(WINDOWS_SIZE, WINDOWS_SIZE); + glutInitDisplayMode(glutDisplayMode); + glutCreateWindow("WebGL"); + glutDisplayFunc(draw); + + glewInit(); + initGlObjects(); + + draw(); + + REPORT_RESULT(); + + return 0; +} diff --git a/tests/test_webgl_context_attributes_sdl.c b/tests/test_webgl_context_attributes_sdl.c new file mode 100644 index 00000000..23ad4378 --- /dev/null +++ b/tests/test_webgl_context_attributes_sdl.c @@ -0,0 +1,50 @@ +#include <GL/glew.h> +#define NO_SDL_GLEXT +#include <SDL/SDL.h> +#include <SDL/SDL_opengl.h> +#include <emscripten.h> + +#include "test_webgl_context_attributes_common.c" + +int main(int argc, char *argv[]) { + + checkContextAttributesSupport(); + + SDL_Init(SDL_INIT_VIDEO); + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + +#ifdef AA_ACTIVATED + antiAliasingActivated = true; + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); +#else + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); +#endif + +#ifdef DEPTH_ACTIVATED + depthActivated = true; + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); +#else + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); +#endif + +#ifdef STENCIL_ACTIVATED + stencilActivated = true; + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); +#else + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); +#endif + + SDL_Surface *screen = SDL_SetVideoMode(WINDOWS_SIZE, WINDOWS_SIZE, 32, SDL_OPENGL); + + glewInit(); + initGlObjects(); + + draw(); + + REPORT_RESULT(); + + return 0; +} diff --git a/tools/file_packager.py b/tools/file_packager.py index a2349a57..1d0ec447 100644 --- a/tools/file_packager.py +++ b/tools/file_packager.py @@ -134,6 +134,11 @@ if (not force) and len(data_files) == 0: ret = ''' var Module; if (typeof Module === 'undefined') Module = eval('(function() { try { return Module || {} } catch(e) { return {} } })()'); +if (!Module.expectedDataFileDownloads) { + Module.expectedDataFileDownloads = 0; + Module.finishedDataFileDownloads = 0; +} +Module.expectedDataFileDownloads++; (function() { ''' @@ -338,18 +343,9 @@ if has_preloaded: send: function() {}, onload: function() { var byteArray = this.byteArray.subarray(this.start, this.end); - if (this.crunched) { - var ddsHeader = byteArray.subarray(0, 128); - var that = this; - requestDecrunch(this.name, byteArray.subarray(128), function(ddsData) { - byteArray = new Uint8Array(ddsHeader.length + ddsData.length); - byteArray.set(ddsHeader, 0); - byteArray.set(ddsData, 128); - that.finish(byteArray); - }); - } else { +%s this.finish(byteArray); - } +%s }, finish: function(byteArray) { var that = this; @@ -365,7 +361,20 @@ if has_preloaded: this.requests[this.name] = null; }, }; - ''' + ''' % ('' if not crunch else ''' + if (this.crunched) { + var ddsHeader = byteArray.subarray(0, 128); + var that = this; + requestDecrunch(this.name, byteArray.subarray(128), function(ddsData) { + byteArray = new Uint8Array(ddsHeader.length + ddsData.length); + byteArray.set(ddsHeader, 0); + byteArray.set(ddsData, 128); + that.finish(byteArray); + }); + } else { +''', '' if not crunch else ''' + } +''') counter = 0 for file_ in data_files: @@ -427,12 +436,6 @@ if has_preloaded: package_uuid = uuid.uuid4(); remote_package_name = os.path.basename(Compression.compressed_name(data_target) if Compression.on else data_target) code += r''' - if (!Module.expectedDataFileDownloads) { - Module.expectedDataFileDownloads = 0; - Module.finishedDataFileDownloads = 0; - } - Module.expectedDataFileDownloads++; - var PACKAGE_PATH = window['encodeURIComponent'](window.location.pathname.toString().substring(0, window.location.pathname.toString().lastIndexOf('/')) + '/'); var PACKAGE_NAME = '%s'; var REMOTE_PACKAGE_NAME = '%s'; |