-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathwebgl-recorder.js
155 lines (135 loc) · 5.32 KB
/
webgl-recorder.js
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
148
149
150
151
152
153
154
155
(function() {
var getContext = HTMLCanvasElement.prototype.getContext;
var requestAnimationFrame = window.requestAnimationFrame;
var frameSincePageLoad = 0;
function countFrames() {
frameSincePageLoad++;
requestAnimationFrame(countFrames);
}
window.requestAnimationFrame = function() {
return requestAnimationFrame.apply(window, arguments);
};
HTMLCanvasElement.prototype.getContext = function(type) {
const canvas = this;
const context = getContext.apply(canvas, arguments);
if (type === 'webgl' || type === 'experimental-webgl') {
let oldWidth = canvas.width;
let oldHeight = canvas.height;
let oldFrameCount = frameSincePageLoad;
const trace = [];
const variables = {};
trace.push(' gl.canvas.width = ' + oldWidth + ';');
trace.push(' gl.canvas.height = ' + oldHeight + ';');
function compileTrace() {
let text = 'function* render(gl) {\n';
text += ' // Recorded using https://github.com/evanw/webgl-recorder\n';
for (let key in variables) {
text += ' const ' + key + 's = [];\n';
}
text += trace.join('\n');
text += '\n}\n';
return text;
}
function downloadTrace() {
const text = compileTrace();
const link = document.createElement('a');
link.href = URL.createObjectURL(new Blob([text], {type: 'application/javascript'}));
link.download = 'trace.js';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function getVariable(value) {
if (value instanceof WebGLActiveInfo ||
value instanceof WebGLBuffer ||
value instanceof WebGLFramebuffer ||
value instanceof WebGLProgram ||
value instanceof WebGLRenderbuffer ||
value instanceof WebGLShader ||
value instanceof WebGLShaderPrecisionFormat ||
value instanceof WebGLTexture ||
value instanceof WebGLUniformLocation ||
value instanceof WebGLVertexArrayObject ||
// In Chrome, value won't be an instanceof WebGLVertexArrayObject.
(value && value.constructor.name == "WebGLVertexArrayObjectOES") ||
typeof value === 'object') {
const name = value.constructor.name;
const list = variables[name] || (variables[name] = []);
let index = list.indexOf(value);
if (index === -1) {
index = list.length;
list.push(value);
}
return name + 's[' + index + ']';
}
return null;
}
function patch(name, object) {
const patched = {};
for (const key in object) {
const value = object[key];
if (typeof value === 'function') {
patched[key] = function () {
const result = value.apply(object, arguments);
if (frameSincePageLoad !== oldFrameCount) {
oldFrameCount = frameSincePageLoad;
trace.push(' yield;');
}
if (canvas.width !== oldWidth || canvas.height !== oldHeight) {
oldWidth = canvas.width;
oldHeight = canvas.height;
trace.push(' gl.canvas.width = ' + oldWidth + ';');
trace.push(' gl.canvas.height = ' + oldHeight + ';');
}
const args = Array.prototype.map.call(arguments, arg => {
if (typeof arg === 'number' || typeof arg === 'boolean' || typeof arg === 'string' || arg === null) {
return JSON.stringify(arg);
} else if (ArrayBuffer.isView(arg)) {
return `new ${arg.constructor.name}([${Array.prototype.slice.call(arg)}])`;
} else {
const variable = getVariable(arg);
if (variable !== null) {
return variable;
} else {
console.warn('unsupported value:', arg, `in call to ${name}.${key}`);
return 'null';
}
}
});
let text = `${name}.${key}(${args.join(', ')});`;
const variable = getVariable(result);
if (variable !== null) text = `${variable} = ${text}`;
trace.push(' ' + text);
if (result === null) return null;
if (result === undefined) return undefined;
// In Firefox, getExtension returns things with constructor.name == 'Object', but in
// Chrome getExtension returns unique constructor.names.
if (result.constructor.name === 'Object' || key == 'getExtension') {
return patch(variable, result);
}
return result;
};
} else { // typeof value !== function
Object.defineProperty(patched, key, {
configurable: false,
enumerable: true,
get() {
return object[key];
}
});
}
}
return patched;
}
const fakeContext = patch('gl', context);
Object.assign(fakeContext, {
trace: trace,
compileTrace: compileTrace,
downloadTrace: downloadTrace,
});
return fakeContext;
}
return context;
};
countFrames();
})();