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.js517
1 files changed, 433 insertions, 84 deletions
diff --git a/src/library_gl.js b/src/library_gl.js
index 8b29e4e2..4977d2e9 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -19,6 +19,28 @@ var LibraryGL = {
uniforms: [],
shaders: [],
+#if FULL_ES2
+ clientBuffers: [],
+ enabledClientBuffers: [],
+#endif
+ currArrayBuffer: 0,
+ currElementArrayBuffer: 0,
+
+ byteSizeByTypeRoot: 0x1400, // GL_BYTE
+ byteSizeByType: [
+ 1, // GL_BYTE
+ 1, // GL_UNSIGNED_BYTE
+ 2, // GL_SHORT
+ 2, // GL_UNSIGNED_SHORT
+ 4, // GL_INT
+ 4, // GL_UNSIGNED_INT
+ 4, // GL_FLOAT
+ 2, // GL_2_BYTES
+ 3, // GL_3_BYTES
+ 4, // GL_4_BYTES
+ 8 // GL_DOUBLE
+ ],
+
uniformTable: {}, // name => uniform ID. the uID must be identical until relinking, cannot create a new uID each call to glGetUniformLocation
packAlignment: 4, // default alignment is 4 bytes
@@ -176,12 +198,48 @@ var LibraryGL = {
}
},
+#if FULL_ES2
+ calcBufLength: function(size, type, stride, count) {
+ if (stride > 0) {
+ return count * stride; // XXXvlad this is not exactly correct I don't think
+ }
+ var typeSize = GL.byteSizeByType[type - GL.byteSizeByTypeRoot];
+ return size * typeSize * count;
+ },
+
+ preDrawHandleClientVertexAttribBindings: function(count) {
+ GL.resetBufferBinding = false;
+ for (var i = 0; i < GL.maxVertexAttribs; ++i) {
+ if (!GL.enabledClientBuffers[i] || !GL.clientBuffers[i]) continue;
+
+ GL.resetBufferBinding = true;
+
+ var cb = GL.clientBuffers[i];
+
+ var buf = Module.ctx.createBuffer();
+ Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, buf);
+ Module.ctx.bufferData(Module.ctx.ARRAY_BUFFER,
+ HEAPU8.subarray(cb.ptr, cb.ptr + GL.calcBufLength(cb.size, cb.type, cb.stride, count)),
+ Module.ctx.DYNAMIC_DRAW);
+ Module.ctx.vertexAttribPointer(i, cb.size, cb.type, cb.normalized, cb.stride, 0);
+ }
+ },
+
+ postDrawHandleClientVertexAttribBindings: function() {
+ if (GL.resetBufferBinding) {
+ Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, GL.buffers[GL.currArrayBuffer]);
+ }
+ },
+#endif
+
initExtensions: function() {
if (GL.initExtensions.done) return;
GL.initExtensions.done = true;
if (!Module.useWebGL) return; // an app might link both gl and 2d backends
+ GL.maxVertexAttribs = Module.ctx.getParameter(Module.ctx.MAX_VERTEX_ATTRIBS);
+
GL.compressionExt = Module.ctx.getExtension('WEBGL_compressed_texture_s3tc') ||
Module.ctx.getExtension('MOZ_WEBGL_compressed_texture_s3tc') ||
Module.ctx.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc');
@@ -408,7 +466,19 @@ var LibraryGL = {
},
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: function(target, texture) {
@@ -444,6 +514,9 @@ var LibraryGL = {
var id = {{{ makeGetValue('buffers', 'i*4', 'i32') }}};
Module.ctx.deleteBuffer(GL.buffers[id]);
GL.buffers[id] = null;
+
+ if (id == GL.currArrayBuffer) GL.currArrayBuffer = 0;
+ if (id == GL.currElementArrayBuffer) GL.currElementArrayBuffer = 0;
}
},
@@ -462,11 +535,9 @@ var LibraryGL = {
},
glIsBuffer: function(buffer) {
- var fb = GL.buffers[buffer];
- if (typeof(fb) == 'undefined') {
- return 0;
- }
- return Module.ctx.isBuffer(fb);
+ var b = GL.buffers[buffer];
+ if (!b) return 0;
+ return Module.ctx.isBuffer(b);
},
glGenRenderbuffers__sig: 'vii',
@@ -497,11 +568,9 @@ var LibraryGL = {
},
glIsRenderbuffer: function(renderbuffer) {
- var fb = GL.renderbuffers[renderbuffer];
- if (typeof(fb) == 'undefined') {
- return 0;
- }
- return Module.ctx.isRenderbuffer(fb);
+ var rb = GL.renderbuffers[renderbuffer];
+ if (!rb) return 0;
+ return Module.ctx.isRenderbuffer(rb);
},
glGetUniformfv: function(program, location, params) {
@@ -542,6 +611,11 @@ var LibraryGL = {
},
glGetVertexAttribfv: function(index, pname, params) {
+#if FULL_ES2
+ if (GL.clientBuffers[index]) {
+ Module.printErr("glGetVertexAttribfv on client-side array: not supported, bad data returned");
+ }
+#endif
var data = Module.ctx.getVertexAttrib(index, pname);
if (typeof data == 'number') {
{{{ makeSetValue('params', '0', 'data', 'float') }}};
@@ -553,6 +627,11 @@ var LibraryGL = {
},
glGetVertexAttribiv: function(index, pname, params) {
+#if FULL_ES2
+ if (GL.clientBuffers[index]) {
+ Module.printErr("glGetVertexAttribiv on client-side array: not supported, bad data returned");
+ }
+#endif
var data = Module.ctx.getVertexAttrib(index, pname);
if (typeof data == 'number' || typeof data == 'boolean') {
{{{ makeSetValue('params', '0', 'data', 'i32') }}};
@@ -564,6 +643,11 @@ var LibraryGL = {
},
glGetVertexAttribPointerv: function(index, pname, pointer) {
+#if FULL_ES2
+ if (GL.clientBuffers[index]) {
+ Module.printErr("glGetVertexAttribPointer on client-side array: not supported, bad data returned");
+ }
+#endif
{{{ makeSetValue('pointer', '0', 'Module.ctx.getVertexAttribOffset(index, pname)', 'i32') }}};
},
@@ -719,6 +803,12 @@ var LibraryGL = {
glBindBuffer__sig: 'vii',
glBindBuffer: function(target, buffer) {
+ if (target == Module.ctx.ARRAY_BUFFER) {
+ GL.currArrayBuffer = buffer;
+ } else if (target == Module.ctx.ELEMENT_ARRAY_BUFFER) {
+ GL.currElementArrayBuffer = buffer;
+ }
+
Module.ctx.bindBuffer(target, buffer ? GL.buffers[buffer] : null);
},
@@ -846,11 +936,9 @@ var LibraryGL = {
},
glIsShader: function(shader) {
- var fb = GL.shaders[shader];
- if (typeof(fb) == 'undefined') {
- return 0;
- }
- return Module.ctx.isShader(fb);
+ var s = GL.shaders[shader];
+ if (!s) return 0;
+ return Module.ctx.isShader(s);
},
glCreateProgram__sig: 'i',
@@ -908,11 +996,9 @@ var LibraryGL = {
},
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',
@@ -965,9 +1051,7 @@ var LibraryGL = {
glIsFramebuffer__sig: 'ii',
glIsFramebuffer: function(framebuffer) {
var fb = GL.framebuffers[framebuffer];
- if (typeof(fb) == 'undefined') {
- return 0;
- }
+ if (!fb) return 0;
return Module.ctx.isFramebuffer(fb);
},
@@ -983,6 +1067,11 @@ var LibraryGL = {
fogMode: 0x0800, // GL_EXP
fogEnabled: false,
+ // VAO support
+ vaos: [],
+ currentVao: null,
+ enabledVertexAttribArrays: {}, // helps with vao cleanups
+
init: function() {
GLEmulation.fogColor = new Float32Array(4);
@@ -1001,6 +1090,7 @@ var LibraryGL = {
0x809E: 1, // GL_SAMPLE_ALPHA_TO_COVERAGE
0x80A0: 1 // GL_SAMPLE_COVERAGE
};
+
_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
@@ -1008,6 +1098,11 @@ 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
+ _glEnableClientState(cap);
+ return;
} else if (!(cap in validCapabilities)) {
return;
}
@@ -1018,6 +1113,11 @@ var LibraryGL = {
if (cap == 0x0B60 /* GL_FOG */) {
GLEmulation.fogEnabled = false;
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 glDisableClientState
+ _glDisableClientState(cap);
+ return;
} else if (!(cap in validCapabilities)) {
return;
}
@@ -1032,6 +1132,17 @@ var LibraryGL = {
return Module.ctx.isEnabled(cap);
};
+ var glGetBooleanv = _glGetBooleanv;
+ _glGetBooleanv = function(pname, p) {
+ var attrib = GLEmulation.getAttributeFromCapability(pname);
+ if (attrib !== null) {
+ var result = GL.immediate.enabledClientAttributes[attrib];
+ {{{ makeSetValue('p', '0', 'result === true ? 1 : 0', 'i8') }}};
+ return;
+ }
+ glGetBooleanv(pname, p);
+ };
+
var glGetIntegerv = _glGetIntegerv;
_glGetIntegerv = function(pname, params) {
switch (pname) {
@@ -1052,6 +1163,51 @@ var LibraryGL = {
return;
}
case 0x8871: pname = Module.ctx.MAX_COMBINED_TEXTURE_IMAGE_UNITS /* close enough */; break; // GL_MAX_TEXTURE_COORDS
+ case 0x807A: { // GL_VERTEX_ARRAY_SIZE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
+ return;
+ }
+ case 0x807B: { // GL_VERTEX_ARRAY_TYPE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
+ return;
+ }
+ case 0x807C: { // GL_VERTEX_ARRAY_STRIDE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
+ return;
+ }
+ case 0x8081: { // GL_COLOR_ARRAY_SIZE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
+ return;
+ }
+ case 0x8082: { // GL_COLOR_ARRAY_TYPE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
+ return;
+ }
+ case 0x8083: { // GL_COLOR_ARRAY_STRIDE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.COLOR];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
+ return;
+ }
+ case 0x8088: { // GL_TEXTURE_COORD_ARRAY_SIZE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.size : 0', 'i32') }}};
+ return;
+ }
+ case 0x8089: { // GL_TEXTURE_COORD_ARRAY_TYPE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.type : 0', 'i32') }}};
+ return;
+ }
+ case 0x808A: { // GL_TEXTURE_COORD_ARRAY_STRIDE
+ var attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0];
+ {{{ makeSetValue('params', '0', 'attribute ? attribute.stride : 0', 'i32') }}};
+ return;
+ }
}
glGetIntegerv(pname, params);
};
@@ -1266,19 +1422,12 @@ var LibraryGL = {
_glBindBuffer = function(target, buffer) {
glBindBuffer(target, buffer);
if (target == Module.ctx.ARRAY_BUFFER) {
- GL.currArrayBuffer = buffer;
+ if (GLEmulation.currentVao) {
+ assert(GLEmulation.currentVao.arrayBuffer == buffer || GLEmulation.currentVao.arrayBuffer == 0 || buffer == 0, 'TODO: support for multiple array buffers in vao');
+ GLEmulation.currentVao.arrayBuffer = buffer;
+ }
} else if (target == Module.ctx.ELEMENT_ARRAY_BUFFER) {
- GL.currElementArrayBuffer = buffer;
- }
- };
-
- var glDeleteBuffers = _glDeleteBuffers;
- _glDeleteBuffers = function(n, buffers) {
- glDeleteBuffers(n, buffers);
- for (var i = 0; i < n; i++) {
- var buffer = {{{ makeGetValue('buffers', 'i*4', 'i32') }}};
- if (buffer == GL.currArrayBuffer) GL.currArrayBuffer = 0;
- if (buffer == GL.currElementArrayBuffer) GL.currElementArrayBuffer = 0;
+ if (GLEmulation.currentVao) GLEmulation.currentVao.elementArrayBuffer = buffer;
}
};
@@ -1312,6 +1461,44 @@ var LibraryGL = {
}
glHint(target, mode);
};
+
+ var glEnableVertexAttribArray = _glEnableVertexAttribArray;
+ _glEnableVertexAttribArray = function(index) {
+ glEnableVertexAttribArray(index);
+ GLEmulation.enabledVertexAttribArrays[index] = 1;
+ if (GLEmulation.currentVao) GLEmulation.currentVao.enabledVertexAttribArrays[index] = 1;
+ };
+
+ var glDisableVertexAttribArray = _glDisableVertexAttribArray;
+ _glDisableVertexAttribArray = function(index) {
+ glDisableVertexAttribArray(index);
+ delete GLEmulation.enabledVertexAttribArrays[index];
+ if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledVertexAttribArrays[index];
+ };
+
+ var glVertexAttribPointer = _glVertexAttribPointer;
+ _glVertexAttribPointer = function(index, size, type, normalized, stride, pointer) {
+ glVertexAttribPointer(index, size, type, normalized, stride, pointer);
+ if (GLEmulation.currentVao) { // TODO: avoid object creation here? likely not hot though
+ GLEmulation.currentVao.vertexAttribPointers[index] = [index, size, type, normalized, stride, pointer];
+ }
+ };
+ },
+
+ getAttributeFromCapability: function(cap) {
+ var attrib = null;
+ switch (cap) {
+ case 0x8078: // GL_TEXTURE_COORD_ARRAY
+ case 0x0de1: // GL_TEXTURE_2D - XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support it
+ attrib = GL.immediate.TEXTURE0 + GL.immediate.clientActiveTexture; break;
+ case 0x8074: // GL_VERTEX_ARRAY
+ attrib = GL.immediate.VERTEX; break;
+ case 0x8075: // GL_NORMAL_ARRAY
+ attrib = GL.immediate.NORMAL; break;
+ case 0x8076: // GL_COLOR_ARRAY
+ attrib = GL.immediate.COLOR; break;
+ }
+ return attrib;
},
getProcAddress: function(name) {
@@ -1375,6 +1562,9 @@ var LibraryGL = {
case 'glIsFramebuffer': ret = {{{ Functions.getIndex('_glIsFramebuffer', true) }}}; break;
case 'glCheckFramebufferStatus': ret = {{{ Functions.getIndex('_glCheckFramebufferStatus', true) }}}; break;
case 'glRenderbufferStorage': ret = {{{ Functions.getIndex('_glRenderbufferStorage', true) }}}; break;
+ case 'glGenVertexArrays': ret = {{{ Functions.getIndex('_glGenVertexArrays', true) }}}; break;
+ case 'glDeleteVertexArrays': ret = {{{ Functions.getIndex('_glDeleteVertexArrays', true) }}}; break;
+ case 'glBindVertexArray': ret = {{{ Functions.getIndex('_glBindVertexArray', true) }}}; break;
}
if (!ret) Module.printErr('WARNING: getProcAddress failed for ' + name);
return ret;
@@ -1427,10 +1617,24 @@ var LibraryGL = {
assert(id == 0);
},
+ glGetPointerv: function(name, p) {
+ var attribute;
+ switch(name) {
+ case 0x808E: // GL_VERTEX_ARRAY_POINTER
+ attribute = GLImmediate.clientAttributes[GLImmediate.VERTEX]; break;
+ case 0x8090: // GL_COLOR_ARRAY_POINTER
+ attribute = GLImmediate.clientAttributes[GLImmediate.COLOR]; break;
+ case 0x8092: // GL_TEXTURE_COORD_ARRAY_POINTER
+ attribute = GLImmediate.clientAttributes[GLImmediate.TEXTURE0]; break;
+ default: throw 'TODO: glGetPointerv for ' + name;
+ }
+ {{{ makeSetValue('p', '0', 'attribute ? attribute.pointer : 0', 'i32') }}};
+ },
+
// GL Immediate mode
$GLImmediate__postset: 'GL.immediate.setupFuncs(); Browser.moduleContextCreatedCallbacks.push(function() { GL.immediate.init() });',
- $GLImmediate__deps: ['$Browser', '$GL'],
+ $GLImmediate__deps: ['$Browser', '$GL', '$GLEmulation'],
$GLImmediate: {
MAX_TEXTURES: 7,
@@ -1479,17 +1683,6 @@ var LibraryGL = {
clientActiveTexture: 0,
clientColor: null,
- byteSizeByTypeRoot: 0x1400, // GL_BYTE
- byteSizeByType: [
- 1, // GL_BYTE
- 1, // GL_UNSIGNED_BYTE
- 2, // GL_SHORT
- 2, // GL_UNSIGNED_SHORT
- 4, // GL_INT
- 4, // GL_UNSIGNED_INT
- 4 // GL_FLOAT
- ],
-
setClientAttribute: function(name, size, type, stride, pointer) {
var attrib = this.clientAttributes[name];
attrib.name = name;
@@ -1565,7 +1758,7 @@ var LibraryGL = {
#endif
this.enabledClientAttributes[name] = true;
this.setClientAttribute(name, size, type, 0, this.rendererComponentPointer);
- this.rendererComponentPointer += size * this.byteSizeByType[type - this.byteSizeByTypeRoot];
+ this.rendererComponentPointer += size * GL.byteSizeByType[type - GL.byteSizeByTypeRoot];
} else {
this.rendererComponents[name]++;
}
@@ -1589,7 +1782,7 @@ var LibraryGL = {
cacheItem = temp ? temp : (cacheItem[attribute.name] = GL.immediate.rendererCacheItemTemplate.slice());
temp = cacheItem[attribute.size];
cacheItem = temp ? temp : (cacheItem[attribute.size] = GL.immediate.rendererCacheItemTemplate.slice());
- var typeIndex = attribute.type - GL.immediate.byteSizeByTypeRoot; // ensure it starts at 0 to keep the cache items dense
+ var typeIndex = attribute.type - GL.byteSizeByTypeRoot; // ensure it starts at 0 to keep the cache items dense
temp = cacheItem[typeIndex];
cacheItem = temp ? temp : (cacheItem[typeIndex] = GL.immediate.rendererCacheItemTemplate.slice());
}
@@ -1784,27 +1977,35 @@ var LibraryGL = {
}
// If the array buffer is unchanged and the renderer as well, then we can avoid all the work here
- // XXX We use some heuristics here, and this may not work in all cases. Try disabling this if you
- // have odd glitches (by setting canSkip always to 0, or even cleaning up the renderer right
- // after rendering)
+ // XXX We use some heuristics here, and this may not work in all cases. Try disabling GL_UNSAFE_OPTS if you
+ // have odd glitches
+#if GL_UNSAFE_OPTS
var lastRenderer = GL.immediate.lastRenderer;
var canSkip = this == lastRenderer &&
arrayBuffer == GL.immediate.lastArrayBuffer &&
(GL.currProgram || this.program) == GL.immediate.lastProgram &&
!GL.immediate.matricesModified;
if (!canSkip && lastRenderer) lastRenderer.cleanup();
+#endif
if (!GL.currArrayBuffer) {
// Bind the array buffer and upload data after cleaning up the previous renderer
+#if GL_UNSAFE_OPTS
+ // Potentially unsafe, since lastArrayBuffer might not reflect the true array buffer in code that mixes immediate/non-immediate
if (arrayBuffer != GL.immediate.lastArrayBuffer) {
+#endif
Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, arrayBuffer);
+#if GL_UNSAFE_OPTS
}
+#endif
Module.ctx.bufferSubData(Module.ctx.ARRAY_BUFFER, start, GL.immediate.vertexData.subarray(start >> 2, end >> 2));
}
+#if GL_UNSAFE_OPTS
if (canSkip) return;
GL.immediate.lastRenderer = this;
GL.immediate.lastArrayBuffer = arrayBuffer;
GL.immediate.lastProgram = GL.currProgram || this.program;
GL.immediate.matricesModified = false;
+#endif
if (!GL.currProgram) {
Module.ctx.useProgram(this.program);
@@ -1880,9 +2081,11 @@ var LibraryGL = {
Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, null);
}
+#if GL_UNSAFE_OPTS
GL.immediate.lastRenderer = null;
GL.immediate.lastArrayBuffer = null;
GL.immediate.lastProgram = null;
+#endif
GL.immediate.matricesModified = true;
}
};
@@ -2015,7 +2218,7 @@ var LibraryGL = {
for (var i = 0; i < attributes.length; i++) {
var attribute = attributes[i];
if (!attribute) break;
- var size = attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot];
+ var size = attribute.size * GL.byteSizeByType[attribute.type - GL.byteSizeByTypeRoot];
if (size % 4 != 0) size += 4 - (size % 4); // align everything
attribute.offset = bytes;
bytes += size;
@@ -2027,7 +2230,7 @@ var LibraryGL = {
for (var i = 0; i < attributes.length; i++) {
var attribute = attributes[i];
if (!attribute) break;
- var size4 = Math.floor((attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot])/4);
+ var size4 = Math.floor((attribute.size * GL.byteSizeByType[attribute.type - GL.byteSizeByTypeRoot])/4);
for (var j = 0; j < count; j++) {
for (var k = 0; k < size4; k++) { // copy in chunks of 4 bytes, our alignment makes this possible
HEAP32[((start + attribute.offset + bytes*j)>>2) + k] = HEAP32[(attribute.pointer>>2) + j*size4 + k];
@@ -2045,7 +2248,7 @@ var LibraryGL = {
assert((attribute.offset - bytes)%4 == 0); // XXX assuming 4-alignment
bytes += attribute.offset - bytes;
}
- bytes += attribute.size * GL.immediate.byteSizeByType[attribute.type - GL.immediate.byteSizeByTypeRoot];
+ bytes += attribute.size * GL.byteSizeByType[attribute.type - GL.byteSizeByTypeRoot];
if (bytes % 4 != 0) bytes += 4 - (bytes % 4); // XXX assuming 4-alignment
}
assert(beginEnd || bytes <= stride); // if not begin-end, explicit stride should make sense with total byte size
@@ -2066,7 +2269,7 @@ var LibraryGL = {
flush: function(numProvidedIndexes, startIndex, ptr) {
#if ASSERTIONS
- assert(numProvidedIndexes >= 0);
+ assert(numProvidedIndexes >= 0 || !numProvidedIndexes);
#endif
startIndex = startIndex || 0;
ptr = ptr || 0;
@@ -2127,6 +2330,10 @@ var LibraryGL = {
if (emulatedElementArrayBuffer) {
Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GL.currElementArrayBuffer] || null);
}
+
+#if GL_UNSAFE_OPTS == 0
+ renderer.cleanup();
+#endif
}
},
@@ -2176,8 +2383,10 @@ var LibraryGL = {
glVertex2fv: function(p) {
_glVertex3f({{{ makeGetValue('p', '0', 'float') }}}, {{{ makeGetValue('p', '4', 'float') }}}, 0);
},
+
+ glVertex3i: 'glVertex3f',
- glVertex2i: function() { throw 'glVertex2i: TODO' },
+ glVertex2i: 'glVertex3f',
glTexCoord2i: function(u, v) {
#if ASSERTIONS
@@ -2341,29 +2550,21 @@ var LibraryGL = {
// ClientState/gl*Pointer
glEnableClientState: function(cap, disable) {
- var attrib;
- switch(cap) {
- case 0x8078: // GL_TEXTURE_COORD_ARRAY
- case 0x0de1: // GL_TEXTURE_2D - XXX not according to spec, and not in desktop GL, but works in some GLES1.x apparently, so support it
- attrib = GL.immediate.TEXTURE0 + GL.immediate.clientActiveTexture; break;
- case 0x8074: // GL_VERTEX_ARRAY
- attrib = GL.immediate.VERTEX; break;
- case 0x8075: // GL_NORMAL_ARRAY
- attrib = GL.immediate.NORMAL; break;
- case 0x8076: // GL_COLOR_ARRAY
- attrib = GL.immediate.COLOR; break;
- default:
+ var attrib = GLEmulation.getAttributeFromCapability(cap);
+ if (attrib === null) {
#if ASSERTIONS
- Module.printErr('WARNING: unhandled clientstate: ' + cap);
+ Module.printErr('WARNING: unhandled clientstate: ' + cap);
#endif
- return;
+ return;
}
if (disable && GL.immediate.enabledClientAttributes[attrib]) {
GL.immediate.enabledClientAttributes[attrib] = false;
GL.immediate.totalEnabledClientAttributes--;
+ if (GLEmulation.currentVao) delete GLEmulation.currentVao.enabledClientStates[cap];
} else if (!disable && !GL.immediate.enabledClientAttributes[attrib]) {
GL.immediate.enabledClientAttributes[attrib] = true;
GL.immediate.totalEnabledClientAttributes++;
+ if (GLEmulation.currentVao) GLEmulation.currentVao.enabledClientStates[cap] = 1;
}
GL.immediate.modifiedClientAttributes = true;
},
@@ -2390,6 +2591,63 @@ var LibraryGL = {
GL.immediate.clientActiveTexture = texture - 0x84C0; // GL_TEXTURE0
},
+ // Vertex array object (VAO) support. TODO: when the WebGL extension is popular, use that and remove this code and GL.vaos
+ glGenVertexArrays__deps: ['$GLEMulation'],
+ glGenVertexArrays__sig: ['vii'],
+ glGenVertexArrays: function(n, vaos) {
+ for (var i = 0; i < n; i++) {
+ var id = GL.getNewId(GLEmulation.vaos);
+ GLEmulation.vaos[id] = {
+ id: id,
+ arrayBuffer: 0,
+ elementArrayBuffer: 0,
+ enabledVertexAttribArrays: {},
+ vertexAttribPointers: {},
+ enabledClientStates: {},
+ };
+ {{{ makeSetValue('vaos', 'i*4', 'id', 'i32') }}};
+ }
+ },
+ glDeleteVertexArrays__sig: ['vii'],
+ glDeleteVertexArrays: function(n, vaos) {
+ for (var i = 0; i < n; i++) {
+ var id = {{{ makeGetValue('vaos', 'i*4', 'i32') }}};
+ GLEmulation.vaos[id] = null;
+ if (GLEmulation.currentVao && GLEmulation.currentVao.id == id) GLEmulation.currentVao = null;
+ }
+ },
+ glBindVertexArray__sig: ['vi'],
+ glBindVertexArray: function(vao) {
+ // undo vao-related things, wipe the slate clean, both for vao of 0 or an actual vao
+ GLEmulation.currentVao = null; // make sure the commands we run here are not recorded
+ if (GL.immediate.lastRenderer) GL.immediate.lastRenderer.cleanup();
+ _glBindBuffer(Module.ctx.ARRAY_BUFFER, 0); // XXX if one was there before we were bound?
+ _glBindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, 0);
+ for (var vaa in GLEmulation.enabledVertexAttribArrays) {
+ Module.ctx.disableVertexAttribArray(vaa);
+ }
+ GLEmulation.enabledVertexAttribArrays = {};
+ GL.immediate.enabledClientAttributes = [0, 0];
+ GL.immediate.totalEnabledClientAttributes = 0;
+ GL.immediate.modifiedClientAttributes = true;
+ if (vao) {
+ // replay vao
+ var info = GLEmulation.vaos[vao];
+ _glBindBuffer(Module.ctx.ARRAY_BUFFER, info.arrayBuffer); // XXX overwrite current binding?
+ _glBindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, info.elementArrayBuffer);
+ for (var vaa in info.enabledVertexAttribArrays) {
+ _glEnableVertexAttribArray(vaa);
+ }
+ for (var vaa in info.vertexAttribPointers) {
+ _glVertexAttribPointer.apply(null, info.vertexAttribPointers[vaa]);
+ }
+ for (var attrib in info.enabledClientStates) {
+ _glEnableClientState(attrib|0);
+ }
+ GLEmulation.currentVao = info; // set currentVao last, so the commands we ran here were not recorded
+ }
+ },
+
// OpenGL Immediate Mode matrix routines.
// Note that in the future we might make these available only in certain modes.
glMatrixMode__deps: ['$GL', '$GLImmediateSetup', '$GLEmulation'], // emulation is not strictly needed, this is a workaround
@@ -2480,6 +2738,7 @@ var LibraryGL = {
GL.immediate.matrix.lib.mat4.multiply(GL.immediate.matrix[GL.immediate.currentMatrix],
GL.immediate.matrix.lib.mat4.frustum(left, right, bottom, top_, nearVal, farVal));
},
+ glFrustumf: 'glFrustum',
glOrtho: function(left, right, bottom, top_, nearVal, farVal) {
GL.immediate.matricesModified = true;
@@ -2581,36 +2840,121 @@ var LibraryGL = {
glTexGeni: function() { throw 'glTexGeni: TODO' },
glTexGenfv: function() { throw 'glTexGenfv: TODO' },
- glTexEnvi: function() { throw 'glTexEnvi: TODO' },
- glTexEnvfv: function() { throw 'glTexEnvfv: TODO' },
+ glTexEnvi: function() { Runtime.warnOnce('glTexEnvi: TODO') },
+ glTexEnvfv: function() { Runtime.warnOnce('glTexEnvfv: TODO') },
glTexImage1D: function() { throw 'glTexImage1D: TODO' },
glTexCoord3f: function() { throw 'glTexCoord3f: TODO' },
glGetTexLevelParameteriv: function() { throw 'glGetTexLevelParameteriv: TODO' },
- // signatures of simple pass-through functions, see later
- glActiveTexture__sig: 'vi',
+ glShadeModel: function() { Runtime.warnOnce('TODO: glShadeModel') },
+
+ glVertexAttribPointer__sig: 'viiiiii',
+ glVertexAttribPointer: function(index, size, type, normalized, stride, ptr) {
+#if FULL_ES2
+ if (!GL.currArrayBuffer) {
+ GL.clientBuffers[index] = { size: size, type: type, normalized: normalized, stride: stride, ptr: ptr };
+ return;
+ }
+
+ GL.clientBuffers[index] = null;
+#endif
+ Module.ctx.vertexAttribPointer(index, size, type, normalized, stride, ptr);
+ },
+
glEnableVertexAttribArray__sig: 'vi',
+ glEnableVertexAttribArray: function(index) {
+#if FULL_ES2
+ GL.enabledClientBuffers[index] = true;
+#endif
+ Module.ctx.enableVertexAttribArray(index);
+ },
+
glDisableVertexAttribArray__sig: 'vi',
- glVertexAttribPointer__sig: 'viiiiii',
+ glDisableVertexAttribArray: function(index) {
+#if FULL_ES2
+ GL.enabledClientBuffers[index] = false;
+#endif
+ Module.ctx.disableVertexAttribArray(index);
+ },
+
+ glDrawArrays: function(mode, first, count) {
+#if FULL_ES2
+ // bind any client-side buffers
+ GL.preDrawHandleClientVertexAttribBindings(count);
+#endif
+
+ Module.ctx.drawArrays(mode, first, count);
+
+#if FULL_ES2
+ GL.postDrawHandleClientVertexAttribBindings();
+#endif
+ },
+
+ glDrawElements: function(mode, count, type, indices) {
+#if FULL_ES2
+ var buf;
+ if (!GL.currElementArrayBuffer) {
+ buf = Module.ctx.createBuffer();
+ Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, buf);
+ Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER,
+ HEAPU8.subarray(indices, indices + GL.calcBufLength(1, type, 0, count)),
+ Module.ctx.DYNAMIC_DRAW);
+ // the index is now 0
+ indices = 0;
+ }
+
+ // bind any client-side buffers
+ GL.preDrawHandleClientVertexAttribBindings(count);
+#endif
+
+ Module.ctx.drawElements(mode, count, type, indices);
+
+#if FULL_ES2
+ GL.postDrawHandleClientVertexAttribBindings(count);
+
+ if (!GL.currElementArrayBuffer) {
+ Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null);
+ Module.ctx.deleteBuffer(buf);
+ }
+#endif
+ },
+
+ // signatures of simple pass-through functions, see later
+ glActiveTexture__sig: 'vi',
glCheckFramebufferStatus__sig: 'ii',
glRenderbufferStorage__sig: 'viiii',
+
+ // Open GLES1.1 compatibility
+ glGenFramebuffersOES : 'glGenFramebuffers',
+ glGenRenderbuffersOES : 'glGenRenderbuffers',
+ glBindFramebufferOES : 'glBindFramebuffer',
+ glBindRenderbufferOES : 'glBindRenderbuffer',
+ glGetRenderbufferParameterivOES : 'glGetRenderbufferParameteriv',
+ glFramebufferRenderbufferOES : 'glFramebufferRenderbuffer',
+ glRenderbufferStorageOES : 'glRenderbufferStorage',
+ glCheckFramebufferStatusOES : 'glCheckFramebufferStatus',
+ glDeleteFramebuffersOES : 'glDeleteFramebuffers',
+ glDeleteRenderbuffersOES : 'glDeleteRenderbuffers',
+ glGenVertexArraysOES: 'glGenVertexArrays',
+ glDeleteVertexArraysOES: 'glDeleteVertexArrays',
+ glBindVertexArrayOES: 'glBindVertexArray',
+ glFramebufferTexture2DOES: 'glFramebufferTexture2D'
};
// Simple pass-through functions. Starred ones have return values. [X] ones have X in the C name but not in the JS name
-[[0, 'shadeModel getError* finish flush'],
- [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear enableVertexAttribArray disableVertexAttribArray lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation sampleCoverage isEnabled*'],
+[[0, 'getError* finish flush'],
+ [1, 'clearDepth clearDepth[f] depthFunc enable disable frontFace cullFace clear lineWidth clearStencil depthMask stencilMask checkFramebufferStatus* generateMipmap activeTexture blendEquation sampleCoverage isEnabled*'],
[2, 'blendFunc blendEquationSeparate depthRange depthRange[f] stencilMaskSeparate hint polygonOffset'],
- [3, 'texParameteri texParameterf drawArrays vertexAttrib2f stencilFunc stencilOp'],
- [4, 'viewport clearColor scissor vertexAttrib3f colorMask drawElements renderbufferStorage blendFuncSeparate blendColor stencilFuncSeparate stencilOpSeparate'],
+ [3, 'texParameteri texParameterf vertexAttrib2f stencilFunc stencilOp'],
+ [4, 'viewport clearColor scissor vertexAttrib3f colorMask renderbufferStorage blendFuncSeparate blendColor stencilFuncSeparate stencilOpSeparate'],
[5, 'vertexAttrib4f'],
- [6, 'vertexAttribPointer'],
[8, 'copyTexImage2D copyTexSubImage2D']].forEach(function(data) {
var num = data[0];
var names = data[1];
var args = range(num).map(function(i) { return 'x' + i }).join(', ');
- var plainStub = '(function(' + args + ') { ' + (num > 0 ? 'Module.ctx.NAME(' + args + ')' : '') + ' })';
- var returnStub = '(function(' + args + ') { ' + (num > 0 ? 'return Module.ctx.NAME(' + args + ')' : '') + ' })';
+ var plainStub = '(function(' + args + ') { Module.ctx.NAME(' + args + ') })';
+ var returnStub = '(function(' + args + ') { return Module.ctx.NAME(' + args + ') })';
names.split(' ').forEach(function(name) {
var stub = plainStub;
if (name[name.length-1] == '*') {
@@ -2643,5 +2987,10 @@ LibraryGL.$GLEmulation__deps.push(function() {
for (var func in Functions.getIndex.tentative) Functions.getIndex(func);
});
+if (FORCE_GL_EMULATION) {
+ LibraryGL.glDrawElements__deps = LibraryGL.glDrawElements__deps.concat('$GLEmulation');
+ LibraryGL.glDrawArrays__deps = LibraryGL.glDrawArrays__deps.concat('$GLEmulation');
+}
+
mergeInto(LibraryManager.library, LibraryGL);