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>