├── .github └── workflows │ └── rebase.yml ├── README.md ├── LICENSE ├── CHANGES ├── external └── fetch_external_sources.py ├── CMakeLists.txt ├── include └── spvgen.h └── source └── spvgen.cpp /.github/workflows/rebase.yml: -------------------------------------------------------------------------------- 1 | name: Automatic Rebase 2 | on: 3 | issue_comment: 4 | types: [created] 5 | jobs: 6 | rebase: 7 | name: Rebase 8 | if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') && 9 | (github.event.comment.author_association == 'OWNER' || 10 | github.event.comment.author_association == 'MEMBER' || 11 | github.event.comment.author_association == 'COLLABORATOR') 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout the latest code 15 | uses: actions/checkout@v2 16 | with: 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | fetch-depth: 0 # otherwise, you will fail to push refs to dest repo 19 | - name: Automatic Rebase 20 | uses: cirrus-actions/rebase@1.4 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SPVGEN 2 | 3 | SPVGEN is a library to generate SPIR-V binary. It integrates [glslang](https://github.com/KhronosGroup/glslang) and [SPIRV-Tools](https://github.com/KhronosGroup/SPIRV-Tools). 4 | 5 | ## APIs 6 | The APIs are listed in include/spvgen.h. 7 | 8 | #### Initialization 9 | * InitSpvGen() 10 | 11 | #### Convert GLSL to SPIR-V binary 12 | * spvCompileAndLinkProgram() 13 | * spvGetSpirvBinaryFromProgram() 14 | * spvDestroyProgram() 15 | 16 | #### Assemble SPIR-V 17 | * spvAssembleSpirv() 18 | 19 | #### Disassemble SPIR-V 20 | * spvDisassembleSpirv() 21 | 22 | #### Optimize SPIR-V 23 | * spvOptimizeSpirv() 24 | * spvFreeBuffer() 25 | 26 | #### Validate SPIR-V 27 | * spvValidateSpirv() 28 | 29 | ## How to build 30 | 31 | SPVGEN is now built into amdllpc statically by default. If you want to build a standalone one, follow the steps below: 32 | 1. ```cd spvgen/external && python fetch_external_sources.py``` 33 | 2. Use cmake to build. 34 | ``` 35 | cd spvgen 36 | mkdir build 37 | cd build 38 | cmake .. 39 | make -j8 40 | ``` 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Advanced Micro Devices, Inc. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | ## 2 | ####################################################################################################################### 3 | # 4 | # Copyright (c) 2018-2025 Advanced Micro Devices, Inc. All Rights Reserved. 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | ####################################################################################################################### 25 | 26 | # Note: this file is parsed by fetch_external_sources.py to get the revision, 27 | # please do not move the lines and change the formats at will. 28 | 29 | Promotion history for /spvgen/external 30 | 31 | [glslang] 32 | Commit: 1b65bd602b2 33 | Date: 2025/02/04 34 | 1b65bd602b2 Implement GL_NV_cooperative_vector 35 | 36 | [SPIRV-tools] 37 | Commit: ce37fd67f83 38 | Date: 2025/02/01 39 | ce37fd67f83 Add validation for SPV_NV_linear_swept_spheres. (#5975) 40 | 41 | [SPIRV-headers] 42 | Commit: e7294a8ebed 43 | Date: 2025/01/30 44 | e7294a8ebed Add headers for SPV_NV_linear_swept_spheres. (#483) 45 | 46 | [SPIRV-cross] 47 | Commit: 6173e24b 48 | Date: 2024/12/13 49 | 6173e24b Merge pull request #2430 from KhronosGroup/hlsl-mesh-single-clip-cull-fix 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /external/fetch_external_sources.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ## 3 | ####################################################################################################################### 4 | # 5 | # Copyright (c) 2024-2025 Advanced Micro Devices, Inc. All Rights Reserved. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | # 25 | ####################################################################################################################### 26 | 27 | 28 | 29 | """Module fetch external source.""" 30 | 31 | # This script is used to download the glslang, SPIRV-Tools, and SPIRV-Headers from github. 32 | 33 | # __future__ must be at the beginning of the file 34 | from __future__ import print_function 35 | 36 | import os 37 | import sys 38 | import argparse 39 | 40 | target_dir = os.getcwd() + "/" # target directory the source code downloaded to. 41 | 42 | class GitRepo: 43 | """Class representing a git repo""" 44 | def __init__(self, https_url, module_name, extract_dir): 45 | self.https_url = https_url 46 | self.module_name = module_name 47 | self.extract_dir = extract_dir 48 | self.revision = '' 49 | 50 | def get_revision(self): 51 | """Function get revision.""" 52 | src_file = globals()['target_dir'] + "../CHANGES" 53 | if not os.path.exists(src_file): 54 | print("Error:", src_file, "does not exist!!!") 55 | sys.exit(1) 56 | 57 | with open(src_file,'r', encoding="utf-8") as rev_file: 58 | lines = rev_file.readlines() 59 | found = False 60 | for line in lines: 61 | if found: 62 | if line.find("Commit:") == 0: 63 | # line is in format: "Commit: HASH". Save only hash. 64 | self.revision = line[8:] 65 | break 66 | found = self.module_name.lower() in line.lower() 67 | rev_file.close() 68 | 69 | if not found: 70 | print("Error: Revision is not gotten from", src_file, "correctly, please check it!!!") 71 | sys.exit(1) 72 | else: 73 | print("Get the revision of", self.extract_dir + ":", self.revision) 74 | 75 | def checkout(self): 76 | """Function checkout code.""" 77 | full_dst_path = os.path.join(globals()['target_dir'], self.extract_dir) 78 | if not os.path.exists(full_dst_path): 79 | os.system("git clone " + self.https_url + " " + full_dst_path) 80 | 81 | os.chdir(full_dst_path) 82 | os.system("git fetch") 83 | os.system("git checkout " + self.revision) 84 | 85 | PACKAGES = [ 86 | GitRepo("https://github.com/KhronosGroup/glslang.git", 87 | "glslang", 88 | "glslang"), 89 | 90 | GitRepo("https://github.com/KhronosGroup/SPIRV-Tools.git", 91 | "spirv-tools", 92 | "SPIRV-tools"), 93 | 94 | GitRepo("https://github.com/KhronosGroup/SPIRV-Headers.git", 95 | "spirv-headers", 96 | "SPIRV-tools/external/spirv-headers"), 97 | 98 | GitRepo("https://github.com/KhronosGroup/SPIRV-Cross.git", 99 | "spirv-cross", 100 | "SPIRV-cross"), 101 | ] 102 | 103 | def get_opt(): 104 | """Class for parser arguments.""" 105 | parser = argparse.ArgumentParser(description='Fetching external source.') 106 | 107 | parser.add_argument("-t", "--target_dir", action="store", 108 | type=str, 109 | default=None, 110 | dest="target_dir", 111 | help="the target directory source code downloaded to") 112 | 113 | args = parser.parse_args() 114 | 115 | if args.target_dir: 116 | print(f"The source code is downloaded to {args.target_dir}") 117 | globals()['target_dir'] = args.target_dir 118 | else: 119 | print("The target directory is not specified, using default:", globals()['target_dir']) 120 | 121 | def download_source_code(): 122 | """Class for download source code.""" 123 | os.chdir(globals()['target_dir']) 124 | 125 | # Touch the spvgen CMakeLists.txt to ensure that the new directories get used. 126 | os.utime('../CMakeLists.txt', None) 127 | 128 | for pkg in PACKAGES: 129 | pkg.get_revision() 130 | pkg.checkout() 131 | 132 | get_opt() 133 | download_source_code() 134 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ## 2 | ####################################################################################################################### 3 | # 4 | # Copyright (c) 2024-2025 Advanced Micro Devices, Inc. All Rights Reserved. 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | ####################################################################################################################### 25 | 26 | cmake_minimum_required(VERSION 3.5) 27 | 28 | PROJECT(spvgen VERSION 1 LANGUAGES C CXX) 29 | 30 | option(SPVGEN_ENABLE_WERROR "Build with -Werror enabled" OFF) 31 | 32 | set(CMAKE_CXX_STANDARD 17) 33 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 34 | 35 | if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") 36 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fPIC") 37 | endif () 38 | 39 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 40 | 41 | if(NOT DEFINED THIRD_PARTY_PATH) 42 | # Third_party path 43 | if(EXISTS ${PROJECT_SOURCE_DIR}/../third_party) 44 | set(THIRD_PARTY_PATH ${PROJECT_SOURCE_DIR}/../third_party CACHE PATH "The path of third_party.") 45 | else() 46 | set(THIRD_PARTY_PATH ${PROJECT_SOURCE_DIR}/../../../third_party CACHE PATH "The path of third_party.") 47 | endif() 48 | endif() 49 | 50 | add_definitions(-DNV_EXTENSIONS) 51 | 52 | add_definitions(-DAMD_EXTENSIONS) 53 | 54 | add_definitions(-DENABLE_HLSL) 55 | 56 | # Disable unnecessary components in SPIRV-tools and SPIRV_CROSS 57 | set(SPIRV_SKIP_TESTS TRUE CACHE BOOL "" FORCE) 58 | set(SPIRV_SKIP_EXECUTABLES ON CACHE BOOL "OGLP override." FORCE) 59 | set(SPIRV_CROSS_ENABLE_TESTS OFF CACHE BOOL "OGLP override." FORCE) 60 | set(SPIRV_CROSS_CLI OFF CACHE BOOL "OGLP override." FORCE) 61 | 62 | # Build third party targets 63 | if(EXISTS ${THIRD_PARTY_PATH}/SPIRV-tools) 64 | set(SPIRV_TOOLS_PATH ${THIRD_PARTY_PATH}/SPIRV-tools) 65 | else() 66 | set(SPIRV_TOOLS_PATH ${PROJECT_SOURCE_DIR}/external/SPIRV-tools) 67 | endif() 68 | set(SPIRV_WERROR ${SPVGEN_ENABLE_WERROR} CACHE BOOL "${PROJECT_NAME} override." FORCE) 69 | add_subdirectory(${SPIRV_TOOLS_PATH} external/SPIRV-tools) 70 | 71 | if(EXISTS ${THIRD_PARTY_PATH}/glslang) 72 | set(GLSLANG_PATH ${THIRD_PARTY_PATH}/glslang) 73 | else() 74 | set(GLSLANG_PATH ${PROJECT_SOURCE_DIR}/external/glslang) 75 | endif() 76 | add_subdirectory(${GLSLANG_PATH} external/glslang) 77 | 78 | if(EXISTS ${THIRD_PARTY_PATH}/SPIRV-cross) 79 | set(SPIRV_CROSS_PATH ${THIRD_PARTY_PATH}/SPIRV-cross) 80 | else() 81 | set(SPIRV_CROSS_PATH ${PROJECT_SOURCE_DIR}/external/SPIRV-cross) 82 | endif() 83 | add_subdirectory(${SPIRV_CROSS_PATH} external/SPIRV-cross) 84 | 85 | set(SPVGEN_SOURCE_FILES 86 | source/spvgen.cpp 87 | ) 88 | 89 | # Build object library 90 | add_library(spvgen_base OBJECT ${SPVGEN_SOURCE_FILES}) 91 | 92 | target_include_directories(spvgen_base 93 | PUBLIC 94 | include 95 | PRIVATE 96 | ${GLSLANG_PATH} 97 | ${GLSLANG_PATH}/SPIRV 98 | ${SPIRV_TOOLS_PATH}/include 99 | ${SPIRV_CROSS_PATH} 100 | ) 101 | 102 | target_link_libraries(spvgen_base glslang SPIRV SPIRV-Tools SPIRV-Tools-opt spirv-cross-c) 103 | 104 | # Touch an empty source file 105 | set(EMPTY_SOURCE_FILES ${CMAKE_CURRENT_BINARY_DIR}/empty.cpp) 106 | add_custom_command( 107 | OUTPUT ${EMPTY_SOURCE_FILES} 108 | COMMAND ${CMAKE_COMMAND} -E touch ${EMPTY_SOURCE_FILES} 109 | COMMENT "Touching ${EMPTY_SOURCE_FILES}" 110 | ) 111 | 112 | # Build static library 113 | add_library(spvgen_static STATIC ${EMPTY_SOURCE_FILES}) 114 | target_link_libraries(spvgen_static spvgen_base) 115 | 116 | # Build shared library 117 | add_library(spvgen SHARED ${EMPTY_SOURCE_FILES}) 118 | target_link_libraries(spvgen spvgen_base) 119 | set_target_properties(spvgen PROPERTIES PREFIX "") 120 | 121 | # Set sub library properties 122 | if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 123 | set_property(TARGET spvgen_base PROPERTY FOLDER spvgen) 124 | set_property(TARGET spvgen_static PROPERTY FOLDER spvgen) 125 | set_property(TARGET glslang PROPERTY FOLDER spvgen/glslang) 126 | set_property(TARGET GenericCodeGen PROPERTY FOLDER spvgen/glslang) 127 | set_property(TARGET SPIRV PROPERTY FOLDER spvgen/glslang) 128 | 129 | set_property(TARGET spirv-cross-c PROPERTY FOLDER spvgen/spirv-cross) 130 | set_property(TARGET spirv-cross-core PROPERTY FOLDER spvgen/spirv-cross) 131 | set_property(TARGET spirv-cross-cpp PROPERTY FOLDER spvgen/spirv-cross) 132 | set_property(TARGET spirv-cross-glsl PROPERTY FOLDER spvgen/spirv-cross) 133 | set_property(TARGET spirv-cross-reflect PROPERTY FOLDER spvgen/spirv-cross) 134 | set_property(TARGET spirv-cross-util PROPERTY FOLDER spvgen/spirv-cross) 135 | set_property(TARGET spirv-cross-hlsl PROPERTY FOLDER spvgen/spirv-cross) 136 | set_property(TARGET spirv-cross-msl PROPERTY FOLDER spvgen/spirv-cross) 137 | 138 | set_property(TARGET spirv-tools-pkg-config PROPERTY FOLDER spvgen/SPIRV-Tools) 139 | set_property(TARGET spirv-tools-build-version PROPERTY FOLDER spvgen/SPIRV-Tools) 140 | set_property(TARGET spirv-tools-header-DebugInfo PROPERTY FOLDER spvgen/SPIRV-Tools) 141 | set_property(TARGET spirv-tools-vimsyntax PROPERTY FOLDER spvgen/SPIRV-Tools) 142 | set_property(TARGET spirv-tools-header-OpenCLDebugInfo100 PROPERTY FOLDER spvgen/SPIRV-Tools) 143 | 144 | set_property(TARGET spv-tools-cldi100 PROPERTY FOLDER spvgen/SPIRV-Tools) 145 | set_property(TARGET spv-tools-shdi100 PROPERTY FOLDER spvgen/SPIRV-Tools) 146 | set_property(TARGET spirv-tools-header-NonSemanticShaderDebugInfo100 PROPERTY FOLDER spvgen/SPIRV-Tools) 147 | set_property(TARGET spv-tools-clspvreflection PROPERTY FOLDER spvgen/SPIRV-Tools) 148 | set_property(TARGET spv-tools-debuginfo PROPERTY FOLDER spvgen/SPIRV-Tools) 149 | set_property(TARGET spv-tools-spv-amd-gs PROPERTY FOLDER spvgen/SPIRV-Tools) 150 | set_property(TARGET spv-tools-spv-amd-sb PROPERTY FOLDER spvgen/SPIRV-Tools) 151 | set_property(TARGET spv-tools-spv-amd-sevp PROPERTY FOLDER spvgen/SPIRV-Tools) 152 | set_property(TARGET spv-tools-spv-amd-stm PROPERTY FOLDER spvgen/SPIRV-Tools) 153 | 154 | set_property(TARGET SPIRV-Tools-link PROPERTY FOLDER spvgen/SPIRV-Tools) 155 | set_property(TARGET SPIRV-Tools-opt PROPERTY FOLDER spvgen/SPIRV-Tools) 156 | set_property(TARGET SPIRV-Tools-shared PROPERTY FOLDER spvgen/SPIRV-Tools) 157 | set_property(TARGET SPIRV-Tools-reduce PROPERTY FOLDER spvgen/SPIRV-Tools) 158 | set_property(TARGET SPIRV-Tools-static PROPERTY FOLDER spvgen/SPIRV-Tools) 159 | 160 | set_property(TARGET core_tables PROPERTY FOLDER spvgen/SPIRV-Tools) 161 | set_property(TARGET enum_string_mapping PROPERTY FOLDER spvgen/SPIRV-Tools) 162 | set_property(TARGET extinst_tables PROPERTY FOLDER spvgen/SPIRV-Tools) 163 | endif() 164 | -------------------------------------------------------------------------------- /include/spvgen.h: -------------------------------------------------------------------------------- 1 | /* 2 | *********************************************************************************************************************** 3 | * 4 | * Copyright (c) 2015-2025 Advanced Micro Devices, Inc. All Rights Reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | **********************************************************************************************************************/ 25 | /** 26 | *********************************************************************************************************************** 27 | * @file spvgen.h 28 | * @brief SPVGEN header file: contains the definition and the wrap implementation of SPIR-V generator entry-points. 29 | *********************************************************************************************************************** 30 | */ 31 | #pragma once 32 | 33 | #define SPVGEN_VERSION 0x20000 34 | #define SPVGEN_REVISION 5 35 | 36 | #define SPVGEN_MAJOR_VERSION(version) (version >> 16) 37 | #define SPVGEN_MINOR_VERSION(version) (version & 0xFFFF) 38 | 39 | #if defined(_WIN32) 40 | #define SPVAPI __cdecl 41 | #else 42 | #define SPVAPI 43 | #endif 44 | 45 | #ifndef SH_IMPORT_EXPORT 46 | #if defined(_WIN32) 47 | #ifdef SH_EXPORTING 48 | #define SH_IMPORT_EXPORT __declspec(dllexport) 49 | #else 50 | #define SH_IMPORT_EXPORT __declspec(dllimport) 51 | #endif 52 | #else 53 | #define SH_IMPORT_EXPORT 54 | #endif 55 | #endif 56 | 57 | enum SpvGenVersion : uint32_t 58 | { 59 | SpvGenVersionGlslang, 60 | SpvGenVersionSpirv, 61 | SpvGenVersionStd450, 62 | SpvGenVersionExtAmd, 63 | SpvGenVersionSpvGen, 64 | SpvGenVersionCount, 65 | }; 66 | 67 | // Command-line options 68 | enum SpvGenOptions : uint32_t 69 | { 70 | SpvGenOptionNone = 0, 71 | SpvGenOptionVulkanRules = (1 << 0), 72 | SpvGenOptionDefaultDesktop = (1 << 1), 73 | SpvGenOptionReadHlsl = (1 << 2), 74 | SpvGenOptionHlslOffsets = (1 << 3), 75 | SpvGenOptionHlslIoMapping = (1 << 4), 76 | SpvGenOptionDebug = (1 << 5), 77 | SpvGenOptionAutoMapBindings = (1 << 6), 78 | SpvGenOptionFlattenUniformArrays = (1 << 7), 79 | SpvGenOptionAutoMapLocations = (1 << 8), 80 | SpvGenOptionOptimizeDisable = (1 << 9), 81 | SpvGenOptionOptimizeSize = (1 << 10), 82 | SpvGenOptionInvertY = (1 << 11), 83 | SpvGenOptionSuppressInfolog = (1 << 12), 84 | SpvGenOptionHlslDX9compatible = (1 << 13), 85 | SpvGenOptionHlslEnable16BitTypes = (1 << 14) 86 | }; 87 | 88 | enum SpvSourceLanguage : uint32_t 89 | { 90 | SpvSourceLanguageGLSL, // OpenGL-style 91 | SpvSourceLanguageVulkan, // Vulkan GLSL 92 | SpvSourceLanguageMSL, // Metal Shading Language 93 | SpvSourceLanguageHLSL, // HLSL-style 94 | SpvSourceLanguageESSL, // ESSL 95 | }; 96 | enum SpvGenStage : uint32_t 97 | { 98 | SpvGenStageTask, 99 | SpvGenStageVertex, 100 | SpvGenStageTessControl, 101 | SpvGenStageTessEvaluation, 102 | SpvGenStageGeometry, 103 | SpvGenStageMesh, 104 | SpvGenStageFragment, 105 | SpvGenStageCompute, 106 | SpvGenStageRayTracingRayGen, 107 | SpvGenStageRayTracingIntersect, 108 | SpvGenStageRayTracingAnyHit, 109 | SpvGenStageRayTracingClosestHit, 110 | SpvGenStageRayTracingMiss, 111 | SpvGenStageRayTracingCallable, 112 | SpvGenStageCount, 113 | SpvGenStageInvalid = ~0u, 114 | SpvGenNativeStageCount = SpvGenStageCompute + 1, 115 | }; 116 | 117 | #ifdef SH_EXPORTING 118 | 119 | #ifdef __cplusplus 120 | extern "C"{ 121 | #endif 122 | bool SH_IMPORT_EXPORT spvCompileAndLinkProgramFromFile( 123 | int fileNum, 124 | const char* fileList[], 125 | void** pProgram, 126 | const char** ppLog); 127 | 128 | bool SH_IMPORT_EXPORT spvCompileAndLinkProgramFromFileEx( 129 | int fileNum, 130 | const char* fileList[], 131 | const char* entryPoints[], 132 | void** pProgram, 133 | const char** ppLog, 134 | int options); 135 | 136 | bool SH_IMPORT_EXPORT spvCompileAndLinkProgram( 137 | int sourceStringCount[SpvGenNativeStageCount], 138 | const char* const* sourceList[SpvGenNativeStageCount], 139 | void** pProgram, 140 | const char** ppLog); 141 | 142 | bool SH_IMPORT_EXPORT spvCompileAndLinkProgramEx( 143 | int stageCount, 144 | const SpvGenStage* stageList, 145 | const int* sourceStringCount, 146 | const char* const* sourceList[], 147 | const char* const* fileList[], 148 | const char* entryPoints[], 149 | void** pProgram, 150 | const char** ppLog, 151 | int options); 152 | 153 | void SH_IMPORT_EXPORT spvDestroyProgram( 154 | void* hProgram); 155 | 156 | int SH_IMPORT_EXPORT spvGetSpirvBinaryFromProgram( 157 | void* hProgram, 158 | int stage, 159 | const unsigned int** ppData); 160 | 161 | SpvGenStage SH_IMPORT_EXPORT spvGetStageTypeFromName( 162 | const char* pName, 163 | bool* pIsHlsl); 164 | 165 | int SH_IMPORT_EXPORT spvAssembleSpirv( 166 | const char* pSpvText, 167 | unsigned int bufSize, 168 | unsigned int* pBuffer, 169 | const char** ppLog); 170 | 171 | bool SH_IMPORT_EXPORT spvDisassembleSpirv( 172 | unsigned int size, 173 | const void* pSpvToken, 174 | unsigned int bufSize, 175 | char* pBuffer); 176 | 177 | bool SH_IMPORT_EXPORT spvCrossSpirv( 178 | SpvSourceLanguage sourceLanguage, 179 | unsigned int size, 180 | const void* pSpvToken, 181 | char** spvCrossSpirv); 182 | 183 | bool SH_IMPORT_EXPORT spvCrossSpirvEx( 184 | SpvSourceLanguage sourceLanguage, 185 | uint32_t version, 186 | unsigned int size, 187 | const void* pSpvToken, 188 | char** spvCrossSpirv); 189 | 190 | bool SH_IMPORT_EXPORT spvValidateSpirv( 191 | unsigned int size, 192 | const void* pSpvToken, 193 | unsigned int logSize, 194 | char* pLog); 195 | 196 | bool SH_IMPORT_EXPORT spvOptimizeSpirv( 197 | unsigned int size, 198 | const void* pSpvToken, 199 | int optionCount, 200 | const char* options[], 201 | unsigned int* pBufSize, 202 | void** ppOptBuf, 203 | unsigned int logSize, 204 | char* pLog); 205 | 206 | void SH_IMPORT_EXPORT spvFreeBuffer( 207 | void* pBuffer); 208 | 209 | bool SH_IMPORT_EXPORT spvGetVersion( 210 | SpvGenVersion version, 211 | unsigned int* pVersion, 212 | unsigned int* pReversion); 213 | 214 | #ifdef __cplusplus 215 | } 216 | #endif 217 | 218 | bool InitSpvGen(const char* pSpvGenDir = nullptr); 219 | 220 | void FinalizeSpvgen(); 221 | 222 | #else 223 | 224 | // ===================================================================================================================== 225 | // SPIR-V generator entrypoints declaration 226 | typedef bool SH_IMPORT_EXPORT (SPVAPI* PFN_spvCompileAndLinkProgramFromFile)( 227 | int fileNum, 228 | const char* fileList[], 229 | void** pProgram, 230 | const char** ppLog); 231 | 232 | typedef bool SH_IMPORT_EXPORT(SPVAPI* PFN_spvCompileAndLinkProgramFromFileEx)( 233 | int fileNum, 234 | const char* fileList[], 235 | const char* entryPoints[], 236 | void** pProgram, 237 | const char** ppLog, 238 | int options); 239 | 240 | typedef bool SH_IMPORT_EXPORT (SPVAPI* PFN_spvCompileAndLinkProgram)( 241 | int sourceStringCount[SpvGenNativeStageCount], 242 | const char* const* sourceList[SpvGenNativeStageCount], 243 | void** pProgram, 244 | const char** ppLog); 245 | 246 | typedef bool SH_IMPORT_EXPORT(SPVAPI* PFN_spvCompileAndLinkProgramEx)( 247 | int stageCount, 248 | const SpvGenStage* stageList, 249 | const int* sourceStringCount, 250 | const char* const* sourceList[], 251 | const char* const* fileList[], 252 | const char* entryPoints[], 253 | void** pProgram, 254 | const char** ppLog, 255 | int options); 256 | 257 | typedef void SH_IMPORT_EXPORT (SPVAPI* PFN_spvDestroyProgram)(void* hProgram); 258 | 259 | typedef int SH_IMPORT_EXPORT (SPVAPI* PFN_spvGetSpirvBinaryFromProgram)( 260 | void* hProgram, 261 | int stage, 262 | const unsigned int** ppData); 263 | 264 | typedef SpvGenStage SH_IMPORT_EXPORT(SPVAPI* PFN_spvGetStageTypeFromName)( 265 | const char* pName, 266 | bool* pIsHlsl); 267 | 268 | typedef int SH_IMPORT_EXPORT (SPVAPI* PFN_spvAssembleSpirv)( 269 | const char* pSpvText, 270 | unsigned int codeBufSize, 271 | unsigned int* pSpvCodeBuf, 272 | const char** ppLog); 273 | 274 | typedef bool SH_IMPORT_EXPORT (SPVAPI* PFN_spvDisassembleSpirv)( 275 | unsigned int size, 276 | const void* pSpvCode, 277 | unsigned int textBufSize, 278 | char* pSpvTextBuf); 279 | 280 | typedef bool SH_IMPORT_EXPORT (SPVAPI* PFN_spvCrossSpirv)( 281 | SpvSourceLanguage sourceLanguage, 282 | unsigned int size, 283 | const void* pSpvToken, 284 | char** ppSourceString); 285 | 286 | typedef bool SH_IMPORT_EXPORT(SPVAPI* PFN_spvCrossSpirvEx)( 287 | SpvSourceLanguage sourceLanguage, 288 | uint32_t version, 289 | unsigned int size, 290 | const void* pSpvToken, 291 | char** ppSourceString); 292 | 293 | typedef bool SH_IMPORT_EXPORT (SPVAPI* PFN_spvValidateSpirv)( 294 | unsigned int size, 295 | const void* pSpvToken, 296 | unsigned int bufSize, 297 | char* pLog); 298 | 299 | typedef bool SH_IMPORT_EXPORT (SPVAPI* PFN_spvOptimizeSpirv)( 300 | unsigned int size, 301 | const void* pSpvToken, 302 | int optionCount, 303 | const char* options[], 304 | unsigned int* pBufSize, 305 | void** ppOptBuf, 306 | unsigned int logSize, 307 | char* pLog); 308 | 309 | typedef void SH_IMPORT_EXPORT (SPVAPI* PFN_spvFreeBuffer)( 310 | void* pBuffer); 311 | 312 | typedef bool SH_IMPORT_EXPORT (SPVAPI* PFN_spvGetVersion)( 313 | SpvGenVersion version, 314 | unsigned int* pVersion, 315 | unsigned int* pReversion); 316 | 317 | // ===================================================================================================================== 318 | // SPIR-V generator entry-points 319 | #define DECL_EXPORT_FUNC(func) \ 320 | extern PFN_##func g_pfn##func 321 | 322 | DECL_EXPORT_FUNC(spvCompileAndLinkProgramFromFile); 323 | DECL_EXPORT_FUNC(spvCompileAndLinkProgramFromFileEx); 324 | DECL_EXPORT_FUNC(spvCompileAndLinkProgram); 325 | DECL_EXPORT_FUNC(spvCompileAndLinkProgramEx); 326 | DECL_EXPORT_FUNC(spvDestroyProgram); 327 | DECL_EXPORT_FUNC(spvGetSpirvBinaryFromProgram); 328 | DECL_EXPORT_FUNC(spvGetStageTypeFromName); 329 | DECL_EXPORT_FUNC(spvAssembleSpirv); 330 | DECL_EXPORT_FUNC(spvDisassembleSpirv); 331 | DECL_EXPORT_FUNC(spvCrossSpirv); 332 | DECL_EXPORT_FUNC(spvCrossSpirvEx); 333 | DECL_EXPORT_FUNC(spvValidateSpirv); 334 | DECL_EXPORT_FUNC(spvOptimizeSpirv); 335 | DECL_EXPORT_FUNC(spvFreeBuffer); 336 | DECL_EXPORT_FUNC(spvGetVersion); 337 | 338 | bool SPVAPI InitSpvGen(const char* pSpvGenDir = nullptr); 339 | 340 | void SPVAPI FinalizeSpvgen(); 341 | 342 | #endif 343 | 344 | #ifdef SPVGEN_STATIC_LIB 345 | 346 | #define DEFI_EXPORT_FUNC(func) \ 347 | PFN_##func g_pfn##func = nullptr 348 | 349 | DEFI_EXPORT_FUNC(spvCompileAndLinkProgramFromFile); 350 | DEFI_EXPORT_FUNC(spvCompileAndLinkProgramFromFileEx); 351 | DEFI_EXPORT_FUNC(spvCompileAndLinkProgram); 352 | DEFI_EXPORT_FUNC(spvCompileAndLinkProgramEx); 353 | DEFI_EXPORT_FUNC(spvDestroyProgram); 354 | DEFI_EXPORT_FUNC(spvGetSpirvBinaryFromProgram); 355 | DEFI_EXPORT_FUNC(spvGetStageTypeFromName); 356 | DEFI_EXPORT_FUNC(spvAssembleSpirv); 357 | DEFI_EXPORT_FUNC(spvDisassembleSpirv); 358 | DEFI_EXPORT_FUNC(spvCrossSpirv); 359 | DEFI_EXPORT_FUNC(spvCrossSpirvEx); 360 | DEFI_EXPORT_FUNC(spvValidateSpirv); 361 | DEFI_EXPORT_FUNC(spvOptimizeSpirv); 362 | DEFI_EXPORT_FUNC(spvFreeBuffer); 363 | DEFI_EXPORT_FUNC(spvGetVersion); 364 | 365 | // SPIR-V generator Windows implementation 366 | #if defined(_WIN32) 367 | 368 | #include 369 | // SPIR-V generator Windows DLL name 370 | static const char* SpvGeneratorName = "spvgen.dll"; 371 | 372 | #define INITFUNC(func) \ 373 | g_pfn##func = reinterpret_cast(GetProcAddress(hModule, #func));\ 374 | if (g_pfn##func == NULL)\ 375 | {\ 376 | success = false;\ 377 | } 378 | 379 | #define INIT_OPT_FUNC(func) \ 380 | g_pfn##func = reinterpret_cast(GetProcAddress(hModule, #func)); 381 | 382 | #else 383 | 384 | #include 385 | #include 386 | #if __APPLE__ && __MACH__ 387 | static const char* SpvGeneratorName = "spvgen.dylib"; 388 | #else 389 | static const char* SpvGeneratorName = "spvgen.so"; 390 | #endif 391 | 392 | #define INITFUNC(func) \ 393 | g_pfn##func = reinterpret_cast(dlsym(hModule, #func));\ 394 | if (g_pfn##func == NULL)\ 395 | {\ 396 | success = false;\ 397 | } 398 | 399 | #define INIT_OPT_FUNC(func) \ 400 | g_pfn##func = reinterpret_cast(dlsym(hModule, #func)); 401 | 402 | #endif // defined(_WIN32) 403 | 404 | #define DEINITFUNC(func) g_pfn##func = nullptr; 405 | 406 | // ===================================================================================================================== 407 | // Initialize SPIR-V generator entry-points 408 | // This can be called multiple times in the same application. 409 | bool SPVAPI InitSpvGen( 410 | const char* pSpvGenDir) // [in] Directory to load SPVGEN library from, or null to use OS's default search path 411 | { 412 | if (g_pfnspvGetVersion != nullptr) 413 | { 414 | // Already loaded. 415 | return true; 416 | } 417 | 418 | bool success = true; 419 | const char* pLibName = SpvGeneratorName; 420 | std::string libNameBuffer; 421 | if (pSpvGenDir != nullptr) 422 | { 423 | libNameBuffer = pSpvGenDir; 424 | libNameBuffer += "/"; 425 | libNameBuffer += pLibName; 426 | pLibName = libNameBuffer.c_str(); 427 | } 428 | #if defined(_WIN32) 429 | HMODULE hModule = LoadLibraryA(pLibName); 430 | #else 431 | void* hModule = dlopen(pLibName, RTLD_GLOBAL | RTLD_NOW); 432 | #endif 433 | 434 | if (hModule != NULL) 435 | { 436 | INITFUNC(spvCompileAndLinkProgramFromFile); 437 | INITFUNC(spvCompileAndLinkProgramFromFileEx); 438 | INITFUNC(spvCompileAndLinkProgram); 439 | INITFUNC(spvCompileAndLinkProgramEx); 440 | INITFUNC(spvDestroyProgram); 441 | INITFUNC(spvGetSpirvBinaryFromProgram); 442 | INITFUNC(spvGetStageTypeFromName); 443 | INITFUNC(spvAssembleSpirv); 444 | INITFUNC(spvDisassembleSpirv); 445 | INITFUNC(spvCrossSpirv); 446 | INITFUNC(spvCrossSpirvEx); 447 | INITFUNC(spvValidateSpirv); 448 | INITFUNC(spvOptimizeSpirv); 449 | INITFUNC(spvFreeBuffer); 450 | INITFUNC(spvGetVersion); 451 | } 452 | else 453 | { 454 | success = false; 455 | } 456 | 457 | if (success) 458 | { 459 | unsigned int version = 0; 460 | unsigned int revsion = 0; 461 | success = g_pfnspvGetVersion(SpvGenVersionSpvGen, &version, &revsion); 462 | if (SPVGEN_MAJOR_VERSION(version) != SPVGEN_MAJOR_VERSION(SPVGEN_VERSION)) 463 | { 464 | success = false; 465 | } 466 | } 467 | 468 | if (success == false) 469 | { 470 | DEINITFUNC(spvCompileAndLinkProgramFromFile); 471 | DEINITFUNC(spvCompileAndLinkProgramFromFileEx); 472 | DEINITFUNC(spvCompileAndLinkProgram); 473 | DEINITFUNC(spvCompileAndLinkProgramEx); 474 | DEINITFUNC(spvDestroyProgram); 475 | DEINITFUNC(spvGetSpirvBinaryFromProgram); 476 | DEINITFUNC(spvGetStageTypeFromName); 477 | DEINITFUNC(spvAssembleSpirv); 478 | DEINITFUNC(spvDisassembleSpirv); 479 | DEINITFUNC(spvCrossSpirv); 480 | DEINITFUNC(spvCrossSpirvEx); 481 | DEINITFUNC(spvValidateSpirv); 482 | DEINITFUNC(spvOptimizeSpirv); 483 | DEINITFUNC(spvFreeBuffer); 484 | DEINITFUNC(spvGetVersion); 485 | } 486 | return success; 487 | } 488 | 489 | // ===================================================================================================================== 490 | // Finalize spvgen; Clean up resource 491 | void SPVAPI FinalizeSpvgen() {} 492 | 493 | #endif 494 | 495 | #ifndef SH_EXPORTING 496 | 497 | #define spvCompileAndLinkProgramFromFile g_pfnspvCompileAndLinkProgramFromFile 498 | #define spvCompileAndLinkProgramFromFileEx g_pfnspvCompileAndLinkProgramFromFileEx 499 | #define spvCompileAndLinkProgram g_pfnspvCompileAndLinkProgram 500 | #define spvCompileAndLinkProgramEx g_pfnspvCompileAndLinkProgramEx 501 | #define spvDestroyProgram g_pfnspvDestroyProgram 502 | #define spvGetSpirvBinaryFromProgram g_pfnspvGetSpirvBinaryFromProgram 503 | #define spvGetStageTypeFromName g_pfnspvGetStageTypeFromName 504 | #define spvAssembleSpirv g_pfnspvAssembleSpirv 505 | #define spvDisassembleSpirv g_pfnspvDisassembleSpirv 506 | #define spvCrossSpirv g_pfnspvCrossSpirv 507 | #define spvCrossSpirvEx g_pfnspvCrossSpirvEx 508 | #define spvValidateSpirv g_pfnspvValidateSpirv 509 | #define spvOptimizeSpirv g_pfnspvOptimizeSpirv 510 | #define spvFreeBuffer g_pfnspvFreeBuffer 511 | #define spvGetVersion g_pfnspvGetVersion 512 | 513 | #endif 514 | 515 | -------------------------------------------------------------------------------- /source/spvgen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | *********************************************************************************************************************** 3 | * 4 | * Copyright (c) 2015-2024 Advanced Micro Devices, Inc. All Rights Reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | * 24 | **********************************************************************************************************************/ 25 | /** 26 | *********************************************************************************************************************** 27 | * @file spvgen.cpp 28 | * @brief SPVGEN source file: defines the exported functions for the DLL application. 29 | *********************************************************************************************************************** 30 | */ 31 | 32 | //Note: this file is based on https://github.com/KhronosGroup/glslang/blob/876a0e392e93c16b4dfa66daf382a53005c1e7b0/StandAlone/StandAlone.cpp 33 | 34 | //Copyright (C) 2002-2005 3Dlabs Inc. Ltd. 35 | //Copyright (C) 2013 LunarG, Inc. 36 | // 37 | //All rights reserved. 38 | // 39 | //Redistribution and use in source and binary forms, with or without 40 | //modification, are permitted provided that the following conditions 41 | //are met: 42 | // 43 | // Redistributions of source code must retain the above copyright 44 | // notice, this list of conditions and the following disclaimer. 45 | // 46 | // Redistributions in binary form must reproduce the above 47 | // copyright notice, this list of conditions and the following 48 | // disclaimer in the documentation and/or other materials provided 49 | // with the distribution. 50 | // 51 | // Neither the name of 3Dlabs Inc. Ltd. nor the names of its 52 | // contributors may be used to endorse or promote products derived 53 | // from this software without specific prior written permission. 54 | // 55 | //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 56 | //"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 57 | //LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 58 | //FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 59 | //COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 60 | //INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 61 | //BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 62 | //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 63 | //CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 64 | //LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 65 | //ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 66 | //POSSIBILITY OF SUCH DAMAGE. 67 | // 68 | 69 | // this only applies to the standalone wrapper, not the front end in general 70 | #include "glslang/Include/ShHandle.h" 71 | #include "glslang/Public/ShaderLang.h" 72 | #include "glslang/build_info.h" 73 | #include "StandAlone/DirStackFileIncluder.h" 74 | #include "SPIRV/GlslangToSpv.h" 75 | 76 | #include "spirv-tools/libspirv.h" 77 | #include "spirv-tools/optimizer.hpp" 78 | 79 | #include "doc.h" 80 | namespace spv { 81 | extern "C" { 82 | // Include C-based headers that don't have a namespace 83 | #include "GLSL.std.450.h" 84 | #include "GLSL.ext.KHR.h" 85 | #include "GLSL.ext.AMD.h" 86 | } 87 | } 88 | 89 | using namespace spv; 90 | #include "spirv_cross_util.hpp" 91 | #include "spirv_glsl.hpp" 92 | #include "spirv_hlsl.hpp" 93 | #include "spirv_msl.hpp" 94 | #include "spirv_parser.hpp" 95 | #include "spirv_reflect.hpp" 96 | 97 | #include "disassemble.h" 98 | #include 99 | #include 100 | #include 101 | #include 102 | 103 | #include "spvgen.h" 104 | 105 | // Forward declarations 106 | EShLanguage SpvGenStageToEShLanguage(SpvGenStage stage); 107 | bool ReadFileData(const char* pFileName, std::string& data); 108 | int Vsnprintf(char* pOutput, size_t bufSize, const char* pFormat, va_list argList); 109 | int Snprintf(char* pOutput, size_t bufSize, const char* pFormat, ...); 110 | spv_result_t spvDiagnosticPrint(const spv_diagnostic diagnostic, char* pBuffer, size_t bufferSize); 111 | 112 | TBuiltInResource Resources; 113 | std::string* pConfigFile; 114 | int DefaultOptions = SpvGenOptionDefaultDesktop | SpvGenOptionVulkanRules; 115 | 116 | // 117 | // These are the default resources for TBuiltInResources, used for both 118 | // - parsing this string for the case where the user didn't supply one 119 | // - dumping out a template for user construction of a config file 120 | // 121 | const char* DefaultConfig = 122 | "MaxLights 32\n" 123 | "MaxClipPlanes 6\n" 124 | "MaxTextureUnits 32\n" 125 | "MaxTextureCoords 32\n" 126 | "MaxVertexAttribs 64\n" 127 | "MaxVertexUniformComponents 4096\n" 128 | "MaxVaryingFloats 64\n" 129 | "MaxVertexTextureImageUnits 65535\n" 130 | "MaxCombinedTextureImageUnits 65535\n" 131 | "MaxTextureImageUnits 65535\n" 132 | "MaxFragmentUniformComponents 4096\n" 133 | "MaxDrawBuffers 8\n" 134 | "MaxVertexUniformVectors 128\n" 135 | "MaxVaryingVectors 8\n" 136 | "MaxFragmentUniformVectors 16\n" 137 | "MaxVertexOutputVectors 32\n" 138 | "MaxFragmentInputVectors 32\n" 139 | "MinProgramTexelOffset -64\n" 140 | "MaxProgramTexelOffset 63\n" 141 | "MaxClipDistances 8\n" 142 | "MaxComputeWorkGroupCountX 65535\n" 143 | "MaxComputeWorkGroupCountY 65535\n" 144 | "MaxComputeWorkGroupCountZ 65535\n" 145 | "MaxComputeWorkGroupSizeX 1024\n" 146 | "MaxComputeWorkGroupSizeY 1024\n" 147 | "MaxComputeWorkGroupSizeZ 64\n" 148 | "MaxComputeUniformComponents 1024\n" 149 | "MaxComputeTextureImageUnits 65535\n" 150 | "MaxComputeImageUniforms 65535\n" 151 | "MaxComputeAtomicCounters 8\n" 152 | "MaxComputeAtomicCounterBuffers 1\n" 153 | "MaxVaryingComponents 60\n" 154 | "MaxVertexOutputComponents 128\n" 155 | "MaxGeometryInputComponents 128\n" 156 | "MaxGeometryOutputComponents 128\n" 157 | "MaxFragmentInputComponents 128\n" 158 | "MaxImageUnits 65536\n" 159 | "MaxCombinedImageUnitsAndFragmentOutputs 65536\n" 160 | "MaxCombinedShaderOutputResources 65536\n" 161 | "MaxImageSamples 8\n" 162 | "MaxVertexImageUniforms 65536\n" 163 | "MaxTessControlImageUniforms 65536\n" 164 | "MaxTessEvaluationImageUniforms 65536\n" 165 | "MaxGeometryImageUniforms 65536\n" 166 | "MaxFragmentImageUniforms 65536\n" 167 | "MaxCombinedImageUniforms 65536\n" 168 | "MaxGeometryTextureImageUnits 65536\n" 169 | "MaxGeometryOutputVertices 256\n" 170 | "MaxGeometryTotalOutputComponents 1024\n" 171 | "MaxGeometryUniformComponents 1024\n" 172 | "MaxGeometryVaryingComponents 64\n" 173 | "MaxTessControlInputComponents 128\n" 174 | "MaxTessControlOutputComponents 128\n" 175 | "MaxTessControlTextureImageUnits 16\n" 176 | "MaxTessControlUniformComponents 1024\n" 177 | "MaxTessControlTotalOutputComponents 4096\n" 178 | "MaxTessEvaluationInputComponents 128\n" 179 | "MaxTessEvaluationOutputComponents 128\n" 180 | "MaxTessEvaluationTextureImageUnits 16\n" 181 | "MaxTessEvaluationUniformComponents 1024\n" 182 | "MaxTessPatchComponents 120\n" 183 | "MaxPatchVertices 32\n" 184 | "MaxTessGenLevel 64\n" 185 | "MaxViewports 16\n" 186 | "MaxVertexAtomicCounters 8\n" 187 | "MaxTessControlAtomicCounters 8\n" 188 | "MaxTessEvaluationAtomicCounters 8\n" 189 | "MaxGeometryAtomicCounters 8\n" 190 | "MaxFragmentAtomicCounters 8\n" 191 | "MaxCombinedAtomicCounters 8\n" 192 | "MaxAtomicCounterBindings 8\n" 193 | "MaxVertexAtomicCounterBuffers 8\n" 194 | "MaxTessControlAtomicCounterBuffers 8\n" 195 | "MaxTessEvaluationAtomicCounterBuffers 8\n" 196 | "MaxGeometryAtomicCounterBuffers 8\n" 197 | "MaxFragmentAtomicCounterBuffers 8\n" 198 | "MaxCombinedAtomicCounterBuffers 8\n" 199 | "MaxAtomicCounterBufferSize 16384\n" 200 | "MaxTransformFeedbackBuffers 4\n" 201 | "MaxTransformFeedbackInterleavedComponents 64\n" 202 | "MaxCullDistances 8\n" 203 | "MaxCombinedClipAndCullDistances 8\n" 204 | "MaxSamples 4\n" 205 | "MaxMeshOutputVerticesEXT 256\n" 206 | "MaxMeshOutputPrimitivesEXT 256\n" 207 | "MaxMeshWorkGroupSizeX_EXT 128\n" 208 | "MaxMeshWorkGroupSizeY_EXT 128\n" 209 | "MaxMeshWorkGroupSizeZ_EXT 128\n" 210 | "MaxTaskWorkGroupSizeX_EXT 128\n" 211 | "MaxTaskWorkGroupSizeY_EXT 128\n" 212 | "MaxTaskWorkGroupSizeZ_EXT 128\n" 213 | "MaxMeshViewCountEXT 4\n" 214 | "MaxDualSourceDrawBuffersEXT 1\n" 215 | 216 | "nonInductiveForLoops 1\n" 217 | "whileLoops 1\n" 218 | "doWhileLoops 1\n" 219 | "generalUniformIndexing 1\n" 220 | "generalAttributeMatrixVectorIndexing 1\n" 221 | "generalVaryingIndexing 1\n" 222 | "generalSamplerIndexing 1\n" 223 | "generalVariableIndexing 1\n" 224 | "generalConstantMatrixVectorIndexing 1\n" 225 | ; 226 | 227 | // ===================================================================================================================== 228 | // Parse either a .conf file provided by the user or the default string above. 229 | void ProcessConfigFile() 230 | { 231 | const char* config = 0; 232 | std::string configData; 233 | 234 | if (pConfigFile == nullptr) 235 | { 236 | pConfigFile = new(std::string); 237 | } 238 | 239 | if (pConfigFile->size() > 0) 240 | { 241 | if (ReadFileData(pConfigFile->c_str(), configData)) 242 | { 243 | config = configData.c_str(); 244 | } 245 | else 246 | { 247 | printf("Error opening configuration file; will instead use the default configuration\n"); 248 | } 249 | } 250 | 251 | if (config == 0) 252 | { 253 | configData = DefaultConfig; 254 | config = configData.c_str(); 255 | } 256 | 257 | const char* delims = " \t\n\r"; 258 | const char* token = strtok(const_cast(config), delims); 259 | while (token) { 260 | const char* valueStr = strtok(0, delims); 261 | if (valueStr == 0 || ! (valueStr[0] == '-' || (valueStr[0] >= '0' && valueStr[0] <= '9'))) { 262 | printf("Error: '%s' bad .conf file. Each name must be followed by one number.\n", valueStr ? valueStr : ""); 263 | return; 264 | } 265 | int value = atoi(valueStr); 266 | 267 | if (strcmp(token, "MaxLights") == 0) 268 | Resources.maxLights = value; 269 | else if (strcmp(token, "MaxClipPlanes") == 0) 270 | Resources.maxClipPlanes = value; 271 | else if (strcmp(token, "MaxTextureUnits") == 0) 272 | Resources.maxTextureUnits = value; 273 | else if (strcmp(token, "MaxTextureCoords") == 0) 274 | Resources.maxTextureCoords = value; 275 | else if (strcmp(token, "MaxVertexAttribs") == 0) 276 | Resources.maxVertexAttribs = value; 277 | else if (strcmp(token, "MaxVertexUniformComponents") == 0) 278 | Resources.maxVertexUniformComponents = value; 279 | else if (strcmp(token, "MaxVaryingFloats") == 0) 280 | Resources.maxVaryingFloats = value; 281 | else if (strcmp(token, "MaxVertexTextureImageUnits") == 0) 282 | Resources.maxVertexTextureImageUnits = value; 283 | else if (strcmp(token, "MaxCombinedTextureImageUnits") == 0) 284 | Resources.maxCombinedTextureImageUnits = value; 285 | else if (strcmp(token, "MaxTextureImageUnits") == 0) 286 | Resources.maxTextureImageUnits = value; 287 | else if (strcmp(token, "MaxFragmentUniformComponents") == 0) 288 | Resources.maxFragmentUniformComponents = value; 289 | else if (strcmp(token, "MaxDrawBuffers") == 0) 290 | Resources.maxDrawBuffers = value; 291 | else if (strcmp(token, "MaxVertexUniformVectors") == 0) 292 | Resources.maxVertexUniformVectors = value; 293 | else if (strcmp(token, "MaxVaryingVectors") == 0) 294 | Resources.maxVaryingVectors = value; 295 | else if (strcmp(token, "MaxFragmentUniformVectors") == 0) 296 | Resources.maxFragmentUniformVectors = value; 297 | else if (strcmp(token, "MaxVertexOutputVectors") == 0) 298 | Resources.maxVertexOutputVectors = value; 299 | else if (strcmp(token, "MaxFragmentInputVectors") == 0) 300 | Resources.maxFragmentInputVectors = value; 301 | else if (strcmp(token, "MinProgramTexelOffset") == 0) 302 | Resources.minProgramTexelOffset = value; 303 | else if (strcmp(token, "MaxProgramTexelOffset") == 0) 304 | Resources.maxProgramTexelOffset = value; 305 | else if (strcmp(token, "MaxClipDistances") == 0) 306 | Resources.maxClipDistances = value; 307 | else if (strcmp(token, "MaxComputeWorkGroupCountX") == 0) 308 | Resources.maxComputeWorkGroupCountX = value; 309 | else if (strcmp(token, "MaxComputeWorkGroupCountY") == 0) 310 | Resources.maxComputeWorkGroupCountY = value; 311 | else if (strcmp(token, "MaxComputeWorkGroupCountZ") == 0) 312 | Resources.maxComputeWorkGroupCountZ = value; 313 | else if (strcmp(token, "MaxComputeWorkGroupSizeX") == 0) 314 | Resources.maxComputeWorkGroupSizeX = value; 315 | else if (strcmp(token, "MaxComputeWorkGroupSizeY") == 0) 316 | Resources.maxComputeWorkGroupSizeY = value; 317 | else if (strcmp(token, "MaxComputeWorkGroupSizeZ") == 0) 318 | Resources.maxComputeWorkGroupSizeZ = value; 319 | else if (strcmp(token, "MaxComputeUniformComponents") == 0) 320 | Resources.maxComputeUniformComponents = value; 321 | else if (strcmp(token, "MaxComputeTextureImageUnits") == 0) 322 | Resources.maxComputeTextureImageUnits = value; 323 | else if (strcmp(token, "MaxComputeImageUniforms") == 0) 324 | Resources.maxComputeImageUniforms = value; 325 | else if (strcmp(token, "MaxComputeAtomicCounters") == 0) 326 | Resources.maxComputeAtomicCounters = value; 327 | else if (strcmp(token, "MaxComputeAtomicCounterBuffers") == 0) 328 | Resources.maxComputeAtomicCounterBuffers = value; 329 | else if (strcmp(token, "MaxVaryingComponents") == 0) 330 | Resources.maxVaryingComponents = value; 331 | else if (strcmp(token, "MaxVertexOutputComponents") == 0) 332 | Resources.maxVertexOutputComponents = value; 333 | else if (strcmp(token, "MaxGeometryInputComponents") == 0) 334 | Resources.maxGeometryInputComponents = value; 335 | else if (strcmp(token, "MaxGeometryOutputComponents") == 0) 336 | Resources.maxGeometryOutputComponents = value; 337 | else if (strcmp(token, "MaxFragmentInputComponents") == 0) 338 | Resources.maxFragmentInputComponents = value; 339 | else if (strcmp(token, "MaxImageUnits") == 0) 340 | Resources.maxImageUnits = value; 341 | else if (strcmp(token, "MaxCombinedImageUnitsAndFragmentOutputs") == 0) 342 | Resources.maxCombinedImageUnitsAndFragmentOutputs = value; 343 | else if (strcmp(token, "MaxCombinedShaderOutputResources") == 0) 344 | Resources.maxCombinedShaderOutputResources = value; 345 | else if (strcmp(token, "MaxImageSamples") == 0) 346 | Resources.maxImageSamples = value; 347 | else if (strcmp(token, "MaxVertexImageUniforms") == 0) 348 | Resources.maxVertexImageUniforms = value; 349 | else if (strcmp(token, "MaxTessControlImageUniforms") == 0) 350 | Resources.maxTessControlImageUniforms = value; 351 | else if (strcmp(token, "MaxTessEvaluationImageUniforms") == 0) 352 | Resources.maxTessEvaluationImageUniforms = value; 353 | else if (strcmp(token, "MaxGeometryImageUniforms") == 0) 354 | Resources.maxGeometryImageUniforms = value; 355 | else if (strcmp(token, "MaxFragmentImageUniforms") == 0) 356 | Resources.maxFragmentImageUniforms = value; 357 | else if (strcmp(token, "MaxCombinedImageUniforms") == 0) 358 | Resources.maxCombinedImageUniforms = value; 359 | else if (strcmp(token, "MaxGeometryTextureImageUnits") == 0) 360 | Resources.maxGeometryTextureImageUnits = value; 361 | else if (strcmp(token, "MaxGeometryOutputVertices") == 0) 362 | Resources.maxGeometryOutputVertices = value; 363 | else if (strcmp(token, "MaxGeometryTotalOutputComponents") == 0) 364 | Resources.maxGeometryTotalOutputComponents = value; 365 | else if (strcmp(token, "MaxGeometryUniformComponents") == 0) 366 | Resources.maxGeometryUniformComponents = value; 367 | else if (strcmp(token, "MaxGeometryVaryingComponents") == 0) 368 | Resources.maxGeometryVaryingComponents = value; 369 | else if (strcmp(token, "MaxTessControlInputComponents") == 0) 370 | Resources.maxTessControlInputComponents = value; 371 | else if (strcmp(token, "MaxTessControlOutputComponents") == 0) 372 | Resources.maxTessControlOutputComponents = value; 373 | else if (strcmp(token, "MaxTessControlTextureImageUnits") == 0) 374 | Resources.maxTessControlTextureImageUnits = value; 375 | else if (strcmp(token, "MaxTessControlUniformComponents") == 0) 376 | Resources.maxTessControlUniformComponents = value; 377 | else if (strcmp(token, "MaxTessControlTotalOutputComponents") == 0) 378 | Resources.maxTessControlTotalOutputComponents = value; 379 | else if (strcmp(token, "MaxTessEvaluationInputComponents") == 0) 380 | Resources.maxTessEvaluationInputComponents = value; 381 | else if (strcmp(token, "MaxTessEvaluationOutputComponents") == 0) 382 | Resources.maxTessEvaluationOutputComponents = value; 383 | else if (strcmp(token, "MaxTessEvaluationTextureImageUnits") == 0) 384 | Resources.maxTessEvaluationTextureImageUnits = value; 385 | else if (strcmp(token, "MaxTessEvaluationUniformComponents") == 0) 386 | Resources.maxTessEvaluationUniformComponents = value; 387 | else if (strcmp(token, "MaxTessPatchComponents") == 0) 388 | Resources.maxTessPatchComponents = value; 389 | else if (strcmp(token, "MaxPatchVertices") == 0) 390 | Resources.maxPatchVertices = value; 391 | else if (strcmp(token, "MaxTessGenLevel") == 0) 392 | Resources.maxTessGenLevel = value; 393 | else if (strcmp(token, "MaxViewports") == 0) 394 | Resources.maxViewports = value; 395 | else if (strcmp(token, "MaxVertexAtomicCounters") == 0) 396 | Resources.maxVertexAtomicCounters = value; 397 | else if (strcmp(token, "MaxTessControlAtomicCounters") == 0) 398 | Resources.maxTessControlAtomicCounters = value; 399 | else if (strcmp(token, "MaxTessEvaluationAtomicCounters") == 0) 400 | Resources.maxTessEvaluationAtomicCounters = value; 401 | else if (strcmp(token, "MaxGeometryAtomicCounters") == 0) 402 | Resources.maxGeometryAtomicCounters = value; 403 | else if (strcmp(token, "MaxFragmentAtomicCounters") == 0) 404 | Resources.maxFragmentAtomicCounters = value; 405 | else if (strcmp(token, "MaxCombinedAtomicCounters") == 0) 406 | Resources.maxCombinedAtomicCounters = value; 407 | else if (strcmp(token, "MaxAtomicCounterBindings") == 0) 408 | Resources.maxAtomicCounterBindings = value; 409 | else if (strcmp(token, "MaxVertexAtomicCounterBuffers") == 0) 410 | Resources.maxVertexAtomicCounterBuffers = value; 411 | else if (strcmp(token, "MaxTessControlAtomicCounterBuffers") == 0) 412 | Resources.maxTessControlAtomicCounterBuffers = value; 413 | else if (strcmp(token, "MaxTessEvaluationAtomicCounterBuffers") == 0) 414 | Resources.maxTessEvaluationAtomicCounterBuffers = value; 415 | else if (strcmp(token, "MaxGeometryAtomicCounterBuffers") == 0) 416 | Resources.maxGeometryAtomicCounterBuffers = value; 417 | else if (strcmp(token, "MaxFragmentAtomicCounterBuffers") == 0) 418 | Resources.maxFragmentAtomicCounterBuffers = value; 419 | else if (strcmp(token, "MaxCombinedAtomicCounterBuffers") == 0) 420 | Resources.maxCombinedAtomicCounterBuffers = value; 421 | else if (strcmp(token, "MaxAtomicCounterBufferSize") == 0) 422 | Resources.maxAtomicCounterBufferSize = value; 423 | else if (strcmp(token, "MaxTransformFeedbackBuffers") == 0) 424 | Resources.maxTransformFeedbackBuffers = value; 425 | else if (strcmp(token, "MaxTransformFeedbackInterleavedComponents") == 0) 426 | Resources.maxTransformFeedbackInterleavedComponents = value; 427 | else if (strcmp(token, "MaxCullDistances") == 0) 428 | Resources.maxCullDistances = value; 429 | else if (strcmp(token, "MaxCombinedClipAndCullDistances") == 0) 430 | Resources.maxCombinedClipAndCullDistances = value; 431 | else if (strcmp(token, "MaxSamples") == 0) 432 | Resources.maxSamples = value; 433 | else if (strcmp(token, "MaxMeshOutputVerticesEXT") == 0) 434 | Resources.maxMeshOutputVerticesEXT = value; 435 | else if (strcmp(token, "MaxMeshOutputPrimitivesEXT") == 0) 436 | Resources.maxMeshOutputPrimitivesEXT = value; 437 | else if (strcmp(token, "MaxMeshWorkGroupSizeX_EXT") == 0) 438 | Resources.maxMeshWorkGroupSizeX_EXT = value; 439 | else if (strcmp(token, "MaxMeshWorkGroupSizeY_EXT") == 0) 440 | Resources.maxMeshWorkGroupSizeY_EXT = value; 441 | else if (strcmp(token, "MaxMeshWorkGroupSizeZ_EXT") == 0) 442 | Resources.maxMeshWorkGroupSizeZ_EXT = value; 443 | else if (strcmp(token, "MaxTaskWorkGroupSizeX_EXT") == 0) 444 | Resources.maxTaskWorkGroupSizeX_EXT = value; 445 | else if (strcmp(token, "MaxTaskWorkGroupSizeY_EXT") == 0) 446 | Resources.maxTaskWorkGroupSizeY_EXT = value; 447 | else if (strcmp(token, "MaxTaskWorkGroupSizeZ_EXT") == 0) 448 | Resources.maxTaskWorkGroupSizeZ_EXT = value; 449 | else if (strcmp(token, "MaxMeshViewCountEXT") == 0) 450 | Resources.maxMeshViewCountEXT = value; 451 | else if (strcmp(token, "MaxDualSourceDrawBuffersEXT") == 0) 452 | Resources.maxDualSourceDrawBuffersEXT = value; 453 | 454 | else if (strcmp(token, "nonInductiveForLoops") == 0) 455 | Resources.limits.nonInductiveForLoops = (value != 0); 456 | else if (strcmp(token, "whileLoops") == 0) 457 | Resources.limits.whileLoops = (value != 0); 458 | else if (strcmp(token, "doWhileLoops") == 0) 459 | Resources.limits.doWhileLoops = (value != 0); 460 | else if (strcmp(token, "generalUniformIndexing") == 0) 461 | Resources.limits.generalUniformIndexing = (value != 0); 462 | else if (strcmp(token, "generalAttributeMatrixVectorIndexing") == 0) 463 | Resources.limits.generalAttributeMatrixVectorIndexing = (value != 0); 464 | else if (strcmp(token, "generalVaryingIndexing") == 0) 465 | Resources.limits.generalVaryingIndexing = (value != 0); 466 | else if (strcmp(token, "generalSamplerIndexing") == 0) 467 | Resources.limits.generalSamplerIndexing = (value != 0); 468 | else if (strcmp(token, "generalVariableIndexing") == 0) 469 | Resources.limits.generalVariableIndexing = (value != 0); 470 | else if (strcmp(token, "generalConstantMatrixVectorIndexing") == 0) 471 | Resources.limits.generalConstantMatrixVectorIndexing = (value != 0); 472 | else 473 | printf("Warning: unrecognized limit (%s) in configuration file.\n", token); 474 | 475 | token = strtok(0, delims); 476 | } 477 | } 478 | 479 | // ===================================================================================================================== 480 | // Get SPIR-V target environment from the input SPIR-V binary 481 | spv_target_env GetSpirvTargetEnv( 482 | const uint32_t* pSpvToken) 483 | { 484 | spv_target_env targetEnv = SPV_ENV_UNIVERSAL_1_0; 485 | 486 | assert(pSpvToken[0] == spv::MagicNumber); 487 | unsigned int version = pSpvToken[1]; 488 | unsigned int versionMajor = ((version >> 16) & 0xFF); 489 | unsigned int versionMinor = ((version >> 8) & 0xFF); 490 | 491 | if ((versionMajor == 1) && (versionMinor == 0)) 492 | { 493 | targetEnv = SPV_ENV_UNIVERSAL_1_0; 494 | } 495 | else if ((versionMajor == 1) && (versionMinor == 1)) 496 | { 497 | targetEnv = SPV_ENV_UNIVERSAL_1_1; 498 | } 499 | else if ((versionMajor == 1) && (versionMinor == 2)) 500 | { 501 | targetEnv = SPV_ENV_UNIVERSAL_1_2; 502 | } 503 | else if ((versionMajor == 1) && (versionMinor == 3)) 504 | { 505 | targetEnv = SPV_ENV_UNIVERSAL_1_3; 506 | } 507 | else if ((versionMajor == 1) && (versionMinor == 4)) 508 | { 509 | targetEnv = SPV_ENV_UNIVERSAL_1_4; 510 | } 511 | else if ((versionMajor == 1) && (versionMinor == 5)) 512 | { 513 | targetEnv = SPV_ENV_UNIVERSAL_1_5; 514 | } 515 | else if ((versionMajor == 1) && (versionMinor == 6)) 516 | { 517 | targetEnv = SPV_ENV_UNIVERSAL_1_6; 518 | } 519 | else 520 | { 521 | assert(!"Unknown SPIR-V version"); // Should be known version 522 | } 523 | 524 | return targetEnv; 525 | } 526 | 527 | // ===================================================================================================================== 528 | // Get SPIR-V target environment from the input SPIR-V text 529 | spv_target_env GetSpirvTargetEnv( 530 | const char* pSpvText) 531 | { 532 | spv_target_env targetEnv = SPV_ENV_UNIVERSAL_1_3; // Set the default to SPIR-V 1.3 533 | 534 | std::string spvText(pSpvText); 535 | 536 | auto pos = spvText.find("; Version: "); 537 | if (pos != std::string::npos) 538 | { 539 | pos += strlen("; Version: "); 540 | unsigned int versionMajor = spvText[pos] - '0'; 541 | unsigned int versionMinor = spvText[pos + 2] - '0'; 542 | if ((versionMajor == 1) && (versionMinor == 0)) 543 | { 544 | targetEnv = SPV_ENV_UNIVERSAL_1_0; 545 | } 546 | else if ((versionMajor == 1) && (versionMinor == 1)) 547 | { 548 | targetEnv = SPV_ENV_UNIVERSAL_1_1; 549 | } 550 | else if ((versionMajor == 1) && (versionMinor == 2)) 551 | { 552 | targetEnv = SPV_ENV_UNIVERSAL_1_2; 553 | } 554 | else if ((versionMajor == 1) && (versionMinor == 3)) 555 | { 556 | targetEnv = SPV_ENV_UNIVERSAL_1_3; 557 | } 558 | else if ((versionMajor == 1) && (versionMinor == 4)) 559 | { 560 | targetEnv = SPV_ENV_UNIVERSAL_1_4; 561 | } 562 | else if ((versionMajor == 1) && (versionMinor == 5)) 563 | { 564 | targetEnv = SPV_ENV_UNIVERSAL_1_5; 565 | } 566 | else if ((versionMajor == 1) && (versionMinor == 6)) 567 | { 568 | targetEnv = SPV_ENV_UNIVERSAL_1_6; 569 | } 570 | } 571 | 572 | return targetEnv; 573 | } 574 | 575 | // ===================================================================================================================== 576 | // *.conf => this is a config file that can set limits/resources 577 | bool SetConfigFile( 578 | const std::string& name) 579 | { 580 | if (name.size() < 5) 581 | return false; 582 | 583 | if (name.compare(name.size() - 5, 5, ".conf") == 0) { 584 | *pConfigFile = name; 585 | return true; 586 | } 587 | 588 | return false; 589 | } 590 | 591 | // ===================================================================================================================== 592 | // Translate the meaningful subset of command-line options to parser-behavior options. 593 | void SetMessageOptions(EShMessages& messages, int options) 594 | { 595 | messages = (EShMessages)(messages | EShMsgSpvRules); 596 | if (options & SpvGenOptionVulkanRules) 597 | messages = (EShMessages)(messages | EShMsgVulkanRules); 598 | if (options & SpvGenOptionReadHlsl) 599 | messages = (EShMessages)(messages | EShMsgReadHlsl); 600 | if (options & SpvGenOptionHlslOffsets) 601 | messages = (EShMessages)(messages | EShMsgHlslOffsets); 602 | if (options & SpvGenOptionDebug) 603 | messages = (EShMessages)(messages | EShMsgDebugInfo); 604 | if (options & SpvGenOptionOptimizeDisable) 605 | messages = (EShMessages)(messages | EShMsgHlslLegalization); 606 | if (options & SpvGenOptionHlslDX9compatible) 607 | messages = (EShMessages)(messages | EShMsgHlslDX9Compatible); 608 | if (options & SpvGenOptionHlslEnable16BitTypes) 609 | messages = (EShMessages)(messages | EShMsgHlslEnable16BitTypes); 610 | } 611 | 612 | // ===================================================================================================================== 613 | // Represents the result of spvCompileAndLinkProgram* 614 | class SpvProgram 615 | { 616 | public: 617 | // Constructor 618 | SpvProgram(uint32_t stageCount) 619 | : 620 | spirvs(stageCount) 621 | { 622 | programs.push_back(new glslang::TProgram); 623 | } 624 | 625 | // Destructor 626 | ~SpvProgram() 627 | { 628 | for (uint32_t i = 0; i < programs.size(); ++i) 629 | { 630 | delete programs[i]; 631 | } 632 | programs.clear(); 633 | } 634 | 635 | // Add shader to current program 636 | void addShader(glslang::TShader* shader) 637 | { 638 | GetCurrentProgram()->addShader(shader); 639 | } 640 | 641 | // Link shader for current program 642 | bool link(EShMessages message) 643 | { 644 | return GetCurrentProgram()->link(message); 645 | } 646 | 647 | // Map IO for current program 648 | bool mapIO() 649 | { 650 | return GetCurrentProgram()->mapIO(); 651 | } 652 | 653 | // Get link log from current program 654 | const char* getInfoLog() 655 | { 656 | return GetCurrentProgram()->getInfoLog(); 657 | } 658 | 659 | // Get link debug log from current program 660 | const char* getInfoDebugLog() 661 | { 662 | return GetCurrentProgram()->getInfoLog(); 663 | } 664 | 665 | // Get intermediate tree from current program with specified shader stage 666 | glslang::TIntermediate* getIntermediate( 667 | EShLanguage stage 668 | ) const 669 | { 670 | return GetCurrentProgram()->getIntermediate(stage); 671 | } 672 | 673 | // Add log to SPV program log 674 | void AddLog( 675 | const char* log) 676 | { 677 | programLog += log; 678 | }; 679 | 680 | // Format link info 681 | void FormatLinkInfo( 682 | const char* linkInfo, 683 | std::string& formatedString) 684 | { 685 | char buffer[256]; 686 | char* infoEntry[EShLangCount] = {}; 687 | int length = (int)strlen(linkInfo); 688 | char* pInfoCopy = new char [length + 1]; 689 | char* pLog = pInfoCopy; 690 | strcpy(pLog, linkInfo); 691 | for (int i = 0; i < EShLangCount; ++i) 692 | { 693 | sprintf(buffer, "\nLinked %s stage:\n\n", glslang::StageName((EShLanguage)i)); 694 | infoEntry[i] = strstr(pLog, buffer); 695 | if (infoEntry[i] != NULL) 696 | { 697 | infoEntry[i][0] = '\0'; 698 | infoEntry[i] += strlen(buffer); 699 | pLog = infoEntry[i]; 700 | } 701 | } 702 | 703 | for (int i = 0; i < EShLangCount; ++i) 704 | { 705 | if ((infoEntry[i] != nullptr) && (strlen(infoEntry[i]) > 0)) 706 | { 707 | sprintf(buffer, "Linking %s stage:\n", glslang::StageName((EShLanguage)i)); 708 | formatedString += buffer; 709 | formatedString += infoEntry[i]; 710 | } 711 | } 712 | delete[] pInfoCopy; 713 | } 714 | 715 | // Get current program 716 | glslang::TProgram* GetCurrentProgram() 717 | { 718 | return programs.back(); 719 | } 720 | 721 | const glslang::TProgram* GetCurrentProgram() const 722 | { 723 | return programs.back(); 724 | } 725 | 726 | // Append a new program 727 | void AddProgram() 728 | { 729 | programs.push_back(new glslang::TProgram); 730 | } 731 | 732 | std::string programLog; 733 | std::vector programs; 734 | std::vector > spirvs; 735 | }; 736 | 737 | // ===================================================================================================================== 738 | // Compile and link GLSL source from file list 739 | bool SH_IMPORT_EXPORT spvCompileAndLinkProgramFromFile( 740 | int fileNum, 741 | const char* fileList[], 742 | void** ppProgram, 743 | const char** ppLog) 744 | { 745 | return spvCompileAndLinkProgramFromFileEx(fileNum, 746 | fileList, 747 | nullptr, 748 | ppProgram, 749 | ppLog, 750 | DefaultOptions); 751 | } 752 | 753 | // ===================================================================================================================== 754 | // Compile and link GLSL source from file list with full parameters 755 | bool SH_IMPORT_EXPORT spvCompileAndLinkProgramFromFileEx( 756 | int fileNum, 757 | const char* fileList[], 758 | const char* entryPoints[], 759 | void** ppProgram, 760 | const char** ppLog, 761 | int options) 762 | { 763 | std::vector sources(fileNum); 764 | std::vector stageTypes(fileNum); 765 | std::vector sourceCount(fileNum); 766 | std::vector sourcesPtr(fileNum); 767 | std::vector sourceListPtr(fileNum); 768 | std::vector fileListPtr(fileNum); 769 | bool isHlsl = false; 770 | 771 | for (int i = 0; i < fileNum; ++i) 772 | { 773 | stageTypes[i] = spvGetStageTypeFromName(fileList[i], &isHlsl); 774 | if (ReadFileData(fileList[i], sources[i])) 775 | { 776 | return false; 777 | } 778 | sourceCount[i] = 1; 779 | } 780 | 781 | for (int i = 0; i < fileNum; ++i) 782 | { 783 | sourcesPtr[i] = sources[i].c_str(); 784 | sourceListPtr[i] = &sourcesPtr[i]; 785 | fileListPtr[i] = &fileList[i]; 786 | } 787 | 788 | if (isHlsl) 789 | { 790 | options |= SpvGenOptionReadHlsl; 791 | } 792 | return spvCompileAndLinkProgramEx(fileNum, 793 | &stageTypes[0], 794 | &sourceCount[0], 795 | &sourceListPtr[0], 796 | &fileListPtr[0], 797 | entryPoints, 798 | ppProgram, 799 | ppLog, 800 | options); 801 | } 802 | 803 | // ===================================================================================================================== 804 | // Compile and link GLSL source strings, and the result is stored in pProgram 805 | bool SH_IMPORT_EXPORT spvCompileAndLinkProgram( 806 | int shaderStageSourceCounts[SpvGenNativeStageCount], 807 | const char* const * shaderStageSources[SpvGenNativeStageCount], 808 | void** ppProgram, 809 | const char** ppLog) 810 | { 811 | static const SpvGenStage stageTypes[SpvGenNativeStageCount] = 812 | { 813 | SpvGenStageTask, 814 | SpvGenStageVertex, 815 | SpvGenStageTessControl, 816 | SpvGenStageTessEvaluation, 817 | SpvGenStageGeometry, 818 | SpvGenStageMesh, 819 | SpvGenStageFragment, 820 | SpvGenStageCompute, 821 | }; 822 | static const char* const* fileList[SpvGenNativeStageCount] = {}; 823 | return spvCompileAndLinkProgramEx(SpvGenNativeStageCount, 824 | stageTypes, 825 | shaderStageSourceCounts, 826 | shaderStageSources, 827 | fileList, 828 | nullptr, 829 | ppProgram, 830 | ppLog, 831 | DefaultOptions); 832 | } 833 | 834 | // ===================================================================================================================== 835 | // Compile and link GLSL source strings with full parameters 836 | bool SH_IMPORT_EXPORT spvCompileAndLinkProgramEx( 837 | int stageCount, 838 | const SpvGenStage* stageTypeList, 839 | const int* shaderStageSourceCounts, 840 | const char* const * shaderStageSources[], 841 | const char* const * fileList[], 842 | const char* entryPoints[], 843 | void** ppProgram, 844 | const char** ppLog, 845 | int options) 846 | { 847 | // Set the version of the input semantics. 848 | const int ClientInputSemanticsVersion = 100; 849 | 850 | EShMessages messages = EShMsgDefault; 851 | SetMessageOptions(messages, options); 852 | 853 | bool compileFailed = false; 854 | bool linkFailed = false; 855 | 856 | SpvProgram* pProgram = new SpvProgram(stageCount); 857 | *ppProgram = pProgram; 858 | 859 | uint32_t stageMask = 0; 860 | std::vector shaders(stageCount); 861 | for (int i = 0, linkIndexBase = 0; i < stageCount; ++i) 862 | { 863 | if (shaderStageSourceCounts[i] > 0) 864 | { 865 | assert(shaderStageSources[i] != nullptr); 866 | assert(stageTypeList[i] < SpvGenStageCount); 867 | EShLanguage stage = SpvGenStageToEShLanguage(stageTypeList[i]); 868 | 869 | // Per-shader processing... 870 | glslang::TShader* pShader = new glslang::TShader(stage); 871 | shaders[i] = pShader; 872 | 873 | if (fileList == nullptr || fileList[i] == nullptr) 874 | { 875 | pShader->setStrings(shaderStageSources[i], shaderStageSourceCounts[i]); 876 | } 877 | else 878 | { 879 | pShader->setStringsWithLengthsAndNames(shaderStageSources[i], nullptr, fileList[i], shaderStageSourceCounts[i]); 880 | } 881 | 882 | if (options & SpvGenOptionVulkanRules) 883 | { 884 | pShader->setEnvInput((options & SpvGenOptionReadHlsl) ? glslang::EShSourceHlsl : glslang::EShSourceGlsl, 885 | stage, 886 | glslang::EShClientVulkan, 887 | ClientInputSemanticsVersion); 888 | pShader->setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_1); 889 | } 890 | else 891 | { 892 | assert((options & SpvGenOptionReadHlsl) == 0); // Assume OGL don't use HLSL 893 | pShader->setEnvInput((options & SpvGenOptionReadHlsl) ? glslang::EShSourceHlsl : glslang::EShSourceGlsl, 894 | stage, 895 | glslang::EShClientOpenGL, 896 | ClientInputSemanticsVersion); 897 | pShader->setEnvClient(glslang::EShClientOpenGL, glslang::EShTargetOpenGL_450); 898 | } 899 | 900 | pShader->setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_6); 901 | 902 | if (entryPoints && entryPoints[i]) 903 | { 904 | pShader->setEntryPoint(entryPoints[i]); 905 | } 906 | 907 | if ((options & SpvGenOptionFlattenUniformArrays) != 0 && 908 | (options & SpvGenOptionReadHlsl) == 0) 909 | { 910 | pProgram->AddLog("uniform array flattening only valid when compiling HLSL source."); 911 | return false; 912 | } 913 | 914 | pShader->setFlattenUniformArrays((options & SpvGenOptionFlattenUniformArrays) != 0); 915 | 916 | if (options & SpvGenOptionHlslIoMapping) 917 | { 918 | pShader->setHlslIoMapping(true); 919 | } 920 | 921 | if (options & SpvGenOptionAutoMapBindings) 922 | { 923 | pShader->setAutoMapBindings(true); 924 | } 925 | 926 | if (options & SpvGenOptionAutoMapLocations) 927 | { 928 | pShader->setAutoMapLocations(true); 929 | } 930 | 931 | if (options & SpvGenOptionInvertY) 932 | { 933 | pShader->setInvertY(true); 934 | } 935 | 936 | DirStackFileIncluder includer; 937 | compileFailed = !pShader->parse(&Resources, 938 | (options & SpvGenOptionDefaultDesktop) ? 110 : 100, 939 | false, 940 | messages, 941 | includer); 942 | 943 | if (compileFailed == false) 944 | { 945 | pProgram->addShader(pShader); 946 | stageMask |= (1 << stageTypeList[i]); 947 | } 948 | 949 | if ((options & SpvGenOptionSuppressInfolog) == false) 950 | { 951 | const char* pInfoLog = pShader->getInfoLog(); 952 | const char* pDebugLog = pShader->getInfoDebugLog(); 953 | if ((strlen(pInfoLog) > 0) || (strlen(pDebugLog) > 0)) 954 | { 955 | char buffer[256]; 956 | sprintf(buffer, "Compiling %s stage:\n", glslang::StageName(stage)); 957 | pProgram->AddLog(buffer); 958 | pProgram->AddLog(pShader->getInfoLog()); 959 | pProgram->AddLog(pShader->getInfoDebugLog()); 960 | } 961 | } 962 | } 963 | 964 | bool doLink = false; 965 | if (compileFailed == false) 966 | { 967 | if (i == stageCount - 1) 968 | { 969 | doLink = true; 970 | } 971 | else if (shaderStageSourceCounts[i + 1] > 0) 972 | { 973 | if (stageMask & (1 << stageTypeList[i + 1])) 974 | { 975 | doLink = true; 976 | } 977 | } 978 | 979 | if (doLink) 980 | { 981 | // Program-level processing... 982 | linkFailed = !pProgram->link(messages); 983 | 984 | // Map IO, consistent with glslangValidator StandAlone: https://github.com/KhronosGroup/glslang/blob/master/StandAlone/StandAlone.cpp line 1138 985 | linkFailed = !pProgram->mapIO(); 986 | 987 | if ((options & SpvGenOptionSuppressInfolog) == false) 988 | { 989 | const char* pInfoLog = pProgram->getInfoLog(); 990 | const char* pDebugLog = pProgram->getInfoDebugLog(); 991 | std::string formatLog; 992 | pProgram->FormatLinkInfo(pInfoLog, formatLog); 993 | if (formatLog.size() > 0 || strlen(pDebugLog) > 0) 994 | { 995 | pProgram->AddLog(formatLog.c_str()); 996 | pProgram->AddLog(pDebugLog); 997 | } 998 | } 999 | 1000 | if (linkFailed) 1001 | { 1002 | *ppLog = pProgram->programLog.c_str(); 1003 | return false; 1004 | } 1005 | 1006 | for (int linkIndex = linkIndexBase; linkIndex <= i; ++linkIndex) 1007 | { 1008 | if (shaders[linkIndex] != nullptr) 1009 | { 1010 | EShLanguage linkStage = shaders[linkIndex]->getStage(); 1011 | auto pIntermediate = pProgram->getIntermediate(linkStage); 1012 | glslang::SpvOptions spvOptions = {}; 1013 | spvOptions.generateDebugInfo = (options & SpvGenOptionDebug) != 0; 1014 | spvOptions.disableOptimizer = (options & SpvGenOptionOptimizeDisable) != 0; 1015 | spvOptions.optimizeSize = (options & SpvGenOptionOptimizeSize) != 0; 1016 | glslang::GlslangToSpv(*pIntermediate, pProgram->spirvs[linkIndex], &spvOptions); 1017 | } 1018 | } 1019 | linkIndexBase = i + 1; 1020 | pProgram->AddProgram(); 1021 | stageMask = 0; 1022 | } 1023 | } 1024 | } 1025 | 1026 | for (size_t i = 0; i < shaders.size(); ++i) 1027 | { 1028 | delete shaders[i]; 1029 | } 1030 | shaders.clear(); 1031 | 1032 | *ppLog = pProgram->programLog.c_str(); 1033 | return (compileFailed || linkFailed) ? false : true; 1034 | } 1035 | 1036 | // ===================================================================================================================== 1037 | // Destroies SpvProgram object 1038 | void SH_IMPORT_EXPORT spvDestroyProgram( 1039 | void* hProgram) 1040 | { 1041 | SpvProgram* pProgram = reinterpret_cast(hProgram); 1042 | delete pProgram; 1043 | } 1044 | 1045 | // ===================================================================================================================== 1046 | // Get SPRIV binary from OGLProgram object with specified shader stage, and return the binary size in bytes 1047 | // 1048 | // NOTE: 0 is returned if SPIRVI binary isn't exist for specified shader stage 1049 | int SH_IMPORT_EXPORT spvGetSpirvBinaryFromProgram( 1050 | void* hProgram, 1051 | int stage, 1052 | const unsigned int** ppData) 1053 | { 1054 | SpvProgram* pProgram = reinterpret_cast(hProgram); 1055 | int programSize = (int)(pProgram->spirvs[stage].size() * sizeof(unsigned int)); 1056 | if (programSize > 0) 1057 | { 1058 | *ppData = &pProgram->spirvs[stage][0]; 1059 | } 1060 | else 1061 | { 1062 | *ppData = nullptr; 1063 | } 1064 | return programSize; 1065 | } 1066 | 1067 | // ===================================================================================================================== 1068 | // Deduce the language from the filename. Files must end in one of the following extensions: 1069 | SpvGenStage SH_IMPORT_EXPORT spvGetStageTypeFromName( 1070 | const char* pName, 1071 | bool* pIsHlsl) 1072 | { 1073 | std::string name = pName; 1074 | size_t ext = name.rfind('.'); 1075 | if (ext == std::string::npos) 1076 | { 1077 | return SpvGenStageInvalid; 1078 | } 1079 | 1080 | std::string suffix = name.substr(ext + 1, std::string::npos); 1081 | if (suffix == "glsl" || suffix == "hlsl") 1082 | { 1083 | if (suffix == "hlsl") 1084 | { 1085 | *pIsHlsl = true; 1086 | } 1087 | 1088 | // for .vert.glsl, .tesc.glsl, ..., .comp.glsl/.hlsl compound suffixes 1089 | name = name.substr(0, ext); 1090 | ext = name.rfind('.'); 1091 | if (ext == std::string::npos) { 1092 | return SpvGenStageInvalid; 1093 | } 1094 | suffix = name.substr(ext + 1, std::string::npos); 1095 | } 1096 | 1097 | if (suffix == "task") 1098 | return SpvGenStageTask; 1099 | else if (suffix == "vert") 1100 | return SpvGenStageVertex; 1101 | else if (suffix == "tesc") 1102 | return SpvGenStageTessControl; 1103 | else if (suffix == "tese") 1104 | return SpvGenStageTessEvaluation; 1105 | else if (suffix == "geom") 1106 | return SpvGenStageGeometry; 1107 | else if (suffix == "mesh") 1108 | return SpvGenStageMesh; 1109 | else if (suffix == "frag") 1110 | return SpvGenStageFragment; 1111 | else if (suffix == "comp") 1112 | return SpvGenStageCompute; 1113 | else if (suffix == "rgen") 1114 | return SpvGenStageRayTracingRayGen; 1115 | else if (suffix == "rint") 1116 | return SpvGenStageRayTracingIntersect; 1117 | else if (suffix == "rahit") 1118 | return SpvGenStageRayTracingAnyHit; 1119 | else if (suffix == "rchit") 1120 | return SpvGenStageRayTracingClosestHit; 1121 | else if (suffix == "rmiss") 1122 | return SpvGenStageRayTracingMiss; 1123 | else if (suffix == "rcall") 1124 | return SpvGenStageRayTracingCallable; 1125 | 1126 | return SpvGenStageInvalid; 1127 | } 1128 | 1129 | // ===================================================================================================================== 1130 | // Gets component version according to specified SpvGenVersion enum 1131 | bool SH_IMPORT_EXPORT spvGetVersion( 1132 | SpvGenVersion version, 1133 | unsigned int* pVersion, 1134 | unsigned int* pReversion) 1135 | { 1136 | bool result = true; 1137 | switch (version) 1138 | { 1139 | case SpvGenVersionGlslang: 1140 | { 1141 | *pVersion = glslang::GetSpirvGeneratorVersion(); 1142 | *pReversion = GLSLANG_VERSION_MINOR; 1143 | break; 1144 | } 1145 | case SpvGenVersionSpirv: 1146 | { 1147 | *pVersion = spv::Version; 1148 | *pReversion = spv::Revision; 1149 | break; 1150 | } 1151 | case SpvGenVersionStd450: 1152 | { 1153 | *pVersion = spv::GLSLstd450Version; 1154 | *pReversion = spv::GLSLstd450Revision; 1155 | break; 1156 | } 1157 | case SpvGenVersionExtAmd: 1158 | { 1159 | *pVersion = spv::GLSLextAMDVersion; 1160 | *pReversion = spv::GLSLextAMDRevision; 1161 | break; 1162 | } 1163 | case SpvGenVersionSpvGen: 1164 | { 1165 | *pVersion = SPVGEN_VERSION; 1166 | *pReversion = SPVGEN_REVISION; 1167 | break; 1168 | } 1169 | default: 1170 | { 1171 | result = false; 1172 | break; 1173 | } 1174 | } 1175 | 1176 | return result; 1177 | } 1178 | 1179 | // ===================================================================================================================== 1180 | // Assemble SPIR-V text, store the result in pBuffer and return SPIRV code size in byte 1181 | // 1182 | // NOTE: If assemble success, *ppLog is nullptr, otherwise, *ppLog is the error message and -1 is returned. 1183 | int SH_IMPORT_EXPORT spvAssembleSpirv( 1184 | const char* pSpvText, 1185 | unsigned int bufSize, 1186 | unsigned int* pBuffer, 1187 | const char** ppLog) 1188 | { 1189 | int retval = -1; 1190 | 1191 | uint32_t options = SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS; 1192 | spv_binary binary; 1193 | spv_diagnostic diagnostic = nullptr; 1194 | spv_context context = spvContextCreate(GetSpirvTargetEnv(pSpvText)); 1195 | spv_result_t result = spvTextToBinaryWithOptions(context, pSpvText, 1196 | strlen(pSpvText), options, &binary, &diagnostic); 1197 | spvContextDestroy(context); 1198 | if (result == SPV_SUCCESS) 1199 | { 1200 | unsigned int codeSize = static_cast(binary->wordCount * sizeof(uint32_t)); 1201 | if (codeSize > bufSize) 1202 | { 1203 | codeSize = bufSize; 1204 | } 1205 | memcpy(pBuffer, binary->code, codeSize); 1206 | *ppLog = nullptr; 1207 | retval = codeSize; 1208 | spvBinaryDestroy(binary); 1209 | } 1210 | else 1211 | { 1212 | *ppLog = reinterpret_cast(pBuffer); 1213 | spvDiagnosticPrint(diagnostic, reinterpret_cast(pBuffer), static_cast(bufSize)); 1214 | spvDiagnosticDestroy(diagnostic); 1215 | retval = -1; 1216 | } 1217 | 1218 | return retval; 1219 | } 1220 | 1221 | // ===================================================================================================================== 1222 | // Disassemble SPIR-V binary token using khronos spirv-tools, and store the output text to pBuffer 1223 | // 1224 | // NOTE: The text will be clampped if buffer size is less than requirement. 1225 | bool SH_IMPORT_EXPORT spvDisassembleSpirv( 1226 | unsigned int size, 1227 | const void* pSpvToken, 1228 | unsigned int bufSize, 1229 | char* pBuffer) 1230 | { 1231 | uint32_t options = SPV_BINARY_TO_TEXT_OPTION_INDENT | SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES; 1232 | // If printing to standard output, then spvBinaryToText should 1233 | // do the printing. In particular, colour printing on Windows is 1234 | // controlled by modifying console objects synchronously while 1235 | // outputting to the stream rather than by injecting escape codes 1236 | // into the output stream. 1237 | // If the printing option is off, then save the text in memory, so 1238 | // it can be emitted later in this function. 1239 | spv_text text = nullptr; 1240 | spv_diagnostic diagnostic = nullptr; 1241 | spv_context context = spvContextCreate(GetSpirvTargetEnv(static_cast(pSpvToken))); 1242 | spv_result_t result = 1243 | spvBinaryToText(context, reinterpret_cast(pSpvToken), 1244 | static_cast(size) / sizeof(uint32_t), 1245 | options, &text, &diagnostic); 1246 | spvContextDestroy(context); 1247 | bool success = (result == SPV_SUCCESS); 1248 | if (success) 1249 | { 1250 | unsigned int textSize = (unsigned int)(text->length); 1251 | memcpy(pBuffer, text->str, textSize); 1252 | pBuffer[textSize] = '\0'; 1253 | } 1254 | else 1255 | { 1256 | spvDiagnosticPrint(diagnostic, pBuffer, bufSize); 1257 | spvDiagnosticDestroy(diagnostic); 1258 | } 1259 | 1260 | spvTextDestroy(text); 1261 | return success; 1262 | } 1263 | 1264 | // ===================================================================================================================== 1265 | // convert SPIR-V binary token to GLSL using Khronos SPIRV-Cross, 1266 | // 1267 | // NOTE: ppGlslSource should be freed by spvFreeBuffer 1268 | bool SH_IMPORT_EXPORT spvCrossSpirv( 1269 | SpvSourceLanguage sourceLanguage, 1270 | unsigned int size, 1271 | const void* pSpvToken, 1272 | char** ppSourceString) 1273 | { 1274 | return spvCrossSpirvEx(sourceLanguage, 0, size, pSpvToken, ppSourceString); 1275 | } 1276 | 1277 | // ===================================================================================================================== 1278 | // convert SPIR-V binary token to other shader languages using Khronos SPIRV-Cross, 1279 | // 1280 | // NOTE: ppGlslSource should be freed by spvFreeBuffer 1281 | // You need to calculate the version number of sourceLanguage; if version is set to 0, will use the default version (GLSL 450). 1282 | // For HLSL, the default version is 30 (shader model 3); version = major * 10 + minor; 1283 | // For MSL, the default version is 1.2; use make_msl_version to calculate the version number: version = (major * 10000) + (minor * 100) + patch; 1284 | bool SH_IMPORT_EXPORT spvCrossSpirvEx( 1285 | SpvSourceLanguage sourceLanguage, 1286 | uint32_t version, 1287 | unsigned int size, 1288 | const void* pSpvToken, 1289 | char** ppSourceString) 1290 | { 1291 | bool success = true; 1292 | std::string sourceString = ""; 1293 | try 1294 | { 1295 | std::vector spvBinary(size / sizeof(uint32_t)); 1296 | memcpy(spvBinary.data(), pSpvToken, size); 1297 | spirv_cross::Parser spvParser(std::move(spvBinary)); 1298 | spvParser.parse(); 1299 | 1300 | bool combineImageSamplers = false; 1301 | bool buildDummySampler = false; 1302 | std::unique_ptr pCompiler; 1303 | if (sourceLanguage == SpvSourceLanguageMSL) 1304 | { 1305 | pCompiler.reset(new spirv_cross::CompilerMSL(std::move(spvParser.get_parsed_ir()))); 1306 | auto* pMslCompiler = static_cast(pCompiler.get()); 1307 | auto mslOptions = pMslCompiler->get_msl_options(); 1308 | if (version != 0) 1309 | { 1310 | mslOptions.msl_version = version; 1311 | } 1312 | mslOptions.capture_output_to_buffer = false; 1313 | mslOptions.swizzle_texture_samples = false; 1314 | mslOptions.invariant_float_math = false; 1315 | mslOptions.pad_fragment_output_components = false; 1316 | mslOptions.tess_domain_origin_lower_left = false; 1317 | mslOptions.argument_buffers = false; 1318 | mslOptions.argument_buffers = false; 1319 | mslOptions.texture_buffer_native = false; 1320 | mslOptions.multiview = false; 1321 | mslOptions.view_index_from_device_index = false; 1322 | mslOptions.dispatch_base = false; 1323 | mslOptions.enable_decoration_binding = false; 1324 | mslOptions.force_active_argument_buffer_resources = false; 1325 | mslOptions.force_native_arrays = false; 1326 | mslOptions.enable_frag_depth_builtin = true; 1327 | mslOptions.enable_frag_stencil_ref_builtin = true; 1328 | mslOptions.enable_frag_output_mask = 0xffffffff; 1329 | mslOptions.enable_clip_distance_user_varying = true; 1330 | pMslCompiler->set_msl_options(mslOptions); 1331 | } 1332 | else if (sourceLanguage == SpvSourceLanguageHLSL) 1333 | { 1334 | pCompiler.reset(new spirv_cross::CompilerHLSL(std::move(spvParser.get_parsed_ir()))); 1335 | } 1336 | else 1337 | { 1338 | if (sourceLanguage == SpvSourceLanguageVulkan) 1339 | { 1340 | combineImageSamplers = false; 1341 | } 1342 | else 1343 | { 1344 | buildDummySampler = true; 1345 | } 1346 | pCompiler.reset(new spirv_cross::CompilerGLSL(std::move(spvParser.get_parsed_ir()))); 1347 | } 1348 | 1349 | spirv_cross::CompilerGLSL::Options commonOptions = pCompiler->get_common_options(); 1350 | if (sourceLanguage == SpvSourceLanguageESSL) 1351 | { 1352 | commonOptions.es = true; 1353 | } 1354 | if (version != 0) 1355 | { 1356 | commonOptions.version = version; 1357 | } 1358 | commonOptions.force_temporary = false; 1359 | commonOptions.separate_shader_objects = false; 1360 | commonOptions.flatten_multidimensional_arrays = false; 1361 | commonOptions.enable_420pack_extension = true; 1362 | commonOptions.vulkan_semantics = true; 1363 | commonOptions.vertex.fixup_clipspace = false; 1364 | commonOptions.vertex.flip_vert_y = false; 1365 | commonOptions.vertex.support_nonzero_base_instance = true; 1366 | commonOptions.emit_push_constant_as_uniform_buffer = false; 1367 | commonOptions.emit_uniform_buffer_as_plain_uniforms = false; 1368 | commonOptions.emit_line_directives = false; 1369 | commonOptions.enable_storage_image_qualifier_deduction = true; 1370 | commonOptions.force_zero_initialized_variables = false; 1371 | pCompiler->set_common_options(commonOptions); 1372 | 1373 | if (sourceLanguage == SpvSourceLanguageHLSL) 1374 | { 1375 | auto* pHlslCompiler = static_cast(pCompiler.get()); 1376 | auto hlslOptions = pHlslCompiler->get_hlsl_options(); 1377 | if (version != 0) 1378 | { 1379 | hlslOptions.shader_model = version; 1380 | } 1381 | hlslOptions.support_nonzero_base_vertex_base_instance = false; 1382 | hlslOptions.force_storage_buffer_as_uav = false; 1383 | hlslOptions.nonwritable_uav_texture_as_srv = false; 1384 | hlslOptions.enable_16bit_types = false; 1385 | pHlslCompiler->set_hlsl_options(hlslOptions); 1386 | 1387 | pHlslCompiler->set_resource_binding_flags(0); 1388 | } 1389 | 1390 | if (buildDummySampler) 1391 | { 1392 | uint32_t sampler = pCompiler->build_dummy_sampler_for_combined_images(); 1393 | if (sampler != 0) 1394 | { 1395 | // Set some defaults to make validation happy. 1396 | pCompiler->set_decoration(sampler, DecorationDescriptorSet, 0); 1397 | pCompiler->set_decoration(sampler, DecorationBinding, 0); 1398 | } 1399 | } 1400 | 1401 | if (combineImageSamplers) 1402 | { 1403 | pCompiler->build_combined_image_samplers(); 1404 | } 1405 | 1406 | if (sourceLanguage == SpvSourceLanguageHLSL) 1407 | { 1408 | auto* pHlslCompiler = static_cast(pCompiler.get()); 1409 | uint32_t newBuiltin = pHlslCompiler->remap_num_workgroups_builtin(); 1410 | if (newBuiltin != 0) 1411 | { 1412 | pHlslCompiler->set_decoration(newBuiltin, DecorationDescriptorSet, 0); 1413 | pHlslCompiler->set_decoration(newBuiltin, DecorationBinding, 0); 1414 | } 1415 | } 1416 | sourceString = pCompiler->compile(); 1417 | 1418 | } 1419 | catch(const std::exception& e) 1420 | { 1421 | printf("SPIRV-Cross threw an exception : %s\n", e.what()); 1422 | assert(!e.what()); 1423 | success = false; 1424 | } 1425 | 1426 | size_t sourceStringSize = sourceString.length() + 1; 1427 | *ppSourceString = static_cast(malloc(sourceStringSize)); 1428 | memcpy(*ppSourceString, sourceString.c_str(), sourceStringSize); 1429 | return success; 1430 | } 1431 | 1432 | // ===================================================================================================================== 1433 | // Validate SPIR-V binary token using khronos spirv-tools, and store the log text to pLog 1434 | // 1435 | // NOTE: The text will be clampped if buffer size is less than requirement. 1436 | bool SH_IMPORT_EXPORT spvValidateSpirv( 1437 | unsigned int size, 1438 | const void* pSpvToken, 1439 | unsigned int logSize, 1440 | char* pLog) 1441 | { 1442 | spv_const_binary_t binary = 1443 | { 1444 | reinterpret_cast(pSpvToken), 1445 | static_cast(size) / sizeof(uint32_t) 1446 | }; 1447 | 1448 | spv_diagnostic diagnostic = nullptr; 1449 | spv_context context = spvContextCreate(GetSpirvTargetEnv(static_cast(pSpvToken))); 1450 | spv_result_t result = spvValidate(context, &binary, &diagnostic); 1451 | spvContextDestroy(context); 1452 | bool success = (result == SPV_SUCCESS); 1453 | if (success == false) 1454 | { 1455 | spvDiagnosticPrint(diagnostic, pLog, logSize); 1456 | spvDiagnosticDestroy(diagnostic); 1457 | } 1458 | 1459 | return success; 1460 | } 1461 | 1462 | // ===================================================================================================================== 1463 | // Optimize SPIR-V binary token using khronos spirv-tools, and store optimized result to ppOptBuf and the log text 1464 | // to pLog 1465 | // 1466 | // NOTE: The text will be clampped if buffer size is less than requirement, and ppOptBuf should be freed by 1467 | // spvFreeBuffer 1468 | bool SH_IMPORT_EXPORT spvOptimizeSpirv( 1469 | unsigned int size, 1470 | const void* pSpvToken, 1471 | int optionCount, 1472 | const char* options[], 1473 | unsigned int* pBufSize, 1474 | void** ppOptBuf, 1475 | unsigned int logSize, 1476 | char* pLog) 1477 | { 1478 | std::string errorMsg; 1479 | spvtools::Optimizer optimizer(GetSpirvTargetEnv(static_cast(pSpvToken))); 1480 | optimizer.SetMessageConsumer([&](spv_message_level_t level, 1481 | const char* source, 1482 | const spv_position_t& position, 1483 | const char* message) 1484 | { 1485 | const char* level_string = nullptr; 1486 | switch (level) 1487 | { 1488 | case SPV_MSG_FATAL: 1489 | level_string = "fatal"; 1490 | break; 1491 | case SPV_MSG_INTERNAL_ERROR: 1492 | level_string = "internal error"; 1493 | break; 1494 | case SPV_MSG_ERROR: 1495 | level_string = "error"; 1496 | break; 1497 | case SPV_MSG_WARNING: 1498 | level_string = "warning"; 1499 | break; 1500 | case SPV_MSG_INFO: 1501 | level_string = "info"; 1502 | break; 1503 | case SPV_MSG_DEBUG: 1504 | level_string = "debug"; 1505 | break; 1506 | } 1507 | std::ostringstream oss; 1508 | oss << level_string << ": "; 1509 | if (source) oss << source << ":"; 1510 | oss << position.line << ":" << position.column << ":"; 1511 | oss << position.index << ": "; 1512 | if (message) oss << message; 1513 | 1514 | errorMsg += oss.str(); 1515 | errorMsg += "\n"; 1516 | } 1517 | ); 1518 | 1519 | if (optionCount == 0) 1520 | { 1521 | optimizer.RegisterPerformancePasses(); 1522 | } 1523 | else 1524 | { 1525 | for (unsigned i = 0; i < optionCount; i++) 1526 | { 1527 | optimizer.RegisterPassFromFlag(options[i]); 1528 | } 1529 | } 1530 | 1531 | std::vector binary; 1532 | 1533 | bool ret = optimizer.Run((const uint32_t*)pSpvToken, size / sizeof(uint32_t), &binary); 1534 | 1535 | if (ret) 1536 | { 1537 | *pBufSize = static_cast(binary.size() * sizeof(uint32_t)); 1538 | *ppOptBuf = malloc(*pBufSize); 1539 | memcpy(*ppOptBuf, binary.data(), *pBufSize); 1540 | } 1541 | 1542 | if (logSize > 0) 1543 | { 1544 | if (errorMsg.empty()) 1545 | { 1546 | pLog[0] = 0; 1547 | } 1548 | else 1549 | { 1550 | strncpy(pLog, errorMsg.c_str(), logSize); 1551 | } 1552 | } 1553 | 1554 | return ret; 1555 | } 1556 | 1557 | // ===================================================================================================================== 1558 | // Free input buffer 1559 | void SH_IMPORT_EXPORT spvFreeBuffer( 1560 | void* pBuffer) 1561 | { 1562 | free(pBuffer); 1563 | } 1564 | 1565 | #if !defined _MSC_VER && !defined MINGW_HAS_SECURE_API 1566 | 1567 | #include 1568 | 1569 | int fopen_s( 1570 | FILE** pFile, 1571 | const char* filename, 1572 | const char* mode 1573 | ) 1574 | { 1575 | if (!pFile || !filename || !mode) { 1576 | return EINVAL; 1577 | } 1578 | 1579 | FILE* f = fopen(filename, mode); 1580 | if (! f) { 1581 | if (errno != 0) { 1582 | return errno; 1583 | } else { 1584 | return ENOENT; 1585 | } 1586 | } 1587 | *pFile = f; 1588 | 1589 | return 0; 1590 | } 1591 | 1592 | #endif 1593 | 1594 | // ===================================================================================================================== 1595 | // Convert SpvGenStage enumerant to corresponding EShLanguage enumerant. 1596 | EShLanguage SpvGenStageToEShLanguage( 1597 | SpvGenStage stage) // SpvGenStage enumerant 1598 | { 1599 | switch (stage) 1600 | { 1601 | case SpvGenStageTask: 1602 | return EShLangTask; 1603 | case SpvGenStageVertex: 1604 | return EShLangVertex; 1605 | case SpvGenStageTessControl: 1606 | return EShLangTessControl; 1607 | case SpvGenStageTessEvaluation: 1608 | return EShLangTessEvaluation; 1609 | case SpvGenStageGeometry: 1610 | return EShLangGeometry; 1611 | case SpvGenStageMesh: 1612 | return EShLangMesh; 1613 | case SpvGenStageFragment: 1614 | return EShLangFragment; 1615 | case SpvGenStageCompute: 1616 | return EShLangCompute; 1617 | case SpvGenStageRayTracingRayGen: 1618 | return EShLangRayGen; 1619 | case SpvGenStageRayTracingIntersect: 1620 | return EShLangIntersect; 1621 | case SpvGenStageRayTracingAnyHit: 1622 | return EShLangAnyHit; 1623 | case SpvGenStageRayTracingClosestHit: 1624 | return EShLangClosestHit; 1625 | case SpvGenStageRayTracingMiss: 1626 | return EShLangMiss; 1627 | case SpvGenStageRayTracingCallable: 1628 | return EShLangCallable; 1629 | default: 1630 | assert(!"Unexpected SpvGenStage enumerant"); 1631 | return EShLangCount; 1632 | } 1633 | } 1634 | 1635 | // ===================================================================================================================== 1636 | // Malloc a string of sufficient size and read a string into it. 1637 | bool ReadFileData( 1638 | const char* pFileName, 1639 | std::string& data) 1640 | { 1641 | std::ifstream inFile(pFileName); 1642 | if (!inFile.good()) 1643 | { 1644 | return false; 1645 | } 1646 | inFile.seekg(0, std::ios::end); 1647 | if (!inFile.good()) 1648 | { 1649 | return false; 1650 | } 1651 | size_t size = inFile.tellg(); 1652 | data.resize(size, '\0'); 1653 | inFile.seekg(0); 1654 | if (!inFile.good()) 1655 | { 1656 | return false; 1657 | } 1658 | inFile.read(&data[0], size); 1659 | if (!inFile.good()) 1660 | { 1661 | return false; 1662 | } 1663 | data.resize(inFile.gcount()); 1664 | return true; 1665 | } 1666 | 1667 | // ===================================================================================================================== 1668 | // Compiler-specific wrapper of the standard vsnprintf implementation. If buffer is a nullptr it returns the length of 1669 | // the string that would be printed had a buffer with enough space been provided. 1670 | int Vsnprintf( 1671 | char* pOutput, // [out] Output string. 1672 | size_t bufSize, // Available space in pOutput (in bytes). 1673 | const char* pFormat, // Printf-style format string. 1674 | va_list argList) // Pre-started variable argument list. 1675 | { 1676 | // It is undefined to request a (count > 0) to be copied while providing a null buffer. Covers common causes of 1677 | // crash in different versions of vsnprintf. 1678 | assert((pOutput == nullptr) ? (bufSize == 0) : (bufSize > 0)); 1679 | 1680 | int length = -1; 1681 | 1682 | #if defined(_WIN32) 1683 | if (pOutput == nullptr) 1684 | { 1685 | // Returns the resultant length of the formatted string excluding the null terminator. 1686 | length = _vscprintf(pFormat, argList); 1687 | } 1688 | else 1689 | { 1690 | // MS compilers provide vsnprintf_s, which will truncate the sprintf to prevent buffer overruns, always 1691 | // guarantee that pOutput is null-terminated, and verifies that "pFormat" doesn't have anything "bad" in it. 1692 | length = vsnprintf_s(pOutput, bufSize, _TRUNCATE, pFormat, argList); 1693 | } 1694 | #else 1695 | // vsnprintf prints upto (bufSize - 1) entries leaving space for the terminating null character. On C99 1696 | // compatible platforms, if a null buffer and size of zero is provided, it returns the length of the string. 1697 | length = vsnprintf(pOutput, bufSize, pFormat, argList); 1698 | #endif 1699 | 1700 | return length; 1701 | } 1702 | 1703 | // ===================================================================================================================== 1704 | // Variable argument wrapper on sprintf function to be used when output needs to be written to a string and no prefix 1705 | // information is required. 1706 | int Snprintf( 1707 | char* pOutput, // [out] Output string. 1708 | size_t bufSize, // Available space in pOutput (in bytes). 1709 | const char* pFormat, // Printf-style format string. 1710 | ...) // Printf-style argument list. 1711 | { 1712 | va_list argList; 1713 | va_start(argList, pFormat); 1714 | 1715 | int ret = Vsnprintf(pOutput, bufSize, pFormat, argList); 1716 | 1717 | va_end(argList); 1718 | return ret; 1719 | } 1720 | 1721 | // ===================================================================================================================== 1722 | // Print error message of spvTextToBinary() and spvBinaryToText() 1723 | spv_result_t spvDiagnosticPrint(const spv_diagnostic diagnostic, char* pBuffer, size_t bufferSize) 1724 | { 1725 | if (!diagnostic) return SPV_ERROR_INVALID_DIAGNOSTIC; 1726 | 1727 | if (diagnostic->isTextSource) { 1728 | // NOTE: This is a text position 1729 | // NOTE: add 1 to the line as editors start at line 1, we are counting new 1730 | // line characters to start at line 0 1731 | Snprintf(pBuffer, bufferSize, "error: %d: %d: %s\n", diagnostic->position.line + 1, diagnostic->position.column + 1, diagnostic->error); 1732 | return SPV_SUCCESS; 1733 | } else { 1734 | // NOTE: Assume this is a binary position 1735 | Snprintf(pBuffer, bufferSize, "error: %d: %s\n", diagnostic->position.index, diagnostic->error); 1736 | return SPV_SUCCESS; 1737 | } 1738 | } 1739 | 1740 | // ===================================================================================================================== 1741 | // Internal initilization 1742 | static void internalInit() 1743 | { 1744 | static bool init = false; 1745 | if (!init) { 1746 | ProcessConfigFile(); 1747 | glslang::InitializeProcess(); 1748 | spv::Parameterize(); 1749 | init = true; 1750 | } 1751 | } 1752 | 1753 | // ===================================================================================================================== 1754 | // Cleanup 1755 | static void internalFinal() 1756 | { 1757 | glslang::FinalizeProcess(); 1758 | } 1759 | 1760 | // ===================================================================================================================== 1761 | // Initilize the static library 1762 | bool InitSpvGen(const char* pSpvGenDir) 1763 | { 1764 | #if defined(_WIN32) 1765 | internalInit(); 1766 | #endif 1767 | return true; 1768 | } 1769 | 1770 | // ===================================================================================================================== 1771 | // Finalize the static library 1772 | void FinalizeSpvgen() 1773 | { 1774 | #if defined(_WIN32) 1775 | internalFinal(); 1776 | #endif 1777 | } 1778 | 1779 | // ===================================================================================================================== 1780 | #if defined(_WIN32) 1781 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 1782 | #include 1783 | BOOL APIENTRY DllMain( HMODULE hModule, 1784 | DWORD ul_reason_for_call, 1785 | LPVOID lpReserved 1786 | ) 1787 | { 1788 | switch (ul_reason_for_call) 1789 | { 1790 | case DLL_PROCESS_ATTACH: 1791 | internalInit(); 1792 | break; 1793 | case DLL_THREAD_ATTACH: 1794 | break; 1795 | case DLL_THREAD_DETACH: 1796 | break; 1797 | case DLL_PROCESS_DETACH: 1798 | internalFinal(); 1799 | break; 1800 | } 1801 | return TRUE; 1802 | } 1803 | #else // Linux 1804 | __attribute__((constructor)) static void Init() 1805 | { 1806 | ProcessConfigFile(); 1807 | glslang::InitializeProcess(); 1808 | spv::Parameterize(); 1809 | } 1810 | 1811 | __attribute__((destructor)) static void Destroy() 1812 | { 1813 | if (pConfigFile != nullptr) 1814 | { 1815 | delete pConfigFile; 1816 | } 1817 | 1818 | glslang::FinalizeProcess(); 1819 | } 1820 | 1821 | #endif 1822 | 1823 | --------------------------------------------------------------------------------