├── .gitignore ├── .gitmodules ├── 3rd_party ├── others │ ├── stb_image.h │ └── tiny_obj_loader.h └── vulkan-header │ └── vulkan │ ├── vk_platform.h │ └── vulkan.h ├── CH1-Draw_a_triangle ├── 0_Setup │ ├── 0_base_code.cpp │ ├── 1_instance.cpp │ ├── 2_Validation_layers.cpp │ ├── 3_physical_device_selection.cpp │ ├── 4_logical_device.cpp │ └── CMakeLists.txt ├── 1_Presentation │ ├── 1_window_surface.cpp │ ├── 2_swapchain.cpp │ ├── 3_image_views.cpp │ └── CMakeLists.txt ├── 2_Graphics_pipline_basics │ ├── 1_shader_modules.cpp │ ├── 2_fixed_functions.cpp │ ├── 3_render_passes.cpp │ ├── 4_graphics_pipeline_complete.cpp │ └── CMakeLists.txt ├── 3_Drawing │ ├── 1_framebuffers.cpp │ ├── 2_command_buffers.cpp │ ├── 3_rendering_and_presentation.cpp │ └── CMakeLists.txt ├── 4_swap_chain_recreation │ ├── 1_swap_chain_recreation.cpp │ └── CMakeLists.txt ├── CMakeLists.txt ├── basic.frag ├── basic.vert ├── frag.spv ├── hello_triangle.cpp └── vert.spv ├── CH2-Vertex_buffer ├── 1_vertex_input_description.cpp ├── 2_vertex_buffer_creation.cpp ├── 3_staging_buffer.cpp ├── 4_index_buffer.cpp ├── CMakeLists.txt ├── frag.spv ├── shader.frag ├── shader.vert └── vert.spv ├── CH3-Uinform_buffer ├── 1_descriptor_layout_and_buffer.cpp ├── 2_descriptor_pool_and_sets.cpp ├── CMakeLists.txt ├── frag.spv ├── shader.frag ├── shader.vert └── vert.spv ├── CH4-Texture_mapping ├── 1_texture_image.cpp ├── 2_image_view_and_sampler.cpp ├── 3_combined_image_sampler.cpp ├── CMakeLists.txt ├── frag.spv ├── shader.frag ├── shader.vert └── vert.spv ├── CH5-Depth_buffer ├── CMakeLists.txt ├── depth_buffering.cpp ├── frag.spv ├── shader.frag ├── shader.vert └── vert.spv ├── CH6-Loading_model ├── CMakeLists.txt ├── frag.spv ├── loading_model.cpp ├── shader.frag ├── shader.vert ├── vert.spv └── vertex_deduplication.cpp ├── CMakeLists.txt ├── LICENSE ├── QtVulkanTest ├── camera.PNG ├── color.frag ├── color.vert ├── color_frag.spv ├── color_vert.spv ├── main.cpp ├── tmp.qrc ├── vulkanTest.pro ├── vulkanTest.pro.user ├── vulkantest.qrc ├── vulkanwindow.cpp ├── vulkanwindow.h ├── vulkanwindowrenderer.cpp └── vulkanwindowrenderer.h ├── README.md ├── cmake └── modules │ └── FindVulkan.cmake └── resources ├── chalet.jpg ├── chalet.obj.zip └── texture.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | CMakeScripts 4 | Testing 5 | Makefile 6 | cmake_install.cmake 7 | install_manifest.txt 8 | compile_commands.json 9 | CTestTestfile.cmake 10 | ### Example user template template 11 | ### Example user template 12 | 13 | # IntelliJ project files 14 | .idea 15 | *.iml 16 | out 17 | gen### macOS template 18 | # General 19 | .DS_Store 20 | .AppleDouble 21 | .LSOverride 22 | 23 | # Icon must end with two \r 24 | Icon 25 | 26 | # Thumbnails 27 | ._* 28 | 29 | # Files that might appear in the root of a volume 30 | .DocumentRevisions-V100 31 | .fseventsd 32 | .Spotlight-V100 33 | .TemporaryItems 34 | .Trashes 35 | .VolumeIcon.icns 36 | .com.apple.timemachine.donotpresent 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | ### CMake template 45 | CMakeCache.txt 46 | CMakeFiles 47 | CMakeScripts 48 | Testing 49 | Makefile 50 | cmake_install.cmake 51 | install_manifest.txt 52 | compile_commands.json 53 | CTestTestfile.cmake 54 | ### Windows template 55 | # Windows thumbnail cache files 56 | Thumbs.db 57 | ehthumbs.db 58 | ehthumbs_vista.db 59 | 60 | # Dump file 61 | *.stackdump 62 | 63 | # Folder config file 64 | Desktop.ini 65 | 66 | # Recycle Bin used on file shares 67 | $RECYCLE.BIN/ 68 | 69 | # Windows Installer files 70 | *.cab 71 | *.msi 72 | *.msm 73 | *.msp 74 | 75 | # Windows shortcuts 76 | *.lnk 77 | 78 | cmake-build-debug/ 79 | 80 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rd_party/glfw"] 2 | path = 3rd_party/glfw 3 | url = https://github.com/glfw/glfw.git 4 | [submodule "3rd_party/glm"] 5 | path = 3rd_party/glm 6 | url = https://github.com/g-truc/glm.git 7 | -------------------------------------------------------------------------------- /3rd_party/vulkan-header/vulkan/vk_platform.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: vk_platform.h 3 | // 4 | /* 5 | ** Copyright (c) 2014-2017 The Khronos Group Inc. 6 | ** 7 | ** Licensed under the Apache License, Version 2.0 (the "License"); 8 | ** you may not use this file except in compliance with the License. 9 | ** You may obtain a copy of the License at 10 | ** 11 | ** http://www.apache.org/licenses/LICENSE-2.0 12 | ** 13 | ** Unless required by applicable law or agreed to in writing, software 14 | ** distributed under the License is distributed on an "AS IS" BASIS, 15 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | ** See the License for the specific language governing permissions and 17 | ** limitations under the License. 18 | */ 19 | 20 | 21 | #ifndef VK_PLATFORM_H_ 22 | #define VK_PLATFORM_H_ 23 | 24 | #ifdef __cplusplus 25 | extern "C" 26 | { 27 | #endif // __cplusplus 28 | 29 | /* 30 | *************************************************************************************************** 31 | * Platform-specific directives and type declarations 32 | *************************************************************************************************** 33 | */ 34 | 35 | /* Platform-specific calling convention macros. 36 | * 37 | * Platforms should define these so that Vulkan clients call Vulkan commands 38 | * with the same calling conventions that the Vulkan implementation expects. 39 | * 40 | * VKAPI_ATTR - Placed before the return type in function declarations. 41 | * Useful for C++11 and GCC/Clang-style function attribute syntax. 42 | * VKAPI_CALL - Placed after the return type in function declarations. 43 | * Useful for MSVC-style calling convention syntax. 44 | * VKAPI_PTR - Placed between the '(' and '*' in function pointer types. 45 | * 46 | * Function declaration: VKAPI_ATTR void VKAPI_CALL vkCommand(void); 47 | * Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void); 48 | */ 49 | #if defined(_WIN32) 50 | // On Windows, Vulkan commands use the stdcall convention 51 | #define VKAPI_ATTR 52 | #define VKAPI_CALL __stdcall 53 | #define VKAPI_PTR VKAPI_CALL 54 | #elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7 55 | #error "Vulkan isn't supported for the 'armeabi' NDK ABI" 56 | #elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE) 57 | // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat" 58 | // calling convention, i.e. float parameters are passed in registers. This 59 | // is true even if the rest of the application passes floats on the stack, 60 | // as it does by default when compiling for the armeabi-v7a NDK ABI. 61 | #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp"))) 62 | #define VKAPI_CALL 63 | #define VKAPI_PTR VKAPI_ATTR 64 | #else 65 | // On other platforms, use the default calling convention 66 | #define VKAPI_ATTR 67 | #define VKAPI_CALL 68 | #define VKAPI_PTR 69 | #endif 70 | 71 | #include 72 | 73 | #if !defined(VK_NO_STDINT_H) 74 | #if defined(_MSC_VER) && (_MSC_VER < 1600) 75 | typedef signed __int8 int8_t; 76 | typedef unsigned __int8 uint8_t; 77 | typedef signed __int16 int16_t; 78 | typedef unsigned __int16 uint16_t; 79 | typedef signed __int32 int32_t; 80 | typedef unsigned __int32 uint32_t; 81 | typedef signed __int64 int64_t; 82 | typedef unsigned __int64 uint64_t; 83 | #else 84 | #include 85 | #endif 86 | #endif // !defined(VK_NO_STDINT_H) 87 | 88 | #ifdef __cplusplus 89 | } // extern "C" 90 | #endif // __cplusplus 91 | 92 | // Platform-specific headers required by platform window system extensions. 93 | // These are enabled prior to #including "vulkan.h". The same enable then 94 | // controls inclusion of the extension interfaces in vulkan.h. 95 | 96 | #ifdef VK_USE_PLATFORM_ANDROID_KHR 97 | #include 98 | #endif 99 | 100 | #ifdef VK_USE_PLATFORM_MIR_KHR 101 | #include 102 | #endif 103 | 104 | #ifdef VK_USE_PLATFORM_WAYLAND_KHR 105 | #include 106 | #endif 107 | 108 | #ifdef VK_USE_PLATFORM_WIN32_KHR 109 | #include 110 | #endif 111 | 112 | #ifdef VK_USE_PLATFORM_XLIB_KHR 113 | #include 114 | #endif 115 | 116 | #ifdef VK_USE_PLATFORM_XCB_KHR 117 | #include 118 | #endif 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/0_Setup/0_base_code.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by AICDG on 2018/2/6. 3 | // 4 | 5 | #define GLFW_INCLUDE_VULKAN 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | const int WIDTH = 800; 13 | const int HEIGHT = 600; 14 | 15 | class HelloTriangleApplication { 16 | public: 17 | void run() { 18 | initWindow(); 19 | initVulkan(); 20 | mainLoop(); 21 | cleanup(); 22 | } 23 | 24 | private: 25 | void initWindow() { 26 | glfwInit(); 27 | 28 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 29 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 30 | 31 | mWindow = glfwCreateWindow(WIDTH, HEIGHT, "vulkan", nullptr, nullptr); 32 | } 33 | 34 | void initVulkan() { 35 | 36 | } 37 | 38 | void mainLoop() { 39 | while (!glfwWindowShouldClose(mWindow)) { 40 | glfwPollEvents(); 41 | } 42 | } 43 | 44 | void cleanup() { 45 | glfwDestroyWindow(mWindow); 46 | 47 | glfwTerminate(); 48 | } 49 | 50 | GLFWwindow* mWindow; 51 | }; 52 | 53 | int main() { 54 | HelloTriangleApplication app; 55 | 56 | try { 57 | app.run(); 58 | } catch (const std::runtime_error& e) { 59 | std::cerr << e.what() << std::endl; 60 | return EXIT_FAILURE; 61 | } 62 | 63 | return EXIT_SUCCESS; 64 | } 65 | -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/0_Setup/1_instance.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Created by AICDG on 2018/2/6. 3 | // 4 | 5 | #define GLFW_INCLUDE_VULKAN 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | const int WIDTH = 800; 13 | const int HEIGHT = 600; 14 | 15 | class HelloTriangleApplication { 16 | public: 17 | void run() { 18 | initWindow(); 19 | initVulkan(); 20 | mainLoop(); 21 | cleanup(); 22 | } 23 | 24 | private: 25 | void initWindow() { 26 | glfwInit(); 27 | 28 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 29 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 30 | 31 | mWindow = glfwCreateWindow(WIDTH, HEIGHT, "vulkan", nullptr, nullptr); 32 | } 33 | 34 | void initVulkan() { 35 | createInstance(); 36 | } 37 | 38 | void mainLoop() { 39 | while (!glfwWindowShouldClose(mWindow)) { 40 | glfwPollEvents(); 41 | } 42 | } 43 | 44 | void cleanup() { 45 | vkDestroyInstance(mInstance, nullptr); 46 | 47 | glfwDestroyWindow(mWindow); 48 | 49 | glfwTerminate(); 50 | } 51 | 52 | void createInstance() { 53 | VkApplicationInfo appInfo = {}; 54 | appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 55 | appInfo.pApplicationName = "Hello Triangle"; 56 | appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 57 | appInfo.pEngineName = "No Engine"; 58 | appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 59 | appInfo.apiVersion = VK_API_VERSION_1_0; 60 | 61 | VkInstanceCreateInfo createInfo = {}; 62 | createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 63 | createInfo.pApplicationInfo = &appInfo; 64 | 65 | uint32_t glfwExtensionCount = 0; 66 | const char** glfwExtensions; 67 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 68 | 69 | createInfo.enabledExtensionCount = glfwExtensionCount; 70 | createInfo.ppEnabledExtensionNames = glfwExtensions; 71 | 72 | createInfo.enabledLayerCount = 0; 73 | 74 | if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS) { 75 | throw std::runtime_error("Failed to create VkInstance"); 76 | } 77 | } 78 | 79 | GLFWwindow* mWindow; 80 | 81 | VkInstance mInstance; 82 | }; 83 | 84 | int main() { 85 | HelloTriangleApplication app; 86 | 87 | try { 88 | app.run(); 89 | } catch (const std::runtime_error& e) { 90 | std::cerr << e.what() << std::endl; 91 | return EXIT_FAILURE; 92 | } 93 | 94 | return EXIT_SUCCESS; 95 | } 96 | -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/0_Setup/2_Validation_layers.cpp: -------------------------------------------------------------------------------- 1 | #define GLFW_INCLUDE_VULKAN 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | const int WIDTH = 800; 10 | const int HEIGHT = 600; 11 | 12 | const std::vector validationLayers = { 13 | "VK_LAYER_LUNARG_standard_validation" 14 | }; 15 | 16 | #ifdef NDEBUG 17 | const bool enableValidationLayers = false; 18 | #else 19 | const bool enableValidationLayers = true; 20 | #endif 21 | 22 | VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) { 23 | auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"); 24 | if (func != nullptr) { 25 | return func(instance, pCreateInfo, pAllocator, pCallback); 26 | } else { 27 | return VK_ERROR_EXTENSION_NOT_PRESENT; 28 | } 29 | } 30 | 31 | void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) { 32 | auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); 33 | if (func != nullptr) { 34 | func(instance, callback, pAllocator); 35 | } 36 | } 37 | 38 | class HelloTriangleApplication { 39 | public: 40 | void run() { 41 | initWindow(); 42 | initVulkan(); 43 | mainLoop(); 44 | cleanup(); 45 | } 46 | 47 | private: 48 | GLFWwindow* mWindow; 49 | 50 | VkInstance mInstance; 51 | VkDebugReportCallbackEXT mCallback; 52 | 53 | void initWindow() { 54 | glfwInit(); 55 | 56 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 57 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 58 | 59 | mWindow = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 60 | } 61 | 62 | void initVulkan() { 63 | createInstance(); 64 | setupDebugCallback(); 65 | } 66 | 67 | void mainLoop() { 68 | while (!glfwWindowShouldClose(mWindow)) { 69 | glfwPollEvents(); 70 | } 71 | } 72 | 73 | void cleanup() { 74 | DestroyDebugReportCallbackEXT(mInstance, mCallback, nullptr); 75 | vkDestroyInstance(mInstance, nullptr); 76 | 77 | glfwDestroyWindow(mWindow); 78 | 79 | glfwTerminate(); 80 | } 81 | 82 | void createInstance() { 83 | if (enableValidationLayers && !checkValidationLayerSupport()) { 84 | throw std::runtime_error("validation layers requested, but not available!"); 85 | } 86 | 87 | VkApplicationInfo appInfo = {}; 88 | appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 89 | appInfo.pApplicationName = "Hello Triangle"; 90 | appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 91 | appInfo.pEngineName = "No Engine"; 92 | appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 93 | appInfo.apiVersion = VK_API_VERSION_1_0; 94 | 95 | VkInstanceCreateInfo createInfo = {}; 96 | createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 97 | createInfo.pApplicationInfo = &appInfo; 98 | 99 | auto extensions = getRequiredExtensions(); 100 | createInfo.enabledExtensionCount = static_cast(extensions.size()); 101 | createInfo.ppEnabledExtensionNames = extensions.data(); 102 | 103 | if (enableValidationLayers) { 104 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 105 | createInfo.ppEnabledLayerNames = validationLayers.data(); 106 | } else { 107 | createInfo.enabledLayerCount = 0; 108 | } 109 | 110 | if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS) { 111 | throw std::runtime_error("failed to create mInstance!"); 112 | } 113 | } 114 | 115 | void setupDebugCallback() { 116 | if (!enableValidationLayers) return; 117 | 118 | VkDebugReportCallbackCreateInfoEXT createInfo = {}; 119 | createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; 120 | createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; 121 | createInfo.pfnCallback = debugCallback; 122 | 123 | if (CreateDebugReportCallbackEXT(mInstance, &createInfo, nullptr, &mCallback) != VK_SUCCESS) { 124 | throw std::runtime_error("failed to set up debug mCallback!"); 125 | } 126 | } 127 | 128 | std::vector getRequiredExtensions() { 129 | uint32_t glfwExtensionCount = 0; 130 | const char** glfwExtensions; 131 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 132 | 133 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 134 | 135 | if (enableValidationLayers) { 136 | extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); 137 | } 138 | 139 | return extensions; 140 | } 141 | 142 | bool checkValidationLayerSupport() { 143 | uint32_t layerCount; 144 | vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 145 | 146 | std::vector availableLayers(layerCount); 147 | vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 148 | 149 | for (const char* layerName : validationLayers) { 150 | bool layerFound = false; 151 | 152 | for (const auto& layerProperties : availableLayers) { 153 | if (strcmp(layerName, layerProperties.layerName) == 0) { 154 | layerFound = true; 155 | break; 156 | } 157 | } 158 | 159 | if (!layerFound) { 160 | return false; 161 | } 162 | } 163 | 164 | return true; 165 | } 166 | 167 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) { 168 | std::cerr << "validation layer: " << msg << std::endl; 169 | 170 | return VK_FALSE; 171 | } 172 | }; 173 | 174 | int main() { 175 | HelloTriangleApplication app; 176 | 177 | try { 178 | app.run(); 179 | } catch (const std::runtime_error& e) { 180 | std::cerr << e.what() << std::endl; 181 | return EXIT_FAILURE; 182 | } 183 | 184 | return EXIT_SUCCESS; 185 | } 186 | -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/0_Setup/3_physical_device_selection.cpp: -------------------------------------------------------------------------------- 1 | #define GLFW_INCLUDE_VULKAN 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | const int WIDTH = 800; 10 | const int HEIGHT = 600; 11 | 12 | const std::vector validationLayers = { 13 | "VK_LAYER_LUNARG_standard_validation" 14 | }; 15 | 16 | #ifdef NDEBUG 17 | const bool enableValidationLayers = false; 18 | #else 19 | const bool enableValidationLayers = true; 20 | #endif 21 | 22 | VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) { 23 | auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"); 24 | if (func != nullptr) { 25 | return func(instance, pCreateInfo, pAllocator, pCallback); 26 | } else { 27 | return VK_ERROR_EXTENSION_NOT_PRESENT; 28 | } 29 | } 30 | 31 | void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) { 32 | auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); 33 | if (func != nullptr) { 34 | func(instance, callback, pAllocator); 35 | } 36 | } 37 | 38 | struct QueueFamilyIndices { 39 | int graphicsFamily = -1; 40 | 41 | bool isComplete() { 42 | return graphicsFamily >= 0; 43 | } 44 | }; 45 | 46 | class HelloTriangleApplication { 47 | public: 48 | void run() { 49 | initWindow(); 50 | initVulkan(); 51 | mainLoop(); 52 | cleanup(); 53 | } 54 | 55 | private: 56 | GLFWwindow* mWindow; 57 | 58 | VkInstance mInstance; 59 | VkDebugReportCallbackEXT mCallback; 60 | 61 | VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; 62 | 63 | void initWindow() { 64 | glfwInit(); 65 | 66 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 67 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 68 | 69 | mWindow = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 70 | } 71 | 72 | void initVulkan() { 73 | createInstance(); 74 | setupDebugCallback(); 75 | pickPhysicalDevice(); 76 | } 77 | 78 | void mainLoop() { 79 | while (!glfwWindowShouldClose(mWindow)) { 80 | glfwPollEvents(); 81 | } 82 | } 83 | 84 | void cleanup() { 85 | DestroyDebugReportCallbackEXT(mInstance, mCallback, nullptr); 86 | vkDestroyInstance(mInstance, nullptr); 87 | 88 | glfwDestroyWindow(mWindow); 89 | 90 | glfwTerminate(); 91 | } 92 | 93 | void createInstance() { 94 | if (enableValidationLayers && !checkValidationLayerSupport()) { 95 | throw std::runtime_error("validation layers requested, but not available!"); 96 | } 97 | 98 | VkApplicationInfo appInfo = {}; 99 | appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 100 | appInfo.pApplicationName = "Hello Triangle"; 101 | appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 102 | appInfo.pEngineName = "No Engine"; 103 | appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 104 | appInfo.apiVersion = VK_API_VERSION_1_0; 105 | 106 | VkInstanceCreateInfo createInfo = {}; 107 | createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 108 | createInfo.pApplicationInfo = &appInfo; 109 | 110 | auto extensions = getRequiredExtensions(); 111 | createInfo.enabledExtensionCount = static_cast(extensions.size()); 112 | createInfo.ppEnabledExtensionNames = extensions.data(); 113 | 114 | if (enableValidationLayers) { 115 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 116 | createInfo.ppEnabledLayerNames = validationLayers.data(); 117 | } else { 118 | createInfo.enabledLayerCount = 0; 119 | } 120 | 121 | if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS) { 122 | throw std::runtime_error("failed to create mInstance!"); 123 | } 124 | } 125 | 126 | void setupDebugCallback() { 127 | if (!enableValidationLayers) return; 128 | 129 | VkDebugReportCallbackCreateInfoEXT createInfo = {}; 130 | createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; 131 | createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; 132 | createInfo.pfnCallback = debugCallback; 133 | 134 | if (CreateDebugReportCallbackEXT(mInstance, &createInfo, nullptr, &mCallback) != VK_SUCCESS) { 135 | throw std::runtime_error("failed to set up debug mCallback!"); 136 | } 137 | } 138 | 139 | void pickPhysicalDevice() { 140 | uint32_t deviceCount = 0; 141 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr); 142 | 143 | if (deviceCount == 0) { 144 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 145 | } 146 | 147 | std::vector devices(deviceCount); 148 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data()); 149 | 150 | for (const auto& device : devices) { 151 | if (isDeviceSuitable(device)) { 152 | mPhysicalDevice = device; 153 | break; 154 | } 155 | } 156 | 157 | if (mPhysicalDevice == VK_NULL_HANDLE) { 158 | throw std::runtime_error("failed to find a suitable GPU!"); 159 | } 160 | } 161 | 162 | bool isDeviceSuitable(VkPhysicalDevice device) { 163 | QueueFamilyIndices indices = findQueueFamilies(device); 164 | 165 | return indices.isComplete(); 166 | } 167 | 168 | QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { 169 | QueueFamilyIndices indices; 170 | 171 | uint32_t queueFamilyCount = 0; 172 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); 173 | 174 | std::vector queueFamilies(queueFamilyCount); 175 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); 176 | 177 | int i = 0; 178 | for (const auto& queueFamily : queueFamilies) { 179 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { 180 | indices.graphicsFamily = i; 181 | } 182 | 183 | if (indices.isComplete()) { 184 | break; 185 | } 186 | 187 | i++; 188 | } 189 | 190 | return indices; 191 | } 192 | 193 | std::vector getRequiredExtensions() { 194 | uint32_t glfwExtensionCount = 0; 195 | const char** glfwExtensions; 196 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 197 | 198 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 199 | 200 | if (enableValidationLayers) { 201 | extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); 202 | } 203 | 204 | return extensions; 205 | } 206 | 207 | bool checkValidationLayerSupport() { 208 | uint32_t layerCount; 209 | vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 210 | 211 | std::vector availableLayers(layerCount); 212 | vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 213 | 214 | for (const char* layerName : validationLayers) { 215 | bool layerFound = false; 216 | 217 | for (const auto& layerProperties : availableLayers) { 218 | if (strcmp(layerName, layerProperties.layerName) == 0) { 219 | layerFound = true; 220 | break; 221 | } 222 | } 223 | 224 | if (!layerFound) { 225 | return false; 226 | } 227 | } 228 | 229 | return true; 230 | } 231 | 232 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) { 233 | std::cerr << "validation layer: " << msg << std::endl; 234 | 235 | return VK_FALSE; 236 | } 237 | }; 238 | 239 | int main() { 240 | HelloTriangleApplication app; 241 | 242 | try { 243 | app.run(); 244 | } catch (const std::runtime_error& e) { 245 | std::cerr << e.what() << std::endl; 246 | return EXIT_FAILURE; 247 | } 248 | 249 | return EXIT_SUCCESS; 250 | } 251 | -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/0_Setup/4_logical_device.cpp: -------------------------------------------------------------------------------- 1 | #define GLFW_INCLUDE_VULKAN 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | const int WIDTH = 800; 10 | const int HEIGHT = 600; 11 | 12 | const std::vector validationLayers = { 13 | "VK_LAYER_LUNARG_standard_validation" 14 | }; 15 | 16 | #ifdef NDEBUG 17 | const bool enableValidationLayers = false; 18 | #else 19 | const bool enableValidationLayers = true; 20 | #endif 21 | 22 | VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) { 23 | auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"); 24 | if (func != nullptr) { 25 | return func(instance, pCreateInfo, pAllocator, pCallback); 26 | } else { 27 | return VK_ERROR_EXTENSION_NOT_PRESENT; 28 | } 29 | } 30 | 31 | void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) { 32 | auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); 33 | if (func != nullptr) { 34 | func(instance, callback, pAllocator); 35 | } 36 | } 37 | 38 | struct QueueFamilyIndices { 39 | int graphicsFamily = -1; 40 | 41 | bool isComplete() { 42 | return graphicsFamily >= 0; 43 | } 44 | }; 45 | 46 | class HelloTriangleApplication { 47 | public: 48 | void run() { 49 | initWindow(); 50 | initVulkan(); 51 | mainLoop(); 52 | cleanup(); 53 | } 54 | 55 | private: 56 | GLFWwindow* mWindow; 57 | 58 | VkInstance mInstance; 59 | VkDebugReportCallbackEXT mCallback; 60 | 61 | VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; 62 | VkDevice mDevice; 63 | 64 | VkQueue mGraphicsQueue; 65 | 66 | void initWindow() { 67 | glfwInit(); 68 | 69 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 70 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 71 | 72 | mWindow = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 73 | } 74 | 75 | void initVulkan() { 76 | createInstance(); 77 | setupDebugCallback(); 78 | pickPhysicalDevice(); 79 | createLogicalDevice(); 80 | } 81 | 82 | void mainLoop() { 83 | while (!glfwWindowShouldClose(mWindow)) { 84 | glfwPollEvents(); 85 | } 86 | } 87 | 88 | void cleanup() { 89 | vkDestroyDevice(mDevice, nullptr); 90 | DestroyDebugReportCallbackEXT(mInstance, mCallback, nullptr); 91 | vkDestroyInstance(mInstance, nullptr); 92 | 93 | glfwDestroyWindow(mWindow); 94 | 95 | glfwTerminate(); 96 | } 97 | 98 | void createInstance() { 99 | if (enableValidationLayers && !checkValidationLayerSupport()) { 100 | throw std::runtime_error("validation layers requested, but not available!"); 101 | } 102 | 103 | VkApplicationInfo appInfo = {}; 104 | appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 105 | appInfo.pApplicationName = "Hello Triangle"; 106 | appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 107 | appInfo.pEngineName = "No Engine"; 108 | appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 109 | appInfo.apiVersion = VK_API_VERSION_1_0; 110 | 111 | VkInstanceCreateInfo createInfo = {}; 112 | createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 113 | createInfo.pApplicationInfo = &appInfo; 114 | 115 | auto extensions = getRequiredExtensions(); 116 | createInfo.enabledExtensionCount = static_cast(extensions.size()); 117 | createInfo.ppEnabledExtensionNames = extensions.data(); 118 | 119 | if (enableValidationLayers) { 120 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 121 | createInfo.ppEnabledLayerNames = validationLayers.data(); 122 | } else { 123 | createInfo.enabledLayerCount = 0; 124 | } 125 | 126 | if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS) { 127 | throw std::runtime_error("failed to create mInstance!"); 128 | } 129 | } 130 | 131 | void setupDebugCallback() { 132 | if (!enableValidationLayers) return; 133 | 134 | VkDebugReportCallbackCreateInfoEXT createInfo = {}; 135 | createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; 136 | createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; 137 | createInfo.pfnCallback = debugCallback; 138 | 139 | if (CreateDebugReportCallbackEXT(mInstance, &createInfo, nullptr, &mCallback) != VK_SUCCESS) { 140 | throw std::runtime_error("failed to set up debug mCallback!"); 141 | } 142 | } 143 | 144 | void pickPhysicalDevice() { 145 | uint32_t deviceCount = 0; 146 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr); 147 | 148 | if (deviceCount == 0) { 149 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 150 | } 151 | 152 | std::vector devices(deviceCount); 153 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data()); 154 | 155 | for (const auto& device : devices) { 156 | if (isDeviceSuitable(device)) { 157 | mPhysicalDevice = device; 158 | break; 159 | } 160 | } 161 | 162 | if (mPhysicalDevice == VK_NULL_HANDLE) { 163 | throw std::runtime_error("failed to find a suitable GPU!"); 164 | } 165 | } 166 | 167 | void createLogicalDevice() { 168 | QueueFamilyIndices indices = findQueueFamilies(mPhysicalDevice); 169 | 170 | VkDeviceQueueCreateInfo queueCreateInfo = {}; 171 | queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 172 | queueCreateInfo.queueFamilyIndex = indices.graphicsFamily; 173 | queueCreateInfo.queueCount = 1; 174 | 175 | float queuePriority = 1.0f; 176 | queueCreateInfo.pQueuePriorities = &queuePriority; 177 | 178 | VkPhysicalDeviceFeatures deviceFeatures = {}; 179 | 180 | VkDeviceCreateInfo createInfo = {}; 181 | createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 182 | 183 | createInfo.pQueueCreateInfos = &queueCreateInfo; 184 | createInfo.queueCreateInfoCount = 1; 185 | 186 | createInfo.pEnabledFeatures = &deviceFeatures; 187 | 188 | createInfo.enabledExtensionCount = 0; 189 | 190 | if (enableValidationLayers) { 191 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 192 | createInfo.ppEnabledLayerNames = validationLayers.data(); 193 | } else { 194 | createInfo.enabledLayerCount = 0; 195 | } 196 | 197 | if (vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mDevice) != VK_SUCCESS) { 198 | throw std::runtime_error("failed to create logical mDevice!"); 199 | } 200 | 201 | vkGetDeviceQueue(mDevice, indices.graphicsFamily, 0, &mGraphicsQueue); 202 | } 203 | 204 | bool isDeviceSuitable(VkPhysicalDevice device) { 205 | QueueFamilyIndices indices = findQueueFamilies(device); 206 | 207 | return indices.isComplete(); 208 | } 209 | 210 | QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { 211 | QueueFamilyIndices indices; 212 | 213 | uint32_t queueFamilyCount = 0; 214 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); 215 | 216 | std::vector queueFamilies(queueFamilyCount); 217 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); 218 | 219 | int i = 0; 220 | for (const auto& queueFamily : queueFamilies) { 221 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { 222 | indices.graphicsFamily = i; 223 | } 224 | 225 | if (indices.isComplete()) { 226 | break; 227 | } 228 | 229 | i++; 230 | } 231 | 232 | return indices; 233 | } 234 | 235 | std::vector getRequiredExtensions() { 236 | uint32_t glfwExtensionCount = 0; 237 | const char** glfwExtensions; 238 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 239 | 240 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 241 | 242 | if (enableValidationLayers) { 243 | extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); 244 | } 245 | 246 | return extensions; 247 | } 248 | 249 | bool checkValidationLayerSupport() { 250 | uint32_t layerCount; 251 | vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 252 | 253 | std::vector availableLayers(layerCount); 254 | vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 255 | 256 | for (const char* layerName : validationLayers) { 257 | bool layerFound = false; 258 | 259 | for (const auto& layerProperties : availableLayers) { 260 | if (strcmp(layerName, layerProperties.layerName) == 0) { 261 | layerFound = true; 262 | break; 263 | } 264 | } 265 | 266 | if (!layerFound) { 267 | return false; 268 | } 269 | } 270 | 271 | return true; 272 | } 273 | 274 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) { 275 | std::cerr << "validation layer: " << msg << std::endl; 276 | 277 | return VK_FALSE; 278 | } 279 | }; 280 | 281 | int main() { 282 | HelloTriangleApplication app; 283 | 284 | try { 285 | app.run(); 286 | } catch (const std::runtime_error& e) { 287 | std::cerr << e.what() << std::endl; 288 | return EXIT_FAILURE; 289 | } 290 | 291 | return EXIT_SUCCESS; 292 | } 293 | -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/0_Setup/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(0_base_code 0_base_code.cpp) 2 | target_link_libraries(0_base_code ${LIBS}) 3 | 4 | add_executable(1_instance 1_instance.cpp) 5 | target_link_libraries(1_instance ${LIBS}) 6 | 7 | add_executable(2_Validation_layers 2_Validation_layers.cpp) 8 | target_link_libraries(2_Validation_layers ${LIBS}) 9 | 10 | add_executable(3_physical_device_selection 3_physical_device_selection.cpp) 11 | target_link_libraries(3_physical_device_selection ${LIBS}) 12 | 13 | add_executable(4_logical_device 4_logical_device.cpp) 14 | target_link_libraries(4_logical_device ${LIBS}) -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/1_Presentation/1_window_surface.cpp: -------------------------------------------------------------------------------- 1 | #define GLFW_INCLUDE_VULKAN 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | const int WIDTH = 800; 11 | const int HEIGHT = 600; 12 | 13 | const std::vector validationLayers = { 14 | "VK_LAYER_LUNARG_standard_validation" 15 | }; 16 | 17 | #ifdef NDEBUG 18 | const bool enableValidationLayers = false; 19 | #else 20 | const bool enableValidationLayers = true; 21 | #endif 22 | 23 | VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) { 24 | auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"); 25 | if (func != nullptr) { 26 | return func(instance, pCreateInfo, pAllocator, pCallback); 27 | } else { 28 | return VK_ERROR_EXTENSION_NOT_PRESENT; 29 | } 30 | } 31 | 32 | void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) { 33 | auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); 34 | if (func != nullptr) { 35 | func(instance, callback, pAllocator); 36 | } 37 | } 38 | 39 | struct QueueFamilyIndices { 40 | int graphicsFamily = -1; 41 | int presentFamily = -1; 42 | 43 | bool isComplete() { 44 | return graphicsFamily >= 0 && presentFamily >= 0; 45 | } 46 | }; 47 | 48 | class HelloTriangleApplication { 49 | public: 50 | void run() { 51 | initWindow(); 52 | initVulkan(); 53 | mainLoop(); 54 | cleanup(); 55 | } 56 | 57 | private: 58 | GLFWwindow* mWindow; 59 | 60 | VkInstance mInstance; 61 | VkDebugReportCallbackEXT mCallback; 62 | VkSurfaceKHR mSurface; 63 | 64 | VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; 65 | VkDevice mDevice; 66 | 67 | VkQueue mGraphicsQueue; 68 | VkQueue mPresentQueue; 69 | 70 | void initWindow() { 71 | glfwInit(); 72 | 73 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 74 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 75 | 76 | mWindow = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 77 | } 78 | 79 | void initVulkan() { 80 | createInstance(); 81 | setupDebugCallback(); 82 | createSurface(); 83 | pickPhysicalDevice(); 84 | createLogicalDevice(); 85 | } 86 | 87 | void mainLoop() { 88 | while (!glfwWindowShouldClose(mWindow)) { 89 | glfwPollEvents(); 90 | } 91 | } 92 | 93 | void cleanup() { 94 | vkDestroyDevice(mDevice, nullptr); 95 | DestroyDebugReportCallbackEXT(mInstance, mCallback, nullptr); 96 | vkDestroySurfaceKHR(mInstance, mSurface, nullptr); 97 | vkDestroyInstance(mInstance, nullptr); 98 | 99 | glfwDestroyWindow(mWindow); 100 | 101 | glfwTerminate(); 102 | } 103 | 104 | void createInstance() { 105 | if (enableValidationLayers && !checkValidationLayerSupport()) { 106 | throw std::runtime_error("validation layers requested, but not available!"); 107 | } 108 | 109 | VkApplicationInfo appInfo = {}; 110 | appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 111 | appInfo.pApplicationName = "Hello Triangle"; 112 | appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 113 | appInfo.pEngineName = "No Engine"; 114 | appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 115 | appInfo.apiVersion = VK_API_VERSION_1_0; 116 | 117 | VkInstanceCreateInfo createInfo = {}; 118 | createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 119 | createInfo.pApplicationInfo = &appInfo; 120 | 121 | auto extensions = getRequiredExtensions(); 122 | createInfo.enabledExtensionCount = static_cast(extensions.size()); 123 | createInfo.ppEnabledExtensionNames = extensions.data(); 124 | 125 | if (enableValidationLayers) { 126 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 127 | createInfo.ppEnabledLayerNames = validationLayers.data(); 128 | } else { 129 | createInfo.enabledLayerCount = 0; 130 | } 131 | 132 | if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS) { 133 | throw std::runtime_error("failed to create mInstance!"); 134 | } 135 | } 136 | 137 | void setupDebugCallback() { 138 | if (!enableValidationLayers) return; 139 | 140 | VkDebugReportCallbackCreateInfoEXT createInfo = {}; 141 | createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; 142 | createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; 143 | createInfo.pfnCallback = debugCallback; 144 | 145 | if (CreateDebugReportCallbackEXT(mInstance, &createInfo, nullptr, &mCallback) != VK_SUCCESS) { 146 | throw std::runtime_error("failed to set up debug mCallback!"); 147 | } 148 | } 149 | 150 | void createSurface() { 151 | if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) { 152 | throw std::runtime_error("failed to create mWindow mSurface!"); 153 | } 154 | } 155 | 156 | void pickPhysicalDevice() { 157 | uint32_t deviceCount = 0; 158 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr); 159 | 160 | if (deviceCount == 0) { 161 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 162 | } 163 | 164 | std::vector devices(deviceCount); 165 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data()); 166 | 167 | for (const auto& device : devices) { 168 | if (isDeviceSuitable(device)) { 169 | mPhysicalDevice = device; 170 | break; 171 | } 172 | } 173 | 174 | if (mPhysicalDevice == VK_NULL_HANDLE) { 175 | throw std::runtime_error("failed to find a suitable GPU!"); 176 | } 177 | } 178 | 179 | void createLogicalDevice() { 180 | QueueFamilyIndices indices = findQueueFamilies(mPhysicalDevice); 181 | 182 | std::vector queueCreateInfos; 183 | std::set uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily}; 184 | 185 | float queuePriority = 1.0f; 186 | for (int queueFamily : uniqueQueueFamilies) { 187 | VkDeviceQueueCreateInfo queueCreateInfo = {}; 188 | queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 189 | queueCreateInfo.queueFamilyIndex = queueFamily; 190 | queueCreateInfo.queueCount = 1; 191 | queueCreateInfo.pQueuePriorities = &queuePriority; 192 | queueCreateInfos.push_back(queueCreateInfo); 193 | } 194 | 195 | VkPhysicalDeviceFeatures deviceFeatures = {}; 196 | 197 | VkDeviceCreateInfo createInfo = {}; 198 | createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 199 | 200 | createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); 201 | createInfo.pQueueCreateInfos = queueCreateInfos.data(); 202 | 203 | createInfo.pEnabledFeatures = &deviceFeatures; 204 | 205 | createInfo.enabledExtensionCount = 0; 206 | 207 | if (enableValidationLayers) { 208 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 209 | createInfo.ppEnabledLayerNames = validationLayers.data(); 210 | } else { 211 | createInfo.enabledLayerCount = 0; 212 | } 213 | 214 | if (vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mDevice) != VK_SUCCESS) { 215 | throw std::runtime_error("failed to create logical mDevice!"); 216 | } 217 | 218 | vkGetDeviceQueue(mDevice, indices.graphicsFamily, 0, &mGraphicsQueue); 219 | vkGetDeviceQueue(mDevice, indices.presentFamily, 0, &mPresentQueue); 220 | } 221 | 222 | bool isDeviceSuitable(VkPhysicalDevice device) { 223 | QueueFamilyIndices indices = findQueueFamilies(device); 224 | 225 | return indices.isComplete(); 226 | } 227 | 228 | QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { 229 | QueueFamilyIndices indices; 230 | 231 | uint32_t queueFamilyCount = 0; 232 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); 233 | 234 | std::vector queueFamilies(queueFamilyCount); 235 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); 236 | 237 | int i = 0; 238 | for (const auto& queueFamily : queueFamilies) { 239 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { 240 | indices.graphicsFamily = i; 241 | } 242 | 243 | VkBool32 presentSupport = false; 244 | vkGetPhysicalDeviceSurfaceSupportKHR(device, i, mSurface, &presentSupport); 245 | 246 | if (queueFamily.queueCount > 0 && presentSupport) { 247 | indices.presentFamily = i; 248 | } 249 | 250 | if (indices.isComplete()) { 251 | break; 252 | } 253 | 254 | i++; 255 | } 256 | 257 | return indices; 258 | } 259 | 260 | std::vector getRequiredExtensions() { 261 | uint32_t glfwExtensionCount = 0; 262 | const char** glfwExtensions; 263 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 264 | 265 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 266 | 267 | if (enableValidationLayers) { 268 | extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); 269 | } 270 | 271 | return extensions; 272 | } 273 | 274 | bool checkValidationLayerSupport() { 275 | uint32_t layerCount; 276 | vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 277 | 278 | std::vector availableLayers(layerCount); 279 | vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 280 | 281 | for (const char* layerName : validationLayers) { 282 | bool layerFound = false; 283 | 284 | for (const auto& layerProperties : availableLayers) { 285 | if (strcmp(layerName, layerProperties.layerName) == 0) { 286 | layerFound = true; 287 | break; 288 | } 289 | } 290 | 291 | if (!layerFound) { 292 | return false; 293 | } 294 | } 295 | 296 | return true; 297 | } 298 | 299 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) { 300 | std::cerr << "validation layer: " << msg << std::endl; 301 | 302 | return VK_FALSE; 303 | } 304 | }; 305 | 306 | int main() { 307 | HelloTriangleApplication app; 308 | 309 | try { 310 | app.run(); 311 | } catch (const std::runtime_error& e) { 312 | std::cerr << e.what() << std::endl; 313 | return EXIT_FAILURE; 314 | } 315 | 316 | return EXIT_SUCCESS; 317 | } 318 | -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/1_Presentation/2_swapchain.cpp: -------------------------------------------------------------------------------- 1 | #define GLFW_INCLUDE_VULKAN 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | const int WIDTH = 800; 12 | const int HEIGHT = 600; 13 | 14 | const std::vector validationLayers = { 15 | "VK_LAYER_LUNARG_standard_validation" 16 | }; 17 | 18 | const std::vector deviceExtensions = { 19 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 20 | }; 21 | 22 | #ifdef NDEBUG 23 | const bool enableValidationLayers = false; 24 | #else 25 | const bool enableValidationLayers = true; 26 | #endif 27 | 28 | VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) { 29 | auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"); 30 | if (func != nullptr) { 31 | return func(instance, pCreateInfo, pAllocator, pCallback); 32 | } else { 33 | return VK_ERROR_EXTENSION_NOT_PRESENT; 34 | } 35 | } 36 | 37 | void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) { 38 | auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); 39 | if (func != nullptr) { 40 | func(instance, callback, pAllocator); 41 | } 42 | } 43 | 44 | struct QueueFamilyIndices { 45 | int graphicsFamily = -1; 46 | int presentFamily = -1; 47 | 48 | bool isComplete() { 49 | return graphicsFamily >= 0 && presentFamily >= 0; 50 | } 51 | }; 52 | 53 | struct SwapChainSupportDetails { 54 | VkSurfaceCapabilitiesKHR capabilities; 55 | std::vector formats; 56 | std::vector presentModes; 57 | }; 58 | 59 | class HelloTriangleApplication { 60 | public: 61 | void run() { 62 | initWindow(); 63 | initVulkan(); 64 | mainLoop(); 65 | cleanup(); 66 | } 67 | 68 | private: 69 | GLFWwindow* mWindow; 70 | 71 | VkInstance mInstance; 72 | VkDebugReportCallbackEXT mCallback; 73 | VkSurfaceKHR mSurface; 74 | 75 | VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; 76 | VkDevice mDevice; 77 | 78 | VkQueue mGraphicsQueue; 79 | VkQueue mPresentQueue; 80 | 81 | VkSwapchainKHR mSwapChain; 82 | std::vector mSwapChainImages; 83 | VkFormat mSwapChainImageFormat; 84 | VkExtent2D mSwapChainExtent; 85 | 86 | void initWindow() { 87 | glfwInit(); 88 | 89 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 90 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 91 | 92 | mWindow = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 93 | } 94 | 95 | void initVulkan() { 96 | createInstance(); 97 | setupDebugCallback(); 98 | createSurface(); 99 | pickPhysicalDevice(); 100 | createLogicalDevice(); 101 | createSwapChain(); 102 | } 103 | 104 | void mainLoop() { 105 | while (!glfwWindowShouldClose(mWindow)) { 106 | glfwPollEvents(); 107 | } 108 | } 109 | 110 | void cleanup() { 111 | vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr); 112 | vkDestroyDevice(mDevice, nullptr); 113 | DestroyDebugReportCallbackEXT(mInstance, mCallback, nullptr); 114 | vkDestroySurfaceKHR(mInstance, mSurface, nullptr); 115 | vkDestroyInstance(mInstance, nullptr); 116 | 117 | glfwDestroyWindow(mWindow); 118 | 119 | glfwTerminate(); 120 | } 121 | 122 | void createInstance() { 123 | if (enableValidationLayers && !checkValidationLayerSupport()) { 124 | throw std::runtime_error("validation layers requested, but not available!"); 125 | } 126 | 127 | VkApplicationInfo appInfo = {}; 128 | appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 129 | appInfo.pApplicationName = "Hello Triangle"; 130 | appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 131 | appInfo.pEngineName = "No Engine"; 132 | appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 133 | appInfo.apiVersion = VK_API_VERSION_1_0; 134 | 135 | VkInstanceCreateInfo createInfo = {}; 136 | createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 137 | createInfo.pApplicationInfo = &appInfo; 138 | 139 | auto extensions = getRequiredExtensions(); 140 | createInfo.enabledExtensionCount = static_cast(extensions.size()); 141 | createInfo.ppEnabledExtensionNames = extensions.data(); 142 | 143 | if (enableValidationLayers) { 144 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 145 | createInfo.ppEnabledLayerNames = validationLayers.data(); 146 | } else { 147 | createInfo.enabledLayerCount = 0; 148 | } 149 | 150 | if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS) { 151 | throw std::runtime_error("failed to create mInstance!"); 152 | } 153 | } 154 | 155 | void setupDebugCallback() { 156 | if (!enableValidationLayers) return; 157 | 158 | VkDebugReportCallbackCreateInfoEXT createInfo = {}; 159 | createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; 160 | createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; 161 | createInfo.pfnCallback = debugCallback; 162 | 163 | if (CreateDebugReportCallbackEXT(mInstance, &createInfo, nullptr, &mCallback) != VK_SUCCESS) { 164 | throw std::runtime_error("failed to set up debug mCallback!"); 165 | } 166 | } 167 | 168 | void createSurface() { 169 | if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) { 170 | throw std::runtime_error("failed to create mWindow mSurface!"); 171 | } 172 | } 173 | 174 | void pickPhysicalDevice() { 175 | uint32_t deviceCount = 0; 176 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr); 177 | 178 | if (deviceCount == 0) { 179 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 180 | } 181 | 182 | std::vector devices(deviceCount); 183 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data()); 184 | 185 | for (const auto& device : devices) { 186 | if (isDeviceSuitable(device)) { 187 | mPhysicalDevice = device; 188 | break; 189 | } 190 | } 191 | 192 | if (mPhysicalDevice == VK_NULL_HANDLE) { 193 | throw std::runtime_error("failed to find a suitable GPU!"); 194 | } 195 | } 196 | 197 | void createLogicalDevice() { 198 | QueueFamilyIndices indices = findQueueFamilies(mPhysicalDevice); 199 | 200 | std::vector queueCreateInfos; 201 | std::set uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily}; 202 | 203 | float queuePriority = 1.0f; 204 | for (int queueFamily : uniqueQueueFamilies) { 205 | VkDeviceQueueCreateInfo queueCreateInfo = {}; 206 | queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 207 | queueCreateInfo.queueFamilyIndex = queueFamily; 208 | queueCreateInfo.queueCount = 1; 209 | queueCreateInfo.pQueuePriorities = &queuePriority; 210 | queueCreateInfos.push_back(queueCreateInfo); 211 | } 212 | 213 | VkPhysicalDeviceFeatures deviceFeatures = {}; 214 | 215 | VkDeviceCreateInfo createInfo = {}; 216 | createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 217 | 218 | createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); 219 | createInfo.pQueueCreateInfos = queueCreateInfos.data(); 220 | 221 | createInfo.pEnabledFeatures = &deviceFeatures; 222 | 223 | createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); 224 | createInfo.ppEnabledExtensionNames = deviceExtensions.data(); 225 | 226 | if (enableValidationLayers) { 227 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 228 | createInfo.ppEnabledLayerNames = validationLayers.data(); 229 | } else { 230 | createInfo.enabledLayerCount = 0; 231 | } 232 | 233 | if (vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mDevice) != VK_SUCCESS) { 234 | throw std::runtime_error("failed to create logical mDevice!"); 235 | } 236 | 237 | vkGetDeviceQueue(mDevice, indices.graphicsFamily, 0, &mGraphicsQueue); 238 | vkGetDeviceQueue(mDevice, indices.presentFamily, 0, &mPresentQueue); 239 | } 240 | 241 | void createSwapChain() { 242 | SwapChainSupportDetails swapChainSupport = querySwapChainSupport(mPhysicalDevice); 243 | 244 | VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); 245 | VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); 246 | VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); 247 | 248 | uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; 249 | if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { 250 | imageCount = swapChainSupport.capabilities.maxImageCount; 251 | } 252 | 253 | VkSwapchainCreateInfoKHR createInfo = {}; 254 | createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; 255 | createInfo.surface = mSurface; 256 | 257 | createInfo.minImageCount = imageCount; 258 | createInfo.imageFormat = surfaceFormat.format; 259 | createInfo.imageColorSpace = surfaceFormat.colorSpace; 260 | createInfo.imageExtent = extent; 261 | createInfo.imageArrayLayers = 1; 262 | createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; 263 | 264 | QueueFamilyIndices indices = findQueueFamilies(mPhysicalDevice); 265 | uint32_t queueFamilyIndices[] = {(uint32_t) indices.graphicsFamily, (uint32_t) indices.presentFamily}; 266 | 267 | if (indices.graphicsFamily != indices.presentFamily) { 268 | createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; 269 | createInfo.queueFamilyIndexCount = 2; 270 | createInfo.pQueueFamilyIndices = queueFamilyIndices; 271 | } else { 272 | createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; 273 | } 274 | 275 | createInfo.preTransform = swapChainSupport.capabilities.currentTransform; 276 | createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; 277 | createInfo.presentMode = presentMode; 278 | createInfo.clipped = VK_TRUE; 279 | 280 | createInfo.oldSwapchain = VK_NULL_HANDLE; 281 | 282 | if (vkCreateSwapchainKHR(mDevice, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS) { 283 | throw std::runtime_error("failed to create swap chain!"); 284 | } 285 | 286 | vkGetSwapchainImagesKHR(mDevice, mSwapChain, &imageCount, nullptr); 287 | mSwapChainImages.resize(imageCount); 288 | vkGetSwapchainImagesKHR(mDevice, mSwapChain, &imageCount, mSwapChainImages.data()); 289 | 290 | mSwapChainImageFormat = surfaceFormat.format; 291 | mSwapChainExtent = extent; 292 | } 293 | 294 | VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { 295 | if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) { 296 | return {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}; 297 | } 298 | 299 | for (const auto& availableFormat : availableFormats) { 300 | if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { 301 | return availableFormat; 302 | } 303 | } 304 | 305 | return availableFormats[0]; 306 | } 307 | 308 | VkPresentModeKHR chooseSwapPresentMode(const std::vector availablePresentModes) { 309 | VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR; 310 | 311 | for (const auto& availablePresentMode : availablePresentModes) { 312 | if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { 313 | return availablePresentMode; 314 | } else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) { 315 | bestMode = availablePresentMode; 316 | } 317 | } 318 | 319 | return bestMode; 320 | } 321 | 322 | VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { 323 | if (capabilities.currentExtent.width != std::numeric_limits::max()) { 324 | return capabilities.currentExtent; 325 | } else { 326 | VkExtent2D actualExtent = {WIDTH, HEIGHT}; 327 | 328 | actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); 329 | actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); 330 | 331 | return actualExtent; 332 | } 333 | } 334 | 335 | SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { 336 | SwapChainSupportDetails details; 337 | 338 | vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &details.capabilities); 339 | 340 | uint32_t formatCount; 341 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, nullptr); 342 | 343 | if (formatCount != 0) { 344 | details.formats.resize(formatCount); 345 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, details.formats.data()); 346 | } 347 | 348 | uint32_t presentModeCount; 349 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, nullptr); 350 | 351 | if (presentModeCount != 0) { 352 | details.presentModes.resize(presentModeCount); 353 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, details.presentModes.data()); 354 | } 355 | 356 | return details; 357 | } 358 | 359 | bool isDeviceSuitable(VkPhysicalDevice device) { 360 | QueueFamilyIndices indices = findQueueFamilies(device); 361 | 362 | bool extensionsSupported = checkDeviceExtensionSupport(device); 363 | 364 | bool swapChainAdequate = false; 365 | if (extensionsSupported) { 366 | SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); 367 | swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); 368 | } 369 | 370 | return indices.isComplete() && extensionsSupported && swapChainAdequate; 371 | } 372 | 373 | bool checkDeviceExtensionSupport(VkPhysicalDevice device) { 374 | uint32_t extensionCount; 375 | vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); 376 | 377 | std::vector availableExtensions(extensionCount); 378 | vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); 379 | 380 | std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); 381 | 382 | for (const auto& extension : availableExtensions) { 383 | requiredExtensions.erase(extension.extensionName); 384 | } 385 | 386 | return requiredExtensions.empty(); 387 | } 388 | 389 | QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { 390 | QueueFamilyIndices indices; 391 | 392 | uint32_t queueFamilyCount = 0; 393 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); 394 | 395 | std::vector queueFamilies(queueFamilyCount); 396 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); 397 | 398 | int i = 0; 399 | for (const auto& queueFamily : queueFamilies) { 400 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { 401 | indices.graphicsFamily = i; 402 | } 403 | 404 | VkBool32 presentSupport = false; 405 | vkGetPhysicalDeviceSurfaceSupportKHR(device, i, mSurface, &presentSupport); 406 | 407 | if (queueFamily.queueCount > 0 && presentSupport) { 408 | indices.presentFamily = i; 409 | } 410 | 411 | if (indices.isComplete()) { 412 | break; 413 | } 414 | 415 | i++; 416 | } 417 | 418 | return indices; 419 | } 420 | 421 | std::vector getRequiredExtensions() { 422 | uint32_t glfwExtensionCount = 0; 423 | const char** glfwExtensions; 424 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 425 | 426 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 427 | 428 | if (enableValidationLayers) { 429 | extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); 430 | } 431 | 432 | return extensions; 433 | } 434 | 435 | bool checkValidationLayerSupport() { 436 | uint32_t layerCount; 437 | vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 438 | 439 | std::vector availableLayers(layerCount); 440 | vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 441 | 442 | for (const char* layerName : validationLayers) { 443 | bool layerFound = false; 444 | 445 | for (const auto& layerProperties : availableLayers) { 446 | if (strcmp(layerName, layerProperties.layerName) == 0) { 447 | layerFound = true; 448 | break; 449 | } 450 | } 451 | 452 | if (!layerFound) { 453 | return false; 454 | } 455 | } 456 | 457 | return true; 458 | } 459 | 460 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) { 461 | std::cerr << "validation layer: " << msg << std::endl; 462 | 463 | return VK_FALSE; 464 | } 465 | }; 466 | 467 | int main() { 468 | HelloTriangleApplication app; 469 | 470 | try { 471 | app.run(); 472 | } catch (const std::runtime_error& e) { 473 | std::cerr << e.what() << std::endl; 474 | return EXIT_FAILURE; 475 | } 476 | 477 | return EXIT_SUCCESS; 478 | } 479 | -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/1_Presentation/3_image_views.cpp: -------------------------------------------------------------------------------- 1 | #define GLFW_INCLUDE_VULKAN 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | const int WIDTH = 800; 12 | const int HEIGHT = 600; 13 | 14 | const std::vector validationLayers = { 15 | "VK_LAYER_LUNARG_standard_validation" 16 | }; 17 | 18 | const std::vector deviceExtensions = { 19 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 20 | }; 21 | 22 | #ifdef NDEBUG 23 | const bool enableValidationLayers = false; 24 | #else 25 | const bool enableValidationLayers = true; 26 | #endif 27 | 28 | VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) { 29 | auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"); 30 | if (func != nullptr) { 31 | return func(instance, pCreateInfo, pAllocator, pCallback); 32 | } else { 33 | return VK_ERROR_EXTENSION_NOT_PRESENT; 34 | } 35 | } 36 | 37 | void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) { 38 | auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); 39 | if (func != nullptr) { 40 | func(instance, callback, pAllocator); 41 | } 42 | } 43 | 44 | struct QueueFamilyIndices { 45 | int graphicsFamily = -1; 46 | int presentFamily = -1; 47 | 48 | bool isComplete() { 49 | return graphicsFamily >= 0 && presentFamily >= 0; 50 | } 51 | }; 52 | 53 | struct SwapChainSupportDetails { 54 | VkSurfaceCapabilitiesKHR capabilities; 55 | std::vector formats; 56 | std::vector presentModes; 57 | }; 58 | 59 | class HelloTriangleApplication { 60 | public: 61 | void run() { 62 | initWindow(); 63 | initVulkan(); 64 | mainLoop(); 65 | cleanup(); 66 | } 67 | 68 | private: 69 | GLFWwindow* mWindow; 70 | 71 | VkInstance mInstance; 72 | VkDebugReportCallbackEXT mCallback; 73 | VkSurfaceKHR mSurface; 74 | 75 | VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; 76 | VkDevice mDevice; 77 | 78 | VkQueue mGraphicsQueue; 79 | VkQueue mPresentQueue; 80 | 81 | VkSwapchainKHR mSwapChain; 82 | std::vector mSwapChainImages; 83 | VkFormat mSwapChainImageFormat; 84 | VkExtent2D mSwapChainExtent; 85 | std::vector mSwapChainImageViews; 86 | 87 | void initWindow() { 88 | glfwInit(); 89 | 90 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 91 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 92 | 93 | mWindow = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 94 | } 95 | 96 | void initVulkan() { 97 | createInstance(); 98 | setupDebugCallback(); 99 | createSurface(); 100 | pickPhysicalDevice(); 101 | createLogicalDevice(); 102 | createSwapChain(); 103 | createImageViews(); 104 | } 105 | 106 | void mainLoop() { 107 | while (!glfwWindowShouldClose(mWindow)) { 108 | glfwPollEvents(); 109 | } 110 | } 111 | 112 | void cleanup() { 113 | for (auto imageView : mSwapChainImageViews) { 114 | vkDestroyImageView(mDevice, imageView, nullptr); 115 | } 116 | 117 | vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr); 118 | vkDestroyDevice(mDevice, nullptr); 119 | DestroyDebugReportCallbackEXT(mInstance, mCallback, nullptr); 120 | vkDestroySurfaceKHR(mInstance, mSurface, nullptr); 121 | vkDestroyInstance(mInstance, nullptr); 122 | 123 | glfwDestroyWindow(mWindow); 124 | 125 | glfwTerminate(); 126 | } 127 | 128 | void createInstance() { 129 | if (enableValidationLayers && !checkValidationLayerSupport()) { 130 | throw std::runtime_error("validation layers requested, but not available!"); 131 | } 132 | 133 | VkApplicationInfo appInfo = {}; 134 | appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 135 | appInfo.pApplicationName = "Hello Triangle"; 136 | appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 137 | appInfo.pEngineName = "No Engine"; 138 | appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 139 | appInfo.apiVersion = VK_API_VERSION_1_0; 140 | 141 | VkInstanceCreateInfo createInfo = {}; 142 | createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 143 | createInfo.pApplicationInfo = &appInfo; 144 | 145 | auto extensions = getRequiredExtensions(); 146 | createInfo.enabledExtensionCount = static_cast(extensions.size()); 147 | createInfo.ppEnabledExtensionNames = extensions.data(); 148 | 149 | if (enableValidationLayers) { 150 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 151 | createInfo.ppEnabledLayerNames = validationLayers.data(); 152 | } else { 153 | createInfo.enabledLayerCount = 0; 154 | } 155 | 156 | if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS) { 157 | throw std::runtime_error("failed to create mInstance!"); 158 | } 159 | } 160 | 161 | void setupDebugCallback() { 162 | if (!enableValidationLayers) return; 163 | 164 | VkDebugReportCallbackCreateInfoEXT createInfo = {}; 165 | createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; 166 | createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; 167 | createInfo.pfnCallback = debugCallback; 168 | 169 | if (CreateDebugReportCallbackEXT(mInstance, &createInfo, nullptr, &mCallback) != VK_SUCCESS) { 170 | throw std::runtime_error("failed to set up debug mCallback!"); 171 | } 172 | } 173 | 174 | void createSurface() { 175 | if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) { 176 | throw std::runtime_error("failed to create mWindow mSurface!"); 177 | } 178 | } 179 | 180 | void pickPhysicalDevice() { 181 | uint32_t deviceCount = 0; 182 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr); 183 | 184 | if (deviceCount == 0) { 185 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 186 | } 187 | 188 | std::vector devices(deviceCount); 189 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data()); 190 | 191 | for (const auto& device : devices) { 192 | if (isDeviceSuitable(device)) { 193 | mPhysicalDevice = device; 194 | break; 195 | } 196 | } 197 | 198 | if (mPhysicalDevice == VK_NULL_HANDLE) { 199 | throw std::runtime_error("failed to find a suitable GPU!"); 200 | } 201 | } 202 | 203 | void createLogicalDevice() { 204 | QueueFamilyIndices indices = findQueueFamilies(mPhysicalDevice); 205 | 206 | std::vector queueCreateInfos; 207 | std::set uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily}; 208 | 209 | float queuePriority = 1.0f; 210 | for (int queueFamily : uniqueQueueFamilies) { 211 | VkDeviceQueueCreateInfo queueCreateInfo = {}; 212 | queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 213 | queueCreateInfo.queueFamilyIndex = queueFamily; 214 | queueCreateInfo.queueCount = 1; 215 | queueCreateInfo.pQueuePriorities = &queuePriority; 216 | queueCreateInfos.push_back(queueCreateInfo); 217 | } 218 | 219 | VkPhysicalDeviceFeatures deviceFeatures = {}; 220 | 221 | VkDeviceCreateInfo createInfo = {}; 222 | createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 223 | 224 | createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); 225 | createInfo.pQueueCreateInfos = queueCreateInfos.data(); 226 | 227 | createInfo.pEnabledFeatures = &deviceFeatures; 228 | 229 | createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); 230 | createInfo.ppEnabledExtensionNames = deviceExtensions.data(); 231 | 232 | if (enableValidationLayers) { 233 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 234 | createInfo.ppEnabledLayerNames = validationLayers.data(); 235 | } else { 236 | createInfo.enabledLayerCount = 0; 237 | } 238 | 239 | if (vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mDevice) != VK_SUCCESS) { 240 | throw std::runtime_error("failed to create logical mDevice!"); 241 | } 242 | 243 | vkGetDeviceQueue(mDevice, indices.graphicsFamily, 0, &mGraphicsQueue); 244 | vkGetDeviceQueue(mDevice, indices.presentFamily, 0, &mPresentQueue); 245 | } 246 | 247 | void createSwapChain() { 248 | SwapChainSupportDetails swapChainSupport = querySwapChainSupport(mPhysicalDevice); 249 | 250 | VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); 251 | VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); 252 | VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); 253 | 254 | uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; 255 | if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { 256 | imageCount = swapChainSupport.capabilities.maxImageCount; 257 | } 258 | 259 | VkSwapchainCreateInfoKHR createInfo = {}; 260 | createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; 261 | createInfo.surface = mSurface; 262 | 263 | createInfo.minImageCount = imageCount; 264 | createInfo.imageFormat = surfaceFormat.format; 265 | createInfo.imageColorSpace = surfaceFormat.colorSpace; 266 | createInfo.imageExtent = extent; 267 | createInfo.imageArrayLayers = 1; 268 | createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; 269 | 270 | QueueFamilyIndices indices = findQueueFamilies(mPhysicalDevice); 271 | uint32_t queueFamilyIndices[] = {(uint32_t) indices.graphicsFamily, (uint32_t) indices.presentFamily}; 272 | 273 | if (indices.graphicsFamily != indices.presentFamily) { 274 | createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; 275 | createInfo.queueFamilyIndexCount = 2; 276 | createInfo.pQueueFamilyIndices = queueFamilyIndices; 277 | } else { 278 | createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; 279 | } 280 | 281 | createInfo.preTransform = swapChainSupport.capabilities.currentTransform; 282 | createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; 283 | createInfo.presentMode = presentMode; 284 | createInfo.clipped = VK_TRUE; 285 | 286 | createInfo.oldSwapchain = VK_NULL_HANDLE; 287 | 288 | if (vkCreateSwapchainKHR(mDevice, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS) { 289 | throw std::runtime_error("failed to create swap chain!"); 290 | } 291 | 292 | vkGetSwapchainImagesKHR(mDevice, mSwapChain, &imageCount, nullptr); 293 | mSwapChainImages.resize(imageCount); 294 | vkGetSwapchainImagesKHR(mDevice, mSwapChain, &imageCount, mSwapChainImages.data()); 295 | 296 | mSwapChainImageFormat = surfaceFormat.format; 297 | mSwapChainExtent = extent; 298 | } 299 | 300 | void createImageViews() { 301 | mSwapChainImageViews.resize(mSwapChainImages.size()); 302 | 303 | for (size_t i = 0; i < mSwapChainImages.size(); i++) { 304 | VkImageViewCreateInfo createInfo = {}; 305 | createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; 306 | createInfo.image = mSwapChainImages[i]; 307 | createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; 308 | createInfo.format = mSwapChainImageFormat; 309 | createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; 310 | createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; 311 | createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; 312 | createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; 313 | createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 314 | createInfo.subresourceRange.baseMipLevel = 0; 315 | createInfo.subresourceRange.levelCount = 1; 316 | createInfo.subresourceRange.baseArrayLayer = 0; 317 | createInfo.subresourceRange.layerCount = 1; 318 | 319 | if (vkCreateImageView(mDevice, &createInfo, nullptr, &mSwapChainImageViews[i]) != VK_SUCCESS) { 320 | throw std::runtime_error("failed to create image views!"); 321 | } 322 | } 323 | } 324 | 325 | VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { 326 | if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) { 327 | return {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}; 328 | } 329 | 330 | for (const auto& availableFormat : availableFormats) { 331 | if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { 332 | return availableFormat; 333 | } 334 | } 335 | 336 | return availableFormats[0]; 337 | } 338 | 339 | VkPresentModeKHR chooseSwapPresentMode(const std::vector availablePresentModes) { 340 | VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR; 341 | 342 | for (const auto& availablePresentMode : availablePresentModes) { 343 | if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { 344 | return availablePresentMode; 345 | } else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) { 346 | bestMode = availablePresentMode; 347 | } 348 | } 349 | 350 | return bestMode; 351 | } 352 | 353 | VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { 354 | if (capabilities.currentExtent.width != std::numeric_limits::max()) { 355 | return capabilities.currentExtent; 356 | } else { 357 | VkExtent2D actualExtent = {WIDTH, HEIGHT}; 358 | 359 | actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); 360 | actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); 361 | 362 | return actualExtent; 363 | } 364 | } 365 | 366 | SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { 367 | SwapChainSupportDetails details; 368 | 369 | vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &details.capabilities); 370 | 371 | uint32_t formatCount; 372 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, nullptr); 373 | 374 | if (formatCount != 0) { 375 | details.formats.resize(formatCount); 376 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, details.formats.data()); 377 | } 378 | 379 | uint32_t presentModeCount; 380 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, nullptr); 381 | 382 | if (presentModeCount != 0) { 383 | details.presentModes.resize(presentModeCount); 384 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, details.presentModes.data()); 385 | } 386 | 387 | return details; 388 | } 389 | 390 | bool isDeviceSuitable(VkPhysicalDevice device) { 391 | QueueFamilyIndices indices = findQueueFamilies(device); 392 | 393 | bool extensionsSupported = checkDeviceExtensionSupport(device); 394 | 395 | bool swapChainAdequate = false; 396 | if (extensionsSupported) { 397 | SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); 398 | swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); 399 | } 400 | 401 | return indices.isComplete() && extensionsSupported && swapChainAdequate; 402 | } 403 | 404 | bool checkDeviceExtensionSupport(VkPhysicalDevice device) { 405 | uint32_t extensionCount; 406 | vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); 407 | 408 | std::vector availableExtensions(extensionCount); 409 | vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); 410 | 411 | std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); 412 | 413 | for (const auto& extension : availableExtensions) { 414 | requiredExtensions.erase(extension.extensionName); 415 | } 416 | 417 | return requiredExtensions.empty(); 418 | } 419 | 420 | QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { 421 | QueueFamilyIndices indices; 422 | 423 | uint32_t queueFamilyCount = 0; 424 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); 425 | 426 | std::vector queueFamilies(queueFamilyCount); 427 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); 428 | 429 | int i = 0; 430 | for (const auto& queueFamily : queueFamilies) { 431 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { 432 | indices.graphicsFamily = i; 433 | } 434 | 435 | VkBool32 presentSupport = false; 436 | vkGetPhysicalDeviceSurfaceSupportKHR(device, i, mSurface, &presentSupport); 437 | 438 | if (queueFamily.queueCount > 0 && presentSupport) { 439 | indices.presentFamily = i; 440 | } 441 | 442 | if (indices.isComplete()) { 443 | break; 444 | } 445 | 446 | i++; 447 | } 448 | 449 | return indices; 450 | } 451 | 452 | std::vector getRequiredExtensions() { 453 | uint32_t glfwExtensionCount = 0; 454 | const char** glfwExtensions; 455 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 456 | 457 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 458 | 459 | if (enableValidationLayers) { 460 | extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); 461 | } 462 | 463 | return extensions; 464 | } 465 | 466 | bool checkValidationLayerSupport() { 467 | uint32_t layerCount; 468 | vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 469 | 470 | std::vector availableLayers(layerCount); 471 | vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 472 | 473 | for (const char* layerName : validationLayers) { 474 | bool layerFound = false; 475 | 476 | for (const auto& layerProperties : availableLayers) { 477 | if (strcmp(layerName, layerProperties.layerName) == 0) { 478 | layerFound = true; 479 | break; 480 | } 481 | } 482 | 483 | if (!layerFound) { 484 | return false; 485 | } 486 | } 487 | 488 | return true; 489 | } 490 | 491 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) { 492 | std::cerr << "validation layer: " << msg << std::endl; 493 | 494 | return VK_FALSE; 495 | } 496 | }; 497 | 498 | int main() { 499 | HelloTriangleApplication app; 500 | 501 | try { 502 | app.run(); 503 | } catch (const std::runtime_error& e) { 504 | std::cerr << e.what() << std::endl; 505 | return EXIT_FAILURE; 506 | } 507 | 508 | return EXIT_SUCCESS; 509 | } 510 | -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/1_Presentation/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(1_window_surface 1_window_surface.cpp) 2 | target_link_libraries(1_window_surface ${LIBS}) 3 | 4 | add_executable(2_swapchain 2_swapchain.cpp) 5 | target_link_libraries(2_swapchain ${LIBS}) 6 | 7 | add_executable(3_image_views 3_image_views.cpp) 8 | target_link_libraries(3_image_views ${LIBS}) -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/2_Graphics_pipline_basics/1_shader_modules.cpp: -------------------------------------------------------------------------------- 1 | #define GLFW_INCLUDE_VULKAN 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | const int WIDTH = 800; 13 | const int HEIGHT = 600; 14 | 15 | const std::vector validationLayers = { 16 | "VK_LAYER_LUNARG_standard_validation" 17 | }; 18 | 19 | const std::vector deviceExtensions = { 20 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 21 | }; 22 | 23 | #ifdef NDEBUG 24 | const bool enableValidationLayers = false; 25 | #else 26 | const bool enableValidationLayers = true; 27 | #endif 28 | 29 | VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) { 30 | auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"); 31 | if (func != nullptr) { 32 | return func(instance, pCreateInfo, pAllocator, pCallback); 33 | } else { 34 | return VK_ERROR_EXTENSION_NOT_PRESENT; 35 | } 36 | } 37 | 38 | void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) { 39 | auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); 40 | if (func != nullptr) { 41 | func(instance, callback, pAllocator); 42 | } 43 | } 44 | 45 | struct QueueFamilyIndices { 46 | int graphicsFamily = -1; 47 | int presentFamily = -1; 48 | 49 | bool isComplete() { 50 | return graphicsFamily >= 0 && presentFamily >= 0; 51 | } 52 | }; 53 | 54 | struct SwapChainSupportDetails { 55 | VkSurfaceCapabilitiesKHR capabilities; 56 | std::vector formats; 57 | std::vector presentModes; 58 | }; 59 | 60 | class HelloTriangleApplication { 61 | public: 62 | void run() { 63 | initWindow(); 64 | initVulkan(); 65 | mainLoop(); 66 | cleanup(); 67 | } 68 | 69 | private: 70 | GLFWwindow* mWindow; 71 | 72 | VkInstance mInstance; 73 | VkDebugReportCallbackEXT mCallback; 74 | VkSurfaceKHR mSurface; 75 | 76 | VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; 77 | VkDevice mDevice; 78 | 79 | VkQueue mGraphicsQueue; 80 | VkQueue mPresentQueue; 81 | 82 | VkSwapchainKHR mSwapChain; 83 | std::vector mSwapChainImages; 84 | VkFormat mSwapChainImageFormat; 85 | VkExtent2D mSwapChainExtent; 86 | std::vector mSwapChainImageViews; 87 | 88 | void initWindow() { 89 | glfwInit(); 90 | 91 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 92 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 93 | 94 | mWindow = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 95 | } 96 | 97 | void initVulkan() { 98 | createInstance(); 99 | setupDebugCallback(); 100 | createSurface(); 101 | pickPhysicalDevice(); 102 | createLogicalDevice(); 103 | createSwapChain(); 104 | createImageViews(); 105 | createGraphicsPipeline(); 106 | } 107 | 108 | void mainLoop() { 109 | while (!glfwWindowShouldClose(mWindow)) { 110 | glfwPollEvents(); 111 | } 112 | } 113 | 114 | void cleanup() { 115 | for (auto imageView : mSwapChainImageViews) { 116 | vkDestroyImageView(mDevice, imageView, nullptr); 117 | } 118 | 119 | vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr); 120 | vkDestroyDevice(mDevice, nullptr); 121 | DestroyDebugReportCallbackEXT(mInstance, mCallback, nullptr); 122 | vkDestroySurfaceKHR(mInstance, mSurface, nullptr); 123 | vkDestroyInstance(mInstance, nullptr); 124 | 125 | glfwDestroyWindow(mWindow); 126 | 127 | glfwTerminate(); 128 | } 129 | 130 | void createInstance() { 131 | if (enableValidationLayers && !checkValidationLayerSupport()) { 132 | throw std::runtime_error("validation layers requested, but not available!"); 133 | } 134 | 135 | VkApplicationInfo appInfo = {}; 136 | appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 137 | appInfo.pApplicationName = "Hello Triangle"; 138 | appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 139 | appInfo.pEngineName = "No Engine"; 140 | appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 141 | appInfo.apiVersion = VK_API_VERSION_1_0; 142 | 143 | VkInstanceCreateInfo createInfo = {}; 144 | createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 145 | createInfo.pApplicationInfo = &appInfo; 146 | 147 | auto extensions = getRequiredExtensions(); 148 | createInfo.enabledExtensionCount = static_cast(extensions.size()); 149 | createInfo.ppEnabledExtensionNames = extensions.data(); 150 | 151 | if (enableValidationLayers) { 152 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 153 | createInfo.ppEnabledLayerNames = validationLayers.data(); 154 | } else { 155 | createInfo.enabledLayerCount = 0; 156 | } 157 | 158 | if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS) { 159 | throw std::runtime_error("failed to create mInstance!"); 160 | } 161 | } 162 | 163 | void setupDebugCallback() { 164 | if (!enableValidationLayers) return; 165 | 166 | VkDebugReportCallbackCreateInfoEXT createInfo = {}; 167 | createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; 168 | createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; 169 | createInfo.pfnCallback = debugCallback; 170 | 171 | if (CreateDebugReportCallbackEXT(mInstance, &createInfo, nullptr, &mCallback) != VK_SUCCESS) { 172 | throw std::runtime_error("failed to set up debug mCallback!"); 173 | } 174 | } 175 | 176 | void createSurface() { 177 | if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) { 178 | throw std::runtime_error("failed to create mWindow mSurface!"); 179 | } 180 | } 181 | 182 | void pickPhysicalDevice() { 183 | uint32_t deviceCount = 0; 184 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr); 185 | 186 | if (deviceCount == 0) { 187 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 188 | } 189 | 190 | std::vector devices(deviceCount); 191 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data()); 192 | 193 | for (const auto& device : devices) { 194 | if (isDeviceSuitable(device)) { 195 | mPhysicalDevice = device; 196 | break; 197 | } 198 | } 199 | 200 | if (mPhysicalDevice == VK_NULL_HANDLE) { 201 | throw std::runtime_error("failed to find a suitable GPU!"); 202 | } 203 | } 204 | 205 | void createLogicalDevice() { 206 | QueueFamilyIndices indices = findQueueFamilies(mPhysicalDevice); 207 | 208 | std::vector queueCreateInfos; 209 | std::set uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily}; 210 | 211 | float queuePriority = 1.0f; 212 | for (int queueFamily : uniqueQueueFamilies) { 213 | VkDeviceQueueCreateInfo queueCreateInfo = {}; 214 | queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 215 | queueCreateInfo.queueFamilyIndex = queueFamily; 216 | queueCreateInfo.queueCount = 1; 217 | queueCreateInfo.pQueuePriorities = &queuePriority; 218 | queueCreateInfos.push_back(queueCreateInfo); 219 | } 220 | 221 | VkPhysicalDeviceFeatures deviceFeatures = {}; 222 | 223 | VkDeviceCreateInfo createInfo = {}; 224 | createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 225 | 226 | createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); 227 | createInfo.pQueueCreateInfos = queueCreateInfos.data(); 228 | 229 | createInfo.pEnabledFeatures = &deviceFeatures; 230 | 231 | createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); 232 | createInfo.ppEnabledExtensionNames = deviceExtensions.data(); 233 | 234 | if (enableValidationLayers) { 235 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 236 | createInfo.ppEnabledLayerNames = validationLayers.data(); 237 | } else { 238 | createInfo.enabledLayerCount = 0; 239 | } 240 | 241 | if (vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mDevice) != VK_SUCCESS) { 242 | throw std::runtime_error("failed to create logical mDevice!"); 243 | } 244 | 245 | vkGetDeviceQueue(mDevice, indices.graphicsFamily, 0, &mGraphicsQueue); 246 | vkGetDeviceQueue(mDevice, indices.presentFamily, 0, &mPresentQueue); 247 | } 248 | 249 | void createSwapChain() { 250 | SwapChainSupportDetails swapChainSupport = querySwapChainSupport(mPhysicalDevice); 251 | 252 | VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); 253 | VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); 254 | VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); 255 | 256 | uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; 257 | if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { 258 | imageCount = swapChainSupport.capabilities.maxImageCount; 259 | } 260 | 261 | VkSwapchainCreateInfoKHR createInfo = {}; 262 | createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; 263 | createInfo.surface = mSurface; 264 | 265 | createInfo.minImageCount = imageCount; 266 | createInfo.imageFormat = surfaceFormat.format; 267 | createInfo.imageColorSpace = surfaceFormat.colorSpace; 268 | createInfo.imageExtent = extent; 269 | createInfo.imageArrayLayers = 1; 270 | createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; 271 | 272 | QueueFamilyIndices indices = findQueueFamilies(mPhysicalDevice); 273 | uint32_t queueFamilyIndices[] = {(uint32_t) indices.graphicsFamily, (uint32_t) indices.presentFamily}; 274 | 275 | if (indices.graphicsFamily != indices.presentFamily) { 276 | createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; 277 | createInfo.queueFamilyIndexCount = 2; 278 | createInfo.pQueueFamilyIndices = queueFamilyIndices; 279 | } else { 280 | createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; 281 | } 282 | 283 | createInfo.preTransform = swapChainSupport.capabilities.currentTransform; 284 | createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; 285 | createInfo.presentMode = presentMode; 286 | createInfo.clipped = VK_TRUE; 287 | 288 | createInfo.oldSwapchain = VK_NULL_HANDLE; 289 | 290 | if (vkCreateSwapchainKHR(mDevice, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS) { 291 | throw std::runtime_error("failed to create swap chain!"); 292 | } 293 | 294 | vkGetSwapchainImagesKHR(mDevice, mSwapChain, &imageCount, nullptr); 295 | mSwapChainImages.resize(imageCount); 296 | vkGetSwapchainImagesKHR(mDevice, mSwapChain, &imageCount, mSwapChainImages.data()); 297 | 298 | mSwapChainImageFormat = surfaceFormat.format; 299 | mSwapChainExtent = extent; 300 | } 301 | 302 | void createImageViews() { 303 | mSwapChainImageViews.resize(mSwapChainImages.size()); 304 | 305 | for (size_t i = 0; i < mSwapChainImages.size(); i++) { 306 | VkImageViewCreateInfo createInfo = {}; 307 | createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; 308 | createInfo.image = mSwapChainImages[i]; 309 | createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; 310 | createInfo.format = mSwapChainImageFormat; 311 | createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; 312 | createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; 313 | createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; 314 | createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; 315 | createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 316 | createInfo.subresourceRange.baseMipLevel = 0; 317 | createInfo.subresourceRange.levelCount = 1; 318 | createInfo.subresourceRange.baseArrayLayer = 0; 319 | createInfo.subresourceRange.layerCount = 1; 320 | 321 | if (vkCreateImageView(mDevice, &createInfo, nullptr, &mSwapChainImageViews[i]) != VK_SUCCESS) { 322 | throw std::runtime_error("failed to create image views!"); 323 | } 324 | } 325 | } 326 | 327 | void createGraphicsPipeline() { 328 | auto vertShaderCode = readFile("shaders/vert.spv"); 329 | auto fragShaderCode = readFile("shaders/frag.spv"); 330 | 331 | VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); 332 | VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); 333 | 334 | VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; 335 | vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; 336 | vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; 337 | vertShaderStageInfo.module = vertShaderModule; 338 | vertShaderStageInfo.pName = "main"; 339 | 340 | VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; 341 | fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; 342 | fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; 343 | fragShaderStageInfo.module = fragShaderModule; 344 | fragShaderStageInfo.pName = "main"; 345 | 346 | VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; 347 | 348 | vkDestroyShaderModule(mDevice, fragShaderModule, nullptr); 349 | vkDestroyShaderModule(mDevice, vertShaderModule, nullptr); 350 | } 351 | 352 | VkShaderModule createShaderModule(const std::vector& code) { 353 | VkShaderModuleCreateInfo createInfo = {}; 354 | createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; 355 | createInfo.codeSize = code.size(); 356 | createInfo.pCode = reinterpret_cast(code.data()); 357 | 358 | VkShaderModule shaderModule; 359 | if (vkCreateShaderModule(mDevice, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { 360 | throw std::runtime_error("failed to create shader module!"); 361 | } 362 | 363 | return shaderModule; 364 | } 365 | 366 | VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { 367 | if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) { 368 | return {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}; 369 | } 370 | 371 | for (const auto& availableFormat : availableFormats) { 372 | if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { 373 | return availableFormat; 374 | } 375 | } 376 | 377 | return availableFormats[0]; 378 | } 379 | 380 | VkPresentModeKHR chooseSwapPresentMode(const std::vector availablePresentModes) { 381 | VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR; 382 | 383 | for (const auto& availablePresentMode : availablePresentModes) { 384 | if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { 385 | return availablePresentMode; 386 | } else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) { 387 | bestMode = availablePresentMode; 388 | } 389 | } 390 | 391 | return bestMode; 392 | } 393 | 394 | VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { 395 | if (capabilities.currentExtent.width != std::numeric_limits::max()) { 396 | return capabilities.currentExtent; 397 | } else { 398 | VkExtent2D actualExtent = {WIDTH, HEIGHT}; 399 | 400 | actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); 401 | actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); 402 | 403 | return actualExtent; 404 | } 405 | } 406 | 407 | SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { 408 | SwapChainSupportDetails details; 409 | 410 | vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &details.capabilities); 411 | 412 | uint32_t formatCount; 413 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, nullptr); 414 | 415 | if (formatCount != 0) { 416 | details.formats.resize(formatCount); 417 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, details.formats.data()); 418 | } 419 | 420 | uint32_t presentModeCount; 421 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, nullptr); 422 | 423 | if (presentModeCount != 0) { 424 | details.presentModes.resize(presentModeCount); 425 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, details.presentModes.data()); 426 | } 427 | 428 | return details; 429 | } 430 | 431 | bool isDeviceSuitable(VkPhysicalDevice device) { 432 | QueueFamilyIndices indices = findQueueFamilies(device); 433 | 434 | bool extensionsSupported = checkDeviceExtensionSupport(device); 435 | 436 | bool swapChainAdequate = false; 437 | if (extensionsSupported) { 438 | SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); 439 | swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); 440 | } 441 | 442 | return indices.isComplete() && extensionsSupported && swapChainAdequate; 443 | } 444 | 445 | bool checkDeviceExtensionSupport(VkPhysicalDevice device) { 446 | uint32_t extensionCount; 447 | vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); 448 | 449 | std::vector availableExtensions(extensionCount); 450 | vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); 451 | 452 | std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); 453 | 454 | for (const auto& extension : availableExtensions) { 455 | requiredExtensions.erase(extension.extensionName); 456 | } 457 | 458 | return requiredExtensions.empty(); 459 | } 460 | 461 | QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { 462 | QueueFamilyIndices indices; 463 | 464 | uint32_t queueFamilyCount = 0; 465 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); 466 | 467 | std::vector queueFamilies(queueFamilyCount); 468 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); 469 | 470 | int i = 0; 471 | for (const auto& queueFamily : queueFamilies) { 472 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { 473 | indices.graphicsFamily = i; 474 | } 475 | 476 | VkBool32 presentSupport = false; 477 | vkGetPhysicalDeviceSurfaceSupportKHR(device, i, mSurface, &presentSupport); 478 | 479 | if (queueFamily.queueCount > 0 && presentSupport) { 480 | indices.presentFamily = i; 481 | } 482 | 483 | if (indices.isComplete()) { 484 | break; 485 | } 486 | 487 | i++; 488 | } 489 | 490 | return indices; 491 | } 492 | 493 | std::vector getRequiredExtensions() { 494 | uint32_t glfwExtensionCount = 0; 495 | const char** glfwExtensions; 496 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 497 | 498 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 499 | 500 | if (enableValidationLayers) { 501 | extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); 502 | } 503 | 504 | return extensions; 505 | } 506 | 507 | bool checkValidationLayerSupport() { 508 | uint32_t layerCount; 509 | vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 510 | 511 | std::vector availableLayers(layerCount); 512 | vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 513 | 514 | for (const char* layerName : validationLayers) { 515 | bool layerFound = false; 516 | 517 | for (const auto& layerProperties : availableLayers) { 518 | if (strcmp(layerName, layerProperties.layerName) == 0) { 519 | layerFound = true; 520 | break; 521 | } 522 | } 523 | 524 | if (!layerFound) { 525 | return false; 526 | } 527 | } 528 | 529 | return true; 530 | } 531 | 532 | static std::vector readFile(const std::string& filename) { 533 | std::ifstream file(filename, std::ios::ate | std::ios::binary); 534 | 535 | if (!file.is_open()) { 536 | throw std::runtime_error("failed to open file!"); 537 | } 538 | 539 | size_t fileSize = (size_t) file.tellg(); 540 | std::vector buffer(fileSize); 541 | 542 | file.seekg(0); 543 | file.read(buffer.data(), fileSize); 544 | 545 | file.close(); 546 | 547 | return buffer; 548 | } 549 | 550 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) { 551 | std::cerr << "validation layer: " << msg << std::endl; 552 | 553 | return VK_FALSE; 554 | } 555 | }; 556 | 557 | int main() { 558 | HelloTriangleApplication app; 559 | 560 | try { 561 | app.run(); 562 | } catch (const std::runtime_error& e) { 563 | std::cerr << e.what() << std::endl; 564 | return EXIT_FAILURE; 565 | } 566 | 567 | return EXIT_SUCCESS; 568 | } 569 | -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/2_Graphics_pipline_basics/2_fixed_functions.cpp: -------------------------------------------------------------------------------- 1 | #define GLFW_INCLUDE_VULKAN 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | const int WIDTH = 800; 13 | const int HEIGHT = 600; 14 | 15 | const std::vector validationLayers = { 16 | "VK_LAYER_LUNARG_standard_validation" 17 | }; 18 | 19 | const std::vector deviceExtensions = { 20 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 21 | }; 22 | 23 | #ifdef NDEBUG 24 | const bool enableValidationLayers = false; 25 | #else 26 | const bool enableValidationLayers = true; 27 | #endif 28 | 29 | VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback) { 30 | auto func = (PFN_vkCreateDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT"); 31 | if (func != nullptr) { 32 | return func(instance, pCreateInfo, pAllocator, pCallback); 33 | } else { 34 | return VK_ERROR_EXTENSION_NOT_PRESENT; 35 | } 36 | } 37 | 38 | void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator) { 39 | auto func = (PFN_vkDestroyDebugReportCallbackEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT"); 40 | if (func != nullptr) { 41 | func(instance, callback, pAllocator); 42 | } 43 | } 44 | 45 | struct QueueFamilyIndices { 46 | int graphicsFamily = -1; 47 | int presentFamily = -1; 48 | 49 | bool isComplete() { 50 | return graphicsFamily >= 0 && presentFamily >= 0; 51 | } 52 | }; 53 | 54 | struct SwapChainSupportDetails { 55 | VkSurfaceCapabilitiesKHR capabilities; 56 | std::vector formats; 57 | std::vector presentModes; 58 | }; 59 | 60 | class HelloTriangleApplication { 61 | public: 62 | void run() { 63 | initWindow(); 64 | initVulkan(); 65 | mainLoop(); 66 | cleanup(); 67 | } 68 | 69 | private: 70 | GLFWwindow* mWindow; 71 | 72 | VkInstance mInstance; 73 | VkDebugReportCallbackEXT mCallback; 74 | VkSurfaceKHR mSurface; 75 | 76 | VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; 77 | VkDevice mDevice; 78 | 79 | VkQueue mGraphicsQueue; 80 | VkQueue mPresentQueue; 81 | 82 | VkSwapchainKHR mSwapChain; 83 | std::vector mSwapChainImages; 84 | VkFormat mSwapChainImageFormat; 85 | VkExtent2D mSwapChainExtent; 86 | std::vector mSwapChainImageViews; 87 | 88 | VkPipelineLayout mPipelineLayout; 89 | 90 | void initWindow() { 91 | glfwInit(); 92 | 93 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 94 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 95 | 96 | mWindow = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 97 | } 98 | 99 | void initVulkan() { 100 | createInstance(); 101 | setupDebugCallback(); 102 | createSurface(); 103 | pickPhysicalDevice(); 104 | createLogicalDevice(); 105 | createSwapChain(); 106 | createImageViews(); 107 | createGraphicsPipeline(); 108 | } 109 | 110 | void mainLoop() { 111 | while (!glfwWindowShouldClose(mWindow)) { 112 | glfwPollEvents(); 113 | } 114 | } 115 | 116 | void cleanup() { 117 | vkDestroyPipelineLayout(mDevice, mPipelineLayout, nullptr); 118 | 119 | for (auto imageView : mSwapChainImageViews) { 120 | vkDestroyImageView(mDevice, imageView, nullptr); 121 | } 122 | 123 | vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr); 124 | vkDestroyDevice(mDevice, nullptr); 125 | DestroyDebugReportCallbackEXT(mInstance, mCallback, nullptr); 126 | vkDestroySurfaceKHR(mInstance, mSurface, nullptr); 127 | vkDestroyInstance(mInstance, nullptr); 128 | 129 | glfwDestroyWindow(mWindow); 130 | 131 | glfwTerminate(); 132 | } 133 | 134 | void createInstance() { 135 | if (enableValidationLayers && !checkValidationLayerSupport()) { 136 | throw std::runtime_error("validation layers requested, but not available!"); 137 | } 138 | 139 | VkApplicationInfo appInfo = {}; 140 | appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 141 | appInfo.pApplicationName = "Hello Triangle"; 142 | appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 143 | appInfo.pEngineName = "No Engine"; 144 | appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 145 | appInfo.apiVersion = VK_API_VERSION_1_0; 146 | 147 | VkInstanceCreateInfo createInfo = {}; 148 | createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 149 | createInfo.pApplicationInfo = &appInfo; 150 | 151 | auto extensions = getRequiredExtensions(); 152 | createInfo.enabledExtensionCount = static_cast(extensions.size()); 153 | createInfo.ppEnabledExtensionNames = extensions.data(); 154 | 155 | if (enableValidationLayers) { 156 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 157 | createInfo.ppEnabledLayerNames = validationLayers.data(); 158 | } else { 159 | createInfo.enabledLayerCount = 0; 160 | } 161 | 162 | if (vkCreateInstance(&createInfo, nullptr, &mInstance) != VK_SUCCESS) { 163 | throw std::runtime_error("failed to create mInstance!"); 164 | } 165 | } 166 | 167 | void setupDebugCallback() { 168 | if (!enableValidationLayers) return; 169 | 170 | VkDebugReportCallbackCreateInfoEXT createInfo = {}; 171 | createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; 172 | createInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; 173 | createInfo.pfnCallback = debugCallback; 174 | 175 | if (CreateDebugReportCallbackEXT(mInstance, &createInfo, nullptr, &mCallback) != VK_SUCCESS) { 176 | throw std::runtime_error("failed to set up debug mCallback!"); 177 | } 178 | } 179 | 180 | void createSurface() { 181 | if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) { 182 | throw std::runtime_error("failed to create mWindow mSurface!"); 183 | } 184 | } 185 | 186 | void pickPhysicalDevice() { 187 | uint32_t deviceCount = 0; 188 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr); 189 | 190 | if (deviceCount == 0) { 191 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 192 | } 193 | 194 | std::vector devices(deviceCount); 195 | vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data()); 196 | 197 | for (const auto& device : devices) { 198 | if (isDeviceSuitable(device)) { 199 | mPhysicalDevice = device; 200 | break; 201 | } 202 | } 203 | 204 | if (mPhysicalDevice == VK_NULL_HANDLE) { 205 | throw std::runtime_error("failed to find a suitable GPU!"); 206 | } 207 | } 208 | 209 | void createLogicalDevice() { 210 | QueueFamilyIndices indices = findQueueFamilies(mPhysicalDevice); 211 | 212 | std::vector queueCreateInfos; 213 | std::set uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily}; 214 | 215 | float queuePriority = 1.0f; 216 | for (int queueFamily : uniqueQueueFamilies) { 217 | VkDeviceQueueCreateInfo queueCreateInfo = {}; 218 | queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 219 | queueCreateInfo.queueFamilyIndex = queueFamily; 220 | queueCreateInfo.queueCount = 1; 221 | queueCreateInfo.pQueuePriorities = &queuePriority; 222 | queueCreateInfos.push_back(queueCreateInfo); 223 | } 224 | 225 | VkPhysicalDeviceFeatures deviceFeatures = {}; 226 | 227 | VkDeviceCreateInfo createInfo = {}; 228 | createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 229 | 230 | createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); 231 | createInfo.pQueueCreateInfos = queueCreateInfos.data(); 232 | 233 | createInfo.pEnabledFeatures = &deviceFeatures; 234 | 235 | createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); 236 | createInfo.ppEnabledExtensionNames = deviceExtensions.data(); 237 | 238 | if (enableValidationLayers) { 239 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 240 | createInfo.ppEnabledLayerNames = validationLayers.data(); 241 | } else { 242 | createInfo.enabledLayerCount = 0; 243 | } 244 | 245 | if (vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mDevice) != VK_SUCCESS) { 246 | throw std::runtime_error("failed to create logical mDevice!"); 247 | } 248 | 249 | vkGetDeviceQueue(mDevice, indices.graphicsFamily, 0, &mGraphicsQueue); 250 | vkGetDeviceQueue(mDevice, indices.presentFamily, 0, &mPresentQueue); 251 | } 252 | 253 | void createSwapChain() { 254 | SwapChainSupportDetails swapChainSupport = querySwapChainSupport(mPhysicalDevice); 255 | 256 | VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); 257 | VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); 258 | VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); 259 | 260 | uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; 261 | if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { 262 | imageCount = swapChainSupport.capabilities.maxImageCount; 263 | } 264 | 265 | VkSwapchainCreateInfoKHR createInfo = {}; 266 | createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; 267 | createInfo.surface = mSurface; 268 | 269 | createInfo.minImageCount = imageCount; 270 | createInfo.imageFormat = surfaceFormat.format; 271 | createInfo.imageColorSpace = surfaceFormat.colorSpace; 272 | createInfo.imageExtent = extent; 273 | createInfo.imageArrayLayers = 1; 274 | createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; 275 | 276 | QueueFamilyIndices indices = findQueueFamilies(mPhysicalDevice); 277 | uint32_t queueFamilyIndices[] = {(uint32_t) indices.graphicsFamily, (uint32_t) indices.presentFamily}; 278 | 279 | if (indices.graphicsFamily != indices.presentFamily) { 280 | createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; 281 | createInfo.queueFamilyIndexCount = 2; 282 | createInfo.pQueueFamilyIndices = queueFamilyIndices; 283 | } else { 284 | createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; 285 | } 286 | 287 | createInfo.preTransform = swapChainSupport.capabilities.currentTransform; 288 | createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; 289 | createInfo.presentMode = presentMode; 290 | createInfo.clipped = VK_TRUE; 291 | 292 | createInfo.oldSwapchain = VK_NULL_HANDLE; 293 | 294 | if (vkCreateSwapchainKHR(mDevice, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS) { 295 | throw std::runtime_error("failed to create swap chain!"); 296 | } 297 | 298 | vkGetSwapchainImagesKHR(mDevice, mSwapChain, &imageCount, nullptr); 299 | mSwapChainImages.resize(imageCount); 300 | vkGetSwapchainImagesKHR(mDevice, mSwapChain, &imageCount, mSwapChainImages.data()); 301 | 302 | mSwapChainImageFormat = surfaceFormat.format; 303 | mSwapChainExtent = extent; 304 | } 305 | 306 | void createImageViews() { 307 | mSwapChainImageViews.resize(mSwapChainImages.size()); 308 | 309 | for (size_t i = 0; i < mSwapChainImages.size(); i++) { 310 | VkImageViewCreateInfo createInfo = {}; 311 | createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; 312 | createInfo.image = mSwapChainImages[i]; 313 | createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; 314 | createInfo.format = mSwapChainImageFormat; 315 | createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; 316 | createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; 317 | createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; 318 | createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; 319 | createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 320 | createInfo.subresourceRange.baseMipLevel = 0; 321 | createInfo.subresourceRange.levelCount = 1; 322 | createInfo.subresourceRange.baseArrayLayer = 0; 323 | createInfo.subresourceRange.layerCount = 1; 324 | 325 | if (vkCreateImageView(mDevice, &createInfo, nullptr, &mSwapChainImageViews[i]) != VK_SUCCESS) { 326 | throw std::runtime_error("failed to create image views!"); 327 | } 328 | } 329 | } 330 | 331 | void createGraphicsPipeline() { 332 | auto vertShaderCode = readFile("shaders/vert.spv"); 333 | auto fragShaderCode = readFile("shaders/frag.spv"); 334 | 335 | VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); 336 | VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); 337 | 338 | VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; 339 | vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; 340 | vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; 341 | vertShaderStageInfo.module = vertShaderModule; 342 | vertShaderStageInfo.pName = "main"; 343 | 344 | VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; 345 | fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; 346 | fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; 347 | fragShaderStageInfo.module = fragShaderModule; 348 | fragShaderStageInfo.pName = "main"; 349 | 350 | VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo}; 351 | 352 | VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; 353 | vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; 354 | vertexInputInfo.vertexBindingDescriptionCount = 0; 355 | vertexInputInfo.vertexAttributeDescriptionCount = 0; 356 | 357 | VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; 358 | inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; 359 | inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; 360 | inputAssembly.primitiveRestartEnable = VK_FALSE; 361 | 362 | VkViewport viewport = {}; 363 | viewport.x = 0.0f; 364 | viewport.y = 0.0f; 365 | viewport.width = (float) mSwapChainExtent.width; 366 | viewport.height = (float) mSwapChainExtent.height; 367 | viewport.minDepth = 0.0f; 368 | viewport.maxDepth = 1.0f; 369 | 370 | VkRect2D scissor = {}; 371 | scissor.offset = {0, 0}; 372 | scissor.extent = mSwapChainExtent; 373 | 374 | VkPipelineViewportStateCreateInfo viewportState = {}; 375 | viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; 376 | viewportState.viewportCount = 1; 377 | viewportState.pViewports = &viewport; 378 | viewportState.scissorCount = 1; 379 | viewportState.pScissors = &scissor; 380 | 381 | VkPipelineRasterizationStateCreateInfo rasterizer = {}; 382 | rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; 383 | rasterizer.depthClampEnable = VK_FALSE; 384 | rasterizer.rasterizerDiscardEnable = VK_FALSE; 385 | rasterizer.polygonMode = VK_POLYGON_MODE_FILL; 386 | rasterizer.lineWidth = 1.0f; 387 | rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; 388 | rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; 389 | rasterizer.depthBiasEnable = VK_FALSE; 390 | 391 | VkPipelineMultisampleStateCreateInfo multisampling = {}; 392 | multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; 393 | multisampling.sampleShadingEnable = VK_FALSE; 394 | multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; 395 | 396 | VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; 397 | colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; 398 | colorBlendAttachment.blendEnable = VK_FALSE; 399 | 400 | VkPipelineColorBlendStateCreateInfo colorBlending = {}; 401 | colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; 402 | colorBlending.logicOpEnable = VK_FALSE; 403 | colorBlending.logicOp = VK_LOGIC_OP_COPY; 404 | colorBlending.attachmentCount = 1; 405 | colorBlending.pAttachments = &colorBlendAttachment; 406 | colorBlending.blendConstants[0] = 0.0f; 407 | colorBlending.blendConstants[1] = 0.0f; 408 | colorBlending.blendConstants[2] = 0.0f; 409 | colorBlending.blendConstants[3] = 0.0f; 410 | 411 | VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; 412 | pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; 413 | pipelineLayoutInfo.setLayoutCount = 0; 414 | pipelineLayoutInfo.pushConstantRangeCount = 0; 415 | 416 | if (vkCreatePipelineLayout(mDevice, &pipelineLayoutInfo, nullptr, &mPipelineLayout) != VK_SUCCESS) { 417 | throw std::runtime_error("failed to create pipeline layout!"); 418 | } 419 | 420 | vkDestroyShaderModule(mDevice, fragShaderModule, nullptr); 421 | vkDestroyShaderModule(mDevice, vertShaderModule, nullptr); 422 | } 423 | 424 | VkShaderModule createShaderModule(const std::vector& code) { 425 | VkShaderModuleCreateInfo createInfo = {}; 426 | createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; 427 | createInfo.codeSize = code.size(); 428 | createInfo.pCode = reinterpret_cast(code.data()); 429 | 430 | VkShaderModule shaderModule; 431 | if (vkCreateShaderModule(mDevice, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { 432 | throw std::runtime_error("failed to create shader module!"); 433 | } 434 | 435 | return shaderModule; 436 | } 437 | 438 | VkSurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { 439 | if (availableFormats.size() == 1 && availableFormats[0].format == VK_FORMAT_UNDEFINED) { 440 | return {VK_FORMAT_B8G8R8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}; 441 | } 442 | 443 | for (const auto& availableFormat : availableFormats) { 444 | if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { 445 | return availableFormat; 446 | } 447 | } 448 | 449 | return availableFormats[0]; 450 | } 451 | 452 | VkPresentModeKHR chooseSwapPresentMode(const std::vector availablePresentModes) { 453 | VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR; 454 | 455 | for (const auto& availablePresentMode : availablePresentModes) { 456 | if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { 457 | return availablePresentMode; 458 | } else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) { 459 | bestMode = availablePresentMode; 460 | } 461 | } 462 | 463 | return bestMode; 464 | } 465 | 466 | VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { 467 | if (capabilities.currentExtent.width != std::numeric_limits::max()) { 468 | return capabilities.currentExtent; 469 | } else { 470 | VkExtent2D actualExtent = {WIDTH, HEIGHT}; 471 | 472 | actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); 473 | actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); 474 | 475 | return actualExtent; 476 | } 477 | } 478 | 479 | SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { 480 | SwapChainSupportDetails details; 481 | 482 | vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &details.capabilities); 483 | 484 | uint32_t formatCount; 485 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, nullptr); 486 | 487 | if (formatCount != 0) { 488 | details.formats.resize(formatCount); 489 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, details.formats.data()); 490 | } 491 | 492 | uint32_t presentModeCount; 493 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, nullptr); 494 | 495 | if (presentModeCount != 0) { 496 | details.presentModes.resize(presentModeCount); 497 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, details.presentModes.data()); 498 | } 499 | 500 | return details; 501 | } 502 | 503 | bool isDeviceSuitable(VkPhysicalDevice device) { 504 | QueueFamilyIndices indices = findQueueFamilies(device); 505 | 506 | bool extensionsSupported = checkDeviceExtensionSupport(device); 507 | 508 | bool swapChainAdequate = false; 509 | if (extensionsSupported) { 510 | SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); 511 | swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); 512 | } 513 | 514 | return indices.isComplete() && extensionsSupported && swapChainAdequate; 515 | } 516 | 517 | bool checkDeviceExtensionSupport(VkPhysicalDevice device) { 518 | uint32_t extensionCount; 519 | vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); 520 | 521 | std::vector availableExtensions(extensionCount); 522 | vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); 523 | 524 | std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); 525 | 526 | for (const auto& extension : availableExtensions) { 527 | requiredExtensions.erase(extension.extensionName); 528 | } 529 | 530 | return requiredExtensions.empty(); 531 | } 532 | 533 | QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { 534 | QueueFamilyIndices indices; 535 | 536 | uint32_t queueFamilyCount = 0; 537 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); 538 | 539 | std::vector queueFamilies(queueFamilyCount); 540 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); 541 | 542 | int i = 0; 543 | for (const auto& queueFamily : queueFamilies) { 544 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { 545 | indices.graphicsFamily = i; 546 | } 547 | 548 | VkBool32 presentSupport = false; 549 | vkGetPhysicalDeviceSurfaceSupportKHR(device, i, mSurface, &presentSupport); 550 | 551 | if (queueFamily.queueCount > 0 && presentSupport) { 552 | indices.presentFamily = i; 553 | } 554 | 555 | if (indices.isComplete()) { 556 | break; 557 | } 558 | 559 | i++; 560 | } 561 | 562 | return indices; 563 | } 564 | 565 | std::vector getRequiredExtensions() { 566 | uint32_t glfwExtensionCount = 0; 567 | const char** glfwExtensions; 568 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 569 | 570 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 571 | 572 | if (enableValidationLayers) { 573 | extensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); 574 | } 575 | 576 | return extensions; 577 | } 578 | 579 | bool checkValidationLayerSupport() { 580 | uint32_t layerCount; 581 | vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 582 | 583 | std::vector availableLayers(layerCount); 584 | vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 585 | 586 | for (const char* layerName : validationLayers) { 587 | bool layerFound = false; 588 | 589 | for (const auto& layerProperties : availableLayers) { 590 | if (strcmp(layerName, layerProperties.layerName) == 0) { 591 | layerFound = true; 592 | break; 593 | } 594 | } 595 | 596 | if (!layerFound) { 597 | return false; 598 | } 599 | } 600 | 601 | return true; 602 | } 603 | 604 | static std::vector readFile(const std::string& filename) { 605 | std::ifstream file(filename, std::ios::ate | std::ios::binary); 606 | 607 | if (!file.is_open()) { 608 | throw std::runtime_error("failed to open file!"); 609 | } 610 | 611 | size_t fileSize = (size_t) file.tellg(); 612 | std::vector buffer(fileSize); 613 | 614 | file.seekg(0); 615 | file.read(buffer.data(), fileSize); 616 | 617 | file.close(); 618 | 619 | return buffer; 620 | } 621 | 622 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType, uint64_t obj, size_t location, int32_t code, const char* layerPrefix, const char* msg, void* userData) { 623 | std::cerr << "validation layer: " << msg << std::endl; 624 | 625 | return VK_FALSE; 626 | } 627 | }; 628 | 629 | int main() { 630 | HelloTriangleApplication app; 631 | 632 | try { 633 | app.run(); 634 | } catch (const std::runtime_error& e) { 635 | std::cerr << e.what() << std::endl; 636 | return EXIT_FAILURE; 637 | } 638 | 639 | return EXIT_SUCCESS; 640 | } 641 | -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/2_Graphics_pipline_basics/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(1_shader_modules 1_shader_modules.cpp) 2 | target_link_libraries(1_shader_modules ${LIBS}) 3 | 4 | add_executable(2_fixed_functions 2_fixed_functions.cpp) 5 | target_link_libraries(2_fixed_functions ${LIBS}) 6 | 7 | add_executable(3_render_passes 3_render_passes.cpp) 8 | target_link_libraries(3_render_passes ${LIBS}) 9 | 10 | add_executable(4_graphics_pipeline_complete 4_graphics_pipeline_complete.cpp) 11 | target_link_libraries(4_graphics_pipeline_complete ${LIBS}) -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/3_Drawing/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(1_framebuffers 1_framebuffers.cpp) 2 | target_link_libraries(1_framebuffers ${LIBS}) 3 | 4 | add_executable(2_command_buffers 2_command_buffers.cpp) 5 | target_link_libraries(2_command_buffers ${LIBS}) 6 | 7 | add_executable(3_rendering_and_presentation 3_rendering_and_presentation.cpp) 8 | target_link_libraries(3_rendering_and_presentation ${LIBS}) -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/4_swap_chain_recreation/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(1_swap_chain_recreation 1_swap_chain_recreation.cpp) 2 | target_link_libraries(1_swap_chain_recreation ${LIBS}) -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(0_Setup) 2 | add_subdirectory(1_Presentation) 3 | add_subdirectory(2_Graphics_pipline_basics) 4 | add_subdirectory(3_Drawing) 5 | add_subdirectory(4_swap_chain_recreation) 6 | 7 | add_executable(hello_triangle hello_triangle.cpp) 8 | target_link_libraries(hello_triangle ${LIBS}) -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/basic.frag: -------------------------------------------------------------------------------- 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() { 9 | outColor = vec4(fragColor, 1.0); 10 | } -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/basic.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | out gl_PerVertex { 5 | vec4 gl_Position; 6 | }; 7 | 8 | layout(location = 0) out vec3 fragColor; 9 | 10 | vec2 positions[3] = vec2[]( 11 | vec2(0.0, -0.5), 12 | vec2(0.5, 0.5), 13 | vec2(-0.5, 0.5) 14 | ); 15 | 16 | vec3 colors[3] = vec3[]( 17 | vec3(1.0, 0.0, 0.0), 18 | vec3(0.0, 1.0, 0.0), 19 | vec3(0.0, 0.0, 1.0) 20 | ); 21 | 22 | void main() { 23 | gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); 24 | fragColor = colors[gl_VertexIndex]; 25 | } -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/CH1-Draw_a_triangle/frag.spv -------------------------------------------------------------------------------- /CH1-Draw_a_triangle/vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/CH1-Draw_a_triangle/vert.spv -------------------------------------------------------------------------------- /CH2-Vertex_buffer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(1_vertex_input_description 1_vertex_input_description.cpp) 2 | target_link_libraries(1_vertex_input_description ${LIBS}) 3 | 4 | add_executable(2_vertex_buffer_creation 2_vertex_buffer_creation.cpp) 5 | target_link_libraries(2_vertex_buffer_creation ${LIBS}) 6 | 7 | add_executable(3_staging_buffer 3_staging_buffer.cpp) 8 | target_link_libraries(3_staging_buffer ${LIBS}) 9 | 10 | add_executable(4_index_buffer 4_index_buffer.cpp) 11 | target_link_libraries(4_index_buffer ${LIBS}) -------------------------------------------------------------------------------- /CH2-Vertex_buffer/frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/CH2-Vertex_buffer/frag.spv -------------------------------------------------------------------------------- /CH2-Vertex_buffer/shader.frag: -------------------------------------------------------------------------------- 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() { 9 | outColor = vec4(fragColor, 1.0); 10 | } -------------------------------------------------------------------------------- /CH2-Vertex_buffer/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec2 inPosition; 5 | layout(location = 1) in vec3 inColor; 6 | 7 | layout(location = 0) out vec3 fragColor; 8 | 9 | out gl_PerVertex { 10 | vec4 gl_Position; 11 | }; 12 | 13 | void main() { 14 | gl_Position = vec4(inPosition, 0.0, 1.0); 15 | fragColor = inColor; 16 | } -------------------------------------------------------------------------------- /CH2-Vertex_buffer/vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/CH2-Vertex_buffer/vert.spv -------------------------------------------------------------------------------- /CH3-Uinform_buffer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(1_descriptor_layout_and_buffer 1_descriptor_layout_and_buffer.cpp) 2 | target_link_libraries(1_descriptor_layout_and_buffer ${LIBS}) 3 | 4 | add_executable(2_descriptor_pool_and_sets 2_descriptor_pool_and_sets.cpp) 5 | target_link_libraries(2_descriptor_pool_and_sets ${LIBS}) -------------------------------------------------------------------------------- /CH3-Uinform_buffer/frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/CH3-Uinform_buffer/frag.spv -------------------------------------------------------------------------------- /CH3-Uinform_buffer/shader.frag: -------------------------------------------------------------------------------- 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() { 9 | outColor = vec4(fragColor, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /CH3-Uinform_buffer/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(binding = 0) uniform UniformBufferObject { 5 | mat4 model; 6 | mat4 view; 7 | mat4 proj; 8 | } ubo; 9 | 10 | layout(location = 0) in vec2 inPosition; 11 | layout(location = 1) in vec3 inColor; 12 | 13 | layout(location = 0) out vec3 fragColor; 14 | 15 | out gl_PerVertex { 16 | vec4 gl_Position; 17 | }; 18 | 19 | void main() { 20 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); 21 | fragColor = inColor; 22 | } 23 | -------------------------------------------------------------------------------- /CH3-Uinform_buffer/vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/CH3-Uinform_buffer/vert.spv -------------------------------------------------------------------------------- /CH4-Texture_mapping/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(1_texture_image 1_texture_image.cpp) 2 | target_link_libraries(1_texture_image ${LIBS}) 3 | 4 | add_executable(2_image_view_and_sampler 2_image_view_and_sampler.cpp) 5 | target_link_libraries(2_image_view_and_sampler ${LIBS}) 6 | 7 | add_executable(3_combined_image_sampler 3_combined_image_sampler.cpp) 8 | target_link_libraries(3_combined_image_sampler ${LIBS}) -------------------------------------------------------------------------------- /CH4-Texture_mapping/frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/CH4-Texture_mapping/frag.spv -------------------------------------------------------------------------------- /CH4-Texture_mapping/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(binding = 1) uniform sampler2D texSampler; 5 | 6 | layout(location = 0) in vec3 fragColor; 7 | layout(location = 1) in vec2 fragTexCoord; 8 | 9 | layout(location = 0) out vec4 outColor; 10 | 11 | void main() { 12 | outColor = texture(texSampler, fragTexCoord); 13 | } -------------------------------------------------------------------------------- /CH4-Texture_mapping/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(binding = 0) uniform UniformBufferObject { 5 | mat4 model; 6 | mat4 view; 7 | mat4 proj; 8 | } ubo; 9 | 10 | layout(location = 0) in vec2 inPosition; 11 | layout(location = 1) in vec3 inColor; 12 | layout(location = 2) in vec2 inTexCoord; 13 | 14 | layout(location = 0) out vec3 fragColor; 15 | layout(location = 1) out vec2 fragTexCoord; 16 | 17 | void main() { 18 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); 19 | fragColor = inColor; 20 | fragTexCoord = inTexCoord; 21 | } 22 | -------------------------------------------------------------------------------- /CH4-Texture_mapping/vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/CH4-Texture_mapping/vert.spv -------------------------------------------------------------------------------- /CH5-Depth_buffer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(depth_buffering depth_buffering.cpp) 2 | target_link_libraries(depth_buffering ${LIBS}) -------------------------------------------------------------------------------- /CH5-Depth_buffer/frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/CH5-Depth_buffer/frag.spv -------------------------------------------------------------------------------- /CH5-Depth_buffer/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(binding = 1) uniform sampler2D texSampler; 5 | 6 | layout(location = 0) in vec3 fragColor; 7 | layout(location = 1) in vec2 fragTexCoord; 8 | 9 | layout(location = 0) out vec4 outColor; 10 | 11 | void main() { 12 | outColor = texture(texSampler, fragTexCoord); 13 | } 14 | -------------------------------------------------------------------------------- /CH5-Depth_buffer/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(binding = 0) uniform UniformBufferObject { 5 | mat4 model; 6 | mat4 view; 7 | mat4 proj; 8 | } ubo; 9 | 10 | layout(location = 0) in vec3 inPosition; 11 | layout(location = 1) in vec3 inColor; 12 | layout(location = 2) in vec2 inTexCoord; 13 | 14 | layout(location = 0) out vec3 fragColor; 15 | layout(location = 1) out vec2 fragTexCoord; 16 | 17 | out gl_PerVertex { 18 | vec4 gl_Position; 19 | }; 20 | 21 | void main() { 22 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); 23 | fragColor = inColor; 24 | fragTexCoord = inTexCoord; 25 | } 26 | -------------------------------------------------------------------------------- /CH5-Depth_buffer/vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/CH5-Depth_buffer/vert.spv -------------------------------------------------------------------------------- /CH6-Loading_model/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(loading_model loading_model.cpp) 2 | target_link_libraries(loading_model ${LIBS}) 3 | 4 | add_executable(vertex_deduplication vertex_deduplication.cpp) 5 | target_link_libraries(vertex_deduplication ${LIBS}) -------------------------------------------------------------------------------- /CH6-Loading_model/frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/CH6-Loading_model/frag.spv -------------------------------------------------------------------------------- /CH6-Loading_model/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(binding = 1) uniform sampler2D texSampler; 5 | 6 | layout(location = 0) in vec3 fragColor; 7 | layout(location = 1) in vec2 fragTexCoord; 8 | 9 | layout(location = 0) out vec4 outColor; 10 | 11 | void main() { 12 | outColor = texture(texSampler, fragTexCoord); 13 | } 14 | -------------------------------------------------------------------------------- /CH6-Loading_model/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(binding = 0) uniform UniformBufferObject { 5 | mat4 model; 6 | mat4 view; 7 | mat4 proj; 8 | } ubo; 9 | 10 | layout(location = 0) in vec3 inPosition; 11 | layout(location = 1) in vec3 inColor; 12 | layout(location = 2) in vec2 inTexCoord; 13 | 14 | layout(location = 0) out vec3 fragColor; 15 | layout(location = 1) out vec2 fragTexCoord; 16 | 17 | out gl_PerVertex { 18 | vec4 gl_Position; 19 | }; 20 | 21 | void main() { 22 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); 23 | fragColor = inColor; 24 | fragTexCoord = inTexCoord; 25 | } 26 | -------------------------------------------------------------------------------- /CH6-Loading_model/vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/CH6-Loading_model/vert.spv -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(vulkan_tutorial) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | 6 | # config vulkan 7 | if (WIN32) 8 | set(Vulkan_INCLUDE_DIRS "$ENV{VULKAN_SDK}/Include") 9 | if (CMAKE_SIZEOF_VOID_P EQUAL 8) 10 | set(VULKAN_LIBRARY "$ENV{VULKAN_SDK}/Lib/vulkan-1.lib") 11 | set(VULKAN_STATIC_LIBRARY "$ENV{VULKAN_SDK}/Lib/vkstatic.1.lib") 12 | else() 13 | set(VULKAN_LIBRARY "$ENV{VULKAN_SDK}/Lib32/vulkan-1.lib") 14 | set(VULKAN_STATIC_LIBRARY "$ENV{VULKAN_SDK}/Lib32/vkstatic.1.lib") 15 | endif() 16 | elseif (APPLE) 17 | set(CMAKE_FIND_FRAMEWORK NEVER) 18 | find_library(VULKAN_LIBRARY MoltenVK) 19 | set(CMAKE_FIND_FRAMEWORK ONLY) 20 | find_library(VULKAN_STATIC_LIBRARY MoltenVK) 21 | find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS 22 | "${VULKAN_LIBRARY}/Headers") 23 | else() 24 | find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS 25 | "$ENV{VULKAN_SDK}/include") 26 | find_library(VULKAN_LIBRARY NAMES vulkan HINTS 27 | "$ENV{VULKAN_SDK}/lib") 28 | endif() 29 | include_directories(${Vulkan_INCLUDE_DIRS}) 30 | list(APPEND LIBS ${VULKAN_LIBRARY}) 31 | message(STATUS "Vulkan Include dirs: ${Vulkan_INCLUDE_DIRS} and libs: ${VULKAN_LIBRARY}") 32 | 33 | # config glfw3 34 | option(GLFW_BUILD_DOCS OFF) 35 | option(GLFW_BUILD_EXAMPLES OFF) 36 | option(GLFW_BUILD_TESTS OFF) 37 | add_subdirectory(3rd_party/glfw) 38 | include_directories(3rd_party/glfw/include) 39 | list(APPEND LIBS glfw) 40 | 41 | # config glm 42 | include_directories(3rd_party/glm) 43 | 44 | # config vulkan header 45 | include_directories(3rd_party/vulkan-header) 46 | 47 | # cinfig stb_image & tinyobjloader 48 | include_directories(3rd_party/others) 49 | 50 | add_subdirectory(CH1-Draw_a_triangle) 51 | add_subdirectory(CH2-Vertex_buffer) 52 | add_subdirectory(CH3-Uinform_buffer) 53 | add_subdirectory(CH4-Texture_mapping) 54 | add_subdirectory(CH5-Depth_buffer) 55 | add_subdirectory(CH6-Loading_model) 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 攻伤菊菊长 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /QtVulkanTest/camera.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/QtVulkanTest/camera.PNG -------------------------------------------------------------------------------- /QtVulkanTest/color.frag: -------------------------------------------------------------------------------- 1 | #version 440 2 | 3 | layout(location = 0) in vec3 v_color; 4 | 5 | layout(location = 0) out vec4 fragColor; 6 | 7 | void main() 8 | { 9 | fragColor = vec4(v_color, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /QtVulkanTest/color.vert: -------------------------------------------------------------------------------- 1 | #version 440 2 | 3 | layout(location = 0) in vec4 position; 4 | layout(location = 1) in vec3 color; 5 | 6 | layout(location = 0) out vec3 v_color; 7 | 8 | layout(std140, binding = 0) uniform buf { 9 | mat4 mvp; 10 | } ubuf; 11 | 12 | out gl_PerVertex { vec4 gl_Position; }; 13 | 14 | void main() 15 | { 16 | v_color = color; 17 | gl_Position = ubuf.mvp * position; 18 | } 19 | -------------------------------------------------------------------------------- /QtVulkanTest/color_frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/QtVulkanTest/color_frag.spv -------------------------------------------------------------------------------- /QtVulkanTest/color_vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/QtVulkanTest/color_vert.spv -------------------------------------------------------------------------------- /QtVulkanTest/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "vulkanwindow.h" 5 | 6 | Q_LOGGING_CATEGORY(lcVk, "qt.vulkan") 7 | 8 | int main(int argc, char *argv[]) 9 | { 10 | QGuiApplication app(argc, argv); 11 | 12 | QLoggingCategory::setFilterRules(QStringLiteral("qt.vulkan=true")); 13 | 14 | //! [0] 15 | QVulkanInstance inst; 16 | 17 | #ifndef Q_OS_ANDROID 18 | inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation"); 19 | #else 20 | inst.setLayers(QByteArrayList() 21 | << "VK_LAYER_GOOGLE_threading" 22 | << "VK_LAYER_LUNARG_parameter_validation" 23 | << "VK_LAYER_LUNARG_object_tracker" 24 | << "VK_LAYER_LUNARG_core_validation" 25 | << "VK_LAYER_LUNARG_image" 26 | << "VK_LAYER_LUNARG_swapchain" 27 | << "VK_LAYER_GOOGLE_unique_objects"); 28 | #endif 29 | 30 | if (!inst.create()) 31 | qFatal("Failed to create Vulkan instance: %d", inst.errorCode()); 32 | //! [0] 33 | 34 | //! [1] 35 | VulkanWindow w; 36 | w.setVulkanInstance(&inst); 37 | 38 | w.resize(1024, 768); 39 | w.show(); 40 | //! [1] 41 | 42 | return app.exec(); 43 | } 44 | -------------------------------------------------------------------------------- /QtVulkanTest/tmp.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /QtVulkanTest/vulkanTest.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2018-03-04T21:07:59 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = vulkanTest 12 | TEMPLATE = app 13 | 14 | # The following define makes your compiler emit warnings if you use 15 | # any feature of Qt which has been marked as deprecated (the exact warnings 16 | # depend on your compiler). Please consult the documentation of the 17 | # deprecated API in order to know how to port your code away from it. 18 | DEFINES += QT_DEPRECATED_WARNINGS 19 | 20 | # You can also make your code fail to compile if you use deprecated APIs. 21 | # In order to do so, uncomment the following line. 22 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 23 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 24 | 25 | INCLUDEPATH += "C:\VulkanSDK\1.0.68.0\Include" 26 | 27 | SOURCES += \ 28 | main.cpp \ 29 | vulkanwindow.cpp \ 30 | vulkanwindowrenderer.cpp 31 | 32 | HEADERS += \ 33 | vulkanwindow.h \ 34 | vulkanwindowrenderer.h 35 | 36 | RESOURCES += \ 37 | vulkantest.qrc 38 | 39 | # install 40 | target.path = $$[QT_INSTALL_EXAMPLES]/vulkanTest 41 | INSTALLS += target 42 | -------------------------------------------------------------------------------- /QtVulkanTest/vulkanTest.pro.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | EnvironmentId 7 | {325e141f-a7f4-4006-a753-67179c99a854} 8 | 9 | 10 | ProjectExplorer.Project.ActiveTarget 11 | 0 12 | 13 | 14 | ProjectExplorer.Project.EditorSettings 15 | 16 | true 17 | false 18 | true 19 | 20 | Cpp 21 | 22 | CppGlobal 23 | 24 | 25 | 26 | QmlJS 27 | 28 | QmlJSGlobal 29 | 30 | 31 | 2 32 | UTF-8 33 | false 34 | 4 35 | false 36 | 80 37 | true 38 | true 39 | 1 40 | true 41 | false 42 | 0 43 | true 44 | true 45 | 0 46 | 8 47 | true 48 | 1 49 | true 50 | true 51 | true 52 | false 53 | 54 | 55 | 56 | ProjectExplorer.Project.PluginSettings 57 | 58 | 59 | 60 | ProjectExplorer.Project.Target.0 61 | 62 | Desktop Qt 5.10.1 MSVC2017 64bit 63 | Desktop Qt 5.10.1 MSVC2017 64bit 64 | qt.qt5.5101.win64_msvc2017_64_kit 65 | 1 66 | 0 67 | 0 68 | 69 | D:/MySpareTime/QT/build-vulkanTest-Desktop_Qt_5_10_1_MSVC2017_64bit-Debug 70 | 71 | 72 | true 73 | qmake 74 | 75 | QtProjectManager.QMakeBuildStep 76 | true 77 | 78 | false 79 | false 80 | false 81 | 82 | 83 | true 84 | Make 85 | 86 | Qt4ProjectManager.MakeStep 87 | 88 | false 89 | 90 | 91 | 92 | 2 93 | 构建 94 | 95 | ProjectExplorer.BuildSteps.Build 96 | 97 | 98 | 99 | true 100 | Make 101 | 102 | Qt4ProjectManager.MakeStep 103 | 104 | true 105 | clean 106 | 107 | 108 | 1 109 | 清理 110 | 111 | ProjectExplorer.BuildSteps.Clean 112 | 113 | 2 114 | false 115 | 116 | Debug 117 | 118 | Qt4ProjectManager.Qt4BuildConfiguration 119 | 2 120 | true 121 | 122 | 123 | D:/MySpareTime/QT/build-vulkanTest-Desktop_Qt_5_10_1_MSVC2017_64bit-Release 124 | 125 | 126 | true 127 | qmake 128 | 129 | QtProjectManager.QMakeBuildStep 130 | false 131 | 132 | false 133 | false 134 | false 135 | 136 | 137 | true 138 | Make 139 | 140 | Qt4ProjectManager.MakeStep 141 | 142 | false 143 | 144 | 145 | 146 | 2 147 | 构建 148 | 149 | ProjectExplorer.BuildSteps.Build 150 | 151 | 152 | 153 | true 154 | Make 155 | 156 | Qt4ProjectManager.MakeStep 157 | 158 | true 159 | clean 160 | 161 | 162 | 1 163 | 清理 164 | 165 | ProjectExplorer.BuildSteps.Clean 166 | 167 | 2 168 | false 169 | 170 | Release 171 | 172 | Qt4ProjectManager.Qt4BuildConfiguration 173 | 0 174 | true 175 | 176 | 177 | D:/MySpareTime/QT/build-vulkanTest-Desktop_Qt_5_10_1_MSVC2017_64bit-Profile 178 | 179 | 180 | true 181 | qmake 182 | 183 | QtProjectManager.QMakeBuildStep 184 | true 185 | 186 | false 187 | true 188 | false 189 | 190 | 191 | true 192 | Make 193 | 194 | Qt4ProjectManager.MakeStep 195 | 196 | false 197 | 198 | 199 | 200 | 2 201 | 构建 202 | 203 | ProjectExplorer.BuildSteps.Build 204 | 205 | 206 | 207 | true 208 | Make 209 | 210 | Qt4ProjectManager.MakeStep 211 | 212 | true 213 | clean 214 | 215 | 216 | 1 217 | 清理 218 | 219 | ProjectExplorer.BuildSteps.Clean 220 | 221 | 2 222 | false 223 | 224 | Profile 225 | 226 | Qt4ProjectManager.Qt4BuildConfiguration 227 | 0 228 | true 229 | 230 | 3 231 | 232 | 233 | 0 234 | 部署 235 | 236 | ProjectExplorer.BuildSteps.Deploy 237 | 238 | 1 239 | 在本地部署 240 | 241 | ProjectExplorer.DefaultDeployConfiguration 242 | 243 | 1 244 | 245 | 246 | false 247 | false 248 | 1000 249 | 250 | true 251 | 252 | false 253 | false 254 | false 255 | false 256 | true 257 | 0.01 258 | 10 259 | true 260 | 1 261 | 25 262 | 263 | 1 264 | true 265 | false 266 | true 267 | valgrind 268 | 269 | 0 270 | 1 271 | 2 272 | 3 273 | 4 274 | 5 275 | 6 276 | 7 277 | 8 278 | 9 279 | 10 280 | 11 281 | 12 282 | 13 283 | 14 284 | 285 | 2 286 | 287 | vulkanTest 288 | 289 | Qt4ProjectManager.Qt4RunConfiguration:D:/MySpareTime/QT/vulkanTest/vulkanTest.pro 290 | true 291 | 292 | vulkanTest.pro 293 | false 294 | 295 | D:/MySpareTime/QT/build-vulkanTest-Desktop_Qt_5_10_1_MSVC2017_64bit-Release 296 | 3768 297 | false 298 | true 299 | false 300 | false 301 | true 302 | 303 | 1 304 | 305 | 306 | 307 | ProjectExplorer.Project.TargetCount 308 | 1 309 | 310 | 311 | ProjectExplorer.Project.Updater.FileVersion 312 | 18 313 | 314 | 315 | Version 316 | 18 317 | 318 | 319 | -------------------------------------------------------------------------------- /QtVulkanTest/vulkantest.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | color_frag.spv 4 | color_vert.spv 5 | color.frag 6 | color.vert 7 | 8 | 9 | -------------------------------------------------------------------------------- /QtVulkanTest/vulkanwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "vulkanwindow.h" 2 | 3 | QVulkanWindowRenderer *VulkanWindow::createRenderer() 4 | { 5 | return new VulkanRenderer(this, true); 6 | } 7 | -------------------------------------------------------------------------------- /QtVulkanTest/vulkanwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef VULKANWINDOW_H 2 | #define VULKANWINDOW_H 3 | 4 | #include 5 | #include "vulkanwindowrenderer.h" 6 | 7 | class VulkanWindow : public QVulkanWindow 8 | { 9 | public: 10 | QVulkanWindowRenderer *createRenderer() override; 11 | }; 12 | 13 | #endif // VULKANWINDOW_H 14 | -------------------------------------------------------------------------------- /QtVulkanTest/vulkanwindowrenderer.cpp: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | ** 3 | ** Copyright (C) 2017 The Qt Company Ltd. 4 | ** Contact: https://www.qt.io/licensing/ 5 | ** 6 | ** This file is part of the examples of the Qt Toolkit. 7 | ** 8 | ** $QT_BEGIN_LICENSE:BSD$ 9 | ** Commercial License Usage 10 | ** Licensees holding valid commercial Qt licenses may use this file in 11 | ** accordance with the commercial license agreement provided with the 12 | ** Software or, alternatively, in accordance with the terms contained in 13 | ** a written agreement between you and The Qt Company. For licensing terms 14 | ** and conditions see https://www.qt.io/terms-conditions. For further 15 | ** information use the contact form at https://www.qt.io/contact-us. 16 | ** 17 | ** BSD License Usage 18 | ** Alternatively, you may use this file under the terms of the BSD license 19 | ** as follows: 20 | ** 21 | ** "Redistribution and use in source and binary forms, with or without 22 | ** modification, are permitted provided that the following conditions are 23 | ** met: 24 | ** * Redistributions of source code must retain the above copyright 25 | ** notice, this list of conditions and the following disclaimer. 26 | ** * Redistributions in binary form must reproduce the above copyright 27 | ** notice, this list of conditions and the following disclaimer in 28 | ** the documentation and/or other materials provided with the 29 | ** distribution. 30 | ** * Neither the name of The Qt Company Ltd nor the names of its 31 | ** contributors may be used to endorse or promote products derived 32 | ** from this software without specific prior written permission. 33 | ** 34 | ** 35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 46 | ** 47 | ** $QT_END_LICENSE$ 48 | ** 49 | ****************************************************************************/ 50 | 51 | #include 52 | #include 53 | #include "vulkanwindowrenderer.h" 54 | 55 | static float vertexData[] = { 56 | 0.0f, 0.5f, 1.0f, 0.0f, 0.0f, 57 | -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 58 | 0.5f, -0.5f, 0.0f, 0.0f, 1.0f 59 | }; 60 | 61 | static const int UNIFORM_DATA_SIZE = 16 * sizeof(float); 62 | 63 | static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign) 64 | { 65 | return (v + byteAlign - 1) & ~(byteAlign - 1); 66 | } 67 | 68 | VulkanRenderer::VulkanRenderer(QVulkanWindow *w, bool msaa) 69 | : m_window(w) 70 | { 71 | if (msaa) { 72 | const QVector counts = w->supportedSampleCounts(); 73 | qDebug() << "Supported sample counts:" << counts; 74 | for (int s = 16; s >= 4; s /= 2) { 75 | if (counts.contains(s)) { 76 | qDebug("Requesting sample count %d", s); 77 | m_window->setSampleCount(s); 78 | break; 79 | } 80 | } 81 | } 82 | } 83 | 84 | VkShaderModule VulkanRenderer::createShader(const QString &name) 85 | { 86 | QFile file(name); 87 | if (!file.open(QIODevice::ReadOnly)) { 88 | qWarning("Failed to read shader %s", qPrintable(name)); 89 | return VK_NULL_HANDLE; 90 | } 91 | QByteArray blob = file.readAll(); 92 | file.close(); 93 | 94 | VkShaderModuleCreateInfo shaderInfo; 95 | memset(&shaderInfo, 0, sizeof(shaderInfo)); 96 | shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; 97 | shaderInfo.codeSize = blob.size(); 98 | shaderInfo.pCode = reinterpret_cast(blob.constData()); 99 | VkShaderModule shaderModule; 100 | VkResult err = m_devFuncs->vkCreateShaderModule(m_window->device(), &shaderInfo, nullptr, &shaderModule); 101 | if (err != VK_SUCCESS) { 102 | qWarning("Failed to create shader module: %d", err); 103 | return VK_NULL_HANDLE; 104 | } 105 | 106 | return shaderModule; 107 | } 108 | 109 | void VulkanRenderer::initResources() 110 | { 111 | qDebug("initResources"); 112 | 113 | VkDevice dev = m_window->device(); 114 | m_devFuncs = m_window->vulkanInstance()->deviceFunctions(dev); 115 | 116 | // Prepare the vertex and uniform data. The vertex data will never 117 | // change so one buffer is sufficient regardless of the value of 118 | // QVulkanWindow::CONCURRENT_FRAME_COUNT. Uniform data is changing per 119 | // frame however so active frames have to have a dedicated copy. 120 | 121 | // Use just one memory allocation and one buffer. We will then specify the 122 | // appropriate offsets for uniform buffers in the VkDescriptorBufferInfo. 123 | // Have to watch out for 124 | // VkPhysicalDeviceLimits::minUniformBufferOffsetAlignment, though. 125 | 126 | // The uniform buffer is not strictly required in this example, we could 127 | // have used push constants as well since our single matrix (64 bytes) fits 128 | // into the spec mandated minimum limit of 128 bytes. However, once that 129 | // limit is not sufficient, the per-frame buffers, as shown below, will 130 | // become necessary. 131 | 132 | const int concurrentFrameCount = m_window->concurrentFrameCount(); 133 | const VkPhysicalDeviceLimits *pdevLimits = &m_window->physicalDeviceProperties()->limits; 134 | const VkDeviceSize uniAlign = pdevLimits->minUniformBufferOffsetAlignment; 135 | qDebug("uniform buffer offset alignment is %u", (uint) uniAlign); 136 | VkBufferCreateInfo bufInfo; 137 | memset(&bufInfo, 0, sizeof(bufInfo)); 138 | bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; 139 | // Our internal layout is vertex, uniform, uniform, ... with each uniform buffer start offset aligned to uniAlign. 140 | const VkDeviceSize vertexAllocSize = aligned(sizeof(vertexData), uniAlign); 141 | const VkDeviceSize uniformAllocSize = aligned(UNIFORM_DATA_SIZE, uniAlign); 142 | bufInfo.size = vertexAllocSize + concurrentFrameCount * uniformAllocSize; 143 | bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; 144 | 145 | VkResult err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_buf); 146 | if (err != VK_SUCCESS) 147 | qFatal("Failed to create buffer: %d", err); 148 | 149 | VkMemoryRequirements memReq; 150 | m_devFuncs->vkGetBufferMemoryRequirements(dev, m_buf, &memReq); 151 | 152 | VkMemoryAllocateInfo memAllocInfo = { 153 | VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, 154 | nullptr, 155 | memReq.size, 156 | m_window->hostVisibleMemoryIndex() 157 | }; 158 | 159 | err = m_devFuncs->vkAllocateMemory(dev, &memAllocInfo, nullptr, &m_bufMem); 160 | if (err != VK_SUCCESS) 161 | qFatal("Failed to allocate memory: %d", err); 162 | 163 | err = m_devFuncs->vkBindBufferMemory(dev, m_buf, m_bufMem, 0); 164 | if (err != VK_SUCCESS) 165 | qFatal("Failed to bind buffer memory: %d", err); 166 | 167 | quint8 *p; 168 | err = m_devFuncs->vkMapMemory(dev, m_bufMem, 0, memReq.size, 0, reinterpret_cast(&p)); 169 | if (err != VK_SUCCESS) 170 | qFatal("Failed to map memory: %d", err); 171 | memcpy(p, vertexData, sizeof(vertexData)); 172 | QMatrix4x4 ident; 173 | memset(m_uniformBufInfo, 0, sizeof(m_uniformBufInfo)); 174 | for (int i = 0; i < concurrentFrameCount; ++i) { 175 | const VkDeviceSize offset = vertexAllocSize + i * uniformAllocSize; 176 | memcpy(p + offset, ident.constData(), 16 * sizeof(float)); 177 | m_uniformBufInfo[i].buffer = m_buf; 178 | m_uniformBufInfo[i].offset = offset; 179 | m_uniformBufInfo[i].range = uniformAllocSize; 180 | } 181 | m_devFuncs->vkUnmapMemory(dev, m_bufMem); 182 | 183 | VkVertexInputBindingDescription vertexBindingDesc = { 184 | 0, // binding 185 | 5 * sizeof(float), 186 | VK_VERTEX_INPUT_RATE_VERTEX 187 | }; 188 | VkVertexInputAttributeDescription vertexAttrDesc[] = { 189 | { // position 190 | 0, // location 191 | 0, // binding 192 | VK_FORMAT_R32G32_SFLOAT, 193 | 0 194 | }, 195 | { // color 196 | 1, 197 | 0, 198 | VK_FORMAT_R32G32B32_SFLOAT, 199 | 2 * sizeof(float) 200 | } 201 | }; 202 | 203 | VkPipelineVertexInputStateCreateInfo vertexInputInfo; 204 | vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; 205 | vertexInputInfo.pNext = nullptr; 206 | vertexInputInfo.flags = 0; 207 | vertexInputInfo.vertexBindingDescriptionCount = 1; 208 | vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDesc; 209 | vertexInputInfo.vertexAttributeDescriptionCount = 2; 210 | vertexInputInfo.pVertexAttributeDescriptions = vertexAttrDesc; 211 | 212 | // Set up descriptor set and its layout. 213 | VkDescriptorPoolSize descPoolSizes = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uint32_t(concurrentFrameCount) }; 214 | VkDescriptorPoolCreateInfo descPoolInfo; 215 | memset(&descPoolInfo, 0, sizeof(descPoolInfo)); 216 | descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; 217 | descPoolInfo.maxSets = concurrentFrameCount; 218 | descPoolInfo.poolSizeCount = 1; 219 | descPoolInfo.pPoolSizes = &descPoolSizes; 220 | err = m_devFuncs->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, &m_descPool); 221 | if (err != VK_SUCCESS) 222 | qFatal("Failed to create descriptor pool: %d", err); 223 | 224 | VkDescriptorSetLayoutBinding layoutBinding = { 225 | 0, // binding 226 | VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 227 | 1, 228 | VK_SHADER_STAGE_VERTEX_BIT, 229 | nullptr 230 | }; 231 | VkDescriptorSetLayoutCreateInfo descLayoutInfo = { 232 | VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, 233 | nullptr, 234 | 0, 235 | 1, 236 | &layoutBinding 237 | }; 238 | err = m_devFuncs->vkCreateDescriptorSetLayout(dev, &descLayoutInfo, nullptr, &m_descSetLayout); 239 | if (err != VK_SUCCESS) 240 | qFatal("Failed to create descriptor set layout: %d", err); 241 | 242 | for (int i = 0; i < concurrentFrameCount; ++i) { 243 | VkDescriptorSetAllocateInfo descSetAllocInfo = { 244 | VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, 245 | nullptr, 246 | m_descPool, 247 | 1, 248 | &m_descSetLayout 249 | }; 250 | err = m_devFuncs->vkAllocateDescriptorSets(dev, &descSetAllocInfo, &m_descSet[i]); 251 | if (err != VK_SUCCESS) 252 | qFatal("Failed to allocate descriptor set: %d", err); 253 | 254 | VkWriteDescriptorSet descWrite; 255 | memset(&descWrite, 0, sizeof(descWrite)); 256 | descWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; 257 | descWrite.dstSet = m_descSet[i]; 258 | descWrite.descriptorCount = 1; 259 | descWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; 260 | descWrite.pBufferInfo = &m_uniformBufInfo[i]; 261 | m_devFuncs->vkUpdateDescriptorSets(dev, 1, &descWrite, 0, nullptr); 262 | } 263 | 264 | // Pipeline cache 265 | VkPipelineCacheCreateInfo pipelineCacheInfo; 266 | memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo)); 267 | pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; 268 | err = m_devFuncs->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &m_pipelineCache); 269 | if (err != VK_SUCCESS) 270 | qFatal("Failed to create pipeline cache: %d", err); 271 | 272 | // Pipeline layout 273 | VkPipelineLayoutCreateInfo pipelineLayoutInfo; 274 | memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo)); 275 | pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; 276 | pipelineLayoutInfo.setLayoutCount = 1; 277 | pipelineLayoutInfo.pSetLayouts = &m_descSetLayout; 278 | err = m_devFuncs->vkCreatePipelineLayout(dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout); 279 | if (err != VK_SUCCESS) 280 | qFatal("Failed to create pipeline layout: %d", err); 281 | 282 | // Shaders 283 | VkShaderModule vertShaderModule = createShader(QStringLiteral(":/color_vert.spv")); 284 | VkShaderModule fragShaderModule = createShader(QStringLiteral(":/color_frag.spv")); 285 | 286 | // Graphics pipeline 287 | VkGraphicsPipelineCreateInfo pipelineInfo; 288 | memset(&pipelineInfo, 0, sizeof(pipelineInfo)); 289 | pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; 290 | 291 | VkPipelineShaderStageCreateInfo shaderStages[2] = { 292 | { 293 | VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 294 | nullptr, 295 | 0, 296 | VK_SHADER_STAGE_VERTEX_BIT, 297 | vertShaderModule, 298 | "main", 299 | nullptr 300 | }, 301 | { 302 | VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, 303 | nullptr, 304 | 0, 305 | VK_SHADER_STAGE_FRAGMENT_BIT, 306 | fragShaderModule, 307 | "main", 308 | nullptr 309 | } 310 | }; 311 | pipelineInfo.stageCount = 2; 312 | pipelineInfo.pStages = shaderStages; 313 | 314 | pipelineInfo.pVertexInputState = &vertexInputInfo; 315 | 316 | VkPipelineInputAssemblyStateCreateInfo ia; 317 | memset(&ia, 0, sizeof(ia)); 318 | ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; 319 | ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; 320 | pipelineInfo.pInputAssemblyState = &ia; 321 | 322 | // The viewport and scissor will be set dynamically via vkCmdSetViewport/Scissor. 323 | // This way the pipeline does not need to be touched when resizing the window. 324 | VkPipelineViewportStateCreateInfo vp; 325 | memset(&vp, 0, sizeof(vp)); 326 | vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; 327 | vp.viewportCount = 1; 328 | vp.scissorCount = 1; 329 | pipelineInfo.pViewportState = &vp; 330 | 331 | VkPipelineRasterizationStateCreateInfo rs; 332 | memset(&rs, 0, sizeof(rs)); 333 | rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; 334 | rs.polygonMode = VK_POLYGON_MODE_FILL; 335 | rs.cullMode = VK_CULL_MODE_NONE; // we want the back face as well 336 | rs.frontFace = VK_FRONT_FACE_CLOCKWISE; 337 | rs.lineWidth = 1.0f; 338 | pipelineInfo.pRasterizationState = &rs; 339 | 340 | VkPipelineMultisampleStateCreateInfo ms; 341 | memset(&ms, 0, sizeof(ms)); 342 | ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; 343 | // Enable multisampling. 344 | ms.rasterizationSamples = m_window->sampleCountFlagBits(); 345 | pipelineInfo.pMultisampleState = &ms; 346 | 347 | VkPipelineDepthStencilStateCreateInfo ds; 348 | memset(&ds, 0, sizeof(ds)); 349 | ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; 350 | ds.depthTestEnable = VK_TRUE; 351 | ds.depthWriteEnable = VK_TRUE; 352 | ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; 353 | pipelineInfo.pDepthStencilState = &ds; 354 | 355 | VkPipelineColorBlendStateCreateInfo cb; 356 | memset(&cb, 0, sizeof(cb)); 357 | cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; 358 | // no blend, write out all of rgba 359 | VkPipelineColorBlendAttachmentState att; 360 | memset(&att, 0, sizeof(att)); 361 | att.colorWriteMask = 0xF; 362 | cb.attachmentCount = 1; 363 | cb.pAttachments = &att; 364 | pipelineInfo.pColorBlendState = &cb; 365 | 366 | VkDynamicState dynEnable[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; 367 | VkPipelineDynamicStateCreateInfo dyn; 368 | memset(&dyn, 0, sizeof(dyn)); 369 | dyn.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; 370 | dyn.dynamicStateCount = sizeof(dynEnable) / sizeof(VkDynamicState); 371 | dyn.pDynamicStates = dynEnable; 372 | pipelineInfo.pDynamicState = &dyn; 373 | 374 | pipelineInfo.layout = m_pipelineLayout; 375 | pipelineInfo.renderPass = m_window->defaultRenderPass(); 376 | 377 | err = m_devFuncs->vkCreateGraphicsPipelines(dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_pipeline); 378 | if (err != VK_SUCCESS) 379 | qFatal("Failed to create graphics pipeline: %d", err); 380 | 381 | if (vertShaderModule) 382 | m_devFuncs->vkDestroyShaderModule(dev, vertShaderModule, nullptr); 383 | if (fragShaderModule) 384 | m_devFuncs->vkDestroyShaderModule(dev, fragShaderModule, nullptr); 385 | } 386 | 387 | void VulkanRenderer::initSwapChainResources() 388 | { 389 | qDebug("initSwapChainResources"); 390 | 391 | // Projection matrix 392 | m_proj = m_window->clipCorrectionMatrix(); // adjust for Vulkan-OpenGL clip space differences 393 | const QSize sz = m_window->swapChainImageSize(); 394 | m_proj.perspective(45.0f, sz.width() / (float) sz.height(), 0.01f, 100.0f); 395 | m_proj.translate(0, 0, -4); 396 | } 397 | 398 | void VulkanRenderer::releaseSwapChainResources() 399 | { 400 | qDebug("releaseSwapChainResources"); 401 | } 402 | 403 | void VulkanRenderer::releaseResources() 404 | { 405 | qDebug("releaseResources"); 406 | 407 | VkDevice dev = m_window->device(); 408 | 409 | if (m_pipeline) { 410 | m_devFuncs->vkDestroyPipeline(dev, m_pipeline, nullptr); 411 | m_pipeline = VK_NULL_HANDLE; 412 | } 413 | 414 | if (m_pipelineLayout) { 415 | m_devFuncs->vkDestroyPipelineLayout(dev, m_pipelineLayout, nullptr); 416 | m_pipelineLayout = VK_NULL_HANDLE; 417 | } 418 | 419 | if (m_pipelineCache) { 420 | m_devFuncs->vkDestroyPipelineCache(dev, m_pipelineCache, nullptr); 421 | m_pipelineCache = VK_NULL_HANDLE; 422 | } 423 | 424 | if (m_descSetLayout) { 425 | m_devFuncs->vkDestroyDescriptorSetLayout(dev, m_descSetLayout, nullptr); 426 | m_descSetLayout = VK_NULL_HANDLE; 427 | } 428 | 429 | if (m_descPool) { 430 | m_devFuncs->vkDestroyDescriptorPool(dev, m_descPool, nullptr); 431 | m_descPool = VK_NULL_HANDLE; 432 | } 433 | 434 | if (m_buf) { 435 | m_devFuncs->vkDestroyBuffer(dev, m_buf, nullptr); 436 | m_buf = VK_NULL_HANDLE; 437 | } 438 | 439 | if (m_bufMem) { 440 | m_devFuncs->vkFreeMemory(dev, m_bufMem, nullptr); 441 | m_bufMem = VK_NULL_HANDLE; 442 | } 443 | } 444 | 445 | void VulkanRenderer::startNextFrame() 446 | { 447 | VkDevice dev = m_window->device(); 448 | VkCommandBuffer cb = m_window->currentCommandBuffer(); 449 | const QSize sz = m_window->swapChainImageSize(); 450 | 451 | VkClearColorValue clearColor = { 0, 0, 0, 1 }; 452 | VkClearDepthStencilValue clearDS = { 1, 0 }; 453 | VkClearValue clearValues[3]; 454 | memset(clearValues, 0, sizeof(clearValues)); 455 | clearValues[0].color = clearValues[2].color = clearColor; 456 | clearValues[1].depthStencil = clearDS; 457 | 458 | VkRenderPassBeginInfo rpBeginInfo; 459 | memset(&rpBeginInfo, 0, sizeof(rpBeginInfo)); 460 | rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; 461 | rpBeginInfo.renderPass = m_window->defaultRenderPass(); 462 | rpBeginInfo.framebuffer = m_window->currentFramebuffer(); 463 | rpBeginInfo.renderArea.extent.width = sz.width(); 464 | rpBeginInfo.renderArea.extent.height = sz.height(); 465 | rpBeginInfo.clearValueCount = m_window->sampleCountFlagBits() > VK_SAMPLE_COUNT_1_BIT ? 3 : 2; 466 | rpBeginInfo.pClearValues = clearValues; 467 | VkCommandBuffer cmdBuf = m_window->currentCommandBuffer(); 468 | m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE); 469 | 470 | quint8 *p; 471 | VkResult err = m_devFuncs->vkMapMemory(dev, m_bufMem, m_uniformBufInfo[m_window->currentFrame()].offset, 472 | UNIFORM_DATA_SIZE, 0, reinterpret_cast(&p)); 473 | if (err != VK_SUCCESS) 474 | qFatal("Failed to map memory: %d", err); 475 | QMatrix4x4 m = m_proj; 476 | m.rotate(m_rotation, 0, 1, 0); 477 | memcpy(p, m.constData(), 16 * sizeof(float)); 478 | m_devFuncs->vkUnmapMemory(dev, m_bufMem); 479 | 480 | // Not exactly a real animation system, just advance on every frame for now. 481 | m_rotation += 1.0f; 482 | 483 | m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline); 484 | m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, 485 | &m_descSet[m_window->currentFrame()], 0, nullptr); 486 | VkDeviceSize vbOffset = 0; 487 | m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_buf, &vbOffset); 488 | 489 | VkViewport viewport; 490 | viewport.x = viewport.y = 0; 491 | viewport.width = sz.width(); 492 | viewport.height = sz.height(); 493 | viewport.minDepth = 0; 494 | viewport.maxDepth = 1; 495 | m_devFuncs->vkCmdSetViewport(cb, 0, 1, &viewport); 496 | 497 | VkRect2D scissor; 498 | scissor.offset.x = scissor.offset.y = 0; 499 | scissor.extent.width = viewport.width; 500 | scissor.extent.height = viewport.height; 501 | m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor); 502 | 503 | m_devFuncs->vkCmdDraw(cb, 3, 1, 0, 0); 504 | 505 | m_devFuncs->vkCmdEndRenderPass(cmdBuf); 506 | 507 | m_window->frameReady(); 508 | m_window->requestUpdate(); // render continuously, throttled by the presentation rate 509 | } 510 | -------------------------------------------------------------------------------- /QtVulkanTest/vulkanwindowrenderer.h: -------------------------------------------------------------------------------- 1 | #ifndef VULKANWINDOWRENDERER_H 2 | #define VULKANWINDOWRENDERER_H 3 | 4 | #include 5 | 6 | class VulkanRenderer : public QVulkanWindowRenderer 7 | { 8 | public: 9 | VulkanRenderer(QVulkanWindow *w, bool msaa = false); 10 | 11 | void initResources() override; 12 | void initSwapChainResources() override; 13 | void releaseSwapChainResources() override; 14 | void releaseResources() override; 15 | 16 | void startNextFrame() override; 17 | 18 | protected: 19 | VkShaderModule createShader(const QString &name); 20 | 21 | QVulkanWindow *m_window; 22 | QVulkanDeviceFunctions *m_devFuncs; 23 | 24 | VkDeviceMemory m_bufMem = VK_NULL_HANDLE; 25 | VkBuffer m_buf = VK_NULL_HANDLE; 26 | VkDescriptorBufferInfo m_uniformBufInfo[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT]; 27 | 28 | VkDescriptorPool m_descPool = VK_NULL_HANDLE; 29 | VkDescriptorSetLayout m_descSetLayout = VK_NULL_HANDLE; 30 | VkDescriptorSet m_descSet[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT]; 31 | 32 | VkPipelineCache m_pipelineCache = VK_NULL_HANDLE; 33 | VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; 34 | VkPipeline m_pipeline = VK_NULL_HANDLE; 35 | 36 | QMatrix4x4 m_proj; 37 | float m_rotation = 0.0f; 38 | }; 39 | 40 | #endif // VULKANWINDOWRENDERER_H 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vulkan-tutorial 2 | vulkan-tutorial source code 3 | 4 | CMake ONLY FOR DEV NOW! -------------------------------------------------------------------------------- /cmake/modules/FindVulkan.cmake: -------------------------------------------------------------------------------- 1 | # Find Vulkan 2 | # 3 | # VULKAN_INCLUDE_DIR 4 | # VULKAN_LIBRARY 5 | # VULKAN_FOUND 6 | 7 | if (WIN32) 8 | find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS 9 | "$ENV{VULKAN_SDK}/Include" 10 | "$ENV{VK_SDK_PATH}/Include") 11 | if (CMAKE_SIZEOF_VOID_P EQUAL 8) 12 | find_library(VULKAN_LIBRARY NAMES vulkan-1 HINTS 13 | "$ENV{VULKAN_SDK}/Lib" 14 | "$ENV{VULKAN_SDK}/Bin" 15 | "$ENV{VK_SDK_PATH}/Bin") 16 | find_library(VULKAN_STATIC_LIBRARY NAMES vkstatic.1 HINTS 17 | "$ENV{VULKAN_SDK}/Lib" 18 | "$ENV{VULKAN_SDK}/Bin" 19 | "$ENV{VK_SDK_PATH}/Bin") 20 | else() 21 | find_library(VULKAN_LIBRARY NAMES vulkan-1 HINTS 22 | "$ENV{VULKAN_SDK}/Lib32" 23 | "$ENV{VULKAN_SDK}/Bin32" 24 | "$ENV{VK_SDK_PATH}/Bin32") 25 | find_library(VULKAN_STATIC_LIBRARY NAMES vkstatic.1 HINTS 26 | "$ENV{VULKAN_SDK}/Lib32" 27 | "$ENV{VULKAN_SDK}/Bin32" 28 | "$ENV{VK_SDK_PATH}/Bin32") 29 | endif() 30 | elseif (APPLE) 31 | set(CMAKE_FIND_FRAMEWORK NEVER) 32 | find_library(VULKAN_LIBRARY MoltenVK) 33 | set(CMAKE_FIND_FRAMEWORK ONLY) 34 | find_library(VULKAN_STATIC_LIBRARY MoltenVK) 35 | find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS 36 | "${VULKAN_LIBRARY}/Headers") 37 | else() 38 | find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS 39 | "$ENV{VULKAN_SDK}/include") 40 | find_library(VULKAN_LIBRARY NAMES vulkan HINTS 41 | "$ENV{VULKAN_SDK}/lib") 42 | endif() 43 | 44 | include(FindPackageHandleStandardArgs) 45 | find_package_handle_standard_args(Vulkan DEFAULT_MSG VULKAN_LIBRARY VULKAN_INCLUDE_DIR) 46 | 47 | mark_as_advanced(VULKAN_INCLUDE_DIR VULKAN_LIBRARY VULKAN_STATIC_LIBRARY) 48 | 49 | -------------------------------------------------------------------------------- /resources/chalet.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/resources/chalet.jpg -------------------------------------------------------------------------------- /resources/chalet.obj.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/resources/chalet.obj.zip -------------------------------------------------------------------------------- /resources/texture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/THISISAGOODNAME/vulkan-tutorial-code/3ed77db52dcdbd9892377a0ba4f0b694e8c13564/resources/texture.jpg --------------------------------------------------------------------------------