Skip to content

Commit

Permalink
Merge branch 'main' into impl-operation-read-only
Browse files Browse the repository at this point in the history
  • Loading branch information
Jiawei-Shao authored Feb 10, 2024
2 parents 9c18f8c + 726c24a commit 414c4cd
Show file tree
Hide file tree
Showing 3 changed files with 288 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ function getPipelineDescriptor(
${attribUsage}
return vec4f(0);
}
@fragment fn fs() -> @location(0) vec4f {
return vec4f(0);
}
`;

const module = device.createShaderModule({ code });
Expand Down Expand Up @@ -146,9 +150,14 @@ g.test('createRenderPipeline,at_over')
const maxUsableBindGroupsPlusVertexBuffers =
device.limits.maxBindGroups + device.limits.maxVertexBuffers;
t.skipIf(
actualLimit > maxUsableBindGroupsPlusVertexBuffers,
maxUsableBindGroupsPlusVertexBuffers < actualLimit,
`can not test because the max usable bindGroups + vertexBuffers (${maxUsableBindGroupsPlusVertexBuffers}) is < maxBindGroupsAndVertexBuffers (${actualLimit})`
);
t.skipIf(
maxUsableBindGroupsPlusVertexBuffers === actualLimit && testValue > actualLimit,
`can not test because the max usable bindGroups + vertexBuffers (${maxUsableBindGroupsPlusVertexBuffers}) === maxBindGroupsAndVertexBuffers (${actualLimit})
but the testValue (${testValue}) > maxBindGroupsAndVertexBuffers (${actualLimit})`
);

const { code, descriptor } = getPipelineDescriptor(
device,
Expand Down Expand Up @@ -190,8 +199,13 @@ g.test('draw,at_over')
const maxUsableBindGroupsPlusVertexBuffers =
device.limits.maxBindGroups + maxUsableVertexBuffers;
t.skipIf(
actualLimit > maxUsableBindGroupsPlusVertexBuffers,
`can not test because the max usable bindGroups + vertexBuffers (${maxUsableBindGroupsPlusVertexBuffers}) is < the maxBindGroupsAndVertexBuffers (${actualLimit})`
maxUsableBindGroupsPlusVertexBuffers < actualLimit,
`can not test because the max usable bindGroups + vertexBuffers (${maxUsableBindGroupsPlusVertexBuffers}) is < maxBindGroupsAndVertexBuffers (${actualLimit})`
);
t.skipIf(
maxUsableBindGroupsPlusVertexBuffers === actualLimit && testValue > actualLimit,
`can not test because the max usable bindGroups + vertexBuffers (${maxUsableBindGroupsPlusVertexBuffers}) === maxBindGroupsAndVertexBuffers (${actualLimit})
but the testValue (${testValue}) > maxBindGroupsAndVertexBuffers (${actualLimit})`
);

// Get the numVertexBuffers and numBindGroups we could use given testValue as a total.
Expand All @@ -204,14 +218,18 @@ g.test('draw,at_over')

const module = device.createShaderModule({
code: `
@vertex fn vs() -> @builtin(position) vec4f {
return vec4f(0);
}
`,
@vertex fn vs() -> @builtin(position) vec4f {
return vec4f(0);
}
@fragment fn fs() -> @location(0) vec4f {
return vec4f(0);
}
`,
});
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: { module },
fragment: { module, targets: [{ format: 'rgba8unorm' }] },
});

