diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/intertyper.js | 2 | ||||
-rw-r--r-- | src/jsifier.js | 7 | ||||
-rw-r--r-- | src/library.js | 103 | ||||
-rw-r--r-- | src/library_browser.js | 99 | ||||
-rw-r--r-- | src/library_gc.js | 14 | ||||
-rw-r--r-- | src/library_gl.js | 554 | ||||
-rw-r--r-- | src/library_glut.js | 2 | ||||
-rw-r--r-- | src/library_sdl.js | 277 | ||||
-rw-r--r-- | src/parseTools.js | 6 | ||||
-rw-r--r-- | src/postamble.js | 21 | ||||
-rw-r--r-- | src/preamble.js | 48 | ||||
-rw-r--r-- | src/settings.js | 4 | ||||
-rw-r--r-- | src/shell.html | 4 | ||||
-rw-r--r-- | src/shell.js | 14 |
14 files changed, 851 insertions, 304 deletions
diff --git a/src/intertyper.js b/src/intertyper.js index 6b91f527..fbad353a 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -794,7 +794,7 @@ function intertyper(data, sidePass, baseLineNums) { value: parseLLVMSegment(typeToken.concat(subSegments[0])) }; return ret; - }); + }).filter(function(param) { return param.value && param.value.ident != 'undef' }); this.forwardItem(item, 'Reintegrator'); } }); diff --git a/src/jsifier.js b/src/jsifier.js index 1d18f292..01ecd7d3 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({ @@ -286,7 +283,7 @@ function JSify(data, functionsOnly, givenFunctions) { var val = LibraryManager.library[shortident]; var padding; if (Runtime.isNumberType(item.type) || isPointerType(item.type)) { - padding = [item.type].concat(zeros(Runtime.getNativeFieldSize(item.type))); + padding = [item.type].concat(zeros(Runtime.getNativeFieldSize(item.type)-1)); } else { padding = makeEmptyStruct(item.type); } diff --git a/src/library.js b/src/library.js index fe6ae0fb..bb73c48a 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: '/', @@ -1488,7 +1495,7 @@ LibraryManager.library = { lseek: function(fildes, offset, whence) { // off_t lseek(int fildes, off_t offset, int whence); // http://pubs.opengroup.org/onlinepubs/000095399/functions/lseek.html - if (FS.streams[fildes] && !FS.streams[fildes].isDevice) { + if (FS.streams[fildes] && !FS.streams[fildes].object.isDevice) { var stream = FS.streams[fildes]; var position = offset; if (whence === 1) { // SEEK_CUR. @@ -2710,24 +2717,19 @@ LibraryManager.library = { }); } else if (next == 's'.charCodeAt(0)) { // String. - var arg = getNextArg('i8*'); - var copiedString; - if (arg) { - copiedString = String_copy(arg); - if (precisionSet && copiedString.length > precision) { - copiedString = copiedString.slice(0, precision); - } - } else { - copiedString = intArrayFromString('(null)', true); - } + var arg = getNextArg('i8*') || 0; // 0 holds '(null)' + var argLength = String_len(arg); + if (precisionSet) argLength = Math.min(String_len(arg), precision); if (!flagLeftAlign) { - while (copiedString.length < width--) { + while (argLength < width--) { ret.push(' '.charCodeAt(0)); } } - ret = ret.concat(copiedString); + for (var i = 0; i < argLength; i++) { + ret.push({{{ makeGetValue('arg++', 0, 'i8', null, true) }}}); + } if (flagLeftAlign) { - while (copiedString.length < width--) { + while (argLength < width--) { ret.push(' '.charCodeAt(0)); } } @@ -4071,6 +4073,28 @@ LibraryManager.library = { } return pdest; }, + + strlwr__deps:['tolower'], + strlwr: function(pstr){ + var i = 0; + while(1) { + var x = {{{ makeGetValue('pstr', 'i', 'i8') }}}; + if(x == 0) break; + {{{ makeSetValue('pstr', 'i', '_tolower(x)', 'i8') }}}; + i++; + } + }, + + strupr__deps:['toupper'], + strupr: function(pstr){ + var i = 0; + while(1) { + var x = {{{ makeGetValue('pstr', 'i', 'i8') }}}; + if(x == 0) break; + {{{ makeSetValue('pstr', 'i', '_toupper(x)', 'i8') }}}; + i++; + } + }, strcat__deps: ['strlen'], strcat: function(pdest, psrc) { @@ -4220,9 +4244,17 @@ LibraryManager.library = { }, strpbrk: function(ptr1, ptr2) { - var searchSet = Runtime.set.apply(null, String_copy(ptr2)); - while ({{{ makeGetValue('ptr1', 0, 'i8') }}}) { - if ({{{ makeGetValue('ptr1', 0, 'i8') }}} in searchSet) return ptr1; + var curr; + var searchSet = {}; + while (1) { + var curr = {{{ makeGetValue('ptr2++', 0, 'i8') }}}; + if (!curr) break; + searchSet[curr] = 1; + } + while (1) { + curr = {{{ makeGetValue('ptr1', 0, 'i8') }}}; + if (!curr) break; + if (curr in searchSet) return ptr1; ptr1++; } return 0; @@ -4761,15 +4793,42 @@ LibraryManager.library = { // type_info for void*. _ZTIPv: [0], + llvm_uadd_with_overflow_i8: function(x, y) { + x = x & 0xff; + y = y & 0xff; + return { + f0: (x+y) & 0xff, + f1: x+y > 255 + }; + }, + + llvm_umul_with_overflow_i8: function(x, y) { + x = x & 0xff; + y = y & 0xff; + return { + f0: (x*y) & 0xff, + f1: x*y > 255 + }; + }, + llvm_uadd_with_overflow_i16: function(x, y) { - x = (x>>>0) & 0xffff; - y = (y>>>0) & 0xffff; + x = x & 0xffff; + y = y & 0xffff; return { f0: (x+y) & 0xffff, f1: x+y > 65535 }; }, + llvm_umul_with_overflow_i16: function(x, y) { + x = x & 0xffff; + y = y & 0xffff; + return { + f0: (x*y) & 0xffff, + f1: x*y > 65535 + }; + }, + llvm_uadd_with_overflow_i32: function(x, y) { x = x>>>0; y = y>>>0; @@ -6193,6 +6252,10 @@ LibraryManager.library = { return eval(Pointer_stringify(ptr)); }, + emscripten_random: function() { + return Math.random(); + }, + $Profiling: { max_: 0, times: null, diff --git a/src/library_browser.js b/src/library_browser.js index 59d37336..ce59dbdd 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -3,12 +3,26 @@ // Utilities for browser environments mergeInto(LibraryManager.library, { - $Browser__postset: 'Module["requestFullScreen"] = function() { Browser.requestFullScreen() };\n', // export requestFullScreen + $Browser__postset: 'Module["requestFullScreen"] = function() { Browser.requestFullScreen() };\n' + // exports + 'Module["requestAnimationFrame"] = function(func) { Browser.requestAnimationFrame(func) };\n' + + 'Module["pauseMainLoop"] = function() { Browser.mainLoop.pause() };\n' + + 'Module["resumeMainLoop"] = function() { Browser.mainLoop.resume() };\n', $Browser: { mainLoop: { scheduler: null, shouldPause: false, - paused: false + paused: false, + queue: [], + pause: function() { + Browser.mainLoop.shouldPause = true; + }, + resume: function() { + if (Browser.mainLoop.paused) { + Browser.mainLoop.paused = false; + Browser.mainLoop.scheduler(); + } + Browser.mainLoop.shouldPause = false; + }, }, pointerLock: false, moduleContextCreatedCallbacks: [], @@ -86,6 +100,7 @@ mergeInto(LibraryManager.library, { requestFullScreen: function() { var canvas = Module.canvas; function fullScreenChange() { + if (Module['onFullScreen']) Module['onFullScreen'](); if (document['webkitFullScreenElement'] === canvas || document['mozFullScreenElement'] === canvas || document['fullScreenElement'] === canvas) { @@ -128,40 +143,69 @@ mergeInto(LibraryManager.library, { window.requestAnimationFrame(func); }, - getMovementX: function(delta, event) { - if (!Browser.pointerLock) return delta; + getMovementX: function(event) { return event['movementX'] || event['mozMovementX'] || event['webkitMovementX'] || - 0; // delta; + 0; }, - getMovementY: function(delta, event) { - if (!Browser.pointerLock) return delta; + getMovementY: function(event) { return event['movementY'] || event['mozMovementY'] || event['webkitMovementY'] || - 0; // delta; + 0; }, - asyncLoad: function(url, callback) { + xhrLoad: function(url, onload, onerror) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'arraybuffer'; xhr.onload = function() { - var arrayBuffer = xhr.response; + if (xhr.status == 200) { + onload(xhr.response); + } else { + onerror(); + } + }; + xhr.onerror = onerror; + xhr.send(null); + }, + + asyncLoad: function(url, callback) { + Browser.xhrLoad(url, function(arrayBuffer) { assert(arrayBuffer, 'Loading data file "' + url + '" failed (no arrayBuffer).'); callback(new Uint8Array(arrayBuffer)); removeRunDependency(); - }; - xhr.onerror = function(event) { - assert(arrayBuffer, 'Loading data file "' + url + '" failed.'); - }; - xhr.send(null); + }, function(event) { + throw 'Loading data file "' + url + '" failed.'; + }); addRunDependency(); } }, + emscripten_async_wget: function(url, file, onload, onerror) { + url = Pointer_stringify(url); + + Browser.xhrLoad(url, function(response) { + var absolute = Pointer_stringify(file); + var index = absolute.lastIndexOf('/'); + FS.createDataFile( + absolute.substr(0, index), + absolute.substr(index +1), + new Uint8Array(response), + true, true); + + if (onload) { + FUNCTION_TABLE[onload](file); + } + }, function(event) { + if (onerror) { + FUNCTION_TABLE[onerror](file); + } + }); + }, + emscripten_async_run_script__deps: ['emscripten_run_script'], emscripten_async_run_script: function(script, millis) { Module['noExitRuntime'] = true; @@ -177,6 +221,12 @@ mergeInto(LibraryManager.library, { var jsFunc = FUNCTION_TABLE[func]; var wrapper = function() { + if (Browser.mainLoop.queue.length > 0) { + Browser.mainLoop.queue.shift()(); + if (Browser.mainLoop.queue.length == 0 && Module['setStatus']) Module['setStatus'](''); + setTimeout(wrapper, 0); + return; + } if (Browser.mainLoop.shouldPause) { // catch pauses from non-main loop sources Browser.mainLoop.paused = true; @@ -204,21 +254,22 @@ mergeInto(LibraryManager.library, { Browser.mainLoop.scheduler(); }, - emscripten_cancel_main_loop: function(func) { + emscripten_cancel_main_loop: function() { Browser.mainLoop.scheduler = null; Browser.mainLoop.shouldPause = true; }, - emscripten_pause_main_loop: function(func) { - Browser.mainLoop.shouldPause = true; + emscripten_pause_main_loop: function() { + Browser.mainLoop.pause(); }, - emscripten_resume_main_loop: function(func) { - if (Browser.mainLoop.paused) { - Browser.mainLoop.paused = false; - Browser.mainLoop.scheduler(); - } - Browser.mainLoop.shouldPause = false; + emscripten_resume_main_loop: function() { + Browser.mainLoop.resume(); + }, + + emscripten_push_main_loop_blocker: function(func) { + if (Module['setStatus']) Module['setStatus']('Please wait..'); + Browser.mainLoop.queue.push(FUNCTION_TABLE[func]); }, emscripten_async_call: function(func, millis) { 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 9f88260a..f3cea384 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -4,6 +4,7 @@ */ var LibraryGL = { + $GL__postset: 'GL.init()', $GL: { #if GL_DEBUG debug: true, @@ -21,6 +22,10 @@ var LibraryGL = { packAlignment: 4, // default alignment is 4 bytes unpackAlignment: 4, // default alignment is 4 bytes + init: function() { + Browser.moduleContextCreatedCallbacks.push(GL.initExtensions); + }, + // Linear lookup in one of the tables (buffers, programs, etc.). TODO: consider using a weakmap to make this faster, if it matters scan: function(table, object) { for (var item in table) { @@ -100,6 +105,79 @@ var LibraryGL = { var alignedRowSize = roundedToNextMultipleOf(plainRowSize, alignment); return (height <= 0) ? 0 : ((height - 1) * alignedRowSize + plainRowSize); + }, + + getTexPixelData: function(type, format, width, height, pixels, internalFormat) { + var sizePerPixel; + switch (type) { + case 0x1401 /* GL_UNSIGNED_BYTE */: + switch (format) { + case 0x1906 /* GL_ALPHA */: + case 0x1909 /* GL_LUMINANCE */: + sizePerPixel = 1; + break; + case 0x1907 /* GL_RGB */: + sizePerPixel = 3; + break; + case 0x1908 /* GL_RGBA */: + sizePerPixel = 4; + break; + case 0x190A /* GL_LUMINANCE_ALPHA */: + sizePerPixel = 2; + break; + default: + throw 'Invalid format (' + format + ')'; + } + break; + case 0x8363 /* GL_UNSIGNED_SHORT_5_6_5 */: + case 0x8033 /* GL_UNSIGNED_SHORT_4_4_4_4 */: + case 0x8034 /* GL_UNSIGNED_SHORT_5_5_5_1 */: + sizePerPixel = 2; + break; + case 0x1406 /* GL_FLOAT */: + assert(GL.floatExt, 'Must have OES_texture_float to use float textures'); + switch (format) { + case 0x1907 /* GL_RGB */: + sizePerPixel = 3*4; + break; + case 0x1908 /* GL_RGBA */: + sizePerPixel = 4*4; + break; + default: + throw 'Invalid format (' + format + ')'; + } + internalFormat = Module.ctx.RGBA; + break; + default: + throw 'Invalid type (' + type + ')'; + } + var bytes = GL.computeImageSize(width, height, sizePerPixel, GL.unpackAlignment); + if (type == 0x1401 /* GL_UNSIGNED_BYTE */) { + pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}}; + } else if (type == 0x1406 /* GL_FLOAT */) { + pixels = {{{ makeHEAPView('F32', 'pixels', 'pixels+bytes') }}}; + } else { + pixels = {{{ makeHEAPView('U16', 'pixels', 'pixels+bytes') }}}; + } + return { + pixels: pixels, + internalFormat: internalFormat + } + }, + + initExtensions: function() { + if (GL.initExtensions.done) return; + GL.initExtensions.done = true; + + GL.compressionExt = Module.ctx.getExtension('WEBGL_compressed_texture_s3tc') || + Module.ctx.getExtension('MOZ_WEBGL_compressed_texture_s3tc') || + Module.ctx.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc'); + + GL.anisotropicExt = Module.ctx.getExtension('EXT_texture_filter_anisotropic') || + Module.ctx.getExtension('MOZ_EXT_texture_filter_anisotropic') || + Module.ctx.getExtension('WEBKIT_EXT_texture_filter_anisotropic'); + + GL.floatExt = Module.ctx.getExtension('OES_texture_float'); } }, @@ -273,96 +351,41 @@ var LibraryGL = { } }, - glCompressedTexImage2D: function(target, level, internalformat, width, height, border, imageSize, data) { + glCompressedTexImage2D: function(target, level, internalFormat, width, height, border, imageSize, data) { + assert(GL.compressionExt); if (data) { data = {{{ makeHEAPView('U8', 'data', 'data+imageSize') }}}; } else { data = null; } - Module.ctx.compressedTexImage2D(target, level, internalformat, width, height, border, data); + Module.ctx['compressedTexImage2D'](target, level, internalFormat, width, height, border, data); }, glCompressedTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, imageSize, data) { + assert(GL.compressionExt); if (data) { data = {{{ makeHEAPView('U8', 'data', 'data+imageSize') }}}; } else { data = null; } - Module.ctx.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, data); + Module.ctx['compressedTexSubImage2D'](target, level, xoffset, yoffset, width, height, data); }, - glTexImage2D: function(target, level, internalformat, width, height, border, format, type, pixels) { + glTexImage2D: function(target, level, internalFormat, width, height, border, format, type, pixels) { if (pixels) { - var sizePerPixel; - switch (type) { - case 0x1401 /* GL_UNSIGNED_BYTE */: - switch (format) { - case 0x1906 /* GL_ALPHA */: - case 0x1909 /* GL_LUMINANCE */: - sizePerPixel = 1; - break; - case 0x1907 /* GL_RGB */: - sizePerPixel = 3; - break; - case 0x1908 /* GL_RGBA */: - sizePerPixel = 4; - break; - case 0x190A /* GL_LUMINANCE_ALPHA */: - sizePerPixel = 2; - break; - default: - throw 'Invalid format (' + format + ') passed to glTexImage2D'; - } - break; - case 0x8363 /* GL_UNSIGNED_SHORT_5_6_5 */: - case 0x8033 /* GL_UNSIGNED_SHORT_4_4_4_4 */: - case 0x8034 /* GL_UNSIGNED_SHORT_5_5_5_1 */: - sizePerPixel = 2; - break; - default: - throw 'Invalid type (' + type + ') passed to glTexImage2D'; - } - var bytes = GL.computeImageSize(width, height, sizePerPixel, GL.unpackAlignment); - pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}}; + var data = GL.getTexPixelData(type, format, width, height, pixels, internalFormat); + pixels = data.pixels; + internalFormat = data.internalFormat; } else { pixels = null; } - Module.ctx.texImage2D(target, level, internalformat, width, height, border, format, type, pixels); + Module.ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixels); }, glTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, type, pixels) { if (pixels) { - var sizePerPixel; - switch (type) { - case 0x1401 /* GL_UNSIGNED_BYTE */: - switch (format) { - case 0x1906 /* GL_ALPHA */: - case 0x1909 /* GL_LUMINANCE */: - sizePerPixel = 1; - break; - case 0x1907 /* GL_RGB */: - sizePerPixel = 3; - break; - case 0x1908 /* GL_RGBA */: - sizePerPixel = 4; - break; - case 0x190A /* GL_LUMINANCE_ALPHA */: - sizePerPixel = 2; - break; - default: - throw 'Invalid format (' + format + ') passed to glTexSubImage2D'; - } - break; - case 0x8363 /* GL_UNSIGNED_SHORT_5_6_5 */: - case 0x8033 /* GL_UNSIGNED_SHORT_4_4_4_4 */: - case 0x8034 /* GL_UNSIGNED_SHORT_5_5_5_1 */: - sizePerPixel = 2; - break; - default: - throw 'Invalid type (' + type + ') passed to glTexSubImage2D'; - } - var bytes = GL.computeImageSize(width, height, sizePerPixel, GL.unpackAlignment); - pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}}; + var data = GL.getTexPixelData(type, format, width, height, pixels, -1); + pixels = data.pixels; } else { pixels = null; } @@ -891,28 +914,62 @@ var LibraryGL = { $GLEmulation__postset: 'GLEmulation.init();', $GLEmulation: { + // Fog support. Partial, we assume shaders are used that implement fog. We just pass them uniforms + fogStart: 0, + fogEnd: 1, + fogDensity: 1.0, + fogColor: null, + fogMode: 0x0800, // GL_EXP + fogEnabled: false, + init: function() { + GLEmulation.fogColor = new Float32Array(4); + // Add some emulation workarounds Module.printErr('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work'); - // 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 + // XXX some of the capabilities we don't support may lead to incorrect rendering, if we do not emulate them in shaders + var validCapabilities = { + 0x0B44: 1, // GL_CULL_FACE + 0x0BE2: 1, // GL_BLEND + 0x0BD0: 1, // GL_DITHER, + 0x0B90: 1, // GL_STENCIL_TEST + 0x0B71: 1, // GL_DEPTH_TEST + 0x0C11: 1, // GL_SCISSOR_TEST + 0x8037: 1, // GL_POLYGON_OFFSET_FILL + 0x809E: 1, // GL_SAMPLE_ALPHA_TO_COVERAGE + 0x80A0: 1 // GL_SAMPLE_COVERAGE }; _glEnable = function(cap) { - if (cap in ignoredCapabilities) return; + // Clean up the renderer on any change to the rendering state. The optimization of + // skipping renderer setup is aimed at the case of multiple glDraw* right after each other + if (GL.immediate.lastRenderer) GL.immediate.lastRenderer.cleanup(); + if (cap == 0x0B60 /* GL_FOG */) { + GLEmulation.fogEnabled = true; + return; + } else if (!(cap in validCapabilities)) { + return; + } Module.ctx.enable(cap); }; _glDisable = function(cap) { - if (cap in ignoredCapabilities) return; + if (GL.immediate.lastRenderer) GL.immediate.lastRenderer.cleanup(); + if (cap == 0x0B60 /* GL_FOG */) { + GLEmulation.fogEnabled = false; + return; + } else if (!(cap in validCapabilities)) { + return; + } Module.ctx.disable(cap); }; + _glIsEnabled = function(cap) { + if (cap == 0x0B60 /* GL_FOG */) { + return GLEmulation.fogEnabled ? 1 : 0; + } else if (!(cap in validCapabilities)) { + return 0; + } + return Module.ctx.isEnabled(cap); + }; var glGetIntegerv = _glGetIntegerv; _glGetIntegerv = function(pname, params) { @@ -942,7 +999,11 @@ var LibraryGL = { _glGetString = function(name_) { switch(name_) { case 0x1F03 /* GL_EXTENSIONS */: // Add various extensions that we can support - return 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'), 'i8', ALLOC_NORMAL); + return 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); } return glGetString(name_); }; @@ -986,7 +1047,7 @@ var LibraryGL = { source = source.replace(/gl_ProjectionMatrix/g, 'u_projection'); if (old != source) need_pm = 1; old = source; - source = source.replace(/gl_ModelViewMatrixTranspose\[2\]/g, 'vec4(u_modelView[0][0], u_modelView[1][0], u_modelView[2][0], u_modelView[3][0])'); // XXX extremely inefficient + source = source.replace(/gl_ModelViewMatrixTranspose\[2\]/g, 'vec4(u_modelView[0][2], u_modelView[1][2], u_modelView[2][2], u_modelView[3][2])'); // XXX extremely inefficient if (old != source) need_mm = 1; old = source; source = source.replace(/gl_ModelViewMatrix/g, 'u_modelView'); @@ -1034,9 +1095,10 @@ var LibraryGL = { source = 'attribute vec3 a_normal; \n' + source.replace(/gl_Normal/g, 'a_normal'); } + // fog if (source.indexOf('gl_FogFragCoord') >= 0) { - source = 'varying float v_fogCoord; \n' + - source.replace(/gl_FogFragCoord/g, 'v_fogCoord'); + source = 'varying float v_fogFragCoord; \n' + + source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord'); } } else { // Fragment shader for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) { @@ -1049,7 +1111,26 @@ var LibraryGL = { if (source.indexOf('gl_Color') >= 0) { source = 'varying vec4 v_color; \n' + source.replace(/gl_Color/g, 'v_color'); } - source = source.replace(/gl_Fog.color/g, 'vec4(0.0)'); // XXX TODO + if (source.indexOf('gl_Fog.color') >= 0) { + source = 'uniform vec4 u_fogColor; \n' + + source.replace(/gl_Fog.color/g, 'u_fogColor'); + } + if (source.indexOf('gl_Fog.end') >= 0) { + source = 'uniform float u_fogEnd; \n' + + source.replace(/gl_Fog.end/g, 'u_fogEnd'); + } + if (source.indexOf('gl_Fog.scale') >= 0) { + source = 'uniform float u_fogScale; \n' + + source.replace(/gl_Fog.scale/g, 'u_fogScale'); + } + if (source.indexOf('gl_Fog.density') >= 0) { + source = 'uniform float u_fogDensity; \n' + + source.replace(/gl_Fog.density/g, 'u_fogDensity'); + } + if (source.indexOf('gl_FogFragCoord') >= 0) { + source = 'varying float v_fogFragCoord; \n' + + source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord'); + } source = 'precision mediump float;\n' + source; } #if GL_DEBUG @@ -1105,6 +1186,21 @@ var LibraryGL = { if (program == GL.currProgram) GL.currProgram = 0; }; + // If attribute 0 was not bound, bind it to 0 for WebGL performance reasons. Track if 0 is free for that. + var zeroUsedPrograms = {}; + var glBindAttribLocation = _glBindAttribLocation; + _glBindAttribLocation = function(program, index, name) { + if (index == 0) zeroUsedPrograms[program] = true; + glBindAttribLocation(program, index, name); + }; + var glLinkProgram = _glLinkProgram; + _glLinkProgram = function(program) { + if (!(program in zeroUsedPrograms)) { + Module.ctx.bindAttribLocation(GL.programs[program], 0, 'a_position'); + } + glLinkProgram(program); + }; + var glBindBuffer = _glBindBuffer; _glBindBuffer = function(target, buffer) { glBindBuffer(target, buffer); @@ -1134,15 +1230,27 @@ var LibraryGL = { } else if (pname == 0x0BA8) { // GL_TEXTURE_MATRIX HEAPF32.set(GL.immediate.matrix['t' + GL.immediate.clientActiveTexture], params >> 2); } else if (pname == 0x0B66) { // GL_FOG_COLOR - {{{ makeSetValue('params', '0', '0', 'float') }}}; + HEAPF32.set(GLEmulation.fogColor, params >> 2); } else if (pname == 0x0B63) { // GL_FOG_START - {{{ makeSetValue('params', '0', '0', 'float') }}}; + {{{ makeSetValue('params', '0', 'GLEmulation.fogStart', 'float') }}}; } else if (pname == 0x0B64) { // GL_FOG_END - {{{ makeSetValue('params', '0', '0', 'float') }}}; + {{{ makeSetValue('params', '0', 'GLEmulation.fogEnd', 'float') }}}; + } else if (pname == 0x0B62) { // GL_FOG_DENSITY + {{{ makeSetValue('params', '0', 'GLEmulation.fogDensity', 'float') }}}; + } else if (pname == 0x0B65) { // GL_FOG_MODE + {{{ makeSetValue('params', '0', 'GLEmulation.fogMode', 'float') }}}; } else { glGetFloatv(pname, params); } }; + + var glHint = _glHint; + _glHint = function(target, mode) { + if (target == 0x84EF) { // GL_TEXTURE_COMPRESSION_HINT + return; + } + glHint(target, mode); + }; }, getProcAddress: function(name) { @@ -1269,12 +1377,16 @@ var LibraryGL = { rendererCache: {}, rendererComponents: {}, // small cache for calls inside glBegin/end. counts how many times the element was seen rendererComponentPointer: 0, // next place to start a glBegin/end component + lastRenderer: null, // used to avoid cleaning up and re-preparing the same renderer + lastArrayBuffer: null, // used in conjunction with lastRenderer + lastProgram: null, // "" // The following data structures are used for OpenGL Immediate Mode matrix routines. matrix: {}, matrixStack: {}, currentMatrix: 'm', // default is modelview tempMatrix: null, + matricesModified: false, // Clientside attributes VERTEX: 0, @@ -1324,6 +1436,7 @@ var LibraryGL = { tempBufferIndexLookup: null, tempVertexBuffers: null, tempIndexBuffers: null, + tempQuadIndexBuffer: null, generateTempBuffers: function() { function ceilPower2(x) { @@ -1357,6 +1470,30 @@ var LibraryGL = { last = size; } } + + // GL_QUAD indexes can be precalculated + this.tempQuadIndexBuffer = Module.ctx.createBuffer(); + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, this.tempQuadIndexBuffer); + var numIndexes = this.MAX_TEMP_BUFFER_SIZE >> 1; + var quadIndexes = new Uint16Array(numIndexes); + var i = 0, v = 0; + while (1) { + quadIndexes[i++] = v; + if (i >= numIndexes) break; + quadIndexes[i++] = v+1; + if (i >= numIndexes) break; + quadIndexes[i++] = v+2; + if (i >= numIndexes) break; + quadIndexes[i++] = v; + if (i >= numIndexes) break; + quadIndexes[i++] = v+2; + if (i >= numIndexes) break; + quadIndexes[i++] = v+3; + if (i >= numIndexes) break; + v += 4; + } + Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER, quadIndexes, Module.ctx.STATIC_DRAW); + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null); }, // Renderers @@ -1394,7 +1531,14 @@ var LibraryGL = { if (!cacheItem[attribute.type]) cacheItem[attribute.type] = {}; cacheItem = cacheItem[attribute.type]; } - if (GL.currProgram) { + if (GLEmulation.fogEnabled) { + var fogParam = GLEmulation.fogMode; + } else { + var fogParam = 0; // all valid modes are non-zero + } + if (!cacheItem[fogParam]) cacheItem[fogParam] = {}; + cacheItem = cacheItem[fogParam]; + if (GL.currProgram) { // Note the order here; this one is last, and optional if (!cacheItem[GL.currProgram] |