Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ignore empty bind group layout when checking setBindGroup against the… #4134

Merged
merged 3 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -830,25 +830,37 @@ g.test('bgl_resource_type_mismatch')
);
});

g.test('empty_bind_group_layouts_requires_empty_bind_groups,compute_pass')
g.test('empty_bind_group_layouts_never_requires_empty_bind_groups,compute_pass')
.desc(
`
Test that a compute pipeline with empty bind groups layouts requires empty bind groups to be set.
Test that a compute pipeline with empty bind group layouts doesn't require empty bind groups to be
set as empty bind group layout items should always be ignored.
`
)
.params(u =>
u
.combine('emptyBindGroupLayoutType', ['Null', 'Undefined', 'Empty'] as const)
.combine('bindGroupLayoutEntryCount', [3, 4])
.combine('computeCommand', ['dispatchIndirect', 'dispatch'] as const)
)
.fn(t => {
const { bindGroupLayoutEntryCount, computeCommand } = t.params;
const { emptyBindGroupLayoutType, bindGroupLayoutEntryCount, computeCommand } = t.params;

const emptyBGLCount = 4;
const emptyBGL = t.device.createBindGroupLayout({ entries: [] });
const emptyBGLs = [];
for (let i = 0; i < emptyBGLCount; i++) {
emptyBGLs.push(emptyBGL);
switch (emptyBindGroupLayoutType) {
case 'Null':
emptyBGLs.push(null);
break;
case 'Undefined':
emptyBGLs.push(undefined);
break;
case 'Empty':
emptyBGLs.push(emptyBGL);
break;
}
}

const pipelineLayout = t.device.createPipelineLayout({
Expand Down Expand Up @@ -880,21 +892,23 @@ g.test('empty_bind_group_layouts_requires_empty_bind_groups,compute_pass')
t.doCompute(computePass, computeCommand, true);
computePass.end();

const success = bindGroupLayoutEntryCount === emptyBGLCount;
const success = true;

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

g.test('empty_bind_group_layouts_requires_empty_bind_groups,render_pass')
g.test('empty_bind_group_layouts_never_requires_empty_bind_groups,render_pass')
.desc(
`
Test that a render pipeline with empty bind groups layouts requires empty bind groups to be set.
Test that a render pipeline with empty bind groups layouts doesn't require empty bind groups to be
set as empty bind group layout items should always be ignored.
`
)
.params(u =>
u
.combine('emptyBindGroupLayoutType', ['Null', 'Undefined', 'Empty'] as const)
.combine('bindGroupLayoutEntryCount', [3, 4])
.combine('renderCommand', [
'draw',
Expand All @@ -904,13 +918,23 @@ g.test('empty_bind_group_layouts_requires_empty_bind_groups,render_pass')
] as const)
)
.fn(t => {
const { bindGroupLayoutEntryCount, renderCommand } = t.params;
const { emptyBindGroupLayoutType, bindGroupLayoutEntryCount, renderCommand } = t.params;

const emptyBGLCount = 4;
const emptyBGL = t.device.createBindGroupLayout({ entries: [] });
const emptyBGLs = [];
for (let i = 0; i < emptyBGLCount; i++) {
emptyBGLs.push(emptyBGL);
switch (emptyBindGroupLayoutType) {
case 'Null':
emptyBGLs.push(null);
break;
case 'Undefined':
emptyBGLs.push(undefined);
break;
case 'Empty':
emptyBGLs.push(emptyBGL);
break;
}
}

const pipelineLayout = t.device.createPipelineLayout({
Expand Down Expand Up @@ -966,7 +990,7 @@ g.test('empty_bind_group_layouts_requires_empty_bind_groups,render_pass')
t.doRender(renderPass, renderCommand, true);
renderPass.end();

const success = bindGroupLayoutEntryCount === emptyBGLCount;
const success = true;

t.expectValidationError(() => {
encoder.finish();
Expand Down Expand Up @@ -997,11 +1021,17 @@ const kPipelineTypesAndBindingTypeParams = [
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.
Test that bind groups created with default bind group layouts never match other layouts, except
when the default bind group layouts are empty because the empty bind group layouts should all be
treated as null bind group layouts and be ignored when checking setBindGroup() against the current
pipeline.

* Test that a pipeline with an explicit layout can not be used with a bindGroup from an auto
layout except the explicit layout is empty.
* Test that a pipeline with an auto layout can not be used with a bindGroup from an explicit
layout except the layout got from the pipeline is empty.
* Test that an auto layout from one pipeline can not be used with an auto layout from a different
pipeline except the layouts got from the pipeline are empty.
* 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(2) and group(3) have the same types of resources they should be compatible.
Expand All @@ -1014,7 +1044,16 @@ g.test('default_bind_group_layouts_never_match,compute_pass')
.combine('computeCommand', ['dispatchIndirect', 'dispatch'] as const)
)
.fn(t => {
const { pipelineType, bindingType, swap, _success: success, computeCommand, empty } = t.params;
const {
pipelineType,
bindingType,
swap,
_success: successWhenNonEmpty,
computeCommand,
empty,
} = t.params;

const success = empty || successWhenNonEmpty;

t.runDefaultLayoutBindingTest<GPUComputePipeline>({
visibility: GPUShaderStage.COMPUTE,
Expand Down Expand Up @@ -1056,11 +1095,17 @@ g.test('default_bind_group_layouts_never_match,compute_pass')
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.
Test that bind groups created with default bind group layouts never match other layouts, except
when the default bind group layouts are empty because the empty bind group layouts should all be
treated as null bind group layouts and be ignored when checking setBindGroup() against the current
pipeline.

* Test that a pipeline with an explicit layout can not be used with a bindGroup from an auto
layout except the explicit layout is empty.
* Test that a pipeline with an auto layout can not be used with a bindGroup from an explicit
layout except the layout got from the pipeline is empty.
* Test that an auto layout from one pipeline can not be used with an auto layout from a different
pipeline except the layouts got from the pipeline are empty.
* 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(2) and group(3) have the same types of resources they should be compatible.
Expand All @@ -1078,7 +1123,16 @@ g.test('default_bind_group_layouts_never_match,render_pass')
] as const)
)
.fn(t => {
const { pipelineType, bindingType, swap, _success: success, renderCommand, empty } = t.params;
const {
pipelineType,
bindingType,
swap,
_success: successWhenNonEmpty,
renderCommand,
empty,
} = t.params;

const success = empty || successWhenNonEmpty;

t.runDefaultLayoutBindingTest<GPURenderPipeline>({
visibility: GPUShaderStage.VERTEX,
Expand Down
4 changes: 2 additions & 2 deletions src/webgpu/listing_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -548,8 +548,8 @@
"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:empty_bind_group_layouts_never_requires_empty_bind_groups,compute_pass:*": { "subcaseMS": 2.325 },
"webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:empty_bind_group_layouts_never_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 },
"webgpu:api,validation,encoding,queries,begin_end:nesting:*": { "subcaseMS": 1.101 },
"webgpu:api,validation,encoding,queries,begin_end:occlusion_query,begin_end_balance:*": { "subcaseMS": 0.820 },
Expand Down
Loading