Updated spirv-cross.

This commit is contained in:
Бранимир Караџић 2019-03-09 10:20:48 -08:00
parent 2044efc6ca
commit 1f69e5e5d9
31 changed files with 5005 additions and 623 deletions

View File

@ -13,10 +13,20 @@
# limitations under the License.
cmake_minimum_required(VERSION 2.8)
project(SPIRV-Cross)
set(CMAKE_CXX_STANDARD 11)
project(SPIRV-Cross LANGUAGES CXX C)
enable_testing()
option(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS "Instead of throwing exceptions assert" OFF)
option(SPIRV_CROSS_SHARED "Build the C API as a single shared library." OFF)
option(SPIRV_CROSS_STATIC "Build the C and C++ API as static libraries." ON)
option(SPIRV_CROSS_CLI "Build the CLI binary. Requires SPIRV_CROSS_STATIC." ON)
option(SPIRV_CROSS_ENABLE_TESTS "Enable SPIRV-Cross tests." ON)
option(SPIRV_CROSS_SANITIZE_ADDRESS "Sanitize address" OFF)
option(SPIRV_CROSS_SANITIZE_MEMORY "Sanitize memory" OFF)
option(SPIRV_CROSS_SANITIZE_THREADS "Sanitize threads" OFF)
option(SPIRV_CROSS_SANITIZE_UNDEFINED "Sanitize undefined" OFF)
if(${CMAKE_GENERATOR} MATCHES "Makefile")
if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR})
@ -26,21 +36,40 @@ endif()
set(spirv-compiler-options "")
set(spirv-compiler-defines "")
set(spirv-cross-link-flags "")
if(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS)
set(spirv-compiler-defines ${spirv-compiler-defines} SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS)
endif()
# To specify special debug or optimization options, use
# -DCMAKE_CXX_COMPILE_FLAGS
# However, we require the C++11 dialect.
if (NOT "${MSVC}")
set(spirv-compiler-options ${spirv-compiler-options} -std=c++11 -Wall -Wextra -Werror -Wshadow)
set(spirv-compiler-defines ${spirv-compiler-defines} __STDC_LIMIT_MACROS)
if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang"))
set(spirv-compiler-options ${spirv-compiler-options} -Wall -Wextra -Werror -Wshadow)
if (SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS)
set(spirv-compiler-options ${spirv-compiler-options} -fno-exceptions)
endif()
if (SPIRV_CROSS_SANITIZE_ADDRESS)
set(spirv-compiler-options ${spirv-compiler-options} -fsanitize=address)
set(spirv-cross-link-flags "${spirv-cross-link-flags} -fsanitize=address")
endif()
if (SPIRV_CROSS_SANITIZE_UNDEFINED)
set(spirv-compiler-options ${spirv-compiler-options} -fsanitize=undefined)
set(spirv-cross-link-flags "${spirv-cross-link-flags} -fsanitize=undefined")
endif()
if (SPIRV_CROSS_SANITIZE_MEMORY)
set(spirv-compiler-options ${spirv-compiler-options} -fsanitize=memory)
set(spirv-cross-link-flags "${spirv-cross-link-flags} -fsanitize=memory")
endif()
if (SPIRV_CROSS_SANITIZE_THREADS)
set(spirv-compiler-options ${spirv-compiler-options} -fsanitize=thread)
set(spirv-cross-link-flags "${spirv-cross-link-flags} -fsanitize=thread")
endif()
elseif (MSVC)
set(spirv-compiler-options ${spirv-compiler-options} /wd4267)
endif()
macro(extract_headers out_abs file_list)
@ -48,9 +77,17 @@ macro(extract_headers out_abs file_list)
foreach(_a ${file_list})
# get_filename_component only returns the longest extension, so use a regex
string(REGEX REPLACE ".*\\.(h|hpp)" "\\1" ext ${_a})
# For shared library, we are only interested in the C header.
if (SPIRV_CROSS_STATIC)
if(("${ext}" STREQUAL "h") OR ("${ext}" STREQUAL "hpp"))
list(APPEND ${out_abs} "${_a}")
endif()
else()
if("${ext}" STREQUAL "h")
list(APPEND ${out_abs} "${_a}")
endif()
endif()
endforeach()
endmacro()
@ -75,8 +112,7 @@ macro(spirv_cross_add_library name config_name)
export(TARGETS ${name} FILE ${config_name}Config.cmake)
endmacro()
spirv_cross_add_library(spirv-cross-core spirv_cross_core STATIC
set(spirv-cross-core-sources
${CMAKE_CURRENT_SOURCE_DIR}/GLSL.std.450.h
${CMAKE_CURRENT_SOURCE_DIR}/spirv_common.hpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv.hpp
@ -89,92 +125,229 @@ spirv_cross_add_library(spirv-cross-core spirv_cross_core STATIC
${CMAKE_CURRENT_SOURCE_DIR}/spirv_cfg.hpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_cfg.cpp)
spirv_cross_add_library(spirv-cross-glsl spirv_cross_glsl STATIC
set(spirv-cross-c-sources
spirv.h
${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_c.cpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_c.h)
set(spirv-cross-glsl-sources
${CMAKE_CURRENT_SOURCE_DIR}/spirv_glsl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_glsl.hpp)
set(spirv-cross-cpp-sources
${CMAKE_CURRENT_SOURCE_DIR}/spirv_cpp.cpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_cpp.hpp)
set(spirv-cross-msl-sources
${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.hpp)
set(spirv-cross-hlsl-sources
${CMAKE_CURRENT_SOURCE_DIR}/spirv_hlsl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_hlsl.hpp)
set(spirv-cross-reflect-sources
${CMAKE_CURRENT_SOURCE_DIR}/spirv_reflect.cpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_reflect.hpp)
set(spirv-cross-util-sources
${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_util.cpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_util.hpp)
if (SPIRV_CROSS_STATIC)
spirv_cross_add_library(spirv-cross-core spirv_cross_core STATIC
${spirv-cross-core-sources})
spirv_cross_add_library(spirv-cross-c spirv_cross_c STATIC
${spirv-cross-c-sources})
spirv_cross_add_library(spirv-cross-glsl spirv_cross_glsl STATIC
${spirv-cross-glsl-sources})
spirv_cross_add_library(spirv-cross-cpp spirv_cross_cpp STATIC
${CMAKE_CURRENT_SOURCE_DIR}/spirv_cpp.hpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_cpp.cpp)
${spirv-cross-cpp-sources})
spirv_cross_add_library(spirv-cross-reflect spirv_cross_reflect STATIC
${CMAKE_CURRENT_SOURCE_DIR}/spirv_reflect.hpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_reflect.cpp)
${spirv-cross-reflect-sources})
spirv_cross_add_library(spirv-cross-msl spirv_cross_msl STATIC
${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.hpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.cpp)
${spirv-cross-msl-sources})
spirv_cross_add_library(spirv-cross-hlsl spirv_cross_hlsl STATIC
${CMAKE_CURRENT_SOURCE_DIR}/spirv_hlsl.hpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_hlsl.cpp)
${spirv-cross-hlsl-sources})
spirv_cross_add_library(spirv-cross-util spirv_cross_util STATIC
${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_util.hpp
${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_util.cpp)
${spirv-cross-util-sources})
target_link_libraries(spirv-cross-util PRIVATE spirv-cross-core)
target_link_libraries(spirv-cross-glsl PRIVATE spirv-cross-core)
target_link_libraries(spirv-cross-msl PRIVATE spirv-cross-glsl)
target_link_libraries(spirv-cross-hlsl PRIVATE spirv-cross-glsl)
target_link_libraries(spirv-cross-cpp PRIVATE spirv-cross-glsl)
target_link_libraries(spirv-cross-c PRIVATE
spirv-cross-core
spirv-cross-glsl
spirv-cross-hlsl
spirv-cross-msl
spirv-cross-cpp
spirv-cross-reflect)
endif()
if (SPIRV_CROSS_SHARED)
set(spirv-cross-abi-major 0)
set(spirv-cross-abi-minor 1)
set(spirv-cross-abi-patch 0)
set(SPIRV_CROSS_VERSION ${spirv-cross-abi-major}.${spirv-cross-abi-minor}.${spirv-cross-abi-patch})
set(SPIRV_CROSS_INSTALL_LIB_DIR ${CMAKE_INSTALL_PREFIX}/lib)
set(SPIRV_CROSS_INSTALL_INC_DIR ${CMAKE_INSTALL_PREFIX}/include/spirv_cross)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/pkg-config/spirv-cross-c-shared.pc.in
${CMAKE_CURRENT_BINARY_DIR}/spirv-cross-c-shared.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/spirv-cross-c-shared.pc DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pkgconfig)
spirv_cross_add_library(spirv-cross-c-shared spirv_cross_c_shared SHARED
${spirv-cross-core-sources}
${spirv-cross-glsl-sources}
${spirv-cross-cpp-sources}
${spirv-cross-reflect-sources}
${spirv-cross-msl-sources}
${spirv-cross-hlsl-sources}
${spirv-cross-c-sources})
if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang"))
# Only export the C API.
target_compile_options(spirv-cross-c-shared PRIVATE -fvisibility=hidden)
if (NOT APPLE)
set_target_properties(spirv-cross-c-shared PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}")
endif()
endif()
target_compile_definitions(spirv-cross-c-shared PRIVATE SPVC_EXPORT_SYMBOLS)
set_target_properties(spirv-cross-c-shared PROPERTIES
VERSION ${SPIRV_CROSS_VERSION}
SOVERSION ${spirv-cross-abi-major})
endif()
if (SPIRV_CROSS_CLI)
if (NOT SPIRV_CROSS_STATIC)
message(FATAL_ERROR "Must build static libraries if building CLI.")
endif()
add_executable(spirv-cross main.cpp)
target_compile_options(spirv-cross PRIVATE ${spirv-compiler-options})
target_compile_definitions(spirv-cross PRIVATE ${spirv-compiler-defines})
set_target_properties(spirv-cross PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}")
install(TARGETS spirv-cross RUNTIME DESTINATION bin)
target_link_libraries(spirv-cross spirv-cross-glsl spirv-cross-hlsl spirv-cross-cpp spirv-cross-reflect spirv-cross-msl spirv-cross-util spirv-cross-core)
target_link_libraries(spirv-cross-util spirv-cross-core)
target_link_libraries(spirv-cross-glsl spirv-cross-core)
target_link_libraries(spirv-cross-msl spirv-cross-glsl)
target_link_libraries(spirv-cross-hlsl spirv-cross-glsl)
target_link_libraries(spirv-cross-cpp spirv-cross-glsl)
target_link_libraries(spirv-cross PRIVATE
spirv-cross-glsl
spirv-cross-hlsl
spirv-cross-cpp
spirv-cross-reflect
spirv-cross-msl
spirv-cross-util
spirv-cross-core)
if (SPIRV_CROSS_ENABLE_TESTS)
# Set up tests, using only the simplest modes of the test_shaders
# script. You have to invoke the script manually to:
# - Update the reference files
# - Get cycle counts from malisc
# - Keep failing outputs
find_package(PythonInterp)
if (${PYTHONINTERP_FOUND})
find_program(spirv-cross-glslang NAMES glslangValidator
PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/glslang-build/output/bin
NO_DEFAULT_PATH)
find_program(spirv-cross-spirv-as NAMES spirv-as
PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/spirv-tools-build/output/bin
NO_DEFAULT_PATH)
find_program(spirv-cross-spirv-val NAMES spirv-val
PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/spirv-tools-build/output/bin
NO_DEFAULT_PATH)
find_program(spirv-cross-spirv-opt NAMES spirv-opt
PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/spirv-tools-build/output/bin
NO_DEFAULT_PATH)
if ((${spirv-cross-glslang} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-as} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-val} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-opt} MATCHES "NOTFOUND"))
set(SPIRV_CROSS_ENABLE_TESTS OFF)
message("Could not find glslang or SPIRV-Tools build under external/. Run ./checkout_glslang_spirv_tools.sh and ./build_glslang_spirv_tools.sh. Testing will be disabled.")
else()
set(SPIRV_CROSS_ENABLE_TESTS ON)
message("Found glslang and SPIRV-Tools. Enabling test suite.")
message("Found glslangValidator in: ${spirv-cross-glslang}.")
message("Found spirv-as in: ${spirv-cross-spirv-as}.")
message("Found spirv-val in: ${spirv-cross-spirv-val}.")
message("Found spirv-opt in: ${spirv-cross-spirv-opt}.")
endif()
set(spirv-cross-externals
--glslang "${spirv-cross-glslang}"
--spirv-as "${spirv-cross-spirv-as}"
--spirv-opt "${spirv-cross-spirv-opt}"
--spirv-val "${spirv-cross-spirv-val}")
if (${PYTHONINTERP_FOUND} AND SPIRV_CROSS_ENABLE_TESTS)
if (${PYTHON_VERSION_MAJOR} GREATER 2)
add_executable(spirv-cross-c-api-test tests-other/c_api_test.c)
target_link_libraries(spirv-cross-c-api-test spirv-cross-c)
set_target_properties(spirv-cross-c-api-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}")
if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang"))
target_compile_options(spirv-cross-c-api-test PRIVATE -std=c89 -Wall -Wextra)
endif()
add_test(NAME spirv-cross-c-api-test
COMMAND $<TARGET_FILE:spirv-cross-c-api-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/c_api_test.spv)
add_test(NAME spirv-cross-test
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel
${spirv-cross-externals}
${CMAKE_CURRENT_SOURCE_DIR}/shaders
WORKING_DIRECTORY $<TARGET_FILE_DIR:spirv-cross>)
add_test(NAME spirv-cross-test-no-opt
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel
${spirv-cross-externals}
${CMAKE_CURRENT_SOURCE_DIR}/shaders-no-opt
WORKING_DIRECTORY $<TARGET_FILE_DIR:spirv-cross>)
add_test(NAME spirv-cross-test-metal
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --metal --parallel
${spirv-cross-externals}
${CMAKE_CURRENT_SOURCE_DIR}/shaders-msl
WORKING_DIRECTORY $<TARGET_FILE_DIR:spirv-cross>)
add_test(NAME spirv-cross-test-metal-no-opt
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --metal --parallel
${spirv-cross-externals}
${CMAKE_CURRENT_SOURCE_DIR}/shaders-msl-no-opt
WORKING_DIRECTORY $<TARGET_FILE_DIR:spirv-cross>)
add_test(NAME spirv-cross-test-hlsl
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --hlsl --parallel
${spirv-cross-externals}
${CMAKE_CURRENT_SOURCE_DIR}/shaders-hlsl
WORKING_DIRECTORY $<TARGET_FILE_DIR:spirv-cross>)
add_test(NAME spirv-cross-test-hlsl-no-opt
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --hlsl --parallel
${spirv-cross-externals}
${CMAKE_CURRENT_SOURCE_DIR}/shaders-hlsl-no-opt
WORKING_DIRECTORY $<TARGET_FILE_DIR:spirv-cross>)
add_test(NAME spirv-cross-test-opt
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --opt --parallel
${spirv-cross-externals}
${CMAKE_CURRENT_SOURCE_DIR}/shaders
WORKING_DIRECTORY $<TARGET_FILE_DIR:spirv-cross>)
add_test(NAME spirv-cross-test-metal-opt
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --metal --opt --parallel
${spirv-cross-externals}
${CMAKE_CURRENT_SOURCE_DIR}/shaders-msl
WORKING_DIRECTORY $<TARGET_FILE_DIR:spirv-cross>)
add_test(NAME spirv-cross-test-hlsl-opt
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --hlsl --opt --parallel
${spirv-cross-externals}
${CMAKE_CURRENT_SOURCE_DIR}/shaders-hlsl
WORKING_DIRECTORY $<TARGET_FILE_DIR:spirv-cross>)
add_test(NAME spirv-cross-test-reflection
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --reflect --parallel
${spirv-cross-externals}
${CMAKE_CURRENT_SOURCE_DIR}/shaders-reflection
WORKING_DIRECTORY $<TARGET_FILE_DIR:spirv-cross>)
endif()
else()
elseif(NOT ${PYTHONINTERP_FOUND})
message(WARNING "Testing disabled. Could not find python3. If you have python3 installed try running "
"cmake with -DPYTHON_EXECUTABLE:FILEPATH=/path/to/python3 to help it find the executable")
endif()
endif()
endif()

View File

@ -10,7 +10,7 @@ STATIC_LIB := lib$(TARGET).a
DEPS := $(OBJECTS:.o=.d) $(CLI_OBJECTS:.o=.d)
CXXFLAGS += -std=c++11 -Wall -Wextra -Wshadow -D__STDC_LIMIT_MACROS
CXXFLAGS += -std=c++11 -Wall -Wextra -Wshadow
ifeq ($(DEBUG), 1)
CXXFLAGS += -O0 -g

View File

