mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-11-23 04:00:05 +00:00
parent
8d2d66f30c
commit
b6150e5170
@ -276,6 +276,16 @@ uint32_t GetMakeVisibleScope(const Instruction* inst, uint32_t mask) {
|
||||
return scope_id;
|
||||
}
|
||||
|
||||
bool DoesStructContainRTA(const ValidationState_t& _, const Instruction* inst) {
|
||||
for (size_t member_index = 1; member_index < inst->operands().size();
|
||||
++member_index) {
|
||||
const auto member_id = inst->GetOperandAs<uint32_t>(member_index);
|
||||
const auto member_type = _.FindDef(member_id);
|
||||
if (member_type->opcode() == SpvOpTypeRuntimeArray) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
|
||||
uint32_t index) {
|
||||
SpvStorageClass dst_sc, src_sc;
|
||||
@ -283,9 +293,9 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
|
||||
if (inst->operands().size() <= index) {
|
||||
if (src_sc == SpvStorageClassPhysicalStorageBufferEXT ||
|
||||
dst_sc == SpvStorageClassPhysicalStorageBufferEXT) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Memory accesses with "
|
||||
"PhysicalStorageBufferEXT "
|
||||
"must use Aligned.";
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Memory accesses with PhysicalStorageBufferEXT must use "
|
||||
"Aligned.";
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
@ -318,7 +328,7 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
|
||||
if (!(mask & SpvMemoryAccessNonPrivatePointerKHRMask)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "NonPrivatePointerKHR must be specified if "
|
||||
"MakePointerVisibleKHR is specified.";
|
||||
<< "MakePointerVisibleKHR is specified.";
|
||||
}
|
||||
|
||||
// Check the associated scope for MakeVisibleKHR.
|
||||
@ -335,8 +345,8 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
|
||||
dst_sc != SpvStorageClassPhysicalStorageBufferEXT) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "NonPrivatePointerKHR requires a pointer in Uniform, "
|
||||
"Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
|
||||
"storage classes.";
|
||||
<< "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
|
||||
<< "storage classes.";
|
||||
}
|
||||
if (src_sc != SpvStorageClassMax && src_sc != SpvStorageClassUniform &&
|
||||
src_sc != SpvStorageClassWorkgroup &&
|
||||
@ -346,17 +356,17 @@ spv_result_t CheckMemoryAccess(ValidationState_t& _, const Instruction* inst,
|
||||
src_sc != SpvStorageClassPhysicalStorageBufferEXT) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "NonPrivatePointerKHR requires a pointer in Uniform, "
|
||||
"Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
|
||||
"storage classes.";
|
||||
<< "Workgroup, CrossWorkgroup, Generic, Image or StorageBuffer "
|
||||
<< "storage classes.";
|
||||
}
|
||||
}
|
||||
|
||||
if (!(mask & SpvMemoryAccessAlignedMask)) {
|
||||
if (src_sc == SpvStorageClassPhysicalStorageBufferEXT ||
|
||||
dst_sc == SpvStorageClassPhysicalStorageBufferEXT) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst) << "Memory accesses with "
|
||||
"PhysicalStorageBufferEXT "
|
||||
"must use Aligned.";
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Memory accesses with PhysicalStorageBufferEXT must use "
|
||||
"Aligned.";
|
||||
}
|
||||
}
|
||||
|
||||
@ -442,15 +452,15 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
// VariablePointersStorageBuffer is implied by VariablePointers.
|
||||
if (pointee->opcode() == SpvOpTypePointer) {
|
||||
if (!_.HasCapability(SpvCapabilityVariablePointersStorageBuffer)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst) << "In Logical addressing, "
|
||||
"variables may not "
|
||||
"allocate a pointer type";
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "In Logical addressing, variables may not allocate a pointer "
|
||||
<< "type";
|
||||
} else if (storage_class != SpvStorageClassFunction &&
|
||||
storage_class != SpvStorageClassPrivate) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "In Logical addressing with variable pointers, variables "
|
||||
"that allocate pointers must be in Function or Private "
|
||||
"storage classes";
|
||||
<< "that allocate pointers must be in Function or Private "
|
||||
<< "storage classes";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -493,10 +503,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
<< "' has illegal type.\n"
|
||||
<< "From Vulkan spec, section 14.5.2:\n"
|
||||
<< "Variables identified with the Uniform storage class are "
|
||||
"used "
|
||||
<< "to access transparent buffer backed resources. Such "
|
||||
"variables "
|
||||
<< "must be typed as OpTypeStruct, or an array of this type";
|
||||
<< "used to access transparent buffer backed resources. Such "
|
||||
<< "variables must be typed as OpTypeStruct, or an array of "
|
||||
<< "this type";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -564,13 +573,13 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpVariable " << inst->id()
|
||||
<< ": expected AliasedPointerEXT or RestrictPointerEXT for "
|
||||
"PhysicalStorageBufferEXT pointer.";
|
||||
<< "PhysicalStorageBufferEXT pointer.";
|
||||
}
|
||||
if (foundAliased && foundRestrict) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpVariable " << inst->id()
|
||||
<< ": can't specify both AliasedPointerEXT and "
|
||||
"RestrictPointerEXT for PhysicalStorageBufferEXT pointer.";
|
||||
<< "RestrictPointerEXT for PhysicalStorageBufferEXT pointer.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -588,9 +597,9 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpVariable, <id> '" << _.getIdName(inst->id())
|
||||
<< "', is attempting to create memory for an illegal type, "
|
||||
"OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray can only "
|
||||
"appear as the final member of an OpTypeStruct, thus cannot "
|
||||
"be instantiated via OpVariable";
|
||||
<< "OpTypeRuntimeArray.\nFor Vulkan OpTypeRuntimeArray can only "
|
||||
<< "appear as the final member of an OpTypeStruct, thus cannot "
|
||||
<< "be instantiated via OpVariable";
|
||||
} else {
|
||||
// A bare variable OpTypeRuntimeArray is allowed in this context, but
|
||||
// still need to check the storage class.
|
||||
@ -599,8 +608,8 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
storage_class != SpvStorageClassUniformConstant) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "For Vulkan with RuntimeDescriptorArrayEXT, a variable "
|
||||
"containing OpTypeRuntimeArray must have storage class of "
|
||||
"StorageBuffer, Uniform, or UniformConstant.";
|
||||
<< "containing OpTypeRuntimeArray must have storage class of "
|
||||
<< "StorageBuffer, Uniform, or UniformConstant.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -610,39 +619,64 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) {
|
||||
// with Block, or it must be in the Uniform storage class and be decorated
|
||||
// as BufferBlock.
|
||||
if (value_type && value_type->opcode() == SpvOpTypeStruct) {
|
||||
bool contains_RTA = false;
|
||||
for (size_t member_type_index = 1;
|
||||
member_type_index < value_type->operands().size();
|
||||
++member_type_index) {
|
||||
const auto member_type_id =
|
||||
value_type->GetOperandAs<uint32_t>(member_type_index);
|
||||
const auto member_type = _.FindDef(member_type_id);
|
||||
if (member_type->opcode() == SpvOpTypeRuntimeArray) {
|
||||
contains_RTA = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (contains_RTA) {
|
||||
if (DoesStructContainRTA(_, value_type)) {
|
||||
if (storage_class == SpvStorageClassStorageBuffer) {
|
||||
if (!_.HasDecoration(value_id, SpvDecorationBlock)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "For Vulkan, an OpTypeStruct variable containing an "
|
||||
"OpTypeRuntimeArray must be decorated with Block if it "
|
||||
"has storage class StorageBuffer.";
|
||||
<< "OpTypeRuntimeArray must be decorated with Block if it "
|
||||
<< "has storage class StorageBuffer.";
|
||||
}
|
||||
} else if (storage_class == SpvStorageClassUniform) {
|
||||
if (!_.HasDecoration(value_id, SpvDecorationBufferBlock)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "For Vulkan, an OpTypeStruct variable containing an "
|
||||
"OpTypeRuntimeArray must be decorated with BufferBlock "
|
||||
"if it has storage class Uniform.";
|
||||
<< "OpTypeRuntimeArray must be decorated with BufferBlock "
|
||||
<< "if it has storage class Uniform.";
|
||||
}
|
||||
} else {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "For Vulkan, OpTypeStruct variables containing "
|
||||
"OpTypeRuntimeArray must have storage class of "
|
||||
"StorageBuffer or Uniform.";
|
||||
<< "OpTypeRuntimeArray must have storage class of "
|
||||
<< "StorageBuffer or Uniform.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WebGPU specific validation rules for OpTypeRuntimeArray
|
||||
if (spvIsWebGPUEnv(_.context()->target_env)) {
|
||||
const auto type_index = 2;
|
||||
const auto value_id = result_type->GetOperandAs<uint32_t>(type_index);
|
||||
auto value_type = _.FindDef(value_id);
|
||||
// OpTypeRuntimeArray should only ever be in an OpTypeStruct,
|
||||
// so should never appear as a bare variable.
|
||||
if (value_type && value_type->opcode() == SpvOpTypeRuntimeArray) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpVariable, <id> '" << _.getIdName(inst->id())
|
||||
<< "', is attempting to create memory for an illegal type, "
|
||||
<< "OpTypeRuntimeArray.\nFor WebGPU OpTypeRuntimeArray can only "
|
||||
<< "appear as the final member of an OpTypeStruct, thus cannot "
|
||||
<< "be instantiated via OpVariable";
|
||||
}
|
||||
|
||||
// If an OpStruct has an OpTypeRuntimeArray somewhere within it, then it
|
||||
// must have the storage class StorageBuffer and be decorated
|
||||
// with Block.
|
||||
if (value_type && value_type->opcode() == SpvOpTypeStruct) {
|
||||
if (DoesStructContainRTA(_, value_type)) {
|
||||
if (storage_class == SpvStorageClassStorageBuffer) {
|
||||
if (!_.HasDecoration(value_id, SpvDecorationBlock)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "For WebGPU, an OpTypeStruct variable containing an "
|
||||
<< "OpTypeRuntimeArray must be decorated with Block if it "
|
||||
<< "has storage class StorageBuffer.";
|
||||
}
|
||||
} else {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "For WebGPU, OpTypeStruct variables containing "
|
||||
<< "OpTypeRuntimeArray must have storage class of "
|
||||
<< "StorageBuffer";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,11 +107,14 @@ spv_result_t ValidateTypeArray(ValidationState_t& _, const Instruction* inst) {
|
||||
<< "' is a void type.";
|
||||
}
|
||||
|
||||
if (spvIsVulkanEnv(_.context()->target_env) &&
|
||||
if ((spvIsVulkanEnv(_.context()->target_env) ||
|
||||
spvIsWebGPUEnv(_.context()->target_env)) &&
|
||||
element_type->opcode() == SpvOpTypeRuntimeArray) {
|
||||
const char* env_text =
|
||||
spvIsVulkanEnv(_.context()->target_env) ? "Vulkan" : "WebGPU";
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeArray Element Type <id> '" << _.getIdName(element_type_id)
|
||||
<< "' is not valid in Vulkan environment.";
|
||||
<< "' is not valid in " << env_text << " environment.";
|
||||
}
|
||||
|
||||
const auto length_index = 2;
|
||||
@ -169,12 +172,15 @@ spv_result_t ValidateTypeRuntimeArray(ValidationState_t& _,
|
||||
<< _.getIdName(element_id) << "' is a void type.";
|
||||
}
|
||||
|
||||
if (spvIsVulkanEnv(_.context()->target_env) &&
|
||||
if ((spvIsVulkanEnv(_.context()->target_env) ||
|
||||
spvIsWebGPUEnv(_.context()->target_env)) &&
|
||||
element_type->opcode() == SpvOpTypeRuntimeArray) {
|
||||
const char* env_text =
|
||||
spvIsVulkanEnv(_.context()->target_env) ? "Vulkan" : "WebGPU";
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "OpTypeRuntimeArray Element Type <id> '"
|
||||
<< _.getIdName(element_id)
|
||||
<< "' is not valid in Vulkan environment.";
|
||||
<< _.getIdName(element_id) << "' is not valid in " << env_text
|
||||
<< " environment.";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
@ -200,11 +206,11 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Structure <id> " << _.getIdName(member_type_id)
|
||||
<< " contains members with BuiltIn decoration. Therefore this "
|
||||
"structure may not be contained as a member of another "
|
||||
"structure "
|
||||
"type. Structure <id> "
|
||||
<< _.getIdName(struct_id) << " contains structure <id> "
|
||||
<< _.getIdName(member_type_id) << ".";
|
||||
<< "structure may not be contained as a member of another "
|
||||
<< "structure "
|
||||
<< "type. Structure <id> " << _.getIdName(struct_id)
|
||||
<< " contains structure <id> " << _.getIdName(member_type_id)
|
||||
<< ".";
|
||||
}
|
||||
if (_.IsForwardPointer(member_type_id)) {
|
||||
// If we're dealing with a forward pointer:
|
||||
@ -223,14 +229,17 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
|
||||
}
|
||||
}
|
||||
|
||||
if (spvIsVulkanEnv(_.context()->target_env) &&
|
||||
if ((spvIsVulkanEnv(_.context()->target_env) ||
|
||||
spvIsWebGPUEnv(_.context()->target_env)) &&
|
||||
member_type->opcode() == SpvOpTypeRuntimeArray) {
|
||||
const bool is_last_member =
|
||||
member_type_index == inst->operands().size() - 1;
|
||||
if (!is_last_member) {
|
||||
const char* env_text =
|
||||
spvIsVulkanEnv(_.context()->target_env) ? "Vulkan" : "WebGPU";
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "In Vulkan, OpTypeRuntimeArray must only be used for the "
|
||||
"last member of an OpTypeStruct";
|
||||
<< "In " << env_text << ", OpTypeRuntimeArray must only be used "
|
||||
<< "for the last member of an OpTypeStruct";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -247,9 +256,9 @@ spv_result_t ValidateTypeStruct(ValidationState_t& _, const Instruction* inst) {
|
||||
if (num_builtin_members > 0 && num_builtin_members != num_struct_members) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "When BuiltIn decoration is applied to a structure-type member, "
|
||||
"all members of that structure type must also be decorated with "
|
||||
"BuiltIn (No allowed mixing of built-in variables and "
|
||||
"non-built-in variables within a single structure). Structure id "
|
||||
<< "all members of that structure type must also be decorated with "
|
||||
<< "BuiltIn (No allowed mixing of built-in variables and "
|
||||
<< "non-built-in variables within a single structure). Structure id "
|
||||
<< struct_id << " does not meet this requirement.";
|
||||
}
|
||||
if (num_builtin_members > 0) {
|
||||
@ -332,7 +341,7 @@ spv_result_t ValidateTypeForwardPointer(ValidationState_t& _,
|
||||
pointer_type_inst->GetOperandAs<uint32_t>(1)) {
|
||||
return _.diag(SPV_ERROR_INVALID_ID, inst)
|
||||
<< "Storage class in OpTypeForwardPointer does not match the "
|
||||
"pointer definition.";
|
||||
<< "pointer definition.";
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
|
@ -872,6 +872,68 @@ TEST_F(ValidateData, vulkan_RTA_not_at_end_of_struct) {
|
||||
"OpTypeStruct %_runtimearr_uint %uint\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, webgpu_RTA_array_at_end_of_struct) {
|
||||
std::string str = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
OpDecorate %array_t ArrayStride 4
|
||||
OpMemberDecorate %struct_t 0 Offset 0
|
||||
OpMemberDecorate %struct_t 1 Offset 4
|
||||
OpDecorate %struct_t Block
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%array_t = OpTypeRuntimeArray %uint_t
|
||||
%struct_t = OpTypeStruct %uint_t %array_t
|
||||
%struct_ptr = OpTypePointer StorageBuffer %struct_t
|
||||
%2 = OpVariable %struct_ptr StorageBuffer
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateData, webgpu_RTA_not_at_end_of_struct) {
|
||||
std::string str = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
OpDecorate %array_t ArrayStride 4
|
||||
OpMemberDecorate %struct_t 0 Offset 0
|
||||
OpMemberDecorate %struct_t 1 Offset 4
|
||||
OpDecorate %struct_t Block
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%array_t = OpTypeRuntimeArray %uint_t
|
||||
%struct_t = OpTypeStruct %array_t %uint_t
|
||||
%struct_ptr = OpTypePointer StorageBuffer %struct_t
|
||||
%2 = OpVariable %struct_ptr StorageBuffer
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(str.c_str(), SPV_ENV_WEBGPU_0);
|
||||
ASSERT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("In WebGPU, OpTypeRuntimeArray must only be used for "
|
||||
"the last member of an OpTypeStruct\n %_struct_3 = "
|
||||
"OpTypeStruct %_runtimearr_uint %uint\n"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace val
|
||||
} // namespace spvtools
|
||||
|
@ -1806,6 +1806,38 @@ OpFunctionEnd
|
||||
"%_ptr_UniformConstant__runtimearr_2 UniformConstant\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAOutsideOfStructBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%sampler_t = OpTypeSampler
|
||||
%array_t = OpTypeRuntimeArray %sampler_t
|
||||
%array_ptr = OpTypePointer UniformConstant %array_t
|
||||
%2 = OpVariable %array_ptr UniformConstant
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"OpVariable, <id> '5[%5]', is attempting to create memory for an "
|
||||
"illegal type, OpTypeRuntimeArray.\nFor WebGPU OpTypeRuntimeArray "
|
||||
"can only appear as the final member of an OpTypeStruct, thus cannot "
|
||||
"be instantiated via OpVariable\n %5 = OpVariable "
|
||||
"%_ptr_UniformConstant__runtimearr_2 UniformConstant\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAOutsideOfStructWithRuntimeDescriptorArrayGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
@ -1890,6 +1922,34 @@ OpFunctionEnd
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAInsideStorageBufferStructGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
OpDecorate %array_t ArrayStride 4
|
||||
OpMemberDecorate %struct_t 0 Offset 0
|
||||
OpDecorate %struct_t Block
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%array_t = OpTypeRuntimeArray %uint_t
|
||||
%struct_t = OpTypeStruct %array_t
|
||||
%struct_ptr = OpTypePointer StorageBuffer %struct_t
|
||||
%2 = OpVariable %struct_ptr StorageBuffer
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAInsideWrongStorageClassStructBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
@ -1919,6 +1979,36 @@ OpFunctionEnd
|
||||
"OpVariable %_ptr_Workgroup__struct_4 Workgroup\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAInsideWrongStorageClassStructBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%array_t = OpTypeRuntimeArray %uint_t
|
||||
%struct_t = OpTypeStruct %array_t
|
||||
%struct_ptr = OpTypePointer Workgroup %struct_t
|
||||
%2 = OpVariable %struct_ptr Workgroup
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, OpTypeStruct variables containing "
|
||||
"OpTypeRuntimeArray must have storage class of StorageBuffer\n "
|
||||
" %6 = OpVariable %_ptr_Workgroup__struct_4 Workgroup\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAInsideStorageBufferStructWithoutBlockBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
@ -1947,6 +2037,36 @@ OpFunctionEnd
|
||||
"%_ptr_StorageBuffer__struct_4 StorageBuffer\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAInsideStorageBufferStructWithoutBlockBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%array_t = OpTypeRuntimeArray %uint_t
|
||||
%struct_t = OpTypeStruct %array_t
|
||||
%struct_ptr = OpTypePointer StorageBuffer %struct_t
|
||||
%2 = OpVariable %struct_ptr StorageBuffer
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, an OpTypeStruct variable containing an "
|
||||
"OpTypeRuntimeArray must be decorated with Block if it "
|
||||
"has storage class StorageBuffer.\n %6 = OpVariable "
|
||||
"%_ptr_StorageBuffer__struct_4 StorageBuffer\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAInsideUniformStructGood) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
@ -1973,6 +2093,39 @@ OpFunctionEnd
|
||||
EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_VULKAN_1_1));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAInsideUniformStructBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
OpDecorate %array_t ArrayStride 4
|
||||
OpMemberDecorate %struct_t 0 Offset 0
|
||||
OpDecorate %struct_t BufferBlock
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%array_t = OpTypeRuntimeArray %uint_t
|
||||
%struct_t = OpTypeStruct %array_t
|
||||
%struct_ptr = OpTypePointer Uniform %struct_t
|
||||
%2 = OpVariable %struct_ptr Uniform
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("For WebGPU, OpTypeStruct variables containing "
|
||||
"OpTypeRuntimeArray must have storage class of StorageBuffer\n "
|
||||
" %6 = OpVariable %_ptr_Uniform__struct_3 Uniform\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAInsideUniformStructWithoutBufferBlockBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
@ -2030,6 +2183,37 @@ OpFunctionEnd
|
||||
"OpTypeRuntimeArray %_runtimearr_2\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAInsideRTABad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%sampler_t = OpTypeSampler
|
||||
%inner_array_t = OpTypeRuntimeArray %sampler_t
|
||||
%array_t = OpTypeRuntimeArray %inner_array_t
|
||||
%array_ptr = OpTypePointer UniformConstant %array_t
|
||||
%2 = OpVariable %array_ptr UniformConstant
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr(
|
||||
"OpTypeRuntimeArray Element Type <id> '3[%_runtimearr_2]' is not "
|
||||
"valid in WebGPU environment.\n %_runtimearr__runtimearr_2 = "
|
||||
"OpTypeRuntimeArray %_runtimearr_2\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAInsideRTAWithRuntimeDescriptorArrayBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability RuntimeDescriptorArrayEXT
|
||||
@ -2190,6 +2374,38 @@ OpFunctionEnd
|
||||
"OpTypeArray %_runtimearr_4 %uint_1\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, WebGPURTAInsideArrayBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability Shader
|
||||
OpCapability VulkanMemoryModelKHR
|
||||
OpExtension "SPV_KHR_vulkan_memory_model"
|
||||
OpMemoryModel Logical VulkanKHR
|
||||
OpEntryPoint Fragment %func "func"
|
||||
OpExecutionMode %func OriginUpperLeft
|
||||
%uint_t = OpTypeInt 32 0
|
||||
%dim = OpConstant %uint_t 1
|
||||
%sampler_t = OpTypeSampler
|
||||
%inner_array_t = OpTypeRuntimeArray %sampler_t
|
||||
%array_t = OpTypeArray %inner_array_t %dim
|
||||
%array_ptr = OpTypePointer UniformConstant %array_t
|
||||
%2 = OpVariable %array_ptr UniformConstant
|
||||
%void = OpTypeVoid
|
||||
%func_t = OpTypeFunction %void
|
||||
%func = OpFunction %void None %func_t
|
||||
%1 = OpLabel
|
||||
OpReturn
|
||||
OpFunctionEnd
|
||||
)";
|
||||
|
||||
CompileSuccessfully(spirv.c_str(), SPV_ENV_WEBGPU_0);
|
||||
EXPECT_EQ(SPV_ERROR_INVALID_ID, ValidateInstructions(SPV_ENV_WEBGPU_0));
|
||||
EXPECT_THAT(
|
||||
getDiagnosticString(),
|
||||
HasSubstr("OpTypeArray Element Type <id> '5[%_runtimearr_4]' is not "
|
||||
"valid in WebGPU environment.\n %_arr__runtimearr_4_uint_1 = "
|
||||
"OpTypeArray %_runtimearr_4 %uint_1\n"));
|
||||
}
|
||||
|
||||
TEST_F(ValidateMemory, VulkanRTAInsideArrayWithRuntimeDescriptorArrayBad) {
|
||||
std::string spirv = R"(
|
||||
OpCapability RuntimeDescriptorArrayEXT
|
||||
|
Loading…
Reference in New Issue
Block a user