From c769105adbb1e1fd5ce8a7dfb43b8c7b14b8d0f6 Mon Sep 17 00:00:00 2001 From: Sergey Stoma Date: Fri, 2 Aug 2024 17:39:28 -0400 Subject: [PATCH] Track object creation (and texture update) stack trace and expose tracked textures in the extension info call. --- src/augment-api.js | 7 +++++++ src/utils.js | 20 ++++++++++++++++++++ test/index.js | 1 + test/tests/info-tests.js | 8 +++++--- test/tests/stack-tests.js | 24 ++++++++++++++++++++++++ 5 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 test/tests/stack-tests.js diff --git a/src/augment-api.js b/src/augment-api.js index 9bff352..5d57c14 100644 --- a/src/augment-api.js +++ b/src/augment-api.js @@ -29,6 +29,8 @@ import { getDrawingbufferInfo, isBufferSource, isNumber, + getStackTrace, + collectObjects, } from './utils.js'; //------------ [ from https://github.com/KhronosGroup/WebGLDeveloperTools ] @@ -87,6 +89,7 @@ export function augmentAPI(ctx, nameOfClass, options = {}) { ctx: { getMemoryInfo() { const drawingbuffer = computeDrawingbufferSize(ctx, drawingBufferInfo); + const textures = collectObjects(sharedState, 'WebGLTexture'); return { memory: { ...memory, @@ -96,6 +99,7 @@ export function augmentAPI(ctx, nameOfClass, options = {}) { resources: { ...resources, }, + textures, }; }, }, @@ -203,6 +207,7 @@ export function augmentAPI(ctx, nameOfClass, options = {}) { ++resources[typeName]; webglObjectToMemory.set(webglObj, { size: 0, + stackCreated: getStackTrace(), }); }; } @@ -314,6 +319,8 @@ export function augmentAPI(ctx, nameOfClass, options = {}) { memory.texture -= oldSize; memory.texture += info.size; + + info.stackUpdated = getStackTrace(); } function updateTexStorage(target, levels, internalFormat, width, height, depth) { diff --git a/src/utils.js b/src/utils.js index 4263e8f..a5dac99 100644 --- a/src/utils.js +++ b/src/utils.js @@ -126,3 +126,23 @@ export function computeDrawingbufferSize(gl, drawingBufferInfo) { export function isNumber(v) { return typeof v === 'number'; } + +export function collectObjects(state, type) { + const list = [...state.webglObjectToMemory.keys()] + .filter((obj) => obj.constructor.name === type) + .map((obj) => state.webglObjectToMemory.get(obj)); + + return list; +} + +function cleanStackLine(line) { + return line.replace(/^\s*at\s+/, ''); +} + +export function getStackTrace() { + const stack = (new Error()).stack; + const lines = stack.split('\n'); + // Remove the first two entries, the error message and this function itself, or the webgl-memory itself. + const userLines = lines.slice(2).filter((l) => !l.includes('webgl-memory')); + return userLines.map((l) => cleanStackLine(l)); +} diff --git a/test/index.js b/test/index.js index b785442..22868bc 100644 --- a/test/index.js +++ b/test/index.js @@ -11,6 +11,7 @@ import './tests/sync-tests.js'; import './tests/texture-tests.js'; import './tests/transformfeedback-tests.js'; import './tests/vertexarray-tests.js'; +import './tests/stack-tests.js'; const settings = Object.fromEntries(new URLSearchParams(window.location.search).entries()); if (settings.reporter) { diff --git a/test/tests/info-tests.js b/test/tests/info-tests.js index a1b9382..e4fb0dd 100644 --- a/test/tests/info-tests.js +++ b/test/tests/info-tests.js @@ -22,7 +22,7 @@ describe('info tests', () => { assertEqual(drawingbufferSize, canvasSize); const info = ext.getMemoryInfo(); - const {memory, resources} = info; + const {memory, resources, textures} = info; assertEqual(memory.buffer, 0); assertEqual(memory.texture, 0); @@ -40,6 +40,7 @@ describe('info tests', () => { assertEqual(resources.texture, 0); assertEqual(resources.transformFeedback, undefined); assertEqual(resources.vertexArray, undefined); + assertEqual(textures.length, 0); }); it('test base state webgl2', () => { @@ -47,7 +48,7 @@ describe('info tests', () => { assertTruthy(ext, 'got extension'); const info = ext.getMemoryInfo(); - const {memory, resources} = info; + const {memory, resources, textures} = info; assertEqual(memory.buffer, 0); assertEqual(memory.texture, 0); @@ -65,6 +66,7 @@ describe('info tests', () => { assertEqual(resources.texture, 0); assertEqual(resources.transformFeedback, 0); assertEqual(resources.vertexArray, 0); + assertEqual(textures.length, 0); }); it('test canvas resize', () => { @@ -92,4 +94,4 @@ describe('info tests', () => { }); -}); \ No newline at end of file +}); diff --git a/test/tests/stack-tests.js b/test/tests/stack-tests.js new file mode 100644 index 0000000..331e61e --- /dev/null +++ b/test/tests/stack-tests.js @@ -0,0 +1,24 @@ +import {describe, it} from '../mocha-support.js'; +import {assertEqual, assertTruthy} from '../assert.js'; +import {createContext} from '../webgl.js'; + +describe('stack tests', () => { + + it('test stack capture', () => { + const {gl, ext} = createContext(); + + const tex1 = gl.createTexture(); + + gl.bindTexture(gl.TEXTURE_2D, tex1); + gl.texImage2D(gl.TEXTURE_2D, 1, gl.RGBA, 16, 8, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + + const info = ext.getMemoryInfo(); + const {textures} = info; + + assertEqual(textures.length, 1); + assertTruthy(textures[0].stackCreated); + assertTruthy(textures[0].stackUpdated); + + gl.deleteTexture(tex1); + }); +});