Files
workinf_Blender_Wasm/web/tests/fixtures/native-gl-trace.ts
mes123456 f3e21c6b98
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
chore: checkpoint web Blender migration source, tools and docs
Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer,
web workflow engine and protocol, tooling, docs and task records.
Large generated evidence, user-testing archives, build snapshots and
imported test assets remain untracked.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-10 11:18:01 +08:00

32 lines
4.1 KiB
TypeScript

import type { Worker } from '@playwright/test';
export async function installNativeGLTrace(worker: Worker, finishBeforeCompute = false) {
await worker.evaluate(finishBeforeCompute => {
const root = self as any, proto = WebGL2RenderingContext.prototype as any, events: any[] = [];
const ids = new WeakMap<object, number>(), current = new WeakMap<object, number>(), uniforms = new WeakMap<object, { name: string; program: number }>(); let next = 1, frozen = false;
const id = (value: object | null) => { if(!value)return 0; if(!ids.has(value))ids.set(value, next++); return ids.get(value)!; };
root.__nativeGLTrace = events;
if(finishBeforeCompute){const post = MessagePort.prototype.postMessage; let sequence = 0;
MessagePort.prototype.postMessage = function(this: MessagePort, envelope: any, ...rest: any[]) { if(envelope?.request?.job && root.__wbv2LastGL){const current = ++sequence; console.info(`WBV2_GL_FINISH_BEGIN ${current}`); root.__wbv2LastGL.finish(); console.info(`WBV2_GL_FINISH_DONE ${current}`);} return Reflect.apply(post, this, [envelope, ...rest]); };
}
const activeUniform = proto.getActiveUniform, uniformLocation = proto.getUniformLocation;
const drawState = (gl: any) => {
const program = gl.getParameter(gl.CURRENT_PROGRAM), result: { program: number; blocks: any[]; samplers: any[] } = { program: id(program), blocks: [], samplers: [] };
if(!program)return result;
for(let i = 0; i < Math.min(16, gl.getProgramParameter(program, gl.ACTIVE_UNIFORM_BLOCKS)); i++){const binding = gl.getActiveUniformBlockParameter(program, i, gl.UNIFORM_BLOCK_BINDING); result.blocks.push({ name: gl.getActiveUniformBlockName(program, i), binding, required: gl.getActiveUniformBlockParameter(program, i, gl.UNIFORM_BLOCK_DATA_SIZE), buffer: id(gl.getIndexedParameter(gl.UNIFORM_BUFFER_BINDING, binding)), size: gl.getIndexedParameter(gl.UNIFORM_BUFFER_SIZE, binding) });}
for(let i = 0; i < Math.min(64, gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS)); i++){const item = Reflect.apply(activeUniform, gl, [program, i]) as WebGLActiveInfo; if([gl.SAMPLER_2D,gl.SAMPLER_3D,gl.SAMPLER_CUBE,gl.SAMPLER_2D_ARRAY,gl.INT_SAMPLER_2D,gl.UNSIGNED_INT_SAMPLER_2D].includes(item.type)){const location = Reflect.apply(uniformLocation, gl, [program, item.name]); if(location){const value = gl.getUniform(program, location); result.samplers.push({ name: item.name, type: item.type, units: ArrayBuffer.isView(value) ? Array.from(value as any) : value });}}}
return result;
};
const methods = ['useProgram','getUniformLocation','getActiveUniform','uniform1i','uniform1iv','uniform2iv','uniform3iv','uniform4iv','uniform1ui','uniform1fv','uniform2fv','uniform3fv','uniform4fv','uniformMatrix3fv','uniformMatrix4fv','uniformBlockBinding','bindBufferBase','bindBufferRange','vertexAttribPointer','vertexAttribIPointer','drawArrays','drawElements','drawArraysInstanced','drawElementsInstanced','activeTexture','bindTexture','texStorage2D','texSubImage2D','readPixels','getError'];
for(const name of methods){const original = proto[name]; if(typeof original !== 'function')continue;
proto[name] = function(this: any, ...args: any[]) {
root.__wbv2LastGL = this;
if(!frozen && name.startsWith('draw'))root.__nativeGLDrawState = drawState(this);
const result = Reflect.apply(original, this, args);
if(name === 'getUniformLocation' && result)uniforms.set(result as object, { name: args[1], program: id(args[0]) }); if(!current.has(this) || name === 'useProgram')current.set(this, id(this.getParameter(this.CURRENT_PROGRAM)));
if(!frozen){const brief = args.slice(0, 5).map(value => value == null || ['number','string','boolean'].includes(typeof value) ? value : ArrayBuffer.isView(value) ? { bytes: value.byteLength } : uniforms.get(value) ?? { kind: value.constructor?.name, id: id(value) }); events.push({ name, args: brief, program: current.get(this), result: name === 'getError' ? result : undefined }); if(events.length > 32)events.shift(); if(name === 'getError' && result)frozen = true;}
return result;
};
}
}, finishBeforeCompute);
}