1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
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!';
|