Skip to content

Commit b383693

Browse files
committed
Add support for QCOM tile shading
1 parent 83dff74 commit b383693

File tree

8 files changed

+513
-53
lines changed

8 files changed

+513
-53
lines changed

source/val/validate_annotation.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec,
233233
case spv::Decoration::DescriptorSet:
234234
if (sc != spv::StorageClass::StorageBuffer &&
235235
sc != spv::StorageClass::Uniform &&
236-
sc != spv::StorageClass::UniformConstant) {
236+
sc != spv::StorageClass::UniformConstant &&
237+
sc != spv::StorageClass::TileAttachmentQCOM) {
237238
return fail(6491) << "must be in the StorageBuffer, Uniform, or "
238239
"UniformConstant storage class";
239240
}

source/val/validate_decorations.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -404,8 +404,7 @@ bool IsAlignedTo(uint32_t offset, uint32_t alignment) {
404404
// or row major-ness.
405405
spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str,
406406
const char* decoration_str, bool blockRules,
407-
bool scalar_block_layout,
408-
uint32_t incoming_offset,
407+
bool scalar_block_layout, uint32_t incoming_offset,
409408
MemberConstraints& constraints,
410409
ValidationState_t& vstate) {
411410
if (vstate.options()->skip_block_layout) return SPV_SUCCESS;
@@ -1023,7 +1022,7 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) {
10231022
}
10241023
if (num_workgroup_variables_with_block > 1 &&
10251024
num_workgroup_variables_with_block !=
1026-
num_workgroup_variables_with_aliased) {
1025+
num_workgroup_variables_with_aliased) {
10271026
return vstate.diag(SPV_ERROR_INVALID_BINARY,
10281027
vstate.FindDef(entry_point))
10291028
<< "When declaring WorkgroupMemoryExplicitLayoutKHR, "
@@ -1246,10 +1245,10 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) {
12461245
}
12471246
// Prepare for messages
12481247
const char* sc_str =
1249-
uniform ? "Uniform"
1250-
: (push_constant ? "PushConstant"
1251-
: (workgroup ? "Workgroup"
1252-
: "StorageBuffer"));
1248+
uniform
1249+
? "Uniform"
1250+
: (push_constant ? "PushConstant"
1251+
: (workgroup ? "Workgroup" : "StorageBuffer"));
12531252

