├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── README.md ├── codegen_text.py ├── generate.py ├── include └── vulkan │ ├── vk_platform.h │ ├── vulkan.cpp │ ├── vulkan.h │ ├── vulkan_string.cpp │ └── vulkan_string.h ├── misc ├── ENUMS AND BITMASKS AND FLAGS, OH MY.xml └── discussion.md ├── module ├── vk_module.h ├── vk_module_interop.h └── vk_platform.h ├── sample ├── CMakeLists.txt ├── basic.cpp ├── shaders │ ├── frag.glsl │ ├── frag.spv │ ├── vert.glsl │ └── vert.spv ├── simple_triangle.cpp └── triangle.cpp └── test ├── CMakeLists.txt ├── fancy-api ├── CMakeLists.txt ├── flags_test.cpp ├── functions_test.cpp ├── main.cpp ├── static_asserts.h └── static_test.cpp └── simple-api ├── CMakeLists.txt └── main.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # Use defaults from the Google style with the following exceptions: 3 | BasedOnStyle: Mozilla 4 | IndentWidth: 4 5 | ColumnLimit: 132 6 | SortIncludes: false 7 | AlwaysBreakAfterDefinitionReturnType : None 8 | AlwaysBreakAfterReturnType : None 9 | ... 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.vscode 2 | *.vs 3 | registry 4 | build 5 | __pycache__ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 4 | # (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 5 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 6 | # so, subject to the following conditions: 7 | 8 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 11 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 12 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | 15 | cmake_minimum_required(VERSION 3.12 FATAL_ERROR) 16 | project(vulkan-module CXX) 17 | 18 | option(VK_MODULE_GENERATE_SOURCE "Get dependencies necessary for generating the bindings" off) 19 | option(VK_MODULE_SAMPLES "Build samples for the bindings, will download glfw" off) 20 | option(VK_MODULE_TESTS "Build tests for the bindings, will download catch2" off) 21 | option(VK_MODULE_ENABLE_ADDRESS_SANITIZER "Enable Address Sanitizer. Mainly for developers only" off) 22 | 23 | if (VK_MODULE_TESTS) 24 | option(SET_COMPILE_TIME_TRACE "enable clang's -ftime-trace " OFF) 25 | endif(VK_MODULE_TESTS) 26 | 27 | if (VK_MODULE_GENERATE_SOURCE OR VK_MODULE_SAMPLES OR VK_MODULE_TESTS) 28 | find_package(PythonInterp 3 QUIET) 29 | 30 | include(FetchContent) 31 | FetchContent_Declare( 32 | vulkan_headers 33 | GIT_REPOSITORY https://github.com/KhronosGroup/Vulkan-Headers 34 | GIT_TAG v1.2.194 35 | ) 36 | FetchContent_GetProperties(vulkan-headers) 37 | if(NOT vulkan_headers_POPULATED) 38 | FetchContent_Populate(vulkan_headers) 39 | add_subdirectory(${vulkan_headers_SOURCE_DIR} ${vulkan_headers_BINARY_DIR}) 40 | endif() 41 | add_custom_target(copy_source ALL 42 | COMMAND ${CMAKE_COMMAND} -E copy ${vulkan_headers_SOURCE_DIR}/registry/vk.xml ${CMAKE_CURRENT_LIST_DIR}/registry/vk.xml 43 | COMMAND ${CMAKE_COMMAND} -E copy ${vulkan_headers_SOURCE_DIR}/include/vulkan/vk_platform.h ${CMAKE_CURRENT_LIST_DIR}/include/vulkan/vk_platform.h 44 | COMMAND ${CMAKE_COMMAND} -E copy ${vulkan_headers_SOURCE_DIR}/include/vulkan/vk_platform.h ${CMAKE_CURRENT_LIST_DIR}/module/vk_platform.h) 45 | add_custom_target(generate_source 46 | COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/generate.py 47 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) 48 | add_dependencies(generate_source copy_source) 49 | 50 | endif() 51 | 52 | 53 | # Determine whether we're compiling with clang++ 54 | string(FIND "${CMAKE_CXX_COMPILER}" "clang++" VK_MODULE_COMPILER_CLANGPP) 55 | if(VK_MODULE_COMPILER_CLANGPP GREATER -1) 56 | set(VK_MODULE_COMPILER_CLANGPP 1) 57 | else() 58 | set(VK_MODULE_COMPILER_CLANGPP 0) 59 | endif() 60 | 61 | add_library(vk-module-compiler-options INTERFACE) 62 | target_compile_options(vk-module-compiler-options 63 | INTERFACE 64 | $<$,$,${VK_MODULE_COMPILER_CLANGPP}>: 65 | -Wall 66 | -Wextra 67 | -pedantic-errors 68 | -Wconversion 69 | -Wsign-conversion> 70 | $<$: 71 | /WX 72 | /W4> 73 | ) 74 | 75 | if (VK_MODULE_ENABLE_ADDRESS_SANITIZER) 76 | if (UNIX and not APPLE) 77 | target_compile_options(vk-module-compiler-options INTERFACE -fsanitize=address) 78 | target_link_options(vk-module-compiler-options INTERFACE -fsanitize=address) 79 | elseif(WIN32) 80 | target_compile_options(vk-module-compiler-options INTERFACE /fsanitize=address) 81 | target_link_options(vk-module-compiler-options INTERFACE /fsanitize=address) 82 | endif() 83 | endif() 84 | 85 | add_library(vk-platform-defines INTERFACE) 86 | target_compile_options(vk-platform-defines INTERFACE 87 | $<$:-DVK_USE_PLATFORM_WIN32_KHR> 88 | $<$:-DVK_USE_PLATFORM_ANDROID_KHR> 89 | $<$:-DVK_USE_PLATFORM_MACOS_MVK> 90 | $<$,$>>: 91 | $<$:-DVK_USE_PLATFORM_XCB_KHR> 92 | $<$:-DVK_USE_PLATFORM_XLIB_KHR> 93 | $<$:-DVK_USE_PLATFORM_WAYLAND_KHR>> 94 | ) 95 | 96 | add_library(vk-module-cpp INTERFACE) 97 | target_include_directories(vk-module-cpp INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include ${CMAKE_CURRENT_LIST_DIR}/module) 98 | target_link_libraries(vk-module-cpp INTERFACE vk-module-compiler-options) 99 | target_compile_features(vk-module-cpp INTERFACE cxx_std_20) 100 | 101 | if (NOT WIN32) 102 | target_link_libraries(vk-module-cpp INTERFACE ${CMAKE_DL_LIBS}) 103 | endif() 104 | 105 | add_library(vulkan-simple-cpp STATIC 106 | ${CMAKE_CURRENT_LIST_DIR}/include/vulkan/vulkan.h 107 | ${CMAKE_CURRENT_LIST_DIR}/include/vulkan/vulkan.cpp 108 | ${CMAKE_CURRENT_LIST_DIR}/include/vulkan/vulkan_string.h 109 | ${CMAKE_CURRENT_LIST_DIR}/include/vulkan/vulkan_string.cpp) 110 | target_include_directories(vulkan-simple-cpp BEFORE PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) 111 | target_include_directories(vulkan-simple-cpp PRIVATE ${CMAKE_CURRENT_LIST_DIR}/include/vulkan) 112 | target_link_libraries(vulkan-simple-cpp PUBLIC vk-module-compiler-options) 113 | target_compile_features(vulkan-simple-cpp PUBLIC cxx_std_11) 114 | set_target_properties(vulkan-simple-cpp PROPERTIES CXX_STANDARD 11) 115 | set_target_properties(vulkan-simple-cpp PROPERTIES LINKER_LANGUAGE CXX) 116 | 117 | if (NOT WIN32) 118 | target_link_libraries(vulkan-simple-cpp PUBLIC ${CMAKE_DL_LIBS}) 119 | endif() 120 | 121 | if (SET_COMPILE_TIME_TRACE) 122 | target_compile_options(vk-module-cpp INTERFACE -ftime-trace) 123 | target_compile_options(vulkan-simple-cpp PUBLIC -ftime-trace) 124 | endif(SET_COMPILE_TIME_TRACE) 125 | 126 | if (VK_MODULE_SAMPLES) 127 | add_subdirectory(sample) 128 | endif(VK_MODULE_SAMPLES) 129 | 130 | if (VK_MODULE_TESTS) 131 | add_subdirectory(test) 132 | endif (VK_MODULE_TESTS) 133 | 134 | 135 | if (VK_MODULE_GENERATE_SOURCE OR VK_MODULE_SAMPLES OR VK_MODULE_TESTS) 136 | add_dependencies(vulkan-simple-cpp generate_source) 137 | add_dependencies(vk-module-cpp generate_source) 138 | endif() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Charles Custom C++ Vulkan Bindings 2 | 3 | My (WIP) C++ bindings for the Vulkan Graphics API. These replace the existing C and C++ headers, so they are completely independent. 4 | 5 | ## Contents 6 | 7 | Currently there are projects 8 | 9 | * vk-simple - C++ API that is like the C API with a handful of QoL improvements 10 | * vk-module - (experimental) A Vulkan C++20 Module, and a different take on `vulkan.hpp` 11 | 12 | ### vk-simple 13 | 14 | NOTE: This is unfinished and not suitable for production use. Reasons: 15 | 16 | * Doesn't compile because types from system headers aren't handled. 17 | * Not updated to 1.2.170 which added 64 bit flags. 18 | * No test suite to verify code quality. Will need CI setup as well. 19 | 20 | Takes the current C headers and adds a few quality of life improvements while staying faithful to the original C API. 21 | 22 | Improvements: 23 | 24 | * Enums and Bitfields use enum classes, makes it impossible to use the wrong enum. 25 | * sType parameters are initialized to their correct value. 26 | * Structs always initialize all values to zero (except sType). Prevents uninitialized memory bugs. 27 | * Add an API to automatically load function pointers with the following benefits: 28 | * Function pointers are known to be faster than using the exported Vulkan functions. 29 | * No compile time dependency on the Vulkan-Loader. It is loaded at runtime instead. 30 | * Automatically querying function pointers from extensions 31 | * Removes the need to use an external library such as [volk](https://github.com/zeux/volk) to accomplish this task 32 | 33 | Notes: 34 | 35 | * This aims to be a production ready solution 36 | * The goal of these bindings is to bridge the gap between the simplicity of the C API and the type safety of `vulkan.hpp`. 37 | * I am not planning on adding additional features, but am open to simple QoL suggestions. 38 | 39 | ### vk-module 40 | 41 | NOTE: This is considered experimental and not suitable for production use. 42 | 43 | `vulkan.hpp` offers many useful features while also containing several drawbacks. These bindings are my attempt at creating an expressive and convenient Vulkan interface in C++ while addressing what I consider to be `vulkan.hpp`s shortcomings. While no API is perfect, `vulkan.hpp` is a vast improvement over the C API in developer quality of life and safety, which should not be understated. 44 | 45 | The main concerns I have with `vulkan.hpp` are: 46 | 47 | 1. Poor function pointer loading interface & implementation 48 | 2. Slow compile times 49 | 3. Lack of (perceived) stability 50 | 4. Single file supports multiple C++ standards 51 | 52 | None of these are deal breakers and as of writing the `vulkan.hpp` developers are exploring solutions to the most egregious in the list, slow compile times. 53 | For further details about why these are a problem and how I address them, look at the `discussion.md` in the misc folder. 54 | 55 | #### Main differences between `vk-module` and `vulkan.hpp` 56 | 57 | * C++20 only. No support for older standards. Features like modules, concepts, and three-way comparison aren't behind a macro guard. 58 | * Streamlined function loading interface 59 | * Only one way to load and use functions which is easier to explain and doesn't have any performance overhead. 60 | * Supports multi-gpu without changing all the underlying code. (unless you chose to manually include the dispatcher in every function call) 61 | * Use `Expected` for error handling instead of exceptions. Functions which return `VkResult` in the C API now return `Expected` and contain either the desired value or a `vk::Result` 62 | * Independence from the C-API. Types are binary compatible with their C counterparts, allowing the API to work without also including `vulkan.h` first. 63 | * Because many popular libraries use a C interface, the `vk_module_interop.h` header exists to bridge the gap between the two. Currently it handles conversions between enums, bitfields, and handles. 64 | * Because of issues with forward declaration, handles are defined in the `vk_module.h` and is the only C API currently required for interop to work. 65 | * Address Sanitizer (ASAN) suppression is built into the API. Some drivers are known to create false positives in ASAN which spam the log with a huge volume of reports. By selectively disabling ASAN before calling into the Vulkan API and then re-enabling it after, `vk-module` eliminates this headache. 66 | * A much more opinionated API. There should be one way to do something and the API shouldn't be afraid to assert a 'correct' idea of how to use the API. The benefit of this is a smaller API that strives to be self-consistent. The hidden benefit is that the implementation is significantly simpler and shorter. `vulkan.hpp` is well over 90k lines of code, `vk_module.h` is 20k. 67 | * Aim for solutions that give 80% of the benefit at 20% the cost, such as templates. They solve problems but also introduce more compilation overhead, so a conscious effort was made to use alternative solutions when it made sense. 68 | 69 | #### Known Issues 70 | 71 | * Does not support platform specific WSI related functions. An application must use SDL, GLFW, or equivalent library to create a VkSurfaceKHR 72 | * `Expected` is a homebrew solution and making error handling enjoyable in vulkan is tough. May change this later on 73 | * Dependence on many standard libraries. No plan to remove or change this. Creating replacements for those standard library features just adds possible places for subtle bugs while introduction friction. (Ie, some say this is an issue, but I see the costs outweighing the benefits) 74 | * pNext chains have no helper code. Need to write the StructuredChain replacement. 75 | * API is big, fat, and wide. Very likely there are inconsistencies and errors lurking in the shadows. 76 | 77 | ## History 78 | 79 | Work began on this project in April 2020. These bindings started for 2 main reasons. 80 | 81 | * C++20 has just been finalized which contains a new way to manage C++ code, modules. 82 | * `vulkan.hpp` is probably the vulkan tool/project which almost everyone knows about and has the biggest range of opinions, from staunch supporters to dug-in detractors. 83 | 84 | The discussion in various online chat groups led me to believe there may be a way to kill two birds with one stone, write C++20 module that are better Vulkan C++ bindings. 85 | 86 | With that in mind, Work began on this project in April 2020. Over the spring and summer, the `generator.py` script was written to parse `vk.xml` and generate the `vk-module` bindings. It was a great learning project as it involved exploring python in detail and the xml document. 87 | 88 | As the project currently stands (April 2021), I didn't know how wrong I was. Firstly, modules are still experimental or very recently released in C++20 compilers. Suffice to say, they aren't ready for primetime. Secondly, `vulkan.hpp`'s problems are SIGNIFICANTLY more complicated than I could of imagined. There are many concerns the developers must balance: performance, safety, implementation complexity, interface simplicity, interoperability with the C API, then finding out just how little room there is to work with. Not to mention the moving target that is `vk.xml` which changes and breaks previous assumptions about what the bindings API design space is. This isn't to say these challenges aren't insurmountable, but is incredibly difficult to find a balance that doesn't leave me defeated in some aspect. 89 | -------------------------------------------------------------------------------- /codegen_text.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 4 | # (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 5 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 6 | # so, subject to the following conditions: 7 | 8 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 11 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 12 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | 15 | license_header = '''/* 16 | * Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 17 | * 18 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 19 | * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 20 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 21 | * furnished to do so, subject to the following conditions: 22 | * 23 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 24 | * 25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 26 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 28 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | */ 30 | ''' 31 | 32 | vk_module_file_header = ''' 33 | // clang-format off 34 | #pragma once 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include "vk_platform.h" 46 | 47 | #if defined(VK_USE_PLATFORM_FUCHSIA) 48 | #include 49 | #endif 50 | 51 | #if defined(VK_USE_PLATFORM_WAYLAND_KHR) 52 | #include 53 | #endif 54 | 55 | #if defined(VK_USE_PLATFORM_XCB_KHR) 56 | #include 57 | #endif 58 | 59 | #if defined(VK_USE_PLATFORM_XLIB_KHR) 60 | #include 61 | #endif 62 | 63 | #if defined(VK_USE_PLATFORM_DIRECTFB_EXT) 64 | #include 65 | #endif 66 | 67 | #if defined(VK_USE_PLATFORM_XLIB_XRANDR_EXT) 68 | #include 69 | #include 70 | #endif 71 | 72 | #if defined(VK_USE_PLATFORM_GGP) 73 | #include 74 | #endif 75 | 76 | // Compatability with compilers that don't support __has_feature 77 | #if !defined(__has_feature) 78 | #define __has_feature(x) 0 79 | #endif 80 | 81 | #if !defined(VK_MODULE_DISABLE_LEAK_SANITIZER_SUPPRESSION) && (__has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) ) 82 | #include 83 | #define VK_MODULE_LEAK_SANITIZER_SUPPRESSION_CODE __lsan::ScopedDisabler lsan_scope{}; 84 | #else 85 | #define VK_MODULE_LEAK_SANITIZER_SUPPRESSION_CODE 86 | #endif 87 | 88 | #if !defined(__has_feature) 89 | #undef __has_feature 90 | #endif 91 | 92 | #if !defined(VULKAN_CUSTOM_ASSERT) 93 | #include 94 | #define VULKAN_CUSTOM_ASSERT assert 95 | #endif 96 | 97 | namespace vk { 98 | constexpr uint32_t make_vk_version(uint32_t major, uint32_t minor, uint32_t patch) { 99 | return major << 22 | minor << 12 | patch; 100 | } 101 | 102 | #if !defined(VK_DEFINE_HANDLE) 103 | #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; 104 | #endif 105 | 106 | #if !defined(VK_DEFINE_NON_DISPATCHABLE_HANDLE) 107 | #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) 108 | #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; 109 | #else 110 | #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; 111 | #endif 112 | #endif 113 | ''' 114 | 115 | custom_data_structures = ''' 116 | /* Return type for Vulkan Module API functions which return a value or values 117 | * Holds a T value or a vk::Result for indicating the error 118 | * Do not use with success codes other than zero 119 | * Example in function definition 120 | * vk::expected CreateBuffer(const BufferCreateInfo& pCreateInfo, const AllocationCallbacks* pAllocator = nullptr) { ... } 121 | * Example usage: 122 | * auto buffer_return = CreateBuffer( ... ); 123 | * if (!buffer_return) 124 | * error_exit("Failed to create buffer", buffer_return.error()); 125 | * vk::Buffer buffer = buffer_return.value(); //Get value now that we've check for errors 126 | */ 127 | template 128 | struct expected { 129 | explicit expected (T const& value, Result result) noexcept: _value{ value}, _result{ result } {} 130 | explicit expected (T&& value, Result result) noexcept: _value{ std::move(value) }, _result{ result } {} 131 | 132 | const T* operator-> () const noexcept { VULKAN_CUSTOM_ASSERT (_result == Result::Success); return &_value; } 133 | T* operator-> () noexcept { VULKAN_CUSTOM_ASSERT (_result == Result::Success); return &_value; } 134 | const T& operator* () const& noexcept { VULKAN_CUSTOM_ASSERT (_result == Result::Success); return _value; } 135 | T& operator* () & noexcept { VULKAN_CUSTOM_ASSERT (_result == Result::Success); return _value; } 136 | T&& operator* () && noexcept { VULKAN_CUSTOM_ASSERT (_result == Result::Success); return std::move (_value); } 137 | const T& value () const& noexcept { VULKAN_CUSTOM_ASSERT (_result == Result::Success); return _value; } 138 | T& value () & noexcept { VULKAN_CUSTOM_ASSERT (_result == Result::Success); return _value; } 139 | const T&& value () const&& noexcept { VULKAN_CUSTOM_ASSERT (_result == Result::Success); return std::move(_value); } 140 | T&& value () && noexcept { VULKAN_CUSTOM_ASSERT (_result == Result::Success); return std::move(_value); } 141 | 142 | Result error() const noexcept { VULKAN_CUSTOM_ASSERT (_result != Result::Success); return _result; } 143 | Result raw_result() const noexcept { return _result; } 144 | 145 | bool has_value () const noexcept { return _result == Result::Success; } 146 | explicit operator bool () const noexcept { return _result == Result::Success; } 147 | bool operator!() const noexcept { return _result != Result::Success; } 148 | 149 | template 150 | auto const& get() const noexcept { 151 | if constexpr (N == 0) return _value; 152 | else if constexpr (N == 1) return _result; 153 | } 154 | template 155 | auto& get() noexcept { 156 | if constexpr (N == 0) return _value; 157 | else if constexpr (N == 1) return _result; 158 | } 159 | 160 | private: 161 | T _value; 162 | Result _result = Result::Success; 163 | }; 164 | 165 | } //namespace vk 166 | 167 | namespace std { 168 | template 169 | struct tuple_size>: std::integral_constant {}; 170 | template struct tuple_element<0, vk::expected> { using type = T; }; 171 | template struct tuple_element<1, vk::expected> { using type = vk::Result; }; 172 | } 173 | 174 | namespace vk { 175 | namespace detail { 176 | template 177 | class span { 178 | public: 179 | constexpr span() noexcept = default; 180 | constexpr span(T const& value) noexcept : _data{ std::addressof(const_cast(value)) }, _count{1} {} 181 | constexpr explicit span(T* data, uint32_t count) noexcept : _data{data}, _count{count} {} 182 | 183 | // requires std::data(Range const&) 184 | // requires std::size(Range const&) 185 | template 186 | explicit span(Range const& range) noexcept : 187 | _data{std::data(range)}, _count{std::size(range)} {} 188 | 189 | template< std::size_t N > 190 | span(std::array& arr ) noexcept : 191 | _data{std::data(arr)}, _count{std::size(arr)} {} 192 | 193 | template< std::size_t N > 194 | span(std::array const& arr ) noexcept : 195 | _data{std::data(arr)}, _count{std::size(arr)} {} 196 | 197 | span( std::initializer_list& data ) noexcept : 198 | _data{data.begin()}, _count{static_cast(data.size())} {} 199 | 200 | span( std::initializer_list const& data ) noexcept : 201 | _data{data.begin()}, _count{static_cast(data.size())} {} 202 | 203 | template ::value, int>::type = 0> 204 | span( std::initializer_list::type> const& data ) noexcept : 205 | _data{data.begin()}, _count{static_cast(data.size())} {} 206 | 207 | template ::value, int>::type = 0> 208 | span( std::initializer_list::type> & data ) noexcept : 209 | _data{data.begin()}, _count{static_cast(data.size())} {} 210 | 211 | [[nodiscard]] uint32_t size() noexcept { return _count; } 212 | [[nodiscard]] uint32_t size() const noexcept { return _count; } 213 | [[nodiscard]] uint32_t size_bytes() const noexcept { return _count * sizeof(T); } 214 | 215 | [[nodiscard]] T* data() noexcept { return _data; } 216 | [[nodiscard]] const T* data() const noexcept { return _data; } 217 | [[nodiscard]] bool empty() const noexcept { return _count == 0; } 218 | 219 | [[nodiscard]] T const& operator[](uint32_t count) & noexcept { return _data[count]; } 220 | [[nodiscard]] T const& operator[](uint32_t count) const& noexcept { return _data[count]; } 221 | 222 | [[nodiscard]] const T* begin() const noexcept { return _data + 0; } 223 | [[nodiscard]] const T* begin() noexcept { return _data + 0; } 224 | [[nodiscard]] const T* end() const noexcept { return _data + _count; } 225 | [[nodiscard]] const T* end() noexcept { return _data + _count; } 226 | private: 227 | T* _data; 228 | uint32_t _count; 229 | }; 230 | } // namespace detail 231 | 232 | // Unique Handle wrapper for RAII handle types 233 | // DispatchableHandle is a `VkInstance` or `VkDevice` handle 234 | // HandleType is a `vk::Handle` type 235 | // Delete is a PFN_vk*** function that matches the desired type 236 | template 237 | class unique_handle 238 | { 239 | public: 240 | unique_handle() = default; 241 | explicit unique_handle(DispatchableHandle dispatch_handle, HandleType handle, Deleter deleter) noexcept 242 | : dispatch_handle(dispatch_handle), handle(handle), deleter(deleter) { } 243 | ~unique_handle() noexcept { reset(); }; 244 | unique_handle(unique_handle const& other) = delete; 245 | unique_handle& operator=(unique_handle const& other) = delete; 246 | 247 | unique_handle(unique_handle&& other) noexcept 248 | : dispatch_handle{other.dispatch_handle}, 249 | handle{std::exchange(other.handle, nullptr)}, 250 | deleter{other.deleter} { } 251 | unique_handle& operator=(unique_handle&& other) noexcept 252 | { 253 | if (this != &other) 254 | { 255 | reset(); 256 | dispatch_handle = other.dispatch_handle; 257 | handle = std::exchange(other.handle, {}); 258 | deleter = other.deleter; 259 | } 260 | return *this; 261 | } 262 | 263 | HandleType release() noexcept { 264 | return std::exchange(handle, {}); 265 | } 266 | 267 | void reset() noexcept { 268 | if (handle) 269 | { 270 | deleter(dispatch_handle, handle.get(), nullptr); 271 | } 272 | } 273 | 274 | const HandleType address() const noexcept { 275 | return std::addressof(handle.get()); 276 | } 277 | 278 | HandleType operator*() const noexcept { 279 | return handle; 280 | } 281 | 282 | [[nodiscard]] HandleType get() const noexcept { return handle; } 283 | 284 | HandleType operator->() const noexcept { return handle; } 285 | 286 | explicit operator bool() const noexcept { return handle; } 287 | 288 | DispatchableHandle dispatch_handle; 289 | HandleType handle; 290 | Deleter deleter; 291 | }; 292 | 293 | // Add special case for VkInstance 294 | ''' 295 | 296 | bitmask_flags_macro = ''' 297 | #define DECLARE_ENUM_FLAG_OPERATORS(FLAG_TYPE, FLAG_BITS, BASE_TYPE) \\ 298 | \\ 299 | struct FLAG_TYPE { \\ 300 | BASE_TYPE flags = static_cast(0); \\ 301 | \\ 302 | constexpr FLAG_TYPE() noexcept = default; \\ 303 | constexpr FLAG_TYPE(BASE_TYPE in) noexcept: flags(in){ } \\ 304 | constexpr FLAG_TYPE(FLAG_BITS in) noexcept: flags(static_cast(in)){ } \\ 305 | constexpr bool operator==(FLAG_TYPE const& right) const { return flags == right.flags;}\\ 306 | constexpr bool operator!=(FLAG_TYPE const& right) const { return flags != right.flags;}\\ 307 | constexpr explicit operator BASE_TYPE() const { return flags;} \\ 308 | constexpr explicit operator bool() const noexcept { \\ 309 | return flags != 0; \\ 310 | } \\ 311 | }; \\ 312 | constexpr FLAG_TYPE operator|(FLAG_TYPE a, FLAG_TYPE b) noexcept { \\ 313 | return static_cast(a.flags | b.flags); \\ 314 | } \\ 315 | constexpr FLAG_TYPE operator&(FLAG_TYPE a, FLAG_TYPE b) noexcept { \\ 316 | return static_cast(a.flags & b.flags); \\ 317 | } \\ 318 | constexpr FLAG_TYPE operator^(FLAG_TYPE a, FLAG_TYPE b) noexcept { \\ 319 | return static_cast(a.flags ^ b.flags); \\ 320 | } \\ 321 | constexpr FLAG_TYPE operator~(FLAG_TYPE a) noexcept { \\ 322 | return static_cast(~a.flags); \\ 323 | } \\ 324 | constexpr FLAG_TYPE& operator|=(FLAG_TYPE& a, FLAG_TYPE b) noexcept { \\ 325 | return a.flags = (a.flags | b.flags), a; \\ 326 | } \\ 327 | constexpr FLAG_TYPE& operator&=(FLAG_TYPE& a, FLAG_TYPE b) noexcept { \\ 328 | return a.flags = (a.flags & b.flags), a; \\ 329 | } \\ 330 | constexpr FLAG_TYPE operator^=(FLAG_TYPE& a, FLAG_TYPE b) noexcept { \\ 331 | return a.flags = (a.flags ^ b.flags), a; \\ 332 | } \\ 333 | constexpr FLAG_TYPE operator|(FLAG_BITS a, FLAG_BITS b) noexcept { \\ 334 | return static_cast(static_cast(a) | static_cast(b)); \\ 335 | } \\ 336 | constexpr FLAG_TYPE operator&(FLAG_BITS a, FLAG_BITS b) noexcept { \\ 337 | return static_cast(static_cast(a) & static_cast(b)); \\ 338 | } \\ 339 | constexpr FLAG_TYPE operator~(FLAG_BITS key) noexcept { \\ 340 | return static_cast(~static_cast(key)); \\ 341 | } \\ 342 | constexpr FLAG_TYPE operator^(FLAG_BITS a, FLAG_BITS b) noexcept { \\ 343 | return static_cast(static_cast(a) ^ static_cast(b)); \\ 344 | } \\ 345 | ''' 346 | vulkan_library_text = ''' 347 | } // namespace vk 348 | #if defined(_WIN32) 349 | typedef struct HINSTANCE__ * HINSTANCE; 350 | #if defined( _WIN64 ) 351 | typedef int64_t( __stdcall * FARPROC )(); 352 | #else 353 | typedef int( __stdcall * FARPROC )(); 354 | #endif 355 | extern "C" __declspec( dllimport ) HINSTANCE __stdcall LoadLibraryA( char const * lpLibFileName ); 356 | extern "C" __declspec( dllimport ) int __stdcall FreeLibrary( HINSTANCE hLibModule ); 357 | extern "C" __declspec( dllimport ) FARPROC __stdcall GetProcAddress( HINSTANCE hModule, const char * lpProcName ); 358 | #elif defined(__linux__) || defined(__APPLE__) 359 | #include 360 | #endif 361 | using PFN_vkVoidFunction = void (VKAPI_PTR *)(void); 362 | using PFN_vkGetInstanceProcAddr = PFN_vkVoidFunction (VKAPI_PTR *)(VkInstance instance, const char* pName); 363 | namespace vk { 364 | class DynamicLibrary { 365 | public: 366 | // Used to enable RAII vk::DynamicLibrary behavior 367 | struct LoadAtConstruction {}; 368 | 369 | explicit DynamicLibrary() noexcept {} 370 | explicit DynamicLibrary([[maybe_unused]] LoadAtConstruction load) noexcept { 371 | init(); 372 | } 373 | explicit DynamicLibrary(PFN_vkGetInstanceProcAddr pfn_vkGetInstanceProcAddr) noexcept : 374 | get_instance_proc_addr(pfn_vkGetInstanceProcAddr) { } 375 | ~DynamicLibrary() noexcept { 376 | close(); 377 | } 378 | DynamicLibrary(DynamicLibrary const& other) = delete; 379 | DynamicLibrary& operator=(DynamicLibrary const& other) = delete; 380 | DynamicLibrary(DynamicLibrary && other) noexcept: library(other.library), get_instance_proc_addr(other.get_instance_proc_addr) { 381 | other.get_instance_proc_addr = 0; 382 | other.library = 0; 383 | } 384 | DynamicLibrary& operator=(DynamicLibrary && other) noexcept { 385 | if (this != &other) 386 | { 387 | close(); 388 | library = other.library; 389 | get_instance_proc_addr = other.get_instance_proc_addr; 390 | other.get_instance_proc_addr = 0; 391 | other.library = 0; 392 | } 393 | return *this; 394 | } 395 | 396 | Result init(PFN_vkGetInstanceProcAddr pfn_vkGetInstanceProcAddr = nullptr) noexcept { 397 | if (pfn_vkGetInstanceProcAddr != nullptr) { 398 | get_instance_proc_addr = pfn_vkGetInstanceProcAddr; 399 | return Result::Success; 400 | } 401 | #if defined(__linux__) 402 | library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL); 403 | if (!library) library = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL); 404 | #elif defined(__APPLE__) 405 | library = dlopen("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL); 406 | #elif defined(_WIN32) 407 | library = ::LoadLibraryA("vulkan-1.dll"); 408 | #endif 409 | if (library == 0) return Result::ErrorInitializationFailed; 410 | Load(get_instance_proc_addr, "vkGetInstanceProcAddr"); 411 | if (get_instance_proc_addr == nullptr) return Result::ErrorInitializationFailed; 412 | return Result::Success; 413 | } 414 | void close() noexcept { 415 | if (library != nullptr) { 416 | #if defined(__linux__) || defined(__APPLE__) 417 | dlclose(library); 418 | #elif defined(_WIN32) 419 | ::FreeLibrary(library); 420 | #endif 421 | library = 0; 422 | } 423 | } 424 | 425 | // Check if vulkan is loaded and ready for use 426 | [[nodiscard]] bool is_init() const noexcept { return get_instance_proc_addr != 0; } 427 | 428 | // Get `vkGetInstanceProcAddr` if it was loaded, 0 if not 429 | [[nodiscard]] PFN_vkGetInstanceProcAddr get() const noexcept { 430 | VULKAN_CUSTOM_ASSERT(get_instance_proc_addr != nullptr && "Must call init() before use"); 431 | return get_instance_proc_addr; 432 | } 433 | 434 | private: 435 | 436 | template 437 | void Load(T &func_dest, const char *func_name) { 438 | #if defined(__linux__) || defined(__APPLE__) 439 | func_dest = reinterpret_cast(dlsym(library, func_name)); 440 | #elif defined(_WIN32) 441 | func_dest = reinterpret_cast(::GetProcAddress(library, func_name)); 442 | #endif 443 | } 444 | 445 | #if defined(__linux__) || defined(__APPLE__) 446 | void *library = nullptr; 447 | #elif defined(_WIN32) 448 | ::HINSTANCE library = nullptr; 449 | #endif 450 | 451 | PFN_vkGetInstanceProcAddr get_instance_proc_addr = nullptr; 452 | 453 | }; 454 | 455 | ''' 456 | 457 | vulkan_simple_cpp_header_guard = ''' 458 | // clang-format off 459 | #ifndef SIMPLE_VULKAN_H_ 460 | #ifdef VULKAN_H_ 461 | #error "Must include these bindings first, not official C vulkan headers (vulkan.h)" 462 | #endif 463 | #endif 464 | #ifndef VULKAN_H_ 465 | #define VULKAN_H_ 1 466 | #define SIMPLE_VULKAN_H_ 1 467 | 468 | #define VK_VERSION_1_0 1 469 | #define VK_VERSION_1_1 1 470 | #define VK_VERSION_1_2 1 471 | #include 472 | #include "vk_platform.h" 473 | ''' 474 | 475 | vulkan_simple_cpp_footer = ''' 476 | #if defined(VULKAN_CPP_IMPLEMENTATION) 477 | #include "vulkan.cpp" 478 | #endif //defined(VULKAN_CPP_IMPLEMENTATION) 479 | 480 | #endif // VULKAN_H_ 481 | // clang-format on 482 | ''' 483 | 484 | vulkan_simple_cpp_platform_headers = ''' 485 | #if defined(VK_USE_PLATFORM_FUCHSIA) 486 | #include 487 | #endif 488 | 489 | #if defined(VK_USE_PLATFORM_WAYLAND_KHR) 490 | #include 491 | #endif 492 | 493 | #if defined(VK_USE_PLATFORM_XCB_KHR) 494 | #include 495 | #endif 496 | 497 | #if defined(VK_USE_PLATFORM_XLIB_KHR) 498 | #include 499 | #endif 500 | 501 | #if defined(VK_USE_PLATFORM_DIRECTFB_EXT) 502 | #include 503 | #endif 504 | 505 | #if defined(VK_USE_PLATFORM_XLIB_XRANDR_EXT) 506 | #include 507 | #include 508 | #endif 509 | 510 | #if defined(VK_USE_PLATFORM_GGP) 511 | #include 512 | #endif 513 | ''' 514 | 515 | vulkan_simple_cpp_footer = ''' 516 | // This function finds the Vulkan-Loader (vulkan-1.dll, libvulkan.so, libvulkan.dylib, etc) on a system, loads it, 517 | // and loads the follwing functions: 518 | // * vkGetInstanceProcAddr 519 | // * vkCreateInstance 520 | // * vkEnumerateInstanceExtensionProperties 521 | // * vkEnumerateInstanceLayerProperties 522 | // * vkEnumerateInstanceVersion 523 | // 524 | // Note: 525 | // This function must be called before all other vulkan calls! 526 | // 527 | // Return Codes: 528 | // VkResult::Success -- Successful initialization & loading of functions 529 | // VkResult::ErrorInitializationFailed -- failure [unable to find Vulkan-Loader] 530 | // 531 | // Optional Parameter: 532 | // PFN_vkGetInstanceProcAddr pfn_vkGetInstanceProcAddr = VK_NULL_HANDLE 533 | VkResult vkInitializeLoaderLibrary(PFN_vkGetInstanceProcAddr pfn_vkGetInstanceProcAddr = VK_NULL_HANDLE); 534 | 535 | // Close the Vulkan-Loader and assigns VK_NULL_HANDLE to vkGetInstanceProcAddr 536 | // 537 | // Note: 538 | // After this function is called, no further vulkan calls can be made, except for `vkInitializeLoaderLibrary()` 539 | void vkCloseLoaderLibrary(); 540 | 541 | // Initialize the instance and physical device functions into the global function pointers 542 | // (all functions which take a VkInstance or VkPhysicalDevice as the first parameter) 543 | // 544 | // Note: This must only be called after the application has created a valid VkInstance with vkCreateInstance 545 | // 546 | // Parameter: 547 | // VkInstance instance 548 | // The VkInstance handle which the application has created. Must not be VK_NULL_HANDLE 549 | void vkInitializeInstanceFunctions(VkInstance instance); 550 | 551 | // Loads device functions into the global function pointers 552 | // 553 | // Notes: 554 | // * This function must not be used for any application which creates multiple VkDevices. 555 | // Instead, the application should use a VkDeviceDispatchTable per device created. 556 | // * This must only be called after the application has created a valid VkDevice with vkCreateDevice 557 | // 558 | // Parameter: 559 | // VkDevice device 560 | // The VkDevice handle which the application has created. Must not be VK_NULL_HANDLE 561 | void vkInitializeGlobalDeviceFunctions(VkDevice device); 562 | 563 | // Loads device functions into the provided VkDeviceDispatchTable 564 | // 565 | // Notes: 566 | // * 567 | // * This must only be called after the application has created a valid VkDevice with vkCreateDevice 568 | // 569 | // Parameters: 570 | // * VkDevice device 571 | // The VkDevice handle which the application has created. Must not be VK_NULL_HANDLE 572 | // * VkDeviceDispatchTable& table 573 | // The table in which holds all loaded device function pointers. 574 | void vkInitializeDeviceDispatchTable(VkDevice device, VkDeviceDispatchTable& table); 575 | 576 | #ifdef __cplusplus 577 | } // extern "C" 578 | #endif 579 | 580 | #if defined(VULKAN_CPP_IMPLEMENTATION) 581 | #include "vulkan.cpp" 582 | #endif //defined(VULKAN_CPP_IMPLEMENTATION) 583 | 584 | #endif // VULKAN_H_ 585 | // clang-format on 586 | ''' 587 | 588 | vulkan_simple_cpp_definition = ''' 589 | 590 | #if defined(_WIN32) 591 | using HINSTANCE = struct HINSTANCE__ *; 592 | #if defined( _WIN64 ) 593 | using FARPROC = int64_t( __stdcall * )(); 594 | #else 595 | using FARPROC = typedef int( __stdcall * )(); 596 | #endif 597 | extern "C" __declspec( dllimport ) HINSTANCE __stdcall LoadLibraryA( char const * lpLibFileName ); 598 | extern "C" __declspec( dllimport ) int __stdcall FreeLibrary( HINSTANCE hLibModule ); 599 | extern "C" __declspec( dllimport ) FARPROC __stdcall GetProcAddress( HINSTANCE hModule, const char * lpProcName ); 600 | #elif defined(__linux__) || defined(__APPLE__) 601 | #include 602 | #endif 603 | 604 | #if defined(__linux__) || defined(__APPLE__) 605 | void *library = nullptr; 606 | #elif defined(_WIN32) 607 | ::HINSTANCE library = nullptr; 608 | #endif 609 | 610 | void* LibraryLoadFunction(const char* name) { 611 | #if defined(__linux__) || defined(__APPLE__) 612 | return dlsym(library, name); 613 | #elif defined(_WIN32) 614 | return ::GetProcAddress(library, name); 615 | #endif 616 | } 617 | void LoadGetInstanceProcAddr(){ 618 | vkGetInstanceProcAddr = reinterpret_cast(LibraryLoadFunction("vkGetInstanceProcAddr")); 619 | } 620 | void LoadGlobalFunctions() { 621 | if (vkGetInstanceProcAddr == VK_NULL_HANDLE) return; 622 | vkCreateInstance = reinterpret_cast(vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance")); 623 | vkEnumerateInstanceExtensionProperties = reinterpret_cast(vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties")); 624 | vkEnumerateInstanceLayerProperties = reinterpret_cast(vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceLayerProperties")); 625 | vkEnumerateInstanceVersion = reinterpret_cast(vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkEnumerateInstanceVersion")); 626 | } 627 | 628 | VkResult vkInitializeLoaderLibrary(PFN_vkGetInstanceProcAddr pfn_vkGetInstanceProcAddr){ 629 | if(pfn_vkGetInstanceProcAddr != VK_NULL_HANDLE){ 630 | vkGetInstanceProcAddr = pfn_vkGetInstanceProcAddr; 631 | LoadGlobalFunctions(); 632 | return VkResult::Success; 633 | } 634 | 635 | #if defined(__linux__) 636 | library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL); 637 | if (!library) library = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL); 638 | #elif defined(__APPLE__) 639 | library = dlopen("libvulkan.dylib", RTLD_NOW | RTLD_LOCAL); 640 | #elif defined(_WIN32) 641 | library = ::LoadLibraryA("vulkan-1.dll"); 642 | #endif 643 | if (library == 0) return VkResult::ErrorInitializationFailed; 644 | LoadGetInstanceProcAddr(); 645 | LoadGlobalFunctions(); 646 | if (vkGetInstanceProcAddr == nullptr) return VkResult::ErrorInitializationFailed; 647 | return VkResult::Success; 648 | } 649 | 650 | void vkCloseLoaderLibrary(){ 651 | if (library != nullptr) { 652 | #if defined(__linux__) || defined(__APPLE__) 653 | dlclose(library); 654 | #elif defined(_WIN32) 655 | ::FreeLibrary(library); 656 | #endif 657 | library = 0; 658 | vkGetInstanceProcAddr = VK_NULL_HANDLE; 659 | } 660 | } 661 | ''' 662 | 663 | begin_extern_c = ''' 664 | #ifdef __cplusplus 665 | extern "C" { 666 | #endif 667 | ''' 668 | 669 | end_extern_c = ''' 670 | #ifdef __cplusplus 671 | } // extern "C" 672 | #endif 673 | ''' -------------------------------------------------------------------------------- /include/vulkan/vk_platform.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: vk_platform.h 3 | // 4 | /* 5 | ** Copyright 2014-2021 The Khronos Group Inc. 6 | ** 7 | ** SPDX-License-Identifier: Apache-2.0 8 | */ 9 | 10 | 11 | #ifndef VK_PLATFORM_H_ 12 | #define VK_PLATFORM_H_ 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif // __cplusplus 18 | 19 | /* 20 | *************************************************************************************************** 21 | * Platform-specific directives and type declarations 22 | *************************************************************************************************** 23 | */ 24 | 25 | /* Platform-specific calling convention macros. 26 | * 27 | * Platforms should define these so that Vulkan clients call Vulkan commands 28 | * with the same calling conventions that the Vulkan implementation expects. 29 | * 30 | * VKAPI_ATTR - Placed before the return type in function declarations. 31 | * Useful for C++11 and GCC/Clang-style function attribute syntax. 32 | * VKAPI_CALL - Placed after the return type in function declarations. 33 | * Useful for MSVC-style calling convention syntax. 34 | * VKAPI_PTR - Placed between the '(' and '*' in function pointer types. 35 | * 36 | * Function declaration: VKAPI_ATTR void VKAPI_CALL vkCommand(void); 37 | * Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void); 38 | */ 39 | #if defined(_WIN32) 40 | // On Windows, Vulkan commands use the stdcall convention 41 | #define VKAPI_ATTR 42 | #define VKAPI_CALL __stdcall 43 | #define VKAPI_PTR VKAPI_CALL 44 | #elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7 45 | #error "Vulkan isn't supported for the 'armeabi' NDK ABI" 46 | #elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE) 47 | // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat" 48 | // calling convention, i.e. float parameters are passed in registers. This 49 | // is true even if the rest of the application passes floats on the stack, 50 | // as it does by default when compiling for the armeabi-v7a NDK ABI. 51 | #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp"))) 52 | #define VKAPI_CALL 53 | #define VKAPI_PTR VKAPI_ATTR 54 | #else 55 | // On other platforms, use the default calling convention 56 | #define VKAPI_ATTR 57 | #define VKAPI_CALL 58 | #define VKAPI_PTR 59 | #endif 60 | 61 | #if !defined(VK_NO_STDDEF_H) 62 | #include 63 | #endif // !defined(VK_NO_STDDEF_H) 64 | 65 | #if !defined(VK_NO_STDINT_H) 66 | #if defined(_MSC_VER) && (_MSC_VER < 1600) 67 | typedef signed __int8 int8_t; 68 | typedef unsigned __int8 uint8_t; 69 | typedef signed __int16 int16_t; 70 | typedef unsigned __int16 uint16_t; 71 | typedef signed __int32 int32_t; 72 | typedef unsigned __int32 uint32_t; 73 | typedef signed __int64 int64_t; 74 | typedef unsigned __int64 uint64_t; 75 | #else 76 | #include 77 | #endif 78 | #endif // !defined(VK_NO_STDINT_H) 79 | 80 | #ifdef __cplusplus 81 | } // extern "C" 82 | #endif // __cplusplus 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /include/vulkan/vulkan_string.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 5 | * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 6 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | * furnished to do so, subject to the following conditions: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 12 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 13 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | */ 16 | // clang-format off 17 | #pragma once 18 | #include "vulkan.h" 19 | #include 20 | const char * to_string(VkAttachmentLoadOp val); 21 | const char * to_string(VkAttachmentStoreOp val); 22 | const char * to_string(VkBlendFactor val); 23 | const char * to_string(VkBlendOp val); 24 | const char * to_string(VkBorderColor val); 25 | const char * to_string(VkPipelineCacheHeaderVersion val); 26 | const char * to_string(VkComponentSwizzle val); 27 | const char * to_string(VkCommandBufferLevel val); 28 | const char * to_string(VkCompareOp val); 29 | const char * to_string(VkDescriptorType val); 30 | const char * to_string(VkDynamicState val); 31 | const char * to_string(VkPolygonMode val); 32 | const char * to_string(VkFormat val); 33 | const char * to_string(VkFrontFace val); 34 | const char * to_string(VkImageLayout val); 35 | const char * to_string(VkImageTiling val); 36 | const char * to_string(VkImageType val); 37 | const char * to_string(VkImageViewType val); 38 | const char * to_string(VkSharingMode val); 39 | const char * to_string(VkIndexType val); 40 | const char * to_string(VkLogicOp val); 41 | const char * to_string(VkPhysicalDeviceType val); 42 | const char * to_string(VkPipelineBindPoint val); 43 | const char * to_string(VkPrimitiveTopology val); 44 | const char * to_string(VkQueryType val); 45 | const char * to_string(VkSubpassContents val); 46 | const char * to_string(VkResult val); 47 | const char * to_string(VkStencilOp val); 48 | const char * to_string(VkStructureType val); 49 | const char * to_string(VkSystemAllocationScope val); 50 | const char * to_string(VkInternalAllocationType val); 51 | const char * to_string(VkSamplerAddressMode val); 52 | const char * to_string(VkFilter val); 53 | const char * to_string(VkSamplerMipmapMode val); 54 | const char * to_string(VkVertexInputRate val); 55 | const char * to_string(VkObjectType val); 56 | const char * to_string(VkIndirectCommandsTokenTypeNV val); 57 | const char * to_string(VkDescriptorUpdateTemplateType val); 58 | const char * to_string(VkViewportCoordinateSwizzleNV val); 59 | const char * to_string(VkDiscardRectangleModeEXT val); 60 | const char * to_string(VkPointClippingBehavior val); 61 | const char * to_string(VkCoverageModulationModeNV val); 62 | const char * to_string(VkCoverageReductionModeNV val); 63 | const char * to_string(VkValidationCacheHeaderVersionEXT val); 64 | const char * to_string(VkShaderInfoTypeAMD val); 65 | const char * to_string(VkQueueGlobalPriorityEXT val); 66 | const char * to_string(VkTimeDomainEXT val); 67 | const char * to_string(VkConservativeRasterizationModeEXT val); 68 | const char * to_string(VkSemaphoreType val); 69 | const char * to_string(VkBuildAccelerationStructureModeKHR val); 70 | const char * to_string(VkCopyAccelerationStructureModeKHR val); 71 | const char * to_string(VkAccelerationStructureTypeKHR val); 72 | const char * to_string(VkGeometryTypeKHR val); 73 | const char * to_string(VkRayTracingShaderGroupTypeKHR val); 74 | const char * to_string(VkAccelerationStructureMemoryRequirementsTypeNV val); 75 | const char * to_string(VkAccelerationStructureBuildTypeKHR val); 76 | const char * to_string(VkAccelerationStructureCompatibilityKHR val); 77 | const char * to_string(VkShaderGroupShaderKHR val); 78 | const char * to_string(VkMemoryOverallocationBehaviorAMD val); 79 | const char * to_string(VkScopeNV val); 80 | const char * to_string(VkComponentTypeNV val); 81 | const char * to_string(VkPerformanceCounterScopeKHR val); 82 | const char * to_string(VkPerformanceCounterUnitKHR val); 83 | const char * to_string(VkPerformanceCounterStorageKHR val); 84 | const char * to_string(VkPerformanceConfigurationTypeINTEL val); 85 | const char * to_string(VkQueryPoolSamplingModeINTEL val); 86 | const char * to_string(VkPerformanceOverrideTypeINTEL val); 87 | const char * to_string(VkPerformanceParameterTypeINTEL val); 88 | const char * to_string(VkPerformanceValueTypeINTEL val); 89 | const char * to_string(VkLineRasterizationModeEXT val); 90 | const char * to_string(VkFragmentShadingRateNV val); 91 | const char * to_string(VkFragmentShadingRateTypeNV val); 92 | const char * to_string(VkProvokingVertexModeEXT val); 93 | const char * to_string(VkColorSpaceKHR val); 94 | const char * to_string(VkPresentModeKHR val); 95 | const char * to_string(VkDebugReportObjectTypeEXT val); 96 | const char * to_string(VkDeviceMemoryReportEventTypeEXT val); 97 | const char * to_string(VkRasterizationOrderAMD val); 98 | const char * to_string(VkValidationCheckEXT val); 99 | const char * to_string(VkValidationFeatureEnableEXT val); 100 | const char * to_string(VkValidationFeatureDisableEXT val); 101 | const char * to_string(VkDisplayPowerStateEXT val); 102 | const char * to_string(VkDeviceEventTypeEXT val); 103 | const char * to_string(VkDisplayEventTypeEXT val); 104 | const char * to_string(VkTessellationDomainOrigin val); 105 | const char * to_string(VkSamplerYcbcrModelConversion val); 106 | const char * to_string(VkSamplerYcbcrRange val); 107 | const char * to_string(VkChromaLocation val); 108 | const char * to_string(VkSamplerReductionMode val); 109 | const char * to_string(VkBlendOverlapEXT val); 110 | #if defined(VK_USE_PLATFORM_WIN32_KHR) 111 | const char * to_string(VkFullScreenExclusiveEXT val); 112 | #endif // defined(VK_USE_PLATFORM_WIN32_KHR) 113 | const char * to_string(VkShaderFloatControlsIndependence val); 114 | const char * to_string(VkFragmentShadingRateCombinerOpKHR val); 115 | const char * to_string(VkVendorId val); 116 | const char * to_string(VkDriverId val); 117 | const char * to_string(VkShadingRatePaletteEntryNV val); 118 | const char * to_string(VkCoarseSampleOrderTypeNV val); 119 | const char * to_string(VkPipelineExecutableStatisticFormatKHR val); 120 | #if defined(VK_ENABLE_BETA_EXTENSIONS) 121 | const char * to_string(VkQueryResultStatusKHR val); 122 | #endif // defined(VK_ENABLE_BETA_EXTENSIONS) 123 | const char * to_string(VkAccelerationStructureMotionInstanceTypeNV val); 124 | const char * to_string(VkPipelineCacheCreateFlagBits val); 125 | std::string to_string(VkPipelineCacheCreateFlags flag); 126 | const char * to_string(VkQueueFlagBits val); 127 | std::string to_string(VkQueueFlags flag); 128 | const char * to_string(VkCullModeFlagBits val); 129 | std::string to_string(VkCullModeFlags flag); 130 | const char * to_string(VkRenderPassCreateFlagBits val); 131 | std::string to_string(VkRenderPassCreateFlags flag); 132 | const char * to_string(VkDeviceQueueCreateFlagBits val); 133 | std::string to_string(VkDeviceQueueCreateFlags flag); 134 | const char * to_string(VkMemoryPropertyFlagBits val); 135 | std::string to_string(VkMemoryPropertyFlags flag); 136 | const char * to_string(VkMemoryHeapFlagBits val); 137 | std::string to_string(VkMemoryHeapFlags flag); 138 | const char * to_string(VkAccessFlagBits val); 139 | std::string to_string(VkAccessFlags flag); 140 | const char * to_string(VkBufferUsageFlagBits val); 141 | std::string to_string(VkBufferUsageFlags flag); 142 | const char * to_string(VkBufferCreateFlagBits val); 143 | std::string to_string(VkBufferCreateFlags flag); 144 | const char * to_string(VkShaderStageFlagBits val); 145 | std::string to_string(VkShaderStageFlags flag); 146 | const char * to_string(VkImageUsageFlagBits val); 147 | std::string to_string(VkImageUsageFlags flag); 148 | const char * to_string(VkImageCreateFlagBits val); 149 | std::string to_string(VkImageCreateFlags flag); 150 | const char * to_string(VkImageViewCreateFlagBits val); 151 | std::string to_string(VkImageViewCreateFlags flag); 152 | const char * to_string(VkSamplerCreateFlagBits val); 153 | std::string to_string(VkSamplerCreateFlags flag); 154 | const char * to_string(VkPipelineCreateFlagBits val); 155 | std::string to_string(VkPipelineCreateFlags flag); 156 | const char * to_string(VkPipelineShaderStageCreateFlagBits val); 157 | std::string to_string(VkPipelineShaderStageCreateFlags flag); 158 | const char * to_string(VkColorComponentFlagBits val); 159 | std::string to_string(VkColorComponentFlags flag); 160 | const char * to_string(VkFenceCreateFlagBits val); 161 | std::string to_string(VkFenceCreateFlags flag); 162 | const char * to_string(VkSemaphoreCreateFlagBits val); 163 | std::string to_string(VkSemaphoreCreateFlags flag); 164 | const char * to_string(VkFormatFeatureFlagBits val); 165 | std::string to_string(VkFormatFeatureFlags flag); 166 | const char * to_string(VkQueryControlFlagBits val); 167 | std::string to_string(VkQueryControlFlags flag); 168 | const char * to_string(VkQueryResultFlagBits val); 169 | std::string to_string(VkQueryResultFlags flag); 170 | const char * to_string(VkCommandBufferUsageFlagBits val); 171 | std::string to_string(VkCommandBufferUsageFlags flag); 172 | const char * to_string(VkQueryPipelineStatisticFlagBits val); 173 | std::string to_string(VkQueryPipelineStatisticFlags flag); 174 | const char * to_string(VkImageAspectFlagBits val); 175 | std::string to_string(VkImageAspectFlags flag); 176 | const char * to_string(VkSparseImageFormatFlagBits val); 177 | std::string to_string(VkSparseImageFormatFlags flag); 178 | const char * to_string(VkSparseMemoryBindFlagBits val); 179 | std::string to_string(VkSparseMemoryBindFlags flag); 180 | const char * to_string(VkPipelineStageFlagBits val); 181 | std::string to_string(VkPipelineStageFlags flag); 182 | const char * to_string(VkCommandPoolCreateFlagBits val); 183 | std::string to_string(VkCommandPoolCreateFlags flag); 184 | const char * to_string(VkCommandPoolResetFlagBits val); 185 | std::string to_string(VkCommandPoolResetFlags flag); 186 | const char * to_string(VkCommandBufferResetFlagBits val); 187 | std::string to_string(VkCommandBufferResetFlags flag); 188 | const char * to_string(VkSampleCountFlagBits val); 189 | std::string to_string(VkSampleCountFlags flag); 190 | const char * to_string(VkAttachmentDescriptionFlagBits val); 191 | std::string to_string(VkAttachmentDescriptionFlags flag); 192 | const char * to_string(VkStencilFaceFlagBits val); 193 | std::string to_string(VkStencilFaceFlags flag); 194 | const char * to_string(VkDescriptorPoolCreateFlagBits val); 195 | std::string to_string(VkDescriptorPoolCreateFlags flag); 196 | const char * to_string(VkDependencyFlagBits val); 197 | std::string to_string(VkDependencyFlags flag); 198 | const char * to_string(VkSemaphoreWaitFlagBits val); 199 | std::string to_string(VkSemaphoreWaitFlags flag); 200 | const char * to_string(VkDisplayPlaneAlphaFlagBitsKHR val); 201 | std::string to_string(VkDisplayPlaneAlphaFlagsKHR flag); 202 | const char * to_string(VkCompositeAlphaFlagBitsKHR val); 203 | std::string to_string(VkCompositeAlphaFlagsKHR flag); 204 | const char * to_string(VkSurfaceTransformFlagBitsKHR val); 205 | std::string to_string(VkSurfaceTransformFlagsKHR flag); 206 | const char * to_string(VkDebugReportFlagBitsEXT val); 207 | std::string to_string(VkDebugReportFlagsEXT flag); 208 | const char * to_string(VkExternalMemoryHandleTypeFlagBitsNV val); 209 | std::string to_string(VkExternalMemoryHandleTypeFlagsNV flag); 210 | const char * to_string(VkExternalMemoryFeatureFlagBitsNV val); 211 | std::string to_string(VkExternalMemoryFeatureFlagsNV flag); 212 | const char * to_string(VkSubgroupFeatureFlagBits val); 213 | std::string to_string(VkSubgroupFeatureFlags flag); 214 | const char * to_string(VkIndirectCommandsLayoutUsageFlagBitsNV val); 215 | std::string to_string(VkIndirectCommandsLayoutUsageFlagsNV flag); 216 | const char * to_string(VkIndirectStateFlagBitsNV val); 217 | std::string to_string(VkIndirectStateFlagsNV flag); 218 | const char * to_string(VkPrivateDataSlotCreateFlagBitsEXT val); 219 | std::string to_string(VkPrivateDataSlotCreateFlagsEXT flag); 220 | const char * to_string(VkDescriptorSetLayoutCreateFlagBits val); 221 | std::string to_string(VkDescriptorSetLayoutCreateFlags flag); 222 | const char * to_string(VkExternalMemoryHandleTypeFlagBits val); 223 | std::string to_string(VkExternalMemoryHandleTypeFlags flag); 224 | const char * to_string(VkExternalMemoryFeatureFlagBits val); 225 | std::string to_string(VkExternalMemoryFeatureFlags flag); 226 | const char * to_string(VkExternalSemaphoreHandleTypeFlagBits val); 227 | std::string to_string(VkExternalSemaphoreHandleTypeFlags flag); 228 | const char * to_string(VkExternalSemaphoreFeatureFlagBits val); 229 | std::string to_string(VkExternalSemaphoreFeatureFlags flag); 230 | const char * to_string(VkSemaphoreImportFlagBits val); 231 | std::string to_string(VkSemaphoreImportFlags flag); 232 | const char * to_string(VkExternalFenceHandleTypeFlagBits val); 233 | std::string to_string(VkExternalFenceHandleTypeFlags flag); 234 | const char * to_string(VkExternalFenceFeatureFlagBits val); 235 | std::string to_string(VkExternalFenceFeatureFlags flag); 236 | const char * to_string(VkFenceImportFlagBits val); 237 | std::string to_string(VkFenceImportFlags flag); 238 | const char * to_string(VkSurfaceCounterFlagBitsEXT val); 239 | std::string to_string(VkSurfaceCounterFlagsEXT flag); 240 | const char * to_string(VkPeerMemoryFeatureFlagBits val); 241 | std::string to_string(VkPeerMemoryFeatureFlags flag); 242 | const char * to_string(VkMemoryAllocateFlagBits val); 243 | std::string to_string(VkMemoryAllocateFlags flag); 244 | const char * to_string(VkDeviceGroupPresentModeFlagBitsKHR val); 245 | std::string to_string(VkDeviceGroupPresentModeFlagsKHR flag); 246 | const char * to_string(VkSwapchainCreateFlagBitsKHR val); 247 | std::string to_string(VkSwapchainCreateFlagsKHR flag); 248 | const char * to_string(VkSubpassDescriptionFlagBits val); 249 | std::string to_string(VkSubpassDescriptionFlags flag); 250 | const char * to_string(VkDebugUtilsMessageSeverityFlagBitsEXT val); 251 | std::string to_string(VkDebugUtilsMessageSeverityFlagsEXT flag); 252 | const char * to_string(VkDebugUtilsMessageTypeFlagBitsEXT val); 253 | std::string to_string(VkDebugUtilsMessageTypeFlagsEXT flag); 254 | const char * to_string(VkDescriptorBindingFlagBits val); 255 | std::string to_string(VkDescriptorBindingFlags flag); 256 | const char * to_string(VkConditionalRenderingFlagBitsEXT val); 257 | std::string to_string(VkConditionalRenderingFlagsEXT flag); 258 | const char * to_string(VkResolveModeFlagBits val); 259 | std::string to_string(VkResolveModeFlags flag); 260 | const char * to_string(VkGeometryInstanceFlagBitsKHR val); 261 | std::string to_string(VkGeometryInstanceFlagsKHR flag); 262 | const char * to_string(VkGeometryFlagBitsKHR val); 263 | std::string to_string(VkGeometryFlagsKHR flag); 264 | const char * to_string(VkBuildAccelerationStructureFlagBitsKHR val); 265 | std::string to_string(VkBuildAccelerationStructureFlagsKHR flag); 266 | const char * to_string(VkAccelerationStructureCreateFlagBitsKHR val); 267 | std::string to_string(VkAccelerationStructureCreateFlagsKHR flag); 268 | const char * to_string(VkFramebufferCreateFlagBits val); 269 | std::string to_string(VkFramebufferCreateFlags flag); 270 | const char * to_string(VkDeviceDiagnosticsConfigFlagBitsNV val); 271 | std::string to_string(VkDeviceDiagnosticsConfigFlagsNV flag); 272 | const char * to_string(VkPipelineCreationFeedbackFlagBitsEXT val); 273 | std::string to_string(VkPipelineCreationFeedbackFlagsEXT flag); 274 | const char * to_string(VkPerformanceCounterDescriptionFlagBitsKHR val); 275 | std::string to_string(VkPerformanceCounterDescriptionFlagsKHR flag); 276 | const char * to_string(VkAcquireProfilingLockFlagBitsKHR val); 277 | std::string to_string(VkAcquireProfilingLockFlagsKHR flag); 278 | const char * to_string(VkShaderCorePropertiesFlagBitsAMD val); 279 | std::string to_string(VkShaderCorePropertiesFlagsAMD flag); 280 | const char * to_string(VkShaderModuleCreateFlagBits val); 281 | std::string to_string(VkShaderModuleCreateFlags flag); 282 | const char * to_string(VkPipelineCompilerControlFlagBitsAMD val); 283 | std::string to_string(VkPipelineCompilerControlFlagsAMD flag); 284 | const char * to_string(VkToolPurposeFlagBitsEXT val); 285 | std::string to_string(VkToolPurposeFlagsEXT flag); 286 | const char * to_string(VkAccessFlagBits2KHR val); 287 | std::string to_string(VkAccessFlags2KHR flag); 288 | const char * to_string(VkPipelineStageFlagBits2KHR val); 289 | std::string to_string(VkPipelineStageFlags2KHR flag); 290 | const char * to_string(VkSubmitFlagBitsKHR val); 291 | std::string to_string(VkSubmitFlagsKHR flag); 292 | const char * to_string(VkEventCreateFlagBits val); 293 | std::string to_string(VkEventCreateFlags flag); 294 | const char * to_string(VkPipelineLayoutCreateFlagBits val); 295 | std::string to_string(VkPipelineLayoutCreateFlags flag); 296 | #if defined(VK_ENABLE_BETA_EXTENSIONS) 297 | const char * to_string(VkVideoCodecOperationFlagBitsKHR val); 298 | std::string to_string(VkVideoCodecOperationFlagsKHR flag); 299 | const char * to_string(VkVideoChromaSubsamplingFlagBitsKHR val); 300 | std::string to_string(VkVideoChromaSubsamplingFlagsKHR flag); 301 | const char * to_string(VkVideoComponentBitDepthFlagBitsKHR val); 302 | std::string to_string(VkVideoComponentBitDepthFlagsKHR flag); 303 | const char * to_string(VkVideoCapabilityFlagBitsKHR val); 304 | std::string to_string(VkVideoCapabilityFlagsKHR flag); 305 | const char * to_string(VkVideoSessionCreateFlagBitsKHR val); 306 | std::string to_string(VkVideoSessionCreateFlagsKHR flag); 307 | const char * to_string(VkVideoCodingQualityPresetFlagBitsKHR val); 308 | std::string to_string(VkVideoCodingQualityPresetFlagsKHR flag); 309 | const char * to_string(VkVideoDecodeH264PictureLayoutFlagBitsEXT val); 310 | std::string to_string(VkVideoDecodeH264PictureLayoutFlagsEXT flag); 311 | const char * to_string(VkVideoCodingControlFlagBitsKHR val); 312 | std::string to_string(VkVideoCodingControlFlagsKHR flag); 313 | const char * to_string(VkVideoDecodeFlagBitsKHR val); 314 | std::string to_string(VkVideoDecodeFlagsKHR flag); 315 | const char * to_string(VkVideoEncodeFlagBitsKHR val); 316 | std::string to_string(VkVideoEncodeFlagsKHR flag); 317 | const char * to_string(VkVideoEncodeRateControlFlagBitsKHR val); 318 | std::string to_string(VkVideoEncodeRateControlFlagsKHR flag); 319 | const char * to_string(VkVideoEncodeRateControlModeFlagBitsKHR val); 320 | std::string to_string(VkVideoEncodeRateControlModeFlagsKHR flag); 321 | const char * to_string(VkVideoEncodeH264CapabilityFlagBitsEXT val); 322 | std::string to_string(VkVideoEncodeH264CapabilityFlagsEXT flag); 323 | const char * to_string(VkVideoEncodeH264InputModeFlagBitsEXT val); 324 | std::string to_string(VkVideoEncodeH264InputModeFlagsEXT flag); 325 | const char * to_string(VkVideoEncodeH264OutputModeFlagBitsEXT val); 326 | std::string to_string(VkVideoEncodeH264OutputModeFlagsEXT flag); 327 | const char * to_string(VkVideoEncodeH264CreateFlagBitsEXT val); 328 | std::string to_string(VkVideoEncodeH264CreateFlagsEXT flag); 329 | #endif // defined(VK_ENABLE_BETA_EXTENSIONS) 330 | #if defined(VK_USE_PLATFORM_FUCHSIA) 331 | const char * to_string(VkImageFormatConstraintsFlagBitsFUCHSIA val); 332 | std::string to_string(VkImageFormatConstraintsFlagsFUCHSIA flag); 333 | const char * to_string(VkImageConstraintsInfoFlagBitsFUCHSIA val); 334 | std::string to_string(VkImageConstraintsInfoFlagsFUCHSIA flag); 335 | #endif // defined(VK_USE_PLATFORM_FUCHSIA) 336 | #if defined(VK_USE_PLATFORM_ANDROID_KHR) 337 | #endif // defined(VK_USE_PLATFORM_ANDROID_KHR) 338 | #if defined(VK_USE_PLATFORM_VI_NN) 339 | #endif // defined(VK_USE_PLATFORM_VI_NN) 340 | #if defined(VK_USE_PLATFORM_WAYLAND_KHR) 341 | #endif // defined(VK_USE_PLATFORM_WAYLAND_KHR) 342 | #if defined(VK_USE_PLATFORM_WIN32_KHR) 343 | #endif // defined(VK_USE_PLATFORM_WIN32_KHR) 344 | #if defined(VK_USE_PLATFORM_XLIB_KHR) 345 | #endif // defined(VK_USE_PLATFORM_XLIB_KHR) 346 | #if defined(VK_USE_PLATFORM_XCB_KHR) 347 | #endif // defined(VK_USE_PLATFORM_XCB_KHR) 348 | #if defined(VK_USE_PLATFORM_DIRECTFB_EXT) 349 | #endif // defined(VK_USE_PLATFORM_DIRECTFB_EXT) 350 | #if defined(VK_USE_PLATFORM_IOS_MVK) 351 | #endif // defined(VK_USE_PLATFORM_IOS_MVK) 352 | #if defined(VK_USE_PLATFORM_MACOS_MVK) 353 | #endif // defined(VK_USE_PLATFORM_MACOS_MVK) 354 | #if defined(VK_USE_PLATFORM_METAL_EXT) 355 | #endif // defined(VK_USE_PLATFORM_METAL_EXT) 356 | #if defined(VK_USE_PLATFORM_FUCHSIA) 357 | #endif // defined(VK_USE_PLATFORM_FUCHSIA) 358 | #if defined(VK_USE_PLATFORM_GGP) 359 | #endif // defined(VK_USE_PLATFORM_GGP) 360 | #if defined(VK_USE_PLATFORM_SCREEN_QNX) 361 | #endif // defined(VK_USE_PLATFORM_SCREEN_QNX) 362 | #if defined(VK_ENABLE_BETA_EXTENSIONS) 363 | #endif // defined(VK_ENABLE_BETA_EXTENSIONS) 364 | 365 | // clang-format on 366 | -------------------------------------------------------------------------------- /misc/ENUMS AND BITMASKS AND FLAGS, OH MY.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bitmask types 6 | 7 | typedef VkFlags 8 | VkNameFlags; 9 | 10 | 11 | 12 | 13 | 14 | WSI extensions 15 | 16 | 17 | 18 | Types generated from corresponding enums tags below 19 | 20 | 21 | 22 | 23 | 24 | 25 | Extensions 26 | 27 | 28 | 29 | WSI extensions 30 | 31 | 32 | 33 | 34 | 36 | 37 | 38 | 39 | 42 | 43 | 44 | 45 | Flags 46 | 47 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 60 | 61 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 74 | 75 | 77 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /misc/discussion.md: -------------------------------------------------------------------------------- 1 | # What does vulkan.hpp get wrong? 2 | 3 | Here I have written up my thoughts on exactly what I think `vulkan.hpp` gets wrong. I understand that so many things influence decisions, so this is not finger pointing or laying blame. It is looking at what currently (as of time of writing) `vulkan.hpp` does and why that specific aspect could be improved. 4 | 5 | While not required, it is suggested that reader look through the Application Interface section of the Loader and Layer Interface document. 6 | 7 | 8 | 9 | 10 | ## Function pointer loading and dispatching 11 | 12 | `vulkan.hpp` tries to be flexible in how it handles loading function pointers for you. I posit that this flexibility hurts more than it helps by creating a multi-step and complex setup process that is very easy to misuse (setup incorrectly) and allows for sub-optimal API usage without knowing. The API also makes adding multi-gpu support much more arduous than with my approach. 13 | 14 | Context: While the reasons that graphics API's must load function pointers is many, in Vulkan, the `Vulkan-Loader` library (vulkan-1.dll, libvulkan.so) sits between applications and drivers to manages this. The downside is that applications link to the `Vulkan-Loader`, not the driver and thus every Vulkan function must pass through the `Vulkan-Loader` first. What complicates matters is that there are two types of functions in Vulkan, Instance and Device. Instance functions are only implemented by the `Vulkan-Loader` while Device functions are implemented by the drivers. But since applications link to the `Vulkan-Loader`, every Device function called must first pass through the loader. Most of the time an application is calling Device functions, so the extra indirection that is added is non-trivial. Thankfully, the API designers were well aware of such an issue and created the `vkGetDeviceProcAddr` function. It is used to query the *driver's* functions, completely bypassing the `Vulkan-Loader`, leading to 2-5% performance improvement. 15 | 16 | Arguments: 17 | 18 | The `README.md` (main documentation page) states that providing a `VkDevice` to `vk::DispatchLoaderDynamic` is optional. If you do not not provide a Device, `vulkan.hpp` will have to make all Device functions pass through the `Vulkan-Loader` unnecessarily. Fixing this is only one line of code (giving the dispatcher a VkDevice). Not adding this line of code leaves 2-5% on the table yet there is no external indication that it is happening, as its all internal to `vk::DispatchLoaderDynamic`. Every Device function will work, just slower than they have to. One aspect of good API design is the 'happy pit' where doing the easy thing is the right thing, and its hard to do the wrong (slow) thing. Yet `vulkan.hpp` is fine with letting users unknowingly do so. 19 | 20 | The default way users use the dispatcher in `vulkan.hpp` is through a very convenient helper called the `VULKAN_HPP_DEFAULT_DISPATCHER` which is globally available to of `vulkan.hpp`. I can't disagree that this is more convenient for the vast majority of users because they do not have to remember to add a `vk::DispatchLoaderDynamic` to the end of every Vulkan function. But if you need multi-gpu support, you leave this happy path. Now each and every vulkan call must have the dispatcher parameter added at the end, and plumb that dispatcher through the codebase to make it accessible. I admit this isn't a "hard" argument against the current mechanism, multi-gpu support in applications is not common. But for bindings that are designed to be useable by everyone, this is very obnoxious code pattern which sacrifices some users for everyone elses benefit. 21 | 22 | My solution is to create user visible Dispatcher objects for each dispatchable handle (Instance, PhysicalDevice, Device, Queue, and CommandBuffer). They are constructed with their handle and the parent Dispatcher. The `DynamicLibrary` loads `Vulkan-Loader` to kick off function loading. The Dispatcher for `VkInstance` is `InstanceFunctions` (IF). Every instance function is now a member function on the IF. Each dispachable handle has a `{HandleName}Functions` object. Creating the `PhysicalDeviceFunctions` (PDF) takes the `PhysicalDevice` (PD) handle and the IF. Creating a `VkDevice` needs only a PD but creating the `DeviceFunctions` (DF) need the `VkDevice` *and* the IF because `vkGetDeviceProcAddr` is an Instance function and is required. I may make it possible to use a PDF in the future for consistency. Then with a DF, creating a `QueueFunctions` and `CommandBuffersFunctions` with a DF and their respective handles is last. Generally, the functions struct could be put in an easily accessible place and/or pass it by reference. 23 | 24 | Example init code using this style 25 | ```c++ 26 | struct RenderContext 27 | { 28 | vk::DynamicLibrary vulkan_library; //keeps the vulkan-1.dll alive 29 | vk::InstanceFunctions instance_functions; 30 | vk::DeviceFunctions device_functions; 31 | }; 32 | 33 | void InitRenderContext(RenderContext& context){ 34 | // Allow loading to fail, in case the app wants to make vulkan support optional 35 | if (context.vulkan_library.init() != vk::Result::Success) { /* handle error */ } 36 | 37 | // GlobalFunctions is a local variable because we don't store it long term 38 | vk::GlobalFunctions global_functions = vk::GlobalFunctions(context.vulkan_library); 39 | 40 | // Call enumerate layers/extensions/versions & initialize instance create info 41 | 42 | auto [instance, instance_ret] = global_functions.CreateInstance(inst_info); 43 | if (!instance_ret) { /* handle error */ } 44 | 45 | context.instance_functions = vk::InstanceFunctions(global_functions, instance) 46 | 47 | auto [physical_devices, physical_devices_ret] = context.instance_functions.EnumeratePhysicalDevices(); 48 | 49 | // Choose a physical device & setup device_info 50 | 51 | auto [device, device_ret] = context.instance_functions.CreateDevice(device_info); 52 | if (!device_ret) { /* handle error */ } 53 | 54 | context.device_functions = vk::DeviceFunctions(instance_functions, device); 55 | 56 | // Now can use all device functions by passing around a `DeviceFunctions` object 57 | context.device_functions.SomeVulkanFunction(paramA, paramB, ...) //don't have to pass Device or Dispatcher object 58 | 59 | // Or put it in a global and make it easily accessible. Multi-gpu is not possible, but at least 60 | // the only thing to change is passing in a `DeviceFunctions` and 61 | } 62 | 63 | void DestroyContext(RenderContext& context) { 64 | // Instance and Device have 0 parameter destroy functions since the handle is stored in the struct 65 | context.device_functions.DestroyDevice(); 66 | context.instance_functions.DestroyInstance(); 67 | } 68 | 69 | ``` 70 | 71 | The benefit of my solution is to always make the fact that you are calling function pointers explicit and make it easy to call vulkan functions from a specific Dispatcher instead of a global one. That way adding multi-gpu wont involve shoving an extra parameter into each vulkan function call made. 72 | 73 | I also would like to think this makes the whole function loading process much more user friendly. The current instruction for `vulkan.hpp` either shows how to create a `vk::DispatchLoaderDynamic` to pass around, or demonstrates how to create a global `vk::DispatchLoaderDynamic`. The former is rather straightforward, and mirrors my initialization (save for how `vkGetInstanceProcAddr` is gotten). The latter option on the other hand isn't so simple. First, one must add `#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1` before including the header. 74 | ```c++ 75 | #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 76 | #include 77 | ``` 78 | Now you have to give storage for the dispatcher. Yes, its just as simple as putting the following statement in the code. No, its not clear that this is defining a global variable that `vulkan.hpp` accesses. Yes, it must appear only once. 79 | ```c++ 80 | //Put this in only one place, so not in a header file. 81 | VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE 82 | ``` 83 | Next, we need a `vk::DynamicLibrary` in which to get the `vkGetInstanceProcAddr` manually. Mind you, the `vk::DynamicLibrary` should not be a local variable because when the local function exits, the `Vulkan-Loader` will unload. Yet the documentation puts it in what appears to be a function. More than once I've seen users not know that and get bitten. With a `vk::DynamicLibrary` Then we can initialize the `VULKAN_HPP_DEFAULT_DISPATCHER`'s global functions by loading `vkGetInstanceProcAddr` from it. 84 | ```c++ 85 | vk::DynamicLoader dl; 86 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl.getProcAddress("vkGetInstanceProcAddr"); 87 | VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr); 88 | ``` 89 | Then follows instance creation and can load all the Instance functions. 90 | ```c++ 91 | vk::Instance instance = vk::createInstance({}, nullptr); 92 | VULKAN_HPP_DEFAULT_DISPATCHER.init(instance); 93 | ``` 94 | Lastly, we can initialize the Device functions 95 | ```c++ 96 | vk::Device device = physicalDevices.createDevice({}, nullptr); 97 | VULKAN_HPP_DEFAULT_DISPATCHER.init(device); 98 | ``` 99 | Note, this is listed as optional. I have described why it shouldn't be optional above, so I won't rehash it. 100 | 101 | The things I want to point out is that the process of setting up the dispatcher with `vulkan.hpp` requires setting 2 macro defines, manually get the `vkGetInstanceProcAddr` from it, not unloading `vk::DynamicLibrary`, and then initializing the dispatcher with 2 required function calls, and 1 optional one. Interspersed with that is creating an instance and a device. 102 | 103 | Constrast this with how I have users create a `vk::DynamicLibrary` (which also shouldn't be a local variable), create a `GlobalFunctions` (GF) object with it, create an instance from GF and then `InstanceFunctions` (IF), create a Device, and finally a `DeviceFunctions` from the Device and IF. There is an explicit setup process, one cannot create a Device without the required steps before it, and the code tells you the order explicitly. 104 | 105 | Is my solution more performant and safe? No, both will call function pointers using member functions. I would like to think its more safe as you 'construct' the Function structs with the required arguments. However to make it placeable in static/global contexts I had to add default constructors, which means the structs aren't inherently safe by construction. 106 | 107 | TODO: clarify why separating handles and function tables. 108 | Putting member functions in the handle structs directly constrains the implementation because of the desire to not make the size of `Device` massive (sizeof(`VkDevice`) + ~100 function pointers). 109 | 110 | ## Compile times 111 | 112 | This is a common complaint about `vulkan.hpp`. Its pretty self explanatory. Additionally, the developers are well aware of this, and are looking at C++20 modules as a possible long term solution. If the file could be split into header and implementation, that would help but I recognize that improving compile times is more art than science. 113 | 114 | ## Stability 115 | 116 | One of the biggest issues people have had with is `vulkan.hpp` breaking user code when updating. I would suggest that since the start of 2020, it has been much better. SDK's aren't shipping with headers that don't compile, and in general the stewardship of the project I feel is better than some people give credit. However, trust is easy to lose and hard to gain. Saying "I promise I will do better next time" after 2-3 time dealing with updating and realizing that vulkan.hpp is wrong or changed the interface somehow is not enough. 117 | 118 | I recognize that code quality is very difficult, and C++ exacerbates it. I do not feel I can create a new library to use instead of `vulkan.hpp` and then spend 2-3 years finding all the corner cases and bugs my initial implementation missed. That isn't an improvement over the original, it is in fact a regression. Library development in C++ is pretty much an expert level activity, and I recognize that while I can reason about the code in isolation pretty well, I lack a lot of experience in writing code. If you were hoping I would state "My solution is better because X" then you will be disappointed. I have spent over a hear hacking away at this project and found this problem is impossible to 'solve'. I cannot in good faith make false claims. 119 | 120 | ## Single file supports multiple C++ standards 121 | 122 | Less of a gripe, more of a 'Breaking backwards compatibility' is nice statement. 123 | If they could put every standard version into its own header, they would. But `vulkan.hpp` can live in a system wide include path, there would have to be named distinctly, and that isn't very nice. `vulkan.hpp` has a lot of 'use C++X features when possible, fallback when not' which makes reading the source code difficult, and no doubt complicates the generation of the code. That is multiplied by the various ENHANCED_MODE and other configuration macros. -------------------------------------------------------------------------------- /module/vk_platform.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: vk_platform.h 3 | // 4 | /* 5 | ** Copyright 2014-2021 The Khronos Group Inc. 6 | ** 7 | ** SPDX-License-Identifier: Apache-2.0 8 | */ 9 | 10 | 11 | #ifndef VK_PLATFORM_H_ 12 | #define VK_PLATFORM_H_ 13 | 14 | #ifdef __cplusplus 15 | extern "C" 16 | { 17 | #endif // __cplusplus 18 | 19 | /* 20 | *************************************************************************************************** 21 | * Platform-specific directives and type declarations 22 | *************************************************************************************************** 23 | */ 24 | 25 | /* Platform-specific calling convention macros. 26 | * 27 | * Platforms should define these so that Vulkan clients call Vulkan commands 28 | * with the same calling conventions that the Vulkan implementation expects. 29 | * 30 | * VKAPI_ATTR - Placed before the return type in function declarations. 31 | * Useful for C++11 and GCC/Clang-style function attribute syntax. 32 | * VKAPI_CALL - Placed after the return type in function declarations. 33 | * Useful for MSVC-style calling convention syntax. 34 | * VKAPI_PTR - Placed between the '(' and '*' in function pointer types. 35 | * 36 | * Function declaration: VKAPI_ATTR void VKAPI_CALL vkCommand(void); 37 | * Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void); 38 | */ 39 | #if defined(_WIN32) 40 | // On Windows, Vulkan commands use the stdcall convention 41 | #define VKAPI_ATTR 42 | #define VKAPI_CALL __stdcall 43 | #define VKAPI_PTR VKAPI_CALL 44 | #elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7 45 | #error "Vulkan isn't supported for the 'armeabi' NDK ABI" 46 | #elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE) 47 | // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat" 48 | // calling convention, i.e. float parameters are passed in registers. This 49 | // is true even if the rest of the application passes floats on the stack, 50 | // as it does by default when compiling for the armeabi-v7a NDK ABI. 51 | #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp"))) 52 | #define VKAPI_CALL 53 | #define VKAPI_PTR VKAPI_ATTR 54 | #else 55 | // On other platforms, use the default calling convention 56 | #define VKAPI_ATTR 57 | #define VKAPI_CALL 58 | #define VKAPI_PTR 59 | #endif 60 | 61 | #if !defined(VK_NO_STDDEF_H) 62 | #include 63 | #endif // !defined(VK_NO_STDDEF_H) 64 | 65 | #if !defined(VK_NO_STDINT_H) 66 | #if defined(_MSC_VER) && (_MSC_VER < 1600) 67 | typedef signed __int8 int8_t; 68 | typedef unsigned __int8 uint8_t; 69 | typedef signed __int16 int16_t; 70 | typedef unsigned __int16 uint16_t; 71 | typedef signed __int32 int32_t; 72 | typedef unsigned __int32 uint32_t; 73 | typedef signed __int64 int64_t; 74 | typedef unsigned __int64 uint64_t; 75 | #else 76 | #include 77 | #endif 78 | #endif // !defined(VK_NO_STDINT_H) 79 | 80 | #ifdef __cplusplus 81 | } // extern "C" 82 | #endif // __cplusplus 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /sample/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 4 | # (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 5 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 6 | # so, subject to the following conditions: 7 | 8 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 11 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 12 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | 15 | option(GLFW_BUILD_TESTS "" OFF) 16 | option(GLFW_BUILD_DOCS "" OFF) 17 | option(GLFW_INSTALL "" OFF) 18 | option(GLFW_BUILD_EXAMPLES "" OFF) 19 | 20 | include(FetchContent) 21 | FetchContent_Declare( 22 | glfw_repo 23 | GIT_REPOSITORY https://github.com/glfw/glfw 24 | GIT_TAG 3.3.2 25 | ) 26 | FetchContent_Declare( 27 | vma_repo 28 | GIT_REPOSITORY https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator 29 | GIT_TAG 59ec0809a7176ce6b9fa3d6341b477102ac3d3aa 30 | ) 31 | FetchContent_Declare( 32 | vk_bootstrap 33 | GIT_REPOSITORY https://github.com/charles-lunarg/vk-bootstrap 34 | GIT_TAG 511cac8db4f9d9d5eda95a6e16af74e0731f5f32 35 | ) 36 | FetchContent_MakeAvailable(glfw_repo vk_bootstrap) 37 | 38 | FetchContent_GetProperties(vma_repo) 39 | if(NOT vma_repoPOPULATED) 40 | FetchContent_Populate(vma_repo) 41 | add_library(VulkanMemoryAllocator INTERFACE) 42 | target_include_directories(VulkanMemoryAllocator INTERFACE ${vma_repo_SOURCE_DIR}/include) 43 | endif() 44 | 45 | find_package(Vulkan REQUIRED) 46 | 47 | add_executable(basic_sample_usage basic.cpp) 48 | target_link_libraries(basic_sample_usage vk-module-cpp) 49 | 50 | add_executable(vk-module-triangle-header triangle.cpp) 51 | target_link_libraries(vk-module-triangle-header 52 | PRIVATE 53 | vk-module-compiler-options 54 | vk-module-cpp 55 | glfw 56 | ) 57 | 58 | add_executable(vk-simple-triangle simple_triangle.cpp) 59 | target_link_libraries(vk-simple-triangle 60 | PRIVATE 61 | vulkan-simple-cpp 62 | glfw 63 | VulkanMemoryAllocator 64 | vk-module-compiler-options) 65 | 66 | add_custom_target(copy-shader-files ALL 67 | COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/sample/shaders ${CMAKE_CURRENT_BINARY_DIR} 68 | DEPENDS vk-module-triangle-header vk-simple-triangle) 69 | 70 | if (SET_COMPILE_TIME_TRACE) 71 | target_compile_options(basic_sample_usage PUBLIC -ftime-trace) 72 | target_compile_options(vk-module-triangle-header PUBLIC -ftime-trace) 73 | target_compile_options(vk-simple-triangle PUBLIC -ftime-trace) 74 | endif(SET_COMPILE_TIME_TRACE) -------------------------------------------------------------------------------- /sample/basic.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 5 | * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 6 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | * furnished to do so, subject to the following conditions: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 12 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 13 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include "vk_module.h" 21 | 22 | int main() 23 | { 24 | vk::DynamicLibrary library; 25 | vk::Result res = library.init(); 26 | if (res != vk::Result::Success) { 27 | std::cout << "couldn't init vulkan library\n"; 28 | return -1; 29 | } 30 | vk::GlobalFunctions free_funcs(library); 31 | 32 | auto layer_props_ret = free_funcs.EnumerateInstanceLayerProperties(); 33 | if (!layer_props_ret) { 34 | std::cout << "couldn't get layer props: " << vk::to_string(layer_props_ret.raw_result()) << "\n"; 35 | return -1; 36 | } 37 | std::cout << "count " << layer_props_ret.value().size() << "\n"; 38 | for (auto& prop : layer_props_ret.value()) 39 | std::cout << prop.layerName << "\n"; 40 | 41 | vk::Instance inst = free_funcs.CreateInstance({}).value(); 42 | if (res != vk::Result::Success) { 43 | std::cout << "failed to create instance\n"; 44 | return static_cast(res); 45 | } 46 | 47 | if (inst && !(!inst)) 48 | std::cout << "should print this\n"; 49 | else 50 | std::cout << "shouldn't print this\n"; 51 | 52 | vk::InstanceFunctions inst_funcs(free_funcs, inst); 53 | auto phys_devices_ret = inst_funcs.EnumeratePhysicalDevices(); 54 | if (!phys_devices_ret) 55 | return -1; 56 | if (phys_devices_ret.value().size() == 0) { 57 | std::cout << "failed to get phys devices\n"; 58 | return -100; 59 | } 60 | 61 | vk::PhysicalDevice phys_dev = phys_devices_ret.value()[0]; 62 | vk::PhysicalDeviceFunctions phys_dev_funcs(inst_funcs, phys_dev); 63 | 64 | vk::ImageCreateFlags img_flags = vk::ImageCreateFlagBits::SparseBinding; 65 | auto img_props_res = phys_dev_funcs.GetImageFormatProperties( 66 | vk::Format::Undefined, vk::ImageType::e1D, vk::ImageTiling::Optimal, vk::ImageUsageFlagBits::TransferDst, img_flags); 67 | if (!img_props_res) 68 | return -1; 69 | vk::SwapchainCreateInfoKHR swap_info; 70 | swap_info.preTransform = vk::SurfaceTransformFlagBitsKHR::IdentityBitKHR; 71 | /* 72 | Should not compile. 73 | vk::SurfaceTransformFlagsKHR transform_flags; 74 | swap_info.preTransform = transform_flags; 75 | */ 76 | 77 | float priority; 78 | vk::DeviceQueueCreateInfo queue_info; 79 | queue_info.queueCount = 1; 80 | queue_info.pQueuePriorities = &priority; 81 | vk::DeviceCreateInfo dev_create_info; 82 | dev_create_info.queueCreateInfoCount = 1; 83 | dev_create_info.pQueueCreateInfos = &queue_info; 84 | auto dev_res = inst_funcs.CreateDevice(phys_dev, dev_create_info); 85 | if (!dev_res) 86 | return -1; 87 | vk::Device device = dev_res.value(); 88 | vk::DeviceFunctions device_functions(inst_funcs, device); 89 | 90 | uint32_t queue_indices = 0; 91 | vk::BufferCreateInfo buffer_info{ .size = 100, 92 | .usage = vk::BufferUsageFlagBits::IndexBuffer, 93 | .sharingMode = vk::SharingMode::Exclusive, 94 | .queueFamilyIndexCount = 1, 95 | .pQueueFamilyIndices = &queue_indices }; 96 | auto buf_res = device_functions.CreateBuffer(buffer_info, nullptr); 97 | if (!buf_res) 98 | return -1; 99 | vk::Buffer buffer = buf_res.value(); 100 | device_functions.DestroyBuffer(buffer); 101 | vk::CommandPoolCreateInfo pool_info{ .flags = vk::CommandPoolCreateFlagBits::ResetCommandBuffer }; 102 | auto cmd_pool = device_functions.CreateCommandPool(pool_info).value(); 103 | vk::CommandBufferAllocateInfo alloc_info{ 104 | .commandPool = cmd_pool, 105 | .level = vk::CommandBufferLevel::Primary, 106 | .commandBufferCount = 1, 107 | }; 108 | 109 | auto [cmds, alloc_ret] = device_functions.AllocateCommandBuffers(alloc_info); 110 | if (alloc_ret != vk::Result::Success) { 111 | return -1; 112 | } 113 | vk::CommandBuffer cmd_buf = cmds[0]; 114 | vk::CommandBufferFunctions cmd_buf_functions{ device_functions, cmd_buf }; 115 | auto begin_ret = cmd_buf_functions.Begin({}); 116 | if (begin_ret != vk::Result::Success) 117 | return -1; 118 | 119 | vk::RenderPass renderpass; 120 | vk::Framebuffer framebuffer; 121 | vk::ClearValue clear_color{ .color = { { 0, 0, 0, 1 } } }; 122 | vk::Rect2D scissor{ { 0, 0 }, { 100, 100 } }; 123 | vk::Viewport viewport{ 0.f, 0.f, 1.f, 1.f, 0.f, 1.f }; 124 | 125 | auto ret = cmd_buf_functions 126 | .BeginRenderPass({ .renderPass = renderpass, 127 | .framebuffer = framebuffer, 128 | .renderArea = scissor, 129 | .clearValueCount = 1, 130 | .pClearValues = &clear_color }, 131 | vk::SubpassContents::Inline) 132 | .SetViewport(0, { viewport, viewport }) 133 | .SetScissor(0, { scissor }) 134 | .BindVertexBuffers(0, { buffer }, 0) 135 | .BindIndexBuffer(buffer, 0, vk::IndexType::Uint16) 136 | .Draw(10, 1, 0, 0) 137 | .EndRenderPass() 138 | .End(); 139 | if (ret == vk::Result::Success) { 140 | std::cout << "success recording command buffer\n"; 141 | } else { 142 | std::cout << "error: " << vk::to_string(ret) << '\n'; 143 | return -1; 144 | } 145 | 146 | auto [fence, fence_ret] = device_functions.CreateFence({}); 147 | if (fence_ret == vk::Result::Success) { 148 | std::cout << "success recording command buffer\n"; 149 | } 150 | return 0; 151 | } -------------------------------------------------------------------------------- /sample/shaders/frag.glsl: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout (location = 0) in vec3 fragColor; 5 | 6 | layout (location = 0) out vec4 outColor; 7 | 8 | void main () { outColor = vec4 (fragColor, 1.0); } 9 | -------------------------------------------------------------------------------- /sample/shaders/frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdgiessen/vk-module/cba515d86e6201a59cd44fa2be34dec80a077aa2/sample/shaders/frag.spv -------------------------------------------------------------------------------- /sample/shaders/vert.glsl: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout (location = 0) out vec3 fragColor; 5 | 6 | vec2 positions[3] = vec2[](vec2 (0.0, -0.5), vec2 (0.5, 0.5), vec2 (-0.5, 0.5)); 7 | 8 | vec3 colors[3] = vec3[](vec3 (1.0, 0.0, 0.0), vec3 (0.0, 1.0, 0.0), vec3 (0.0, 0.0, 1.0)); 9 | 10 | void main () 11 | { 12 | gl_Position = vec4 (positions[gl_VertexIndex], 0.0, 1.0); 13 | fragColor = colors[gl_VertexIndex]; 14 | } -------------------------------------------------------------------------------- /sample/shaders/vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cdgiessen/vk-module/cba515d86e6201a59cd44fa2be34dec80a077aa2/sample/shaders/vert.spv -------------------------------------------------------------------------------- /sample/simple_triangle.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 5 | * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 6 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | * furnished to do so, subject to the following conditions: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 12 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 13 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | */ 16 | 17 | #include "vulkan/vulkan.h" 18 | #include "vulkan/vulkan_string.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | constexpr uint32_t width = 512; 31 | constexpr uint32_t height = 512; 32 | 33 | struct RendererContext 34 | { 35 | GLFWwindow* window; 36 | VkInstance instance; 37 | VkSurfaceKHR surface; 38 | VkPhysicalDevice physical_device; 39 | VkDevice device; 40 | VkDeviceDispatchTable functions; 41 | VkQueue graphics_queue; 42 | VkSwapchainKHR swapchain; 43 | std::vector swapchain_images; 44 | uint32_t image_count = 0; 45 | std::vector swapchain_image_views; 46 | VkFormat swapchain_img_format; 47 | VkRenderPass render_pass; 48 | std::vector framebuffers; 49 | VkPipelineLayout pipeline_layout; 50 | VkPipeline pipeline; 51 | VkCommandPool cmd_pool; 52 | std::vector cmd_buffers; 53 | uint32_t current_frame = 0; 54 | std::vector fences; 55 | std::vector available_semaphores; 56 | std::vector finished_semaphores; 57 | 58 | operator VkDevice() { return device; } 59 | VkDeviceDispatchTable* operator->() { return &functions; } 60 | }; 61 | void check_res(bool result, const char* msg) 62 | { 63 | if (!result) { 64 | std::cerr << msg << "\n"; 65 | assert(false); 66 | } 67 | } 68 | void check_res(VkResult result, const char* msg) 69 | { 70 | if (result != VkResult::Success) { 71 | std::cerr << msg << ": Result = " << to_string(result) << "\n"; 72 | assert(false); 73 | } 74 | } 75 | GLFWwindow* create_window_glfw(const char* window_name = "", bool resize = true) 76 | { 77 | glfwInit(); 78 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 79 | if (!resize) 80 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 81 | 82 | return glfwCreateWindow(width, height, window_name, NULL, NULL); 83 | } 84 | void destroy_window_glfw(GLFWwindow* window) 85 | { 86 | glfwDestroyWindow(window); 87 | glfwTerminate(); 88 | } 89 | VkSurfaceKHR create_surface_glfw(VkInstance instance, GLFWwindow* window) 90 | { 91 | VkSurfaceKHR surface = VK_NULL_HANDLE; 92 | VkResult err = glfwCreateWindowSurface(instance, window, NULL, &surface); 93 | if (err != VkResult::Success) { 94 | const char* error_msg; 95 | int ret = glfwGetError(&error_msg); 96 | if (ret != 0) { 97 | std::cerr << ret << " "; 98 | if (error_msg != nullptr) 99 | std::cerr << error_msg; 100 | std::cerr << "\n"; 101 | } 102 | surface = VK_NULL_HANDLE; 103 | } 104 | return surface; 105 | } 106 | 107 | // Helper for robustly executing the two-call pattern 108 | // NOTE: doesn't work on functions that dont return VkResult 109 | template 110 | auto GetVectorInit(F&& f, T init, Ts&&... ts) -> std::vector 111 | { 112 | uint32_t count = 0; 113 | std::vector results; 114 | VkResult err; 115 | do { 116 | err = f(ts..., &count, nullptr); 117 | check_res(err, "failed to get size"); 118 | results.resize(count, init); 119 | err = f(ts..., &count, results.data()); 120 | results.resize(count); 121 | } while (err == VK_INCOMPLETE); 122 | check_res(err, "failed to get data"); 123 | return results; 124 | } 125 | 126 | template 127 | auto GetVector(F&& f, Ts&&... ts) -> std::vector 128 | { 129 | return GetVectorInit(f, T(), ts...); 130 | } 131 | 132 | void create_renderer_context(RendererContext& context) 133 | { 134 | 135 | check_res(vkInitializeLoaderLibrary(), "Failed to initialize the loader library"); 136 | 137 | context.window = create_window_glfw("Sample Triangle", false); 138 | check_res(context.window != nullptr, "Failed to create glfw window"); 139 | 140 | uint32_t glfw_extension_count = 0; 141 | const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count); 142 | VkInstanceCreateInfo inst_info{}; 143 | inst_info.enabledExtensionCount = glfw_extension_count; 144 | inst_info.ppEnabledExtensionNames = glfw_extensions; 145 | 146 | auto layer_props = GetVector(vkEnumerateInstanceLayerProperties); 147 | 148 | const char* layer_names[] = { "VK_LAYER_KHRONOS_validation" }; 149 | for (auto& layer : layer_props) { 150 | if (std::strcmp(layer.layerName, "VK_LAYER_KHRONOS_validation") == 0) { 151 | inst_info.ppEnabledLayerNames = layer_names; 152 | inst_info.enabledLayerCount = 1; 153 | break; 154 | } 155 | } 156 | 157 | check_res(vkCreateInstance(&inst_info, nullptr, &context.instance), "Failed to init Vulkan Instance"); 158 | 159 | vkInitializeInstanceFunctions(context.instance); 160 | 161 | context.surface = create_surface_glfw(context.instance, context.window); 162 | check_res(context.surface != VK_NULL_HANDLE, "Failed to create glfw surface"); 163 | 164 | auto physical_devices = GetVector(vkEnumeratePhysicalDevices, context.instance); 165 | 166 | context.physical_device = physical_devices[0]; // get first physical device returned 167 | VkBool32 suppprted = 0; 168 | check_res(vkGetPhysicalDeviceSurfaceSupportKHR(context.physical_device, 0, context.surface, &suppprted), 169 | "Failed to query surface support"); 170 | check_res(suppprted, "Surface doesn't support present"); 171 | } 172 | 173 | void create_device_context(RendererContext& context) 174 | { 175 | const char* extensions[] = { "VK_KHR_swapchain" }; 176 | float priority = 1.f; 177 | VkDeviceQueueCreateInfo queue_infos; 178 | queue_infos.pQueuePriorities = &priority; 179 | queue_infos.queueCount = 1; 180 | queue_infos.queueFamilyIndex = 0; 181 | VkDeviceCreateInfo info; 182 | info.enabledExtensionCount = 1; 183 | info.ppEnabledExtensionNames = extensions; 184 | info.queueCreateInfoCount = 1; 185 | info.pQueueCreateInfos = &queue_infos; 186 | check_res(vkCreateDevice(context.physical_device, &info, nullptr, &context.device), "Failed to create a vulkan device"); 187 | 188 | vkInitializeDeviceDispatchTable(context.device, context.functions); 189 | } 190 | 191 | void setup_queues(RendererContext& context) 192 | { 193 | uint32_t queue_family_size = 0; 194 | vkGetPhysicalDeviceQueueFamilyProperties(context.physical_device, &queue_family_size, nullptr); 195 | std::vector queue_family_props(queue_family_size); 196 | vkGetPhysicalDeviceQueueFamilyProperties(context.physical_device, &queue_family_size, queue_family_props.data()); 197 | 198 | uint32_t graphics_queue_family = 0; 199 | for (uint32_t i = 0; i < queue_family_props.size(); i++) { 200 | if (queue_family_props[0].queueFlags & VkQueueFlagBits::Graphics && queue_family_props[0].queueCount > 0) { 201 | graphics_queue_family = i; 202 | break; 203 | } 204 | } 205 | check_res(graphics_queue_family == 0, "First queue isn't the graphics queue, implicit assumptions failed"); 206 | 207 | context->vkGetDeviceQueue(context.device, graphics_queue_family, 0, &context.graphics_queue); 208 | } 209 | 210 | void setup_swapchain(RendererContext& context) 211 | { 212 | auto surf_formats = 213 | GetVector(vkGetPhysicalDeviceSurfaceFormatsKHR, context.physical_device, context.surface); 214 | 215 | VkSurfaceCapabilitiesKHR surf_caps; 216 | check_res(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context.physical_device, context.surface, &surf_caps), 217 | "Failed to get surface capabilities"); 218 | 219 | context.swapchain_img_format = surf_formats[0].format; 220 | context.image_count = 3; 221 | 222 | VkSwapchainCreateInfoKHR info; 223 | info.surface = context.surface; 224 | info.minImageCount = 3; 225 | info.imageFormat = context.swapchain_img_format; 226 | info.imageColorSpace = surf_formats[0].colorSpace; 227 | info.imageExtent = { width, height }; 228 | info.imageArrayLayers = 1; 229 | info.imageUsage = VkImageUsageFlagBits::ColorAttachment; 230 | info.imageSharingMode = VkSharingMode::Exclusive; 231 | info.pQueueFamilyIndices = nullptr; 232 | info.preTransform = surf_caps.currentTransform; 233 | info.compositeAlpha = VkCompositeAlphaFlagBitsKHR::OpaqueBitKHR; 234 | info.presentMode = VkPresentModeKHR::FifoKHR; 235 | 236 | check_res(context->vkCreateSwapchainKHR(context, &info, nullptr, &context.swapchain), "Unable to create Swapchain"); 237 | 238 | context.swapchain_images = GetVector(context->vkGetSwapchainImagesKHR, context.device, context.swapchain); 239 | 240 | for (auto& image : context.swapchain_images) { 241 | VkImageViewCreateInfo view_info; 242 | view_info.image = image; 243 | view_info.viewType = VkImageViewType::e2D; 244 | view_info.format = context.swapchain_img_format; 245 | view_info.subresourceRange = { VkImageAspectFlagBits::Color, 0, 1, 0, 1 }; 246 | VkImageView view; 247 | check_res(context->vkCreateImageView(context, &view_info, nullptr, &view), "Failed to create swapchain image view"); 248 | 249 | context.swapchain_image_views.push_back(view); 250 | } 251 | } 252 | 253 | void setup_renderpass(RendererContext& context) 254 | { 255 | VkAttachmentDescription desc; 256 | desc.format = context.swapchain_img_format; 257 | desc.samples = VkSampleCountFlagBits::e1; 258 | desc.loadOp = VkAttachmentLoadOp::Clear; 259 | desc.storeOp = VkAttachmentStoreOp::Store; 260 | desc.stencilLoadOp = VkAttachmentLoadOp::DontCare; 261 | desc.stencilStoreOp = VkAttachmentStoreOp::DontCare; 262 | desc.initialLayout = VkImageLayout::Undefined; 263 | desc.finalLayout = VkImageLayout::PresentSrcKHR; 264 | 265 | VkAttachmentReference ref{ 0, VkImageLayout::ColorAttachmentOptimal }; 266 | VkSubpassDescription sub_desc; 267 | sub_desc.pipelineBindPoint = VkPipelineBindPoint::Graphics; 268 | sub_desc.pColorAttachments = &ref; 269 | sub_desc.colorAttachmentCount = 1; 270 | VkSubpassDependency dep; 271 | dep.srcSubpass = VK_SUBPASS_EXTERNAL; 272 | dep.dstSubpass = 0; 273 | dep.srcStageMask = VkPipelineStageFlagBits::ColorAttachmentOutput; 274 | dep.dstStageMask = VkPipelineStageFlagBits::ColorAttachmentOutput; 275 | dep.dstAccessMask = VkAccessFlagBits::ColorAttachmentRead | VkAccessFlagBits::ColorAttachmentWrite; 276 | 277 | VkRenderPassCreateInfo info; 278 | info.attachmentCount = 1; 279 | info.pAttachments = &desc; 280 | info.dependencyCount = 1; 281 | info.pDependencies = &dep; 282 | info.subpassCount = 1; 283 | info.pSubpasses = &sub_desc; 284 | check_res(context->vkCreateRenderPass(context, &info, nullptr, &context.render_pass), "Failed to create renderpass"); 285 | } 286 | void create_framebuffers(RendererContext& context) 287 | { 288 | for (auto& view : context.swapchain_image_views) { 289 | VkFramebufferCreateInfo info; 290 | info.renderPass = context.render_pass; 291 | info.attachmentCount = 1; 292 | info.pAttachments = &view; 293 | info.width = width; 294 | info.height = height; 295 | info.layers = 1; 296 | VkFramebuffer framebuffer; 297 | check_res(context->vkCreateFramebuffer(context, &info, nullptr, &framebuffer), "Failed to create framebuffer"); 298 | context.framebuffers.push_back(framebuffer); 299 | } 300 | } 301 | 302 | std::vector read_file(const std::string& filename) 303 | { 304 | std::ifstream file(filename, std::ios::ate | std::ios::binary); 305 | check_res(file.is_open(), "Failed to open shader file"); 306 | 307 | size_t file_size = (size_t)file.tellg(); 308 | std::vector buffer(file_size); 309 | file.seekg(0); 310 | file.read(buffer.data(), static_cast(file_size)); 311 | file.close(); 312 | return buffer; 313 | } 314 | 315 | VkShaderModule create_shader_module(RendererContext& context, std::string const& filename) 316 | { 317 | auto code = read_file(filename); 318 | VkShaderModule mod; 319 | VkShaderModuleCreateInfo info; 320 | info.codeSize = code.size(); 321 | info.pCode = reinterpret_cast(code.data()); 322 | check_res(context->vkCreateShaderModule(context, &info, nullptr, &mod), "Failed to create shader module"); 323 | return mod; 324 | } 325 | 326 | void create_pipeline(RendererContext& context) 327 | { 328 | VkShaderModule vert = create_shader_module(context, "vert.spv"); 329 | VkShaderModule frag = create_shader_module(context, "frag.spv"); 330 | VkPipelineLayoutCreateInfo p_info_cr; 331 | check_res(context->vkCreatePipelineLayout(context, &p_info_cr, nullptr, &context.pipeline_layout), 332 | "Failed to create pipeline layout"); 333 | 334 | VkViewport viewport = { 0.f, 0.f, static_cast(width), static_cast(height), 0.f, 1.f }; 335 | VkRect2D scissor = { { 0, 0 }, { width, height } }; 336 | VkPipelineShaderStageCreateInfo vert_stage; 337 | vert_stage.stage = VkShaderStageFlagBits::Vertex; 338 | vert_stage.module = vert; 339 | vert_stage.pName = "main"; 340 | VkPipelineShaderStageCreateInfo frag_stage; 341 | frag_stage.stage = VkShaderStageFlagBits::Fragment; 342 | frag_stage.module = frag; 343 | frag_stage.pName = "main"; 344 | VkPipelineShaderStageCreateInfo shaders[2] = { vert_stage, frag_stage }; 345 | VkPipelineVertexInputStateCreateInfo vert_input; 346 | VkPipelineInputAssemblyStateCreateInfo input_assembly; 347 | input_assembly.topology = VkPrimitiveTopology::TriangleList; 348 | VkPipelineViewportStateCreateInfo viewport_state; 349 | viewport_state.viewportCount = 1; 350 | viewport_state.pViewports = &viewport; 351 | viewport_state.scissorCount = 1; 352 | viewport_state.pScissors = &scissor; 353 | VkPipelineRasterizationStateCreateInfo rasterization; 354 | rasterization.polygonMode = VkPolygonMode::Fill; 355 | rasterization.cullMode = VkCullModeFlagBits::Back; 356 | rasterization.frontFace = VkFrontFace::Clockwise; 357 | rasterization.lineWidth = 1.f; 358 | VkPipelineMultisampleStateCreateInfo multisample; 359 | multisample.rasterizationSamples = VkSampleCountFlagBits::e1; 360 | multisample.sampleShadingEnable = false; 361 | VkPipelineColorBlendAttachmentState blend_attachment; 362 | blend_attachment.blendEnable = false; 363 | blend_attachment.colorWriteMask = 364 | VkColorComponentFlagBits::R | VkColorComponentFlagBits::G | VkColorComponentFlagBits::B | VkColorComponentFlagBits::A; 365 | VkPipelineColorBlendStateCreateInfo color_blend; 366 | color_blend.logicOpEnable = false; 367 | color_blend.pAttachments = &blend_attachment; 368 | color_blend.attachmentCount = 1; 369 | 370 | VkDynamicState states[2] = { VkDynamicState::Viewport, VkDynamicState::Scissor }; 371 | VkPipelineDynamicStateCreateInfo dynamic_state; 372 | dynamic_state.dynamicStateCount = 2; 373 | dynamic_state.pDynamicStates = states; 374 | 375 | VkGraphicsPipelineCreateInfo info; 376 | info.renderPass = context.render_pass; 377 | info.layout = context.pipeline_layout; 378 | info.stageCount = 2; 379 | info.pStages = &shaders[0]; 380 | info.pVertexInputState = &vert_input; 381 | info.pInputAssemblyState = &input_assembly; 382 | info.pViewportState = &viewport_state; 383 | info.pRasterizationState = &rasterization; 384 | info.pMultisampleState = &multisample; 385 | info.pColorBlendState = &color_blend; 386 | info.pDynamicState = &dynamic_state; 387 | 388 | check_res(context->vkCreateGraphicsPipelines(context, VK_NULL_HANDLE, 1, &info, nullptr, &context.pipeline), 389 | "Failed to create graphipcs pipeline"); 390 | context->vkDestroyShaderModule(context, vert, nullptr); 391 | context->vkDestroyShaderModule(context, frag, nullptr); 392 | } 393 | 394 | void create_command_buffers(RendererContext& context) 395 | { 396 | VkCommandPoolCreateInfo pool_info; 397 | check_res(context->vkCreateCommandPool(context, &pool_info, nullptr, &context.cmd_pool), "Failed to create command pool"); 398 | 399 | VkCommandBufferAllocateInfo alloc_info; 400 | alloc_info.commandPool = context.cmd_pool; 401 | alloc_info.level = VkCommandBufferLevel::Primary; 402 | alloc_info.commandBufferCount = context.image_count; 403 | context.cmd_buffers.resize(3); 404 | check_res(context->vkAllocateCommandBuffers(context, &alloc_info, context.cmd_buffers.data()), 405 | "Failed to create command buffers"); 406 | 407 | int i = 0; 408 | for (auto& cmd_buf : context.cmd_buffers) { 409 | 410 | VkCommandBufferBeginInfo begin_info{}; 411 | check_res(context->vkBeginCommandBuffer(cmd_buf, &begin_info), "Failed to begin command buffer"); 412 | 413 | VkViewport viewport = { 0.f, 0.f, static_cast(width), static_cast(height), 0.f, 1.f }; 414 | VkRect2D scissor = { { 0, 0 }, { width, height } }; 415 | VkClearValue clear_colors = { { 0.f, 0.f, 0.f, 1.f } }; 416 | 417 | VkRenderPassBeginInfo renderpass_info; 418 | renderpass_info.renderPass = context.render_pass; 419 | renderpass_info.framebuffer = context.framebuffers[i++]; 420 | renderpass_info.renderArea = scissor; 421 | renderpass_info.clearValueCount = 4; 422 | renderpass_info.pClearValues = &clear_colors; 423 | 424 | context->vkCmdBeginRenderPass(cmd_buf, &renderpass_info, VkSubpassContents::Inline); 425 | context->vkCmdBindPipeline(cmd_buf, VkPipelineBindPoint::Graphics, context.pipeline); 426 | context->vkCmdSetViewport(cmd_buf, 0, 1, &viewport); 427 | context->vkCmdSetScissor(cmd_buf, 0, 1, &scissor); 428 | context->vkCmdDraw(cmd_buf, 3, 1, 0, 0); 429 | context->vkCmdEndRenderPass(cmd_buf); 430 | check_res(context->vkEndCommandBuffer(cmd_buf), "Failed to end command buffer"); 431 | } 432 | } 433 | 434 | void setup_sync_objects(RendererContext& context) 435 | { 436 | context.fences.resize(context.image_count); 437 | context.available_semaphores.resize(context.image_count); 438 | context.finished_semaphores.resize(context.image_count); 439 | for (uint32_t i = 0; i < context.image_count; i++) { 440 | VkFenceCreateInfo fence_info; 441 | fence_info.flags = VkFenceCreateFlagBits::Signaled; 442 | check_res(context->vkCreateFence(context, &fence_info, nullptr, &context.fences[i]), "Failed to create fence"); 443 | VkSemaphoreCreateInfo sem_info; 444 | check_res(context->vkCreateSemaphore(context, &sem_info, nullptr, &context.available_semaphores[i]), 445 | "Failed to create semaphore"); 446 | check_res(context->vkCreateSemaphore(context, &sem_info, nullptr, &context.finished_semaphores[i]), 447 | "Failed to create semaphore"); 448 | } 449 | } 450 | 451 | void recreate_swapchain(RendererContext& context) 452 | { 453 | check_res(context->vkQueueWaitIdle(context.graphics_queue), ""); 454 | context->vkDestroyCommandPool(context, context.cmd_pool, nullptr); 455 | 456 | for (auto& framebuffer : context.framebuffers) { 457 | context->vkDestroyFramebuffer(context, framebuffer, nullptr); 458 | } 459 | for (auto& image_view : context.swapchain_image_views) { 460 | context->vkDestroyImageView(context, image_view, nullptr); 461 | } 462 | 463 | setup_swapchain(context); 464 | create_framebuffers(context); 465 | create_command_buffers(context); 466 | } 467 | 468 | void draw_frame(RendererContext& context) 469 | { 470 | check_res(context->vkWaitForFences(context, 1, &context.fences[context.current_frame], true, UINT64_MAX), 471 | "Failed to wait for fence"); 472 | 473 | check_res(context->vkResetFences(context, 1, &context.fences[context.current_frame]), "Failed to reset fence"); 474 | 475 | uint32_t image_index; 476 | auto image_index_ret = context->vkAcquireNextImageKHR( 477 | context, context.swapchain, UINT64_MAX, context.available_semaphores[context.current_frame], nullptr, &image_index); 478 | if (image_index_ret == VkResult::ErrorOutOfDateKHR) { 479 | return recreate_swapchain(context); 480 | } else if (image_index_ret != VkResult::Success && image_index_ret != VkResult::SuboptimalKHR) { 481 | std::cerr << "failed to acquire swapchain image. Error " << to_string(image_index_ret) << "\n"; 482 | } 483 | VkPipelineStageFlags dst_stage_mask = VkPipelineStageFlagBits::ColorAttachmentOutput; 484 | VkSubmitInfo submit_info; 485 | submit_info.waitSemaphoreCount = 1; 486 | submit_info.pWaitSemaphores = &context.available_semaphores[context.current_frame]; 487 | submit_info.pWaitDstStageMask = &dst_stage_mask; 488 | submit_info.commandBufferCount = 1; 489 | submit_info.pCommandBuffers = &context.cmd_buffers[image_index]; 490 | submit_info.signalSemaphoreCount = 1; 491 | submit_info.pSignalSemaphores = &context.finished_semaphores[context.current_frame]; 492 | check_res(context->vkQueueSubmit(context.graphics_queue, 1, &submit_info, context.fences[context.current_frame]), 493 | "Failed to submit command buffer"); 494 | 495 | VkPresentInfoKHR present_info; 496 | present_info.waitSemaphoreCount = 1; 497 | present_info.pWaitSemaphores = &context.finished_semaphores[context.current_frame]; 498 | present_info.swapchainCount = 1; 499 | present_info.pSwapchains = &context.swapchain; 500 | present_info.pImageIndices = &image_index; 501 | auto present_ret = context->vkQueuePresentKHR(context.graphics_queue, &present_info); 502 | if (present_ret == VkResult::ErrorOutOfDateKHR || present_ret == VkResult::SuboptimalKHR) { 503 | return recreate_swapchain(context); 504 | } 505 | check_res(present_ret, "Failed to present"); 506 | 507 | context.current_frame = (context.current_frame + 1) % context.image_count; 508 | } 509 | 510 | void destroy_device(RendererContext& context) 511 | { 512 | for (auto& sem : context.available_semaphores) { 513 | context->vkDestroySemaphore(context, sem, nullptr); 514 | } 515 | for (auto& sem : context.finished_semaphores) { 516 | context->vkDestroySemaphore(context, sem, nullptr); 517 | } 518 | for (auto& fence : context.fences) { 519 | context->vkDestroyFence(context, fence, nullptr); 520 | } 521 | context->vkDestroyCommandPool(context, context.cmd_pool, nullptr); 522 | for (auto& framebuffer : context.framebuffers) { 523 | context->vkDestroyFramebuffer(context, framebuffer, nullptr); 524 | } 525 | context->vkDestroyPipeline(context, context.pipeline, nullptr); 526 | context->vkDestroyPipelineLayout(context, context.pipeline_layout, nullptr); 527 | context->vkDestroyRenderPass(context, context.render_pass, nullptr); 528 | for (auto& image_view : context.swapchain_image_views) { 529 | context->vkDestroyImageView(context, image_view, nullptr); 530 | } 531 | context->vkDestroySwapchainKHR(context, context.swapchain, nullptr); 532 | context->vkDestroyDevice(context, nullptr); 533 | } 534 | 535 | void destroy_renderer(RendererContext& context) 536 | { 537 | vkDestroySurfaceKHR(context.instance, context.surface, nullptr); 538 | vkDestroyInstance(context.instance, nullptr); 539 | destroy_window_glfw(context.window); 540 | } 541 | 542 | int main() 543 | { 544 | RendererContext context; 545 | 546 | create_renderer_context(context); 547 | create_device_context(context); 548 | setup_queues(context); 549 | setup_swapchain(context); 550 | setup_renderpass(context); 551 | create_framebuffers(context); 552 | create_pipeline(context); 553 | create_command_buffers(context); 554 | setup_sync_objects(context); 555 | 556 | while (!glfwWindowShouldClose(context.window)) { 557 | glfwPollEvents(); 558 | draw_frame(context); 559 | } 560 | check_res(context->vkQueueWaitIdle(context.graphics_queue), "Couldn't wait to shut down"); 561 | destroy_device(context); 562 | destroy_renderer(context); 563 | return 0; 564 | } -------------------------------------------------------------------------------- /sample/triangle.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 5 | * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 6 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | * furnished to do so, subject to the following conditions: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 12 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 13 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | */ 16 | 17 | #include "vk_module.h" 18 | #include "vk_module_interop.h" 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #define GLFW_INCLUDE_VULKAN 27 | #include 28 | 29 | constexpr uint32_t width = 512; 30 | constexpr uint32_t height = 512; 31 | 32 | struct RendererContext 33 | { 34 | vk::DynamicLibrary library; 35 | GLFWwindow* window; 36 | vk::Instance instance; 37 | vk::SurfaceKHR surface; 38 | vk::InstanceFunctions instance_functions; 39 | vk::PhysicalDevice physical_device; 40 | vk::PhysicalDeviceFunctions physical_device_functions; 41 | vk::Device device; 42 | vk::DeviceFunctions functions; 43 | vk::Queue graphics_queue; 44 | vk::QueueFunctions queue_functions; 45 | vk::SwapchainKHR swapchain; 46 | std::vector swapchain_images; 47 | uint32_t image_count = 0; 48 | std::vector swapchain_image_views; 49 | vk::SurfaceFormatKHR swapchain_surface_format; 50 | vk::RenderPass render_pass; 51 | std::vector framebuffers; 52 | vk::PipelineLayout pipeline_layout; 53 | vk::Pipeline pipeline; 54 | vk::CommandPool cmd_pool; 55 | std::vector cmd_buffers; 56 | uint32_t current_frame = 0; 57 | std::vector fences; 58 | std::vector available_semaphores; 59 | std::vector finished_semaphores; 60 | 61 | vk::DeviceFunctions* operator->() { return &functions; } 62 | }; 63 | void check_res(bool result, const char* msg) 64 | { 65 | if (!result) { 66 | std::cerr << msg << "\n"; 67 | assert(false); 68 | } 69 | } 70 | void check_res(vk::Result result, const char* msg) 71 | { 72 | if (result != vk::Result::Success) { 73 | std::cerr << msg << ": Result = " << vk::to_string(result) << "\n"; 74 | assert(false); 75 | } 76 | } 77 | template 78 | void check_res(vk::expected const& expected, const char* msg) 79 | { 80 | if (!expected) { 81 | std::cerr << msg << ": Result = " << vk::to_string(expected.error()) << "\n"; 82 | assert(false); 83 | } 84 | } 85 | 86 | GLFWwindow* create_window_glfw(const char* window_name = "", bool resize = true) 87 | { 88 | glfwInit(); 89 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 90 | if (!resize) 91 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 92 | 93 | return glfwCreateWindow(width, height, window_name, NULL, NULL); 94 | } 95 | void destroy_window_glfw(GLFWwindow* window) 96 | { 97 | glfwDestroyWindow(window); 98 | glfwTerminate(); 99 | } 100 | vk::SurfaceKHR create_surface_glfw(VkInstance instance, GLFWwindow* window) 101 | { 102 | VkSurfaceKHR surface = VK_NULL_HANDLE; 103 | VkResult err = glfwCreateWindowSurface(instance, window, NULL, &surface); 104 | if (vk::from_c(err) != vk::Result::Success) { 105 | const char* error_msg; 106 | int ret = glfwGetError(&error_msg); 107 | if (ret != 0) { 108 | std::cerr << ret << " "; 109 | if (error_msg != nullptr) 110 | std::cerr << error_msg; 111 | std::cerr << "\n"; 112 | } 113 | surface = VK_NULL_HANDLE; 114 | } 115 | return vk::SurfaceKHR{ surface }; 116 | } 117 | 118 | void create_renderer_context(RendererContext& context) 119 | { 120 | check_res(context.library.init(), "Failed to init vulkan library"); 121 | 122 | context.window = create_window_glfw("Sample Triangle", false); 123 | check_res(context.window != nullptr, "Failed to create glfw window"); 124 | 125 | vk::GlobalFunctions global_functions = vk::GlobalFunctions(context.library); 126 | 127 | uint32_t glfw_extension_count = 0; 128 | const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count); 129 | vk::InstanceCreateInfo inst_info{}; 130 | inst_info.setEnabledExtensionCount(glfw_extension_count); 131 | inst_info.setPpEnabledExtensionNames(glfw_extensions); 132 | 133 | auto layers = global_functions.EnumerateInstanceLayerProperties().value(); 134 | const char* layer_names[] = { "VK_LAYER_KHRONOS_validation" }; 135 | for (auto& layer : layers) { 136 | if (std::string(layer.layerName) == std::string("VK_LAYER_KHRONOS_validation")) { 137 | // inst_builder.addEnabledLayerNames("VK_LAYER_KHRONOS_validation"); 138 | inst_info.ppEnabledLayerNames = layer_names; 139 | break; 140 | } 141 | } 142 | 143 | context.instance = global_functions.CreateInstance(inst_info).value(); 144 | context.instance_functions = vk::InstanceFunctions(global_functions, context.instance); 145 | 146 | context.surface = create_surface_glfw(context.instance.get(), context.window); 147 | check_res(context.surface.valid(), "Failed to create glfw surface"); 148 | 149 | context.physical_device = 150 | context.instance_functions.EnumeratePhysicalDevices().value().at(0); // get first physical device returned 151 | context.physical_device_functions = vk::PhysicalDeviceFunctions(context.instance_functions, context.physical_device); 152 | 153 | // needed to make the validation layers shut up 154 | auto [surface_supported, support_ret] = context.physical_device_functions.GetSurfaceSupportKHR(0, context.surface); 155 | check_res(support_ret, "Failed to query surface support"); 156 | check_res(surface_supported, "Surface doesn't support present"); 157 | } 158 | 159 | void create_device_context(RendererContext& context) 160 | { 161 | const char* extensions[] = { "VK_KHR_swapchain" }; 162 | context.device = 163 | context.physical_device_functions 164 | .CreateDevice(vk::DeviceCreateInfo{} 165 | .setEnabledExtensionCount(1) 166 | .setPpEnabledExtensionNames(extensions) 167 | .setPQueueCreateInfos({ vk::DeviceQueueCreateInfo{}.setQueueFamilyIndex(0).setPQueuePriorities({ 1.f }) })) 168 | .value(); 169 | context.functions = vk::DeviceFunctions(context.instance_functions, context.device); 170 | context.physical_device_functions = context.physical_device_functions; 171 | } 172 | 173 | void setup_queues(RendererContext& context) 174 | { 175 | auto queue_family_props = context.physical_device_functions.GetQueueFamilyProperties(); 176 | uint32_t graphics_queue_family = 0; 177 | for (uint32_t i = 0; i < queue_family_props.size(); i++) { 178 | if (queue_family_props[0].queueFlags & vk::QueueFlagBits::Graphics && queue_family_props[0].queueCount > 0) { 179 | graphics_queue_family = i; 180 | break; 181 | } 182 | } 183 | 184 | check_res(graphics_queue_family == 0, "First queue isn't the graphics queue, implicit assumptions failed"); 185 | 186 | context.graphics_queue = context->GetDeviceQueue(graphics_queue_family, 0); 187 | context.queue_functions = vk::QueueFunctions(context.functions, context.graphics_queue); 188 | } 189 | 190 | void setup_swapchain(RendererContext& context) 191 | { 192 | auto caps = context.physical_device_functions.GetSurfaceCapabilitiesKHR(context.surface).value(); 193 | context.swapchain_surface_format = context.physical_device_functions.GetSurfaceFormatsKHR(context.surface).value().at(0); 194 | context.image_count = 3; 195 | context.swapchain = context.functions 196 | .CreateSwapchainKHR(vk::SwapchainCreateInfoKHR{ 197 | .surface = context.surface, 198 | .minImageCount = 3, 199 | .imageFormat = context.swapchain_surface_format.format, 200 | .imageColorSpace = context.swapchain_surface_format.colorSpace, 201 | .imageExtent = { width, height }, 202 | .imageArrayLayers = 1, 203 | .imageUsage = vk::ImageUsageFlagBits::ColorAttachment, 204 | .imageSharingMode = vk::SharingMode::Exclusive, 205 | .pQueueFamilyIndices = nullptr, 206 | .preTransform = caps.currentTransform, 207 | .compositeAlpha = vk::CompositeAlphaFlagBitsKHR::OpaqueBitKHR, 208 | .presentMode = vk::PresentModeKHR::FifoKHR, 209 | }) 210 | .value(); 211 | context.swapchain_images = context->GetSwapchainImagesKHR(context.swapchain).value(); 212 | 213 | for (auto& image : context.swapchain_images) { 214 | context.swapchain_image_views.push_back( 215 | context.functions 216 | .CreateImageView(vk::ImageViewCreateInfo{ 217 | .image = image, 218 | .viewType = vk::ImageViewType::e2D, 219 | .format = context.swapchain_surface_format.format, 220 | .subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::Color, .levelCount = 1, .layerCount = 1 } }) 221 | .value()); 222 | } 223 | } 224 | 225 | void setup_renderpass(RendererContext& context) 226 | { 227 | auto attach_desc = vk::AttachmentDescription{} 228 | .setFormat(context.swapchain_surface_format.format) 229 | .setSamples(vk::SampleCountFlagBits::e1) 230 | .setLoadOp(vk::AttachmentLoadOp::Clear) 231 | .setStoreOp(vk::AttachmentStoreOp::Store) 232 | .setStencilLoadOp(vk::AttachmentLoadOp::DontCare) 233 | .setStencilStoreOp(vk::AttachmentStoreOp::DontCare) 234 | .setInitialLayout(vk::ImageLayout::Undefined) 235 | .setFinalLayout(vk::ImageLayout::PresentSrcKHR); 236 | auto subpass_desc = 237 | vk::SubpassDescription{} 238 | .setPipelineBindPoint(vk::PipelineBindPoint::Graphics) 239 | .setPColorAttachments({ vk::AttachmentReference{}.setAttachment(0).setLayout(vk::ImageLayout::ColorAttachmentOptimal) }); 240 | auto subpass_dep = vk::SubpassDependency{} 241 | .setSrcSubpass(vk::SUBPASS_EXTERNAL) 242 | .setDstSubpass(0) 243 | .setSrcStageMask(vk::PipelineStageFlagBits::ColorAttachmentOutput) 244 | .setDstStageMask(vk::PipelineStageFlagBits::ColorAttachmentOutput) 245 | .setDstAccessMask(vk::AccessFlagBits::ColorAttachmentRead | vk::AccessFlagBits::ColorAttachmentWrite); 246 | context.render_pass = context.functions 247 | .CreateRenderPass(vk::RenderPassCreateInfo{} 248 | .setPAttachments({ attach_desc }) 249 | .setPSubpasses({ subpass_desc }) 250 | .setPDependencies({ subpass_dep })) 251 | .value(); 252 | } 253 | void create_framebuffers(RendererContext& context) 254 | { 255 | auto framebuffer = 256 | vk::FramebufferCreateInfo{}.setRenderPass(context.render_pass).setWidth(width).setHeight(height).setLayers(1); 257 | for (auto& view : context.swapchain_image_views) { 258 | context.framebuffers.push_back(context.functions.CreateFramebuffer(framebuffer.setPAttachments({ view })).value()); 259 | } 260 | } 261 | 262 | std::vector read_file(const std::string& filename) 263 | { 264 | std::ifstream file(filename, std::ios::ate | std::ios::binary); 265 | check_res(file.is_open(), "Failed to open shader file"); 266 | 267 | size_t file_size = (size_t)file.tellg(); 268 | std::vector buffer(file_size); 269 | file.seekg(0); 270 | file.read(buffer.data(), static_cast(file_size)); 271 | file.close(); 272 | return buffer; 273 | } 274 | 275 | vk::ShaderModule create_shader_module(RendererContext& context, std::string const& filename) 276 | { 277 | auto code = read_file(filename); 278 | return context.functions 279 | .CreateShaderModule({ .codeSize = code.size(), .pCode = reinterpret_cast(code.data()) }) 280 | .value(); 281 | } 282 | 283 | void create_pipeline(RendererContext& context) 284 | { 285 | vk::ShaderModule vert = create_shader_module(context, "vert.spv"); 286 | vk::ShaderModule frag = create_shader_module(context, "frag.spv"); 287 | 288 | context.pipeline_layout = context->CreatePipelineLayout({ .setLayoutCount = 0, .pushConstantRangeCount = 0 }).value(); 289 | 290 | float blend_constant[4] = { 0.f, 0.f, 0.f, 0.f }; 291 | context.pipeline = 292 | context.functions 293 | .CreateGraphicsPipelines( 294 | nullptr, 295 | { vk::GraphicsPipelineCreateInfo{} 296 | .setPStages( 297 | { vk::PipelineShaderStageCreateInfo{}.setStage(vk::ShaderStageFlagBits::Vertex).setModule(vert).setPName("main"), 298 | vk::PipelineShaderStageCreateInfo{} 299 | .setStage(vk::ShaderStageFlagBits::Fragment) 300 | .setModule(frag) 301 | .setPName("main") }) 302 | .setPVertexInputState(vk::PipelineVertexInputStateCreateInfo{}) 303 | .setPInputAssemblyState(vk::PipelineInputAssemblyStateCreateInfo{}.setTopology(vk::PrimitiveTopology::TriangleList)) 304 | .setPViewportState(vk::PipelineViewportStateCreateInfo{} 305 | .setPViewports({ { 0.f, 0.f, static_cast(width), static_cast(height), 0.f, 1.f } }) 306 | .setPScissors({ { .offset = { 0, 0 }, .extent = { width, height } } })) 307 | .setPRasterizationState(vk::PipelineRasterizationStateCreateInfo{} 308 | .setPolygonMode(vk::PolygonMode::Fill) 309 | .setCullMode(vk::CullModeFlagBits::Back) 310 | .setFrontFace(vk::FrontFace::Clockwise) 311 | .setLineWidth(1.f)) 312 | .setPMultisampleState(vk::PipelineMultisampleStateCreateInfo{} 313 | .setRasterizationSamples(vk::SampleCountFlagBits::e1) 314 | .setSampleShadingEnable(false)) 315 | .setPColorBlendState( 316 | vk::PipelineColorBlendStateCreateInfo{} 317 | .setLogicOpEnable(false) 318 | .setPAttachments({ vk::PipelineColorBlendAttachmentState{}.setBlendEnable(false).setColorWriteMask( 319 | vk::ColorComponentFlagBits::R | vk::ColorComponentFlagBits::G | vk::ColorComponentFlagBits::B | 320 | vk::ColorComponentFlagBits::A) }) 321 | .setBlendConstants(blend_constant)) 322 | .setPDynamicState( 323 | vk::PipelineDynamicStateCreateInfo{}.setPDynamicStates({ vk::DynamicState::Viewport, vk::DynamicState::Scissor })) 324 | .setLayout(context.pipeline_layout) 325 | .setRenderPass(context.render_pass) }) 326 | .value()[0]; 327 | 328 | context->DestroyShaderModule(vert); 329 | context->DestroyShaderModule(frag); 330 | } 331 | 332 | void create_command_buffers(RendererContext& context) 333 | { 334 | context.cmd_pool = context->CreateCommandPool({ .queueFamilyIndex = 0 }).value(); 335 | context.cmd_buffers = 336 | context.functions 337 | .AllocateCommandBuffers( 338 | { .commandPool = context.cmd_pool, .level = vk::CommandBufferLevel::Primary, .commandBufferCount = context.image_count }) 339 | .value(); 340 | 341 | for (uint32_t i = 0; i < context.cmd_buffers.size(); i++) { 342 | vk::CommandBufferFunctions funcs(context.functions, context.cmd_buffers[i]); 343 | check_res(funcs.Begin({}), "Failed to begin command buffer"); 344 | 345 | vk::Rect2D scissor = { .offset = { 0, 0 }, .extent = { width, height } }; 346 | check_res(funcs 347 | .BeginRenderPass(vk::RenderPassBeginInfo{} 348 | .setRenderPass(context.render_pass) 349 | .setFramebuffer(context.framebuffers[i]) 350 | .setRenderArea(scissor) 351 | .setPClearValues({ vk::ClearValue{ .color = { { 0.f, 0.f, 0.f, 1.f } } } }), 352 | vk::SubpassContents::Inline) 353 | .BindPipeline(vk::PipelineBindPoint::Graphics, context.pipeline) 354 | .SetViewport(0, vk::Viewport{ 0.f, 0.f, static_cast(width), static_cast(height), 0.f, 1.f }) 355 | .SetScissor(0, scissor) 356 | .Draw(3, 1, 0, 0) 357 | .EndRenderPass() 358 | .End(), 359 | "Failed to end command buffer"); 360 | } 361 | } 362 | 363 | void setup_sync_objects(RendererContext& context) 364 | { 365 | context.fences.resize(context.image_count); 366 | context.available_semaphores.resize(context.image_count); 367 | context.finished_semaphores.resize(context.image_count); 368 | for (uint32_t i = 0; i < context.image_count; i++) { 369 | context.fences[i] = context->CreateFence({ .flags = vk::FenceCreateFlagBits::Signaled }).value(); 370 | context.available_semaphores[i] = context->CreateSemaphore({}).value(); 371 | context.finished_semaphores[i] = context->CreateSemaphore({}).value(); 372 | } 373 | } 374 | 375 | void recreate_swapchain(RendererContext& context) 376 | { 377 | check_res(context.queue_functions.WaitIdle(), ""); 378 | context->DestroyCommandPool(context.cmd_pool); 379 | 380 | for (auto& framebuffer : context.framebuffers) { 381 | context->DestroyFramebuffer(framebuffer); 382 | } 383 | for (auto& image_view : context.swapchain_image_views) { 384 | context->DestroyImageView(image_view); 385 | } 386 | 387 | setup_swapchain(context); 388 | create_framebuffers(context); 389 | create_command_buffers(context); 390 | } 391 | 392 | void draw_frame(RendererContext& context) 393 | { 394 | check_res(context->WaitForFences({ context.fences[context.current_frame] }, true, UINT64_MAX), "Failed to wait for fence"); 395 | 396 | check_res(context->ResetFences({ context.fences[context.current_frame] }), "Failed to reset fence"); 397 | 398 | auto image_index_ret = 399 | context->AcquireNextImageKHR(context.swapchain, UINT64_MAX, context.available_semaphores[context.current_frame], nullptr); 400 | if (image_index_ret.raw_result() == vk::Result::ErrorOutOfDateKHR) { 401 | return recreate_swapchain(context); 402 | } else if (image_index_ret.raw_result() != vk::Result::Success && image_index_ret.raw_result() != vk::Result::SuboptimalKHR) { 403 | std::cerr << "failed to acquire swapchain image. Error " << vk::to_string(image_index_ret.raw_result()) << "\n"; 404 | } 405 | 406 | check_res(context.queue_functions.Submit(vk::SubmitInfo{} 407 | .setPWaitSemaphores({ context.available_semaphores[context.current_frame] }) 408 | .setPWaitDstStageMask({ vk::PipelineStageFlagBits::ColorAttachmentOutput }) 409 | .setPCommandBuffers({ context.cmd_buffers[image_index_ret.value()] }) 410 | .setPSignalSemaphores({ context.finished_semaphores[context.current_frame] }), 411 | context.fences[context.current_frame]), 412 | "Failed to submit command buffer"); 413 | 414 | auto present_ret = 415 | context.queue_functions.PresentKHR(vk::PresentInfoKHR{} 416 | .setPWaitSemaphores({ context.finished_semaphores[context.current_frame] }) 417 | .setPSwapchains({ context.swapchain }) 418 | .setPImageIndices({ image_index_ret.value() })); 419 | if (present_ret == vk::Result::ErrorOutOfDateKHR || present_ret == vk::Result::SuboptimalKHR) { 420 | return recreate_swapchain(context); 421 | } 422 | check_res(present_ret, "Failed to present"); 423 | 424 | context.current_frame = (context.current_frame + 1) % context.image_count; 425 | } 426 | 427 | void destroy_device(RendererContext& context) 428 | { 429 | for (auto& sem : context.available_semaphores) { 430 | context->DestroySemaphore(sem); 431 | } 432 | for (auto& sem : context.finished_semaphores) { 433 | context->DestroySemaphore(sem); 434 | } 435 | for (auto& fence : context.fences) { 436 | context->DestroyFence(fence); 437 | } 438 | context->DestroyCommandPool(context.cmd_pool); 439 | for (auto& framebuffer : context.framebuffers) { 440 | context->DestroyFramebuffer(framebuffer); 441 | } 442 | context->DestroyPipeline(context.pipeline); 443 | context->DestroyPipelineLayout(context.pipeline_layout); 444 | context->DestroyRenderPass(context.render_pass); 445 | for (auto& image_view : context.swapchain_image_views) { 446 | context->DestroyImageView(image_view); 447 | } 448 | context->DestroySwapchainKHR(context.swapchain); 449 | context->DestroyDevice(); 450 | } 451 | 452 | void destroy_renderer(RendererContext& renderer) 453 | { 454 | renderer.instance_functions.DestroySurfaceKHR(renderer.surface); 455 | renderer.instance_functions.DestroyInstance(); 456 | destroy_window_glfw(renderer.window); 457 | } 458 | 459 | int main() 460 | { 461 | RendererContext context; 462 | 463 | create_renderer_context(context); 464 | create_device_context(context); 465 | setup_queues(context); 466 | setup_swapchain(context); 467 | setup_renderpass(context); 468 | create_framebuffers(context); 469 | create_pipeline(context); 470 | create_command_buffers(context); 471 | setup_sync_objects(context); 472 | 473 | while (!glfwWindowShouldClose(context.window)) { 474 | glfwPollEvents(); 475 | draw_frame(context); 476 | } 477 | check_res(context.queue_functions.WaitIdle(), "Couldn't wait to shut down"); 478 | destroy_device(context); 479 | destroy_renderer(context); 480 | return 0; 481 | } -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 4 | # (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 5 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 6 | # so, subject to the following conditions: 7 | 8 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 11 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 12 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | 15 | include(FetchContent) 16 | FetchContent_Declare( 17 | catch2 18 | GIT_REPOSITORY https://github.com/catchorg/Catch2 19 | GIT_TAG v2.13.1 20 | ) 21 | FetchContent_MakeAvailable(catch2) 22 | 23 | find_package(Vulkan REQUIRED) 24 | 25 | add_subdirectory(simple-api) 26 | add_subdirectory(fancy-api) -------------------------------------------------------------------------------- /test/fancy-api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 4 | # (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 5 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 6 | # so, subject to the following conditions: 7 | 8 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 11 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 12 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | 15 | add_executable(vk-module-static-checks static_test.cpp) 16 | target_link_libraries(vk-module-static-checks vk-module-cpp Vulkan::Headers) 17 | 18 | add_executable(vk-module-test main.cpp flags_test.cpp functions_test.cpp) 19 | target_link_libraries(vk-module-test vk-module-cpp Vulkan::Vulkan Catch2) 20 | -------------------------------------------------------------------------------- /test/fancy-api/flags_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 5 | * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 6 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | * furnished to do so, subject to the following conditions: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 12 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 13 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include "vk_module.h" 19 | 20 | TEST_CASE("Flags bitwise operation", "[vk-module.flags]") 21 | { 22 | 23 | REQUIRE((vk::BufferCreateFlagBits::SparseBinding | vk::BufferCreateFlagBits::SparseResidency).flags == 3); 24 | REQUIRE((vk::BufferCreateFlagBits::SparseBinding & vk::BufferCreateFlagBits::SparseBinding).flags == 1U); 25 | REQUIRE((vk::BufferCreateFlagBits::SparseBinding ^ vk::BufferCreateFlagBits::SparseResidency).flags == 3); 26 | REQUIRE((~(vk::BufferCreateFlagBits::SparseResidency)).flags == 0xfffffffd); 27 | 28 | vk::BufferCreateFlags flags; 29 | flags = ~vk::BufferCreateFlagBits::SparseBinding; 30 | REQUIRE(flags.flags == 0xfffffffe); 31 | flags = vk::BufferCreateFlagBits::SparseResidency; 32 | REQUIRE(flags.flags == 2); 33 | flags |= vk::BufferCreateFlagBits::SparseBinding; 34 | REQUIRE(flags.flags == 3); 35 | flags &= vk::BufferCreateFlagBits::SparseBinding; 36 | REQUIRE(flags.flags == 1); 37 | flags ^= vk::BufferCreateFlagBits::SparseAliased; 38 | REQUIRE(flags.flags == 5); 39 | 40 | flags = flags | vk::BufferCreateFlagBits::SparseBinding; 41 | REQUIRE(flags.flags == 5); 42 | flags = flags & vk::BufferCreateFlagBits::SparseBinding; 43 | REQUIRE(flags.flags == 1); 44 | flags = vk::BufferCreateFlagBits::SparseResidency | flags; 45 | REQUIRE(flags.flags == 3); 46 | flags |= vk::BufferCreateFlags(3U); 47 | REQUIRE(flags.flags == 3); 48 | flags = vk::BufferCreateFlagBits::SparseBinding & flags; 49 | REQUIRE(flags.flags == 1); 50 | flags &= vk::BufferCreateFlags(1U); 51 | REQUIRE(flags.flags == 1); 52 | flags = ~flags; 53 | REQUIRE(flags.flags == 0xfffffffe); 54 | flags = vk::BufferCreateFlagBits::SparseResidency; 55 | flags = vk::BufferCreateFlagBits::SparseBinding ^ flags; 56 | REQUIRE(flags.flags == 3); 57 | flags = flags ^ vk::BufferCreateFlagBits::SparseBinding; 58 | REQUIRE(flags.flags == 2); 59 | flags ^= vk::BufferCreateFlags(2U); 60 | REQUIRE(flags.flags == 0); 61 | 62 | // VkImageUsageFlagBits c_flag_bits = c_enum(vk::ImageUsageFlagBits::TransferDst); 63 | // REQUIRE(c_flag_bits == 2); 64 | } -------------------------------------------------------------------------------- /test/fancy-api/functions_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 5 | * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 6 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | * furnished to do so, subject to the following conditions: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 12 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 13 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | */ 16 | 17 | #include 18 | #include "vk_module.h" 19 | 20 | TEST_CASE("Global Function Usage", "[vk-module.functions]") 21 | { 22 | vk::DynamicLibrary library; 23 | vk::Result res = library.init(); 24 | REQUIRE(library.is_init()); 25 | REQUIRE(res == vk::Result::Success); 26 | vk::GlobalFunctions free_funcs(library); 27 | auto layers_ret = free_funcs.EnumerateInstanceLayerProperties(); 28 | REQUIRE(layers_ret.raw_result() == vk::Result::Success); 29 | 30 | auto exts_ret = free_funcs.EnumerateInstanceExtensionProperties(nullptr); 31 | REQUIRE(exts_ret.raw_result() == vk::Result::Success); 32 | uint32_t version = free_funcs.EnumerateInstanceVersion().value(); 33 | 34 | vk::ApplicationInfo app_info; 35 | app_info.apiVersion = version; 36 | vk::InstanceCreateInfo info; 37 | info.pApplicationInfo = &app_info; 38 | auto inst_ret = free_funcs.CreateInstance(info); 39 | REQUIRE(inst_ret.raw_result() == vk::Result::Success); 40 | } 41 | 42 | TEST_CASE("Manual .init()", "[vk-module.functions]") 43 | { 44 | vk::DynamicLibrary library; 45 | vk::Result res = library.init(); 46 | REQUIRE(library.is_init()); 47 | REQUIRE(res == vk::Result::Success); 48 | vk::GlobalFunctions free_funcs(library); 49 | REQUIRE(free_funcs.pfn_CreateInstance != nullptr); 50 | REQUIRE(free_funcs.pfn_EnumerateInstanceExtensionProperties != nullptr); 51 | REQUIRE(free_funcs.pfn_EnumerateInstanceLayerProperties != nullptr); 52 | #if defined(VK_VERSION_1_1) 53 | REQUIRE(free_funcs.pfn_EnumerateInstanceVersion != nullptr); 54 | #endif 55 | } 56 | TEST_CASE("Constructor init", "[vk-module.functions]") 57 | { 58 | vk::DynamicLibrary library(vk::DynamicLibrary::LoadAtConstruction{}); 59 | REQUIRE(library.is_init()); 60 | vk::GlobalFunctions free_funcs(library); 61 | REQUIRE(free_funcs.pfn_CreateInstance != nullptr); 62 | REQUIRE(free_funcs.pfn_EnumerateInstanceExtensionProperties != nullptr); 63 | REQUIRE(free_funcs.pfn_EnumerateInstanceLayerProperties != nullptr); 64 | #if defined(VK_VERSION_1_1) 65 | REQUIRE(free_funcs.pfn_EnumerateInstanceVersion != nullptr); 66 | #endif 67 | } 68 | #include 69 | 70 | TEST_CASE("Pass vkGetInstanceProcAddr to constructor", "[vk-module.functions]") 71 | { 72 | vk::DynamicLibrary library(vkGetInstanceProcAddr); 73 | REQUIRE(library.is_init()); 74 | vk::GlobalFunctions free_funcs(library); 75 | REQUIRE(free_funcs.pfn_CreateInstance != nullptr); 76 | REQUIRE(free_funcs.pfn_EnumerateInstanceExtensionProperties != nullptr); 77 | REQUIRE(free_funcs.pfn_EnumerateInstanceLayerProperties != nullptr); 78 | #if defined(VK_VERSION_1_1) 79 | REQUIRE(free_funcs.pfn_EnumerateInstanceVersion != nullptr); 80 | #endif 81 | } 82 | TEST_CASE("Pass vkGetInstanceProcAddr to init", "[vk-module.functions]") 83 | { 84 | vk::DynamicLibrary library; 85 | vk::Result res = library.init(vkGetInstanceProcAddr); 86 | REQUIRE(library.is_init()); 87 | REQUIRE(res == vk::Result::Success); 88 | vk::GlobalFunctions free_funcs(library); 89 | REQUIRE(free_funcs.pfn_CreateInstance != nullptr); 90 | REQUIRE(free_funcs.pfn_EnumerateInstanceExtensionProperties != nullptr); 91 | REQUIRE(free_funcs.pfn_EnumerateInstanceLayerProperties != nullptr); 92 | #if defined(VK_VERSION_1_1) 93 | REQUIRE(free_funcs.pfn_EnumerateInstanceVersion != nullptr); 94 | #endif 95 | } 96 | 97 | TEST_CASE("Create instance functions", "[vk-module.functions]") 98 | { 99 | vk::DynamicLibrary library{ vkGetInstanceProcAddr }; 100 | vk::GlobalFunctions free_funcs(library); 101 | 102 | vk::InstanceCreateInfo info; 103 | auto ret = free_funcs.CreateInstance(info); 104 | if (!ret) 105 | return; 106 | REQUIRE(ret.raw_result() == vk::Result::Success); 107 | vk::Instance inst = ret.value(); 108 | 109 | vk::InstanceFunctions inst_funcs(free_funcs, inst); 110 | REQUIRE(inst_funcs.pfn_EnumeratePhysicalDevices != nullptr); 111 | auto devices_ret = inst_funcs.EnumeratePhysicalDevices(); 112 | REQUIRE(devices_ret.raw_result() == vk::Result::Success); 113 | } -------------------------------------------------------------------------------- /test/fancy-api/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 5 | * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 6 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | * furnished to do so, subject to the following conditions: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 12 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 13 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | */ 16 | 17 | #define CATCH_CONFIG_MAIN 18 | #include 19 | 20 | #include "vk_module.h" 21 | 22 | TEST_CASE("Union operations", "[vk-module.union]") 23 | { 24 | vk::ClearColorValue x, y; 25 | x.float32[0] = 1.f; 26 | y.uint32[0] = 1; 27 | REQUIRE((x == y) == false); 28 | REQUIRE((x != y) == true); 29 | } 30 | TEST_CASE("Struct operations", "[vk-module.struct]") 31 | { 32 | vk::Extent2D extent = { 100, 200 }; 33 | REQUIRE(extent.width == 100); 34 | REQUIRE(extent.height == 200); 35 | } -------------------------------------------------------------------------------- /test/fancy-api/static_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 5 | * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 6 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | * furnished to do so, subject to the following conditions: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 12 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 13 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | */ 16 | 17 | #include "static_asserts.h" 18 | 19 | int main() 20 | { 21 | return 0; 22 | } -------------------------------------------------------------------------------- /test/simple-api/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 2 | 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 4 | # (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 5 | # publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 6 | # so, subject to the following conditions: 7 | 8 | # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 11 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 12 | # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 13 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 14 | 15 | -------------------------------------------------------------------------------- /test/simple-api/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Charles Giessen (cdgiessen@gmail.com) 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 5 | * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 6 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | * furnished to do so, subject to the following conditions: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 12 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 13 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | */ 16 | 17 | #define CATCH_CONFIG_MAIN 18 | #include 19 | 20 | #include "vulkan/vulkan.h" --------------------------------------------------------------------------------