Compare commits

...

1 commit

Author SHA1 Message Date
Brandon Jones
84529407ca Handling async pipeline errors more gracefully 2023-12-12 10:51:25 -08:00
3 changed files with 144 additions and 110 deletions

View file

@ -220,12 +220,12 @@ type PipelineCache = Map<String, GPUComputePipeline>;
* @param create the function used to construct a value, if not found in the cache
* @returns the value, either fetched from the cache, or newly built.
*/
function getOrCreate<K, V>(map: Map<K, V>, key: K, create: () => V) {
async function getOrCreate<K, V>(map: Map<K, V>, key: K, create: () => Promise<V>) {
const existing = map.get(key);
if (existing !== undefined) {
return existing;
}
const value = create();
const value = await create();
map.set(key, value);
return value;
}
@ -307,16 +307,24 @@ export async function run(
};
const processBatch = async (batchCases: CaseList) => {
const checkBatch = await submitBatch(
t,
shaderBuilder,
parameterTypes,
resultType,
batchCases,
cfg.inputSource,
pipelineCache
);
checkBatch();
try {
const checkBatch = await submitBatch(
t,
shaderBuilder,
parameterTypes,
resultType,
batchCases,
cfg.inputSource,
pipelineCache
);
checkBatch();
} catch (err) {
if (err instanceof GPUPipelineError) {
t.fail(`Pipeline Creation Error, ${err.reason}: ${err.message}`);
} else {
throw err;
}
}
void t.queue.onSubmittedWorkDone().finally(batchFinishedCallback);
};
@ -993,6 +1001,7 @@ async function buildPipeline(
const module = t.device.createShaderModule({ code: source });
// build the pipeline
const pipeline = await t.device.createComputePipelineAsync({
layout: 'auto',
compute: { module, entryPoint: 'main' },
@ -1037,12 +1046,12 @@ async function buildPipeline(
}
// build the compute pipeline, if the shader hasn't been compiled already.
const pipeline = getOrCreate(pipelineCache, source, () => {
const pipeline = await getOrCreate(pipelineCache, source, () => {
// build the shader module
const module = t.device.createShaderModule({ code: source });
// build the pipeline
return t.device.createComputePipeline({
return t.device.createComputePipelineAsync({
layout: 'auto',
compute: { module, entryPoint: 'main' },
});

View file

@ -62,35 +62,43 @@ fn main() {
t.debug(source);
const module = t.device.createShaderModule({ code: source });
const pipeline = await t.device.createComputePipelineAsync({
layout,
compute: { module, entryPoint: 'main' },
});
const group = t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(1),
entries: [
{ binding: 0, resource: { buffer: constantsBuffer } },
{ binding: 1, resource: { buffer: resultBuffer } },
],
});
try {
const pipeline = await t.device.createComputePipelineAsync({
layout,
compute: { module, entryPoint: 'main' },
});
const testGroup = t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: testBindings,
});
const group = t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(1),
entries: [
{ binding: 0, resource: { buffer: constantsBuffer } },
{ binding: 1, resource: { buffer: resultBuffer } },
],
});
const encoder = t.device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, testGroup, dynamicOffsets);
pass.setBindGroup(1, group);
pass.dispatchWorkgroups(1);
pass.end();
const testGroup = t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: testBindings,
});
t.queue.submit([encoder.finish()]);
const encoder = t.device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, testGroup, dynamicOffsets);
pass.setBindGroup(1, group);
pass.dispatchWorkgroups(1);
pass.end();
t.expectGPUBufferValuesEqual(resultBuffer, new Uint32Array([0]));
t.queue.submit([encoder.finish()]);
t.expectGPUBufferValuesEqual(resultBuffer, new Uint32Array([0]));
} catch (err) {
if (err instanceof GPUPipelineError) {
t.fail(`Pipeline Creation Error, ${err.reason}: ${err.message}`);
} else {
throw err;
}
}
}
/** Fill an ArrayBuffer with sentinel values, except clear a region to zero. */

View file

