├── .gitattributes ├── .gitignore ├── README.md ├── main.cpp ├── shaders ├── compile.bat ├── shader.frag └── shader.vert ├── steps ├── 00_base_code.cpp ├── 00_base_code.cpp.original.diff ├── 01_instance_creation.cpp ├── 01_instance_creation.cpp.original.diff ├── 01_instance_creation.cpp.previous.diff ├── 02_validation_layers.cpp ├── 02_validation_layers.cpp.original.diff ├── 02_validation_layers.cpp.previous.diff ├── 03_physical_device_selection.cpp ├── 03_physical_device_selection.cpp.original.diff ├── 03_physical_device_selection.cpp.previous.diff ├── 04_logical_device.cpp ├── 04_logical_device.cpp.original.diff ├── 04_logical_device.cpp.previous.diff ├── 05_window_surface.cpp ├── 05_window_surface.cpp.original.diff ├── 05_window_surface.cpp.previous.diff ├── 06_swap_chain_creation.cpp ├── 06_swap_chain_creation.cpp.original.diff ├── 06_swap_chain_creation.cpp.previous.diff ├── 07_image_views.cpp ├── 07_image_views.cpp.original.diff ├── 07_image_views.cpp.previous.diff ├── 08_graphics_pipeline.cpp ├── 08_graphics_pipeline.cpp.original.diff ├── 08_graphics_pipeline.cpp.previous.diff ├── 09_shader_modules.cpp ├── 09_shader_modules.cpp.original.diff ├── 09_shader_modules.cpp.previous.diff ├── 11_render_passes.cpp ├── 11_render_passes.cpp.original.diff ├── 11_render_passes.cpp.previous.diff ├── 12_graphics_pipeline_complete.cpp ├── 12_graphics_pipeline_complete.cpp.original.diff ├── 12_graphics_pipeline_complete.cpp.previous.diff ├── 13_framebuffers.cpp ├── 13_framebuffers.cpp.original.diff ├── 13_framebuffers.cpp.previous.diff ├── 14_command_buffers.cpp ├── 14_command_buffers.cpp.original.diff ├── 14_command_buffers.cpp.previous.diff ├── 15_hello_triangle.cpp ├── 15_hello_triangle.cpp.original.diff ├── 15_hello_triangle.cpp.previous.diff ├── 16_swap_chain_recreation.cpp ├── 16_swap_chain_recreation.cpp.original.diff ├── 16_swap_chain_recreation.cpp.previous.diff ├── 17_vertex_input.cpp ├── 17_vertex_input.cpp.original.diff ├── 17_vertex_input.cpp.previous.diff ├── 18_vertex_buffer.cpp ├── 18_vertex_buffer.cpp.original.diff ├── 18_vertex_buffer.cpp.previous.diff ├── 19_staging_buffer.cpp ├── 19_staging_buffer.cpp.original.diff ├── 19_staging_buffer.cpp.previous.diff ├── copy_and_diff.sh └── diff_previous.sh ├── vulkan-tutorial-hpp.sln ├── vulkan-tutorial-hpp.vcxproj ├── vulkan-tutorial-hpp.vcxproj.filters └── vulkan-utils.h /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc 262 | 263 | # shaders 264 | *.spv 265 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vulkan-tutorial-hpp 2 | Following [vulkan-tutorial.com](https://vulkan-tutorial.com/) using [Vulkan-Hpp](https://github.com/KhronosGroup/Vulkan-Hpp). 3 | 4 | `main.cpp` contains the latest state and the [steps](/steps) folder contains the code for individual steps and diffs between them (`previous`) and between the Vulkan-Hpp version and the [original](https://github.com/Overv/VulkanTutorial/tree/master/code) (`original`). 5 | 6 | ## Notes: 7 | - using Vulkan SDK 1.1.92.1 8 | - dependency setup can be simplified with [vcpk](https://github.com/Microsoft/vcpkg) 9 | - `.\vcpkg.exe install glm:x64-windows glfw3:x64-windows` (Hint: can be set permanently: `VCPKG_DEFAULT_TRIPLET=x64-windows`) 10 | - `.\vcpkg.exe integrate install` does not work for x64 apparently -> had to set additional include directories and linker settings manually in Visual Studio 11 | -------------------------------------------------------------------------------- /shaders/compile.bat: -------------------------------------------------------------------------------- 1 | glslangValidator.exe -V shader.vert 2 | glslangValidator.exe -V shader.frag 3 | -------------------------------------------------------------------------------- /shaders/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 | -------------------------------------------------------------------------------- /shaders/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 | void main() { 10 | gl_Position = vec4(inPosition, 0.0, 1.0); 11 | fragColor = inColor; 12 | } -------------------------------------------------------------------------------- /steps/00_base_code.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | const int WIDTH = 800; 9 | const int HEIGHT = 600; 10 | 11 | class HelloTriangleApplication { 12 | public: 13 | void run() { 14 | initWindow(); 15 | initVulkan(); 16 | mainLoop(); 17 | cleanup(); 18 | } 19 | 20 | private: 21 | GLFWwindow* window; 22 | 23 | void initWindow() { 24 | glfwInit(); 25 | 26 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 27 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 28 | 29 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 30 | } 31 | 32 | void initVulkan() { 33 | 34 | } 35 | 36 | void mainLoop() { 37 | while (!glfwWindowShouldClose(window)) { 38 | glfwPollEvents(); 39 | } 40 | } 41 | 42 | void cleanup() { 43 | glfwDestroyWindow(window); 44 | 45 | glfwTerminate(); 46 | } 47 | }; 48 | 49 | int main() { 50 | HelloTriangleApplication app; 51 | 52 | try { 53 | app.run(); 54 | } catch (const std::exception& e) { 55 | std::cerr << e.what() << std::endl; 56 | return EXIT_FAILURE; 57 | } 58 | 59 | return EXIT_SUCCESS; 60 | } 61 | -------------------------------------------------------------------------------- /steps/00_base_code.cpp.original.diff: -------------------------------------------------------------------------------- 1 | --- b/../../VulkanTutorial/code/00_base_code.cpp 2 | +++ a/../main.cpp 3 | @@ -1,4 +1,4 @@ 4 | -#define GLFW_INCLUDE_VULKAN 5 | +#include 6 | #include 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /steps/01_instance_creation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | const int WIDTH = 800; 9 | const int HEIGHT = 600; 10 | 11 | class HelloTriangleApplication { 12 | public: 13 | void run() { 14 | initWindow(); 15 | initVulkan(); 16 | mainLoop(); 17 | cleanup(); 18 | } 19 | 20 | private: 21 | GLFWwindow* window; 22 | 23 | vk::UniqueInstance instance; 24 | 25 | void initWindow() { 26 | glfwInit(); 27 | 28 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 29 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 30 | 31 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 32 | } 33 | 34 | void initVulkan() { 35 | createInstance(); 36 | } 37 | 38 | void mainLoop() { 39 | while (!glfwWindowShouldClose(window)) { 40 | glfwPollEvents(); 41 | } 42 | } 43 | 44 | void cleanup() { 45 | // NOTE: instance destruction is handled by UniqueInstance 46 | 47 | glfwDestroyWindow(window); 48 | 49 | glfwTerminate(); 50 | } 51 | 52 | void createInstance() { 53 | auto appInfo = vk::ApplicationInfo( 54 | "Hello Triangle", 55 | VK_MAKE_VERSION(1, 0, 0), 56 | "No Engine", 57 | VK_MAKE_VERSION(1, 0, 0), 58 | VK_API_VERSION_1_0 59 | ); 60 | 61 | uint32_t glfwExtensionCount = 0; 62 | const char** glfwExtensions; 63 | 64 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 65 | 66 | auto createInfo = vk::InstanceCreateInfo( 67 | vk::InstanceCreateFlags(), 68 | &appInfo, 69 | 0, nullptr, // enabled layers 70 | glfwExtensionCount, glfwExtensions // enabled extensions 71 | ); 72 | 73 | try { 74 | instance = vk::createInstanceUnique(createInfo, nullptr); 75 | } 76 | catch (vk::SystemError err) { 77 | throw std::runtime_error("failed to create instance!"); 78 | } 79 | 80 | std::cout << "available extensions:" << std::endl; 81 | 82 | for (const auto& extension : vk::enumerateInstanceExtensionProperties()) { 83 | std::cout << "\t" << extension.extensionName << std::endl; 84 | } 85 | } 86 | 87 | }; 88 | 89 | int main() { 90 | HelloTriangleApplication app; 91 | 92 | try { 93 | app.run(); 94 | } catch (const std::exception& e) { 95 | std::cerr << e.what() << std::endl; 96 | return EXIT_FAILURE; 97 | } 98 | 99 | return EXIT_SUCCESS; 100 | } 101 | -------------------------------------------------------------------------------- /steps/01_instance_creation.cpp.original.diff: -------------------------------------------------------------------------------- 1 | --- a/../../VulkanTutorial/code/01_instance_creation.cpp 2 | +++ b/01_instance_creation.cpp 3 | @@ -1,4 +1,4 @@ 4 | -#define GLFW_INCLUDE_VULKAN 5 | +#include 6 | #include 7 | 8 | #include 9 | @@ -19,8 +19,7 @@ public: 10 | 11 | private: 12 | GLFWwindow* window; 13 | 14 | - VkInstance instance; 15 | + vk::UniqueInstance instance; 16 | 17 | void initWindow() { 18 | glfwInit(); 19 | @@ -42,7 +41,7 @@ private: 20 | } 21 | 22 | void cleanup() { 23 | - vkDestroyInstance(instance, nullptr); 24 | + // NOTE: instance destruction is handled by UniqueInstance 25 | 26 | glfwDestroyWindow(window); 27 | 28 | @@ -50,31 +49,40 @@ private: 29 | } 30 | 31 | void createInstance() { 32 | - VkApplicationInfo appInfo = {}; 33 | - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 34 | - appInfo.pApplicationName = "Hello Triangle"; 35 | - appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 36 | - appInfo.pEngineName = "No Engine"; 37 | - appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 38 | - appInfo.apiVersion = VK_API_VERSION_1_0; 39 | - 40 | - VkInstanceCreateInfo createInfo = {}; 41 | - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 42 | - createInfo.pApplicationInfo = &appInfo; 43 | - 44 | + auto appInfo = vk::ApplicationInfo( 45 | + "Hello Triangle", 46 | + VK_MAKE_VERSION(1, 0, 0), 47 | + "No Engine", 48 | + VK_MAKE_VERSION(1, 0, 0), 49 | + VK_API_VERSION_1_0 50 | + ); 51 | + 52 | uint32_t glfwExtensionCount = 0; 53 | const char** glfwExtensions; 54 | - glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 55 | 56 | - createInfo.enabledExtensionCount = glfwExtensionCount; 57 | - createInfo.ppEnabledExtensionNames = glfwExtensions; 58 | + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 59 | 60 | - createInfo.enabledLayerCount = 0; 61 | + auto createInfo = vk::InstanceCreateInfo( 62 | + vk::InstanceCreateFlags(), 63 | + &appInfo, 64 | + 0, nullptr, // enabled layers 65 | + glfwExtensionCount, glfwExtensions // enabled extensions 66 | + ); 67 | 68 | - if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { 69 | + try { 70 | + instance = vk::createInstanceUnique(createInfo, nullptr); 71 | + } 72 | + catch (vk::SystemError err) { 73 | throw std::runtime_error("failed to create instance!"); 74 | } 75 | + 76 | + std::cout << "available extensions:" << std::endl; 77 | + 78 | + for (const auto& extension : vk::enumerateInstanceExtensionProperties()) { 79 | + std::cout << "\t" << extension.extensionName << std::endl; 80 | + } 81 | } 82 | + 83 | }; 84 | 85 | int main() { 86 | -------------------------------------------------------------------------------- /steps/01_instance_creation.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/00_base_code.cpp 2 | +++ b/01_instance_creation.cpp 3 | @@ -20,6 +20,8 @@ public: 4 | private: 5 | GLFWwindow* window; 6 | 7 | + vk::UniqueInstance instance; 8 | + 9 | void initWindow() { 10 | glfwInit(); 11 | 12 | @@ -30,7 +32,7 @@ private: 13 | } 14 | 15 | void initVulkan() { 16 | - 17 | + createInstance(); 18 | } 19 | 20 | void mainLoop() { 21 | @@ -40,10 +42,48 @@ private: 22 | } 23 | 24 | void cleanup() { 25 | + // NOTE: instance destruction is handled by UniqueInstance 26 | + 27 | glfwDestroyWindow(window); 28 | 29 | glfwTerminate(); 30 | } 31 | + 32 | + void createInstance() { 33 | + auto appInfo = vk::ApplicationInfo( 34 | + "Hello Triangle", 35 | + VK_MAKE_VERSION(1, 0, 0), 36 | + "No Engine", 37 | + VK_MAKE_VERSION(1, 0, 0), 38 | + VK_API_VERSION_1_0 39 | + ); 40 | + 41 | + uint32_t glfwExtensionCount = 0; 42 | + const char** glfwExtensions; 43 | + 44 | + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 45 | + 46 | + auto createInfo = vk::InstanceCreateInfo( 47 | + vk::InstanceCreateFlags(), 48 | + &appInfo, 49 | + 0, nullptr, // enabled layers 50 | + glfwExtensionCount, glfwExtensions // enabled extensions 51 | + ); 52 | + 53 | + try { 54 | + instance = vk::createInstanceUnique(createInfo, nullptr); 55 | + } 56 | + catch (vk::SystemError err) { 57 | + throw std::runtime_error("failed to create instance!"); 58 | + } 59 | + 60 | + std::cout << "available extensions:" << std::endl; 61 | + 62 | + for (const auto& extension : vk::enumerateInstanceExtensionProperties()) { 63 | + std::cout << "\t" << extension.extensionName << std::endl; 64 | + } 65 | + } 66 | + 67 | }; 68 | 69 | int main() { 70 | -------------------------------------------------------------------------------- /steps/02_validation_layers.cpp: -------------------------------------------------------------------------------- 1 | #include 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 CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pCallback) { 24 | auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); 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 DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT callback, const VkAllocationCallbacks* pAllocator) { 33 | auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); 34 | if (func != nullptr) { 35 | func(instance, callback, pAllocator); 36 | } 37 | } 38 | 39 | class HelloTriangleApplication { 40 | public: 41 | void run() { 42 | initWindow(); 43 | initVulkan(); 44 | mainLoop(); 45 | cleanup(); 46 | } 47 | 48 | private: 49 | GLFWwindow* window; 50 | 51 | vk::UniqueInstance instance; 52 | VkDebugUtilsMessengerEXT callback; 53 | 54 | void initWindow() { 55 | glfwInit(); 56 | 57 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 58 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 59 | 60 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 61 | } 62 | 63 | void initVulkan() { 64 | createInstance(); 65 | setupDebugCallback(); 66 | } 67 | 68 | void mainLoop() { 69 | while (!glfwWindowShouldClose(window)) { 70 | glfwPollEvents(); 71 | } 72 | } 73 | 74 | void cleanup() { 75 | if (enableValidationLayers) { 76 | DestroyDebugUtilsMessengerEXT(*instance, callback, nullptr); 77 | } 78 | 79 | // NOTE: instance destruction is handled by UniqueInstance 80 | 81 | glfwDestroyWindow(window); 82 | 83 | glfwTerminate(); 84 | } 85 | 86 | void createInstance() { 87 | if (enableValidationLayers && !checkValidationLayerSupport()) { 88 | throw std::runtime_error("validation layers requested, but not available!"); 89 | } 90 | 91 | auto appInfo = vk::ApplicationInfo( 92 | "Hello Triangle", 93 | VK_MAKE_VERSION(1, 0, 0), 94 | "No Engine", 95 | VK_MAKE_VERSION(1, 0, 0), 96 | VK_API_VERSION_1_0 97 | ); 98 | 99 | auto extensions = getRequiredExtensions(); 100 | 101 | auto createInfo = vk::InstanceCreateInfo( 102 | vk::InstanceCreateFlags(), 103 | &appInfo, 104 | 0, nullptr, // enabled layers 105 | static_cast(extensions.size()), extensions.data() // enabled extensions 106 | ); 107 | 108 | if (enableValidationLayers) { 109 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 110 | createInfo.ppEnabledLayerNames = validationLayers.data(); 111 | } 112 | 113 | try { 114 | instance = vk::createInstanceUnique(createInfo, nullptr); 115 | } 116 | catch (vk::SystemError err) { 117 | throw std::runtime_error("failed to create instance!"); 118 | } 119 | } 120 | 121 | void setupDebugCallback() { 122 | if (!enableValidationLayers) return; 123 | 124 | auto createInfo = vk::DebugUtilsMessengerCreateInfoEXT( 125 | vk::DebugUtilsMessengerCreateFlagsEXT(), 126 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, 127 | vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 128 | debugCallback, 129 | nullptr 130 | ); 131 | 132 | // NOTE: Vulkan-hpp has methods for this, but they trigger linking errors... 133 | //instance->createDebugUtilsMessengerEXT(createInfo); 134 | //instance->createDebugUtilsMessengerEXTUnique(createInfo); 135 | 136 | // NOTE: reinterpret_cast is also used by vulkan.hpp internally for all these structs 137 | if (CreateDebugUtilsMessengerEXT(*instance, reinterpret_cast(&createInfo), nullptr, &callback) != VK_SUCCESS) { 138 | throw std::runtime_error("failed to set up debug callback!"); 139 | } 140 | } 141 | 142 | std::vector getRequiredExtensions() { 143 | uint32_t glfwExtensionCount = 0; 144 | const char** glfwExtensions; 145 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 146 | 147 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 148 | 149 | if (enableValidationLayers) { 150 | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 151 | } 152 | 153 | return extensions; 154 | } 155 | 156 | bool checkValidationLayerSupport() { 157 | auto availableLayers = vk::enumerateInstanceLayerProperties(); 158 | for (const char* layerName : validationLayers) { 159 | bool layerFound = false; 160 | 161 | for (const auto& layerProperties : availableLayers) { 162 | if (strcmp(layerName, layerProperties.layerName) == 0) { 163 | layerFound = true; 164 | break; 165 | } 166 | } 167 | 168 | if (!layerFound) { 169 | return false; 170 | } 171 | } 172 | 173 | return true; 174 | } 175 | 176 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { 177 | std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; 178 | 179 | return VK_FALSE; 180 | } 181 | }; 182 | 183 | int main() { 184 | HelloTriangleApplication app; 185 | 186 | try { 187 | app.run(); 188 | } catch (const std::exception& e) { 189 | std::cerr << e.what() << std::endl; 190 | return EXIT_FAILURE; 191 | } 192 | 193 | return EXIT_SUCCESS; 194 | } 195 | -------------------------------------------------------------------------------- /steps/02_validation_layers.cpp.original.diff: -------------------------------------------------------------------------------- 1 | --- a/../../VulkanTutorial/code/02_validation_layers.cpp 2 | +++ b/02_validation_layers.cpp 3 | @@ -1,4 +1,4 @@ 4 | -#define GLFW_INCLUDE_VULKAN 5 | +#include 6 | #include 7 | 8 | #include 9 | @@ -48,7 +48,7 @@ public: 10 | private: 11 | GLFWwindow* window; 12 | 13 | - VkInstance instance; 14 | + vk::UniqueInstance instance; 15 | VkDebugUtilsMessengerEXT callback; 16 | 17 | void initWindow() { 18 | @@ -73,10 +73,10 @@ private: 19 | 20 | void cleanup() { 21 | if (enableValidationLayers) { 22 | - DestroyDebugUtilsMessengerEXT(instance, callback, nullptr); 23 | + DestroyDebugUtilsMessengerEXT(*instance, callback, nullptr); 24 | } 25 | 26 | - vkDestroyInstance(instance, nullptr); 27 | + // NOTE: instance destruction is handled by UniqueInstance 28 | 29 | glfwDestroyWindow(window); 30 | 31 | @@ -88,30 +88,32 @@ private: 32 | throw std::runtime_error("validation layers requested, but not available!"); 33 | } 34 | 35 | - VkApplicationInfo appInfo = {}; 36 | - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 37 | - appInfo.pApplicationName = "Hello Triangle"; 38 | - appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 39 | - appInfo.pEngineName = "No Engine"; 40 | - appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 41 | - appInfo.apiVersion = VK_API_VERSION_1_0; 42 | - 43 | - VkInstanceCreateInfo createInfo = {}; 44 | - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 45 | - createInfo.pApplicationInfo = &appInfo; 46 | - 47 | + auto appInfo = vk::ApplicationInfo( 48 | + "Hello Triangle", 49 | + VK_MAKE_VERSION(1, 0, 0), 50 | + "No Engine", 51 | + VK_MAKE_VERSION(1, 0, 0), 52 | + VK_API_VERSION_1_0 53 | + ); 54 | + 55 | auto extensions = getRequiredExtensions(); 56 | - createInfo.enabledExtensionCount = static_cast(extensions.size()); 57 | - createInfo.ppEnabledExtensionNames = extensions.data(); 58 | + 59 | + auto createInfo = vk::InstanceCreateInfo( 60 | + vk::InstanceCreateFlags(), 61 | + &appInfo, 62 | + 0, nullptr, // enabled layers 63 | + static_cast(extensions.size()), extensions.data() // enabled extensions 64 | + ); 65 | 66 | if (enableValidationLayers) { 67 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 68 | createInfo.ppEnabledLayerNames = validationLayers.data(); 69 | - } else { 70 | - createInfo.enabledLayerCount = 0; 71 | } 72 | 73 | - if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { 74 | + try { 75 | + instance = vk::createInstanceUnique(createInfo, nullptr); 76 | + } 77 | + catch (vk::SystemError err) { 78 | throw std::runtime_error("failed to create instance!"); 79 | } 80 | } 81 | @@ -119,13 +121,20 @@ private: 82 | void setupDebugCallback() { 83 | if (!enableValidationLayers) return; 84 | 85 | - VkDebugUtilsMessengerCreateInfoEXT createInfo = {}; 86 | - createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; 87 | - createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; 88 | - createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; 89 | - createInfo.pfnUserCallback = debugCallback; 90 | + auto createInfo = vk::DebugUtilsMessengerCreateInfoEXT( 91 | + vk::DebugUtilsMessengerCreateFlagsEXT(), 92 | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, 93 | + vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 94 | + debugCallback, 95 | + nullptr 96 | + ); 97 | 98 | - if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { 99 | + // NOTE: Vulkan-hpp has methods for this, but they trigger linking errors... 100 | + //instance->createDebugUtilsMessengerEXT(createInfo); 101 | + //instance->createDebugUtilsMessengerEXTUnique(createInfo); 102 | + 103 | + // NOTE: reinterpret_cast is also used by vulkan.hpp internally for all these structs 104 | + if (CreateDebugUtilsMessengerEXT(*instance, reinterpret_cast(&createInfo), nullptr, &callback) != VK_SUCCESS) { 105 | throw std::runtime_error("failed to set up debug callback!"); 106 | } 107 | } 108 | @@ -145,12 +154,7 @@ private: 109 | } 110 | 111 | bool checkValidationLayerSupport() { 112 | - uint32_t layerCount; 113 | - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 114 | - 115 | - std::vector availableLayers(layerCount); 116 | - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 117 | - 118 | + auto availableLayers = vk::enumerateInstanceLayerProperties(); 119 | for (const char* layerName : validationLayers) { 120 | bool layerFound = false; 121 | 122 | -------------------------------------------------------------------------------- /steps/02_validation_layers.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/01_instance_creation.cpp 2 | +++ b/02_validation_layers.cpp 3 | @@ -3,11 +3,39 @@ 4 | 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 | +#ifdef NDEBUG 19 | +const bool enableValidationLayers = false; 20 | +#else 21 | +const bool enableValidationLayers = true; 22 | +#endif 23 | + 24 | +VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pCallback) { 25 | + auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); 26 | + if (func != nullptr) { 27 | + return func(instance, pCreateInfo, pAllocator, pCallback); 28 | + } else { 29 | + return VK_ERROR_EXTENSION_NOT_PRESENT; 30 | + } 31 | +} 32 | + 33 | +void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT callback, const VkAllocationCallbacks* pAllocator) { 34 | + auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); 35 | + if (func != nullptr) { 36 | + func(instance, callback, pAllocator); 37 | + } 38 | +} 39 | + 40 | class HelloTriangleApplication { 41 | public: 42 | void run() { 43 | @@ -21,6 +49,7 @@ private: 44 | GLFWwindow* window; 45 | 46 | vk::UniqueInstance instance; 47 | + VkDebugUtilsMessengerEXT callback; 48 | 49 | void initWindow() { 50 | glfwInit(); 51 | @@ -33,6 +62,7 @@ private: 52 | 53 | void initVulkan() { 54 | createInstance(); 55 | + setupDebugCallback(); 56 | } 57 | 58 | void mainLoop() { 59 | @@ -42,6 +72,10 @@ private: 60 | } 61 | 62 | void cleanup() { 63 | + if (enableValidationLayers) { 64 | + DestroyDebugUtilsMessengerEXT(*instance, callback, nullptr); 65 | + } 66 | + 67 | // NOTE: instance destruction is handled by UniqueInstance 68 | 69 | glfwDestroyWindow(window); 70 | @@ -50,6 +84,10 @@ private: 71 | } 72 | 73 | void createInstance() { 74 | + if (enableValidationLayers && !checkValidationLayerSupport()) { 75 | + throw std::runtime_error("validation layers requested, but not available!"); 76 | + } 77 | + 78 | auto appInfo = vk::ApplicationInfo( 79 | "Hello Triangle", 80 | VK_MAKE_VERSION(1, 0, 0), 81 | @@ -58,32 +96,88 @@ private: 82 | VK_API_VERSION_1_0 83 | ); 84 | 85 | - uint32_t glfwExtensionCount = 0; 86 | - const char** glfwExtensions; 87 | - 88 | - glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 89 | + auto extensions = getRequiredExtensions(); 90 | 91 | auto createInfo = vk::InstanceCreateInfo( 92 | vk::InstanceCreateFlags(), 93 | &appInfo, 94 | 0, nullptr, // enabled layers 95 | - glfwExtensionCount, glfwExtensions // enabled extensions 96 | + static_cast(extensions.size()), extensions.data() // enabled extensions 97 | ); 98 | 99 | + if (enableValidationLayers) { 100 | + createInfo.enabledLayerCount = static_cast(validationLayers.size()); 101 | + createInfo.ppEnabledLayerNames = validationLayers.data(); 102 | + } 103 | + 104 | try { 105 | instance = vk::createInstanceUnique(createInfo, nullptr); 106 | } 107 | catch (vk::SystemError err) { 108 | throw std::runtime_error("failed to create instance!"); 109 | } 110 | + } 111 | 112 | - std::cout << "available extensions:" << std::endl; 113 | + void setupDebugCallback() { 114 | + if (!enableValidationLayers) return; 115 | 116 | - for (const auto& extension : vk::enumerateInstanceExtensionProperties()) { 117 | - std::cout << "\t" << extension.extensionName << std::endl; 118 | + auto createInfo = vk::DebugUtilsMessengerCreateInfoEXT( 119 | + vk::DebugUtilsMessengerCreateFlagsEXT(), 120 | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, 121 | + vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 122 | + debugCallback, 123 | + nullptr 124 | + ); 125 | + 126 | + // NOTE: Vulkan-hpp has methods for this, but they trigger linking errors... 127 | + //instance->createDebugUtilsMessengerEXT(createInfo); 128 | + //instance->createDebugUtilsMessengerEXTUnique(createInfo); 129 | + 130 | + // NOTE: reinterpret_cast is also used by vulkan.hpp internally for all these structs 131 | + if (CreateDebugUtilsMessengerEXT(*instance, reinterpret_cast(&createInfo), nullptr, &callback) != VK_SUCCESS) { 132 | + throw std::runtime_error("failed to set up debug callback!"); 133 | + } 134 | + } 135 | + 136 | + std::vector getRequiredExtensions() { 137 | + uint32_t glfwExtensionCount = 0; 138 | + const char** glfwExtensions; 139 | + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 140 | + 141 | + std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 142 | + 143 | + if (enableValidationLayers) { 144 | + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 145 | } 146 | + 147 | + return extensions; 148 | } 149 | 150 | + bool checkValidationLayerSupport() { 151 | + auto availableLayers = vk::enumerateInstanceLayerProperties(); 152 | + for (const char* layerName : validationLayers) { 153 | + bool layerFound = false; 154 | + 155 | + for (const auto& layerProperties : availableLayers) { 156 | + if (strcmp(layerName, layerProperties.layerName) == 0) { 157 | + layerFound = true; 158 | + break; 159 | + } 160 | + } 161 | + 162 | + if (!layerFound) { 163 | + return false; 164 | + } 165 | + } 166 | + 167 | + return true; 168 | + } 169 | + 170 | + static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { 171 | + std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; 172 | + 173 | + return VK_FALSE; 174 | + } 175 | }; 176 | 177 | int main() { 178 | -------------------------------------------------------------------------------- /steps/03_physical_device_selection.cpp: -------------------------------------------------------------------------------- 1 | #include 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 | #ifdef NDEBUG 19 | const bool enableValidationLayers = false; 20 | #else 21 | const bool enableValidationLayers = true; 22 | #endif 23 | 24 | VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pCallback) { 25 | auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); 26 | if (func != nullptr) { 27 | return func(instance, pCreateInfo, pAllocator, pCallback); 28 | } else { 29 | return VK_ERROR_EXTENSION_NOT_PRESENT; 30 | } 31 | } 32 | 33 | void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT callback, const VkAllocationCallbacks* pAllocator) { 34 | auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); 35 | if (func != nullptr) { 36 | func(instance, callback, pAllocator); 37 | } 38 | } 39 | 40 | struct QueueFamilyIndices { 41 | std::optional graphicsFamily; 42 | 43 | bool isComplete() { 44 | return graphicsFamily.has_value(); 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* window; 59 | 60 | vk::UniqueInstance instance; 61 | VkDebugUtilsMessengerEXT callback; 62 | 63 | vk::PhysicalDevice physicalDevice; 64 | 65 | void initWindow() { 66 | glfwInit(); 67 | 68 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 69 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 70 | 71 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 72 | } 73 | 74 | void initVulkan() { 75 | createInstance(); 76 | setupDebugCallback(); 77 | pickPhysicalDevice(); 78 | } 79 | 80 | void mainLoop() { 81 | while (!glfwWindowShouldClose(window)) { 82 | glfwPollEvents(); 83 | } 84 | } 85 | 86 | void cleanup() { 87 | if (enableValidationLayers) { 88 | DestroyDebugUtilsMessengerEXT(*instance, callback, nullptr); 89 | } 90 | 91 | // NOTE: instance destruction is handled by UniqueInstance 92 | 93 | glfwDestroyWindow(window); 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 | auto appInfo = vk::ApplicationInfo( 104 | "Hello Triangle", 105 | VK_MAKE_VERSION(1, 0, 0), 106 | "No Engine", 107 | VK_MAKE_VERSION(1, 0, 0), 108 | VK_API_VERSION_1_0 109 | ); 110 | 111 | auto extensions = getRequiredExtensions(); 112 | 113 | auto createInfo = vk::InstanceCreateInfo( 114 | vk::InstanceCreateFlags(), 115 | &appInfo, 116 | 0, nullptr, // enabled layers 117 | static_cast(extensions.size()), extensions.data() // enabled extensions 118 | ); 119 | 120 | if (enableValidationLayers) { 121 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 122 | createInfo.ppEnabledLayerNames = validationLayers.data(); 123 | } 124 | 125 | try { 126 | instance = vk::createInstanceUnique(createInfo, nullptr); 127 | } 128 | catch (vk::SystemError err) { 129 | throw std::runtime_error("failed to create instance!"); 130 | } 131 | } 132 | 133 | void setupDebugCallback() { 134 | if (!enableValidationLayers) return; 135 | 136 | auto createInfo = vk::DebugUtilsMessengerCreateInfoEXT( 137 | vk::DebugUtilsMessengerCreateFlagsEXT(), 138 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, 139 | vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 140 | debugCallback, 141 | nullptr 142 | ); 143 | 144 | // NOTE: Vulkan-hpp has methods for this, but they trigger linking errors... 145 | //instance->createDebugUtilsMessengerEXT(createInfo); 146 | //instance->createDebugUtilsMessengerEXTUnique(createInfo); 147 | 148 | // NOTE: reinterpret_cast is also used by vulkan.hpp internally for all these structs 149 | if (CreateDebugUtilsMessengerEXT(*instance, reinterpret_cast(&createInfo), nullptr, &callback) != VK_SUCCESS) { 150 | throw std::runtime_error("failed to set up debug callback!"); 151 | } 152 | } 153 | 154 | void pickPhysicalDevice() { 155 | auto devices = instance->enumeratePhysicalDevices(); 156 | if (devices.size() == 0) { 157 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 158 | } 159 | 160 | for (const auto& device : devices) { 161 | if (isDeviceSuitable(device)) { 162 | physicalDevice = device; 163 | break; 164 | } 165 | } 166 | 167 | if (!physicalDevice) { 168 | throw std::runtime_error("failed to find a suitable GPU!"); 169 | } 170 | } 171 | 172 | bool isDeviceSuitable(const vk::PhysicalDevice& device) { 173 | QueueFamilyIndices indices = findQueueFamilies(device); 174 | 175 | return indices.isComplete(); 176 | } 177 | 178 | QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice device) { 179 | QueueFamilyIndices indices; 180 | 181 | auto queueFamilies = device.getQueueFamilyProperties(); 182 | 183 | int i = 0; 184 | for (const auto& queueFamily : queueFamilies) { 185 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & vk::QueueFlagBits::eGraphics) { 186 | indices.graphicsFamily = i; 187 | } 188 | 189 | if (indices.isComplete()) { 190 | break; 191 | } 192 | 193 | i++; 194 | } 195 | 196 | return indices; 197 | } 198 | 199 | std::vector getRequiredExtensions() { 200 | uint32_t glfwExtensionCount = 0; 201 | const char** glfwExtensions; 202 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 203 | 204 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 205 | 206 | if (enableValidationLayers) { 207 | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 208 | } 209 | 210 | return extensions; 211 | } 212 | 213 | bool checkValidationLayerSupport() { 214 | auto availableLayers = vk::enumerateInstanceLayerProperties(); 215 | for (const char* layerName : validationLayers) { 216 | bool layerFound = false; 217 | 218 | for (const auto& layerProperties : availableLayers) { 219 | if (strcmp(layerName, layerProperties.layerName) == 0) { 220 | layerFound = true; 221 | break; 222 | } 223 | } 224 | 225 | if (!layerFound) { 226 | return false; 227 | } 228 | } 229 | 230 | return true; 231 | } 232 | 233 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { 234 | std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; 235 | 236 | return VK_FALSE; 237 | } 238 | }; 239 | 240 | int main() { 241 | HelloTriangleApplication app; 242 | 243 | try { 244 | app.run(); 245 | } catch (const std::exception& e) { 246 | std::cerr << e.what() << std::endl; 247 | return EXIT_FAILURE; 248 | } 249 | 250 | return EXIT_SUCCESS; 251 | } 252 | -------------------------------------------------------------------------------- /steps/03_physical_device_selection.cpp.original.diff: -------------------------------------------------------------------------------- 1 | --- a/../../VulkanTutorial/code/03_physical_device_selection.cpp 2 | +++ b/03_physical_device_selection.cpp 3 | @@ -1,4 +1,4 @@ 4 | -#define GLFW_INCLUDE_VULKAN 5 | +#include 6 | #include 7 | 8 | #include 9 | @@ -57,10 +57,10 @@ public: 10 | private: 11 | GLFWwindow* window; 12 | 13 | - VkInstance instance; 14 | + vk::UniqueInstance instance; 15 | VkDebugUtilsMessengerEXT callback; 16 | 17 | - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; 18 | + vk::PhysicalDevice physicalDevice; 19 | 20 | void initWindow() { 21 | glfwInit(); 22 | @@ -85,10 +85,10 @@ private: 23 | 24 | void cleanup() { 25 | if (enableValidationLayers) { 26 | - DestroyDebugUtilsMessengerEXT(instance, callback, nullptr); 27 | + DestroyDebugUtilsMessengerEXT(*instance, callback, nullptr); 28 | } 29 | 30 | - vkDestroyInstance(instance, nullptr); 31 | + // NOTE: instance destruction is handled by UniqueInstance 32 | 33 | glfwDestroyWindow(window); 34 | 35 | @@ -100,30 +100,32 @@ private: 36 | throw std::runtime_error("validation layers requested, but not available!"); 37 | } 38 | 39 | - VkApplicationInfo appInfo = {}; 40 | - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 41 | - appInfo.pApplicationName = "Hello Triangle"; 42 | - appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 43 | - appInfo.pEngineName = "No Engine"; 44 | - appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 45 | - appInfo.apiVersion = VK_API_VERSION_1_0; 46 | - 47 | - VkInstanceCreateInfo createInfo = {}; 48 | - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 49 | - createInfo.pApplicationInfo = &appInfo; 50 | - 51 | + auto appInfo = vk::ApplicationInfo( 52 | + "Hello Triangle", 53 | + VK_MAKE_VERSION(1, 0, 0), 54 | + "No Engine", 55 | + VK_MAKE_VERSION(1, 0, 0), 56 | + VK_API_VERSION_1_0 57 | + ); 58 | + 59 | auto extensions = getRequiredExtensions(); 60 | - createInfo.enabledExtensionCount = static_cast(extensions.size()); 61 | - createInfo.ppEnabledExtensionNames = extensions.data(); 62 | + 63 | + auto createInfo = vk::InstanceCreateInfo( 64 | + vk::InstanceCreateFlags(), 65 | + &appInfo, 66 | + 0, nullptr, // enabled layers 67 | + static_cast(extensions.size()), extensions.data() // enabled extensions 68 | + ); 69 | 70 | if (enableValidationLayers) { 71 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 72 | createInfo.ppEnabledLayerNames = validationLayers.data(); 73 | - } else { 74 | - createInfo.enabledLayerCount = 0; 75 | } 76 | 77 | - if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { 78 | + try { 79 | + instance = vk::createInstanceUnique(createInfo, nullptr); 80 | + } 81 | + catch (vk::SystemError err) { 82 | throw std::runtime_error("failed to create instance!"); 83 | } 84 | } 85 | @@ -131,28 +133,30 @@ private: 86 | void setupDebugCallback() { 87 | if (!enableValidationLayers) return; 88 | 89 | - VkDebugUtilsMessengerCreateInfoEXT createInfo = {}; 90 | - createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; 91 | - createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; 92 | - createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; 93 | - createInfo.pfnUserCallback = debugCallback; 94 | + auto createInfo = vk::DebugUtilsMessengerCreateInfoEXT( 95 | + vk::DebugUtilsMessengerCreateFlagsEXT(), 96 | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, 97 | + vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 98 | + debugCallback, 99 | + nullptr 100 | + ); 101 | + 102 | + // NOTE: Vulkan-hpp has methods for this, but they trigger linking errors... 103 | + //instance->createDebugUtilsMessengerEXT(createInfo); 104 | + //instance->createDebugUtilsMessengerEXTUnique(createInfo); 105 | 106 | - if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { 107 | + // NOTE: reinterpret_cast is also used by vulkan.hpp internally for all these structs 108 | + if (CreateDebugUtilsMessengerEXT(*instance, reinterpret_cast(&createInfo), nullptr, &callback) != VK_SUCCESS) { 109 | throw std::runtime_error("failed to set up debug callback!"); 110 | } 111 | } 112 | 113 | void pickPhysicalDevice() { 114 | - uint32_t deviceCount = 0; 115 | - vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); 116 | - 117 | - if (deviceCount == 0) { 118 | + auto devices = instance->enumeratePhysicalDevices(); 119 | + if (devices.size() == 0) { 120 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 121 | } 122 | 123 | - std::vector devices(deviceCount); 124 | - vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); 125 | - 126 | for (const auto& device : devices) { 127 | if (isDeviceSuitable(device)) { 128 | physicalDevice = device; 129 | @@ -160,29 +164,25 @@ private: 130 | } 131 | } 132 | 133 | - if (physicalDevice == VK_NULL_HANDLE) { 134 | + if (!physicalDevice) { 135 | throw std::runtime_error("failed to find a suitable GPU!"); 136 | } 137 | } 138 | 139 | - bool isDeviceSuitable(VkPhysicalDevice device) { 140 | + bool isDeviceSuitable(const vk::PhysicalDevice& device) { 141 | QueueFamilyIndices indices = findQueueFamilies(device); 142 | 143 | return indices.isComplete(); 144 | } 145 | 146 | - QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { 147 | + QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice device) { 148 | QueueFamilyIndices indices; 149 | 150 | - uint32_t queueFamilyCount = 0; 151 | - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); 152 | - 153 | - std::vector queueFamilies(queueFamilyCount); 154 | - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); 155 | + auto queueFamilies = device.getQueueFamilyProperties(); 156 | 157 | int i = 0; 158 | for (const auto& queueFamily : queueFamilies) { 159 | - if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { 160 | + if (queueFamily.queueCount > 0 && queueFamily.queueFlags & vk::QueueFlagBits::eGraphics) { 161 | indices.graphicsFamily = i; 162 | } 163 | 164 | @@ -211,12 +211,7 @@ private: 165 | } 166 | 167 | bool checkValidationLayerSupport() { 168 | - uint32_t layerCount; 169 | - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 170 | - 171 | - std::vector availableLayers(layerCount); 172 | - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 173 | - 174 | + auto availableLayers = vk::enumerateInstanceLayerProperties(); 175 | for (const char* layerName : validationLayers) { 176 | bool layerFound = false; 177 | 178 | -------------------------------------------------------------------------------- /steps/03_physical_device_selection.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/02_validation_layers.cpp 2 | +++ b/03_physical_device_selection.cpp 3 | @@ -6,6 +6,7 @@ 4 | #include 5 | #include 6 | #include 7 | +#include 8 | 9 | const int WIDTH = 800; 10 | const int HEIGHT = 600; 11 | @@ -36,6 +37,14 @@ void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT 12 | } 13 | } 14 | 15 | +struct QueueFamilyIndices { 16 | + std::optional graphicsFamily; 17 | + 18 | + bool isComplete() { 19 | + return graphicsFamily.has_value(); 20 | + } 21 | +}; 22 | + 23 | class HelloTriangleApplication { 24 | public: 25 | void run() { 26 | @@ -51,6 +60,8 @@ private: 27 | vk::UniqueInstance instance; 28 | VkDebugUtilsMessengerEXT callback; 29 | 30 | + vk::PhysicalDevice physicalDevice; 31 | + 32 | void initWindow() { 33 | glfwInit(); 34 | 35 | @@ -63,6 +74,7 @@ private: 36 | void initVulkan() { 37 | createInstance(); 38 | setupDebugCallback(); 39 | + pickPhysicalDevice(); 40 | } 41 | 42 | void mainLoop() { 43 | @@ -139,6 +151,51 @@ private: 44 | } 45 | } 46 | 47 | + void pickPhysicalDevice() { 48 | + auto devices = instance->enumeratePhysicalDevices(); 49 | + if (devices.size() == 0) { 50 | + throw std::runtime_error("failed to find GPUs with Vulkan support!"); 51 | + } 52 | + 53 | + for (const auto& device : devices) { 54 | + if (isDeviceSuitable(device)) { 55 | + physicalDevice = device; 56 | + break; 57 | + } 58 | + } 59 | + 60 | + if (!physicalDevice) { 61 | + throw std::runtime_error("failed to find a suitable GPU!"); 62 | + } 63 | + } 64 | + 65 | + bool isDeviceSuitable(const vk::PhysicalDevice& device) { 66 | + QueueFamilyIndices indices = findQueueFamilies(device); 67 | + 68 | + return indices.isComplete(); 69 | + } 70 | + 71 | + QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice device) { 72 | + QueueFamilyIndices indices; 73 | + 74 | + auto queueFamilies = device.getQueueFamilyProperties(); 75 | + 76 | + int i = 0; 77 | + for (const auto& queueFamily : queueFamilies) { 78 | + if (queueFamily.queueCount > 0 && queueFamily.queueFlags & vk::QueueFlagBits::eGraphics) { 79 | + indices.graphicsFamily = i; 80 | + } 81 | + 82 | + if (indices.isComplete()) { 83 | + break; 84 | + } 85 | + 86 | + i++; 87 | + } 88 | + 89 | + return indices; 90 | + } 91 | + 92 | std::vector getRequiredExtensions() { 93 | uint32_t glfwExtensionCount = 0; 94 | const char** glfwExtensions; 95 | -------------------------------------------------------------------------------- /steps/04_logical_device.cpp: -------------------------------------------------------------------------------- 1 | #include 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 | #ifdef NDEBUG 19 | const bool enableValidationLayers = false; 20 | #else 21 | const bool enableValidationLayers = true; 22 | #endif 23 | 24 | VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pCallback) { 25 | auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); 26 | if (func != nullptr) { 27 | return func(instance, pCreateInfo, pAllocator, pCallback); 28 | } else { 29 | return VK_ERROR_EXTENSION_NOT_PRESENT; 30 | } 31 | } 32 | 33 | void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT callback, const VkAllocationCallbacks* pAllocator) { 34 | auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); 35 | if (func != nullptr) { 36 | func(instance, callback, pAllocator); 37 | } 38 | } 39 | 40 | struct QueueFamilyIndices { 41 | std::optional graphicsFamily; 42 | 43 | bool isComplete() { 44 | return graphicsFamily.has_value(); 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* window; 59 | 60 | vk::UniqueInstance instance; 61 | VkDebugUtilsMessengerEXT callback; 62 | 63 | vk::PhysicalDevice physicalDevice; 64 | vk::UniqueDevice device; 65 | 66 | vk::Queue graphicsQueue; 67 | 68 | void initWindow() { 69 | glfwInit(); 70 | 71 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 72 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 73 | 74 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 75 | } 76 | 77 | void initVulkan() { 78 | createInstance(); 79 | setupDebugCallback(); 80 | pickPhysicalDevice(); 81 | createLogicalDevice(); 82 | } 83 | 84 | void mainLoop() { 85 | while (!glfwWindowShouldClose(window)) { 86 | glfwPollEvents(); 87 | } 88 | } 89 | 90 | void cleanup() { 91 | // NOTE: instance destruction is handled by UniqueInstance, same for device 92 | 93 | if (enableValidationLayers) { 94 | DestroyDebugUtilsMessengerEXT(*instance, callback, nullptr); 95 | } 96 | 97 | glfwDestroyWindow(window); 98 | 99 | glfwTerminate(); 100 | } 101 | 102 | void createInstance() { 103 | if (enableValidationLayers && !checkValidationLayerSupport()) { 104 | throw std::runtime_error("validation layers requested, but not available!"); 105 | } 106 | 107 | auto appInfo = vk::ApplicationInfo( 108 | "Hello Triangle", 109 | VK_MAKE_VERSION(1, 0, 0), 110 | "No Engine", 111 | VK_MAKE_VERSION(1, 0, 0), 112 | VK_API_VERSION_1_0 113 | ); 114 | 115 | auto extensions = getRequiredExtensions(); 116 | 117 | auto createInfo = vk::InstanceCreateInfo( 118 | vk::InstanceCreateFlags(), 119 | &appInfo, 120 | 0, nullptr, // enabled layers 121 | static_cast(extensions.size()), extensions.data() // enabled extensions 122 | ); 123 | 124 | if (enableValidationLayers) { 125 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 126 | createInfo.ppEnabledLayerNames = validationLayers.data(); 127 | } 128 | 129 | try { 130 | instance = vk::createInstanceUnique(createInfo, nullptr); 131 | } 132 | catch (vk::SystemError err) { 133 | throw std::runtime_error("failed to create instance!"); 134 | } 135 | } 136 | 137 | void setupDebugCallback() { 138 | if (!enableValidationLayers) return; 139 | 140 | auto createInfo = vk::DebugUtilsMessengerCreateInfoEXT( 141 | vk::DebugUtilsMessengerCreateFlagsEXT(), 142 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, 143 | vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 144 | debugCallback, 145 | nullptr 146 | ); 147 | 148 | // NOTE: Vulkan-hpp has methods for this, but they trigger linking errors... 149 | //instance->createDebugUtilsMessengerEXT(createInfo); 150 | //instance->createDebugUtilsMessengerEXTUnique(createInfo); 151 | 152 | // NOTE: reinterpret_cast is also used by vulkan.hpp internally for all these structs 153 | if (CreateDebugUtilsMessengerEXT(*instance, reinterpret_cast(&createInfo), nullptr, &callback) != VK_SUCCESS) { 154 | throw std::runtime_error("failed to set up debug callback!"); 155 | } 156 | } 157 | 158 | void pickPhysicalDevice() { 159 | auto devices = instance->enumeratePhysicalDevices(); 160 | if (devices.size() == 0) { 161 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 162 | } 163 | 164 | for (const auto& device : devices) { 165 | if (isDeviceSuitable(device)) { 166 | physicalDevice = device; 167 | break; 168 | } 169 | } 170 | 171 | if (!physicalDevice) { 172 | throw std::runtime_error("failed to find a suitable GPU!"); 173 | } 174 | } 175 | 176 | void createLogicalDevice() { 177 | QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 178 | float queuePriority = 1.0f; 179 | auto queueCreateInfo = vk::DeviceQueueCreateInfo( 180 | vk::DeviceQueueCreateFlags(), 181 | indices.graphicsFamily.value(), 182 | 1, // queueCount 183 | &queuePriority 184 | ); 185 | 186 | auto deviceFeatures = vk::PhysicalDeviceFeatures(); 187 | auto createInfo = vk::DeviceCreateInfo( 188 | vk::DeviceCreateFlags(), 189 | 1, &queueCreateInfo 190 | ); 191 | createInfo.pEnabledFeatures = &deviceFeatures; 192 | createInfo.enabledExtensionCount = 0; 193 | 194 | if (enableValidationLayers) { 195 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 196 | createInfo.ppEnabledLayerNames = validationLayers.data(); 197 | } 198 | 199 | try { 200 | device = physicalDevice.createDeviceUnique(createInfo); 201 | } catch (vk::SystemError err) { 202 | throw std::runtime_error("failed to create logical device!"); 203 | } 204 | 205 | graphicsQueue = device->getQueue(indices.graphicsFamily.value(), 0); 206 | } 207 | 208 | bool isDeviceSuitable(const vk::PhysicalDevice& device) { 209 | QueueFamilyIndices indices = findQueueFamilies(device); 210 | 211 | return indices.isComplete(); 212 | } 213 | 214 | QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice device) { 215 | QueueFamilyIndices indices; 216 | 217 | auto queueFamilies = device.getQueueFamilyProperties(); 218 | 219 | int i = 0; 220 | for (const auto& queueFamily : queueFamilies) { 221 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & vk::QueueFlagBits::eGraphics) { 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_UTILS_EXTENSION_NAME); 244 | } 245 | 246 | return extensions; 247 | } 248 | 249 | bool checkValidationLayerSupport() { 250 | auto availableLayers = vk::enumerateInstanceLayerProperties(); 251 | for (const char* layerName : validationLayers) { 252 | bool layerFound = false; 253 | 254 | for (const auto& layerProperties : availableLayers) { 255 | if (strcmp(layerName, layerProperties.layerName) == 0) { 256 | layerFound = true; 257 | break; 258 | } 259 | } 260 | 261 | if (!layerFound) { 262 | return false; 263 | } 264 | } 265 | 266 | return true; 267 | } 268 | 269 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { 270 | std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; 271 | 272 | return VK_FALSE; 273 | } 274 | }; 275 | 276 | int main() { 277 | HelloTriangleApplication app; 278 | 279 | try { 280 | app.run(); 281 | } catch (const std::exception& e) { 282 | std::cerr << e.what() << std::endl; 283 | return EXIT_FAILURE; 284 | } 285 | 286 | return EXIT_SUCCESS; 287 | } 288 | -------------------------------------------------------------------------------- /steps/04_logical_device.cpp.original.diff: -------------------------------------------------------------------------------- 1 | --- a/../../VulkanTutorial/code/04_logical_device.cpp 2 | +++ b/04_logical_device.cpp 3 | @@ -1,4 +1,4 @@ 4 | -#define GLFW_INCLUDE_VULKAN 5 | +#include 6 | #include 7 | 8 | #include 9 | @@ -57,13 +57,13 @@ public: 10 | private: 11 | GLFWwindow* window; 12 | 13 | - VkInstance instance; 14 | + vk::UniqueInstance instance; 15 | VkDebugUtilsMessengerEXT callback; 16 | 17 | - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; 18 | - VkDevice device; 19 | + vk::PhysicalDevice physicalDevice; 20 | + vk::UniqueDevice device; 21 | 22 | - VkQueue graphicsQueue; 23 | + vk::Queue graphicsQueue; 24 | 25 | void initWindow() { 26 | glfwInit(); 27 | @@ -88,14 +88,12 @@ private: 28 | } 29 | 30 | void cleanup() { 31 | - vkDestroyDevice(device, nullptr); 32 | + // NOTE: instance destruction is handled by UniqueInstance, same for device 33 | 34 | if (enableValidationLayers) { 35 | - DestroyDebugUtilsMessengerEXT(instance, callback, nullptr); 36 | + DestroyDebugUtilsMessengerEXT(*instance, callback, nullptr); 37 | } 38 | 39 | - vkDestroyInstance(instance, nullptr); 40 | - 41 | glfwDestroyWindow(window); 42 | 43 | glfwTerminate(); 44 | @@ -106,30 +104,32 @@ private: 45 | throw std::runtime_error("validation layers requested, but not available!"); 46 | } 47 | 48 | - VkApplicationInfo appInfo = {}; 49 | - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 50 | - appInfo.pApplicationName = "Hello Triangle"; 51 | - appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 52 | - appInfo.pEngineName = "No Engine"; 53 | - appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 54 | - appInfo.apiVersion = VK_API_VERSION_1_0; 55 | - 56 | - VkInstanceCreateInfo createInfo = {}; 57 | - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 58 | - createInfo.pApplicationInfo = &appInfo; 59 | - 60 | + auto appInfo = vk::ApplicationInfo( 61 | + "Hello Triangle", 62 | + VK_MAKE_VERSION(1, 0, 0), 63 | + "No Engine", 64 | + VK_MAKE_VERSION(1, 0, 0), 65 | + VK_API_VERSION_1_0 66 | + ); 67 | + 68 | auto extensions = getRequiredExtensions(); 69 | - createInfo.enabledExtensionCount = static_cast(extensions.size()); 70 | - createInfo.ppEnabledExtensionNames = extensions.data(); 71 | + 72 | + auto createInfo = vk::InstanceCreateInfo( 73 | + vk::InstanceCreateFlags(), 74 | + &appInfo, 75 | + 0, nullptr, // enabled layers 76 | + static_cast(extensions.size()), extensions.data() // enabled extensions 77 | + ); 78 | 79 | if (enableValidationLayers) { 80 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 81 | createInfo.ppEnabledLayerNames = validationLayers.data(); 82 | - } else { 83 | - createInfo.enabledLayerCount = 0; 84 | } 85 | 86 | - if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { 87 | + try { 88 | + instance = vk::createInstanceUnique(createInfo, nullptr); 89 | + } 90 | + catch (vk::SystemError err) { 91 | throw std::runtime_error("failed to create instance!"); 92 | } 93 | } 94 | @@ -137,28 +137,30 @@ private: 95 | void setupDebugCallback() { 96 | if (!enableValidationLayers) return; 97 | 98 | - VkDebugUtilsMessengerCreateInfoEXT createInfo = {}; 99 | - createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; 100 | - createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; 101 | - createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; 102 | - createInfo.pfnUserCallback = debugCallback; 103 | + auto createInfo = vk::DebugUtilsMessengerCreateInfoEXT( 104 | + vk::DebugUtilsMessengerCreateFlagsEXT(), 105 | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, 106 | + vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 107 | + debugCallback, 108 | + nullptr 109 | + ); 110 | 111 | - if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { 112 | + // NOTE: Vulkan-hpp has methods for this, but they trigger linking errors... 113 | + //instance->createDebugUtilsMessengerEXT(createInfo); 114 | + //instance->createDebugUtilsMessengerEXTUnique(createInfo); 115 | + 116 | + // NOTE: reinterpret_cast is also used by vulkan.hpp internally for all these structs 117 | + if (CreateDebugUtilsMessengerEXT(*instance, reinterpret_cast(&createInfo), nullptr, &callback) != VK_SUCCESS) { 118 | throw std::runtime_error("failed to set up debug callback!"); 119 | } 120 | } 121 | 122 | void pickPhysicalDevice() { 123 | - uint32_t deviceCount = 0; 124 | - vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); 125 | - 126 | - if (deviceCount == 0) { 127 | + auto devices = instance->enumeratePhysicalDevices(); 128 | + if (devices.size() == 0) { 129 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 130 | } 131 | 132 | - std::vector devices(deviceCount); 133 | - vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); 134 | - 135 | for (const auto& device : devices) { 136 | if (isDeviceSuitable(device)) { 137 | physicalDevice = device; 138 | @@ -166,66 +168,57 @@ private: 139 | } 140 | } 141 | 142 | - if (physicalDevice == VK_NULL_HANDLE) { 143 | + if (!physicalDevice) { 144 | throw std::runtime_error("failed to find a suitable GPU!"); 145 | } 146 | } 147 | 148 | void createLogicalDevice() { 149 | QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 150 | - 151 | - VkDeviceQueueCreateInfo queueCreateInfo = {}; 152 | - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 153 | - queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value(); 154 | - queueCreateInfo.queueCount = 1; 155 | - 156 | float queuePriority = 1.0f; 157 | - queueCreateInfo.pQueuePriorities = &queuePriority; 158 | - 159 | - VkPhysicalDeviceFeatures deviceFeatures = {}; 160 | - 161 | - VkDeviceCreateInfo createInfo = {}; 162 | - createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 163 | - 164 | - createInfo.pQueueCreateInfos = &queueCreateInfo; 165 | - createInfo.queueCreateInfoCount = 1; 166 | - 167 | + auto queueCreateInfo = vk::DeviceQueueCreateInfo( 168 | + vk::DeviceQueueCreateFlags(), 169 | + indices.graphicsFamily.value(), 170 | + 1, // queueCount 171 | + &queuePriority 172 | + ); 173 | + 174 | + auto deviceFeatures = vk::PhysicalDeviceFeatures(); 175 | + auto createInfo = vk::DeviceCreateInfo( 176 | + vk::DeviceCreateFlags(), 177 | + 1, &queueCreateInfo 178 | + ); 179 | createInfo.pEnabledFeatures = &deviceFeatures; 180 | - 181 | createInfo.enabledExtensionCount = 0; 182 | 183 | if (enableValidationLayers) { 184 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 185 | createInfo.ppEnabledLayerNames = validationLayers.data(); 186 | - } else { 187 | - createInfo.enabledLayerCount = 0; 188 | } 189 | 190 | - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { 191 | + try { 192 | + device = physicalDevice.createDeviceUnique(createInfo); 193 | + } catch (vk::SystemError err) { 194 | throw std::runtime_error("failed to create logical device!"); 195 | } 196 | 197 | - vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); 198 | + graphicsQueue = device->getQueue(indices.graphicsFamily.value(), 0); 199 | } 200 | 201 | - bool isDeviceSuitable(VkPhysicalDevice device) { 202 | + bool isDeviceSuitable(const vk::PhysicalDevice& device) { 203 | QueueFamilyIndices indices = findQueueFamilies(device); 204 | 205 | return indices.isComplete(); 206 | } 207 | 208 | - QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { 209 | + QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice device) { 210 | QueueFamilyIndices indices; 211 | 212 | - uint32_t queueFamilyCount = 0; 213 | - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); 214 | - 215 | - std::vector queueFamilies(queueFamilyCount); 216 | - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); 217 | + auto queueFamilies = device.getQueueFamilyProperties(); 218 | 219 | int i = 0; 220 | for (const auto& queueFamily : queueFamilies) { 221 | - if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { 222 | + if (queueFamily.queueCount > 0 && queueFamily.queueFlags & vk::QueueFlagBits::eGraphics) { 223 | indices.graphicsFamily = i; 224 | } 225 | 226 | @@ -254,12 +247,7 @@ private: 227 | } 228 | 229 | bool checkValidationLayerSupport() { 230 | - uint32_t layerCount; 231 | - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 232 | - 233 | - std::vector availableLayers(layerCount); 234 | - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 235 | - 236 | + auto availableLayers = vk::enumerateInstanceLayerProperties(); 237 | for (const char* layerName : validationLayers) { 238 | bool layerFound = false; 239 | 240 | -------------------------------------------------------------------------------- /steps/04_logical_device.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/03_physical_device_selection.cpp 2 | +++ b/04_logical_device.cpp 3 | @@ -61,6 +61,9 @@ private: 4 | VkDebugUtilsMessengerEXT callback; 5 | 6 | vk::PhysicalDevice physicalDevice; 7 | + vk::UniqueDevice device; 8 | + 9 | + vk::Queue graphicsQueue; 10 | 11 | void initWindow() { 12 | glfwInit(); 13 | @@ -75,6 +78,7 @@ private: 14 | createInstance(); 15 | setupDebugCallback(); 16 | pickPhysicalDevice(); 17 | + createLogicalDevice(); 18 | } 19 | 20 | void mainLoop() { 21 | @@ -84,12 +88,12 @@ private: 22 | } 23 | 24 | void cleanup() { 25 | + // NOTE: instance destruction is handled by UniqueInstance, same for device 26 | + 27 | if (enableValidationLayers) { 28 | DestroyDebugUtilsMessengerEXT(*instance, callback, nullptr); 29 | } 30 | 31 | - // NOTE: instance destruction is handled by UniqueInstance 32 | - 33 | glfwDestroyWindow(window); 34 | 35 | glfwTerminate(); 36 | @@ -169,6 +173,38 @@ private: 37 | } 38 | } 39 | 40 | + void createLogicalDevice() { 41 | + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 42 | + float queuePriority = 1.0f; 43 | + auto queueCreateInfo = vk::DeviceQueueCreateInfo( 44 | + vk::DeviceQueueCreateFlags(), 45 | + indices.graphicsFamily.value(), 46 | + 1, // queueCount 47 | + &queuePriority 48 | + ); 49 | + 50 | + auto deviceFeatures = vk::PhysicalDeviceFeatures(); 51 | + auto createInfo = vk::DeviceCreateInfo( 52 | + vk::DeviceCreateFlags(), 53 | + 1, &queueCreateInfo 54 | + ); 55 | + createInfo.pEnabledFeatures = &deviceFeatures; 56 | + createInfo.enabledExtensionCount = 0; 57 | + 58 | + if (enableValidationLayers) { 59 | + createInfo.enabledLayerCount = static_cast(validationLayers.size()); 60 | + createInfo.ppEnabledLayerNames = validationLayers.data(); 61 | + } 62 | + 63 | + try { 64 | + device = physicalDevice.createDeviceUnique(createInfo); 65 | + } catch (vk::SystemError err) { 66 | + throw std::runtime_error("failed to create logical device!"); 67 | + } 68 | + 69 | + graphicsQueue = device->getQueue(indices.graphicsFamily.value(), 0); 70 | + } 71 | + 72 | bool isDeviceSuitable(const vk::PhysicalDevice& device) { 73 | QueueFamilyIndices indices = findQueueFamilies(device); 74 | 75 | -------------------------------------------------------------------------------- /steps/05_window_surface.cpp: -------------------------------------------------------------------------------- 1 | #include 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 | #ifdef NDEBUG 20 | const bool enableValidationLayers = false; 21 | #else 22 | const bool enableValidationLayers = true; 23 | #endif 24 | 25 | VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pCallback) { 26 | auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); 27 | if (func != nullptr) { 28 | return func(instance, pCreateInfo, pAllocator, pCallback); 29 | } else { 30 | return VK_ERROR_EXTENSION_NOT_PRESENT; 31 | } 32 | } 33 | 34 | void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT callback, const VkAllocationCallbacks* pAllocator) { 35 | auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); 36 | if (func != nullptr) { 37 | func(instance, callback, pAllocator); 38 | } 39 | } 40 | 41 | struct QueueFamilyIndices { 42 | std::optional graphicsFamily; 43 | std::optional presentFamily; 44 | 45 | bool isComplete() { 46 | return graphicsFamily.has_value() && presentFamily.has_value(); 47 | } 48 | }; 49 | 50 | class HelloTriangleApplication { 51 | public: 52 | void run() { 53 | initWindow(); 54 | initVulkan(); 55 | mainLoop(); 56 | cleanup(); 57 | } 58 | 59 | private: 60 | GLFWwindow* window; 61 | 62 | vk::UniqueInstance instance; 63 | VkDebugUtilsMessengerEXT callback; 64 | vk::SurfaceKHR surface; 65 | 66 | vk::PhysicalDevice physicalDevice; 67 | vk::UniqueDevice device; 68 | 69 | vk::Queue graphicsQueue; 70 | vk::Queue presentQueue; 71 | 72 | void initWindow() { 73 | glfwInit(); 74 | 75 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 76 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 77 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 78 | } 79 | 80 | void initVulkan() { 81 | createInstance(); 82 | setupDebugCallback(); 83 | createSurface(); 84 | pickPhysicalDevice(); 85 | createLogicalDevice(); 86 | } 87 | 88 | void mainLoop() { 89 | while (!glfwWindowShouldClose(window)) { 90 | glfwPollEvents(); 91 | } 92 | } 93 | 94 | void cleanup() { 95 | // NOTE: instance destruction is handled by UniqueInstance, same for device 96 | 97 | // surface is created by glfw, therefore not using a Unique handle 98 | instance->destroySurfaceKHR(surface); 99 | 100 | if (enableValidationLayers) { 101 | DestroyDebugUtilsMessengerEXT(*instance, callback, nullptr); 102 | } 103 | 104 | glfwDestroyWindow(window); 105 | 106 | glfwTerminate(); 107 | } 108 | 109 | void createInstance() { 110 | if (enableValidationLayers && !checkValidationLayerSupport()) { 111 | throw std::runtime_error("validation layers requested, but not available!"); 112 | } 113 | 114 | auto appInfo = vk::ApplicationInfo( 115 | "Hello Triangle", 116 | VK_MAKE_VERSION(1, 0, 0), 117 | "No Engine", 118 | VK_MAKE_VERSION(1, 0, 0), 119 | VK_API_VERSION_1_0 120 | ); 121 | 122 | auto extensions = getRequiredExtensions(); 123 | 124 | auto createInfo = vk::InstanceCreateInfo( 125 | vk::InstanceCreateFlags(), 126 | &appInfo, 127 | 0, nullptr, // enabled layers 128 | static_cast(extensions.size()), extensions.data() // enabled extensions 129 | ); 130 | 131 | if (enableValidationLayers) { 132 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 133 | createInfo.ppEnabledLayerNames = validationLayers.data(); 134 | } 135 | 136 | try { 137 | instance = vk::createInstanceUnique(createInfo, nullptr); 138 | } 139 | catch (vk::SystemError err) { 140 | throw std::runtime_error("failed to create instance!"); 141 | } 142 | } 143 | 144 | void setupDebugCallback() { 145 | if (!enableValidationLayers) return; 146 | 147 | auto createInfo = vk::DebugUtilsMessengerCreateInfoEXT( 148 | vk::DebugUtilsMessengerCreateFlagsEXT(), 149 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, 150 | vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 151 | debugCallback, 152 | nullptr 153 | ); 154 | 155 | // NOTE: Vulkan-hpp has methods for this, but they trigger linking errors... 156 | //instance->createDebugUtilsMessengerEXT(createInfo); 157 | //instance->createDebugUtilsMessengerEXTUnique(createInfo); 158 | 159 | // NOTE: reinterpret_cast is also used by vulkan.hpp internally for all these structs 160 | if (CreateDebugUtilsMessengerEXT(*instance, reinterpret_cast(&createInfo), nullptr, &callback) != VK_SUCCESS) { 161 | throw std::runtime_error("failed to set up debug callback!"); 162 | } 163 | } 164 | 165 | void createSurface() { 166 | VkSurfaceKHR rawSurface; 167 | if (glfwCreateWindowSurface(*instance, window, nullptr, &rawSurface) != VK_SUCCESS) { 168 | throw std::runtime_error("failed to create window surface!"); 169 | } 170 | surface = rawSurface; 171 | } 172 | 173 | void pickPhysicalDevice() { 174 | auto devices = instance->enumeratePhysicalDevices(); 175 | if (devices.size() == 0) { 176 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 177 | } 178 | 179 | for (const auto& device : devices) { 180 | if (isDeviceSuitable(device)) { 181 | physicalDevice = device; 182 | break; 183 | } 184 | } 185 | 186 | if (!physicalDevice) { 187 | throw std::runtime_error("failed to find a suitable GPU!"); 188 | } 189 | } 190 | 191 | void createLogicalDevice() { 192 | QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 193 | 194 | std::vector queueCreateInfos; 195 | std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() }; 196 | 197 | float queuePriority = 1.0f; 198 | 199 | for (uint32_t queueFamily : uniqueQueueFamilies) { 200 | queueCreateInfos.push_back({ 201 | vk::DeviceQueueCreateFlags(), 202 | queueFamily, 203 | 1, // queueCount 204 | &queuePriority 205 | }); 206 | } 207 | 208 | auto deviceFeatures = vk::PhysicalDeviceFeatures(); 209 | auto createInfo = vk::DeviceCreateInfo( 210 | vk::DeviceCreateFlags(), 211 | static_cast(queueCreateInfos.size()), 212 | queueCreateInfos.data() 213 | ); 214 | createInfo.pEnabledFeatures = &deviceFeatures; 215 | createInfo.enabledExtensionCount = 0; 216 | 217 | if (enableValidationLayers) { 218 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 219 | createInfo.ppEnabledLayerNames = validationLayers.data(); 220 | } 221 | 222 | try { 223 | device = physicalDevice.createDeviceUnique(createInfo); 224 | } catch (vk::SystemError err) { 225 | throw std::runtime_error("failed to create logical device!"); 226 | } 227 | 228 | graphicsQueue = device->getQueue(indices.graphicsFamily.value(), 0); 229 | presentQueue = device->getQueue(indices.presentFamily.value(), 0); 230 | } 231 | 232 | bool isDeviceSuitable(const vk::PhysicalDevice& device) { 233 | QueueFamilyIndices indices = findQueueFamilies(device); 234 | 235 | return indices.isComplete(); 236 | } 237 | 238 | QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice device) { 239 | QueueFamilyIndices indices; 240 | 241 | auto queueFamilies = device.getQueueFamilyProperties(); 242 | 243 | int i = 0; 244 | for (const auto& queueFamily : queueFamilies) { 245 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & vk::QueueFlagBits::eGraphics) { 246 | indices.graphicsFamily = i; 247 | } 248 | 249 | if (queueFamily.queueCount > 0 && device.getSurfaceSupportKHR(i, surface)) { 250 | indices.presentFamily = i; 251 | } 252 | 253 | if (indices.isComplete()) { 254 | break; 255 | } 256 | 257 | i++; 258 | } 259 | 260 | return indices; 261 | } 262 | 263 | std::vector getRequiredExtensions() { 264 | uint32_t glfwExtensionCount = 0; 265 | const char** glfwExtensions; 266 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 267 | 268 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 269 | 270 | if (enableValidationLayers) { 271 | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 272 | } 273 | 274 | return extensions; 275 | } 276 | 277 | bool checkValidationLayerSupport() { 278 | auto availableLayers = vk::enumerateInstanceLayerProperties(); 279 | for (const char* layerName : validationLayers) { 280 | bool layerFound = false; 281 | 282 | for (const auto& layerProperties : availableLayers) { 283 | if (strcmp(layerName, layerProperties.layerName) == 0) { 284 | layerFound = true; 285 | break; 286 | } 287 | } 288 | 289 | if (!layerFound) { 290 | return false; 291 | } 292 | } 293 | 294 | return true; 295 | } 296 | 297 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { 298 | std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; 299 | 300 | return VK_FALSE; 301 | } 302 | }; 303 | 304 | int main() { 305 | HelloTriangleApplication app; 306 | 307 | try { 308 | app.run(); 309 | } catch (const std::exception& e) { 310 | std::cerr << e.what() << std::endl; 311 | return EXIT_FAILURE; 312 | } 313 | 314 | return EXIT_SUCCESS; 315 | } 316 | -------------------------------------------------------------------------------- /steps/05_window_surface.cpp.original.diff: -------------------------------------------------------------------------------- 1 | --- a/../../VulkanTutorial/code/05_window_surface.cpp 2 | +++ b/05_window_surface.cpp 3 | @@ -1,4 +1,4 @@ 4 | -#define GLFW_INCLUDE_VULKAN 5 | +#include 6 | #include 7 | 8 | #include 9 | @@ -59,22 +59,21 @@ public: 10 | private: 11 | GLFWwindow* window; 12 | 13 | - VkInstance instance; 14 | + vk::UniqueInstance instance; 15 | VkDebugUtilsMessengerEXT callback; 16 | - VkSurfaceKHR surface; 17 | + vk::SurfaceKHR surface; 18 | 19 | - VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; 20 | - VkDevice device; 21 | + vk::PhysicalDevice physicalDevice; 22 | + vk::UniqueDevice device; 23 | 24 | - VkQueue graphicsQueue; 25 | - VkQueue presentQueue; 26 | + vk::Queue graphicsQueue; 27 | + vk::Queue presentQueue; 28 | 29 | void initWindow() { 30 | glfwInit(); 31 | 32 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 33 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 34 | - 35 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 36 | } 37 | 38 | @@ -93,15 +92,15 @@ private: 39 | } 40 | 41 | void cleanup() { 42 | - vkDestroyDevice(device, nullptr); 43 | + // NOTE: instance destruction is handled by UniqueInstance, same for device 44 | + 45 | + // surface is created by glfw, therefore not using a Unique handle 46 | + instance->destroySurfaceKHR(surface); 47 | 48 | if (enableValidationLayers) { 49 | - DestroyDebugUtilsMessengerEXT(instance, callback, nullptr); 50 | + DestroyDebugUtilsMessengerEXT(*instance, callback, nullptr); 51 | } 52 | 53 | - vkDestroySurfaceKHR(instance, surface, nullptr); 54 | - vkDestroyInstance(instance, nullptr); 55 | - 56 | glfwDestroyWindow(window); 57 | 58 | glfwTerminate(); 59 | @@ -112,30 +111,32 @@ private: 60 | throw std::runtime_error("validation layers requested, but not available!"); 61 | } 62 | 63 | - VkApplicationInfo appInfo = {}; 64 | - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 65 | - appInfo.pApplicationName = "Hello Triangle"; 66 | - appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 67 | - appInfo.pEngineName = "No Engine"; 68 | - appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 69 | - appInfo.apiVersion = VK_API_VERSION_1_0; 70 | - 71 | - VkInstanceCreateInfo createInfo = {}; 72 | - createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 73 | - createInfo.pApplicationInfo = &appInfo; 74 | - 75 | + auto appInfo = vk::ApplicationInfo( 76 | + "Hello Triangle", 77 | + VK_MAKE_VERSION(1, 0, 0), 78 | + "No Engine", 79 | + VK_MAKE_VERSION(1, 0, 0), 80 | + VK_API_VERSION_1_0 81 | + ); 82 | + 83 | auto extensions = getRequiredExtensions(); 84 | - createInfo.enabledExtensionCount = static_cast(extensions.size()); 85 | - createInfo.ppEnabledExtensionNames = extensions.data(); 86 | + 87 | + auto createInfo = vk::InstanceCreateInfo( 88 | + vk::InstanceCreateFlags(), 89 | + &appInfo, 90 | + 0, nullptr, // enabled layers 91 | + static_cast(extensions.size()), extensions.data() // enabled extensions 92 | + ); 93 | 94 | if (enableValidationLayers) { 95 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 96 | createInfo.ppEnabledLayerNames = validationLayers.data(); 97 | - } else { 98 | - createInfo.enabledLayerCount = 0; 99 | } 100 | 101 | - if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { 102 | + try { 103 | + instance = vk::createInstanceUnique(createInfo, nullptr); 104 | + } 105 | + catch (vk::SystemError err) { 106 | throw std::runtime_error("failed to create instance!"); 107 | } 108 | } 109 | @@ -143,34 +144,38 @@ private: 110 | void setupDebugCallback() { 111 | if (!enableValidationLayers) return; 112 | 113 | - VkDebugUtilsMessengerCreateInfoEXT createInfo = {}; 114 | - createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; 115 | - createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; 116 | - createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; 117 | - createInfo.pfnUserCallback = debugCallback; 118 | + auto createInfo = vk::DebugUtilsMessengerCreateInfoEXT( 119 | + vk::DebugUtilsMessengerCreateFlagsEXT(), 120 | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, 121 | + vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 122 | + debugCallback, 123 | + nullptr 124 | + ); 125 | + 126 | + // NOTE: Vulkan-hpp has methods for this, but they trigger linking errors... 127 | + //instance->createDebugUtilsMessengerEXT(createInfo); 128 | + //instance->createDebugUtilsMessengerEXTUnique(createInfo); 129 | 130 | - if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &callback) != VK_SUCCESS) { 131 | + // NOTE: reinterpret_cast is also used by vulkan.hpp internally for all these structs 132 | + if (CreateDebugUtilsMessengerEXT(*instance, reinterpret_cast(&createInfo), nullptr, &callback) != VK_SUCCESS) { 133 | throw std::runtime_error("failed to set up debug callback!"); 134 | } 135 | } 136 | 137 | void createSurface() { 138 | - if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) { 139 | + VkSurfaceKHR rawSurface; 140 | + if (glfwCreateWindowSurface(*instance, window, nullptr, &rawSurface) != VK_SUCCESS) { 141 | throw std::runtime_error("failed to create window surface!"); 142 | } 143 | + surface = rawSurface; 144 | } 145 | 146 | void pickPhysicalDevice() { 147 | - uint32_t deviceCount = 0; 148 | - vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); 149 | - 150 | - if (deviceCount == 0) { 151 | + auto devices = instance->enumeratePhysicalDevices(); 152 | + if (devices.size() == 0) { 153 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 154 | } 155 | 156 | - std::vector devices(deviceCount); 157 | - vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); 158 | - 159 | for (const auto& device : devices) { 160 | if (isDeviceSuitable(device)) { 161 | physicalDevice = device; 162 | @@ -178,7 +183,7 @@ private: 163 | } 164 | } 165 | 166 | - if (physicalDevice == VK_NULL_HANDLE) { 167 | + if (!physicalDevice) { 168 | throw std::runtime_error("failed to find a suitable GPU!"); 169 | } 170 | } 171 | @@ -186,71 +191,62 @@ private: 172 | void createLogicalDevice() { 173 | QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 174 | 175 | - std::vector queueCreateInfos; 176 | - std::set uniqueQueueFamilies = {indices.graphicsFamily.value(), indices.presentFamily.value()}; 177 | + std::vector queueCreateInfos; 178 | + std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() }; 179 | 180 | float queuePriority = 1.0f; 181 | + 182 | for (uint32_t queueFamily : uniqueQueueFamilies) { 183 | - VkDeviceQueueCreateInfo queueCreateInfo = {}; 184 | - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 185 | - queueCreateInfo.queueFamilyIndex = queueFamily; 186 | - queueCreateInfo.queueCount = 1; 187 | - queueCreateInfo.pQueuePriorities = &queuePriority; 188 | - queueCreateInfos.push_back(queueCreateInfo); 189 | + queueCreateInfos.push_back({ 190 | + vk::DeviceQueueCreateFlags(), 191 | + queueFamily, 192 | + 1, // queueCount 193 | + &queuePriority 194 | + }); 195 | } 196 | 197 | - VkPhysicalDeviceFeatures deviceFeatures = {}; 198 | - 199 | - VkDeviceCreateInfo createInfo = {}; 200 | - createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 201 | - 202 | - createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); 203 | - createInfo.pQueueCreateInfos = queueCreateInfos.data(); 204 | - 205 | + auto deviceFeatures = vk::PhysicalDeviceFeatures(); 206 | + auto createInfo = vk::DeviceCreateInfo( 207 | + vk::DeviceCreateFlags(), 208 | + static_cast(queueCreateInfos.size()), 209 | + queueCreateInfos.data() 210 | + ); 211 | createInfo.pEnabledFeatures = &deviceFeatures; 212 | - 213 | createInfo.enabledExtensionCount = 0; 214 | 215 | if (enableValidationLayers) { 216 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 217 | createInfo.ppEnabledLayerNames = validationLayers.data(); 218 | - } else { 219 | - createInfo.enabledLayerCount = 0; 220 | } 221 | 222 | - if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) { 223 | + try { 224 | + device = physicalDevice.createDeviceUnique(createInfo); 225 | + } catch (vk::SystemError err) { 226 | throw std::runtime_error("failed to create logical device!"); 227 | } 228 | 229 | - vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); 230 | - vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); 231 | + graphicsQueue = device->getQueue(indices.graphicsFamily.value(), 0); 232 | + presentQueue = device->getQueue(indices.presentFamily.value(), 0); 233 | } 234 | 235 | - bool isDeviceSuitable(VkPhysicalDevice device) { 236 | + bool isDeviceSuitable(const vk::PhysicalDevice& device) { 237 | QueueFamilyIndices indices = findQueueFamilies(device); 238 | 239 | return indices.isComplete(); 240 | } 241 | 242 | - QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { 243 | + QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice device) { 244 | QueueFamilyIndices indices; 245 | 246 | - uint32_t queueFamilyCount = 0; 247 | - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); 248 | - 249 | - std::vector queueFamilies(queueFamilyCount); 250 | - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); 251 | + auto queueFamilies = device.getQueueFamilyProperties(); 252 | 253 | int i = 0; 254 | for (const auto& queueFamily : queueFamilies) { 255 | - if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { 256 | + if (queueFamily.queueCount > 0 && queueFamily.queueFlags & vk::QueueFlagBits::eGraphics) { 257 | indices.graphicsFamily = i; 258 | } 259 | 260 | - VkBool32 presentSupport = false; 261 | - vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); 262 | - 263 | - if (queueFamily.queueCount > 0 && presentSupport) { 264 | + if (queueFamily.queueCount > 0 && device.getSurfaceSupportKHR(i, surface)) { 265 | indices.presentFamily = i; 266 | } 267 | 268 | @@ -279,12 +275,7 @@ private: 269 | } 270 | 271 | bool checkValidationLayerSupport() { 272 | - uint32_t layerCount; 273 | - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 274 | - 275 | - std::vector availableLayers(layerCount); 276 | - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 277 | - 278 | + auto availableLayers = vk::enumerateInstanceLayerProperties(); 279 | for (const char* layerName : validationLayers) { 280 | bool layerFound = false; 281 | 282 | -------------------------------------------------------------------------------- /steps/05_window_surface.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/04_logical_device.cpp 2 | +++ b/05_window_surface.cpp 3 | @@ -7,6 +7,7 @@ 4 | #include 5 | #include 6 | #include 7 | +#include 8 | 9 | const int WIDTH = 800; 10 | const int HEIGHT = 600; 11 | @@ -39,9 +40,10 @@ void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT 12 | 13 | struct QueueFamilyIndices { 14 | std::optional graphicsFamily; 15 | + std::optional presentFamily; 16 | 17 | bool isComplete() { 18 | - return graphicsFamily.has_value(); 19 | + return graphicsFamily.has_value() && presentFamily.has_value(); 20 | } 21 | }; 22 | 23 | @@ -59,24 +61,26 @@ private: 24 | 25 | vk::UniqueInstance instance; 26 | VkDebugUtilsMessengerEXT callback; 27 | + vk::SurfaceKHR surface; 28 | 29 | vk::PhysicalDevice physicalDevice; 30 | vk::UniqueDevice device; 31 | 32 | vk::Queue graphicsQueue; 33 | + vk::Queue presentQueue; 34 | 35 | void initWindow() { 36 | glfwInit(); 37 | 38 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 39 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 40 | - 41 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 42 | } 43 | 44 | void initVulkan() { 45 | createInstance(); 46 | setupDebugCallback(); 47 | + createSurface(); 48 | pickPhysicalDevice(); 49 | createLogicalDevice(); 50 | } 51 | @@ -90,6 +94,9 @@ private: 52 | void cleanup() { 53 | // NOTE: instance destruction is handled by UniqueInstance, same for device 54 | 55 | + // surface is created by glfw, therefore not using a Unique handle 56 | + instance->destroySurfaceKHR(surface); 57 | + 58 | if (enableValidationLayers) { 59 | DestroyDebugUtilsMessengerEXT(*instance, callback, nullptr); 60 | } 61 | @@ -155,6 +162,14 @@ private: 62 | } 63 | } 64 | 65 | + void createSurface() { 66 | + VkSurfaceKHR rawSurface; 67 | + if (glfwCreateWindowSurface(*instance, window, nullptr, &rawSurface) != VK_SUCCESS) { 68 | + throw std::runtime_error("failed to create window surface!"); 69 | + } 70 | + surface = rawSurface; 71 | + } 72 | + 73 | void pickPhysicalDevice() { 74 | auto devices = instance->enumeratePhysicalDevices(); 75 | if (devices.size() == 0) { 76 | @@ -175,18 +190,26 @@ private: 77 | 78 | void createLogicalDevice() { 79 | QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 80 | + 81 | + std::vector queueCreateInfos; 82 | + std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() }; 83 | + 84 | float queuePriority = 1.0f; 85 | - auto queueCreateInfo = vk::DeviceQueueCreateInfo( 86 | - vk::DeviceQueueCreateFlags(), 87 | - indices.graphicsFamily.value(), 88 | - 1, // queueCount 89 | - &queuePriority 90 | - ); 91 | + 92 | + for (uint32_t queueFamily : uniqueQueueFamilies) { 93 | + queueCreateInfos.push_back({ 94 | + vk::DeviceQueueCreateFlags(), 95 | + queueFamily, 96 | + 1, // queueCount 97 | + &queuePriority 98 | + }); 99 | + } 100 | 101 | auto deviceFeatures = vk::PhysicalDeviceFeatures(); 102 | auto createInfo = vk::DeviceCreateInfo( 103 | vk::DeviceCreateFlags(), 104 | - 1, &queueCreateInfo 105 | + static_cast(queueCreateInfos.size()), 106 | + queueCreateInfos.data() 107 | ); 108 | createInfo.pEnabledFeatures = &deviceFeatures; 109 | createInfo.enabledExtensionCount = 0; 110 | @@ -203,6 +226,7 @@ private: 111 | } 112 | 113 | graphicsQueue = device->getQueue(indices.graphicsFamily.value(), 0); 114 | + presentQueue = device->getQueue(indices.presentFamily.value(), 0); 115 | } 116 | 117 | bool isDeviceSuitable(const vk::PhysicalDevice& device) { 118 | @@ -222,6 +246,10 @@ private: 119 | indices.graphicsFamily = i; 120 | } 121 | 122 | + if (queueFamily.queueCount > 0 && device.getSurfaceSupportKHR(i, surface)) { 123 | + indices.presentFamily = i; 124 | + } 125 | + 126 | if (indices.isComplete()) { 127 | break; 128 | } 129 | -------------------------------------------------------------------------------- /steps/06_swap_chain_creation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | const int WIDTH = 800; 14 | const int HEIGHT = 600; 15 | 16 | const std::vector validationLayers = { 17 | "VK_LAYER_LUNARG_standard_validation" 18 | }; 19 | 20 | const std::vector deviceExtensions = { 21 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 22 | }; 23 | 24 | #ifdef NDEBUG 25 | const bool enableValidationLayers = false; 26 | #else 27 | const bool enableValidationLayers = true; 28 | #endif 29 | 30 | VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pCallback) { 31 | auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); 32 | if (func != nullptr) { 33 | return func(instance, pCreateInfo, pAllocator, pCallback); 34 | } 35 | else { 36 | return VK_ERROR_EXTENSION_NOT_PRESENT; 37 | } 38 | } 39 | 40 | void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT callback, const VkAllocationCallbacks* pAllocator) { 41 | auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); 42 | if (func != nullptr) { 43 | func(instance, callback, pAllocator); 44 | } 45 | } 46 | 47 | struct QueueFamilyIndices { 48 | std::optional graphicsFamily; 49 | std::optional presentFamily; 50 | 51 | bool isComplete() { 52 | return graphicsFamily.has_value() && presentFamily.has_value(); 53 | } 54 | }; 55 | 56 | struct SwapChainSupportDetails { 57 | vk::SurfaceCapabilitiesKHR capabilities; 58 | std::vector formats; 59 | std::vector presentModes; 60 | }; 61 | 62 | class HelloTriangleApplication { 63 | public: 64 | void run() { 65 | initWindow(); 66 | initVulkan(); 67 | mainLoop(); 68 | cleanup(); 69 | } 70 | 71 | private: 72 | GLFWwindow* window; 73 | 74 | vk::UniqueInstance instance; 75 | VkDebugUtilsMessengerEXT callback; 76 | vk::SurfaceKHR surface; 77 | 78 | vk::PhysicalDevice physicalDevice; 79 | vk::UniqueDevice device; 80 | 81 | vk::Queue graphicsQueue; 82 | vk::Queue presentQueue; 83 | 84 | vk::SwapchainKHR swapChain; 85 | std::vector swapChainImages; 86 | vk::Format swapChainImageFormat; 87 | vk::Extent2D swapChainExtent; 88 | 89 | void initWindow() { 90 | glfwInit(); 91 | 92 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 93 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 94 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 95 | } 96 | 97 | void initVulkan() { 98 | createInstance(); 99 | setupDebugCallback(); 100 | createSurface(); 101 | pickPhysicalDevice(); 102 | createLogicalDevice(); 103 | createSwapChain(); 104 | } 105 | 106 | void mainLoop() { 107 | while (!glfwWindowShouldClose(window)) { 108 | glfwPollEvents(); 109 | } 110 | } 111 | 112 | void cleanup() { 113 | // NOTE: instance destruction is handled by UniqueInstance, same for device 114 | 115 | // not using UniqeSwapchain to destroy in correct order - before the surface 116 | device->destroySwapchainKHR(swapChain); 117 | 118 | // surface is created by glfw, therefore not using a Unique handle 119 | instance->destroySurfaceKHR(surface); 120 | 121 | if (enableValidationLayers) { 122 | DestroyDebugUtilsMessengerEXT(*instance, callback, nullptr); 123 | } 124 | 125 | glfwDestroyWindow(window); 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 | auto appInfo = vk::ApplicationInfo( 136 | "Hello Triangle", 137 | VK_MAKE_VERSION(1, 0, 0), 138 | "No Engine", 139 | VK_MAKE_VERSION(1, 0, 0), 140 | VK_API_VERSION_1_0 141 | ); 142 | 143 | auto extensions = getRequiredExtensions(); 144 | 145 | auto createInfo = vk::InstanceCreateInfo( 146 | vk::InstanceCreateFlags(), 147 | &appInfo, 148 | 0, nullptr, // enabled layers 149 | static_cast(extensions.size()), extensions.data() // enabled extensions 150 | ); 151 | 152 | if (enableValidationLayers) { 153 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 154 | createInfo.ppEnabledLayerNames = validationLayers.data(); 155 | } 156 | 157 | try { 158 | instance = vk::createInstanceUnique(createInfo, nullptr); 159 | } 160 | catch (vk::SystemError err) { 161 | throw std::runtime_error("failed to create instance!"); 162 | } 163 | } 164 | 165 | void setupDebugCallback() { 166 | if (!enableValidationLayers) return; 167 | 168 | auto createInfo = vk::DebugUtilsMessengerCreateInfoEXT( 169 | vk::DebugUtilsMessengerCreateFlagsEXT(), 170 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, 171 | vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 172 | debugCallback, 173 | nullptr 174 | ); 175 | 176 | // NOTE: Vulkan-hpp has methods for this, but they trigger linking errors... 177 | //instance->createDebugUtilsMessengerEXT(createInfo); 178 | //instance->createDebugUtilsMessengerEXTUnique(createInfo); 179 | 180 | // NOTE: reinterpret_cast is also used by vulkan.hpp internally for all these structs 181 | if (CreateDebugUtilsMessengerEXT(*instance, reinterpret_cast(&createInfo), nullptr, &callback) != VK_SUCCESS) { 182 | throw std::runtime_error("failed to set up debug callback!"); 183 | } 184 | } 185 | 186 | void createSurface() { 187 | VkSurfaceKHR rawSurface; 188 | if (glfwCreateWindowSurface(*instance, window, nullptr, &rawSurface) != VK_SUCCESS) { 189 | throw std::runtime_error("failed to create window surface!"); 190 | } 191 | 192 | surface = rawSurface; 193 | } 194 | 195 | void pickPhysicalDevice() { 196 | auto devices = instance->enumeratePhysicalDevices(); 197 | if (devices.size() == 0) { 198 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 199 | } 200 | 201 | for (const auto& device : devices) { 202 | if (isDeviceSuitable(device)) { 203 | physicalDevice = device; 204 | break; 205 | } 206 | } 207 | 208 | if (!physicalDevice) { 209 | throw std::runtime_error("failed to find a suitable GPU!"); 210 | } 211 | } 212 | 213 | void createLogicalDevice() { 214 | QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 215 | 216 | std::vector queueCreateInfos; 217 | std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() }; 218 | 219 | float queuePriority = 1.0f; 220 | 221 | for (uint32_t queueFamily : uniqueQueueFamilies) { 222 | queueCreateInfos.push_back({ 223 | vk::DeviceQueueCreateFlags(), 224 | queueFamily, 225 | 1, // queueCount 226 | &queuePriority 227 | }); 228 | } 229 | 230 | auto deviceFeatures = vk::PhysicalDeviceFeatures(); 231 | auto createInfo = vk::DeviceCreateInfo( 232 | vk::DeviceCreateFlags(), 233 | static_cast(queueCreateInfos.size()), 234 | queueCreateInfos.data() 235 | ); 236 | createInfo.pEnabledFeatures = &deviceFeatures; 237 | createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); 238 | createInfo.ppEnabledExtensionNames = deviceExtensions.data(); 239 | 240 | if (enableValidationLayers) { 241 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 242 | createInfo.ppEnabledLayerNames = validationLayers.data(); 243 | } 244 | 245 | try { 246 | device = physicalDevice.createDeviceUnique(createInfo); 247 | } 248 | catch (vk::SystemError err) { 249 | throw std::runtime_error("failed to create logical device!"); 250 | } 251 | 252 | graphicsQueue = device->getQueue(indices.graphicsFamily.value(), 0); 253 | presentQueue = device->getQueue(indices.presentFamily.value(), 0); 254 | } 255 | 256 | void createSwapChain() { 257 | SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); 258 | 259 | vk::SurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); 260 | vk::PresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); 261 | vk::Extent2D extent = chooseSwapExtent(swapChainSupport.capabilities); 262 | 263 | uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; 264 | if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { 265 | imageCount = swapChainSupport.capabilities.maxImageCount; 266 | } 267 | 268 | vk::SwapchainCreateInfoKHR createInfo( 269 | vk::SwapchainCreateFlagsKHR(), 270 | surface, 271 | imageCount, 272 | surfaceFormat.format, 273 | surfaceFormat.colorSpace, 274 | extent, 275 | 1, // imageArrayLayers 276 | vk::ImageUsageFlagBits::eColorAttachment 277 | ); 278 | 279 | QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 280 | uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() }; 281 | 282 | if (indices.graphicsFamily != indices.presentFamily) { 283 | createInfo.imageSharingMode = vk::SharingMode::eConcurrent; 284 | createInfo.queueFamilyIndexCount = 2; 285 | createInfo.pQueueFamilyIndices = queueFamilyIndices; 286 | } 287 | else { 288 | createInfo.imageSharingMode = vk::SharingMode::eExclusive; 289 | } 290 | 291 | createInfo.preTransform = swapChainSupport.capabilities.currentTransform; 292 | createInfo.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque; 293 | createInfo.presentMode = presentMode; 294 | createInfo.clipped = VK_TRUE; 295 | 296 | createInfo.oldSwapchain = vk::SwapchainKHR(nullptr); 297 | 298 | try { 299 | swapChain = device->createSwapchainKHR(createInfo); 300 | } 301 | catch (vk::SystemError err) { 302 | throw std::runtime_error("failed to create swap chain!"); 303 | } 304 | 305 | swapChainImages = device->getSwapchainImagesKHR(swapChain); 306 | 307 | swapChainImageFormat = surfaceFormat.format; 308 | swapChainExtent = extent; 309 | } 310 | 311 | vk::SurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { 312 | if (availableFormats.size() == 1 && availableFormats[0].format == vk::Format::eUndefined) { 313 | return { vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear }; 314 | } 315 | 316 | for (const auto& availableFormat : availableFormats) { 317 | if (availableFormat.format == vk::Format::eB8G8R8A8Unorm && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { 318 | return availableFormat; 319 | } 320 | } 321 | 322 | return availableFormats[0]; 323 | } 324 | 325 | vk::PresentModeKHR chooseSwapPresentMode(const std::vector availablePresentModes) { 326 | vk::PresentModeKHR bestMode = vk::PresentModeKHR::eFifo; 327 | 328 | for (const auto& availablePresentMode : availablePresentModes) { 329 | if (availablePresentMode == vk::PresentModeKHR::eMailbox) { 330 | return availablePresentMode; 331 | } 332 | else if (availablePresentMode == vk::PresentModeKHR::eImmediate) { 333 | bestMode = availablePresentMode; 334 | } 335 | } 336 | 337 | return bestMode; 338 | } 339 | 340 | vk::Extent2D chooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities) { 341 | if (capabilities.currentExtent.width != std::numeric_limits::max()) { 342 | return capabilities.currentExtent; 343 | } 344 | else { 345 | vk::Extent2D actualExtent = { static_cast(WIDTH), static_cast(HEIGHT) }; 346 | 347 | actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); 348 | actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); 349 | 350 | return actualExtent; 351 | } 352 | } 353 | 354 | SwapChainSupportDetails querySwapChainSupport(const vk::PhysicalDevice& device) { 355 | SwapChainSupportDetails details; 356 | details.capabilities = device.getSurfaceCapabilitiesKHR(surface); 357 | details.formats = device.getSurfaceFormatsKHR(surface); 358 | details.presentModes = device.getSurfacePresentModesKHR(surface); 359 | 360 | return details; 361 | } 362 | 363 | bool isDeviceSuitable(const vk::PhysicalDevice& device) { 364 | QueueFamilyIndices indices = findQueueFamilies(device); 365 | 366 | bool extensionsSupported = checkDeviceExtensionSupport(device); 367 | 368 | bool swapChainAdequate = false; 369 | if (extensionsSupported) { 370 | SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); 371 | swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); 372 | } 373 | 374 | return indices.isComplete() && extensionsSupported && swapChainAdequate; 375 | } 376 | 377 | bool checkDeviceExtensionSupport(const vk::PhysicalDevice& device) { 378 | std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); 379 | 380 | for (const auto& extension : device.enumerateDeviceExtensionProperties()) { 381 | requiredExtensions.erase(extension.extensionName); 382 | } 383 | 384 | return requiredExtensions.empty(); 385 | } 386 | 387 | QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice device) { 388 | QueueFamilyIndices indices; 389 | 390 | auto queueFamilies = device.getQueueFamilyProperties(); 391 | 392 | int i = 0; 393 | for (const auto& queueFamily : queueFamilies) { 394 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & vk::QueueFlagBits::eGraphics) { 395 | indices.graphicsFamily = i; 396 | } 397 | 398 | if (queueFamily.queueCount > 0 && device.getSurfaceSupportKHR(i, surface)) { 399 | indices.presentFamily = i; 400 | } 401 | 402 | if (indices.isComplete()) { 403 | break; 404 | } 405 | 406 | i++; 407 | } 408 | 409 | return indices; 410 | } 411 | 412 | std::vector getRequiredExtensions() { 413 | uint32_t glfwExtensionCount = 0; 414 | const char** glfwExtensions; 415 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 416 | 417 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 418 | 419 | if (enableValidationLayers) { 420 | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 421 | } 422 | 423 | return extensions; 424 | } 425 | 426 | bool checkValidationLayerSupport() { 427 | auto availableLayers = vk::enumerateInstanceLayerProperties(); 428 | for (const char* layerName : validationLayers) { 429 | bool layerFound = false; 430 | 431 | for (const auto& layerProperties : availableLayers) { 432 | if (strcmp(layerName, layerProperties.layerName) == 0) { 433 | layerFound = true; 434 | break; 435 | } 436 | } 437 | 438 | if (!layerFound) { 439 | return false; 440 | } 441 | } 442 | 443 | return true; 444 | } 445 | 446 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { 447 | std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; 448 | 449 | return VK_FALSE; 450 | } 451 | }; 452 | 453 | int main() { 454 | HelloTriangleApplication app; 455 | 456 | try { 457 | app.run(); 458 | } 459 | catch (const std::exception& e) { 460 | std::cerr << e.what() << std::endl; 461 | return EXIT_FAILURE; 462 | } 463 | 464 | return EXIT_SUCCESS; 465 | } 466 | -------------------------------------------------------------------------------- /steps/06_swap_chain_creation.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/05_window_surface.cpp 2 | +++ b/06_swap_chain_creation.cpp 3 | @@ -3,6 +3,7 @@ 4 | 5 | #include 6 | #include 7 | +#include 8 | #include 9 | #include 10 | #include 11 | @@ -16,6 +17,10 @@ const std::vector validationLayers = { 12 | "VK_LAYER_LUNARG_standard_validation" 13 | }; 14 | 15 | +const std::vector deviceExtensions = { 16 | + VK_KHR_SWAPCHAIN_EXTENSION_NAME 17 | +}; 18 | + 19 | #ifdef NDEBUG 20 | const bool enableValidationLayers = false; 21 | #else 22 | @@ -23,16 +28,17 @@ const bool enableValidationLayers = true; 23 | #endif 24 | 25 | VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pCallback) { 26 | - auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); 27 | + auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); 28 | if (func != nullptr) { 29 | return func(instance, pCreateInfo, pAllocator, pCallback); 30 | - } else { 31 | + } 32 | + else { 33 | return VK_ERROR_EXTENSION_NOT_PRESENT; 34 | } 35 | } 36 | 37 | void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT callback, const VkAllocationCallbacks* pAllocator) { 38 | - auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); 39 | + auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); 40 | if (func != nullptr) { 41 | func(instance, callback, pAllocator); 42 | } 43 | @@ -47,6 +53,12 @@ struct QueueFamilyIndices { 44 | } 45 | }; 46 | 47 | +struct SwapChainSupportDetails { 48 | + vk::SurfaceCapabilitiesKHR capabilities; 49 | + std::vector formats; 50 | + std::vector presentModes; 51 | +}; 52 | + 53 | class HelloTriangleApplication { 54 | public: 55 | void run() { 56 | @@ -69,6 +81,11 @@ private: 57 | vk::Queue graphicsQueue; 58 | vk::Queue presentQueue; 59 | 60 | + vk::SwapchainKHR swapChain; 61 | + std::vector swapChainImages; 62 | + vk::Format swapChainImageFormat; 63 | + vk::Extent2D swapChainExtent; 64 | + 65 | void initWindow() { 66 | glfwInit(); 67 | 68 | @@ -83,6 +100,7 @@ private: 69 | createSurface(); 70 | pickPhysicalDevice(); 71 | createLogicalDevice(); 72 | + createSwapChain(); 73 | } 74 | 75 | void mainLoop() { 76 | @@ -94,6 +112,9 @@ private: 77 | void cleanup() { 78 | // NOTE: instance destruction is handled by UniqueInstance, same for device 79 | 80 | + // not using UniqeSwapchain to destroy in correct order - before the surface 81 | + device->destroySwapchainKHR(swapChain); 82 | + 83 | // surface is created by glfw, therefore not using a Unique handle 84 | instance->destroySurfaceKHR(surface); 85 | 86 | @@ -118,7 +139,7 @@ private: 87 | VK_MAKE_VERSION(1, 0, 0), 88 | VK_API_VERSION_1_0 89 | ); 90 | - 91 | + 92 | auto extensions = getRequiredExtensions(); 93 | 94 | auto createInfo = vk::InstanceCreateInfo( 95 | @@ -167,6 +188,7 @@ private: 96 | if (glfwCreateWindowSurface(*instance, window, nullptr, &rawSurface) != VK_SUCCESS) { 97 | throw std::runtime_error("failed to create window surface!"); 98 | } 99 | + 100 | surface = rawSurface; 101 | } 102 | 103 | @@ -202,17 +224,18 @@ private: 104 | queueFamily, 105 | 1, // queueCount 106 | &queuePriority 107 | - }); 108 | + }); 109 | } 110 | 111 | auto deviceFeatures = vk::PhysicalDeviceFeatures(); 112 | auto createInfo = vk::DeviceCreateInfo( 113 | vk::DeviceCreateFlags(), 114 | - static_cast(queueCreateInfos.size()), 115 | + static_cast(queueCreateInfos.size()), 116 | queueCreateInfos.data() 117 | ); 118 | createInfo.pEnabledFeatures = &deviceFeatures; 119 | - createInfo.enabledExtensionCount = 0; 120 | + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); 121 | + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); 122 | 123 | if (enableValidationLayers) { 124 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 125 | @@ -221,7 +244,8 @@ private: 126 | 127 | try { 128 | device = physicalDevice.createDeviceUnique(createInfo); 129 | - } catch (vk::SystemError err) { 130 | + } 131 | + catch (vk::SystemError err) { 132 | throw std::runtime_error("failed to create logical device!"); 133 | } 134 | 135 | @@ -229,10 +253,135 @@ private: 136 | presentQueue = device->getQueue(indices.presentFamily.value(), 0); 137 | } 138 | 139 | + void createSwapChain() { 140 | + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); 141 | + 142 | + vk::SurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); 143 | + vk::PresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); 144 | + vk::Extent2D extent = chooseSwapExtent(swapChainSupport.capabilities); 145 | + 146 | + uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; 147 | + if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { 148 | + imageCount = swapChainSupport.capabilities.maxImageCount; 149 | + } 150 | + 151 | + vk::SwapchainCreateInfoKHR createInfo( 152 | + vk::SwapchainCreateFlagsKHR(), 153 | + surface, 154 | + imageCount, 155 | + surfaceFormat.format, 156 | + surfaceFormat.colorSpace, 157 | + extent, 158 | + 1, // imageArrayLayers 159 | + vk::ImageUsageFlagBits::eColorAttachment 160 | + ); 161 | + 162 | + QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 163 | + uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() }; 164 | + 165 | + if (indices.graphicsFamily != indices.presentFamily) { 166 | + createInfo.imageSharingMode = vk::SharingMode::eConcurrent; 167 | + createInfo.queueFamilyIndexCount = 2; 168 | + createInfo.pQueueFamilyIndices = queueFamilyIndices; 169 | + } 170 | + else { 171 | + createInfo.imageSharingMode = vk::SharingMode::eExclusive; 172 | + } 173 | + 174 | + createInfo.preTransform = swapChainSupport.capabilities.currentTransform; 175 | + createInfo.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque; 176 | + createInfo.presentMode = presentMode; 177 | + createInfo.clipped = VK_TRUE; 178 | + 179 | + createInfo.oldSwapchain = vk::SwapchainKHR(nullptr); 180 | + 181 | + try { 182 | + swapChain = device->createSwapchainKHR(createInfo); 183 | + } 184 | + catch (vk::SystemError err) { 185 | + throw std::runtime_error("failed to create swap chain!"); 186 | + } 187 | + 188 | + swapChainImages = device->getSwapchainImagesKHR(swapChain); 189 | + 190 | + swapChainImageFormat = surfaceFormat.format; 191 | + swapChainExtent = extent; 192 | + } 193 | + 194 | + vk::SurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { 195 | + if (availableFormats.size() == 1 && availableFormats[0].format == vk::Format::eUndefined) { 196 | + return { vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear }; 197 | + } 198 | + 199 | + for (const auto& availableFormat : availableFormats) { 200 | + if (availableFormat.format == vk::Format::eB8G8R8A8Unorm && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { 201 | + return availableFormat; 202 | + } 203 | + } 204 | + 205 | + return availableFormats[0]; 206 | + } 207 | + 208 | + vk::PresentModeKHR chooseSwapPresentMode(const std::vector availablePresentModes) { 209 | + vk::PresentModeKHR bestMode = vk::PresentModeKHR::eFifo; 210 | + 211 | + for (const auto& availablePresentMode : availablePresentModes) { 212 | + if (availablePresentMode == vk::PresentModeKHR::eMailbox) { 213 | + return availablePresentMode; 214 | + } 215 | + else if (availablePresentMode == vk::PresentModeKHR::eImmediate) { 216 | + bestMode = availablePresentMode; 217 | + } 218 | + } 219 | + 220 | + return bestMode; 221 | + } 222 | + 223 | + vk::Extent2D chooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities) { 224 | + if (capabilities.currentExtent.width != std::numeric_limits::max()) { 225 | + return capabilities.currentExtent; 226 | + } 227 | + else { 228 | + vk::Extent2D actualExtent = { static_cast(WIDTH), static_cast(HEIGHT) }; 229 | + 230 | + actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); 231 | + actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); 232 | + 233 | + return actualExtent; 234 | + } 235 | + } 236 | + 237 | + SwapChainSupportDetails querySwapChainSupport(const vk::PhysicalDevice& device) { 238 | + SwapChainSupportDetails details; 239 | + details.capabilities = device.getSurfaceCapabilitiesKHR(surface); 240 | + details.formats = device.getSurfaceFormatsKHR(surface); 241 | + details.presentModes = device.getSurfacePresentModesKHR(surface); 242 | + 243 | + return details; 244 | + } 245 | + 246 | bool isDeviceSuitable(const vk::PhysicalDevice& device) { 247 | QueueFamilyIndices indices = findQueueFamilies(device); 248 | 249 | - return indices.isComplete(); 250 | + bool extensionsSupported = checkDeviceExtensionSupport(device); 251 | + 252 | + bool swapChainAdequate = false; 253 | + if (extensionsSupported) { 254 | + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); 255 | + swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); 256 | + } 257 | + 258 | + return indices.isComplete() && extensionsSupported && swapChainAdequate; 259 | + } 260 | + 261 | + bool checkDeviceExtensionSupport(const vk::PhysicalDevice& device) { 262 | + std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); 263 | + 264 | + for (const auto& extension : device.enumerateDeviceExtensionProperties()) { 265 | + requiredExtensions.erase(extension.extensionName); 266 | + } 267 | + 268 | + return requiredExtensions.empty(); 269 | } 270 | 271 | QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice device) { 272 | @@ -306,7 +455,8 @@ int main() { 273 | 274 | try { 275 | app.run(); 276 | - } catch (const std::exception& e) { 277 | + } 278 | + catch (const std::exception& e) { 279 | std::cerr << e.what() << std::endl; 280 | return EXIT_FAILURE; 281 | } 282 | -------------------------------------------------------------------------------- /steps/07_image_views.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | const int WIDTH = 800; 14 | const int HEIGHT = 600; 15 | 16 | const std::vector validationLayers = { 17 | "VK_LAYER_LUNARG_standard_validation" 18 | }; 19 | 20 | const std::vector deviceExtensions = { 21 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 22 | }; 23 | 24 | #ifdef NDEBUG 25 | const bool enableValidationLayers = false; 26 | #else 27 | const bool enableValidationLayers = true; 28 | #endif 29 | 30 | VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pCallback) { 31 | auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); 32 | if (func != nullptr) { 33 | return func(instance, pCreateInfo, pAllocator, pCallback); 34 | } 35 | else { 36 | return VK_ERROR_EXTENSION_NOT_PRESENT; 37 | } 38 | } 39 | 40 | void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT callback, const VkAllocationCallbacks* pAllocator) { 41 | auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); 42 | if (func != nullptr) { 43 | func(instance, callback, pAllocator); 44 | } 45 | } 46 | 47 | struct QueueFamilyIndices { 48 | std::optional graphicsFamily; 49 | std::optional presentFamily; 50 | 51 | bool isComplete() { 52 | return graphicsFamily.has_value() && presentFamily.has_value(); 53 | } 54 | }; 55 | 56 | struct SwapChainSupportDetails { 57 | vk::SurfaceCapabilitiesKHR capabilities; 58 | std::vector formats; 59 | std::vector presentModes; 60 | }; 61 | 62 | class HelloTriangleApplication { 63 | public: 64 | void run() { 65 | initWindow(); 66 | initVulkan(); 67 | mainLoop(); 68 | cleanup(); 69 | } 70 | 71 | private: 72 | GLFWwindow* window; 73 | 74 | vk::UniqueInstance instance; 75 | VkDebugUtilsMessengerEXT callback; 76 | vk::SurfaceKHR surface; 77 | 78 | vk::PhysicalDevice physicalDevice; 79 | vk::UniqueDevice device; 80 | 81 | vk::Queue graphicsQueue; 82 | vk::Queue presentQueue; 83 | 84 | vk::SwapchainKHR swapChain; 85 | std::vector swapChainImages; 86 | vk::Format swapChainImageFormat; 87 | vk::Extent2D swapChainExtent; 88 | std::vector swapChainImageViews; 89 | 90 | void initWindow() { 91 | glfwInit(); 92 | 93 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 94 | glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 95 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 96 | } 97 | 98 | void initVulkan() { 99 | createInstance(); 100 | setupDebugCallback(); 101 | createSurface(); 102 | pickPhysicalDevice(); 103 | createLogicalDevice(); 104 | createSwapChain(); 105 | createImageViews(); 106 | } 107 | 108 | void mainLoop() { 109 | while (!glfwWindowShouldClose(window)) { 110 | glfwPollEvents(); 111 | } 112 | } 113 | 114 | void cleanup() { 115 | // NOTE: instance destruction is handled by UniqueInstance, same for device 116 | 117 | for (auto imageView : swapChainImageViews) { 118 | device->destroyImageView(imageView); 119 | } 120 | 121 | // not using UniqeSwapchain to destroy in correct order - before the surface 122 | device->destroySwapchainKHR(swapChain); 123 | 124 | // surface is created by glfw, therefore not using a Unique handle 125 | instance->destroySurfaceKHR(surface); 126 | 127 | if (enableValidationLayers) { 128 | DestroyDebugUtilsMessengerEXT(*instance, callback, nullptr); 129 | } 130 | 131 | glfwDestroyWindow(window); 132 | 133 | glfwTerminate(); 134 | } 135 | 136 | void createInstance() { 137 | if (enableValidationLayers && !checkValidationLayerSupport()) { 138 | throw std::runtime_error("validation layers requested, but not available!"); 139 | } 140 | 141 | auto appInfo = vk::ApplicationInfo( 142 | "Hello Triangle", 143 | VK_MAKE_VERSION(1, 0, 0), 144 | "No Engine", 145 | VK_MAKE_VERSION(1, 0, 0), 146 | VK_API_VERSION_1_0 147 | ); 148 | 149 | auto extensions = getRequiredExtensions(); 150 | 151 | auto createInfo = vk::InstanceCreateInfo( 152 | vk::InstanceCreateFlags(), 153 | &appInfo, 154 | 0, nullptr, // enabled layers 155 | static_cast(extensions.size()), extensions.data() // enabled extensions 156 | ); 157 | 158 | if (enableValidationLayers) { 159 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 160 | createInfo.ppEnabledLayerNames = validationLayers.data(); 161 | } 162 | 163 | try { 164 | instance = vk::createInstanceUnique(createInfo, nullptr); 165 | } 166 | catch (vk::SystemError err) { 167 | throw std::runtime_error("failed to create instance!"); 168 | } 169 | } 170 | 171 | void setupDebugCallback() { 172 | if (!enableValidationLayers) return; 173 | 174 | auto createInfo = vk::DebugUtilsMessengerCreateInfoEXT( 175 | vk::DebugUtilsMessengerCreateFlagsEXT(), 176 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, 177 | vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 178 | debugCallback, 179 | nullptr 180 | ); 181 | 182 | // NOTE: Vulkan-hpp has methods for this, but they trigger linking errors... 183 | //instance->createDebugUtilsMessengerEXT(createInfo); 184 | //instance->createDebugUtilsMessengerEXTUnique(createInfo); 185 | 186 | // NOTE: reinterpret_cast is also used by vulkan.hpp internally for all these structs 187 | if (CreateDebugUtilsMessengerEXT(*instance, reinterpret_cast(&createInfo), nullptr, &callback) != VK_SUCCESS) { 188 | throw std::runtime_error("failed to set up debug callback!"); 189 | } 190 | } 191 | 192 | void createSurface() { 193 | VkSurfaceKHR rawSurface; 194 | if (glfwCreateWindowSurface(*instance, window, nullptr, &rawSurface) != VK_SUCCESS) { 195 | throw std::runtime_error("failed to create window surface!"); 196 | } 197 | 198 | surface = rawSurface; 199 | } 200 | 201 | void pickPhysicalDevice() { 202 | auto devices = instance->enumeratePhysicalDevices(); 203 | if (devices.size() == 0) { 204 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 205 | } 206 | 207 | for (const auto& device : devices) { 208 | if (isDeviceSuitable(device)) { 209 | physicalDevice = device; 210 | break; 211 | } 212 | } 213 | 214 | if (!physicalDevice) { 215 | throw std::runtime_error("failed to find a suitable GPU!"); 216 | } 217 | } 218 | 219 | void createLogicalDevice() { 220 | QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 221 | 222 | std::vector queueCreateInfos; 223 | std::set uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() }; 224 | 225 | float queuePriority = 1.0f; 226 | 227 | for (uint32_t queueFamily : uniqueQueueFamilies) { 228 | queueCreateInfos.push_back({ 229 | vk::DeviceQueueCreateFlags(), 230 | queueFamily, 231 | 1, // queueCount 232 | &queuePriority 233 | }); 234 | } 235 | 236 | auto deviceFeatures = vk::PhysicalDeviceFeatures(); 237 | auto createInfo = vk::DeviceCreateInfo( 238 | vk::DeviceCreateFlags(), 239 | static_cast(queueCreateInfos.size()), 240 | queueCreateInfos.data() 241 | ); 242 | createInfo.pEnabledFeatures = &deviceFeatures; 243 | createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); 244 | createInfo.ppEnabledExtensionNames = deviceExtensions.data(); 245 | 246 | if (enableValidationLayers) { 247 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 248 | createInfo.ppEnabledLayerNames = validationLayers.data(); 249 | } 250 | 251 | try { 252 | device = physicalDevice.createDeviceUnique(createInfo); 253 | } 254 | catch (vk::SystemError err) { 255 | throw std::runtime_error("failed to create logical device!"); 256 | } 257 | 258 | graphicsQueue = device->getQueue(indices.graphicsFamily.value(), 0); 259 | presentQueue = device->getQueue(indices.presentFamily.value(), 0); 260 | } 261 | 262 | void createSwapChain() { 263 | SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice); 264 | 265 | vk::SurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); 266 | vk::PresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes); 267 | vk::Extent2D extent = chooseSwapExtent(swapChainSupport.capabilities); 268 | 269 | uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; 270 | if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) { 271 | imageCount = swapChainSupport.capabilities.maxImageCount; 272 | } 273 | 274 | vk::SwapchainCreateInfoKHR createInfo( 275 | vk::SwapchainCreateFlagsKHR(), 276 | surface, 277 | imageCount, 278 | surfaceFormat.format, 279 | surfaceFormat.colorSpace, 280 | extent, 281 | 1, // imageArrayLayers 282 | vk::ImageUsageFlagBits::eColorAttachment 283 | ); 284 | 285 | QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 286 | uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() }; 287 | 288 | if (indices.graphicsFamily != indices.presentFamily) { 289 | createInfo.imageSharingMode = vk::SharingMode::eConcurrent; 290 | createInfo.queueFamilyIndexCount = 2; 291 | createInfo.pQueueFamilyIndices = queueFamilyIndices; 292 | } 293 | else { 294 | createInfo.imageSharingMode = vk::SharingMode::eExclusive; 295 | } 296 | 297 | createInfo.preTransform = swapChainSupport.capabilities.currentTransform; 298 | createInfo.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque; 299 | createInfo.presentMode = presentMode; 300 | createInfo.clipped = VK_TRUE; 301 | 302 | createInfo.oldSwapchain = vk::SwapchainKHR(nullptr); 303 | 304 | try { 305 | swapChain = device->createSwapchainKHR(createInfo); 306 | } 307 | catch (vk::SystemError err) { 308 | throw std::runtime_error("failed to create swap chain!"); 309 | } 310 | 311 | swapChainImages = device->getSwapchainImagesKHR(swapChain); 312 | 313 | swapChainImageFormat = surfaceFormat.format; 314 | swapChainExtent = extent; 315 | } 316 | 317 | void createImageViews() { 318 | swapChainImageViews.resize(swapChainImages.size()); 319 | 320 | for (size_t i = 0; i < swapChainImages.size(); i++) { 321 | vk::ImageViewCreateInfo createInfo = {}; 322 | createInfo.image = swapChainImages[i]; 323 | createInfo.viewType = vk::ImageViewType::e2D; 324 | createInfo.format = swapChainImageFormat; 325 | createInfo.components.r = vk::ComponentSwizzle::eIdentity; 326 | createInfo.components.g = vk::ComponentSwizzle::eIdentity; 327 | createInfo.components.b = vk::ComponentSwizzle::eIdentity; 328 | createInfo.components.a = vk::ComponentSwizzle::eIdentity; 329 | createInfo.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor; 330 | createInfo.subresourceRange.baseMipLevel = 0; 331 | createInfo.subresourceRange.levelCount = 1; 332 | createInfo.subresourceRange.baseArrayLayer = 0; 333 | createInfo.subresourceRange.layerCount = 1; 334 | 335 | try { 336 | swapChainImageViews[i] = device->createImageView(createInfo); 337 | } 338 | catch (vk::SystemError err) { 339 | throw std::runtime_error("failed to create image views!"); 340 | } 341 | } 342 | } 343 | 344 | vk::SurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { 345 | if (availableFormats.size() == 1 && availableFormats[0].format == vk::Format::eUndefined) { 346 | return { vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear }; 347 | } 348 | 349 | for (const auto& availableFormat : availableFormats) { 350 | if (availableFormat.format == vk::Format::eB8G8R8A8Unorm && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { 351 | return availableFormat; 352 | } 353 | } 354 | 355 | return availableFormats[0]; 356 | } 357 | 358 | vk::PresentModeKHR chooseSwapPresentMode(const std::vector availablePresentModes) { 359 | vk::PresentModeKHR bestMode = vk::PresentModeKHR::eFifo; 360 | 361 | for (const auto& availablePresentMode : availablePresentModes) { 362 | if (availablePresentMode == vk::PresentModeKHR::eMailbox) { 363 | return availablePresentMode; 364 | } 365 | else if (availablePresentMode == vk::PresentModeKHR::eImmediate) { 366 | bestMode = availablePresentMode; 367 | } 368 | } 369 | 370 | return bestMode; 371 | } 372 | 373 | vk::Extent2D chooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities) { 374 | if (capabilities.currentExtent.width != std::numeric_limits::max()) { 375 | return capabilities.currentExtent; 376 | } 377 | else { 378 | vk::Extent2D actualExtent = { static_cast(WIDTH), static_cast(HEIGHT) }; 379 | 380 | actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); 381 | actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); 382 | 383 | return actualExtent; 384 | } 385 | } 386 | 387 | SwapChainSupportDetails querySwapChainSupport(const vk::PhysicalDevice& device) { 388 | SwapChainSupportDetails details; 389 | details.capabilities = device.getSurfaceCapabilitiesKHR(surface); 390 | details.formats = device.getSurfaceFormatsKHR(surface); 391 | details.presentModes = device.getSurfacePresentModesKHR(surface); 392 | 393 | return details; 394 | } 395 | 396 | bool isDeviceSuitable(const vk::PhysicalDevice& device) { 397 | QueueFamilyIndices indices = findQueueFamilies(device); 398 | 399 | bool extensionsSupported = checkDeviceExtensionSupport(device); 400 | 401 | bool swapChainAdequate = false; 402 | if (extensionsSupported) { 403 | SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); 404 | swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); 405 | } 406 | 407 | return indices.isComplete() && extensionsSupported && swapChainAdequate; 408 | } 409 | 410 | bool checkDeviceExtensionSupport(const vk::PhysicalDevice& device) { 411 | std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); 412 | 413 | for (const auto& extension : device.enumerateDeviceExtensionProperties()) { 414 | requiredExtensions.erase(extension.extensionName); 415 | } 416 | 417 | return requiredExtensions.empty(); 418 | } 419 | 420 | QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice device) { 421 | QueueFamilyIndices indices; 422 | 423 | auto queueFamilies = device.getQueueFamilyProperties(); 424 | 425 | int i = 0; 426 | for (const auto& queueFamily : queueFamilies) { 427 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & vk::QueueFlagBits::eGraphics) { 428 | indices.graphicsFamily = i; 429 | } 430 | 431 | if (queueFamily.queueCount > 0 && device.getSurfaceSupportKHR(i, surface)) { 432 | indices.presentFamily = i; 433 | } 434 | 435 | if (indices.isComplete()) { 436 | break; 437 | } 438 | 439 | i++; 440 | } 441 | 442 | return indices; 443 | } 444 | 445 | std::vector getRequiredExtensions() { 446 | uint32_t glfwExtensionCount = 0; 447 | const char** glfwExtensions; 448 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 449 | 450 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 451 | 452 | if (enableValidationLayers) { 453 | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 454 | } 455 | 456 | return extensions; 457 | } 458 | 459 | bool checkValidationLayerSupport() { 460 | auto availableLayers = vk::enumerateInstanceLayerProperties(); 461 | for (const char* layerName : validationLayers) { 462 | bool layerFound = false; 463 | 464 | for (const auto& layerProperties : availableLayers) { 465 | if (strcmp(layerName, layerProperties.layerName) == 0) { 466 | layerFound = true; 467 | break; 468 | } 469 | } 470 | 471 | if (!layerFound) { 472 | return false; 473 | } 474 | } 475 | 476 | return true; 477 | } 478 | 479 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { 480 | std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; 481 | 482 | return VK_FALSE; 483 | } 484 | }; 485 | 486 | int main() { 487 | HelloTriangleApplication app; 488 | 489 | try { 490 | app.run(); 491 | } 492 | catch (const std::exception& e) { 493 | std::cerr << e.what() << std::endl; 494 | return EXIT_FAILURE; 495 | } 496 | 497 | return EXIT_SUCCESS; 498 | } 499 | -------------------------------------------------------------------------------- /steps/07_image_views.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/06_swap_chain_creation.cpp 2 | +++ b/07_image_views.cpp 3 | @@ -85,6 +85,7 @@ private: 4 | std::vector swapChainImages; 5 | vk::Format swapChainImageFormat; 6 | vk::Extent2D swapChainExtent; 7 | + std::vector swapChainImageViews; 8 | 9 | void initWindow() { 10 | glfwInit(); 11 | @@ -101,6 +102,7 @@ private: 12 | pickPhysicalDevice(); 13 | createLogicalDevice(); 14 | createSwapChain(); 15 | + createImageViews(); 16 | } 17 | 18 | void mainLoop() { 19 | @@ -112,6 +114,10 @@ private: 20 | void cleanup() { 21 | // NOTE: instance destruction is handled by UniqueInstance, same for device 22 | 23 | + for (auto imageView : swapChainImageViews) { 24 | + device->destroyImageView(imageView); 25 | + } 26 | + 27 | // not using UniqeSwapchain to destroy in correct order - before the surface 28 | device->destroySwapchainKHR(swapChain); 29 | 30 | @@ -308,6 +314,33 @@ private: 31 | swapChainExtent = extent; 32 | } 33 | 34 | + void createImageViews() { 35 | + swapChainImageViews.resize(swapChainImages.size()); 36 | + 37 | + for (size_t i = 0; i < swapChainImages.size(); i++) { 38 | + vk::ImageViewCreateInfo createInfo = {}; 39 | + createInfo.image = swapChainImages[i]; 40 | + createInfo.viewType = vk::ImageViewType::e2D; 41 | + createInfo.format = swapChainImageFormat; 42 | + createInfo.components.r = vk::ComponentSwizzle::eIdentity; 43 | + createInfo.components.g = vk::ComponentSwizzle::eIdentity; 44 | + createInfo.components.b = vk::ComponentSwizzle::eIdentity; 45 | + createInfo.components.a = vk::ComponentSwizzle::eIdentity; 46 | + createInfo.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor; 47 | + createInfo.subresourceRange.baseMipLevel = 0; 48 | + createInfo.subresourceRange.levelCount = 1; 49 | + createInfo.subresourceRange.baseArrayLayer = 0; 50 | + createInfo.subresourceRange.layerCount = 1; 51 | + 52 | + try { 53 | + swapChainImageViews[i] = device->createImageView(createInfo); 54 | + } 55 | + catch (vk::SystemError err) { 56 | + throw std::runtime_error("failed to create image views!"); 57 | + } 58 | + } 59 | + } 60 | + 61 | vk::SurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { 62 | if (availableFormats.size() == 1 && availableFormats[0].format == vk::Format::eUndefined) { 63 | return { vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear }; 64 | -------------------------------------------------------------------------------- /steps/08_graphics_pipeline.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/07_image_views.cpp 2 | +++ b/08_graphics_pipeline.cpp 3 | @@ -103,6 +103,7 @@ private: 4 | createLogicalDevice(); 5 | createSwapChain(); 6 | createImageViews(); 7 | + createGraphicsPipeline(); 8 | } 9 | 10 | void mainLoop() { 11 | @@ -341,6 +342,10 @@ private: 12 | } 13 | } 14 | 15 | + void createGraphicsPipeline() { 16 | + 17 | + } 18 | + 19 | vk::SurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { 20 | if (availableFormats.size() == 1 && availableFormats[0].format == vk::Format::eUndefined) { 21 | return { vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear }; 22 | -------------------------------------------------------------------------------- /steps/09_shader_modules.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/08_graphics_pipeline.cpp 2 | +++ b/09_shader_modules.cpp 3 | @@ -2,6 +2,7 @@ 4 | #include 5 | 6 | #include 7 | +#include 8 | #include 9 | #include 10 | #include 11 | @@ -343,7 +344,38 @@ private: 12 | } 13 | 14 | void createGraphicsPipeline() { 15 | - 16 | + auto vertShaderCode = readFile("shaders/vert.spv"); 17 | + auto fragShaderCode = readFile("shaders/frag.spv"); 18 | + 19 | + auto vertShaderModule = createShaderModule(vertShaderCode); 20 | + auto fragShaderModule = createShaderModule(fragShaderCode); 21 | + 22 | + vk::PipelineShaderStageCreateInfo shaderStages[] = { 23 | + { 24 | + vk::PipelineShaderStageCreateFlags(), 25 | + vk::ShaderStageFlagBits::eVertex, 26 | + *vertShaderModule, 27 | + "main" 28 | + }, 29 | + { 30 | + vk::PipelineShaderStageCreateFlags(), 31 | + vk::ShaderStageFlagBits::eFragment, 32 | + *fragShaderModule, 33 | + "main" 34 | + } 35 | + }; 36 | + } 37 | + 38 | + vk::UniqueShaderModule createShaderModule(const std::vector& code) { 39 | + try { 40 | + return device->createShaderModuleUnique({ 41 | + vk::ShaderModuleCreateFlags(), 42 | + code.size(), 43 | + reinterpret_cast(code.data()) 44 | + }); 45 | + } catch (vk::SystemError err) { 46 | + throw std::runtime_error("failed to create shader module!"); 47 | + } 48 | } 49 | 50 | vk::SurfaceFormatKHR chooseSwapSurfaceFormat(const std::vector& availableFormats) { 51 | @@ -481,6 +513,24 @@ private: 52 | return true; 53 | } 54 | 55 | + static std::vector readFile(const std::string& filename) { 56 | + std::ifstream file(filename, std::ios::ate | std::ios::binary); 57 | + 58 | + if (!file.is_open()) { 59 | + throw std::runtime_error("failed to open file!"); 60 | + } 61 | + 62 | + size_t fileSize = (size_t)file.tellg(); 63 | + std::vector buffer(fileSize); 64 | + 65 | + file.seekg(0); 66 | + file.read(buffer.data(), fileSize); 67 | + 68 | + file.close(); 69 | + 70 | + return buffer; 71 | + } 72 | + 73 | static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { 74 | std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; 75 | 76 | -------------------------------------------------------------------------------- /steps/11_render_passes.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/09_shader_modules.cpp 2 | +++ b/11_render_passes.cpp 3 | @@ -88,6 +88,9 @@ private: 4 | vk::Extent2D swapChainExtent; 5 | std::vector swapChainImageViews; 6 | 7 | + vk::RenderPass renderPass; 8 | + vk::PipelineLayout pipelineLayout; 9 | + 10 | void initWindow() { 11 | glfwInit(); 12 | 13 | @@ -104,6 +107,7 @@ private: 14 | createLogicalDevice(); 15 | createSwapChain(); 16 | createImageViews(); 17 | + createRenderPass(); 18 | createGraphicsPipeline(); 19 | } 20 | 21 | @@ -116,6 +120,9 @@ private: 22 | void cleanup() { 23 | // NOTE: instance destruction is handled by UniqueInstance, same for device 24 | 25 | + device->destroyPipelineLayout(pipelineLayout); 26 | + device->destroyRenderPass(renderPass); 27 | + 28 | for (auto imageView : swapChainImageViews) { 29 | device->destroyImageView(imageView); 30 | } 31 | @@ -343,6 +350,39 @@ private: 32 | } 33 | } 34 | 35 | + void createRenderPass() { 36 | + vk::AttachmentDescription colorAttachment = {}; 37 | + colorAttachment.format = swapChainImageFormat; 38 | + colorAttachment.samples = vk::SampleCountFlagBits::e1; 39 | + colorAttachment.loadOp = vk::AttachmentLoadOp::eClear; 40 | + colorAttachment.storeOp = vk::AttachmentStoreOp::eStore; 41 | + colorAttachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare; 42 | + colorAttachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare; 43 | + colorAttachment.initialLayout = vk::ImageLayout::eUndefined; 44 | + colorAttachment.finalLayout = vk::ImageLayout::ePresentSrcKHR; 45 | + 46 | + vk::AttachmentReference colorAttachmentRef = {}; 47 | + colorAttachmentRef.attachment = 0; 48 | + colorAttachmentRef.layout = vk::ImageLayout::eColorAttachmentOptimal; 49 | + 50 | + vk::SubpassDescription subpass = {}; 51 | + subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics; 52 | + subpass.colorAttachmentCount = 1; 53 | + subpass.pColorAttachments = &colorAttachmentRef; 54 | + 55 | + vk::RenderPassCreateInfo renderPassInfo = {}; 56 | + renderPassInfo.attachmentCount = 1; 57 | + renderPassInfo.pAttachments = &colorAttachment; 58 | + renderPassInfo.subpassCount = 1; 59 | + renderPassInfo.pSubpasses = &subpass; 60 | + 61 | + try { 62 | + renderPass = device->createRenderPass(renderPassInfo); 63 | + } catch (vk::SystemError err) { 64 | + throw std::runtime_error("failed to create render pass!"); 65 | + } 66 | + } 67 | + 68 | void createGraphicsPipeline() { 69 | auto vertShaderCode = readFile("shaders/vert.spv"); 70 | auto fragShaderCode = readFile("shaders/frag.spv"); 71 | @@ -364,6 +404,69 @@ private: 72 | "main" 73 | } 74 | }; 75 | + 76 | + vk::PipelineVertexInputStateCreateInfo vertexInputInfo = {}; 77 | + vertexInputInfo.vertexBindingDescriptionCount = 0; 78 | + vertexInputInfo.vertexAttributeDescriptionCount = 0; 79 | + 80 | + vk::PipelineInputAssemblyStateCreateInfo inputAssembly = {}; 81 | + inputAssembly.topology = vk::PrimitiveTopology::eTriangleList; 82 | + inputAssembly.primitiveRestartEnable = VK_FALSE; 83 | + 84 | + vk::Viewport viewport = {}; 85 | + viewport.x = 0.0f; 86 | + viewport.y = 0.0f; 87 | + viewport.width = (float)swapChainExtent.width; 88 | + viewport.height = (float)swapChainExtent.height; 89 | + viewport.minDepth = 0.0f; 90 | + viewport.maxDepth = 1.0f; 91 | + 92 | + vk::Rect2D scissor = {}; 93 | + scissor.offset = { 0, 0 }; 94 | + scissor.extent = swapChainExtent; 95 | + 96 | + vk::PipelineViewportStateCreateInfo viewportState = {}; 97 | + viewportState.viewportCount = 1; 98 | + viewportState.pViewports = &viewport; 99 | + viewportState.scissorCount = 1; 100 | + viewportState.pScissors = &scissor; 101 | + 102 | + vk::PipelineRasterizationStateCreateInfo rasterizer = {}; 103 | + rasterizer.depthClampEnable = VK_FALSE; 104 | + rasterizer.rasterizerDiscardEnable = VK_FALSE; 105 | + rasterizer.polygonMode = vk::PolygonMode::eFill; 106 | + rasterizer.lineWidth = 1.0f; 107 | + rasterizer.cullMode = vk::CullModeFlagBits::eBack; 108 | + rasterizer.frontFace = vk::FrontFace::eClockwise; 109 | + rasterizer.depthBiasEnable = VK_FALSE; 110 | + 111 | + vk::PipelineMultisampleStateCreateInfo multisampling = {}; 112 | + multisampling.sampleShadingEnable = VK_FALSE; 113 | + multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1; 114 | + 115 | + vk::PipelineColorBlendAttachmentState colorBlendAttachment = {}; 116 | + colorBlendAttachment.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA; 117 | + colorBlendAttachment.blendEnable = VK_FALSE; 118 | + 119 | + vk::PipelineColorBlendStateCreateInfo colorBlending = {}; 120 | + colorBlending.logicOpEnable = VK_FALSE; 121 | + colorBlending.logicOp = vk::LogicOp::eCopy; 122 | + colorBlending.attachmentCount = 1; 123 | + colorBlending.pAttachments = &colorBlendAttachment; 124 | + colorBlending.blendConstants[0] = 0.0f; 125 | + colorBlending.blendConstants[1] = 0.0f; 126 | + colorBlending.blendConstants[2] = 0.0f; 127 | + colorBlending.blendConstants[3] = 0.0f; 128 | + 129 | + vk::PipelineLayoutCreateInfo pipelineLayoutInfo = {}; 130 | + pipelineLayoutInfo.setLayoutCount = 0; 131 | + pipelineLayoutInfo.pushConstantRangeCount = 0; 132 | + 133 | + try { 134 | + pipelineLayout = device->createPipelineLayout(pipelineLayoutInfo); 135 | + } catch (vk::SystemError err) { 136 | + throw std::runtime_error("failed to create pipeline layout!"); 137 | + } 138 | } 139 | 140 | vk::UniqueShaderModule createShaderModule(const std::vector& code) { 141 | -------------------------------------------------------------------------------- /steps/12_graphics_pipeline_complete.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/11_render_passes.cpp 2 | +++ b/12_graphics_pipeline_complete.cpp 3 | @@ -90,6 +90,7 @@ private: 4 | 5 | vk::RenderPass renderPass; 6 | vk::PipelineLayout pipelineLayout; 7 | + vk::Pipeline graphicsPipeline; 8 | 9 | void initWindow() { 10 | glfwInit(); 11 | @@ -120,6 +121,7 @@ private: 12 | void cleanup() { 13 | // NOTE: instance destruction is handled by UniqueInstance, same for device 14 | 15 | + device->destroyPipeline(graphicsPipeline); 16 | device->destroyPipelineLayout(pipelineLayout); 17 | device->destroyRenderPass(renderPass); 18 | 19 | @@ -467,6 +469,27 @@ private: 20 | } catch (vk::SystemError err) { 21 | throw std::runtime_error("failed to create pipeline layout!"); 22 | } 23 | + 24 | + vk::GraphicsPipelineCreateInfo pipelineInfo = {}; 25 | + pipelineInfo.stageCount = 2; 26 | + pipelineInfo.pStages = shaderStages; 27 | + pipelineInfo.pVertexInputState = &vertexInputInfo; 28 | + pipelineInfo.pInputAssemblyState = &inputAssembly; 29 | + pipelineInfo.pViewportState = &viewportState; 30 | + pipelineInfo.pRasterizationState = &rasterizer; 31 | + pipelineInfo.pMultisampleState = &multisampling; 32 | + pipelineInfo.pColorBlendState = &colorBlending; 33 | + pipelineInfo.layout = pipelineLayout; 34 | + pipelineInfo.renderPass = renderPass; 35 | + pipelineInfo.subpass = 0; 36 | + pipelineInfo.basePipelineHandle = nullptr; 37 | + 38 | + try { 39 | + graphicsPipeline = device->createGraphicsPipeline(nullptr, pipelineInfo); 40 | + } 41 | + catch (vk::SystemError err) { 42 | + throw std::runtime_error("failed to create graphics pipeline!"); 43 | + } 44 | } 45 | 46 | vk::UniqueShaderModule createShaderModule(const std::vector& code) { 47 | -------------------------------------------------------------------------------- /steps/13_framebuffers.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/12_graphics_pipeline_complete.cpp 2 | +++ b/13_framebuffers.cpp 3 | @@ -87,6 +87,7 @@ private: 4 | vk::Format swapChainImageFormat; 5 | vk::Extent2D swapChainExtent; 6 | std::vector swapChainImageViews; 7 | + std::vector swapChainFramebuffers; 8 | 9 | vk::RenderPass renderPass; 10 | vk::PipelineLayout pipelineLayout; 11 | @@ -110,6 +111,7 @@ private: 12 | createImageViews(); 13 | createRenderPass(); 14 | createGraphicsPipeline(); 15 | + createFramebuffers(); 16 | } 17 | 18 | void mainLoop() { 19 | @@ -121,6 +123,10 @@ private: 20 | void cleanup() { 21 | // NOTE: instance destruction is handled by UniqueInstance, same for device 22 | 23 | + for (auto framebuffer : swapChainFramebuffers) { 24 | + device->destroyFramebuffer(framebuffer); 25 | + } 26 | + 27 | device->destroyPipeline(graphicsPipeline); 28 | device->destroyPipelineLayout(pipelineLayout); 29 | device->destroyRenderPass(renderPass); 30 | @@ -492,6 +498,30 @@ private: 31 | } 32 | } 33 | 34 | + void createFramebuffers() { 35 | + swapChainFramebuffers.resize(swapChainImageViews.size()); 36 | + 37 | + for (size_t i = 0; i < swapChainImageViews.size(); i++) { 38 | + vk::ImageView attachments[] = { 39 | + swapChainImageViews[i] 40 | + }; 41 | + 42 | + vk::FramebufferCreateInfo framebufferInfo = {}; 43 | + framebufferInfo.renderPass = renderPass; 44 | + framebufferInfo.attachmentCount = 1; 45 | + framebufferInfo.pAttachments = attachments; 46 | + framebufferInfo.width = swapChainExtent.width; 47 | + framebufferInfo.height = swapChainExtent.height; 48 | + framebufferInfo.layers = 1; 49 | + 50 | + try { 51 | + swapChainFramebuffers[i] = device->createFramebuffer(framebufferInfo); 52 | + } catch (vk::SystemError err) { 53 | + throw std::runtime_error("failed to create framebuffer!"); 54 | + } 55 | + } 56 | + } 57 | + 58 | vk::UniqueShaderModule createShaderModule(const std::vector& code) { 59 | try { 60 | return device->createShaderModuleUnique({ 61 | -------------------------------------------------------------------------------- /steps/14_command_buffers.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/13_framebuffers.cpp 2 | +++ b/14_command_buffers.cpp 3 | @@ -93,6 +93,9 @@ private: 4 | vk::PipelineLayout pipelineLayout; 5 | vk::Pipeline graphicsPipeline; 6 | 7 | + VkCommandPool commandPool; 8 | + std::vector> commandBuffers; 9 | + 10 | void initWindow() { 11 | glfwInit(); 12 | 13 | @@ -112,6 +115,8 @@ private: 14 | createRenderPass(); 15 | createGraphicsPipeline(); 16 | createFramebuffers(); 17 | + createCommandPool(); 18 | + createCommandBuffers(); 19 | } 20 | 21 | void mainLoop() { 22 | @@ -123,6 +128,8 @@ private: 23 | void cleanup() { 24 | // NOTE: instance destruction is handled by UniqueInstance, same for device 25 | 26 | + device->destroyCommandPool(commandPool); 27 | + 28 | for (auto framebuffer : swapChainFramebuffers) { 29 | device->destroyFramebuffer(framebuffer); 30 | } 31 | @@ -521,6 +528,70 @@ private: 32 | } 33 | } 34 | } 35 | + void createCommandPool() { 36 | + QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); 37 | + 38 | + vk::CommandPoolCreateInfo poolInfo = {}; 39 | + poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); 40 | + 41 | + try { 42 | + commandPool = device->createCommandPool(poolInfo); 43 | + } 44 | + catch (vk::SystemError err) { 45 | + throw std::runtime_error("failed to create command pool!"); 46 | + } 47 | + } 48 | + 49 | + void createCommandBuffers() { 50 | + commandBuffers.resize(swapChainFramebuffers.size()); 51 | + 52 | + vk::CommandBufferAllocateInfo allocInfo = {}; 53 | + allocInfo.commandPool = commandPool; 54 | + allocInfo.level = vk::CommandBufferLevel::ePrimary; 55 | + allocInfo.commandBufferCount = (uint32_t)commandBuffers.size(); 56 | + 57 | + try { 58 | + commandBuffers = device->allocateCommandBuffers(allocInfo); 59 | + } catch (vk::SystemError err) { 60 | + throw std::runtime_error("failed to allocate command buffers!"); 61 | + } 62 | + 63 | + for (size_t i = 0; i < commandBuffers.size(); i++) { 64 | + vk::CommandBufferBeginInfo beginInfo = {}; 65 | + beginInfo.flags = vk::CommandBufferUsageFlagBits::eSimultaneousUse; 66 | + 67 | + try { 68 | + commandBuffers[i].begin(beginInfo); 69 | + } 70 | + catch (vk::SystemError err) { 71 | + throw std::runtime_error("failed to begin recording command buffer!"); 72 | + } 73 | + 74 | + vk::RenderPassBeginInfo renderPassInfo = {}; 75 | + renderPassInfo.renderPass = renderPass; 76 | + renderPassInfo.framebuffer = swapChainFramebuffers[i]; 77 | + renderPassInfo.renderArea.offset = { 0, 0 }; 78 | + renderPassInfo.renderArea.extent = swapChainExtent; 79 | + 80 | + vk::ClearValue clearColor = { std::array{ 0.0f, 0.0f, 0.0f, 1.0f } }; 81 | + renderPassInfo.clearValueCount = 1; 82 | + renderPassInfo.pClearValues = &clearColor; 83 | + 84 | + commandBuffers[i].beginRenderPass(renderPassInfo, vk::SubpassContents::eInline); 85 | + 86 | + commandBuffers[i].bindPipeline(vk::PipelineBindPoint::eGraphics, graphicsPipeline); 87 | + 88 | + commandBuffers[i].draw(3, 1, 0, 0); 89 | + 90 | + commandBuffers[i].endRenderPass(); 91 | + 92 | + try { 93 | + commandBuffers[i].end(); 94 | + } catch (vk::SystemError err) { 95 | + throw std::runtime_error("failed to record command buffer!"); 96 | + } 97 | + } 98 | + } 99 | 100 | vk::UniqueShaderModule createShaderModule(const std::vector& code) { 101 | try { 102 | -------------------------------------------------------------------------------- /steps/15_hello_triangle.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/14_command_buffers.cpp 2 | +++ b/15_hello_triangle.cpp 3 | @@ -14,6 +14,8 @@ 4 | const int WIDTH = 800; 5 | const int HEIGHT = 600; 6 | 7 | +const int MAX_FRAMES_IN_FLIGHT = 2; 8 | + 9 | const std::vector validationLayers = { 10 | "VK_LAYER_LUNARG_standard_validation" 11 | }; 12 | @@ -96,6 +98,11 @@ private: 13 | VkCommandPool commandPool; 14 | std::vector> commandBuffers; 15 | 16 | + std::vector imageAvailableSemaphores; 17 | + std::vector renderFinishedSemaphores; 18 | + std::vector inFlightFences; 19 | + size_t currentFrame = 0; 20 | + 21 | void initWindow() { 22 | glfwInit(); 23 | 24 | @@ -117,17 +124,27 @@ private: 25 | createFramebuffers(); 26 | createCommandPool(); 27 | createCommandBuffers(); 28 | + createSyncObjects(); 29 | } 30 | 31 | void mainLoop() { 32 | while (!glfwWindowShouldClose(window)) { 33 | glfwPollEvents(); 34 | + drawFrame(); 35 | } 36 | + 37 | + device->waitIdle(); 38 | } 39 | 40 | void cleanup() { 41 | // NOTE: instance destruction is handled by UniqueInstance, same for device 42 | 43 | + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 44 | + device->destroySemaphore(renderFinishedSemaphores[i]); 45 | + device->destroySemaphore(imageAvailableSemaphores[i]); 46 | + device->destroyFence(inFlightFences[i]); 47 | + } 48 | + 49 | device->destroyCommandPool(commandPool); 50 | 51 | for (auto framebuffer : swapChainFramebuffers) { 52 | @@ -385,11 +402,21 @@ private: 53 | subpass.colorAttachmentCount = 1; 54 | subpass.pColorAttachments = &colorAttachmentRef; 55 | 56 | + vk::SubpassDependency dependency = {}; 57 | + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; 58 | + dependency.dstSubpass = 0; 59 | + dependency.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput; 60 | + //dependency.srcAccessMask = 0; 61 | + dependency.dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput; 62 | + dependency.dstAccessMask = vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite; 63 | + 64 | vk::RenderPassCreateInfo renderPassInfo = {}; 65 | renderPassInfo.attachmentCount = 1; 66 | renderPassInfo.pAttachments = &colorAttachment; 67 | renderPassInfo.subpassCount = 1; 68 | renderPassInfo.pSubpasses = &subpass; 69 | + renderPassInfo.dependencyCount = 1; 70 | + renderPassInfo.pDependencies = &dependency; 71 | 72 | try { 73 | renderPass = device->createRenderPass(renderPassInfo); 74 | @@ -593,6 +620,65 @@ private: 75 | } 76 | } 77 | 78 | + void createSyncObjects() { 79 | + imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); 80 | + renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); 81 | + inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); 82 | + 83 | + try { 84 | + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 85 | + imageAvailableSemaphores[i] = device->createSemaphore({}); 86 | + renderFinishedSemaphores[i] = device->createSemaphore({}); 87 | + inFlightFences[i] = device->createFence({vk::FenceCreateFlagBits::eSignaled}); 88 | + } 89 | + } catch (vk::SystemError err) { 90 | + throw std::runtime_error("failed to create synchronization objects for a frame!"); 91 | + } 92 | + } 93 | + 94 | + void drawFrame() { 95 | + device->waitForFences(1, &inFlightFences[currentFrame], VK_TRUE, std::numeric_limits::max()); 96 | + device->resetFences(1, &inFlightFences[currentFrame]); 97 | + 98 | + uint32_t imageIndex = device->acquireNextImageKHR(swapChain, std::numeric_limits::max(), 99 | + imageAvailableSemaphores[currentFrame], nullptr).value; 100 | + 101 | + vk::SubmitInfo submitInfo = {}; 102 | + 103 | + vk::Semaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] }; 104 | + vk::PipelineStageFlags waitStages[] = { vk::PipelineStageFlagBits::eColorAttachmentOutput }; 105 | + submitInfo.waitSemaphoreCount = 1; 106 | + submitInfo.pWaitSemaphores = waitSemaphores; 107 | + submitInfo.pWaitDstStageMask = waitStages; 108 | + 109 | + submitInfo.commandBufferCount = 1; 110 | + submitInfo.pCommandBuffers = &commandBuffers[imageIndex]; 111 | + 112 | + vk::Semaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] }; 113 | + submitInfo.signalSemaphoreCount = 1; 114 | + submitInfo.pSignalSemaphores = signalSemaphores; 115 | + 116 | + try { 117 | + graphicsQueue.submit(submitInfo, inFlightFences[currentFrame]); 118 | + } catch (vk::SystemError err) { 119 | + throw std::runtime_error("failed to submit draw command buffer!"); 120 | + } 121 | + 122 | + vk::PresentInfoKHR presentInfo = {}; 123 | + presentInfo.waitSemaphoreCount = 1; 124 | + presentInfo.pWaitSemaphores = signalSemaphores; 125 | + 126 | + vk::SwapchainKHR swapChains[] = { swapChain }; 127 | + presentInfo.swapchainCount = 1; 128 | + presentInfo.pSwapchains = swapChains; 129 | + presentInfo.pImageIndices = &imageIndex; 130 | + presentInfo.pResults = nullptr; // Optional 131 | + 132 | + presentQueue.presentKHR(presentInfo); 133 | + 134 | + currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; 135 | + } 136 | + 137 | vk::UniqueShaderModule createShaderModule(const std::vector& code) { 138 | try { 139 | return device->createShaderModuleUnique({ 140 | -------------------------------------------------------------------------------- /steps/16_swap_chain_recreation.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/15_hello_triangle.cpp 2 | +++ b/16_swap_chain_recreation.cpp 3 | @@ -31,7 +31,7 @@ const bool enableValidationLayers = true; 4 | #endif 5 | 6 | VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pCallback) { 7 | - auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); 8 | + auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); 9 | if (func != nullptr) { 10 | return func(instance, pCreateInfo, pAllocator, pCallback); 11 | } 12 | @@ -41,7 +41,7 @@ VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMes 13 | } 14 | 15 | void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT callback, const VkAllocationCallbacks* pAllocator) { 16 | - auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); 17 | + auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); 18 | if (func != nullptr) { 19 | func(instance, callback, pAllocator); 20 | } 21 | @@ -103,12 +103,21 @@ private: 22 | std::vector inFlightFences; 23 | size_t currentFrame = 0; 24 | 25 | + bool framebufferResized = false; 26 | + 27 | void initWindow() { 28 | glfwInit(); 29 | 30 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 31 | - glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); 32 | + 33 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 34 | + glfwSetWindowUserPointer(window, this); 35 | + glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); 36 | + } 37 | + 38 | + static void framebufferResizeCallback(GLFWwindow* window, int width, int height) { 39 | + auto app = reinterpret_cast(glfwGetWindowUserPointer(window)); 40 | + app->framebufferResized = true; 41 | } 42 | 43 | void initVulkan() { 44 | @@ -136,21 +145,13 @@ private: 45 | device->waitIdle(); 46 | } 47 | 48 | - void cleanup() { 49 | - // NOTE: instance destruction is handled by UniqueInstance, same for device 50 | - 51 | - for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 52 | - device->destroySemaphore(renderFinishedSemaphores[i]); 53 | - device->destroySemaphore(imageAvailableSemaphores[i]); 54 | - device->destroyFence(inFlightFences[i]); 55 | - } 56 | - 57 | - device->destroyCommandPool(commandPool); 58 | - 59 | + void cleanupSwapChain() { 60 | for (auto framebuffer : swapChainFramebuffers) { 61 | device->destroyFramebuffer(framebuffer); 62 | } 63 | 64 | + device->freeCommandBuffers(commandPool, commandBuffers); 65 | + 66 | device->destroyPipeline(graphicsPipeline); 67 | device->destroyPipelineLayout(pipelineLayout); 68 | device->destroyRenderPass(renderPass); 69 | @@ -159,8 +160,21 @@ private: 70 | device->destroyImageView(imageView); 71 | } 72 | 73 | - // not using UniqeSwapchain to destroy in correct order - before the surface 74 | device->destroySwapchainKHR(swapChain); 75 | + } 76 | + 77 | + void cleanup() { 78 | + // NOTE: instance destruction is handled by UniqueInstance, same for device 79 | + 80 | + cleanupSwapChain(); 81 | + 82 | + for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 83 | + device->destroySemaphore(renderFinishedSemaphores[i]); 84 | + device->destroySemaphore(imageAvailableSemaphores[i]); 85 | + device->destroyFence(inFlightFences[i]); 86 | + } 87 | + 88 | + device->destroyCommandPool(commandPool); 89 | 90 | // surface is created by glfw, therefore not using a Unique handle 91 | instance->destroySurfaceKHR(surface); 92 | @@ -174,6 +188,25 @@ private: 93 | glfwTerminate(); 94 | } 95 | 96 | + void recreateSwapChain() { 97 | + int width = 0, height = 0; 98 | + while (width == 0 || height == 0) { 99 | + glfwGetFramebufferSize(window, &width, &height); 100 | + glfwWaitEvents(); 101 | + } 102 | + 103 | + device->waitIdle(); 104 | + 105 | + cleanupSwapChain(); 106 | + 107 | + createSwapChain(); 108 | + createImageViews(); 109 | + createRenderPass(); 110 | + createGraphicsPipeline(); 111 | + createFramebuffers(); 112 | + createCommandBuffers(); 113 | + } 114 | + 115 | void createInstance() { 116 | if (enableValidationLayers && !checkValidationLayerSupport()) { 117 | throw std::runtime_error("validation layers requested, but not available!"); 118 | @@ -638,10 +671,18 @@ private: 119 | 120 | void drawFrame() { 121 | device->waitForFences(1, &inFlightFences[currentFrame], VK_TRUE, std::numeric_limits::max()); 122 | - device->resetFences(1, &inFlightFences[currentFrame]); 123 | 124 | - uint32_t imageIndex = device->acquireNextImageKHR(swapChain, std::numeric_limits::max(), 125 | - imageAvailableSemaphores[currentFrame], nullptr).value; 126 | + uint32_t imageIndex; 127 | + try { 128 | + vk::ResultValue result = device->acquireNextImageKHR(swapChain, std::numeric_limits::max(), 129 | + imageAvailableSemaphores[currentFrame], nullptr); 130 | + imageIndex = result.value; 131 | + } catch (vk::OutOfDateKHRError err) { 132 | + recreateSwapChain(); 133 | + return; 134 | + } catch (vk::SystemError err) { 135 | + throw std::runtime_error("failed to acquire swap chain image!"); 136 | + } 137 | 138 | vk::SubmitInfo submitInfo = {}; 139 | 140 | @@ -658,6 +699,8 @@ private: 141 | submitInfo.signalSemaphoreCount = 1; 142 | submitInfo.pSignalSemaphores = signalSemaphores; 143 | 144 | + device->resetFences(1, &inFlightFences[currentFrame]); 145 | + 146 | try { 147 | graphicsQueue.submit(submitInfo, inFlightFences[currentFrame]); 148 | } catch (vk::SystemError err) { 149 | @@ -672,9 +715,22 @@ private: 150 | presentInfo.swapchainCount = 1; 151 | presentInfo.pSwapchains = swapChains; 152 | presentInfo.pImageIndices = &imageIndex; 153 | - presentInfo.pResults = nullptr; // Optional 154 | 155 | - presentQueue.presentKHR(presentInfo); 156 | + vk::Result resultPresent; 157 | + try { 158 | + resultPresent = presentQueue.presentKHR(presentInfo); 159 | + } catch (vk::OutOfDateKHRError err) { 160 | + resultPresent = vk::Result::eErrorOutOfDateKHR; 161 | + } catch (vk::SystemError err) { 162 | + throw std::runtime_error("failed to present swap chain image!"); 163 | + } 164 | + 165 | + if (resultPresent == vk::Result::eSuboptimalKHR || resultPresent == vk::Result::eSuboptimalKHR || framebufferResized) { 166 | + std::cout << "swap chain out of date/suboptimal/window resized - recreating" << std::endl; 167 | + framebufferResized = false; 168 | + recreateSwapChain(); 169 | + return; 170 | + } 171 | 172 | currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; 173 | } 174 | @@ -725,7 +781,10 @@ private: 175 | return capabilities.currentExtent; 176 | } 177 | else { 178 | - vk::Extent2D actualExtent = { static_cast(WIDTH), static_cast(HEIGHT) }; 179 | + int width, height; 180 | + glfwGetFramebufferSize(window, &width, &height); 181 | + 182 | + vk::Extent2D actualExtent = { static_cast(width), static_cast(height) }; 183 | 184 | actualExtent.width = std::max(capabilities.minImageExtent.width, std::min(capabilities.maxImageExtent.width, actualExtent.width)); 185 | actualExtent.height = std::max(capabilities.minImageExtent.height, std::min(capabilities.maxImageExtent.height, actualExtent.height)); 186 | -------------------------------------------------------------------------------- /steps/17_vertex_input.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/16_swap_chain_recreation.cpp 2 | +++ b/17_vertex_input.cpp 3 | @@ -1,6 +1,8 @@ 4 | #include 5 | #include 6 | 7 | +#include 8 | + 9 | #include 10 | #include 11 | #include 12 | @@ -8,6 +10,7 @@ 13 | #include 14 | #include 15 | #include 16 | +#include 17 | #include 18 | #include 19 | 20 | @@ -62,6 +65,41 @@ struct SwapChainSupportDetails { 21 | std::vector presentModes; 22 | }; 23 | 24 | +struct Vertex { 25 | + glm::vec2 pos; 26 | + glm::vec3 color; 27 | + 28 | + static vk::VertexInputBindingDescription getBindingDescription() { 29 | + vk::VertexInputBindingDescription bindingDescription = {}; 30 | + bindingDescription.binding = 0; 31 | + bindingDescription.stride = sizeof(Vertex); 32 | + bindingDescription.inputRate = vk::VertexInputRate::eVertex; 33 | + 34 | + return bindingDescription; 35 | + } 36 | + 37 | + static std::array getAttributeDescriptions() { 38 | + std::array attributeDescriptions = {}; 39 | + attributeDescriptions[0].binding = 0; 40 | + attributeDescriptions[0].location = 0; 41 | + attributeDescriptions[0].format = vk::Format::eR32G32Sfloat; 42 | + attributeDescriptions[0].offset = offsetof(Vertex, pos); 43 | + 44 | + attributeDescriptions[1].binding = 0; 45 | + attributeDescriptions[1].location = 1; 46 | + attributeDescriptions[1].format = vk::Format::eR32G32Sfloat; 47 | + attributeDescriptions[1].offset = offsetof(Vertex, color); 48 | + 49 | + return attributeDescriptions; 50 | + } 51 | +}; 52 | + 53 | +const std::vector vertices = { 54 | + {{0.0f, -0.5f}, {1.0f, 0.0f, 0.0f}}, 55 | + {{0.5f, 0.5f}, {0.0f, 1.0f, 0.0f}}, 56 | + {{-0.5f, 0.5f}, {0.0f, 0.0f, 1.0f}} 57 | +}; 58 | + 59 | class HelloTriangleApplication { 60 | public: 61 | void run() { 62 | @@ -484,6 +522,14 @@ private: 63 | vertexInputInfo.vertexBindingDescriptionCount = 0; 64 | vertexInputInfo.vertexAttributeDescriptionCount = 0; 65 | 66 | + auto bindingDescription = Vertex::getBindingDescription(); 67 | + auto attributeDescriptions = Vertex::getAttributeDescriptions(); 68 | + 69 | + vertexInputInfo.vertexBindingDescriptionCount = 1; 70 | + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributeDescriptions.size()); 71 | + vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; 72 | + vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); 73 | + 74 | vk::PipelineInputAssemblyStateCreateInfo inputAssembly = {}; 75 | inputAssembly.topology = vk::PrimitiveTopology::eTriangleList; 76 | inputAssembly.primitiveRestartEnable = VK_FALSE; 77 | -------------------------------------------------------------------------------- /steps/18_vertex_buffer.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/17_vertex_input.cpp 2 | +++ b/18_vertex_buffer.cpp 3 | @@ -87,7 +87,7 @@ struct Vertex { 4 | 5 | attributeDescriptions[1].binding = 0; 6 | attributeDescriptions[1].location = 1; 7 | - attributeDescriptions[1].format = vk::Format::eR32G32Sfloat; 8 | + attributeDescriptions[1].format = vk::Format::eR32G32B32Sfloat; 9 | attributeDescriptions[1].offset = offsetof(Vertex, color); 10 | 11 | return attributeDescriptions; 12 | @@ -133,7 +133,11 @@ private: 13 | vk::PipelineLayout pipelineLayout; 14 | vk::Pipeline graphicsPipeline; 15 | 16 | - VkCommandPool commandPool; 17 | + vk::CommandPool commandPool; 18 | + 19 | + vk::Buffer vertexBuffer; 20 | + vk::DeviceMemory vertexBufferMemory; 21 | + 22 | std::vector> commandBuffers; 23 | 24 | std::vector imageAvailableSemaphores; 25 | @@ -170,6 +174,7 @@ private: 26 | createGraphicsPipeline(); 27 | createFramebuffers(); 28 | createCommandPool(); 29 | + createVertexBuffer(); 30 | createCommandBuffers(); 31 | createSyncObjects(); 32 | } 33 | @@ -206,6 +211,9 @@ private: 34 | 35 | cleanupSwapChain(); 36 | 37 | + device->destroyBuffer(vertexBuffer); 38 | + device->freeMemory(vertexBufferMemory); 39 | + 40 | for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 41 | device->destroySemaphore(renderFinishedSemaphores[i]); 42 | device->destroySemaphore(imageAvailableSemaphores[i]); 43 | @@ -648,6 +656,49 @@ private: 44 | } 45 | } 46 | 47 | + void createVertexBuffer() { 48 | + vk::BufferCreateInfo bufferInfo = {}; 49 | + bufferInfo.size = sizeof(vertices[0]) * vertices.size(); 50 | + bufferInfo.usage = vk::BufferUsageFlagBits::eVertexBuffer; 51 | + bufferInfo.sharingMode = vk::SharingMode::eExclusive; 52 | + 53 | + try { 54 | + vertexBuffer = device->createBuffer(bufferInfo); 55 | + } catch (vk::SystemError err) { 56 | + throw std::runtime_error("failed to create vertex buffer!"); 57 | + } 58 | + 59 | + vk::MemoryRequirements memRequirements = device->getBufferMemoryRequirements(vertexBuffer); 60 | + 61 | + vk::MemoryAllocateInfo allocInfo = {}; 62 | + allocInfo.allocationSize = memRequirements.size; 63 | + allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); 64 | + 65 | + try { 66 | + vertexBufferMemory = device->allocateMemory(allocInfo); 67 | + } catch (vk::SystemError err) { 68 | + throw std::runtime_error("failed to allocate vertex buffer memory!"); 69 | + } 70 | + 71 | + device->bindBufferMemory(vertexBuffer, vertexBufferMemory, 0); 72 | + 73 | + void* data = device->mapMemory(vertexBufferMemory, 0, bufferInfo.size); 74 | + memcpy(data, vertices.data(), (size_t)bufferInfo.size); 75 | + device->unmapMemory(vertexBufferMemory); 76 | + } 77 | + 78 | + uint32_t findMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags properties) { 79 | + vk::PhysicalDeviceMemoryProperties memProperties = physicalDevice.getMemoryProperties(); 80 | + 81 | + for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { 82 | + if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { 83 | + return i; 84 | + } 85 | + } 86 | + 87 | + throw std::runtime_error("failed to find suitable memory type!"); 88 | + } 89 | + 90 | void createCommandBuffers() { 91 | commandBuffers.resize(swapChainFramebuffers.size()); 92 | 93 | @@ -687,7 +738,11 @@ private: 94 | 95 | commandBuffers[i].bindPipeline(vk::PipelineBindPoint::eGraphics, graphicsPipeline); 96 | 97 | - commandBuffers[i].draw(3, 1, 0, 0); 98 | + vk::Buffer vertexBuffers[] = { vertexBuffer }; 99 | + vk::DeviceSize offsets[] = { 0 }; 100 | + commandBuffers[i].bindVertexBuffers(0, 1, vertexBuffers, offsets); 101 | + 102 | + commandBuffers[i].draw(static_cast(vertices.size()), 1, 0, 0); 103 | 104 | commandBuffers[i].endRenderPass(); 105 | 106 | @@ -772,7 +827,6 @@ private: 107 | } 108 | 109 | if (resultPresent == vk::Result::eSuboptimalKHR || resultPresent == vk::Result::eSuboptimalKHR || framebufferResized) { 110 | - std::cout << "swap chain out of date/suboptimal/window resized - recreating" << std::endl; 111 | framebufferResized = false; 112 | recreateSwapChain(); 113 | return; 114 | -------------------------------------------------------------------------------- /steps/19_staging_buffer.cpp.previous.diff: -------------------------------------------------------------------------------- 1 | --- a/18_vertex_buffer.cpp 2 | +++ b/19_staging_buffer.cpp 3 | @@ -657,34 +657,82 @@ private: 4 | } 5 | 6 | void createVertexBuffer() { 7 | + vk::DeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); 8 | + 9 | + vk::Buffer stagingBuffer; 10 | + vk::DeviceMemory stagingBufferMemory; 11 | + createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent, stagingBuffer, stagingBufferMemory); 12 | + 13 | + void* data = device->mapMemory(stagingBufferMemory, 0, bufferSize); 14 | + memcpy(data, vertices.data(), (size_t)bufferSize); 15 | + device->unmapMemory(stagingBufferMemory); 16 | + 17 | + createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eVertexBuffer, vk::MemoryPropertyFlagBits::eDeviceLocal, vertexBuffer, vertexBufferMemory); 18 | + 19 | + copyBuffer(stagingBuffer, vertexBuffer, bufferSize); 20 | + 21 | + device->destroyBuffer(stagingBuffer); 22 | + device->freeMemory(stagingBufferMemory); 23 | + } 24 | + 25 | + void createBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties, vk::Buffer& buffer, vk::DeviceMemory& bufferMemory) { 26 | vk::BufferCreateInfo bufferInfo = {}; 27 | - bufferInfo.size = sizeof(vertices[0]) * vertices.size(); 28 | - bufferInfo.usage = vk::BufferUsageFlagBits::eVertexBuffer; 29 | + bufferInfo.size = size; 30 | + bufferInfo.usage = usage; 31 | bufferInfo.sharingMode = vk::SharingMode::eExclusive; 32 | 33 | try { 34 | - vertexBuffer = device->createBuffer(bufferInfo); 35 | - } catch (vk::SystemError err) { 36 | - throw std::runtime_error("failed to create vertex buffer!"); 37 | + buffer = device->createBuffer(bufferInfo); 38 | + } 39 | + catch (vk::SystemError err) { 40 | + throw std::runtime_error("failed to create buffer!"); 41 | } 42 | 43 | - vk::MemoryRequirements memRequirements = device->getBufferMemoryRequirements(vertexBuffer); 44 | + vk::MemoryRequirements memRequirements = device->getBufferMemoryRequirements(buffer); 45 | 46 | vk::MemoryAllocateInfo allocInfo = {}; 47 | allocInfo.allocationSize = memRequirements.size; 48 | - allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); 49 | - 50 | + allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties); 51 | + 52 | try { 53 | - vertexBufferMemory = device->allocateMemory(allocInfo); 54 | - } catch (vk::SystemError err) { 55 | - throw std::runtime_error("failed to allocate vertex buffer memory!"); 56 | + bufferMemory = device->allocateMemory(allocInfo); 57 | + } 58 | + catch (vk::SystemError err) { 59 | + throw std::runtime_error("failed to allocate buffer memory!"); 60 | } 61 | 62 | - device->bindBufferMemory(vertexBuffer, vertexBufferMemory, 0); 63 | + device->bindBufferMemory(buffer, bufferMemory, 0); 64 | + } 65 | + 66 | + void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { 67 | + vk::CommandBufferAllocateInfo allocInfo = {}; 68 | + allocInfo.level = vk::CommandBufferLevel::ePrimary; 69 | + allocInfo.commandPool = commandPool; 70 | + allocInfo.commandBufferCount = 1; 71 | + 72 | + vk::CommandBuffer commandBuffer = device->allocateCommandBuffers(allocInfo)[0]; 73 | + 74 | + vk::CommandBufferBeginInfo beginInfo = {}; 75 | + beginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit; 76 | + 77 | + commandBuffer.begin(beginInfo); 78 | + 79 | + vk::BufferCopy copyRegion = {}; 80 | + copyRegion.srcOffset = 0; // Optional 81 | + copyRegion.dstOffset = 0; // Optional 82 | + copyRegion.size = size; 83 | + commandBuffer.copyBuffer(srcBuffer, dstBuffer, copyRegion); 84 | + 85 | + commandBuffer.end(); 86 | + 87 | + vk::SubmitInfo submitInfo = {}; 88 | + submitInfo.commandBufferCount = 1; 89 | + submitInfo.pCommandBuffers = &commandBuffer; 90 | + 91 | + graphicsQueue.submit(submitInfo, nullptr); 92 | + graphicsQueue.waitIdle(); 93 | 94 | - void* data = device->mapMemory(vertexBufferMemory, 0, bufferInfo.size); 95 | - memcpy(data, vertices.data(), (size_t)bufferInfo.size); 96 | - device->unmapMemory(vertexBufferMemory); 97 | + device->freeCommandBuffers(commandPool, commandBuffer); 98 | } 99 | 100 | uint32_t findMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags properties) { 101 | -------------------------------------------------------------------------------- /steps/copy_and_diff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | stepFilename=$(basename $2) 3 | echo $stepFilename 4 | cp "$1" ./"$stepFilename" 5 | git diff --color-words --no-index "$2" "$stepFilename" 6 | git diff --no-index "$2" "$stepFilename" | tail -n+3 > "$stepFilename".original.diff 7 | -------------------------------------------------------------------------------- /steps/diff_previous.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | git diff --color-words --no-index "$1" "$2" 3 | git diff --no-index "$1" "$2" | tail -n+3 > "$2".previous.diff -------------------------------------------------------------------------------- /vulkan-tutorial-hpp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.168 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vulkan-tutorial-hpp", "vulkan-tutorial-hpp.vcxproj", "{A9DE6A26-2FEB-406D-9C5F-45563ED59906}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {A9DE6A26-2FEB-406D-9C5F-45563ED59906}.Debug|x64.ActiveCfg = Debug|x64 17 | {A9DE6A26-2FEB-406D-9C5F-45563ED59906}.Debug|x64.Build.0 = Debug|x64 18 | {A9DE6A26-2FEB-406D-9C5F-45563ED59906}.Debug|x86.ActiveCfg = Debug|Win32 19 | {A9DE6A26-2FEB-406D-9C5F-45563ED59906}.Debug|x86.Build.0 = Debug|Win32 20 | {A9DE6A26-2FEB-406D-9C5F-45563ED59906}.Release|x64.ActiveCfg = Release|x64 21 | {A9DE6A26-2FEB-406D-9C5F-45563ED59906}.Release|x64.Build.0 = Release|x64 22 | {A9DE6A26-2FEB-406D-9C5F-45563ED59906}.Release|x86.ActiveCfg = Release|Win32 23 | {A9DE6A26-2FEB-406D-9C5F-45563ED59906}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {C176BB5E-0A1D-4B0A-AFD3-6EC2780A6B8D} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /vulkan-tutorial-hpp.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {A9DE6A26-2FEB-406D-9C5F-45563ED59906} 24 | Win32Proj 25 | vulkantutorialhpp 26 | 10.0.17763.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v141 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v141 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v141 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v141 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | CppCoreCheckRules.ruleset 76 | false 77 | 78 | 79 | true 80 | CppCoreCheckRules.ruleset 81 | false 82 | 83 | 84 | false 85 | CppCoreCheckRules.ruleset 86 | false 87 | 88 | 89 | false 90 | CppCoreCheckRules.ruleset 91 | false 92 | 93 | 94 | 95 | Level3 96 | Disabled 97 | true 98 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 99 | true 100 | C:\VulkanSDK\1.1.92.1\Include;%(AdditionalIncludeDirectories) 101 | stdcpp17 102 | false 103 | 104 | 105 | true 106 | Console 107 | C:\VulkanSDK\1.1.92.1\Lib32;%(AdditionalLibraryDirectories) 108 | vulkan-1.lib;%(AdditionalDependencies) 109 | 110 | 111 | 112 | 113 | Level3 114 | Disabled 115 | true 116 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 117 | true 118 | %(AdditionalUsingDirectories) 119 | C:\dev\vcpkg\installed\x64-windows\include;C:\VulkanSDK\1.1.92.1\Include;%(AdditionalIncludeDirectories) 120 | stdcpp17 121 | false 122 | 123 | 124 | true 125 | Console 126 | C:\VulkanSDK\1.1.92.1\Lib;%(AdditionalLibraryDirectories) 127 | vulkan-1.lib;%(AdditionalDependencies) 128 | 129 | 130 | 131 | 132 | Level3 133 | MaxSpeed 134 | true 135 | true 136 | true 137 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 138 | true 139 | C:\VulkanSDK\1.1.92.1\Include;%(AdditionalIncludeDirectories) 140 | stdcpp17 141 | false 142 | 143 | 144 | true 145 | true 146 | true 147 | Console 148 | C:\VulkanSDK\1.1.92.1\Lib32;%(AdditionalLibraryDirectories) 149 | vulkan-1.lib;%(AdditionalDependencies) 150 | 151 | 152 | 153 | 154 | Level3 155 | MaxSpeed 156 | true 157 | true 158 | true 159 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 160 | true 161 | %(AdditionalUsingDirectories) 162 | C:\dev\vcpkg\installed\x64-windows\include;C:\VulkanSDK\1.1.92.1\Include;%(AdditionalIncludeDirectories) 163 | stdcpp17 164 | false 165 | 166 | 167 | true 168 | true 169 | true 170 | Console 171 | C:\VulkanSDK\1.1.92.1\Lib;%(AdditionalLibraryDirectories) 172 | vulkan-1.lib;%(AdditionalDependencies) 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /vulkan-tutorial-hpp.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | -------------------------------------------------------------------------------- /vulkan-utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace vkutils { 8 | 9 | namespace internal { 10 | 11 | std::vector getRequiredExtensions(bool enableValidationLayers) { 12 | uint32_t glfwExtensionCount = 0; 13 | const char** glfwExtensions; 14 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 15 | 16 | std::vector extensions(glfwExtensions, glfwExtensions + glfwExtensionCount); 17 | 18 | if (enableValidationLayers) { 19 | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 20 | } 21 | 22 | return extensions; 23 | } 24 | 25 | bool checkValidationLayerSupport(const std::vector& validationLayers) { 26 | auto availableLayers = vk::enumerateInstanceLayerProperties(); 27 | for (const char* layerName : validationLayers) { 28 | bool layerFound = false; 29 | 30 | for (const auto& layerProperties : availableLayers) { 31 | if (strcmp(layerName, layerProperties.layerName) == 0) { 32 | layerFound = true; 33 | break; 34 | } 35 | } 36 | 37 | if (!layerFound) { 38 | return false; 39 | } 40 | } 41 | 42 | return true; 43 | } 44 | 45 | VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pCallback) { 46 | auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); 47 | if (func != nullptr) { 48 | return func(instance, pCreateInfo, pAllocator, pCallback); 49 | } 50 | else { 51 | return VK_ERROR_EXTENSION_NOT_PRESENT; 52 | } 53 | } 54 | 55 | void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT callback, const VkAllocationCallbacks* pAllocator) { 56 | auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); 57 | if (func != nullptr) { 58 | func(instance, callback, pAllocator); 59 | } 60 | } 61 | 62 | } // namespace internal 63 | 64 | vk::UniqueInstance createInstance( 65 | const char* applicationName = "Application", 66 | bool enableValidationLayers = false, 67 | const std::vector& validationLayers = { "VK_LAYER_LUNARG_standard_validation" } 68 | ) { 69 | if (enableValidationLayers && !internal::checkValidationLayerSupport(validationLayers)) { 70 | throw std::runtime_error("validation layers requested, but not available!"); 71 | } 72 | 73 | auto appInfo = vk::ApplicationInfo( 74 | applicationName, 75 | VK_MAKE_VERSION(1, 0, 0), 76 | "No Engine", 77 | VK_MAKE_VERSION(1, 0, 0), 78 | VK_API_VERSION_1_0 79 | ); 80 | 81 | // TODO!: remove glfw requirement from createInstance 82 | auto extensions = internal::getRequiredExtensions(enableValidationLayers); 83 | 84 | auto createInfo = vk::InstanceCreateInfo( 85 | vk::InstanceCreateFlags(), 86 | &appInfo, 87 | 0, nullptr, // enabled layers 88 | static_cast(extensions.size()), extensions.data() // enabled extensions 89 | ); 90 | 91 | if (enableValidationLayers) { 92 | createInfo.enabledLayerCount = static_cast(validationLayers.size()); 93 | createInfo.ppEnabledLayerNames = validationLayers.data(); 94 | } 95 | 96 | try { 97 | return vk::createInstanceUnique(createInfo, nullptr); 98 | } 99 | catch (vk::SystemError err) { 100 | throw std::runtime_error("failed to create instance!"); 101 | } 102 | } 103 | 104 | VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { 105 | std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; 106 | 107 | return VK_FALSE; 108 | } 109 | 110 | // TODO!: add method destroyDebugCallback? unique ptr? 111 | VkDebugUtilsMessengerEXT setupDebugCallback(const vk::Instance& instance) { 112 | auto createInfo = vk::DebugUtilsMessengerCreateInfoEXT( 113 | vk::DebugUtilsMessengerCreateFlagsEXT(), 114 | vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, 115 | vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, 116 | debugCallback, 117 | nullptr 118 | ); 119 | 120 | // NOTE: Vulkan-hpp has methods for this, but they trigger linking errors... 121 | //instance->createDebugUtilsMessengerEXT(createInfo); 122 | //instance->createDebugUtilsMessengerEXTUnique(createInfo); 123 | 124 | VkDebugUtilsMessengerEXT callback; 125 | // NOTE: reinterpret_cast is also used by vulkan.hpp internally for all these structs 126 | if (internal::CreateDebugUtilsMessengerEXT(instance, reinterpret_cast(&createInfo), nullptr, &callback) != VK_SUCCESS) { 127 | throw std::runtime_error("failed to set up debug callback!"); 128 | } 129 | 130 | return callback; 131 | } 132 | 133 | // TODO!: unique... 134 | vk::SurfaceKHR createSurface(const vk::Instance& instance, GLFWwindow* window) { 135 | VkSurfaceKHR rawSurface; 136 | if (glfwCreateWindowSurface(instance, window, nullptr, &rawSurface) != VK_SUCCESS) { 137 | throw std::runtime_error("failed to create window surface!"); 138 | } 139 | 140 | vk::SurfaceKHR surface = rawSurface; 141 | return surface; 142 | } 143 | 144 | } // namespace vkutils --------------------------------------------------------------------------------