diff options
-rwxr-xr-x | emcc | 268 | ||||
-rw-r--r-- | src/jsifier.js | 5 | ||||
-rw-r--r-- | src/library.js | 13 | ||||
-rw-r--r-- | src/library_gc.js | 14 | ||||
-rw-r--r-- | src/library_gl.js | 13 | ||||
-rw-r--r-- | src/library_sdl.js | 18 | ||||
-rw-r--r-- | src/postamble.js | 4 | ||||
-rw-r--r-- | src/preamble.js | 3 | ||||
-rw-r--r-- | src/settings.js | 2 | ||||
-rw-r--r-- | system/include/emscripten.h | 5 | ||||
-rw-r--r-- | tests/gl_matrix_identity.c | 129 | ||||
-rwxr-xr-x | tests/runner.py | 34 | ||||
-rw-r--r-- | tools/file_packager.py | 290 | ||||
-rw-r--r-- | tools/shared.py | 26 |
14 files changed, 544 insertions, 280 deletions
@@ -77,15 +77,7 @@ emcc can be influenced by a few environment variables: import os, sys, shutil, tempfile, subprocess, shlex from subprocess import PIPE, STDOUT from tools import shared - -def execute(cmd, *args, **kw): - try: - return subprocess.Popen(cmd, *args, **kw).communicate() # let compiler frontend print directly, so colors are saved (PIPE kills that) - except: - if not isinstance(cmd, str): - cmd = ' '.join(cmd) - print >> sys.stderr, 'Invoking Process failed: <<< ' + cmd + ' >>>' - raise +from tools.shared import Compression, execute # Mapping of emcc opt levels to llvm opt levels. We use llvm opt level 3 in emcc opt # levels 2 and 3 (emcc 3 is unsafe opts, so unsuitable for the only level to get @@ -220,9 +212,14 @@ Options that are modified or new in %s include: compiled code asynchronously. Otherwise similar to --embed-file, except that this option is only relevant when generating - HTML (it uses asynchronous binary XHRs). + HTML (it uses asynchronous binary XHRs), + or JS that will be used in a web page. If a directory is passed here, its entire contents will be preloaded. + Preloaded files are stored in filename.data, + where filename.html is the main file you + are compiling to. To run your code, you + will need both the .html and the .data. --compression <codec> Compress both the compiled code and embedded/ preloaded files. <codec> should be a triple, @@ -332,16 +329,12 @@ if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += shlex.split(EMMAKEN_CFLAGS) # ---------------- Utilities --------------- SOURCE_SUFFIXES = ('.c', '.cpp', '.cxx', '.cc') -BITCODE_SUFFIXES = ('.bc', '.o') +BITCODE_SUFFIXES = ('.bc', '.o', '.obj') DYNAMICLIB_SUFFIXES = ('.dylib', '.so', '.dll') STATICLIB_SUFFIXES = ('.a',) ASSEMBLY_SUFFIXES = ('.ll',) LIB_PREFIXES = ('', 'lib') -IMAGE_SUFFIXES = ('.jpg', '.png', '.bmp') -AUDIO_SUFFIXES = ('.ogg', '.wav', '.mp3') -AUDIO_MIMETYPES = { 'ogg': 'audio/ogg', 'wav': 'audio/wav', 'mp3': 'audio/mpeg' } - def suffix(name): return name.split('.')[-1] @@ -413,21 +406,6 @@ else: def in_temp(name): return os.path.join(temp_dir, name) -class Compression: - on = False - - @staticmethod - def compressed_name(filename): - return filename + '.compress' - - @staticmethod - def compress(filename): - execute(Compression.encoder, stdin=open(filename, 'rb'), stdout=open(Compression.compressed_name(filename), 'wb')) - - @staticmethod - def worth_it(original, compressed): - return compressed < original - 1500 # save at least one TCP packet or so - try: call = CXX if use_cxx else CC @@ -443,7 +421,8 @@ try: pre_js = None post_js = None minify_whitespace = None - data_files = [] + preload_files = [] + embed_files = [] compression = None ignore_dynamic_linking = False shell_path = shared.path_from_root('src', 'shell.html') @@ -497,12 +476,12 @@ try: newargs[i+1] = '' elif newargs[i].startswith('--embed-file'): check_bad_eq(newargs[i]) - data_files.append({ 'name': newargs[i+1], 'mode': 'embed' }) + embed_files.append(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' elif newargs[i].startswith('--preload-file'): check_bad_eq(newargs[i]) - data_files.append({ 'name': newargs[i+1], 'mode': 'preload' }) + preload_files.append(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' elif newargs[i].startswith('--compression'): @@ -722,9 +701,6 @@ try: if DEBUG: print >> sys.stderr, 'emcc: will generate JavaScript' - if final_suffix == 'html': - shared.Settings.GENERATING_HTML = 1 - extra_files_to_link = [] if not LEAVE_INPUTS_RAW and not AUTODEBUG: @@ -871,216 +847,18 @@ try: if DEBUG: save_intermediate('original') # Embed and preload files - if len(data_files) > 0: + if len(preload_files) + len(embed_files) > 0: if DEBUG: print >> sys.stderr, 'emcc: setting up files' - code = '' - - if final_suffix == 'html': - code += ''' - var BlobBuilder = typeof MozBlobBuilder != "undefined" ? MozBlobBuilder : (typeof WebKitBlobBuilder != "undefined" ? WebKitBlobBuilder : console.log("warning: cannot build blobs")); - var URLObject = typeof window != "undefined" ? (window.URL ? window.URL : window.webkitURL) : console.log("warning: cannot create object URLs"); - var hasBlobConstructor; - try { - new Blob(); - hasBlobConstructor = true; - } catch(e) { - hasBlobConstructor = false; - console.log("warning: no blob constructor, cannot create blobs with mimetypes"); - } -''' - - code += 'var preloadedImages = {}; // maps url to image data\n' - code += 'var preloadedAudios = {}; // maps url to audio data\n' - - # Expand directories into individual files - def add(mode, dirname, names): - for name in names: - fullname = os.path.join(dirname, name) - if not os.path.isdir(fullname): - data_files.append({ 'name': fullname, 'mode': mode }) - - for file_ in data_files: - if os.path.isdir(file_['name']): - os.path.walk(file_['name'], add, file_['mode']) - data_files = filter(lambda file_: not os.path.isdir(file_['name']), data_files) - - for file_ in data_files: - file_['name'] = file_['name'].replace(os.path.sep, '/') - file_['net_name'] = file_['name'] - - data_target = unsuffixed(target) + '.data' - - # Set up folders - partial_dirs = [] - for file_ in data_files: - dirname = os.path.dirname(file_['name']) - dirname = dirname.lstrip('/') # absolute paths start with '/', remove that - if dirname != '': - parts = dirname.split('/') - for i in range(len(parts)): - partial = '/'.join(parts[:i+1]) - if partial not in partial_dirs: - code += '''FS.createFolder('/%s', '%s', true, false);\n''' % ('/'.join(parts[:i]), parts[i]) - partial_dirs.append(partial) - - if final_suffix == 'html': - # Bundle all datafiles into one archive. Avoids doing lots of simultaneous XHRs which has overhead. - data = open(data_target, 'wb') - start = 0 - for file_ in data_files: - file_['data_start'] = start - curr = open(file_['name'], 'rb').read() - file_['data_end'] = start + len(curr) - start += len(curr) - data.write(curr) - data.close() - if Compression.on: - Compression.compress(data_target) - - # Data requests - for getting a block of data out of the big archive - have a similar API to XHRs - code += ''' - function DataRequest() {} - DataRequest.prototype = { - requests: {}, - open: function(mode, name) { - this.requests[name] = this; - }, - send: function() {} - }; - ''' - - counter = 0 - for file_ in data_files: - filename = file_['name'] - if file_['mode'] == 'embed': - # Embed - code += '''FS.createDataFile('/', '%s', %s, true, true);\n''' % (os.path.basename(filename), str(map(ord, open(filename, 'rb').read()))) - elif file_['mode'] == 'preload': - # Preload - assert final_suffix == 'html', 'Can only preload files when generating HTML' - - varname = 'filePreload%d' % counter - counter += 1 - image = filename.endswith(IMAGE_SUFFIXES) - audio = filename.endswith(AUDIO_SUFFIXES) - - if image: - finish = ''' - var bb = new BlobBuilder(); - bb.append(byteArray.buffer); - var b = bb.getBlob(); - var url = URLObject.createObjectURL(b); - var img = new Image(); - img.onload = function() { - assert(img.complete, 'Image %(filename)s could not be decoded'); - var canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - var ctx = canvas.getContext('2d'); - ctx.drawImage(img, 0, 0); - preloadedImages['%(filename)s'] = canvas; - URLObject.revokeObjectURL(url); - removeRunDependency(); - }; - img.onerror = function(event) { - console.log('Image %(filename)s could not be decoded'); - }; - img.src = url; -''' % { 'filename': filename } - elif audio: - # Need actual blob constructor here, to set the mimetype or else audios fail to decode - finish = ''' - if (hasBlobConstructor) { - var b = new Blob([byteArray.buffer], { type: '%(mimetype)s' }); - var url = URLObject.createObjectURL(b); // XXX we never revoke this! - var audio = new Audio(); - audio.removedDependency = false; - audio['oncanplaythrough'] = function() { // XXX string for closure - audio['oncanplaythrough'] = null; - preloadedAudios['%(filename)s'] = audio; - if (!audio.removedDependency) { - removeRunDependency(); - audio.removedDependency = true; - } - }; - audio.onerror = function(event) { - if (!audio.removedDependency) { - console.log('Audio %(filename)s could not be decoded or timed out trying to decode'); - removeRunDependency(); - audio.removedDependency = true; - } - }; - setTimeout(audio.onerror, 2000); // workaround for chromium bug 124926 (still no audio with this, but at least we don't hang) - audio.src = url; - } else { - preloadedAudios['%(filename)s'] = new Audio(); // empty shim - removeRunDependency(); - } -''' % { 'filename': filename, 'mimetype': AUDIO_MIMETYPES[suffix(filename)] } - else: - finish = 'removeRunDependency();\n' - - code += ''' - var %(varname)s = new %(request)s(); - %(varname)s.open('GET', '%(netname)s', true); - %(varname)s.responseType = 'arraybuffer'; - %(varname)s.onload = function() { - var arrayBuffer = %(varname)s.response; - assert(arrayBuffer, 'Loading file %(filename)s failed.'); - var byteArray = arrayBuffer.byteLength ? new Uint8Array(arrayBuffer) : arrayBuffer; - FS.createDataFile('/%(dirname)s', '%(basename)s', byteArray, true, true); - %(finish)s - }; - addRunDependency(); - %(varname)s.send(null); -''' % { - 'request': 'DataRequest', # In the past we also supported XHRs here - 'varname': varname, - 'filename': filename, - 'netname': file_['net_name'], - 'dirname': os.path.dirname(filename), - 'basename': os.path.basename(filename), - 'finish': finish - } - else: - assert 0 - - if final_suffix == 'html': - # Get the big archive and split it up - use_data = '' - for file_ in data_files: - if file_['mode'] == 'preload': - use_data += ''' - curr = DataRequest.prototype.requests['%s']; - curr.response = byteArray.subarray(%d,%d); - curr.onload(); - ''' % (file_['name'], file_['data_start'], file_['data_end']) - use_data += ' removeRunDependency();\n' - - if Compression.on: - use_data = ''' - Module["decompress"](byteArray, function(decompressed) { - byteArray = new Uint8Array(decompressed); - %s - }); - ''' % use_data - - code += ''' - var dataFile = new XMLHttpRequest(); - dataFile.open('GET', '%s', true); - dataFile.responseType = 'arraybuffer'; - dataFile.onload = function() { - var arrayBuffer = dataFile.response; - assert(arrayBuffer, 'Loading data file failed.'); - var byteArray = new Uint8Array(arrayBuffer); - var curr; - %s - }; - addRunDependency(); - dataFile.send(null); - if (Module['setStatus']) Module['setStatus']('Downloading...'); - ''' % (Compression.compressed_name(data_target) if Compression.on else data_target, use_data) - + file_args = [] + if len(preload_files) > 0: + file_args.append('--preload') + file_args += preload_files + if len(embed_files) > 0: + file_args.append('--embed') + file_args += embed_files + if Compression.on: + file_args += ['--compress', Compression.encoder, Compression.decoder, Compression.js_name] + code = execute(shared.ENV_PREFIX + ['python', shared.FILE_PACKAGER, unsuffixed(target) + '.data'] + file_args, stdout=PIPE)[0] src = open(final).read().replace('// {{PRE_RUN_ADDITIONS}}', code) final += '.files.js' open(final, 'w').write(src) diff --git a/src/jsifier.js b/src/jsifier.js index 1d18f292..e76407b2 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -71,10 +71,7 @@ function JSify(data, functionsOnly, givenFunctions) { } } } else { - libFuncsToInclude = ['memcpy', 'memset', 'malloc', 'free']; - } - if (GENERATING_HTML) { - libFuncsToInclude.push('$Browser'); + libFuncsToInclude = ['memcpy', 'memset', 'malloc', 'free', '$Browser']; } libFuncsToInclude.forEach(function(ident) { data.functionStubs.push({ diff --git a/src/library.js b/src/library.js index 9ebe926a..cb94fccb 100644 --- a/src/library.js +++ b/src/library.js @@ -28,7 +28,14 @@ LibraryManager.library = { $FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'], $FS__postset: '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + '__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' + - '__ATEXIT__.push({ func: function() { FS.quit() } });', + '__ATEXIT__.push({ func: function() { FS.quit() } });' + + // export some names through closure + 'Module["FS_createFolder"] = FS.createFolder;' + + 'Module["FS_createPath"] = FS.createPath;' + + 'Module["FS_createDataFile"] = FS.createDataFile;' + + 'Module["FS_createLazyFile"] = FS.createLazyFile;' + + 'Module["FS_createLink"] = FS.createLink;' + + 'Module["FS_createDevice"] = FS.createDevice;', $FS: { // The path to the current folder. currentPath: '/', @@ -6196,6 +6203,10 @@ LibraryManager.library = { return eval(Pointer_stringify(ptr)); }, + emscripten_random: function() { + return Math.random(); + }, + $Profiling: { max_: 0, times: null, diff --git a/src/library_gc.js b/src/library_gc.js index ccf6656d..bf0a6aff 100644 --- a/src/library_gc.js +++ b/src/library_gc.js @@ -16,13 +16,15 @@ if (GC_SUPPORT) { init: function() { assert(!GC.initted); GC.initted = true; -#if GENERATING_HTML - setInterval(function() { - GC.maybeCollect(); - }, 1000); -#else - // No HTML intervals, so you need to call GC.maybeCollect() or GC.collect() manually + if (ENVIRONMENT_IS_WEB) { + setInterval(function() { + GC.maybeCollect(); + }, 1000); + } else { +#if ASSERTIONS + Module.print('No HTML intervals, so you need to call GC.maybeCollect() or GC.collect() manually'); #endif + } }, malloc: function(bytes, clear, scannable) { diff --git a/src/library_gl.js b/src/library_gl.js index 153473f1..1b6ec5c0 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -897,13 +897,14 @@ var LibraryGL = { // XXX some of these ignored capabilities may lead to incorrect rendering, if we do not emulate them in shaders var ignoredCapabilities = { - 0x0DE1: 1, // GL_TEXTURE_2D 0x0B20: 1, // GL_LINE_SMOOTH 0x0B60: 1, // GL_FOG - 0x8513: 1, // GL_TEXTURE_CUBE_MAP 0x0BA1: 1, // GL_NORMALIZE 0x0C60: 1, // GL_TEXTURE_GEN_S - 0x0C61: 1 // GL_TEXTURE_GEN_T + 0x0C61: 1, // GL_TEXTURE_GEN_T + 0x0DE1: 1, // GL_TEXTURE_2D + 0x8513: 1, // GL_TEXTURE_CUBE_MAP + 0x2A02: 1 // GL_POLYGON_OFFSET_LINE }; _glEnable = function(cap) { // Clean up the renderer on any change to the rendering state. The optimization of @@ -1683,7 +1684,9 @@ var LibraryGL = { // Initialize matrix library GL.immediate.matrix['m'] = GL.immediate.matrix.lib.mat4.create(); + GL.immediate.matrix.lib.mat4.identity(GL.immediate.matrix['m']); GL.immediate.matrix['p'] = GL.immediate.matrix.lib.mat4.create(); + GL.immediate.matrix.lib.mat4.identity(GL.immediate.matrix['p']); for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { GL.immediate.matrix['t' + i] = GL.immediate.matrix.lib.mat4.create(); } @@ -2220,8 +2223,8 @@ var LibraryGL = { // Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name [[0, 'shadeModel getError* finish flush'], - [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear enableVertexAttribArray disableVertexAttribArray lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation polygonOffset sampleCoverage isEnabled*'], - [2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate hint'], + [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear enableVertexAttribArray disableVertexAttribArray lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation sampleCoverage isEnabled*'], + [2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate hint polygonOffset'], [3, 'texParameteri texParameterf drawArrays vertexAttrib2f stencilFunc stencilOp'], [4, 'viewport clearColor scissor vertexAttrib3f colorMask drawElements renderbufferStorage blendFuncSeparate blendColor stencilFuncSeparate stencilOpSeparate'], [5, 'vertexAttrib4f'], diff --git a/src/library_sdl.js b/src/library_sdl.js index 7011e679..6250d6e3 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -934,7 +934,7 @@ var LibrarySDL = { // Convert the path to relative filename = filename.substr(1); } - var raw = preloadedImages[filename]; + var raw = Module["preloadedImages"][filename]; if (!raw) { Runtime.warnOnce('Cannot find preloaded image ' + filename); return 0; @@ -1058,7 +1058,7 @@ var LibrarySDL = { Mix_LoadWAV_RW: function(filename, freesrc) { filename = FS.standardizePath(Pointer_stringify(filename)); - var raw = preloadedAudios[filename]; + var raw = Module["preloadedAudios"][filename]; if (!raw) { Runtime.warnOnce('Cannot find preloaded audio ' + filename); return 0; @@ -1117,8 +1117,12 @@ var LibrarySDL = { return 0; }, + Mix_HookMusicFinished__deps: ['Mix_HaltMusic'], Mix_HookMusicFinished: function(func) { SDL.hookMusicFinished = func; + if (SDL.music) { // ensure the callback will be called, if a music is already playing + SDL.music['onended'] = _Mix_HaltMusic; + } }, Mix_VolumeMusic: function(func) { @@ -1129,12 +1133,14 @@ var LibrarySDL = { Mix_FreeMusic: 'Mix_FreeChunk', + Mix_PlayMusic__deps: ['Mix_HaltMusic'], Mix_PlayMusic: function(id, loops) { loops = Math.max(loops, 1); var audio = SDL.audios[id].audio; if (!audio) return 0; audio.loop = loops != 1; // TODO: handle N loops for finite N audio.play(); + audio['onended'] = _Mix_HaltMusic; // will send callback SDL.music = audio; return 0; }, @@ -1169,6 +1175,14 @@ var LibrarySDL = { Mix_FadeOutMusic: 'Mix_HaltMusic', // XXX ignore fading out effect + Mix_PlayingMusic: function() { + return (SDL.music && !SDL.music.paused) ? 1 : 0; + }, + + Mix_PausedMusic: function() { + return (SDL.music && SDL.music.paused) ? 1 : 0; + }, + // SDL TTF TTF_Init: function() { return 0 }, diff --git a/src/postamble.js b/src/postamble.js index ea03391c..56586487 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -56,7 +56,6 @@ function run(args) { return ret; } -#if GENERATING_HTML if (Module['setStatus']) { Module['setStatus']('Running...'); setTimeout(function() { @@ -69,9 +68,6 @@ function run(args) { } else { return doRun(); } -#else - return doRun(); -#endif } Module['run'] = run; diff --git a/src/preamble.js b/src/preamble.js index 541b4bb5..ae00b796 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -326,6 +326,7 @@ function ccall(ident, returnType, argTypes, args) { var stack = 0; function toC(value, type) { if (type == 'string') { + if (value === null || value === undefined || value === 0) return 0; // null string if (!stack) stack = Runtime.stackSave(); var ret = Runtime.stackAlloc(value.length+1); writeStringToMemory(value, ret); @@ -819,6 +820,7 @@ function addRunDependency() { Module['monitorRunDependencies'](runDependencies); } } +Module['addRunDependency'] = addRunDependency; function removeRunDependency() { runDependencies--; if (Module['monitorRunDependencies']) { @@ -826,6 +828,7 @@ function removeRunDependency() { } if (runDependencies == 0) run(); } +Module['removeRunDependency'] = removeRunDependency; // === Body === diff --git a/src/settings.js b/src/settings.js index e0286213..2526081b 100644 --- a/src/settings.js +++ b/src/settings.js @@ -205,8 +205,6 @@ var LINKABLE = 0; // If set to 1, this file can be linked with others, either as // LINKABLE of 0 is very useful in that we can reduce the size of the // generated code very significantly, by removing everything not actually used. -var GENERATING_HTML = 0; // Set to 1 when generating .html and not just .js - var RUNTIME_TYPE_INFO = 0; // Whether to expose type info to the script at run time. This // increases the size of the generated script, but allows you // to more easily perform operations from handwritten JS on diff --git a/system/include/emscripten.h b/system/include/emscripten.h index 4da31ec3..70524835 100644 --- a/system/include/emscripten.h +++ b/system/include/emscripten.h @@ -80,6 +80,11 @@ void emscripten_set_canvas_size(int width, int height); float emscripten_get_now(); /* + * Simple random number generation in [0, 1), maps to Math.random(). + */ +float emscripten_random(); + +/* * This macro-looking function will cause Emscripten to * generate a comment in the generated code. * XXX This is deprecated for now, because it requires us to diff --git a/tests/gl_matrix_identity.c b/tests/gl_matrix_identity.c new file mode 100644 index 00000000..98b1c21f --- /dev/null +++ b/tests/gl_matrix_identity.c @@ -0,0 +1,129 @@ +/* +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. +*/ + +#if !EMSCRIPTEN +#define USE_GLEW 1 +#endif + +#if USE_GLEW +#include "GL/glew.h" +#endif + +#include "SDL/SDL.h" +#if !USE_GLEW +#include "SDL/SDL_opengl.h" +#endif + +#include <stdio.h> +#include <string.h> +#include <assert.h> + +void verify() { + int width = 640, height = 480; + unsigned char *data = (unsigned char*)malloc(width*height*4); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); + int sum = 0; + for (int x = 0; x < width*height*4; x++) { + if (x % 4 != 3) sum += x * data[x]; + } +#if EMSCRIPTEN + int result = sum; + REPORT_RESULT(); +#endif +} + +int main(int argc, char *argv[]) +{ + SDL_Surface *screen; + 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 ); + screen = SDL_SetVideoMode( 640, 480, 24, SDL_OPENGL ); + if ( !screen ) { + printf("Unable to set video mode: %s\n", SDL_GetError()); + return 1; + } + + // Create a texture + + GLuint texture; + glGenTextures( 1, &texture ); + glBindTexture( GL_TEXTURE_2D, texture ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + GLubyte textureData[] = { 0x7f, 0, 0, + 0, 0xff, 0, + 0x7f, 0, 0, + 0, 0xff, 0}; + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, + GL_RGB, GL_UNSIGNED_BYTE, textureData ); + + + // BEGIN + +#if USE_GLEW + glewInit(); +#endif + + glClearColor( 0, 0, 0.5, 1.0 ); + glClear( GL_COLOR_BUFFER_BIT ); + + glColor4f(0.8, 0.8, 0.8, 1); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glEnable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + + const int kRowSize = 20; + GLuint buffer; + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + float fbuf[] = {0, 1, 0, 0, 1, + 1, 1, 0, 1, 1, + 1, 0, 0, 1, 0, + 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, + 0, 0, 0, 0, 0}; + glBufferData(GL_ARRAY_BUFFER, sizeof(fbuf) * sizeof(float), fbuf, GL_STATIC_DRAW); + + glTexCoordPointer(2, GL_FLOAT, kRowSize, (GLvoid*)(3*4)); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + + glVertexPointer(3, GL_FLOAT, kRowSize, 0); + glEnableClientState(GL_VERTEX_ARRAY); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + + // END + + SDL_GL_SwapBuffers(); + + verify(); + +#if !EMSCRIPTEN + SDL_Delay(1500); +#endif + + SDL_Quit(); + + return 0; +} diff --git a/tests/runner.py b/tests/runner.py index 49991498..31408b96 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -259,12 +259,14 @@ process(sys.argv[1]) def run_native(self, filename, args): Popen([filename+'.native'] + args, stdout=PIPE).communicate()[0] - def assertIdentical(self, x, y): - if x != y: - raise Exception("Expected to have '%s' == '%s', diff:\n\n%s" % ( - limit_size(x), limit_size(y), - limit_size(''.join([a.rstrip()+'\n' for a in difflib.unified_diff(x.split('\n'), y.split('\n'), fromfile='expected', tofile='actual')])) - )) + def assertIdentical(self, values, y): + if type(values) not in [list, tuple]: values = [values] + for x in values: + if x == y: return # success + raise Exception("Expected to have '%s' == '%s', diff:\n\n%s" % ( + limit_size(values[0]), limit_size(y), + limit_size(''.join([a.rstrip()+'\n' for a in difflib.unified_diff(x.split('\n'), y.split('\n'), fromfile='expected', tofile='actual')])) + )) def assertContained(self, values, string, additional_info=''): if type(values) not in [list, tuple]: values = [values] @@ -4023,8 +4025,9 @@ def process(filename): var Module = { 'noFSInit': true, 'preRun': function() { - FS.createDataFile('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory FS.createLazyFile('/', 'test.file', 'test.file', true, false); + // Test FS_* exporting + Module['FS_createDataFile']('/', 'somefile.binary', [100, 200, 50, 25, 10, 77, 123], true, false); // 200 becomes -56, since signed chars are used in memory var test_files_input = 'hi there!'; var test_files_input_index = 0; FS.init(function() { @@ -6836,6 +6839,10 @@ f.close() Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt']).communicate() self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + # preload twice, should not err + Popen(['python', EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt', '--embed-file', 'somefile.txt']).communicate() + self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + def test_multidynamic_link(self): # Linking the same dynamic library in will error, normally, since we statically link it, causing dupe symbols # A workaround is to use --ignore-dynamic-linking, see emcc --help for details @@ -7176,6 +7183,7 @@ elif 'browser' in str(sys.argv): output = queue.get() break time.sleep(0.1) + self.assertIdentical(expectedResult, output) finally: server.terminate() @@ -7417,11 +7425,11 @@ elif 'browser' in str(sys.argv): self.run_browser('page.html', '', '/report_result?1') def test_sdl_image(self): - # load an image file, get pixel data + # load an image file, get pixel data. Also O2 coverage for --preload-file shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), os.path.join(self.get_dir(), 'screenshot.jpg')) open(os.path.join(self.get_dir(), 'sdl_image.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_image.c')).read())) - Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '--preload-file', 'screenshot.jpg', '-o', 'page.html']).communicate() + Popen(['python', EMCC, os.path.join(self.get_dir(), 'sdl_image.c'), '-O2', '--preload-file', 'screenshot.jpg', '-o', 'page.html']).communicate() self.run_browser('page.html', '', '/report_result?600') def test_sdl_image_compressed(self): @@ -7548,7 +7556,7 @@ elif 'browser' in str(sys.argv): # SDL, OpenGL, textures, immediate mode. Closure for more coverage shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.reftest(path_from_root('tests', 'screenshot-gray-purple.png')) - Popen(['python', EMCC, path_from_root('tests', 'sdl_ogl_defaultMatrixMode.c'), '-O2', '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() + Popen(['python', EMCC, path_from_root('tests', 'sdl_ogl_defaultMatrixMode.c'), '--minify', '0', '-o', 'something.html', '--pre-js', 'reftest.js', '--preload-file', 'screenshot.png']).communicate() self.run_browser('something.html', 'You should see an image with gray at the top.', '/report_result?0') def test_sdl_ogl_p(self): @@ -7647,7 +7655,8 @@ elif 'browser' in str(sys.argv): self.reftest(path_from_root('tests', reference)) args += ['--pre-js', 'reftest.js'] Popen(['python', EMCC, os.path.join(self.get_dir(), filename), '-o', 'test.html'] + args).communicate() - self.run_browser('test.html', '.', '/report_result?' + expected) + if type(expected) is str: expected = [expected] + self.run_browser('test.html', '.', ['/report_result?' + e for e in expected]) def test_emscripten_api(self): self.btest('emscripten_api_browser.cpp', '1') @@ -7666,6 +7675,9 @@ elif 'browser' in str(sys.argv): shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png')) self.btest('gl_ps.c', reference='gl_ps.png', args=['--preload-file', 'screenshot.png']) + def test_matrix_identity(self): + self.btest('gl_matrix_identity.c', expected=['-1882984448', '460451840']) + def test_cubegeom_pre(self): self.btest('cubegeom_pre.c', expected='-1472804742') diff --git a/tools/file_packager.py b/tools/file_packager.py new file mode 100644 index 00000000..43f04d8c --- /dev/null +++ b/tools/file_packager.py @@ -0,0 +1,290 @@ +''' +A tool that generates FS API calls to generate a filesystem, and packages the files +to work with that. + +This is called by emcc. You can also call it yourself. + +Usage: + + file_packager.py TARGET [--preload A [B..]] [--embed C [D..]] [--compress COMPRESSION_DATA] [--pre-run] + +''' + +import os, sys + +from shared import Compression, execute + +def suffix(name): + return name.split('.')[-1] + +IMAGE_SUFFIXES = ('.jpg', '.png', '.bmp') |