tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

draw-storage-compute.html (4105B)


      1 <!doctype html>
      2 
      3 <!-- Render a triangle to a storage texture using a compute shader. The
      4     triangle should be displayed. Regression test for
      5     https://bugzilla.mozilla.org/show_bug.cgi?id=1972921. -->
      6 
      7 <html class="reftest-wait">
      8  <head>
      9    <meta charset="utf-8" />
     10  </head>
     11  <body>
     12    <canvas id="canvas" width=512 height=512></canvas>
     13  </body>
     14  <script>
     15    (async function() {
     16      try {
     17        var code = `
     18        @group(0) @binding(0) var output: texture_storage_2d<{PRESENTATION_FORMAT}, write>;
     19 
     20        @compute @workgroup_size(16, 16)
     21        fn main(
     22          @builtin(global_invocation_id) invocation_id: vec3u
     23        ) {
     24          let x = i32(invocation_id.x);
     25          let y = i32(invocation_id.y);
     26          if (y >= 128 && y < 384 && 2 * x >= 640 - y && 2 * x < 384 + y) {
     27            textureStore(output, invocation_id.xy, vec4f(1, 0, 0, 1));
     28          } else {
     29            textureStore(output, invocation_id.xy, vec4f(0, 0, 0, 1));
     30          }
     31        }
     32        `;
     33 
     34        const canvas = document.querySelector('canvas');
     35        const adapter = await navigator.gpu?.requestAdapter({ });
     36 
     37        const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
     38        if (presentationFormat != 'rgba8unorm' && presentationFormat != 'bgra8unorm') {
     39          throw new Error('Unsupported presentation format: ' + presentationFormat);
     40        }
     41        const deviceDescriptor = {};
     42        if (presentationFormat == 'bgra8unorm') {
     43          if (!adapter.features.has('bgra8unorm-storage')) {
     44            console.warn('Using rgba8unorm because bgra8unorm-storage feature is not available');
     45            presentationFormat = 'rgba8unorm';
     46          } else {
     47            deviceDescriptor.requiredFeatures = ['bgra8unorm-storage'];
     48          }
     49        }
     50 
     51        const device = await adapter?.requestDevice(deviceDescriptor);
     52        const context = canvas.getContext('webgpu');
     53        const devicePixelRatio = window.devicePixelRatio;
     54        canvas.width = canvas.clientWidth * devicePixelRatio;
     55        canvas.height = canvas.clientHeight * devicePixelRatio;
     56        context.configure({
     57          device,
     58          format: presentationFormat,
     59          usage: GPUTextureUsage.STORAGE_BINDING,
     60        });
     61 
     62        const canvasView = context.getCurrentTexture().createView();
     63        const bindGroupLayout = device.createBindGroupLayout({
     64          entries: [
     65            {
     66              binding: 0,
     67              visibility: GPUShaderStage.COMPUTE,
     68              storageTexture: {
     69                access: 'write-only',
     70                format: presentationFormat,
     71                viewDimension: '2d',
     72              },
     73            },
     74          ],
     75        });
     76        const bindGroup = device.createBindGroup({
     77          layout: bindGroupLayout,
     78          entries: [
     79            {
     80              binding: 0,
     81              resource: canvasView,
     82            },
     83          ],
     84        });
     85 
     86        const pipelineLayout = device.createPipelineLayout({
     87          bindGroupLayouts: [bindGroupLayout],
     88        });
     89        const pipeline = device.createComputePipeline({
     90          layout: pipelineLayout,
     91          compute: {
     92            module: device.createShaderModule({
     93              code: code.replace('{PRESENTATION_FORMAT}', presentationFormat),
     94            }),
     95          },
     96        });
     97 
     98        const commandEncoder = device.createCommandEncoder();
     99        const passEncoder = commandEncoder.beginComputePass();
    100        passEncoder.setBindGroup(0, bindGroup);
    101        passEncoder.setPipeline(pipeline);
    102        passEncoder.dispatchWorkgroups(32, 32);
    103        passEncoder.end();
    104        device.queue.submit([commandEncoder.finish()]);
    105        await device.queue.onSubmittedWorkDone();
    106        requestAnimationFrame(() => {
    107          requestAnimationFrame(() => document.documentElement.className = '');
    108        });
    109      } catch (error) {
    110        console.error(error);
    111        document.getElementById('canvas').style.display = 'none';
    112        document.body.append(error.toString());
    113        document.documentElement.className = '';
    114      }
    115    })();
    116  </script>
    117 </html>