Skip to content

Commit b0ebeb8

Browse files
committed
add getResourcesInfo
1 parent c769105 commit b0ebeb8

File tree

5 files changed

+178
-17
lines changed

5 files changed

+178
-17
lines changed

README.md

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ Then in your code
3030
const ext = gl.getExtension('GMAN_webgl_memory');
3131
...
3232
if (ext) {
33+
// memory info
3334
const info = ext.getMemoryInfo();
35+
// every texture, it's size, a stack of where it was created and a stack of where it was last updated.
36+
const textures = ext.getResourcesInfo(WebGLTexture);
37+
// every buffer, it's size, a stack of where it was created and a stack of where it was last updated.
38+
const buffers = ext.getResourcesInfo(WebGLBuffer);
3439
}
3540
```
3641

@@ -60,6 +65,35 @@ The info returned is
6065
}
6166
```
6267

68+
The data for textures and buffers
69+
70+
```js
71+
const ext = gl.getExtension('GMAN_webgl_memory');
72+
...
73+
if (ext) {
74+
const tex = gl.createTexture(); // 1
75+
gl.bindTexture(gl.TEXTURE_2D, tex);
76+
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 4, 1); // 2
77+
78+
const buf = gl.createBuffer(); // 3
79+
gl.bindBuffer(gl.ARRAY_BUFFER);
80+
gl.bufferData(gl.ARRAY_BUFFER, 32, gl.STATIC_DRAW); // 4
81+
82+
83+
const textures = ext.getResourcesInfo(WebGLTexture);
84+
const buffers = ext.getResourcesInfo(WebGLBuffer);
85+
```
86+
87+
```js
88+
textures = [
89+
{ size: 16, stackCreated: '...1...', stackUpdated: '...2...' }
90+
]
91+
92+
buffers = [
93+
{ size: 32, stackCreated: '...3'''., stackUpdated: '...4...' }
94+
]
95+
```
96+
6397
## Caveats
6498
6599
1. You must have WebGL error free code.
@@ -85,8 +119,73 @@ The info returned is
85119
the issue by watching your resources counts climb.
86120
87121
Given that it seemed okay to skip this for now.
88-
89-
3. `texImage2D/3D` vs `texStorage2D/3D`
122+
123+
3. Deletion by Garbage Collection (GC) is not supported
124+
125+
In JavaScript and WebGL, it's possible to let things get auto deleted by GC.
126+
127+
```js
128+
{
129+
const buf = gl.createBuffer();
130+
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
131+
gl.bufferData(gl.ARRAY_BUFFER, 1024 * 1024 * 256, gl.STATIC_DRAW);
132+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
133+
}
134+
```
135+
136+
Given the code above, buffer will, at some point in the future, get automatically
137+
deleted. The problem is you have no idea when. JavaScript does know now the size of
138+
VRAM nor does it have any concept of the size of the WebGL buffer (256meg in this case).
139+
All JavaScript has is a tiny object that holds an ID for the actual OpenGL buffer
140+
and maybe a little metadata.
141+
142+
That means there's absolutely no pressure to delete the buffer above in a timely
143+
manner nor either is there any way for JavaScript to know that releasing that
144+
object would free up VRAM.
145+
146+
In other words. Let's say you had a total of 384meg of ram. You'd expect this to
147+
work.
148+
149+
```js
150+
{
151+
const a = new Uint32Array(256 * 1024 * 1024)
152+
}
153+
{
154+
const b = new Uint32Array(256 * 1024 * 1024)
155+
}
156+
```
157+
158+
The code above allocates 512meg. Given we were pretending the system only has 384meg,
159+
JavaScript will likely free `a` to make room for `b`
160+
161+
Now, Let's do the WebGL case and assume 384meg of VRAM
162+
163+
```js
164+
{
165+
const a = gl.createBuffer();
166+
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
167+
gl.bufferData(gl.ARRAY_BUFFER, 1024 * 1024 * 256, gl.STATIC_DRAW);
168+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
169+
}
170+
{
171+
const b = gl.createBuffer();
172+
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
173+
gl.bufferData(gl.ARRAY_BUFFER, 1024 * 1024 * 256, gl.STATIC_DRAW);
174+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
175+
}
176+
```
177+
178+
In this case, JavaScript only sees `a` as taking a few bytes (the object that tracks
179+
the OpenGL resource) so it has no idea that it needs to free `a` to make room for `b`.
180+
This could would fail, ideally with `gl.OUT_OF_MEMORY`.
181+
182+
That was the long way of saying, you should never count on GC for WebGL!
183+
Free your resources explicitly!
184+
185+
That's also part of the reason why we don't support this case because
186+
counting on GC is not a useful solution.
187+
188+
4. `texImage2D/3D` vs `texStorage2D/3D`
90189
91190
Be aware that `texImage2D/3D` *may* require double the memory of
92191
`texStorage2D/3D`.
@@ -109,7 +208,7 @@ The info returned is
109208
you can just upload the new image to the existing texture. With `texStorage`
110209
you'd be required to create a new texture.
111210
112-
4. `ELEMENT_ARRAY_BUFFER`
211+
5. `ELEMENT_ARRAY_BUFFER`
113212
114213
Buffers used with `ELEMENT_ARRAY_BUFFER` may need a second copy in ram.
115214
This is because WebGL requires no out of bounds memory access (eg,
@@ -209,6 +308,6 @@ vs just some library you call like `webglMemoryTracker.init(someWebGLRenderingCo
209308
I structured it this way just because I used [webgl-lint](https://greggman.github.io/webgl-lint) as
210309
the basis to get this working.
211310

212-
## Licence
311+
## License
213312

214313
[MIT](https://github.com/greggman/webgl-memory/blob/main/LICENCE.md)

src/augment-api.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ export function augmentAPI(ctx, nameOfClass, options = {}) {
8989
ctx: {
9090
getMemoryInfo() {
9191
const drawingbuffer = computeDrawingbufferSize(ctx, drawingBufferInfo);
92-
const textures = collectObjects(sharedState, 'WebGLTexture');
9392
return {
9493
memory: {
9594
...memory,
@@ -99,9 +98,11 @@ export function augmentAPI(ctx, nameOfClass, options = {}) {
9998
resources: {
10099
...resources,
101100
},
102-
textures,
103101
};
104102
},
103+
getResourcesInfo(type) {
104+
return collectObjects(sharedState, type);
105+
},
105106
},
106107
},
107108
},
@@ -250,6 +251,7 @@ export function augmentAPI(ctx, nameOfClass, options = {}) {
250251

251252
memory.renderbuffer -= info.size;
252253
info.size = newSize;
254+
info.stackUpdated = getStackTrace();
253255
memory.renderbuffer += newSize;
254256
}
255257

@@ -407,6 +409,7 @@ export function augmentAPI(ctx, nameOfClass, options = {}) {
407409

408410
memory.buffer -= info.size;
409411
info.size = newSize;
412+
info.stackUpdated = getStackTrace();
410413
memory.buffer += newSize;
411414
},
412415

src/utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export function isNumber(v) {
129129

130130
export function collectObjects(state, type) {
131131
const list = [...state.webglObjectToMemory.keys()]
132-
.filter((obj) => obj.constructor.name === type)
132+
.filter(obj => obj instanceof type)
133133
.map((obj) => state.webglObjectToMemory.get(obj));
134134

135135
return list;

test/tests/info-tests.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe('info tests', () => {
2222
assertEqual(drawingbufferSize, canvasSize);
2323

2424
const info = ext.getMemoryInfo();
25-
const {memory, resources, textures} = info;
25+
const {memory, resources} = info;
2626

2727
assertEqual(memory.buffer, 0);
2828
assertEqual(memory.texture, 0);
@@ -40,6 +40,8 @@ describe('info tests', () => {
4040
assertEqual(resources.texture, 0);
4141
assertEqual(resources.transformFeedback, undefined);
4242
assertEqual(resources.vertexArray, undefined);
43+
44+
const textures = ext.getResourcesInfo(WebGLTexture);
4345
assertEqual(textures.length, 0);
4446
});
4547

@@ -48,7 +50,7 @@ describe('info tests', () => {
4850
assertTruthy(ext, 'got extension');
4951

5052
const info = ext.getMemoryInfo();
51-
const {memory, resources, textures} = info;
53+
const {memory, resources} = info;
5254

5355
assertEqual(memory.buffer, 0);
5456
assertEqual(memory.texture, 0);
@@ -66,6 +68,8 @@ describe('info tests', () => {
6668
assertEqual(resources.texture, 0);
6769
assertEqual(resources.transformFeedback, 0);
6870
assertEqual(resources.vertexArray, 0);
71+
72+
const textures = ext.getResourcesInfo(WebGLTexture);
6973
assertEqual(textures.length, 0);
7074
});
7175

test/tests/stack-tests.js

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,79 @@
11
import {describe, it} from '../mocha-support.js';
2-
import {assertEqual, assertTruthy} from '../assert.js';
2+
import {assertEqual, assertFalsy, assertTruthy} from '../assert.js';
33
import {createContext} from '../webgl.js';
44

55
describe('stack tests', () => {
66

7-
it('test stack capture', () => {
7+
it('test texture stack capture', () => {
88
const {gl, ext} = createContext();
99

1010
const tex1 = gl.createTexture();
1111

1212
gl.bindTexture(gl.TEXTURE_2D, tex1);
1313
gl.texImage2D(gl.TEXTURE_2D, 1, gl.RGBA, 16, 8, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
1414

15-
const info = ext.getMemoryInfo();
16-
const {textures} = info;
17-
18-
assertEqual(textures.length, 1);
19-
assertTruthy(textures[0].stackCreated);
20-
assertTruthy(textures[0].stackUpdated);
15+
{
16+
const textures = ext.getResourcesInfo(WebGLTexture);
17+
assertEqual(textures.length, 1);
18+
assertTruthy(textures[0].stackCreated);
19+
assertTruthy(textures[0].stackUpdated);
20+
}
2121

2222
gl.deleteTexture(tex1);
23+
24+
{
25+
const textures = ext.getResourcesInfo(WebGLTexture);
26+
assertEqual(textures.length, 0);
27+
}
2328
});
29+
30+
it('test buffers stack capture', () => {
31+
const {gl, ext} = createContext();
32+
33+
const buf = gl.createBuffer();
34+
35+
{
36+
const buffers = ext.getResourcesInfo(WebGLBuffer);
37+
assertEqual(buffers.length, 1);
38+
assertTruthy(buffers[0].stackCreated);
39+
assertFalsy(buffers[0].stackUpdated);
40+
}
41+
42+
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
43+
gl.bufferData(gl.ARRAY_BUFFER, 16, gl.STATIC_DRAW);
44+
45+
{
46+
const buffers = ext.getResourcesInfo(WebGLBuffer);
47+
assertEqual(buffers.length, 1);
48+
assertTruthy(buffers[0].stackCreated);
49+
assertTruthy(buffers[0].stackUpdated);
50+
}
51+
52+
gl.deleteBuffer(buf);
53+
54+
{
55+
const buffers = ext.getResourcesInfo(WebGLBuffer);
56+
assertEqual(buffers.length, 0);
57+
}
58+
});
59+
60+
it('test program stack capture', () => {
61+
const {gl, ext} = createContext();
62+
63+
const program = gl.createProgram();
64+
65+
{
66+
const programs = ext.getResourcesInfo(WebGLProgram);
67+
assertEqual(programs.length, 1);
68+
assertTruthy(programs[0].stackCreated);
69+
}
70+
71+
gl.deleteProgram(program);
72+
73+
{
74+
const programs = ext.getResourcesInfo(WebGLProgram);
75+
assertEqual(programs.length, 0);
76+
}
77+
});
78+
2479
});

0 commit comments

Comments
 (0)