const vertexBuffer = device.createBuffer({
Expand Down Expand Up @@ -244,7 +262,7 @@ g.test('draw,at_over')
passEncoder.setPipeline(pipeline);

const indirectBuffer = device.createBuffer({
size: 4,
size: 20,
usage: GPUBufferUsage.INDIRECT,
});
t.trackForCleanup(indirectBuffer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ type ComputeCmd = (typeof kComputeCmds)[number];
const kRenderCmds = ['draw', 'drawIndexed', 'drawIndirect', 'drawIndexedIndirect'] as const;
type RenderCmd = (typeof kRenderCmds)[number];

const kPipelineTypes = ['auto0', 'explicit'] as const;
type PipelineType = (typeof kPipelineTypes)[number];
const kBindingTypes = ['auto0', 'auto1', 'explicit'] as const;
type BindingType = (typeof kBindingTypes)[number];

const kEmptyBindGroupNdx = 0;
const kNonEmptyBindGroupNdx = 1;

// Test resource type compatibility in pipeline and bind group
// [1]: Need to add externalTexture
const kResourceTypes: ValidBindableResource[] = [
Expand Down Expand Up @@ -277,6 +285,106 @@ class F extends ValidationTest {

validateFinish(success);
}

runDefaultLayoutBindingTest<T extends GPURenderPipeline | GPUComputePipeline>({
visibility,
empty,
pipelineType,
bindingType,
makePipelinesFn,
doCommandFn,
}: {
visibility: number;
empty: boolean;
pipelineType: PipelineType;
bindingType: BindingType;
makePipelinesFn: (t: F, explicitPipelineLayout: GPUPipelineLayout) => T[];
doCommandFn: (params: {
t: F;
encoder: GPUCommandEncoder;
pipeline: T;
emptyBindGroup: GPUBindGroup;
nonEmptyBindGroup: GPUBindGroup;
}) => void;
}) {
const { device } = this;
const explicitEmptyBindGroupLayout = device.createBindGroupLayout({
entries: [],
});
const explicitBindGroupLayout = device.createBindGroupLayout({
entries: [
{
binding: 0,
visibility,
buffer: {},
},
],
});
const explicitPipelineLayout = device.createPipelineLayout({
bindGroupLayouts: [explicitEmptyBindGroupLayout, explicitBindGroupLayout],
});

const [pipelineAuto0, pipelineAuto1, pipelineExplicit] = makePipelinesFn(
this,
explicitPipelineLayout
);

const buffer = device.createBuffer({
size: 16,
usage: GPUBufferUsage.UNIFORM,
});
this.trackForCleanup(buffer);

let emptyBindGroupLayout;
let nonEmptyBindGroupLayout;
let pipeline: T;

if (empty) {
// Testing empty:
// - nonEmptyBindGroupLayout comes from the pipeline we'll render/compute with.
// - emptyBindGroupLayout comes from a possibly incompatible place.
pipeline = pipelineType === 'auto0' ? pipelineAuto0 : pipelineExplicit;
emptyBindGroupLayout =
bindingType === 'explicit'
? explicitEmptyBindGroupLayout
: bindingType === 'auto0'
? pipelineAuto0.getBindGroupLayout(kEmptyBindGroupNdx)
: pipelineAuto1.getBindGroupLayout(kEmptyBindGroupNdx);
nonEmptyBindGroupLayout = pipeline.getBindGroupLayout(kNonEmptyBindGroupNdx);
} else {
// Testing non-empty:
// - emptyBindGroupLayout comes from the pipeline we'll render/compute with.
// - nonEmptyBindGroupLayout comes from a possibly incompatible place.
pipeline = pipelineType === 'auto0' ? pipelineAuto0 : pipelineExplicit;
nonEmptyBindGroupLayout =
bindingType === 'explicit'
? explicitBindGroupLayout
: bindingType === 'auto0'
? pipelineAuto0.getBindGroupLayout(kNonEmptyBindGroupNdx)
: pipelineAuto1.getBindGroupLayout(kNonEmptyBindGroupNdx);
emptyBindGroupLayout = pipeline.getBindGroupLayout(kEmptyBindGroupNdx);
}

const emptyBindGroup = device.createBindGroup({
layout: emptyBindGroupLayout,
entries: [],
});

const nonEmptyBindGroup = device.createBindGroup({
layout: nonEmptyBindGroupLayout,
entries: [{ binding: 0, resource: { buffer } }],
});

const encoder = device.createCommandEncoder();

doCommandFn({ t: this, encoder, pipeline, emptyBindGroup, nonEmptyBindGroup });

const success = bindingType === pipelineType;

this.expectValidationError(() => {
encoder.finish();
}, !success);
}
}

export const g = makeTestGroup(F);
Expand Down Expand Up @@ -793,3 +901,155 @@ g.test('empty_bind_group_layouts_requires_empty_bind_groups,render_pass')
encoder.finish();
}, !success);
});

const kPipelineTypesAndBindingTypeParams = [
{ pipelineType: 'auto0', bindingType: 'auto0' }, // successful case
{ pipelineType: 'explicit', bindingType: 'explicit' }, // successful case
{ pipelineType: 'explicit', bindingType: 'auto0' },
{ pipelineType: 'auto0', bindingType: 'explicit' },
{ pipelineType: 'auto0', bindingType: 'auto1' },
] as const;

