diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /dom/webgpu/tests/cts/checkout/src/stress/shaders | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/stress/shaders')
4 files changed, 468 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/stress/shaders/README.txt b/dom/webgpu/tests/cts/checkout/src/stress/shaders/README.txt new file mode 100644 index 0000000000..628b4e86fa --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/stress/shaders/README.txt @@ -0,0 +1 @@ +Stress tests covering very long-running and/or resource-intensive shaders. diff --git a/dom/webgpu/tests/cts/checkout/src/stress/shaders/entry_points.spec.ts b/dom/webgpu/tests/cts/checkout/src/stress/shaders/entry_points.spec.ts new file mode 100644 index 0000000000..95b647ba73 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/stress/shaders/entry_points.spec.ts @@ -0,0 +1,78 @@ +export const description = ` +Stress tests covering behavior around shader entry points. +`; + +import { makeTestGroup } from '../../common/framework/test_group.js'; +import { range } from '../../common/util/util.js'; +import { GPUTest } from '../../webgpu/gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +const makeCode = (numEntryPoints: number) => { + const kBaseCode = ` + struct Buffer { data: u32, }; + @group(0) @binding(0) var<storage, read_write> buffer: Buffer; + fn main() { buffer.data = buffer.data + 1u; } + `; + const makeEntryPoint = (i: number) => ` + @compute @workgroup_size(1) fn computeMain${i}() { main(); } + `; + return kBaseCode + range(numEntryPoints, makeEntryPoint).join(''); +}; + +g.test('many') + .desc( + `Tests compilation and usage of shaders with a huge number of entry points. + +TODO: There may be a normative limit to the number of entry points allowed in +a shader, in which case this would become a validation test instead.` + ) + .fn(async t => { + const data = new Uint32Array([0]); + const buffer = t.makeBufferWithContents(data, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC); + + // NOTE: Initial shader compilation time seems to scale exponentially with + // this value in Chrome. + const kNumEntryPoints = 200; + + const shader = t.device.createShaderModule({ + code: makeCode(kNumEntryPoints), + }); + + const layout = t.device.createBindGroupLayout({ + entries: [ + { + binding: 0, + visibility: GPUShaderStage.COMPUTE, + buffer: { type: 'storage' }, + }, + ], + }); + const pipelineLayout = t.device.createPipelineLayout({ + bindGroupLayouts: [layout], + }); + const bindGroup = t.device.createBindGroup({ + layout, + entries: [{ binding: 0, resource: { buffer } }], + }); + + const encoder = t.device.createCommandEncoder(); + range(kNumEntryPoints, i => { + const pipeline = t.device.createComputePipeline({ + layout: pipelineLayout, + compute: { + module: shader, + entryPoint: `computeMain${i}`, + }, + }); + + const pass = encoder.beginComputePass(); + pass.setPipeline(pipeline); + pass.setBindGroup(0, bindGroup); + pass.dispatchWorkgroups(1); + pass.end(); + }); + + t.device.queue.submit([encoder.finish()]); + t.expectGPUBufferValuesEqual(buffer, new Uint32Array([kNumEntryPoints])); + }); diff --git a/dom/webgpu/tests/cts/checkout/src/stress/shaders/non_halting.spec.ts b/dom/webgpu/tests/cts/checkout/src/stress/shaders/non_halting.spec.ts new file mode 100644 index 0000000000..b88aa083b3 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/stress/shaders/non_halting.spec.ts @@ -0,0 +1,194 @@ +export const description = ` +Stress tests covering robustness in the presence of non-halting shaders. +`; + +import { makeTestGroup } from '../../common/framework/test_group.js'; +import { GPUTest } from '../../webgpu/gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('compute') + .desc( + `Tests execution of compute passes with non-halting dispatch operations. + +This is expected to hang for a bit, but it should ultimately result in graceful +device loss.` + ) + .fn(async t => { + const data = new Uint32Array([0]); + const buffer = t.makeBufferWithContents(data, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC); + const module = t.device.createShaderModule({ + code: ` + struct Buffer { data: u32, }; + @group(0) @binding(0) var<storage, read_write> buffer: Buffer; + @compute @workgroup_size(1) fn main() { + loop { + if (buffer.data == 1u) { + break; + } + buffer.data = buffer.data + 2u; + } + } + `, + }); + const pipeline = t.device.createComputePipeline({ + layout: 'auto', + compute: { module, entryPoint: 'main' }, + }); + const encoder = t.device.createCommandEncoder(); + const pass = encoder.beginComputePass(); + pass.setPipeline(pipeline); + const bindGroup = t.device.createBindGroup({ + layout: pipeline.getBindGroupLayout(0), + entries: [{ binding: 0, resource: { buffer } }], + }); + pass.setBindGroup(0, bindGroup); + pass.dispatchWorkgroups(1); + pass.end(); + t.device.queue.submit([encoder.finish()]); + await t.device.lost; + }); + +g.test('vertex') + .desc( + `Tests execution of render passes with a non-halting vertex stage. + +This is expected to hang for a bit, but it should ultimately result in graceful +device loss.` + ) + .fn(async t => { + const module = t.device.createShaderModule({ + code: ` + struct Data { counter: u32, increment: u32, }; + @group(0) @binding(0) var<uniform> data: Data; + @vertex fn vmain() -> @builtin(position) vec4<f32> { + var counter: u32 = data.counter; + loop { + if (counter % 2u == 1u) { + break; + } + counter = counter + data.increment; + } + return vec4<f32>(1.0, 1.0, 0.0, f32(counter)); + } + @fragment fn fmain() -> @location(0) vec4<f32> { + return vec4<f32>(1.0); + } + `, + }); + + const pipeline = t.device.createRenderPipeline({ + layout: 'auto', + vertex: { module, entryPoint: 'vmain', buffers: [] }, + primitive: { topology: 'point-list' }, + fragment: { + targets: [{ format: 'rgba8unorm' }], + module, + entryPoint: 'fmain', + }, + }); + const uniforms = t.makeBufferWithContents(new Uint32Array([0, 2]), GPUBufferUsage.UNIFORM); + const bindGroup = t.device.createBindGroup({ + layout: pipeline.getBindGroupLayout(0), + entries: [ + { + binding: 0, + resource: { buffer: uniforms }, + }, + ], + }); + const renderTarget = t.device.createTexture({ + size: [1, 1], + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, + format: 'rgba8unorm', + }); + const encoder = t.device.createCommandEncoder(); + const pass = encoder.beginRenderPass({ + colorAttachments: [ + { + view: renderTarget.createView(), + clearValue: [0, 0, 0, 0], + loadOp: 'clear', + storeOp: 'store', + }, + ], + }); + pass.setPipeline(pipeline); + pass.setBindGroup(0, bindGroup); + pass.draw(1); + pass.end(); + t.device.queue.submit([encoder.finish()]); + await t.device.lost; + }); + +g.test('fragment') + .desc( + `Tests execution of render passes with a non-halting fragment stage. + +This is expected to hang for a bit, but it should ultimately result in graceful +device loss.` + ) + .fn(async t => { + const module = t.device.createShaderModule({ + code: ` + struct Data { counter: u32, increment: u32, }; + @group(0) @binding(0) var<uniform> data: Data; + @vertex fn vmain() -> @builtin(position) vec4<f32> { + return vec4<f32>(0.0, 0.0, 0.0, 1.0); + } + @fragment fn fmain() -> @location(0) vec4<f32> { + var counter: u32 = data.counter; + loop { + if (counter % 2u == 1u) { + break; + } + counter = counter + data.increment; + } + return vec4<f32>(1.0 / f32(counter), 0.0, 0.0, 1.0); + } + `, + }); + + const pipeline = t.device.createRenderPipeline({ + layout: 'auto', + vertex: { module, entryPoint: 'vmain', buffers: [] }, + primitive: { topology: 'point-list' }, + fragment: { + targets: [{ format: 'rgba8unorm' }], + module, + entryPoint: 'fmain', + }, + }); + const uniforms = t.makeBufferWithContents(new Uint32Array([0, 2]), GPUBufferUsage.UNIFORM); + const bindGroup = t.device.createBindGroup({ + layout: pipeline.getBindGroupLayout(0), + entries: [ + { + binding: 0, + resource: { buffer: uniforms }, + }, + ], + }); + const renderTarget = t.device.createTexture({ + size: [1, 1], + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, + format: 'rgba8unorm', + }); + const encoder = t.device.createCommandEncoder(); + const pass = encoder.beginRenderPass({ + colorAttachments: [ + { + view: renderTarget.createView(), + clearValue: [0, 0, 0, 0], + loadOp: 'clear', + storeOp: 'store', + }, + ], + }); + pass.setPipeline(pipeline); + pass.setBindGroup(0, bindGroup); + pass.draw(1); + pass.end(); + t.device.queue.submit([encoder.finish()]); + await t.device.lost; + }); diff --git a/dom/webgpu/tests/cts/checkout/src/stress/shaders/slow.spec.ts b/dom/webgpu/tests/cts/checkout/src/stress/shaders/slow.spec.ts new file mode 100644 index 0000000000..9359b976a3 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/stress/shaders/slow.spec.ts @@ -0,0 +1,195 @@ +export const description = ` +Stress tests covering robustness in the presence of slow shaders. +`; + +import { makeTestGroup } from '../../common/framework/test_group.js'; +import { GPUTest } from '../../webgpu/gpu_test.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('compute') + .desc(`Tests execution of compute passes with very long-running dispatch operations.`) + .fn(async t => { + const kDispatchSize = 1000; + const data = new Uint32Array(kDispatchSize); + const buffer = t.makeBufferWithContents(data, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC); + const module = t.device.createShaderModule({ + code: ` + struct Buffer { data: array<u32>, }; + @group(0) @binding(0) var<storage, read_write> buffer: Buffer; + @compute @workgroup_size(1) fn main( + @builtin(global_invocation_id) id: vec3<u32>) { + loop { + if (buffer.data[id.x] == 1000000u) { + break; + } + buffer.data[id.x] = buffer.data[id.x] + 1u; + } + } + `, + }); + const pipeline = t.device.createComputePipeline({ + layout: 'auto', + compute: { module, entryPoint: 'main' }, + }); + const encoder = t.device.createCommandEncoder(); + const pass = encoder.beginComputePass(); + pass.setPipeline(pipeline); + const bindGroup = t.device.createBindGroup({ + layout: pipeline.getBindGroupLayout(0), + entries: [{ binding: 0, resource: { buffer } }], + }); + pass.setBindGroup(0, bindGroup); + pass.dispatchWorkgroups(kDispatchSize); + pass.end(); + t.device.queue.submit([encoder.finish()]); + t.expectGPUBufferValuesEqual(buffer, new Uint32Array(new Array(kDispatchSize).fill(1000000))); + }); + +g.test('vertex') + .desc(`Tests execution of render passes with a very long-running vertex stage.`) + .fn(async t => { + const module = t.device.createShaderModule({ + code: ` + struct Data { counter: u32, increment: u32, }; + @group(0) @binding(0) var<uniform> data: Data; + @vertex fn vmain() -> @builtin(position) vec4<f32> { + var counter: u32 = data.counter; + loop { + counter = counter + data.increment; + if (counter % 50000000u == 0u) { + break; + } + } + return vec4<f32>(1.0, 1.0, 0.0, f32(counter)); + } + @fragment fn fmain() -> @location(0) vec4<f32> { + return vec4<f32>(1.0, 1.0, 0.0, 1.0); + } + `, + }); + + const pipeline = t.device.createRenderPipeline({ + layout: 'auto', + vertex: { module, entryPoint: 'vmain', buffers: [] }, + primitive: { topology: 'point-list' }, + fragment: { + targets: [{ format: 'rgba8unorm' }], + module, + entryPoint: 'fmain', + }, + }); + const uniforms = t.makeBufferWithContents(new Uint32Array([0, 1]), GPUBufferUsage.UNIFORM); + const bindGroup = t.device.createBindGroup({ + layout: pipeline.getBindGroupLayout(0), + entries: [ + { + binding: 0, + resource: { buffer: uniforms }, + }, + ], + }); + const renderTarget = t.device.createTexture({ + size: [3, 3], + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, + format: 'rgba8unorm', + }); + const encoder = t.device.createCommandEncoder(); + const pass = encoder.beginRenderPass({ + colorAttachments: [ + { + view: renderTarget.createView(), + clearValue: [0, 0, 0, 0], + loadOp: 'clear', + storeOp: 'store', + }, + ], + }); + pass.setPipeline(pipeline); + pass.setBindGroup(0, bindGroup); + pass.draw(1); + pass.end(); + t.device.queue.submit([encoder.finish()]); + t.expectSinglePixelIn2DTexture( + renderTarget, + 'rgba8unorm', + { x: 1, y: 1 }, + { + exp: new Uint8Array([255, 255, 0, 255]), + } + ); + }); + +g.test('fragment') + .desc(`Tests execution of render passes with a very long-running fragment stage.`) + .fn(async t => { + const module = t.device.createShaderModule({ + code: ` + struct Data { counter: u32, increment: u32, }; + @group(0) @binding(0) var<uniform> data: Data; + @vertex fn vmain() -> @builtin(position) vec4<f32> { + return vec4<f32>(0.0, 0.0, 0.0, 1.0); + } + @fragment fn fmain() -> @location(0) vec4<f32> { + var counter: u32 = data.counter; + loop { + counter = counter + data.increment; + if (counter % 50000000u == 0u) { + break; + } + } + return vec4<f32>(1.0, 1.0, 1.0 / f32(counter), 1.0); + } + `, + }); + + const pipeline = t.device.createRenderPipeline({ + layout: 'auto', + vertex: { module, entryPoint: 'vmain', buffers: [] }, + primitive: { topology: 'point-list' }, + fragment: { + targets: [{ format: 'rgba8unorm' }], + module, + entryPoint: 'fmain', + }, + }); + const uniforms = t.makeBufferWithContents(new Uint32Array([0, 1]), GPUBufferUsage.UNIFORM); + const bindGroup = t.device.createBindGroup({ + layout: pipeline.getBindGroupLayout(0), + entries: [ + { + binding: 0, + resource: { buffer: uniforms }, + }, + ], + }); + const renderTarget = t.device.createTexture({ + size: [3, 3], + usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC, + format: 'rgba8unorm', + }); + const encoder = t.device.createCommandEncoder(); + const pass = encoder.beginRenderPass({ + colorAttachments: [ + { + view: renderTarget.createView(), + clearValue: [0, 0, 0, 0], + loadOp: 'clear', + storeOp: 'store', + }, + ], + }); + pass.setPipeline(pipeline); + pass.setBindGroup(0, bindGroup); + pass.draw(1); + pass.end(); + t.device.queue.submit([encoder.finish()]); + t.expectSinglePixelIn2DTexture( + renderTarget, + 'rgba8unorm', + { x: 1, y: 1 }, + { + exp: new Uint8Array([255, 255, 0, 255]), + } + ); + }); |