@ -10,7 +10,7 @@ SPIRV-Cross is a tool designed for parsing and converting SPIR-V to other shader
- Convert SPIR-V to readable, usable and efficient GLSL
- Convert SPIR-V to readable, usable and efficient Metal Shading Language (MSL)
- Convert SPIR-V to readable, usable and efficient HLSL
- Convert SPIR-V to debuggable C++ [EXPERIMENTAL]
- Convert SPIR-V to debuggable C++ [DEPRECATED]
- Convert SPIR-V to a JSON reflection format [EXPERIMENTAL]
- Reflection API to simplify the creation of Vulkan pipeline layouts
- Reflection API to modify and tweak OpDecorations
@ -48,11 +48,18 @@ SPIRV-Cross is only useful as a library here. Use the CMake build to link SPIRV-
The make and CMake build flavors offer the option to treat exceptions as assertions. To disable exceptions for make just append `SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS=1` to the command line. For CMake append `-DSPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS=ON`. By default exceptions are enabled.
### Static, shared and CLI
You can use `-DSPIRV_CROSS_STATIC=ON/OFF` `-DSPIRV_CROSS_SHARED=ON/OFF` `-DSPIRV_CROSS_CLI=ON/OFF` to control which modules are built (and installed).
## Usage
### Using the C++ API
For more in-depth documentation than what's provided in this README, please have a look at the [Wiki](https://github.com/KhronosGroup/SPIRV-Cross/wiki).
The C++ API is the main API for SPIRV-Cross. For more in-depth documentation than what's provided in this README,
please have a look at the [Wiki](https://github.com/KhronosGroup/SPIRV-Cross/wiki).
**NOTE**: This API is not guaranteed to be ABI-stable, and it is highly recommended to link against this API statically.
The API is generally quite stable, but it can change over time, see the C API for more stability.
To perform reflection and convert to other shader languages you can use the SPIRV-Cross API.
For example:
@ -99,19 +106,143 @@ int main()
}
```
### Using the C API wrapper
To facilitate C compatibility and compatibility with foreign programming languages, a C89-compatible API wrapper is provided. Unlike the C++ API,
the goal of this wrapper is to be fully stable, both API and ABI-wise.
This is the only interface which is supported when building SPIRV-Cross as a shared library.
An important point of the wrapper is that all memory allocations are contained in the `spvc_context`.
This simplifies the use of the API greatly. However, you should destroy the context as soon as reasonable,
or use `spvc_context_release_allocations()` if you intend to reuse the `spvc_context` object again soon.
Most functions return a `spvc_result`, where `SPVC_SUCCESS` is the only success code.
For brevity, the code below does not do any error checking.
```c
#include <spirv_cross_c.h>
const SpvId *spirv = get_spirv_data();
size_t word_count = get_spirv_word_count();
spvc_context context = NULL;
spvc_parsed_ir ir = NULL;
spvc_compiler compiler_glsl = NULL;
spvc_compiler_options options = NULL;
spvc_resources resources = NULL;
const spvc_reflected_resource *list = NULL;
const char *result = NULL;
size_t count;
size_t i;
// Create context.
spvc_context_create(&context);
// Set debug callback.
spvc_context_set_error_callback(context, error_callback, userdata);
// Parse the SPIR-V.
spvc_context_parse_spirv(context, spirv, word_count, &ir);
// Hand it off to a compiler instance and give it ownership of the IR.
spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler_glsl);
// Do some basic reflection.
spvc_compiler_create_shader_resources(compiler_glsl, &resources);
spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, &list, &count);
for (i = 0; i < count; i++)
{
printf("ID: %u, BaseTypeID: %u, TypeID: %u, Name: %s\n", list[i].id, list[i].base_type_id, list[i].type_id,
list[i].name);
printf(" Set: %u, Binding: %u\n",
spvc_compiler_get_decoration(compiler_glsl, list[i].id, SpvDecorationDescriptorSet),
spvc_compiler_get_decoration(compiler_glsl, list[i].id, SpvDecorationBinding));
}
// Modify options.
spvc_compiler_create_compiler_options(context, &options);
spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION, 330);
spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, SPVC_FALSE);
spvc_compiler_install_compiler_options(compiler_glsl, options);
spvc_compiler_compile(compiler, &result);
printf("Cross-compiled source: %s\n", result);
// Frees all memory we allocated so far.
spvc_context_destroy(context);
```
### Linking
#### CMake add_subdirectory()
This is the recommended way if you are using CMake and want to link against SPIRV-Cross statically.
#### Integrating SPIRV-Cross in a custom build system
To add SPIRV-Cross to your own codebase, just copy the source and header files from root directory
and build the relevant .cpp files you need. Make sure to build with C++11 support, e.g. `-std=c++11` in GCC and Clang.
Alternatively, the Makefile generates a libspirv-cross.a static library during build that can be linked in.
### Creating a SPIR-V file from GLSL with glslang
#### Linking against SPIRV-Cross as a system library
It is possible to link against SPIRV-Cross when it is installed as a system library,
which would be mostly relevant for Unix-like platforms.
##### pkg-config
For Unix-based systems, a pkg-config is installed for the C API, e.g.:
```
$ pkg-config spirv-cross-c-shared --libs --cflags
-I/usr/local/include/spirv_cross -L/usr/local/lib -lspirv-cross-c-shared
```
##### CMake
If the project is installed, it can be found with `find_package()`, e.g.:
```
cmake_minimum_required(VERSION 3.5)
set(CMAKE_C_STANDARD 99)
project(Test LANGUAGES C)
find_package(spirv_cross_c_shared)
if (spirv_cross_c_shared_FOUND)
message(STATUS "Found SPIRV-Cross C API! :)")
else()
message(STATUS "Could not find SPIRV-Cross C API! :(")
endif()
add_executable(test test.c)
target_link_libraries(test spirv-cross-c-shared)
```
test.c:
```c
#include <spirv_cross_c.h>
int main(void)
{
spvc_context context;
spvc_context_create(&context);
spvc_context_destroy(context);
}
```
### CLI
The CLI is suitable for basic cross-compilation tasks, but it cannot support the full flexibility that the API can.
Some examples below.
#### Creating a SPIR-V file from GLSL with glslang
```
glslangValidator -H -V -o test.spv test.frag
```
### Converting a SPIR-V file to GLSL ES
#### Converting a SPIR-V file to GLSL ES
```
glslangValidator -H -V -o test.spv shaders/comp/basic.comp
@ -122,7 +253,7 @@ glslangValidator -H -V -o test.spv shaders/comp/basic.comp
```
glslangValidator -H -V -o test.spv shaders/comp/basic.comp
./spirv-cross --version 330 test.spv --output test.comp
./spirv-cross --version 330 --no-es test.spv --output test.comp
```
#### Disable prettifying optimizations
@ -278,17 +409,22 @@ Contributions to SPIRV-Cross are welcome. See Testing and Licensing sections for
### Testing
SPIRV-Cross maintains a test suite of shaders with reference output of how the output looks after going through a roundtrip through
glslangValidator then back through SPIRV-Cross again. The reference files are stored inside the repository in order to be able to track regressions.
glslangValidator/spirv-as then back through SPIRV-Cross again.
The reference files are stored inside the repository in order to be able to track regressions.
All pull requests should ensure that test output does not change unexpectedly. This can be tested with:
```
./test_shaders.py shaders
./test_shaders.py shaders --opt
./test_shaders.py shaders-hlsl --hlsl
./test_shaders.py shaders-hlsl --hlsl --opt
./test_shaders.py shaders-msl --msl
./test_shaders.py shaders-msl --msl --opt
./test_shaders.py shaders || exit 1
./test_shaders.py shaders --opt || exit 1
./test_shaders.py shaders-no-opt || exit 1
./test_shaders.py shaders-msl --msl || exit 1
./test_shaders.py shaders-msl --msl --opt || exit 1
./test_shaders.py shaders-msl-no-opt --msl || exit 1
./test_shaders.py shaders-hlsl --hlsl || exit 1
./test_shaders.py shaders-hlsl --hlsl --opt || exit 1
./test_shaders.py shaders-hlsl-no-opt --hlsl || exit 1
./test_shaders.py shaders-reflection --reflect || exit 1
```
although there are a couple of convenience script for doing this:
@ -316,6 +452,8 @@ A pull request which does not pass testing on Travis will not be accepted howeve
When adding support for new features to SPIRV-Cross, a new shader and reference file should be added which covers usage of the new shader features in question.
Travis CI runs the test suite with the CMake, by running `ctest`. This method is compatible with MSVC.
### Licensing
Contributors of new files should add a copyright header at the top of every new source code file with their copyright
@ -332,13 +470,6 @@ command line:
./format_all.sh
## ABI concerns
### SPIR-V headers
The current repository uses the latest SPIR-V and GLSL.std.450 headers.
SPIR-V files created from older headers could have ABI issues.
## Regression testing
In shaders/ a collection of shaders are maintained for purposes of regression testing.

View File

@ -1109,8 +1109,11 @@ static int main_inner(int argc, char *argv[])
for (uint32_t i = 0; i < args.iterations; i++)
{
if (args.hlsl)
glsl = static_cast<CompilerHLSL *>(compiler.get())->compile(move(args.hlsl_attr_remap));
else
{
for (auto &remap : args.hlsl_attr_remap)
static_cast<CompilerHLSL *>(compiler.get())->add_vertex_attribute_remap(remap);
}
glsl = compiler->compile();
}

View File

@ -0,0 +1,13 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@SPIRV_CROSS_INSTALL_LIB_DIR@
sharedlibdir=@SPIRV_CROSS_INSTALL_LIB_DIR@
includedir=@SPIRV_CROSS_INSTALL_INC_DIR@
Name: spirv-cross-c-shared
Description: C API for SPIRV-Cross
Version: @SPIRV_CROSS_VERSION@
Requires:
Libs: -L${libdir} -L${sharedlibdir} -lspirv-cross-c-shared
Cflags: -I${includedir}

View File

@ -0,0 +1,13 @@
#version 450
void main()
{
int j = 0;
int i = 0;
do
{
j = ((j + i) + 1) * j;
i++;
} while (!(i == 20));
}

View File

@ -0,0 +1,11 @@
#version 450
void main()
{
int _13;
for (int _12 = 0; !(_12 == 16); _12 = _13)
{
_13 = _12 + 1;
}
}

View File

@ -0,0 +1,11 @@
#version 450
void main()
{
int _13;
for (int _12 = 0; _12 != 16; _12 = _13)
{
_13 = _12 + 1;
}
}

View File

@ -0,0 +1,11 @@
#version 450
void main()
{
int _13;
for (int _12 = 0; !(_12 == 16); _12 = _13)
{
_13 = _12 + 1;
}
}

View File

@ -0,0 +1,13 @@
#version 450
void main()
{
int i = 0;
int j = 0;
while (!(i == 20))
{
j = ((j + i) + 1) * j;
i++;
}
}

View File

@ -0,0 +1,51 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 7
; Bound: 28
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %i "i"
OpName %j "j"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%int_1 = OpConstant %int 1
%int_20 = OpConstant %int 20
%bool = OpTypeBool
%main = OpFunction %void None %3
%5 = OpLabel
%i = OpVariable %_ptr_Function_int Function
%j = OpVariable %_ptr_Function_int Function
OpStore %i %int_0
OpStore %j %int_0
OpBranch %11
%11 = OpLabel
OpLoopMerge %13 %14 None
OpBranch %12
%12 = OpLabel
%15 = OpLoad %int %j
%16 = OpLoad %int %i
%17 = OpIAdd %int %15 %16
%19 = OpIAdd %int %17 %int_1
%20 = OpLoad %int %j
%21 = OpIMul %int %19 %20
OpStore %j %21
%22 = OpLoad %int %i
%23 = OpIAdd %int %22 %int_1
OpStore %i %23
OpBranch %14
%14 = OpLabel
%24 = OpLoad %int %i
%27 = OpIEqual %bool %24 %int_20
OpBranchConditional %27 %13 %11
%13 = OpLabel
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,37 @@
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_16 = OpConstant %int 16
%bool = OpTypeBool
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
OpBranch %8
%8 = OpLabel
%10 = OpPhi %int %12 %7 %int_0 %5
OpLoopMerge %6 %7 None
OpBranch %11
%11 = OpLabel
%16 = OpIEqual %bool %10 %int_16
OpBranchConditional %16 %18 %19
%18 = OpLabel
OpBranch %6
%19 = OpLabel
OpBranch %17
%17 = OpLabel
%21 = OpIAdd %int %10 %int_1
OpBranch %7
%7 = OpLabel
%12 = OpPhi %int %21 %17
OpBranch %8
%6 = OpLabel
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,37 @@
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_16 = OpConstant %int 16
%bool = OpTypeBool
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
OpBranch %8
%8 = OpLabel
%10 = OpPhi %int %12 %7 %int_0 %5
OpLoopMerge %6 %7 None
OpBranch %11
%11 = OpLabel
%16 = OpINotEqual %bool %10 %int_16
OpBranchConditional %16 %19 %18
%18 = OpLabel
OpBranch %6
%19 = OpLabel
OpBranch %17
%17 = OpLabel
%21 = OpIAdd %int %10 %int_1
OpBranch %7
%7 = OpLabel
%12 = OpPhi %int %21 %17
OpBranch %8
%6 = OpLabel
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,35 @@
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%int_16 = OpConstant %int 16
%bool = OpTypeBool
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
OpBranch %8
%8 = OpLabel
%10 = OpPhi %int %12 %7 %int_0 %5
OpLoopMerge %6 %7 None
OpBranch %11
%11 = OpLabel
%16 = OpIEqual %bool %10 %int_16
OpBranchConditional %16 %6 %19
%19 = OpLabel
OpBranch %17
%17 = OpLabel
%21 = OpIAdd %int %10 %int_1
OpBranch %7
%7 = OpLabel
%12 = OpPhi %int %21 %17
OpBranch %8
%6 = OpLabel
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,53 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 7
; Bound: 29
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main"
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 450
OpName %main "main"
OpName %i "i"
OpName %j "j"
%void = OpTypeVoid
%3 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%int_20 = OpConstant %int 20
%bool = OpTypeBool
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%i = OpVariable %_ptr_Function_int Function
%j = OpVariable %_ptr_Function_int Function
OpStore %i %int_0
OpStore %j %int_0
OpBranch %11
%11 = OpLabel
OpLoopMerge %13 %14 None
OpBranch %15
%15 = OpLabel
%16 = OpLoad %int %i
%19 = OpIEqual %bool %16 %int_20
OpBranchConditional %19 %13 %12
%12 = OpLabel
%20 = OpLoad %int %j
%21 = OpLoad %int %i
%22 = OpIAdd %int %20 %21
%24 = OpIAdd %int %22 %int_1
%25 = OpLoad %int %j
%26 = OpIMul %int %24 %25
OpStore %j %26
%27 = OpLoad %int %i
%28 = OpIAdd %int %27 %int_1
OpStore %i %28
OpBranch %14
%14 = OpLabel
OpBranch %11
%13 = OpLabel
OpReturn
OpFunctionEnd

