diff --git a/3rdparty/spirv-tools/include/generated/build-version.inc b/3rdparty/spirv-tools/include/generated/build-version.inc index f8ba62e23..ed0a03fc6 100644 --- a/3rdparty/spirv-tools/include/generated/build-version.inc +++ b/3rdparty/spirv-tools/include/generated/build-version.inc @@ -1 +1 @@ -"v2020.7-dev", "SPIRV-Tools v2020.7-dev 93efa0c36bc823ed865bd45418c57390547cef74" +"v2020.7-dev", "SPIRV-Tools v2020.7-dev 45234794d9cc274023fbed948636fd006b589aef" diff --git a/3rdparty/spirv-tools/include/spirv-tools/libspirv.h b/3rdparty/spirv-tools/include/spirv-tools/libspirv.h index 505793959..ced8d8307 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/libspirv.h +++ b/3rdparty/spirv-tools/include/spirv-tools/libspirv.h @@ -626,6 +626,12 @@ SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetUniformBufferStandardLayout( SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetScalarBlockLayout( spv_validator_options options, bool val); +// Records whether the validator should use "scalar" block layout +// rules (as defined above) for Workgroup blocks. See Vulkan +// extension VK_KHR_workgroup_memory_explicit_layout. +SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetWorkgroupScalarBlockLayout( + spv_validator_options options, bool val); + // Records whether or not the validator should skip validating standard // uniform/storage block layout. SPIRV_TOOLS_EXPORT void spvValidatorOptionsSetSkipBlockLayout( diff --git a/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp b/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp index 2018e4610..e7e7fc7aa 100644 --- a/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp +++ b/3rdparty/spirv-tools/include/spirv-tools/libspirv.hpp @@ -104,6 +104,12 @@ class ValidatorOptions { spvValidatorOptionsSetScalarBlockLayout(options_, val); } + // Enables scalar layout when validating Workgroup blocks. See + // VK_KHR_workgroup_memory_explicit_layout. + void SetWorkgroupScalarBlockLayout(bool val) { + spvValidatorOptionsSetWorkgroupScalarBlockLayout(options_, val); + } + // Skips validating standard uniform/storage buffer/push-constant layout. void SetSkipBlockLayout(bool val) { spvValidatorOptionsSetSkipBlockLayout(options_, val); diff --git a/3rdparty/spirv-tools/source/opt/convert_to_half_pass.cpp b/3rdparty/spirv-tools/source/opt/convert_to_half_pass.cpp index 5022e1b68..6b3b540aa 100644 --- a/3rdparty/spirv-tools/source/opt/convert_to_half_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/convert_to_half_pass.cpp @@ -147,8 +147,8 @@ bool ConvertToHalfPass::MatConvertCleanup(Instruction* inst) { return true; } -void ConvertToHalfPass::RemoveRelaxedDecoration(uint32_t id) { - context()->get_decoration_mgr()->RemoveDecorationsFrom( +bool ConvertToHalfPass::RemoveRelaxedDecoration(uint32_t id) { + return context()->get_decoration_mgr()->RemoveDecorationsFrom( id, [](const Instruction& dec) { if (dec.opcode() == SpvOpDecorate && dec.GetSingleWordInOperand(1u) == SpvDecorationRelaxedPrecision) @@ -344,10 +344,14 @@ Pass::Status ConvertToHalfPass::ProcessImpl() { // If modified, make sure module has Float16 capability if (modified) context()->AddCapability(SpvCapabilityFloat16); // Remove all RelaxedPrecision decorations from instructions and globals - for (auto c_id : relaxed_ids_set_) RemoveRelaxedDecoration(c_id); + for (auto c_id : relaxed_ids_set_) { + modified |= RemoveRelaxedDecoration(c_id); + } for (auto& val : get_module()->types_values()) { uint32_t v_id = val.result_id(); - if (v_id != 0) RemoveRelaxedDecoration(v_id); + if (v_id != 0) { + modified |= RemoveRelaxedDecoration(v_id); + } } return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange; } diff --git a/3rdparty/spirv-tools/source/opt/convert_to_half_pass.h b/3rdparty/spirv-tools/source/opt/convert_to_half_pass.h index 143aebfa2..b647dd4a3 100644 --- a/3rdparty/spirv-tools/source/opt/convert_to_half_pass.h +++ b/3rdparty/spirv-tools/source/opt/convert_to_half_pass.h @@ -74,7 +74,7 @@ class ConvertToHalfPass : public Pass { void GenConvert(uint32_t* val_idp, uint32_t width, Instruction* inst); // Remove RelaxedPrecision decoration of |id|. - void RemoveRelaxedDecoration(uint32_t id); + bool RemoveRelaxedDecoration(uint32_t id); // Add |inst| to relaxed instruction set if warranted. Specifically, if // it is float32 and either decorated relaxed or a composite or phi diff --git a/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp b/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp index 11b5131d7..e782641f2 100644 --- a/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp +++ b/3rdparty/spirv-tools/source/opt/debug_info_manager.cpp @@ -500,14 +500,15 @@ bool DebugInfoManager::AddDebugValueIfVarDeclIsVisible( insert_before->opcode() == SpvOpVariable) { insert_before = insert_before->NextNode(); } - modified |= AddDebugValueForDecl(dbg_decl_or_val, value_id, - insert_before) != nullptr; + modified |= AddDebugValueForDecl(dbg_decl_or_val, value_id, insert_before, + scope_and_line) != nullptr; } return modified; } Instruction* DebugInfoManager::AddDebugValueForDecl( - Instruction* dbg_decl, uint32_t value_id, Instruction* insert_before) { + Instruction* dbg_decl, uint32_t value_id, Instruction* insert_before, + Instruction* scope_and_line) { if (dbg_decl == nullptr || !IsDebugDeclare(dbg_decl)) return nullptr; std::unique_ptr dbg_val(dbg_decl->Clone(context())); @@ -517,6 +518,7 @@ Instruction* DebugInfoManager::AddDebugValueForDecl( dbg_val->SetOperand(kDebugDeclareOperandVariableIndex, {value_id}); dbg_val->SetOperand(kDebugValueOperandExpressionIndex, {GetEmptyDebugExpression()->result_id()}); + dbg_val->UpdateDebugInfoFrom(scope_and_line); auto* added_dbg_val = insert_before->InsertBefore(std::move(dbg_val)); AnalyzeDebugInst(added_dbg_val); diff --git a/3rdparty/spirv-tools/source/opt/debug_info_manager.h b/3rdparty/spirv-tools/source/opt/debug_info_manager.h index 92b38183e..776e9baab 100644 --- a/3rdparty/spirv-tools/source/opt/debug_info_manager.h +++ b/3rdparty/spirv-tools/source/opt/debug_info_manager.h @@ -152,11 +152,14 @@ class DebugInfoManager { std::unordered_set* invisible_decls); // Creates a DebugValue for DebugDeclare |dbg_decl| and inserts it before - // |insert_before|. The new DebugValue has the same line, scope, and - // operands with DebugDeclare but it uses |value_id| for value. Returns - // the added DebugValue, or nullptr if it does not add a DebugValue. + // |insert_before|. The new DebugValue has the same line and scope as + // |scope_and_line|, or no scope and line information if |scope_and_line| + // is nullptr. The new DebugValue has the same operands as DebugDeclare + // but it uses |value_id| for the value. Returns the created DebugValue, + // or nullptr if fails to create one. Instruction* AddDebugValueForDecl(Instruction* dbg_decl, uint32_t value_id, - Instruction* insert_before); + Instruction* insert_before, + Instruction* scope_and_line); // Erases |instr| from data structures of this class. void ClearDebugInfo(Instruction* instr); diff --git a/3rdparty/spirv-tools/source/opt/decoration_manager.cpp b/3rdparty/spirv-tools/source/opt/decoration_manager.cpp index 8b4aee58d..4bf026efd 100644 --- a/3rdparty/spirv-tools/source/opt/decoration_manager.cpp +++ b/3rdparty/spirv-tools/source/opt/decoration_manager.cpp @@ -53,11 +53,12 @@ namespace spvtools { namespace opt { namespace analysis { -void DecorationManager::RemoveDecorationsFrom( +bool DecorationManager::RemoveDecorationsFrom( uint32_t id, std::function pred) { + bool was_modified = false; const auto ids_iter = id_to_decoration_insts_.find(id); if (ids_iter == id_to_decoration_insts_.end()) { - return; + return was_modified; } TargetData& decorations_info = ids_iter->second; @@ -99,7 +100,6 @@ void DecorationManager::RemoveDecorationsFrom( // Otherwise, remove |id| from the targets of |group_id| const uint32_t stride = inst->opcode() == SpvOpGroupDecorate ? 1u : 2u; - bool was_modified = false; for (uint32_t i = 1u; i < inst->NumInOperands();) { if (inst->GetSingleWordInOperand(i) != id) { i += stride; @@ -155,6 +155,7 @@ void DecorationManager::RemoveDecorationsFrom( }), indirect_decorations.end()); + was_modified |= !insts_to_kill.empty(); for (Instruction* inst : insts_to_kill) context->KillInst(inst); insts_to_kill.clear(); @@ -165,6 +166,7 @@ void DecorationManager::RemoveDecorationsFrom( for (Instruction* inst : decorations_info.decorate_insts) insts_to_kill.push_back(inst); } + was_modified |= !insts_to_kill.empty(); for (Instruction* inst : insts_to_kill) context->KillInst(inst); if (decorations_info.direct_decorations.empty() && @@ -172,6 +174,7 @@ void DecorationManager::RemoveDecorationsFrom( decorations_info.decorate_insts.empty()) { id_to_decoration_insts_.erase(ids_iter); } + return was_modified; } std::vector DecorationManager::GetDecorationsFor( diff --git a/3rdparty/spirv-tools/source/opt/decoration_manager.h b/3rdparty/spirv-tools/source/opt/decoration_manager.h index e1ae8d57b..b753e6be1 100644 --- a/3rdparty/spirv-tools/source/opt/decoration_manager.h +++ b/3rdparty/spirv-tools/source/opt/decoration_manager.h @@ -36,8 +36,9 @@ class DecorationManager { } DecorationManager() = delete; - // Changes all of the decorations (direct and through groups) where |pred| is - // true and that apply to |id| so that they no longer apply to |id|. + // Removes all decorations (direct and through groups) where |pred| is + // true and that apply to |id| so that they no longer apply to |id|. Returns + // true if something changed. // // If |id| is part of a group, it will be removed from the group if it // does not use all of the group's decorations, or, if there are no @@ -52,9 +53,9 @@ class DecorationManager { // removed, then the |OpGroupDecorate| and // |OpGroupMemberDecorate| for the group will be killed, but not the defining // |OpDecorationGroup| instruction. - void RemoveDecorationsFrom(uint32_t id, - std::function pred = - [](const Instruction&) { return true; }); + bool RemoveDecorationsFrom( + uint32_t id, std::function pred = + [](const Instruction&) { return true; }); // Removes all decorations from the result id of |inst|. // diff --git a/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp b/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp index b8d9091ac..d322a2fb5 100644 --- a/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/local_single_store_elim_pass.cpp @@ -175,7 +175,7 @@ bool LocalSingleStoreElimPass::RewriteDebugDeclares(Instruction* store_inst, for (auto* decl : invisible_decls) { if (dominator_analysis->Dominates(store_inst, decl)) { context()->get_debug_info_mgr()->AddDebugValueForDecl(decl, value_id, - decl); + decl, store_inst); modified = true; } } diff --git a/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp b/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp index c8e0da5ba..ba2d06750 100644 --- a/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/scalar_replacement_pass.cpp @@ -175,7 +175,8 @@ bool ScalarReplacementPass::ReplaceWholeDebugDeclare( Instruction* added_dbg_value = context()->get_debug_info_mgr()->AddDebugValueForDecl( dbg_decl, /*value_id=*/var->result_id(), - /*insert_before=*/var->NextNode()); + /*insert_before=*/var->NextNode(), /*scope_and_line=*/dbg_decl); + if (added_dbg_value == nullptr) return false; added_dbg_value->AddOperand( {SPV_OPERAND_TYPE_ID, diff --git a/3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp b/3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp index 0489f03aa..81770d779 100644 --- a/3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp +++ b/3rdparty/spirv-tools/source/opt/ssa_rewrite_pass.cpp @@ -658,8 +658,8 @@ Pass::Status SSARewriter::AddDebugValuesForInvisibleDebugDecls(Function* fp) { // a = 3; // foo(3); // After inlining: - // a = 3; // we want to specify "DebugValue: %x = %int_3" - // foo and x disappeared! + // a = 3; + // foo and x disappeared but we want to specify "DebugValue: %x = %int_3". // // We want to specify the value for the variable using |defs_at_block_[bb]|, // where |bb| is the basic block contains the decl. @@ -681,16 +681,17 @@ Pass::Status SSARewriter::AddDebugValuesForInvisibleDebugDecls(Function* fp) { if (value && (pass_->context()->get_instr_block(value) == nullptr || dom_tree->Dominates(value, decl))) { if (pass_->context()->get_debug_info_mgr()->AddDebugValueForDecl( - decl, value->result_id(), decl) == nullptr) { + decl, value->result_id(), decl, value) == nullptr) { return Pass::Status::Failure; } } else { // If |value| in the same basic block does not dominate |decl|, we can // assign the value in the immediate dominator. value_id = GetValueAtBlock(var_id, dom_tree->ImmediateDominator(bb)); + if (value_id) value = pass_->get_def_use_mgr()->GetDef(value_id); if (value_id && pass_->context()->get_debug_info_mgr()->AddDebugValueForDecl( - decl, value_id, decl) == nullptr) { + decl, value_id, decl, value) == nullptr) { return Pass::Status::Failure; } } diff --git a/3rdparty/spirv-tools/source/spirv_validator_options.cpp b/3rdparty/spirv-tools/source/spirv_validator_options.cpp index 01aa79747..2716cca9b 100644 --- a/3rdparty/spirv-tools/source/spirv_validator_options.cpp +++ b/3rdparty/spirv-tools/source/spirv_validator_options.cpp @@ -111,6 +111,11 @@ void spvValidatorOptionsSetScalarBlockLayout(spv_validator_options options, options->scalar_block_layout = val; } +void spvValidatorOptionsSetWorkgroupScalarBlockLayout(spv_validator_options options, + bool val) { + options->workgroup_scalar_block_layout = val; +} + void spvValidatorOptionsSetSkipBlockLayout(spv_validator_options options, bool val) { options->skip_block_layout = val; diff --git a/3rdparty/spirv-tools/source/spirv_validator_options.h b/3rdparty/spirv-tools/source/spirv_validator_options.h index b7da5d8ea..baaa5359e 100644 --- a/3rdparty/spirv-tools/source/spirv_validator_options.h +++ b/3rdparty/spirv-tools/source/spirv_validator_options.h @@ -45,6 +45,7 @@ struct spv_validator_options_t { relax_block_layout(false), uniform_buffer_standard_layout(false), scalar_block_layout(false), + workgroup_scalar_block_layout(false), skip_block_layout(false), before_hlsl_legalization(false) {} @@ -54,6 +55,7 @@ struct spv_validator_options_t { bool relax_block_layout; bool uniform_buffer_standard_layout; bool scalar_block_layout; + bool workgroup_scalar_block_layout; bool skip_block_layout; bool before_hlsl_legalization; }; diff --git a/3rdparty/spirv-tools/source/val/validate_conversion.cpp b/3rdparty/spirv-tools/source/val/validate_conversion.cpp index 0060d0b7b..b4e39cfe4 100644 --- a/3rdparty/spirv-tools/source/val/validate_conversion.cpp +++ b/3rdparty/spirv-tools/source/val/validate_conversion.cpp @@ -14,12 +14,12 @@ // Validates correctness of conversion instructions. -#include "source/val/validate.h" - #include "source/diagnostic.h" #include "source/opcode.h" #include "source/spirv_constant.h" +#include "source/spirv_target_env.h" #include "source/val/instruction.h" +#include "source/val/validate.h" #include "source/val/validation_state.h" namespace spvtools { @@ -263,16 +263,25 @@ spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) { << "Logical addressing not supported: " << spvOpcodeString(opcode); - if (_.addressing_model() == - SpvAddressingModelPhysicalStorageBuffer64EXT) { + if (_.addressing_model() == SpvAddressingModelPhysicalStorageBuffer64) { uint32_t input_storage_class = 0; uint32_t input_data_type = 0; _.GetPointerTypeInfo(input_type, &input_data_type, &input_storage_class); - if (input_storage_class != SpvStorageClassPhysicalStorageBufferEXT) + if (input_storage_class != SpvStorageClassPhysicalStorageBuffer) return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Pointer storage class must be PhysicalStorageBufferEXT: " + << "Pointer storage class must be PhysicalStorageBuffer: " << spvOpcodeString(opcode); + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (_.GetBitWidth(result_type) != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4710) + << "PhysicalStorageBuffer64 addressing mode requires the " + "result integer type to have a 64-bit width for Vulkan " + "environment."; + } + } } break; } @@ -314,16 +323,25 @@ spv_result_t ConversionPass(ValidationState_t& _, const Instruction* inst) { << "Logical addressing not supported: " << spvOpcodeString(opcode); - if (_.addressing_model() == - SpvAddressingModelPhysicalStorageBuffer64EXT) { + if (_.addressing_model() == SpvAddressingModelPhysicalStorageBuffer64) { uint32_t result_storage_class = 0; uint32_t result_data_type = 0; _.GetPointerTypeInfo(result_type, &result_data_type, &result_storage_class); - if (result_storage_class != SpvStorageClassPhysicalStorageBufferEXT) + if (result_storage_class != SpvStorageClassPhysicalStorageBuffer) return _.diag(SPV_ERROR_INVALID_DATA, inst) - << "Pointer storage class must be PhysicalStorageBufferEXT: " + << "Pointer storage class must be PhysicalStorageBuffer: " << spvOpcodeString(opcode); + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (_.GetBitWidth(input_type) != 64) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4710) + << "PhysicalStorageBuffer64 addressing mode requires the " + "input integer to have a 64-bit width for Vulkan " + "environment."; + } + } } break; } diff --git a/3rdparty/spirv-tools/source/val/validate_decorations.cpp b/3rdparty/spirv-tools/source/val/validate_decorations.cpp index 01b0ecabc..ed336b477 100644 --- a/3rdparty/spirv-tools/source/val/validate_decorations.cpp +++ b/3rdparty/spirv-tools/source/val/validate_decorations.cpp @@ -379,6 +379,7 @@ bool IsAlignedTo(uint32_t offset, uint32_t alignment) { // or row major-ness. spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, const char* decoration_str, bool blockRules, + bool scalar_block_layout, uint32_t incoming_offset, MemberConstraints& constraints, ValidationState_t& vstate) { @@ -392,7 +393,6 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, // For example, relaxed layout is implied by Vulkan 1.1. But scalar layout // is more permissive than relaxed layout. const bool relaxed_block_layout = vstate.IsRelaxedBlockLayout(); - const bool scalar_block_layout = vstate.options()->scalar_block_layout; auto fail = [&vstate, struct_id, storage_class_str, decoration_str, blockRules, relaxed_block_layout, @@ -501,6 +501,7 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, if (SpvOpTypeStruct == opcode && SPV_SUCCESS != (recursive_status = checkLayout( id, storage_class_str, decoration_str, blockRules, + scalar_block_layout, offset, constraints, vstate))) return recursive_status; // Check matrix stride. @@ -553,7 +554,8 @@ spv_result_t checkLayout(uint32_t struct_id, const char* storage_class_str, if (SpvOpTypeStruct == element_inst->opcode() && SPV_SUCCESS != (recursive_status = checkLayout( typeId, storage_class_str, decoration_str, - blockRules, next_offset, constraints, vstate))) + blockRules, scalar_block_layout, + next_offset, constraints, vstate))) return recursive_status; // If offsets accumulate up to a 16-byte multiple stop checking since // it will just repeat. @@ -698,6 +700,9 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { const auto& descs = vstate.entry_point_descriptions(entry_point); int num_builtin_inputs = 0; int num_builtin_outputs = 0; + int num_workgroup_variables = 0; + int num_workgroup_variables_with_block = 0; + int num_workgroup_variables_with_aliased = 0; for (const auto& desc : descs) { std::unordered_set seen_vars; for (auto interface : desc.interfaces) { @@ -754,6 +759,16 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { if (auto error = CheckBuiltInVariable(interface, vstate)) return error; } + + if (storage_class == SpvStorageClassWorkgroup) { + ++num_workgroup_variables; + if (type_instr && SpvOpTypeStruct == type_instr->opcode()) { + if (hasDecoration(type_id, SpvDecorationBlock, vstate)) + ++num_workgroup_variables_with_block; + if (hasDecoration(var_instr->id(), SpvDecorationAliased, vstate)) + ++num_workgroup_variables_with_aliased; + } + } } if (num_builtin_inputs > 1 || num_builtin_outputs > 1) { return vstate.diag(SPV_ERROR_INVALID_BINARY, @@ -777,6 +792,30 @@ spv_result_t CheckDecorationsOfEntryPoints(ValidationState_t& vstate) { << " because it is targeted by an OpEntryPoint instruction."; } } + + if (vstate.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayoutKHR) && + num_workgroup_variables > 0 && + num_workgroup_variables_with_block > 0) { + if (num_workgroup_variables != num_workgroup_variables_with_block) { + return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point)) + << "When declaring WorkgroupMemoryExplicitLayoutKHR, " + "either all or none of the Workgroup Storage Class variables " + "in the entry point interface must point to struct types " + "decorated with Block. Entry point id " + << entry_point << " does not meet this requirement."; + } + if (num_workgroup_variables_with_block > 1 && + num_workgroup_variables_with_block != + num_workgroup_variables_with_aliased) { + return vstate.diag(SPV_ERROR_INVALID_BINARY, vstate.FindDef(entry_point)) + << "When declaring WorkgroupMemoryExplicitLayoutKHR, " + "if more than one Workgroup Storage Class variable in " + "the entry point interface point to a type decorated " + "with Block, all of them must be decorated with Aliased. " + "Entry point id " + << entry_point << " does not meet this requirement."; + } + } } } return SPV_SUCCESS; @@ -942,14 +981,16 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { const bool phys_storage_buffer = storageClass == SpvStorageClassPhysicalStorageBufferEXT; - if (uniform || push_constant || storage_buffer || phys_storage_buffer) { + const bool workgroup = storageClass == SpvStorageClassWorkgroup; + if (uniform || push_constant || storage_buffer || phys_storage_buffer || + workgroup) { const auto ptrInst = vstate.FindDef(words[1]); assert(SpvOpTypePointer == ptrInst->opcode()); auto id = ptrInst->words()[3]; auto id_inst = vstate.FindDef(id); // Jump through one level of arraying. - if (id_inst->opcode() == SpvOpTypeArray || - id_inst->opcode() == SpvOpTypeRuntimeArray) { + if (!workgroup && (id_inst->opcode() == SpvOpTypeArray || + id_inst->opcode() == SpvOpTypeRuntimeArray)) { id = id_inst->GetOperandAs(1u); id_inst = vstate.FindDef(id); } @@ -961,7 +1002,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { // Prepare for messages const char* sc_str = uniform ? "Uniform" - : (push_constant ? "PushConstant" : "StorageBuffer"); + : (push_constant ? "PushConstant" + : (workgroup ? "Workgroup" + : "StorageBuffer")); if (spvIsVulkanEnv(vstate.context()->target_env)) { const bool block = hasDecoration(id, SpvDecorationBlock, vstate); @@ -1029,8 +1072,9 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { const bool bufferDeco = SpvDecorationBufferBlock == dec.dec_type(); const bool blockRules = uniform && blockDeco; const bool bufferRules = - (uniform && bufferDeco) || (push_constant && blockDeco) || - ((storage_buffer || phys_storage_buffer) && blockDeco); + (uniform && bufferDeco) || + ((push_constant || storage_buffer || + phys_storage_buffer || workgroup) && blockDeco); if (uniform && blockDeco) { vstate.RegisterPointerToUniformBlock(ptrInst->id()); vstate.RegisterStructForUniformBlock(id); @@ -1044,6 +1088,10 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { if (blockRules || bufferRules) { const char* deco_str = blockDeco ? "Block" : "BufferBlock"; spv_result_t recursive_status = SPV_SUCCESS; + const bool scalar_block_layout = workgroup ? + vstate.options()->workgroup_scalar_block_layout : + vstate.options()->scalar_block_layout; + if (isMissingOffsetInStruct(id, vstate)) { return vstate.diag(SPV_ERROR_INVALID_ID, vstate.FindDef(id)) << "Structure id " << id << " decorated as " << deco_str @@ -1072,12 +1120,14 @@ spv_result_t CheckDecorationsOfBuffers(ValidationState_t& vstate) { "decorations."; } else if (blockRules && (SPV_SUCCESS != (recursive_status = checkLayout( - id, sc_str, deco_str, true, 0, + id, sc_str, deco_str, true, + scalar_block_layout, 0, constraints, vstate)))) { return recursive_status; } else if (bufferRules && (SPV_SUCCESS != (recursive_status = checkLayout( - id, sc_str, deco_str, false, 0, + id, sc_str, deco_str, false, + scalar_block_layout, 0, constraints, vstate)))) { return recursive_status; } diff --git a/3rdparty/spirv-tools/source/val/validate_extensions.cpp b/3rdparty/spirv-tools/source/val/validate_extensions.cpp index af6ae2b7a..dc8c0243a 100644 --- a/3rdparty/spirv-tools/source/val/validate_extensions.cpp +++ b/3rdparty/spirv-tools/source/val/validate_extensions.cpp @@ -27,6 +27,7 @@ #include "source/latest_version_glsl_std_450_header.h" #include "source/latest_version_opencl_std_header.h" #include "source/opcode.h" +#include "source/spirv_constant.h" #include "source/spirv_target_env.h" #include "source/val/instruction.h" #include "source/val/validate.h" @@ -685,6 +686,20 @@ bool IsDebugVariableWithIntScalarType(ValidationState_t& _, } // anonymous namespace +spv_result_t ValidateExtension(ValidationState_t& _, const Instruction* inst) { + if (_.version() < SPV_SPIRV_VERSION_WORD(1, 4)) { + std::string extension = GetExtensionString(&(inst->c_inst())); + if (extension == + ExtensionToString(kSPV_KHR_workgroup_memory_explicit_layout)) { + return _.diag(SPV_ERROR_WRONG_VERSION, inst) + << "SPV_KHR_workgroup_memory_explicit_layout extension " + "requires SPIR-V version 1.4 or later."; + } + } + + return SPV_SUCCESS; +} + spv_result_t ValidateExtInstImport(ValidationState_t& _, const Instruction* inst) { const auto name_id = 1; @@ -3124,6 +3139,7 @@ spv_result_t ValidateExtInst(ValidationState_t& _, const Instruction* inst) { spv_result_t ExtensionPass(ValidationState_t& _, const Instruction* inst) { const SpvOp opcode = inst->opcode(); + if (opcode == SpvOpExtension) return ValidateExtension(_, inst); if (opcode == SpvOpExtInstImport) return ValidateExtInstImport(_, inst); if (opcode == SpvOpExtInst) return ValidateExtInst(_, inst); diff --git a/3rdparty/spirv-tools/source/val/validate_image.cpp b/3rdparty/spirv-tools/source/val/validate_image.cpp index 78dfefa21..bdb2516e7 100644 --- a/3rdparty/spirv-tools/source/val/validate_image.cpp +++ b/3rdparty/spirv-tools/source/val/validate_image.cpp @@ -441,6 +441,17 @@ spv_result_t ValidateImageOperands(ValidationState_t& _, << "Expected Image Operand Offset to have " << plane_size << " components, but given " << offset_size; } + + if (spvIsVulkanEnv(_.context()->target_env)) { + if (opcode != SpvOpImageGather && opcode != SpvOpImageDrefGather && + opcode != SpvOpImageSparseGather && + opcode != SpvOpImageSparseDrefGather) { + return _.diag(SPV_ERROR_INVALID_DATA, inst) + << _.VkErrorID(4663) + << "Image Operand Offset can only be used with " + "OpImage*Gather operations"; + } + } } if (mask & SpvImageOperandsConstOffsetsMask) { @@ -888,6 +899,7 @@ spv_result_t ValidateTypeSampledImage(ValidationState_t& _, // Vulkan uses the Sampled=1 case. 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"; } @@ -1445,12 +1457,21 @@ spv_result_t ValidateImageGather(ValidationState_t& _, } if (opcode == SpvOpImageGather || opcode == SpvOpImageSparseGather) { - const uint32_t component_index_type = _.GetOperandTypeId(inst, 4); + const uint32_t component = inst->GetOperandAs(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 == SpvOpImageDrefGather || opcode == SpvOpImageSparseDrefGather); diff --git a/3rdparty/spirv-tools/source/val/validate_memory.cpp b/3rdparty/spirv-tools/source/val/validate_memory.cpp index 45a232daf..4dd6d9415 100644 --- a/3rdparty/spirv-tools/source/val/validate_memory.cpp +++ b/3rdparty/spirv-tools/source/val/validate_memory.cpp @@ -407,6 +407,10 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { << "' is not a pointer type."; } + const auto type_index = 2; + const auto value_id = result_type->GetOperandAs(type_index); + auto value_type = _.FindDef(value_id); + const auto initializer_index = 3; const auto storage_class_index = 2; if (initializer_index < inst->operands().size()) { @@ -423,7 +427,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { << "OpVariable Initializer '" << _.getIdName(initializer_id) << "' is not a constant or module-scope variable."; } - if (initializer->type_id() != result_type->GetOperandAs(2u)) { + if (initializer->type_id() != value_id) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "Initializer type must match the type pointed to by the Result " "Type"; @@ -440,9 +444,6 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { storage_class != SpvStorageClassHitAttributeNV && storage_class != SpvStorageClassCallableDataNV && storage_class != SpvStorageClassIncomingCallableDataNV) { - const auto storage_index = 2; - const auto storage_id = result_type->GetOperandAs(storage_index); - const auto storage = _.FindDef(storage_id); bool storage_input_or_output = storage_class == SpvStorageClassInput || storage_class == SpvStorageClassOutput; bool builtin = false; @@ -455,7 +456,7 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } } if (!(storage_input_or_output && builtin) && - ContainsInvalidBool(_, storage, storage_input_or_output)) { + ContainsInvalidBool(_, value_type, storage_input_or_output)) { return _.diag(SPV_ERROR_INVALID_ID, inst) << "If OpTypeBool is stored in conjunction with OpVariable, it " << "can only be used with non-externally visible shader Storage " @@ -535,18 +536,14 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { if (!IsAllowedTypeOrArrayOfSame( _, pointee, {SpvOpTypeImage, SpvOpTypeSampler, SpvOpTypeSampledImage, - SpvOpTypeAccelerationStructureNV, - SpvOpTypeAccelerationStructureKHR, SpvOpTypeRayQueryKHR})) { + SpvOpTypeAccelerationStructureKHR})) { return _.diag(SPV_ERROR_INVALID_ID, inst) - << "UniformConstant OpVariable '" << _.getIdName(inst->id()) - << "' has illegal type.\n" - << "From Vulkan spec, section 14.5.2:\n" + << _.VkErrorID(4655) << "UniformConstant OpVariable '" + << _.getIdName(inst->id()) << "' has illegal type.\n" << "Variables identified with the UniformConstant storage class " << "are used only as handles to refer to opaque resources. Such " << "variables must be typed as OpTypeImage, OpTypeSampler, " - << "OpTypeSampledImage, OpTypeAccelerationStructureNV, " - "OpTypeAccelerationStructureKHR, " - "OpTypeRayQueryKHR, " + << "OpTypeSampledImage, OpTypeAccelerationStructureKHR, " << "or an array of one of these types."; } } @@ -576,6 +573,28 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { "of this type"; } } + + // Check for invalid use of Invariant + if (storage_class != SpvStorageClassInput && + storage_class != SpvStorageClassOutput) { + if (_.HasDecoration(inst->id(), SpvDecorationInvariant)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4677) + << "Variable decorated with Invariant must only be identified " + "with the Input or Output storage class in Vulkan " + "environment."; + } + // Need to check if only the members in a struct are decorated + if (value_type && value_type->opcode() == SpvOpTypeStruct) { + if (_.HasDecoration(value_id, SpvDecorationInvariant)) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4677) + << "Variable struct member decorated with Invariant must only " + "be identified with the Input or Output storage class in " + "Vulkan environment."; + } + } + } } // Vulkan Appendix A: Check that if contains initializer, then @@ -584,16 +603,26 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { storage_class != SpvStorageClassPrivate && storage_class != SpvStorageClassFunction) { if (spvIsVulkanEnv(_.context()->target_env)) { - return _.diag(SPV_ERROR_INVALID_ID, inst) - << _.VkErrorID(4651) << "OpVariable, '" - << _.getIdName(inst->id()) - << "', has a disallowed initializer & storage class " - << "combination.\n" - << "From " << spvLogStringForEnv(_.context()->target_env) - << " spec:\n" - << "Variable declarations that include initializers must have " - << "one of the following storage classes: Output, Private, or " - << "Function"; + if (storage_class == SpvStorageClassWorkgroup) { + auto init_id = inst->GetOperandAs(3); + auto init = _.FindDef(init_id); + if (init->opcode() != SpvOpConstantNull) { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << "Variable initializers in Workgroup storage class are " + "limited to OpConstantNull"; + } + } else { + return _.diag(SPV_ERROR_INVALID_ID, inst) + << _.VkErrorID(4651) << "OpVariable, '" + << _.getIdName(inst->id()) + << "', has a disallowed initializer & storage class " + << "combination.\n" + << "From " << spvLogStringForEnv(_.context()->target_env) + << " spec:\n" + << "Variable declarations that include initializers must have " + << "one of the following storage classes: Output, Private, " + << "Function or Workgroup"; + } } } @@ -630,9 +659,6 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { } // Vulkan specific validation rules for OpTypeRuntimeArray - const auto type_index = 2; - const auto value_id = result_type->GetOperandAs(type_index); - auto value_type = _.FindDef(value_id); if (spvIsVulkanEnv(_.context()->target_env)) { // OpTypeRuntimeArray should only ever be in a container like OpTypeStruct, // so should never appear as a bare variable. @@ -749,6 +775,11 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { storage_class_ok = false; } break; + case SpvStorageClassWorkgroup: + if (!_.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR)) { + storage_class_ok = false; + } + break; default: return _.diag(SPV_ERROR_INVALID_ID, inst) << "Cannot allocate a variable containing a 16-bit type in " @@ -800,6 +831,11 @@ spv_result_t ValidateVariable(ValidationState_t& _, const Instruction* inst) { storage_class_ok = false; } break; + case SpvStorageClassWorkgroup: + if (!_.HasCapability(SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR)) { + storage_class_ok = false; + } + break; default: return _.diag(SPV_ERROR_INVALID_ID, inst) << "Cannot allocate a variable containing a 8-bit type in " diff --git a/3rdparty/spirv-tools/source/val/validation_state.cpp b/3rdparty/spirv-tools/source/val/validation_state.cpp index d61956d0a..6dfc7bf6b 100644 --- a/3rdparty/spirv-tools/source/val/validation_state.cpp +++ b/3rdparty/spirv-tools/source/val/validation_state.cpp @@ -359,6 +359,7 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) { case SpvCapabilityStorageBuffer8BitAccess: case SpvCapabilityUniformAndStorageBuffer8BitAccess: case SpvCapabilityStoragePushConstant8: + case SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR: features_.declare_int8_type = true; break; case SpvCapabilityInt16: @@ -372,6 +373,7 @@ void ValidationState_t::RegisterCapability(SpvCapability cap) { case SpvCapabilityStorageUniform16: case SpvCapabilityStoragePushConstant16: case SpvCapabilityStorageInputOutput16: + case SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR: features_.declare_int16_type = true; features_.declare_float16_type = true; features_.free_fp_rounding_mode = true; @@ -1692,24 +1694,36 @@ std::string ValidationState_t::VkErrorID(uint32_t id, return VUID_WRAP(VUID-StandaloneSpirv-OriginLowerLeft-04653); case 4654: return VUID_WRAP(VUID-StandaloneSpirv-PixelCenterInteger-04654); + case 4655: + return VUID_WRAP(VUID-StandaloneSpirv-UniformConstant-04655); case 4656: return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-04656); case 4657: return VUID_WRAP(VUID-StandaloneSpirv-OpTypeImage-04657); case 4658: return VUID_WRAP(VUID-StandaloneSpirv-OpImageTexelPointer-04658); + case 4659: + return VUID_WRAP(VUID-StandaloneSpirv-OpImageQuerySizeLod-04659); case 4662: return VUID_WRAP(VUID-StandaloneSpirv-Offset-04662); + case 4663: + return VUID_WRAP(VUID-StandaloneSpirv-Offset-04663); + case 4664: + return VUID_WRAP(VUID-StandaloneSpirv-OpImageGather-04664); case 4669: return VUID_WRAP(VUID-StandaloneSpirv-GLSLShared-04669); case 4675: return VUID_WRAP(VUID-StandaloneSpirv-FPRoundingMode-04675); + case 4677: + return VUID_WRAP(VUID-StandaloneSpirv-Invariant-04677); case 4683: return VUID_WRAP(VUID-StandaloneSpirv-LocalSize-04683); case 4685: return VUID_WRAP(VUID-StandaloneSpirv-OpGroupNonUniformBallotBitCount-04685); case 4686: return VUID_WRAP(VUID-StandaloneSpirv-None-04686); + case 4710: + return VUID_WRAP(VUID-StandaloneSpirv-PhysicalStorageBuffer64-04710); case 4711: return VUID_WRAP(VUID-StandaloneSpirv-OpTypeForwardPointer-04711); case 4730: diff --git a/3rdparty/spirv-tools/source/val/validation_state.h b/3rdparty/spirv-tools/source/val/validation_state.h index aeb1ca861..851113914 100644 --- a/3rdparty/spirv-tools/source/val/validation_state.h +++ b/3rdparty/spirv-tools/source/val/validation_state.h @@ -103,6 +103,10 @@ class ValidationState_t { // Members need not be listed in offset order bool scalar_block_layout = false; + // Use scalar block layout (as defined above) for Workgroup block + // variables. See VK_KHR_workgroup_memory_explicit_layout. + bool workgroup_scalar_block_layout = false; + // SPIR-V 1.4 allows us to select between any two composite values // of the same type. bool select_between_composites = false;