{"id":19218214,"url":"https://github.com/greggman/webgl-memory","last_synced_at":"2025-05-15T05:04:31.906Z","repository":{"id":38810417,"uuid":"406601558","full_name":"greggman/webgl-memory","owner":"greggman","description":"A library to track webgl-memory","archived":false,"fork":false,"pushed_at":"2024-12-21T23:34:33.000Z","size":893,"stargazers_count":371,"open_issues_count":2,"forks_count":17,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-06T22:51:20.084Z","etag":null,"topics":["webgl","webgl2"],"latest_commit_sha":null,"homepage":"https://greggman.github.io/webgl-memory/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/greggman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-15T03:32:08.000Z","updated_at":"2025-03-31T08:19:50.000Z","dependencies_parsed_at":"2024-06-12T23:31:28.853Z","dependency_job_id":"9a5f9de0-87c7-460e-94c3-074a2d64b881","html_url":"https://github.com/greggman/webgl-memory","commit_stats":{"total_commits":93,"total_committers":8,"mean_commits":11.625,"dds":0.09677419354838712,"last_synced_commit":"b699345061ec132a22501554a44f354948b00f6d"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greggman%2Fwebgl-memory","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greggman%2Fwebgl-memory/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greggman%2Fwebgl-memory/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/greggman%2Fwebgl-memory/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/greggman","download_url":"https://codeload.github.com/greggman/webgl-memory/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247563923,"owners_count":20958971,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["webgl","webgl2"],"created_at":"2024-11-09T14:25:45.932Z","updated_at":"2025-04-10T06:18:03.639Z","avatar_url":"https://github.com/greggman.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WebGL-Memory\n\n\u003cimg src=\"./webgl-memory.png\" style=\"max-width: 640px\"\u003e\n\nThis is a WebGL-Memory tracker. You add the script to your page\nbefore you initialize WebGL and then for a given context\nyou can ask how much WebGL memory you're using.\n\nNote: This is only a guess as various GPUs have different\ninternal requirements. For example a GPU might require that\nRGB be expanded internally to RGBA. Similarly a GPU might\nhave alignment requirements. Still, this is likely to give\na reasonable approximation.\n\n## Usage\n\n```html\n\u003cscript src=\"https://greggman.github.io/webgl-memory/webgl-memory.js\" crossorigin\u003e\u003c/script\u003e\n```\n\nor \n\n```js\nimport 'https://greggman.github.io/webgl-memory/webgl-memory.js';\n```\n\nThen in your code\n\n```js\nconst ext = gl.getExtension('GMAN_webgl_memory');\n...\nif (ext) {\n  // memory info\n  const info = ext.getMemoryInfo();\n  // every texture, it's size, a stack of where it was created and a stack of where it was last updated.\n  const textures = ext.getResourcesInfo(WebGLTexture);\n  // every buffer, it's size, a stack of where it was created and a stack of where it was last updated.\n  const buffers = ext.getResourcesInfo(WebGLBuffer);\n}\n```\n\nThe info returned is \n\n```js\n{\n  memory: {\n    buffer: \u003cbytes used by buffers\u003e\n    texture: \u003cbytes used by textures\u003e\n    renderbuffer: \u003cbytes used by renderbuffers\u003e\n    drawingbuffer: \u003cbytes used by the canvas\u003e\n    total: \u003cbytes used in total\u003e\n  },\n  resources: {\n    buffer: \u003ccount of buffers\u003e,\n    renderbuffer: \u003ccount of renderbuffers\u003e\n    program: \u003ccount of programs\u003e\n    query: \u003ccount of query objects, WebGL2 only\u003e\n    sampler: \u003ccount of samplers, WebGL2 only\u003e\n    shader: \u003ccount of shaders\u003e\n    sync: \u003ccount of sync objects, WebGL2 only\u003e\n    texture: \u003ccount of textures\u003e\n    transformFeedback: \u003ccount of transformfeedbacks, WebGL2 only\u003e\n    vertexArray: \u003ccount of vertexArrays, only if used or WebGL2\u003e\n  }\n}\n```\n\nThe data for textures and buffers\n\n```js\nconst ext = gl.getExtension('GMAN_webgl_memory');\n...\nif (ext) {\n  const tex = gl.createTexture(); // 1\n  gl.bindTexture(gl.TEXTURE_2D, tex);\n  gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 4, 1); // 2\n\n  const buf = gl.createBuffer(); // 3\n  gl.bindBuffer(gl.ARRAY_BUFFER);\n  gl.bufferData(gl.ARRAY_BUFFER, 32, gl.STATIC_DRAW); // 4\n\n\n  const textures = ext.getResourcesInfo(WebGLTexture);\n  const buffers = ext.getResourcesInfo(WebGLBuffer);\n```\n\n```js\n  textures = [\n    { size: 16, stackCreated: '...1...', stackUpdated: '...2...' }\n  ]\n\n  buffers = [\n    { size: 32, stackCreated: '...3'''., stackUpdated: '...4...' }\n  ]\n```\n\n## Caveats\n\n1. You must have WebGL error free code. \n\n   If your code is generating WebGL errors you must fix those first\n   before using this library. Consider using [webgl-lint](https://greggman.github.io/webgl-lint) to help find your errors.\n\n2. Resource reference counting is not supported.\n\n   In WebGL if you delete a WebGLObject (a buffer, a texture, etc..),\n   then, if that object is still attached to something else (a buffer\n   attached to a vertex array, a texture attached to a framebuffer,\n   a shader attached to a program), the object is not actually deleted\n   until it's detached or the thing it's attached to is itself deleted  \n   \u003csub\u003eunless the thing it's attached to is currently bound. It's complicated 😭\u003c/sub\u003e\n\n   Tracking all of that in JavaScript is more work than I was willing\n   to put in ATM. My belief is that the stuff that is still attached\n   is usually not a problem because either (a) you'll delete the objects\n   that are holding the attachments (b) you'll detach the attachments\n   by binding new ones (c) you have a leak where you're creating more and\n   more of these objects that hold attachments in which case you can find\n   the issue by watching your resources counts climb.\n\n   Given that it seemed okay to skip this for now.\n\n3. Deletion by Garbage Collection (GC) is not supported\n\n   In JavaScript and WebGL, it's possible to let things get auto deleted by GC.\n\n   ```js\n   {\n      const buf = gl.createBuffer();\n      gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n      gl.bufferData(gl.ARRAY_BUFFER, 1024 * 1024 * 256, gl.STATIC_DRAW);\n      gl.bindBuffer(gl.ARRAY_BUFFER, null);\n   }\n   ```\n\n   Given the code above, buffer will, at some point in the future, get automatically\n   deleted. The problem is you have no idea when. JavaScript does know now the size of\n   VRAM nor does it have any concept of the size of the WebGL buffer (256meg in this case).\n   All JavaScript has is a tiny object that holds an ID for the actual OpenGL buffer\n   and maybe a little metadata. \n\n   That means there's absolutely no pressure to delete the buffer above in a timely\n   manner nor either is there any way for JavaScript to know that releasing that\n   object would free up VRAM.\n\n   In other words. Let's say you had a total of 384meg of ram. You'd expect this to\n   work.\n\n   ```js\n   {\n     const a = new Uint32Array(256 * 1024 * 1024)\n   }\n   {\n     const b = new Uint32Array(256 * 1024 * 1024)\n   }\n   ```\n\n   The code above allocates 512meg. Given we were pretending the system only has 384meg,\n   JavaScript will likely free `a` to make room for `b`\n\n   Now, Let's do the WebGL case and assume 384meg of VRAM\n\n   ```js\n   {\n      const a = gl.createBuffer();\n      gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n      gl.bufferData(gl.ARRAY_BUFFER, 1024 * 1024 * 256, gl.STATIC_DRAW);\n      gl.bindBuffer(gl.ARRAY_BUFFER, null);\n   }\n   {\n      const b = gl.createBuffer();\n      gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n      gl.bufferData(gl.ARRAY_BUFFER, 1024 * 1024 * 256, gl.STATIC_DRAW);\n      gl.bindBuffer(gl.ARRAY_BUFFER, null);\n   }\n   ```\n\n   In this case, JavaScript only sees `a` as taking a few bytes (the object that tracks\n   the OpenGL resource) so it has no idea that it needs to free `a` to make room for `b`.\n   This could would fail, ideally with `gl.OUT_OF_MEMORY`.\n\n   That was the long way of saying, you should never count on GC for WebGL!\n   Free your resources explicitly!\n\n   That's also part of the reason why we don't support this case because\n   counting on GC is not a useful solution.\n\n4. `texImage2D/3D` vs `texStorage2D/3D`\n\n   Be aware that `texImage2D/3D` *may* require double the memory of\n   `texStorage2D/3D`. \n\n   Based on the design of `texImage2D/3D`, every mip level can have a\n   different size/format and so until it's time to draw, there is\n   no way to know if those levels will be updated by the app to be\n   matching. Further, in WebGL2, there's no way to know before draw time\n   if the app will set `TEXTURE_BASE_LEVEL` and `TEXTURE_MAX_LEVEL` to\n   be a *texture complete* subset of mip levels.\n   \n   WebGL-memory does not report this difference\n   because it's up to the implementation what really happens behind the scenes.\n   In general though, `texStorage2D/3D` has a much higher probability\n   of using less memory overall.\n   \n   The tradeoff for using `texStorage` is that the texture's size is immutable. \n   So, for example, if you wanted to wrap a user's image to a cube, and then\n   change that image when the user selects a different sized image, with `texImage`\n   you can just upload the new image to the existing texture. With `texStorage`\n   you'd be required to create a new texture. \n   \n5. `ELEMENT_ARRAY_BUFFER`\n\n   Buffers used with `ELEMENT_ARRAY_BUFFER` may need a second copy in ram.\n   This is because WebGL requires no out of bounds memory access (eg,\n   you have a buffer with 10 vertices but you have an index greater \u003e= 10).\n   This can be handled in 2 ways (1) if the driver advertizes \"robustness\"\n   then rely on the driver (2) keep a copy of that data in ram and check\n   before draw time that no indices are out of range.\n   \n   WebGL-memory does not report this difference because it's up to the\n   implementation. Further, unlike the texture issue above there is\n   nothing an app can do. Fortunately such buffers are usually\n   a small percent of the data used by most WebGL apps.\n\n## Example:\n\n[Click here for an Example](https://jsgist.org/?src=57dafa41cb1d2d5bc1520832db49f946)  \n[Unity example here](https://greggman.github.io/webgl-memory-unity-example/)\n\n## Development\n\n```bash\ngit clone https://github.com/greggman/webgl-memory.git\ncd webgl-memory\nnpm install\n```\n\nnow serve the folder\n\n```\nnpx servez\n```\n\nand go to [`http://localhost:8080/test?src=true`](http://localhost:8080/test?src=true)\n\n`src=true` tells the test harness to use the unrolled source from the `src` folder\nwhere as without it uses `webgl-memory.js` in the root folder which is built using\n`npm run build`.\n\n`grep=\u003csome expression\u003e` will limit the tests as in `...?src=true\u0026grep=renderbuffer` only\nruns the tests with `renderbuffer` in their description.\n\n## Live Tests\n\n[built version](https://greggman.github.io/webgl-memory/test/)  \n[source version](https://greggman.github.io/webgl-memory/test/?src=true)\n\n## Thoughts\n\nI'm not totally convinced this is the right way to do this. If I was making a\nwebgl app and I wanted to know this stuff I think I'd track it myself by wrapping\nmy own creation functions.\n\nIn other words, lets say I wanted to know how many times I call\n`fetch`.\n\n```js\nconst req = await fetch(url);\nconst text = await req.text();\n```\n\nI'd just refactor that \n\n```js\nlet fetchCount = 0;\nfunction doFetch(url) {\n  fetchCount++;\n  return fetch(url);\n}\n\n...\nconst req = await doFetch(url);\nconst text = await req.text();\n```\n\nNo need for some fancy library. Simple.\n\nI could do similar things for WebGL functions.\n\n```js\nlet textureCount = 0;\nfunction makeTexture(gl) {\n  textureCount++;\n  return gl.createTexture(gl);\n}\nfunction freeTexture(gl, tex) {\n  --textureCount;\n  gl.deleteTexture(tex);\n}\n\nconst tex = makeTexture(gl);\n...\nfreeTexture(gl, tex);\n```\n\nAlso, even if webgl-memory is an okay way to do it I'm not sure making it an extension was the best way\nvs just some library you call like `webglMemoryTracker.init(someWebGLRenderingContext)`. \nI structured it this way just because I used [webgl-lint](https://greggman.github.io/webgl-lint) as\nthe basis to get this working.\n\n## License\n\n[MIT](https://github.com/greggman/webgl-memory/blob/main/LICENCE.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreggman%2Fwebgl-memory","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgreggman%2Fwebgl-memory","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreggman%2Fwebgl-memory/lists"}