1213
3rdparty/spirv-cross/spirv.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2018 The Khronos Group Inc.
// Copyright (c) 2014-2019 The Khronos Group Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and/or associated documentation files (the "Materials"),
@ -26,13 +26,16 @@
// the Binary Section of the SPIR-V specification.
// Enumeration tokens for SPIR-V, in various styles:
// C, C++, C++11, JSON, Lua, Python
// C, C++, C++11, JSON, Lua, Python, C#, D
//
// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL
// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL
// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL
// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL
// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL']
// - C# will use enum classes in the Specification class located in the "Spv" namespace,
// e.g.: Spv.Specification.SourceLanguage.GLSL
// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL
//
// Some tokens act like mask values, which can be OR'd together,
// while others are mutually exclusive. The mask-like ones have
@ -47,11 +50,11 @@ namespace spv {
typedef unsigned int Id;
#define SPV_VERSION 0x10300
#define SPV_REVISION 1
#define SPV_REVISION 6
static const unsigned int MagicNumber = 0x07230203;
static const unsigned int Version = 0x00010300;
static const unsigned int Revision = 1;
static const unsigned int Revision = 6;
static const unsigned int OpCodeMask = 0xffff;
static const unsigned int WordCountShift = 16;
@ -73,6 +76,14 @@ enum ExecutionModel {
ExecutionModelFragment = 4,
ExecutionModelGLCompute = 5,
ExecutionModelKernel = 6,
ExecutionModelTaskNV = 5267,
ExecutionModelMeshNV = 5268,
ExecutionModelRayGenerationNV = 5313,
ExecutionModelIntersectionNV = 5314,
ExecutionModelAnyHitNV = 5315,
ExecutionModelClosestHitNV = 5316,
ExecutionModelMissNV = 5317,
ExecutionModelCallableNV = 5318,
ExecutionModelMax = 0x7fffffff,
};
@ -80,6 +91,7 @@ enum AddressingModel {
AddressingModelLogical = 0,
AddressingModelPhysical32 = 1,
AddressingModelPhysical64 = 2,
AddressingModelPhysicalStorageBuffer64EXT = 5348,
AddressingModelMax = 0x7fffffff,
};
@ -87,6 +99,7 @@ enum MemoryModel {
MemoryModelSimple = 0,
MemoryModelGLSL450 = 1,
MemoryModelOpenCL = 2,
MemoryModelVulkanKHR = 3,
MemoryModelMax = 0x7fffffff,
};
@ -130,7 +143,17 @@ enum ExecutionMode {
ExecutionModeLocalSizeId = 38,
ExecutionModeLocalSizeHintId = 39,
ExecutionModePostDepthCoverage = 4446,
ExecutionModeDenormPreserve = 4459,
ExecutionModeDenormFlushToZero = 4460,
ExecutionModeSignedZeroInfNanPreserve = 4461,
ExecutionModeRoundingModeRTE = 4462,
ExecutionModeRoundingModeRTZ = 4463,
ExecutionModeStencilRefReplacingEXT = 5027,
ExecutionModeOutputLinesNV = 5269,
ExecutionModeOutputPrimitivesNV = 5270,
ExecutionModeDerivativeGroupQuadsNV = 5289,
ExecutionModeDerivativeGroupLinearNV = 5290,
ExecutionModeOutputTrianglesNV = 5298,
ExecutionModeMax = 0x7fffffff,
};
@ -148,6 +171,13 @@ enum StorageClass {
StorageClassAtomicCounter = 10,
StorageClassImage = 11,
StorageClassStorageBuffer = 12,
StorageClassCallableDataNV = 5328,
StorageClassIncomingCallableDataNV = 5329,
StorageClassRayPayloadNV = 5338,
StorageClassHitAttributeNV = 5339,
StorageClassIncomingRayPayloadNV = 5342,
StorageClassShaderRecordBufferNV = 5343,
StorageClassPhysicalStorageBufferEXT = 5349,
StorageClassMax = 0x7fffffff,
};
@ -275,6 +305,10 @@ enum ImageOperandsShift {
ImageOperandsConstOffsetsShift = 5,
ImageOperandsSampleShift = 6,
ImageOperandsMinLodShift = 7,
ImageOperandsMakeTexelAvailableKHRShift = 8,
ImageOperandsMakeTexelVisibleKHRShift = 9,
ImageOperandsNonPrivateTexelKHRShift = 10,
ImageOperandsVolatileTexelKHRShift = 11,
ImageOperandsMax = 0x7fffffff,
};
@ -288,6 +322,10 @@ enum ImageOperandsMask {
ImageOperandsConstOffsetsMask = 0x00000020,
ImageOperandsSampleMask = 0x00000040,
ImageOperandsMinLodMask = 0x00000080,
ImageOperandsMakeTexelAvailableKHRMask = 0x00000100,
ImageOperandsMakeTexelVisibleKHRMask = 0x00000200,
ImageOperandsNonPrivateTexelKHRMask = 0x00000400,
ImageOperandsVolatileTexelKHRMask = 0x00000800,
};
enum FPFastMathModeShift {
@ -388,11 +426,20 @@ enum Decoration {
DecorationMaxByteOffset = 45,
DecorationAlignmentId = 46,
DecorationMaxByteOffsetId = 47,
DecorationNoSignedWrap = 4469,
DecorationNoUnsignedWrap = 4470,
DecorationExplicitInterpAMD = 4999,
DecorationOverrideCoverageNV = 5248,
DecorationPassthroughNV = 5250,
DecorationViewportRelativeNV = 5252,
DecorationSecondaryViewportRelativeNV = 5256,
DecorationPerPrimitiveNV = 5271,
DecorationPerViewNV = 5272,
DecorationPerTaskNV = 5273,
DecorationPerVertexNV = 5285,
DecorationNonUniformEXT = 5300,
DecorationRestrictPointerEXT = 5355,
DecorationAliasedPointerEXT = 5356,
DecorationHlslCounterBufferGOOGLE = 5634,
DecorationHlslSemanticGOOGLE = 5635,
DecorationMax = 0x7fffffff,
@ -469,6 +516,34 @@ enum BuiltIn {
BuiltInPositionPerViewNV = 5261,
BuiltInViewportMaskPerViewNV = 5262,
BuiltInFullyCoveredEXT = 5264,
BuiltInTaskCountNV = 5274,
BuiltInPrimitiveCountNV = 5275,
BuiltInPrimitiveIndicesNV = 5276,
BuiltInClipDistancePerViewNV = 5277,
BuiltInCullDistancePerViewNV = 5278,
BuiltInLayerPerViewNV = 5279,
BuiltInMeshViewCountNV = 5280,
BuiltInMeshViewIndicesNV = 5281,
BuiltInBaryCoordNV = 5286,
BuiltInBaryCoordNoPerspNV = 5287,
BuiltInFragSizeEXT = 5292,
BuiltInFragmentSizeNV = 5292,
BuiltInFragInvocationCountEXT = 5293,
BuiltInInvocationsPerPixelNV = 5293,
BuiltInLaunchIdNV = 5319,
BuiltInLaunchSizeNV = 5320,
BuiltInWorldRayOriginNV = 5321,
BuiltInWorldRayDirectionNV = 5322,
BuiltInObjectRayOriginNV = 5323,
BuiltInObjectRayDirectionNV = 5324,
BuiltInRayTminNV = 5325,
BuiltInRayTmaxNV = 5326,
BuiltInInstanceCustomIndexNV = 5327,
BuiltInObjectToWorldNV = 5330,
BuiltInWorldToObjectNV = 5331,
BuiltInHitTNV = 5332,
BuiltInHitKindNV = 5333,
BuiltInIncomingRayFlagsNV = 5351,
BuiltInMax = 0x7fffffff,
};
@ -527,6 +602,9 @@ enum MemorySemanticsShift {
MemorySemanticsCrossWorkgroupMemoryShift = 9,
MemorySemanticsAtomicCounterMemoryShift = 10,
MemorySemanticsImageMemoryShift = 11,
MemorySemanticsOutputMemoryKHRShift = 12,
MemorySemanticsMakeAvailableKHRShift = 13,
MemorySemanticsMakeVisibleKHRShift = 14,
MemorySemanticsMax = 0x7fffffff,
};
@ -542,12 +620,18 @@ enum MemorySemanticsMask {
MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200,
MemorySemanticsAtomicCounterMemoryMask = 0x00000400,
MemorySemanticsImageMemoryMask = 0x00000800,
MemorySemanticsOutputMemoryKHRMask = 0x00001000,
MemorySemanticsMakeAvailableKHRMask = 0x00002000,
MemorySemanticsMakeVisibleKHRMask = 0x00004000,
};
enum MemoryAccessShift {
MemoryAccessVolatileShift = 0,
MemoryAccessAlignedShift = 1,
MemoryAccessNontemporalShift = 2,
MemoryAccessMakePointerAvailableKHRShift = 3,
MemoryAccessMakePointerVisibleKHRShift = 4,
MemoryAccessNonPrivatePointerKHRShift = 5,
MemoryAccessMax = 0x7fffffff,
};
@ -556,6 +640,9 @@ enum MemoryAccessMask {
MemoryAccessVolatileMask = 0x00000001,
MemoryAccessAlignedMask = 0x00000002,
MemoryAccessNontemporalMask = 0x00000004,
MemoryAccessMakePointerAvailableKHRMask = 0x00000008,
MemoryAccessMakePointerVisibleKHRMask = 0x00000010,
MemoryAccessNonPrivatePointerKHRMask = 0x00000020,
};
enum Scope {
@ -564,6 +651,7 @@ enum Scope {
ScopeWorkgroup = 2,
ScopeSubgroup = 3,
ScopeInvocation = 4,
ScopeQueueFamilyKHR = 5,
ScopeMax = 0x7fffffff,
};
@ -572,6 +660,9 @@ enum GroupOperation {
GroupOperationInclusiveScan = 1,
GroupOperationExclusiveScan = 2,
GroupOperationClusteredReduce = 3,
GroupOperationPartitionedReduceNV = 6,
GroupOperationPartitionedInclusiveScanNV = 7,
GroupOperationPartitionedExclusiveScanNV = 8,
GroupOperationMax = 0x7fffffff,
};
@ -675,6 +766,14 @@ enum Capability {
CapabilityVariablePointers = 4442,
CapabilityAtomicStorageOps = 4445,
CapabilitySampleMaskPostDepthCoverage = 4447,
CapabilityStorageBuffer8BitAccess = 4448,
CapabilityUniformAndStorageBuffer8BitAccess = 4449,
CapabilityStoragePushConstant8 = 4450,
CapabilityDenormPreserve = 4464,
CapabilityDenormFlushToZero = 4465,
CapabilitySignedZeroInfNanPreserve = 4466,
CapabilityRoundingModeRTE = 4467,
CapabilityRoundingModeRTZ = 4468,
CapabilityFloat16ImageAMD = 5008,
CapabilityImageGatherBiasLodAMD = 5009,
CapabilityFragmentMaskAMD = 5010,
@ -688,9 +787,34 @@ enum Capability {
CapabilityShaderStereoViewNV = 5259,
CapabilityPerViewAttributesNV = 5260,
CapabilityFragmentFullyCoveredEXT = 5265,
CapabilityMeshShadingNV = 5266,
CapabilityImageFootprintNV = 5282,
CapabilityFragmentBarycentricNV = 5284,
CapabilityComputeDerivativeGroupQuadsNV = 5288,
CapabilityFragmentDensityEXT = 5291,
CapabilityShadingRateNV = 5291,
CapabilityGroupNonUniformPartitionedNV = 5297,
CapabilityShaderNonUniformEXT = 5301,
CapabilityRuntimeDescriptorArrayEXT = 5302,
CapabilityInputAttachmentArrayDynamicIndexingEXT = 5303,
CapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304,
CapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305,
CapabilityUniformBufferArrayNonUniformIndexingEXT = 5306,
CapabilitySampledImageArrayNonUniformIndexingEXT = 5307,
CapabilityStorageBufferArrayNonUniformIndexingEXT = 5308,
CapabilityStorageImageArrayNonUniformIndexingEXT = 5309,
CapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310,
CapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311,
CapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312,
CapabilityRayTracingNV = 5340,
CapabilityVulkanMemoryModelKHR = 5345,
CapabilityVulkanMemoryModelDeviceScopeKHR = 5346,
CapabilityPhysicalStorageBufferAddressesEXT = 5347,
CapabilityComputeDerivativeGroupLinearNV = 5350,
CapabilitySubgroupShuffleINTEL = 5568,
CapabilitySubgroupBufferBlockIOINTEL = 5569,
CapabilitySubgroupImageBlockIOINTEL = 5570,
CapabilitySubgroupImageMediaBlockIOINTEL = 5579,
CapabilityMax = 0x7fffffff,
};
@ -1051,6 +1175,15 @@ enum Op {
OpGroupSMaxNonUniformAMD = 5007,
OpFragmentMaskFetchAMD = 5011,
OpFragmentFetchAMD = 5012,
OpImageSampleFootprintNV = 5283,
OpGroupNonUniformPartitionNV = 5296,
OpWritePackedPrimitiveIndices4x8NV = 5299,
OpReportIntersectionNV = 5334,
OpIgnoreIntersectionNV = 5335,
OpTerminateRayNV = 5336,
OpTraceNV = 5337,
OpTypeAccelerationStructureNV = 5341,
OpExecuteCallableNV = 5344,
OpSubgroupShuffleINTEL = 5571,
OpSubgroupShuffleDownINTEL = 5572,
OpSubgroupShuffleUpINTEL = 5573,
@ -1059,6 +1192,8 @@ enum Op {
OpSubgroupBlockWriteINTEL = 5576,
OpSubgroupImageBlockReadINTEL = 5577,
OpSubgroupImageBlockWriteINTEL = 5578,
OpSubgroupImageMediaBlockReadINTEL = 5580,
OpSubgroupImageMediaBlockWriteINTEL = 5581,
OpDecorateStringGOOGLE = 5632,
OpMemberDecorateStringGOOGLE = 5633,
OpMax = 0x7fffffff,

View File

@ -59,7 +59,7 @@ report_and_abort(const std::string &msg)
class CompilerError : public std::runtime_error
{
public:
CompilerError(const std::string &str)
explicit CompilerError(const std::string &str)
: std::runtime_error(str)
{
}
@ -359,7 +359,8 @@ struct SPIRUndef : IVariant
{
type = TypeUndef
};
SPIRUndef(uint32_t basetype_)
explicit SPIRUndef(uint32_t basetype_)
: basetype(basetype_)
{
}
@ -511,7 +512,7 @@ struct SPIRExtension : IVariant
SPV_AMD_gcn_shader
};
SPIRExtension(Extension ext_)
explicit SPIRExtension(Extension ext_)
: ext(ext_)
{
}
@ -546,7 +547,7 @@ struct SPIREntryPoint
} workgroup_size;
uint32_t invocations = 0;
uint32_t output_vertices = 0;
spv::ExecutionModel model;
spv::ExecutionModel model = spv::ExecutionModelMax;
};
struct SPIRExpression : IVariant
@ -606,7 +607,7 @@ struct SPIRFunctionPrototype : IVariant
type = TypeFunctionPrototype
};
SPIRFunctionPrototype(uint32_t return_type_)
explicit SPIRFunctionPrototype(uint32_t return_type_)
: return_type(return_type_)
{
}
@ -857,7 +858,7 @@ struct SPIRAccessChain : IVariant
int32_t static_index_)
: basetype(basetype_)
, storage(storage_)
, base(base_)
, base(std::move(base_))
, dynamic_index(std::move(dynamic_index_))
, static_index(static_index_)
{
@ -1215,7 +1216,7 @@ struct SPIRConstant : IVariant
}
}
uint32_t constant_type;
uint32_t constant_type = 0;
ConstantMatrix m;
// If this constant is a specialization constant (i.e. created with OpSpecConstant*).

View File

@ -931,50 +931,6 @@ void Compiler::parse_fixup()
fixup_type_alias();
}
void Compiler::flatten_interface_block(uint32_t id)
{
auto &var = get<SPIRVariable>(id);
auto &type = get<SPIRType>(var.basetype);
auto &flags = ir.meta[type.self].decoration.decoration_flags;
if (!type.array.empty())
SPIRV_CROSS_THROW("Type is array of UBOs.");
if (type.basetype != SPIRType::Struct)
SPIRV_CROSS_THROW("Type is not a struct.");
if (!flags.get(DecorationBlock))
SPIRV_CROSS_THROW("Type is not a block.");
if (type.member_types.empty())
SPIRV_CROSS_THROW("Member list of struct is empty.");
uint32_t t = type.member_types[0];
for (auto &m : type.member_types)
if (t != m)
SPIRV_CROSS_THROW("Types in block differ.");
auto &mtype = get<SPIRType>(t);
if (!mtype.array.empty())
SPIRV_CROSS_THROW("Member type cannot be arrays.");
if (mtype.basetype == SPIRType::Struct)
SPIRV_CROSS_THROW("Member type cannot be struct.");
// Inherit variable name from interface block name.
ir.meta[var.self].decoration.alias = ir.meta[type.self].decoration.alias;
auto storage = var.storage;
if (storage == StorageClassUniform)
storage = StorageClassUniformConstant;
// Change type definition in-place into an array instead.
// Access chains will still work as-is.
uint32_t array_size = uint32_t(type.member_types.size());
type = mtype;
type.array.push_back(array_size);
type.pointer = true;
type.storage = storage;
type.parent_type = t;
var.storage = storage;
}
void Compiler::update_name_cache(unordered_set<string> &cache_primary, const unordered_set<string> &cache_secondary,
string &name)
{
@ -1155,11 +1111,6 @@ uint32_t Compiler::get_member_decoration(uint32_t id, uint32_t index, Decoration
return ir.get_member_decoration(id, index, decoration);
}
uint64_t Compiler::get_member_decoration_mask(uint32_t id, uint32_t index) const
{
return get_member_decoration_bitset(id, index).get_lower();
}
const Bitset &Compiler::get_member_decoration_bitset(uint32_t id, uint32_t index) const
{
return ir.get_member_decoration_bitset(id, index);
@ -1412,11 +1363,6 @@ const std::string Compiler::get_block_fallback_name(uint32_t id) const
return get_name(id);
}
uint64_t Compiler::get_decoration_mask(uint32_t id) const
{
return get_decoration_bitset(id).get_lower();
}
const Bitset &Compiler::get_decoration_bitset(uint32_t id) const
{
return ir.get_decoration_bitset(id);
@ -1474,12 +1420,29 @@ bool Compiler::block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method
// which the code backend can use to create cleaner code.
// for(;;) { if (cond) { some_body; } else { break; } }
// is the pattern we're looking for.
bool ret = block.terminator == SPIRBlock::Select && block.merge == SPIRBlock::MergeLoop &&
block.true_block != block.merge_block && block.true_block != block.self &&
block.false_block == block.merge_block;
const auto *false_block = maybe_get<SPIRBlock>(block.false_block);
const auto *true_block = maybe_get<SPIRBlock>(block.true_block);
const auto *merge_block = maybe_get<SPIRBlock>(block.merge_block);
if (ret && method == SPIRBlock::MergeToSelectContinueForLoop)
bool false_block_is_merge = block.false_block == block.merge_block ||
(false_block && merge_block && execution_is_noop(*false_block, *merge_block));
bool true_block_is_merge = block.true_block == block.merge_block ||
(true_block && merge_block && execution_is_noop(*true_block, *merge_block));
bool positive_candidate =
block.true_block != block.merge_block && block.true_block != block.self && false_block_is_merge;
bool negative_candidate =
block.false_block != block.merge_block && block.false_block != block.self && true_block_is_merge;
bool ret = block.terminator == SPIRBlock::Select && block.merge == SPIRBlock::MergeLoop &&
(positive_candidate || negative_candidate);
if (ret && positive_candidate && method == SPIRBlock::MergeToSelectContinueForLoop)
ret = block.true_block == block.continue_block;
else if (ret && negative_candidate && method == SPIRBlock::MergeToSelectContinueForLoop)
ret = block.false_block == block.continue_block;
// If we have OpPhi which depends on branches which came from our own block,
// we need to flush phi variables in else block instead of a trivial break,
@ -1508,9 +1471,25 @@ bool Compiler::block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method
return false;
auto &child = get<SPIRBlock>(block.next_block);
const auto *false_block = maybe_get<SPIRBlock>(child.false_block);
const auto *true_block = maybe_get<SPIRBlock>(child.true_block);
const auto *merge_block = maybe_get<SPIRBlock>(block.merge_block);
bool false_block_is_merge = child.false_block == block.merge_block ||
(false_block && merge_block && execution_is_noop(*false_block, *merge_block));
bool true_block_is_merge = child.true_block == block.merge_block ||
(true_block && merge_block && execution_is_noop(*true_block, *merge_block));
bool positive_candidate =
child.true_block != block.merge_block && child.true_block != block.self && false_block_is_merge;
bool negative_candidate =
child.false_block != block.merge_block && child.false_block != block.self && true_block_is_merge;
ret = child.terminator == SPIRBlock::Select && child.merge == SPIRBlock::MergeNone &&
child.false_block == block.merge_block && child.true_block != block.merge_block &&
child.true_block != block.self;
(positive_candidate || negative_candidate);
// If we have OpPhi which depends on branches which came from our own block,
// we need to flush phi variables in else block instead of a trivial break,
@ -1628,8 +1607,20 @@ SPIRBlock::ContinueBlockType Compiler::continue_block_type(const SPIRBlock &bloc
return SPIRBlock::ForLoop;
else
{
const auto *false_block = maybe_get<SPIRBlock>(block.false_block);
const auto *true_block = maybe_get<SPIRBlock>(block.true_block);
const auto *merge_block = maybe_get<SPIRBlock>(dominator.merge_block);
bool positive_do_while = block.true_block == dominator.self &&
(block.false_block == dominator.merge_block ||
(false_block && merge_block && execution_is_noop(*false_block, *merge_block)));
bool negative_do_while = block.false_block == dominator.self &&
(block.true_block == dominator.merge_block ||
(true_block && merge_block && execution_is_noop(*true_block, *merge_block)));
if (block.merge == SPIRBlock::MergeNone && block.terminator == SPIRBlock::Select &&
block.true_block == dominator.self && block.false_block == dominator.merge_block)
(positive_do_while || negative_do_while))
{
return SPIRBlock::DoWhileLoop;
}
@ -1904,11 +1895,6 @@ bool Compiler::types_are_logically_equivalent(const SPIRType &a, const SPIRType
return true;
}
uint64_t Compiler::get_execution_mode_mask() const
{
return get_entry_point().flags.get_lower();
}
const Bitset &Compiler::get_execution_mode_bitset() const
{
return get_entry_point().flags;
@ -2094,14 +2080,6 @@ void Compiler::inherit_expression_dependencies(uint32_t dst, uint32_t source_exp
e_deps.erase(unique(begin(e_deps), end(e_deps)), end(e_deps));
}
vector<string> Compiler::get_entry_points() const
{
vector<string> entries;
for (auto &entry : ir.entry_points)
entries.push_back(entry.second.orig_name);
return entries;
}
vector<EntryPoint> Compiler::get_entry_points_and_stages() const
{
vector<EntryPoint> entries;
@ -2110,13 +2088,6 @@ vector<EntryPoint> Compiler::get_entry_points_and_stages() const
return entries;
}
void Compiler::rename_entry_point(const std::string &old_name, const std::string &new_name)
{
auto &entry = get_first_entry_point(old_name);
entry.orig_name = new_name;
entry.name = new_name;
}
void Compiler::rename_entry_point(const std::string &old_name, const std::string &new_name, spv::ExecutionModel model)
{
auto &entry = get_entry_point(old_name, model);
@ -2124,28 +2095,12 @@ void Compiler::rename_entry_point(const std::string &old_name, const std::string
entry.name = new_name;
}
void Compiler::set_entry_point(const std::string &name)
{
auto &entry = get_first_entry_point(name);
ir.default_entry_point = entry.self;
}
void Compiler::set_entry_point(const std::string &name, spv::ExecutionModel model)
{
auto &entry = get_entry_point(name, model);
ir.default_entry_point = entry.self;
}
SPIREntryPoint &Compiler::get_entry_point(const std::string &name)
{
return get_first_entry_point(name);
}
const SPIREntryPoint &Compiler::get_entry_point(const std::string &name) const
{
return get_first_entry_point(name);
}
SPIREntryPoint &Compiler::get_first_entry_point(const std::string &name)
{
auto itr = find_if(
@ -2196,11 +2151,6 @@ const SPIREntryPoint &Compiler::get_entry_point(const std::string &name, Executi
return itr->second;
}
const string &Compiler::get_cleansed_entry_point_name(const std::string &name) const
{
return get_first_entry_point(name).name;
}
const string &Compiler::get_cleansed_entry_point_name(const std::string &name, ExecutionModel model) const
{
return get_entry_point(name, model).name;

View File

@ -160,8 +160,6 @@ public:
// Gets a bitmask for the decorations which are applied to ID.
// I.e. (1ull << spv::DecorationFoo) | (1ull << spv::DecorationBar)
SPIRV_CROSS_DEPRECATED("Please use get_decoration_bitset instead.")
uint64_t get_decoration_mask(uint32_t id) const;
const Bitset &get_decoration_bitset(uint32_t id) const;
// Returns whether the decoration has been applied to the ID.
@ -215,8 +213,6 @@ public:
void set_member_qualified_name(uint32_t type_id, uint32_t index, const std::string &name);
// Gets the decoration mask for a member of a struct, similar to get_decoration_mask.
SPIRV_CROSS_DEPRECATED("Please use get_member_decoration_bitset instead.")
uint64_t get_member_decoration_mask(uint32_t id, uint32_t index) const;
const Bitset &get_member_decoration_bitset(uint32_t id, uint32_t index) const;
// Returns whether the decoration has been applied to a member of a struct.
@ -260,9 +256,6 @@ public:
// Returns the effective size of a buffer block struct member.
virtual size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const;
// Legacy GLSL compatibility method. Deprecated in favor of CompilerGLSL::flatten_buffer_block
SPIRV_CROSS_DEPRECATED("Please use flatten_buffer_block instead.") void flatten_interface_block(uint32_t id);
// Returns a set of all global variables which are statically accessed
// by the control flow graph from the current entry point.
// Only variables which change the interface for a shader are returned, that is,
@ -303,24 +296,6 @@ public:
// Entry points should be set right after the constructor completes as some reflection functions traverse the graph from the entry point.
// Resource reflection also depends on the entry point.
// By default, the current entry point is set to the first OpEntryPoint which appears in the SPIR-V module.
SPIRV_CROSS_DEPRECATED("Please use get_entry_points_and_stages instead.")
std::vector<std::string> get_entry_points() const;
SPIRV_CROSS_DEPRECATED("Please use set_entry_point(const std::string &, spv::ExecutionModel) instead.")
void set_entry_point(const std::string &name);
// Renames an entry point from old_name to new_name.
// If old_name is currently selected as the current entry point, it will continue to be the current entry point,
// albeit with a new name.
// get_entry_points() is essentially invalidated at this point.
SPIRV_CROSS_DEPRECATED(
"Please use rename_entry_point(const std::string&, const std::string&, spv::ExecutionModel) instead.")
void rename_entry_point(const std::string &old_name, const std::string &new_name);
// Returns the internal data structure for entry points to allow poking around.
SPIRV_CROSS_DEPRECATED("Please use get_entry_point(const std::string &, spv::ExecutionModel instead.")
const SPIREntryPoint &get_entry_point(const std::string &name) const;
SPIRV_CROSS_DEPRECATED("Please use get_entry_point(const std::string &, spv::ExecutionModel instead.")
SPIREntryPoint &get_entry_point(const std::string &name);
// Some shader languages restrict the names that can be given to entry points, and the
// corresponding backend will automatically rename an entry point name, during the call
@ -330,15 +305,17 @@ public:
// the name, as updated by the backend during the call to compile(). If the name is not
// illegal, and has not been renamed, or if this function is called before compile(),
// this function will simply return the same name.
SPIRV_CROSS_DEPRECATED(
"Please use get_cleansed_entry_point_name(const std::string &, spv::ExecutionModel) instead.")
const std::string &get_cleansed_entry_point_name(const std::string &name) const;
// New variants of entry point query and reflection.
// Names for entry points in the SPIR-V module may alias if they belong to different execution models.
// To disambiguate, we must pass along with the entry point names the execution model.
std::vector<EntryPoint> get_entry_points_and_stages() const;
void set_entry_point(const std::string &entry, spv::ExecutionModel execution_model);
// Renames an entry point from old_name to new_name.
// If old_name is currently selected as the current entry point, it will continue to be the current entry point,
// albeit with a new name.
// get_entry_points() is essentially invalidated at this point.
void rename_entry_point(const std::string &old_name, const std::string &new_name,
spv::ExecutionModel execution_model);
const SPIREntryPoint &get_entry_point(const std::string &name, spv::ExecutionModel execution_model) const;
@ -347,8 +324,6 @@ public:
spv::ExecutionModel execution_model) const;
// Query and modify OpExecutionMode.
SPIRV_CROSS_DEPRECATED("Please use get_execution_mode_bitset instead.")
uint64_t get_execution_mode_mask() const;
const Bitset &get_execution_mode_bitset() const;
void unset_execution_mode(spv::ExecutionMode mode);

1657
3rdparty/spirv-cross/spirv_cross_c.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

697
3rdparty/spirv-cross/spirv_cross_c.h vendored Normal file
View File

@ -0,0 +1,697 @@
/*
* Copyright 2019 Hans-Kristian Arntzen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SPIRV_CROSS_C_API_H
#define SPIRV_CROSS_C_API_H
#include <stddef.h>
#include "spirv.h"
/*
* C89-compatible wrapper for SPIRV-Cross' API.
* Documentation here is sparse unless the behavior does not map 1:1 with C++ API.
* It is recommended to look at the canonical C++ API for more detailed information.
*/
#ifdef __cplusplus
extern "C" {
#endif
/* Bumped if ABI or API breaks backwards compatibility. */
#define SPVC_C_API_VERSION_MAJOR 0
/* Bumped if APIs or enumerations are added in a backwards compatible way. */
#define SPVC_C_API_VERSION_MINOR 1
/* Bumped if internal implementation details change. */
#define SPVC_C_API_VERSION_PATCH 0
#if !defined(SPVC_PUBLIC_API)
#if defined(SPVC_EXPORT_SYMBOLS)
/* Exports symbols. Standard C calling convention is used. */
#if defined(__GNUC__)
#define SPVC_PUBLIC_API __attribute__((visibility("default")))
#elif defined(_MSC_VER)
#define SPVC_PUBLIC_API __declspec(dllexport)
#else
#define SPVC_PUBLIC_API
#endif
#else
#define SPVC_PUBLIC_API
#endif
#endif
/*
* Gets the SPVC_C_API_VERSION_* used to build this library.
* Can be used to check for ABI mismatch if so-versioning did not catch it.
*/
SPVC_PUBLIC_API void spvc_get_version(unsigned *major, unsigned *minor, unsigned *patch);
/* These types are opaque to the user. */
typedef struct spvc_context_s *spvc_context;
typedef struct spvc_parsed_ir_s *spvc_parsed_ir;
typedef struct spvc_compiler_s *spvc_compiler;
typedef struct spvc_compiler_options_s *spvc_compiler_options;
typedef struct spvc_resources_s *spvc_resources;
struct spvc_type_s;
typedef const struct spvc_type_s *spvc_type;
typedef struct spvc_constant_s *spvc_constant;
struct spvc_set_s;
typedef const struct spvc_set_s *spvc_set;
/*
* Shallow typedefs. All SPIR-V IDs are plain 32-bit numbers, but this helps communicate which data is used.
* Maps to a SPIRType.
*/
typedef SpvId spvc_type_id;
/* Maps to a SPIRVariable. */
typedef SpvId spvc_variable_id;
/* Maps to a SPIRConstant. */
typedef SpvId spvc_constant_id;
/* See C++ API. */
typedef struct spvc_reflected_resource
{
spvc_variable_id id;
spvc_type_id base_type_id;
spvc_type_id type_id;
const char *name;
} spvc_reflected_resource;
/* See C++ API. */
typedef struct spvc_entry_point
{
SpvExecutionModel execution_model;
const char *name;
} spvc_entry_point;
/* See C++ API. */
typedef struct spvc_combined_image_sampler
{
spvc_variable_id combined_id;
spvc_variable_id image_id;
spvc_variable_id sampler_id;
} spvc_combined_image_sampler;
/* See C++ API. */
typedef struct spvc_specialization_constant
{
spvc_constant_id id;
unsigned constant_id;
} spvc_specialization_constant;
/* See C++ API. */
typedef struct spvc_hlsl_root_constants
{
unsigned start;
unsigned end;
unsigned binding;
unsigned space;
} spvc_hlsl_root_constants;
/* See C++ API. */
typedef struct spvc_hlsl_vertex_attribute_remap
{
unsigned location;
const char *semantic;
} spvc_hlsl_vertex_attribute_remap;
/*
* Be compatible with non-C99 compilers, which do not have stdbool.
* Only recent MSVC compilers supports this for example, and ideally SPIRV-Cross should be linkable
* from a wide range of compilers in its C wrapper.
*/
typedef unsigned char spvc_bool;
#define SPVC_TRUE ((spvc_bool)1)
#define SPVC_FALSE ((spvc_bool)0)
typedef enum spvc_result
{
/* Success. */
SPVC_SUCCESS = 0,
/* The SPIR-V is invalid. Should have been caught by validation ideally. */
SPVC_ERROR_INVALID_SPIRV = -1,
/* The SPIR-V might be valid or invalid, but SPIRV-Cross currently cannot correctly translate this to your target language. */
SPVC_ERROR_UNSUPPORTED_SPIRV = -2,
/* If for some reason we hit this, new or malloc failed. */
SPVC_ERROR_OUT_OF_MEMORY = -3,
/* Invalid API argument. */
SPVC_ERROR_INVALID_ARGUMENT = -4,
SPVC_ERROR_INT_MAX = 0x7fffffff
} spvc_result;
typedef enum spvc_capture_mode
{
/* The Parsed IR payload will be copied, and the handle can be reused to create other compiler instances. */
SPVC_CAPTURE_MODE_COPY = 0,
/*
* The payload will now be owned by the compiler.
* parsed_ir should now be considered a dead blob and must not be used further.
* This is optimal for performance and should be the go-to option.
*/
SPVC_CAPTURE_MODE_TAKE_OWNERSHIP = 1,
SPVC_CAPTURE_MODE_INT_MAX = 0x7fffffff
} spvc_capture_mode;
typedef enum spvc_backend
{
/* This backend can only perform reflection, no compiler options are supported. Maps to spirv_cross::Compiler. */
SPVC_BACKEND_NONE = 0,
SPVC_BACKEND_GLSL = 1, /* spirv_cross::CompilerGLSL */
SPVC_BACKEND_HLSL = 2, /* CompilerHLSL */
SPVC_BACKEND_MSL = 3, /* CompilerMSL */
SPVC_BACKEND_CPP = 4, /* CompilerCPP */
SPVC_BACKEND_JSON = 5, /* CompilerReflection w/ JSON backend */
SPVC_BACKEND_INT_MAX = 0x7fffffff
} spvc_backend;
/* Maps to C++ API. */
typedef enum spvc_resource_type
{
SPVC_RESOURCE_TYPE_UNKNOWN = 0,
SPVC_RESOURCE_TYPE_UNIFORM_BUFFER = 1,
SPVC_RESOURCE_TYPE_STORAGE_BUFFER = 2,
SPVC_RESOURCE_TYPE_STAGE_INPUT = 3,
SPVC_RESOURCE_TYPE_STAGE_OUTPUT = 4,
SPVC_RESOURCE_TYPE_SUBPASS_INPUT = 5,
SPVC_RESOURCE_TYPE_STORAGE_IMAGE = 6,
SPVC_RESOURCE_TYPE_SAMPLED_IMAGE = 7,
SPVC_RESOURCE_TYPE_ATOMIC_COUNTER = 8,
SPVC_RESOURCE_TYPE_PUSH_CONSTANT = 9,
SPVC_RESOURCE_TYPE_SEPARATE_IMAGE = 10,
SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS = 11,
SPVC_RESOURCE_TYPE_INT_MAX = 0x7fffffff
} spvc_resource_type;
/* Maps to spirv_cross::SPIRType::BaseType. */
typedef enum spvc_basetype
{
SPVC_BASETYPE_UNKNOWN = 0,
SPVC_BASETYPE_VOID = 1,
SPVC_BASETYPE_BOOLEAN = 2,
SPVC_BASETYPE_INT8 = 3,
SPVC_BASETYPE_UINT8 = 4,
SPVC_BASETYPE_INT16 = 5,
SPVC_BASETYPE_UINT16 = 6,
SPVC_BASETYPE_INT32 = 7,
SPVC_BASETYPE_UINT32 = 8,
SPVC_BASETYPE_INT64 = 9,
SPVC_BASETYPE_UINT64 = 10,
SPVC_BASETYPE_ATOMIC_COUNTER = 11,
SPVC_BASETYPE_FP16 = 12,
SPVC_BASETYPE_FP32 = 13,
SPVC_BASETYPE_FP64 = 14,
SPVC_BASETYPE_STRUCT = 15,
SPVC_BASETYPE_IMAGE = 16,
SPVC_BASETYPE_SAMPLED_IMAGE = 17,
SPVC_BASETYPE_SAMPLER = 18,
SPVC_BASETYPE_INT_MAX = 0x7fffffff
} spvc_basetype;
#define SPVC_COMPILER_OPTION_COMMON_BIT 0x1000000
#define SPVC_COMPILER_OPTION_GLSL_BIT 0x2000000
#define SPVC_COMPILER_OPTION_HLSL_BIT 0x4000000
#define SPVC_COMPILER_OPTION_MSL_BIT 0x8000000
#define SPVC_COMPILER_OPTION_LANG_BITS 0x0f000000
#define SPVC_COMPILER_OPTION_ENUM_BITS 0xffffff
#define SPVC_MAKE_MSL_VERSION(major, minor, patch) ((major) * 10000 + (minor) * 100 + (patch))
/* Maps to C++ API. */
typedef enum spvc_msl_platform
{
SPVC_MSL_PLATFORM_IOS = 0,
SPVC_MSL_PLATFORM_MACOS = 1,
SPVC_MSL_PLATFORM_MAX_INT = 0x7fffffff
} spvc_msl_platform;
/* Maps to C++ API. */
typedef enum spvc_msl_vertex_format
{
SPVC_MSL_VERTEX_FORMAT_OTHER = 0,
SPVC_MSL_VERTEX_FORMAT_UINT8 = 1,
SPVC_MSL_VERTEX_FORMAT_UINT16 = 2
} spvc_msl_vertex_format;
/* Maps to C++ API. */
typedef struct spvc_msl_vertex_attribute
{
unsigned location;
unsigned msl_buffer;
unsigned msl_offset;
unsigned msl_stride;
spvc_bool per_instance;
spvc_msl_vertex_format format;
SpvBuiltIn builtin;
} spvc_msl_vertex_attribute;
/*
* Initializes the vertex attribute struct.
*/
SPVC_PUBLIC_API void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr);
/* Maps to C++ API. */
typedef struct spvc_msl_resource_binding
{
SpvExecutionModel stage;
unsigned desc_set;
unsigned binding;
unsigned msl_buffer;
unsigned msl_texture;
unsigned msl_sampler;
} spvc_msl_resource_binding;
/*
* Initializes the resource binding struct.
* The defaults are non-zero.
*/
SPVC_PUBLIC_API void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding);
#define SPVC_MSL_PUSH_CONSTANT_DESC_SET (~(0u))
#define SPVC_MSL_PUSH_CONSTANT_BINDING (0)
#define SPVC_MSL_AUX_BUFFER_STRUCT_VERSION 1
/* Runtime check for incompatibility. */
SPVC_PUBLIC_API unsigned spvc_msl_get_aux_buffer_struct_version(void);
/* Maps to C++ API. */
typedef enum spvc_msl_sampler_coord
{
SPVC_MSL_SAMPLER_COORD_NORMALIZED = 0,
SPVC_MSL_SAMPLER_COORD_PIXEL = 1,
SPVC_MSL_SAMPLER_INT_MAX = 0x7fffffff
} spvc_msl_sampler_coord;
/* Maps to C++ API. */
typedef enum spvc_msl_sampler_filter
{
SPVC_MSL_SAMPLER_FILTER_NEAREST = 0,
SPVC_MSL_SAMPLER_FILTER_LINEAR = 1,
SPVC_MSL_SAMPLER_FILTER_INT_MAX = 0x7fffffff
} spvc_msl_sampler_filter;
/* Maps to C++ API. */
typedef enum spvc_msl_sampler_mip_filter
{
SPVC_MSL_SAMPLER_MIP_FILTER_NONE = 0,
SPVC_MSL_SAMPLER_MIP_FILTER_NEAREST = 1,
SPVC_MSL_SAMPLER_MIP_FILTER_LINEAR = 2,
SPVC_MSL_SAMPLER_MIP_FILTER_INT_MAX = 0x7fffffff
} spvc_msl_sampler_mip_filter;
/* Maps to C++ API. */
typedef enum spvc_msl_sampler_address
{
SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO = 0,
SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE = 1,
SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER = 2,
SPVC_MSL_SAMPLER_ADDRESS_REPEAT = 3,
SPVC_MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT = 4,
SPVC_MSL_SAMPLER_ADDRESS_INT_MAX = 0x7fffffff
} spvc_msl_sampler_address;
/* Maps to C++ API. */
typedef enum spvc_msl_sampler_compare_func
{
SPVC_MSL_SAMPLER_COMPARE_FUNC_NEVER = 0,
SPVC_MSL_SAMPLER_COMPARE_FUNC_LESS = 1,
SPVC_MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL = 2,
SPVC_MSL_SAMPLER_COMPARE_FUNC_GREATER = 3,
SPVC_MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL = 4,
SPVC_MSL_SAMPLER_COMPARE_FUNC_EQUAL = 5,
SPVC_MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL = 6,
SPVC_MSL_SAMPLER_COMPARE_FUNC_ALWAYS = 7,
SPVC_MSL_SAMPLER_COMPARE_FUNC_INT_MAX = 0x7fffffff
} spvc_msl_sampler_compare_func;
/* Maps to C++ API. */
typedef enum spvc_msl_sampler_border_color
{
SPVC_MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK = 0,
SPVC_MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK = 1,
SPVC_MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE = 2,
SPVC_MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff
} spvc_msl_sampler_border_color;
/* Maps to C++ API. */
typedef struct spvc_msl_constexpr_sampler
{
spvc_msl_sampler_coord coord;
spvc_msl_sampler_filter min_filter;
spvc_msl_sampler_filter mag_filter;
spvc_msl_sampler_mip_filter mip_filter;
spvc_msl_sampler_address s_address;
spvc_msl_sampler_address t_address;
spvc_msl_sampler_address r_address;
spvc_msl_sampler_compare_func compare_func;
spvc_msl_sampler_border_color border_color;
float lod_clamp_min;
float lod_clamp_max;
int max_anisotropy;
spvc_bool compare_enable;
spvc_bool lod_clamp_enable;
spvc_bool anisotropy_enable;
} spvc_msl_constexpr_sampler;
/*
* Initializes the constexpr sampler struct.
* The defaults are non-zero.
*/
SPVC_PUBLIC_API void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler);
/* Maps to the various spirv_cross::Compiler*::Option structures. See C++ API for defaults and details. */
typedef enum spvc_compiler_option
{
SPVC_COMPILER_OPTION_UNKNOWN = 0,
SPVC_COMPILER_OPTION_FORCE_TEMPORARY = 1 | SPVC_COMPILER_OPTION_COMMON_BIT,
SPVC_COMPILER_OPTION_FLATTEN_MULTIDIMENSIONAL_ARRAYS = 2 | SPVC_COMPILER_OPTION_COMMON_BIT,
SPVC_COMPILER_OPTION_FIXUP_DEPTH_CONVENTION = 3 | SPVC_COMPILER_OPTION_COMMON_BIT,
SPVC_COMPILER_OPTION_FLIP_VERTEX_Y = 4 | SPVC_COMPILER_OPTION_COMMON_BIT,
SPVC_COMPILER_OPTION_GLSL_SUPPORT_NONZERO_BASE_INSTANCE = 5 | SPVC_COMPILER_OPTION_GLSL_BIT,
SPVC_COMPILER_OPTION_GLSL_SEPARATE_SHADER_OBJECTS = 6 | SPVC_COMPILER_OPTION_GLSL_BIT,
SPVC_COMPILER_OPTION_GLSL_ENABLE_420PACK_EXTENSION = 7 | SPVC_COMPILER_OPTION_GLSL_BIT,
SPVC_COMPILER_OPTION_GLSL_VERSION = 8 | SPVC_COMPILER_OPTION_GLSL_BIT,
SPVC_COMPILER_OPTION_GLSL_ES = 9 | SPVC_COMPILER_OPTION_GLSL_BIT,
SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS = 10 | SPVC_COMPILER_OPTION_GLSL_BIT,
SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_FLOAT_PRECISION_HIGHP = 11 | SPVC_COMPILER_OPTION_GLSL_BIT,
SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_INT_PRECISION_HIGHP = 12 | SPVC_COMPILER_OPTION_GLSL_BIT,
SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL = 13 | SPVC_COMPILER_OPTION_HLSL_BIT,
SPVC_COMPILER_OPTION_HLSL_POINT_SIZE_COMPAT = 14 | SPVC_COMPILER_OPTION_HLSL_BIT,
SPVC_COMPILER_OPTION_HLSL_POINT_COORD_COMPAT = 15 | SPVC_COMPILER_OPTION_HLSL_BIT,
SPVC_COMPILER_OPTION_HLSL_SUPPORT_NONZERO_BASE_VERTEX_BASE_INSTANCE = 16 | SPVC_COMPILER_OPTION_HLSL_BIT,
SPVC_COMPILER_OPTION_MSL_VERSION = 17 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_TEXEL_BUFFER_TEXTURE_WIDTH = 18 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_AUX_BUFFER_INDEX = 19 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_INDIRECT_PARAMS_BUFFER_INDEX = 20 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_SHADER_OUTPUT_BUFFER_INDEX = 21 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_OUTPUT_BUFFER_INDEX = 22 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_SHADER_TESS_FACTOR_OUTPUT_BUFFER_INDEX = 23 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_WORKGROUP_INDEX = 24 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_ENABLE_POINT_SIZE_BUILTIN = 25 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_DISABLE_RASTERIZATION = 26 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_CAPTURE_OUTPUT_TO_BUFFER = 27 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_SWIZZLE_TEXTURE_SAMPLES = 28 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS = 29 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_TESS_DOMAIN_ORIGIN_LOWER_LEFT = 30 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_MSL_PLATFORM = 31 | SPVC_COMPILER_OPTION_MSL_BIT,
SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff
} spvc_compiler_option;
/*
* Context is the highest-level API construct.
* The context owns all memory allocations made by its child object hierarchy, including various non-opaque structs and strings.
* This means that the API user only has to care about one "destroy" call ever when using the C API.
* All pointers handed out by the APIs are only valid as long as the context
* is alive and spvc_context_release_allocations has not been called.
*/
SPVC_PUBLIC_API spvc_result spvc_context_create(spvc_context *context);
/* Frees all memory allocations and objects associated with the context and its child objects. */
SPVC_PUBLIC_API void spvc_context_destroy(spvc_context context);
/* Frees all memory allocations and objects associated with the context and its child objects, but keeps the context alive. */
SPVC_PUBLIC_API void spvc_context_release_allocations(spvc_context context);
/* Get the string for the last error which was logged. */
SPVC_PUBLIC_API const char *spvc_context_get_last_error_string(spvc_context context);
/* Get notified in a callback when an error triggers. Useful for debugging. */
typedef void (*spvc_error_callback)(void *userdata, const char *error);
SPVC_PUBLIC_API void spvc_context_set_error_callback(spvc_context context, spvc_error_callback cb, void *userdata);
/* SPIR-V parsing interface. Maps to Parser which then creates a ParsedIR, and that IR is extracted into the handle. */
SPVC_PUBLIC_API spvc_result spvc_context_parse_spirv(spvc_context context, const SpvId *spirv, size_t word_count,
spvc_parsed_ir *parsed_ir);
/*
* Create a compiler backend. Capture mode controls if we construct by copy or move semantics.
* It is always recommended to use SPVC_CAPTURE_MODE_TAKE_OWNERSHIP if you only intend to cross-compile the IR once.
*/
SPVC_PUBLIC_API spvc_result spvc_context_create_compiler(spvc_context context, spvc_backend backend,
spvc_parsed_ir parsed_ir, spvc_capture_mode mode,
spvc_compiler *compiler);
/* Maps directly to C++ API. */
SPVC_PUBLIC_API unsigned spvc_compiler_get_current_id_bound(spvc_compiler compiler);
/* Create compiler options, which will initialize defaults. */
SPVC_PUBLIC_API spvc_result spvc_compiler_create_compiler_options(spvc_compiler compiler,
spvc_compiler_options *options);
/* Override options. Will return error if e.g. MSL options are used for the HLSL backend, etc. */
SPVC_PUBLIC_API spvc_result spvc_compiler_options_set_bool(spvc_compiler_options options,
spvc_compiler_option option, spvc_bool value);
SPVC_PUBLIC_API spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options,
spvc_compiler_option option, unsigned value);
/* Set compiler options. */
SPVC_PUBLIC_API spvc_result spvc_compiler_install_compiler_options(spvc_compiler compiler,
spvc_compiler_options options);
/* Compile IR into a string. *source is owned by the context, and caller must not free it themselves. */
SPVC_PUBLIC_API spvc_result spvc_compiler_compile(spvc_compiler compiler, const char **source);
/* Maps to C++ API. */
SPVC_PUBLIC_API spvc_result spvc_compiler_add_header_line(spvc_compiler compiler, const char *line);
SPVC_PUBLIC_API spvc_result spvc_compiler_require_extension(spvc_compiler compiler, const char *ext);
SPVC_PUBLIC_API spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_variable_id id);
/*
* HLSL specifics.
* Maps to C++ API.
*/
SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_set_root_constants_layout(spvc_compiler compiler,
const spvc_hlsl_root_constants *constant_info,
size_t count);
SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_add_vertex_attribute_remap(spvc_compiler compiler,
const spvc_hlsl_vertex_attribute_remap *remap,
size_t remaps);
SPVC_PUBLIC_API spvc_variable_id spvc_compiler_hlsl_remap_num_workgroups_builtin(spvc_compiler compiler);
/*
* MSL specifics.
* Maps to C++ API.
*/
SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_rasterization_disabled(spvc_compiler compiler);
SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_aux_buffer(spvc_compiler compiler);
SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_output_buffer(spvc_compiler compiler);
SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_patch_output_buffer(spvc_compiler compiler);
SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_input_threadgroup_mem(spvc_compiler compiler);
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler compiler,
const spvc_msl_vertex_attribute *attrs);
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler,
const spvc_msl_resource_binding *binding);
SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_vertex_attribute_used(spvc_compiler compiler, unsigned location);
SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_resource_used(spvc_compiler compiler,
SpvExecutionModel model,
unsigned set,
unsigned binding);
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler(spvc_compiler compiler, spvc_variable_id id, const spvc_msl_constexpr_sampler *sampler);
SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_fragment_output_components(spvc_compiler compiler, unsigned location, unsigned components);
/*
* Reflect resources.
* Maps almost 1:1 to C++ API.
*/
SPVC_PUBLIC_API spvc_result spvc_compiler_get_active_interface_variables(spvc_compiler compiler, spvc_set *set);
SPVC_PUBLIC_API spvc_result spvc_compiler_set_enabled_interface_variables(spvc_compiler compiler, spvc_set set);
SPVC_PUBLIC_API spvc_result spvc_compiler_create_shader_resources(spvc_compiler compiler, spvc_resources *resources);
SPVC_PUBLIC_API spvc_result spvc_compiler_create_shader_resources_for_active_variables(spvc_compiler compiler,
spvc_resources *resources,
spvc_set active);
SPVC_PUBLIC_API spvc_result spvc_resources_get_resource_list_for_type(spvc_resources resources, spvc_resource_type type,
const spvc_reflected_resource **resource_list,
size_t *resource_size);
/*
* Decorations.
* Maps to C++ API.
*/
SPVC_PUBLIC_API void spvc_compiler_set_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration,
unsigned argument);
SPVC_PUBLIC_API void spvc_compiler_set_decoration_string(spvc_compiler compiler, SpvId id, SpvDecoration decoration,
const char *argument);
SPVC_PUBLIC_API void spvc_compiler_set_name(spvc_compiler compiler, SpvId id, const char *argument);
SPVC_PUBLIC_API void spvc_compiler_set_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index,
SpvDecoration decoration, unsigned argument);
SPVC_PUBLIC_API void spvc_compiler_set_member_decoration_string(spvc_compiler compiler, spvc_type_id id,
unsigned member_index, SpvDecoration decoration,
const char *argument);
SPVC_PUBLIC_API void spvc_compiler_set_member_name(spvc_compiler compiler, spvc_type_id id, unsigned member_index,
const char *argument);
SPVC_PUBLIC_API void spvc_compiler_unset_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration);
SPVC_PUBLIC_API void spvc_compiler_unset_member_decoration(spvc_compiler compiler, spvc_type_id id,
unsigned member_index, SpvDecoration decoration);
SPVC_PUBLIC_API spvc_bool spvc_compiler_has_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration);
SPVC_PUBLIC_API spvc_bool spvc_compiler_has_member_decoration(spvc_compiler compiler, spvc_type_id id,
unsigned member_index, SpvDecoration decoration);
SPVC_PUBLIC_API const char *spvc_compiler_get_name(spvc_compiler compiler, SpvId id);
SPVC_PUBLIC_API unsigned spvc_compiler_get_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration);
SPVC_PUBLIC_API const char *spvc_compiler_get_decoration_string(spvc_compiler compiler, SpvId id,
SpvDecoration decoration);
SPVC_PUBLIC_API unsigned spvc_compiler_get_member_decoration(spvc_compiler compiler, spvc_type_id id,
unsigned member_index, SpvDecoration decoration);
SPVC_PUBLIC_API const char *spvc_compiler_get_member_decoration_string(spvc_compiler compiler, spvc_type_id id,
unsigned member_index, SpvDecoration decoration);
/*
* Entry points.
* Maps to C++ API.
*/
SPVC_PUBLIC_API spvc_result spvc_compiler_get_entry_points(spvc_compiler compiler,
const spvc_entry_point **entry_points,
size_t *num_entry_points);
SPVC_PUBLIC_API spvc_result spvc_compiler_set_entry_point(spvc_compiler compiler, const char *name,
SpvExecutionModel model);
SPVC_PUBLIC_API spvc_result spvc_compiler_rename_entry_point(spvc_compiler compiler, const char *old_name,
const char *new_name, SpvExecutionModel model);
SPVC_PUBLIC_API const char *spvc_compiler_get_cleansed_entry_point_name(spvc_compiler compiler, const char *name,
SpvExecutionModel model);
SPVC_PUBLIC_API void spvc_compiler_set_execution_mode(spvc_compiler compiler, SpvExecutionMode mode);
SPVC_PUBLIC_API void spvc_compiler_unset_execution_mode(spvc_compiler compiler, SpvExecutionMode mode);
SPVC_PUBLIC_API void spvc_compiler_set_execution_mode_with_arguments(spvc_compiler compiler, SpvExecutionMode mode,
unsigned arg0, unsigned arg1, unsigned arg2);
SPVC_PUBLIC_API spvc_result spvc_compiler_get_execution_modes(spvc_compiler compiler, const SpvExecutionMode **modes,
size_t *num_modes);
SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument(spvc_compiler compiler, SpvExecutionMode mode);
SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument_by_index(spvc_compiler compiler,
SpvExecutionMode mode, unsigned index);
SPVC_PUBLIC_API SpvExecutionModel spvc_compiler_get_execution_model(spvc_compiler compiler);
/*
* Type query interface.
* Maps to C++ API, except it's read-only.
*/
SPVC_PUBLIC_API spvc_type spvc_compiler_get_type_handle(spvc_compiler compiler, spvc_type_id id);
SPVC_PUBLIC_API spvc_basetype spvc_type_get_basetype(spvc_type type);
SPVC_PUBLIC_API unsigned spvc_type_get_bit_width(spvc_type type);
SPVC_PUBLIC_API unsigned spvc_type_get_vector_size(spvc_type type);
SPVC_PUBLIC_API unsigned spvc_type_get_columns(spvc_type type);
SPVC_PUBLIC_API unsigned spvc_type_get_num_array_dimensions(spvc_type type);
SPVC_PUBLIC_API spvc_bool spvc_type_array_dimension_is_literal(spvc_type type, unsigned dimension);
SPVC_PUBLIC_API SpvId spvc_type_get_array_dimension(spvc_type type, unsigned dimension);
SPVC_PUBLIC_API unsigned spvc_type_get_num_member_types(spvc_type type);
SPVC_PUBLIC_API spvc_type_id spvc_type_get_member_type(spvc_type type, unsigned index);
SPVC_PUBLIC_API SpvStorageClass spvc_type_get_storage_class(spvc_type type);
/* Image type query. */
SPVC_PUBLIC_API spvc_type_id spvc_type_get_image_sampled_type(spvc_type type);
SPVC_PUBLIC_API SpvDim spvc_type_get_image_dimension(spvc_type type);
SPVC_PUBLIC_API spvc_bool spvc_type_get_image_is_depth(spvc_type type);
SPVC_PUBLIC_API spvc_bool spvc_type_get_image_arrayed(spvc_type type);
SPVC_PUBLIC_API spvc_bool spvc_type_get_image_multisampled(spvc_type type);
SPVC_PUBLIC_API spvc_bool spvc_type_get_image_is_storage(spvc_type type);
SPVC_PUBLIC_API SpvImageFormat spvc_type_get_image_storage_format(spvc_type type);
SPVC_PUBLIC_API SpvAccessQualifier spvc_type_get_image_access_qualifier(spvc_type type);
/*
* Buffer layout query.
* Maps to C++ API.
*/
SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_struct_size(spvc_compiler compiler, spvc_type struct_type, size_t *size);
SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_struct_size_runtime_array(spvc_compiler compiler,
spvc_type struct_type, size_t array_size, size_t *size);
SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_offset(spvc_compiler compiler,
spvc_type type, unsigned index, unsigned *offset);
SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_array_stride(spvc_compiler compiler,
spvc_type type, unsigned index, unsigned *stride);
SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_matrix_stride(spvc_compiler compiler,
spvc_type type, unsigned index, unsigned *stride);
/*
* Workaround helper functions.
* Maps to C++ API.
*/
SPVC_PUBLIC_API spvc_result spvc_compiler_build_dummy_sampler_for_combined_images(spvc_compiler compiler, spvc_variable_id *id);
SPVC_PUBLIC_API spvc_result spvc_compiler_build_combined_image_samplers(spvc_compiler compiler);
SPVC_PUBLIC_API spvc_result spvc_compiler_get_combined_image_samplers(spvc_compiler compiler,
const spvc_combined_image_sampler **samplers,
size_t *num_samplers);
/*
* Constants
* Maps to C++ API.
*/
SPVC_PUBLIC_API spvc_result spvc_compiler_get_specialization_constants(spvc_compiler compiler,
const spvc_specialization_constant **constants,
size_t *num_constants);
SPVC_PUBLIC_API spvc_constant spvc_compiler_get_constant_handle(spvc_compiler compiler,
spvc_constant_id id);
SPVC_PUBLIC_API spvc_constant_id spvc_compiler_get_work_group_size_specialization_constants(spvc_compiler compiler,
spvc_specialization_constant *x,
spvc_specialization_constant *y,
spvc_specialization_constant *z);
/*
* No stdint.h until C99, sigh :(
* For smaller types, the result is sign or zero-extended as appropriate.
* Maps to C++ API.
* TODO: The SPIRConstant query interface and modification interface is not quite complete.
*/
SPVC_PUBLIC_API float spvc_constant_get_scalar_fp16(spvc_constant constant, unsigned column, unsigned row);
SPVC_PUBLIC_API float spvc_constant_get_scalar_fp32(spvc_constant constant, unsigned column, unsigned row);
SPVC_PUBLIC_API double spvc_constant_get_scalar_fp64(spvc_constant constant, unsigned column, unsigned row);
SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u32(spvc_constant constant, unsigned column, unsigned row);
SPVC_PUBLIC_API int spvc_constant_get_scalar_i32(spvc_constant constant, unsigned column, unsigned row);
SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u16(spvc_constant constant, unsigned column, unsigned row);
SPVC_PUBLIC_API int spvc_constant_get_scalar_i16(spvc_constant constant, unsigned column, unsigned row);
SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u8(spvc_constant constant, unsigned column, unsigned row);
SPVC_PUBLIC_API int spvc_constant_get_scalar_i8(spvc_constant constant, unsigned column, unsigned row);
SPVC_PUBLIC_API void spvc_constant_get_subconstants(spvc_constant constant, const spvc_constant_id **constituents, size_t *count);
SPVC_PUBLIC_API spvc_type_id spvc_constant_get_type(spvc_constant constant);
/*
* Misc reflection
* Maps to C++ API.
*/
SPVC_PUBLIC_API spvc_bool spvc_compiler_get_binary_offset_for_decoration(spvc_compiler compiler,
spvc_variable_id id,
SpvDecoration decoration,
unsigned *word_offset);
SPVC_PUBLIC_API spvc_bool spvc_compiler_buffer_is_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id);
SPVC_PUBLIC_API spvc_bool spvc_compiler_buffer_get_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id,
spvc_variable_id *counter_id);
SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_capabilities(spvc_compiler compiler,
const SpvCapability **capabilities,
size_t *num_capabilities);
SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_extensions(spvc_compiler compiler, const char ***extensions,
size_t *num_extensions);
SPVC_PUBLIC_API const char *spvc_compiler_get_remapped_declared_block_name(spvc_compiler compiler, spvc_variable_id id);
SPVC_PUBLIC_API spvc_result spvc_compiler_get_buffer_block_decorations(spvc_compiler compiler, spvc_variable_id id,
const SpvDecoration **decorations,
size_t *num_decorations);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -10205,7 +10205,7 @@ void CompilerGLSL::propagate_loop_dominators(const SPIRBlock &block)
// FIXME: This currently cannot handle complex continue blocks
// as in do-while.
// This should be seen as a "trivial" continue block.
string CompilerGLSL::emit_continue_block(uint32_t continue_block)
string CompilerGLSL::emit_continue_block(uint32_t continue_block, bool follow_true_block, bool follow_false_block)
{
auto *block = &get<SPIRBlock>(continue_block);
@ -10233,11 +10233,20 @@ string CompilerGLSL::emit_continue_block(uint32_t continue_block)
block = &get<SPIRBlock>(block->next_block);
}
// For do while blocks. The last block will be a select block.
else if (block->true_block)
else if (block->true_block && follow_true_block)
{
flush_phi(continue_block, block->true_block);
block = &get<SPIRBlock>(block->true_block);
}
else if (block->false_block && follow_false_block)
{
flush_phi(continue_block, block->false_block);
block = &get<SPIRBlock>(block->false_block);
}
else
{
SPIRV_CROSS_THROW("Invalid continue block detected!");
}
}
// Restore old pointer.
@ -10392,10 +10401,15 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method
// emitting the continue block can invalidate the condition expression.
auto initializer = emit_for_loop_initializers(block);
auto condition = to_expression(block.condition);
// Condition might have to be inverted.
if (execution_is_noop(get<SPIRBlock>(block.true_block), get<SPIRBlock>(block.merge_block)))
condition = join("!", enclose_expression(condition));
emit_block_hints(block);
if (method != SPIRBlock::MergeToSelectContinueForLoop)
{
auto continue_block = emit_continue_block(block.continue_block);
auto continue_block = emit_continue_block(block.continue_block, false, false);
statement("for (", initializer, "; ", condition, "; ", continue_block, ")");
}
else
@ -10404,12 +10418,20 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method
}
case SPIRBlock::WhileLoop:
{
// This block may be a dominating block, so make sure we flush undeclared variables before building the while loop header.
flush_undeclared_variables(block);
emit_while_loop_initializers(block);
emit_block_hints(block);
statement("while (", to_expression(block.condition), ")");
auto condition = to_expression(block.condition);
// Condition might have to be inverted.
if (execution_is_noop(get<SPIRBlock>(block.true_block), get<SPIRBlock>(block.merge_block)))
condition = join("!", enclose_expression(condition));
statement("while (", condition, ")");
break;
}
default:
SPIRV_CROSS_THROW("For/while loop detected, but need while/for loop semantics.");
@ -10445,6 +10467,7 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method
if (current_count == statement_count && condition_is_temporary)
{
propagate_loop_dominators(child);
uint32_t target_block = child.true_block;
switch (continue_type)
{
@ -10454,24 +10477,43 @@ bool CompilerGLSL::attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method
// emitting the continue block can invalidate the condition expression.
auto initializer = emit_for_loop_initializers(block);
auto condition = to_expression(child.condition);
auto continue_block = emit_continue_block(block.continue_block);
// Condition might have to be inverted.
if (execution_is_noop(get<SPIRBlock>(child.true_block), get<SPIRBlock>(block.merge_block)))
{
condition = join("!", enclose_expression(condition));
target_block = child.false_block;
}
auto continue_block = emit_continue_block(block.continue_block, false, false);
emit_block_hints(block);
statement("for (", initializer, "; ", condition, "; ", continue_block, ")");
break;
}
case SPIRBlock::WhileLoop:
{
emit_while_loop_initializers(block);
emit_block_hints(block);
statement("while (", to_expression(child.condition), ")");
auto condition = to_expression(child.condition);
// Condition might have to be inverted.
if (execution_is_noop(get<SPIRBlock>(child.true_block), get<SPIRBlock>(block.merge_block)))
{
condition = join("!", enclose_expression(condition));
target_block = child.false_block;
}
statement("while (", condition, ")");
break;
}
default:
SPIRV_CROSS_THROW("For/while loop detected, but need while/for loop semantics.");
}
begin_scope();
branch(child.self, child.true_block);
branch(child.self, target_block);
return true;
}
else
@ -10521,8 +10563,9 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
propagate_loop_dominators(block);
bool select_branch_to_true_block = false;
bool select_branch_to_false_block = false;
bool skip_direct_branch = false;
bool emitted_for_loop_header = false;
bool emitted_loop_header_variables = false;
bool force_complex_continue_block = false;
emit_hoisted_temporaries(block.declare_temporary);
@ -10544,8 +10587,12 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
flush_undeclared_variables(block);
if (attempt_emit_loop_header(block, SPIRBlock::MergeToSelectContinueForLoop))
{
if (execution_is_noop(get<SPIRBlock>(block.true_block), get<SPIRBlock>(block.merge_block)))
select_branch_to_false_block = true;
else
select_branch_to_true_block = true;
emitted_for_loop_header = true;
emitted_loop_header_variables = true;
force_complex_continue_block = true;
}
}
@ -10555,9 +10602,13 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
flush_undeclared_variables(block);
if (attempt_emit_loop_header(block, SPIRBlock::MergeToSelectForLoop))
{
// The body of while, is actually just the true block, so always branch there unconditionally.
// The body of while, is actually just the true (or false) block, so always branch there unconditionally.
if (execution_is_noop(get<SPIRBlock>(block.true_block), get<SPIRBlock>(block.merge_block)))
select_branch_to_false_block = true;
else
select_branch_to_true_block = true;
emitted_for_loop_header = true;
emitted_loop_header_variables = true;
}
}
// This is the newer loop behavior in glslang which branches from Loop header directly to
@ -10568,13 +10619,14 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
if (attempt_emit_loop_header(block, SPIRBlock::MergeToDirectForLoop))
{
skip_direct_branch = true;
emitted_for_loop_header = true;
emitted_loop_header_variables = true;
}
}
else if (continue_type == SPIRBlock::DoWhileLoop)
{
flush_undeclared_variables(block);
emit_while_loop_initializers(block);
emitted_loop_header_variables = true;
// We have some temporaries where the loop header is the dominator.
// We risk a case where we have code like:
// for (;;) { create-temporary; break; } consume-temporary;
@ -10589,6 +10641,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
{
flush_undeclared_variables(block);
emit_while_loop_initializers(block);
emitted_loop_header_variables = true;
// We have a generic loop without any distinguishable pattern like for, while or do while.
get<SPIRBlock>(block.continue_block).complex_continue = true;
@ -10611,7 +10664,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
// If we didn't successfully emit a loop header and we had loop variable candidates, we have a problem
// as writes to said loop variables might have been masked out, we need a recompile.
if (!emitted_for_loop_header && !block.loop_variables.empty())
if (!emitted_loop_header_variables && !block.loop_variables.empty())
{
force_recompile = true;
for (auto var : block.loop_variables)
@ -10660,6 +10713,22 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
else
branch(block.self, block.true_block);
}
else if (select_branch_to_false_block)
{
if (force_complex_continue_block)
{
assert(block.false_block == block.continue_block);
// We're going to emit a continue block directly here, so make sure it's marked as complex.
auto &complex_continue = get<SPIRBlock>(block.continue_block).complex_continue;
bool old_complex = complex_continue;
complex_continue = true;
branch(block.self, block.false_block);
complex_continue = old_complex;
}
else
branch(block.self, block.false_block);
}
else
branch(block.self, block.condition, block.true_block, block.false_block);
break;
@ -10850,7 +10919,11 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
// Make sure that we run the continue block to get the expressions set, but this
// should become an empty string.
// We have no fallbacks if we cannot forward everything to temporaries ...
auto statements = emit_continue_block(block.continue_block);
const auto &continue_block = get<SPIRBlock>(block.continue_block);
bool positive_test = execution_is_noop(get<SPIRBlock>(continue_block.true_block),
get<SPIRBlock>(continue_block.loop_dominator));
auto statements = emit_continue_block(block.continue_block, positive_test, !positive_test);
if (!statements.empty())
{
// The DoWhile block has side effects, force ComplexLoop pattern next pass.
@ -10858,7 +10931,12 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
force_recompile = true;
}
end_scope_decl(join("while (", to_expression(get<SPIRBlock>(block.continue_block).condition), ")"));
// Might have to invert the do-while test here.
auto condition = to_expression(continue_block.condition);
if (!positive_test)
condition = join("!", enclose_expression(condition));
end_scope_decl(join("while (", condition, ")"));
}
else
end_scope();

View File

@ -161,27 +161,11 @@ public:
init();
}
// Deprecate this interface because it doesn't overload properly with subclasses.
// Requires awkward static casting, which was a mistake.
SPIRV_CROSS_DEPRECATED("get_options() is obsolete, use get_common_options() instead.")
const Options &get_options() const
{
return options;
}
const Options &get_common_options() const
{
return options;
}
// Deprecate this interface because it doesn't overload properly with subclasses.
// Requires awkward static casting, which was a mistake.
SPIRV_CROSS_DEPRECATED("set_options() is obsolete, use set_common_options() instead.")
void set_options(Options &opts)
{
options = opts;
}
void set_common_options(const Options &opts)
{
options = opts;
@ -419,7 +403,7 @@ protected:
std::string constant_value_macro_name(uint32_t id);
void emit_constant(const SPIRConstant &constant);
void emit_specialization_constant_op(const SPIRConstantOp &constant);
std::string emit_continue_block(uint32_t continue_block);
std::string emit_continue_block(uint32_t continue_block, bool follow_true_block, bool follow_false_block);
bool attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method method);
void propagate_loop_dominators(const SPIRBlock &block);

