tor-browser

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

draw-storage-bundle.html (5478B)


      1 <!doctype html>
      2 
      3 <!-- Render a triangle to a storage texture. The triangle should be displayed.
      4     Regression test for https://bugzilla.mozilla.org/show_bug.cgi?id=1972921. -->
      5 
      6 <html class="reftest-wait">
      7  <head>
      8    <meta charset="utf-8" />
      9  </head>
     10  <body>
     11    <canvas id="canvas" width=512 height=512></canvas>
     12  </body>
     13  <script>
     14    (async function() {
     15      try {
     16        var triangleVertWGSL = `@vertex
     17        fn main(
     18          @builtin(vertex_index) VertexIndex : u32
     19        ) -> @builtin(position) vec4f {
     20          var pos = array<vec2f, 3>(
     21            vec2(0.0, 3.0),
     22            vec2(-2.0, -1.0),
     23            vec2(2.0, -1.0)
     24          );
     25 
     26          return vec4f(pos[VertexIndex], 0.0, 1.0);
     27        }
     28        `;
     29 
     30        var redFragWGSL = `
     31        @group(0) @binding(0) var output: texture_storage_2d<{PRESENTATION_FORMAT}, write>;
     32 
     33        @fragment
     34        fn main(@builtin(position) pos: vec4f) -> @location(0) vec4f {
     35          let x = i32(pos.x);
     36          let y = i32(pos.y);
     37          if (y >= 128 && y < 384 && 2 * x >= 640 - y && 2 * x < 384 + y) {
     38            textureStore(output, vec2(x, y), vec4f(1, 0, 0, 1));
     39          } else {
     40            textureStore(output, vec2(x, y), vec4f(0, 0, 0, 1));
     41          }
     42          return vec4(0.0, 0.0, 0.0, 0.0);
     43        }`;
     44 
     45        const canvas = document.querySelector('canvas');
     46        const adapter = await navigator.gpu?.requestAdapter({ });
     47 
     48        const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
     49        if (presentationFormat != 'rgba8unorm' && presentationFormat != 'bgra8unorm') {
     50          throw new Error('Unsupported presentation format: ' + presentationFormat);
     51        }
     52        const deviceDescriptor = {};
     53        if (presentationFormat == 'bgra8unorm') {
     54          if (!adapter.features.has('bgra8unorm-storage')) {
     55            console.warn('Using rgba8unorm because bgra8unorm-storage feature is not available');
     56            presentationFormat = 'rgba8unorm';
     57          } else {
     58            deviceDescriptor.requiredFeatures = ['bgra8unorm-storage'];
     59          }
     60        }
     61 
     62        const device = await adapter?.requestDevice(deviceDescriptor);
     63        const context = canvas.getContext('webgpu');
     64        const devicePixelRatio = window.devicePixelRatio;
     65        canvas.width = canvas.clientWidth * devicePixelRatio;
     66        canvas.height = canvas.clientHeight * devicePixelRatio;
     67        context.configure({
     68          device,
     69          format: presentationFormat,
     70          usage: GPUTextureUsage.STORAGE_BINDING,
     71        });
     72        const canvasView = context.getCurrentTexture().createView();
     73 
     74        const dummyTexture = device.createTexture({
     75          size: { width: 512, height: 512, depth: 1 },
     76          format: presentationFormat,
     77          usage: GPUTextureUsage.RENDER_ATTACHMENT,
     78        });
     79        const dummyView = dummyTexture.createView();
     80 
     81        const bindGroupLayout = device.createBindGroupLayout({
     82          entries: [
     83            {
     84              binding: 0,
     85              visibility: GPUShaderStage.FRAGMENT,
     86              storageTexture: {
     87                access: 'write-only',
     88                format: presentationFormat,
     89                viewDimension: '2d',
     90              },
     91            },
     92          ],
     93        });
     94        const bindGroup = device.createBindGroup({
     95          layout: bindGroupLayout,
     96          entries: [
     97            {
     98              binding: 0,
     99              resource: canvasView,
    100            },
    101          ],
    102        });
    103 
    104        const pipelineLayout = device.createPipelineLayout({
    105          bindGroupLayouts: [bindGroupLayout],
    106        });
    107        const pipeline = device.createRenderPipeline({
    108          layout: pipelineLayout,
    109          vertex: {
    110            module: device.createShaderModule({
    111              code: triangleVertWGSL,
    112            }),
    113          },
    114          fragment: {
    115            module: device.createShaderModule({
    116              code: redFragWGSL.replace('{PRESENTATION_FORMAT}', presentationFormat),
    117            }),
    118            targets: [
    119              {
    120                format: presentationFormat,
    121              },
    122            ],
    123          },
    124          primitive: {
    125            topology: 'triangle-list',
    126          },
    127        });
    128 
    129        const renderPassDescriptor = {
    130          colorAttachments: [
    131            {
    132              view: dummyView,
    133              clearValue: [0, 0, 0, 1],
    134              loadOp: 'clear',
    135              storeOp: 'store',
    136            },
    137          ],
    138        };
    139        const bundleEncoder = device.createRenderBundleEncoder({
    140          colorFormats: [presentationFormat],
    141        });
    142        bundleEncoder.setBindGroup(0, bindGroup);
    143        bundleEncoder.setPipeline(pipeline);
    144        bundleEncoder.draw(3);
    145        const bundle = bundleEncoder.finish({});
    146        const commandEncoder = device.createCommandEncoder();
    147        const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
    148        passEncoder.executeBundles([bundle]);
    149        passEncoder.end();
    150        device.queue.submit([commandEncoder.finish()]);
    151        await device.queue.onSubmittedWorkDone();
    152        requestAnimationFrame(() => {
    153          requestAnimationFrame(() => document.documentElement.className = '');
    154        });
    155      } catch (error) {
    156        console.error(error);
    157        document.getElementById('canvas').style.display = 'none';
    158        document.body.append(error.toString());
    159        document.documentElement.className = '';
    160      }
    161    })();
    162  </script>
    163 </html>