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.js198
1 files changed, 147 insertions, 51 deletions
diff --git a/src/library_gl.js b/src/library_gl.js
index 2be48ba1..578b8437 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -128,6 +128,14 @@ var LibraryGL = {
},
glGetIntegerv: function(name_, p) {
+ switch(name_) { // Handle a few trivial GLES values
+ case 0x8DFA: // GL_SHADER_COMPILER
+ {{{ makeSetValue('p', '0', '1', 'i32') }}};
+ return;
+ case 0x8DF9: // GL_NUM_SHADER_BINARY_FORMATS
+ {{{ makeSetValue('p', '0', '0', 'i32') }}};
+ return;
+ }
var result = Module.ctx.getParameter(name_);
switch (typeof(result)) {
case "number":
@@ -816,7 +824,7 @@ var LibraryGL = {
},
glUseProgram: function(program) {
- Module.ctx.useProgram(GL.programs[program]);
+ Module.ctx.useProgram(program ? GL.programs[program] : null);
},
glValidateProgram: function(program) {
@@ -930,6 +938,15 @@ var LibraryGL = {
glGetIntegerv(pname, params);
};
+ var glGetString = _glGetString;
+ _glGetString = function(name_) {
+ switch(name_) {
+ case 0x1F03 /* GL_EXTENSIONS */: // Add various extensions that we can support
+ return allocate(intArrayFromString(Module.ctx.getSupportedExtensions().join(' ') + ' GL_EXT_texture_env_combine GL_ARB_texture_env_crossbar GL_ATI_texture_env_combine3 GL_NV_texture_env_combine4 GL_EXT_texture_env_dot3 GL_ARB_multitexture GL_ARB_vertex_buffer_object GL_EXT_framebuffer_object GL_ARB_vertex_program GL_ARB_fragment_program GL_ARB_shading_language_100 GL_ARB_shader_objects GL_ARB_vertex_shader GL_ARB_fragment_shader GL_ARB_texture_cube_map GL_EXT_draw_range_elements'), 'i8', ALLOC_NORMAL);
+ }
+ return glGetString(name_);
+ };
+
// Do some automatic rewriting to work around GLSL differences. Note that this must be done in
// tandem with the rest of the program, by itself it cannot suffice.
// Note that we need to remember shader types for this rewriting, saving sources makes it easier to debug.
@@ -969,6 +986,9 @@ var LibraryGL = {
source = source.replace(/gl_ProjectionMatrix/g, 'u_projection');
if (old != source) need_pm = 1;
old = source;
+ source = source.replace(/gl_ModelViewMatrixTranspose\[2\]/g, 'vec4(u_modelView[0][0], u_modelView[1][0], u_modelView[2][0], u_modelView[3][0])'); // XXX extremely inefficient
+ if (old != source) need_mm = 1;
+ old = source;
source = source.replace(/gl_ModelViewMatrix/g, 'u_modelView');
if (old != source) need_mm = 1;
old = source;
@@ -977,9 +997,6 @@ var LibraryGL = {
old = source;
source = source.replace(/gl_ModelViewProjectionMatrix/g, '(u_projection * u_modelView)');
if (old != source) need_pm = need_mm = 1;
- old = source;
- source = source.replace(/gl_ModelViewMatrixTranspose\[2\]/g, 'vec3(u_modelView[0][0], u_modelView[1][0], u_modelView[2][0])'); // XXX extremely inefficient
- if (old != source) need_mm = 1;
if (need_pv && !has_pv) source = 'attribute vec4 a_position; \n' + source;
if (need_mm && !has_mm) source = 'uniform mat4 u_modelView; \n' + source;
if (need_pm && !has_pm) source = 'uniform mat4 u_projection; \n' + source;
@@ -1045,14 +1062,14 @@ var LibraryGL = {
_glCompileShader = function(shader) {
Module.ctx.compileShader(GL.shaders[shader]);
if (!Module.ctx.getShaderParameter(GL.shaders[shader], Module.ctx.COMPILE_STATUS)) {
- console.log('Failed to compile shader: ' + Module.ctx.getShaderInfoLog(GL.shaders[shader]));
- console.log('Info: ' + JSON.stringify(GL.shaderInfos[shader]));
+ Module.printErr('Failed to compile shader: ' + Module.ctx.getShaderInfoLog(GL.shaders[shader]));
+ Module.printErr('Info: ' + JSON.stringify(GL.shaderInfos[shader]));
#if GL_DEBUG
- console.log('Original source: ' + GL.shaderOriginalSources[shader]);
- console.log('Source: ' + GL.shaderSources[shader]);
+ Module.printErr('Original source: ' + GL.shaderOriginalSources[shader]);
+ Module.printErr('Source: ' + GL.shaderSources[shader]);
throw 'Shader compilation halt';
#else
- console.log('Enable GL_DEBUG to see shader source');
+ Module.printErr('Enable GL_DEBUG to see shader source');
#endif
}
};
@@ -1069,11 +1086,13 @@ var LibraryGL = {
_glUseProgram = function(program) {
#if GL_DEBUG
if (GL.debug) {
- console.log('[using program with shaders:]');
- GL.programShaders[program].forEach(function(shader) {
- console.log(' shader ' + shader + ', original source: ' + GL.shaderOriginalSources[shader]);
- console.log(' Source: ' + GL.shaderSources[shader]);
- });
+ Module.printErr('[using program with shaders]');
+ if (program) {
+ GL.programShaders[program].forEach(function(shader) {
+ Module.printErr(' shader ' + shader + ', original source: ' + GL.shaderOriginalSources[shader]);
+ Module.printErr(' Source: ' + GL.shaderSources[shader]);
+ });
+ }
}
#endif
GL.currProgram = program;
@@ -1140,7 +1159,7 @@ var LibraryGL = {
} else if (GL.shaders[id]) {
_glDeleteShader(id);
} else {
- console.log('WARNING: deleteObject received invalid id: ' + id);
+ Module.printErr('WARNING: deleteObject received invalid id: ' + id);
}
}; break;
case 'glGetObjectParameteriv': func = function(id, type, result) {
@@ -1157,7 +1176,7 @@ var LibraryGL = {
}
_glGetShaderiv(id, type, result);
} else {
- console.log('WARNING: getObjectParameteriv received invalid id: ' + id);
+ Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id);
}
}; break;
case 'glGetInfoLog': func = function(id, maxLength, length, infoLog) {
@@ -1166,12 +1185,13 @@ var LibraryGL = {
} else if (GL.shaders[id]) {
_glGetShaderInfoLog(id, maxLength, length, infoLog);
} else {
- console.log('WARNING: getObjectParameteriv received invalid id: ' + id);
+ Module.printErr('WARNING: getObjectParameteriv received invalid id: ' + id);
}
}; break;
case 'glBindProgram': func = function(type, id) {
assert(id == 0);
}; break;
+ case 'glDrawRangeElements': func = _glDrawRangeElements; break;
case 'glShaderSource': func = _glShaderSource; break;
case 'glCompileShader': func = _glCompileShader; break;
case 'glLinkProgram': func = _glLinkProgram; break;
@@ -1220,9 +1240,9 @@ var LibraryGL = {
case 'glCheckFramebufferStatus': func = _glCheckFramebufferStatus; break;
case 'glRenderbufferStorage': func = _glRenderbufferStorage; break;
default: {
- console.log('WARNING: getProcAddress failed for ' + name);
+ Module.printErr('WARNING: getProcAddress failed for ' + name);
func = function() {
- console.log('WARNING: empty replacement for ' + name + ' called, no-op');
+ Module.printErr('WARNING: empty replacement for ' + name + ' called, no-op');
return 0;
};
}
@@ -1236,9 +1256,9 @@ var LibraryGL = {
$GLImmediate__postset: 'Browser.moduleContextCreatedCallbacks.push(function() { GL.immediate.init() });',
$GLImmediate__deps: ['$Browser'],
$GLImmediate: {
- // Vertex and index data
- maxElements: 10240,
MAX_TEXTURES: 7,
+
+ // Vertex and index data
vertexData: null, // current vertex data. either tempData (glBegin etc.) or a view into the heap (gl*Pointer). Default view is F32
vertexDataU8: null, // U8 view
tempData: null,
@@ -1297,6 +1317,46 @@ var LibraryGL = {
attrib.pointer = pointer;
},
+ // Temporary buffers
+ MAX_TEMP_BUFFER_SIZE: 2*1024*1024,
+ tempBufferIndexLookup: null,
+ tempVertexBuffers: null,
+ tempIndexBuffers: null,
+
+ generateTempBuffers: function() {
+ function ceilPower2(x) {
+ return Math.pow(2, Math.ceil(Math.log(i || 1)/Math.log(2)));
+ }
+ this.tempBufferIndexLookup = new Uint8Array(this.MAX_TEMP_BUFFER_SIZE+1);
+ var last = -1, curr = -1;
+ for (var i = 0; i <= this.MAX_TEMP_BUFFER_SIZE; i++) {
+ var size = ceilPower2(i);
+ if (size != last) {
+ curr++;
+ last = size;
+ }
+ this.tempBufferIndexLookup[i] = curr;
+ }
+ this.tempVertexBuffers = [];
+ this.tempIndexBuffers = [];
+ last = -1;
+ for (var i = 0; i <= this.MAX_TEMP_BUFFER_SIZE; i++) {
+ var size = ceilPower2(i);
+ curr = this.tempBufferIndexLookup[i];
+ if (size != last) {
+ this.tempVertexBuffers[curr] = Module.ctx.createBuffer();
+ Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, this.tempVertexBuffers[curr]);
+ Module.ctx.bufferData(Module.ctx.ARRAY_BUFFER, size, Module.ctx.DYNAMIC_DRAW);
+ Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, null);
+ this.tempIndexBuffers[curr] = Module.ctx.createBuffer();
+ Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, this.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;
+ }
+ }
+ },
+
// Renderers
addRendererComponent: function(name, size, type) {
if (!this.rendererComponents[name]) {
@@ -1338,7 +1398,7 @@ var LibraryGL = {
}
if (!cacheItem.renderer) {
#if GL_DEBUG
- console.log('generating renderer for ' + JSON.stringify(attributes));
+ Module.printErr('generating renderer for ' + JSON.stringify(attributes));
#endif
cacheItem.renderer = this.createRenderer();
}
@@ -1545,20 +1605,12 @@ var LibraryGL = {
}
// Buffers for data
- this.tempData = new Float32Array(this.maxElements);
- this.indexData = new Uint16Array(this.maxElements);
+ this.tempData = new Float32Array(this.MAX_TEMP_BUFFER_SIZE >> 2);
+ this.indexData = new Uint16Array(this.MAX_TEMP_BUFFER_SIZE >> 1);
this.vertexDataU8 = new Uint8Array(this.tempData.buffer);
- this.vertexObject = Module.ctx.createBuffer();
- Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, this.vertexObject);
- Module.ctx.bufferData(Module.ctx.ARRAY_BUFFER, this.maxElements*4, Module.ctx.DYNAMIC_DRAW);
- Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, null);
-
- this.indexObject = Module.ctx.createBuffer();
- Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, this.indexObject);
- Module.ctx.bufferData(Module.ctx.ELEMENT_ARRAY_BUFFER, this.maxElements*2, Module.ctx.DYNAMIC_DRAW);
- Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, null);
+ this.generateTempBuffers();
this.clientColor = new Float32Array([1, 1, 1, 1]);
@@ -1572,17 +1624,30 @@ var LibraryGL = {
}
GL.immediate.prepareClientAttributes(count, false);
GL.immediate.mode = mode;
+ if (!GL.currArrayBuffer) {
+ GL.immediate.vertexData = {{{ makeHEAPView('F32', 'GL.immediate.vertexPointer', 'GL.immediate.vertexPointer + (first+count)*GL.immediate.stride') }}}; // XXX assuming float
+ GL.immediate.firstVertex = first;
+ GL.immediate.lastVertex = first + count;
+ }
GL.immediate.flush(null, first);
};
- _glDrawElements = function(mode, count, type, indices) {
- if (GL.immediate.totalEnabledClientAttributes == 0 && mode <= 6) {
+ _glDrawElements = function(mode, count, type, indices, start, end) { // start, end are given if we come from glDrawRangeElements
+ if (GL.immediate.totalEnabledClientAttributes == 0 && mode <= 6 && GL.currElementArrayBuffer) {
Module.ctx.drawElements(mode, count, type, indices);
return;
}
+ if (!GL.currElementArrayBuffer) {
+ assert(type == Module.ctx.UNSIGNED_SHORT); // We can only emulate buffers of this kind, for now
+ }
GL.immediate.prepareClientAttributes(count, false);
GL.immediate.mode = mode;
- GL.immediate.flush(count, indices);
+ if (!GL.currArrayBuffer) {
+ GL.immediate.firstVertex = end ? start : TOTAL_MEMORY; // if we don't know the start, set an invalid value and we will calculate it later from the indices
+ GL.immediate.lastVertex = end ? end+1 : 0;
+ GL.immediate.vertexData = {{{ makeHEAPView('F32', 'GL.immediate.vertexPointer', '(end ? GL.immediate.vertexPointer + (end+1)*GL.immediate.stride : TOTAL_MEMORY)') }}}; // XXX assuming float
+ }
+ GL.immediate.flush(count, 0, indices);
};
},
@@ -1629,14 +1694,15 @@ var LibraryGL = {
if (!beginEnd) {
bytes *= count;
if (!GL.currArrayBuffer) {
- GL.immediate.vertexData = {{{ makeHEAPView('F32', 'start', 'start + bytes') }}}; // XXX assuming float
+ GL.immediate.vertexPointer = start;
}
GL.immediate.vertexCounter = bytes / 4; // XXX assuming float
}
},
- flush: function(numProvidedIndexes, startIndex) {
+ flush: function(numProvidedIndexes, startIndex, ptr) {
startIndex = startIndex || 0;
+ ptr = ptr || 0;
var renderer = this.getRenderer();
@@ -1644,10 +1710,31 @@ var LibraryGL = {
var numVertexes = 4 * this.vertexCounter / GL.immediate.stride; // XXX assuming float
assert(numVertexes % 1 == 0);
- var restoreElementArrayBuffer = false;
+ var emulatedElementArrayBuffer = false;
var numIndexes = 0;
if (numProvidedIndexes) {
numIndexes = numProvidedIndexes;
+ if (!GL.currArrayBuffer && GL.immediate.firstVertex > GL.immediate.lastVertex) {
+ // Figure out the first and last vertex from the index data
+ assert(!GL.currElementArrayBuffer); // If we are going to upload array buffer data, we need to find which range to
+ // upload based on the indices. If they are in a buffer on the GPU, that is very
+ // inconvenient! So if you do not have an array buffer, you should also not have
+ // an element array buffer. But best is to use both buffers!
+ for (var i = 0; i < numProvidedIndexes; i++) {
+ var currIndex = {{{ makeGetValue('ptr', 'i*2', 'i16', null, 1) }}};
+ GL.immediate.firstVertex = Math.min(GL.immediate.firstVertex, currIndex);
+ GL.immediate.lastVertex = Math.max(GL.immediate.lastVertex, currIndex+1);
+ }
+ }
+ if (!GL.currElementArrayBuffer) {
+ // If no element array buffer is bound, then indices is a literal pointer to clientside data
+ assert(numProvidedIndexes << 1 <= GL.immediate.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (a)');
+ var indexBuffer = GL.immediate.tempIndexBuffers[GL.immediate.tempBufferIndexLookup[numProvidedIndexes << 1]];
+ Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, indexBuffer);
+ Module.ctx.bufferSubData(Module.ctx.ELEMENT_ARRAY_BUFFER, 0, {{{ makeHEAPView('U16', 'ptr', 'ptr + (numProvidedIndexes << 1)') }}});
+ ptr = 0;
+ emulatedElementArrayBuffer = true;
+ }
} else if (GL.immediate.mode > 6) { // above GL_TRIANGLE_FAN are the non-GL ES modes
if (GL.immediate.mode == 7) { // GL_QUADS
var numQuads = numVertexes / 4;
@@ -1664,16 +1751,21 @@ var LibraryGL = {
} else {
throw 'unsupported immediate mode ' + GL.immediate.mode;
}
- assert(numIndexes < GL.immediate.maxElements, 'too many immediate mode indexes');
-
- Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, this.indexObject);
+ assert(numIndexes << 1 <= GL.immediate.MAX_TEMP_BUFFER_SIZE, 'too many immediate mode indexes (b)');
+ var indexBuffer = GL.immediate.tempIndexBuffers[GL.immediate.tempBufferIndexLookup[numIndexes << 1]];
+ Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, indexBuffer);
Module.ctx.bufferSubData(Module.ctx.ELEMENT_ARRAY_BUFFER, 0, this.indexData.subarray(0, numIndexes));
- restoreElementArrayBuffer = true;
+ emulatedElementArrayBuffer = true;
}
if (!GL.currArrayBuffer) {
Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, this.vertexObject);
- Module.ctx.bufferSubData(Module.ctx.ARRAY_BUFFER, 0, this.vertexData.subarray(0, this.vertexCounter));
+ var start = GL.immediate.firstVertex*GL.immediate.stride;
+ var end = GL.immediate.lastVertex*GL.immediate.stride;
+ assert(end <= GL.immediate.MAX_TEMP_BUFFER_SIZE, 'too much vertex data');
+ var vertexBuffer = GL.immediate.tempVertexBuffers[GL.immediate.tempBufferIndexLookup[end]];
+ Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, vertexBuffer);
+ Module.ctx.bufferSubData(Module.ctx.ARRAY_BUFFER, start, this.vertexData.subarray(start >> 2, end >> 2));
}
// Render
@@ -1684,10 +1776,7 @@ var LibraryGL = {
renderer.prepare();
if (numIndexes) {
- if (!numProvidedIndexes) {
- Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, this.indexObject);
- }
- Module.ctx.drawElements(Module.ctx.TRIANGLES, numIndexes, Module.ctx.UNSIGNED_SHORT, startIndex);
+ Module.ctx.drawElements(Module.ctx.TRIANGLES, numIndexes, Module.ctx.UNSIGNED_SHORT, ptr);
} else {
Module.ctx.drawArrays(GL.immediate.mode, startIndex, numVertexes);
}
@@ -1697,7 +1786,7 @@ var LibraryGL = {
if (!GL.currArrayBuffer) {
Module.ctx.bindBuffer(Module.ctx.ARRAY_BUFFER, null);
}
- if (restoreElementArrayBuffer) {
+ if (emulatedElementArrayBuffer) {
Module.ctx.bindBuffer(Module.ctx.ELEMENT_ARRAY_BUFFER, GL.buffers[GL.currElementArrayBuffer] || null);
}
if (!GL.currProgram) {
@@ -1722,6 +1811,8 @@ var LibraryGL = {
glEnd: function() {
GL.immediate.prepareClientAttributes(GL.immediate.rendererComponents['V'], true);
+ GL.immediate.firstVertex = 0;
+ GL.immediate.lastVertex = GL.immediate.vertexCounter / (GL.immediate.stride >> 2);
GL.immediate.flush();
GL.immediate.disableBeginEndClientAttributes();
},
@@ -1734,7 +1825,7 @@ var LibraryGL = {
GL.immediate.vertexData[GL.immediate.vertexCounter++] = y;
GL.immediate.vertexData[GL.immediate.vertexCounter++] = z || 0;
#if ASSERTIONS
- assert(GL.immediate.vertexCounter < GL.immediate.maxElements);
+ assert(GL.immediate.vertexCounter << 2 < GL.immediate.MAX_TEMP_BUFFER_SIZE);
#endif
GL.immediate.addRendererComponent('V', 3, Module.ctx.FLOAT);
},
@@ -1840,6 +1931,11 @@ var LibraryGL = {
glNormal3f: function(){}, // TODO
+ // Additional non-GLES rendering calls
+
+ glDrawRangeElements: function(mode, start, end, count, type, indices) {
+ _glDrawElements(mode, count, type, indices, start, end);
+ },
// ClientState/gl*Pointer
@@ -1922,7 +2018,7 @@ var LibraryGL = {
glLoadMatrixf: function(matrix) {
#if GL_DEBUG
- console.log('glLoadMatrixf receiving: ' + Array.prototype.slice.call(HEAPF32.subarray(matrix >> 2, (matrix >> 2) + 16)));
+ if (GL.debug) Module.printErr('glLoadMatrixf receiving: ' + Array.prototype.slice.call(HEAPF32.subarray(matrix >> 2, (matrix >> 2) + 16)));
#endif
GL.immediate.matrix.lib.mat4.set({{{ makeHEAPView('F32', 'matrix', 'matrix+16*4') }}}, GL.immediate.matrix[GL.immediate.currentMatrix]);
},