non_halting.spec.ts (5830B)
1 export const description = ` 2 Stress tests covering robustness in the presence of non-halting shaders. 3 `; 4 5 import { makeTestGroup } from '../../common/framework/test_group.js'; 6 import { GPUTest } from '../../webgpu/gpu_test.js'; 7 8 export const g = makeTestGroup(GPUTest); 9 10 g.test('compute') 11 .desc( 12 `Tests execution of compute passes with non-halting dispatch operations. 13 14 This is expected to hang for a bit, but it should ultimately result in graceful 15 device loss.` 16 ) 17 .fn(async t => { 18 const data = new Uint32Array([0]); 19 const buffer = t.makeBufferWithContents(data, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC); 20 const module = t.device.createShaderModule({ 21 code: ` 22 struct Buffer { data: u32, }; 23 @group(0) @binding(0) var<storage, read_write> buffer: Buffer; 24 @compute @workgroup_size(1) fn main() { 25 loop { 26 if (buffer.data == 1u) { 27 break; 28 } 29 buffer.data = buffer.data + 2u; 30 } 31 } 32 `, 33 }); 34 const pipeline = t.device.createComputePipeline({ 35 layout: 'auto', 36 compute: { module, entryPoint: 'main' }, 37 }); 38 const encoder = t.device.createCommandEncoder(); 39 const pass = encoder.beginComputePass(); 40 pass.setPipeline(pipeline); 41 const bindGroup = t.device.createBindGroup({ 42 layout: pipeline.getBindGroupLayout(0), 43 entries: [{ binding: 0, resource: { buffer } }], 44 }); 45 pass.setBindGroup(0, bindGroup); 46 pass.dispatchWorkgroups(1); 47 pass.end(); 48 t.device.queue.submit([encoder.finish()]); 49 await t.device.lost; 50 }); 51 52 g.test('vertex') 53 .desc( 54 `Tests execution of render passes with a non-halting vertex stage. 55 56 This is expected to hang for a bit, but it should ultimately result in graceful 57 device loss.` 58 ) 59 .fn(async t => { 60 const module = t.device.createShaderModule({ 61 code: ` 62 struct Data { counter: u32, increment: u32, }; 63 @group(0) @binding(0) var<uniform> data: Data; 64 @vertex fn vmain() -> @builtin(position) vec4<f32> { 65 var counter: u32 = data.counter; 66 loop { 67 if (counter % 2u == 1u) { 68 break; 69 } 70 counter = counter + data.increment; 71 } 72 return vec4<f32>(1.0, 1.0, 0.0, f32(counter)); 73 } 74 @fragment fn fmain() -> @location(0) vec4<f32> { 75 return vec4<f32>(1.0); 76 } 77 `, 78 }); 79 80 const pipeline = t.device.createRenderPipeline({ 81 layout: 'auto', 82 vertex: { module, entryPoint: 'vmain', buffers: [] }, 83 primitive: { topology: 'point-list' }, 84 fragment: { 85 targets: [{ format: 'rgba8unorm' }], 86 module, 87 entryPoint: 'fmain', 88 }, 89 }); 90 const uniforms = t.makeBufferWithContents(new Uint32Array([0, 2]), GPUBufferUsage.UNIFORM); 91 const bindGroup = t.device.createBindGroup({ 92 layout: pipeline.getBindGroupLayout(0), 93 entries: [ 94 { 95 binding: 0, 96 resource: { buffer: uniforms }, 97 }, 98 ], 99 }); 100 const renderTarget = t.createTextureTracked({ 101 size: [1, 1], 102 usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, 103 format: 'rgba8unorm', 104 }); 105 const encoder = t.device.createCommandEncoder(); 106 const pass = encoder.beginRenderPass({ 107 colorAttachments: [ 108 { 109 view: renderTarget.createView(), 110 clearValue: [0, 0, 0, 0], 111 loadOp: 'clear', 112 storeOp: 'store', 113 }, 114 ], 115 }); 116 pass.setPipeline(pipeline); 117 pass.setBindGroup(0, bindGroup); 118 pass.draw(1); 119 pass.end(); 120 t.device.queue.submit([encoder.finish()]); 121 await t.device.lost; 122 }); 123 124 g.test('fragment') 125 .desc( 126 `Tests execution of render passes with a non-halting fragment stage. 127 128 This is expected to hang for a bit, but it should ultimately result in graceful 129 device loss.` 130 ) 131 .fn(async t => { 132 const module = t.device.createShaderModule({ 133 code: ` 134 struct Data { counter: u32, increment: u32, }; 135 @group(0) @binding(0) var<uniform> data: Data; 136 @vertex fn vmain() -> @builtin(position) vec4<f32> { 137 return vec4<f32>(0.0, 0.0, 0.0, 1.0); 138 } 139 @fragment fn fmain() -> @location(0) vec4<f32> { 140 var counter: u32 = data.counter; 141 loop { 142 if (counter % 2u == 1u) { 143 break; 144 } 145 counter = counter + data.increment; 146 } 147 return vec4<f32>(1.0 / f32(counter), 0.0, 0.0, 1.0); 148 } 149 `, 150 }); 151 152 const pipeline = t.device.createRenderPipeline({ 153 layout: 'auto', 154 vertex: { module, entryPoint: 'vmain', buffers: [] }, 155 primitive: { topology: 'point-list' }, 156 fragment: { 157 targets: [{ format: 'rgba8unorm' }], 158 module, 159 entryPoint: 'fmain', 160 }, 161 }); 162 const uniforms = t.makeBufferWithContents(new Uint32Array([0, 2]), GPUBufferUsage.UNIFORM); 163 const bindGroup = t.device.createBindGroup({ 164 layout: pipeline.getBindGroupLayout(0), 165 entries: [ 166 { 167 binding: 0, 168 resource: { buffer: uniforms }, 169 }, 170 ], 171 }); 172 const renderTarget = t.createTextureTracked({ 173 size: [1, 1], 174 usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, 175 format: 'rgba8unorm', 176 }); 177 const encoder = t.device.createCommandEncoder(); 178 const pass = encoder.beginRenderPass({ 179 colorAttachments: [ 180 { 181 view: renderTarget.createView(), 182 clearValue: [0, 0, 0, 0], 183 loadOp: 'clear', 184 storeOp: 'store', 185 }, 186 ], 187 }); 188 pass.setPipeline(pipeline); 189 pass.setBindGroup(0, bindGroup); 190 pass.draw(1); 191 pass.end(); 192 t.device.queue.submit([encoder.finish()]); 193 await t.device.lost; 194 });