aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc23
-rw-r--r--src/jsifier.js1
-rw-r--r--src/library_browser.js180
-rw-r--r--src/library_gl.js19
-rw-r--r--src/library_glut.js7
-rw-r--r--src/library_sdl.js47
-rw-r--r--src/proxyClient.js49
-rw-r--r--src/proxyWorker.js123
-rw-r--r--src/settings.js5
-rw-r--r--src/webGLClient.js234
-rw-r--r--src/webGLWorker.js910
-rw-r--r--tests/gles2_uniform_arrays.cpp29
-rw-r--r--tests/hello_world_gles_proxy.c765
-rwxr-xr-xtests/runner.py32
-rw-r--r--tests/sdlglshader2.c168
-rw-r--r--tests/test_browser.py45
16 files changed, 2508 insertions, 129 deletions
diff --git a/emcc b/emcc
index 20f04dd1..e10d260d 100755
--- a/emcc
+++ b/emcc
@@ -503,8 +503,13 @@ Options that are modified or new in %s include:
to hide these warnings and acknowledge that the
explicit use of absolute paths is intentional.
- --proxy-to-worker Generates both html and js files. The main
- program is in js, and the html proxies to/from it.
+ --proxy-to-worker Runs the main application code in a worker, proxying
+ events to it and output from it.
+ If emitting htmlL, this emits an html and a js file,
+ with the js to be run in a worker. If emitting
+ js, the target filename contains the part to be run
+ on the main thread, while a second js file with
+ suffix ".worker.js" will contain the worker portion.
--emrun Enables the generated output to be aware of the
emrun command line tool. This allows stdout, stderr
@@ -1832,7 +1837,7 @@ try:
js_target = unsuffixed(target) + '.js'
base_js_target = os.path.basename(js_target)
if proxy_to_worker:
- html.write(shell.replace('{{{ SCRIPT }}}', '<script>' + open(shared.path_from_root('src', 'proxyClient.js')).read().replace('{{{ filename }}}', target_basename) + '</script>'))
+ html.write(shell.replace('{{{ SCRIPT }}}', '<script>' + open(shared.path_from_root('src', 'webGLClient.js')).read() + '\n' + open(shared.path_from_root('src', 'proxyClient.js')).read().replace('{{{ filename }}}', shared.Settings.PROXY_TO_WORKER_FILENAME or target_basename) + '</script>'))
shutil.move(final, js_target)
elif not Compression.on:
# Normal code generation path
@@ -1977,8 +1982,13 @@ try {
split_javascript_file(final, unsuffixed(target), split_js_file)
else:
if debug_level >= 4: generate_source_map(target)
- # copy final JS to output
- shutil.move(final, target)
+ if proxy_to_worker:
+ worker_target_basename = target_basename + '.worker'
+ open(target, 'w').write(open(shared.path_from_root('src', 'webGLClient.js')).read() + '\n' + open(shared.path_from_root('src', 'proxyClient.js')).read().replace('{{{ filename }}}', shared.Settings.PROXY_TO_WORKER_FILENAME or worker_target_basename))
+ shutil.move(final, target[:-3] + '.worker.js')
+ else:
+ # copy final JS to output normally
+ shutil.move(final, target)
log_time('final emitting')
@@ -1993,6 +2003,3 @@ finally:
else:
logging.info('emcc saved files are in:' + temp_dir)
-#//eliminate a = a in js opt. will kill STACKTOP = STACKTOP in funcs that do not use the C stack! add test for no STACKTOP or sp in such a func
-#// minify if into ?: ?
-
diff --git a/src/jsifier.js b/src/jsifier.js
index d1001dac..1f6440dd 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -1899,6 +1899,7 @@ function JSify(data, functionsOnly) {
print('}');
}
if (PROXY_TO_WORKER) {
+ print(read('webGLWorker.js'));
print(read('proxyWorker.js'));
}
if (DETERMINISTIC) {
diff --git a/src/library_browser.js b/src/library_browser.js
index 1740eaed..a3e68209 100644
--- a/src/library_browser.js
+++ b/src/library_browser.js
@@ -41,6 +41,26 @@ mergeInto(LibraryManager.library, {
Module['setStatus']('');
}
}
+ },
+ runIter: function(func) {
+ if (ABORT) return;
+ if (Module['preMainLoop']) {
+ var preRet = Module['preMainLoop']();
+ if (preRet === false) {
+ return; // |return false| skips a frame
+ }
+ }
+ try {
+ func();
+ } catch (e) {
+ if (e instanceof ExitStatus) {
+ return;
+ } else {
+ if (e && typeof e === 'object' && e.stack) Module.printErr('exception thrown: ' + [e, e.stack]);
+ throw e;
+ }
+ }
+ if (Module['postMainLoop']) Module['postMainLoop']();
}
},
isFullScreen: false,
@@ -51,7 +71,7 @@ mergeInto(LibraryManager.library, {
init: function() {
if (!Module["preloadPlugins"]) Module["preloadPlugins"] = []; // needs to exist even in workers
- if (Browser.initted || ENVIRONMENT_IS_WORKER) return;
+ if (Browser.initted || ENVIRONMENT_IS_WORKER) return; // workers do not support Image and Audio elements
Browser.initted = true;
try {
@@ -242,6 +262,8 @@ mergeInto(LibraryManager.library, {
return null;
}
#endif
+ if (useWebGL && Module.ctx) return Module.ctx; // no need to recreate singleton GL context
+
var ctx;
var errorInfo = '?';
function onContextCreationError(event) {
@@ -282,49 +304,98 @@ mergeInto(LibraryManager.library, {
}
if (useWebGL) {
#if GL_DEBUG
- // Useful to debug native webgl apps: var Module = { printErr: function(x) { console.log(x) } };
- var tempCtx = ctx;
- var wrapper = {};
- for (var prop in tempCtx) {
- (function(prop) {
- switch (typeof tempCtx[prop]) {
- case 'function': {
- wrapper[prop] = function gl_wrapper() {
- if (GL.debug) {
- var printArgs = Array.prototype.slice.call(arguments).map(Runtime.prettyPrint);
- Module.printErr('[gl_f:' + prop + ':' + printArgs + ']');
- }
- var ret = tempCtx[prop].apply(tempCtx, arguments);
- if (GL.debug && typeof ret != 'undefined') {
- Module.printErr('[ gl:' + prop + ':return:' + Runtime.prettyPrint(ret) + ']');
- }
- return ret;
- }
- break;
+ function wrapDebugGL(ctx) {
+
+ var printObjectList = [];
+
+ function prettyPrint(arg) {
+ if (typeof arg == 'undefined') return '!UNDEFINED!';
+ if (typeof arg == 'boolean') arg = arg + 0;
+ if (!arg) return arg;
+ var index = printObjectList.indexOf(arg);
+ if (index >= 0) return '<' + arg + '|'; // + index + '>';
+ if (arg.toString() == '[object HTMLImageElement]') {
+ return arg + '\n\n';
+ }
+ if (arg.byteLength) {
+ return '{' + Array.prototype.slice.call(arg, 0, Math.min(arg.length, 400)) + '}'; // Useful for correct arrays, less so for compiled arrays, see the code below for that
+ var buf = new ArrayBuffer(32);
+ var i8buf = new Int8Array(buf);
+ var i16buf = new Int16Array(buf);
+ var f32buf = new Float32Array(buf);
+ switch(arg.toString()) {
+ case '[object Uint8Array]':
+ i8buf.set(arg.subarray(0, 32));
+ break;
+ case '[object Float32Array]':
+ f32buf.set(arg.subarray(0, 5));
+ break;
+ case '[object Uint16Array]':
+ i16buf.set(arg.subarray(0, 16));
+ break;
+ default:
+ alert('unknown array for debugging: ' + arg);
+ throw 'see alert';
}
- case 'number': case 'string': {
- wrapper.__defineGetter__(prop, function() {
- //Module.printErr('[gl_g:' + prop + ':' + tempCtx[prop] + ']');
- return tempCtx[prop];
- });
- wrapper.__defineSetter__(prop, function(value) {
- if (GL.debug) {
- Module.printErr('[gl_s:' + prop + ':' + value + ']');
+ var ret = '{' + arg.byteLength + ':\n';
+ var arr = Array.prototype.slice.call(i8buf);
+ ret += 'i8:' + arr.toString().replace(/,/g, ',') + '\n';
+ arr = Array.prototype.slice.call(f32buf, 0, 8);
+ ret += 'f32:' + arr.toString().replace(/,/g, ',') + '}';
+ return ret;
+ }
+ if (typeof arg == 'object') {
+ printObjectList.push(arg);
+ return '<' + arg + '|'; // + (printObjectList.length-1) + '>';
+ }
+ if (typeof arg == 'number') {
+ if (arg > 0) return '0x' + arg.toString(16) + ' (' + arg + ')';
+ }
+ return arg;
+ }
+
+ var wrapper = {};
+ for (var prop in ctx) {
+ (function(prop) {
+ switch (typeof ctx[prop]) {
+ case 'function': {
+ wrapper[prop] = function gl_wrapper() {
+ var printArgs = Array.prototype.slice.call(arguments).map(prettyPrint);
+ dump('[gl_f:' + prop + ':' + printArgs + ']\n');
+ var ret = ctx[prop].apply(ctx, arguments);
+ if (typeof ret != 'undefined') {
+ dump('[ gl:' + prop + ':return:' + prettyPrint(ret) + ']\n');
+ }
+ return ret;
}
- tempCtx[prop] = value;
- });
- break;
+ break;
+ }
+ case 'number': case 'string': {
+ wrapper.__defineGetter__(prop, function() {
+ //dump('[gl_g:' + prop + ':' + ctx[prop] + ']\n');
+ return ctx[prop];
+ });
+ wrapper.__defineSetter__(prop, function(value) {
+ dump('[gl_s:' + prop + ':' + value + ']\n');
+ ctx[prop] = value;
+ });
+ break;
+ }
}
- }
- })(prop);
+ })(prop);
+ }
+ return wrapper;
}
- ctx = wrapper;
#endif
+ // possible GL_DEBUG entry point: ctx = wrapDebugGL(ctx);
+
// Set the background of the WebGL canvas to black
canvas.style.backgroundColor = "black";
}
if (setInModule) {
- GLctx = Module.ctx = ctx;
+ if (!useWebGL) assert(typeof GLctx === 'undefined', 'cannot set in module if GLctx is used, but we are a non-GL context that would replace it');
+ Module.ctx = ctx;
+ if (useWebGL) GLctx = ctx;
Module.useWebGL = useWebGL;
Browser.moduleContextCreatedCallbacks.forEach(function(callback) { callback() });
Browser.init();
@@ -395,9 +466,25 @@ mergeInto(LibraryManager.library, {
canvasContainer.requestFullScreen();
},
+ nextRAF: 0,
+
+ fakeRequestAnimationFrame: function(func) {
+ // try to keep 60fps between calls to here
+ var now = Date.now();
+ if (Browser.nextRAF === 0) {
+ Browser.nextRAF = now + 1000/60;
+ } else {
+ while (now + 2 >= Browser.nextRAF) { // fudge a little, to avoid timer jitter causing us to do lots of delay:0
+ Browser.nextRAF += 1000/60;
+ }
+ }
+ var delay = Math.max(Browser.nextRAF - now, 0);
+ setTimeout(func, delay);
+ },
+
requestAnimationFrame: function requestAnimationFrame(func) {
if (typeof window === 'undefined') { // Provide fallback to setTimeout if window is undefined (e.g. in Node.js)
- setTimeout(func, 1000/60);
+ Browser.fakeRequestAnimationFrame(func);
} else {
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = window['requestAnimationFrame'] ||
@@ -405,7 +492,7 @@ mergeInto(LibraryManager.library, {
window['webkitRequestAnimationFrame'] ||
window['msRequestAnimationFrame'] ||
window['oRequestAnimationFrame'] ||
- window['setTimeout'];
+ Browser.fakeRequestAnimationFrame;
}
window.requestAnimationFrame(func);
}
@@ -954,28 +1041,13 @@ mergeInto(LibraryManager.library, {
Browser.mainLoop.method = ''; // just warn once per call to set main loop
}
- if (Module['preMainLoop']) {
- Module['preMainLoop']();
- }
-
- try {
+ Browser.mainLoop.runIter(function() {
if (typeof arg !== 'undefined') {
Runtime.dynCall('vi', func, [arg]);
} else {
Runtime.dynCall('v', func);
}
- } catch (e) {
- if (e instanceof ExitStatus) {
- return;
- } else {
- if (e && typeof e === 'object' && e.stack) Module.printErr('exception thrown: ' + [e, e.stack]);
- throw e;
- }
- }
-
- if (Module['postMainLoop']) {
- Module['postMainLoop']();
- }
+ });
if (Browser.mainLoop.shouldPause) {
// catch pauses from the main loop itself
diff --git a/src/library_gl.js b/src/library_gl.js
index 2659a9d9..e67ec29b 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -833,6 +833,7 @@ var LibraryGL = {
for (var i = 0; i < n; i++) {
var id = {{{ makeGetValue('textures', 'i*4', 'i32') }}};
var texture = GL.textures[id];
+ if (!texture) continue;
GLctx.deleteTexture(texture);
texture.name = 0;
GL.textures[id] = null;
@@ -1316,7 +1317,7 @@ var LibraryGL = {
#endif
location = GL.uniforms[location];
var view;
- if (count == 1) {
+ if (count === 1) {
// avoid allocation for the common case of uploading one uniform
view = GL.miniTempBufferViews[0];
view[0] = {{{ makeGetValue('value', '0', 'float') }}};
@@ -1333,7 +1334,7 @@ var LibraryGL = {
#endif
location = GL.uniforms[location];
var view;
- if (count == 1) {
+ if (count === 1) {
// avoid allocation for the common case of uploading one uniform
view = GL.miniTempBufferViews[1];
view[0] = {{{ makeGetValue('value', '0', 'float') }}};
@@ -1351,7 +1352,7 @@ var LibraryGL = {
#endif
location = GL.uniforms[location];
var view;
- if (count == 1) {
+ if (count === 1) {
// avoid allocation for the common case of uploading one uniform
view = GL.miniTempBufferViews[2];
view[0] = {{{ makeGetValue('value', '0', 'float') }}};
@@ -1370,7 +1371,7 @@ var LibraryGL = {
#endif
location = GL.uniforms[location];
var view;
- if (count == 1) {
+ if (count === 1) {
// avoid allocation for the common case of uploading one uniform
view = GL.miniTempBufferViews[3];
view[0] = {{{ makeGetValue('value', '0', 'float') }}};
@@ -1390,7 +1391,7 @@ var LibraryGL = {
#endif
location = GL.uniforms[location];
var view;
- if (count == 1) {
+ if (count === 1) {
// avoid allocation for the common case of uploading one uniform matrix
view = GL.miniTempBufferViews[3];
for (var i = 0; i < 4; i++) {
@@ -1409,7 +1410,7 @@ var LibraryGL = {
#endif
location = GL.uniforms[location];
var view;
- if (count == 1) {
+ if (count === 1) {
// avoid allocation for the common case of uploading one uniform matrix
view = GL.miniTempBufferViews[8];
for (var i = 0; i < 9; i++) {
@@ -1428,7 +1429,7 @@ var LibraryGL = {
#endif
location = GL.uniforms[location];
var view;
- if (count == 1) {
+ if (count === 1) {
// avoid allocation for the common case of uploading one uniform matrix
view = GL.miniTempBufferViews[15];
for (var i = 0; i < 16; i++) {
@@ -4347,6 +4348,10 @@ var LibraryGL = {
}
break;
}
+ case 0x84FF: { // GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
+ {{{ makeSetValue('params', '0', '0', 'i32') }}}; // no valid value to return here
+ return;
+ }
}
glGetIntegerv(pname, params);
};
diff --git a/src/library_glut.js b/src/library_glut.js
index 445e08a4..d6293d85 100644
--- a/src/library_glut.js
+++ b/src/library_glut.js
@@ -383,7 +383,7 @@ var LibraryGLUT = {
function callback() {
if (GLUT.idleFunc) {
Runtime.dynCall('v', GLUT.idleFunc);
- Browser.safeSetTimeout(callback, 0);
+ Browser.safeSetTimeout(callback, 4); // HTML spec specifies a 4ms minimum delay on the main thread; workers might get more, but we standardize here
}
}
if (!GLUT.idleFunc) {
@@ -489,8 +489,9 @@ var LibraryGLUT = {
GLUT.requestedAnimationFrame = true;
Browser.requestAnimationFrame(function() {
GLUT.requestedAnimationFrame = false;
- if (ABORT) return;
- Runtime.dynCall('v', GLUT.displayFunc);
+ Browser.mainLoop.runIter(function() {
+ Runtime.dynCall('v', GLUT.displayFunc);
+ });
});
}
},
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 07a618a3..eaa2b5bb 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -1314,20 +1314,14 @@ var LibrarySDL = {
Module['canvas'].addEventListener(event, SDL.receiveEvent, true);
});
+ var canvas = Module['canvas'];
+
// (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) {
- SDL.freeSurface(SDL.screen);
- assert(!SDL.screen);
- }
- SDL.screen = SDL.makeSurface(width, height, flags, true, 'screen');
if (!SDL.addedResizeListener) {
SDL.addedResizeListener = true;
Browser.resizeListeners.push(function(w, h) {
@@ -1338,6 +1332,21 @@ var LibrarySDL = {
});
});
}
+
+ if (width !== canvas.width || height !== canvas.height) {
+ Browser.setCanvasSize(width, height);
+ }
+
+ // Free the old surface first if there is one
+ if (SDL.screen) {
+ SDL.freeSurface(SDL.screen);
+ assert(!SDL.screen);
+ }
+
+ if (SDL.GL) flags = flags | 0x04000000; // SDL_OPENGL - if we are using GL, then later calls to SetVideoMode may not mention GL, but we do need it. Once in GL mode, we never leave it.
+
+ SDL.screen = SDL.makeSurface(width, height, flags, true, 'screen');
+
return SDL.screen;
},
@@ -1946,8 +1955,10 @@ var LibrarySDL = {
} else {
var imageData = surfData.ctx.getImageData(0, 0, surfData.width, surfData.height);
if (raw.bpp == 4) {
+ // rgba
imageData.data.set({{{ makeHEAPView('U8', 'raw.data', 'raw.data+raw.size') }}});
} else if (raw.bpp == 3) {
+ // rgb
var pixels = raw.size/3;
var data = imageData.data;
var sourcePtr = raw.data;
@@ -1958,6 +1969,19 @@ var LibrarySDL = {
data[destPtr++] = {{{ makeGetValue('sourcePtr++', 0, 'i8', null, 1) }}};
data[destPtr++] = 255;
}
+ } else if (raw.bpp == 1) {
+ // grayscale
+ var pixels = raw.size;
+ var data = imageData.data;
+ var sourcePtr = raw.data;
+ var destPtr = 0;
+ for (var i = 0; i < pixels; i++) {
+ var value = {{{ makeGetValue('sourcePtr++', 0, 'i8', null, 1) }}};
+ data[destPtr++] = value;
+ data[destPtr++] = value;
+ data[destPtr++] = value;
+ data[destPtr++] = 255;
+ }
} else {
Module.printErr('cannot handle bpp ' + raw.bpp);
return 0;
@@ -2883,7 +2907,12 @@ var LibrarySDL = {
return _emscripten_GetProcAddress(name_);
},
- SDL_GL_SwapBuffers: function() {},
+ SDL_GL_SwapBuffers: function() {
+#if PROXY_TO_WORKER
+ // postMainLoop is where the proxy code listens, to know when to proxy buffered render commands
+ if (Module['postMainLoop']) Module['postMainLoop']();
+#endif
+ },
// SDL 2
diff --git a/src/proxyClient.js b/src/proxyClient.js
index 2d1c76fe..94d8161a 100644
--- a/src/proxyClient.js
+++ b/src/proxyClient.js
@@ -1,8 +1,6 @@
// proxy to/from worker
-Module.ctx = Module.canvas.getContext('2d');
-
// render
var renderFrameData = null;
@@ -24,13 +22,41 @@ window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequest
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame ||
renderFrame;
+/*
+var trueRAF = window.requestAnimationFrame;
+var lastRAF = 0;
+var meanFPS = 0;
+window.requestAnimationFrame = function(func) {
+ trueRAF(function() {
+ var now = performance.now();
+ if (lastRAF > 0) {
+ var diff = now - lastRAF;
+ var fps = 1000/diff;
+ meanFPS = 0.95*meanFPS + 0.05*fps;
+ dump('client fps ' + meanFPS + '\n');
+ }
+ lastRAF = now;
+ func();
+ });
+}
+*/
+
// end render
+// Frame throttling
+
+var frameId = 0;
+
+// Worker
+
var worker = new Worker('{{{ filename }}}.js');
+WebGLClient.prefetch(); // XXX not guaranteed to be before worker main()
+
var workerResponded = false;
worker.onmessage = function worker_onmessage(event) {
+ //dump('\nclient got ' + JSON.stringify(event.data).substr(0, 150) + '\n');
if (!workerResponded) {
workerResponded = true;
if (Module.setStatus) Module.setStatus('');
@@ -52,10 +78,18 @@ worker.onmessage = function worker_onmessage(event) {
}
case 'canvas': {
switch (data.op) {
+ case 'getContext': {
+ Module.ctx = Module.canvas.getContext(data.type, data.attributes);
+ if (data.type !== '2d') {
+ // possible GL_DEBUG entry point: Module.ctx = wrapDebugGL(Module.ctx);
+ Module.glClient = new WebGLClient();
+ }
+ break;
+ }
case 'resize': {
Module.canvas.width = data.width;
Module.canvas.height = data.height;
- Module.canvasData = Module.ctx.getImageData(0, 0, data.width, data.height);
+ if (Module.ctx && Module.ctx.getImageData) Module.canvasData = Module.ctx.getImageData(0, 0, data.width, data.height);
worker.postMessage({ target: 'canvas', boundingClientRect: cloneObject(Module.canvas.getBoundingClientRect()) });
break;
}
@@ -74,6 +108,15 @@ worker.onmessage = function worker_onmessage(event) {
}
break;
}
+ case 'gl': {
+ Module.glClient.onmessage(data);
+ break;
+ }
+ case 'tick': {
+ frameId = data.id;
+ worker.postMessage({ target: 'tock', id: frameId });
+ break;
+ }
default: throw 'what?';
}
};
diff --git a/src/proxyWorker.js b/src/proxyWorker.js
index cfe0c26e..4c397cbb 100644
--- a/src/proxyWorker.js
+++ b/src/proxyWorker.js
@@ -1,3 +1,7 @@
+function PropertyBag() {
+ this.addProperty = function(){};
+ this.removeProperty = function(){};
+};
function EventListener() {
this.listeners = {};
@@ -7,6 +11,14 @@ function EventListener() {
this.listeners[event].push(func);
};
+ this.removeEventListener = function(event, func) {
+ var list = this.listeners[event];
+ if (!list) return;
+ var me = list.indexOf(func);
+ if (me < 0) return;
+ list.splice(me, 1);
+ };
+
this.fireEvent = function fireEvent(event) {
event.preventDefault = function(){};
@@ -26,6 +38,10 @@ window.close = function window_close() {
postMessage({ target: 'window', method: 'close' });
};
+window.scrollX = window.scrollY = 0; // TODO: proxy these
+
+var webGLWorker = new WebGLWorker();
+
var document = new EventListener();
document.createElement = function document_createElement(what) {
@@ -39,28 +55,38 @@ document.createElement = function document_createElement(what) {
height: canvas.height,
data: new Uint8Array(canvas.width*canvas.height*4)
};
- postMessage({ target: 'canvas', op: 'resize', width: canvas.width, height: canvas.height });
+ if (canvas === Module['canvas']) {
+ postMessage({ target: 'canvas', op: 'resize', width: canvas.width, height: canvas.height });
+ }
}
};
- canvas.getContext = function canvas_getContext(type) {
- assert(type == '2d');
- return {
- getImageData: function(x, y, w, h) {
- assert(x == 0 && y == 0 && w == canvas.width && h == canvas.height);
- canvas.ensureData();
- return {
- width: canvas.data.width,
- height: canvas.data.height,
- data: new Uint8Array(canvas.data.data) // TODO: can we avoid this copy?
- };
- },
- putImageData: function(image, x, y) {
- canvas.ensureData();
- assert(x == 0 && y == 0 && image.width == canvas.width && image.height == canvas.height);
- canvas.data.data.set(image.data); // TODO: can we avoid this copy?
- postMessage({ target: 'canvas', op: 'render', image: canvas.data });
- }
- };
+ canvas.getContext = function canvas_getContext(type, attributes) {
+ if (canvas === Module['canvas']) {
+ postMessage({ target: 'canvas', op: 'getContext', type: type, attributes: attributes });
+ }
+ if (type === '2d') {
+ return {
+ getImageData: function(x, y, w, h) {
+ assert(x == 0 && y == 0 && w == canvas.width && h == canvas.height);
+ canvas.ensureData();
+ return {
+ width: canvas.data.width,
+ height: canvas.data.height,
+ data: new Uint8Array(canvas.data.data) // TODO: can we avoid this copy?
+ };
+ },
+ putImageData: function(image, x, y) {
+ canvas.ensureData();
+ assert(x == 0 && y == 0 && image.width == canvas.width && image.height == canvas.height);
+ canvas.data.data.set(image.data); // TODO: can we avoid this copy?
+ if (canvas === Module['canvas']) {
+ postMessage({ target: 'canvas', op: 'render', image: canvas.data });
+ }
+ }
+ };
+ } else {
+ return webGLWorker;
+ }
};
canvas.boundingClientRect = {};
canvas.getBoundingClientRect = function canvas_getBoundingClientRect() {
@@ -73,17 +99,42 @@ document.createElement = function document_createElement(what) {
right: canvas.boundingClientRect.right
};
};
+ canvas.style = new PropertyBag();
+ canvas.exitPointerLock = function(){};
return canvas;
}
default: throw 'document.createElement ' + what;
}
};
+document.documentElement = {};
+
+document.styleSheets = [{
+ cssRules: [], // TODO: forward to client
+ insertRule: function(rule, i) {
+ this.cssRules.splice(i, 0, rule);
+ }
+}];
+
+function Audio() {
+ Runtime.warnOnce('faking Audio elements, no actual sound will play');
+}
+
+Audio.prototype.play = function(){};
+Audio.prototype.pause = function(){};
+
+Audio.prototype.cloneNode = function() {
+ return new Audio;
+}
+
if (typeof console === 'undefined') {
var console = {
log: function(x) {
Module.printErr(x);
- }
+ },
+ error: function(x) {
+ Module.printErr(x);
+ },
};
}
@@ -92,12 +143,33 @@ Module.canvas = document.createElement('canvas');
Module.setStatus = function(){};
Module.print = function Module_print(x) {
+ //dump('OUT: ' + x + '\n');
postMessage({ target: 'stdout', content: x });
};
Module.printErr = function Module_printErr(x) {
+ //dump('ERR: ' + x + '\n');
postMessage({ target: 'stderr', content: x });
};
+// Browser hooks
+
+Browser.resizeListeners.push(function(width, height) {
+ postMessage({ target: 'canvas', op: 'resize', width: width, height: height });
+});
+
+// Frame throttling
+
+var frameId = 0;
+var clientFrameId = 0;
+
+var postMainLoop = Module['postMainLoop'];
+Module['postMainLoop'] = function() {
+ if (postMainLoop) postMainLoop();
+ // frame complete, send a frame id
+ postMessage({ target: 'tick', id: frameId++ });
+ commandBuffer = [];
+};
+
// buffer messages until the program starts to run
var messageBuffer = null;
@@ -122,6 +194,7 @@ onmessage = function onmessage(message) {
}
messageBuffer.push(message);
}
+ //dump('worker got ' + JSON.stringify(message.data) + '\n');
switch (message.data.target) {
case 'document': {
document.fireEvent(message.data.event);
@@ -139,6 +212,14 @@ onmessage = function onmessage(message) {
} else throw 'ey?';
break;
}
+ case 'gl': {
+ webGLWorker.onmessage(message.data);
+ break;
+ }
+ case 'tock': {
+ clientFrameId = message.data.id;
+ break;
+ }
default: throw 'wha? ' + message.data.target;
}
};
diff --git a/src/settings.js b/src/settings.js
index 26883051..14077efd 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -422,9 +422,14 @@ var RUNTIME_LINKED_LIBS = []; // If this is a main file (BUILD_AS_SHARED_LIB ==
// linked libraries can break things.
var BUILD_AS_WORKER = 0; // If set to 1, this is a worker library, a special kind of library
// that is run in a worker. See emscripten.h
+
var PROXY_TO_WORKER = 0; // If set to 1, we build the project into a js file that will run
// in a worker, and generate an html file that proxies input and
// output to/from it.
+var PROXY_TO_WORKER_FILENAME = ''; // If set, the script file name the main thread loads.
+ // Useful if your project doesn't run the main emscripten-
+ // generated script immediately but does some setup before
+
var LINKABLE = 0; // If set to 1, this file can be linked with others, either as a shared
// library or as the main file that calls a shared library. To enable that,
// we will not internalize all symbols and cull the unused ones, in other
diff --git a/src/webGLClient.js b/src/webGLClient.js
new file mode 100644
index 00000000..397cfa8c
--- /dev/null
+++ b/src/webGLClient.js
@@ -0,0 +1,234 @@
+// WebGLWorker client code
+
+function assert(x) {
+ if (!x) throw 'failed assert';
+}
+
+function WebGLClient() {
+ var objects = {};
+
+ var ctx = null;
+ var buffer = null;
+ var i = 0;
+
+ function func0(name) {
+ ctx[name]();
+ }
+ function func1(name) {
+ ctx[name](buffer[i]);
+ i++;
+ }
+ function func2(name) {
+ ctx[name](buffer[i], buffer[i+1]);
+ i += 2;
+ }
+ function func3(name) {
+ ctx[name](buffer[i], buffer[i+1], buffer[i+2]);
+ i += 3;
+ }
+ function func4(name) {
+ ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3]);
+ i += 4;
+ }
+ function func5(name) {
+ ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4]);
+ i += 5;
+ }
+ function func6(name) {
+ ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4], buffer[i+5]);
+ i += 6;
+ }
+ function func7(name) {
+ ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4], buffer[i+5], buffer[i+6]);
+ i += 7;
+ }
+ function func9(name) {
+ ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3], buffer[i+4], buffer[i+5], buffer[i+6], buffer[i+7], buffer[i+8]);
+ i += 9;
+ }
+
+ // lookuppers, convert integer ids to cached objects for some args
+ function func1L0(name) {
+ ctx[name](objects[buffer[i]]);
+ i++;
+ }
+ function func2L0(name) {
+ ctx[name](objects[buffer[i]], buffer[i+1]);
+ i += 2;
+ }
+ function func2L0L1(name) {
+ ctx[name](objects[buffer[i]], objects[buffer[i+1]]);
+ i += 2;
+ }
+ function func2L1_(name) {
+ ctx[name](buffer[i], buffer[i+1] ? objects[buffer[i+1]] : null);
+ i += 2;
+ }
+ function func3L0(name) {
+ ctx[name](objects[buffer[i]], buffer[i+1], buffer[i+2]);
+ i += 3;
+ }
+ function func4L3_(name) {
+ ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3] ? objects[buffer[i+3]] : null);
+ i += 4;
+ }
+ function func5L3_(name) {
+ ctx[name](buffer[i], buffer[i+1], buffer[i+2], buffer[i+3] ? objects[buffer[i+3]] : null, buffer[i+4]);
+ i += 5;
+ }
+
+ // constructors, last argument is the id to save as
+ function funcC0(name) {
+ var object = ctx[name]();
+ var id = buffer[i++];
+ objects[id] = object;
+ }
+ function funcC1(name) {
+ var object = ctx[name](buffer[i++]);
+ var id = buffer[i++];
+ objects[id] = object;
+ }
+ function funcC2(name) {
+ var object = ctx[name](buffer[i++], buffer[i++]);
+ var id = buffer[i++];
+ objects[id] = object;
+ }
+ function funcC2L0(name) {
+ var object = ctx[name](objects[buffer[i++]], buffer[i++]);
+ var id = buffer[i++];
+ objects[id] = object;
+ }
+
+ // destructors, stop holding on to the object in the cache
+ function funcD0(name) {
+ var id = buffer[i++];
+ var object = objects[id];
+ objects[id] = null;
+ ctx[name](object);
+ }
+
+ var calls = {
+ 0: { name: 'NULL', func: func0 },
+ 1: { name: 'getExtension', func: func1 },
+ 2: { name: 'enable', func: func1 },
+ 3: { name: 'disable', func: func1 },
+ 4: { name: 'clear', func: func1 },
+ 5: { name: 'clearColor', func: func4 },
+ 6: { name: 'createShader', func: funcC1 },
+ 7: { name: 'deleteShader', func: funcD0 },
+ 8: { name: 'shaderSource', func: func2L0 },
+ 9: { name: 'compileShader', func: func1L0 },
+ 10: { name: 'createProgram', func: funcC0 },
+ 11: { name: 'deleteProgram', func: funcD0 },
+ 12: { name: 'attachShader', func: func2L0L1 },
+ 13: { name: 'bindAttribLocation', func: func3L0 },
+ 14: { name: 'linkProgram', func: func1L0 },
+ 15: { name: 'getProgramParameter', func: function() { assert(ctx.getProgramParameter(objects[buffer[i++]], buffer[i++]), 'we cannot handle errors, we are async proxied WebGL'); } },
+ 16: { name: 'getUniformLocation', func: funcC2L0 },
+ 17: { name: 'useProgram', func: func1L0 },
+ 18: { name: 'uniform1i', func: func2L0 },
+ 19: { name: 'uniform1f', func: func2L0 },
+ 20: { name: 'uniform3fv', func: func2L0 },
+ 21: { name: 'uniform4fv', func: func2L0 },
+ 22: { name: 'uniformMatrix4fv', func: func3L0 },
+ 23: { name: 'vertexAttrib4fv', func: func2 },
+ 24: { name: 'createBuffer', func: funcC0 },
+ 25: { name: 'deleteBuffer', func: funcD0 },
+ 26: { name: 'bindBuffer', func: func2L1_ },
+ 27: { name: 'bufferData', func: func3 },
+ 28: { name: 'bufferSubData', func: func3 },
+ 29: { name: 'viewport', func: func4 },
+ 30: { name: 'vertexAttribPointer', func: func6 },
+ 31: { name: 'enableVertexAttribArray', func: func1 },
+ 32: { name: 'disableVertexAttribArray', func: func1 },
+ 33: { name: 'drawArrays', func: func3 },
+ 34: { name: 'drawElements', func: func4 },
+ 35: { name: 'getError', func: function() { assert(ctx.getError() === ctx.NO_ERROR, 'we cannot handle errors, we are async proxied WebGL') } },
+ 36: { name: 'createTexture', func: funcC0 },
+ 37: { name: 'deleteTexture', func: funcD0 },
+ 38: { name: 'bindTexture', func: func2L1_ },
+ 39: { name: 'texParameteri', func: func3 },
+ 40: { name: 'texImage2D', func: func9 },
+ 41: { name: 'compressedTexImage2D', func: func7 },
+ 42: { name: 'activeTexture', func: func1 },
+ 43: { name: 'getShaderParameter', func: function() { assert(ctx.getShaderParameter(objects[buffer[i++]], buffer[i++]), 'we cannot handle errors, we are async proxied WebGL'); } },
+ 44: { name: 'clearDepth', func: func1 },
+ 45: { name: 'depthFunc', func: func1 },
+ 46: { name: 'frontFace', func: func1 },
+ 47: { name: 'cullFace', func: func1 },
+ 48: { name: 'pixelStorei', func: func2 },
+ 49: { name: 'depthMask', func: func1 },
+ 50: { name: 'depthRange', func: func2 },
+ 51: { name: 'blendFunc', func: func2 },
+ 52: { name: 'scissor', func: func4 },
+ 53: { name: 'colorMask', func: func4 },
+ 54: { name: 'lineWidth', func: func1 },
+ 55: { name: 'createFramebuffer', func: funcC0 },
+ 56: { name: 'deleteFramebuffer', func: funcD0 },
+ 57: { name: 'bindFramebuffer', func: func2L1_ },
+ 58: { name: 'framebufferTexture2D', func: func5L3_ },
+ 59: { name: 'createRenderbuffer', func: funcC0 },
+ 60: { name: 'deleteRenderbuffer', func: funcD0 },
+ 61: { name: 'bindRenderbuffer', func: func2L1_ },
+ 62: { name: 'renderbufferStorage', func: func4 },
+ 63: { name: 'framebufferRenderbuffer', func: func4L3_ },
+ 64: { name: 'debugPrint', func: func1 },
+ };
+
+ function renderCommands(buf) {
+ ctx = Module.ctx;
+ i = 0;
+ buffer = buf;
+ var len = buffer.length;
+ //dump('issuing commands, buffer len: ' + len + '\n');
+ while (i < len) {
+ var info = calls[buffer[i++]];
+ var name = info.name;
+ info.func(name);
+ //var err;
+ //while ((err = ctx.getError()) !== ctx.NO_ERROR) {
+ // dump('warning: GL error ' + err + ', after ' + [command, numArgs] + '\n');
+ //}
+ assert(i <= len);
+ }
+ }
+
+ var commandBuffers = [];
+
+ function renderAllCommands() {
+ // TODO: we can avoid running commands from buffers that are not the last, if they
+ // have no side effects, as each buffer is from a different frame
+ //if (commandBuffers.length > 1) dump('extra buffs: ' + (commandBuffers.length-1) + '\n');
+ for (var i = 0; i < commandBuffers.length; i++) {
+ renderCommands(commandBuffers[i]);
+ }
+ commandBuffers.length = 0;
+ }
+
+ this.onmessage = function(msg) {
+ //dump('client GL got ' + JSON.stringify(msg) + '\n');
+ switch(msg.op) {
+ case 'render': {
+ if (commandBuffers.length === 0) {
+ // requestion a new frame, we will clear the buffers after rendering them
+ window.requestAnimationFrame(renderAllCommands);
+ }
+ commandBuffers.push(msg.commandBuffer);
+ break;
+ }
+ default: throw 'weird gl onmessage ' + JSON.stringify(msg);
+ }
+ };
+}
+
+WebGLClient.prefetch = function() {
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('webgl-experimental') || canvas.getContext('webgl');
+ if (!ctx) return;
+ var parameters = {};
+ ['MAX_VERTEX_ATTRIBS', 'MAX_TEXTURE_IMAGE_UNITS', 'MAX_TEXTURE_SIZE', 'MAX_CUBE_MAP_TEXTURE_SIZE', 'MAX_VERTEX_UNIFORM_VECTORS', 'MAX_FRAGMENT_UNIFORM_VECTORS', 'MAX_VARYING_VECTORS', 'MAX_COMBINED_TEXTURE_IMAGE_UNITS', 'VENDOR', 'RENDERER', 'VERSION'].forEach(function(name) {
+ parameters[ctx[name]] = ctx.getParameter(ctx[name]);
+ });
+ worker.postMessage({ target: 'gl', op: 'setPrefetched', parameters: parameters, extensions: ctx.getSupportedExtensions() });
+};
+
diff --git a/src/webGLWorker.js b/src/webGLWorker.js
new file mode 100644
index 00000000..90a1bed9
--- /dev/null
+++ b/src/webGLWorker.js
@@ -0,0 +1,910 @@
+// WebGLWorker worker code
+
+function WebGLBuffer(id) {
+ this.what = 'buffer';
+ this.id = id;
+}
+function WebGLProgram(id) {
+ this.what = 'program';
+ this.id = id;
+ this.shaders = [];
+ this.attributes = {};
+ this.attributeVec = [];
+}
+function WebGLFramebuffer(id) {
+ this.what = 'frameBuffer';
+ this.id = id;
+}
+function WebGLRenderbuffer(id) {
+ this.what = 'renderBuffer';
+ this.id = id;
+}
+function WebGLTexture(id) {
+ this.what = 'texture';
+ this.id = id;
+ this.binding = 0;
+}
+
+function WebGLWorker() {
+ //=======
+ // State
+ //=======
+
+ var commandBuffer = [];
+
+ var nextId = 1; // valid ids are > 0
+
+ var bindings = {
+ texture2D: null,
+ arrayBuffer: null,
+ elementArrayBuffer: null,
+ program: null
+ };
+
+ //===========
+ // Constants
+ //===========
+
+ /* ClearBufferMask */
+ this.DEPTH_BUFFER_BIT = 0x00000100;
+ this.STENCIL_BUFFER_BIT = 0x00000400;
+ this.COLOR_BUFFER_BIT = 0x00004000;
+
+ /* BeginMode */
+ this.POINTS = 0x0000;
+ this.LINES = 0x0001;
+ this.LINE_LOOP = 0x0002;
+ this.LINE_STRIP = 0x0003;
+ this.TRIANGLES = 0x0004;
+ this.TRIANGLE_STRIP = 0x0005;
+ this.TRIANGLE_FAN = 0x0006;
+
+ /* AlphaFunction (not supported in ES20) */
+ /* NEVER */
+ /* LESS */
+ /* EQUAL */
+ /* LEQUAL */
+ /* GREATER */
+ /* NOTEQUAL */
+ /* GEQUAL */
+ /* ALWAYS */
+
+ /* BlendingFactorDest */
+ this.ZERO = 0;
+ this.ONE = 1;
+ this.SRC_COLOR = 0x0300;
+ this.ONE_MINUS_SRC_COLOR = 0x0301;
+ this.SRC_ALPHA = 0x0302;
+ this.ONE_MINUS_SRC_ALPHA = 0x0303;
+ this.DST_ALPHA = 0x0304;
+ this.ONE_MINUS_DST_ALPHA = 0x0305;
+
+ /* BlendingFactorSrc */
+ /* ZERO */
+ /* ONE */
+ this.DST_COLOR = 0x0306;
+ this.ONE_MINUS_DST_COLOR = 0x0307;
+ this.SRC_ALPHA_SATURATE = 0x0308;
+ /* SRC_ALPHA */
+ /* ONE_MINUS_SRC_ALPHA */
+ /* DST_ALPHA */
+ /* ONE_MINUS_DST_ALPHA */
+
+ /* BlendEquationSeparate */
+ this.FUNC_ADD = 0x8006;
+ this.BLEND_EQUATION = 0x8009;
+ this.BLEND_EQUATION_RGB = 0x8009; /* same as BLEND_EQUATION */
+ this.BLEND_EQUATION_ALPHA = 0x883D;
+
+ /* BlendSubtract */
+ this.FUNC_SUBTRACT = 0x800A;
+ this.FUNC_REVERSE_SUBTRACT = 0x800B;
+
+ /* Separate Blend Functions */
+ this.BLEND_DST_RGB = 0x80C8;
+ this.BLEND_SRC_RGB = 0x80C9;
+ this.BLEND_DST_ALPHA = 0x80CA;
+ this.BLEND_SRC_ALPHA = 0x80CB;
+ this.CONSTANT_COLOR = 0x8001;
+ this.ONE_MINUS_CONSTANT_COLOR = 0x8002;
+ this.CONSTANT_ALPHA = 0x8003;
+ this.ONE_MINUS_CONSTANT_ALPHA = 0x8004;
+ this.BLEND_COLOR = 0x8005;
+
+ /* Buffer Objects */
+ this.ARRAY_BUFFER = 0x8892;
+ this.ELEMENT_ARRAY_BUFFER = 0x8893;
+ this.ARRAY_BUFFER_BINDING = 0x8894;
+ this.ELEMENT_ARRAY_BUFFER_BINDING = 0x8895;
+
+ this.STREAM_DRAW = 0x88E0;
+ this.STATIC_DRAW = 0x88E4;
+ this.DYNAMIC_DRAW = 0x88E8;
+
+ this.BUFFER_SIZE = 0x8764;
+ this.BUFFER_USAGE = 0x8765;
+
+ this.CURRENT_VERTEX_ATTRIB = 0x8626;
+
+ /* CullFaceMode */
+ this.FRONT = 0x0404;
+ this.BACK = 0x0405;
+ this.FRONT_AND_BACK = 0x0408;
+
+ /* DepthFunction */
+ /* NEVER */
+ /* LESS */
+ /* EQUAL */
+ /* LEQUAL */
+ /* GREATER */
+ /* NOTEQUAL */
+ /* GEQUAL */
+ /* ALWAYS */
+
+ /* EnableCap */
+ /* TEXTURE_2D */
+ this.CULL_FACE = 0x0B44;
+ this.BLEND = 0x0BE2;
+ this.DITHER = 0x0BD0;
+ this.STENCIL_TEST = 0x0B90;
+ this.DEPTH_TEST = 0x0B71;
+ this.SCISSOR_TEST = 0x0C11;
+ this.POLYGON_OFFSET_FILL = 0x8037;
+ this.SAMPLE_ALPHA_TO_COVERAGE = 0x809E;
+ this.SAMPLE_COVERAGE = 0x80A0;
+
+ /* ErrorCode */
+ this.NO_ERROR = 0;
+ this.INVALID_ENUM = 0x0500;
+ this.INVALID_VALUE = 0x0501;
+ this.INVALID_OPERATION = 0x0502;
+ this.OUT_OF_MEMORY = 0x0505;
+
+ /* FrontFaceDirection */
+ this.CW = 0x0900;
+ this.CCW = 0x0901;
+
+ /* GetPName */
+ this.LINE_WIDTH = 0x0B21;
+ this.ALIASED_POINT_SIZE_RANGE = 0x846D;
+ this.ALIASED_LINE_WIDTH_RANGE = 0x846E;
+ this.CULL_FACE_MODE = 0x0B45;
+ this.FRONT_FACE = 0x0B46;
+ this.DEPTH_RANGE = 0x0B70;
+ this.DEPTH_WRITEMASK = 0x0B72;
+ this.DEPTH_CLEAR_VALUE = 0x0B73;
+ this.DEPTH_FUNC = 0x0B74;
+ this.STENCIL_CLEAR_VALUE = 0x0B91;
+ this.STENCIL_FUNC = 0x0B92;
+ this.STENCIL_FAIL = 0x0B94;
+ this.STENCIL_PASS_DEPTH_FAIL = 0x0B95;
+ this.STENCIL_PASS_DEPTH_PASS = 0x0B96;
+ this.STENCIL_REF = 0x0B97;
+ this.STENCIL_VALUE_MASK = 0x0B93;
+ this.STENCIL_WRITEMASK = 0x0B98;
+ this.STENCIL_BACK_FUNC = 0x8800;
+ this.STENCIL_BACK_FAIL = 0x8801;
+ this.STENCIL_BACK_PASS_DEPTH_FAIL = 0x8802;
+ this.STENCIL_BACK_PASS_DEPTH_PASS = 0x8803;
+ this.STENCIL_BACK_REF = 0x8CA3;
+ this.STENCIL_BACK_VALUE_MASK = 0x8CA4;
+ this.STENCIL_BACK_WRITEMASK = 0x8CA5;
+ this.VIEWPORT = 0x0BA2;
+ this.SCISSOR_BOX = 0x0C10;
+ /* SCISSOR_TEST */
+ this.COLOR_CLEAR_VALUE = 0x0C22;
+ this.COLOR_WRITEMASK = 0x0C23;
+ this.UNPACK_ALIGNMENT = 0x0CF5;
+ this.PACK_ALIGNMENT = 0x0D05;
+ this.MAX_TEXTURE_SIZE = 0x0D33;
+ this.MAX_VIEWPORT_DIMS = 0x0D3A;
+ this.SUBPIXEL_BITS = 0x0D50;
+ this.RED_BITS = 0x0D52;
+ this.GREEN_BITS = 0x0D53;
+ this.BLUE_BITS = 0x0D54;
+ this.ALPHA_BITS = 0x0D55;
+ this.DEPTH_BITS = 0x0D56;
+ this.STENCIL_BITS = 0x0D57;
+ this.POLYGON_OFFSET_UNITS = 0x2A00;
+ /* POLYGON_OFFSET_FILL */
+ this.POLYGON_OFFSET_FACTOR = 0x8038;
+ this.TEXTURE_BINDING_2D = 0x8069;
+ this.SAMPLE_BUFFERS = 0x80A8;
+ this.SAMPLES = 0x80A9;
+ this.SAMPLE_COVERAGE_VALUE = 0x80AA;
+ this.SAMPLE_COVERAGE_INVERT = 0x80AB;
+
+ /* GetTextureParameter */
+ /* TEXTURE_MAG_FILTER */
+ /* TEXTURE_MIN_FILTER */
+ /* TEXTURE_WRAP_S */
+ /* TEXTURE_WRAP_T */
+
+ this.COMPRESSED_TEXTURE_FORMATS = 0x86A3;
+
+ /* HintMode */
+ this.DONT_CARE = 0x1100;
+ this.FASTEST = 0x1101;
+ this.NICEST = 0x1102;
+
+ /* HintTarget */
+ this.GENERATE_MIPMAP_HINT = 0x8192;
+
+ /* DataType */
+ this.BYTE = 0x1400;
+ this.UNSIGNED_BYTE = 0x1401;
+ this.SHORT = 0x1402;
+ this.UNSIGNED_SHORT = 0x1403;
+ this.INT = 0x1404;
+ this.UNSIGNED_INT = 0x1405;
+ this.FLOAT = 0x1406;
+
+ /* PixelFormat */
+ this.DEPTH_COMPONENT = 0x1902;
+ this.ALPHA = 0x1906;
+ this.RGB = 0x1907;
+ this.RGBA = 0x1908;
+ this.LUMINANCE = 0x1909;
+ this.LUMINANCE_ALPHA = 0x190A;
+
+ /* PixelType */
+ /* UNSIGNED_BYTE */
+ this.UNSIGNED_SHORT_4_4_4_4 = 0x8033;
+ this.UNSIGNED_SHORT_5_5_5_1 = 0x8034;
+ this.UNSIGNED_SHORT_5_6_5 = 0x8363;
+
+ /* Shaders */
+ this.FRAGMENT_SHADER = 0x8B30;
+ this.VERTEX_SHADER = 0x8B31;
+ this.MAX_VERTEX_ATTRIBS = 0x8869;
+ this.MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB;
+ this.MAX_VARYING_VECTORS = 0x8DFC;
+ this.MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0x8B4D;
+ this.MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C;
+ this.MAX_TEXTURE_IMAGE_UNITS = 0x8872;
+ this.MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD;
+ this.SHADER_TYPE = 0x8B4F;
+ this.DELETE_STATUS = 0x8B80;
+ this.LINK_STATUS = 0x8B82;
+ this.VALIDATE_STATUS = 0x8B83;
+ this.ATTACHED_SHADERS = 0x8B85;
+ this.ACTIVE_UNIFORMS = 0x8B86;
+ this.ACTIVE_ATTRIBUTES = 0x8B89;
+ this.SHADING_LANGUAGE_VERSION = 0x8B8C;
+ this.CURRENT_PROGRAM = 0x8B8D;
+
+ /* StencilFunction */
+ this.NEVER = 0x0200;
+ this.LESS = 0x0201;
+ this.EQUAL = 0x0202;
+ this.LEQUAL = 0x0203;
+ this.GREATER = 0x0204;
+ this.NOTEQUAL = 0x0205;
+ this.GEQUAL = 0x0206;
+ this.ALWAYS = 0x0207;
+
+ /* StencilOp */
+ /* ZERO */
+ this.KEEP = 0x1E00;
+ this.REPLACE = 0x1E01;
+ this.INCR = 0x1E02;
+ this.DECR = 0x1E03;
+ this.INVERT = 0x150A;
+ this.INCR_WRAP = 0x8507;
+ this.DECR_WRAP = 0x8508;
+
+ /* StringName */
+ this.VENDOR = 0x1F00;
+ this.RENDERER = 0x1F01;
+ this.VERSION = 0x1F02;
+
+ /* TextureMagFilter */
+ this.NEAREST = 0x2600;
+ this.LINEAR = 0x2601;
+
+ /* TextureMinFilter */
+ /* NEAREST */
+ /* LINEAR */
+ this.NEAREST_MIPMAP_NEAREST = 0x2700;
+ this.LINEAR_MIPMAP_NEAREST = 0x2701;
+ this.NEAREST_MIPMAP_LINEAR = 0x2702;
+ this.LINEAR_MIPMAP_LINEAR = 0x2703;
+
+ /* TextureParameterName */
+ this.TEXTURE_MAG_FILTER = 0x2800;
+ this.TEXTURE_MIN_FILTER = 0x2801;
+ this.TEXTURE_WRAP_S = 0x2802;
+ this.TEXTURE_WRAP_T = 0x2803;
+
+ /* TextureTarget */
+ this.TEXTURE_2D = 0x0DE1;
+ this.TEXTURE = 0x1702;
+
+ this.TEXTURE_CUBE_MAP = 0x8513;
+ this.TEXTURE_BINDING_CUBE_MAP = 0x8514;
+ this.TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515;
+ this.TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516;
+ this.TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517;
+ this.TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518;
+ this.TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519;
+ this.TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A;
+ this.MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C;
+
+ /* TextureUnit */
+ this.TEXTURE0 = 0x84C0;
+ this.TEXTURE1 = 0x84C1;
+ this.TEXTURE2 = 0x84C2;
+ this.TEXTURE3 = 0x84C3;
+ this.TEXTURE4 = 0x84C4;
+ this.TEXTURE5 = 0x84C5;
+ this.TEXTURE6 = 0x84C6;
+ this.TEXTURE7 = 0x84C7;
+ this.TEXTURE8 = 0x84C8;
+ this.TEXTURE9 = 0x84C9;
+ this.TEXTURE10 = 0x84CA;
+ this.TEXTURE11 = 0x84CB;
+ this.TEXTURE12 = 0x84CC;
+ this.TEXTURE13 = 0x84CD;
+ this.TEXTURE14 = 0x84CE;
+ this.TEXTURE15 = 0x84CF;
+ this.TEXTURE16 = 0x84D0;
+ this.TEXTURE17 = 0x84D1;
+ this.TEXTURE18 = 0x84D2;
+ this.TEXTURE19 = 0x84D3;
+ this.TEXTURE20 = 0x84D4;
+ this.TEXTURE21 = 0x84D5;
+ this.TEXTURE22 = 0x84D6;
+ this.TEXTURE23 = 0x84D7;
+ this.TEXTURE24 = 0x84D8;
+ this.TEXTURE25 = 0x84D9;
+ this.TEXTURE26 = 0x84DA;
+ this.TEXTURE27 = 0x84DB;
+ this.TEXTURE28 = 0x84DC;
+ this.TEXTURE29 = 0x84DD;
+ this.TEXTURE30 = 0x84DE;
+ this.TEXTURE31 = 0x84DF;
+ this.ACTIVE_TEXTURE = 0x84E0;
+
+ /* TextureWrapMode */
+ this.REPEAT = 0x2901;
+ this.CLAMP_TO_EDGE = 0x812F;
+ this.MIRRORED_REPEAT = 0x8370;
+
+ /* Uniform Types */
+ this.FLOAT_VEC2 = 0x8B50;
+ this.FLOAT_VEC3 = 0x8B51;
+ this.FLOAT_VEC4 = 0x8B52;
+ this.INT_VEC2 = 0x8B53;
+ this.INT_VEC3 = 0x8B54;
+ this.INT_VEC4 = 0x8B55;
+ this.BOOL = 0x8B56;
+ this.BOOL_VEC2 = 0x8B57;
+ this.BOOL_VEC3 = 0x8B58;
+ this.BOOL_VEC4 = 0x8B59;
+ this.FLOAT_MAT2 = 0x8B5A;
+ this.FLOAT_MAT3 = 0x8B5B;
+ this.FLOAT_MAT4 = 0x8B5C;
+ this.SAMPLER_2D = 0x8B5E;
+ this.SAMPLER_CUBE = 0x8B60;
+
+ /* Vertex Arrays */
+ this.VERTEX_ATTRIB_ARRAY_ENABLED = 0x8622;
+ this.VERTEX_ATTRIB_ARRAY_SIZE = 0x8623;
+ this.VERTEX_ATTRIB_ARRAY_STRIDE = 0x8624;
+ this.VERTEX_ATTRIB_ARRAY_TYPE = 0x8625;
+ this.VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A;
+ this.VERTEX_ATTRIB_ARRAY_POINTER = 0x8645;
+ this.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F;
+
+ /* Read Format */
+ this.IMPLEMENTATION_COLOR_READ_TYPE = 0x8B9A;
+ this.IMPLEMENTATION_COLOR_READ_FORMAT = 0x8B9B;
+
+ /* Shader Source */
+ this.COMPILE_STATUS = 0x8B81;
+
+ /* Shader Precision-Specified Types */
+ this.LOW_FLOAT = 0x8DF0;
+ this.MEDIUM_FLOAT = 0x8DF1;
+ this.HIGH_FLOAT = 0x8DF2;
+ this.LOW_INT = 0x8DF3;
+ this.MEDIUM_INT = 0x8DF4;
+ this.HIGH_INT = 0x8DF5;
+
+ /* Framebuffer Object. */
+ this.FRAMEBUFFER = 0x8D40;
+ this.RENDERBUFFER = 0x8D41;
+
+ this.RGBA4 = 0x8056;
+ this.RGB5_A1 = 0x8057;
+ this.RGB565 = 0x8D62;
+ this.DEPTH_COMPONENT16 = 0x81A5;
+ this.STENCIL_INDEX = 0x1901;
+ this.STENCIL_INDEX8 = 0x8D48;
+ this.DEPTH_STENCIL = 0x84F9;
+
+ this.RENDERBUFFER_WIDTH = 0x8D42;
+ this.RENDERBUFFER_HEIGHT = 0x8D43;
+ this.RENDERBUFFER_INTERNAL_FORMAT = 0x8D44;
+ this.RENDERBUFFER_RED_SIZE = 0x8D50;
+ this.RENDERBUFFER_GREEN_SIZE = 0x8D51;
+ this.RENDERBUFFER_BLUE_SIZE = 0x8D52;
+ this.RENDERBUFFER_ALPHA_SIZE = 0x8D53;
+ this.RENDERBUFFER_DEPTH_SIZE = 0x8D54;
+ this.RENDERBUFFER_STENCIL_SIZE = 0x8D55;
+
+ this.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = 0x8CD0;
+ this.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = 0x8CD1;
+ this.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = 0x8CD2;
+ this.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = 0x8CD3;
+
+ this.COLOR_ATTACHMENT0 = 0x8CE0;
+ this.DEPTH_ATTACHMENT = 0x8D00;
+ this.STENCIL_ATTACHMENT = 0x8D20;
+ this.DEPTH_STENCIL_ATTACHMENT = 0x821A;
+
+ this.NONE = 0;
+
+ this.FRAMEBUFFER_COMPLETE = 0x8CD5;
+ this.FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6;
+ this.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7;
+ this.FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9;
+ this.FRAMEBUFFER_UNSUPPORTED = 0x8CDD;
+
+ this.FRAMEBUFFER_BINDING = 0x8CA6;
+ this.RENDERBUFFER_BINDING = 0x8CA7;
+ this.MAX_RENDERBUFFER_SIZE = 0x84E8;
+
+ this.INVALID_FRAMEBUFFER_OPERATION = 0x0506;
+
+ /* WebGL-specific enums */
+ this.UNPACK_FLIP_Y_WEBGL = 0x9240;
+ this.UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241;
+ this.CONTEXT_LOST_WEBGL = 0x9242;
+ this.UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243;
+ this.BROWSER_DEFAULT_WEBGL = 0x9244;
+
+ //==========
+ // Functions
+ //==========
+
+ var that = this;
+
+ // Helpers
+
+ this.onmessage = function(msg) {
+ //dump('worker GL got ' + JSON.stringify(msg) + '\n');
+ switch(msg.op) {
+ case 'setPrefetched': {
+ WebGLWorker.prototype.prefetchedParameters = msg.parameters;
+ WebGLWorker.prototype.prefetchedExtensions = msg.extensions;
+ break;
+ }
+ default: throw 'weird gl onmessage ' + JSON.stringify(msg);
+ }
+ };
+
+ function revname(name) {
+ for (var x in that) if (that[x] === name) return x;
+ return null;
+ }
+
+ // GL
+
+ this.getParameter = function(name) {
+ assert(name);
+ if (name in this.prefetchedParameters) return this.prefetchedParameters[name];
+ switch (name) {
+ case this.TEXTURE_BINDING_2D: {
+ return bindings.texture2D;
+ }
+ case this.ARRAY_BUFFER_BINDING: {
+ return bindings.arrayBuffer;
+ }
+ case this.ELEMENT_ARRAY_BUFFER_BINDING: {
+ return bindings.elementArrayBuffer;
+ }
+ case this.CURRENT_PROGRAM: {
+ return bindings.program;
+ }
+ default: throw 'TODO: get parameter ' + name + ' : ' + revname(name);
+ }
+ };
+ this.getExtension = function(name) {
+ var i = this.prefetchedExtensions.indexOf(name);
+ if (i < 0) return null;
+ commandBuffer.push(1, name);
+ return true; // TODO: return an object here
+ };
+ this.getSupportedExtensions = function() {
+ return this.prefetchedExtensions;
+ };
+ this.enable = function(cap) {
+ commandBuffer.push(2, cap);
+ };
+ this.disable = function(cap) {
+ commandBuffer.push(3, cap);
+ };
+ this.clear = function(mask) {
+ commandBuffer.push(4, mask);
+ };
+ this.clearColor = function(r, g, b, a) {
+ commandBuffer.push(5, r, g, b, a);
+ };
+ this.createShader = function(type) {
+ var id = nextId++;
+ commandBuffer.push(6, type, id);
+ return { id: id, what: 'shader', type: type };
+ };
+ this.deleteShader = function(shader) {
+ if (!shader) return;
+ commandBuffer.push(7, shader.id);
+ };
+ this.shaderSource = function(shader, source) {
+ shader.source = source;
+ commandBuffer.push(8, shader.id, source);
+ };
+ this.compileShader = function(shader) {
+ commandBuffer.push(9, shader.id);
+ };
+ this.getShaderInfoLog = function(shader) {
+ return ''; // optimistic assumption of success; no proxying
+ };
+ this.createProgram = function() {
+ var id = nextId++;
+ commandBuffer.push(10, id);
+ return new WebGLProgram(id);
+ };
+ this.deleteProgram = function(program) {
+ if (!program) return;
+ commandBuffer.push(11, program.id);
+ };
+ this.attachShader = function(program, shader) {
+ program.shaders.push(shader);
+ commandBuffer.push(12, program.id, shader.id);
+ };
+ this.bindAttribLocation = function(program, index, name) {
+ program.attributes[name] = { what: 'attribute', name: name, size: -1, location: index }; // fill in size later
+ program.attributeVec[index] = name;
+ commandBuffer.push(13, program.id, index, name);
+ };
+ this.getAttribLocation = function(program, name) {
+ // all existing attribs are cached locally
+ if (name in program.attributes) return program.attributes[name].location;
+ return -1;
+ };
+ this.linkProgram = function(program) {
+ // parse shader sources
+ function parseElementType(shader, type, obj, vec) {
+ var source = shader.source;
+ source = source.replace(/\n/g, '|\n'); // barrier between lines, to make regexing easier
+ var newItems = source.match(new RegExp(type + '\\s+\\w+\\s+[\\w,\\s\[\\]]+;', 'g'));
+ if (!newItems) return;
+ newItems.forEach(function(item) {
+ var m = new RegExp(type + '\\s+\\w+\\s+([\\w,\\s\[\\]]+);').exec(item);
+ assert(m);
+ m[1].split(',').map(function(name) { name = name.trim(); return name.search(/\s/) >= 0 ? '' : name }).filter(function(name) { return !!name }).forEach(function(name) {
+ var size = 1;
+ var open = name.indexOf('[');
+ var fullname = name;
+ if (open >= 0) {
+ var close = name.indexOf(']');
+ size = parseInt(name.substring(open+1, close));
+ name = name.substr(0, open);
+ fullname = name + '[0]';
+ }
+ if (!obj[name]) {
+ obj[name] = { what: type, name: fullname, size: size, location: -1 };
+ if (vec) vec.push(name);
+ }
+ });
+ });
+ }
+
+ program.uniforms = {};
+ program.uniformVec = [];
+
+ var existingAttributes = {};
+
+ program.shaders.forEach(function(shader) {
+ parseElementType(shader, 'uniform', program.uniforms, program.uniformVec);
+ parseElementType(shader, 'attribute', existingAttributes, null);
+ });
+
+ // bind not-yet bound attributes
+ for (var attr in existingAttributes) {
+ if (!(attr in program.attributes)) {
+ var index = program.attributeVec.length;
+ this.bindAttribLocation(program, index, attr);
+ }
+ program.attributes[attr].size = existingAttributes[attr].size;
+ }
+
+ commandBuffer.push(14, program.id);
+ };
+ this.getProgramParameter = function(program, name) {
+ switch (name) {
+ case this.ACTIVE_UNIFORMS: return program.uniformVec.length;
+ case this.ACTIVE_ATTRIBUTES: return program.attributeVec.length;
+ case this.LINK_STATUS: {
+ // optimisticaly return success; client will abort on an actual error. we assume an error-free async workflow
+ commandBuffer.push(15, program.id, name);
+ return true;
+ }
+ default: throw 'bad getProgramParameter ' + revname(name);
+ }
+ };
+ this.getActiveAttrib = function(program, index) {
+ var name = program.attributeVec[index];
+ if (!name) return null;
+ return program.attributes[name];
+ };
+ this.getActiveUniform = function(program, index) {
+ var name = program.uniformVec[index];
+ if (!name) return null;
+ return program.uniforms[name];
+ };
+ this.getUniformLocation = function(program, name) {
+ var fullname = name;
+ var index = -1;
+ var open = name.indexOf('[');
+ if (open >= 0) {
+ var close = name.indexOf(']');
+ index = parseInt(name.substring(open+1, close));
+ name = name.substr(0, open);
+ }
+ if (!(name in program.uniforms)) return null;
+ var id = nextId++;
+ commandBuffer.push(16, program.id, fullname, id);
+ return { what: 'location', uniform: program.uniforms[name], id: id, index: index };
+ };
+ this.getProgramInfoLog = function(shader) {
+ return ''; // optimistic assumption of success; no proxying
+ };
+ this.useProgram = function(program) {
+ commandBuffer.push(17, program ? program.id : 0);
+ bindings.program = program;
+ };
+ this.uniform1i = function(location, data) {
+ if (!location) return;
+ commandBuffer.push(18, location.id, data);
+ };
+ this.uniform1f = function(location, data) {
+ if (!location) return;
+ commandBuffer.push(19, location.id, data);
+ };
+ this.uniform3fv = function(location, data) {
+ if (!location) return;
+ commandBuffer.push(20, location.id, new Float32Array(data));
+ };
+ this.uniform4fv = function(location, data) {
+ if (!location) return;
+ commandBuffer.push(21, location.id, new Float32Array(data));
+ };
+ this.uniformMatrix4fv = function(location, transpose, data) {
+ if (!location) return;
+ commandBuffer.push(22, location.id, transpose, new Float32Array(data));
+ };
+ this.vertexAttrib4fv = function(index, values) {
+ commandBuffer.push(23, index, new Float32Array(values));
+ };
+ this.createBuffer = function() {
+ var id = nextId++;
+ commandBuffer.push(24, id);
+ return new WebGLBuffer(id);
+ };
+ this.deleteBuffer = function(buffer) {
+ if (!buffer) return;
+ commandBuffer.push(25, buffer.id);
+ };
+ this.bindBuffer = function(target, buffer) {
+ commandBuffer.push(26, target, buffer ? buffer.id : 0);
+ switch (target) {
+ case this.ARRAY_BUFFER_BINDING: {
+ bindings.arrayBuffer = buffer;
+ break;
+ }
+ case this.ELEMENT_ARRAY_BUFFER_BINDING: {
+ bindings.elementArrayBuffer = buffer;
+ break;
+ }
+ }
+ };
+ this.bufferData = function(target, something, usage) {
+ if (typeof something !== 'number') something = new something.constructor(something);
+ commandBuffer.push(27, target, something, usage);
+ };
+ this.bufferSubData = function(target, offset, something) {
+ if (typeof something !== 'number') something = new something.constructor(something);
+ commandBuffer.push(28, target, offset, something);
+ };
+ this.viewport = function(x, y, w, h) {
+ commandBuffer.push(29, x, y, w, h);
+ };
+ this.vertexAttribPointer = function(index, size, type, normalized, stride, offset) {
+ commandBuffer.push(30, index, size, type, normalized, stride, offset);
+ };
+ this.enableVertexAttribArray = function(index) {
+ commandBuffer.push(31, index);
+ };
+ this.disableVertexAttribArray = function(index) {
+ commandBuffer.push(32, index);
+ };
+ this.drawArrays = function(mode, first, count) {
+ commandBuffer.push(33, mode, first, count);
+ };
+ this.drawElements = function(mode, count, type, offset) {
+ commandBuffer.push(34, mode, count, type, offset);
+ };
+ this.getError = function() {
+ // optimisticaly return success; client will abort on an actual error. we assume an error-free async workflow
+ commandBuffer.push(35);
+ return this.NO_ERROR;
+ };
+ this.createTexture = function() {
+ var id = nextId++;
+ commandBuffer.push(36, id);
+ return new WebGLTexture(id);
+ };
+ this.deleteTexture = function(texture) {
+ if (!texture) return;
+ commandBuffer.push(37, texture.id);
+ texture.id = 0;
+ };
+ this.isTexture = function(texture) {
+ return texture && texture.what === 'texture' && texture.id > 0 && texture.binding;
+ };
+ this.bindTexture = function(target, texture) {
+ switch (target) {
+ case that.TEXTURE_2D: {
+ bindings.texture2D = texture;
+ break;
+ }
+ }
+ texture.binding = target;
+ commandBuffer.push(38, target, texture ? texture.id : 0);
+ };
+ this.texParameteri = function(target, pname, param) {
+ commandBuffer.push(39, target, pname, param);
+ };
+ this.texImage2D = function(target, level, internalformat, width, height, border, format, type, pixels) {
+ assert(pixels || pixels === null); // we do not support the overloads that have fewer params
+ commandBuffer.push(40, target, level, internalformat, width, height, border, format, type, pixels ? new pixels.constructor(pixels) : pixels);
+ };
+ this.compressedTexImage2D = function(target, level, internalformat, width, height, border, pixels) {
+ commandBuffer.push(41, target, level, internalformat, width, height, border, new pixels.constructor(pixels));
+ };
+ this.activeTexture = function(texture) {
+ commandBuffer.push(42, texture);
+ };
+ this.getShaderParameter = function(shader, pname) {
+ switch (pname) {
+ case this.SHADER_TYPE: return shader.type;
+ case this.COMPILE_STATUS: {
+ // optimisticaly return success; client will abort on an actual error. we assume an error-free async workflow
+ commandBuffer.push(43, shader.id, pname);
+ return true;
+ }
+ default: throw 'unsupported getShaderParameter ' + pname;
+ }
+ };
+ this.clearDepth = function(depth) {
+ commandBuffer.push(44, depth);
+ };
+ this.depthFunc = function(depth) {
+ commandBuffer.push(45, depth);
+ };
+ this.frontFace = function(depth) {
+ commandBuffer.push(46, depth);
+ };
+ this.cullFace = function(depth) {
+ commandBuffer.push(47, depth);
+ };
+ this.readPixels = function(depth) {
+ abort('readPixels is impossible, we are async GL');
+ };
+ this.pixelStorei = function(pname, param) {
+ commandBuffer.push(48, pname, param);
+ };
+ this.depthMask = function(flag) {
+ commandBuffer.push(49, flag);
+ };
+ this.depthRange = function(near, far) {
+ commandBuffer.push(50, near, far);
+ };
+ this.blendFunc = function(sfactor, dfactor) {
+ commandBuffer.push(51, sfactor, dfactor);
+ };
+ this.scissor = function(x, y, width, height) {
+ commandBuffer.push(52, x, y, width, height);
+ };
+ this.colorMask = function(red, green, blue, alpha) {
+ commandBuffer.push(53, red, green, blue, alpha);
+ };
+ this.lineWidth = function(width) {
+ commandBuffer.push(54, width);
+ };
+ this.createFramebuffer = function() {
+ var id = nextId++;
+ commandBuffer.push(55, id);
+ return new WebGLFramebuffer(id);
+ };
+ this.deleteFramebuffer = function(framebuffer) {
+ if (!framebuffer) return;
+ commandBuffer.push(56, framebuffer.id);
+ };
+ this.bindFramebuffer = function(target, framebuffer) {
+ commandBuffer.push(57, target, framebuffer ? framebuffer.id : 0);
+ };
+ this.framebufferTexture2D = function(target, attachment, textarget, texture, level) {
+ commandBuffer.push(58, target, attachment, textarget, texture ? texture.id : 0, level);
+ };
+ this.checkFramebufferStatus = function(target) {
+ return this.FRAMEBUFFER_COMPLETE; // XXX totally wrong
+ };
+ this.createRenderbuffer = function() {
+ var id = nextId++;
+ commandBuffer.push(59, id);
+ return new WebGLRenderbuffer(id);
+ };
+ this.deleteRenderbuffer = function(renderbuffer) {
+ if (!renderbuffer) return;
+ commandBuffer.push(60, renderbuffer.id);
+ };
+ this.bindRenderbuffer = function(target, renderbuffer) {
+ commandBuffer.push(61, target, renderbuffer ? renderbuffer.id : 0);
+ };
+ this.renderbufferStorage = function(target, internalformat, width, height) {
+ commandBuffer.push(62, target, internalformat, width, height);
+ };
+ this.framebufferRenderbuffer = function(target, attachment, renderbuffertarget, renderbuffer) {
+ commandBuffer.push(63, target, attachment, renderbuffertarget, renderbuffer ? renderbuffer.id : 0);
+ };
+ //this.debugPrint = function(text) { // useful to interleave debug output properly with client GL commands
+ // commandBuffer.push(64, text);
+ //};
+
+ // Setup
+ var dropped = 0;
+ var average = 0;
+ var last = 0;
+ var meanDiff = 0;
+ var preMainLoop = Module['preMainLoop'];
+ Module['preMainLoop'] = function() {
+ /*
+ var now = Date.now();
+ if (last > 0) {
+ var diff = now - last;
+ meanDiff = 0.95*meanDiff + 0.05*diff;
+ if (clientFrameId % 10 === 0) dump('server fps ' + (1000/meanDiff) + ' (ignoring client throttling)\n');
+ }
+ last = now;
+ */
+ if (preMainLoop) {
+ var ret = preMainLoop();
+ if (ret === false) return ret;
+ }
+ // if too many frames in queue, skip a main loop iter
+ if (Math.abs(frameId - clientFrameId) >= 3) {
+ //dropped++;
+ //if (dropped % 10 === 0) dump('dropped: ' + [dropped, frameId, Math.round(100*dropped/(frameId + dropped)) + '%\n']);
+ return false;
+ }
+ };
+ var postMainLoop = Module['postMainLoop'];
+ Module['postMainLoop'] = function() {
+ if (postMainLoop) postMainLoop();
+ if (commandBuffer.length > 0) {
+ //average = (average + commandBuffer.length)/2;
+ //dump('buffer size: ' + Math.round(average) + '\n');
+ postMessage({ target: 'gl', op: 'render', commandBuffer: commandBuffer });
+ commandBuffer = [];
+ }
+ };
+}
+
+// share prefetched data among all instances
+
+WebGLWorker.prototype.prefetchedParameters = {};
+WebGLWorker.prototype.prefetchedExtensions = {};
+
diff --git a/tests/gles2_uniform_arrays.cpp b/tests/gles2_uniform_arrays.cpp
index 7293f9a9..b6be5348 100644
--- a/tests/gles2_uniform_arrays.cpp
+++ b/tests/gles2_uniform_arrays.cpp
@@ -4,6 +4,7 @@
#include <stdio.h>
#include <string.h>
#include <assert.h>
+#include <emscripten.h>
void RunTest(int testVariant)
{
@@ -57,6 +58,7 @@ void RunTest(int testVariant)
// so to exhibit extra issues in old code (and to keep new code from regressing), must test both with and without excess glGetUniformLocation calls.
if ((testVariant&1) != 0)
{
+ printf("check glGetUniformLocation with indexes\n");
// Deliberately check in odd order to make sure any kind of lazy operations won't affect the indices we get.
assert(glGetUniformLocation(program, "colors[2]") == loc+2);
assert(glGetUniformLocation(program, "colors[0]") == loc);
@@ -65,6 +67,7 @@ void RunTest(int testVariant)
assert(glGetUniformLocation(program, "colors[]") == loc);
assert(glGetUniformLocation(program, "colors[-100]") == -1);
assert(glGetUniformLocation(program, "colors[bleh]") == -1);
+ printf(" ...ok\n");
}
float colors[4*3] = { 1,0,0, 0,0.5,0, 0,0,0.2, 1,1,1 };
@@ -95,13 +98,20 @@ void RunTest(int testVariant)
glDrawArrays(GL_TRIANGLES, 0, 6);
- unsigned char pixel[4];
- glReadPixels(1,1,1,1,GL_RGBA,GL_UNSIGNED_BYTE, pixel);
- //printf("%d,%d,%d,%d\n", pixel[0], pixel[1], pixel[2], pixel[3]);
- assert(pixel[0] == 255);
- assert(pixel[1] == 178);
- assert(pixel[2] == 102);
- assert(pixel[3] == 255);
+ int in_worker = EM_ASM_INT_V({
+ return typeof importScripts !== 'undefined'
+ });
+
+ if (!in_worker) {
+ printf("Doing readpixels check\n");
+ unsigned char pixel[4];
+ glReadPixels(1,1,1,1,GL_RGBA,GL_UNSIGNED_BYTE, pixel);
+ //printf("%d,%d,%d,%d\n", pixel[0], pixel[1], pixel[2], pixel[3]);
+ assert(pixel[0] == 255);
+ assert(pixel[1] == 178);
+ assert(pixel[2] == 102);
+ assert(pixel[3] == 255);
+ }
printf("OK: Case %d passed.\n", testVariant);
// Lazy, don't clean up afterwards.
@@ -126,5 +136,10 @@ int main(int argc, char *argv[])
for(int i = 0; i < 4; ++i)
RunTest(i);
+#ifdef REPORT_RESULT
+ int result = 1;
+ REPORT_RESULT();
+#endif
+
return 0;
}
diff --git a/tests/hello_world_gles_proxy.c b/tests/hello_world_gles_proxy.c
new file mode 100644
index 00000000..1a8e5f61
--- /dev/null
+++ b/tests/hello_world_gles_proxy.c
@@ -0,0 +1,765 @@
+/*
+ * Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * Ported to GLES2.
+ * Kristian Høgsberg <krh@bitplanet.net>
+ * May 3, 2010
+ *
+ * Improve GLES2 port:
+ * * Refactor gear drawing.
+ * * Use correct normals for surfaces.
+ * * Improve shader.
+ * * Use perspective projection transformation.
+ * * Add FPS count.
+ * * Add comments.
+ * Alexandros Frantzis <alexandros.frantzis@linaro.org>
+ * Jul 13, 2010
+ */
+
+#define GL_GLEXT_PROTOTYPES
+#define EGL_EGLEXT_PROTOTYPES
+
+#define _GNU_SOURCE
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+#ifdef __APPLE__
+#include <OpenGL/gl.h>
+#include <Glut/glut.h>
+#else
+#include <GL/gl.h>
+#include <GL/glut.h>
+#endif
+
+#define STRIPS_PER_TOOTH 7
+#define VERTICES_PER_TOOTH 34
+#define GEAR_VERTEX_STRIDE 6
+
+#ifndef HAVE_BUILTIN_SINCOS
+#define sincos _sincos
+static void
+sincos (double a, double *s, double *c)
+{
+ *s = sin (a);
+ *c = cos (a);
+}
+#endif
+
+/**
+ * Struct describing the vertices in triangle strip
+ */
+struct vertex_strip {
+ /** The first vertex in the strip */
+ GLint first;
+ /** The number of consecutive vertices in the strip after the first */
+ GLint count;
+};
+
+/* Each vertex consist of GEAR_VERTEX_STRIDE GLfloat attributes */
+typedef GLfloat GearVertex[GEAR_VERTEX_STRIDE];
+
+/**
+ * Struct representing a gear.
+ */
+struct gear {
+ /** The array of vertices comprising the gear */
+ GearVertex *vertices;
+ /** The number of vertices comprising the gear */
+ int nvertices;
+ /** The array of triangle strips comprising the gear */
+ struct vertex_strip *strips;
+ /** The number of triangle strips comprising the gear */
+ int nstrips;
+ /** The Vertex Buffer Object holding the vertices in the graphics card */
+ GLuint vbo;
+};
+
+/** The view rotation [x, y, z] */
+static GLfloat view_rot[3] = { 20.0, 30.0, 0.0 };
+/** The gears */
+static struct gear *gear1, *gear2, *gear3;
+/** The current gear rotation angle */
+static GLfloat angle = 0.0;
+/** The location of the shader uniforms */
+static GLuint ModelViewProjectionMatrix_location,
+ NormalMatrix_location,
+ LightSourcePosition_location,
+ MaterialColor_location;
+/** The projection matrix */
+static GLfloat ProjectionMatrix[16];
+/** The direction of the directional light for the scene */
+static const GLfloat LightSourcePosition[4] = { 5.0, 5.0, 10.0, 1.0};
+
+/**
+ * Fills a gear vertex.
+ *
+ * @param v the vertex to fill
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param z the z coortinate
+ * @param n pointer to the normal table
+ *
+ * @return the operation error code
+ */
+static GearVertex *
+vert(GearVertex *v, GLfloat x, GLfloat y, GLfloat z, GLfloat n[3])
+{
+ v[0][0] = x;
+ v[0][1] = y;
+ v[0][2] = z;
+ v[0][3] = n[0];
+ v[0][4] = n[1];
+ v[0][5] = n[2];
+
+ return v + 1;
+}
+
+/**
+ * Create a gear wheel.
+ *
+ * @param inner_radius radius of hole at center
+ * @param outer_radius radius at center of teeth
+ * @param width width of gear
+ * @param teeth number of teeth
+ * @param tooth_depth depth of tooth
+ *
+ * @return pointer to the constructed struct gear
+ */
+static struct gear *
+create_gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width,
+ GLint teeth, GLfloat tooth_depth)
+{
+ GLfloat r0, r1, r2;
+ GLfloat da;
+ GearVertex *v;
+ struct gear *gear;
+ double s[5], c[5];
+ GLfloat normal[3];
+ int cur_strip = 0;
+ int i;
+
+ /* Allocate memory for the gear */
+ gear = malloc(sizeof *gear);
+ if (gear == NULL)
+ return NULL;
+
+ /* Calculate the radii used in the gear */
+ r0 = inner_radius;
+ r1 = outer_radius - tooth_depth / 2.0;
+ r2 = outer_radius + tooth_depth / 2.0;
+
+ da = 2.0 * M_PI / teeth / 4.0;
+
+ /* Allocate memory for the triangle strip information */
+ gear->nstrips = STRIPS_PER_TOOTH * teeth;
+ gear->strips = calloc(gear->nstrips, sizeof (*gear->strips));
+
+ /* Allocate memory for the vertices */
+ gear->vertices = calloc(VERTICES_PER_TOOTH * teeth, sizeof(*gear->vertices));
+ v = gear->vertices;
+
+ for (i = 0; i < teeth; i++) {
+ /* Calculate needed sin/cos for varius angles */
+ sincos(i * 2.0 * M_PI / teeth, &s[0], &c[0]);
+ sincos(i * 2.0 * M_PI / teeth + da, &s[1], &c[1]);
+ sincos(i * 2.0 * M_PI / teeth + da * 2, &s[2], &c[2]);
+ sincos(i * 2.0 * M_PI / teeth + da * 3, &s[3], &c[3]);
+ sincos(i * 2.0 * M_PI / teeth + da * 4, &s[4], &c[4]);
+
+ /* A set of macros for making the creation of the gears easier */
+#define GEAR_POINT(r, da) { (r) * c[(da)], (r) * s[(da)] }
+#define SET_NORMAL(x, y, z) do { \
+ normal[0] = (x); normal[1] = (y); normal[2] = (z); \
+} while(0)
+
+#define GEAR_VERT(v, point, sign) vert((v), p[(point)].x, p[(point)].y, (sign) * width * 0.5, normal)
+
+#define START_STRIP do { \
+ gear->strips[cur_strip].first = v - gear->vertices; \
+} while(0);
+
+#define END_STRIP do { \
+ int _tmp = (v - gear->vertices); \
+ gear->strips[cur_strip].count = _tmp - gear->strips[cur_strip].first; \
+ cur_strip++; \
+} while (0)
+
+#define QUAD_WITH_NORMAL(p1, p2) do { \
+ SET_NORMAL((p[(p1)].y - p[(p2)].y), -(p[(p1)].x - p[(p2)].x), 0); \
+ v = GEAR_VERT(v, (p1), -1); \
+ v = GEAR_VERT(v, (p1), 1); \
+ v = GEAR_VERT(v, (p2), -1); \
+ v = GEAR_VERT(v, (p2), 1); \
+} while(0)
+
+ struct point {
+ GLfloat x;
+ GLfloat y;
+ };
+
+ /* Create the 7 points (only x,y coords) used to draw a tooth */
+ struct point p[7] = {
+ GEAR_POINT(r2, 1), // 0
+ GEAR_POINT(r2, 2), // 1
+ GEAR_POINT(r1, 0), // 2
+ GEAR_POINT(r1, 3), // 3
+ GEAR_POINT(r0, 0), // 4
+ GEAR_POINT(r1, 4), // 5
+ GEAR_POINT(r0, 4), // 6
+ };
+
+ /* Front face */
+ START_STRIP;
+ SET_NORMAL(0, 0, 1.0);
+ v = GEAR_VERT(v, 0, +1);
+ v = GEAR_VERT(v, 1, +1);
+ v = GEAR_VERT(v, 2, +1);
+ v = GEAR_VERT(v, 3, +1);
+ v = GEAR_VERT(v, 4, +1);
+ v = GEAR_VERT(v, 5, +1);
+ v = GEAR_VERT(v, 6, +1);
+ END_STRIP;
+
+ /* Inner face */
+ START_STRIP;
+ QUAD_WITH_NORMAL(4, 6);
+ END_STRIP;
+
+ /* Back face */
+ START_STRIP;
+ SET_NORMAL(0, 0, -1.0);
+ v = GEAR_VERT(v, 6, -1);
+ v = GEAR_VERT(v, 5, -1);
+ v = GEAR_VERT(v, 4, -1);
+ v = GEAR_VERT(v, 3, -1);
+ v = GEAR_VERT(v, 2, -1);
+ v = GEAR_VERT(v, 1, -1);
+ v = GEAR_VERT(v, 0, -1);
+ END_STRIP;
+
+ /* Outer face */
+ START_STRIP;
+ QUAD_WITH_NORMAL(0, 2);
+ END_STRIP;
+
+ START_STRIP;
+ QUAD_WITH_NORMAL(1, 0);
+ END_STRIP;
+
+ START_STRIP;
+ QUAD_WITH_NORMAL(3, 1);
+ END_STRIP;
+
+ START_STRIP;
+ QUAD_WITH_NORMAL(5, 3);
+ END_STRIP;
+ }
+
+ gear->nvertices = (v - gear->vertices);
+
+ /* Store the vertices in a vertex buffer object (VBO) */
+ glGenBuffers(1, &gear->vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, gear->vbo);
+ glBufferData(GL_ARRAY_BUFFER, gear->nvertices * sizeof(GearVertex),
+ gear->vertices, GL_STATIC_DRAW);
+
+ return gear;
+}
+
+/**
+ * Multiplies two 4x4 matrices.
+ *
+ * The result is stored in matrix m.
+ *
+ * @param m the first matrix to multiply
+ * @param n the second matrix to multiply
+ */
+static void
+multiply(GLfloat *m, const GLfloat *n)
+{
+ GLfloat tmp[16];
+ const GLfloat *row, *column;
+ div_t d;
+ int i, j;
+
+ for (i = 0; i < 16; i++) {
+ tmp[i] = 0;
+ d = div(i, 4);
+ row = n + d.quot * 4;
+ column = m + d.rem;
+ for (j = 0; j < 4; j++)
+ tmp[i] += row[j] * column[j * 4];
+ }
+ memcpy(m, &tmp, sizeof tmp);
+}
+
+/**
+ * Rotates a 4x4 matrix.
+ *
+ * @param[in,out] m the matrix to rotate
+ * @param angle the angle to rotate
+ * @param x the x component of the direction to rotate to
+ * @param y the y component of the direction to rotate to
+ * @param z the z component of the direction to rotate to
+ */
+static void
+rotate(GLfloat *m, GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
+{
+ double s, c;
+
+ sincos(angle, &s, &c);
+ GLfloat r[16] = {
+ x * x * (1 - c) + c, y * x * (1 - c) + z * s, x * z * (1 - c) - y * s, 0,
+ x * y * (1 - c) - z * s, y * y * (1 - c) + c, y * z * (1 - c) + x * s, 0,
+ x * z * (1 - c) + y * s, y * z * (1 - c) - x * s, z * z * (1 - c) + c, 0,
+ 0, 0, 0, 1
+ };
+
+ multiply(m, r);
+}
+
+
+/**
+ * Translates a 4x4 matrix.
+ *
+ * @param[in,out] m the matrix to translate
+ * @param x the x component of the direction to translate to
+ * @param y the y component of the direction to translate to
+ * @param z the z component of the direction to translate to
+ */
+static void
+translate(GLfloat *m, GLfloat x, GLfloat y, GLfloat z)
+{
+ GLfloat t[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1 };
+
+ multiply(m, t);
+}
+
+/**
+ * Creates an identity 4x4 matrix.
+ *
+ * @param m the matrix make an identity matrix
+ */
+static void
+identity(GLfloat *m)
+{
+ GLfloat t[16] = {
+ 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0,
+ };
+
+ memcpy(m, t, sizeof(t));
+}
+
+/**
+ * Transposes a 4x4 matrix.
+ *
+ * @param m the matrix to transpose
+ */
+static void
+transpose(GLfloat *m)
+{
+ GLfloat t[16] = {
+ m[0], m[4], m[8], m[12],
+ m[1], m[5], m[9], m[13],
+ m[2], m[6], m[10], m[14],
+ m[3], m[7], m[11], m[15]};
+
+ memcpy(m, t, sizeof(t));
+}
+
+/**
+ * Inverts a 4x4 matrix.
+ *
+ * This function can currently handle only pure translation-rotation matrices.
+ * Read http://www.gamedev.net/community/forums/topic.asp?topic_id=425118
+ * for an explanation.
+ */
+static void
+invert(GLfloat *m)
+{
+ GLfloat t[16];
+ identity(t);
+
+ // Extract and invert the translation part 't'. The inverse of a
+ // translation matrix can be calculated by negating the translation
+ // coordinates.
+ t[12] = -m[12]; t[13] = -m[13]; t[14] = -m[14];
+
+ // Invert the rotation part 'r'. The inverse of a rotation matrix is
+ // equal to its transpose.
+ m[12] = m[13] = m[14] = 0;
+ transpose(m);
+
+ // inv(m) = inv(r) * inv(t)
+ multiply(m, t);
+}
+
+/**
+ * Calculate a perspective projection transformation.
+ *
+ * @param m the matrix to save the transformation in
+ * @param fovy the field of view in the y direction
+ * @param aspect the view aspect ratio
+ * @param zNear the near clipping plane
+ * @param zFar the far clipping plane
+ */
+void perspective(GLfloat *m, GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar)
+{
+ GLfloat tmp[16];
+ identity(tmp);
+
+ double sine, cosine, cotangent, deltaZ;
+ GLfloat radians = fovy / 2 * M_PI / 180;
+
+ deltaZ = zFar - zNear;
+ sincos(radians, &sine, &cosine);
+
+ if ((deltaZ == 0) || (sine == 0) || (aspect == 0))
+ return;
+
+ cotangent = cosine / sine;
+
+ tmp[0] = cotangent / aspect;
+ tmp[5] = cotangent;
+ tmp[10] = -(zFar + zNear) / deltaZ;
+ tmp[11] = -1;
+ tmp[14] = -2 * zNear * zFar / deltaZ;
+ tmp[15] = 0;
+
+ memcpy(m, tmp, sizeof(tmp));
+}
+
+/**
+ * Draws a gear.
+ *
+ * @param gear the gear to draw
+ * @param transform the current transformation matrix
+ * @param x the x position to draw the gear at
+ * @param y the y position to draw the gear at
+ * @param angle the rotation angle of the gear
+ * @param color the color of the gear
+ */
+static void
+draw_gear(struct gear *gear, GLfloat *transform,
+ GLfloat x, GLfloat y, GLfloat angle, const GLfloat color[4])
+{
+ GLfloat model_view[16];
+ GLfloat normal_matrix[16];
+ GLfloat model_view_projection[16];
+
+ /* Translate and rotate the gear */
+ memcpy(model_view, transform, sizeof (model_view));
+ translate(model_view, x, y, 0);
+ rotate(model_view, 2 * M_PI * angle / 360.0, 0, 0, 1);
+
+ /* Create and set the ModelViewProjectionMatrix */
+ memcpy(model_view_projection, ProjectionMatrix, sizeof(model_view_projection));
+ multiply(model_view_projection, model_view);
+
+ glUniformMatrix4fv(ModelViewProjectionMatrix_location, 1, GL_FALSE,
+ model_view_projection);
+
+ /*
+ * Create and set the NormalMatrix. It's the inverse transpose of the
+ * ModelView matrix.
+ */
+ memcpy(normal_matrix, model_view, sizeof (normal_matrix));
+ invert(normal_matrix);
+ transpose(normal_matrix);
+ glUniformMatrix4fv(NormalMatrix_location, 1, GL_FALSE, normal_matrix);
+
+ /* Set the gear color */
+ glUniform4fv(MaterialColor_location, 1, color);
+
+ /* Set the vertex buffer object to use */
+ glBindBuffer(GL_ARRAY_BUFFER, gear->vbo);
+
+ /* Set up the position of the attributes in the vertex buffer object */
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
+ 6 * sizeof(GLfloat), NULL);
+ glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE,
+ 6 * sizeof(GLfloat), (GLfloat *) 0 + 3);
+
+ /* Enable the attributes */
+ glEnableVertexAttribArray(0);
+ glEnableVertexAttribArray(1);
+
+ /* Draw the triangle strips that comprise the gear */
+ int n;
+ for (n = 0; n < gear->nstrips; n++)
+ glDrawArrays(GL_TRIANGLE_STRIP, gear->strips[n].first, gear->strips[n].count);
+
+ /* Disable the attributes */
+ glDisableVertexAttribArray(1);
+ glDisableVertexAttribArray(0);
+}
+
+/**
+ * Draws the gears.
+ */
+static void
+gears_draw(void)
+{
+#ifdef __EMSCRIPTEN__
+ #include <emscripten.h>
+ glutIdleFunc (NULL);
+ glutReshapeFunc(NULL);
+ glutDisplayFunc(NULL);
+ glutSpecialFunc(NULL);
+#endif
+
+ const static GLfloat red[4] = { 0.8, 0.1, 0.0, 1.0 };
+ const static GLfloat green[4] = { 0.0, 0.8, 0.2, 1.0 };
+ const static GLfloat blue[4] = { 0.2, 0.2, 1.0, 1.0 };
+ GLfloat transform[16];
+ identity(transform);
+
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ /* Translate and rotate the view */
+ translate(transform, 0, 0, -20);
+ rotate(transform, 2 * M_PI * view_rot[0] / 360.0, 1, 0, 0);
+ rotate(transform, 2 * M_PI * view_rot[1] / 360.0, 0, 1, 0);
+ rotate(transform, 2 * M_PI * view_rot[2] / 360.0, 0, 0, 1);
+
+ /* Draw the gears */
+ draw_gear(gear1, transform, -3.0, -2.0, angle, red);
+ draw_gear(gear2, transform, 3.1, -2.0, -2 * angle - 9.0, green);
+ draw_gear(gear3, transform, -3.1, 4.2, -2 * angle - 25.0, blue);
+
+ glutSwapBuffers();
+
+#ifdef LONGTEST
+ glutPostRedisplay(); // check for issues with not throttling calls
+#endif
+
+ EM_ASM(dump('close!!!\n'); window.close());
+}
+
+/**
+ * Handles a new window size or exposure.
+ *
+ * @param width the window width
+ * @param height the window height
+ */
+static void
+gears_reshape(int width, int height)
+{
+ /* Update the projection matrix */
+ perspective(ProjectionMatrix, 60.0, width / (float)height, 1.0, 1024.0);
+
+ /* Set the viewport */
+ glViewport(0, 0, (GLint) width, (GLint) height);
+}
+
+/**
+ * Handles special glut events.
+ *
+ * @param special the event to handle.
+ */
+static void
+gears_special(int special, int crap, int morecrap)
+{
+ switch (special) {
+ case GLUT_KEY_LEFT:
+ view_rot[1] += 5.0;
+ break;
+ case GLUT_KEY_RIGHT:
+ view_rot[1] -= 5.0;
+ break;
+ case GLUT_KEY_UP:
+ view_rot[0] += 5.0;
+ break;
+ case GLUT_KEY_DOWN:
+ view_rot[0] -= 5.0;
+ break;
+ case GLUT_KEY_F11:
+ glutFullScreen();
+ break;
+ }
+}
+
+static void
+gears_idle(void)
+{
+ static int frames = 0;
+ static double tRot0 = -1.0, tRate0 = -1.0;
+ double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
+
+ if (tRot0 < 0.0)
+ tRot0 = t;
+ dt = t - tRot0;
+ tRot0 = t;
+
+ /* advance rotation for next frame */
+ angle += 70.0 * dt; /* 70 degrees per second */
+ if (angle > 3600.0)
+ angle -= 3600.0;
+
+ glutPostRedisplay();
+ frames++;
+
+ if (tRate0 < 0.0)
+ tRate0 = t;
+ if (t - tRate0 >= 5.0) {
+ GLfloat seconds = t - tRate0;
+ GLfloat fps = frames / seconds;
+ printf("%d frames in %3.1f seconds = %6.3f FPS\n", frames, seconds,
+ fps);
+ tRate0 = t;
+ frames = 0;
+#ifdef LONGTEST
+ static runs = 0;
+ runs++;
+ if (runs == 4) {
+ int result = fps;
+ REPORT_RESULT();
+ }
+#endif
+ }
+}
+
+static const char vertex_shader[] =
+"attribute vec3 position;\n"
+"attribute vec3 normal;\n"
+"\n"
+"uniform mat4 ModelViewProjectionMatrix;\n"
+"uniform mat4 NormalMatrix;\n"
+"uniform vec4 LightSourcePosition;\n"
+"uniform vec4 MaterialColor;\n"
+"\n"
+"varying vec4 Color;\n"
+"\n"
+"void main(void)\n"
+"{\n"
+" // Transform the normal to eye coordinates\n"
+" vec3 N = normalize(vec3(NormalMatrix * vec4(normal, 1.0)));\n"
+"\n"
+" // The LightSourcePosition is actually its direction for directional light\n"
+" vec3 L = normalize(LightSourcePosition.xyz);\n"
+"\n"
+" // Multiply the diffuse value by the vertex color (which is fixed in this case)\n"
+" // to get the actual color that we will use to draw this vertex with\n"
+" float diffuse = max(dot(N, L), 0.0);\n"
+" Color = diffuse * MaterialColor;\n"
+"\n"
+" // Transform the position to clip coordinates\n"
+" gl_Position = ModelViewProjectionMatrix * vec4(position, 1.0);\n"
+"}";
+
+static const char fragment_shader[] =
+"#ifdef GL_ES\n"
+"precision mediump float;\n"
+"#endif\n"
+"varying vec4 Color;\n"
+"\n"
+"void main(void)\n"
+"{\n"
+" gl_FragColor = Color;\n"
+"}";
+
+static void
+gears_init(void)
+{
+ GLuint v, f, program;
+ const char *p;
+ char msg[512];
+
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
+
+ /* Compile the vertex shader */
+ p = vertex_shader;
+ v = glCreateShader(GL_VERTEX_SHADER);
+ glShaderSource(v, 1, &p, NULL);
+ glCompileShader(v);
+ glGetShaderInfoLog(v, sizeof msg, NULL, msg);
+ printf("vertex shader info: %s\n", msg);
+
+ /* Compile the fragment shader */
+ p = fragment_shader;
+ f = glCreateShader(GL_FRAGMENT_SHADER);
+ glShaderSource(f, 1, &p, NULL);
+ glCompileShader(f);
+ glGetShaderInfoLog(f, sizeof msg, NULL, msg);
+ printf("fragment shader info: %s\n", msg);
+
+ /* Create and link the shader program */
+ program = glCreateProgram();
+ glAttachShader(program, v);
+ glAttachShader(program, f);
+ glBindAttribLocation(program, 0, "position");
+ glBindAttribLocation(program, 1, "normal");
+
+ glLinkProgram(program);
+ glGetProgramInfoLog(program, sizeof msg, NULL, msg);
+ printf("info: %s\n", msg);
+
+ /* Enable the shaders */
+ glUseProgram(program);
+
+ /* Get the locations of the uniforms so we can access them */
+ ModelViewProjectionMatrix_location = glGetUniformLocation(program, "ModelViewProjectionMatrix");
+ NormalMatrix_location = glGetUniformLocation(program, "NormalMatrix");
+ LightSourcePosition_location = glGetUniformLocation(program, "LightSourcePosition");
+ MaterialColor_location = glGetUniformLocation(program, "MaterialColor");
+
+ /* Set the LightSourcePosition uniform which is constant throught the program */
+ glUniform4fv(LightSourcePosition_location, 1, LightSourcePosition);
+
+ /* make the gears */
+ gear1 = create_gear(1.0, 4.0, 1.0, 20, 0.7);
+ gear2 = create_gear(0.5, 2.0, 2.0, 10, 0.7);
+ gear3 = create_gear(1.3, 2.0, 0.5, 10, 0.7);
+}
+
+int
+main(int argc, char *argv[])
+{
+ /* Initialize the window */
+ glutInit(&argc, argv);
+ glutInitWindowSize(300, 300);
+ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
+
+ glutCreateWindow("es2gears");
+
+ /* Set up glut callback functions */
+ glutIdleFunc (gears_idle);
+ glutReshapeFunc(gears_reshape);
+ glutDisplayFunc(gears_draw);
+ glutSpecialFunc(gears_special);
+
+ /* Initialize the gears */
+ gears_init();
+
+ glutMainLoop();
+
+ return 0;
+}
diff --git a/tests/runner.py b/tests/runner.py
index 87f8a036..8d9de0a3 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -662,15 +662,26 @@ class BrowserCore(RunnerCore):
Module['preRun'].push(function() {
setTimeout(doReftest, 1000); // if run() throws an exception and postRun is not called, this will kick in
});
+
+ if (typeof WebGLClient !== 'undefined') {
+ // trigger reftest from RAF as well, needed for workers where there is no pre|postRun on the main thread
+ var realRAF = window.requestAnimationFrame;
+ window.requestAnimationFrame = function(func) {
+ realRAF(func);
+ setTimeout(doReftest, 1000);
+ };
+ }
+
''' % basename)
def btest(self, filename, expected=None, reference=None, force_c=False, reference_slack=0, manual_reference=False, post_build=None,
- args=[], outfile='test.html', message='.'): # TODO: use in all other tests
+ args=[], outfile='test.html', message='.', also_proxied=False): # TODO: use in all other tests
# if we are provided the source and not a path, use that
filename_is_src = '\n' in filename
src = filename if filename_is_src else ''
filepath = path_from_root('tests', filename) if not filename_is_src else ('main.c' if force_c else 'main.cpp')
temp_filepath = os.path.join(self.get_dir(), os.path.basename(filepath))
+ original_args = args[:]
if filename_is_src:
with open(temp_filepath, 'w') as f: f.write(src)
if not reference:
@@ -678,16 +689,33 @@ class BrowserCore(RunnerCore):
with open(filepath) as f: src = f.read()
with open(temp_filepath, 'w') as f: f.write(self.with_report_result(src))
else:
+ self.reference = reference
expected = [str(i) for i in range(0, reference_slack+1)]
shutil.copyfile(filepath, temp_filepath)
self.reftest(path_from_root('tests', reference))
if not manual_reference:
args = args + ['--pre-js', 'reftest.js', '-s', 'GL_TESTING=1']
- Popen([PYTHON, EMCC, temp_filepath, '-o', outfile] + args).communicate()
+ all_args = [PYTHON, EMCC, temp_filepath, '-o', outfile] + args
+ #print 'all args:', all_args
+ Popen(all_args).communicate()
assert os.path.exists(outfile)
if post_build: post_build()
if type(expected) is str: expected = [expected]
self.run_browser(outfile, message, ['/report_result?' + e for e in expected])
+ if also_proxied:
+ print 'proxied...'
+ # save non-proxied
+ if not os.path.exists('normal'):
+ os.mkdir('normal')
+ shutil.copyfile('test.html', os.path.join('normal', 'test.html'))
+ shutil.copyfile('test.js', os.path.join('normal', 'test.js'))
+ if reference:
+ assert not manual_reference
+ manual_reference = True
+ assert not post_build
+ post_build = self.post_manual_reftest
+ # run proxied
+ self.btest(filename, expected, reference, force_c, reference_slack, manual_reference, post_build, original_args + ['--proxy-to-worker', '-s', 'GL_TESTING=1'], outfile, message)
###################################################################################################
diff --git a/tests/sdlglshader2.c b/tests/sdlglshader2.c
new file mode 100644
index 00000000..c75a0f5f
--- /dev/null
+++ b/tests/sdlglshader2.c
@@ -0,0 +1,168 @@
+/*
+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_opengl.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+// GL_ARB_shading_language_100, GL_ARB_shader_objects, GL_ARB_fragment_shader, GL_ARB_vertex_shader
+PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObject_ = NULL;
+PFNGLDELETEOBJECTARBPROC glDeleteObject_ = NULL;
+PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObject_ = NULL;
+PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObject_ = NULL;
+PFNGLSHADERSOURCEARBPROC glShaderSource_ = NULL;
+PFNGLCOMPILESHADERARBPROC glCompileShader_ = NULL;
+PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameteriv_ = NULL;
+PFNGLATTACHOBJECTARBPROC glAttachObject_ = NULL;
+PFNGLGETINFOLOGARBPROC glGetInfoLog_ = NULL;
+PFNGLLINKPROGRAMARBPROC glLinkProgram_ = NULL;
+PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocation_ = NULL;
+PFNGLUNIFORM1FARBPROC glUniform1f_ = NULL;
+PFNGLUNIFORM2FARBPROC glUniform2f_ = NULL;
+PFNGLUNIFORM3FARBPROC glUniform3f_ = NULL;
+PFNGLUNIFORM4FARBPROC glUniform4f_ = NULL;
+PFNGLUNIFORM1FVARBPROC glUniform1fv_ = NULL;
+PFNGLUNIFORM2FVARBPROC glUniform2fv_ = NULL;
+PFNGLUNIFORM3FVARBPROC glUniform3fv_ = NULL;
+PFNGLUNIFORM4FVARBPROC glUniform4fv_ = NULL;
+PFNGLUNIFORM1IARBPROC glUniform1i_ = NULL;
+PFNGLBINDATTRIBLOCATIONARBPROC glBindAttribLocation_ = NULL;
+PFNGLGETACTIVEUNIFORMARBPROC glGetActiveUniform_ = NULL;
+
+void initARB() {
+ glCreateProgramObject_ = (PFNGLCREATEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glCreateProgramObjectARB");
+ glDeleteObject_ = (PFNGLDELETEOBJECTARBPROC) SDL_GL_GetProcAddress("glDeleteObjectARB");
+ glUseProgramObject_ = (PFNGLUSEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glUseProgramObjectARB");
+ glCreateShaderObject_ = (PFNGLCREATESHADEROBJECTARBPROC) SDL_GL_GetProcAddress("glCreateShaderObjectARB");
+ glShaderSource_ = (PFNGLSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glShaderSourceARB");
+ glCompileShader_ = (PFNGLCOMPILESHADERARBPROC) SDL_GL_GetProcAddress("glCompileShaderARB");
+ glGetObjectParameteriv_ = (PFNGLGETOBJECTPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterivARB");
+ glAttachObject_ = (PFNGLATTACHOBJECTARBPROC) SDL_GL_GetProcAddress("glAttachObjectARB");
+ glGetInfoLog_ = (PFNGLGETINFOLOGARBPROC) SDL_GL_GetProcAddress("glGetInfoLogARB");
+ glLinkProgram_ = (PFNGLLINKPROGRAMARBPROC) SDL_GL_GetProcAddress("glLinkProgramARB");
+ glGetUniformLocation_ = (PFNGLGETUNIFORMLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetUniformLocationARB");
+ glUniform1f_ = (PFNGLUNIFORM1FARBPROC) SDL_GL_GetProcAddress("glUniform1fARB");
+ glUniform2f_ = (PFNGLUNIFORM2FARBPROC) SDL_GL_GetProcAddress("glUniform2fARB");
+ glUniform3f_ = (PFNGLUNIFORM3FARBPROC) SDL_GL_GetProcAddress("glUniform3fARB");
+ glUniform4f_ = (PFNGLUNIFORM4FARBPROC) SDL_GL_GetProcAddress("glUniform4fARB");
+ glUniform1fv_ = (PFNGLUNIFORM1FVARBPROC) SDL_GL_GetProcAddress("glUniform1fvARB");
+ glUniform2fv_ = (PFNGLUNIFORM2FVARBPROC) SDL_GL_GetProcAddress("glUniform2fvARB");
+ glUniform3fv_ = (PFNGLUNIFORM3FVARBPROC) SDL_GL_GetProcAddress("glUniform3fvARB");
+ glUniform4fv_ = (PFNGLUNIFORM4FVARBPROC) SDL_GL_GetProcAddress("glUniform4fvARB");
+ glUniform1i_ = (PFNGLUNIFORM1IARBPROC) SDL_GL_GetProcAddress("glUniform1iARB");
+ glBindAttribLocation_ = (PFNGLBINDATTRIBLOCATIONARBPROC) SDL_GL_GetProcAddress("glBindAttribLocationARB");
+ glGetActiveUniform_ = (PFNGLGETACTIVEUNIFORMARBPROC) SDL_GL_GetProcAddress("glGetActiveUniformARB");
+}
+
+void setShaders() {
+ GLuint v, f, p;
+ GLint ok;
+
+ const char *vv = "#pragma CUBE2_uniform animdata AnimData 0 16\n"
+ "uniform vec4 animdata[246];\n"
+ "void main() \n"
+ "{ \n"
+ " gl_Position = ftransform() + animdata[0]; \n"
+ "}";
+ const char *ff = "void main() \n"
+ "{ \n"
+ " gl_FragColor = vec4(gl_FragCoord.y/480.0, gl_FragCoord.x/640.0, 0.66, 1.0); \n"
+ "}";
+
+ v = glCreateShaderObject_(GL_VERTEX_SHADER);
+ f = glCreateShaderObject_(GL_FRAGMENT_SHADER);
+
+ glShaderSource_(v, 1, &vv,NULL);
+ glShaderSource_(f, 1, &ff,NULL);
+
+ glCompileShader_(v);
+ glGetObjectParameteriv_(v, GL_OBJECT_COMPILE_STATUS_ARB, &ok);
+ if (!ok) {
+ char msg[512];
+ glGetShaderInfoLog(v, sizeof msg, NULL, msg);
+ printf("shader compilation issue: %s\n", msg);
+ }
+ assert(ok);
+
+ glCompileShader_(f);
+ glGetObjectParameteriv_(f, GL_OBJECT_COMPILE_STATUS_ARB, &ok);
+ assert(ok);
+
+ p = glCreateProgramObject_();
+ glAttachObject_(p,f);
+ glAttachObject_(p,v);
+
+ glLinkProgram_(p);
+ glGetObjectParameteriv_(p, GL_OBJECT_LINK_STATUS_ARB, &ok);
+ assert(ok);
+
+ int loc = glGetUniformLocation_(p, "animdata");
+ printf("loc: %d\n", loc);
+ assert(loc > 0);
+ int loc2 = glGetUniformLocation_(p, "animdata[0]");
+ printf("loc2: %d\n", loc2);
+ assert(loc2 == loc);
+
+ glUseProgramObject_(p);
+}
+
+int main(int argc, char *argv[])
+{
+ SDL_Surface *screen;
+
+ assert(SDL_Init(SDL_INIT_VIDEO) == 0);
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+ screen = SDL_SetVideoMode( 640, 480, 16, SDL_OPENGL );
+ assert(screen);
+
+ glClearColor(0, 0, 0, 0);
+ glViewport(0, 0, 640, 480);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, 640, 480, 0, -1, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ initARB();
+ setShaders();
+
+ glColor3f(0, 1, 1); // is overridden by the shader, useful for debugging native builds
+ glBegin( GL_TRIANGLES );
+ glTexCoord2i(0, 0); glVertex3f( 10, 10, 0);
+ glTexCoord2i(1, 0); glVertex3f( 300, 10, 0);
+ glTexCoord2i(1, 1); glVertex3f( 300, 328, 0);
+ glEnd();
+
+ glColor3f(1, 1, 0); // is overridden by the shader, useful for debugging native builds
+ glBegin( GL_TRIANGLES );
+ glTexCoord2f(0, 0.5); glVertex3f(410, 10, 0);
+ glTexCoord2f(1, 0.5); glVertex3f(600, 10, 0);
+ glTexCoord2f(1, 1 ); glVertex3f(630, 400, 0);
+ glEnd();
+
+ SDL_GL_SwapBuffers();
+
+#ifdef REPORT_RESULT
+ int result = 1;
+ REPORT_RESULT();
+#endif
+
+ SDL_Quit();
+ return 0;
+}
+
diff --git a/tests/test_browser.py b/tests/test_browser.py
index 42a1c0a0..80f94f2b 100644
--- a/tests/test_browser.py
+++ b/tests/test_browser.py
@@ -719,10 +719,11 @@ If manually bisecting:
self.clear()
self.btest('sdl_canvas.c', expected='1', args=['-s', 'LEGACY_GL_EMULATION=1', '-O2', '-s', 'SAFE_HEAP=1'])
- def test_sdl_canvas_proxy(self):
- def post():
- html = open('test.html').read()
- html = html.replace('</body>', '''
+ def post_manual_reftest(self, reference=None):
+ self.reftest(path_from_root('tests', self.reference if reference is None else reference))
+
+ html = open('test.html').read()
+ html = html.replace('</body>', '''
<script>
function assert(x, y) { if (!x) throw 'assertion failed ' + y }
@@ -738,11 +739,21 @@ window.close = function() {
};
</script>
</body>''' % open('reftest.js').read())
- open('test.html', 'w').write(html)
+ open('test.html', 'w').write(html)
+ def test_sdl_canvas_proxy(self):
open('data.txt', 'w').write('datum')
+ self.btest('sdl_canvas_proxy.c', reference='sdl_canvas_proxy.png', args=['--proxy-to-worker', '--preload-file', 'data.txt'], manual_reference=True, post_build=self.post_manual_reftest)
- self.btest('sdl_canvas_proxy.c', reference='sdl_canvas_proxy.png', args=['--proxy-to-worker', '--preload-file', 'data.txt'], manual_reference=True, post_build=post)
+ def test_glgears_proxy(self):
+ self.btest('hello_world_gles_proxy.c', reference='gears.png', args=['--proxy-to-worker', '-s', 'GL_TESTING=1'], manual_reference=True, post_build=self.post_manual_reftest)
+
+ def test_glgears_proxy_jstarget(self):
+ # test .js target with --proxy-worker; emits 2 js files, client and worker
+ Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_gles_proxy.c'), '-o', 'test.js', '--proxy-to-worker', '-s', 'GL_TESTING=1']).communicate()
+ open('test.html', 'w').write(open(path_from_root('src', 'shell_minimal.html')).read().replace('{{{ SCRIPT }}}', '<script src="test.js"></script>'))
+ self.post_manual_reftest('gears.png')
+ self.run_browser('test.html', None, '/report_result?0')
def test_sdl_canvas_alpha(self):
self.btest('sdl_canvas_alpha.c', reference='sdl_canvas_alpha.png', reference_slack=9)
@@ -1307,7 +1318,9 @@ keydown(100);keyup(100); // trigger the end
message='You should see animating gears.')
def test_glgears_long(self):
- self.btest('hello_world_gles.c', expected=map(str, range(30, 1000)), args=['-DHAVE_BUILTIN_SINCOS', '-DLONGTEST'])
+ for proxy in [0, 1]:
+ print 'proxy', proxy
+ self.btest('hello_world_gles.c', expected=map(str, range(30, 500)), args=['-DHAVE_BUILTIN_SINCOS', '-DLONGTEST'] + (['--proxy-to-worker'] if proxy else []))
def test_glgears_animation(self):
es2_suffix = ['', '_full', '_full_944']
@@ -1427,6 +1440,9 @@ keydown(100);keyup(100); // trigger the end
def test_sdlglshader(self):
self.btest('sdlglshader.c', reference='sdlglshader.png', args=['-O2', '--closure', '1', '-s', 'LEGACY_GL_EMULATION=1'])
+ def test_sdlglshader2(self):
+ self.btest('sdlglshader2.c', expected='1', args=['-s', 'LEGACY_GL_EMULATION=1'], also_proxied=True)
+
def test_gl_glteximage(self):
self.btest('gl_teximage.c', '1')
@@ -1456,9 +1472,8 @@ keydown(100);keyup(100); // trigger the end
def test_gl_vertex_buffer(self):
self.btest('gl_vertex_buffer.c', reference='gl_vertex_buffer.png', args=['-s', 'GL_UNSAFE_OPTS=0', '-s', 'LEGACY_GL_EMULATION=1'], reference_slack=1)
- # Does not pass due to https://bugzilla.mozilla.org/show_bug.cgi?id=924264 so disabled for now.
- # def test_gles2_uniform_arrays(self):
- # self.btest('gles2_uniform_arrays.cpp', args=['-s', 'GL_ASSERTIONS=1'], expected=['1'])
+ def test_gles2_uniform_arrays(self):
+ self.btest('gles2_uniform_arrays.cpp', args=['-s', 'GL_ASSERTIONS=1'], expected=['1'], also_proxied=True)
def test_gles2_conformance(self):
self.btest('gles2_conformance.cpp', args=['-s', 'GL_ASSERTIONS=1'], expected=['1'])
@@ -1476,7 +1491,7 @@ keydown(100);keyup(100); // trigger the end
self.btest('cubegeom_pre3.c', reference='cubegeom_pre2.png', args=['-s', 'LEGACY_GL_EMULATION=1'])
def test_cubegeom(self):
- self.btest('cubegeom.c', reference='cubegeom.png', args=['-O2', '-g', '-s', 'LEGACY_GL_EMULATION=1'])
+ self.btest('cubegeom.c', reference='cubegeom.png', args=['-O2', '-g', '-s', 'LEGACY_GL_EMULATION=1'], also_proxied=True)
def test_cubegeom_proc(self):
open('side.c', 'w').write(r'''
@@ -1500,10 +1515,10 @@ void *getBindBuffer() {
self.btest('cubegeom_color.c', reference='cubegeom_color.png', args=['-s', 'LEGACY_GL_EMULATION=1'])
def test_cubegeom_normal(self):
- self.btest('cubegeom_normal.c', reference='cubegeom_normal.png', args=['-s', 'LEGACY_GL_EMULATION=1'])
+ self.btest('cubegeom_normal.c', reference='cubegeom_normal.png', args=['-s', 'LEGACY_GL_EMULATION=1'], also_proxied=True)
def test_cubegeom_normal_dap(self): # draw is given a direct pointer to clientside memory, no element array buffer
- self.btest('cubegeom_normal_dap.c', reference='cubegeom_normal.png', args=['-s', 'LEGACY_GL_EMULATION=1'])
+ self.btest('cubegeom_normal_dap.c', reference='cubegeom_normal.png', args=['-s', 'LEGACY_GL_EMULATION=1'], also_proxied=True)
def test_cubegeom_normal_dap_far(self): # indices do nto start from 0
self.btest('cubegeom_normal_dap_far.c', reference='cubegeom_normal.png', args=['-s', 'LEGACY_GL_EMULATION=1'])
@@ -1521,7 +1536,7 @@ void *getBindBuffer() {
self.btest('cubegeom_mt.c', reference='cubegeom_mt.png', args=['-s', 'LEGACY_GL_EMULATION=1']) # multitexture
def test_cubegeom_color2(self):
- self.btest('cubegeom_color2.c', reference='cubegeom_color2.png', args=['-s', 'LEGACY_GL_EMULATION=1'])
+ self.btest('cubegeom_color2.c', reference='cubegeom_color2.png', args=['-s', 'LEGACY_GL_EMULATION=1'], also_proxied=True)
def test_cubegeom_texturematrix(self):
self.btest('cubegeom_texturematrix.c', reference='cubegeom_texturematrix.png', args=['-s', 'LEGACY_GL_EMULATION=1'])
@@ -1539,7 +1554,7 @@ void *getBindBuffer() {
self.btest('cubegeom_pre2_vao2.c', reference='cubegeom_pre2_vao2.png', args=['-s', 'LEGACY_GL_EMULATION=1'])
def test_cube_explosion(self):
- self.btest('cube_explosion.c', reference='cube_explosion.png', args=['-s', 'LEGACY_GL_EMULATION=1'])
+ self.btest('cube_explosion.c', reference='cube_explosion.png', args=['-s', 'LEGACY_GL_EMULATION=1'], also_proxied=True)
def test_glgettexenv(self):
self.btest('glgettexenv.c', args=['-s', 'LEGACY_GL_EMULATION=1'], expected=['1'])