@ -446,101 +446,118 @@ g.test('compute,zero_init')
],
});
const fillPipeline = await t.device.createComputePipelineAsync({
layout: t.device.createPipelineLayout({ bindGroupLayouts: [fillLayout] }),
label: 'Workgroup Fill Pipeline',
try {
const fillPipeline = await t.device.createComputePipelineAsync({
layout: t.device.createPipelineLayout({ bindGroupLayouts: [fillLayout] }),
label: 'Workgroup Fill Pipeline',
compute: {
module: t.device.createShaderModule({
code: wgsl,
}),
entryPoint: 'fill',
},
});
const inputBuffer = t.makeBufferWithContents(
new Uint32Array([...iterRange(wg_memory_limits / 4, _i => 0xdeadbeef)]),
GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
);
t.trackForCleanup(inputBuffer);
const outputBuffer = t.device.createBuffer({
size: wg_memory_limits,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});
t.trackForCleanup(outputBuffer);
const bg = t.device.createBindGroup({
layout: fillPipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: {
buffer: inputBuffer,
},
},
{
binding: 1,
resource: {
buffer: outputBuffer,
},
},
],
});
const e = t.device.createCommandEncoder();
const p = e.beginComputePass();
p.setPipeline(fillPipeline);
p.setBindGroup(0, bg);
p.dispatchWorkgroups(1);
p.end();
t.queue.submit([e.finish()]);
} catch (err) {
if (err instanceof GPUPipelineError) {
t.fail(`Pipeline Creation Error, ${err.reason}: ${err.message}`);
return;
} else {
throw err;
}
}
}
try {
const pipeline = await t.device.createComputePipelineAsync({
layout: 'auto',
compute: {
module: t.device.createShaderModule({
code: wgsl,
}),
entryPoint: 'fill',
entryPoint: 'main',
},
});
const inputBuffer = t.makeBufferWithContents(
new Uint32Array([...iterRange(wg_memory_limits / 4, _i => 0xdeadbeef)]),
GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
);
t.trackForCleanup(inputBuffer);
const outputBuffer = t.device.createBuffer({
size: wg_memory_limits,
const resultBuffer = t.device.createBuffer({
size: 4,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});
t.trackForCleanup(outputBuffer);
t.trackForCleanup(resultBuffer);
const bg = t.device.createBindGroup({
layout: fillPipeline.getBindGroupLayout(0),
const zeroBuffer = t.device.createBuffer({
size: 4,
usage: GPUBufferUsage.UNIFORM,
});
t.trackForCleanup(zeroBuffer);
const bindGroup = t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: {
buffer: inputBuffer,
buffer: resultBuffer,
},
},
{
binding: 1,
resource: {
buffer: outputBuffer,
buffer: zeroBuffer,
},
},
],
});
const e = t.device.createCommandEncoder();
const p = e.beginComputePass();
p.setPipeline(fillPipeline);
p.setBindGroup(0, bg);
p.dispatchWorkgroups(1);
p.end();
t.queue.submit([e.finish()]);
const encoder = t.device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(1);
pass.end();
t.queue.submit([encoder.finish()]);
t.expectGPUBufferValuesEqual(resultBuffer, new Uint32Array([0]));
} catch (err) {
if (err instanceof GPUPipelineError) {
t.fail(`Pipeline Creation Error, ${err.reason}: ${err.message}`);
} else {
throw err;
}
}
const pipeline = await t.device.createComputePipelineAsync({
layout: 'auto',
compute: {
module: t.device.createShaderModule({
code: wgsl,
}),
entryPoint: 'main',
},
});
const resultBuffer = t.device.createBuffer({
size: 4,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});
t.trackForCleanup(resultBuffer);
const zeroBuffer = t.device.createBuffer({
size: 4,
usage: GPUBufferUsage.UNIFORM,
});
t.trackForCleanup(zeroBuffer);
const bindGroup = t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: {
buffer: resultBuffer,
},
},
{
binding: 1,
resource: {
buffer: zeroBuffer,
},
},
],
});
const encoder = t.device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(1);
pass.end();
t.queue.submit([encoder.finish()]);
t.expectGPUBufferValuesEqual(resultBuffer, new Uint32Array([0]));
});