Skip to content

Commit

Permalink
Address reviewer's comments
Browse files Browse the repository at this point in the history
  • Loading branch information
Jiawei-Shao committed Dec 20, 2024
1 parent eef4f21 commit 8bbc5e8
Showing 1 changed file with 83 additions and 188 deletions.
271 changes: 83 additions & 188 deletions src/webgpu/api/validation/createPipelineLayout.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,9 +260,9 @@ g.test('bind_group_layouts,null_bind_group_layouts')
g.test('bind_group_layouts,create_pipeline_with_null_bind_group_layouts')
.desc(
`
Tests that it is valid to create a render pipeline or compute pipeline with a pipeline layout
created with null bind group layouts as long as the pipeline layout matches the declarations in
the shaders.
Tests that it is valid to create and use a render pipeline or compute pipeline with a pipeline
layout created with null bind group layouts as long as the pipeline layout matches the
declarations in the shaders.
`
)
.paramsSubcasesOnly(u =>
Expand All @@ -271,15 +271,37 @@ g.test('bind_group_layouts,create_pipeline_with_null_bind_group_layouts')
.combine('emptyBindGroupLayoutType', ['Null', 'Undefined'] as const)
.combine('emptyBindGroupLayoutIndex', [0, 1, 2, 3] as const)
.combine('emptyBindGroupLayoutIndexMissedInShader', [true, false])
.combine('setBindGroupOnEmptyBindGroupLayoutIndex', [true, false])
.filter(p => {
// p.emptyBindGroupLayoutIndexMissedInShader === true will cause the failure of the pipeline
// creation so we don't need to test the behavior about the use of the pipeline.
return (
p.emptyBindGroupLayoutIndexMissedInShader || p.setBindGroupOnEmptyBindGroupLayoutIndex
);
})
)
.fn(t => {
const {
pipelineType,
emptyBindGroupLayoutType,
emptyBindGroupLayoutIndex,
emptyBindGroupLayoutIndexMissedInShader,
setBindGroupOnEmptyBindGroupLayoutIndex,
} = t.params;

const nonEmptyBindGroupLayout = t.device.createBindGroupLayout({
entries: [
{
binding: 0,
visibility: GPUConst.ShaderStage.COMPUTE | GPUConst.ShaderStage.FRAGMENT,
buffer: {
type: 'uniform',
},
},
],
});

const bindGroups: GPUBindGroup[] = [];
const bindGroupLayouts: (GPUBindGroupLayout | null | undefined)[] = [];
for (let i = 0; i < 4; ++i) {
if (i === emptyBindGroupLayoutIndex) {
Expand All @@ -292,19 +314,25 @@ g.test('bind_group_layouts,create_pipeline_with_null_bind_group_layouts')
break;
}
} else {
const nonEmptyBindGroupLayout = t.device.createBindGroupLayout({
entries: [
{
binding: 0,
visibility: GPUConst.ShaderStage.COMPUTE | GPUConst.ShaderStage.FRAGMENT,
buffer: {
type: 'uniform',
},
},
],
});
bindGroupLayouts.push(nonEmptyBindGroupLayout);
}

const buffer = t.createBufferTracked({
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.UNIFORM,
size: 4,
});
const bindGroup = t.device.createBindGroup({
layout: nonEmptyBindGroupLayout,
entries: [
{
binding: 0,
resource: {
buffer,
},
},
],
});
bindGroups.push(bindGroup);
}
const layout = t.device.createPipelineLayout({ bindGroupLayouts });

Expand Down Expand Up @@ -339,8 +367,9 @@ g.test('bind_group_layouts,create_pipeline_with_null_bind_group_layouts')
code,
});

const format = 'rgba8unorm';
t.expectValidationError(() => {
t.device.createRenderPipeline({
const renderPipeline = t.device.createRenderPipeline({
layout,
vertex: {
module: shaderModule,
Expand All @@ -349,11 +378,38 @@ g.test('bind_group_layouts,create_pipeline_with_null_bind_group_layouts')
module: shaderModule,
targets: [
{
format: 'rgba8unorm',
format,
},
],
},
});

const renderTarget = t.createTextureTracked({
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
size: [4, 4, 1],
format,
});
const commandEncoder = t.device.createCommandEncoder();
const renderPassEncoder = commandEncoder.beginRenderPass({
colorAttachments: [
{
view: renderTarget.createView(),
loadOp: 'load',
storeOp: 'store',
},
],
});
for (let i = 0; i < 4; ++i) {
if (!setBindGroupOnEmptyBindGroupLayoutIndex && i === emptyBindGroupLayoutIndex) {
continue;
}
renderPassEncoder.setBindGroup(i, bindGroups[i]);
}
renderPassEncoder.setPipeline(renderPipeline);
renderPassEncoder.draw(1);
renderPassEncoder.end();

t.queue.submit([commandEncoder.finish()]);
}, shouldError);
break;
}
Expand All @@ -369,186 +425,25 @@ g.test('bind_group_layouts,create_pipeline_with_null_bind_group_layouts')
code,
});
t.expectValidationError(() => {
t.device.createComputePipeline({
const computePipeline = t.device.createComputePipeline({
layout,
compute: {
module: shaderModule,
},
});
}, shouldError);
break;
}
}
});

