diff --git a/Android.mk b/Android.mk index 3cf3f19b1d2..c46475099f2 100644 --- a/Android.mk +++ b/Android.mk @@ -4,6 +4,9 @@ SPVTOOLS_OUT_PATH=$(if $(call host-path-is-absolute,$(TARGET_OUT)),$(TARGET_OUT) ifeq ($(SPVHEADERS_LOCAL_PATH),) SPVHEADERS_LOCAL_PATH := $(LOCAL_PATH)/external/spirv-headers endif +ifeq ($(VKHEADERS_LOCAL_PATH),) + VKHEADERS_LOCAL_PATH := $(LOCAL_PATH)/external/vulkan-headers +endif SPVTOOLS_SRC_FILES := \ source/assembly_grammar.cpp \ @@ -192,6 +195,8 @@ SPVTOOLS_OPT_SRC_FILES := \ source/opt/upgrade_memory_model.cpp \ source/opt/value_number_table.cpp \ source/opt/vector_dce.cpp \ + source/opt/vksp_passes.cpp \ + source/opt/vksp_passes.h \ source/opt/workaround1209.cpp \ source/opt/wrap_opkill.cpp @@ -289,6 +294,7 @@ $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-bal $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-explicit-vertex-parameter,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),spv-amd-shader-trinary-minmax,"")) $(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),nonsemantic.clspvreflection,"")) +$(eval $(call gen_spvtools_vendor_tables,$(SPVTOOLS_OUT_PATH),nonsemantic.vkspreflection,"")) define gen_spvtools_enum_string_mapping $(call generate-file-dir,$(1)/extension_enum.inc.inc) @@ -354,6 +360,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/include \ $(LOCAL_PATH)/source \ $(SPVHEADERS_LOCAL_PATH)/include \ + $(VKHEADERS_LOCAL_PATH)/include \ $(SPVTOOLS_OUT_PATH) LOCAL_CXXFLAGS:=-std=c++17 -fno-exceptions -fno-rtti -Werror LOCAL_STATIC_LIBRARIES:=SPIRV-Tools diff --git a/BUILD.bazel b/BUILD.bazel index b83fd5aeadc..ed29122cc23 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -58,6 +58,8 @@ generate_vendor_tables(extension = "debuginfo") generate_vendor_tables(extension = "nonsemantic.clspvreflection") +generate_vendor_tables(extension = "nonsemantic.vkspreflection") + generate_vendor_tables( extension = "opencl.debuginfo.100", operand_kind_prefix = "CLDEBUG100_", @@ -147,6 +149,7 @@ cc_library( ":gen_vendor_tables_debuginfo", ":gen_vendor_tables_nonsemantic_clspvreflection", ":gen_vendor_tables_nonsemantic_shader_debuginfo_100", + ":gen_vendor_tables_nonsemantic_vkspreflection", ":gen_vendor_tables_opencl_debuginfo_100", ":gen_vendor_tables_spv_amd_gcn_shader", ":gen_vendor_tables_spv_amd_shader_ballot", @@ -201,6 +204,7 @@ cc_library( deps = [ ":spirv_tools_internal", "@spirv_headers//:spirv_common_headers", + "@vulkan_headers//:vulkan_headers_config", ], ) diff --git a/BUILD.gn b/BUILD.gn index 7c42a99a53f..d3bfa1e42f5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -30,6 +30,7 @@ if (defined(is_fuchsia_tree) && is_fuchsia_tree) { spvtools_build_executables = true } +vulkan_headers = spirv_tools_vulkan_headers_dir spirv_headers = spirv_tools_spirv_headers_dir spirv_is_winuwp = is_win && target_os == "winuwp" @@ -331,6 +332,10 @@ spvtools_vendor_tables = [ "nonsemantic.shader.debuginfo.100", "SHDEBUG100_", ], + [ + "nonsemantic.vkspreflection", + "...nil...", + ], ] foreach(table_def, spvtools_vendor_tables) { @@ -810,6 +815,8 @@ static_library("spvtools_opt") { "source/opt/value_number_table.h", "source/opt/vector_dce.cpp", "source/opt/vector_dce.h", + "source/opt/vksp_passes.cpp", + "source/opt/vksp_passes.h", "source/opt/workaround1209.cpp", "source/opt/workaround1209.h", "source/opt/wrap_opkill.cpp", @@ -825,6 +832,7 @@ static_library("spvtools_opt") { ":spvtools_headers", ":spvtools_language_header_cldebuginfo100", ":spvtools_language_header_vkdebuginfo100", + "${vulkan_headers}:vulkan_headers" ] if (build_with_chromium) { diff --git a/DEPS b/DEPS index 323728a34c9..4002d480c48 100644 --- a/DEPS +++ b/DEPS @@ -14,6 +14,7 @@ vars = { 're2_revision': 'ed9fc269e2fdb299afe59e912928d31ad3fdcf7d', 'spirv_headers_revision': '05cc486580771e4fa7ddc89f5c9ee1e97382689a', + 'vulkan_headers_revision': '217e93c664ec6704ec2d8c36fa116c1a4a1e2d40', } deps = { @@ -35,5 +36,9 @@ deps = { 'external/spirv-headers': Var('github') + '/KhronosGroup/SPIRV-Headers.git@' + Var('spirv_headers_revision'), + + 'external/vulkan-headers': + Var('github') + '/KhronosGroup/Vulkan-Headers.git@' + + Var('vulkan_headers_revision'), } diff --git a/README.md b/README.md index 042b83da136..f08062230f6 100644 --- a/README.md +++ b/README.md @@ -53,13 +53,13 @@ Some versions of SPIRV-Tools are tagged as stable releases (see [tags](https://github.com/KhronosGroup/SPIRV-Tools/tags) on github). These versions undergo extra testing. Releases are not directly related to releases (or versions) of -[SPIRV-Headers][spirv-headers]. -Releases of SPIRV-Tools are tested against the version of SPIRV-Headers listed -in the [DEPS](DEPS) file. +[SPIRV-Headers][spirv-headers] and [Vulkan-Headers][vulkan-headers]. +Releases of SPIRV-Tools are tested against the version of SPIRV-Headers and +Vulkan-Headers listed in the [DEPS](DEPS) file. The release generally uses the most recent compatible version of SPIRV-Headers -available at the time of release. -No version of SPIRV-Headers other than the one listed in the DEPS file is -guaranteed to work with the SPIRV-Tools release. +and Vulkan-Headers available at the time of release. +No version of SPIRV-Headers and Vulkan-Headers other than the one listed in the +DEPS file is guaranteed to work with the SPIRV-Tools release. ## Supported features @@ -67,7 +67,7 @@ guaranteed to work with the SPIRV-Tools release. * Support for SPIR-V 1.0, through 1.5 * Based on SPIR-V syntax described by JSON grammar files in the - [SPIRV-Headers](https://github.com/KhronosGroup/SPIRV-Headers) repository. + [SPIRV-Headers][spirv-headers] repository. * Usually, support for a new version of SPIR-V is ready within days after publication. * Support for extended instruction sets: @@ -289,11 +289,12 @@ Example of getting sources, assuming SPIRV-Tools is configured as a standalone p For some kinds of development, you may need the latest sources from the third-party projects: - git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers - git clone https://github.com/google/googletest.git spirv-tools/external/googletest - git clone https://github.com/google/effcee.git spirv-tools/external/effcee - git clone https://github.com/google/re2.git spirv-tools/external/re2 - git clone https://github.com/abseil/abseil-cpp.git spirv-tools/external/abseil_cpp + git clone https://github.com/KhronosGroup/SPIRV-Headers.git spirv-tools/external/spirv-headers + git clone https://github.com/KhronosGroup/Vulkan-Headers.git spirv-tools/external/vulkan-headers + git clone https://github.com/google/googletest.git spirv-tools/external/googletest + git clone https://github.com/google/effcee.git spirv-tools/external/effcee + git clone https://github.com/google/re2.git spirv-tools/external/re2 + git clone https://github.com/abseil/abseil-cpp.git spirv-tools/external/abseil_cpp #### Dependency on Effcee @@ -322,6 +323,8 @@ Effcee itself depends on [RE2][re2], and RE2 depends on [Abseil][abseil-cpp]. * `include/`: API clients should add this directory to the include search path * `external/spirv-headers`: Intended location for [SPIR-V headers][spirv-headers], not provided +* `external/vulkan-headers`: Intended location for + [Vulkan headers][vulkan-headers], not provided * `include/spirv-tools/libspirv.h`: C API public interface * `source/`: API implementation * `test/`: Tests, using the [googletest][googletest] framework @@ -792,6 +795,7 @@ limitations under the License. [spirv-tools-mailing-list]: https://www.khronos.org/spir/spirv-tools-mailing-list [spirv-registry]: https://www.khronos.org/registry/spir-v/ [spirv-headers]: https://github.com/KhronosGroup/SPIRV-Headers +[vulkan-headers]: https://github.com/KhronosGroup/Vulkan-Headers [googletest]: https://github.com/google/googletest [googletest-pull-612]: https://github.com/google/googletest/pull/612 [googletest-issue-610]: https://github.com/google/googletest/issues/610 diff --git a/build_overrides/spirv_tools.gni b/build_overrides/spirv_tools.gni index 24aa033d7b1..a2cade1c8f8 100644 --- a/build_overrides/spirv_tools.gni +++ b/build_overrides/spirv_tools.gni @@ -23,3 +23,4 @@ spirv_tools_standalone = true # The path to SPIRV-Tools' dependencies spirv_tools_googletest_dir = "//external/googletest" spirv_tools_spirv_headers_dir = "//external/spirv-headers" +spirv_tools_vulkan_headers_dir = "//external/vulkan-headers" diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 5d8a3dab092..9f3b802c1bb 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -48,6 +48,10 @@ else() "SPIRV-Headers was not found - please checkout a copy under external/.") endif() +if (NOT DEFINED VULKAN_HEADER_INCLUDE_DIR) + set(VULKAN_HEADER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/vulkan-headers) +endif() + if (NOT ${SPIRV_SKIP_TESTS}) # Find gmock if we can. If it's not already configured, then try finding # it in external/googletest. diff --git a/include/spirv-tools/libspirv.h b/include/spirv-tools/libspirv.h index abdfc15d4fe..030e2c0f909 100644 --- a/include/spirv-tools/libspirv.h +++ b/include/spirv-tools/libspirv.h @@ -327,6 +327,7 @@ typedef enum spv_ext_inst_type_t { SPV_EXT_INST_TYPE_OPENCL_DEBUGINFO_100, SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100, + SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION, // Multiple distinct extended instruction set types could return this // value, if they are prefixed with NonSemantic. and are otherwise diff --git a/include/spirv-tools/optimizer.hpp b/include/spirv-tools/optimizer.hpp index 926e438fe2f..4500eedee13 100644 --- a/include/spirv-tools/optimizer.hpp +++ b/include/spirv-tools/optimizer.hpp @@ -1007,6 +1007,141 @@ Optimizer::PassToken CreateInvocationInterlockPlacementPass(); // Creates a pass to add/remove maximal reconvergence execution mode. // This pass either adds or removes maximal reconvergence from all entry points. Optimizer::PassToken CreateModifyMaximalReconvergencePass(bool add); + +struct vksp_push_constant { + uint32_t offset; + uint32_t size; + uint32_t stageFlags; + const char* pValues; +}; + +#define VKSP_DESCRIPTOR_TYPE_STORAGE_BUFFER_COUNTER_BITS (0xf0000000) +#define VKSP_DESCRIPTOR_TYPE_STORAGE_BUFFER_COUNTER_MASK \ + (~VKSP_DESCRIPTOR_TYPE_STORAGE_BUFFER_COUNTER_BITS) +#define VKSP_DESCRIPTOR_TYPE_STORAGE_BUFFER_COUNTER \ + (VK_DESCRIPTOR_TYPE_STORAGE_BUFFER | \ + VKSP_DESCRIPTOR_TYPE_STORAGE_BUFFER_COUNTER_BITS) +struct vksp_descriptor_set { + uint32_t ds; + uint32_t binding; + uint32_t type; + union { + struct { + uint32_t flags; + uint32_t queueFamilyIndexCount; + uint32_t sharingMode; + uint32_t size; + uint32_t usage; + uint32_t range; + uint32_t offset; + uint32_t memorySize; + uint32_t memoryType; + uint32_t bindOffset; + } buffer; + struct { + uint32_t imageLayout; + uint32_t imageFlags; + uint32_t imageType; + uint32_t format; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t mipLevels; + uint32_t arrayLayers; + uint32_t samples; + uint32_t tiling; + uint32_t usage; + uint32_t sharingMode; + uint32_t queueFamilyIndexCount; + uint32_t initialLayout; + uint32_t aspectMask; + uint32_t baseMipLevel; + uint32_t levelCount; + uint32_t baseArrayLayer; + uint32_t layerCount; + uint32_t viewFlags; + uint32_t viewType; + uint32_t viewFormat; + uint32_t component_a; + uint32_t component_b; + uint32_t component_g; + uint32_t component_r; + uint32_t memorySize; + uint32_t memoryType; + uint32_t bindOffset; + } image; + struct { + uint32_t flags; + uint32_t magFilter; + uint32_t minFilter; + uint32_t mipmapMode; + uint32_t addressModeU; + uint32_t addressModeV; + uint32_t addressModeW; + union { + float fMipLodBias; + uint32_t uMipLodBias; + }; + uint32_t anisotropyEnable; + union { + float fMaxAnisotropy; + uint32_t uMaxAnisotropy; + }; + uint32_t compareEnable; + uint32_t compareOp; + union { + float fMinLod; + uint32_t uMinLod; + }; + union { + float fMaxLod; + uint32_t uMaxLod; + }; + uint32_t borderColor; + uint32_t unnormalizedCoordinates; + } sampler; + }; +}; + +struct vksp_configuration { + const char* enabledExtensionNames; + uint32_t specializationInfoDataSize; + const char* specializationInfoData; + const char* shaderName; + const char* entryPoint; + uint32_t groupCountX; + uint32_t groupCountY; + uint32_t groupCountZ; +}; + +struct vksp_specialization_map_entry { + uint32_t constantID; + uint32_t offset; + uint32_t size; +}; + +struct vksp_counter { + uint32_t index; + const char* name; +}; + +// Creates a vksp insert reflection info pass. +// This pass inserts vksp non semantic instructions representing the different +// vksp structures passed in argument in the module. +Optimizer::PassToken CreateInsertVkspReflectInfoPass( + std::vector* pc, std::vector* ds, + std::vector* me, vksp_configuration* config); + +// Creates a vksp extract reflection info pass. +// This pass extracts the vksp information into the different vksp structures +// from the vksp non semantic instructions present in the module. +// It also adds a global counter for the whole module as well as replacing all +// `StartCounter` and `StopCounter` non semantic instructions by a real counter. +Optimizer::PassToken CreateExtractVkspReflectInfoPass( + std::vector* pc, std::vector* ds, + std::vector* me, + std::vector* counters, vksp_configuration* config); + } // namespace spvtools #endif // INCLUDE_SPIRV_TOOLS_OPTIMIZER_HPP_ diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index f4ee3c84cf6..d0454c6c706 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -156,6 +156,7 @@ spvtools_vendor_tables("debuginfo" "debuginfo" "") spvtools_vendor_tables("opencl.debuginfo.100" "cldi100" "CLDEBUG100_") spvtools_vendor_tables("nonsemantic.shader.debuginfo.100" "shdi100" "SHDEBUG100_") spvtools_vendor_tables("nonsemantic.clspvreflection" "clspvreflection" "") +spvtools_vendor_tables("nonsemantic.vkspreflection" "vkspreflection" "") spvtools_extinst_lang_headers("DebugInfo" ${DEBUGINFO_GRAMMAR_JSON_FILE}) spvtools_extinst_lang_headers("OpenCLDebugInfo100" ${CLDEBUGINFO100_GRAMMAR_JSON_FILE}) spvtools_extinst_lang_headers("NonSemanticShaderDebugInfo100" ${VKDEBUGINFO100_GRAMMAR_JSON_FILE}) diff --git a/source/ext_inst.cpp b/source/ext_inst.cpp index 4e2795453f4..9a5ba84e466 100644 --- a/source/ext_inst.cpp +++ b/source/ext_inst.cpp @@ -30,6 +30,7 @@ #include "glsl.std.450.insts.inc" #include "nonsemantic.clspvreflection.insts.inc" #include "nonsemantic.shader.debuginfo.100.insts.inc" +#include "nonsemantic.vkspreflection.insts.inc" #include "opencl.debuginfo.100.insts.inc" #include "opencl.std.insts.inc" @@ -62,6 +63,9 @@ static const spv_ext_inst_group_t kGroups_1_0[] = { {SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION, ARRAY_SIZE(nonsemantic_clspvreflection_entries), nonsemantic_clspvreflection_entries}, + {SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION, + ARRAY_SIZE(nonsemantic_vkspreflection_entries), + nonsemantic_vkspreflection_entries}, }; static const spv_ext_inst_table_t kTable_1_0 = {ARRAY_SIZE(kGroups_1_0), @@ -138,6 +142,9 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) { if (!strncmp("NonSemantic.ClspvReflection.", name, 28)) { return SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION; } + if (!strncmp("NonSemantic.VkspReflection.", name, 27)) { + return SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION; + } // ensure to add any known non-semantic extended instruction sets // above this point, and update spvExtInstIsNonSemantic() if (!strncmp("NonSemantic.", name, 12)) { @@ -149,7 +156,8 @@ spv_ext_inst_type_t spvExtInstImportTypeGet(const char* name) { bool spvExtInstIsNonSemantic(const spv_ext_inst_type_t type) { if (type == SPV_EXT_INST_TYPE_NONSEMANTIC_UNKNOWN || type == SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100 || - type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) { + type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION || + type == SPV_EXT_INST_TYPE_NONSEMANTIC_VKSPREFLECTION) { return true; } return false; diff --git a/source/opt/CMakeLists.txt b/source/opt/CMakeLists.txt index 4e7d92d5eac..8d1fbfbcd83 100644 --- a/source/opt/CMakeLists.txt +++ b/source/opt/CMakeLists.txt @@ -134,6 +134,7 @@ set(SPIRV_TOOLS_OPT_SOURCES vector_dce.h workaround1209.h wrap_opkill.h + vksp_passes.h fix_func_call_arguments.cpp aggressive_dead_code_elim_pass.cpp @@ -252,6 +253,7 @@ set(SPIRV_TOOLS_OPT_SOURCES vector_dce.cpp workaround1209.cpp wrap_opkill.cpp + vksp_passes.cpp ) if(MSVC AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) @@ -269,7 +271,9 @@ target_include_directories(SPIRV-Tools-opt $ $ $ - PRIVATE ${spirv-tools_BINARY_DIR} + PRIVATE + ${spirv-tools_BINARY_DIR} + ${VULKAN_HEADER_INCLUDE_DIR} ) # We need the assembling and disassembling functionalities in the main library. target_link_libraries(SPIRV-Tools-opt diff --git a/source/opt/constants.cpp b/source/opt/constants.cpp index a487a45b885..6eebbb572a9 100644 --- a/source/opt/constants.cpp +++ b/source/opt/constants.cpp @@ -498,7 +498,7 @@ const Constant* ConstantManager::GetIntConst(uint64_t val, int32_t bitWidth, int32_t num_of_bit_to_ignore = 64 - bitWidth; val = static_cast(val << num_of_bit_to_ignore) >> num_of_bit_to_ignore; - } else { + } else if (bitWidth < 64) { // Clear the upper bit that are not used. uint64_t mask = ((1ull << bitWidth) - 1); val &= mask; @@ -511,7 +511,7 @@ const Constant* ConstantManager::GetIntConst(uint64_t val, int32_t bitWidth, // If the value is more than 32-bit, we need to split the operands into two // 32-bit integers. return GetConstant( - int_type, {static_cast(val >> 32), static_cast(val)}); + int_type, {static_cast(val), static_cast(val >> 32)}); } uint32_t ConstantManager::GetUIntConstId(uint32_t val) { diff --git a/source/opt/optimizer.cpp b/source/opt/optimizer.cpp index c4c2b0f554a..7f133125523 100644 --- a/source/opt/optimizer.cpp +++ b/source/opt/optimizer.cpp @@ -1166,6 +1166,23 @@ Optimizer::PassToken CreateModifyMaximalReconvergencePass(bool add) { return MakeUnique( MakeUnique(add)); } + +Optimizer::PassToken CreateInsertVkspReflectInfoPass( + std::vector* pc, std::vector* ds, + std::vector* me, + vksp_configuration* config) { + return MakeUnique( + MakeUnique(pc, ds, me, config)); +} +Optimizer::PassToken CreateExtractVkspReflectInfoPass( + std::vector* pc, std::vector* ds, + std::vector* me, + std::vector* counters, vksp_configuration* config) { + return MakeUnique( + MakeUnique(pc, ds, me, counters, + config)); +} + } // namespace spvtools extern "C" { diff --git a/source/opt/passes.h b/source/opt/passes.h index 9d027fbf4c3..c26fb10b8e3 100644 --- a/source/opt/passes.h +++ b/source/opt/passes.h @@ -91,5 +91,6 @@ #include "source/opt/vector_dce.h" #include "source/opt/workaround1209.h" #include "source/opt/wrap_opkill.h" +#include "source/opt/vksp_passes.h" #endif // SOURCE_OPT_PASSES_H_ diff --git a/source/opt/vksp_passes.cpp b/source/opt/vksp_passes.cpp new file mode 100644 index 00000000000..82338604113 --- /dev/null +++ b/source/opt/vksp_passes.cpp @@ -0,0 +1,784 @@ +// Copyright (c) 2023 Google LLC +// +// 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. + +#include "source/opt/vksp_passes.h" + +#include "source/opt/instruction.h" +#include "source/opt/types.h" +#include "spirv-tools/libspirv.h" +#include "spirv-tools/optimizer.hpp" +#include "spirv/unified1/NonSemanticVkspReflection.h" +#include "spirv/unified1/spirv.hpp11" +#include "vulkan/vulkan.h" + +#define UNDEFINED_ID (UINT32_MAX) + +namespace spvtools { +namespace opt { + +Pass::Status InsertVkspReflectInfoPass::Process() { + auto module = context()->module(); + + std::vector ext_words = + spvtools::utils::MakeVector("NonSemantic.VkspReflection.1"); + auto ExtInstId = context()->TakeNextId(); + auto ExtInst = + new Instruction(context(), spv::Op::OpExtInstImport, 0u, ExtInstId, + {{SPV_OPERAND_TYPE_LITERAL_STRING, ext_words}}); + module->AddExtInstImport(std::unique_ptr(ExtInst)); + + uint32_t void_ty_id = context()->get_type_mgr()->GetVoidTypeId(); + + std::vector enabledExtensions = + spvtools::utils::MakeVector(config_->enabledExtensionNames); + std::vector pData = + spvtools::utils::MakeVector(config_->specializationInfoData); + std::vector shaderName = + spvtools::utils::MakeVector(config_->shaderName); + std::vector entryPoint = + spvtools::utils::MakeVector(config_->entryPoint); + auto ConfigId = context()->TakeNextId(); + auto ConfigInst = new Instruction( + context(), spv::Op::OpExtInst, void_ty_id, ConfigId, + { + {SPV_OPERAND_TYPE_ID, {ExtInstId}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {NonSemanticVkspReflectionConfiguration}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, enabledExtensions}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {config_->specializationInfoDataSize}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, pData}, + {SPV_OPERAND_TYPE_LITERAL_STRING, shaderName}, + {SPV_OPERAND_TYPE_LITERAL_STRING, entryPoint}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {config_->groupCountX}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {config_->groupCountY}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {config_->groupCountZ}}, + }); + module->AddExtInstDebugInfo(std::unique_ptr(ConfigInst)); + + for (auto& pc : *pc_) { + std::vector pValues = spvtools::utils::MakeVector(pc.pValues); + auto PcInstId = context()->TakeNextId(); + auto PcInst = + new Instruction(context(), spv::Op::OpExtInst, void_ty_id, PcInstId, + { + {SPV_OPERAND_TYPE_ID, {ExtInstId}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {NonSemanticVkspReflectionPushConstants}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {pc.offset}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {pc.size}}, + {SPV_OPERAND_TYPE_LITERAL_STRING, pValues}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {pc.stageFlags}}, + }); + module->AddExtInstDebugInfo(std::unique_ptr(PcInst)); + } + + for (auto& ds : *ds_) { + switch (ds.type) { + case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: + case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { + auto DsInstId = context()->TakeNextId(); + auto DstInst = new Instruction( + context(), spv::Op::OpExtInst, void_ty_id, DsInstId, + { + {SPV_OPERAND_TYPE_ID, {ExtInstId}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {NonSemanticVkspReflectionDescriptorSetBuffer}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.ds}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.binding}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.type}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.buffer.flags}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {ds.buffer.queueFamilyIndexCount}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.buffer.sharingMode}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.buffer.size}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.buffer.usage}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.buffer.range}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.buffer.offset}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.buffer.memorySize}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.buffer.memoryType}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.buffer.bindOffset}}, + }); + module->AddExtInstDebugInfo(std::unique_ptr(DstInst)); + } break; + case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: + case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: { + auto DsInstId = context()->TakeNextId(); + auto DstInst = new Instruction( + context(), spv::Op::OpExtInst, void_ty_id, DsInstId, + { + {SPV_OPERAND_TYPE_ID, {ExtInstId}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {NonSemanticVkspReflectionDescriptorSetImage}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.ds}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.binding}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.type}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.imageLayout}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.imageFlags}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.imageType}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.format}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.width}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.height}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.depth}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.mipLevels}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.arrayLayers}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.samples}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.tiling}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.usage}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.sharingMode}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {ds.image.queueFamilyIndexCount}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.initialLayout}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.aspectMask}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.baseMipLevel}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.levelCount}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.baseArrayLayer}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.layerCount}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.viewFlags}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.viewType}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.viewFormat}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.component_a}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.component_b}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.component_g}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.component_r}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.memorySize}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.memoryType}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.image.bindOffset}}, + }); + module->AddExtInstDebugInfo(std::unique_ptr(DstInst)); + } break; + case VK_DESCRIPTOR_TYPE_SAMPLER: { + auto DsInstId = context()->TakeNextId(); + auto DstInst = new Instruction( + context(), spv::Op::OpExtInst, void_ty_id, DsInstId, + { + {SPV_OPERAND_TYPE_ID, {ExtInstId}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {NonSemanticVkspReflectionDescriptorSetSampler}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.ds}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.binding}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.type}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.sampler.flags}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.sampler.magFilter}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.sampler.minFilter}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.sampler.mipmapMode}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.sampler.addressModeU}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.sampler.addressModeV}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.sampler.addressModeW}}, + {SPV_OPERAND_TYPE_LITERAL_FLOAT, {ds.sampler.uMipLodBias}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {ds.sampler.anisotropyEnable}}, + {SPV_OPERAND_TYPE_LITERAL_FLOAT, {ds.sampler.uMaxAnisotropy}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.sampler.compareEnable}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.sampler.compareOp}}, + {SPV_OPERAND_TYPE_LITERAL_FLOAT, {ds.sampler.uMinLod}}, + {SPV_OPERAND_TYPE_LITERAL_FLOAT, {ds.sampler.uMaxLod}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {ds.sampler.borderColor}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, + {ds.sampler.unnormalizedCoordinates}}, + }); + module->AddExtInstDebugInfo(std::unique_ptr(DstInst)); + } break; + default: + break; + } + } + + for (auto& me : *me_) { + auto MapEntryId = context()->TakeNextId(); + auto MapEntryInst = + new Instruction(context(), spv::Op::OpExtInst, void_ty_id, MapEntryId, + { + {SPV_OPERAND_TYPE_ID, {ExtInstId}}, + {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER, + {NonSemanticVkspReflectionSpecializationMapEntry}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {me.constantID}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {me.offset}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {me.size}}, + }); + module->AddExtInstDebugInfo(std::unique_ptr(MapEntryInst)); + } + + return Status::SuccessWithChange; +} + +int32_t ExtractVkspReflectInfoPass::UpdateMaxBinding(uint32_t ds, + uint32_t binding, + int32_t max_binding) { + if (ds != 0) { + return max_binding; + } else { + return std::max(max_binding, (int32_t)binding); + } +} + +void ExtractVkspReflectInfoPass::ParseInstruction( + Instruction* inst, uint32_t ext_inst_id, + std::map& id_to_descriptor_set, + std::map& id_to_binding, + int32_t& descriptor_set_0_max_binding, + std::vector& start_counters, + std::vector& stop_counters) { + uint32_t op_id = 2; + if (inst->opcode() == spv::Op::OpDecorate) { + spv::Decoration decoration = (spv::Decoration)inst->GetOperand(1).words[0]; + if (decoration == spv::Decoration::DescriptorSet) { + auto id = inst->GetOperand(0).AsId(); + auto ds = inst->GetOperand(2).words[0]; + id_to_descriptor_set[id] = ds; + if (ds == 0 && id_to_binding.count(id) > 0) { + descriptor_set_0_max_binding = UpdateMaxBinding( + ds, id_to_binding[id], descriptor_set_0_max_binding); + } + } else if (decoration == spv::Decoration::Binding) { + auto id = inst->GetOperand(0).AsId(); + auto binding = inst->GetOperand(2).words[0]; + id_to_binding[id] = binding; + if (id_to_descriptor_set.count(id) > 0) { + descriptor_set_0_max_binding = UpdateMaxBinding( + id_to_descriptor_set[id], binding, descriptor_set_0_max_binding); + } + } + return; + } else if (inst->opcode() != spv::Op::OpExtInst || + ext_inst_id != inst->GetOperand(op_id++).AsId()) { + return; + } + + auto vksp_inst = inst->GetOperand(op_id++).words[0]; + switch (vksp_inst) { + case NonSemanticVkspReflectionConfiguration: + config_->enabledExtensionNames = + strdup(inst->GetOperand(op_id++).AsString().c_str()); + config_->specializationInfoDataSize = inst->GetOperand(op_id++).words[0]; + config_->specializationInfoData = + strdup(inst->GetOperand(op_id++).AsString().c_str()); + config_->shaderName = + strdup(inst->GetOperand(op_id++).AsString().c_str()); + config_->entryPoint = + strdup(inst->GetOperand(op_id++).AsString().c_str()); + config_->groupCountX = inst->GetOperand(op_id++).words[0]; + config_->groupCountY = inst->GetOperand(op_id++).words[0]; + config_->groupCountZ = inst->GetOperand(op_id++).words[0]; + break; + case NonSemanticVkspReflectionDescriptorSetBuffer: { + vksp_descriptor_set ds; + ds.ds = inst->GetOperand(op_id++).words[0]; + ds.binding = inst->GetOperand(op_id++).words[0]; + ds.type = inst->GetOperand(op_id++).words[0]; + ds.buffer.flags = inst->GetOperand(op_id++).words[0]; + ds.buffer.queueFamilyIndexCount = inst->GetOperand(op_id++).words[0]; + ds.buffer.sharingMode = inst->GetOperand(op_id++).words[0]; + ds.buffer.size = inst->GetOperand(op_id++).words[0]; + ds.buffer.usage = inst->GetOperand(op_id++).words[0]; + ds.buffer.range = inst->GetOperand(op_id++).words[0]; + ds.buffer.offset = inst->GetOperand(op_id++).words[0]; + ds.buffer.memorySize = inst->GetOperand(op_id++).words[0]; + ds.buffer.memoryType = inst->GetOperand(op_id++).words[0]; + ds.buffer.bindOffset = inst->GetOperand(op_id++).words[0]; + ds_->push_back(ds); + descriptor_set_0_max_binding = + UpdateMaxBinding(ds.ds, ds.binding, descriptor_set_0_max_binding); + } break; + case NonSemanticVkspReflectionDescriptorSetImage: { + vksp_descriptor_set ds; + ds.ds = inst->GetOperand(op_id++).words[0]; + ds.binding = inst->GetOperand(op_id++).words[0]; + ds.type = inst->GetOperand(op_id++).words[0]; + ds.image.imageLayout = inst->GetOperand(op_id++).words[0]; + ds.image.imageFlags = inst->GetOperand(op_id++).words[0]; + ds.image.imageType = inst->GetOperand(op_id++).words[0]; + ds.image.format = inst->GetOperand(op_id++).words[0]; + ds.image.width = inst->GetOperand(op_id++).words[0]; + ds.image.height = inst->GetOperand(op_id++).words[0]; + ds.image.depth = inst->GetOperand(op_id++).words[0]; + ds.image.mipLevels = inst->GetOperand(op_id++).words[0]; + ds.image.arrayLayers = inst->GetOperand(op_id++).words[0]; + ds.image.samples = inst->GetOperand(op_id++).words[0]; + ds.image.tiling = inst->GetOperand(op_id++).words[0]; + ds.image.usage = inst->GetOperand(op_id++).words[0]; + ds.image.sharingMode = inst->GetOperand(op_id++).words[0]; + ds.image.queueFamilyIndexCount = inst->GetOperand(op_id++).words[0]; + ds.image.initialLayout = inst->GetOperand(op_id++).words[0]; + ds.image.aspectMask = inst->GetOperand(op_id++).words[0]; + ds.image.baseMipLevel = inst->GetOperand(op_id++).words[0]; + ds.image.levelCount = inst->GetOperand(op_id++).words[0]; + ds.image.baseArrayLayer = inst->GetOperand(op_id++).words[0]; + ds.image.layerCount = inst->GetOperand(op_id++).words[0]; + ds.image.viewFlags = inst->GetOperand(op_id++).words[0]; + ds.image.viewType = inst->GetOperand(op_id++).words[0]; + ds.image.viewFormat = inst->GetOperand(op_id++).words[0]; + ds.image.component_a = inst->GetOperand(op_id++).words[0]; + ds.image.component_b = inst->GetOperand(op_id++).words[0]; + ds.image.component_g = inst->GetOperand(op_id++).words[0]; + ds.image.component_r = inst->GetOperand(op_id++).words[0]; + ds.image.memorySize = inst->GetOperand(op_id++).words[0]; + ds.image.memoryType = inst->GetOperand(op_id++).words[0]; + ds.image.bindOffset = inst->GetOperand(op_id++).words[0]; + ds_->push_back(ds); + descriptor_set_0_max_binding = + UpdateMaxBinding(ds.ds, ds.binding, descriptor_set_0_max_binding); + } break; + case NonSemanticVkspReflectionDescriptorSetSampler: { + vksp_descriptor_set ds; + ds.ds = inst->GetOperand(op_id++).words[0]; + ds.binding = inst->GetOperand(op_id++).words[0]; + ds.type = inst->GetOperand(op_id++).words[0]; + ds.sampler.flags = inst->GetOperand(op_id++).words[0]; + ds.sampler.magFilter = inst->GetOperand(op_id++).words[0]; + ds.sampler.minFilter = inst->GetOperand(op_id++).words[0]; + ds.sampler.mipmapMode = inst->GetOperand(op_id++).words[0]; + ds.sampler.addressModeU = inst->GetOperand(op_id++).words[0]; + ds.sampler.addressModeV = inst->GetOperand(op_id++).words[0]; + ds.sampler.addressModeW = inst->GetOperand(op_id++).words[0]; + ds.sampler.uMipLodBias = inst->GetOperand(op_id++).words[0]; + ds.sampler.anisotropyEnable = inst->GetOperand(op_id++).words[0]; + ds.sampler.uMaxAnisotropy = inst->GetOperand(op_id++).words[0]; + ds.sampler.compareEnable = inst->GetOperand(op_id++).words[0]; + ds.sampler.compareOp = inst->GetOperand(op_id++).words[0]; + ds.sampler.uMinLod = inst->GetOperand(op_id++).words[0]; + ds.sampler.uMaxLod = inst->GetOperand(op_id++).words[0]; + ds.sampler.borderColor = inst->GetOperand(op_id++).words[0]; + ds.sampler.unnormalizedCoordinates = inst->GetOperand(op_id++).words[0]; + ds_->push_back(ds); + descriptor_set_0_max_binding = + UpdateMaxBinding(ds.ds, ds.binding, descriptor_set_0_max_binding); + } break; + case NonSemanticVkspReflectionPushConstants: + vksp_push_constant pc; + pc.offset = inst->GetOperand(op_id++).words[0]; + pc.size = inst->GetOperand(op_id++).words[0]; + pc.pValues = strdup(inst->GetOperand(op_id++).AsString().c_str()); + pc.stageFlags = inst->GetOperand(op_id++).words[0]; + pc_->push_back(pc); + break; + case NonSemanticVkspReflectionSpecializationMapEntry: + vksp_specialization_map_entry me; + me.constantID = inst->GetOperand(op_id++).words[0]; + me.offset = inst->GetOperand(op_id++).words[0]; + me.size = inst->GetOperand(op_id++).words[0]; + me_->push_back(me); + break; + case NonSemanticVkspReflectionStartCounter: + start_counters.push_back(inst); + break; + case NonSemanticVkspReflectionStopCounter: + stop_counters.push_back(inst); + break; + default: + break; + } +} + +void ExtractVkspReflectInfoPass::CreateVariables( + uint32_t u64_arr_ty_id, uint32_t u64_arr_st_ty_id, + uint32_t local_counters_ty_id, uint32_t counters_ty_id, + uint32_t global_counters_ds, uint32_t global_counters_binding, + uint32_t& global_counters_id, uint32_t& local_counters_id) { + auto module = context()->module(); + + auto decorate_arr_inst = new Instruction( + context(), spv::Op::OpDecorate, 0, 0, + {{SPV_OPERAND_TYPE_ID, {u64_arr_ty_id}}, + {SPV_OPERAND_TYPE_DECORATION, {(uint32_t)spv::Decoration::ArrayStride}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {8}}}); + module->AddAnnotationInst(std::unique_ptr(decorate_arr_inst)); + + auto decorate_member_offset_inst = new Instruction( + context(), spv::Op::OpMemberDecorate, 0, 0, + {{SPV_OPERAND_TYPE_ID, {u64_arr_st_ty_id}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0}}, + {SPV_OPERAND_TYPE_DECORATION, {(uint32_t)spv::Decoration::Offset}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {0}}}); + module->AddAnnotationInst( + std::unique_ptr(decorate_member_offset_inst)); + + auto decorate_arr_st_inst = new Instruction( + context(), spv::Op::OpDecorate, 0, 0, + {{SPV_OPERAND_TYPE_ID, {u64_arr_st_ty_id}}, + {SPV_OPERAND_TYPE_DECORATION, {(uint32_t)spv::Decoration::Block}}}); + module->AddAnnotationInst(std::unique_ptr(decorate_arr_st_inst)); + + if (local_counters_ty_id != UNDEFINED_ID) { + local_counters_id = context()->TakeNextId(); + auto local_counters_inst = new Instruction( + context(), spv::Op::OpVariable, local_counters_ty_id, local_counters_id, + {{SPV_OPERAND_TYPE_LITERAL_INTEGER, + {(uint32_t)spv::StorageClass::Private}}}); + module->AddGlobalValue(std::unique_ptr(local_counters_inst)); + } else { + local_counters_id = UNDEFINED_ID; + } + + global_counters_id = context()->TakeNextId(); + auto global_counters_inst = new Instruction( + context(), spv::Op::OpVariable, counters_ty_id, global_counters_id, + {{SPV_OPERAND_TYPE_LITERAL_INTEGER, + {(uint32_t)spv::StorageClass::StorageBuffer}}}); + module->AddGlobalValue(std::unique_ptr(global_counters_inst)); + + auto counters_descriptor_set_inst = new Instruction( + context(), spv::Op::OpDecorate, 0, 0, + {{SPV_OPERAND_TYPE_ID, {global_counters_inst->result_id()}}, + {SPV_OPERAND_TYPE_DECORATION, + {(uint32_t)spv::Decoration::DescriptorSet}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {global_counters_ds}}}); + module->AddAnnotationInst( + std::unique_ptr(counters_descriptor_set_inst)); + + auto counters_binding_inst = new Instruction( + context(), spv::Op::OpDecorate, 0, 0, + {{SPV_OPERAND_TYPE_ID, {global_counters_inst->result_id()}}, + {SPV_OPERAND_TYPE_DECORATION, {(uint32_t)spv::Decoration::Binding}}, + {SPV_OPERAND_TYPE_LITERAL_INTEGER, {global_counters_binding}}}); + module->AddAnnotationInst( + std::unique_ptr(counters_binding_inst)); +} + +void ExtractVkspReflectInfoPass::CreatePrologue( + Instruction* entry_point_inst, uint32_t u64_private_ptr_ty_id, + uint32_t u64_ty_id, uint32_t subgroup_scope_id, uint32_t global_counters_id, + uint32_t local_counters_id, std::vector& start_counters, + Function*& function, uint32_t& read_clock_id) { + auto* cst_mgr = context()->get_constant_mgr(); + entry_point_inst->AddOperand({SPV_OPERAND_TYPE_ID, {global_counters_id}}); + if (local_counters_id != UNDEFINED_ID) { + entry_point_inst->AddOperand({SPV_OPERAND_TYPE_ID, {local_counters_id}}); + } + + auto function_id = entry_point_inst->GetOperand(1).AsId(); + function = context()->GetFunction(function_id); + + auto& function_first_inst = *function->entry()->begin(); + + auto u64_cst0_id = + cst_mgr->GetDefiningInstruction(cst_mgr->GetIntConst(0, 64, 0)) + ->result_id(); + + for (unsigned i = 0; i < start_counters.size(); i++) { + auto get_id = context()->TakeNextId(); + auto gep_inst = new Instruction( + context(), spv::Op::OpAccessChain, u64_private_ptr_ty_id, get_id, + {{SPV_OPERAND_TYPE_ID, {local_counters_id}}, + {SPV_OPERAND_TYPE_ID, {cst_mgr->GetUIntConstId(i)}}}); + gep_inst->InsertBefore(&function_first_inst); + + auto store_inst = new Instruction(context(), spv::Op::OpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {get_id}}, + {SPV_OPERAND_TYPE_ID, {u64_cst0_id}}}); + store_inst->InsertAfter(gep_inst); + } + + read_clock_id = context()->TakeNextId(); + auto read_clock_inst = new Instruction( + context(), spv::Op::OpReadClockKHR, u64_ty_id, read_clock_id, + {{SPV_OPERAND_TYPE_SCOPE_ID, {subgroup_scope_id}}}); + read_clock_inst->InsertBefore(&function_first_inst); +} + +void ExtractVkspReflectInfoPass::CreateEpilogue( + Instruction* return_inst, uint32_t read_clock_id, uint32_t u64_ty_id, + uint32_t u64_ptr_ty_id, uint32_t u64_private_ptr_ty_id, + uint32_t subgroup_scope_id, uint32_t device_scope_id, + uint32_t acq_rel_mem_sem_id, uint32_t global_counters_id, + uint32_t local_counters_id, std::vector& start_counters) { + auto* cst_mgr = context()->get_constant_mgr(); + + auto read_clock_end_id = context()->TakeNextId(); + auto read_clock_end_inst = new Instruction( + context(), spv::Op::OpReadClockKHR, u64_ty_id, read_clock_end_id, + {{SPV_OPERAND_TYPE_SCOPE_ID, {subgroup_scope_id}}}); + read_clock_end_inst->InsertBefore(return_inst); + + auto substraction_id = context()->TakeNextId(); + auto substraction_inst = + new Instruction(context(), spv::Op::OpISub, u64_ty_id, substraction_id, + {{SPV_OPERAND_TYPE_ID, {read_clock_end_id}}, + {SPV_OPERAND_TYPE_ID, {read_clock_id}}}); + substraction_inst->InsertAfter(read_clock_end_inst); + + auto gep_invocations_id = context()->TakeNextId(); + auto gep_invocations_inst = new Instruction( + context(), spv::Op::OpAccessChain, u64_ptr_ty_id, gep_invocations_id, + {{SPV_OPERAND_TYPE_ID, {global_counters_id}}, + {SPV_OPERAND_TYPE_ID, {cst_mgr->GetUIntConstId(0)}}, + {SPV_OPERAND_TYPE_ID, {cst_mgr->GetUIntConstId(0)}}}); + gep_invocations_inst->InsertAfter(substraction_inst); + + auto atomic_incr_id = context()->TakeNextId(); + auto atomic_incr_inst = new Instruction( + context(), spv::Op::OpAtomicIIncrement, u64_ty_id, atomic_incr_id, + {{SPV_OPERAND_TYPE_ID, {gep_invocations_id}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {device_scope_id}}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, {acq_rel_mem_sem_id}}}); + atomic_incr_inst->InsertAfter(gep_invocations_inst); + + auto gep_entrypoint_counter_id = context()->TakeNextId(); + auto gep_entrypoint_counter_inst = + new Instruction(context(), spv::Op::OpAccessChain, u64_ptr_ty_id, + gep_entrypoint_counter_id, + {{SPV_OPERAND_TYPE_ID, {global_counters_id}}, + {SPV_OPERAND_TYPE_ID, {cst_mgr->GetUIntConstId(0)}}, + {SPV_OPERAND_TYPE_ID, {cst_mgr->GetUIntConstId(1)}}}); + gep_entrypoint_counter_inst->InsertAfter(atomic_incr_inst); + + auto atomic_add_id = context()->TakeNextId(); + auto atomic_add_inst = new Instruction( + context(), spv::Op::OpAtomicIAdd, u64_ty_id, atomic_add_id, + {{SPV_OPERAND_TYPE_ID, {gep_entrypoint_counter_id}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {device_scope_id}}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, {acq_rel_mem_sem_id}}, + {SPV_OPERAND_TYPE_ID, {substraction_id}}}); + atomic_add_inst->InsertAfter(gep_entrypoint_counter_inst); + + for (unsigned i = 0; i < start_counters.size(); i++) { + auto gep_id = context()->TakeNextId(); + auto gep_inst = new Instruction( + context(), spv::Op::OpAccessChain, u64_private_ptr_ty_id, gep_id, + {{SPV_OPERAND_TYPE_ID, {local_counters_id}}, + {SPV_OPERAND_TYPE_ID, {cst_mgr->GetUIntConstId(i)}}}); + gep_inst->InsertAfter(atomic_add_inst); + + auto load_id = context()->TakeNextId(); + auto load_inst = + new Instruction(context(), spv::Op::OpLoad, u64_ty_id, load_id, + {{SPV_OPERAND_TYPE_ID, {gep_id}}}); + load_inst->InsertAfter(gep_inst); + + auto gep_atomic_id = context()->TakeNextId(); + auto gep_atomic_inst = new Instruction( + context(), spv::Op::OpAccessChain, u64_ptr_ty_id, gep_atomic_id, + {{SPV_OPERAND_TYPE_ID, {global_counters_id}}, + {SPV_OPERAND_TYPE_ID, {cst_mgr->GetUIntConstId(0)}}, + {SPV_OPERAND_TYPE_ID, {cst_mgr->GetUIntConstId(2 + i)}}}); + gep_atomic_inst->InsertAfter(load_inst); + + atomic_add_id = context()->TakeNextId(); + atomic_add_inst = new Instruction( + context(), spv::Op::OpAtomicIAdd, u64_ty_id, atomic_add_id, + {{SPV_OPERAND_TYPE_ID, {gep_atomic_id}}, + {SPV_OPERAND_TYPE_SCOPE_ID, {device_scope_id}}, + {SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID, {acq_rel_mem_sem_id}}, + {SPV_OPERAND_TYPE_ID, {load_id}}}); + atomic_add_inst->InsertAfter(gep_atomic_inst); + } +} + +void ExtractVkspReflectInfoPass::CreateCounters( + uint32_t u64_ty_id, uint32_t u64_private_ptr_ty_id, + uint32_t subgroup_scope_id, std::vector& start_counters, + std::vector& stop_counters, uint32_t local_counters_id) { + auto* cst_mgr = context()->get_constant_mgr(); + std::map> start_counters_id_map; + uint32_t next_counter_id = 2; + + for (auto* inst : start_counters) { + const char* counter_name = strdup(inst->GetOperand(4).AsString().c_str()); + + auto read_clock_id = context()->TakeNextId(); + auto read_clock_inst = new Instruction( + context(), spv::Op::OpReadClockKHR, u64_ty_id, read_clock_id, + {{SPV_OPERAND_TYPE_SCOPE_ID, {subgroup_scope_id}}}); + read_clock_inst->InsertBefore(inst); + + counters_->push_back({next_counter_id, counter_name}); + start_counters_id_map[inst->result_id()] = + std::make_pair(read_clock_id, next_counter_id); + next_counter_id++; + } + + for (auto* inst : stop_counters) { + auto read_clock_ext_inst_id = inst->GetOperand(4).AsId(); + if (start_counters_id_map.count(read_clock_ext_inst_id) == 0) { + continue; + } + auto pair = start_counters_id_map[read_clock_ext_inst_id]; + auto read_clock_id = pair.first; + auto counters_var_index = pair.second; + + auto read_clock_end_id = context()->TakeNextId(); + auto read_clock_end_inst = new Instruction( + context(), spv::Op::OpReadClockKHR, u64_ty_id, read_clock_end_id, + {{SPV_OPERAND_TYPE_SCOPE_ID, {subgroup_scope_id}}}); + read_clock_end_inst->InsertAfter(inst); + + auto substraction_id = context()->TakeNextId(); + auto substraction_inst = + new Instruction(context(), spv::Op::OpISub, u64_ty_id, substraction_id, + {{SPV_OPERAND_TYPE_ID, {read_clock_end_id}}, + {SPV_OPERAND_TYPE_ID, {read_clock_id}}}); + substraction_inst->InsertAfter(read_clock_end_inst); + + auto gep_id = context()->TakeNextId(); + auto gep_inst = new Instruction( + context(), spv::Op::OpAccessChain, u64_private_ptr_ty_id, gep_id, + {{SPV_OPERAND_TYPE_ID, {local_counters_id}}, + {SPV_OPERAND_TYPE_ID, + {cst_mgr->GetUIntConstId(counters_var_index - 2)}}}); + gep_inst->InsertAfter(substraction_inst); + + auto load_id = context()->TakeNextId(); + auto load_inst = + new Instruction(context(), spv::Op::OpLoad, u64_ty_id, load_id, + {{SPV_OPERAND_TYPE_ID, {gep_id}}}); + load_inst->InsertAfter(gep_inst); + + auto add_id = context()->TakeNextId(); + auto add_inst = + new Instruction(context(), spv::Op::OpIAdd, u64_ty_id, add_id, + {{SPV_OPERAND_TYPE_ID, {load_id}}, + {SPV_OPERAND_TYPE_ID, {substraction_id}}}); + add_inst->InsertAfter(load_inst); + + auto store_inst = new Instruction( + context(), spv::Op::OpStore, 0, 0, + {{SPV_OPERAND_TYPE_ID, {gep_id}}, {SPV_OPERAND_TYPE_ID, {add_id}}}); + store_inst->InsertAfter(add_inst); + } +} + +Pass::Status ExtractVkspReflectInfoPass::Process() { + auto module = context()->module(); + uint32_t ext_inst_id = + module->GetExtInstImportId("NonSemantic.VkspReflection.1"); + int32_t descriptor_set_0_max_binding = -1; + std::map id_to_descriptor_set; + std::map id_to_binding; + std::vector start_counters; + std::vector stop_counters; + + module->ForEachInst([this, ext_inst_id, &id_to_descriptor_set, &id_to_binding, + &descriptor_set_0_max_binding, &start_counters, + &stop_counters](Instruction* inst) { + ParseInstruction(inst, ext_inst_id, id_to_descriptor_set, id_to_binding, + descriptor_set_0_max_binding, start_counters, + stop_counters); + }); + + context()->AddExtension("SPV_KHR_shader_clock"); + context()->AddExtension("SPV_KHR_storage_buffer_storage_class"); + context()->AddCapability(spv::Capability::ShaderClockKHR); + context()->AddCapability(spv::Capability::Int64); + context()->AddCapability(spv::Capability::Int64Atomics); + + uint32_t global_counters_ds = 0; + uint32_t global_counters_binding = descriptor_set_0_max_binding + 1; + auto counters_size = + (uint32_t)(sizeof(uint64_t) * + (2 + start_counters + .size())); // 2 for the number of invocations and the + // time of the whole entry point + ds_->push_back( + {global_counters_ds, + global_counters_binding, + (uint32_t)VKSP_DESCRIPTOR_TYPE_STORAGE_BUFFER_COUNTER, + {.buffer = {0, 0, VK_SHARING_MODE_EXCLUSIVE, counters_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + counters_size, 0, counters_size, UINT32_MAX, 0}}}); + + auto* cst_mgr = context()->get_constant_mgr(); + auto* type_mgr = context()->get_type_mgr(); + + auto u64_ty = type_mgr->GetIntType(64, 0); + analysis::RuntimeArray run_arr(u64_ty); + auto u64_run_arr_ty = type_mgr->GetRegisteredType(&run_arr); + analysis::Struct st({u64_run_arr_ty}); + auto u64_run_arr_st_ty = type_mgr->GetRegisteredType(&st); + analysis::Pointer u64_run_arr_st_ty_ptr(u64_run_arr_st_ty, + spv::StorageClass::StorageBuffer); + auto u64_run_arr_st_ptr_ty = + type_mgr->GetRegisteredType(&u64_run_arr_st_ty_ptr); + + analysis::Pointer u64_ty_ptr(u64_ty, spv::StorageClass::StorageBuffer); + auto u64_ptr_ty = type_mgr->GetRegisteredType(&u64_ty_ptr); + + auto counters_ty_id = type_mgr->GetId(u64_run_arr_st_ptr_ty); + auto u64_ty_id = type_mgr->GetId(u64_ty); + auto u64_ptr_ty_id = type_mgr->GetId(u64_ptr_ty); + auto u64_arr_ty_id = type_mgr->GetId(u64_run_arr_ty); + auto u64_arr_st_ty_id = type_mgr->GetId(u64_run_arr_st_ty); + + uint32_t local_counters_ty_id = UNDEFINED_ID; + uint32_t u64_private_ptr_ty_id = UNDEFINED_ID; + + if (start_counters.size() > 0) { + analysis::Array arr( + u64_ty, analysis::Array::LengthInfo{ + cst_mgr->GetUIntConstId((uint32_t)start_counters.size()), + {0, (uint32_t)start_counters.size()}}); + auto u64_arr_ty = type_mgr->GetRegisteredType(&arr); + analysis::Pointer u64_arr_ty_ptr(u64_arr_ty, spv::StorageClass::Private); + auto u64_arr_ptr_ty = type_mgr->GetRegisteredType(&u64_arr_ty_ptr); + analysis::Pointer u64_ty_ptr_private(u64_ty, spv::StorageClass::Private); + auto u64_private_ptr_ty = type_mgr->GetRegisteredType(&u64_ty_ptr_private); + + local_counters_ty_id = type_mgr->GetId(u64_arr_ptr_ty); + u64_private_ptr_ty_id = type_mgr->GetId(u64_private_ptr_ty); + } + + auto subgroup_scope_id = + cst_mgr->GetUIntConstId((uint32_t)spv::Scope::Subgroup); + auto device_scope_id = cst_mgr->GetUIntConstId((uint32_t)spv::Scope::Device); + auto acq_rel_mem_sem_id = cst_mgr->GetUIntConstId( + (uint32_t)spv::MemorySemanticsMask::AcquireRelease); + + uint32_t global_counters_id; + uint32_t local_counters_id; + CreateVariables(u64_arr_ty_id, u64_arr_st_ty_id, local_counters_ty_id, + counters_ty_id, global_counters_ds, global_counters_binding, + global_counters_id, local_counters_id); + + bool found = false; + for (auto& entry_point_inst : module->entry_points()) { + auto function_name = entry_point_inst.GetOperand(2).AsString(); + if (function_name != std::string(config_->entryPoint)) { + continue; + } + found = true; + + uint32_t read_clock_id; + Function* function; + CreatePrologue(&entry_point_inst, u64_private_ptr_ty_id, u64_ty_id, + subgroup_scope_id, global_counters_id, local_counters_id, + start_counters, function, read_clock_id); + + function->ForEachInst([this, read_clock_id, u64_ty_id, u64_ptr_ty_id, + u64_private_ptr_ty_id, subgroup_scope_id, + device_scope_id, acq_rel_mem_sem_id, + global_counters_id, local_counters_id, + &start_counters](Instruction* inst) { + if (inst->opcode() != spv::Op::OpReturn) { + return; + } + CreateEpilogue(inst, read_clock_id, u64_ty_id, u64_ptr_ty_id, + u64_private_ptr_ty_id, subgroup_scope_id, device_scope_id, + acq_rel_mem_sem_id, global_counters_id, local_counters_id, + start_counters); + }); + + break; + } + if (!found) { + return Status::Failure; + } + + CreateCounters(u64_ty_id, u64_private_ptr_ty_id, subgroup_scope_id, + start_counters, stop_counters, local_counters_id); + + return Status::SuccessWithChange; +} + +} // namespace opt +} // namespace spvtools diff --git a/source/opt/vksp_passes.h b/source/opt/vksp_passes.h new file mode 100644 index 00000000000..cd9fdfd376b --- /dev/null +++ b/source/opt/vksp_passes.h @@ -0,0 +1,101 @@ +// Copyright (c) 2023 Google LLC +// +// 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. + +#ifndef SOURCE_OPT_VKSP_PASSES_H_ +#define SOURCE_OPT_VKSP_PASSES_H_ + +#include "source/opt/module.h" +#include "source/opt/pass.h" +#include "spirv-tools/optimizer.hpp" + +namespace spvtools { +namespace opt { + +class InsertVkspReflectInfoPass : public Pass { + public: + InsertVkspReflectInfoPass(std::vector* pc, + std::vector* ds, + std::vector* me, + vksp_configuration* config) + : pc_(pc), ds_(ds), me_(me), config_(config) {} + const char* name() const override { return "insert-vksp-reflect-info"; } + Status Process() override; + + private: + std::vector* pc_; + std::vector* ds_; + std::vector* me_; + vksp_configuration* config_; +}; + +class ExtractVkspReflectInfoPass : public Pass { + public: + ExtractVkspReflectInfoPass(std::vector* pc, + std::vector* ds, + std::vector* me, + std::vector* counters, + vksp_configuration* config) + : pc_(pc), ds_(ds), me_(me), counters_(counters), config_(config) {} + const char* name() const override { return "extract-vksp-reflect-info"; } + Status Process() override; + + private: + int32_t UpdateMaxBinding(uint32_t ds, uint32_t binding, int32_t max_binding); + + void ParseInstruction(Instruction* inst, uint32_t ext_inst_id, + std::map& id_to_descriptor_set, + std::map& id_to_binding, + int32_t& descriptor_set_0_max_binding, + std::vector& start_counters, + std::vector& stop_counters); + + void CreateVariables(uint32_t u64_arr_ty_id, uint32_t u64_arr_st_ty_id, + uint32_t local_counters_ty_id, uint32_t counters_ty_id, + uint32_t global_counters_ds, + uint32_t global_counters_binding, + uint32_t& global_counters_id, + uint32_t& local_counters_id); + + void CreatePrologue(Instruction* entry_point_inst, + uint32_t u64_private_ptr_ty_id, uint32_t u64_ty_id, + uint32_t subgroup_scope_id, uint32_t global_counters_id, + uint32_t local_counters_id, + std::vector& start_counters, + Function*& function, uint32_t& read_clock_id); + + void CreateEpilogue(Instruction* inst, uint32_t read_clock_id, + uint32_t u64_ty_id, uint32_t u64_ptr_ty_id, + uint32_t u64_private_ptr_ty_id, + uint32_t subgroup_scope_id, uint32_t device_scope_id, + uint32_t acq_rel_mem_sem_id, uint32_t global_counters_id, + uint32_t local_counters_id, + std::vector& start_counters); + + void CreateCounters(uint32_t u64_ty_id, uint32_t u64_private_ptr_ty_id, + uint32_t subgroup_scope_id, + std::vector& start_counters, + std::vector& stop_counters, + uint32_t local_counters_id); + + std::vector* pc_; + std::vector* ds_; + std::vector* me_; + std::vector* counters_; + vksp_configuration* config_; +}; + +} // namespace opt +} // namespace spvtools + +#endif // SOURCE_OPT_VKSP_REFLECT_INFO_PASS_H_ diff --git a/source/table.h b/source/table.h index 8097f13f776..c481265a4ae 100644 --- a/source/table.h +++ b/source/table.h @@ -74,7 +74,7 @@ typedef struct spv_ext_inst_desc_t { const uint32_t ext_inst; const uint32_t numCapabilities; const spv::Capability* capabilities; - const spv_operand_type_t operandTypes[16]; // TODO: Smaller/larger? + const spv_operand_type_t operandTypes[40]; // TODO: Smaller/larger? } spv_ext_inst_desc_t; typedef struct spv_ext_inst_group_t {