diff options
Diffstat (limited to 'src/library_gl.js')
-rw-r--r-- | src/library_gl.js | 2887 |
1 files changed, 2406 insertions, 481 deletions
diff --git a/src/library_gl.js b/src/library_gl.js index 55f9600e..1fa0cc9c 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -19,6 +19,27 @@ var LibraryGL = { uniforms: [], shaders: [], +#if FULL_ES2 + clientBuffers: [], +#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 @@ -37,6 +58,70 @@ var LibraryGL = { return ret; }, + // Mini temp buffer + MINI_TEMP_BUFFER_SIZE: 16, + miniTempBuffer: null, + miniTempBufferViews: [0], // index i has the view of size i+1 + + // Large temporary buffers + MAX_TEMP_BUFFER_SIZE: {{{ GL_MAX_TEMP_BUFFER_SIZE }}}, + tempBufferIndexLookup: null, + tempVertexBuffers: null, + tempIndexBuffers: null, + tempQuadIndexBuffer: null, + + generateTempBuffers: function(quads) { + GL.tempBufferIndexLookup = new Uint8Array(GL.MAX_TEMP_BUFFER_SIZE+1); + GL.tempVertexBuffers = []; + GL.tempIndexBuffers = []; + var last = -1, curr = -1; + var size = 1; + for (var i = 0; i <= GL.MAX_TEMP_BUFFER_SIZE; i++) { + if (i > size) { + size <<= 1; + } + if (size != last) { + curr++; + GL.tempVertexBuffers[curr] = Module.ctx.createBuffer(); + Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, GL.tempVertexBuffers[curr]); + Module.ctx.bufferData(Module.ctx.ARRAY_BUFFER, size, Module.ctx.DYNAMIC_DRAW); + Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, null); + GL.tempIndexBuffers[curr] = Module.ctx.createBuffer(); + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, GL.tempIndexBuffers[curr]); + Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER, size, Module.ctx.DYNAMIC_DRAW); + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null); + last = size; + } + GL.tempBufferIndexLookup[i] = curr; + } + + if (quads) { + // GL_QUAD indexes can be precalculated + GL.tempQuadIndexBuffer = Module.ctx.createBuffer(); + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, GL.tempQuadIndexBuffer); + var numIndexes = GL.MAX_TEMP_BUFFER_SIZE >> 1; + var quadIndexes = new Uint16Array(numIndexes); + var i = 0, v = 0; + while (1) { + quadIndexes[i++] = v; + if (i >= numIndexes) break; + quadIndexes[i++] = v+1; + if (i >= numIndexes) break; + quadIndexes[i++] = v+2; + if (i >= numIndexes) break; + quadIndexes[i++] = v; + if (i >= numIndexes) break; + quadIndexes[i++] = v+2; + if (i >= numIndexes) break; + quadIndexes[i++] = v+3; + if (i >= numIndexes) break; + v += 4; + } + Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER, quadIndexes, Module.ctx.STATIC_DRAW); + Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null); + } + }, + // 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) { @@ -176,12 +261,75 @@ 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; + }, + + usedTempBuffers: [], + + preDrawHandleClientVertexAttribBindings: function(count) { + GL.resetBufferBinding = false; + + var used = GL.usedTempBuffers; + used.length = 0; + + // TODO: initial pass to detect ranges we need to upload, might not need an upload per attrib + for (var i = 0; i < GL.maxVertexAttribs; ++i) { + var cb = GL.clientBuffers[i]; + if (!cb.clientside || !cb.enabled) continue; + + GL.resetBufferBinding = true; + + var size = GL.calcBufLength(cb.size, cb.type, cb.stride, count); + var index = GL.tempBufferIndexLookup[size]; + var buf; + do { +#if ASSERTIONS + assert(index < GL.tempVertexBuffers.length); +#endif + buf = GL.tempVertexBuffers[index++]; + } while (used.indexOf(buf) >= 0); + used.push(buf); + Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, buf); + Module.ctx.bufferSubData(Module.ctx.ARRAY_BUFFER, + 0, + HEAPU8.subarray(cb.ptr, cb.ptr + size)); + 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.miniTempBuffer = new Float32Array(GL.MINI_TEMP_BUFFER_SIZE); + for (var i = 0; i < GL.MINI_TEMP_BUFFER_SIZE; i++) { + GL.miniTempBufferViews[i] = GL.miniTempBuffer.subarray(0, i+1); + } + + GL.maxVertexAttribs = Module.ctx.getParameter(Module.ctx.MAX_VERTEX_ATTRIBS); +#if FULL_ES2 + for (var i = 0; i < GL.maxVertexAttribs; i++) { + GL.clientBuffers[i] = { enabled: false, clientside: false, size: 0, type: 0, normalized: 0, stride: 0, ptr: 0 }; + } + + GL.generateTempBuffers(); +#endif + 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'); @@ -191,9 +339,13 @@ var LibraryGL = { Module.ctx.getExtension('WEBKIT_EXT_texture_filter_anisotropic'); GL.floatExt = Module.ctx.getExtension('OES_texture_float'); + + GL.elementIndexUintExt = Module.ctx.getExtension('OES_element_index_uint'); + GL.standardDerivativesExt = Module.ctx.getExtension('OES_standard_derivatives'); } }, + glPixelStorei__sig: 'vii', glPixelStorei: function(pname, param) { if (pname == 0x0D05 /* GL_PACK_ALIGNMENT */) { GL.packAlignment = param; @@ -203,6 +355,7 @@ var LibraryGL = { Module.ctx.pixelStorei(pname, param); }, + glGetString__sig: 'ii', glGetString: function(name_) { switch(name_) { case 0x1F00 /* GL_VENDOR */: @@ -218,8 +371,9 @@ var LibraryGL = { } }, + glGetIntegerv__sig: 'vii', glGetIntegerv: function(name_, p) { - switch(name_) { // Handle a few trivial GLES values + switch(name_) { // Handle a few trivial GLES values case 0x8DFA: // GL_SHADER_COMPILER {{{ makeSetValue('p', '0', '1', 'i32') }}}; return; @@ -268,6 +422,7 @@ var LibraryGL = { } }, + glGetFloatv__sig: 'vii', glGetFloatv: function(name_, p) { var result = Module.ctx.getParameter(name_); switch (typeof(result)) { @@ -310,6 +465,7 @@ var LibraryGL = { } }, + glGetBooleanv__sig: 'vii', glGetBooleanv: function(name_, p) { var result = Module.ctx.getParameter(name_); switch (typeof(result)) { @@ -348,14 +504,16 @@ var LibraryGL = { } }, + glGenTextures__sig: 'vii', glGenTextures: function(n, textures) { for (var i = 0; i < n; i++) { - var id = GL.getNewId(GL.textures); + var id = GL.getNewId(GL.textures); GL.textures[id] = Module.ctx.createTexture(); {{{ makeSetValue('textures', 'i*4', 'id', 'i32') }}}; } }, + glDeleteTextures__sig: 'vii', glDeleteTextures: function(n, textures) { for (var i = 0; i < n; i++) { var id = {{{ makeGetValue('textures', 'i*4', 'i32') }}}; @@ -364,6 +522,7 @@ var LibraryGL = { } }, + glCompressedTexImage2D__sig: 'viiiiiiii', glCompressedTexImage2D: function(target, level, internalFormat, width, height, border, imageSize, data) { assert(GL.compressionExt); if (data) { @@ -374,6 +533,7 @@ var LibraryGL = { Module.ctx['compressedTexImage2D'](target, level, internalFormat, width, height, border, data); }, + glCompressedTexSubImage2D__sig: 'viiiiiiiii', glCompressedTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, imageSize, data) { assert(GL.compressionExt); if (data) { @@ -384,6 +544,7 @@ var LibraryGL = { Module.ctx['compressedTexSubImage2D'](target, level, xoffset, yoffset, width, height, data); }, + glTexImage2D__sig: 'viiiiiiiii', glTexImage2D: function(target, level, internalFormat, width, height, border, format, type, pixels) { if (pixels) { var data = GL.getTexPixelData(type, format, width, height, pixels, internalFormat); @@ -395,6 +556,7 @@ var LibraryGL = { Module.ctx.texImage2D(target, level, internalFormat, width, height, border, format, type, pixels); }, + glTexSubImage2D__sig: 'viiiiiiiii', glTexSubImage2D: function(target, level, xoffset, yoffset, width, height, format, type, pixels) { if (pixels) { var data = GL.getTexPixelData(type, format, width, height, pixels, -1); @@ -405,30 +567,58 @@ var LibraryGL = { Module.ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); }, + glReadPixels__sig: 'viiiiiii', glReadPixels: function(x, y, width, height, format, type, pixels) { - Module.ctx.readPixels(x, y, width, height, format, type, HEAPU8.subarray(pixels)); + assert(type == 0x1401 /* GL_UNSIGNED_BYTE */); + var sizePerPixel; + switch (format) { + case 0x1907 /* GL_RGB */: + sizePerPixel = 3; + break; + case 0x1908 /* GL_RGBA */: + sizePerPixel = 4; + break; + default: throw 'unsupported glReadPixels format'; + } + var totalSize = width*height*sizePerPixel; + Module.ctx.readPixels(x, y, width, height, format, type, HEAPU8.subarray(pixels, pixels + totalSize)); }, + glBindTexture__sig: 'vii', glBindTexture: function(target, texture) { Module.ctx.bindTexture(target, texture ? GL.textures[texture] : null); }, + glGetTexParameterfv__sig: 'viii', glGetTexParameterfv: function(target, pname, params) { {{{ makeSetValue('params', '0', 'Module.getTexParameter(target, pname)', 'float') }}}; }, + glGetTexParameteriv__sig: 'viii', glGetTexParameteriv: function(target, pname, params) { {{{ makeSetValue('params', '0', 'Module.getTexParameter(target, pname)', 'i32') }}}; }, + glTexParameterfv__sig: 'viii', + glTexParameterfv: function(target, pname, params) { + var param = {{{ makeGetValue('params', '0', 'float') }}}; + Module.ctx.texParameterf(target, pname, param); + }, + + glTexParameteriv__sig: 'viii', + glTexParameteriv: function(target, pname, params) { + var param = {{{ makeGetValue('params', '0', 'i32') }}}; + Module.ctx.texParameteri(target, pname, param); + }, + + glIsTexture__sig: 'ii', glIsTexture: function(texture) { - var fb = GL.textures[texture]; - if (typeof(fb) == 'undefined') { - return 0; - } - return Module.ctx.isTexture(fb); + var texture = GL.textures[texture]; + if (!texture) return 0; + return Module.ctx.isTexture(texture); }, + glGenBuffers__sig: 'vii', glGenBuffers: function(n, buffers) { for (var i = 0; i < n; i++) { var id = GL.getNewId(GL.buffers); @@ -437,34 +627,41 @@ var LibraryGL = { } }, + glDeleteBuffers__sig: 'vii', glDeleteBuffers: function(n, buffers) { for (var i = 0; i < n; i++) { 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; } }, + glGetBufferParameteriv__sig: 'viii', glGetBufferParameteriv: function(target, value, data) { {{{ makeSetValue('data', '0', 'Module.ctx.getBufferParameter(target, value)', 'i32') }}}; }, + glBufferData__sig: 'viiii', glBufferData: function(target, size, data, usage) { Module.ctx.bufferData(target, HEAPU8.subarray(data, data+size), usage); }, + glBufferSubData__sig: 'viiii', glBufferSubData: function(target, offset, size, data) { Module.ctx.bufferSubData(target, offset, HEAPU8.subarray(data, data+size)); }, + glIsBuffer__sig: 'ii', 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', glGenRenderbuffers: function(n, renderbuffers) { for (var i = 0; i < n; i++) { var id = GL.getNewId(GL.renderbuffers); @@ -473,6 +670,7 @@ var LibraryGL = { } }, + glDeleteRenderbuffers__sig: 'vii', glDeleteRenderbuffers: function(n, renderbuffers) { for (var i = 0; i < n; i++) { var id = {{{ makeGetValue('renderbuffers', 'i*4', 'i32') }}}; @@ -481,22 +679,24 @@ var LibraryGL = { } }, + glBindRenderbuffer__sig: 'vii', glBindRenderbuffer: function(target, renderbuffer) { Module.ctx.bindRenderbuffer(target, renderbuffer ? GL.renderbuffers[renderbuffer] : null); }, + glGetRenderbufferParameteriv__sig: 'viii', glGetRenderbufferParameteriv: function(target, pname, params) { {{{ makeSetValue('params', '0', 'Module.ctx.getRenderbufferParameter(target, pname)', 'i32') }}}; }, + glIsRenderbuffer__sig: 'ii', 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__sig: 'viii', glGetUniformfv: function(program, location, params) { var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]); if (typeof data == 'number') { @@ -508,6 +708,7 @@ var LibraryGL = { } }, + glGetUniformiv__sig: 'viii', glGetUniformiv: function(program, location, params) { var data = Module.ctx.getUniform(GL.programs[program], GL.uniforms[location]); if (typeof data == 'number' || typeof data == 'boolean') { @@ -519,12 +720,13 @@ var LibraryGL = { } }, + glGetUniformLocation__sig: 'iii', glGetUniformLocation: function(program, name) { name = Pointer_stringify(name); var ptable = GL.uniformTable[program]; if (!ptable) ptable = GL.uniformTable[program] = {}; var id = ptable[name]; - if (id) return id; + if (id) return id; var loc = Module.ctx.getUniformLocation(GL.programs[program], name); if (!loc) return -1; id = GL.getNewId(GL.uniforms); @@ -533,7 +735,13 @@ var LibraryGL = { return id; }, + glGetVertexAttribfv__sig: 'viii', glGetVertexAttribfv: function(index, pname, params) { +#if FULL_ES2 + if (GL.clientBuffers[index].enabled) { + 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') }}}; @@ -544,7 +752,13 @@ var LibraryGL = { } }, + glGetVertexAttribiv__sig: 'viii', glGetVertexAttribiv: function(index, pname, params) { +#if FULL_ES2 + if (GL.clientBuffers[index].enabled) { + 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') }}}; @@ -555,15 +769,22 @@ var LibraryGL = { } }, + glGetVertexAttribPointerv__sig: 'viii', glGetVertexAttribPointerv: function(index, pname, pointer) { +#if FULL_ES2 + if (GL.clientBuffers[index].enabled) { + Module.printErr("glGetVertexAttribPointer on client-side array: not supported, bad data returned"); + } +#endif {{{ makeSetValue('pointer', '0', 'Module.ctx.getVertexAttribOffset(index, pname)', 'i32') }}}; }, + glGetActiveUniform__sig: 'viiiiiii', 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); + var infoname = info.name.slice(0, Math.max(0, bufSize - 1)); writeStringToMemory(infoname, name); if (length) { @@ -577,52 +798,62 @@ var LibraryGL = { } }, + glUniform1f__sig: 'vif', glUniform1f: function(location, v0) { location = GL.uniforms[location]; Module.ctx.uniform1f(location, v0); }, + glUniform2f__sig: 'viff', glUniform2f: function(location, v0, v1) { location = GL.uniforms[location]; Module.ctx.uniform2f(location, v0, v1); }, + glUniform3f__sig: 'vifff', glUniform3f: function(location, v0, v1, v2) { location = GL.uniforms[location]; Module.ctx.uniform3f(location, v0, v1, v2); }, + glUniform4f__sig: 'viffff', glUniform4f: function(location, v0, v1, v2, v3) { location = GL.uniforms[location]; Module.ctx.uniform4f(location, v0, v1, v2, v3); }, + glUniform1i__sig: 'vii', glUniform1i: function(location, v0) { location = GL.uniforms[location]; Module.ctx.uniform1i(location, v0); }, + glUniform2i__sig: 'viii', glUniform2i: function(location, v0, v1) { location = GL.uniforms[location]; Module.ctx.uniform2i(location, v0, v1); }, + glUniform3i__sig: 'viiii', glUniform3i: function(location, v0, v1, v2) { location = GL.uniforms[location]; Module.ctx.uniform3i(location, v0, v1, v2); }, + glUniform4i__sig: 'viiiii', glUniform4i: function(location, v0, v1, v2, v3) { location = GL.uniforms[location]; Module.ctx.uniform4i(location, v0, v1, v2, v3); }, + glUniform1iv__sig: 'viii', glUniform1iv: function(location, count, value) { location = GL.uniforms[location]; value = {{{ makeHEAPView('32', 'value', 'value+count*4') }}}; Module.ctx.uniform1iv(location, value); }, + glUniform2iv__sig: 'viii', glUniform2iv: function(location, count, value) { location = GL.uniforms[location]; count *= 2; @@ -630,6 +861,7 @@ var LibraryGL = { Module.ctx.uniform2iv(location, value); }, + glUniform3iv__sig: 'viii', glUniform3iv: function(location, count, value) { location = GL.uniforms[location]; count *= 3; @@ -637,6 +869,7 @@ var LibraryGL = { Module.ctx.uniform3iv(location, value); }, + glUniform4iv__sig: 'viii', glUniform4iv: function(location, count, value) { location = GL.uniforms[location]; count *= 4; @@ -644,89 +877,166 @@ var LibraryGL = { Module.ctx.uniform4iv(location, value); }, + glUniform1fv__sig: 'viii', glUniform1fv: function(location, count, value) { location = GL.uniforms[location]; - value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; - Module.ctx.uniform1fv(location, value); + var view; + if (count == 1) { + // avoid allocation for the common case of uploading one uniform + view = GL.miniTempBufferViews[0]; + view[0] = {{{ makeGetValue('value', '0', 'float') }}}; + } else { + view = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; + } + Module.ctx.uniform1fv(location, view); }, + glUniform2fv__sig: 'viii', glUniform2fv: function(location, count, value) { location = GL.uniforms[location]; - count *= 2; - value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; - Module.ctx.uniform2fv(location, value); + var view; + if (count == 1) { + // avoid allocation for the common case of uploading one uniform + view = GL.miniTempBufferViews[1]; + view[0] = {{{ makeGetValue('value', '0', 'float') }}}; + view[1] = {{{ makeGetValue('value', '4', 'float') }}}; + } else { + view = {{{ makeHEAPView('F32', 'value', 'value+count*8') }}}; + } + Module.ctx.uniform2fv(location, view); }, + glUniform3fv__sig: 'viii', glUniform3fv: function(location, count, value) { location = GL.uniforms[location]; - count *= 3; - value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; - Module.ctx.uniform3fv(location, value); + var view; + if (count == 1) { + // avoid allocation for the common case of uploading one uniform + view = GL.miniTempBufferViews[2]; + view[0] = {{{ makeGetValue('value', '0', 'float') }}}; + view[1] = {{{ makeGetValue('value', '4', 'float') }}}; + view[2] = {{{ makeGetValue('value', '8', 'float') }}}; + } else { + view = {{{ makeHEAPView('F32', 'value', 'value+count*12') }}}; + } + Module.ctx.uniform3fv(location, view); }, + glUniform4fv__sig: 'viii', glUniform4fv: function(location, count, value) { location = GL.uniforms[location]; - count *= 4; - value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; - Module.ctx.uniform4fv(location, value); + var view; + if (count == 1) { + // avoid allocation for the common case of uploading one uniform + view = GL.miniTempBufferViews[3]; + view[0] = {{{ makeGetValue('value', '0', 'float') }}}; + view[1] = {{{ makeGetValue('value', '4', 'float') }}}; + view[2] = {{{ makeGetValue('value', '8', 'float') }}}; + view[3] = {{{ makeGetValue('value', '12', 'float') }}}; + } else { + view = {{{ makeHEAPView('F32', 'value', 'value+count*16') }}}; + } + Module.ctx.uniform4fv(location, view); }, + glUniformMatrix2fv__sig: 'viiii', glUniformMatrix2fv: function(location, count, transpose, value) { location = GL.uniforms[location]; - count *= 4; - value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; - Module.ctx.uniformMatrix2fv(location, transpose, value); + var view; + 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++) { + view[i] = {{{ makeGetValue('value', 'i*4', 'float') }}}; + } + } else { + view = {{{ makeHEAPView('F32', 'value', 'value+count*16') }}}; + } + Module.ctx.uniformMatrix2fv(location, transpose, view); }, + glUniformMatrix3fv__sig: 'viiii', glUniformMatrix3fv: function(location, count, transpose, value) { location = GL.uniforms[location]; - count *= 9; - value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; - Module.ctx.uniformMatrix3fv(location, transpose, value); + var view; + 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++) { + view[i] = {{{ makeGetValue('value', 'i*4', 'float') }}}; + } + } else { + view = {{{ makeHEAPView('F32', 'value', 'value+count*36') }}}; + } + Module.ctx.uniformMatrix3fv(location, transpose, view); }, + glUniformMatrix4fv__sig: 'viiii', glUniformMatrix4fv: function(location, count, transpose, value) { location = GL.uniforms[location]; - count *= 16; - value = {{{ makeHEAPView('F32', 'value', 'value+count*4') }}}; - Module.ctx.uniformMatrix4fv(location, transpose, value); + var view; + 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++) { + view[i] = {{{ makeGetValue('value', 'i*4', 'float') }}}; + } + } else { + view = {{{ makeHEAPView('F32', 'value', 'value+count*64') }}}; + } + Module.ctx.uniformMatrix4fv(location, transpose, view); }, + glBindBuffer__sig: 'vii', glBindBuffer: function(target, buffer) { - Module.ctx.bindBuffer(target, buffer ? GL.buffers[buffer] : null); + var bufferObj = buffer ? GL.buffers[buffer] : null; + + if (target == Module.ctx.ARRAY_BUFFER) { + GL.currArrayBuffer = buffer; + } else if (target == Module.ctx.ELEMENT_ARRAY_BUFFER) { + GL.currElementArrayBuffer = buffer; + } + + Module.ctx.bindBuffer(target, bufferObj); }, + glVertexAttrib1fv__sig: 'vii', glVertexAttrib1fv: function(index, v) { - v = {{{ makeHEAPView('F32', 'v', 'v+1*4') }}}; + v = {{{ makeHEAPView('F32', 'v', 'v+' + (1*4)) }}}; Module.ctx.vertexAttrib1fv(index, v); }, + glVertexAttrib2fv__sig: 'vii', glVertexAttrib2fv: function(index, v) { - v = {{{ makeHEAPView('F32', 'v', 'v+2*4') }}}; + v = {{{ makeHEAPView('F32', 'v', 'v+' + (2*4)) }}}; Module.ctx.vertexAttrib2fv(index, v); }, + glVertexAttrib3fv__sig: 'vii', glVertexAttrib3fv: function(index, v) { - v = {{{ makeHEAPView('F32', 'v', 'v+3*4') }}}; + v = {{{ makeHEAPView('F32', 'v', 'v+' + (3*4)) }}}; Module.ctx.vertexAttrib3fv(index, v); }, + glVertexAttrib4fv__sig: 'vii', glVertexAttrib4fv: function(index, v) { - v = {{{ makeHEAPView('F32', 'v', 'v+4*4') }}}; + v = {{{ makeHEAPView('F32', 'v', 'v+' + (4*4)) }}}; Module.ctx.vertexAttrib4fv(index, v); }, + glGetAttribLocation__sig: 'vii', glGetAttribLocation: function(program, name) { program = GL.programs[program]; name = Pointer_stringify(name); return Module.ctx.getAttribLocation(program, name); }, + glGetActiveAttrib__sig: 'viiiiiii', 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); + var infoname = info.name.slice(0, Math.max(0, bufSize - 1)); writeStringToMemory(infoname, name); if (length) { @@ -740,22 +1050,20 @@ var LibraryGL = { } }, + glCreateShader__sig: 'ii', glCreateShader: function(shaderType) { var id = GL.getNewId(GL.shaders); GL.shaders[id] = Module.ctx.createShader(shaderType); return id; }, + glDeleteShader__sig: 'vi', 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__sig: 'viiii', glGetAttachedShaders: function(program, maxCount, count, shaders) { var result = Module.ctx.getAttachedShaders(GL.programs[program]); var len = result.length; @@ -768,24 +1076,28 @@ var LibraryGL = { } }, + glShaderSource__sig: 'viiii', glShaderSource: function(shader, count, string, length) { var source = GL.getSource(shader, count, string, length); Module.ctx.shaderSource(GL.shaders[shader], source); }, + glGetShaderSource__sig: 'viiii', glGetShaderSource: function(shader, bufSize, length, source) { var result = Module.ctx.getShaderSource(GL.shaders[shader]); - result.slice(0, bufSize - 1); + result = result.slice(0, Math.max(0, bufSize - 1)); writeStringToMemory(result, source); if (length) { {{{ makeSetValue('length', '0', 'result.length', 'i32') }}}; } }, + glCompileShader__sig: 'vi', glCompileShader: function(shader) { Module.ctx.compileShader(GL.shaders[shader]); }, + glGetShaderInfoLog__sig: 'viiii', glGetShaderInfoLog: function(shader, maxLength, length, infoLog) { var log = Module.ctx.getShaderInfoLog(GL.shaders[shader]); // Work around a bug in Chromium which causes getShaderInfoLog to return null @@ -799,6 +1111,7 @@ var LibraryGL = { } }, + glGetShaderiv__sig: 'viii', glGetShaderiv : function(shader, pname, p) { if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH {{{ makeSetValue('p', '0', 'Module.ctx.getShaderInfoLog(GL.shaders[shader]).length + 1', 'i32') }}}; @@ -807,6 +1120,7 @@ var LibraryGL = { } }, + glGetProgramiv__sig: 'viii', glGetProgramiv : function(program, pname, p) { if (pname == 0x8B84) { // GL_INFO_LOG_LENGTH {{{ makeSetValue('p', '0', 'Module.ctx.getProgramInfoLog(GL.programs[program]).length + 1', 'i32') }}}; @@ -815,31 +1129,39 @@ var LibraryGL = { } }, + glIsShader__sig: 'ii', 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', glCreateProgram: function() { var id = GL.getNewId(GL.programs); GL.programs[id] = Module.ctx.createProgram(); return id; }, + glDeleteProgram__sig: 'vi', glDeleteProgram: function(program) { Module.ctx.deleteProgram(GL.programs[program]); GL.programs[program] = null; GL.uniformTable[program] = null; }, + glAttachShader__sig: 'vii', glAttachShader: function(program, shader) { Module.ctx.attachShader(GL.programs[program], GL.shaders[shader]); }, + glDetachShader__sig: 'vii', + glDetachShader: function(program, shader) { + Module.ctx.detachShader(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') }}}; @@ -847,11 +1169,13 @@ var LibraryGL = { {{{ makeSetValue('precision', '0', 'result.precision', 'i32') }}}; }, + glLinkProgram__sig: 'vi', glLinkProgram: function(program) { Module.ctx.linkProgram(GL.programs[program]); GL.uniformTable[program] = {}; // uniforms no longer keep the same names after linking }, + glGetProgramInfoLog__sig: 'viiii', glGetProgramInfoLog: function(program, maxLength, length, infoLog) { var log = Module.ctx.getProgramInfoLog(GL.programs[program]); // Work around a bug in Chromium which causes getProgramInfoLog to return null @@ -865,31 +1189,35 @@ var LibraryGL = { } }, + glUseProgram__sig: 'vi', glUseProgram: function(program) { Module.ctx.useProgram(program ? GL.programs[program] : null); }, + glValidateProgram__sig: 'vi', glValidateProgram: function(program) { Module.ctx.validateProgram(GL.programs[program]); }, + glIsProgram__sig: 'ii', glIsProgram: function(program) { - var fb = GL.programs[program]; - if (typeof(fb) == 'undefined') { - return 0; - } - return Module.ctx.isProgram(fb); + var program = GL.programs[program]; + if (!program) return 0; + return Module.ctx.isProgram(program); }, + glBindAttribLocation__sig: 'viii', glBindAttribLocation: function(program, index, name) { name = Pointer_stringify(name); Module.ctx.bindAttribLocation(GL.programs[program], index, name); }, + glBindFramebuffer__sig: 'vii', glBindFramebuffer: function(target, framebuffer) { Module.ctx.bindFramebuffer(target, framebuffer ? GL.framebuffers[framebuffer] : null); }, + glGenFramebuffers__sig: 'vii', glGenFramebuffers: function(n, ids) { for (var i = 0; i < n; ++i) { var id = GL.getNewId(GL.framebuffers); @@ -898,6 +1226,7 @@ var LibraryGL = { } }, + glDeleteFramebuffers__sig: 'vii', glDeleteFramebuffers: function(n, framebuffers) { for (var i = 0; i < n; ++i) { var id = {{{ makeGetValue('framebuffers', 'i*4', 'i32') }}}; @@ -906,26 +1235,28 @@ var LibraryGL = { } }, + glFramebufferRenderbuffer__sig: 'viiii', glFramebufferRenderbuffer: function(target, attachment, renderbuffertarget, renderbuffer) { Module.ctx.framebufferRenderbuffer(target, attachment, renderbuffertarget, GL.renderbuffers[renderbuffer]); }, + glFramebufferTexture2D__sig: 'viiiii', glFramebufferTexture2D: function(target, attachment, textarget, texture, level) { Module.ctx.framebufferTexture2D(target, attachment, textarget, GL.textures[texture], level); }, + glGetFramebufferAttachmentParameteriv__sig: 'viiii', glGetFramebufferAttachmentParameteriv: function(target, attachment, pname, params) { var result = Module.ctx.getFramebufferAttachmentParameter(target, attachment, pname); {{{ makeSetValue('params', '0', 'params', 'i32') }}}; }, + 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); }, @@ -941,11 +1272,34 @@ var LibraryGL = { fogMode: 0x0800, // GL_EXP fogEnabled: false, + // VAO support + vaos: [], + currentVao: null, + enabledVertexAttribArrays: {}, // helps with vao cleanups + + hasRunInit: false, + init: function() { + // Do not activate immediate/emulation code (e.g. replace glDrawElements) when in FULL_ES2 mode. + // We do not need full emulation, we instead emulate client-side arrays etc. in FULL_ES2 code in + // a straightforward manner, and avoid not having a bound buffer be ambiguous between es2 emulation + // code and legacy gl emulation code. +#if FULL_ES2 + return; +#endif + + if (GLEmulation.hasRunInit) { + return; + } + GLEmulation.hasRunInit = true; + GLEmulation.fogColor = new Float32Array(4); // Add some emulation workarounds Module.printErr('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work'); +#if GL_UNSAFE_OPTS == 0 + Module.printErr('WARNING: using emscripten GL emulation unsafe opts. If weirdness happens, try -s GL_UNSAFE_OPTS=0'); +#endif // XXX some of the capabilities we don't support may lead to incorrect rendering, if we do not emulate them in shaders var validCapabilities = { @@ -959,6 +1313,8 @@ var LibraryGL = { 0x809E: 1, // GL_SAMPLE_ALPHA_TO_COVERAGE 0x80A0: 1 // GL_SAMPLE_COVERAGE }; + + var glEnable = _glEnable; _glEnable = function(cap) { // Clean up the renderer on any change to the rendering state. The optimization of // skipping renderer setup is aimed at the case of multiple glDraw* right after each other @@ -966,20 +1322,38 @@ var LibraryGL = { if (cap == 0x0B60 /* GL_FOG */) { GLEmulation.fogEnabled = true; return; + } else if (cap == 0x0de1 /* GL_TEXTURE_2D */) { + // XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support + // it by forwarding to glEnableClientState + /* Actually, let's not, for now. (This sounds exceedingly broken) + * This is in gl_ps_workaround2.c. + _glEnableClientState(cap); + */ + return; } else if (!(cap in validCapabilities)) { return; } - Module.ctx.enable(cap); + glEnable(cap); }; |