| // Copyright (c) 2017 Google Inc. | |
| // Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights | |
| // reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // Validates correctness of image instructions. | |
| #include <string> | |
| #include "source/opcode.h" | |
| #include "source/spirv_constant.h" | |
| #include "source/spirv_target_env.h" | |
| #include "source/util/bitutils.h" | |
| #include "source/val/instruction.h" | |
| #include "source/val/validate.h" | |
| #include "source/val/validate_scopes.h" | |
| #include "source/val/validation_state.h" | |
| namespace spvtools { | |
| namespace val { | |
| namespace { | |
| // Performs compile time check that all spv::ImageOperandsMask::XXX cases are | |
| // handled in this module. If spv::ImageOperandsMask::XXX list changes, this | |
| // function will fail the build. For all other purposes this is a placeholder | |
| // function. | |
| bool CheckAllImageOperandsHandled() { | |
| spv::ImageOperandsMask enum_val = spv::ImageOperandsMask::Bias; | |
| // Some improvised code to prevent the compiler from considering enum_val | |
| // constant and optimizing the switch away. | |
| uint32_t stack_var = 0; | |
| if (reinterpret_cast<uintptr_t>(&stack_var) % 256) | |
| enum_val = spv::ImageOperandsMask::Lod; | |
| switch (enum_val) { | |
| // Please update the validation rules in this module if you are changing | |
| // the list of image operands, and add new enum values to this switch. | |
| case spv::ImageOperandsMask::MaskNone: | |
| return false; | |
| case spv::ImageOperandsMask::Bias: | |
| case spv::ImageOperandsMask::Lod: | |
| case spv::ImageOperandsMask::Grad: | |
| case spv::ImageOperandsMask::ConstOffset: | |
| case spv::ImageOperandsMask::Offset: | |
| case spv::ImageOperandsMask::ConstOffsets: | |
| case spv::ImageOperandsMask::Sample: | |
| case spv::ImageOperandsMask::MinLod: | |
| // TODO(dneto): Support image operands related to the Vulkan memory model. | |
| // https://gitlab.khronos.org/spirv/spirv-tools/issues/32 | |
| case spv::ImageOperandsMask::MakeTexelAvailableKHR: | |
| case spv::ImageOperandsMask::MakeTexelVisibleKHR: | |
| case spv::ImageOperandsMask::NonPrivateTexelKHR: | |
| case spv::ImageOperandsMask::VolatileTexelKHR: | |
| case spv::ImageOperandsMask::SignExtend: | |
| case spv::ImageOperandsMask::ZeroExtend: | |
| // TODO(jaebaek): Move this line properly after handling image offsets | |
| // operand. This line temporarily fixes CI failure that | |
| // blocks other PRs. | |
| // https://github.com/KhronosGroup/SPIRV-Tools/issues/4565 | |
| case spv::ImageOperandsMask::Offsets: | |
| case spv::ImageOperandsMask::Nontemporal: | |
| return true; | |
| } | |
| return false; | |
| } | |
| // Used by GetImageTypeInfo. See OpTypeImage spec for more information. | |
| struct ImageTypeInfo { | |
| uint32_t sampled_type = 0; | |
| spv::Dim dim = spv::Dim::Max; | |
| uint32_t depth = 0; | |
| uint32_t arrayed = 0; | |
| uint32_t multisampled = 0; | |
| uint32_t sampled = 0; | |
| spv::ImageFormat format = spv::ImageFormat::Max; | |
| spv::AccessQualifier access_qualifier = spv::AccessQualifier::Max; | |
| }; | |
| // Provides information on image type. |id| should be object of either | |
| // OpTypeImage or OpTypeSampledImage type. Returns false in case of failure | |
| // (not a valid id, failed to parse the instruction, etc). | |
| bool GetImageTypeInfo(const ValidationState_t& _, uint32_t id, | |
| ImageTypeInfo* info) { | |
| if (!id || !info) return false; | |
| const Instruction* inst = _.FindDef(id); | |
| assert(inst); | |
| if (inst->opcode() == spv::Op::OpTypeSampledImage) { | |
| inst = _.FindDef(inst->word(2)); | |
| assert(inst); | |
| } | |
| if (inst->opcode() != spv::Op::OpTypeImage) return false; | |
| const size_t num_words = inst->words().size(); | |
| if (num_words != 9 && num_words != 10) return false; | |
| info->sampled_type = inst->word(2); | |
| info->dim = static_cast<spv::Dim>(inst->word(3)); | |
| info->depth = inst->word(4); | |
| info->arrayed = inst->word(5); | |
| info->multisampled = inst->word(6); | |
| info->sampled = inst->word(7); | |
| info->format = static_cast<spv::ImageFormat>(inst->word(8)); | |
| info->access_qualifier = | |
| num_words < 10 ? spv::AccessQualifier::Max | |
| : static_cast<spv::AccessQualifier>(inst->word(9)); | |
| return true; | |
| } | |
| bool IsImplicitLod(spv::Op opcode) { | |
| switch (opcode) { | |
| case spv::Op::OpImageSampleImplicitLod: | |
| case spv::Op::OpImageSampleDrefImplicitLod: | |
| case spv::Op::OpImageSampleProjImplicitLod: | |
| case spv::Op::OpImageSampleProjDrefImplicitLod: | |
| case spv::Op::OpImageSparseSampleImplicitLod: | |
| case spv::Op::OpImageSparseSampleDrefImplicitLod: | |
| case spv::Op::OpImageSparseSampleProjImplicitLod: | |
| case spv::Op::OpImageSparseSampleProjDrefImplicitLod: | |
| return true; | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| bool IsExplicitLod(spv::Op opcode) { | |
| switch (opcode) { | |
| case spv::Op::OpImageSampleExplicitLod: | |
| case spv::Op::OpImageSampleDrefExplicitLod: | |
| case spv::Op::OpImageSampleProjExplicitLod: | |
| case spv::Op::OpImageSampleProjDrefExplicitLod: | |
| case spv::Op::OpImageSparseSampleExplicitLod: | |
| case spv::Op::OpImageSparseSampleDrefExplicitLod: | |
| case spv::Op::OpImageSparseSampleProjExplicitLod: | |
| case spv::Op::OpImageSparseSampleProjDrefExplicitLod: | |
| return true; | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| bool IsValidLodOperand(const ValidationState_t& _, spv::Op opcode) { | |
| switch (opcode) { | |
| case spv::Op::OpImageRead: | |
| case spv::Op::OpImageWrite: | |
| case spv::Op::OpImageSparseRead: | |
| return _.HasCapability(spv::Capability::ImageReadWriteLodAMD); | |
| default: | |
| return IsExplicitLod(opcode); | |
| } | |
| } | |
| bool IsValidGatherLodBiasAMD(const ValidationState_t& _, spv::Op opcode) { | |
| switch (opcode) { | |
| case spv::Op::OpImageGather: | |
| case spv::Op::OpImageSparseGather: | |
| return _.HasCapability(spv::Capability::ImageGatherBiasLodAMD); | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| // Returns true if the opcode is a Image instruction which applies | |
| // homogenous projection to the coordinates. | |
| bool IsProj(spv::Op opcode) { | |
| switch (opcode) { | |
| case spv::Op::OpImageSampleProjImplicitLod: | |
| case spv::Op::OpImageSampleProjDrefImplicitLod: | |
| case spv::Op::OpImageSparseSampleProjImplicitLod: | |
| case spv::Op::OpImageSparseSampleProjDrefImplicitLod: | |
| case spv::Op::OpImageSampleProjExplicitLod: | |
| case spv::Op::OpImageSampleProjDrefExplicitLod: | |
| case spv::Op::OpImageSparseSampleProjExplicitLod: | |
| case spv::Op::OpImageSparseSampleProjDrefExplicitLod: | |
| return true; | |
| default: | |
| break; | |
| } | |
| return false; | |
| } | |
| // Returns the number of components in a coordinate used to access a texel in | |
| // a single plane of an image with the given parameters. | |
| uint32_t GetPlaneCoordSize(const ImageTypeInfo& info) { | |
| uint32_t plane_size = 0; | |
| // If this switch breaks your build, please add new values below. | |
| switch (info.dim) { | |
| case spv::Dim::Dim1D: | |
| case spv::Dim::Buffer: | |
| plane_size = 1; | |
| break; | |
| case spv::Dim::Dim2D: | |
| case spv::Dim::Rect: | |
| case spv::Dim::SubpassData: | |
| case spv::Dim::TileImageDataEXT: | |
| plane_size = 2; | |
| break; | |
| case spv::Dim::Dim3D: | |
| case spv::Dim::Cube: | |
| // For Cube direction vector is used instead of UV. | |
| plane_size = 3; | |
| break; | |
| case spv::Dim::Max: | |
| default: | |
| assert(0); | |
| break; | |
| } | |
| return plane_size; | |
| } | |
| // Returns minimal number of coordinates based on image dim, arrayed and whether | |
| // the instruction uses projection coordinates. | |
| uint32_t GetMinCoordSize(spv::Op opcode, const ImageTypeInfo& info) { | |
| if (info.dim == spv::Dim::Cube && | |
| (opcode == spv::Op::OpImageRead || opcode == spv::Op::OpImageWrite || | |
| opcode == spv::Op::OpImageSparseRead)) { | |
| // These opcodes use UV for Cube, not direction vector. | |
| return 3; | |
| } | |
| return GetPlaneCoordSize(info) + info.arrayed + (IsProj(opcode) ? 1 : 0); | |
| } | |
| // Checks ImageOperand bitfield and respective operands. | |
| // word_index is the index of the first word after the image-operand mask word. | |
| spv_result_t ValidateImageOperands(ValidationState_t& _, | |
| const Instruction* inst, | |
| const ImageTypeInfo& info, | |
| uint32_t word_index) { | |
| static const bool kAllImageOperandsHandled = CheckAllImageOperandsHandled(); | |
| (void)kAllImageOperandsHandled; | |
| const spv::Op opcode = inst->opcode(); | |
| const size_t num_words = inst->words().size(); | |
| const bool have_explicit_mask = (word_index - 1 < num_words); | |
| const uint32_t mask = have_explicit_mask ? inst->word(word_index - 1) : 0u; | |
| if (have_explicit_mask) { | |
| // NonPrivate, Volatile, SignExtend, ZeroExtend take no operand words. | |
| const uint32_t mask_bits_having_operands = | |
| mask & ~uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR | | |
| spv::ImageOperandsMask::VolatileTexelKHR | | |
| spv::ImageOperandsMask::SignExtend | | |
| spv::ImageOperandsMask::ZeroExtend | | |
| spv::ImageOperandsMask::Nontemporal); | |
| size_t expected_num_image_operand_words = | |
| spvtools::utils::CountSetBits(mask_bits_having_operands); | |
| if (mask & uint32_t(spv::ImageOperandsMask::Grad)) { | |
| // Grad uses two words. | |
| ++expected_num_image_operand_words; | |
| } | |
| if (expected_num_image_operand_words != num_words - word_index) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Number of image operand ids doesn't correspond to the bit " | |
| "mask"; | |
| } | |
| } else if (num_words != word_index - 1) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Number of image operand ids doesn't correspond to the bit mask"; | |
| } | |
| if (info.multisampled & | |
| (0 == (mask & uint32_t(spv::ImageOperandsMask::Sample)))) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand Sample is required for operation on " | |
| "multi-sampled image"; | |
| } | |
| // After this point, only set bits in the image operands mask can cause | |
| // the module to be invalid. | |
| if (mask == 0) return SPV_SUCCESS; | |
| if (spvtools::utils::CountSetBits( | |
| mask & uint32_t(spv::ImageOperandsMask::Offset | | |
| spv::ImageOperandsMask::ConstOffset | | |
| spv::ImageOperandsMask::ConstOffsets | | |
| spv::ImageOperandsMask::Offsets)) > 1) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(4662) | |
| << "Image Operands Offset, ConstOffset, ConstOffsets, Offsets " | |
| "cannot be used together"; | |
| } | |
| const bool is_implicit_lod = IsImplicitLod(opcode); | |
| const bool is_explicit_lod = IsExplicitLod(opcode); | |
| const bool is_valid_lod_operand = IsValidLodOperand(_, opcode); | |
| const bool is_valid_gather_lod_bias_amd = IsValidGatherLodBiasAMD(_, opcode); | |
| // The checks should be done in the order of definition of OperandImage. | |
| if (mask & uint32_t(spv::ImageOperandsMask::Bias)) { | |
| if (!is_implicit_lod && !is_valid_gather_lod_bias_amd) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand Bias can only be used with ImplicitLod opcodes"; | |
| } | |
| const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); | |
| if (!_.IsFloatScalarType(type_id)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand Bias to be float scalar"; | |
| } | |
| if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && | |
| info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand Bias requires 'Dim' parameter to be 1D, 2D, 3D " | |
| "or Cube"; | |
| } | |
| // Multisampled is already checked. | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::Lod)) { | |
| if (!is_valid_lod_operand && opcode != spv::Op::OpImageFetch && | |
| opcode != spv::Op::OpImageSparseFetch && | |
| !is_valid_gather_lod_bias_amd) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand Lod can only be used with ExplicitLod opcodes " | |
| << "and OpImageFetch"; | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::Grad)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand bits Lod and Grad cannot be set at the same " | |
| "time"; | |
| } | |
| const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); | |
| if (is_explicit_lod || is_valid_gather_lod_bias_amd) { | |
| if (!_.IsFloatScalarType(type_id)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand Lod to be float scalar when used " | |
| << "with ExplicitLod"; | |
| } | |
| } else { | |
| if (!_.IsIntScalarType(type_id)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand Lod to be int scalar when used with " | |
| << "OpImageFetch"; | |
| } | |
| } | |
| if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && | |
| info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand Lod requires 'Dim' parameter to be 1D, 2D, 3D " | |
| "or Cube"; | |
| } | |
| // Multisampled is already checked. | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::Grad)) { | |
| if (!is_explicit_lod) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand Grad can only be used with ExplicitLod opcodes"; | |
| } | |
| const uint32_t dx_type_id = _.GetTypeId(inst->word(word_index++)); | |
| const uint32_t dy_type_id = _.GetTypeId(inst->word(word_index++)); | |
| if (!_.IsFloatScalarOrVectorType(dx_type_id) || | |
| !_.IsFloatScalarOrVectorType(dy_type_id)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected both Image Operand Grad ids to be float scalars or " | |
| << "vectors"; | |
| } | |
| const uint32_t plane_size = GetPlaneCoordSize(info); | |
| const uint32_t dx_size = _.GetDimension(dx_type_id); | |
| const uint32_t dy_size = _.GetDimension(dy_type_id); | |
| if (plane_size != dx_size) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand Grad dx to have " << plane_size | |
| << " components, but given " << dx_size; | |
| } | |
| if (plane_size != dy_size) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand Grad dy to have " << plane_size | |
| << " components, but given " << dy_size; | |
| } | |
| // Multisampled is already checked. | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) { | |
| if (info.dim == spv::Dim::Cube) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand ConstOffset cannot be used with Cube Image " | |
| "'Dim'"; | |
| } | |
| const uint32_t id = inst->word(word_index++); | |
| const uint32_t type_id = _.GetTypeId(id); | |
| if (!_.IsIntScalarOrVectorType(type_id)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand ConstOffset to be int scalar or " | |
| << "vector"; | |
| } | |
| if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand ConstOffset to be a const object"; | |
| } | |
| const uint32_t plane_size = GetPlaneCoordSize(info); | |
| const uint32_t offset_size = _.GetDimension(type_id); | |
| if (plane_size != offset_size) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand ConstOffset to have " << plane_size | |
| << " components, but given " << offset_size; | |
| } | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::Offset)) { | |
| if (info.dim == spv::Dim::Cube) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand Offset cannot be used with Cube Image 'Dim'"; | |
| } | |
| const uint32_t id = inst->word(word_index++); | |
| const uint32_t type_id = _.GetTypeId(id); | |
| if (!_.IsIntScalarOrVectorType(type_id)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand Offset to be int scalar or " | |
| << "vector"; | |
| } | |
| const uint32_t plane_size = GetPlaneCoordSize(info); | |
| const uint32_t offset_size = _.GetDimension(type_id); | |
| if (plane_size != offset_size) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand Offset to have " << plane_size | |
| << " components, but given " << offset_size; | |
| } | |
| if (!_.options()->before_hlsl_legalization && | |
| spvIsVulkanEnv(_.context()->target_env)) { | |
| if (opcode != spv::Op::OpImageGather && | |
| opcode != spv::Op::OpImageDrefGather && | |
| opcode != spv::Op::OpImageSparseGather && | |
| opcode != spv::Op::OpImageSparseDrefGather) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(4663) | |
| << "Image Operand Offset can only be used with " | |
| "OpImage*Gather operations"; | |
| } | |
| } | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::ConstOffsets)) { | |
| if (opcode != spv::Op::OpImageGather && | |
| opcode != spv::Op::OpImageDrefGather && | |
| opcode != spv::Op::OpImageSparseGather && | |
| opcode != spv::Op::OpImageSparseDrefGather) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand ConstOffsets can only be used with " | |
| "OpImageGather and OpImageDrefGather"; | |
| } | |
| if (info.dim == spv::Dim::Cube) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand ConstOffsets cannot be used with Cube Image " | |
| "'Dim'"; | |
| } | |
| const uint32_t id = inst->word(word_index++); | |
| const uint32_t type_id = _.GetTypeId(id); | |
| const Instruction* type_inst = _.FindDef(type_id); | |
| assert(type_inst); | |
| if (type_inst->opcode() != spv::Op::OpTypeArray) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand ConstOffsets to be an array of size 4"; | |
| } | |
| uint64_t array_size = 0; | |
| if (!_.GetConstantValUint64(type_inst->word(3), &array_size)) { | |
| assert(0 && "Array type definition is corrupt"); | |
| } | |
| if (array_size != 4) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand ConstOffsets to be an array of size 4"; | |
| } | |
| const uint32_t component_type = type_inst->word(2); | |
| if (!_.IsIntVectorType(component_type) || | |
| _.GetDimension(component_type) != 2) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand ConstOffsets array components to be " | |
| "int vectors of size 2"; | |
| } | |
| if (!spvOpcodeIsConstant(_.GetIdOpcode(id))) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand ConstOffsets to be a const object"; | |
| } | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::Sample)) { | |
| if (opcode != spv::Op::OpImageFetch && opcode != spv::Op::OpImageRead && | |
| opcode != spv::Op::OpImageWrite && | |
| opcode != spv::Op::OpImageSparseFetch && | |
| opcode != spv::Op::OpImageSparseRead) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand Sample can only be used with OpImageFetch, " | |
| << "OpImageRead, OpImageWrite, OpImageSparseFetch and " | |
| << "OpImageSparseRead"; | |
| } | |
| if (info.multisampled == 0) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand Sample requires non-zero 'MS' parameter"; | |
| } | |
| const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); | |
| if (!_.IsIntScalarType(type_id)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand Sample to be int scalar"; | |
| } | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::MinLod)) { | |
| if (!is_implicit_lod && !(mask & uint32_t(spv::ImageOperandsMask::Grad))) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand MinLod can only be used with ImplicitLod " | |
| << "opcodes or together with Image Operand Grad"; | |
| } | |
| const uint32_t type_id = _.GetTypeId(inst->word(word_index++)); | |
| if (!_.IsFloatScalarType(type_id)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image Operand MinLod to be float scalar"; | |
| } | |
| if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && | |
| info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand MinLod requires 'Dim' parameter to be 1D, 2D, " | |
| "3D or Cube"; | |
| } | |
| if (info.multisampled != 0) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand MinLod requires 'MS' parameter to be 0"; | |
| } | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::MakeTexelAvailableKHR)) { | |
| // Checked elsewhere: capability and memory model are correct. | |
| if (opcode != spv::Op::OpImageWrite) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand MakeTexelAvailableKHR can only be used with Op" | |
| << spvOpcodeString(spv::Op::OpImageWrite) << ": Op" | |
| << spvOpcodeString(opcode); | |
| } | |
| if (!(mask & uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR))) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand MakeTexelAvailableKHR requires " | |
| "NonPrivateTexelKHR is also specified: Op" | |
| << spvOpcodeString(opcode); | |
| } | |
| const auto available_scope = inst->word(word_index++); | |
| if (auto error = ValidateMemoryScope(_, inst, available_scope)) | |
| return error; | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::MakeTexelVisibleKHR)) { | |
| // Checked elsewhere: capability and memory model are correct. | |
| if (opcode != spv::Op::OpImageRead && | |
| opcode != spv::Op::OpImageSparseRead) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand MakeTexelVisibleKHR can only be used with Op" | |
| << spvOpcodeString(spv::Op::OpImageRead) << " or Op" | |
| << spvOpcodeString(spv::Op::OpImageSparseRead) << ": Op" | |
| << spvOpcodeString(opcode); | |
| } | |
| if (!(mask & uint32_t(spv::ImageOperandsMask::NonPrivateTexelKHR))) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Operand MakeTexelVisibleKHR requires NonPrivateTexelKHR " | |
| "is also specified: Op" | |
| << spvOpcodeString(opcode); | |
| } | |
| const auto visible_scope = inst->word(word_index++); | |
| if (auto error = ValidateMemoryScope(_, inst, visible_scope)) return error; | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::SignExtend)) { | |
| // Checked elsewhere: SPIR-V 1.4 version or later. | |
| // "The texel value is converted to the target value via sign extension. | |
| // Only valid when the texel type is a scalar or vector of integer type." | |
| // | |
| // We don't have enough information to know what the texel type is. | |
| // In OpenCL, knowledge is deferred until runtime: the image SampledType is | |
| // void, and the Format is Unknown. | |
| // In Vulkan, the texel type is only known in all cases by the pipeline | |
| // setup. | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::ZeroExtend)) { | |
| // Checked elsewhere: SPIR-V 1.4 version or later. | |
| // "The texel value is converted to the target value via zero extension. | |
| // Only valid when the texel type is a scalar or vector of integer type." | |
| // | |
| // We don't have enough information to know what the texel type is. | |
| // In OpenCL, knowledge is deferred until runtime: the image SampledType is | |
| // void, and the Format is Unknown. | |
| // In Vulkan, the texel type is only known in all cases by the pipeline | |
| // setup. | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::Offsets)) { | |
| // TODO: add validation | |
| } | |
| if (mask & uint32_t(spv::ImageOperandsMask::Nontemporal)) { | |
| // Checked elsewhere: SPIR-V 1.6 version or later. | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| // Validate OpImage*Proj* instructions | |
| spv_result_t ValidateImageProj(ValidationState_t& _, const Instruction* inst, | |
| const ImageTypeInfo& info) { | |
| if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && | |
| info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Rect) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'Dim' parameter to be 1D, 2D, 3D or Rect"; | |
| } | |
| if (info.multisampled != 0) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'MS' parameter to be 0"; | |
| } | |
| if (info.arrayed != 0) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'arrayed' parameter to be 0"; | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| // Validate OpImage*Read and OpImage*Write instructions | |
| spv_result_t ValidateImageReadWrite(ValidationState_t& _, | |
| const Instruction* inst, | |
| const ImageTypeInfo& info) { | |
| if (info.sampled == 2) { | |
| if (info.dim == spv::Dim::Dim1D && | |
| !_.HasCapability(spv::Capability::Image1D)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Capability Image1D is required to access storage image"; | |
| } else if (info.dim == spv::Dim::Rect && | |
| !_.HasCapability(spv::Capability::ImageRect)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Capability ImageRect is required to access storage image"; | |
| } else if (info.dim == spv::Dim::Buffer && | |
| !_.HasCapability(spv::Capability::ImageBuffer)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Capability ImageBuffer is required to access storage image"; | |
| } else if (info.dim == spv::Dim::Cube && info.arrayed == 1 && | |
| !_.HasCapability(spv::Capability::ImageCubeArray)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Capability ImageCubeArray is required to access " | |
| << "storage image"; | |
| } | |
| if (info.multisampled == 1 && | |
| !_.HasCapability(spv::Capability::ImageMSArray)) { | |
| #if 0 | |
| // TODO(atgoo@github.com) The description of this rule in the spec | |
| // is unclear and Glslang doesn't declare ImageMSArray. Need to clarify | |
| // and reenable. | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Capability ImageMSArray is required to access storage " | |
| << "image"; | |
| #endif | |
| } | |
| } else if (info.sampled != 0) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'Sampled' parameter to be 0 or 2"; | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| // Returns true if opcode is *ImageSparse*, false otherwise. | |
| bool IsSparse(spv::Op opcode) { | |
| switch (opcode) { | |
| case spv::Op::OpImageSparseSampleImplicitLod: | |
| case spv::Op::OpImageSparseSampleExplicitLod: | |
| case spv::Op::OpImageSparseSampleDrefImplicitLod: | |
| case spv::Op::OpImageSparseSampleDrefExplicitLod: | |
| case spv::Op::OpImageSparseSampleProjImplicitLod: | |
| case spv::Op::OpImageSparseSampleProjExplicitLod: | |
| case spv::Op::OpImageSparseSampleProjDrefImplicitLod: | |
| case spv::Op::OpImageSparseSampleProjDrefExplicitLod: | |
| case spv::Op::OpImageSparseFetch: | |
| case spv::Op::OpImageSparseGather: | |
| case spv::Op::OpImageSparseDrefGather: | |
| case spv::Op::OpImageSparseTexelsResident: | |
| case spv::Op::OpImageSparseRead: { | |
| return true; | |
| } | |
| default: { return false; } | |
| } | |
| return false; | |
| } | |
| // Checks sparse image opcode result type and returns the second struct member. | |
| // Returns inst.type_id for non-sparse image opcodes. | |
| // Not valid for sparse image opcodes which do not return a struct. | |
| spv_result_t GetActualResultType(ValidationState_t& _, const Instruction* inst, | |
| uint32_t* actual_result_type) { | |
| const spv::Op opcode = inst->opcode(); | |
| if (IsSparse(opcode)) { | |
| const Instruction* const type_inst = _.FindDef(inst->type_id()); | |
| assert(type_inst); | |
| if (!type_inst || type_inst->opcode() != spv::Op::OpTypeStruct) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to be OpTypeStruct"; | |
| } | |
| if (type_inst->words().size() != 4 || | |
| !_.IsIntScalarType(type_inst->word(2))) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to be a struct containing an int " | |
| "scalar and a texel"; | |
| } | |
| *actual_result_type = type_inst->word(3); | |
| } else { | |
| *actual_result_type = inst->type_id(); | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| // Returns a string describing actual result type of an opcode. | |
| // Not valid for sparse image opcodes which do not return a struct. | |
| const char* GetActualResultTypeStr(spv::Op opcode) { | |
| if (IsSparse(opcode)) return "Result Type's second member"; | |
| return "Result Type"; | |
| } | |
| spv_result_t ValidateTypeImage(ValidationState_t& _, const Instruction* inst) { | |
| assert(inst->type_id() == 0); | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, inst->word(1), &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| if (_.IsIntScalarType(info.sampled_type) && | |
| (64 == _.GetBitWidth(info.sampled_type)) && | |
| !_.HasCapability(spv::Capability::Int64ImageEXT)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Capability Int64ImageEXT is required when using Sampled Type of " | |
| "64-bit int"; | |
| } | |
| const auto target_env = _.context()->target_env; | |
| if (spvIsVulkanEnv(target_env)) { | |
| if ((!_.IsFloatScalarType(info.sampled_type) && | |
| !_.IsIntScalarType(info.sampled_type)) || | |
| ((32 != _.GetBitWidth(info.sampled_type)) && | |
| (64 != _.GetBitWidth(info.sampled_type))) || | |
| ((64 == _.GetBitWidth(info.sampled_type)) && | |
| _.IsFloatScalarType(info.sampled_type))) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(4656) | |
| << "Expected Sampled Type to be a 32-bit int, 64-bit int or " | |
| "32-bit float scalar type for Vulkan environment"; | |
| } | |
| } else if (spvIsOpenCLEnv(target_env)) { | |
| if (!_.IsVoidType(info.sampled_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Sampled Type must be OpTypeVoid in the OpenCL environment."; | |
| } | |
| } else { | |
| const spv::Op sampled_type_opcode = _.GetIdOpcode(info.sampled_type); | |
| if (sampled_type_opcode != spv::Op::OpTypeVoid && | |
| sampled_type_opcode != spv::Op::OpTypeInt && | |
| sampled_type_opcode != spv::Op::OpTypeFloat) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Sampled Type to be either void or" | |
| << " numerical scalar type"; | |
| } | |
| } | |
| // Universal checks on image type operands | |
| // Dim and Format and Access Qualifier are checked elsewhere. | |
| if (info.depth > 2) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Invalid Depth " << info.depth << " (must be 0, 1 or 2)"; | |
| } | |
| if (info.arrayed > 1) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Invalid Arrayed " << info.arrayed << " (must be 0 or 1)"; | |
| } | |
| if (info.multisampled > 1) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Invalid MS " << info.multisampled << " (must be 0 or 1)"; | |
| } | |
| if (info.sampled > 2) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Invalid Sampled " << info.sampled << " (must be 0, 1 or 2)"; | |
| } | |
| if (info.dim == spv::Dim::SubpassData) { | |
| if (info.sampled != 2) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(6214) << "Dim SubpassData requires Sampled to be 2"; | |
| } | |
| if (info.format != spv::ImageFormat::Unknown) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Dim SubpassData requires format Unknown"; | |
| } | |
| } else if (info.dim == spv::Dim::TileImageDataEXT) { | |
| if (_.IsVoidType(info.sampled_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Dim TileImageDataEXT requires Sampled Type to be not " | |
| "OpTypeVoid"; | |
| } | |
| if (info.sampled != 2) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Dim TileImageDataEXT requires Sampled to be 2"; | |
| } | |
| if (info.format != spv::ImageFormat::Unknown) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Dim TileImageDataEXT requires format Unknown"; | |
| } | |
| if (info.depth != 0) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Dim TileImageDataEXT requires Depth to be 0"; | |
| } | |
| if (info.arrayed != 0) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Dim TileImageDataEXT requires Arrayed to be 0"; | |
| } | |
| } else { | |
| if (info.multisampled && (info.sampled == 2) && | |
| !_.HasCapability(spv::Capability::StorageImageMultisample)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Capability StorageImageMultisample is required when using " | |
| "multisampled storage image"; | |
| } | |
| } | |
| if (spvIsOpenCLEnv(target_env)) { | |
| if ((info.arrayed == 1) && (info.dim != spv::Dim::Dim1D) && | |
| (info.dim != spv::Dim::Dim2D)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "In the OpenCL environment, Arrayed may only be set to 1 " | |
| << "when Dim is either 1D or 2D."; | |
| } | |
| if (info.multisampled != 0) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "MS must be 0 in the OpenCL environment."; | |
| } | |
| if (info.sampled != 0) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Sampled must be 0 in the OpenCL environment."; | |
| } | |
| if (info.access_qualifier == spv::AccessQualifier::Max) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "In the OpenCL environment, the optional Access Qualifier" | |
| << " must be present."; | |
| } | |
| } | |
| if (spvIsVulkanEnv(target_env)) { | |
| if (info.sampled == 0) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(4657) | |
| << "Sampled must be 1 or 2 in the Vulkan environment."; | |
| } | |
| if (info.dim == spv::Dim::SubpassData && info.arrayed != 0) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(6214) << "Dim SubpassData requires Arrayed to be 0"; | |
| } | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateTypeSampledImage(ValidationState_t& _, | |
| const Instruction* inst) { | |
| const uint32_t image_type = inst->word(2); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image to be of type OpTypeImage"; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| // OpenCL requires Sampled=0, checked elsewhere. | |
| // Vulkan uses the Sampled=1 case. | |
| // If Dim is TileImageDataEXT, Sampled must be 2 and this is validated | |
| // elsewhere. | |
| if ((info.sampled != 0) && (info.sampled != 1)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(4657) | |
| << "Sampled image type requires an image type with \"Sampled\" " | |
| "operand set to 0 or 1"; | |
| } | |
| // This covers both OpTypeSampledImage and OpSampledImage. | |
| if (_.version() >= SPV_SPIRV_VERSION_WORD(1, 6) && | |
| info.dim == spv::Dim::Buffer) { | |
| return _.diag(SPV_ERROR_INVALID_ID, inst) | |
| << "In SPIR-V 1.6 or later, sampled image dimension must not be " | |
| "Buffer"; | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| bool IsAllowedSampledImageOperand(spv::Op opcode, ValidationState_t& _) { | |
| switch (opcode) { | |
| case spv::Op::OpSampledImage: | |
| case spv::Op::OpImageSampleImplicitLod: | |
| case spv::Op::OpImageSampleExplicitLod: | |
| case spv::Op::OpImageSampleDrefImplicitLod: | |
| case spv::Op::OpImageSampleDrefExplicitLod: | |
| case spv::Op::OpImageSampleProjImplicitLod: | |
| case spv::Op::OpImageSampleProjExplicitLod: | |
| case spv::Op::OpImageSampleProjDrefImplicitLod: | |
| case spv::Op::OpImageSampleProjDrefExplicitLod: | |
| case spv::Op::OpImageGather: | |
| case spv::Op::OpImageDrefGather: | |
| case spv::Op::OpImage: | |
| case spv::Op::OpImageQueryLod: | |
| case spv::Op::OpImageSparseSampleImplicitLod: | |
| case spv::Op::OpImageSparseSampleExplicitLod: | |
| case spv::Op::OpImageSparseSampleDrefImplicitLod: | |
| case spv::Op::OpImageSparseSampleDrefExplicitLod: | |
| case spv::Op::OpImageSparseGather: | |
| case spv::Op::OpImageSparseDrefGather: | |
| case spv::Op::OpCopyObject: | |
| return true; | |
| case spv::Op::OpStore: | |
| if (_.HasCapability(spv::Capability::BindlessTextureNV)) return true; | |
| return false; | |
| default: | |
| return false; | |
| } | |
| } | |
| spv_result_t ValidateSampledImage(ValidationState_t& _, | |
| const Instruction* inst) { | |
| if (_.GetIdOpcode(inst->type_id()) != spv::Op::OpTypeSampledImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to be OpTypeSampledImage."; | |
| } | |
| const uint32_t image_type = _.GetOperandTypeId(inst, 2); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image to be of type OpTypeImage."; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| // TODO(atgoo@github.com) Check compatibility of result type and received | |
| // image. | |
| if (spvIsVulkanEnv(_.context()->target_env)) { | |
| if (info.sampled != 1) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(6671) | |
| << "Expected Image 'Sampled' parameter to be 1 for Vulkan " | |
| "environment."; | |
| } | |
| } else { | |
| if (info.sampled != 0 && info.sampled != 1) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'Sampled' parameter to be 0 or 1"; | |
| } | |
| } | |
| if (info.dim == spv::Dim::SubpassData) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'Dim' parameter to be not SubpassData."; | |
| } | |
| if (_.GetIdOpcode(_.GetOperandTypeId(inst, 3)) != spv::Op::OpTypeSampler) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Sampler to be of type OpTypeSampler"; | |
| } | |
| // We need to validate 2 things: | |
| // * All OpSampledImage instructions must be in the same block in which their | |
| // Result <id> are consumed. | |
| // * Result <id> from OpSampledImage instructions must not appear as operands | |
| // to OpPhi instructions or OpSelect instructions, or any instructions other | |
| // than the image lookup and query instructions specified to take an operand | |
| // whose type is OpTypeSampledImage. | |
| std::vector<Instruction*> consumers = _.getSampledImageConsumers(inst->id()); | |
| if (!consumers.empty()) { | |
| for (auto consumer_instr : consumers) { | |
| const auto consumer_opcode = consumer_instr->opcode(); | |
| if (consumer_instr->block() != inst->block()) { | |
| return _.diag(SPV_ERROR_INVALID_ID, inst) | |
| << "All OpSampledImage instructions must be in the same block " | |
| "in " | |
| "which their Result <id> are consumed. OpSampledImage Result " | |
| "Type <id> " | |
| << _.getIdName(inst->id()) | |
| << " has a consumer in a different basic " | |
| "block. The consumer instruction <id> is " | |
| << _.getIdName(consumer_instr->id()) << "."; | |
| } | |
| if (consumer_opcode == spv::Op::OpPhi || | |
| consumer_opcode == spv::Op::OpSelect) { | |
| return _.diag(SPV_ERROR_INVALID_ID, inst) | |
| << "Result <id> from OpSampledImage instruction must not appear " | |
| "as " | |
| "operands of Op" | |
| << spvOpcodeString(static_cast<spv::Op>(consumer_opcode)) << "." | |
| << " Found result <id> " << _.getIdName(inst->id()) | |
| << " as an operand of <id> " << _.getIdName(consumer_instr->id()) | |
| << "."; | |
| } | |
| if (!IsAllowedSampledImageOperand(consumer_opcode, _)) { | |
| return _.diag(SPV_ERROR_INVALID_ID, inst) | |
| << "Result <id> from OpSampledImage instruction must not appear " | |
| "as operand for Op" | |
| << spvOpcodeString(static_cast<spv::Op>(consumer_opcode)) | |
| << ", since it is not specified as taking an " | |
| << "OpTypeSampledImage." | |
| << " Found result <id> " << _.getIdName(inst->id()) | |
| << " as an operand of <id> " << _.getIdName(consumer_instr->id()) | |
| << "."; | |
| } | |
| } | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImageTexelPointer(ValidationState_t& _, | |
| const Instruction* inst) { | |
| const auto result_type = _.FindDef(inst->type_id()); | |
| if (result_type->opcode() != spv::Op::OpTypePointer) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to be OpTypePointer"; | |
| } | |
| const auto storage_class = result_type->GetOperandAs<spv::StorageClass>(1); | |
| if (storage_class != spv::StorageClass::Image) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to be OpTypePointer whose Storage Class " | |
| "operand is Image"; | |
| } | |
| const auto ptr_type = result_type->GetOperandAs<uint32_t>(2); | |
| const auto ptr_opcode = _.GetIdOpcode(ptr_type); | |
| if (ptr_opcode != spv::Op::OpTypeInt && ptr_opcode != spv::Op::OpTypeFloat && | |
| ptr_opcode != spv::Op::OpTypeVoid) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to be OpTypePointer whose Type operand " | |
| "must be a scalar numerical type or OpTypeVoid"; | |
| } | |
| const auto image_ptr = _.FindDef(_.GetOperandTypeId(inst, 2)); | |
| if (!image_ptr || image_ptr->opcode() != spv::Op::OpTypePointer) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image to be OpTypePointer"; | |
| } | |
| const auto image_type = image_ptr->GetOperandAs<uint32_t>(2); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image to be OpTypePointer with Type OpTypeImage"; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| if (info.sampled_type != ptr_type) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'Sampled Type' to be the same as the Type " | |
| "pointed to by Result Type"; | |
| } | |
| if (info.dim == spv::Dim::SubpassData) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Dim SubpassData cannot be used with OpImageTexelPointer"; | |
| } | |
| if (info.dim == spv::Dim::TileImageDataEXT) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Dim TileImageDataEXT cannot be used with " | |
| "OpImageTexelPointer"; | |
| } | |
| const uint32_t coord_type = _.GetOperandTypeId(inst, 3); | |
| if (!coord_type || !_.IsIntScalarOrVectorType(coord_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to be integer scalar or vector"; | |
| } | |
| uint32_t expected_coord_size = 0; | |
| if (info.arrayed == 0) { | |
| expected_coord_size = GetPlaneCoordSize(info); | |
| } else if (info.arrayed == 1) { | |
| switch (info.dim) { | |
| case spv::Dim::Dim1D: | |
| expected_coord_size = 2; | |
| break; | |
| case spv::Dim::Cube: | |
| case spv::Dim::Dim2D: | |
| expected_coord_size = 3; | |
| break; | |
| default: | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'Dim' must be one of 1D, 2D, or Cube when " | |
| "Arrayed is 1"; | |
| break; | |
| } | |
| } | |
| const uint32_t actual_coord_size = _.GetDimension(coord_type); | |
| if (expected_coord_size != actual_coord_size) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to have " << expected_coord_size | |
| << " components, but given " << actual_coord_size; | |
| } | |
| const uint32_t sample_type = _.GetOperandTypeId(inst, 4); | |
| if (!sample_type || !_.IsIntScalarType(sample_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Sample to be integer scalar"; | |
| } | |
| if (info.multisampled == 0) { | |
| uint64_t ms = 0; | |
| if (!_.GetConstantValUint64(inst->GetOperandAs<uint32_t>(4), &ms) || | |
| ms != 0) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Sample for Image with MS 0 to be a valid <id> for " | |
| "the value 0"; | |
| } | |
| } | |
| if (spvIsVulkanEnv(_.context()->target_env)) { | |
| if ((info.format != spv::ImageFormat::R64i) && | |
| (info.format != spv::ImageFormat::R64ui) && | |
| (info.format != spv::ImageFormat::R32f) && | |
| (info.format != spv::ImageFormat::R32i) && | |
| (info.format != spv::ImageFormat::R32ui)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(4658) | |
| << "Expected the Image Format in Image to be R64i, R64ui, R32f, " | |
| "R32i, or R32ui for Vulkan environment"; | |
| } | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImageLod(ValidationState_t& _, const Instruction* inst) { | |
| const spv::Op opcode = inst->opcode(); | |
| uint32_t actual_result_type = 0; | |
| if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) { | |
| return error; | |
| } | |
| if (!_.IsIntVectorType(actual_result_type) && | |
| !_.IsFloatVectorType(actual_result_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected " << GetActualResultTypeStr(opcode) | |
| << " to be int or float vector type"; | |
| } | |
| if (_.GetDimension(actual_result_type) != 4) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected " << GetActualResultTypeStr(opcode) | |
| << " to have 4 components"; | |
| } | |
| const uint32_t image_type = _.GetOperandTypeId(inst, 2); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Sampled Image to be of type OpTypeSampledImage"; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| if (IsProj(opcode)) { | |
| if (spv_result_t result = ValidateImageProj(_, inst, info)) return result; | |
| } | |
| if (info.multisampled) { | |
| // When using image operands, the Sample image operand is required if and | |
| // only if the image is multisampled (MS=1). The Sample image operand is | |
| // only allowed for fetch, read, and write. | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Sampling operation is invalid for multisample image"; | |
| } | |
| if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) { | |
| const uint32_t texel_component_type = | |
| _.GetComponentType(actual_result_type); | |
| if (texel_component_type != info.sampled_type) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'Sampled Type' to be the same as " | |
| << GetActualResultTypeStr(opcode) << " components"; | |
| } | |
| } | |
| const uint32_t coord_type = _.GetOperandTypeId(inst, 3); | |
| if ((opcode == spv::Op::OpImageSampleExplicitLod || | |
| opcode == spv::Op::OpImageSparseSampleExplicitLod) && | |
| _.HasCapability(spv::Capability::Kernel)) { | |
| if (!_.IsFloatScalarOrVectorType(coord_type) && | |
| !_.IsIntScalarOrVectorType(coord_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to be int or float scalar or vector"; | |
| } | |
| } else { | |
| if (!_.IsFloatScalarOrVectorType(coord_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to be float scalar or vector"; | |
| } | |
| } | |
| const uint32_t min_coord_size = GetMinCoordSize(opcode, info); | |
| const uint32_t actual_coord_size = _.GetDimension(coord_type); | |
| if (min_coord_size > actual_coord_size) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to have at least " << min_coord_size | |
| << " components, but given only " << actual_coord_size; | |
| } | |
| const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5); | |
| if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) { | |
| if (spvIsOpenCLEnv(_.context()->target_env)) { | |
| if (opcode == spv::Op::OpImageSampleExplicitLod) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "ConstOffset image operand not allowed " | |
| << "in the OpenCL environment."; | |
| } | |
| } | |
| } | |
| if (spv_result_t result = | |
| ValidateImageOperands(_, inst, info, /* word_index = */ 6)) | |
| return result; | |
| return SPV_SUCCESS; | |
| } | |
| // Validates anything OpImage*Dref* instruction | |
| spv_result_t ValidateImageDref(ValidationState_t& _, const Instruction* inst, | |
| const ImageTypeInfo& info) { | |
| const uint32_t dref_type = _.GetOperandTypeId(inst, 4); | |
| if (!_.IsFloatScalarType(dref_type) || _.GetBitWidth(dref_type) != 32) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Dref to be of 32-bit float type"; | |
| } | |
| if (spvIsVulkanEnv(_.context()->target_env)) { | |
| if (info.dim == spv::Dim::Dim3D) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(4777) | |
| << "In Vulkan, OpImage*Dref* instructions must not use images " | |
| "with a 3D Dim"; | |
| } | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImageDrefLod(ValidationState_t& _, | |
| const Instruction* inst) { | |
| const spv::Op opcode = inst->opcode(); | |
| uint32_t actual_result_type = 0; | |
| if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) { | |
| return error; | |
| } | |
| if (!_.IsIntScalarType(actual_result_type) && | |
| !_.IsFloatScalarType(actual_result_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected " << GetActualResultTypeStr(opcode) | |
| << " to be int or float scalar type"; | |
| } | |
| const uint32_t image_type = _.GetOperandTypeId(inst, 2); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Sampled Image to be of type OpTypeSampledImage"; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| if (IsProj(opcode)) { | |
| if (spv_result_t result = ValidateImageProj(_, inst, info)) return result; | |
| } | |
| if (info.multisampled) { | |
| // When using image operands, the Sample image operand is required if and | |
| // only if the image is multisampled (MS=1). The Sample image operand is | |
| // only allowed for fetch, read, and write. | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Dref sampling operation is invalid for multisample image"; | |
| } | |
| if (actual_result_type != info.sampled_type) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'Sampled Type' to be the same as " | |
| << GetActualResultTypeStr(opcode); | |
| } | |
| const uint32_t coord_type = _.GetOperandTypeId(inst, 3); | |
| if (!_.IsFloatScalarOrVectorType(coord_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to be float scalar or vector"; | |
| } | |
| const uint32_t min_coord_size = GetMinCoordSize(opcode, info); | |
| const uint32_t actual_coord_size = _.GetDimension(coord_type); | |
| if (min_coord_size > actual_coord_size) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to have at least " << min_coord_size | |
| << " components, but given only " << actual_coord_size; | |
| } | |
| if (spv_result_t result = ValidateImageDref(_, inst, info)) return result; | |
| if (spv_result_t result = | |
| ValidateImageOperands(_, inst, info, /* word_index = */ 7)) | |
| return result; | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImageFetch(ValidationState_t& _, const Instruction* inst) { | |
| uint32_t actual_result_type = 0; | |
| if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) { | |
| return error; | |
| } | |
| const spv::Op opcode = inst->opcode(); | |
| if (!_.IsIntVectorType(actual_result_type) && | |
| !_.IsFloatVectorType(actual_result_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected " << GetActualResultTypeStr(opcode) | |
| << " to be int or float vector type"; | |
| } | |
| if (_.GetDimension(actual_result_type) != 4) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected " << GetActualResultTypeStr(opcode) | |
| << " to have 4 components"; | |
| } | |
| const uint32_t image_type = _.GetOperandTypeId(inst, 2); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image to be of type OpTypeImage"; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) { | |
| const uint32_t result_component_type = | |
| _.GetComponentType(actual_result_type); | |
| if (result_component_type != info.sampled_type) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'Sampled Type' to be the same as " | |
| << GetActualResultTypeStr(opcode) << " components"; | |
| } | |
| } | |
| if (info.dim == spv::Dim::Cube) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' cannot be Cube"; | |
| } | |
| if (info.sampled != 1) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'Sampled' parameter to be 1"; | |
| } | |
| const uint32_t coord_type = _.GetOperandTypeId(inst, 3); | |
| if (!_.IsIntScalarOrVectorType(coord_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to be int scalar or vector"; | |
| } | |
| const uint32_t min_coord_size = GetMinCoordSize(opcode, info); | |
| const uint32_t actual_coord_size = _.GetDimension(coord_type); | |
| if (min_coord_size > actual_coord_size) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to have at least " << min_coord_size | |
| << " components, but given only " << actual_coord_size; | |
| } | |
| if (spv_result_t result = | |
| ValidateImageOperands(_, inst, info, /* word_index = */ 6)) | |
| return result; | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImageGather(ValidationState_t& _, | |
| const Instruction* inst) { | |
| uint32_t actual_result_type = 0; | |
| if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) | |
| return error; | |
| const spv::Op opcode = inst->opcode(); | |
| if (!_.IsIntVectorType(actual_result_type) && | |
| !_.IsFloatVectorType(actual_result_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected " << GetActualResultTypeStr(opcode) | |
| << " to be int or float vector type"; | |
| } | |
| if (_.GetDimension(actual_result_type) != 4) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected " << GetActualResultTypeStr(opcode) | |
| << " to have 4 components"; | |
| } | |
| const uint32_t image_type = _.GetOperandTypeId(inst, 2); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Sampled Image to be of type OpTypeSampledImage"; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| if (info.multisampled) { | |
| // When using image operands, the Sample image operand is required if and | |
| // only if the image is multisampled (MS=1). The Sample image operand is | |
| // only allowed for fetch, read, and write. | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Gather operation is invalid for multisample image"; | |
| } | |
| if (opcode == spv::Op::OpImageDrefGather || | |
| opcode == spv::Op::OpImageSparseDrefGather || | |
| _.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) { | |
| const uint32_t result_component_type = | |
| _.GetComponentType(actual_result_type); | |
| if (result_component_type != info.sampled_type) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'Sampled Type' to be the same as " | |
| << GetActualResultTypeStr(opcode) << " components"; | |
| } | |
| } | |
| if (info.dim != spv::Dim::Dim2D && info.dim != spv::Dim::Cube && | |
| info.dim != spv::Dim::Rect) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(4777) | |
| << "Expected Image 'Dim' to be 2D, Cube, or Rect"; | |
| } | |
| const uint32_t coord_type = _.GetOperandTypeId(inst, 3); | |
| if (!_.IsFloatScalarOrVectorType(coord_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to be float scalar or vector"; | |
| } | |
| const uint32_t min_coord_size = GetMinCoordSize(opcode, info); | |
| const uint32_t actual_coord_size = _.GetDimension(coord_type); | |
| if (min_coord_size > actual_coord_size) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to have at least " << min_coord_size | |
| << " components, but given only " << actual_coord_size; | |
| } | |
| if (opcode == spv::Op::OpImageGather || | |
| opcode == spv::Op::OpImageSparseGather) { | |
| const uint32_t component = inst->GetOperandAs<uint32_t>(4); | |
| const uint32_t component_index_type = _.GetTypeId(component); | |
| if (!_.IsIntScalarType(component_index_type) || | |
| _.GetBitWidth(component_index_type) != 32) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Component to be 32-bit int scalar"; | |
| } | |
| if (spvIsVulkanEnv(_.context()->target_env)) { | |
| if (!spvOpcodeIsConstant(_.GetIdOpcode(component))) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(4664) | |
| << "Expected Component Operand to be a const object for Vulkan " | |
| "environment"; | |
| } | |
| } | |
| } else { | |
| assert(opcode == spv::Op::OpImageDrefGather || | |
| opcode == spv::Op::OpImageSparseDrefGather); | |
| if (spv_result_t result = ValidateImageDref(_, inst, info)) return result; | |
| } | |
| if (spv_result_t result = | |
| ValidateImageOperands(_, inst, info, /* word_index = */ 7)) | |
| return result; | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImageRead(ValidationState_t& _, const Instruction* inst) { | |
| const spv::Op opcode = inst->opcode(); | |
| uint32_t actual_result_type = 0; | |
| if (spv_result_t error = GetActualResultType(_, inst, &actual_result_type)) { | |
| return error; | |
| } | |
| if (!_.IsIntScalarOrVectorType(actual_result_type) && | |
| !_.IsFloatScalarOrVectorType(actual_result_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected " << GetActualResultTypeStr(opcode) | |
| << " to be int or float scalar or vector type"; | |
| } | |
| const auto target_env = _.context()->target_env; | |
| // Vulkan requires the result to be a 4-element int or float | |
| // vector. | |
| if (spvIsVulkanEnv(target_env)) { | |
| if (_.GetDimension(actual_result_type) != 4) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(4780) << "Expected " | |
| << GetActualResultTypeStr(opcode) << " to have 4 components"; | |
| } | |
| } // Check OpenCL below, after we get the image info. | |
| const uint32_t image_type = _.GetOperandTypeId(inst, 2); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image to be of type OpTypeImage"; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| if (spvIsOpenCLEnv(target_env)) { | |
| // In OpenCL, a read from a depth image returns a scalar float. In other | |
| // cases, the result is always a 4-element vector. | |
| // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_Env.html#_data_format_for_reading_and_writing_images | |
| // https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_C.html#image-read-and-write-functions | |
| // The builtins for reading depth images are: | |
| // float read_imagef(aQual image2d_depth_t image, int2 coord) | |
| // float read_imagef(aQual image2d_array_depth_t image, int4 coord) | |
| if (info.depth) { | |
| if (!_.IsFloatScalarType(actual_result_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected " << GetActualResultTypeStr(opcode) | |
| << " from a depth image read to result in a scalar float value"; | |
| } | |
| } else { | |
| if (_.GetDimension(actual_result_type) != 4) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected " << GetActualResultTypeStr(opcode) | |
| << " to have 4 components"; | |
| } | |
| } | |
| const uint32_t mask = inst->words().size() <= 5 ? 0 : inst->word(5); | |
| if (mask & uint32_t(spv::ImageOperandsMask::ConstOffset)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "ConstOffset image operand not allowed " | |
| << "in the OpenCL environment."; | |
| } | |
| } | |
| if (info.dim == spv::Dim::SubpassData) { | |
| if (opcode == spv::Op::OpImageSparseRead) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Dim SubpassData cannot be used with ImageSparseRead"; | |
| } | |
| _.function(inst->function()->id()) | |
| ->RegisterExecutionModelLimitation( | |
| spv::ExecutionModel::Fragment, | |
| std::string("Dim SubpassData requires Fragment execution model: ") + | |
| spvOpcodeString(opcode)); | |
| } | |
| if (info.dim == spv::Dim::TileImageDataEXT) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image Dim TileImageDataEXT cannot be used with " | |
| << spvOpcodeString(opcode); | |
| } | |
| if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) { | |
| const uint32_t result_component_type = | |
| _.GetComponentType(actual_result_type); | |
| if (result_component_type != info.sampled_type) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'Sampled Type' to be the same as " | |
| << GetActualResultTypeStr(opcode) << " components"; | |
| } | |
| } | |
| if (spv_result_t result = ValidateImageReadWrite(_, inst, info)) | |
| return result; | |
| const uint32_t coord_type = _.GetOperandTypeId(inst, 3); | |
| if (!_.IsIntScalarOrVectorType(coord_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to be int scalar or vector"; | |
| } | |
| const uint32_t min_coord_size = GetMinCoordSize(opcode, info); | |
| const uint32_t actual_coord_size = _.GetDimension(coord_type); | |
| if (min_coord_size > actual_coord_size) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to have at least " << min_coord_size | |
| << " components, but given only " << actual_coord_size; | |
| } | |
| if (spvIsVulkanEnv(_.context()->target_env)) { | |
| if (info.format == spv::ImageFormat::Unknown && | |
| info.dim != spv::Dim::SubpassData && | |
| !_.HasCapability(spv::Capability::StorageImageReadWithoutFormat)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Capability StorageImageReadWithoutFormat is required to " | |
| << "read storage image"; | |
| } | |
| } | |
| if (spv_result_t result = | |
| ValidateImageOperands(_, inst, info, /* word_index = */ 6)) | |
| return result; | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImageWrite(ValidationState_t& _, const Instruction* inst) { | |
| const uint32_t image_type = _.GetOperandTypeId(inst, 0); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image to be of type OpTypeImage"; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| if (info.dim == spv::Dim::SubpassData) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image 'Dim' cannot be SubpassData"; | |
| } | |
| if (info.dim == spv::Dim::TileImageDataEXT) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image 'Dim' cannot be TileImageDataEXT"; | |
| } | |
| if (spv_result_t result = ValidateImageReadWrite(_, inst, info)) | |
| return result; | |
| const uint32_t coord_type = _.GetOperandTypeId(inst, 1); | |
| if (!_.IsIntScalarOrVectorType(coord_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to be int scalar or vector"; | |
| } | |
| const uint32_t min_coord_size = GetMinCoordSize(inst->opcode(), info); | |
| const uint32_t actual_coord_size = _.GetDimension(coord_type); | |
| if (min_coord_size > actual_coord_size) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to have at least " << min_coord_size | |
| << " components, but given only " << actual_coord_size; | |
| } | |
| // because it needs to match with 'Sampled Type' the Texel can't be a boolean | |
| const uint32_t texel_type = _.GetOperandTypeId(inst, 2); | |
| if (!_.IsIntScalarOrVectorType(texel_type) && | |
| !_.IsFloatScalarOrVectorType(texel_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Texel to be int or float vector or scalar"; | |
| } | |
| if (_.GetIdOpcode(info.sampled_type) != spv::Op::OpTypeVoid) { | |
| const uint32_t texel_component_type = _.GetComponentType(texel_type); | |
| if (texel_component_type != info.sampled_type) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image 'Sampled Type' to be the same as Texel " | |
| << "components"; | |
| } | |
| } | |
| if (spvIsVulkanEnv(_.context()->target_env)) { | |
| if (info.format == spv::ImageFormat::Unknown && | |
| info.dim != spv::Dim::SubpassData && | |
| !_.HasCapability(spv::Capability::StorageImageWriteWithoutFormat)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Capability StorageImageWriteWithoutFormat is required to " | |
| "write " | |
| << "to storage image"; | |
| } | |
| } | |
| if (inst->words().size() > 4) { | |
| if (spvIsOpenCLEnv(_.context()->target_env)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Optional Image Operands are not allowed in the OpenCL " | |
| << "environment."; | |
| } | |
| } | |
| if (spv_result_t result = | |
| ValidateImageOperands(_, inst, info, /* word_index = */ 5)) | |
| return result; | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImage(ValidationState_t& _, const Instruction* inst) { | |
| const uint32_t result_type = inst->type_id(); | |
| if (_.GetIdOpcode(result_type) != spv::Op::OpTypeImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to be OpTypeImage"; | |
| } | |
| const uint32_t sampled_image_type = _.GetOperandTypeId(inst, 2); | |
| const Instruction* sampled_image_type_inst = _.FindDef(sampled_image_type); | |
| assert(sampled_image_type_inst); | |
| if (sampled_image_type_inst->opcode() != spv::Op::OpTypeSampledImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Sample Image to be of type OpTypeSampleImage"; | |
| } | |
| if (sampled_image_type_inst->word(2) != result_type) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Sample Image image type to be equal to Result Type"; | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImageQuerySizeLod(ValidationState_t& _, | |
| const Instruction* inst) { | |
| const uint32_t result_type = inst->type_id(); | |
| if (!_.IsIntScalarOrVectorType(result_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to be int scalar or vector type"; | |
| } | |
| const uint32_t image_type = _.GetOperandTypeId(inst, 2); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image to be of type OpTypeImage"; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| uint32_t expected_num_components = info.arrayed; | |
| switch (info.dim) { | |
| case spv::Dim::Dim1D: | |
| expected_num_components += 1; | |
| break; | |
| case spv::Dim::Dim2D: | |
| case spv::Dim::Cube: | |
| expected_num_components += 2; | |
| break; | |
| case spv::Dim::Dim3D: | |
| expected_num_components += 3; | |
| break; | |
| default: | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image 'Dim' must be 1D, 2D, 3D or Cube"; | |
| } | |
| if (info.multisampled != 0) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 0"; | |
| } | |
| const auto target_env = _.context()->target_env; | |
| if (spvIsVulkanEnv(target_env)) { | |
| if (info.sampled != 1) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(4659) | |
| << "OpImageQuerySizeLod must only consume an \"Image\" operand " | |
| "whose type has its \"Sampled\" operand set to 1"; | |
| } | |
| } | |
| uint32_t result_num_components = _.GetDimension(result_type); | |
| if (result_num_components != expected_num_components) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Result Type has " << result_num_components << " components, " | |
| << "but " << expected_num_components << " expected"; | |
| } | |
| const uint32_t lod_type = _.GetOperandTypeId(inst, 3); | |
| if (!_.IsIntScalarType(lod_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Level of Detail to be int scalar"; | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImageQuerySize(ValidationState_t& _, | |
| const Instruction* inst) { | |
| const uint32_t result_type = inst->type_id(); | |
| if (!_.IsIntScalarOrVectorType(result_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to be int scalar or vector type"; | |
| } | |
| const uint32_t image_type = _.GetOperandTypeId(inst, 2); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image to be of type OpTypeImage"; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| uint32_t expected_num_components = info.arrayed; | |
| switch (info.dim) { | |
| case spv::Dim::Dim1D: | |
| case spv::Dim::Buffer: | |
| expected_num_components += 1; | |
| break; | |
| case spv::Dim::Dim2D: | |
| case spv::Dim::Cube: | |
| case spv::Dim::Rect: | |
| expected_num_components += 2; | |
| break; | |
| case spv::Dim::Dim3D: | |
| expected_num_components += 3; | |
| break; | |
| default: | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image 'Dim' must be 1D, Buffer, 2D, Cube, 3D or Rect"; | |
| } | |
| if (info.dim == spv::Dim::Dim1D || info.dim == spv::Dim::Dim2D || | |
| info.dim == spv::Dim::Dim3D || info.dim == spv::Dim::Cube) { | |
| if (info.multisampled != 1 && info.sampled != 0 && info.sampled != 2) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image must have either 'MS'=1 or 'Sampled'=0 or 'Sampled'=2"; | |
| } | |
| } | |
| uint32_t result_num_components = _.GetDimension(result_type); | |
| if (result_num_components != expected_num_components) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Result Type has " << result_num_components << " components, " | |
| << "but " << expected_num_components << " expected"; | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImageQueryFormatOrOrder(ValidationState_t& _, | |
| const Instruction* inst) { | |
| if (!_.IsIntScalarType(inst->type_id())) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to be int scalar type"; | |
| } | |
| const uint32_t image_type = _.GetOperandTypeId(inst, 2); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected operand to be of type OpTypeImage"; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| if (info.dim == spv::Dim::TileImageDataEXT) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image 'Dim' cannot be TileImageDataEXT"; | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImageQueryLod(ValidationState_t& _, | |
| const Instruction* inst) { | |
| _.function(inst->function()->id()) | |
| ->RegisterExecutionModelLimitation( | |
| [&](spv::ExecutionModel model, std::string* message) { | |
| if (model != spv::ExecutionModel::Fragment && | |
| model != spv::ExecutionModel::GLCompute) { | |
| if (message) { | |
| *message = std::string( | |
| "OpImageQueryLod requires Fragment or GLCompute execution " | |
| "model"); | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| _.function(inst->function()->id()) | |
| ->RegisterLimitation([](const ValidationState_t& state, | |
| const Function* entry_point, | |
| std::string* message) { | |
| const auto* models = state.GetExecutionModels(entry_point->id()); | |
| const auto* modes = state.GetExecutionModes(entry_point->id()); | |
| if (models->find(spv::ExecutionModel::GLCompute) != models->end() && | |
| modes->find(spv::ExecutionMode::DerivativeGroupLinearNV) == | |
| modes->end() && | |
| modes->find(spv::ExecutionMode::DerivativeGroupQuadsNV) == | |
| modes->end()) { | |
| if (message) { | |
| *message = std::string( | |
| "OpImageQueryLod requires DerivativeGroupQuadsNV " | |
| "or DerivativeGroupLinearNV execution mode for GLCompute " | |
| "execution model"); | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| const uint32_t result_type = inst->type_id(); | |
| if (!_.IsFloatVectorType(result_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to be float vector type"; | |
| } | |
| if (_.GetDimension(result_type) != 2) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to have 2 components"; | |
| } | |
| const uint32_t image_type = _.GetOperandTypeId(inst, 2); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeSampledImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image operand to be of type OpTypeSampledImage"; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && | |
| info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image 'Dim' must be 1D, 2D, 3D or Cube"; | |
| } | |
| const uint32_t coord_type = _.GetOperandTypeId(inst, 3); | |
| if (_.HasCapability(spv::Capability::Kernel)) { | |
| if (!_.IsFloatScalarOrVectorType(coord_type) && | |
| !_.IsIntScalarOrVectorType(coord_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to be int or float scalar or vector"; | |
| } | |
| } else { | |
| if (!_.IsFloatScalarOrVectorType(coord_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to be float scalar or vector"; | |
| } | |
| } | |
| const uint32_t min_coord_size = GetPlaneCoordSize(info); | |
| const uint32_t actual_coord_size = _.GetDimension(coord_type); | |
| if (min_coord_size > actual_coord_size) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Coordinate to have at least " << min_coord_size | |
| << " components, but given only " << actual_coord_size; | |
| } | |
| // The operand is a sampled image. | |
| // The sampled image type is already checked to be parameterized by an image | |
| // type with Sampled=0 or Sampled=1. Vulkan bans Sampled=0, and so we have | |
| // Sampled=1. So the validator already enforces Vulkan VUID 4659: | |
| // OpImageQuerySizeLod must only consume an "Image" operand whose type has | |
| // its "Sampled" operand set to 1 | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImageSparseLod(ValidationState_t& _, | |
| const Instruction* inst) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Instruction reserved for future use, use of this instruction " | |
| << "is invalid"; | |
| } | |
| spv_result_t ValidateImageQueryLevelsOrSamples(ValidationState_t& _, | |
| const Instruction* inst) { | |
| if (!_.IsIntScalarType(inst->type_id())) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to be int scalar type"; | |
| } | |
| const uint32_t image_type = _.GetOperandTypeId(inst, 2); | |
| if (_.GetIdOpcode(image_type) != spv::Op::OpTypeImage) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Image to be of type OpTypeImage"; | |
| } | |
| ImageTypeInfo info; | |
| if (!GetImageTypeInfo(_, image_type, &info)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Corrupt image type definition"; | |
| } | |
| const spv::Op opcode = inst->opcode(); | |
| if (opcode == spv::Op::OpImageQueryLevels) { | |
| if (info.dim != spv::Dim::Dim1D && info.dim != spv::Dim::Dim2D && | |
| info.dim != spv::Dim::Dim3D && info.dim != spv::Dim::Cube) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Image 'Dim' must be 1D, 2D, 3D or Cube"; | |
| } | |
| const auto target_env = _.context()->target_env; | |
| if (spvIsVulkanEnv(target_env)) { | |
| if (info.sampled != 1) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << _.VkErrorID(4659) | |
| << "OpImageQueryLevels must only consume an \"Image\" operand " | |
| "whose type has its \"Sampled\" operand set to 1"; | |
| } | |
| } | |
| } else { | |
| assert(opcode == spv::Op::OpImageQuerySamples); | |
| if (info.dim != spv::Dim::Dim2D) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'Dim' must be 2D"; | |
| } | |
| if (info.multisampled != 1) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) << "Image 'MS' must be 1"; | |
| } | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| spv_result_t ValidateImageSparseTexelsResident(ValidationState_t& _, | |
| const Instruction* inst) { | |
| if (!_.IsBoolScalarType(inst->type_id())) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Result Type to be bool scalar type"; | |
| } | |
| const uint32_t resident_code_type = _.GetOperandTypeId(inst, 2); | |
| if (!_.IsIntScalarType(resident_code_type)) { | |
| return _.diag(SPV_ERROR_INVALID_DATA, inst) | |
| << "Expected Resident Code to be int scalar"; | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| } // namespace | |
| // Validates correctness of image instructions. | |
| spv_result_t ImagePass(ValidationState_t& _, const Instruction* inst) { | |
| const spv::Op opcode = inst->opcode(); | |
| if (IsImplicitLod(opcode)) { | |
| _.function(inst->function()->id()) | |
| ->RegisterExecutionModelLimitation([opcode](spv::ExecutionModel model, | |
| std::string* message) { | |
| if (model != spv::ExecutionModel::Fragment && | |
| model != spv::ExecutionModel::GLCompute) { | |
| if (message) { | |
| *message = | |
| std::string( | |
| "ImplicitLod instructions require Fragment or GLCompute " | |
| "execution model: ") + | |
| spvOpcodeString(opcode); | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| _.function(inst->function()->id()) | |
| ->RegisterLimitation([opcode](const ValidationState_t& state, | |
| const Function* entry_point, | |
| std::string* message) { | |
| const auto* models = state.GetExecutionModels(entry_point->id()); | |
| const auto* modes = state.GetExecutionModes(entry_point->id()); | |
| if (models && | |
| models->find(spv::ExecutionModel::GLCompute) != models->end() && | |
| (!modes || | |
| (modes->find(spv::ExecutionMode::DerivativeGroupLinearNV) == | |
| modes->end() && | |
| modes->find(spv::ExecutionMode::DerivativeGroupQuadsNV) == | |
| modes->end()))) { | |
| if (message) { | |
| *message = | |
| std::string( | |
| "ImplicitLod instructions require DerivativeGroupQuadsNV " | |
| "or DerivativeGroupLinearNV execution mode for GLCompute " | |
| "execution model: ") + | |
| spvOpcodeString(opcode); | |
| } | |
| return false; | |
| } | |
| return true; | |
| }); | |
| } | |
| switch (opcode) { | |
| case spv::Op::OpTypeImage: | |
| return ValidateTypeImage(_, inst); | |
| case spv::Op::OpTypeSampledImage: | |
| return ValidateTypeSampledImage(_, inst); | |
| case spv::Op::OpSampledImage: | |
| return ValidateSampledImage(_, inst); | |
| case spv::Op::OpImageTexelPointer: | |
| return ValidateImageTexelPointer(_, inst); | |
| case spv::Op::OpImageSampleImplicitLod: | |
| case spv::Op::OpImageSampleExplicitLod: | |
| case spv::Op::OpImageSampleProjImplicitLod: | |
| case spv::Op::OpImageSampleProjExplicitLod: | |
| case spv::Op::OpImageSparseSampleImplicitLod: | |
| case spv::Op::OpImageSparseSampleExplicitLod: | |
| return ValidateImageLod(_, inst); | |
| case spv::Op::OpImageSampleDrefImplicitLod: | |
| case spv::Op::OpImageSampleDrefExplicitLod: | |
| case spv::Op::OpImageSampleProjDrefImplicitLod: | |
| case spv::Op::OpImageSampleProjDrefExplicitLod: | |
| case spv::Op::OpImageSparseSampleDrefImplicitLod: | |
| case spv::Op::OpImageSparseSampleDrefExplicitLod: | |
| return ValidateImageDrefLod(_, inst); | |
| case spv::Op::OpImageFetch: | |
| case spv::Op::OpImageSparseFetch: | |
| return ValidateImageFetch(_, inst); | |
| case spv::Op::OpImageGather: | |
| case spv::Op::OpImageDrefGather: | |
| case spv::Op::OpImageSparseGather: | |
| case spv::Op::OpImageSparseDrefGather: | |
| return ValidateImageGather(_, inst); | |
| case spv::Op::OpImageRead: | |
| case spv::Op::OpImageSparseRead: | |
| return ValidateImageRead(_, inst); | |
| case spv::Op::OpImageWrite: | |
| return ValidateImageWrite(_, inst); | |
| case spv::Op::OpImage: | |
| return ValidateImage(_, inst); | |
| case spv::Op::OpImageQueryFormat: | |
| case spv::Op::OpImageQueryOrder: | |
| return ValidateImageQueryFormatOrOrder(_, inst); | |
| case spv::Op::OpImageQuerySizeLod: | |
| return ValidateImageQuerySizeLod(_, inst); | |
| case spv::Op::OpImageQuerySize: | |
| return ValidateImageQuerySize(_, inst); | |
| case spv::Op::OpImageQueryLod: | |
| return ValidateImageQueryLod(_, inst); | |
| case spv::Op::OpImageQueryLevels: | |
| case spv::Op::OpImageQuerySamples: | |
| return ValidateImageQueryLevelsOrSamples(_, inst); | |
| case spv::Op::OpImageSparseSampleProjImplicitLod: | |
| case spv::Op::OpImageSparseSampleProjExplicitLod: | |
| case spv::Op::OpImageSparseSampleProjDrefImplicitLod: | |
| case spv::Op::OpImageSparseSampleProjDrefExplicitLod: | |
| return ValidateImageSparseLod(_, inst); | |
| case spv::Op::OpImageSparseTexelsResident: | |
| return ValidateImageSparseTexelsResident(_, inst); | |
| default: | |
| break; | |
| } | |
| return SPV_SUCCESS; | |
| } | |
| } // namespace val | |
| } // namespace spvtools |