View File

@ -4558,10 +4558,14 @@ void CompilerHLSL::require_texture_query_variant(const SPIRType &type)
}
}
string CompilerHLSL::compile(std::vector<HLSLVertexAttributeRemap> vertex_attributes)
void CompilerHLSL::set_root_constant_layouts(vector<RootConstants> layout)
{
remap_vertex_attributes = move(vertex_attributes);
return compile();
root_constants_layout = move(layout);
}
void CompilerHLSL::add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes)
{
remap_vertex_attributes.push_back(vertex_attributes);
}
uint32_t CompilerHLSL::remap_num_workgroups_builtin()

View File

@ -82,23 +82,11 @@ public:
{
}
SPIRV_CROSS_DEPRECATED("CompilerHLSL::get_options() is obsolete, use get_hlsl_options() instead.")
const Options &get_options() const
{
return hlsl_options;
}
const Options &get_hlsl_options() const
{
return hlsl_options;
}
SPIRV_CROSS_DEPRECATED("CompilerHLSL::get_options() is obsolete, use set_hlsl_options() instead.")
void set_options(Options &opts)
{
hlsl_options = opts;
}
void set_hlsl_options(const Options &opts)
{
hlsl_options = opts;
@ -108,16 +96,13 @@ public:
//
// Push constants ranges will be split up according to the
// layout specified.
void set_root_constant_layouts(std::vector<RootConstants> layout)
{
root_constants_layout = std::move(layout);
}
void set_root_constant_layouts(std::vector<RootConstants> layout);
// Compiles and remaps vertex attributes at specific locations to a fixed semantic.
// The default is TEXCOORD# where # denotes location.
// Matrices are unrolled to vectors with notation ${SEMANTIC}_#, where # denotes row.
// $SEMANTIC is either TEXCOORD# or a semantic name specified here.
std::string compile(std::vector<HLSLVertexAttributeRemap> vertex_attributes);
void add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes);
std::string compile() override;
// This is a special HLSL workaround for the NumWorkGroups builtin.