12541253
if (spvIsVulkanEnv(vstate.context()->target_env)) {
12551254
const bool block = hasDecoration(id, spv::Decoration::Block, vstate);
@@ -2031,7 +2030,8 @@ spv_result_t CheckRelaxPrecisionDecoration(ValidationState_t& vstate,
20312030
{ \
20322031
spv_result_t e##LINE = (X); \
20332032
if (e##LINE != SPV_SUCCESS) return e##LINE; \
2034-
} static_assert(true, "require extra semicolon")
2033+
} \
2034+
static_assert(true, "require extra semicolon")
20352035
#define PASS_OR_BAIL(X) PASS_OR_BAIL_AT_LINE(X, __LINE__)
20362036

20372037
// Check rules for decorations where we start from the decoration rather

source/val/validate_memory.cpp

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -939,34 +939,46 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
939939

940940
if (_.HasCapability(spv::Capability::TileShadingQCOM) &&
941941
storage_class == spv::StorageClass::TileAttachmentQCOM) {
942-
if (result_type->opcode() == spv::Op::OpTypeImage) {
943-
spv::Dim dim = static_cast<spv::Dim>(inst->word(3));
944-
if (dim != spv::Dim::Dim2D) {
945-
return _.diag(SPV_ERROR_INVALID_DATA, inst)
946-
<< "Any OpTpeImage variable in the TileAttachmentQCOM Storage Class must "
947-
"have 2D as its dimension";
948-
}
949-
unsigned sampled = inst->word(7);
950-
if (sampled != 1 && sampled != 2) {
951-
return _.diag(SPV_ERROR_INVALID_DATA, inst)
952-
<< "Any OpTpeImage variable in the TileAttachmentQCOM Storage Class must "
953-
"have 1 or 2 as Image 'Sampled' parameter";
954-
}
955-
for (const auto& pair : inst->uses()) {
956-
const auto* use_inst = pair.first;
957-
switch (use_inst->opcode()) {
958-
case spv::Op::OpImageQueryFormat:
959-
case spv::Op::OpImageQueryOrder:
960-
case spv::Op::OpImageQuerySizeLod:
961-
case spv::Op::OpImageQuerySize:
962-
case spv::Op::OpImageQueryLod:
963-
case spv::Op::OpImageQueryLevels:
964-
case spv::Op::OpImageQuerySamples:
965-
return _.diag(SPV_ERROR_INVALID_DATA, inst)
966-
<< "Any variable in the TileAttachmentQCOM Storage Class must "
967-
"not be consumed by an OpImageQuery* instruction";
968-
default:
969-
break;
942+
if (result_type->opcode() == spv::Op::OpTypePointer) {
943+
const auto pointee_type =
944+
_.FindDef(result_type->GetOperandAs<uint32_t>(2));
945+
if (pointee_type && pointee_type->opcode() == spv::Op::OpTypeImage) {
946+
spv::Dim dim = static_cast<spv::Dim>(pointee_type->word(3));
947+
if (dim != spv::Dim::Dim2D) {
948+
return _.diag(SPV_ERROR_INVALID_DATA, inst)
949+
<< "Any OpTypeImage variable in the TileAttachmentQCOM "
950+
"Storage Class must "
951+
"have 2D as its dimension";
952+
}
953+
unsigned sampled = pointee_type->word(7);
954+
if (sampled != 1 && sampled != 2) {
955+
return _.diag(SPV_ERROR_INVALID_DATA, inst)
956+
<< "Any OpyTpeImage variable in the TileAttachmentQCOM "
957+
"Storage Class must "
958+
"have 1 or 2 as Image 'Sampled' parameter";
959+
}
960+
for (const auto& pair_o : inst->uses()) {
961+
const auto* use_inst_o = pair_o.first;
962+
if (use_inst_o->opcode() == spv::Op::OpLoad) {
963+
for (const auto& pair_i : use_inst_o->uses()) {
964+
const auto* use_inst_i = pair_i.first;
965+
switch (use_inst_i->opcode()) {
966+
case spv::Op::OpImageQueryFormat:
967+
case spv::Op::OpImageQueryOrder:
968+
case spv::Op::OpImageQuerySizeLod:
969+
case spv::Op::OpImageQuerySize:
970+
case spv::Op::OpImageQueryLod:
971+
case spv::Op::OpImageQueryLevels:
972+
case spv::Op::OpImageQuerySamples:
973+
return _.diag(SPV_ERROR_INVALID_DATA, inst)
974+
<< "Any variable in the TileAttachmentQCOM Storage "
975+
"Class must "
976+
"not be consumed by an OpImageQuery* instruction";
977+
default:
978+
break;
979+
}
980+
}
981+
}
970982
}
971983
}
972984
}

source/val/validate_mode_setting.cpp

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -286,15 +286,11 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
286286
}
287287

288288
if (spvIsVulkanEnv(_.context()->target_env)) {
289-
bool ok = false;
290-
bool foundLocalSizeId = false;
291289
switch (execution_model) {
292290
case spv::ExecutionModel::GLCompute:
293-
if (_.HasCapability(spv::Capability::TileShadingQCOM)) {
294-
ok = (execution_modes && execution_modes->count(spv::ExecutionMode::TileShadingRateQCOM));
295-
}
296-
if (!ok && (!execution_modes ||
297-
!execution_modes->count(spv::ExecutionMode::LocalSize))) {
291+
if (!execution_modes ||
292+
!execution_modes->count(spv::ExecutionMode::LocalSize)) {
293+
bool ok = false;
298294
for (auto& i : _.ordered_instructions()) {
299295
if (i.opcode() == spv::Op::OpDecorate) {
300296
if (i.operands().size() > 2) {
@@ -311,34 +307,86 @@ spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
311307
const auto mode = i.GetOperandAs<spv::ExecutionMode>(1);
312308
if (mode == spv::ExecutionMode::LocalSizeId) {
313309
ok = true;
314-
foundLocalSizeId = true;
315310
break;
316311
}
317312
}
318313
}
314+
if (!ok && _.HasCapability(spv::Capability::TileShadingQCOM)) {
315+
ok =
316+
execution_modes &&
317+
execution_modes->count(spv::ExecutionMode::TileShadingRateQCOM);
318+
}
319319
if (!ok) {
320320
return _.diag(SPV_ERROR_INVALID_DATA, inst)
321321
<< _.VkErrorID(6426)
322322
<< "In the Vulkan environment, GLCompute execution model "
323323
"entry points require either the "
324-
<< (_.HasCapability(spv::Capability::TileShadingQCOM) ? "TileShadingRateQCOM, " : "")
324+
<< (_.HasCapability(spv::Capability::TileShadingQCOM)
325+
? "TileShadingRateQCOM, "
326+
: "")
325327
<< "LocalSize or LocalSizeId execution mode or an object "
326328
"decorated with WorkgroupSize must be specified.";
327329
}
328330
}
329331

330332
if (_.HasCapability(spv::Capability::TileShadingQCOM)) {
331333
if (execution_modes) {
332-
if (execution_modes->count(spv::ExecutionMode::TileShadingRateQCOM) &&
333-
(execution_modes->count(spv::ExecutionMode::LocalSize) || foundLocalSizeId)) {
334+
if (execution_modes->count(
335+
spv::ExecutionMode::TileShadingRateQCOM) &&
336+
(execution_modes->count(spv::ExecutionMode::LocalSize) ||
337+
execution_modes->count(spv::ExecutionMode::LocalSizeId))) {
334338
return _.diag(SPV_ERROR_INVALID_DATA, inst)
335-
<< "If the TileShadingRateQCOM Execution Mode is used, "
339+
<< "If the TileShadingRateQCOM execution mode is used, "
336340
<< "LocalSize and LocalSizeId must not be specified.";
337341
}
342+
if (execution_modes->count(
343+
spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
344+
return _.diag(SPV_ERROR_INVALID_DATA, inst)
345+
<< "The NonCoherentTileAttachmentQCOM execution mode must "
346+
"not be used in any stage other than fragment.";
347+
}
348+
}
349+
} else {
350+
if (execution_modes &&
351+
execution_modes->count(spv::ExecutionMode::TileShadingRateQCOM)) {
352+
return _.diag(SPV_ERROR_INVALID_DATA, inst)
353+
<< "If the TileShadingRateQCOM execution mode is used, the "
354+
"TileShadingQCOM capability must be enabled.";
338355
}
339356
}
340357
break;
341358
default:
359+
if (execution_modes &&
360+
execution_modes->count(spv::ExecutionMode::TileShadingRateQCOM)) {
361+
return _.diag(SPV_ERROR_INVALID_DATA, inst)
362+
<< "The TileShadingRateQCOM execution mode must not be used "
363+
"in any stage other than compute.";
364+
}
365+
if (execution_model != spv::ExecutionModel::Fragment) {
366+
if (execution_modes &&
367+
execution_modes->count(
368+
spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
369+
return _.diag(SPV_ERROR_INVALID_DATA, inst)
370+
<< "The NonCoherentTileAttachmentQCOM execution mode must "
371+
"not be used in any stage other than fragment.";
372+
}
373+
if (_.HasCapability(spv::Capability::TileShadingQCOM)) {
374+
return _.diag(SPV_ERROR_INVALID_CAPABILITY, inst)
375+
<< "The TileShadingQCOM capability must not be enabled in "
376+
"any stage other than compute or fragment.";
377+
}
378+
} else {
379+
if (execution_modes &&
380+
execution_modes->count(
381+
spv::ExecutionMode::NonCoherentTileAttachmentReadQCOM)) {
382+
if (!_.HasCapability(spv::Capability::TileShadingQCOM)) {
383+
return _.diag(SPV_ERROR_INVALID_DATA, inst)
384+
<< "If the NonCoherentTileAttachmentReadQCOM execution "
385+
"mode is used, the TileShadingQCOM capability must be "
386+
"enabled.";
387+
}
388+
}
389+
}
342390
break;
343391
}
344392
}
@@ -775,6 +823,14 @@ spv_result_t ValidateExecutionMode(ValidationState_t& _,
775823
<< "In the Vulkan environment, the PixelCenterInteger execution "
776824
"mode must not be used.";
777825
}
826+
if (mode == spv::ExecutionMode::TileShadingRateQCOM) {
827+
const auto rateX = inst->GetOperandAs<int>(2);
828+
const auto rateY = inst->GetOperandAs<int>(3);
829+
if ((rateX & (rateX - 1)) != 0 || (rateY & (rateY - 1)) != 0)
830+
return _.diag(SPV_ERROR_INVALID_DATA, inst)
831+
<< "The TileShadingRateQCOM execution mode's x and y values "
832+
"must be powers of 2.";
833+
}
778834
}
779835

780836
return SPV_SUCCESS;

source/val/validation_state.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1883,6 +1883,7 @@ bool ValidationState_t::IsValidStorageClass(
18831883
case spv::StorageClass::HitObjectAttributeNV:
18841884
case spv::StorageClass::TileImageEXT:
18851885
case spv::StorageClass::NodePayloadAMDX:
1886+
case spv::StorageClass::TileAttachmentQCOM:
18861887
return true;
18871888
default:
18881889
return false;

test/val/val_capability_test.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3214,7 +3214,8 @@ std::string MinimalShaderModuleWithCapability(std::string cap) {
32143214
: "";
32153215
return std::string("OpCapability ") + cap + extra_cap + R"(
32163216
OpCapability Shader
3217-
OpMemoryModel Logical )" + mem_model + R"(
3217+
OpMemoryModel Logical )" +
3218+
mem_model + R"(
32183219
OpEntryPoint Vertex %main "main"
32193220
%void = OpTypeVoid
32203221
%void_fn = OpTypeFunction %void
@@ -3383,6 +3384,23 @@ OpMemoryModel Logical GLSL450
33833384
"the VulkanMemoryModel capability must also be declared"));
33843385
}
33853386

3387+
TEST_F(ValidateCapability, TileShadingQCOM) {
3388+
const auto spirv = R"(
3389+
OpCapability Shader
3390+
OpCapability TileShadingQCOM
3391+
OpExtension "SPV_QCOM_tile_shading"
3392+
OpMemoryModel Logical GLSL450
3393+
OpEntryPoint Vertex %func "main"
3394+
)" + std::string(kVoidFVoid);
3395+
3396+
spv_target_env env = SPV_ENV_VULKAN_1_4;
3397+
CompileSuccessfully(spirv, env);
3398+
EXPECT_THAT(SPV_ERROR_INVALID_CAPABILITY, ValidateInstructions(env));
3399+
EXPECT_THAT(getDiagnosticString(),
3400+
HasSubstr("The TileShadingQCOM capability must not be enabled "
3401+
"in any stage other than compute or fragment"));
3402+
}
3403+
33863404
} // namespace
33873405
} // namespace val
33883406
} // namespace spvtools

0 commit comments

Comments
 (0)