diff options
Diffstat (limited to 'src/library_gl.js')
-rw-r--r-- | src/library_gl.js | 198 |
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]); }, |