View File

@ -30,75 +30,51 @@ static const uint32_t k_unknown_component = ~0u;
static const uint32_t k_aux_mbr_idx_swizzle_const = 0u;
CompilerMSL::CompilerMSL(vector<uint32_t> spirv_, vector<MSLVertexAttr> *p_vtx_attrs,
vector<MSLResourceBinding> *p_res_bindings)
CompilerMSL::CompilerMSL(vector<uint32_t> spirv_)
: CompilerGLSL(move(spirv_))
{
if (p_vtx_attrs)
for (auto &va : *p_vtx_attrs)
{
vtx_attrs_by_location[va.location] = &va;
if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
vtx_attrs_by_builtin[va.builtin] = &va;
}
if (p_res_bindings)
for (auto &rb : *p_res_bindings)
resource_bindings.push_back(&rb);
}
CompilerMSL::CompilerMSL(const uint32_t *ir_, size_t word_count, MSLVertexAttr *p_vtx_attrs, size_t vtx_attrs_count,
MSLResourceBinding *p_res_bindings, size_t res_bindings_count)
CompilerMSL::CompilerMSL(const uint32_t *ir_, size_t word_count)
: CompilerGLSL(ir_, word_count)
{
if (p_vtx_attrs)
for (size_t i = 0; i < vtx_attrs_count; i++)
{
auto &va = p_vtx_attrs[i];
vtx_attrs_by_location[va.location] = &va;
if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
vtx_attrs_by_builtin[va.builtin] = &va;
}
if (p_res_bindings)
for (size_t i = 0; i < res_bindings_count; i++)
resource_bindings.push_back(&p_res_bindings[i]);
}
CompilerMSL::CompilerMSL(const ParsedIR &ir_, MSLVertexAttr *p_vtx_attrs, size_t vtx_attrs_count,
MSLResourceBinding *p_res_bindings, size_t res_bindings_count)
CompilerMSL::CompilerMSL(const ParsedIR &ir_)
: CompilerGLSL(ir_)
{
if (p_vtx_attrs)
for (size_t i = 0; i < vtx_attrs_count; i++)
{
auto &va = p_vtx_attrs[i];
vtx_attrs_by_location[va.location] = &va;
if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
vtx_attrs_by_builtin[va.builtin] = &va;
}
if (p_res_bindings)
for (size_t i = 0; i < res_bindings_count; i++)
resource_bindings.push_back(&p_res_bindings[i]);
}
CompilerMSL::CompilerMSL(ParsedIR &&ir_, MSLVertexAttr *p_vtx_attrs, size_t vtx_attrs_count,
MSLResourceBinding *p_res_bindings, size_t res_bindings_count)
CompilerMSL::CompilerMSL(ParsedIR &&ir_)
: CompilerGLSL(std::move(ir_))
{
if (p_vtx_attrs)
for (size_t i = 0; i < vtx_attrs_count; i++)
{
auto &va = p_vtx_attrs[i];
vtx_attrs_by_location[va.location] = &va;
if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
vtx_attrs_by_builtin[va.builtin] = &va;
}
if (p_res_bindings)
for (size_t i = 0; i < res_bindings_count; i++)
resource_bindings.push_back(&p_res_bindings[i]);
void CompilerMSL::add_msl_vertex_attribute(const MSLVertexAttr &va)
{
vtx_attrs_by_location[va.location] = va;
if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
vtx_attrs_by_builtin[va.builtin] = va;
}
void CompilerMSL::add_msl_resource_binding(const MSLResourceBinding &binding)
{
resource_bindings.push_back({ binding, false });
}
bool CompilerMSL::is_msl_vertex_attribute_used(uint32_t location)
{
return vtx_attrs_in_use.count(location) != 0;
}
bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding)
{
auto itr = find_if(begin(resource_bindings), end(resource_bindings),
[&](const std::pair<MSLResourceBinding, bool> &resource) -> bool {
return model == resource.first.stage && desc_set == resource.first.desc_set &&
binding == resource.first.binding;
});
return itr != end(resource_bindings) && itr->second;
}
void CompilerMSL::set_fragment_output_components(uint32_t location, uint32_t components)
@ -662,7 +638,10 @@ string CompilerMSL::compile()
reset();
next_metal_resource_index = MSLResourceBinding(); // Start bindings at zero
// Start bindings at zero.
next_metal_resource_index_buffer = 0;
next_metal_resource_index_texture = 0;
next_metal_resource_index_sampler = 0;
// Move constructor for this type is broken on GCC 4.9 ...
buffer = unique_ptr<ostringstream>(new ostringstream());
@ -679,36 +658,6 @@ string CompilerMSL::compile()
return buffer->str();
}
string CompilerMSL::compile(vector<MSLVertexAttr> *p_vtx_attrs, vector<MSLResourceBinding> *p_res_bindings)
{
if (p_vtx_attrs)
{
vtx_attrs_by_location.clear();
for (auto &va : *p_vtx_attrs)
{
vtx_attrs_by_location[va.location] = &va;
if (va.builtin != BuiltInMax && !vtx_attrs_by_builtin.count(va.builtin))
vtx_attrs_by_builtin[va.builtin] = &va;
}
}
if (p_res_bindings)
{
resource_bindings.clear();
for (auto &rb : *p_res_bindings)
resource_bindings.push_back(&rb);
}
return compile();
}
string CompilerMSL::compile(MSLConfiguration &msl_cfg, vector<MSLVertexAttr> *p_vtx_attrs,
vector<MSLResourceBinding> *p_res_bindings)
{
msl_options = msl_cfg;
return compile(p_vtx_attrs, p_res_bindings);
}
// Register the need to output any custom functions.
void CompilerMSL::preprocess_op_codes()
{
@ -1030,10 +979,8 @@ void CompilerMSL::mark_as_packable(SPIRType &type)
// If a vertex attribute exists at the location, it is marked as being used by this shader
void CompilerMSL::mark_location_as_used_by_shader(uint32_t location, StorageClass storage)
{
MSLVertexAttr *p_va;
if ((get_execution_model() == ExecutionModelVertex || is_tessellation_shader()) && (storage == StorageClassInput) &&
(p_va = vtx_attrs_by_location[location]))
p_va->used_by_shader = true;
if ((get_execution_model() == ExecutionModelVertex || is_tessellation_shader()) && (storage == StorageClassInput))
vtx_attrs_in_use.insert(location);
}
uint32_t CompilerMSL::get_target_components_for_fragment_location(uint32_t location) const
@ -1141,7 +1088,7 @@ void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, co
}
else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
{
uint32_t locn = vtx_attrs_by_builtin[builtin]->location;
uint32_t locn = vtx_attrs_by_builtin[builtin].location;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
@ -1268,7 +1215,7 @@ void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage
}
else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
{
uint32_t locn = vtx_attrs_by_builtin[builtin]->location + i;
uint32_t locn = vtx_attrs_by_builtin[builtin].location + i;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
@ -1417,7 +1364,7 @@ void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass
}
else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
{
uint32_t locn = vtx_attrs_by_builtin[builtin]->location + i;
uint32_t locn = vtx_attrs_by_builtin[builtin].location + i;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
@ -1553,7 +1500,10 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor
}
else if (is_builtin && is_tessellation_shader() && vtx_attrs_by_builtin.count(builtin))
{
uint32_t locn = vtx_attrs_by_builtin[builtin]->location;
uint32_t locn = 0;
auto builtin_itr = vtx_attrs_by_builtin.find(builtin);
if (builtin_itr != end(vtx_attrs_by_builtin))
locn = builtin_itr->second.location;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, storage);
}
@ -1635,7 +1585,7 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
}
else if (vtx_attrs_by_builtin.count(builtin))
{
uint32_t locn = vtx_attrs_by_builtin[builtin]->location;
uint32_t locn = vtx_attrs_by_builtin[builtin].location;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, StorageClassInput);
}
@ -1696,7 +1646,7 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
}
else if (vtx_attrs_by_builtin.count(builtin))
{
uint32_t locn = vtx_attrs_by_builtin[builtin]->location;
uint32_t locn = vtx_attrs_by_builtin[builtin].location;
set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn);
mark_location_as_used_by_shader(locn, StorageClassInput);
}
@ -2119,11 +2069,11 @@ uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t l
{
auto &type = get<SPIRType>(type_id);
MSLVertexAttr *p_va = vtx_attrs_by_location[location];
if (!p_va)
auto p_va = vtx_attrs_by_location.find(location);
if (p_va == end(vtx_attrs_by_location))
return type_id;
switch (p_va->format)
switch (p_va->second.format)
{
case MSL_VERTEX_FORMAT_UINT8:
{
@ -2157,6 +2107,7 @@ uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t l
ptr_type.parent_type = base_type_id;
return ptr_type_id;
}
case MSL_VERTEX_FORMAT_UINT16:
{
switch (type.basetype)
@ -2188,6 +2139,7 @@ uint32_t CompilerMSL::ensure_correct_attribute_type(uint32_t type_id, uint32_t l
return ptr_type_id;
}
default:
case MSL_VERTEX_FORMAT_OTHER:
break;
}
@ -5792,26 +5744,27 @@ uint32_t CompilerMSL::get_metal_resource_index(SPIRVariable &var, SPIRType::Base
uint32_t var_binding = (var.storage == StorageClassPushConstant) ? kPushConstBinding : var_dec.binding;
// If a matching binding has been specified, find and use it
for (auto p_res_bind : resource_bindings)
{
if (p_res_bind->stage == execution.model && p_res_bind->desc_set == var_desc_set &&
p_res_bind->binding == var_binding)
{
auto itr = find_if(begin(resource_bindings), end(resource_bindings),
[&](const pair<MSLResourceBinding, bool> &resource) -> bool {
return var_desc_set == resource.first.desc_set && var_binding == resource.first.binding &&
execution.model == resource.first.stage;
});
p_res_bind->used_by_shader = true;
if (itr != end(resource_bindings))
{
itr->second = true;
switch (basetype)
{
case SPIRType::Struct:
return p_res_bind->msl_buffer;
return itr->first.msl_buffer;
case SPIRType::Image:
return p_res_bind->msl_texture;
return itr->first.msl_texture;
case SPIRType::Sampler:
return p_res_bind->msl_sampler;
return itr->first.msl_sampler;
default:
return 0;
}
}
}
// If there is no explicit mapping of bindings to MSL, use the declared binding.
if (has_decoration(var.self, DecorationBinding))
@ -5827,16 +5780,16 @@ uint32_t CompilerMSL::get_metal_resource_index(SPIRVariable &var, SPIRType::Base
switch (basetype)
{
case SPIRType::Struct:
resource_index = next_metal_resource_index.msl_buffer;
next_metal_resource_index.msl_buffer += binding_stride;
resource_index = next_metal_resource_index_buffer;
next_metal_resource_index_buffer += binding_stride;
break;
case SPIRType::Image:
resource_index = next_metal_resource_index.msl_texture;
next_metal_resource_index.msl_texture += binding_stride;
resource_index = next_metal_resource_index_texture;
next_metal_resource_index_texture += binding_stride;
break;
case SPIRType::Sampler:
resource_index = next_metal_resource_index.msl_sampler;
next_metal_resource_index.msl_sampler += binding_stride;
resource_index = next_metal_resource_index_sampler;
next_metal_resource_index_sampler += binding_stride;
break;
default:
resource_index = 0;

View File

@ -32,14 +32,14 @@ namespace spirv_cross
// some other format.
enum MSLVertexFormat
{
MSL_VERTEX_FORMAT_OTHER,
MSL_VERTEX_FORMAT_UINT8,
MSL_VERTEX_FORMAT_UINT16
MSL_VERTEX_FORMAT_OTHER = 0,
MSL_VERTEX_FORMAT_UINT8 = 1,
MSL_VERTEX_FORMAT_UINT16 = 2,
MSL_VERTEX_FORMAT_INT_MAX = 0x7fffffff
};
// Defines MSL characteristics of a vertex attribute at a particular location.
// The used_by_shader flag is set to true during compilation of SPIR-V to MSL
// if the shader makes use of this vertex attribute.
// After compilation, it is possible to query whether or not this location was used.
struct MSLVertexAttr
{
uint32_t location = 0;
@ -49,72 +49,72 @@ struct MSLVertexAttr
bool per_instance = false;
MSLVertexFormat format = MSL_VERTEX_FORMAT_OTHER;
spv::BuiltIn builtin = spv::BuiltInMax;
bool used_by_shader = false;
};
// Matches the binding index of a MSL resource for a binding within a descriptor set.
// Taken together, the stage, desc_set and binding combine to form a reference to a resource
// descriptor used in a particular shading stage. Generally, only one of the buffer, texture,
// or sampler elements will be populated. The used_by_shader flag is set to true during
// compilation of SPIR-V to MSL if the shader makes use of this vertex attribute.
// descriptor used in a particular shading stage.
struct MSLResourceBinding
{
spv::ExecutionModel stage;
spv::ExecutionModel stage = spv::ExecutionModelMax;
uint32_t desc_set = 0;
uint32_t binding = 0;
uint32_t msl_buffer = 0;
uint32_t msl_texture = 0;
uint32_t msl_sampler = 0;
bool used_by_shader = false;
};
enum MSLSamplerCoord
{
MSL_SAMPLER_COORD_NORMALIZED,
MSL_SAMPLER_COORD_PIXEL
MSL_SAMPLER_COORD_NORMALIZED = 0,
MSL_SAMPLER_COORD_PIXEL = 1,
MSL_SAMPLER_INT_MAX = 0x7fffffff
};
enum MSLSamplerFilter
{
MSL_SAMPLER_FILTER_NEAREST,
MSL_SAMPLER_FILTER_LINEAR
MSL_SAMPLER_FILTER_NEAREST = 0,
MSL_SAMPLER_FILTER_LINEAR = 1,
MSL_SAMPLER_FILTER_INT_MAX = 0x7fffffff
};
enum MSLSamplerMipFilter
{
MSL_SAMPLER_MIP_FILTER_NONE,
MSL_SAMPLER_MIP_FILTER_NEAREST,
MSL_SAMPLER_MIP_FILTER_LINEAR,
MSL_SAMPLER_MIP_FILTER_NONE = 0,
MSL_SAMPLER_MIP_FILTER_NEAREST = 1,
MSL_SAMPLER_MIP_FILTER_LINEAR = 2,
MSL_SAMPLER_MIP_FILTER_INT_MAX = 0x7fffffff
};
enum MSLSamplerAddress
{
MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO,
MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE,
MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER,
MSL_SAMPLER_ADDRESS_REPEAT,
MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT
MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO = 0,
MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE = 1,
MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER = 2,
MSL_SAMPLER_ADDRESS_REPEAT = 3,
MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT = 4,
MSL_SAMPLER_ADDRESS_INT_MAX = 0x7fffffff
};
enum MSLSamplerCompareFunc
{
MSL_SAMPLER_COMPARE_FUNC_NEVER,
MSL_SAMPLER_COMPARE_FUNC_LESS,
MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL,
MSL_SAMPLER_COMPARE_FUNC_GREATER,
MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL,
MSL_SAMPLER_COMPARE_FUNC_EQUAL,
MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL,
MSL_SAMPLER_COMPARE_FUNC_ALWAYS
MSL_SAMPLER_COMPARE_FUNC_NEVER = 0,
MSL_SAMPLER_COMPARE_FUNC_LESS = 1,
MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL = 2,
MSL_SAMPLER_COMPARE_FUNC_GREATER = 3,
MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL = 4,
MSL_SAMPLER_COMPARE_FUNC_EQUAL = 5,
MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL = 6,
MSL_SAMPLER_COMPARE_FUNC_ALWAYS = 7,
MSL_SAMPLER_COMPARE_FUNC_INT_MAX = 0x7fffffff
};
enum MSLSamplerBorderColor
{
MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK,
MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK,
MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE
MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK = 0,
MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK = 1,
MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE = 2,
MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff
};
struct MSLConstexprSampler
@ -161,8 +161,8 @@ public:
{
typedef enum
{
iOS,
macOS,
iOS = 0,
macOS = 1
} Platform;
Platform platform = macOS;
@ -210,23 +210,11 @@ public:
}
};
SPIRV_CROSS_DEPRECATED("CompilerMSL::get_options() is obsolete, use get_msl_options() instead.")
const Options &get_options() const
{
return msl_options;
}
const Options &get_msl_options() const
{
return msl_options;
}
SPIRV_CROSS_DEPRECATED("CompilerMSL::set_options() is obsolete, use set_msl_options() instead.")
void set_options(Options &opts)
{
msl_options = opts;
}
void set_msl_options(const Options &opts)
{
msl_options = opts;
@ -269,6 +257,44 @@ public:
return capture_output_to_buffer && stage_in_var_id != 0;
}
explicit CompilerMSL(std::vector<uint32_t> spirv);
CompilerMSL(const uint32_t *ir, size_t word_count);
explicit CompilerMSL(const ParsedIR &ir);
explicit CompilerMSL(ParsedIR &&ir);
// attr is a vertex attribute binding used to match
// vertex content locations to MSL attributes. If vertex attributes are provided,
// is_msl_vertex_attribute_used() will return true after calling ::compile() if
// the location was used by the MSL code.
void add_msl_vertex_attribute(const MSLVertexAttr &attr);
// resource is a resource binding to indicate the MSL buffer,
// texture or sampler index to use for a particular SPIR-V description set
// and binding. If resource bindings are provided,
// is_msl_resource_binding_used() will return true after calling ::compile() if
// the set/binding combination was used by the MSL code.
void add_msl_resource_binding(const MSLResourceBinding &resource);
// Query after compilation is done. This allows you to check if a location or set/binding combination was used by the shader.
bool is_msl_vertex_attribute_used(uint32_t location);
bool is_msl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding);
// Compiles the SPIR-V code into Metal Shading Language.
std::string compile() override;
// Remap a sampler with ID to a constexpr sampler.
// Older iOS targets must use constexpr samplers in certain cases (PCF),
// so a static sampler must be used.
// The sampler will not consume a binding, but be declared in the entry point as a constexpr sampler.
// This can be used on both combined image/samplers (sampler2D) or standalone samplers.
// The remapped sampler must not be an array of samplers.
void remap_constexpr_sampler(uint32_t id, const MSLConstexprSampler &sampler);
// If using CompilerMSL::Options::pad_fragment_output_components, override the number of components we expect
// to use for a particular location. The default is 4 if number of components is not overridden.
void set_fragment_output_components(uint32_t location, uint32_t components);
protected:
// An enum of SPIR-V functions that are implemented in additional
// source code that is added to the shader if necessary.
enum SPVFuncImpl
@ -304,58 +330,6 @@ public:
SPVFuncImplArrayCopyMultidimMax = 6
};
// Constructs an instance to compile the SPIR-V code into Metal Shading Language,
// using the configuration parameters, if provided:
// - p_vtx_attrs is an optional list of vertex attribute bindings used to match
// vertex content locations to MSL attributes. If vertex attributes are provided,
// the compiler will set the used_by_shader flag to true in any vertex attribute
// actually used by the MSL code.
// - p_res_bindings is a list of resource bindings to indicate the MSL buffer,
// texture or sampler index to use for a particular SPIR-V description set
// and binding. If resource bindings are provided, the compiler will set the
// used_by_shader flag to true in any resource binding actually used by the MSL code.
CompilerMSL(std::vector<uint32_t> spirv, std::vector<MSLVertexAttr> *p_vtx_attrs = nullptr,
std::vector<MSLResourceBinding> *p_res_bindings = nullptr);
// Alternate constructor avoiding use of std::vectors.
CompilerMSL(const uint32_t *ir, size_t word_count, MSLVertexAttr *p_vtx_attrs = nullptr, size_t vtx_attrs_count = 0,
MSLResourceBinding *p_res_bindings = nullptr, size_t res_bindings_count = 0);
// Alternate constructors taking pre-parsed IR directly.
CompilerMSL(const ParsedIR &ir, MSLVertexAttr *p_vtx_attrs = nullptr, size_t vtx_attrs_count = 0,
MSLResourceBinding *p_res_bindings = nullptr, size_t res_bindings_count = 0);
CompilerMSL(ParsedIR &&ir, MSLVertexAttr *p_vtx_attrs = nullptr, size_t vtx_attrs_count = 0,
MSLResourceBinding *p_res_bindings = nullptr, size_t res_bindings_count = 0);
// Compiles the SPIR-V code into Metal Shading Language.
std::string compile() override;
// Compiles the SPIR-V code into Metal Shading Language, overriding configuration parameters.
// Any of the parameters here may be null to indicate that the configuration provided in the
// constructor should be used. They are not declared as optional to avoid a conflict with the
// inherited and overridden zero-parameter compile() function.
std::string compile(std::vector<MSLVertexAttr> *p_vtx_attrs, std::vector<MSLResourceBinding> *p_res_bindings);
// This legacy method is deprecated.
typedef Options MSLConfiguration;
SPIRV_CROSS_DEPRECATED("Please use get_msl_options() and set_msl_options() instead.")
std::string compile(MSLConfiguration &msl_cfg, std::vector<MSLVertexAttr> *p_vtx_attrs = nullptr,
std::vector<MSLResourceBinding> *p_res_bindings = nullptr);
// Remap a sampler with ID to a constexpr sampler.
// Older iOS targets must use constexpr samplers in certain cases (PCF),
// so a static sampler must be used.
// The sampler will not consume a binding, but be declared in the entry point as a constexpr sampler.
// This can be used on both combined image/samplers (sampler2D) or standalone samplers.
// The remapped sampler must not be an array of samplers.
void remap_constexpr_sampler(uint32_t id, const MSLConstexprSampler &sampler);
// If using CompilerMSL::Options::pad_fragment_output_components, override the number of components we expect
// to use for a particular location. The default is 4 if number of components is not overridden.
void set_fragment_output_components(uint32_t location, uint32_t components);
protected:
void emit_binary_unord_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op);
void emit_instruction(const Instruction &instr) override;
void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args,
@ -495,15 +469,20 @@ protected:
Options msl_options;
std::set<SPVFuncImpl> spv_function_implementations;
std::unordered_map<uint32_t, MSLVertexAttr *> vtx_attrs_by_location;
std::unordered_map<uint32_t, MSLVertexAttr *> vtx_attrs_by_builtin;
std::unordered_map<uint32_t, MSLVertexAttr> vtx_attrs_by_location;
std::unordered_map<uint32_t, MSLVertexAttr> vtx_attrs_by_builtin;
std::unordered_set<uint32_t> vtx_attrs_in_use;
std::unordered_map<uint32_t, uint32_t> fragment_output_components;
std::unordered_map<MSLStructMemberKey, uint32_t> struct_member_padding;
std::set<std::string> pragma_lines;
std::set<std::string> typedef_lines;
std::vector<uint32_t> vars_needing_early_declaration;
std::vector<MSLResourceBinding *> resource_bindings;
MSLResourceBinding next_metal_resource_index;
std::vector<std::pair<MSLResourceBinding, bool>> resource_bindings;
uint32_t next_metal_resource_index_buffer = 0;
uint32_t next_metal_resource_index_texture = 0;
uint32_t next_metal_resource_index_sampler = 0;
uint32_t stage_in_var_id = 0;
uint32_t stage_out_var_id = 0;
uint32_t patch_stage_in_var_id = 0;

View File

@ -16,6 +16,13 @@ import multiprocessing
import errno
from functools import partial
class Paths():
def __init__(self, glslang, spirv_as, spirv_val, spirv_opt):
self.glslang = glslang
self.spirv_as = spirv_as
self.spirv_val = spirv_val
self.spirv_opt = spirv_opt
def remove_file(path):
#print('Removing file:', path)
os.remove(path)
@ -131,21 +138,21 @@ def validate_shader_msl(shader, opt):
print('Error compiling Metal shader: ' + msl_path)
raise RuntimeError('Failed to compile Metal shader')
def cross_compile_msl(shader, spirv, opt):
def cross_compile_msl(shader, spirv, opt, paths):
spirv_path = create_temporary()
msl_path = create_temporary(os.path.basename(shader))
spirv_cmd = ['spirv-as', '-o', spirv_path, shader]
spirv_cmd = [paths.spirv_as, '-o', spirv_path, shader]
if '.preserve.' in shader:
spirv_cmd.append('--preserve-numeric-ids')
if spirv:
subprocess.check_call(spirv_cmd)
else:
subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader])
subprocess.check_call([paths.glslang, '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader])
if opt:
subprocess.check_call(['spirv-opt', '--skip-validation', '-O', '-o', spirv_path, spirv_path])
subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path])
spirv_cross_path = './spirv-cross'
@ -166,7 +173,7 @@ def cross_compile_msl(shader, spirv, opt):
subprocess.check_call(msl_args)
if not shader_is_invalid_spirv(msl_path):
subprocess.check_call(['spirv-val', '--target-env', 'vulkan1.1', spirv_path])
subprocess.check_call([paths.spirv_val, '--target-env', 'vulkan1.1', spirv_path])
return (spirv_path, msl_path)
@ -201,8 +208,8 @@ def shader_to_win_path(shader):
return shader
ignore_fxc = False
def validate_shader_hlsl(shader, force_no_external_validation):
subprocess.check_call(['glslangValidator', '-e', 'main', '-D', '--target-env', 'vulkan1.1', '-V', shader])
def validate_shader_hlsl(shader, force_no_external_validation, paths):
subprocess.check_call([paths.glslang, '-e', 'main', '-D', '--target-env', 'vulkan1.1', '-V', shader])
is_no_fxc = '.nofxc.' in shader
global ignore_fxc
if (not ignore_fxc) and (not force_no_external_validation) and (not is_no_fxc):
@ -231,21 +238,21 @@ def shader_to_sm(shader):
else:
return '50'
def cross_compile_hlsl(shader, spirv, opt, force_no_external_validation):
def cross_compile_hlsl(shader, spirv, opt, force_no_external_validation, paths):
spirv_path = create_temporary()
hlsl_path = create_temporary(os.path.basename(shader))
spirv_cmd = ['spirv-as', '-o', spirv_path, shader]
spirv_cmd = [paths.spirv_as, '-o', spirv_path, shader]
if '.preserve.' in shader:
spirv_cmd.append('--preserve-numeric-ids')
if spirv:
subprocess.check_call(spirv_cmd)
else:
subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader])
subprocess.check_call([paths.glslang, '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader])
if opt:
subprocess.check_call(['spirv-opt', '--skip-validation', '-O', '-o', spirv_path, spirv_path])
subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path])
spirv_cross_path = './spirv-cross'
@ -253,27 +260,27 @@ def cross_compile_hlsl(shader, spirv, opt, force_no_external_validation):
subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', hlsl_path, spirv_path, '--hlsl-enable-compat', '--hlsl', '--shader-model', sm])
if not shader_is_invalid_spirv(hlsl_path):
subprocess.check_call(['spirv-val', '--target-env', 'vulkan1.1', spirv_path])
subprocess.check_call([paths.spirv_val, '--target-env', 'vulkan1.1', spirv_path])
validate_shader_hlsl(hlsl_path, force_no_external_validation)
validate_shader_hlsl(hlsl_path, force_no_external_validation, paths)
return (spirv_path, hlsl_path)
def cross_compile_reflect(shader, spirv, opt):
def cross_compile_reflect(shader, spirv, opt, paths):
spirv_path = create_temporary()
reflect_path = create_temporary(os.path.basename(shader))
spirv_cmd = ['spirv-as', '-o', spirv_path, shader]
spirv_cmd = [paths.spirv_as, '-o', spirv_path, shader]
if '.preserve.' in shader:
spirv_cmd.append('--preserve-numeric-ids')
if spirv:
subprocess.check_call(spirv_cmd)
else:
subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader])
subprocess.check_call([paths.glslang, '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader])
if opt:
subprocess.check_call(['spirv-opt', '--skip-validation', '-O', '-o', spirv_path, spirv_path])
subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path])
spirv_cross_path = './spirv-cross'
@ -281,33 +288,33 @@ def cross_compile_reflect(shader, spirv, opt):
subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', reflect_path, spirv_path, '--reflect'])
return (spirv_path, reflect_path)
def validate_shader(shader, vulkan):
def validate_shader(shader, vulkan, paths):
if vulkan:
subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', shader])
subprocess.check_call([paths.glslang, '--target-env', 'vulkan1.1', '-V', shader])
else:
subprocess.check_call(['glslangValidator', shader])
subprocess.check_call([paths.glslang, shader])
def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, opt):
def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, opt, paths):
spirv_path = create_temporary()
glsl_path = create_temporary(os.path.basename(shader))
if vulkan or spirv:
vulkan_glsl_path = create_temporary('vk' + os.path.basename(shader))
spirv_cmd = ['spirv-as', '-o', spirv_path, shader]
spirv_cmd = [paths.spirv_as, '-o', spirv_path, shader]
if '.preserve.' in shader:
spirv_cmd.append('--preserve-numeric-ids')
if spirv:
subprocess.check_call(spirv_cmd)
else:
subprocess.check_call(['glslangValidator', '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader])
subprocess.check_call([paths.glslang, '--target-env', 'vulkan1.1', '-V', '-o', spirv_path, shader])
if opt and (not invalid_spirv):
subprocess.check_call(['spirv-opt', '--skip-validation', '-O', '-o', spirv_path, spirv_path])
subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path])
if not invalid_spirv:
subprocess.check_call(['spirv-val', '--target-env', 'vulkan1.1', spirv_path])
subprocess.check_call([paths.spirv_val, '--target-env', 'vulkan1.1', spirv_path])
extra_args = []
if eliminate:
@ -326,14 +333,14 @@ def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, fl
# A shader might not be possible to make valid GLSL from, skip validation for this case.
if not ('nocompat' in glsl_path):
subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', glsl_path, spirv_path] + extra_args)
validate_shader(glsl_path, False)
validate_shader(glsl_path, False, paths)
else:
remove_file(glsl_path)
glsl_path = None
if vulkan or spirv:
subprocess.check_call([spirv_cross_path, '--entry', 'main', '--vulkan-semantics', '--output', vulkan_glsl_path, spirv_path] + extra_args)
validate_shader(vulkan_glsl_path, True)
validate_shader(vulkan_glsl_path, True, paths)
# SPIR-V shaders might just want to validate Vulkan GLSL output, we don't always care about the output.
if not vulkan:
remove_file(vulkan_glsl_path)
@ -481,7 +488,7 @@ def shader_is_flatten_dimensions(shader):
def shader_is_noopt(shader):
return '.noopt.' in shader
def test_shader(stats, shader, update, keep, opt):
def test_shader(stats, shader, update, keep, opt, paths):
joined_path = os.path.join(shader[0], shader[1])
vulkan = shader_is_vulkan(shader[1])
desktop = shader_is_desktop(shader[1])
@ -495,7 +502,7 @@ def test_shader(stats, shader, update, keep, opt):
noopt = shader_is_noopt(shader[1])
print('Testing shader:', joined_path)
spirv, glsl, vulkan_glsl = cross_compile(joined_path, vulkan, is_spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, opt and (not noopt))
spirv, glsl, vulkan_glsl = cross_compile(joined_path, vulkan, is_spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, opt and (not noopt), paths)
# Only test GLSL stats if we have a shader following GL semantics.
if stats and (not vulkan) and (not is_spirv) and (not desktop):
@ -519,12 +526,12 @@ def test_shader(stats, shader, update, keep, opt):
a.append(str(i))
print(','.join(a), file = stats)
def test_shader_msl(stats, shader, update, keep, opt, force_no_external_validation):
def test_shader_msl(stats, shader, update, keep, opt, force_no_external_validation, paths):
joined_path = os.path.join(shader[0], shader[1])
print('\nTesting MSL shader:', joined_path)
is_spirv = shader_is_spirv(shader[1])
noopt = shader_is_noopt(shader[1])
spirv, msl = cross_compile_msl(joined_path, is_spirv, opt and (not noopt))
spirv, msl = cross_compile_msl(joined_path, is_spirv, opt and (not noopt), paths)
regression_check(shader, msl, update, keep, opt)
# Uncomment the following line to print the temp SPIR-V file path.
@ -540,34 +547,35 @@ def test_shader_msl(stats, shader, update, keep, opt, force_no_external_validati
remove_file(spirv)
def test_shader_hlsl(stats, shader, update, keep, opt, force_no_external_validation):
def test_shader_hlsl(stats, shader, update, keep, opt, force_no_external_validation, paths):
joined_path = os.path.join(shader[0], shader[1])
print('Testing HLSL shader:', joined_path)
is_spirv = shader_is_spirv(shader[1])
noopt = shader_is_noopt(shader[1])
spirv, hlsl = cross_compile_hlsl(joined_path, is_spirv, opt and (not noopt), force_no_external_validation)
spirv, hlsl = cross_compile_hlsl(joined_path, is_spirv, opt and (not noopt), force_no_external_validation, paths)
regression_check(shader, hlsl, update, keep, opt)
remove_file(spirv)
def test_shader_reflect(stats, shader, update, keep, opt):
def test_shader_reflect(stats, shader, update, keep, opt, paths):
joined_path = os.path.join(shader[0], shader[1])
print('Testing shader reflection:', joined_path)
is_spirv = shader_is_spirv(shader[1])
noopt = shader_is_noopt(shader[1])
spirv, reflect = cross_compile_reflect(joined_path, is_spirv, opt and (not noopt))
spirv, reflect = cross_compile_reflect(joined_path, is_spirv, opt and (not noopt), paths)
regression_check_reflect(shader, reflect, update, keep, opt)
remove_file(spirv)
def test_shader_file(relpath, stats, shader_dir, update, keep, opt, force_no_external_validation, backend):
def test_shader_file(relpath, stats, args, backend):
paths = Paths(args.glslang, args.spirv_as, args.spirv_val, args.spirv_opt)
try:
if backend == 'msl':
test_shader_msl(stats, (shader_dir, relpath), update, keep, opt, force_no_external_validation)
test_shader_msl(stats, (args.folder, relpath), args.update, args.keep, args.opt, args.force_no_external_validation, paths)
elif backend == 'hlsl':
test_shader_hlsl(stats, (shader_dir, relpath), update, keep, opt, force_no_external_validation)
test_shader_hlsl(stats, (args.folder, relpath), args.update, args.keep, args.opt, args.force_no_external_validation, paths)
elif backend == 'reflect':
test_shader_reflect(stats, (shader_dir, relpath), update, keep, opt)
test_shader_reflect(stats, (args.folder, relpath), args.update, args.keep, args.opt, paths)
else:
test_shader(stats, (shader_dir, relpath), update, keep, opt)
test_shader(stats, (args.folder, relpath), args.update, args.keep, args.opt, paths)
return None
except Exception as e:
return e
@ -589,9 +597,7 @@ def test_shaders_helper(stats, backend, args):
results = []
for f in all_files:
results.append(pool.apply_async(test_shader_file,
args = (f, stats,
args.folder, args.update, args.keep, args.opt, args.force_no_external_validation,
backend)))
args = (f, stats, args, backend)))
for res in results:
error = res.get()
@ -602,7 +608,7 @@ def test_shaders_helper(stats, backend, args):
sys.exit(1)
else:
for i in all_files:
e = test_shader_file(i, stats, args.folder, args.update, args.keep, args.opt, args.force_no_external_validation, backend)
e = test_shader_file(i, stats, args, backend)
if e is not None:
print('Error:', e)
sys.exit(1)
@ -649,6 +655,18 @@ def main():
parser.add_argument('--parallel',
action = 'store_true',
help = 'Execute tests in parallel. Useful for doing regression quickly, but bad for debugging and stat output.')
parser.add_argument('--glslang',
default = 'glslangValidator',
help = 'Explicit path to glslangValidator')
parser.add_argument('--spirv-as',
default = 'spirv-as',
help = 'Explicit path to spirv-as')
parser.add_argument('--spirv-val',
default = 'spirv-val',
help = 'Explicit path to spirv-val')
parser.add_argument('--spirv-opt',
default = 'spirv-opt',
help = 'Explicit path to spirv-opt')
args = parser.parse_args()
if not args.folder:

