aboutsummaryrefslogtreecommitdiff
path: root/src/library_gl.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/library_gl.js')
-rw-r--r--src/library_gl.js2887
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);
};