| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <title>WebGPU Memory Ceiling Test — Strix Halo</title> |
| <style> |
| body { font-family: monospace; background: #0d1117; color: #c9d1d9; padding: 24px; } |
| h1 { color: #58a6ff; } |
| #log { background: #010409; border: 1px solid #30363d; padding: 10px; white-space: pre-wrap; max-height: 600px; overflow-y: auto; } |
| button { background: #238636; color: white; border: none; padding: 8px 16px; cursor: pointer; margin: 4px; border-radius: 4px; } |
| </style> |
| </head> |
| <body> |
| <h1>WebGPU Memory Ceiling Test</h1> |
| <p>Allocates increasingly large GPU buffers to find the maximum WebGPU memory on Strix Halo.</p> |
| <button onclick="runTest()">Run Memory Test</button> |
| <div id="log"></div> |
|
|
| <script> |
| const log = document.getElementById('log'); |
| function l(msg) { log.textContent += msg + '\n'; log.scrollTop = log.scrollHeight; } |
| |
| async function runTest() { |
| if (!navigator.gpu) { l('WebGPU not supported'); return; } |
| const adapter = await navigator.gpu.requestAdapter(); |
| if (!adapter) { l('No adapter'); return; } |
| |
| const limits = adapter.limits; |
| l('=== Adapter Limits ==='); |
| l('maxBufferSize: ' + (limits.maxBufferSize / 1024 / 1024 / 1024).toFixed(2) + ' GB'); |
| l('maxStorageBufferBindingSize: ' + (limits.maxStorageBufferBindingSize / 1024 / 1024).toFixed(0) + ' MB'); |
| |
| const device = await adapter.requestDevice({ |
| requiredLimits: { |
| maxBufferSize: limits.maxBufferSize, |
| maxStorageBufferBindingSize: limits.maxStorageBufferBindingSize, |
| } |
| }); |
| |
| device.lost.then(info => l('DEVICE LOST: ' + info.reason + ' ' + info.message)); |
| |
| l('\n=== Allocation Test ==='); |
| l('Allocating 512MB buffers until failure...'); |
| |
| const buffers = []; |
| const CHUNK = 512 * 1024 * 1024; |
| let totalMB = 0; |
| |
| for (let i = 0; i < 128; i++) { |
| try { |
| device.pushErrorScope('out-of-memory'); |
| device.pushErrorScope('validation'); |
| |
| const buf = device.createBuffer({ |
| size: CHUNK, |
| usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, |
| label: 'test_' + i, |
| }); |
| buffers.push(buf); |
| totalMB += 512; |
| |
| const valErr = await device.popErrorScope(); |
| const oomErr = await device.popErrorScope(); |
| |
| if (valErr) { l('Validation error at ' + totalMB + ' MB: ' + valErr.message); break; } |
| if (oomErr) { l('OOM error at ' + totalMB + ' MB: ' + oomErr.message); break; } |
| |
| |
| device.queue.writeBuffer(buf, 0, new Uint32Array([42])); |
| await device.queue.onSubmittedWorkDone(); |
| |
| l('Allocated: ' + totalMB + ' MB (' + (totalMB / 1024).toFixed(1) + ' GB) - ' + (i + 1) + ' buffers'); |
| |
| } catch (e) { |
| l('EXCEPTION at ' + totalMB + ' MB: ' + e.message); |
| break; |
| } |
| } |
| |
| l('\n=== RESULT ==='); |
| l('Maximum WebGPU allocation: ' + totalMB + ' MB (' + (totalMB / 1024).toFixed(1) + ' GB)'); |
| l('Buffers allocated: ' + buffers.length); |
| l('Buffer size: 512 MB each'); |
| |
| |
| for (const buf of buffers) buf.destroy(); |
| l('\nBuffers destroyed. Test complete.'); |
| } |
| </script> |
| </body> |
| </html> |
|
|