diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/reproduceriter.js | 216 | ||||
-rwxr-xr-x | tools/reproduceriter.py | 327 | ||||
-rw-r--r-- | tools/reproduceriter_shell.js | 871 | ||||
-rw-r--r-- | tools/shared.py | 38 |
4 files changed, 1151 insertions, 301 deletions
diff --git a/tools/reproduceriter.js b/tools/reproduceriter.js new file mode 100644 index 00000000..9dce8199 --- /dev/null +++ b/tools/reproduceriter.js @@ -0,0 +1,216 @@ + +var Recorder = (function() { + var recorder; + var init = 'reproduce='; + var initLocation = window.location.search.indexOf(init); + var replaying = initLocation >= 0; + var raf = window['requestAnimationFrame'] || + window['mozRequestAnimationFrame'] || + window['webkitRequestAnimationFrame'] || + window['msRequestAnimationFrame'] || + window['oRequestAnimationFrame']; + if (!replaying) { + // Prepare to record + recorder = {}; + // Start + recorder.frameCounter = 0; // the frame counter is used to know when to replay events + recorder.start = function() { + alert("Starting recording! Don't forget to Recorder.finish() afterwards!"); + function count() { + recorder.frameCounter++; + raf(count); + } + count(); + recorder.started = true; + }; + // Math.random + recorder.randoms = []; + recorder.random = Math.random; + Math.random = function() { + var ret = recorder.random(); + recorder.randoms.push(ret); + return ret; + }; + // Date.now, performance.now + recorder.dnows = []; + recorder.dnow = Date.now; + Date.now = function() { + var ret = recorder.dnow(); + recorder.dnows.push(ret); + return ret; + }; + recorder.pnows = []; + var pnow = performance.now || performance.webkitNow || performance.mozNow || performance.oNow || performance.msNow; + recorder.pnow = function() { return pnow.call(performance) }; + performance.now = function() { + var ret = recorder.pnow(); + recorder.pnows.push(ret); + return ret; + }; + // Events + recorder.devents = []; // document events + recorder.onEvent = function(which, callback) { + document['on' + which] = function(event) { + if (!recorder.started) return true; + event.frameCounter = recorder.frameCounter; + recorder.devents.push(event); + return callback(event); // XXX do we need to record the return value? + }; + }; + recorder.tevents = []; // custom-target events. Currently we assume a single such custom target (aside from document), e.g., a canvas for the game. + recorder.addListener = function(target, which, callback, arg) { + target.addEventListener(which, function(event) { + if (!recorder.started) return true; + event.frameCounter = recorder.frameCounter; + recorder.tevents.push(event); + return callback(event); // XXX do we need to record the return value? + }, arg); + }; + // Finish + recorder.finish = function() { + // Reorder data because pop() is faster than shift() + recorder.randoms.reverse(); + recorder.dnows.reverse(); + recorder.pnows.reverse(); + recorder.devents.reverse(); + recorder.tevents.reverse(); + // Make JSON.stringify work on data from native event objects (and only store relevant ones) + var importantProperties = { + type: 1, + movementX: 1, mozMovementX: 1, webkitMovementX: 1, + movementY: 1, mozMovementY: 1, webkitMovementY: 1, + detail: 1, + wheelDelta: 1, + pageX: 1, + pageY: 1, + button: 1, + keyCode: 1, + frameCounter: 1 + }; + function importantize(event) { + var ret = {}; + for (var prop in importantProperties) { + if (prop in event) { + ret[prop] = event[prop]; + } + } + return ret; + } + recorder.devents = recorder.devents.map(importantize); + recorder.tevents = recorder.tevents.map(importantize); + // Write out + alert('Writing out data, remember to save!'); + setTimeout(function() { + document.open(); + document.write(JSON.stringify(recorder)); + document.close(); + }, 0); + return '.'; + }; + } else { + // Load recording + var dataPath = window.location.search.substring(initLocation + init.length); + var baseURL = window.location.toString().replace('://', 'cheez999').split('?')[0].split('/').slice(0, -1).join('/').replace('cheez999', '://'); + if (baseURL[baseURL.length-1] != '/') baseURL += '/'; + var path = baseURL + dataPath; + alert('Loading replay from ' + path); + var request = new XMLHttpRequest(); + request.open('GET', path, false); + request.send(); + var raw = request.responseText; + raw = raw.substring(raw.indexOf('{'), raw.lastIndexOf('}')+1); // remove <html> etc + recorder = JSON.parse(raw); + // prepare to replay + // Start + recorder.frameCounter = 0; // the frame counter is used to know when to replay events + recorder.start = function() { + function count() { + recorder.frameCounter++; + raf(count); + // replay relevant events for this frame + while (recorder.devents.length && recorder.devents[recorder.devents.length-1].frameCounter <= recorder.frameCounter) { + var event = recorder.devents.pop(); + recorder['on' + event.type](event); + } + while (recorder.tevents.length && recorder.tevents[recorder.tevents.length-1].frameCounter <= recorder.frameCounter) { + var event = recorder.tevents.pop(); + recorder['event' + event.type](event); + } + } + count(); + }; + // Math.random + recorder.random = Math.random; + Math.random = function() { + if (recorder.randoms.length > 0) { + return recorder.randoms.pop(); + } else { + recorder.finish(); + throw 'consuming too many random values!'; + } + }; + // Date.now, performance.now + recorder.dnow = Date.now; + Date.now = function() { + if (recorder.dnows.length > 0) { + return recorder.dnows.pop(); + } else { + recorder.finish(); + throw 'consuming too many Date.now values!'; + } + }; + var pnow = performance.now || performance.webkitNow || performance.mozNow || performance.oNow || performance.msNow; + recorder.pnow = function() { return pnow.call(performance) }; + performance.now = function() { + if (recorder.pnows.length > 0) { + return recorder.pnows.pop(); + } else { + recorder.finish(); + throw 'consuming too many performance.now values!'; + } + }; + // Events + recorder.onEvent = function(which, callback) { + recorder['on' + which] = callback; + }; + recorder.eventCallbacks = {}; + recorder.addListener = function(target, which, callback, arg) { + recorder['event' + which] = callback; + }; + recorder.onFinish = []; + // Benchmarking hooks - emscripten specific + setTimeout(function() { + var totalTime = 0; + var totalSquared = 0; + var iterations = 0; + var maxTime = 0; + var curr = 0; + Module.preMainLoop = function() { + curr = recorder.pnow(); + } + Module.postMainLoop = function() { + var time = recorder.pnow() - curr; + totalTime += time; + totalSquared += time*time; + maxTime = Math.max(maxTime, time); + iterations++; + }; + recorder.onFinish.push(function() { + var mean = totalTime / iterations; + var meanSquared = totalSquared / iterations; + console.log('mean frame : ' + mean + ' ms'); + console.log('frame std dev: ' + Math.sqrt(meanSquared - (mean*mean)) + ' ms'); + console.log('max frame : ' + maxTime + ' ms'); + }); + }); + // Finish + recorder.finish = function() { + recorder.onFinish.forEach(function(finish) { + finish(); + }); + }; + } + recorder.replaying = replaying; + return recorder; +})(); + diff --git a/tools/reproduceriter.py b/tools/reproduceriter.py index 6696915d..a1912396 100755 --- a/tools/reproduceriter.py +++ b/tools/reproduceriter.py @@ -1,9 +1,6 @@ #!/usr/bin/env python ''' - -* This is a work in progress * - Reproducer Rewriter =================== @@ -19,13 +16,19 @@ Usage: 1. Run this script as - reproduceriter.py IN_DIR OUT_DIR FIRST_JS + reproduceriter.py IN_DIR OUT_DIR FIRST_JS [WINDOW_LOCATION] [ON_IDLE] IN_DIR should be the project directory, and OUT_DIR will be created with the instrumented code (OUT_DIR will be overwritten if it exists). FIRST_JS should be a path (relative to IN_DIR) to the first JavaScript file loaded by the project (this tool - will add code to that). + will add code to that). The last two parameters, WINDOW_LOCATION + and ON_IDLE, are relevant for shell builds. If WINDOW_LOCATION is + specified, we will make a build that runs in the shell and not in + a browser. WINDOW_LOCATION is the fake window.location we set in the + fake DOM, and ON_IDLE is code that runs when the fake main browser + event loop runs out of actions. (Note that only a browser build can + do recording, shell builds just replay.) You will need to call @@ -79,7 +82,26 @@ Examples * BananaBread: Unpack into a directory called bb, then one directory up, run - emscripten/tools/reproduceriter.py bb bench js/game-setup.js game.html?low,low,reproduce=repro.data + emscripten/tools/reproduceriter.py bb bench js/game-setup.js game.html?low,low,reproduce=repro.data "function(){ print('triggering click'); document.querySelector('.fullscreen-button.low-res').callEventListeners('click'); window.onIdle = null; }" + + for a shell build, or + + emscripten/tools/reproduceriter.py bb bench js/game-setup.js + + for a browser build. Since only a browser build can do recording, you would normally + make a browser build, record a trace, then make a shell build and copy the trace + there so you can run it. + + The last parameter specifies what to do when the event loop is idle: We fire an event and then set onIdle (which was this function) to null, so this is a one-time occurence. + +Notes + + * Replay can depend on browser state. One case is if you are replaying a fullscreen + game with pointer lock, then you will need to manually allow pointer lock if it + isn't already on for the machine. If you do it too early or too late, the replay + can be different, since mousemove events mean different things depending on + whether the pointer is locked or not. + ''' import os, sys, shutil, re @@ -92,6 +114,11 @@ in_dir = sys.argv[1] out_dir = sys.argv[2] first_js = sys.argv[3] window_location = sys.argv[4] if len(sys.argv) >= 5 else '' +on_idle = sys.argv[5] if len(sys.argv) >= 6 else '' + +shell = not not window_location + +dirs_to_drop = 0 if not os.path.dirname(first_js) else len(os.path.dirname(first_js).split('/')) if os.path.exists(out_dir): shutil.rmtree(out_dir) @@ -121,287 +148,13 @@ for parent, dirs, files in os.walk(out_dir): print 'add boilerplate...' -open(os.path.join(out_dir, first_js), 'w').write(''' - -// environment for shell -if (typeof nagivator == 'undefined') { - var window = { - location: { - toString: function() { - return '%s'; - }, - search: '%s', - }, - rafs: [], - requestAnimationFrame: function(func) { - window.rafs.push(func); - }, - runEventLoop: function() { - while (1) { // run forever until an exception stops this replay - var currRafs = window.rafs; - window.rafs = []; - for (var i = 0; i < currRafs.length; i++) { - currRafs[i](); - } - } - }, - }; - var document = { - getElementById: function(id) { - switch(id) { - case 'canvas': { - return { - getContext: function(which) { - switch(which) { - case 'experimental-webgl': { - return { - getExtension: function() { return 1 }, - requestPointerLock: function() { - throw 'pointerLock'; - }, - }; - } - default: throw 'canvas.getContext: ' + which; - } - }, - }; - } - default: throw 'getElementById: ' + id; - } - }, - querySelector: function() { - return { - classList: { - add: function(){}, - remove: function(){}, - }, - }; - }, - }; - var performance = { - now: function() { - return Date.now(); - }, - }; -} - -var Recorder = (function() { - var recorder; - var init = 'reproduce='; - var initLocation = window.location.search.indexOf(init); - var replaying = initLocation >= 0; - var raf = window['requestAnimationFrame'] || - window['mozRequestAnimationFrame'] || - window['webkitRequestAnimationFrame'] || - window['msRequestAnimationFrame'] || - window['oRequestAnimationFrame']; - if (!replaying) { - // Prepare to record - recorder = {}; - // Start - recorder.frameCounter = 0; // the frame counter is used to know when to replay events - recorder.start = function() { - alert("Starting recording! Don't forget to Recorder.finish() afterwards!"); - function count() { - recorder.frameCounter++; - raf(count); - } - count(); - recorder.started = true; - }; - // Math.random - recorder.randoms = []; - var random = Math.random; - Math.random = function() { - var ret = random(); - recorder.randoms.push(ret); - return ret; - }; - // Date.now, performance.now - recorder.dnows = []; - recorder.dnow = Date.now; - Date.now = function() { - var ret = recorder.dnow(); - recorder.dnows.push(ret); - return ret; - }; - recorder.pnows = []; - recorder.pnow = performance.now; - performance.now = function() { - var ret = recorder.pnow(); - recorder.pnows.push(ret); - return ret; - }; - // Events - recorder.devents = []; // document events - recorder.onEvent = function(which, callback) { - document['on' + which] = function(event) { - if (!recorder.started) return true; - event.frameCounter = recorder.frameCounter; - recorder.devents.push(event); - return callback(event); // XXX do we need to record the return value? - }; - }; - recorder.tevents = []; // custom-target events. Currently we assume a single such custom target (aside from document), e.g., a canvas for the game. - recorder.addListener = function(target, which, callback, arg) { - target.addEventListener(which, function(event) { - if (!recorder.started) return true; - event.frameCounter = recorder.frameCounter; - recorder.tevents.push(event); - return callback(event); // XXX do we need to record the return value? - }, arg); - }; - // Finish - recorder.finish = function() { - // Reorder data because pop() is faster than shift() - recorder.randoms.reverse(); - recorder.dnows.reverse(); - recorder.pnows.reverse(); - recorder.devents.reverse(); - recorder.tevents.reverse(); - // Make JSON.stringify work on data from native event objects (and only store relevant ones) - var importantProperties = { - type: 1, - movementX: 1, mozMovementX: 1, webkitMovementX: 1, - movementY: 1, mozMovementY: 1, webkitMovementY: 1, - detail: 1, - wheelDelta: 1, - pageX: 1, - pageY: 1, - button: 1, - keyCode: 1, - frameCounter: 1 - }; - function importantize(event) { - var ret = {}; - for (var prop in importantProperties) { - if (prop in event) { - ret[prop] = event[prop]; - } - } - return ret; - } - recorder.devents = recorder.devents.map(importantize); - recorder.tevents = recorder.tevents.map(importantize); - // Write out - alert('Writing out data, remember to save!'); - setTimeout(function() { - document.open(); - document.write(JSON.stringify(recorder)); - document.close(); - }, 0); - return '.'; - }; - } else { - // Load recording - var dataPath = window.location.search.substring(initLocation + init.length); - var baseURL = window.location.toString().replace('://', 'cheez999').split('?')[0].split('/').slice(0, -1).join('/').replace('cheez999', '://'); - if (baseURL[baseURL.length-1] != '/') baseURL += '/'; - var path = baseURL + dataPath; - alert('Loading replay from ' + path); - var request = new XMLHttpRequest(); - request.open('GET', path, false); - request.send(); - var raw = request.responseText; - raw = raw.substring(raw.indexOf('{'), raw.lastIndexOf('}')+1); // remove <html> etc - recorder = JSON.parse(raw); - // prepare to replay - // Start - recorder.frameCounter = 0; // the frame counter is used to know when to replay events - recorder.start = function() { - function count() { - recorder.frameCounter++; - raf(count); - // replay relevant events for this frame - while (recorder.devents.length && recorder.devents[recorder.devents.length-1].frameCounter <= recorder.frameCounter) { - var event = recorder.devents.pop(); - recorder['on' + event.type](event); - } - while (recorder.tevents.length && recorder.tevents[recorder.tevents.length-1].frameCounter <= recorder.frameCounter) { - var event = recorder.tevents.pop(); - recorder['event' + event.type](event); - } - } - count(); - }; - // Math.random - Math.random = function() { - if (recorder.randoms.length > 0) { - return recorder.randoms.pop(); - } else { - recorder.finish(); - throw 'consuming too many values!'; - } - }; - // Date.now, performance.now - recorder.dnow = Date.now; - Date.now = function() { - if (recorder.dnows.length > 0) { - return recorder.dnows.pop(); - } else { - recorder.finish(); - throw 'consuming too many values!'; - } - }; - var pnow = performance.now || performance.webkitNow || performance.mozNow || performance.oNow || performance.msNow || dnow; - recorder.pnow = function() { return pnow.call(performance) }; - performance.now = function() { - if (recorder.pnows.length > 0) { - return recorder.pnows.pop(); - } else { - recorder.finish(); - throw 'consuming too many values!'; - } - }; - // Events - recorder.onEvent = function(which, callback) { - recorder['on' + which] = callback; - }; - recorder.eventCallbacks = {}; - recorder.addListener = function(target, which, callback, arg) { - recorder['event' + which] = callback; - }; - recorder.onFinish = []; - // Benchmarking hooks - emscripten specific - setTimeout(function() { - var totalTime = 0; - var totalSquared = 0; - var iterations = 0; - var maxTime = 0; - var curr = 0; - Module.preMainLoop = function() { - curr = recorder.pnow(); - } - Module.postMainLoop = function() { - var time = recorder.pnow() - curr; - totalTime += time; - totalSquared += time*time; - maxTime = Math.max(maxTime, time); - iterations++; - }; - recorder.onFinish.push(function() { - var mean = totalTime / iterations; - var meanSquared = totalSquared / iterations; - console.log('mean frame : ' + mean + ' ms'); - console.log('frame std dev: ' + Math.sqrt(meanSquared - (mean*mean)) + ' ms'); - console.log('max frame : ' + maxTime + ' ms'); - }); - }); - // Finish - recorder.finish = function() { - recorder.onFinish.forEach(function(finish) { - finish(); - }); - }; - } - recorder.replaying = replaying; - return recorder; -})(); -''' % (window_location, window_location.split('?')[-1]) + open(os.path.join(in_dir, first_js)).read() + ''' -if (typeof nagivator == 'undefined') { - window.runEventLoop(); -} -''') +open(os.path.join(out_dir, first_js), 'w').write( + (open(os.path.join(os.path.dirname(__file__), 'reproduceriter_shell.js')).read() % ( + window_location, window_location.split('?')[-1], on_idle or 'null', dirs_to_drop + ) if shell else '') + + open(os.path.join(os.path.dirname(__file__), 'reproduceriter.js')).read() + + open(os.path.join(in_dir, first_js)).read() + ('\nwindow.runEventLoop();\n' if shell else '') +) print 'done!' diff --git a/tools/reproduceriter_shell.js b/tools/reproduceriter_shell.js new file mode 100644 index 00000000..f425df82 --- /dev/null +++ b/tools/reproduceriter_shell.js @@ -0,0 +1,871 @@ + +var window = { + location: { + toString: function() { + return '%s'; + }, + search: '?%s', + }, + fakeNow: 0, // we don't use Date.now() + rafs: [], + timeouts: [], + uid: 0, + requestAnimationFrame: function(func) { + func.uid = window.uid++; + print('adding raf ' + func.uid); + window.rafs.push(func); + }, + setTimeout: function(func, ms) { + func.uid = window.uid++; + print('adding timeout ' + func.uid); + window.timeouts.push({ + func: func, + when: window.fakeNow + (ms || 0) + }); + window.timeouts.sort(function(x, y) { return y.when - x.when }); + }, + onIdle: %s, + runEventLoop: function() { + // run forever until an exception stops this replay + var iter = 0; + while (1) { + var start = Recorder.dnow(); + print('event loop: ' + (iter++)); + if (window.rafs.length == 0 && window.timeouts.length == 0) { + if (window.onIdle) { + window.onIdle(); + } else { + throw 'main loop is idle!'; + } + } + // rafs + var currRafs = window.rafs; + window.rafs = []; + for (var i = 0; i < currRafs.length; i++) { + var raf = currRafs[i]; + print('calling raf: ' + raf.uid);// + ': ' + raf.toString().substring(0, 50)); + raf(); + } + // timeouts + var now = window.fakeNow; + var timeouts = window.timeouts; + window.timeouts = []; + while (timeouts.length && timeouts[timeouts.length-1].when <= now) { + var timeout = timeouts.pop(); + print('calling timeout: ' + timeout.func.uid);// + ': ' + timeout.func.toString().substring(0, 50)); + timeout.func(); + } + // increment 'time' + window.fakeNow += 16.666; + print('main event loop iteration took ' + (Recorder.dnow() - start) + ' ms'); + } + }, + URL: { + createObjectURL: function(x) { + return x; // the blob itself is returned + }, + revokeObjectURL: function(x) {}, + }, +}; +var setTimeout = window.setTimeout; +var document = { + eventListeners: {}, + addEventListener: function(id, func) { + var listeners = this.eventListeners[id]; + if (!listeners) { + listeners = this.eventListeners[id] = []; + } + listeners.push(func); + }, + callEventListeners: function(id) { + var listeners = this.eventListeners[id]; + if (listeners) { + listeners.forEach(function(listener) { listener() }); + } + }, + getElementById: function(id) { + switch(id) { + case 'canvas': { + if (this.canvas) return this.canvas; + return this.canvas = { + getContext: function(which) { + switch(which) { + case 'experimental-webgl': { + return { + /* ClearBufferMask */ + DEPTH_BUFFER_BIT : 0x00000100, + STENCIL_BUFFER_BIT : 0x00000400, + COLOR_BUFFER_BIT : 0x00004000, + + /* BeginMode */ + POINTS : 0x0000, + LINES : 0x0001, + LINE_LOOP : 0x0002, + LINE_STRIP : 0x0003, + TRIANGLES : 0x0004, + TRIANGLE_STRIP : 0x0005, + TRIANGLE_FAN : 0x0006, + + /* AlphaFunction (not supported in ES20) */ + /* NEVER */ + /* LESS */ + /* EQUAL */ + /* LEQUAL */ + /* GREATER */ + /* NOTEQUAL */ + /* GEQUAL */ + /* ALWAYS */ + + /* BlendingFactorDest */ + ZERO : 0, + ONE : 1, + SRC_COLOR : 0x0300, + ONE_MINUS_SRC_COLOR : 0x0301, + SRC_ALPHA : 0x0302, + ONE_MINUS_SRC_ALPHA : 0x0303, + DST_ALPHA : 0x0304, + ONE_MINUS_DST_ALPHA : 0x0305, + + /* BlendingFactorSrc */ + /* ZERO */ + /* ONE */ + DST_COLOR : 0x0306, + ONE_MINUS_DST_COLOR : 0x0307, + SRC_ALPHA_SATURATE : 0x0308, + /* SRC_ALPHA */ + /* ONE_MINUS_SRC_ALPHA */ + /* DST_ALPHA */ + /* ONE_MINUS_DST_ALPHA */ + + /* BlendEquationSeparate */ + FUNC_ADD : 0x8006, + BLEND_EQUATION : 0x8009, + BLEND_EQUATION_RGB : 0x8009, /* same as BLEND_EQUATION */ + BLEND_EQUATION_ALPHA : 0x883D, + + /* BlendSubtract */ + FUNC_SUBTRACT : 0x800A, + FUNC_REVERSE_SUBTRACT : 0x800B, + + /* Separate Blend Functions */ + BLEND_DST_RGB : 0x80C8, + BLEND_SRC_RGB : 0x80C9, + BLEND_DST_ALPHA : 0x80CA, + BLEND_SRC_ALPHA : 0x80CB, + CONSTANT_COLOR : 0x8001, + ONE_MINUS_CONSTANT_COLOR : 0x8002, + CONSTANT_ALPHA : 0x8003, + ONE_MINUS_CONSTANT_ALPHA : 0x8004, + BLEND_COLOR : 0x8005, + + /* Buffer Objects */ + ARRAY_BUFFER : 0x8892, + ELEMENT_ARRAY_BUFFER : 0x8893, + ARRAY_BUFFER_BINDING : 0x8894, + ELEMENT_ARRAY_BUFFER_BINDING : 0x8895, + + STREAM_DRAW : 0x88E0, + STATIC_DRAW : 0x88E4, + DYNAMIC_DRAW : 0x88E8, + + BUFFER_SIZE : 0x8764, + BUFFER_USAGE : 0x8765, + + CURRENT_VERTEX_ATTRIB : 0x8626, + + /* CullFaceMode */ + FRONT : 0x0404, + BACK : 0x0405, + FRONT_AND_BACK : 0x0408, + + /* DepthFunction */ + /* NEVER */ + /* LESS */ + /* EQUAL */ + /* LEQUAL */ + /* GREATER */ + /* NOTEQUAL */ + /* GEQUAL */ + /* ALWAYS */ + + /* EnableCap */ + /* TEXTURE_2D */ + CULL_FACE : 0x0B44, + BLEND : 0x0BE2, + DITHER : 0x0BD0, + STENCIL_TEST : 0x0B90, + DEPTH_TEST : 0x0B71, + SCISSOR_TEST : 0x0C11, + POLYGON_OFFSET_FILL : 0x8037, + SAMPLE_ALPHA_TO_COVERAGE : 0x809E, + SAMPLE_COVERAGE : 0x80A0, + + /* ErrorCode */ + NO_ERROR : 0, + INVALID_ENUM : 0x0500, + INVALID_VALUE : 0x0501, + INVALID_OPERATION : 0x0502, + OUT_OF_MEMORY : 0x0505, + + /* FrontFaceDirection */ + CW : 0x0900, + CCW : 0x0901, + + /* GetPName */ + LINE_WIDTH : 0x0B21, + ALIASED_POINT_SIZE_RANGE : 0x846D, + ALIASED_LINE_WIDTH_RANGE : 0x846E, + CULL_FACE_MODE : 0x0B45, + FRONT_FACE : 0x0B46, + DEPTH_RANGE : 0x0B70, + DEPTH_WRITEMASK : 0x0B72, + DEPTH_CLEAR_VALUE : 0x0B73, + DEPTH_FUNC : 0x0B74, + STENCIL_CLEAR_VALUE : 0x0B91, + STENCIL_FUNC : 0x0B92, + STENCIL_FAIL : 0x0B94, + STENCIL_PASS_DEPTH_FAIL : 0x0B95, + STENCIL_PASS_DEPTH_PASS : 0x0B96, + STENCIL_REF : 0x0B97, + STENCIL_VALUE_MASK : 0x0B93, + STENCIL_WRITEMASK : 0x0B98, + STENCIL_BACK_FUNC : 0x8800, + STENCIL_BACK_FAIL : 0x8801, + STENCIL_BACK_PASS_DEPTH_FAIL : 0x8802, + STENCIL_BACK_PASS_DEPTH_PASS : 0x8803, + STENCIL_BACK_REF : 0x8CA3, + STENCIL_BACK_VALUE_MASK : 0x8CA4, + STENCIL_BACK_WRITEMASK : 0x8CA5, + VIEWPORT : 0x0BA2, + SCISSOR_BOX : 0x0C10, + /* SCISSOR_TEST */ + COLOR_CLEAR_VALUE : 0x0C22, + COLOR_WRITEMASK : 0x0C23, + UNPACK_ALIGNMENT : 0x0CF5, + PACK_ALIGNMENT : 0x0D05, + MAX_TEXTURE_SIZE : 0x0D33, + MAX_VIEWPORT_DIMS : 0x0D3A, + SUBPIXEL_BITS : 0x0D50, + RED_BITS : 0x0D52, + GREEN_BITS : 0x0D53, + BLUE_BITS : 0x0D54, + ALPHA_BITS : 0x0D55, + DEPTH_BITS : 0x0D56, + STENCIL_BITS : 0x0D57, + POLYGON_OFFSET_UNITS : 0x2A00, + /* POLYGON_OFFSET_FILL */ + POLYGON_OFFSET_FACTOR : 0x8038, + TEXTURE_BINDING_2D : 0x8069, + SAMPLE_BUFFERS : 0x80A8, + SAMPLES : 0x80A9, + SAMPLE_COVERAGE_VALUE : 0x80AA, + SAMPLE_COVERAGE_INVERT : 0x80AB, + + /* GetTextureParameter */ + /* TEXTURE_MAG_FILTER */ + /* TEXTURE_MIN_FILTER */ + /* TEXTURE_WRAP_S */ + /* TEXTURE_WRAP_T */ + + COMPRESSED_TEXTURE_FORMATS : 0x86A3, + + /* HintMode */ + DONT_CARE : 0x1100, + FASTEST : 0x1101, + NICEST : 0x1102, + + /* HintTarget */ + GENERATE_MIPMAP_HINT : 0x8192, + + /* DataType */ + BYTE : 0x1400, + UNSIGNED_BYTE : 0x1401, + SHORT : 0x1402, + UNSIGNED_SHORT : 0x1403, + INT : 0x1404, + UNSIGNED_INT : 0x1405, + FLOAT : 0x1406, + + /* PixelFormat */ + DEPTH_COMPONENT : 0x1902, + ALPHA : 0x1906, + RGB : 0x1907, + RGBA : 0x1908, + LUMINANCE : 0x1909, + LUMINANCE_ALPHA : 0x190A, + + /* PixelType */ + /* UNSIGNED_BYTE */ + UNSIGNED_SHORT_4_4_4_4 : 0x8033, + UNSIGNED_SHORT_5_5_5_1 : 0x8034, + UNSIGNED_SHORT_5_6_5 : 0x8363, + + /* Shaders */ + FRAGMENT_SHADER : 0x8B30, + VERTEX_SHADER : 0x8B31, + MAX_VERTEX_ATTRIBS : 0x8869, + MAX_VERTEX_UNIFORM_VECTORS : 0x8DFB, + MAX_VARYING_VECTORS : 0x8DFC, + MAX_COMBINED_TEXTURE_IMAGE_UNITS : 0x8B4D, + MAX_VERTEX_TEXTURE_IMAGE_UNITS : 0x8B4C, + MAX_TEXTURE_IMAGE_UNITS : 0x8872, + MAX_FRAGMENT_UNIFORM_VECTORS : 0x8DFD, + SHADER_TYPE : 0x8B4F, + DELETE_STATUS : 0x8B80, + LINK_STATUS : 0x8B82, + VALIDATE_STATUS : 0x8B83, + ATTACHED_SHADERS : 0x8B85, + ACTIVE_UNIFORMS : 0x8B86, + ACTIVE_ATTRIBUTES : 0x8B89, + SHADING_LANGUAGE_VERSION : 0x8B8C, + CURRENT_PROGRAM : 0x8B8D, + + /* StencilFunction */ + NEVER : 0x0200, + LESS : 0x0201, + EQUAL : 0x0202, + LEQUAL : 0x0203, + GREATER : 0x0204, + NOTEQUAL : 0x0205, + GEQUAL : 0x0206, + ALWAYS : 0x0207, + + /* StencilOp */ + /* ZERO */ + KEEP : 0x1E00, + REPLACE : 0x1E01, + INCR : 0x1E02, + DECR : 0x1E03, + INVERT : 0x150A, + INCR_WRAP : 0x8507, + DECR_WRAP : 0x8508, + + /* StringName */ + VENDOR : 0x1F00, + RENDERER : 0x1F01, + VERSION : 0x1F02, + + /* TextureMagFilter */ + NEAREST : 0x2600, + LINEAR : 0x2601, + + /* TextureMinFilter */ + /* NEAREST */ + /* LINEAR */ + NEAREST_MIPMAP_NEAREST : 0x2700, + LINEAR_MIPMAP_NEAREST : 0x2701, + NEAREST_MIPMAP_LINEAR : 0x2702, + LINEAR_MIPMAP_LINEAR : 0x2703, + + /* TextureParameterName */ + TEXTURE_MAG_FILTER : 0x2800, + TEXTURE_MIN_FILTER : 0x2801, + TEXTURE_WRAP_S : 0x2802, + TEXTURE_WRAP_T : 0x2803, + + /* TextureTarget */ + TEXTURE_2D : 0x0DE1, + |