Skip to content

Commit

Permalink
default entry points to shader modules (#3190)
Browse files Browse the repository at this point in the history
Co-authored-by: Kai Ninomiya <[email protected]>
  • Loading branch information
beaufortfrancois and kainino0x authored Nov 28, 2023
1 parent f2546cb commit 4f3cbf4
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 28 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"@types/serve-index": "^1.9.3",
"@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.9.1",
"@webgpu/types": "^0.1.39",
"@webgpu/types": "^0.1.40",
"ansi-colors": "4.1.3",
"babel-plugin-add-header-comment": "^1.0.3",
"babel-plugin-const-enum": "^1.2.0",
Expand Down
226 changes: 206 additions & 20 deletions src/webgpu/api/validation/shader_module/entry_point.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ This tests entry point validation of compute/render pipelines and their shader m
The entryPoint in shader module include standard "main" and others.
The entryPoint assigned in descriptor include:
- Undefined with matching entry point for stage
- Matching case (control case)
- Empty string
- Mistyping
- Containing invalid char, including space and control codes (Null character)
- Unicode entrypoints and their ASCIIfied version
TODO:
- Test unicode normalization (gpuweb/gpuweb#1160)
- Fine-tune test cases to reduce number by removing trivially similar cases
`;

Expand Down Expand Up @@ -43,23 +43,52 @@ const kEntryPointTestCases = [
g.test('compute')
.desc(
`
Tests calling createComputePipeline(Async) with valid vertex stage shader and different entryPoints,
Tests calling createComputePipeline(Async) with valid compute stage shader and different entryPoints,
and check that the APIs only accept matching entryPoint.
`
)
.params(u => u.combine('isAsync', [true, false]).combineWithParams(kEntryPointTestCases))
.params(u =>
u
.combine('isAsync', [true, false])
.combine('shaderModuleStage', ['compute', 'vertex', 'fragment'] as const)
.beginSubcases()
.combine('provideEntryPoint', [true, false])
.combine('extraEntryPoint', [true, false])
.combineWithParams(kEntryPointTestCases)
)
.fn(t => {
const { isAsync, shaderModuleEntryPoint, stageEntryPoint } = t.params;
const {
isAsync,
provideEntryPoint,
extraEntryPoint,
shaderModuleStage,
shaderModuleEntryPoint,
stageEntryPoint,
} = t.params;
const entryPoint = provideEntryPoint ? stageEntryPoint : undefined;
let code = getShaderWithEntryPoint(shaderModuleStage, shaderModuleEntryPoint);
if (extraEntryPoint) {
code += ` ${getShaderWithEntryPoint(shaderModuleStage, 'extra')}`;
}
const descriptor: GPUComputePipelineDescriptor = {
layout: 'auto',
compute: {
module: t.device.createShaderModule({
code: getShaderWithEntryPoint('compute', shaderModuleEntryPoint),
code,
}),
entryPoint: stageEntryPoint,
entryPoint,
},
};
const _success = shaderModuleEntryPoint === stageEntryPoint;
let _success = true;
if (shaderModuleStage !== 'compute') {
_success = false;
}
if (!provideEntryPoint && extraEntryPoint) {
_success = false;
}
if (shaderModuleEntryPoint !== stageEntryPoint && provideEntryPoint) {
_success = false;
}
t.doCreateComputePipelineTest(isAsync, _success, descriptor);
});

Expand All @@ -70,19 +99,46 @@ Tests calling createRenderPipeline(Async) with valid vertex stage shader and dif
and check that the APIs only accept matching entryPoint.
`
)
.params(u => u.combine('isAsync', [true, false]).combineWithParams(kEntryPointTestCases))
.params(u =>
u
.combine('isAsync', [true, false])
.combine('shaderModuleStage', ['compute', 'vertex', 'fragment'] as const)
.beginSubcases()
.combine('provideEntryPoint', [true, false])
.combine('extraEntryPoint', [true, false])
.combineWithParams(kEntryPointTestCases)
)
.fn(t => {
const { isAsync, shaderModuleEntryPoint, stageEntryPoint } = t.params;
const {
isAsync,
provideEntryPoint,
extraEntryPoint,
shaderModuleStage,
shaderModuleEntryPoint,
stageEntryPoint,
} = t.params;
const entryPoint = provideEntryPoint ? stageEntryPoint : undefined;
let code = getShaderWithEntryPoint(shaderModuleStage, shaderModuleEntryPoint);
if (extraEntryPoint) {
code += ` ${getShaderWithEntryPoint(shaderModuleStage, 'extra')}`;
}
const descriptor: GPURenderPipelineDescriptor = {
layout: 'auto',
vertex: {
module: t.device.createShaderModule({
code: getShaderWithEntryPoint('vertex', shaderModuleEntryPoint),
}),
entryPoint: stageEntryPoint,
module: t.device.createShaderModule({ code }),
entryPoint,
},
};
const _success = shaderModuleEntryPoint === stageEntryPoint;
let _success = true;
if (shaderModuleStage !== 'vertex') {
_success = false;
}
if (!provideEntryPoint && extraEntryPoint) {
_success = false;
}
if (shaderModuleEntryPoint !== stageEntryPoint && provideEntryPoint) {
_success = false;
}
t.doCreateRenderPipelineTest(isAsync, _success, descriptor);
});

Expand All @@ -93,25 +149,155 @@ Tests calling createRenderPipeline(Async) with valid fragment stage shader and d
and check that the APIs only accept matching entryPoint.
`
)
.params(u => u.combine('isAsync', [true, false]).combineWithParams(kEntryPointTestCases))
.params(u =>
u
.combine('isAsync', [true, false])
.combine('shaderModuleStage', ['compute', 'vertex', 'fragment'] as const)
.beginSubcases()
.combine('provideEntryPoint', [true, false])
.combine('extraEntryPoint', [true, false])
.combineWithParams(kEntryPointTestCases)
)
.fn(t => {
const { isAsync, shaderModuleEntryPoint, stageEntryPoint } = t.params;
const {
isAsync,
provideEntryPoint,
extraEntryPoint,
shaderModuleStage,
shaderModuleEntryPoint,
stageEntryPoint,
} = t.params;
const entryPoint = provideEntryPoint ? stageEntryPoint : undefined;
let code = getShaderWithEntryPoint(shaderModuleStage, shaderModuleEntryPoint);
if (extraEntryPoint) {
code += ` ${getShaderWithEntryPoint(shaderModuleStage, 'extra')}`;
}
const descriptor: GPURenderPipelineDescriptor = {
layout: 'auto',
vertex: {
module: t.device.createShaderModule({
code: kDefaultVertexShaderCode,
}),
entryPoint: 'main',
},
fragment: {
module: t.device.createShaderModule({
code: getShaderWithEntryPoint('fragment', shaderModuleEntryPoint),
code,
}),
entryPoint: stageEntryPoint,
entryPoint,
targets: [{ format: 'rgba8unorm' }],
},
};
const _success = shaderModuleEntryPoint === stageEntryPoint;
let _success = true;
if (shaderModuleStage !== 'fragment') {
_success = false;
}
if (!provideEntryPoint && extraEntryPoint) {
_success = false;
}
if (shaderModuleEntryPoint !== stageEntryPoint && provideEntryPoint) {
_success = false;
}
t.doCreateRenderPipelineTest(isAsync, _success, descriptor);
});

g.test('compute_undefined_entry_point_and_extra_stage')
.desc(
`
Tests calling createComputePipeline(Async) with compute stage shader and
an undefined entryPoint is valid if there's an extra shader stage.
`
)
.params(u =>
u
.combine('isAsync', [true, false])
.combine('extraShaderModuleStage', ['compute', 'vertex', 'fragment'] as const)
)
.fn(t => {
const { isAsync, extraShaderModuleStage } = t.params;
const code = `
${getShaderWithEntryPoint('compute', 'main')}
${getShaderWithEntryPoint(extraShaderModuleStage, 'extra')}
`;
const descriptor: GPUComputePipelineDescriptor = {
layout: 'auto',
compute: {
module: t.device.createShaderModule({
code,
}),
entryPoint: undefined,
},
};

const success = extraShaderModuleStage !== 'compute';
t.doCreateComputePipelineTest(isAsync, success, descriptor);
});

g.test('vertex_undefined_entry_point_and_extra_stage')
.desc(
`
Tests calling createRenderPipeline(Async) with vertex stage shader and
an undefined entryPoint is valid if there's an extra shader stage.
`
)
.params(u =>
u
.combine('isAsync', [true, false])
.combine('extraShaderModuleStage', ['compute', 'vertex', 'fragment'] as const)
)
.fn(t => {
const { isAsync, extraShaderModuleStage } = t.params;
const code = `
${getShaderWithEntryPoint('vertex', 'main')}
${getShaderWithEntryPoint(extraShaderModuleStage, 'extra')}
`;
const descriptor: GPURenderPipelineDescriptor = {
layout: 'auto',
vertex: {
module: t.device.createShaderModule({
code,
}),
entryPoint: undefined,
},
};

const success = extraShaderModuleStage !== 'vertex';
t.doCreateRenderPipelineTest(isAsync, success, descriptor);
});

g.test('fragment_undefined_entry_point_and_extra_stage')
.desc(
`
Tests calling createRenderPipeline(Async) with fragment stage shader and
an undefined entryPoint is valid if there's an extra shader stage.
`
)
.params(u =>
u
.combine('isAsync', [true, false])
.combine('extraShaderModuleStage', ['compute', 'vertex', 'fragment'] as const)
)
.fn(t => {
const { isAsync, extraShaderModuleStage } = t.params;
const code = `
${getShaderWithEntryPoint('fragment', 'main')}
${getShaderWithEntryPoint(extraShaderModuleStage, 'extra')}
`;
const descriptor: GPURenderPipelineDescriptor = {
layout: 'auto',
vertex: {
module: t.device.createShaderModule({
code: kDefaultVertexShaderCode,
}),
},
fragment: {
module: t.device.createShaderModule({
code,
}),
entryPoint: undefined,
targets: [{ format: 'rgba8unorm' }],
},
};

const success = extraShaderModuleStage !== 'fragment';
t.doCreateRenderPipelineTest(isAsync, success, descriptor);
});
3 changes: 3 additions & 0 deletions src/webgpu/listing_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -775,8 +775,11 @@
"webgpu:api,validation,resource_usages,texture,in_render_misc:subresources,set_unused_bind_group:*": { "subcaseMS": 6.200 },
"webgpu:api,validation,resource_usages,texture,in_render_misc:subresources,texture_usages_in_copy_and_render_pass:*": { "subcaseMS": 4.763 },
"webgpu:api,validation,shader_module,entry_point:compute:*": { "subcaseMS": 4.439 },
"webgpu:api,validation,shader_module,entry_point:compute_undefined_entry_point_and_extra_stage:*": { "subcaseMS": 17.075 },
"webgpu:api,validation,shader_module,entry_point:fragment:*": { "subcaseMS": 5.865 },
"webgpu:api,validation,shader_module,entry_point:fragment_undefined_entry_point_and_extra_stage:*": { "subcaseMS": 16.050 },
"webgpu:api,validation,shader_module,entry_point:vertex:*": { "subcaseMS": 5.803 },
"webgpu:api,validation,shader_module,entry_point:vertex_undefined_entry_point_and_extra_stage:*": { "subcaseMS": 15.851 },
"webgpu:api,validation,shader_module,overrides:id_conflict:*": { "subcaseMS": 36.700 },
"webgpu:api,validation,shader_module,overrides:name_conflict:*": { "subcaseMS": 1.500 },
"webgpu:api,validation,state,device_lost,destroy:command,clearBuffer:*": { "subcaseMS": 11.826 },
Expand Down

0 comments on commit 4f3cbf4

Please sign in to comment.