Last active
December 5, 2025 22:29
-
-
Save greggman/4650be4a40492e3e9f6ab0a1b28dec6d to your computer and use it in GitHub Desktop.
WebGPU: buffer gc test - commandBuffer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /*bug-in-github-api-content-can-not-be-empty*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /*bug-in-github-api-content-can-not-be-empty*/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const shortSize = (function() { | |
| const suffixes = ['b', 'k', 'mb', 'gb', 'tb', 'pb']; | |
| return function(size) { | |
| const suffixNdx = Math.log2(Math.abs(size)) / 10 | 0; | |
| const suffix = suffixes[Math.min(suffixNdx, suffixes.length - 1)]; | |
| const base = 2 ** (suffixNdx * 10); | |
| return `${(size / base).toFixed(0)}${suffix}`; | |
| }; | |
| })(); | |
| const device = await (await navigator.gpu.requestAdapter()).requestDevice(); | |
| device.addEventListener('uncapturederror', e => console.error(e.error.message)); | |
| function makeAndBindBuffer(device) { | |
| const module = device.createShaderModule({ | |
| code: ` | |
| @group(0) @binding(0) var<uniform> u: vec4f; | |
| @vertex fn vs() -> @builtin(position) vec4f { | |
| return u; | |
| } | |
| @fragment fn fs() -> @location(0) vec4f { | |
| return vec4f(0, 0, 0, 1); | |
| } | |
| `, | |
| }); | |
| const pipeline = device.createRenderPipeline({ | |
| layout: 'auto', | |
| vertex: { module }, | |
| fragment: { module, targets: [{ format: 'rgba8unorm' }] }, | |
| primitive: { topology: 'point-list' }, | |
| }); | |
| const buffer = device.createBuffer({ | |
| size: 16, | |
| usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.UNIFORM, | |
| }); | |
| const bindGroup = device.createBindGroup({ | |
| layout: pipeline.getBindGroupLayout(0), | |
| entries: [ | |
| { binding: 0, resource: { buffer }}, | |
| ], | |
| }); | |
| const rbEncoder = device.createRenderBundleEncoder({ | |
| colorFormats: ['rgba8unorm'], | |
| }); | |
| rbEncoder.setPipeline(pipeline); | |
| rbEncoder.setBindGroup(0, bindGroup); | |
| rbEncoder.draw(1); | |
| const renderBundle = rbEncoder.finish(); | |
| const tex = device.createTexture({ | |
| size: [1], | |
| format: 'rgba8unorm', | |
| usage: GPUTextureUsage.RENDER_ATTACHMENT, | |
| }) | |
| const encoder = device.createCommandEncoder(); | |
| const pass = encoder.beginRenderPass({ | |
| colorAttachments: [ | |
| { | |
| view: tex.createView(), | |
| loadOp: 'clear', | |
| storeOp: 'store', | |
| } | |
| ], | |
| }); | |
| pass.executeBundles([renderBundle]); | |
| pass.end(); | |
| const commandBuffer = encoder.finish(); | |
| return { | |
| commandBuffer, | |
| texRef: new WeakRef(tex), | |
| renderBundleRef: new WeakRef(renderBundle), | |
| bufRef: new WeakRef(buffer), | |
| bgRef: new WeakRef(bindGroup), | |
| }; | |
| } | |
| const size = 1024 * 1024 * 1; | |
| function makeTypedArray() { | |
| return new WeakRef(new Uint8Array(size)); | |
| } | |
| const {bufRef, bgRef, renderBundleRef, texRef, commandBuffer} = makeAndBindBuffer(device); | |
| const memRef = makeTypedArray(); | |
| function exists(e) { | |
| return e ? 'exists' : 'GCed'; | |
| } | |
| const data = new Float32Array(4); | |
| const memory = []; | |
| const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); | |
| let anythingExists = true; | |
| while (anythingExists) { | |
| await wait(1000); | |
| if (globalThis.gc) { | |
| globalThis.gc(); | |
| } | |
| device.queue.submit([]); | |
| memory.push(new Uint8Array(size)); | |
| const bufExists = !!bufRef.deref(); | |
| const bgExists = !!bgRef.deref(); | |
| const texExists = !!texRef.deref(); | |
| const bundleExists = !!renderBundleRef.deref(); | |
| const memExists = !!memRef.deref(); | |
| console.log(`buffer: ${exists(bufExists)}, bg: ${exists(bgExists)}, tex: ${exists(texExists)}, bundle: ${exists(bundleExists)}, mem: ${exists(memExists)}, mem-used: ${shortSize(memory.length * size)}`); | |
| anythingExists = bufExists || bgExists || texExists || bundleExists || memExists; | |
| } | |
| for (let i = 10; i > 0; --i) { | |
| console.log(`wait: ${i}`); | |
| await wait(1000); | |
| } | |
| memory.length = 0; | |
| const memRef2 = makeTypedArray(); | |
| anythingExists = true; | |
| while (anythingExists) { | |
| await wait(1000); | |
| if (globalThis.gc) { | |
| globalThis.gc(); | |
| } | |
| device.queue.submit([]); | |
| memory.push(new Uint8Array(size)); | |
| const memExists = !!memRef2.deref(); | |
| console.log(`mem: ${exists(memExists)}, mem-used: ${shortSize(memory.length * size)}`); | |
| anythingExists = memExists; | |
| } | |
| { | |
| device.queue.submit([]); | |
| console.log(`wait: a`); | |
| await wait(1000); | |
| device.queue.submit([]); | |
| console.log(`wait: b`); | |
| await wait(1000); | |
| device.queue.submit([]); | |
| console.log(`wait: c`); | |
| await wait(1000); | |
| console.log('submit command buffer'); | |
| device.queue.submit([commandBuffer]); | |
| await wait(1000); | |
| device.queue.submit([]); | |
| } | |
| console.log('stopped'); | |
| memory.length = 0; | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| {"name":"WebGPU: buffer gc test - commandBuffer","settings":{},"filenames":["index.html","index.css","index.js"]} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment