| diff --git a/src/library_gl.js b/src/library_gl.js |
| index 7471578..9228964 100644 |
| --- a/src/library_gl.js |
| +++ b/src/library_gl.js |
| @@ -1256,28 +1256,28 @@ var LibraryGL = { |
| |
| setClientAttribute: function(name, size, type, stride, pointer) { |
| var attrib = this.clientAttributes[GL.immediate.ATTRIBUTE_BY_NAME[name]]; |
| attrib.size = size; |
| attrib.type = type; |
| attrib.stride = stride; |
| attrib.pointer = pointer; |
| - attrib.name = name + size; |
| + attrib.name = Runtime.getStringConcat(name, size); |
| }, |
| |
| // Renderers |
| addRendererComponent: function(component) { |
| if (this.rendererComponents[component]) return; |
| this.rendererComponents[component] = 1; |
| - this.renderer += component; |
| + this.renderer = Runtime.getStringConcat(this.renderer, component); |
| }, |
| |
| setRenderer: function(renderer) { |
| var name = renderer; |
| if (GL.currProgram && renderer[0] != 'U') { |
| - name = 'UD' + GL.currProgram + '|' + renderer; // user-defined program renderer |
| + name = Runtime.getStringConcat(Runtime.getStringConcat('UD', GL.currProgram), Runtime.getStringConcat('|', renderer)); // user-defined program renderer |
| } |
| this.renderer = name; |
| if (this.renderers[name]) return this.renderers[name]; |
| this.renderers[name] = this.createRenderer(renderer); |
| return this.renderers[name]; |
| }, |
| |
| @@ -1300,15 +1300,18 @@ var LibraryGL = { |
| } |
| vertexSize += size * 4; // XXX assuming float |
| } else if (which == 'N') { |
| vertexSize += 4; // 1 char, + alignment |
| } else if (which == 'C') { |
| vertexSize += 4; // Up to 4 chars, + alignment |
| } else { |
| - console.log('Warning: Ignoring renderer attribute ' + which); |
| +#if ASSERTIONS |
| + console.log('Warning: Ignoring renderer attribute'); |
| + console.log(which); |
| +#endif |
| size = parseInt(renderer[i+1]); |
| vertexSize += size * 4; // XXX assuming float |
| } |
| } |
| assert(positionSize > 0); |
| // TODO: verify vertexSize is equal to the stride in enabled client arrays |
| var useCurrProgram = !!GL.currProgram; |
| @@ -1465,30 +1468,30 @@ var LibraryGL = { |
| var renderer = '', bytes = 0; |
| for (var i = 0; i < attributes.length; i++) { |
| var attribute = attributes[i]; |
| if (!attribute) break; |
| attribute.offset = attribute.pointer - start; |
| if (attribute.offset > bytes) { // ensure we start where we should |
| assert((attribute.offset - bytes)%4 == 0); // XXX assuming 4-alignment |
| - renderer += '?' + ((attribute.offset - bytes)/4); |
| + renderer = Runtime.getStringConcat(renderer, Runtime.getStringConcat('?', ((attribute.offset - bytes)/4))); |
| bytes += attribute.offset - bytes; |
| } |
| - renderer += attribute.name; |
| + renderer = Runtime.getStringConcat(renderer, attribute.name); |
| bytes += attribute.size * GL.immediate.byteSizeByType[attribute.type]; |
| if (bytes % 4 != 0) bytes += 4 - (bytes % 4); // XXX assuming 4-alignment |
| #if ASSERTIONS |
| assert(0 <= attribute.offset && attribute.offset < stride); // must all be in the same buffer |
| #endif |
| } |
| |
| assert(stride == 0 || bytes <= stride); |
| |
| if (bytes < stride) { // ensure the size is that of the stride |
| assert((stride - bytes)%4 == 0); // assuming float |
| - renderer += '?' + ((stride-bytes)/4); |
| + renderer = Runtime.getStringConcat(renderer, Runtime.getStringConcat('?', ((stride-bytes)/4))); |
| bytes = stride; |
| } |
| |
| bytes *= count; |
| if (!GL.currArrayBuffer) { |
| GL.immediate.vertexData = {{{ makeHEAPView('F32', 'start', 'start + bytes') }}}; // XXX assuming float |
| } |
| @@ -1671,15 +1674,15 @@ var LibraryGL = { |
| }, |
| |
| glVertexPointer__deps: ['$GLEmulation'], // if any pointers are used, glVertexPointer must be, and if it is, then we need emulation |
| glVertexPointer: function(size, type, stride, pointer) { |
| GL.immediate.setClientAttribute('V', size, type, stride, pointer); |
| }, |
| glTexCoordPointer: function(size, type, stride, pointer) { |
| - GL.immediate.setClientAttribute('T' + GL.immediate.clientActiveTexture, size, type, stride, pointer); |
| + GL.immediate.setClientAttribute(Runtime.getStringConcat('T', GL.immediate.clientActiveTexture), size, type, stride, pointer); |
| }, |
| glNormalPointer: function(type, stride, pointer) { |
| GL.immediate.setClientAttribute('N', 1, type, stride, pointer); |
| }, |
| glColorPointer: function(size, type, stride, pointer) { |
| GL.immediate.setClientAttribute('C', size, type, stride, pointer); |
| }, |
| diff --git a/src/runtime.js b/src/runtime.js |
| index 6a251c4..012a66d 100644 |
| --- a/src/runtime.js |
| +++ b/src/runtime.js |
| @@ -319,25 +319,34 @@ var Runtime = { |
| if (!Runtime.warnOnce.shown) Runtime.warnOnce.shown = {}; |
| if (!Runtime.warnOnce.shown[text]) { |
| Runtime.warnOnce.shown[text] = 1; |
| Module.printErr(text); |
| } |
| }, |
| |
| + // Cache for JS function wrappers for C functions in FUNCTION_TABLE |
| funcWrappers: {}, |
| - |
| getFuncWrapper: function(func) { |
| if (!Runtime.funcWrappers[func]) { |
| Runtime.funcWrappers[func] = function() { |
| FUNCTION_TABLE[func].apply(null, arguments); |
| }; |
| } |
| return Runtime.funcWrappers[func]; |
| }, |
| |
| + // Cache for small recurring strings generated by concatenating other |
| + // strings, use this to avoid needless allocation and collection |
| + stringCache: {}, |
| + getStringConcat: function(a, b) { |
| + var cacheItem = Runtime.stringCache[a]; |
| + if (!cacheItem) cacheItem = Runtime.stringCache[a] = {}; |
| + return cacheItem[b] || (cacheItem[b] = a + b); |
| + }, |
| + |
| #if RUNTIME_DEBUG |
| debug: true, // Switch to false at runtime to disable logging at the right times |
| |
| printObjectList: [], |
| |
| prettyPrint: function(arg) { |
| if (typeof arg == 'undefined') return '!UNDEFINED!'; |