g.test('bind_group_layouts,set_pipeline_with_null_bind_group_layouts')
.desc(
`
Tests that it is valid to use a render pipeline or compute pipeline with null bind group layouts
used in the pipeline layout in a render pass encoder or compute pass encoder.
`
)
.paramsSubcasesOnly(u =>
u
.combine('pipelineType', ['Render', 'Compute'] as const)
.combine('emptyBindGroupLayoutType', ['Null', 'Undefined'] as const)
.combine('emptyBindGroupLayoutIndex', [0, 1, 2, 3] as const)
.combine('setBindGroupOnEmptyBindGroupLayoutIndex', [true, false])
)
.fn(t => {
const {
pipelineType,
emptyBindGroupLayoutType,
emptyBindGroupLayoutIndex,
setBindGroupOnEmptyBindGroupLayoutIndex,
} = t.params;

const nonEmptyBindGroupLayout = t.device.createBindGroupLayout({
entries: [
{
binding: 0,
visibility: GPUConst.ShaderStage.COMPUTE | GPUConst.ShaderStage.FRAGMENT,
buffer: {
type: 'uniform',
},
},
],
});
const bindGroups: GPUBindGroup[] = [];
const bindGroupLayouts: (GPUBindGroupLayout | null | undefined)[] = [];
let declarations = '';
let statement = '_ = 1';
for (let i = 0; i < 4; ++i) {
if (i === emptyBindGroupLayoutIndex) {
switch (emptyBindGroupLayoutType) {
case 'Null':
bindGroupLayouts.push(null);
break;
case 'Undefined':
bindGroupLayouts.push(undefined);
break;
}
} else {
bindGroupLayouts.push(nonEmptyBindGroupLayout);
declarations += `@group(${i}) @binding(0) var<uniform> input${i} : u32;\n`;
statement += ` + input${i}`;
}

const buffer = t.createBufferTracked({
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.UNIFORM,
size: 4,
});
const bindGroup = t.device.createBindGroup({
layout: nonEmptyBindGroupLayout,
entries: [
{
binding: 0,
resource: {
buffer,
},
},
],
});
bindGroups.push(bindGroup);
}
const layout = t.device.createPipelineLayout({ bindGroupLayouts });

const shouldError = false;
switch (pipelineType) {
case 'Render': {
const code = `
${declarations}
@vertex
fn vert_main() -> @builtin(position) vec4f {
return vec4f(0.0, 0.0, 0.0, 1.0);
}
@fragment
fn frag_main() -> @location(0) vec4f {
${statement};
return vec4f(0.0, 0.0, 0.0, 1.0);
}
`;
const shaderModule = t.device.createShaderModule({
code,
});

const format = 'rgba8unorm';
const renderPipeline = t.device.createRenderPipeline({
layout,
vertex: {
module: shaderModule,
},
fragment: {
module: shaderModule,
targets: [
{
format,
},
],
},
});

const renderTarget = t.createTextureTracked({
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
size: [4, 4, 1],
format,
});
const commandEncoder = t.device.createCommandEncoder();
const renderPassEncoder = commandEncoder.beginRenderPass({
colorAttachments: [
{
view: renderTarget.createView(),
loadOp: 'load',
storeOp: 'store',
},
],
});
for (let i = 0; i < 4; ++i) {
if (!setBindGroupOnEmptyBindGroupLayoutIndex && i === emptyBindGroupLayoutIndex) {
continue;
const commandEncoder = t.device.createCommandEncoder();
const computePassEncoder = commandEncoder.beginComputePass();
for (let i = 0; i < 4; ++i) {
if (!setBindGroupOnEmptyBindGroupLayoutIndex && i === emptyBindGroupLayoutIndex) {
continue;
}
computePassEncoder.setBindGroup(i, bindGroups[i]);
}
renderPassEncoder.setBindGroup(i, bindGroups[i]);
}
renderPassEncoder.setPipeline(renderPipeline);
renderPassEncoder.draw(1);
renderPassEncoder.end();
computePassEncoder.setPipeline(computePipeline);
computePassEncoder.dispatchWorkgroups(1);
computePassEncoder.end();

t.expectValidationError(() => {
t.queue.submit([commandEncoder.finish()]);
}, shouldError);
break;
}

case 'Compute': {
const code = `
${declarations}
@compute @workgroup_size(1) fn cs_main() {
${statement};
}
`;
const shaderModule = t.device.createShaderModule({
code,
});

const computePipeline = t.device.createComputePipeline({
layout,
compute: {
module: shaderModule,
},
});
const commandEncoder = t.device.createCommandEncoder();
const computePassEncoder = commandEncoder.beginComputePass();
for (let i = 0; i < 4; ++i) {
if (!setBindGroupOnEmptyBindGroupLayoutIndex && i === emptyBindGroupLayoutIndex) {
continue;
}
computePassEncoder.setBindGroup(i, bindGroups[i]);
}
computePassEncoder.setPipeline(computePipeline);
computePassEncoder.dispatchWorkgroups(1);
computePassEncoder.end();

t.expectValidationError(() => {
t.queue.submit([commandEncoder.finish()]);
}, shouldError);
break;
Expand Down

0 comments on commit 8bbc5e8

Please sign in to comment.