View File

@ -0,0 +1,161 @@
/* Smoke test for the C API. */
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <spirv_cross_c.h>
#include <stdlib.h>
#include <stdio.h>
#define SPVC_CHECKED_CALL(x) do { \
if ((x) != SPVC_SUCCESS) { \
fprintf(stderr, "Failed at line %d.\n", __LINE__); \
exit(1); \
} \
} while(0)
#define SPVC_CHECKED_CALL_NEGATIVE(x) do { \
g_fail_on_error = SPVC_FALSE; \
if ((x) == SPVC_SUCCESS) { \
fprintf(stderr, "Failed at line %d.\n", __LINE__); \
exit(1); \
} \
g_fail_on_error = SPVC_TRUE; \
} while(0)
static int read_file(const char *path, SpvId **buffer, size_t *word_count)
{
long len;
FILE *file = fopen(path, "rb");
if (!file)
return -1;
fseek(file, 0, SEEK_END);
len = ftell(file);
rewind(file);
*buffer = malloc(len);
if (fread(*buffer, 1, len, file) != (size_t)len)
{
fclose(file);
free(*buffer);
return -1;
}
fclose(file);
*word_count = len / sizeof(SpvId);
return 0;
}
static spvc_bool g_fail_on_error = SPVC_TRUE;
static void error_callback(void *userdata, const char *error)
{
(void)userdata;
if (g_fail_on_error)
{
fprintf(stderr, "Error: %s\n", error);
exit(1);
}
else
printf("Expected error hit: %s.\n", error);
}
static void dump_resource_list(spvc_compiler compiler, spvc_resources resources, spvc_resource_type type, const char *tag)
{
const spvc_reflected_resource *list = NULL;
size_t count = 0;
size_t i;
SPVC_CHECKED_CALL(spvc_resources_get_resource_list_for_type(resources, type, &list, &count));
printf("%s\n", tag);
for (i = 0; i < count; i++)
{
printf("ID: %u, BaseTypeID: %u, TypeID: %u, Name: %s\n", list[i].id, list[i].base_type_id, list[i].type_id,
list[i].name);
printf(" Set: %u, Binding: %u\n",
spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationDescriptorSet),
spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationBinding));
}
}
static void dump_resources(spvc_compiler compiler, spvc_resources resources)
{
dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, "UBO");
dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_STORAGE_BUFFER, "SSBO");
dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_PUSH_CONSTANT, "Push");
dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS, "Samplers");
dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_SEPARATE_IMAGE, "Image");
dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, "Combined image samplers");
dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_STAGE_INPUT, "Stage input");
dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_STAGE_OUTPUT, "Stage output");
dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_STORAGE_IMAGE, "Storage image");
dump_resource_list(compiler, resources, SPVC_RESOURCE_TYPE_SUBPASS_INPUT, "Subpass input");
}
static void compile(spvc_compiler compiler, const char *tag)
{
const char *result = NULL;
SPVC_CHECKED_CALL(spvc_compiler_compile(compiler, &result));
printf("\n%s\n=======\n", tag);
printf("%s\n=======\n", result);
}
int main(int argc, char **argv)
{
spvc_context context = NULL;
spvc_parsed_ir ir = NULL;
spvc_compiler compiler_glsl = NULL;
spvc_compiler compiler_hlsl = NULL;
spvc_compiler compiler_msl = NULL;
spvc_compiler compiler_cpp = NULL;
spvc_compiler compiler_json = NULL;
spvc_compiler compiler_none = NULL;
spvc_compiler_options options = NULL;
spvc_resources resources = NULL;
SpvId *buffer = NULL;
size_t word_count = 0;
if (argc != 2)
return 1;
if (read_file(argv[1], &buffer, &word_count) < 0)
return 1;
SPVC_CHECKED_CALL(spvc_context_create(&context));
spvc_context_set_error_callback(context, error_callback, NULL);
SPVC_CHECKED_CALL(spvc_context_parse_spirv(context, buffer, word_count, &ir));
SPVC_CHECKED_CALL(spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_COPY, &compiler_glsl));
SPVC_CHECKED_CALL(spvc_context_create_compiler(context, SPVC_BACKEND_HLSL, ir, SPVC_CAPTURE_MODE_COPY, &compiler_hlsl));
SPVC_CHECKED_CALL(spvc_context_create_compiler(context, SPVC_BACKEND_MSL, ir, SPVC_CAPTURE_MODE_COPY, &compiler_msl));
SPVC_CHECKED_CALL(spvc_context_create_compiler(context, SPVC_BACKEND_CPP, ir, SPVC_CAPTURE_MODE_COPY, &compiler_cpp));
SPVC_CHECKED_CALL(spvc_context_create_compiler(context, SPVC_BACKEND_JSON, ir, SPVC_CAPTURE_MODE_COPY, &compiler_json));
SPVC_CHECKED_CALL(spvc_context_create_compiler(context, SPVC_BACKEND_NONE, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler_none));
SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler_none, &options));
SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler_none, options));
SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler_json, &options));
SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler_json, options));
SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler_cpp, &options));
SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler_cpp, options));
SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler_msl, &options));
SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler_msl, options));
SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler_hlsl, &options));
SPVC_CHECKED_CALL(spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL, 50));
SPVC_CHECKED_CALL_NEGATIVE(spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_MSL_PLATFORM, 1));
SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler_hlsl, options));
SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler_glsl, &options));
SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler_glsl, options));
SPVC_CHECKED_CALL(spvc_compiler_create_shader_resources(compiler_none, &resources));
dump_resources(compiler_none, resources);
compile(compiler_glsl, "GLSL");
compile(compiler_hlsl, "HLSL");
compile(compiler_msl, "MSL");
compile(compiler_json, "JSON");
compile(compiler_cpp, "CPP");
spvc_context_destroy(context);
free(buffer);
return 0;
}

Binary file not shown.