draw-storage.html (5254B)
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 commandEncoder = device.createCommandEncoder(); 140 const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor); 141 passEncoder.setBindGroup(0, bindGroup); 142 passEncoder.setPipeline(pipeline); 143 passEncoder.draw(3); 144 passEncoder.end(); 145 device.queue.submit([commandEncoder.finish()]); 146 await device.queue.onSubmittedWorkDone(); 147 requestAnimationFrame(() => { 148 requestAnimationFrame(() => document.documentElement.className = ''); 149 }); 150 } catch (error) { 151 console.error(error); 152 document.getElementById('canvas').style.display = 'none'; 153 document.body.append(error.toString()); 154 document.documentElement.className = ''; 155 } 156 })(); 157 </script> 158 </html>