diff options
34 files changed, 4086 insertions, 164 deletions
@@ -181,16 +181,18 @@ Options that are modified or new in %s include: tools/shared.py and also src/settings.js.) Note: Optimizations are only done when compiling to JavaScript, not to intermediate - bitcode. + bitcode, *unless* you build with + EMCC_OPTIMIZE_NORMALLY=1 (not recommended + unless you know what you are doing!) -O2 As -O1, plus the relooper (loop recreation), plus LLVM -O2 optimizations -O3 As -O2, plus dangerous optimizations that may break the generated code! This adds - -s INLINING_LIMIT=0 -s DOUBLE_MODE=0 -s PRECISE_I64_MATH=0 --closure 1 + --llvm-lto 1 This is not recommended at all. A better idea is to try each of these separately on top of @@ -233,8 +235,8 @@ Options that are modified or new in %s include: 2: -O2 LLVM optimizations 3: -O3 LLVM optimizations (default in -O2+) - --llvm-lto <level> 0: No LLVM LTO (default in -O0) - 1: LLVM LTO (default in -O1+) + --llvm-lto <level> 0: No LLVM LTO (default in -O2 and below) + 1: LLVM LTO (default in -O3) Note: If LLVM optimizations are not run (see --llvm-opts), setting this to 1 has no effect. @@ -400,6 +402,25 @@ Options that are modified or new in %s include: for a later incremental build (where you also enable it) to be sped up. + Caching works separately on 4 parts of compilation: + 'pre' which is types and global variables; that + information is then fed into 'funcs' which are + the functions (which we parallelize), and then + 'post' which adds final information based on + the functions (e.g., do we need long64 support + code). Finally, 'jsfuncs' are JavaScript-level + optimizations. Each of the 4 parts can be cached + separately, but note that they can affect each + other: If you recompile a single C++ file that + changes a global variable - e.g., adds, removes + or modifies a global variable, say by adding + a printf or by adding a compile-time timestamp, + then 'pre' cannot be loaded from the cache. And + since 'pre's output is sent to 'funcs' and 'post', + they will get invalidated as well, and only + 'jsfuncs' will be cached. So avoid modifying + globals to let caching work fully. + --clear-cache Manually clears the cache of compiled emscripten system libraries (libc++, libc++abi, libc). This is normally @@ -795,7 +816,7 @@ try: newargs = newargs + [default_cxx_std] if llvm_opts is None: llvm_opts = LLVM_OPT_LEVEL[opt_level] - if llvm_lto is None: llvm_lto = llvm_opts > 0 + if llvm_lto is None: llvm_lto = opt_level >= 3 if opt_level <= 0: keep_llvm_debug = keep_js_debug = True # always keep debug in -O0 if opt_level > 0: keep_llvm_debug = False # JS optimizer wipes out llvm debug info from being visible if closure is None and opt_level == 3: closure = True @@ -990,7 +1011,15 @@ try: # If we were just asked to generate bitcode, stop there if final_suffix not in JS_CONTAINING_SUFFIXES: if llvm_opts > 0: - print >> sys.stderr, 'emcc: warning: -Ox flags ignored, since not generating JavaScript' + if not os.environ.get('EMCC_OPTIMIZE_NORMALLY'): + print >> sys.stderr, 'emcc: warning: -Ox flags ignored, since not generating JavaScript' + else: + for input_file in input_files: + if input_file.endswith(SOURCE_SUFFIXES): + if DEBUG: print >> sys.stderr, 'emcc: optimizing %s with -O%d since EMCC_OPTIMIZE_NORMALLY defined' % (input_file, llvm_opts) + shared.Building.llvm_opt(in_temp(unsuffixed(uniquename(input_file)) + '.o'), llvm_opts) + else: + if DEBUG: print >> sys.stderr, 'emcc: not optimizing %s despite EMCC_OPTIMIZE_NORMALLY since not source code' % (input_file) if not specified_target: for input_file in input_files: shutil.move(in_temp(unsuffixed(uniquename(input_file)) + '.o'), unsuffixed_basename(input_file) + '.' + final_suffix) @@ -1155,9 +1184,12 @@ try: if not LEAVE_INPUTS_RAW: link_opts = [] if keep_llvm_debug else ['-strip-debug'] # remove LLVM debug info in -O1+, since the optimizer removes it anyhow if llvm_opts > 0: - shared.Building.llvm_opt(in_temp(target_basename + '.bc'), llvm_opts) - if DEBUG: save_intermediate('opt', 'bc') - # Do LTO in a separate pass to work around LLVM bug XXX (see failure e.g. in cubescript) + if not os.environ.get('EMCC_OPTIMIZE_NORMALLY'): + shared.Building.llvm_opt(in_temp(target_basename + '.bc'), llvm_opts) + if DEBUG: save_intermediate('opt', 'bc') + # Do LTO in a separate pass to work around LLVM bug XXX (see failure e.g. in cubescript) + else: + if DEBUG: print >> sys.stderr, 'emcc: not running opt because EMCC_OPTIMIZE_NORMALLY was specified, opt should have been run before' if shared.Building.can_build_standalone(): # If we can LTO, do it before dce, since it opens up dce opportunities if llvm_lto and shared.Building.can_use_unsafe_opts(): diff --git a/emscripten.py b/emscripten.py index 704e2b25..8bd1b58a 100755 --- a/emscripten.py +++ b/emscripten.py @@ -242,6 +242,8 @@ def emscript(infile, settings, outfile, libraries=[]): if jcache: outputs += cached_outputs # TODO: preserve order outputs = [output.split('//FORWARDED_DATA:') for output in outputs] + for output in outputs: + assert len(output) == 2, 'Did not receive forwarded data in an output - process failed? We only got: ' + output[1] if DEBUG: print >> sys.stderr, ' emscript: phase 2 took %s seconds' % (time.time() - t) if DEBUG: t = time.time() @@ -310,7 +312,7 @@ def emscript(infile, settings, outfile, libraries=[]): post_file = temp_files.get('.post.ll').name open(post_file, 'w').write('\n') # no input, just processing of forwarded data out = shared.run_js(compiler, shared.COMPILER_ENGINE, [settings_file, post_file, 'post', forwarded_file] + libraries, stdout=subprocess.PIPE, cwd=path_from_root('src')) - post, last_forwarded_data = out.split('//FORWARDED_DATA:') + post, last_forwarded_data = out.split('//FORWARDED_DATA:') # if this fails, perhaps the process failed prior to printing forwarded data? last_forwarded_json = json.loads(last_forwarded_data) if settings.get('ASM_JS'): @@ -345,6 +347,7 @@ def emscript(infile, settings, outfile, libraries=[]): asm_setup += '\n'.join(['var %s = %s;' % (f.replace('.', '_'), f) for f in math_envs]) basic_funcs = ['abort', 'assert', 'asmPrintInt', 'asmPrintFloat', 'copyTempDouble', 'copyTempFloat'] + [m.replace('.', '_') for m in math_envs] if settings['SAFE_HEAP']: basic_funcs += ['SAFE_HEAP_LOAD', 'SAFE_HEAP_STORE', 'SAFE_HEAP_CLEAR'] + if settings['CHECK_HEAP_ALIGN']: basic_funcs += ['CHECK_ALIGN_2', 'CHECK_ALIGN_4', 'CHECK_ALIGN_8'] basic_vars = ['STACKTOP', 'STACK_MAX', 'tempDoublePtr', 'ABORT'] basic_float_vars = ['NaN', 'Infinity'] if forwarded_json['Types']['preciseI64MathUsed']: diff --git a/src/analyzer.js b/src/analyzer.js index c930231f..f9b0c5af 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -18,6 +18,7 @@ function recomputeLines(func) { // Handy sets var BRANCH_INVOKE = set('branch', 'invoke'); +var LABEL_ENDERS = set('branch', 'return'); var SIDE_EFFECT_CAUSERS = set('call', 'invoke', 'atomic'); var UNUNFOLDABLE = set('value', 'structvalue', 'type', 'phiparam'); @@ -88,7 +89,7 @@ function analyzer(data, sidePass) { // Internal line if (!currLabelFinished) { item.functions.slice(-1)[0].labels.slice(-1)[0].lines.push(subItem); // If this line fails, perhaps missing a label? - if (subItem.intertype === 'branch') { + if (subItem.intertype in LABEL_ENDERS) { currLabelFinished = true; } } else { diff --git a/src/compiler.js b/src/compiler.js index 0b43842e..14816f1e 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -185,7 +185,7 @@ if (phase == 'pre') { print('// Note: For maximum-speed code, see "Optimizing Code" on the Emscripten wiki, https://github.com/kripken/emscripten/wiki/Optimizing-Code'); } - if (DOUBLE_MODE || CORRECT_SIGNS || CORRECT_OVERFLOWS || CORRECT_ROUNDINGS) { + if (DOUBLE_MODE || CORRECT_SIGNS || CORRECT_OVERFLOWS || CORRECT_ROUNDINGS || CHECK_HEAP_ALIGN) { print('// Note: Some Emscripten settings may limit the speed of the generated code.'); } } diff --git a/src/jsifier.js b/src/jsifier.js index cb234061..7066f8c5 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -375,6 +375,7 @@ function JSify(data, functionsOnly, givenFunctions) { var ret = [item]; item.JS = 'var ' + item.ident + ';'; // Set the actual value in a postset, since it may be a global variable. We also order by dependencies there + Variables.globals[item.ident].targetIdent = item.value.ident; var value = Variables.globals[item.ident].resolvedAlias = finalizeLLVMParameter(item.value); var fix = ''; if (BUILD_AS_SHARED_LIB == 2 && !item.private_) { @@ -913,7 +914,11 @@ function JSify(data, functionsOnly, givenFunctions) { case VAR_NATIVIZED: if (isNumber(item.ident)) { // Direct write to a memory address; this may be an intentional segfault, if not, it is a bug in the source - return 'throw "fault on write to ' + item.ident + '";'; + if (ASM_JS) { + return 'abort(' + item.ident + ')'; + } else { + return 'throw "fault on write to ' + item.ident + '";'; + } } return item.ident + '=' + value + ';'; // We have the actual value here break; @@ -1221,6 +1226,15 @@ function JSify(data, functionsOnly, givenFunctions) { var impl = item.ident ? getVarImpl(item.funcData, item.ident) : VAR_EMULATED; switch (impl) { case VAR_NATIVIZED: { + if (isNumber(item.ident)) { + item.assignTo = null; + // Direct read from a memory address; this may be an intentional segfault, if not, it is a bug in the source + if (ASM_JS) { + return 'abort(' + item.ident + ')'; + } else { + return 'throw "fault on read from ' + item.ident + '";'; + } + } return value; // We have the actual value here } case VAR_EMULATED: return makeGetValue(value, 0, item.type, 0, item.unsigned, 0, item.align); @@ -1244,7 +1258,7 @@ function JSify(data, functionsOnly, givenFunctions) { makeFuncLineActor('insertvalue', function(item) { assert(item.indexes.length == 1); // TODO: see extractvalue var ret = '(', ident; - if (item.ident === 'undef') { + if (item.ident === '0') { item.ident = 'tempValue'; ret += item.ident + ' = [' + makeEmptyStruct(item.type) + '], '; } @@ -1284,6 +1298,7 @@ function JSify(data, functionsOnly, givenFunctions) { // We cannot compile assembly. See comment in intertyper.js:'Call' assert(ident != 'asm', 'Inline assembly cannot be compiled to JavaScript!'); + ident = Variables.resolveAliasToIdent(ident); var shortident = ident.slice(1); var callIdent = LibraryManager.getRootIdent(shortident); if (callIdent) { diff --git a/src/library.js b/src/library.js index 83e1b115..7f177441 100644 --- a/src/library.js +++ b/src/library.js @@ -6702,6 +6702,9 @@ LibraryManager.library = { pthread_mutexattr_destroy: function() {}, pthread_mutex_lock: function() {}, pthread_mutex_unlock: function() {}, + pthread_mutex_trylock: function() { + return 0; + }, pthread_cond_init: function() {}, pthread_cond_destroy: function() {}, pthread_cond_broadcast: function() {}, @@ -6756,6 +6759,15 @@ LibraryManager.library = { _pthread_key_create.keys[key] = value; }, + pthread_key_delete: ['$ERRNO_CODES'], + pthread_key_delete: function(key) { + if (_pthread_key_create.keys[key]) { + delete _pthread_key_create.keys[key]; + return 0; + } + return ERRNO_CODES.EINVAL; + }, + pthread_cleanup_push: function(routine, arg) { __ATEXIT__.push({ func: function() { Runtime.dynCall('vi', routine, [arg]) } }) _pthread_cleanup_push.level = __ATEXIT__.length; diff --git a/src/library_gc.js b/src/library_gc.js index 083019ca..f6db74d8 100644 --- a/src/library_gc.js +++ b/src/library_gc.js @@ -4,7 +4,7 @@ if (GC_SUPPORT) { EXPORTED_FUNCTIONS['_realloc'] = 1; var LibraryGC = { - $GC__deps: ['sbrk', 'realloc'], + $GC__deps: ['sbrk', 'realloc', 'calloc'], $GC: { ALLOCATIONS_TO_GC: 1*1024*1024, diff --git a/src/library_gl.js b/src/library_gl.js index 995f2358..b0bf9650 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -19,6 +19,28 @@ var LibraryGL = { uniforms: [], shaders: [], +#if FULL_ES2 + clientBuffers: [], + enabledClientBuffers: [], +#endif + currArrayBuffer: 0, + currElementArrayBuffer: 0, + + byteSizeByTypeRoot: 0x1400, // GL_BYTE + byteSizeByType: [ + 1, // GL_BYTE + 1, // GL_UNSIGNED_BYTE + 2, // GL_SHORT + 2, // GL_UNSIGNED_SHORT + 4, // GL_INT + 4, // GL_UNSIGNED_INT + 4, // GL_FLOAT + 2, // GL_2_BYTES + 3, // GL_3_BYTES + 4, // GL_4_BYTES + 8 // GL_DOUBLE + ], + uniformTable: {}, // name => uniform ID. the uID must be identical until relinking, cannot create a new uID each call to glGetUniformLocation packAlignment: 4, // default alignment is 4 bytes @@ -176,12 +198,48 @@ var LibraryGL = { } }, +#if FULL_ES2 + calcBufLength: function(size, type, stride, count) { + if (stride > 0) { + return count * stride; // XXXvlad this is not exactly correct I don't think + } + var typeSize = GL.byteSizeByType[type - GL.byteSizeByTypeRoot]; + return size * typeSize * count; + }, + + preDrawHandleClientVertexAttribBindings: function(count) { + GL.resetBufferBinding = false; + for (var i = 0; i < GL.maxVertexAttribs; ++i) { + if (!GL.enabledClientBuffers[i] || !GL.clientBuffers[i]) continue; + + GL.resetBufferBinding = true; + + var cb = GL.clientBuffers[i]; + + var buf = Module.ctx.createBuffer(); + Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, buf); + Module.ctx.bufferData(Module.ctx.ARRAY_BUFFER, + HEAPU8.subarray(cb.ptr, cb.ptr + GL.calcBufLength(cb.size, cb.type, cb.stride, count)), + Module.ctx.DYNAMIC_DRAW); + Module.ctx.vertexAttribPointer(i, cb.size, cb.type, cb.normalized, cb.stride, 0); + } + }, + + postDrawHandleClientVertexAttribBindings: function() { + if (GL.resetBufferBinding) { + Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, GL.buffers[GL.currArrayBuffer]); + } + }, +#endif + initExtensions: function() { if (GL.initExtensions.done) return; GL.initExtensions.done = true; if (!Module.useWebGL) return; // an app might link both gl and 2d backends + GL.maxVertexAttribs = Module.ctx.getParameter(Module.ctx.MAX_VERTEX_ATTRIBS); + GL.compressionExt = Module.ctx.getExtension('WEBGL_compressed_texture_s3tc') || Module.ctx.getExtension('MOZ_WEBGL_compressed_texture_s3tc') || Module.ctx.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc'); @@ -456,6 +514,9 @@ var LibraryGL = { var id = {{{ makeGetValue('buffers', 'i*4', 'i32') }}}; Module.ctx.deleteBuffer(GL.buffers[id]); GL.buffers[id] = null; + + if (id == GL.currArrayBuffer) GL.currArrayBuffer = 0; + if (id == GL.currElementArrayBuffer) GL.currElementArrayBuffer = 0; } }, @@ -474,11 +535,9 @@ var LibraryGL = { }, glIsBuffer: function(buffer) { - var fb = GL.buffers[buffer]; - if (typeof(fb) == 'undefined') { - return 0; - } - return Module.ctx.isBuffer(fb); + var b = GL.buffers[buffer]; + if (!b) return 0; + return Module.ctx.isBuffer(b); }, glGenRenderbuffers__sig: 'vii', @@ -509,11 +568,9 @@ var LibraryGL = { }, glIsRenderbuffer: function(renderbuffer) { - var fb = GL.renderbuffers[renderbuffer]; - if (typeof(fb) == 'undefined') { - return 0; - } - return Module.ctx.isRenderbuffer(fb); + var rb = GL.renderbuffers[renderbuffer]; + if (!rb) return 0; + return Module.ctx.isRenderbuffer(rb); }, glGetUniformfv: function(program, location, params) { @@ -554,6 +611,11 @@ var LibraryGL = { }, glGetVertexAttribfv: function(index, pname, params) { +#if FULL_ES2 + if (GL.clientBuffers[index]) { + Module.printErr("glGetVertexAttribfv on client-side array: not supported, bad data returned"); + } +#endif var data = Module.ctx.getVertexAttrib(index, pname); if (typeof data == 'number') { {{{ makeSetValue('params', '0', 'data', 'float') }}}; @@ -565,6 +627,11 @@ var LibraryGL = { }, glGetVertexAttribiv: function(index, pname, params) { +#if FULL_ES2 + if (GL.clientBuffers[index]) { + Module.printErr("glGetVertexAttribiv on client-side array: not supported, bad data returned"); + } +#endif var data = Module.ctx.getVertexAttrib(index, pname); if (typeof data == 'number' || typeof data == 'boolean') { {{{ makeSetValue('params', '0', 'data', 'i32') }}}; @@ -576,6 +643,11 @@ var LibraryGL = { }, glGetVertexAttribPointerv: function(index, pname, pointer) { +#if FULL_ES2 + if (GL.clientBuffers[index]) { + Module.printErr("glGetVertexAttribPointer on client-side array: not supported, bad data returned"); + } +#endif {{{ makeSetValue('pointer', '0', 'Module.ctx.getVertexAttribOffset(index, pname)', 'i32') }}}; }, @@ -731,6 +803,12 @@ var LibraryGL = { glBindBuffer__sig: 'vii', glBindBuffer: function(target, buffer) { + if (target == Module.ctx.ARRAY_BUFFER) { + GL.currArrayBuffer = buffer; + } else if (target == Module.ctx.ELEMENT_ARRAY_BUFFER) { + GL.currElementArrayBuffer = buffer; + } + Module.ctx.bindBuffer(target, buffer ? GL.buffers[buffer] : null); }, @@ -858,11 +936,9 @@ var LibraryGL = { }, glIsShader: function(shader) { - var fb = GL.shaders[shader]; - if (typeof(fb) == 'undefined') { - return 0; - } - return Module.ctx.isShader(fb); + var s = GL.shaders[shader]; + if (!s) return 0; + return Module.ctx.isShader(s); }, glCreateProgram__sig: 'i', @@ -975,9 +1051,7 @@ var LibraryGL = { glIsFramebuffer__sig: 'ii', glIsFramebuffer: function(framebuffer) { var fb = GL.framebuffers[framebuffer]; - if (typeof(fb) == 'undefined') { - return 0; - } + if (!fb) return 0; return Module.ctx.isFramebuffer(fb); }, @@ -1348,27 +1422,15 @@ var LibraryGL = { _glBindBuffer = function(target, buffer) { glBindBuffer(target, buffer); if (target == Module.ctx.ARRAY_BUFFER) { - GL.currArrayBuffer = buffer; if (GLEmulation.currentVao) { assert(GLEmulation.currentVao.arrayBuffer == buffer || GLEmulation.currentVao.arrayBuffer == 0 || buffer == 0, 'TODO: support for multiple array buffers in vao'); GLEmulation.currentVao.arrayBuffer = buffer; } } else if (target == Module.ctx.ELEMENT_ARRAY_BUFFER) { - GL.currElementArrayBuffer = buffer; if (GLEmulation.currentVao) GLEmulation.currentVao.elementArrayBuffer = buffer; } }; - var glDeleteBuffers = _glDeleteBuffers; - _glDeleteBuffers = function(n, buffers) { - glDeleteBuffers(n, buffers); - for (var i = 0; i < n; i++) { - var buffer = {{{ makeGetValue('buffers', 'i*4', 'i32') }}}; - if (buffer == GL.currArrayBuffer) GL.currArrayBuffer = 0; - if (buffer == GL.currElementArrayBuffer) GL.currElementArrayBuffer = 0; - } - }; - var glGetFloatv = _glGetFloatv; _glGetFloatv = function(pname, params) { if (pname == 0x0BA6) { // GL_MODELVIEW_MATRIX @@ -1621,17 +1683,6 @@ var LibraryGL = { clientActiveTexture: 0, clientColor: null, - byteSizeByTypeRoot: 0x1400, // GL_BYTE - byteSizeByType: [ - 1, // GL_BYTE - 1, // GL_UNSIGNED_BYTE - 2, // GL_SHORT - 2, // GL_UNSIGNED_SHORT - 4, // GL_INT - 4, // GL_UNSIGNED_INT - 4 // GL_FLOAT - ], - setClientAttribute: function(name, size, type, stride, pointer) { var attrib = this.clientAttributes[name]; attrib.name = name; @@ -1707,7 +1758,7 @@ var LibraryGL = { #endif this.enabledClientAttributes[name] = true; this.setClientAttribute(name, size, type, 0, this.rendererComponentPointer); - this.rendererComponentPointer += size * this.byteSizeByType[type - this.byteSizeByTypeRoot]; + this.rendererComponentPointer += size * GL.byteSizeByType[type - GL.byteSizeByTypeRoot]; } else { this.rendererComponents[name]++; } @@ -1731,7 +1782,7 @@ var LibraryGL = { cacheItem = temp ? temp : (cacheItem[attribute.name] = GL.immediate.rendererCacheItemTemplate.slice()); temp = cacheItem[attribute.size]; cacheItem = temp ? temp : (cacheItem[attribute.size] = GL.immediate.rendererCacheItemTemplate.slice()); - var typeIndex = attribute.type - GL.immediate.byteSizeByTypeRoot; // ensure it starts at 0 to keep the cache items dense + var typeIndex = attribute.type - GL.byteSizeByTypeRoot; // ensure it starts at 0 to keep the cache items dense temp = cacheItem[typeIndex]; cacheItem = temp ? temp : (cacheItem[typeIndex] = GL.immediate.rendererCacheItemTemplate.slice()); } @@ -2167,7 +2218,7 @@ var LibraryGL = { for (var i = 0; i < attributes.length; i++) { var attribute = attributes[i]; if (!attribute) break; - var size = attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot]; + var size = attribute.size * GL.byteSizeByType[attribute.type - GL.byteSizeByTypeRoot]; if (size % 4 != 0) size += 4 - (size % 4); // align everything attribute.offset = bytes; bytes += size; @@ -2179,7 +2230,7 @@ var LibraryGL = { for (var i = 0; i < attributes.length; i++) { var attribute = attributes[i]; if (!attribute) break; - var size4 = Math.floor((attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot])/4); + var size4 = Math.floor((attribute.size * GL.byteSizeByType[attribute.type - GL.byteSizeByTypeRoot])/4); for (var j = 0; j < count; j++) { for (var k = 0; k < size4; k++) { // copy in chunks of 4 bytes, our alignment makes this possible HEAP32[((start + attribute.offset + bytes*j)>>2) + k] = HEAP32[(attribute.pointer>>2) + j*size4 + k]; @@ -2197,7 +2248,7 @@ var LibraryGL = { assert((attribute.offset - bytes)%4 == 0); // XXX assuming 4-alignment bytes += attribute.offset - bytes; } - bytes += attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot]; + bytes += attribute.size * GL.byteSizeByType[attribute.type - GL.byteSizeByTypeRoot]; if (bytes % 4 != 0) bytes += 4 - (bytes % 4); // XXX assuming 4-alignment } assert(beginEnd || bytes <= stride); // if not begin-end, explicit stride should make sense with total byte size @@ -2332,8 +2383,10 @@ var LibraryGL = { glVertex2fv: function(p) { _glVertex3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, 0); }, + + glVertex3i: 'glVertex3f', - glVertex2i: function() { throw 'glVertex2i: TODO' }, + glVertex2i: 'glVertex3f', glTexCoord2i: function(u, v) { #if ASSERTIONS @@ -2794,11 +2847,81 @@ var LibraryGL = { glTexCoord3f: function() { throw 'glTexCoord3f: TODO' }, glGetTexLevelParameteriv: function() { throw 'glGetTexLevelParameteriv: TODO' }, - // signatures of simple pass-through functions, see later - glActiveTexture__sig: 'vi', + glShadeModel: function() { Runtime.warnOnce('TODO: glShadeModel') }, + + glVertexAttribPointer__sig: 'viiiiii', + glVertexAttribPointer: function(index, size, type, normalized, stride, ptr) { +#if FULL_ES2 + if (!GL.currArrayBuffer) { + GL.clientBuffers[index] = { size: size, type: type, normalized: normalized, stride: stride, ptr: ptr }; + return; + } + + GL.clientBuffers[index] = null; +#endif + Module.ctx.vertexAttribPointer(index, size, type, normalized, stride, ptr); + }, + glEnableVertexAttribArray__sig: 'vi', + glEnableVertexAttribArray: function(index) { +#if FULL_ES2 + GL.enabledClientBuffers[index] = true; +#endif + Module.ctx.enableVertexAttribArray(index); + }, + glDisableVertexAttribArray__sig: 'vi', - glVertexAttribPointer__sig: 'viiiiii', + glDisableVertexAttribArray: function(index) { +#if FULL_ES2 + GL.enabledClientBuffers[index] = false; +#endif + Module.ctx.disableVertexAttribArray(index); + }, + + glDrawArrays: function(mode, first, count) { +#if FULL_ES2 + // bind any client-side buffers + GL.preDrawHandleClientVertexAttribBindings(count); +#endif + + Module.ctx.drawArrays(mode, first, count); + +#if FULL_ES2 + GL.postDrawHandleClientVertexAttribBindings(); +#endif + }, + + glDrawElements: function(mode, count, type, indices) { +#if FULL_ES2 + var buf; + if (!GL.currElementArrayBuffer) { + buf = Module.ctx.createBuffer(); + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, buf); + Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER, + HEAPU8.subarray(indices, indices + GL.calcBufLength(1, type, 0, count)), + Module.ctx.DYNAMIC_DRAW); + // the index is now 0 + indices = 0; + } + + // bind any client-side buffers + GL.preDrawHandleClientVertexAttribBindings(count); +#endif + + Module.ctx.drawElements(mode, count, type, indices); + +#if FULL_ES2 + GL.postDrawHandleClientVertexAttribBindings(count); + + if (!GL.currElementArrayBuffer) { + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null); + Module.ctx.deleteBuffer(buf); + } +#endif + }, + + // signatures of simple pass-through functions, see later + glActiveTexture__sig: 'vi', glCheckFramebufferStatus__sig: 'ii', glRenderbufferStorage__sig: 'viiii', @@ -2820,19 +2943,18 @@ var LibraryGL = { }; // Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name -[[0, 'shadeModel getError* finish flush'], - [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear enableVertexAttribArray disableVertexAttribArray lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation sampleCoverage isEnabled*'], +[[0, 'getError* finish flush'], + [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation sampleCoverage isEnabled*'], [2, 'blendFunc blendEq |