Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix webgl tracing issues #21389

Merged
merged 1 commit into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 22 additions & 55 deletions src/library_webgl.js
Original file line number Diff line number Diff line change
Expand Up @@ -611,76 +611,43 @@ for (/**@suppress{duplicate}*/var i = 0; i < {{{ GL_POOL_TEMP_BUFFERS_SIZE }}};

#if TRACE_WEBGL_CALLS
hookWebGLFunction: (f, glCtx) => {
var realf = 'real_' + f;
glCtx[realf] = glCtx[f];
var numArgs = glCtx[realf].length;
if (numArgs === undefined) console.warn(`Unexpected WebGL function ${f} when binding TRACE_WEBGL_CALLS`);
var orig = glCtx[f];
var contextHandle = glCtx.canvas.GLctxObject.handle;
var threadId = (typeof _pthread_self != 'undefined') ? _pthread_self : () => 1;
// Accessing 'arguments' is super slow, so to avoid overhead, statically reason the number of arguments.
switch (numArgs) {
case 0: glCtx[f] = () => { var ret = glCtx[realf](); err(`[Thread ${threadId()}, GL ctx: ${contextHandle}]: ${f}() -> ${ret}`); return ret; }; break;
case 1: glCtx[f] = (a1) => { var ret = glCtx[realf](a1); err(`[Thread ${threadId()}, GL ctx: ${contextHandle}]: ${f}(${a1}) -> ${ret}`); return ret; }; break;
case 2: glCtx[f] = (a1, a2) => { var ret = glCtx[realf](a1, a2); err(`[Thread ${threadId()}, GL ctx: ${contextHandle}]: ${f}(${a1}, ${a2}) -> ${ret}`); return ret; }; break;
case 3: glCtx[f] = (a1, a2, a3) => { var ret = glCtx[realf](a1, a2, a3); err(`[Thread ${threadId()}, GL ctx: ${contextHandle}]: ${f}(${a1}, ${a2}, ${a3}) -> ${ret}`); return ret; }; break;
case 4: glCtx[f] = (a1, a2, a3, a4) => { var ret = glCtx[realf](a1, a2, a3, a4); err(`[Thread ${threadId()}, GL ctx: ${contextHandle}]: ${f}(${a1}, ${a2}, ${a3}, ${a4}) -> ${ret}`); return ret; }; break;
case 5: glCtx[f] = (a1, a2, a3, a4, a5) => { var ret = glCtx[realf](a1, a2, a3, a4, a5); err(`[Thread ${threadId()}, GL ctx: ${contextHandle}]: ${f}(${a1}, ${a2}, ${a3}, ${a4}, ${a5}) -> ${ret}`); return ret; }; break;
case 6: glCtx[f] = (a1, a2, a3, a4, a5, a6) => { var ret = glCtx[realf](a1, a2, a3, a4, a5, a6); err(`[Thread ${threadId()}, GL ctx: ${contextHandle}]: ${f}(${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}) -> ${ret}`); return ret; }; break;
case 7: glCtx[f] = (a1, a2, a3, a4, a5, a6, a7) => { var ret = glCtx[realf](a1, a2, a3, a4, a5, a6, a7); err(`[Thread ${threadId()}, GL ctx: ${contextHandle}]: ${f}(${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}) -> ${ret}`); return ret; }; break;
case 8: glCtx[f] = (a1, a2, a3, a4, a5, a6, a7, a8) => { var ret = glCtx[realf](a1, a2, a3, a4, a5, a6, a7, a8); err(`[Thread ${threadId()}, GL ctx: ${contextHandle}]: ${f}(${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}) -> ${ret}`); return ret; }; break;
case 9: glCtx[f] = (a1, a2, a3, a4, a5, a6, a7, a8, a9) => { var ret = glCtx[realf](a1, a2, a3, a4, a5, a6, a7, a8, a9); err(`[Thread ${threadId()}, GL ctx: ${contextHandle}]: ${f}(${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9}) -> ${ret}`); return ret; }; break;
case 10: glCtx[f] = (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) => { var ret = glCtx[realf](a1, a2, a3, a4, a5, a6, a7, a8, a9, a10); err(`[Thread ${threadId()}, GL ctx: ${contextHandle}]: ${f}(${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9}, ${a10}) -> ${ret}`); return ret; }; break;
case 11: glCtx[f] = (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) => { var ret = glCtx[realf](a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11); err(`[Thread ${threadId()}, GL ctx: ${contextHandle}]: ${f}(${a1}, ${a2}, ${a3}, ${a4}, ${a5}, ${a6}, ${a7}, ${a8}, ${a9}, ${a10}, ${a11}) -> ${ret}`); return ret; }; break;
default: console.warn('hookWebGL failed! Unexpected length ' + glCtx[realf].length);
}
glCtx[f] = function(...args) {
var ret = orig.apply(this, args);
// Some GL functions take a view of the entire linear memory. Replace
// such arguments with the string 'HEAP' to avoid serializing all of
// memory.
for (var i in args) {
if (ArrayBuffer.isView(args[i]) && args[i].byteLength === HEAPU8.byteLength) {
args[i] = 'HEAP';
}
}
#if PTHREADS
err(`[Thread ${_pthread_self()}, GL ctx: ${contextHandle}]: ${f}(${args}) -> ${ret}`);
#else
err(`[ctx: ${contextHandle}]: ${f}(${args}) -> ${ret}`);
#endif
return ret;
};
},

hookWebGL: function(glCtx) {
if (!glCtx) glCtx = this.detectWebGLContext();
if (!glCtx) return;
if (!((typeof WebGLRenderingContext != 'undefined' && glCtx instanceof WebGLRenderingContext)
|| (typeof WebGL2RenderingContext != 'undefined' && glCtx instanceof WebGL2RenderingContext))) {
|| (typeof WebGL2RenderingContext != 'undefined' && glCtx instanceof WebGL2RenderingContext))) {
return;
}

if (glCtx.webGlTracerAlreadyHooked) return;
glCtx.webGlTracerAlreadyHooked = true;

// Hot GL functions are ones that you'd expect to find during render loops
// (render calls, dynamic resource uploads), cold GL functions are load
// time functions (shader compilation, texture/mesh creation).
// Distinguishing between these two allows pinpointing locations of
// troublesome GL usage that might cause performance issues.
for (var f in glCtx) {
if (typeof glCtx[f] != 'function' || f.startsWith('real_')) continue;
this.hookWebGLFunction(f, glCtx);
if (typeof glCtx[f] == 'function') {
this.hookWebGLFunction(f, glCtx);
}
}
// The above injection won't work for texImage2D and texSubImage2D, which
// have multiple overloads.
glCtx['texImage2D'] = (a1, a2, a3, a4, a5, a6, a7, a8, a9) => {
var ret = (a7 !== undefined) ? glCtx['real_texImage2D'](a1, a2, a3, a4, a5, a6, a7, a8, a9) : glCtx['real_texImage2D'](a1, a2, a3, a4, a5, a6);
return ret;
};
glCtx['texSubImage2D'] = (a1, a2, a3, a4, a5, a6, a7, a8, a9) => {
var ret = (a8 !== undefined) ? glCtx['real_texSubImage2D'](a1, a2, a3, a4, a5, a6, a7, a8, a9) : glCtx['real_texSubImage2D'](a1, a2, a3, a4, a5, a6, a7);
return ret;
};
glCtx['texSubImage3D'] = (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) => {
var ret = (a9 !== undefined) ? glCtx['real_texSubImage3D'](a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) : glCtx['real_texSubImage2D'](a1, a2, a3, a4, a5, a6, a7, a8);
return ret;
};
glCtx['bufferData'] = (a1, a2, a3, a4, a5) => {
// WebGL1/2 versions have different parameters (not just extra ones)
var ret = (a4 !== undefined) ? glCtx['real_bufferData'](a1, a2, a3, a4, a5) : glCtx['real_bufferData'](a1, a2, a3);
return ret;
};
const matrixFuncs = ['uniformMatrix2fv', 'uniformMatrix3fv', 'uniformMatrix4fv'];
matrixFuncs.forEach(f => {
glCtx[f] = (a1, a2, a3, a4, a5) => {
// WebGL2 version has 2 extra optional parameters, ensure we forward them
return glCtx['real_' + f](a1, a2, a3, a4, a5);
}
});
},
#endif
// Returns the context handle to the new context.
Expand Down
4 changes: 3 additions & 1 deletion test/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2325,10 +2325,12 @@ def test_float_tex(self):
@requires_graphics_hardware
@parameterized({
'': ([],),
'tracing': (['-sTRACE_WEBGL_CALLS'],),
'es2': (['-sMIN_WEBGL_VERSION=2', '-sFULL_ES2', '-sWEBGL2_BACKWARDS_COMPATIBILITY_EMULATION'],),
'es2_tracing': (['-sMIN_WEBGL_VERSION=2', '-sFULL_ES2', '-sWEBGL2_BACKWARDS_COMPATIBILITY_EMULATION', '-sTRACE_WEBGL_CALLS'],),
})
def test_subdata(self, args):
if self.is_4gb() and args:
if self.is_4gb() and '-sMIN_WEBGL_VERSION=2' in args:
self.skipTest('texSubImage2D fails: https://crbug.com/325090165')
self.btest('gl_subdata.c', reference='float_tex.png', args=['-lGL', '-lglut'] + args)

Expand Down
Loading