aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/intertyper.js2
-rw-r--r--src/jsifier.js7
-rw-r--r--src/library.js103
-rw-r--r--src/library_browser.js99
-rw-r--r--src/library_gc.js14
-rw-r--r--src/library_gl.js554
-rw-r--r--src/library_glut.js2
-rw-r--r--src/library_sdl.js277
-rw-r--r--src/parseTools.js6
-rw-r--r--src/postamble.js21
-rw-r--r--src/preamble.js48
-rw-r--r--src/settings.js4
-rw-r--r--src/shell.html4
-rw-r--r--src/shell.js14
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]