├── textures └── viking_room.png ├── vcpkg.json ├── vksample.h ├── shaders ├── shader.frag └── shader.vert ├── vcpkg-configuration.json ├── CMakeLists.txt ├── .gitattributes ├── CMakePresets.json ├── README.md ├── .gitignore └── vksample.cpp /textures/viking_room.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meggsOmatic/vulkan-example-cmake-vcpkg/HEAD/textures/viking_room.png -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": [ 3 | "glfw3", 4 | "glm", 5 | "stb", 6 | "tinyobjloader", 7 | "vulkan-sdk-components", 8 | "vulkan-validationlayers" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /vksample.h: -------------------------------------------------------------------------------- 1 | // vksample.h : Include file for standard system include files, 2 | // or project specific include files. 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | // TODO: Reference additional headers your program requires here. 9 | -------------------------------------------------------------------------------- /shaders/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 1) uniform sampler2D texSampler; 4 | 5 | layout(location = 0) in vec3 fragColor; 6 | layout(location = 1) in vec2 fragTexCoord; 7 | 8 | layout(location = 0) out vec4 outColor; 9 | 10 | void main() { 11 | outColor = texture(texSampler, fragTexCoord); 12 | } -------------------------------------------------------------------------------- /vcpkg-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "default-registry": { 3 | "kind": "git", 4 | "baseline": "01f602195983451bc83e72f4214af2cbc495aa94", 5 | "repository": "https://github.com/microsoft/vcpkg" 6 | }, 7 | "registries": [ 8 | { 9 | "kind": "artifact", 10 | "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip", 11 | "name": "microsoft" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /shaders/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 0) uniform UniformBufferObject { 4 | mat4 model; 5 | mat4 view; 6 | mat4 proj; 7 | } ubo; 8 | 9 | layout(location = 0) in vec3 inPosition; 10 | layout(location = 1) in vec3 inColor; 11 | layout(location = 2) in vec2 inTexCoord; 12 | 13 | layout(location = 0) out vec3 fragColor; 14 | layout(location = 1) out vec2 fragTexCoord; 15 | 16 | void main() { 17 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); 18 | fragColor = inColor; 19 | fragTexCoord = inTexCoord; 20 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMakeList.txt : CMake project for vksample, include source and define 2 | # project specific logic here. 3 | # 4 | cmake_minimum_required (VERSION 3.28) 5 | 6 | # Enable Hot Reload for MSVC compilers if supported. 7 | if (POLICY CMP0141) 8 | cmake_policy(SET CMP0141 NEW) 9 | set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") 10 | endif() 11 | 12 | project ("vksample") 13 | 14 | # Add source to this project's executable. 15 | add_executable (vksample "vksample.cpp" "vksample.h") 16 | 17 | # https://cmake.org/cmake/help/latest/module/FindVulkan.html 18 | find_package(Vulkan REQUIRED) 19 | target_link_libraries(vksample PRIVATE Vulkan::Vulkan) 20 | 21 | find_package(glfw3 CONFIG REQUIRED) 22 | target_link_libraries(vksample PRIVATE glfw) 23 | 24 | find_package(Stb REQUIRED) 25 | target_include_directories(vksample PRIVATE ${Stb_INCLUDE_DIR}) 26 | 27 | find_package(glm CONFIG REQUIRED) 28 | target_link_libraries(vksample PRIVATE glm::glm) 29 | 30 | find_package(tinyobjloader CONFIG REQUIRED) 31 | target_link_libraries(vksample PRIVATE tinyobjloader::tinyobjloader) 32 | 33 | set(SHADER_OUTPUT "") 34 | function(add_shader SRCNAME DSTNAME) 35 | set(SRCPATH "${CMAKE_SOURCE_DIR}/shaders/${SRCNAME}") 36 | set(DSTPATH "${CMAKE_CURRENT_BINARY_DIR}/shaders/${DSTNAME}") 37 | add_custom_command( 38 | OUTPUT "${DSTPATH}" 39 | MAIN_DEPENDENCY "${SRCPATH}" 40 | COMMAND "${PACKAGE_PREFIX_DIR}/tools/shaderc/glslc.exe" "${SRCPATH}" -o "${DSTPATH}") 41 | list(APPEND SHADER_OUTPUT "${DSTPATH}") 42 | set(SHADER_OUTPUT "${SHADER_OUTPUT}" PARENT_SCOPE) 43 | endfunction() 44 | add_shader("shader.vert" "vert.spv") 45 | add_shader("shader.frag" "frag.spv") 46 | add_custom_target(shader_bytecode DEPENDS ${SHADER_OUTPUT}) 47 | add_dependencies(vksample shader_bytecode) 48 | 49 | add_custom_command(TARGET vksample POST_BUILD 50 | COMMAND ${CMAKE_COMMAND} -E copy_directory 51 | "${CMAKE_SOURCE_DIR}/textures" 52 | "${CMAKE_CURRENT_BINARY_DIR}/textures") 53 | 54 | add_custom_command(TARGET vksample POST_BUILD 55 | COMMAND ${CMAKE_COMMAND} -E copy_directory 56 | "${CMAKE_SOURCE_DIR}/models" 57 | "${CMAKE_CURRENT_BINARY_DIR}/models") 58 | 59 | set_property(TARGET vksample PROPERTY CXX_STANDARD 20) 60 | 61 | # TODO: Add tests and install targets if needed. 62 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "configurePresets": [ 4 | { 5 | "name": "windows-base", 6 | "hidden": true, 7 | "generator": "Ninja", 8 | "binaryDir": "${sourceDir}/out/build/${presetName}", 9 | "installDir": "${sourceDir}/out/install/${presetName}", 10 | "cacheVariables": { 11 | "CMAKE_C_COMPILER": "cl.exe", 12 | "CMAKE_CXX_COMPILER": "cl.exe", 13 | "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" 14 | }, 15 | "condition": { 16 | "type": "equals", 17 | "lhs": "${hostSystemName}", 18 | "rhs": "Windows" 19 | } 20 | }, 21 | { 22 | "name": "x64-debug", 23 | "displayName": "x64 Debug", 24 | "inherits": "windows-base", 25 | "architecture": { 26 | "value": "x64", 27 | "strategy": "external" 28 | }, 29 | "cacheVariables": { 30 | "CMAKE_BUILD_TYPE": "Debug" 31 | } 32 | }, 33 | { 34 | "name": "x64-release", 35 | "displayName": "x64 Release", 36 | "inherits": "x64-debug", 37 | "cacheVariables": { 38 | "CMAKE_BUILD_TYPE": "Release" 39 | } 40 | }, 41 | { 42 | "name": "x86-debug", 43 | "displayName": "x86 Debug", 44 | "inherits": "windows-base", 45 | "architecture": { 46 | "value": "x86", 47 | "strategy": "external" 48 | }, 49 | "cacheVariables": { 50 | "CMAKE_BUILD_TYPE": "Debug" 51 | } 52 | }, 53 | { 54 | "name": "x86-release", 55 | "displayName": "x86 Release", 56 | "inherits": "x86-debug", 57 | "cacheVariables": { 58 | "CMAKE_BUILD_TYPE": "Release" 59 | } 60 | }, 61 | { 62 | "name": "linux-debug", 63 | "displayName": "Linux Debug", 64 | "generator": "Ninja", 65 | "binaryDir": "${sourceDir}/out/build/${presetName}", 66 | "installDir": "${sourceDir}/out/install/${presetName}", 67 | "cacheVariables": { 68 | "CMAKE_BUILD_TYPE": "Debug" 69 | }, 70 | "condition": { 71 | "type": "equals", 72 | "lhs": "${hostSystemName}", 73 | "rhs": "Linux" 74 | }, 75 | "vendor": { 76 | "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { 77 | "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" 78 | } 79 | } 80 | }, 81 | { 82 | "name": "macos-debug", 83 | "displayName": "macOS Debug", 84 | "generator": "Ninja", 85 | "binaryDir": "${sourceDir}/out/build/${presetName}", 86 | "installDir": "${sourceDir}/out/install/${presetName}", 87 | "cacheVariables": { 88 | "CMAKE_BUILD_TYPE": "Debug" 89 | }, 90 | "condition": { 91 | "type": "equals", 92 | "lhs": "${hostSystemName}", 93 | "rhs": "Darwin" 94 | }, 95 | "vendor": { 96 | "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { 97 | "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" 98 | } 99 | } 100 | } 101 | ] 102 | } 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vulkan CMake VCPkg Example 2 | 3 | This is a cleanly setup project to configure and build the [Multisampling Example](https://vulkan-tutorial.com/Multisampling) from the [Vulkan Tutorial Website](https://vulkan-tutorial.com/), using CMake and VCPkg, with all dependencies set up correctly. The goal is to have something that will *Just Work* so you can focus on the Vulkan stuff instead of the configuration stuff. 4 | 5 | I have intentionally contributed nothing notable to the C++ and shader code, which is as close to unmodified as possible. The interesting/useful bits here are the build and dependency configuration. Hopefully this will serve as a starting point to clone your own empty Vulkan project. 6 | 7 | The Multisample tutorial was chosen because it was the last sample before the Vulkan tutorial switched to compute shaders. It has all the bits to load models and textures and get them on-screen. 8 | 9 | 10 | 11 | ## How to use 12 | On Windows, the steps to make this *Just Work* are: 13 | 14 | 1. Install Visual Studio 2022 Community Edition. Make sure you include the desktop C++ tools, VCPkg, CMake, and Git for Windows. 15 | 2. Open a Visual Studio terminal window (i.e. with vcvars included) and run `vcpkg integrate install` if you never have. 16 | 3. Clone the repository. Just this repository. 17 | 4. Open the repo's directory in Visual Studio as a CMake project. 18 | 5. Wait a bit for vcpkg to fetch and resolve and build the dependencies. 19 | 6. Hit [F5] to build the vksample.exe project itself. 20 | 21 | ## How it works 22 | 23 | STB, GLFW, GLM, GLSLC, the Vulkan SDK, and the Vulkan validation layers are all downloaded and set up through VCPkg. 24 | ``` 25 | { 26 | "dependencies": [ 27 | "glfw3", 28 | "glm", 29 | "stb", 30 | "tinyobjloader", 31 | "vulkan-sdk-components", 32 | "vulkan-validationlayers" 33 | ] 34 | } 35 | ``` 36 | 37 | The correct dependencies for all of them are all in the CMakeLists.txt file. 38 | ``` 39 | find_package(Vulkan REQUIRED) 40 | target_link_libraries(vksample PRIVATE Vulkan::Vulkan) 41 | 42 | find_package(glfw3 CONFIG REQUIRED) 43 | target_link_libraries(vksample PRIVATE glfw) 44 | 45 | find_package(Stb REQUIRED) 46 | target_include_directories(vksample PRIVATE ${Stb_INCLUDE_DIR}) 47 | 48 | find_package(glm CONFIG REQUIRED) 49 | target_link_libraries(vksample PRIVATE glm::glm) 50 | 51 | find_package(tinyobjloader CONFIG REQUIRED) 52 | target_link_libraries(vksample PRIVATE tinyobjloader::tinyobjloader) 53 | ``` 54 | 55 | CMake uses GLSLC to build the shaders and deploy the SPIR-V bytecode to the output directory, and rebuild them as needed if you change the shader source. 56 | ``` 57 | set(SHADER_OUTPUT "") 58 | function(add_shader SRCNAME DSTNAME) 59 | set(SRCPATH "${CMAKE_SOURCE_DIR}/shaders/${SRCNAME}") 60 | set(DSTPATH "${CMAKE_CURRENT_BINARY_DIR}/shaders/${DSTNAME}") 61 | add_custom_command( 62 | OUTPUT "${DSTPATH}" 63 | MAIN_DEPENDENCY "${SRCPATH}" 64 | COMMAND "${PACKAGE_PREFIX_DIR}/tools/shaderc/glslc.exe" "${SRCPATH}" -o "${DSTPATH}") 65 | list(APPEND SHADER_OUTPUT "${DSTPATH}") 66 | set(SHADER_OUTPUT "${SHADER_OUTPUT}" PARENT_SCOPE) 67 | endfunction() 68 | add_shader("shader.vert" "vert.spv") 69 | add_shader("shader.frag" "frag.spv") 70 | add_custom_target(shader_bytecode DEPENDS ${SHADER_OUTPUT}) 71 | add_dependencies(vksample shader_bytecode) 72 | ``` 73 | 74 | CMake also copies the contents of the `models` and `textures` directories to your output directory as part of the build process, where they can be loaded by the C++ code. 75 | ``` 76 | add_custom_command(TARGET vksample POST_BUILD 77 | COMMAND ${CMAKE_COMMAND} -E copy_directory 78 | "${CMAKE_SOURCE_DIR}/textures" 79 | "${CMAKE_CURRENT_BINARY_DIR}/textures") 80 | 81 | add_custom_command(TARGET vksample POST_BUILD 82 | COMMAND ${CMAKE_COMMAND} -E copy_directory 83 | "${CMAKE_SOURCE_DIR}/models" 84 | "${CMAKE_CURRENT_BINARY_DIR}/models") 85 | ``` 86 | 87 | Note that the models directory has a special exclusion in `.gitignore` so that `*.obj` files there are NOT ignored. Unfortunately, `.obj` is an extension for both compiled object code and mesh objects, and we don't want to ignore our meshes! 88 | ``` 89 | # We exclude .obj in general, but we want them under the models directory 90 | !models/*.obj 91 | ``` 92 | 93 | ## Finding the Vulkan Validation Layers 94 | 95 | The Vulkan Validation layers are a slight pain, because they require you to set a `VK_ADD_LAYER_PATH` environment variable to point to the DLLs. When the SDK comes in via VCPkg that's not going to be in a consistent place on your system. To work around that, I made a single change to the tutorial source code. At the top of the `main()` function, before loading Vulkan, I set that environment variable (only for the current project) based on your launch path: 96 | ``` 97 | int main() { 98 | auto path = std::filesystem::current_path() / "vcpkg_installed" / 99 | "x64-windows" / "bin"; 100 | std::string set = "VK_ADD_LAYER_PATH=" + path.string(); 101 | _putenv(set.c_str()); 102 | ``` 103 | 104 | It's ugly, and it'll need to change for targets other than x64-windows. I don't have such targets to test on, but would welcome a pull request from you! 105 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd 364 | 365 | # We exclude .obj in general, but we want them under the models directory 366 | !models/*.obj -------------------------------------------------------------------------------- /vksample.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define GLFW_INCLUDE_VULKAN 4 | #include 5 | 6 | #define GLM_FORCE_RADIANS 7 | #define GLM_FORCE_DEPTH_ZERO_TO_ONE 8 | #define GLM_ENABLE_EXPERIMENTAL 9 | #include 10 | #include 11 | #include 12 | 13 | #define STB_IMAGE_IMPLEMENTATION 14 | #include 15 | 16 | #define TINYOBJLOADER_IMPLEMENTATION 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | const uint32_t WIDTH = 800; 35 | const uint32_t HEIGHT = 600; 36 | 37 | const std::string MODEL_PATH = "models/viking_room.obj"; 38 | const std::string TEXTURE_PATH = "textures/viking_room.png"; 39 | 40 | const int MAX_FRAMES_IN_FLIGHT = 2; 41 | 42 | const std::vector validationLayers = { 43 | "VK_LAYER_KHRONOS_validation"}; 44 | 45 | const std::vector deviceExtensions = { 46 | VK_KHR_SWAPCHAIN_EXTENSION_NAME}; 47 | 48 | #if defined(NDEBUG) || 0 49 | const bool enableValidationLayers = false; 50 | #else 51 | const bool enableValidationLayers = true; 52 | #endif 53 | 54 | VkResult CreateDebugUtilsMessengerEXT( 55 | VkInstance instance, 56 | const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, 57 | const VkAllocationCallbacks* pAllocator, 58 | VkDebugUtilsMessengerEXT* pDebugMessenger) { 59 | auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( 60 | instance, "vkCreateDebugUtilsMessengerEXT"); 61 | if (func != nullptr) { 62 | return func(instance, pCreateInfo, pAllocator, pDebugMessenger); 63 | } else { 64 | return VK_ERROR_EXTENSION_NOT_PRESENT; 65 | } 66 | } 67 | 68 | void DestroyDebugUtilsMessengerEXT(VkInstance instance, 69 | VkDebugUtilsMessengerEXT debugMessenger, 70 | const VkAllocationCallbacks* pAllocator) { 71 | auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( 72 | instance, "vkDestroyDebugUtilsMessengerEXT"); 73 | if (func != nullptr) { 74 | func(instance, debugMessenger, pAllocator); 75 | } 76 | } 77 | 78 | struct QueueFamilyIndices { 79 | std::optional graphicsFamily; 80 | std::optional presentFamily; 81 | 82 | bool isComplete() { 83 | return graphicsFamily.has_value() && presentFamily.has_value(); 84 | } 85 | }; 86 | 87 | struct SwapChainSupportDetails { 88 | VkSurfaceCapabilitiesKHR capabilities; 89 | std::vector formats; 90 | std::vector presentModes; 91 | }; 92 | 93 | struct Vertex { 94 | glm::vec3 pos; 95 | glm::vec3 color; 96 | glm::vec2 texCoord; 97 | 98 | static VkVertexInputBindingDescription getBindingDescription() { 99 | VkVertexInputBindingDescription bindingDescription{}; 100 | bindingDescription.binding = 0; 101 | bindingDescription.stride = sizeof(Vertex); 102 | bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; 103 | 104 | return bindingDescription; 105 | } 106 | 107 | static std::array 108 | getAttributeDescriptions() { 109 | std::array attributeDescriptions{}; 110 | 111 | attributeDescriptions[0].binding = 0; 112 | attributeDescriptions[0].location = 0; 113 | attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT; 114 | attributeDescriptions[0].offset = offsetof(Vertex, pos); 115 | 116 | attributeDescriptions[1].binding = 0; 117 | attributeDescriptions[1].location = 1; 118 | attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT; 119 | attributeDescriptions[1].offset = offsetof(Vertex, color); 120 | 121 | attributeDescriptions[2].binding = 0; 122 | attributeDescriptions[2].location = 2; 123 | attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT; 124 | attributeDescriptions[2].offset = offsetof(Vertex, texCoord); 125 | 126 | return attributeDescriptions; 127 | } 128 | 129 | bool operator==(const Vertex& other) const { 130 | return pos == other.pos && color == other.color && 131 | texCoord == other.texCoord; 132 | } 133 | }; 134 | 135 | namespace std { 136 | template <> 137 | struct hash { 138 | size_t operator()(Vertex const& vertex) const { 139 | return ((hash()(vertex.pos) ^ 140 | (hash()(vertex.color) << 1)) >> 141 | 1) ^ 142 | (hash()(vertex.texCoord) << 1); 143 | } 144 | }; 145 | } // namespace std 146 | 147 | struct UniformBufferObject { 148 | alignas(16) glm::mat4 model; 149 | alignas(16) glm::mat4 view; 150 | alignas(16) glm::mat4 proj; 151 | }; 152 | 153 | class HelloTriangleApplication { 154 | public: 155 | void run() { 156 | initWindow(); 157 | initVulkan(); 158 | mainLoop(); 159 | cleanup(); 160 | } 161 | 162 | private: 163 | GLFWwindow* window; 164 | 165 | VkInstance instance; 166 | VkDebugUtilsMessengerEXT debugMessenger; 167 | VkSurfaceKHR surface; 168 | 169 | VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; 170 | VkSampleCountFlagBits msaaSamples = VK_SAMPLE_COUNT_1_BIT; 171 | VkDevice device; 172 | 173 | VkQueue graphicsQueue; 174 | VkQueue presentQueue; 175 | 176 | VkSwapchainKHR swapChain; 177 | std::vector swapChainImages; 178 | VkFormat swapChainImageFormat; 179 | VkExtent2D swapChainExtent; 180 | std::vector swapChainImageViews; 181 | std::vector swapChainFramebuffers; 182 | 183 | VkRenderPass renderPass; 184 | VkDescriptorSetLayout descriptorSetLayout; 185 | VkPipelineLayout pipelineLayout; 186 | VkPipeline graphicsPipeline; 187 | 188 | VkCommandPool commandPool; 189 | 190 | VkImage colorImage; 191 | VkDeviceMemory colorImageMemory; 192 | VkImageView colorImageView; 193 | 194 | VkImage depthImage; 195 | VkDeviceMemory depthImageMemory; 196 | VkImageView depthImageView; 197 | 198 | uint32_t mipLevels; 199 | VkImage textureImage; 200 | VkDeviceMemory textureImageMemory; 201 | VkImageView textureImageView; 202 | VkSampler textureSampler; 203 | 204 | std::vector vertices; 205 | std::vector indices; 206 | VkBuffer vertexBuffer; 207 | VkDeviceMemory vertexBufferMemory; 208 | VkBuffer indexBuffer; 209 | VkDeviceMemory indexBufferMemory; 210 | 211 | std::vector uniformBuffers; 212 | std::vector uniformBuffersMemory; 213 | std::vector uniformBuffersMapped; 214 | 215 | VkDescriptorPool descriptorPool; 216 | std::vector descriptorSets; 217 | 218 | std::vector commandBuffers; 219 | 220 | std::vector imageAvailableSemaphores; 221 | std::vector renderFinishedSemaphores; 222 | std::vector inFlightFences; 223 | uint32_t currentFrame = 0; 224 | 225 | bool framebufferResized = false; 226 | 227 | void initWindow() { 228 | glfwInit(); 229 | 230 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 231 | 232 | window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); 233 | glfwSetWindowUserPointer(window, this); 234 | glfwSetFramebufferSizeCallback(window, framebufferResizeCallback); 235 | } 236 | 237 | static void framebufferResizeCallback(GLFWwindow* window, 238 | int width, 239 | int height) { 240 | auto app = reinterpret_cast( 241 | glfwGetWindowUserPointer(window)); 242 | app->framebufferResized = true; 243 | } 244 | 245 | void initVulkan() { 246 | createInstance(); 247 | setupDebugMessenger(); 248 | createSurface(); 249 | pickPhysicalDevice(); 250 | createLogicalDevice(); 251 | createSwapChain(); 252 | createImageViews(); 253 | createRenderPass(); 254 | createDescriptorSetLayout(); 255 | createGraphicsPipeline(); 256 | createCommandPool(); 257 | createColorResources(); 258 | createDepthResources(); 259 | createFramebuffers(); 260 | createTextureImage(); 261 | createTextureImageView(); 262 | createTextureSampler(); 263 | loadModel(); 264 | createVertexBuffer(); 265 | createIndexBuffer(); 266 | createUniformBuffers(); 267 | createDescriptorPool(); 268 | createDescriptorSets(); 269 | createCommandBuffers(); 270 | createSyncObjects(); 271 | } 272 | 273 | void mainLoop() { 274 | while (!glfwWindowShouldClose(window)) { 275 | glfwPollEvents(); 276 | drawFrame(); 277 | } 278 | 279 | vkDeviceWaitIdle(device); 280 | } 281 | 282 | void cleanupSwapChain() { 283 | vkDestroyImageView(device, depthImageView, nullptr); 284 | vkDestroyImage(device, depthImage, nullptr); 285 | vkFreeMemory(device, depthImageMemory, nullptr); 286 | 287 | vkDestroyImageView(device, colorImageView, nullptr); 288 | vkDestroyImage(device, colorImage, nullptr); 289 | vkFreeMemory(device, colorImageMemory, nullptr); 290 | 291 | for (auto framebuffer : swapChainFramebuffers) { 292 | vkDestroyFramebuffer(device, framebuffer, nullptr); 293 | } 294 | 295 | for (auto imageView : swapChainImageViews) { 296 | vkDestroyImageView(device, imageView, nullptr); 297 | } 298 | 299 | vkDestroySwapchainKHR(device, swapChain, nullptr); 300 | } 301 | 302 | void cleanup() { 303 | cleanupSwapChain(); 304 | 305 | vkDestroyPipeline(device, graphicsPipeline, nullptr); 306 | vkDestroyPipelineLayout(device, pipelineLayout, nullptr); 307 | vkDestroyRenderPass(device, renderPass, nullptr); 308 | 309 | for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 310 | vkDestroyBuffer(device, uniformBuffers[i], nullptr); 311 | vkFreeMemory(device, uniformBuffersMemory[i], nullptr); 312 | } 313 | 314 | vkDestroyDescriptorPool(device, descriptorPool, nullptr); 315 | 316 | vkDestroySampler(device, textureSampler, nullptr); 317 | vkDestroyImageView(device, textureImageView, nullptr); 318 | 319 | vkDestroyImage(device, textureImage, nullptr); 320 | vkFreeMemory(device, textureImageMemory, nullptr); 321 | 322 | vkDestroyDescriptorSetLayout(device, descriptorSetLayout, nullptr); 323 | 324 | vkDestroyBuffer(device, indexBuffer, nullptr); 325 | vkFreeMemory(device, indexBufferMemory, nullptr); 326 | 327 | vkDestroyBuffer(device, vertexBuffer, nullptr); 328 | vkFreeMemory(device, vertexBufferMemory, nullptr); 329 | 330 | for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 331 | vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); 332 | vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); 333 | vkDestroyFence(device, inFlightFences[i], nullptr); 334 | } 335 | 336 | vkDestroyCommandPool(device, commandPool, nullptr); 337 | 338 | vkDestroyDevice(device, nullptr); 339 | 340 | if (enableValidationLayers) { 341 | DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr); 342 | } 343 | 344 | vkDestroySurfaceKHR(instance, surface, nullptr); 345 | vkDestroyInstance(instance, nullptr); 346 | 347 | glfwDestroyWindow(window); 348 | 349 | glfwTerminate(); 350 | } 351 | 352 | void recreateSwapChain() { 353 | int width = 0, height = 0; 354 | glfwGetFramebufferSize(window, &width, &height); 355 | while (width == 0 || height == 0) { 356 | glfwGetFramebufferSize(window, &width, &height); 357 | glfwWaitEvents(); 358 | } 359 | 360 | vkDeviceWaitIdle(device); 361 | 362 | cleanupSwapChain(); 363 | 364 | createSwapChain(); 365 | createImageViews(); 366 | createColorResources(); 367 | createDepthResources(); 368 | createFramebuffers(); 369 | } 370 | 371 | void createInstance() { 372 | if (enableValidationLayers && !checkValidationLayerSupport()) { 373 | throw std::runtime_error( 374 | "validation layers requested, but not available!"); 375 | } 376 | 377 | VkApplicationInfo appInfo{}; 378 | appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 379 | appInfo.pApplicationName = "Hello Triangle"; 380 | appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); 381 | appInfo.pEngineName = "No Engine"; 382 | appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); 383 | appInfo.apiVersion = VK_API_VERSION_1_0; 384 | 385 | VkInstanceCreateInfo createInfo{}; 386 | createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 387 | createInfo.pApplicationInfo = &appInfo; 388 | 389 | auto extensions = getRequiredExtensions(); 390 | createInfo.enabledExtensionCount = static_cast(extensions.size()); 391 | createInfo.ppEnabledExtensionNames = extensions.data(); 392 | 393 | VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{}; 394 | if (enableValidationLayers) { 395 | createInfo.enabledLayerCount = 396 | static_cast(validationLayers.size()); 397 | createInfo.ppEnabledLayerNames = validationLayers.data(); 398 | 399 | populateDebugMessengerCreateInfo(debugCreateInfo); 400 | createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*)&debugCreateInfo; 401 | } else { 402 | createInfo.enabledLayerCount = 0; 403 | 404 | createInfo.pNext = nullptr; 405 | } 406 | 407 | if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { 408 | throw std::runtime_error("failed to create instance!"); 409 | } 410 | } 411 | 412 | void populateDebugMessengerCreateInfo( 413 | VkDebugUtilsMessengerCreateInfoEXT& createInfo) { 414 | createInfo = {}; 415 | createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; 416 | createInfo.messageSeverity = 417 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | 418 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | 419 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; 420 | createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | 421 | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | 422 | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; 423 | createInfo.pfnUserCallback = debugCallback; 424 | } 425 | 426 | void setupDebugMessenger() { 427 | if (!enableValidationLayers) 428 | return; 429 | 430 | VkDebugUtilsMessengerCreateInfoEXT createInfo; 431 | populateDebugMessengerCreateInfo(createInfo); 432 | 433 | if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, 434 | &debugMessenger) != VK_SUCCESS) { 435 | throw std::runtime_error("failed to set up debug messenger!"); 436 | } 437 | } 438 | 439 | void createSurface() { 440 | if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != 441 | VK_SUCCESS) { 442 | throw std::runtime_error("failed to create window surface!"); 443 | } 444 | } 445 | 446 | void pickPhysicalDevice() { 447 | uint32_t deviceCount = 0; 448 | vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); 449 | 450 | if (deviceCount == 0) { 451 | throw std::runtime_error("failed to find GPUs with Vulkan support!"); 452 | } 453 | 454 | std::vector devices(deviceCount); 455 | vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); 456 | 457 | for (const auto& device : devices) { 458 | if (isDeviceSuitable(device)) { 459 | physicalDevice = device; 460 | msaaSamples = getMaxUsableSampleCount(); 461 | break; 462 | } 463 | } 464 | 465 | if (physicalDevice == VK_NULL_HANDLE) { 466 | throw std::runtime_error("failed to find a suitable GPU!"); 467 | } 468 | } 469 | 470 | void createLogicalDevice() { 471 | QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 472 | 473 | std::vector queueCreateInfos; 474 | std::set uniqueQueueFamilies = {indices.graphicsFamily.value(), 475 | indices.presentFamily.value()}; 476 | 477 | float queuePriority = 1.0f; 478 | for (uint32_t queueFamily : uniqueQueueFamilies) { 479 | VkDeviceQueueCreateInfo queueCreateInfo{}; 480 | queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 481 | queueCreateInfo.queueFamilyIndex = queueFamily; 482 | queueCreateInfo.queueCount = 1; 483 | queueCreateInfo.pQueuePriorities = &queuePriority; 484 | queueCreateInfos.push_back(queueCreateInfo); 485 | } 486 | 487 | VkPhysicalDeviceFeatures deviceFeatures{}; 488 | deviceFeatures.samplerAnisotropy = VK_TRUE; 489 | 490 | VkDeviceCreateInfo createInfo{}; 491 | createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 492 | 493 | createInfo.queueCreateInfoCount = 494 | static_cast(queueCreateInfos.size()); 495 | createInfo.pQueueCreateInfos = queueCreateInfos.data(); 496 | 497 | createInfo.pEnabledFeatures = &deviceFeatures; 498 | 499 | createInfo.enabledExtensionCount = 500 | static_cast(deviceExtensions.size()); 501 | createInfo.ppEnabledExtensionNames = deviceExtensions.data(); 502 | 503 | if (enableValidationLayers) { 504 | createInfo.enabledLayerCount = 505 | static_cast(validationLayers.size()); 506 | createInfo.ppEnabledLayerNames = validationLayers.data(); 507 | } else { 508 | createInfo.enabledLayerCount = 0; 509 | } 510 | 511 | if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != 512 | VK_SUCCESS) { 513 | throw std::runtime_error("failed to create logical device!"); 514 | } 515 | 516 | vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue); 517 | vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue); 518 | } 519 | 520 | void createSwapChain() { 521 | SwapChainSupportDetails swapChainSupport = 522 | querySwapChainSupport(physicalDevice); 523 | 524 | VkSurfaceFormatKHR surfaceFormat = 525 | chooseSwapSurfaceFormat(swapChainSupport.formats); 526 | VkPresentModeKHR presentMode = 527 | chooseSwapPresentMode(swapChainSupport.presentModes); 528 | VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); 529 | 530 | uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1; 531 | if (swapChainSupport.capabilities.maxImageCount > 0 && 532 | imageCount > swapChainSupport.capabilities.maxImageCount) { 533 | imageCount = swapChainSupport.capabilities.maxImageCount; 534 | } 535 | 536 | VkSwapchainCreateInfoKHR createInfo{}; 537 | createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; 538 | createInfo.surface = surface; 539 | 540 | createInfo.minImageCount = imageCount; 541 | createInfo.imageFormat = surfaceFormat.format; 542 | createInfo.imageColorSpace = surfaceFormat.colorSpace; 543 | createInfo.imageExtent = extent; 544 | createInfo.imageArrayLayers = 1; 545 | createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; 546 | 547 | QueueFamilyIndices indices = findQueueFamilies(physicalDevice); 548 | uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), 549 | indices.presentFamily.value()}; 550 | 551 | if (indices.graphicsFamily != indices.presentFamily) { 552 | createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; 553 | createInfo.queueFamilyIndexCount = 2; 554 | createInfo.pQueueFamilyIndices = queueFamilyIndices; 555 | } else { 556 | createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; 557 | } 558 | 559 | createInfo.preTransform = swapChainSupport.capabilities.currentTransform; 560 | createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; 561 | createInfo.presentMode = presentMode; 562 | createInfo.clipped = VK_TRUE; 563 | 564 | if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != 565 | VK_SUCCESS) { 566 | throw std::runtime_error("failed to create swap chain!"); 567 | } 568 | 569 | vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr); 570 | swapChainImages.resize(imageCount); 571 | vkGetSwapchainImagesKHR(device, swapChain, &imageCount, 572 | swapChainImages.data()); 573 | 574 | swapChainImageFormat = surfaceFormat.format; 575 | swapChainExtent = extent; 576 | } 577 | 578 | void createImageViews() { 579 | swapChainImageViews.resize(swapChainImages.size()); 580 | 581 | for (uint32_t i = 0; i < swapChainImages.size(); i++) { 582 | swapChainImageViews[i] = 583 | createImageView(swapChainImages[i], swapChainImageFormat, 584 | VK_IMAGE_ASPECT_COLOR_BIT, 1); 585 | } 586 | } 587 | 588 | void createRenderPass() { 589 | VkAttachmentDescription colorAttachment{}; 590 | colorAttachment.format = swapChainImageFormat; 591 | colorAttachment.samples = msaaSamples; 592 | colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; 593 | colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; 594 | colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; 595 | colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; 596 | colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 597 | colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 598 | 599 | VkAttachmentDescription depthAttachment{}; 600 | depthAttachment.format = findDepthFormat(); 601 | depthAttachment.samples = msaaSamples; 602 | depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; 603 | depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; 604 | depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; 605 | depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; 606 | depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 607 | depthAttachment.finalLayout = 608 | VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; 609 | 610 | VkAttachmentDescription colorAttachmentResolve{}; 611 | colorAttachmentResolve.format = swapChainImageFormat; 612 | colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT; 613 | colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; 614 | colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE; 615 | colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; 616 | colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; 617 | colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 618 | colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; 619 | 620 | VkAttachmentReference colorAttachmentRef{}; 621 | colorAttachmentRef.attachment = 0; 622 | colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 623 | 624 | VkAttachmentReference depthAttachmentRef{}; 625 | depthAttachmentRef.attachment = 1; 626 | depthAttachmentRef.layout = 627 | VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; 628 | 629 | VkAttachmentReference colorAttachmentResolveRef{}; 630 | colorAttachmentResolveRef.attachment = 2; 631 | colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; 632 | 633 | VkSubpassDescription subpass{}; 634 | subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; 635 | subpass.colorAttachmentCount = 1; 636 | subpass.pColorAttachments = &colorAttachmentRef; 637 | subpass.pDepthStencilAttachment = &depthAttachmentRef; 638 | subpass.pResolveAttachments = &colorAttachmentResolveRef; 639 | 640 | VkSubpassDependency dependency{}; 641 | dependency.srcSubpass = VK_SUBPASS_EXTERNAL; 642 | dependency.dstSubpass = 0; 643 | dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | 644 | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; 645 | dependency.srcAccessMask = 0; 646 | dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | 647 | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; 648 | dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | 649 | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; 650 | 651 | std::array attachments = { 652 | colorAttachment, depthAttachment, colorAttachmentResolve}; 653 | VkRenderPassCreateInfo renderPassInfo{}; 654 | renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; 655 | renderPassInfo.attachmentCount = static_cast(attachments.size()); 656 | renderPassInfo.pAttachments = attachments.data(); 657 | renderPassInfo.subpassCount = 1; 658 | renderPassInfo.pSubpasses = &subpass; 659 | renderPassInfo.dependencyCount = 1; 660 | renderPassInfo.pDependencies = &dependency; 661 | 662 | if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != 663 | VK_SUCCESS) { 664 | throw std::runtime_error("failed to create render pass!"); 665 | } 666 | } 667 | 668 | void createDescriptorSetLayout() { 669 | VkDescriptorSetLayoutBinding uboLayoutBinding{}; 670 | uboLayoutBinding.binding = 0; 671 | uboLayoutBinding.descriptorCount = 1; 672 | uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; 673 | uboLayoutBinding.pImmutableSamplers = nullptr; 674 | uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; 675 | 676 | VkDescriptorSetLayoutBinding samplerLayoutBinding{}; 677 | samplerLayoutBinding.binding = 1; 678 | samplerLayoutBinding.descriptorCount = 1; 679 | samplerLayoutBinding.descriptorType = 680 | VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; 681 | samplerLayoutBinding.pImmutableSamplers = nullptr; 682 | samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; 683 | 684 | std::array bindings = { 685 | uboLayoutBinding, samplerLayoutBinding}; 686 | VkDescriptorSetLayoutCreateInfo layoutInfo{}; 687 | layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; 688 | layoutInfo.bindingCount = static_cast(bindings.size()); 689 | layoutInfo.pBindings = bindings.data(); 690 | 691 | if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, 692 | &descriptorSetLayout) != VK_SUCCESS) { 693 | throw std::runtime_error("failed to create descriptor set layout!"); 694 | } 695 | } 696 | 697 | void createGraphicsPipeline() { 698 | auto vertShaderCode = readFile("shaders/vert.spv"); 699 | auto fragShaderCode = readFile("shaders/frag.spv"); 700 | 701 | VkShaderModule vertShaderModule = createShaderModule(vertShaderCode); 702 | VkShaderModule fragShaderModule = createShaderModule(fragShaderCode); 703 | 704 | VkPipelineShaderStageCreateInfo vertShaderStageInfo{}; 705 | vertShaderStageInfo.sType = 706 | VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; 707 | vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; 708 | vertShaderStageInfo.module = vertShaderModule; 709 | vertShaderStageInfo.pName = "main"; 710 | 711 | VkPipelineShaderStageCreateInfo fragShaderStageInfo{}; 712 | fragShaderStageInfo.sType = 713 | VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; 714 | fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; 715 | fragShaderStageInfo.module = fragShaderModule; 716 | fragShaderStageInfo.pName = "main"; 717 | 718 | VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, 719 | fragShaderStageInfo}; 720 | 721 | VkPipelineVertexInputStateCreateInfo vertexInputInfo{}; 722 | vertexInputInfo.sType = 723 | VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; 724 | 725 | auto bindingDescription = Vertex::getBindingDescription(); 726 | auto attributeDescriptions = Vertex::getAttributeDescriptions(); 727 | 728 | vertexInputInfo.vertexBindingDescriptionCount = 1; 729 | vertexInputInfo.vertexAttributeDescriptionCount = 730 | static_cast(attributeDescriptions.size()); 731 | vertexInputInfo.pVertexBindingDescriptions = &bindingDescription; 732 | vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data(); 733 | 734 | VkPipelineInputAssemblyStateCreateInfo inputAssembly{}; 735 | inputAssembly.sType = 736 | VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; 737 | inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; 738 | inputAssembly.primitiveRestartEnable = VK_FALSE; 739 | 740 | VkPipelineViewportStateCreateInfo viewportState{}; 741 | viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; 742 | viewportState.viewportCount = 1; 743 | viewportState.scissorCount = 1; 744 | 745 | VkPipelineRasterizationStateCreateInfo rasterizer{}; 746 | rasterizer.sType = 747 | VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; 748 | rasterizer.depthClampEnable = VK_FALSE; 749 | rasterizer.rasterizerDiscardEnable = VK_FALSE; 750 | rasterizer.polygonMode = VK_POLYGON_MODE_FILL; 751 | rasterizer.lineWidth = 1.0f; 752 | rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; 753 | rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; 754 | rasterizer.depthBiasEnable = VK_FALSE; 755 | 756 | VkPipelineMultisampleStateCreateInfo multisampling{}; 757 | multisampling.sType = 758 | VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; 759 | multisampling.sampleShadingEnable = VK_FALSE; 760 | multisampling.rasterizationSamples = msaaSamples; 761 | 762 | VkPipelineDepthStencilStateCreateInfo depthStencil{}; 763 | depthStencil.sType = 764 | VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; 765 | depthStencil.depthTestEnable = VK_TRUE; 766 | depthStencil.depthWriteEnable = VK_TRUE; 767 | depthStencil.depthCompareOp = VK_COMPARE_OP_LESS; 768 | depthStencil.depthBoundsTestEnable = VK_FALSE; 769 | depthStencil.stencilTestEnable = VK_FALSE; 770 | 771 | VkPipelineColorBlendAttachmentState colorBlendAttachment{}; 772 | colorBlendAttachment.colorWriteMask = 773 | VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | 774 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; 775 | colorBlendAttachment.blendEnable = VK_FALSE; 776 | 777 | VkPipelineColorBlendStateCreateInfo colorBlending{}; 778 | colorBlending.sType = 779 | VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; 780 | colorBlending.logicOpEnable = VK_FALSE; 781 | colorBlending.logicOp = VK_LOGIC_OP_COPY; 782 | colorBlending.attachmentCount = 1; 783 | colorBlending.pAttachments = &colorBlendAttachment; 784 | colorBlending.blendConstants[0] = 0.0f; 785 | colorBlending.blendConstants[1] = 0.0f; 786 | colorBlending.blendConstants[2] = 0.0f; 787 | colorBlending.blendConstants[3] = 0.0f; 788 | 789 | std::vector dynamicStates = {VK_DYNAMIC_STATE_VIEWPORT, 790 | VK_DYNAMIC_STATE_SCISSOR}; 791 | VkPipelineDynamicStateCreateInfo dynamicState{}; 792 | dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; 793 | dynamicState.dynamicStateCount = 794 | static_cast(dynamicStates.size()); 795 | dynamicState.pDynamicStates = dynamicStates.data(); 796 | 797 | VkPipelineLayoutCreateInfo pipelineLayoutInfo{}; 798 | pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; 799 | pipelineLayoutInfo.setLayoutCount = 1; 800 | pipelineLayoutInfo.pSetLayouts = &descriptorSetLayout; 801 | 802 | if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, 803 | &pipelineLayout) != VK_SUCCESS) { 804 | throw std::runtime_error("failed to create pipeline layout!"); 805 | } 806 | 807 | VkGraphicsPipelineCreateInfo pipelineInfo{}; 808 | pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; 809 | pipelineInfo.stageCount = 2; 810 | pipelineInfo.pStages = shaderStages; 811 | pipelineInfo.pVertexInputState = &vertexInputInfo; 812 | pipelineInfo.pInputAssemblyState = &inputAssembly; 813 | pipelineInfo.pViewportState = &viewportState; 814 | pipelineInfo.pRasterizationState = &rasterizer; 815 | pipelineInfo.pMultisampleState = &multisampling; 816 | pipelineInfo.pDepthStencilState = &depthStencil; 817 | pipelineInfo.pColorBlendState = &colorBlending; 818 | pipelineInfo.pDynamicState = &dynamicState; 819 | pipelineInfo.layout = pipelineLayout; 820 | pipelineInfo.renderPass = renderPass; 821 | pipelineInfo.subpass = 0; 822 | pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; 823 | 824 | if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, 825 | nullptr, &graphicsPipeline) != VK_SUCCESS) { 826 | throw std::runtime_error("failed to create graphics pipeline!"); 827 | } 828 | 829 | vkDestroyShaderModule(device, fragShaderModule, nullptr); 830 | vkDestroyShaderModule(device, vertShaderModule, nullptr); 831 | } 832 | 833 | void createFramebuffers() { 834 | swapChainFramebuffers.resize(swapChainImageViews.size()); 835 | 836 | for (size_t i = 0; i < swapChainImageViews.size(); i++) { 837 | std::array attachments = {colorImageView, depthImageView, 838 | swapChainImageViews[i]}; 839 | 840 | VkFramebufferCreateInfo framebufferInfo{}; 841 | framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; 842 | framebufferInfo.renderPass = renderPass; 843 | framebufferInfo.attachmentCount = 844 | static_cast(attachments.size()); 845 | framebufferInfo.pAttachments = attachments.data(); 846 | framebufferInfo.width = swapChainExtent.width; 847 | framebufferInfo.height = swapChainExtent.height; 848 | framebufferInfo.layers = 1; 849 | 850 | if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, 851 | &swapChainFramebuffers[i]) != VK_SUCCESS) { 852 | throw std::runtime_error("failed to create framebuffer!"); 853 | } 854 | } 855 | } 856 | 857 | void createCommandPool() { 858 | QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice); 859 | 860 | VkCommandPoolCreateInfo poolInfo{}; 861 | poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; 862 | poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; 863 | poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value(); 864 | 865 | if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != 866 | VK_SUCCESS) { 867 | throw std::runtime_error("failed to create graphics command pool!"); 868 | } 869 | } 870 | 871 | void createColorResources() { 872 | VkFormat colorFormat = swapChainImageFormat; 873 | 874 | createImage(swapChainExtent.width, swapChainExtent.height, 1, msaaSamples, 875 | colorFormat, VK_IMAGE_TILING_OPTIMAL, 876 | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | 877 | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 878 | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, colorImage, 879 | colorImageMemory); 880 | colorImageView = 881 | createImageView(colorImage, colorFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1); 882 | } 883 | 884 | void createDepthResources() { 885 | VkFormat depthFormat = findDepthFormat(); 886 | 887 | createImage(swapChainExtent.width, swapChainExtent.height, 1, msaaSamples, 888 | depthFormat, VK_IMAGE_TILING_OPTIMAL, 889 | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 890 | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, 891 | depthImageMemory); 892 | depthImageView = 893 | createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT, 1); 894 | } 895 | 896 | VkFormat findSupportedFormat(const std::vector& candidates, 897 | VkImageTiling tiling, 898 | VkFormatFeatureFlags features) { 899 | for (VkFormat format : candidates) { 900 | VkFormatProperties props; 901 | vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props); 902 | 903 | if (tiling == VK_IMAGE_TILING_LINEAR && 904 | (props.linearTilingFeatures & features) == features) { 905 | return format; 906 | } else if (tiling == VK_IMAGE_TILING_OPTIMAL && 907 | (props.optimalTilingFeatures & features) == features) { 908 | return format; 909 | } 910 | } 911 | 912 | throw std::runtime_error("failed to find supported format!"); 913 | } 914 | 915 | VkFormat findDepthFormat() { 916 | return findSupportedFormat( 917 | {VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, 918 | VK_FORMAT_D24_UNORM_S8_UINT}, 919 | VK_IMAGE_TILING_OPTIMAL, 920 | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); 921 | } 922 | 923 | bool hasStencilComponent(VkFormat format) { 924 | return format == VK_FORMAT_D32_SFLOAT_S8_UINT || 925 | format == VK_FORMAT_D24_UNORM_S8_UINT; 926 | } 927 | 928 | void createTextureImage() { 929 | int texWidth, texHeight, texChannels; 930 | stbi_uc* pixels = stbi_load(TEXTURE_PATH.c_str(), &texWidth, &texHeight, 931 | &texChannels, STBI_rgb_alpha); 932 | VkDeviceSize imageSize = texWidth * texHeight * 4; 933 | mipLevels = static_cast( 934 | std::floor(std::log2(std::max(texWidth, texHeight)))) + 935 | 1; 936 | 937 | if (!pixels) { 938 | throw std::runtime_error("failed to load texture image!"); 939 | } 940 | 941 | VkBuffer stagingBuffer; 942 | VkDeviceMemory stagingBufferMemory; 943 | createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, 944 | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | 945 | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 946 | stagingBuffer, stagingBufferMemory); 947 | 948 | void* data; 949 | vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data); 950 | memcpy(data, pixels, static_cast(imageSize)); 951 | vkUnmapMemory(device, stagingBufferMemory); 952 | 953 | stbi_image_free(pixels); 954 | 955 | createImage( 956 | texWidth, texHeight, mipLevels, VK_SAMPLE_COUNT_1_BIT, 957 | VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, 958 | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | 959 | VK_IMAGE_USAGE_SAMPLED_BIT, 960 | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, textureImage, textureImageMemory); 961 | 962 | transitionImageLayout(textureImage, VK_FORMAT_R8G8B8A8_SRGB, 963 | VK_IMAGE_LAYOUT_UNDEFINED, 964 | VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, mipLevels); 965 | copyBufferToImage(stagingBuffer, textureImage, 966 | static_cast(texWidth), 967 | static_cast(texHeight)); 968 | // transitioned to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL while generating 969 | // mipmaps 970 | 971 | vkDestroyBuffer(device, stagingBuffer, nullptr); 972 | vkFreeMemory(device, stagingBufferMemory, nullptr); 973 | 974 | generateMipmaps(textureImage, VK_FORMAT_R8G8B8A8_SRGB, texWidth, texHeight, 975 | mipLevels); 976 | } 977 | 978 | void generateMipmaps(VkImage image, 979 | VkFormat imageFormat, 980 | int32_t texWidth, 981 | int32_t texHeight, 982 | uint32_t mipLevels) { 983 | // Check if image format supports linear blitting 984 | VkFormatProperties formatProperties; 985 | vkGetPhysicalDeviceFormatProperties(physicalDevice, imageFormat, 986 | &formatProperties); 987 | 988 | if (!(formatProperties.optimalTilingFeatures & 989 | VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { 990 | throw std::runtime_error( 991 | "texture image format does not support linear blitting!"); 992 | } 993 | 994 | VkCommandBuffer commandBuffer = beginSingleTimeCommands(); 995 | 996 | VkImageMemoryBarrier barrier{}; 997 | barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; 998 | barrier.image = image; 999 | barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 1000 | barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 1001 | barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 1002 | barrier.subresourceRange.baseArrayLayer = 0; 1003 | barrier.subresourceRange.layerCount = 1; 1004 | barrier.subresourceRange.levelCount = 1; 1005 | 1006 | int32_t mipWidth = texWidth; 1007 | int32_t mipHeight = texHeight; 1008 | 1009 | for (uint32_t i = 1; i < mipLevels; i++) { 1010 | barrier.subresourceRange.baseMipLevel = i - 1; 1011 | barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; 1012 | barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; 1013 | barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; 1014 | barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; 1015 | 1016 | vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, 1017 | VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, 1018 | nullptr, 1, &barrier); 1019 | 1020 | VkImageBlit blit{}; 1021 | blit.srcOffsets[0] = {0, 0, 0}; 1022 | blit.srcOffsets[1] = {mipWidth, mipHeight, 1}; 1023 | blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 1024 | blit.srcSubresource.mipLevel = i - 1; 1025 | blit.srcSubresource.baseArrayLayer = 0; 1026 | blit.srcSubresource.layerCount = 1; 1027 | blit.dstOffsets[0] = {0, 0, 0}; 1028 | blit.dstOffsets[1] = {mipWidth > 1 ? mipWidth / 2 : 1, 1029 | mipHeight > 1 ? mipHeight / 2 : 1, 1}; 1030 | blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 1031 | blit.dstSubresource.mipLevel = i; 1032 | blit.dstSubresource.baseArrayLayer = 0; 1033 | blit.dstSubresource.layerCount = 1; 1034 | 1035 | vkCmdBlitImage(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 1036 | image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, 1037 | VK_FILTER_LINEAR); 1038 | 1039 | barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; 1040 | barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; 1041 | barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; 1042 | barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; 1043 | 1044 | vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, 1045 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 1046 | 0, nullptr, 1, &barrier); 1047 | 1048 | if (mipWidth > 1) 1049 | mipWidth /= 2; 1050 | if (mipHeight > 1) 1051 | mipHeight /= 2; 1052 | } 1053 | 1054 | barrier.subresourceRange.baseMipLevel = mipLevels - 1; 1055 | barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; 1056 | barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; 1057 | barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; 1058 | barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; 1059 | 1060 | vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, 1061 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 1062 | 0, nullptr, 1, &barrier); 1063 | 1064 | endSingleTimeCommands(commandBuffer); 1065 | } 1066 | 1067 | VkSampleCountFlagBits getMaxUsableSampleCount() { 1068 | VkPhysicalDeviceProperties physicalDeviceProperties; 1069 | vkGetPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties); 1070 | 1071 | VkSampleCountFlags counts = 1072 | physicalDeviceProperties.limits.framebufferColorSampleCounts & 1073 | physicalDeviceProperties.limits.framebufferDepthSampleCounts; 1074 | if (counts & VK_SAMPLE_COUNT_64_BIT) { 1075 | return VK_SAMPLE_COUNT_64_BIT; 1076 | } 1077 | if (counts & VK_SAMPLE_COUNT_32_BIT) { 1078 | return VK_SAMPLE_COUNT_32_BIT; 1079 | } 1080 | if (counts & VK_SAMPLE_COUNT_16_BIT) { 1081 | return VK_SAMPLE_COUNT_16_BIT; 1082 | } 1083 | if (counts & VK_SAMPLE_COUNT_8_BIT) { 1084 | return VK_SAMPLE_COUNT_8_BIT; 1085 | } 1086 | if (counts & VK_SAMPLE_COUNT_4_BIT) { 1087 | return VK_SAMPLE_COUNT_4_BIT; 1088 | } 1089 | if (counts & VK_SAMPLE_COUNT_2_BIT) { 1090 | return VK_SAMPLE_COUNT_2_BIT; 1091 | } 1092 | 1093 | return VK_SAMPLE_COUNT_1_BIT; 1094 | } 1095 | 1096 | void createTextureImageView() { 1097 | textureImageView = createImageView(textureImage, VK_FORMAT_R8G8B8A8_SRGB, 1098 | VK_IMAGE_ASPECT_COLOR_BIT, mipLevels); 1099 | } 1100 | 1101 | void createTextureSampler() { 1102 | VkPhysicalDeviceProperties properties{}; 1103 | vkGetPhysicalDeviceProperties(physicalDevice, &properties); 1104 | 1105 | VkSamplerCreateInfo samplerInfo{}; 1106 | samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; 1107 | samplerInfo.magFilter = VK_FILTER_LINEAR; 1108 | samplerInfo.minFilter = VK_FILTER_LINEAR; 1109 | samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; 1110 | samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; 1111 | samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; 1112 | samplerInfo.anisotropyEnable = VK_TRUE; 1113 | samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy; 1114 | samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; 1115 | samplerInfo.unnormalizedCoordinates = VK_FALSE; 1116 | samplerInfo.compareEnable = VK_FALSE; 1117 | samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; 1118 | samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; 1119 | samplerInfo.minLod = 0.0f; 1120 | samplerInfo.maxLod = static_cast(mipLevels); 1121 | samplerInfo.mipLodBias = 0.0f; 1122 | 1123 | if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != 1124 | VK_SUCCESS) { 1125 | throw std::runtime_error("failed to create texture sampler!"); 1126 | } 1127 | } 1128 | 1129 | VkImageView createImageView(VkImage image, 1130 | VkFormat format, 1131 | VkImageAspectFlags aspectFlags, 1132 | uint32_t mipLevels) { 1133 | VkImageViewCreateInfo viewInfo{}; 1134 | viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; 1135 | viewInfo.image = image; 1136 | viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; 1137 | viewInfo.format = format; 1138 | viewInfo.subresourceRange.aspectMask = aspectFlags; 1139 | viewInfo.subresourceRange.baseMipLevel = 0; 1140 | viewInfo.subresourceRange.levelCount = mipLevels; 1141 | viewInfo.subresourceRange.baseArrayLayer = 0; 1142 | viewInfo.subresourceRange.layerCount = 1; 1143 | 1144 | VkImageView imageView; 1145 | if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != 1146 | VK_SUCCESS) { 1147 | throw std::runtime_error("failed to create texture image view!"); 1148 | } 1149 | 1150 | return imageView; 1151 | } 1152 | 1153 | void createImage(uint32_t width, 1154 | uint32_t height, 1155 | uint32_t mipLevels, 1156 | VkSampleCountFlagBits numSamples, 1157 | VkFormat format, 1158 | VkImageTiling tiling, 1159 | VkImageUsageFlags usage, 1160 | VkMemoryPropertyFlags properties, 1161 | VkImage& image, 1162 | VkDeviceMemory& imageMemory) { 1163 | VkImageCreateInfo imageInfo{}; 1164 | imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; 1165 | imageInfo.imageType = VK_IMAGE_TYPE_2D; 1166 | imageInfo.extent.width = width; 1167 | imageInfo.extent.height = height; 1168 | imageInfo.extent.depth = 1; 1169 | imageInfo.mipLevels = mipLevels; 1170 | imageInfo.arrayLayers = 1; 1171 | imageInfo.format = format; 1172 | imageInfo.tiling = tiling; 1173 | imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; 1174 | imageInfo.usage = usage; 1175 | imageInfo.samples = numSamples; 1176 | imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 1177 | 1178 | if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) { 1179 | throw std::runtime_error("failed to create image!"); 1180 | } 1181 | 1182 | VkMemoryRequirements memRequirements; 1183 | vkGetImageMemoryRequirements(device, image, &memRequirements); 1184 | 1185 | VkMemoryAllocateInfo allocInfo{}; 1186 | allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; 1187 | allocInfo.allocationSize = memRequirements.size; 1188 | allocInfo.memoryTypeIndex = 1189 | findMemoryType(memRequirements.memoryTypeBits, properties); 1190 | 1191 | if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != 1192 | VK_SUCCESS) { 1193 | throw std::runtime_error("failed to allocate image memory!"); 1194 | } 1195 | 1196 | vkBindImageMemory(device, image, imageMemory, 0); 1197 | } 1198 | 1199 | void transitionImageLayout(VkImage image, 1200 | VkFormat format, 1201 | VkImageLayout oldLayout, 1202 | VkImageLayout newLayout, 1203 | uint32_t mipLevels) { 1204 | VkCommandBuffer commandBuffer = beginSingleTimeCommands(); 1205 | 1206 | VkImageMemoryBarrier barrier{}; 1207 | barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; 1208 | barrier.oldLayout = oldLayout; 1209 | barrier.newLayout = newLayout; 1210 | barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 1211 | barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; 1212 | barrier.image = image; 1213 | barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 1214 | barrier.subresourceRange.baseMipLevel = 0; 1215 | barrier.subresourceRange.levelCount = mipLevels; 1216 | barrier.subresourceRange.baseArrayLayer = 0; 1217 | barrier.subresourceRange.layerCount = 1; 1218 | 1219 | VkPipelineStageFlags sourceStage; 1220 | VkPipelineStageFlags destinationStage; 1221 | 1222 | if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && 1223 | newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { 1224 | barrier.srcAccessMask = 0; 1225 | barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; 1226 | 1227 | sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; 1228 | destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; 1229 | } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && 1230 | newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { 1231 | barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; 1232 | barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; 1233 | 1234 | sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; 1235 | destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; 1236 | } else { 1237 | throw std::invalid_argument("unsupported layout transition!"); 1238 | } 1239 | 1240 | vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0, 0, 1241 | nullptr, 0, nullptr, 1, &barrier); 1242 | 1243 | endSingleTimeCommands(commandBuffer); 1244 | } 1245 | 1246 | void copyBufferToImage(VkBuffer buffer, 1247 | VkImage image, 1248 | uint32_t width, 1249 | uint32_t height) { 1250 | VkCommandBuffer commandBuffer = beginSingleTimeCommands(); 1251 | 1252 | VkBufferImageCopy region{}; 1253 | region.bufferOffset = 0; 1254 | region.bufferRowLength = 0; 1255 | region.bufferImageHeight = 0; 1256 | region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; 1257 | region.imageSubresource.mipLevel = 0; 1258 | region.imageSubresource.baseArrayLayer = 0; 1259 | region.imageSubresource.layerCount = 1; 1260 | region.imageOffset = {0, 0, 0}; 1261 | region.imageExtent = {width, height, 1}; 1262 | 1263 | vkCmdCopyBufferToImage(commandBuffer, buffer, image, 1264 | VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); 1265 | 1266 | endSingleTimeCommands(commandBuffer); 1267 | } 1268 | 1269 | void loadModel() { 1270 | tinyobj::attrib_t attrib; 1271 | std::vector shapes; 1272 | std::vector materials; 1273 | std::string warn, err; 1274 | 1275 | if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, 1276 | MODEL_PATH.c_str())) { 1277 | throw std::runtime_error(warn + err); 1278 | } 1279 | 1280 | std::unordered_map uniqueVertices{}; 1281 | 1282 | for (const auto& shape : shapes) { 1283 | for (const auto& index : shape.mesh.indices) { 1284 | Vertex vertex{}; 1285 | 1286 | vertex.pos = {attrib.vertices[3 * index.vertex_index + 0], 1287 | attrib.vertices[3 * index.vertex_index + 1], 1288 | attrib.vertices[3 * index.vertex_index + 2]}; 1289 | 1290 | vertex.texCoord = { 1291 | attrib.texcoords[2 * index.texcoord_index + 0], 1292 | 1.0f - attrib.texcoords[2 * index.texcoord_index + 1]}; 1293 | 1294 | vertex.color = {1.0f, 1.0f, 1.0f}; 1295 | 1296 | if (uniqueVertices.count(vertex) == 0) { 1297 | uniqueVertices[vertex] = static_cast(vertices.size()); 1298 | vertices.push_back(vertex); 1299 | } 1300 | 1301 | indices.push_back(uniqueVertices[vertex]); 1302 | } 1303 | } 1304 | } 1305 | 1306 | void createVertexBuffer() { 1307 | VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size(); 1308 | 1309 | VkBuffer stagingBuffer; 1310 | VkDeviceMemory stagingBufferMemory; 1311 | createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, 1312 | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | 1313 | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 1314 | stagingBuffer, stagingBufferMemory); 1315 | 1316 | void* data; 1317 | vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data); 1318 | memcpy(data, vertices.data(), (size_t)bufferSize); 1319 | vkUnmapMemory(device, stagingBufferMemory); 1320 | 1321 | createBuffer( 1322 | bufferSize, 1323 | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, 1324 | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory); 1325 | 1326 | copyBuffer(stagingBuffer, vertexBuffer, bufferSize); 1327 | 1328 | vkDestroyBuffer(device, stagingBuffer, nullptr); 1329 | vkFreeMemory(device, stagingBufferMemory, nullptr); 1330 | } 1331 | 1332 | void createIndexBuffer() { 1333 | VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size(); 1334 | 1335 | VkBuffer stagingBuffer; 1336 | VkDeviceMemory stagingBufferMemory; 1337 | createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, 1338 | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | 1339 | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 1340 | stagingBuffer, stagingBufferMemory); 1341 | 1342 | void* data; 1343 | vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data); 1344 | memcpy(data, indices.data(), (size_t)bufferSize); 1345 | vkUnmapMemory(device, stagingBufferMemory); 1346 | 1347 | createBuffer( 1348 | bufferSize, 1349 | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, 1350 | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory); 1351 | 1352 | copyBuffer(stagingBuffer, indexBuffer, bufferSize); 1353 | 1354 | vkDestroyBuffer(device, stagingBuffer, nullptr); 1355 | vkFreeMemory(device, stagingBufferMemory, nullptr); 1356 | } 1357 | 1358 | void createUniformBuffers() { 1359 | VkDeviceSize bufferSize = sizeof(UniformBufferObject); 1360 | 1361 | uniformBuffers.resize(MAX_FRAMES_IN_FLIGHT); 1362 | uniformBuffersMemory.resize(MAX_FRAMES_IN_FLIGHT); 1363 | uniformBuffersMapped.resize(MAX_FRAMES_IN_FLIGHT); 1364 | 1365 | for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 1366 | createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, 1367 | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | 1368 | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 1369 | uniformBuffers[i], uniformBuffersMemory[i]); 1370 | 1371 | vkMapMemory(device, uniformBuffersMemory[i], 0, bufferSize, 0, 1372 | &uniformBuffersMapped[i]); 1373 | } 1374 | } 1375 | 1376 | void createDescriptorPool() { 1377 | std::array poolSizes{}; 1378 | poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; 1379 | poolSizes[0].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); 1380 | poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; 1381 | poolSizes[1].descriptorCount = static_cast(MAX_FRAMES_IN_FLIGHT); 1382 | 1383 | VkDescriptorPoolCreateInfo poolInfo{}; 1384 | poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; 1385 | poolInfo.poolSizeCount = static_cast(poolSizes.size()); 1386 | poolInfo.pPoolSizes = poolSizes.data(); 1387 | poolInfo.maxSets = static_cast(MAX_FRAMES_IN_FLIGHT); 1388 | 1389 | if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &descriptorPool) != 1390 | VK_SUCCESS) { 1391 | throw std::runtime_error("failed to create descriptor pool!"); 1392 | } 1393 | } 1394 | 1395 | void createDescriptorSets() { 1396 | std::vector layouts(MAX_FRAMES_IN_FLIGHT, 1397 | descriptorSetLayout); 1398 | VkDescriptorSetAllocateInfo allocInfo{}; 1399 | allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; 1400 | allocInfo.descriptorPool = descriptorPool; 1401 | allocInfo.descriptorSetCount = static_cast(MAX_FRAMES_IN_FLIGHT); 1402 | allocInfo.pSetLayouts = layouts.data(); 1403 | 1404 | descriptorSets.resize(MAX_FRAMES_IN_FLIGHT); 1405 | if (vkAllocateDescriptorSets(device, &allocInfo, descriptorSets.data()) != 1406 | VK_SUCCESS) { 1407 | throw std::runtime_error("failed to allocate descriptor sets!"); 1408 | } 1409 | 1410 | for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 1411 | VkDescriptorBufferInfo bufferInfo{}; 1412 | bufferInfo.buffer = uniformBuffers[i]; 1413 | bufferInfo.offset = 0; 1414 | bufferInfo.range = sizeof(UniformBufferObject); 1415 | 1416 | VkDescriptorImageInfo imageInfo{}; 1417 | imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; 1418 | imageInfo.imageView = textureImageView; 1419 | imageInfo.sampler = textureSampler; 1420 | 1421 | std::array descriptorWrites{}; 1422 | 1423 | descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; 1424 | descriptorWrites[0].dstSet = descriptorSets[i]; 1425 | descriptorWrites[0].dstBinding = 0; 1426 | descriptorWrites[0].dstArrayElement = 0; 1427 | descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; 1428 | descriptorWrites[0].descriptorCount = 1; 1429 | descriptorWrites[0].pBufferInfo = &bufferInfo; 1430 | 1431 | descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; 1432 | descriptorWrites[1].dstSet = descriptorSets[i]; 1433 | descriptorWrites[1].dstBinding = 1; 1434 | descriptorWrites[1].dstArrayElement = 0; 1435 | descriptorWrites[1].descriptorType = 1436 | VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; 1437 | descriptorWrites[1].descriptorCount = 1; 1438 | descriptorWrites[1].pImageInfo = &imageInfo; 1439 | 1440 | vkUpdateDescriptorSets(device, 1441 | static_cast(descriptorWrites.size()), 1442 | descriptorWrites.data(), 0, nullptr); 1443 | } 1444 | } 1445 | 1446 | void createBuffer(VkDeviceSize size, 1447 | VkBufferUsageFlags usage, 1448 | VkMemoryPropertyFlags properties, 1449 | VkBuffer& buffer, 1450 | VkDeviceMemory& bufferMemory) { 1451 | VkBufferCreateInfo bufferInfo{}; 1452 | bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; 1453 | bufferInfo.size = size; 1454 | bufferInfo.usage = usage; 1455 | bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 1456 | 1457 | if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { 1458 | throw std::runtime_error("failed to create buffer!"); 1459 | } 1460 | 1461 | VkMemoryRequirements memRequirements; 1462 | vkGetBufferMemoryRequirements(device, buffer, &memRequirements); 1463 | 1464 | VkMemoryAllocateInfo allocInfo{}; 1465 | allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; 1466 | allocInfo.allocationSize = memRequirements.size; 1467 | allocInfo.memoryTypeIndex = 1468 | findMemoryType(memRequirements.memoryTypeBits, properties); 1469 | 1470 | if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != 1471 | VK_SUCCESS) { 1472 | throw std::runtime_error("failed to allocate buffer memory!"); 1473 | } 1474 | 1475 | vkBindBufferMemory(device, buffer, bufferMemory, 0); 1476 | } 1477 | 1478 | VkCommandBuffer beginSingleTimeCommands() { 1479 | VkCommandBufferAllocateInfo allocInfo{}; 1480 | allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; 1481 | allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; 1482 | allocInfo.commandPool = commandPool; 1483 | allocInfo.commandBufferCount = 1; 1484 | 1485 | VkCommandBuffer commandBuffer; 1486 | vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer); 1487 | 1488 | VkCommandBufferBeginInfo beginInfo{}; 1489 | beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 1490 | beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; 1491 | 1492 | vkBeginCommandBuffer(commandBuffer, &beginInfo); 1493 | 1494 | return commandBuffer; 1495 | } 1496 | 1497 | void endSingleTimeCommands(VkCommandBuffer commandBuffer) { 1498 | vkEndCommandBuffer(commandBuffer); 1499 | 1500 | VkSubmitInfo submitInfo{}; 1501 | submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 1502 | submitInfo.commandBufferCount = 1; 1503 | submitInfo.pCommandBuffers = &commandBuffer; 1504 | 1505 | vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); 1506 | vkQueueWaitIdle(graphicsQueue); 1507 | 1508 | vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); 1509 | } 1510 | 1511 | void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { 1512 | VkCommandBuffer commandBuffer = beginSingleTimeCommands(); 1513 | 1514 | VkBufferCopy copyRegion{}; 1515 | copyRegion.size = size; 1516 | vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region); 1517 | 1518 | endSingleTimeCommands(commandBuffer); 1519 | } 1520 | 1521 | uint32_t findMemoryType(uint32_t typeFilter, 1522 | VkMemoryPropertyFlags properties) { 1523 | VkPhysicalDeviceMemoryProperties memProperties; 1524 | vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties); 1525 | 1526 | for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { 1527 | if ((typeFilter & (1 << i)) && 1528 | (memProperties.memoryTypes[i].propertyFlags & properties) == 1529 | properties) { 1530 | return i; 1531 | } 1532 | } 1533 | 1534 | throw std::runtime_error("failed to find suitable memory type!"); 1535 | } 1536 | 1537 | void createCommandBuffers() { 1538 | commandBuffers.resize(MAX_FRAMES_IN_FLIGHT); 1539 | 1540 | VkCommandBufferAllocateInfo allocInfo{}; 1541 | allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; 1542 | allocInfo.commandPool = commandPool; 1543 | allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; 1544 | allocInfo.commandBufferCount = (uint32_t)commandBuffers.size(); 1545 | 1546 | if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != 1547 | VK_SUCCESS) { 1548 | throw std::runtime_error("failed to allocate command buffers!"); 1549 | } 1550 | } 1551 | 1552 | void recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { 1553 | VkCommandBufferBeginInfo beginInfo{}; 1554 | beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; 1555 | 1556 | if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) { 1557 | throw std::runtime_error("failed to begin recording command buffer!"); 1558 | } 1559 | 1560 | VkRenderPassBeginInfo renderPassInfo{}; 1561 | renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; 1562 | renderPassInfo.renderPass = renderPass; 1563 | renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex]; 1564 | renderPassInfo.renderArea.offset = {0, 0}; 1565 | renderPassInfo.renderArea.extent = swapChainExtent; 1566 | 1567 | std::array clearValues{}; 1568 | clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}}; 1569 | clearValues[1].depthStencil = {1.0f, 0}; 1570 | 1571 | renderPassInfo.clearValueCount = static_cast(clearValues.size()); 1572 | renderPassInfo.pClearValues = clearValues.data(); 1573 | 1574 | vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, 1575 | VK_SUBPASS_CONTENTS_INLINE); 1576 | 1577 | vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, 1578 | graphicsPipeline); 1579 | 1580 | VkViewport viewport{}; 1581 | viewport.x = 0.0f; 1582 | viewport.y = 0.0f; 1583 | viewport.width = (float)swapChainExtent.width; 1584 | viewport.height = (float)swapChainExtent.height; 1585 | viewport.minDepth = 0.0f; 1586 | viewport.maxDepth = 1.0f; 1587 | vkCmdSetViewport(commandBuffer, 0, 1, &viewport); 1588 | 1589 | VkRect2D scissor{}; 1590 | scissor.offset = {0, 0}; 1591 | scissor.extent = swapChainExtent; 1592 | vkCmdSetScissor(commandBuffer, 0, 1, &scissor); 1593 | 1594 | VkBuffer vertexBuffers[] = {vertexBuffer}; 1595 | VkDeviceSize offsets[] = {0}; 1596 | vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); 1597 | 1598 | vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT32); 1599 | 1600 | vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, 1601 | pipelineLayout, 0, 1, &descriptorSets[currentFrame], 1602 | 0, nullptr); 1603 | 1604 | vkCmdDrawIndexed(commandBuffer, static_cast(indices.size()), 1, 0, 1605 | 0, 0); 1606 | 1607 | vkCmdEndRenderPass(commandBuffer); 1608 | 1609 | if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) { 1610 | throw std::runtime_error("failed to record command buffer!"); 1611 | } 1612 | } 1613 | 1614 | void createSyncObjects() { 1615 | imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT); 1616 | renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT); 1617 | inFlightFences.resize(MAX_FRAMES_IN_FLIGHT); 1618 | 1619 | VkSemaphoreCreateInfo semaphoreInfo{}; 1620 | semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; 1621 | 1622 | VkFenceCreateInfo fenceInfo{}; 1623 | fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; 1624 | fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; 1625 | 1626 | for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 1627 | if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, 1628 | &imageAvailableSemaphores[i]) != VK_SUCCESS || 1629 | vkCreateSemaphore(device, &semaphoreInfo, nullptr, 1630 | &renderFinishedSemaphores[i]) != VK_SUCCESS || 1631 | vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != 1632 | VK_SUCCESS) { 1633 | throw std::runtime_error( 1634 | "failed to create synchronization objects for a frame!"); 1635 | } 1636 | } 1637 | } 1638 | 1639 | void updateUniformBuffer(uint32_t currentImage) { 1640 | static auto startTime = std::chrono::high_resolution_clock::now(); 1641 | 1642 | auto currentTime = std::chrono::high_resolution_clock::now(); 1643 | float time = std::chrono::duration( 1644 | currentTime - startTime) 1645 | .count(); 1646 | 1647 | UniformBufferObject ubo{}; 1648 | ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), 1649 | glm::vec3(0.0f, 0.0f, 1.0f)); 1650 | ubo.view = 1651 | glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), 1652 | glm::vec3(0.0f, 0.0f, 1.0f)); 1653 | ubo.proj = glm::perspective( 1654 | glm::radians(45.0f), 1655 | swapChainExtent.width / (float)swapChainExtent.height, 0.1f, 10.0f); 1656 | ubo.proj[1][1] *= -1; 1657 | 1658 | memcpy(uniformBuffersMapped[currentImage], &ubo, sizeof(ubo)); 1659 | } 1660 | 1661 | void drawFrame() { 1662 | vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, 1663 | UINT64_MAX); 1664 | 1665 | uint32_t imageIndex; 1666 | VkResult result = vkAcquireNextImageKHR( 1667 | device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], 1668 | VK_NULL_HANDLE, &imageIndex); 1669 | 1670 | if (result == VK_ERROR_OUT_OF_DATE_KHR) { 1671 | recreateSwapChain(); 1672 | return; 1673 | } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { 1674 | throw std::runtime_error("failed to acquire swap chain image!"); 1675 | } 1676 | 1677 | updateUniformBuffer(currentFrame); 1678 | 1679 | vkResetFences(device, 1, &inFlightFences[currentFrame]); 1680 | 1681 | vkResetCommandBuffer(commandBuffers[currentFrame], 1682 | /*VkCommandBufferResetFlagBits*/ 0); 1683 | recordCommandBuffer(commandBuffers[currentFrame], imageIndex); 1684 | 1685 | VkSubmitInfo submitInfo{}; 1686 | submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; 1687 | 1688 | VkSemaphore waitSemaphores[] = {imageAvailableSemaphores[currentFrame]}; 1689 | VkPipelineStageFlags waitStages[] = { 1690 | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; 1691 | submitInfo.waitSemaphoreCount = 1; 1692 | submitInfo.pWaitSemaphores = waitSemaphores; 1693 | submitInfo.pWaitDstStageMask = waitStages; 1694 | 1695 | submitInfo.commandBufferCount = 1; 1696 | submitInfo.pCommandBuffers = &commandBuffers[currentFrame]; 1697 | 1698 | VkSemaphore signalSemaphores[] = {renderFinishedSemaphores[currentFrame]}; 1699 | submitInfo.signalSemaphoreCount = 1; 1700 | submitInfo.pSignalSemaphores = signalSemaphores; 1701 | 1702 | if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, 1703 | inFlightFences[currentFrame]) != VK_SUCCESS) { 1704 | throw std::runtime_error("failed to submit draw command buffer!"); 1705 | } 1706 | 1707 | VkPresentInfoKHR presentInfo{}; 1708 | presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; 1709 | 1710 | presentInfo.waitSemaphoreCount = 1; 1711 | presentInfo.pWaitSemaphores = signalSemaphores; 1712 | 1713 | VkSwapchainKHR swapChains[] = {swapChain}; 1714 | presentInfo.swapchainCount = 1; 1715 | presentInfo.pSwapchains = swapChains; 1716 | 1717 | presentInfo.pImageIndices = &imageIndex; 1718 | 1719 | result = vkQueuePresentKHR(presentQueue, &presentInfo); 1720 | 1721 | if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || 1722 | framebufferResized) { 1723 | framebufferResized = false; 1724 | recreateSwapChain(); 1725 | } else if (result != VK_SUCCESS) { 1726 | throw std::runtime_error("failed to present swap chain image!"); 1727 | } 1728 | 1729 | currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; 1730 | } 1731 | 1732 | VkShaderModule createShaderModule(const std::vector& code) { 1733 | VkShaderModuleCreateInfo createInfo{}; 1734 | createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; 1735 | createInfo.codeSize = code.size(); 1736 | createInfo.pCode = reinterpret_cast(code.data()); 1737 | 1738 | VkShaderModule shaderModule; 1739 | if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != 1740 | VK_SUCCESS) { 1741 | throw std::runtime_error("failed to create shader module!"); 1742 | } 1743 | 1744 | return shaderModule; 1745 | } 1746 | 1747 | VkSurfaceFormatKHR chooseSwapSurfaceFormat( 1748 | const std::vector& availableFormats) { 1749 | for (const auto& availableFormat : availableFormats) { 1750 | if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && 1751 | availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { 1752 | return availableFormat; 1753 | } 1754 | } 1755 | 1756 | return availableFormats[0]; 1757 | } 1758 | 1759 | VkPresentModeKHR chooseSwapPresentMode( 1760 | const std::vector& availablePresentModes) { 1761 | for (const auto& availablePresentMode : availablePresentModes) { 1762 | if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) { 1763 | return availablePresentMode; 1764 | } 1765 | } 1766 | 1767 | return VK_PRESENT_MODE_FIFO_KHR; 1768 | } 1769 | 1770 | VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) { 1771 | if (capabilities.currentExtent.width != 1772 | std::numeric_limits::max()) { 1773 | return capabilities.currentExtent; 1774 | } else { 1775 | int width, height; 1776 | glfwGetFramebufferSize(window, &width, &height); 1777 | 1778 | VkExtent2D actualExtent = {static_cast(width), 1779 | static_cast(height)}; 1780 | 1781 | actualExtent.width = 1782 | std::clamp(actualExtent.width, capabilities.minImageExtent.width, 1783 | capabilities.maxImageExtent.width); 1784 | actualExtent.height = 1785 | std::clamp(actualExtent.height, capabilities.minImageExtent.height, 1786 | capabilities.maxImageExtent.height); 1787 | 1788 | return actualExtent; 1789 | } 1790 | } 1791 | 1792 | SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) { 1793 | SwapChainSupportDetails details; 1794 | 1795 | vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, 1796 | &details.capabilities); 1797 | 1798 | uint32_t formatCount; 1799 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, 1800 | nullptr); 1801 | 1802 | if (formatCount != 0) { 1803 | details.formats.resize(formatCount); 1804 | vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, 1805 | details.formats.data()); 1806 | } 1807 | 1808 | uint32_t presentModeCount; 1809 | vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, 1810 | &presentModeCount, nullptr); 1811 | 1812 | if (presentModeCount != 0) { 1813 | details.presentModes.resize(presentModeCount); 1814 | vkGetPhysicalDeviceSurfacePresentModesKHR( 1815 | device, surface, &presentModeCount, details.presentModes.data()); 1816 | } 1817 | 1818 | return details; 1819 | } 1820 | 1821 | bool isDeviceSuitable(VkPhysicalDevice device) { 1822 | QueueFamilyIndices indices = findQueueFamilies(device); 1823 | 1824 | bool extensionsSupported = checkDeviceExtensionSupport(device); 1825 | 1826 | bool swapChainAdequate = false; 1827 | if (extensionsSupported) { 1828 | SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); 1829 | swapChainAdequate = !swapChainSupport.formats.empty() && 1830 | !swapChainSupport.presentModes.empty(); 1831 | } 1832 | 1833 | VkPhysicalDeviceFeatures supportedFeatures; 1834 | vkGetPhysicalDeviceFeatures(device, &supportedFeatures); 1835 | 1836 | return indices.isComplete() && extensionsSupported && swapChainAdequate && 1837 | supportedFeatures.samplerAnisotropy; 1838 | } 1839 | 1840 | bool checkDeviceExtensionSupport(VkPhysicalDevice device) { 1841 | uint32_t extensionCount; 1842 | vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, 1843 | nullptr); 1844 | 1845 | std::vector availableExtensions(extensionCount); 1846 | vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, 1847 | availableExtensions.data()); 1848 | 1849 | std::set requiredExtensions(deviceExtensions.begin(), 1850 | deviceExtensions.end()); 1851 | 1852 | for (const auto& extension : availableExtensions) { 1853 | requiredExtensions.erase(extension.extensionName); 1854 | } 1855 | 1856 | return requiredExtensions.empty(); 1857 | } 1858 | 1859 | QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) { 1860 | QueueFamilyIndices indices; 1861 | 1862 | uint32_t queueFamilyCount = 0; 1863 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, 1864 | nullptr); 1865 | 1866 | std::vector queueFamilies(queueFamilyCount); 1867 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, 1868 | queueFamilies.data()); 1869 | 1870 | int i = 0; 1871 | for (const auto& queueFamily : queueFamilies) { 1872 | if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { 1873 | indices.graphicsFamily = i; 1874 | } 1875 | 1876 | VkBool32 presentSupport = false; 1877 | vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); 1878 | 1879 | if (presentSupport) { 1880 | indices.presentFamily = i; 1881 | } 1882 | 1883 | if (indices.isComplete()) { 1884 | break; 1885 | } 1886 | 1887 | i++; 1888 | } 1889 | 1890 | return indices; 1891 | } 1892 | 1893 | std::vector getRequiredExtensions() { 1894 | uint32_t glfwExtensionCount = 0; 1895 | const char** glfwExtensions; 1896 | glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 1897 | 1898 | std::vector extensions(glfwExtensions, 1899 | glfwExtensions + glfwExtensionCount); 1900 | 1901 | if (enableValidationLayers) { 1902 | extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 1903 | } 1904 | 1905 | return extensions; 1906 | } 1907 | 1908 | bool checkValidationLayerSupport() { 1909 | uint32_t layerCount; 1910 | vkEnumerateInstanceLayerProperties(&layerCount, nullptr); 1911 | 1912 | std::vector availableLayers(layerCount); 1913 | vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); 1914 | 1915 | for (const char* layerName : validationLayers) { 1916 | bool layerFound = false; 1917 | 1918 | for (const auto& layerProperties : availableLayers) { 1919 | if (strcmp(layerName, layerProperties.layerName) == 0) { 1920 | layerFound = true; 1921 | break; 1922 | } 1923 | } 1924 | 1925 | if (!layerFound) { 1926 | return false; 1927 | } 1928 | } 1929 | 1930 | return true; 1931 | } 1932 | 1933 | static std::vector readFile(const std::string& filename) { 1934 | std::ifstream file(filename, std::ios::ate | std::ios::binary); 1935 | 1936 | if (!file.is_open()) { 1937 | throw std::runtime_error("failed to open file!"); 1938 | } 1939 | 1940 | size_t fileSize = (size_t)file.tellg(); 1941 | std::vector buffer(fileSize); 1942 | 1943 | file.seekg(0); 1944 | file.read(buffer.data(), fileSize); 1945 | 1946 | file.close(); 1947 | 1948 | return buffer; 1949 | } 1950 | 1951 | static VKAPI_ATTR VkBool32 VKAPI_CALL 1952 | debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, 1953 | VkDebugUtilsMessageTypeFlagsEXT messageType, 1954 | const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, 1955 | void* pUserData) { 1956 | std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; 1957 | 1958 | return VK_FALSE; 1959 | } 1960 | }; 1961 | 1962 | int main() { 1963 | auto path = std::filesystem::current_path() / "vcpkg_installed" / 1964 | "x64-windows" / "bin"; 1965 | std::string set = "VK_ADD_LAYER_PATH=" + path.string(); 1966 | _putenv(set.c_str()); 1967 | HelloTriangleApplication app; 1968 | 1969 | try { 1970 | app.run(); 1971 | } catch (const std::exception& e) { 1972 | std::cerr << e.what() << std::endl; 1973 | return EXIT_FAILURE; 1974 | } 1975 | 1976 | return EXIT_SUCCESS; 1977 | } 1978 | --------------------------------------------------------------------------------