g.test('default_bind_group_layouts_never_match,compute_pass')
.desc(
`
Test that bind groups created with default bind group layouts never match other layouts, including empty bind groups.
* Test that a pipeline with an explicit layout can not be used with a bindGroup from an auto layout
* Test that a pipeline with an auto layout can not be used with a bindGroup from an explicit layout
* Test that an auto layout from one pipeline can not be used with an auto layout from a different pipeline.
TODO:
* Test matching bindgroup layouts on the same default layout pipeline are compatible. In other words if
you only define group(2) then group(0)'s empty layout and group(1)'s empty layout should be compatible.
Similarly if group(0) and group(1) have the same types of resources they should be compatible.
`
)
.params(u =>
u
.combineWithParams(kPipelineTypesAndBindingTypeParams)
.combine('empty', [false, true])
.combine('computeCommand', ['dispatchIndirect', 'dispatch'] as const)
)
.fn(t => {
const { pipelineType, bindingType, computeCommand, empty } = t.params;

t.runDefaultLayoutBindingTest<GPUComputePipeline>({
visibility: GPUShaderStage.COMPUTE,
empty,
pipelineType,
bindingType,
makePipelinesFn: (t, explicitPipelineLayout) => {
return (['auto', 'auto', explicitPipelineLayout] as const).map<GPUComputePipeline>(layout =>
t.device.createComputePipeline({
layout,
compute: {
module: t.device.createShaderModule({
code: `
@group(1) @binding(0) var<uniform> u: vec4f;
@compute @workgroup_size(1) fn main() { _ = u; }
`,
}),
entryPoint: 'main',
},
})
);
},
doCommandFn: ({ t, encoder, pipeline, emptyBindGroup, nonEmptyBindGroup }) => {
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(kEmptyBindGroupNdx, emptyBindGroup);
pass.setBindGroup(kNonEmptyBindGroupNdx, nonEmptyBindGroup);
t.doCompute(pass, computeCommand, true);
pass.end();
},
});
});

g.test('default_bind_group_layouts_never_match,render_pass')
.desc(
`
Test that bind groups created with default bind group layouts never match other layouts, including empty bind groups.
* Test that a pipeline with an explicit layout can not be used with a bindGroup from an auto layout
* Test that a pipeline with an auto layout can not be used with a bindGroup from an explicit layout
* Test that an auto layout from one pipeline can not be used with an auto layout from a different pipeline.
TODO:
* Test matching bindgroup layouts on the same default layout pipeline are compatible. In other words if
you only define group(2) then group(0)'s empty layout and group(1)'s empty layout should be compatible.
Similarly if group(0) and group(1) have the same types of resources they should be compatible.
`
)
.params(u =>
u
.combineWithParams(kPipelineTypesAndBindingTypeParams)
.combine('empty', [false, true])
.combine('renderCommand', [
'draw',
'drawIndexed',
'drawIndirect',
'drawIndexedIndirect',
] as const)
)
.fn(t => {
const { pipelineType, bindingType, renderCommand, empty } = t.params;

t.runDefaultLayoutBindingTest<GPURenderPipeline>({
visibility: GPUShaderStage.VERTEX,
empty,
pipelineType,
bindingType,
makePipelinesFn: (t, explicitPipelineLayout) => {
return (['auto', 'auto', explicitPipelineLayout] as const).map<GPURenderPipeline>(
layout => {
const colorFormat = 'rgba8unorm';
return t.device.createRenderPipeline({
layout,
vertex: {
module: t.device.createShaderModule({
code: `
@group(1) @binding(0) var<uniform> u: vec4f;
@vertex fn main() -> @builtin(position) vec4f { return u; }
`,
}),
entryPoint: 'main',
},
fragment: {
module: t.device.createShaderModule({
code: `@fragment fn main() {}`,
}),
entryPoint: 'main',
targets: [{ format: colorFormat, writeMask: 0 }],
},
});
}
);
},
doCommandFn: ({ t, encoder, pipeline, emptyBindGroup, nonEmptyBindGroup }) => {
const attachmentTexture = t.device.createTexture({
format: 'rgba8unorm',
size: { width: 16, height: 16, depthOrArrayLayers: 1 },
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
t.trackForCleanup(attachmentTexture);

const renderPass = encoder.beginRenderPass({
colorAttachments: [
{
view: attachmentTexture.createView(),
clearValue: [0, 0, 0, 0],
loadOp: 'clear',
storeOp: 'store',
},
],
});

renderPass.setPipeline(pipeline);
renderPass.setBindGroup(kEmptyBindGroupNdx, emptyBindGroup);
renderPass.setBindGroup(kNonEmptyBindGroupNdx, nonEmptyBindGroup);
t.doRender(renderPass, renderCommand, true);
renderPass.end();
},
});
});
2 changes: 2 additions & 0 deletions src/webgpu/listing_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,8 @@
"webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:bgl_visibility_mismatch:*": { "subcaseMS": 0.608 },
"webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:bind_groups_and_pipeline_layout_mismatch:*": { "subcaseMS": 1.535 },
"webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:buffer_binding,render_pipeline:*": { "subcaseMS": 1.734 },
"webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:default_bind_group_layouts_never_match,compute_pass:*": { "subcaseMS": 1.734 },
"webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:default_bind_group_layouts_never_match,render_pass:*": { "subcaseMS": 1.734 },
"webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:empty_bind_group_layouts_requires_empty_bind_groups,compute_pass:*": { "subcaseMS": 2.325 },
"webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:empty_bind_group_layouts_requires_empty_bind_groups,render_pass:*": { "subcaseMS": 10.838 },
"webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:sampler_binding,render_pipeline:*": { "subcaseMS": 10.523 },
Expand Down

0 comments on commit 414c4cd

Please sign in to comment.