diff options
Diffstat (limited to 'src/library_gl.js')
-rw-r--r-- | src/library_gl.js | 1662 |
1 files changed, 1342 insertions, 320 deletions
diff --git a/src/library_gl.js b/src/library_gl.js index 1d5168fb..052226cf 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -1,49 +1,120 @@ -//"use strict"; - -// XXX FIXME Hardcoded '4' in many places, here and in library_SDL, for RGBA +/* + * GL support. See https://github.com/kripken/emscripten/wiki/OpenGL-support + * for current status. + */ var LibraryGL = { $GL: { - hashtable: function(name) { - if (!this._hashtables) { - this._hashtables = {}; + counter: 1, + buffers: {}, + programs: {}, + framebuffers: {}, + renderbuffers: {}, + textures: {}, + uniforms: {}, + shaders: {}, + + packAlignment: 4, // default alignment is 4 bytes + unpackAlignment: 4, // default alignment is 4 bytes + + // Linear lookup in one of the tables (buffers, programs, etc.). TODO: consider using a weakmap to make this faster, if it matters + scan: function(table, object) { + for (var item in table) { + if (table[item] == object) return item; } - if (!(name in this._hashtables)) { - this._hashtables[name] = { - table: {}, - counter: 1, - add: function(obj) { - var id = this.counter++; - this.table[id] = obj; - return id; - }, - get: function(id) { - if( id == 0 ) return null; -#if ASSERTIONS - assert(id < this.counter, "Invalid id " + id + " for the hashtable " + name); -#endif - return this.table[id]; - }, - remove: function(id) { - if( id == 0 ) return; -#if ASSERTIONS - assert(id < this.counter, "Invalid id " + id + " for the hashtable " + name); -#endif - delete this.table[id]; + return 0; + }, + + // Find a token in a shader source string + findToken: function(source, token) { + function isIdentChar(ch) { + if (ch >= 48 && ch <= 57) // 0-9 + return true; + if (ch >= 65 && ch <= 90) // A-Z + return true; + if (ch >= 97 && ch <= 122) // a-z + return true; + return false; + } + var i = -1; + do { + i = source.indexOf(token, i + 1); + if (i < 0) { + break; + } + if (i > 0 && isIdentChar(source[i - 1])) { + continue; + } + i += token.length; + if (i < source.length - 1 && isIdentChar(source[i + 1])) { + continue; + } + return true; + } while (true); + return false; + }, + + getSource: function(shader, count, string, length) { + var source = ''; + for (var i = 0; i < count; ++i) { + var frag; + if (length) { + var len = {{{ makeGetValue('length', 'i*4', 'i32') }}}; + if (len < 0) { + frag = Pointer_stringify({{{ makeGetValue('string', 'i*4', 'i32') }}}); + } else { + frag = Pointer_stringify({{{ makeGetValue('string', 'i*4', 'i32') }}}, len); } - }; + } else { + frag = Pointer_stringify({{{ makeGetValue('string', 'i*4', 'i32') }}}); + } + source += frag; + } + // Let's see if we need to enable the standard derivatives extension + type = Module.ctx.getShaderParameter(GL.shaders[shader], 0x8B4F /* GL_SHADER_TYPE */); + if (type == 0x8B30 /* GL_FRAGMENT_SHADER */) { + if (GL.findToken(source, "dFdx") || + GL.findToken(source, "dFdy") || + GL.findToken(source, "fwidth")) { + source = "#extension GL_OES_standard_derivatives : enable\n" + source; + var extension = Module.ctx.getExtension("OES_standard_derivatives"); +#if GL_DEBUG + if (!extension) { + Module.printErr("Shader attempts to use the standard derivatives extension which is not available."); + } +#endif + } } - return this._hashtables[name]; + return source; }, + + computeImageSize: function(width, height, sizePerPixel, alignment) { + function roundedToNextMultipleOf(x, y) { + return Math.floor((x + y - 1) / y) * y + } + var plainRowSize = width * sizePerPixel; + var alignedRowSize = roundedToNextMultipleOf(plainRowSize, alignment); + return (height <= 0) ? 0 : + ((height - 1) * alignedRowSize + plainRowSize); + } + }, + + glPixelStorei: function(pname, param) { + if (pname == 0x0D05 /* GL_PACK_ALIGNMENT */) { + GL.packAlignment = param; + } else if (pname == 0x0cf5 /* GL_UNPACK_ALIGNMENT */) { + GL.unpackAlignment = param; + } + Module.ctx.pixelStorei(pname, param); }, glGetString: function(name_) { switch(name_) { - case Module.ctx.VENDOR: - case Module.ctx.RENDERER: - case Module.ctx.VERSION: + case 0x1F00 /* GL_VENDOR */: + case 0x1F01 /* GL_RENDERER */: + case 0x1F02 /* GL_VERSION */: return allocate(intArrayFromString(Module.ctx.getParameter(name_)), 'i8', ALLOC_NORMAL); - case 0x1F03: // Extensions + case 0x1F03 /* GL_EXTENSIONS */: return allocate(intArrayFromString(Module.ctx.getSupportedExtensions().join(' ')), 'i8', ALLOC_NORMAL); default: throw 'Failure: Invalid glGetString value: ' + name_; @@ -51,289 +122,674 @@ var LibraryGL = { }, glGetIntegerv: function(name_, p) { - {{{ makeSetValue('p', '0', 'Module.ctx.getParameter(name_)', 'i32') }}}; + var result = Module.ctx.getParameter(name_); + switch (typeof(result)) { + case "number": + {{{ makeSetValue('p', '0', 'result', 'i32') }}}; + break; + case "boolean": + {{{ makeSetValue('p', '0', 'result ? 1 : 0', 'i8') }}}; + break; + case "string": + throw 'Native code calling glGetIntegerv(' + name_ + ') on a name which returns a string!'; + case "object": + if (result === null) { + {{{ makeSetValue('p', '0', '0', 'i32') }}}; + } else if (result instanceof Float32Array || + result instanceof Uint32Array || + result instanceof Int32Array || + result instanceof Array) { + for (var i = 0; i < result.length; ++i) { + {{{ makeSetValue('p', 'i*4', 'result[i]', 'i32') }}}; + } + } else if (result instanceof WebGLBuffer) { + {{{ makeSetValue('p', '0', 'GL.scan(GL.buffers, result)', 'i32') }}}; + } else if (result instanceof WebGLProgram) { + {{{ makeSetValue('p', '0', 'GL.scan(GL.programs, result)', 'i32') }}}; + } else if (result instanceof WebGLFramebuffer) { + {{{ makeSetValue('p', '0', 'GL.scan(GL.framebuffers, result)', 'i32') }}}; + } else if (result instanceof WebGLRenderbuffer) { + {{{ makeSetValue('p', '0', 'GL.scan(GL.renderbuffers, result)', 'i32') }}}; + } else if (result instanceof WebGLTexture) { + {{{ makeSetValue('p', '0', 'GL.scan(GL.textures, result)', 'i32') }}}; + } else { + throw 'Unknown object returned from WebGL getParameter'; + } + break; + case "undefined": + throw 'Native code calling glGetIntegerv(' + name_ + ') and it returns undefined'; + default: + throw 'Why did we hit the default case?'; + } + }, + + glGetFloatv: function(name_, p) { + var result = Module.ctx.getParameter(name_); + switch (typeof(result)) { + case "number": + {{{ makeSetValue('p', '0', 'result', 'float') }}}; + break; + case "boolean": + {{{ makeSetValue('p', '0', 'result ? 1.0 : 0.0', 'float') }}}; + break; + case "string": + {{{ makeSetValue('p', '0', '0', 'float') }}}; + case "object": + if (result === null) { + throw 'Native code calling glGetFloatv(' + name_ + ') and it returns null'; + } else if (result instanceof Float32Array || + result instanceof Uint32Array || + result instanceof Int32Array || + result instanceof Array) { + for (var i = 0; i < result.length; ++i) { + {{{ makeSetValue('p', 'i*4', 'result[i]', 'float') }}}; + } + } else if (result instanceof WebGLBuffer) { + {{{ makeSetValue('p', '0', 'GL.scan(GL.buffers, result)', 'float') }}}; + } else if (result instanceof WebGLProgram) { + {{{ makeSetValue('p', '0', 'GL.scan(GL.programs, result)', 'float') }}}; + } else if (result instanceof WebGLFramebuffer) { + {{{ makeSetValue('p', '0', 'GL.scan(GL.framebuffers, result)', 'float') }}}; + } else if (result instanceof WebGLRenderbuffer) { + {{{ makeSetValue('p', '0', 'GL.scan(GL.renderbuffers, result)', 'float') }}}; + } else if (result instanceof WebGLTexture) { + {{{ makeSetValue('p', '0', 'GL.scan(GL.textures, result)', 'float') }}}; + } else { + throw 'Unknown object returned from WebGL getParameter'; + } + break; + case "undefined": + throw 'Native code calling glGetFloatv(' + name_ + ') and it returns undefined'; + default: + throw 'Why did we hit the default case?'; + } + }, + + glGetBooleanv: function(name_, p) { + var result = Module.ctx.getParameter(name_); + switch (typeof(result)) { + case "number": + {{{ makeSetValue('p', '0', 'result != 0', 'i8') }}}; + break; + case "boolean": + {{{ makeSetValue('p', '0', 'result != 0', 'i8') }}}; + break; + case "string": + throw 'Native code calling glGetBooleanv(' + name_ + ') on a name which returns a string!'; + case "object": + if (result === null) { + {{{ makeSetValue('p', '0', '0', 'i8') }}}; + } else if (result instanceof Float32Array || + result instanceof Uint32Array || + result instanceof Int32Array || + result instanceof Array) { + for (var i = 0; i < result.length; ++i) { + {{{ makeSetValue('p', 'i', 'result[i] != 0', 'i8') }}}; + } + } else if (result instanceof WebGLBuffer || + result instanceof WebGLProgram || + result instanceof WebGLFramebuffer || + result instanceof WebGLRenderbuffer || + result instanceof WebGLTexture) { + {{{ makeSetValue('p', '0', '1', 'i8') }}}; // non-zero ID is always 1! + } else { + throw 'Unknown object returned from WebGL getParameter'; + } + break; + case "undefined": + throw 'Unknown object returned from WebGL getParameter'; + default: + throw 'Why did we hit the default case?'; + } }, - glGenTextures__deps: ['$GL'], glGenTextures: function(n, textures) { for (var i = 0; i < n; i++) { - var id = GL.hashtable("texture").add(Module.ctx.createTexture()); - {{{ makeSetValue('textures', 'i', 'id', 'i32') }}}; + var id = GL.counter++; + GL.textures[id] = Module.ctx.createTexture(); + {{{ makeSetValue('textures', 'i*4', 'id', 'i32') }}}; } }, glDeleteTextures: function(n, textures) { for (var i = 0; i < n; i++) { - var id = {{{ makeGetValue('textures', 'i', 'i32') }}}; - Module.ctx.deleteTexture(GL.hashtable("texture").get(id)); - GL.hashtable("texture").remove(id); + var id = {{{ makeGetValue('textures', 'i*4', 'i32') }}}; + Module.ctx.deleteTexture(GL.textures[id]); + GL.textures[id] = null; + } + }, + + glCompressedTexImage2D: function(target, level, internalformat, width, height, border, imageSize, data) { + if (data) { + data = {{{ makeHEAPView('U8', 'data', 'data+imageSize') }}}; + } else { + data = null; } + Module.ctx.compressedTexImage2D(target, level, internalformat, width, height, border, data); + }, + + glCompressedTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, imageSize, data) { + if (data) { + data = {{{ makeHEAPView('U8', 'data', 'data+imageSize') }}}; + } else { + data = null; + } + Module.ctx.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, data); }, glTexImage2D: function(target, level, internalformat, width, height, border, format, type, pixels) { if (pixels) { - pixels = new Uint8Array(Array_copy(pixels, pixels + width*height*4)); // TODO: optimize + var sizePerPixel; + switch (type) { + case 0x1401 /* GL_UNSIGNED_BYTE */: + switch (format) { + case 0x1906 /* GL_ALPHA */: + case 0x1909 /* GL_LUMINANCE */: + sizePerPixel = 1; + break; + case 0x1907 /* GL_RGB */: + sizePerPixel = 3; + break; + case 0x1908 /* GL_RGBA */: + sizePerPixel = 4; + break; + case 0x190A /* GL_LUMINANCE_ALPHA */: + sizePerPixel = 2; + break; + default: + throw 'Invalid format (' + format + ') passed to glTexImage2D'; + } + break; + case 0x8363 /* GL_UNSIGNED_SHORT_5_6_5 */: + case 0x8033 /* GL_UNSIGNED_SHORT_4_4_4_4 */: + case 0x8034 /* GL_UNSIGNED_SHORT_5_5_5_1 */: + sizePerPixel = 2; + break; + default: + throw 'Invalid type (' + type + ') passed to glTexImage2D'; + } + var bytes = GL.computeImageSize(width, height, sizePerPixel, GL.unpackAlignment); + pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}}; + } else { + pixels = null; } Module.ctx.texImage2D(target, level, internalformat, width, height, border, format, type, pixels); }, glTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, type, pixels) { if (pixels) { - pixels = new Uint8Array(Array_copy(pixels, pixels + width*height*4)); // TODO: optimize + var sizePerPixel; + switch (type) { + case 0x1401 /* GL_UNSIGNED_BYTE */: + switch (format) { + case 0x1906 /* GL_ALPHA */: + case 0x1909 /* GL_LUMINANCE */: + sizePerPixel = 1; + break; + case 0x1907 /* GL_RGB */: + sizePerPixel = 3; + break; + case 0x1908 /* GL_RGBA */: + sizePerPixel = 4; + break; + case 0x190A /* GL_LUMINANCE_ALPHA */: + sizePerPixel = 2; + break; + default: + throw 'Invalid format (' + format + ') passed to glTexSubImage2D'; + } + break; + case 0x8363 /* GL_UNSIGNED_SHORT_5_6_5 */: + case 0x8033 /* GL_UNSIGNED_SHORT_4_4_4_4 */: + case 0x8034 /* GL_UNSIGNED_SHORT_5_5_5_1 */: + sizePerPixel = 2; + break; + default: + throw 'Invalid type (' + type + ') passed to glTexSubImage2D'; + } + var bytes = GL.computeImageSize(width, height, sizePerPixel, GL.unpackAlignment); + pixels = {{{ makeHEAPView('U8', 'pixels', 'pixels+bytes') }}}; + } else { + pixels = null; } Module.ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); }, + glReadPixels: function(x, y, width, height, format, type, pixels) { + Module.ctx.readPixels(x, y, width, height, format, type, HEAPU8.subarray(pixels)); + }, + glBindTexture: function(target, texture) { - Module.ctx.bindTexture(target, GL.hashtable("texture").get(texture)); + Module.ctx.bindTexture(target, GL.textures[texture]); + }, + + glGetTexParameterfv: function(target, pname, params) { + {{{ makeSetValue('params', '0', 'Module.getTexParameter(target, pname)', 'float') }}}; + }, + + glGetTexParameteriv: function(target, pname, params) { + {{{ makeSetValue('params', '0', 'Module.getTexParameter(target, pname)', 'i32') }}}; + }, + + glIsTexture: function(texture) { + var fb = GL.textures[texture]; + if (typeof(fb) == 'undefined') { + return 0; + } + return Module.ctx.isTexture(fb); }, - glGenBuffers__deps: ['$GL'], glGenBuffers: function(n, buffers) { for (var i = 0; i < n; i++) { - var id = GL.hashtable("buffer").add(Module.ctx.createBuffer()); - {{{ makeSetValue('buffers', 'i', 'id', 'i32') }}}; + var id = GL.counter++; + GL.buffers[id] = Module.ctx.createBuffer(); + {{{ makeSetValue('buffers', 'i*4', 'id', 'i32') }}}; } }, glDeleteBuffers: function(n, buffers) { for (var i = 0; i < n; i++) { - var id = {{{ makeGetValue('buffers', 'i', 'i32') }}}; - Module.ctx.deleteBuffer(GL.hashtable("buffer").get(id)); - GL.hashtable("buffer").remove(id); + var id = {{{ makeGetValue('buffers', 'i*4', 'i32') }}}; + Module.ctx.deleteBuffer(GL.buffers[id]); + GL.buffers[id] = null; } }, + glGetBufferParameteriv: function(target, value, data) { + {{{ makeSetValue('data', '0', 'Module.ctx.getBufferParameter(target, value)', 'i32') }}}; + }, + glBufferData: function(target, size, data, usage) { - var floatArray = new Float32Array(TypedArray_copy(data, size)); - Module.ctx.bufferData(target, floatArray, usage); + Module.ctx.bufferData(target, HEAPU8.subarray(data, data+size), usage); + }, + + glBufferSubData: function(target, offset, size, data) { + var floatArray = {{{ makeHEAPView('F32', 'data', 'data+size') }}}; + Module.ctx.bufferSubData(target, offset, floatArray); + }, + + glIsBuffer: function(buffer) { + var fb = GL.buffers[buffer]; + if (typeof(fb) == 'undefined') { + return 0; + } + return Module.ctx.isBuffer(fb); + }, + + glGenRenderbuffers: function(n, renderbuffers) { + for (var i = 0; i < n; i++) { + var id = GL.counter++; + GL.renderbuffers[id] = Module.ctx.createRenderbuffer(); + {{{ makeSetValue('renderbuffers', 'i*4', 'id', 'i32') }}}; + } + }, + + glDeleteRenderbuffers: function(n, renderbuffers) { + for (var i = 0; i < n; i++) { + var id = {{{ makeGetValue('renderbuffers', 'i*4', 'i32') }}}; + Module.ctx.deleteRenderbuffer(GL.renderbuffers[id]); + GL.renderbuffers[id]; + } + }, + + glBindRenderbuffer: function(target, renderbuffer) { + Module.ctx.bindRenderbuffer(target, GL.renderbuffers[renderbuffer]); + }, + + glGetRenderbufferParameteriv: function(target, pname, params) { + {{{ makeSetValue('params', '0', 'Module.ctx.getRenderbufferParameter(target, pname)', 'i32') }}}; + }, + + glIsRenderbuffer: function(renderbuffer) { + var fb = GL.renderbuffers[renderbuffer]; + if (typeof(fb) == 'undefined') { + return 0; + } + return Module.ctx.isRenderbuffer(fb); + }, + + glGetUniformfv: function(program, location, params) { + var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]); + if (typeof data == 'number') { + {{{ makeSetValue('params', '0', 'data', 'float') }}}; + } else { + for (var i = 0; i < data.length; i++) { + {{{ makeSetValue('params', 'i', 'data[i]', 'float') }}}; + } + } + }, + + glGetUniformiv: function(program, location, params) { + var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]); + if (typeof data == 'number' || typeof data == 'boolean') { + {{{ makeSetValue('params', '0', 'data', 'i32') }}}; + } else { + for (var i = 0; i < data.length; i++) { + {{{ makeSetValue('params', 'i', 'data[i]', 'i32') }}}; + } + } }, - glBindAttribLocation_deps: ['$GL'], glGetUniformLocation: function(program, name) { name = Pointer_stringify(name); - return GL.hashtable("uniform").add( - Module.ctx.getUniformLocation(GL.hashtable("program").get(program), name)); + var loc = Module.ctx.getUniformLocation(GL.programs[program], name); + if (!loc) return -1; + var id = GL.counter++; + GL.uniforms[id] = loc; + return id; + }, + + glGetVertexAttribfv: function(index, pname, params) { + var data = Module.ctx.getVertexAttrib(index, pname); + if (typeof data == 'number') { + {{{ makeSetValue('params', '0', 'data', 'float') }}}; + } else { + for (var i = 0; i < data.length; i++) { + {{{ makeSetValue('params', 'i', 'data[i]', 'float') }}}; + } + } + }, + + glGetVertexAttribiv: function(index, pname, params) { + var data = Module.ctx.getVertexAttrib(index, pname); + if (typeof data == 'number' || typeof data == 'boolean') { + {{{ makeSetValue('params', '0', 'data', 'i32') }}}; + } else { + for (var i = 0; i < data.length; i++) { + {{{ makeSetValue('params', 'i', 'data[i]', 'i32') }}}; + } + } + }, + + glGetVertexAttribPointerv: function(index, pname, pointer) { + {{{ makeSetValue('pointer', '0', 'Module.ctx.getVertexAttribOffset(index, pname)', 'i32') }}}; + }, + + glGetActiveUniform: function(program, index, bufSize, length, size, type, name) { + program = GL.programs[program]; + var info = Module.ctx.getActiveUniform(program, index); + + var infoname = info.name.slice(0, bufsize - 1); + writeStringToMemory(infoname, name); + + if (length) { + {{{ makeSetValue('length', '0', 'infoname.length', 'i32') }}}; + } + if (size) { + {{{ makeSetValue('size', '0', 'info.size', 'i32') }}}; + } + if (type) { + {{{ makeSetValue('type', '0', 'info.type', 'i32') }}}; + } }, - glUniform1f: function(Location, v0) { - Location = GL.hashtable("uniform").get(Location); - Module.ctx.uniform1f(Location, v0); + glUniform1f: function(location, v0) { + location = GL.uniforms[location]; + Module.ctx.uniform1f(location, v0); }, - glUniform2f: function(Location, v0, v1) { - Location = GL.hashtable("uniform").get(Location); - Module.ctx.uniform2f(Location, v0, v1); + glUniform2f: function(location, v0, v1) { + location = GL.uniforms[location]; + Module.ctx.uniform2f(location, v0, v1); }, - glUniform3f: function(Location, v0, v1, v2) { - Location = GL.hashtable("uniform").get(Location); - Module.ctx.uniform3f(Location, v0, v1, v2); + glUniform3f: function(location, v0, v1, v2) { + location = GL.uniforms[location]; + Module.ctx.uniform3f(location, v0, v1, v2); }, - glUniform4f: function(Location, v0, v1, v2, v3) { - Location = GL.hashtable("uniform").get(Location); - Module.ctx.uniform4f(Location, v0, v1, v2, v3); + glUniform4f: function(location, v0, v1, v2, v3) { + location = GL.uniforms[location]; + Module.ctx.uniform4f(location, v0, v1, v2, v3); }, - glUniform1i: function(Location, v0) { - Location = GL.hashtable("uniform").get(Location); - Module.ctx.uniform1i(Location, v0); + glUniform1i: function(location, v0) { + location = GL.uniforms[location]; + Module.ctx.uniform1i(location, v0); }, - glUniform2i: function(Location, v0, v1) { - Location = GL.hashtable("uniform").get(Location); - Module.ctx.uniform2i(Location, v0, v1); + glUniform2i: function(location, v0, v1) { + location = GL.uniforms[location]; + Module.ctx.uniform2i(location, v0, v1); }, - glUniform3i: function(Location, v0, v1, v2) { - Location = GL.hashtable("uniform").get(Location); - Module.ctx.uniform3i(Location, v0, v1, v2); + glUniform3i: function(location, v0, v1, v2) { + location = GL.uniforms[location]; + Module.ctx.uniform3i(location, v0, v1, v2); }, - glUniform4i: function(Location, v0, v1, v2, v3) { - Location = GL.hashtable("uniform").get(Location); - Module.ctx.uniform4i(Location, v0, v1, v2, v3); + glUniform4i: function(location, v0, v1, v2, v3) { + location = GL.uniforms[location]; + Module.ctx.uniform4i(location, v0, v1, v2, v3); }, - glUniform1fv: function(Location, count, value) { - Location = GL.hashtable("uniform").get(Location); - value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize - Module.ctx.uniform1fv(Location, value); + glUniform1iv: function(location, count, value) { + location = GL.uniforms[location]; + value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}}; + Module.ctx.uniform1iv(location, value); }, - glUniform2fv: function(Location, count, value) { - Location = GL.hashtable("uniform").get(Location); + glUniform2iv: function(location, count, value) { + location = GL.uniforms[location]; count *= 2; - value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize - Module.ctx.uniform2fv(Location, value); + value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}}; + Module.ctx.uniform2iv(location, value); }, - glUniform3fv: function(Location, count, value) { - Location = GL.hashtable("uniform").get(Location); + glUniform3iv: function(location, count, value) { + location = GL.uniforms[location]; count *= 3; - value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize - Module.ctx.uniform3fv(Location, value); + value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}}; + Module.ctx.uniform3iv(location, value); }, - glUniform4fv: function(Location, count, value) { - Location = GL.hashtable("uniform").get(Location); + glUniform4iv: function(location, count, value) { + location = GL.uniforms[location]; count *= 4; - value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize - Module.ctx.uniform4fv(Location, value); + value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}}; + Module.ctx.uniform4iv(location, value); }, - glUniform1fi: function(Location, count, value) { - Location = GL.hashtable("uniform").get(Location); - value = new Uint32Array(TypedArray_copy(value, count*4)); // TODO: optimize - Module.ctx.uniform1fi(Location, value); + glUniform1fv: function(location, count, value) { + location = GL.uniforms[location]; + value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; + Module.ctx.uniform1fv(location, value); }, - glUniform2fi: function(Location, count, value) { - Location = GL.hashtable("uniform").get(Location); + glUniform2fv: function(location, count, value) { + location = GL.uniforms[location]; count *= 2; - value = new Uint32Array(TypedArray_copy(value, count*4)); // TODO: optimize - Module.ctx.uniform2fi(Location, value); + value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; + Module.ctx.uniform2fv(location, value); }, - glUniform3fi: function(Location, count, value) { - Location = GL.hashtable("uniform").get(Location); + glUniform3fv: function(location, count, value) { + location = GL.uniforms[location]; count *= 3; - value = new Uint32Array(TypedArray_copy(value, count*4)); // TODO: optimize - Module.ctx.uniform3fi(Location, value); + value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; + Module.ctx.uniform3fv(location, value); }, - glUniform4fi: function(Location, count, value) { - Location = GL.hashtable("uniform").get(Location); + glUniform4fv: function(location, count, value) { + location = GL.uniforms[location]; count *= 4; - value = new Uint32Array(TypedArray_copy(value, count*4)); // TODO: optimize - Module.ctx.uniform4fi(Location, value); + value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; + Module.ctx.uniform4fv(location, value); }, - glUniformMatrix2fv: function(Location, count, transpose, value) { - Location = GL.hashtable("uniform").get(Location); + glUniformMatrix2fv: function(location, count, transpose, value) { + location = GL.uniforms[location]; count *= 4; - value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize - Module.ctx.uniformMatrix2fv(Location, transpose, value); + value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; + Module.ctx.uniformMatrix2fv(location, transpose, value); }, - glUniformMatrix3fv: function(Location, count, transpose, value) { - Location = GL.hashtable("uniform").get(Location); + glUniformMatrix3fv: function(location, count, transpose, value) { + location = GL.uniforms[location]; count *= 9; - value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize - Module.ctx.uniformMatrix3fv(Location, transpose, value); + value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; + Module.ctx.uniformMatrix3fv(location, transpose, value); }, - glUniformMatrix4fv: function(Location, count, transpose, value) { - Location = GL.hashtable("uniform").get(Location); + glUniformMatrix4fv: function(location, count, transpose, value) { + location = GL.uniforms[location]; count *= 16; - value = new Float32Array(TypedArray_copy(value, count*4)); // TODO: optimize - Module.ctx.uniformMatrix4fv(Location, transpose, value); + value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; + Module.ctx.uniformMatrix4fv(location, transpose, value); }, glBindBuffer: function(target, buffer) { - Module.ctx.bindBuffer(target, GL.hashtable("buffer").get(buffer)); + Module.ctx.bindBuffer(target, GL.buffers[buffer]); }, glVertexAttrib1fv: function(index, v) { - v = new Float32Array(TypedArray_copy(v, 1*4)); // TODO: optimize + v = {{{ makeHEAPView('F32', 'v', 'v+1*4') }}}; Module.ctx.vertexAttrib1fv(index, v); }, glVertexAttrib2fv: function(index, v) { - v = new Float32Array(TypedArray_copy(v, 2*4)); // TODO: optimize + v = {{{ makeHEAPView('F32', 'v', 'v+2*4') }}}; Module.ctx.vertexAttrib2fv(index, v); }, glVertexAttrib3fv: function(index, v) { - v = new Float32Array(TypedArray_copy(v, 3*4)); // TODO: optimize + v = {{{ makeHEAPView('F32', 'v', 'v+3*4') }}}; Module.ctx.vertexAttrib3fv(index, v); }, glVertexAttrib4fv: function(index, v) { - v = new Float32Array(TypedArray_copy(v, 4*4)); // TODO: optimize + v = {{{ makeHEAPView('F32', 'v', 'v+4*4') }}}; Module.ctx.vertexAttrib4fv(index, v); }, glGetAttribLocation: function(program, name) { - program = GL.hashtable("program").get(program); + program = GL.programs[program]; name = Pointer_stringify(name); - Module.ctx.getAttribLocation(program, name); + return Module.ctx.getAttribLocation(program, name); + }, + + glGetActiveAttrib: function(program, index, bufSize, length, size, type, name) { + program = GL.programs[program]; + var info = Module.ctx.getActiveAttrib(program, index); + + var infoname = info.name.slice(0, bufsize - 1); + writeStringToMemory(infoname, name); + + if (length) { + {{{ makeSetValue('length', '0', 'infoname.length', 'i32') }}}; + } + if (size) { + {{{ makeSetValue('size', '0', 'info.size', 'i32') }}}; + } + if (type) { + {{{ makeSetValue('type', '0', 'info.type', 'i32') }}}; + } }, - glCreateShader_deps: ['$GL'], glCreateShader: function(shaderType) { - var shader = Module.ctx.createShader(shaderType); - return GL.hashtable("shader").add(shader); + var id = GL.counter++; + GL.shaders[id] = Module.ctx.createShader(shaderType); + return id; + }, + + glDeleteShader: function(shader) { + Module.ctx.deleteShader(GL.shaders[shader]); + GL.shaders[shader] = null; + }, + + glDetachShader: function(program, shader) { + Module.ctx.detachShader(GL.programs[program], + GL.shaders[shader]); + }, + + glGetAttachedShaders: function(program, maxCount, count, shaders) { + var result = Module.ctx.getAttachedShaders(GL.programs[program]); + var len = result.length; + if (len > maxCount) { + len = maxCount; + } + {{{ makeSetValue('count', '0', 'len', 'i32') }}}; + for (var i = 0; i < len; ++i) { + {{{ makeSetValue('shaders', 'i*4', 'GL.shaders[result[i]]', 'i32') }}}; + } }, - glShaderSource_deps: ['$GL'], glShaderSource: function(shader, count, string, length) { - var source = ""; - for (var i = 0; i < count; ++i) { - var frag = string[i]; - if (length) { - var len = {{{ makeGetValue('length', 'i', 'i32') }}}; - if (len < 0) { - frag = Pointer_stringify({{{ makeGetValue('string', 'i', 'i32') }}}); - } else { - frag = Pointer_stringify({{{ makeGetValue('string', 'i', 'i32') }}}, len); - } - } else { - frag = Pointer_stringify({{{ makeGetValue('string', 'i', 'i32') }}}); - } - if (source.length) { - source += "\n"; - } - source += frag; + var source = GL.getSource(shader, count, string, length); + Module.ctx.shaderSource(GL.shaders[shader], source); + }, + + glGetShaderSource: function(shader, bufsize, length, source) { + var result = Module.ctx.getShaderSource(GL.shaders[shader]); + result.slice(0, bufsize - 1); + writeStringToMemory(result, source); + if (length) { + {{{ makeSetValue('length', '0', 'result.length', 'i32') }}}; } - Module.ctx.shaderSource(GL.hashtable("shader").get(shader), source); }, - glCompileShader_deps: ['$GL'], glCompileShader: function(shader) { - Module.ctx.compileShader(GL.hashtable("shader").get(shader)); + Module.ctx.compileShader(GL.shaders[shader]); }, - glGetShaderInfoLog_deps: ['$GL'], glGetShaderInfoLog: function(shader, maxLength, length, infoLog) { - var log = Module.ctx.getShaderInfoLog(GL.hashtable("shader").get(shader)); - log.slice(0, maxLength - 1); + var log = Module.ctx.getShaderInfoLog(GL.shaders[shader]); + // Work around a bug in Chromium which causes getShaderInfoLog to return null + if (!log) { + log = ""; + } + log = log.substr(0, maxLength - 1); writeStringToMemory(log, infoLog); if (length) { - {{{ makeSetValue('length', 'i', 'log.length', 'i32') }}} + {{{ makeSetValue('length', '0', 'log.length', 'i32') }}} } }, - - glGetShaderiv_deps: ['$GL'], - glGetShaderiv : function(shader, pname, p) { - {{{ makeSetValue('p', '0', 'Module.ctx.getShaderParameter(GL.hashtable("shader").get(shader),pname)', 'i32') }}}; + + glGetShaderiv : function(shader, pname, p) { + {{{ makeSetValue('p', '0', 'Module.ctx.getShaderParameter(GL.shaders[shader], pname)', 'i32') }}}; + }, + + glGetProgramiv : function(program, pname, p) { + {{{ makeSetValue('p', '0', 'Module.ctx.getProgramParameter(GL.programs[program], pname)', 'i32') }}}; }, - glGetProgramiv_deps: ['$GL'], - glGetProgramiv : function(program, pname, p) { - {{{ makeSetValue('p', '0', 'Module.ctx.getProgramParameter(GL.hashtable("program").get(program),pname)', 'i32') }}}; + glIsShader: function(shader) { + var fb = GL.shaders[shader]; + if (typeof(fb) == 'undefined') { + return 0; + } + return Module.ctx.isShader(fb); }, - glCreateProgram_deps: ['$GL'], glCreateProgram: function() { - return GL.hashtable("program").add(Module.ctx.createProgram()); + var id = GL.counter++; + GL.programs[id] = Module.ctx.createProgram(); + return id; + }, + + glDeleteProgram: function(program) { + Module.ctx.deleteProgram(GL.programs[program]); + GL.programs[program] = null; }, - glAttachShader_deps: ['$GL'], glAttachShader: function(program, shader) { - Module.ctx.attachShader(GL.hashtable("program").get(program), - GL.hashtable("shader").get(shader)); + Module.ctx.attachShader(GL.programs[program], + GL.shaders[shader]); + }, + + glGetShaderPrecisionFormat: function(shaderType, precisionType, range, precision) { + var result = Module.ctx.getShaderPrecisionFormat(shaderType, precisionType); + {{{ makeSetValue('range', '0', 'result.rangeMin', 'i32') }}}; + {{{ makeSetValue('range', '4', 'result.rangeMax', 'i32') }}}; + {{{ makeSetValue('precision', '0', 'result.precision', 'i32') }}}; }, - glLinkProgram_deps: ['$GL'], glLinkProgram: function(program) { - Module.ctx.linkProgram(GL.hashtable("program").get(program)); + Module.ctx.linkProgram(GL.programs[program]); }, - glGetProgramInfoLog_deps: ['$GL'], glGetProgramInfoLog: function(program, maxLength, length, infoLog) { - var log = Module.ctx.getProgramInfoLog(GL.hashtable("program").get(program)); + var log = Module.ctx.getProgramInfoLog(GL.programs[program]); // Work around a bug in Chromium which causes getProgramInfoLog to return null if (!log) { log = ""; @@ -341,224 +797,790 @@ var LibraryGL = { log = log.substr(0, maxLength - 1); writeStringToMemory(log, infoLog); if (length) { - {{{ makeSetValue('length', 'i', 'log.length', 'i32') }}} + {{{ makeSetValue('length', '0', 'log.length', 'i32') }}} } }, - glUseProgram_deps: ['$Gl'], glUseProgram: function(program) { - Module.ctx.useProgram(GL.hashtable("program").get(program)); + Module.ctx.useProgram(GL.programs[program]); + }, + + glValidateProgram: function(program) { + Module.ctx.validateProgram(GL.programs[program]); + }, + + glIsProgram: function(program) { + var fb = GL.programs[program]; + if (typeof(fb) == 'undefined') { + return 0; + } + return Module.ctx.isProgram(fb); }, - glBindAttribLocation_deps: ['$GL'], glBindAttribLocation: function(program, index, name) { name = Pointer_stringify(name); - Module.ctx.bindAttribLocation(GL.hashtable("program").get(program), index, name); + Module.ctx.bindAttribLocation(GL.programs[program], index, name); }, - glBindFramebuffer_deps: ['$GL'], glBindFramebuffer: function(target, framebuffer) { - Module.ctx.bindFramebuffer(target, GL.hashtable("framebuffer").get( |