├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── assets ├── .gitignore ├── android.png ├── apple.png ├── linux.png ├── vulkan.png └── windows.png ├── azure-pipelines.yml ├── ci ├── azure-install-rust.yml └── azure-steps.yml ├── download_asset.py ├── shaders ├── spv │ ├── 09-shader-base.frag.spv │ ├── 09-shader-base.vert.spv │ ├── 17-shader-vertexbuffer.frag.spv │ ├── 17-shader-vertexbuffer.vert.spv │ ├── 21-shader-ubo.frag.spv │ ├── 21-shader-ubo.vert.spv │ ├── 25-shader-textures.frag.spv │ ├── 25-shader-textures.vert.spv │ ├── 26-shader-depth.frag.spv │ └── 26-shader-depth.vert.spv └── src │ ├── 09-shader-base.frag │ ├── 09-shader-base.vert │ ├── 17-shader-vertexbuffer.frag │ ├── 17-shader-vertexbuffer.vert │ ├── 21-shader-ubo.frag │ ├── 21-shader-ubo.vert │ ├── 25-shader-textures.frag │ ├── 25-shader-textures.vert │ ├── 26-shader-depth.frag │ └── 26-shader-depth.vert ├── snapshot ├── 16-swapchain-recreation.png ├── 18-vertex-buffer.png ├── 20-index-buffer.png ├── 22-descriptor-sets.png ├── 25-texture-mapping.png ├── 29-multi-sampling.png ├── CombineImage.shadron └── snapshot_all.png └── src ├── lib.rs ├── tutorials ├── 00_base_code.rs ├── 01_instance_creation.rs ├── 02_validation_layers.rs ├── 03_physical_device_selection.rs ├── 04_logical_device.rs ├── 05_window_surface.rs ├── 06_swap_chain_creation.rs ├── 07_image_view.rs ├── 08_graphics_pipeline.rs ├── 09_shader_modules.rs ├── 10_fixed_functions.rs ├── 11_render_passes.rs ├── 12_graphics_pipeline_complete.rs ├── 13_framebuffers.rs ├── 14_command_buffers.rs ├── 15_hello_triangle.rs ├── 16_swap_chain_recreation.rs ├── 17_vertex_input.rs ├── 18_vertex_buffer.rs ├── 19_staging_buffer.rs ├── 20_index_buffer.rs ├── 21_descriptor_layout.rs ├── 22_descriptor_sets.rs ├── 23_texture_image.rs ├── 24_sampler.rs ├── 25_texture_mapping.rs ├── 26_depth_buffering.rs ├── 27_model_loading.rs ├── 28_mipmapping.rs └── 29_multisampling.rs └── utility ├── constants.rs ├── debug.rs ├── fps_limiter.rs ├── mod.rs ├── platforms.rs ├── share ├── mod.rs ├── v1.rs └── v2.rs ├── structures.rs ├── tools.rs └── window.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | 5 | # macOS 6 | .DS_STORE 7 | 8 | # CLion 9 | .idea/ 10 | **/.vscode/** 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "vulkan-tutorial-rust" 4 | version = "1.0.0" 5 | edition = "2018" 6 | authors = ["unknownue "] 7 | 8 | [dependencies] 9 | winit = "0.20.0" 10 | ash = "0.29.0" 11 | num = "0.2" 12 | cgmath = "0.17.0" 13 | image = "0.22" 14 | memoffset = "0.5.1" 15 | tobj = "0.1.10" 16 | 17 | [dependencies.bitflags] 18 | version = ">= 1.0.4" 19 | 20 | [target.'cfg(target_os = "macos")'.dependencies] 21 | metal = "0.17.0" 22 | cocoa = "0.18.4" 23 | objc = "0.2.5" 24 | 25 | [target.'cfg(target_os = "windows")'.dependencies] 26 | winapi = { version = "0.3.5", features = ["windef", "libloaderapi"] } 27 | 28 | 29 | # tutorial examples shortcut --------------------------------- 30 | [[bin]] 31 | name = "00" 32 | path = "src/tutorials/00_base_code.rs" 33 | [[bin]] 34 | name = "01" 35 | path = "src/tutorials/01_instance_creation.rs" 36 | [[bin]] 37 | name = "02" 38 | path = "src/tutorials/02_validation_layers.rs" 39 | [[bin]] 40 | name = "03" 41 | path = "src/tutorials/03_physical_device_selection.rs" 42 | [[bin]] 43 | name = "04" 44 | path = "src/tutorials/04_logical_device.rs" 45 | [[bin]] 46 | name = "05" 47 | path = "src/tutorials/05_window_surface.rs" 48 | [[bin]] 49 | name = "06" 50 | path = "src/tutorials/06_swap_chain_creation.rs" 51 | [[bin]] 52 | name = "07" 53 | path = "src/tutorials/07_image_view.rs" 54 | [[bin]] 55 | name = "08" 56 | path = "src/tutorials/08_graphics_pipeline.rs" 57 | [[bin]] 58 | name = "09" 59 | path = "src/tutorials/09_shader_modules.rs" 60 | [[bin]] 61 | name = "10" 62 | path = "src/tutorials/10_fixed_functions.rs" 63 | [[bin]] 64 | name = "11" 65 | path = "src/tutorials/11_render_passes.rs" 66 | [[bin]] 67 | name = "12" 68 | path = "src/tutorials/12_graphics_pipeline_complete.rs" 69 | [[bin]] 70 | name = "13" 71 | path = "src/tutorials/13_framebuffers.rs" 72 | [[bin]] 73 | name = "14" 74 | path = "src/tutorials/14_command_buffers.rs" 75 | [[bin]] 76 | name = "15" 77 | path = "src/tutorials/15_hello_triangle.rs" 78 | [[bin]] 79 | name = "16" 80 | path = "src/tutorials/16_swap_chain_recreation.rs" 81 | [[bin]] 82 | name = "17" 83 | path = "src/tutorials/17_vertex_input.rs" 84 | [[bin]] 85 | name = "18" 86 | path = "src/tutorials/18_vertex_buffer.rs" 87 | [[bin]] 88 | name = "19" 89 | path = "src/tutorials/19_staging_buffer.rs" 90 | [[bin]] 91 | name = "20" 92 | path = "src/tutorials/20_index_buffer.rs" 93 | [[bin]] 94 | name = "21" 95 | path = "src/tutorials/21_descriptor_layout.rs" 96 | [[bin]] 97 | name = "22" 98 | path = "src/tutorials/22_descriptor_sets.rs" 99 | [[bin]] 100 | name = "23" 101 | path = "src/tutorials/23_texture_image.rs" 102 | [[bin]] 103 | name = "24" 104 | path = "src/tutorials/24_sampler.rs" 105 | [[bin]] 106 | name = "25" 107 | path = "src/tutorials/25_texture_mapping.rs" 108 | [[bin]] 109 | name = "26" 110 | path = "src/tutorials/26_depth_buffering.rs" 111 | [[bin]] 112 | name = "27" 113 | path = "src/tutorials/27_model_loading.rs" 114 | [[bin]] 115 | name = "28" 116 | path = "src/tutorials/28_mipmapping.rs" 117 | [[bin]] 118 | name = "29" 119 | path = "src/tutorials/29_multisampling.rs" 120 | # ---------------------------------------------------------- 121 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Usami Renko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vulkan-tutorial-rust 2 | 3 | [![Build Status](https://dev.azure.com/usami-ssc/usami-ssc/_apis/build/status/unknownue.vulkan-tutorial-rust?branchName=master)](https://dev.azure.com/usami-ssc/usami-ssc/_build/latest?definitionId=5&branchName=master) [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 4 | 5 | A Rust implementation of the [Vulkan Tutorial](https://vulkan-tutorial.com) based on [ash crate](https://crates.io/crates/ash). 6 | 7 | ## Status 8 | 9 | The environment configuration is same as ash. See [here](https://github.com/MaikKlein/ash#example) for more detail. 10 | 11 | Vulkan is quite hard. All examples on official website have been finished. :beers: 12 | 13 | ## Usage 14 | 15 | Enter the project root directory, download the assets by the python3 script: 16 | 17 | ```shell 18 | vulkan-tutorial-rust$ python download_asset.py 19 | ``` 20 | 21 | Run any example with the following command: 22 | 23 | ```shell 24 | $ cargo run --bin example_number 25 | ``` 26 | 27 | Here replace `example_number` with option in the following table: 28 | 29 | | example_number | Code | Reference | Note | 30 | | -------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | 31 | | 00 | [00_base_code.rs](./src/tutorials/00_base_code.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Base_code) | | 32 | | 01 | [01_instance_creation.rs](./src/tutorials/01_instance_creation.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Instance) | | 33 | | 02 | [02_validation_layers.rs](./src/tutorials/02_validation_layers.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Validation_layers) | | 34 | | 03 | [03_physical_device_selection.rs](./src/tutorials/03_physical_device_selection.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Physical_devices_and_queue_families) | | 35 | | 04 | [04_logical_device.rs](./src/tutorials/04_logical_device.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Logical_device_and_queues) | | 36 | | 05 | [05_window_surface.rs](./src/tutorials/05_window_surface.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Window_surface) | | 37 | | 06 | [06_swap_chain_creation.rs](./src/tutorials/06_swap_chain_creation.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Swap_chain) | | 38 | | 07 | [07_image_view.rs](./src/tutorials/07_image_view.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Image_views) | | 39 | | 08 | [08_graphics_pipeline.rs](./src/tutorials/08_graphics_pipeline.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics) | | 40 | | 09 | [09_shader_modules.rs](./src/tutorials/09_shader_modules.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Shader_modules) | | 41 | | 10 | [10_fixed_functions.rs](./src/tutorials/10_fixed_functions.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Fixed_functions) | | 42 | | 11 | [11_render_passes.rs](./src/tutorials/11_render_passes.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Render_passes) | | 43 | | 12 | [12_graphics_pipeline_complete.rs](./src/tutorials/12_graphics_pipeline_complete.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Conclusion) | | 44 | | 13 | [13_framebuffers.rs](./src/tutorials/13_framebuffers.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Framebuffers) | | 45 | | 14 | [14_command_buffers.rs](./src/tutorials/14_command_buffers.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Command_buffers) | | 46 | | 15 | [15_hello_triangle.rs](./src/tutorials/15_hello_triangle.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Rendering_and_presentation) | | 47 | | 16 | [16_swap_chain_recreation.rs](./src/tutorials/16_swap_chain_recreation.rs) | [Link](https://vulkan-tutorial.com/Drawing_a_triangle/Swap_chain_recreation) | | 48 | | 17 | [17_vertex_input.rs](./src/tutorials/17_vertex_input.rs) | [Link](https://vulkan-tutorial.com/Vertex_buffers/Vertex_input_description) | The Validation Layer will complain. This example may crash on Windows. | 49 | | 18 | [18_vertex_buffer.rs](./src/tutorials/18_vertex_buffer.rs) | [Link](https://vulkan-tutorial.com/Vertex_buffers/Vertex_buffer_creation) | | 50 | | 19 | [19_staging_buffer.rs](./src/tutorials/19_staging_buffer.rs) | [Link](https://vulkan-tutorial.com/Vertex_buffers/Staging_buffer) | | 51 | | 20 | [20_index_buffer.rs](./src/tutorials/20_index_buffer.rs) | [Link](https://vulkan-tutorial.com/Vertex_buffers/Index_buffer) | | 52 | | 21 | [21_descriptor_layout.rs](./src/tutorials/21_descriptor_layout.rs) | [Link](https://vulkan-tutorial.com/Uniform_buffers/Descriptor_layout_and_buffer) | The Validation Layer will complain. | 53 | | 22 | [22_descriptor_sets.rs](./src/tutorials/22_descriptor_sets.rs) | [Link](https://vulkan-tutorial.com/Uniform_buffers/Descriptor_pool_and_sets) | | 54 | | 23 | [23_texture_image.rs](./src/tutorials/23_texture_image.rs) | [Link](https://vulkan-tutorial.com/Texture_mapping/Images) | | 55 | | 24 | [24_sampler.rs](./src/tutorials/24_sampler.rs) | [Link](https://vulkan-tutorial.com/Texture_mapping/Image_view_and_sampler) | | 56 | | 25 | [25_texture_mapping.rs](./src/tutorials/25_texture_mapping.rs) | [Link](https://vulkan-tutorial.com/Texture_mapping/Combined_image_sampler) | | 57 | | 26 | [26_depth_buffering.rs](./src/tutorials/26_depth_buffering.rs) | [Link](https://vulkan-tutorial.com/Depth_buffering) | | 58 | | 27 | [27_model_loading.rs](./src/tutorials/27_model_loading.rs) | [Link](https://vulkan-tutorial.com/Loading_models) | Test this example in release mode. | 59 | | 28 | [28_mipmapping.rs](./src/tutorials/28_mipmapping.rs) | [Link](https://vulkan-tutorial.com/Generating_Mipmaps) | Test this example in release mode. | 60 | | 29 | [29_multisampling.rs](./src/tutorials/29_multisampling.rs) | [Link](https://vulkan-tutorial.com/Multisampling) | Test this example in release mode. | 61 | 62 | ### example usage 63 | 64 | ``` 65 | $ cargo run --bin 00 66 | ``` 67 | 68 | ## Snapshot 69 | 70 | ![All snapshots](snapshot/snapshot_all.png) 71 | 72 | ## See also 73 | 74 | [bwasty/vulkan-tutorial-rs](https://github.com/bwasty/vulkan-tutorial-rs) — another Rust implementation using [Vulkano](https://crates.io/crates/vulkano) 75 | 76 | [adrien-ben/vulkan-tutorial-rs](https://github.com/adrien-ben/vulkan-tutorial-rs) — another Rust implementation with some more custom chapters. 77 | 78 | [KhronosGroup/Vulkan-samples](https://github.com/khronosGroup/Vulkan-samples) — Offical examples from KhronosGroup 79 | 80 | [SaschaWillems/Vulkan](https://github.com/SaschaWillems/Vulkan) — More examples in C++ 81 | 82 | -------------------------------------------------------------------------------- /assets/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /chalet.jpg 3 | /chalet.obj 4 | /texture.jpg 5 | 6 | -------------------------------------------------------------------------------- /assets/android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/assets/android.png -------------------------------------------------------------------------------- /assets/apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/assets/apple.png -------------------------------------------------------------------------------- /assets/linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/assets/linux.png -------------------------------------------------------------------------------- /assets/vulkan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/assets/vulkan.png -------------------------------------------------------------------------------- /assets/windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/assets/windows.png -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: 2 | - master 3 | 4 | jobs: 5 | 6 | # - job: min 7 | # displayName: Minimum Rust 8 | # steps: 9 | # - template: ci/azure-install-rust.yml 10 | # parameters: 11 | # toolchain: 1.33.0 12 | # - script: cargo build 13 | 14 | - job: Linux 15 | pool: 16 | vmImage: ubuntu-18.04 17 | steps: 18 | - template: ci/azure-steps.yml 19 | strategy: 20 | matrix: 21 | x86_64: 22 | TARGET: x86_64-unknown-linux-gnu 23 | i686: 24 | TARGET: i686-unknown-linux-gnu 25 | 26 | # Disable beta and nightly build 27 | # x86_64-beta: 28 | # TARGET: x86_64-unknown-linux-gnu 29 | # TOOLCHAIN: beta 30 | # x86_64-nightly: 31 | # TARGET: x86_64-unknown-linux-gnu 32 | # TOOLCHAIN: nightly 33 | 34 | - job: macOS 35 | pool: 36 | vmImage: macos-10.14 37 | steps: 38 | - template: ci/azure-steps.yml 39 | strategy: 40 | matrix: 41 | x86_64: 42 | TARGET: x86_64-apple-darwin 43 | 44 | # Disable iOS build 45 | # aarch64-ios: 46 | # TARGET: aarch64-apple-ios 47 | # NO_RUN: --no-run 48 | 49 | - job: Windows_vs2019 50 | pool: 51 | vmImage: windows-2019 52 | steps: 53 | - template: ci/azure-steps.yml 54 | strategy: 55 | matrix: 56 | x86_64-msvc: 57 | TARGET: x86_64-pc-windows-msvc 58 | 59 | # - job: Windows_vs2017 60 | # pool: 61 | # vmImage: vs2017-win2016 62 | # steps: 63 | # - template: ci/azure-steps.yml 64 | # strategy: 65 | # matrix: 66 | # x86_64-msvc: 67 | # TARGET: x86_64-pc-windows-msvc 68 | # i686-msvc: 69 | # TARGET: i686-pc-windows-msvc 70 | # x86_64-gnu: 71 | # TARGET: x86_64-pc-windows-gnu 72 | # i686-gnu: 73 | # TARGET: i686-pc-windows-gnu 74 | 75 | # - job: docs 76 | # steps: 77 | # - template: ci/azure-install-rust.yml 78 | # - script: cargo doc --no-deps --all-features 79 | # - script: curl -LsSf https://git.io/fhJ8n | rustc - && (cd target/doc && ../../rust_out) 80 | # condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) 81 | # env: 82 | # GITHUB_DEPLOY_KEY: $(GITHUB_DEPLOY_KEY) 83 | -------------------------------------------------------------------------------- /ci/azure-install-rust.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - bash: | 3 | set -e 4 | toolchain=$TOOLCHAIN 5 | if [ "$toolchain" = "" ]; then 6 | toolchain=stable 7 | fi 8 | curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $toolchain 9 | echo "##vso[task.prependpath]$HOME/.cargo/bin" 10 | displayName: Install rust (unix) 11 | condition: ne( variables['Agent.OS'], 'Windows_NT' ) 12 | - script: | 13 | curl -sSf -o rustup-init.exe https://win.rustup.rs 14 | rustup-init.exe -y --default-toolchain stable-%TARGET% 15 | echo ##vso[task.prependpath]%USERPROFILE%\.cargo\bin 16 | displayName: Install rust (windows) 17 | condition: eq( variables['Agent.OS'], 'Windows_NT' ) 18 | - script: | 19 | rustc -Vv 20 | cargo -V 21 | displayName: Query rust and cargo versions 22 | -------------------------------------------------------------------------------- /ci/azure-steps.yml: -------------------------------------------------------------------------------- 1 | 2 | steps: 3 | - template: azure-install-rust.yml 4 | - bash: rustup target add $TARGET 5 | displayName: Install Rust target 6 | 7 | # - bash: sudo apt-get install g++-multilib 8 | # condition: eq( variables['Agent.OS'], 'Linux' ) 9 | # displayName: Install g++-multilib 10 | 11 | - script: cargo build 12 | displayName: "Normal build" 13 | # - bash: cargo test $NO_RUN -- --test-threads 1 14 | # displayName: "Crate tests" 15 | # - bash: cargo test $NO_RUN --features parallel -- --test-threads 1 16 | # displayName: "Crate tests (parallel)" 17 | # - bash: cargo test $NO_RUN --manifest-path cc-test/Cargo.toml --target $TARGET 18 | # displayName: "vulkan-tutorial-rust tests" 19 | # - bash: cargo test $NO_RUN --manifest-path cc-test/Cargo.toml --target $TARGET --features parallel 20 | # displayName: "vulkan-tutorial-rust tests (parallel)" 21 | # - bash: cargo test $NO_RUN --manifest-path cc-test/Cargo.toml --target $TARGET --release 22 | # displayName: "vulkan-tutorial-rust tests (release)" 23 | -------------------------------------------------------------------------------- /download_asset.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This script is modified from https://github.com/SaschaWillems/Vulkan/blob/master/download_assets.py 4 | 5 | import sys 6 | import os 7 | from urllib.request import urlretrieve 8 | 9 | # ASSET_GENERAL_TEXTURE_URL = "https://vulkan-tutorial.com/images/texture.jpg" 10 | ASSET_GENERAL_TEXTURE_URL = "https://raw.githubusercontent.com/heitaoflower/vulkan-tutorial/master/Tutorial29/textures/texture.jpg" 11 | # ASSET_CHALET_TEXTURE_URL = "https://vulkan-tutorial.com/resources/chalet.jpg" 12 | ASSET_CHALET_TEXTURE_URL = "https://raw.githubusercontent.com/heitaoflower/vulkan-tutorial/master/Tutorial29/textures/chalet.jpg" 13 | # ASSET_CHALET_OBJ_URL = "https://vulkan-tutorial.com/resources/chalet.obj.zip" 14 | ASSET_CHALET_OBJ_URL = "https://raw.githubusercontent.com/heitaoflower/vulkan-tutorial/master/Tutorial29/models/chalet.obj" 15 | 16 | ASSET_GENERAL_TEXTURE_PATH = "./assets/texture.jpg" 17 | ASSET_CHALET_TEXTURE_PATH = "./assets/chalet.jpg" 18 | ASSET_CHALET_OBJ_PATH = "./assets/chalet.obj" 19 | 20 | def reporthook(blocknum, blocksize, totalsize): 21 | bytesread = blocknum * blocksize 22 | if totalsize > 0: 23 | percent = bytesread * 1e2 / totalsize 24 | s = "\r%5.1f%% (%*d / %d bytes)" % (percent, len(str(totalsize)), bytesread, totalsize) 25 | sys.stderr.write(s) 26 | if bytesread >= totalsize: 27 | sys.stderr.write("\n") 28 | else: 29 | sys.stderr.write("read %d\n" % (bytesread,)) 30 | 31 | print("Downloading CC0 licensed image...") 32 | urlretrieve(ASSET_GENERAL_TEXTURE_URL, ASSET_GENERAL_TEXTURE_PATH, reporthook) 33 | 34 | print("Downloading chalet texture...") 35 | urlretrieve(ASSET_CHALET_TEXTURE_URL, ASSET_CHALET_TEXTURE_PATH, reporthook) 36 | 37 | print("Downloading chalet obj...") 38 | urlretrieve(ASSET_CHALET_OBJ_URL, ASSET_CHALET_OBJ_PATH, reporthook) 39 | 40 | print("Download finished") 41 | 42 | print('..done!') 43 | -------------------------------------------------------------------------------- /shaders/spv/09-shader-base.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/shaders/spv/09-shader-base.frag.spv -------------------------------------------------------------------------------- /shaders/spv/09-shader-base.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/shaders/spv/09-shader-base.vert.spv -------------------------------------------------------------------------------- /shaders/spv/17-shader-vertexbuffer.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/shaders/spv/17-shader-vertexbuffer.frag.spv -------------------------------------------------------------------------------- /shaders/spv/17-shader-vertexbuffer.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/shaders/spv/17-shader-vertexbuffer.vert.spv -------------------------------------------------------------------------------- /shaders/spv/21-shader-ubo.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/shaders/spv/21-shader-ubo.frag.spv -------------------------------------------------------------------------------- /shaders/spv/21-shader-ubo.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/shaders/spv/21-shader-ubo.vert.spv -------------------------------------------------------------------------------- /shaders/spv/25-shader-textures.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/shaders/spv/25-shader-textures.frag.spv -------------------------------------------------------------------------------- /shaders/spv/25-shader-textures.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/shaders/spv/25-shader-textures.vert.spv -------------------------------------------------------------------------------- /shaders/spv/26-shader-depth.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/shaders/spv/26-shader-depth.frag.spv -------------------------------------------------------------------------------- /shaders/spv/26-shader-depth.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/shaders/spv/26-shader-depth.vert.spv -------------------------------------------------------------------------------- /shaders/src/09-shader-base.frag: -------------------------------------------------------------------------------- 1 | 2 | 3 | #version 450 4 | 5 | #extension GL_ARB_separate_shader_objects : enable 6 | 7 | layout(location = 0) in vec3 fragColor; 8 | 9 | layout(location = 0) out vec4 outColor; 10 | 11 | void main() { 12 | 13 | outColor = vec4(fragColor, 1.0); 14 | } 15 | 16 | -------------------------------------------------------------------------------- /shaders/src/09-shader-base.vert: -------------------------------------------------------------------------------- 1 | 2 | 3 | #version 450 4 | #extension GL_ARB_separate_shader_objects : enable 5 | 6 | out gl_PerVertex { 7 | vec4 gl_Position; 8 | }; 9 | 10 | layout(location = 0) out vec3 fragColor; 11 | 12 | vec2 positions[3] = vec2[]( 13 | vec2(0.0, -0.5), 14 | vec2(0.5, 0.5), 15 | vec2(-0.5, 0.5) 16 | ); 17 | 18 | vec3 colors[3] = vec3[]( 19 | vec3(1.0, 0.0, 0.0), 20 | vec3(0.0, 1.0, 0.0), 21 | vec3(0.0, 0.0, 1.0) 22 | ); 23 | 24 | void main() { 25 | 26 | gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); 27 | fragColor = colors[gl_VertexIndex]; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /shaders/src/17-shader-vertexbuffer.frag: -------------------------------------------------------------------------------- 1 | 2 | #version 450 3 | 4 | #extension GL_ARB_separate_shader_objects: enable 5 | 6 | layout (location = 0) in vec3 fragColor; 7 | 8 | layout (location = 0) out vec4 outColor; 9 | 10 | void main() { 11 | 12 | outColor = vec4(fragColor, 1.0); 13 | } 14 | -------------------------------------------------------------------------------- /shaders/src/17-shader-vertexbuffer.vert: -------------------------------------------------------------------------------- 1 | 2 | #version 450 3 | 4 | #extension GL_ARB_separate_shader_objects: enable 5 | 6 | layout (location = 0) in vec2 inPosition; 7 | layout (location = 1) in vec3 inColor; 8 | 9 | layout (location = 0) out vec3 fragColor; 10 | 11 | out gl_PerVertex { 12 | vec4 gl_Position; 13 | }; 14 | 15 | void main() { 16 | 17 | gl_Position = vec4(inPosition, 0.0, 1.0); 18 | fragColor = inColor; 19 | } 20 | -------------------------------------------------------------------------------- /shaders/src/21-shader-ubo.frag: -------------------------------------------------------------------------------- 1 | 2 | #version 450 3 | 4 | #extension GL_ARB_separate_shader_objects : enable 5 | 6 | layout(location = 0) in vec3 fragColor; 7 | 8 | layout(location = 0) out vec4 outColor; 9 | 10 | void main() { 11 | 12 | outColor = vec4(fragColor, 1.0); 13 | } 14 | -------------------------------------------------------------------------------- /shaders/src/21-shader-ubo.vert: -------------------------------------------------------------------------------- 1 | 2 | #version 450 3 | 4 | #extension GL_ARB_separate_shader_objects : enable 5 | 6 | layout(set = 0, binding = 0) uniform UniformBufferObject { 7 | mat4 model; 8 | mat4 view; 9 | mat4 proj; 10 | } ubo; 11 | 12 | layout(location = 0) in vec2 inPosition; 13 | layout(location = 1) in vec3 inColor; 14 | 15 | layout(location = 0) out vec3 fragColor; 16 | 17 | out gl_PerVertex { 18 | 19 | vec4 gl_Position; 20 | }; 21 | 22 | void main() { 23 | 24 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); 25 | fragColor = inColor; 26 | } 27 | -------------------------------------------------------------------------------- /shaders/src/25-shader-textures.frag: -------------------------------------------------------------------------------- 1 | 2 | #version 450 3 | 4 | #extension GL_ARB_separate_shader_objects : enable 5 | 6 | layout (binding = 1) uniform sampler2D texSampler; 7 | 8 | layout (location = 0) in vec3 fragColor; 9 | layout (location = 1) in vec2 fragTexColor; 10 | 11 | layout (location = 0) out vec4 outColor; 12 | 13 | void main() { 14 | 15 | outColor = texture(texSampler, fragTexColor); 16 | } 17 | -------------------------------------------------------------------------------- /shaders/src/25-shader-textures.vert: -------------------------------------------------------------------------------- 1 | 2 | #version 450 3 | 4 | #extension GL_ARB_separate_shader_objects : enable 5 | 6 | layout(set = 0, binding = 0) uniform UniformBufferObject { 7 | mat4 model; 8 | mat4 view; 9 | mat4 proj; 10 | } ubo; 11 | 12 | layout (location = 0) in vec2 inPosition; 13 | layout (location = 1) in vec3 inColor; 14 | layout (location = 2) in vec2 inTexCoord; 15 | 16 | layout (location = 0) out vec3 fragColor; 17 | layout (location = 1) out vec2 fragTexCoord; 18 | 19 | out gl_PerVertex { 20 | 21 | vec4 gl_Position; 22 | }; 23 | 24 | void main() { 25 | 26 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); 27 | fragColor = inColor; 28 | fragTexCoord = inTexCoord; 29 | } 30 | -------------------------------------------------------------------------------- /shaders/src/26-shader-depth.frag: -------------------------------------------------------------------------------- 1 | 2 | #version 450 3 | 4 | #extension GL_ARB_separate_shader_objects : enable 5 | 6 | layout (binding = 1) uniform sampler2D texSampler; 7 | 8 | layout (location = 0) in vec3 fragColor; 9 | layout (location = 1) in vec2 fragTexCoord; 10 | 11 | layout (location = 0) out vec4 outColor; 12 | 13 | void main() { 14 | 15 | outColor = texture(texSampler, fragTexCoord); 16 | } 17 | -------------------------------------------------------------------------------- /shaders/src/26-shader-depth.vert: -------------------------------------------------------------------------------- 1 | 2 | #version 450 3 | 4 | #extension GL_ARB_separate_shader_objects : enable 5 | 6 | layout (binding = 0) uniform UniformBufferObject { 7 | mat4 model; 8 | mat4 view; 9 | mat4 proj; 10 | } ubo; 11 | 12 | layout (location = 0) in vec3 inPosition; 13 | layout (location = 1) in vec3 inColor; 14 | layout (location = 2) in vec2 inTexCoord; 15 | 16 | layout (location = 0) out vec3 fragColor; 17 | layout (location = 1) out vec2 fragTexCoord; 18 | 19 | out gl_PerVertex { 20 | vec4 gl_Position; 21 | }; 22 | 23 | void main() { 24 | 25 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); 26 | fragColor = inColor; 27 | fragTexCoord = inTexCoord; 28 | } -------------------------------------------------------------------------------- /snapshot/16-swapchain-recreation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/snapshot/16-swapchain-recreation.png -------------------------------------------------------------------------------- /snapshot/18-vertex-buffer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/snapshot/18-vertex-buffer.png -------------------------------------------------------------------------------- /snapshot/20-index-buffer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/snapshot/20-index-buffer.png -------------------------------------------------------------------------------- /snapshot/22-descriptor-sets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/snapshot/22-descriptor-sets.png -------------------------------------------------------------------------------- /snapshot/25-texture-mapping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/snapshot/25-texture-mapping.png -------------------------------------------------------------------------------- /snapshot/29-multi-sampling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/snapshot/29-multi-sampling.png -------------------------------------------------------------------------------- /snapshot/CombineImage.shadron: -------------------------------------------------------------------------------- 1 | 2 | image png1 = file("16-swapchain-recreation.png"); 3 | image png2 = file("18-vertex-buffer.png"); 4 | image png3 = file("20-index-buffer.png"); 5 | image png4 = file("22-descriptor-sets.png"); 6 | image png5 = file("25-texture-mapping.png"); 7 | image png6 = file("29-multi-sampling.png"); 8 | 9 | const float tile_width = 1.0 / 3.0; 10 | const float tile_height = 1.0 / 2.0; 11 | 12 | const int output_width = 2400; 13 | const int output_height = 1200; 14 | 15 | glsl vec4 CombineImages(vec2 pos) { 16 | vec4 sample = vec4(1.0); 17 | 18 | if (pos.x < tile_width && pos.y > tile_height) { 19 | float tex_x = pos.x / tile_width; 20 | float tex_y = (pos.y - tile_height) / tile_height; 21 | 22 | sample = texture(png1, vec2(tex_x, tex_y)); 23 | } else if (pos.x < tile_width * 2 && pos.y > tile_height) { 24 | float tex_x = (pos.x - tile_width) / tile_width; 25 | float tex_y = (pos.y - tile_height) / tile_height; 26 | 27 | sample = texture(png2, vec2(tex_x, tex_y)); 28 | } else if (pos.x < 1.0 && pos.y > tile_height) { 29 | float tex_x = (pos.x - tile_width * 2) / tile_width; 30 | float tex_y = (pos.y - tile_height) / tile_height; 31 | 32 | sample = texture(png3, vec2(tex_x, tex_y)); 33 | } else if (pos.x < tile_width && pos.y < tile_height) { 34 | float tex_x = pos.x / tile_width; 35 | float tex_y = pos.y / tile_height; 36 | 37 | sample = texture(png4, vec2(tex_x, tex_y)); 38 | } else if (pos.x < tile_width * 2 && pos.y < tile_height) { 39 | float tex_x = (pos.x - tile_width) / tile_width; 40 | float tex_y = pos.y / tile_height; 41 | 42 | sample = texture(png5, vec2(tex_x, tex_y)); 43 | } else if (pos.x < 1.0 && pos.y < tile_height) { 44 | float tex_x = (pos.x - tile_width * 2) / tile_width; 45 | float tex_y = pos.y / tile_height; 46 | 47 | sample = texture(png6, vec2(tex_x, tex_y)); 48 | } 49 | 50 | return sample; 51 | } 52 | 53 | image Output = glsl(CombineImages, output_width, output_height); 54 | -------------------------------------------------------------------------------- /snapshot/snapshot_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unknownue/vulkan-tutorial-rust/9ab61f12f1cd641ddeacb5be8cf97d39db0aa9f0/snapshot/snapshot_all.png -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod utility; 2 | -------------------------------------------------------------------------------- /src/tutorials/00_base_code.rs: -------------------------------------------------------------------------------- 1 | 2 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 3 | use winit::event_loop::{EventLoop, ControlFlow}; 4 | 5 | // Constants 6 | const WINDOW_TITLE: &'static str = "00.Base Code"; 7 | const WINDOW_WIDTH: u32 = 800; 8 | const WINDOW_HEIGHT: u32 = 600; 9 | 10 | struct VulkanApp; 11 | 12 | impl VulkanApp { 13 | 14 | fn init_window(event_loop: &EventLoop<()>) -> winit::window::Window { 15 | winit::window::WindowBuilder::new() 16 | .with_title(WINDOW_TITLE) 17 | .with_inner_size(winit::dpi::LogicalSize::new(WINDOW_WIDTH, WINDOW_HEIGHT)) 18 | .build(event_loop) 19 | .expect("Failed to create window.") 20 | } 21 | 22 | pub fn main_loop(event_loop: EventLoop<()>) { 23 | 24 | event_loop.run(move |event, _, control_flow| { 25 | 26 | match event { 27 | | Event::WindowEvent { event, .. } => { 28 | match event { 29 | | WindowEvent::CloseRequested => { 30 | *control_flow = ControlFlow::Exit 31 | }, 32 | | WindowEvent::KeyboardInput { input, .. } => { 33 | match input { 34 | | KeyboardInput { virtual_keycode, state, .. } => { 35 | match (virtual_keycode, state) { 36 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 37 | dbg!(); 38 | *control_flow = ControlFlow::Exit 39 | }, 40 | | _ => {}, 41 | } 42 | }, 43 | } 44 | }, 45 | | _ => {}, 46 | } 47 | }, 48 | _ => (), 49 | } 50 | 51 | }) 52 | } 53 | } 54 | 55 | fn main() { 56 | 57 | let event_loop = EventLoop::new(); 58 | let _window = VulkanApp::init_window(&event_loop); 59 | 60 | VulkanApp::main_loop(event_loop); 61 | } 62 | -------------------------------------------------------------------------------- /src/tutorials/01_instance_creation.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::utility; 2 | use vulkan_tutorial_rust::utility::constants::*; 3 | 4 | use ash::version::EntryV1_0; 5 | use ash::version::InstanceV1_0; 6 | use ash::vk; 7 | use std::ffi::CString; 8 | use std::ptr; 9 | 10 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 11 | use winit::event_loop::{EventLoop, ControlFlow}; 12 | use winit::window::Window; 13 | 14 | // Constants 15 | const WINDOW_TITLE: &'static str = "01.Instance Creation"; 16 | 17 | struct VulkanApp { 18 | _entry: ash::Entry, 19 | instance: ash::Instance, 20 | } 21 | 22 | impl VulkanApp { 23 | 24 | pub fn new() -> VulkanApp { 25 | 26 | // init vulkan stuff 27 | let entry = ash::Entry::new().unwrap(); 28 | let instance = VulkanApp::create_instance(&entry); 29 | 30 | // cleanup(); the 'drop' function will take care of it. 31 | VulkanApp { 32 | _entry: entry, 33 | instance, 34 | } 35 | } 36 | 37 | fn init_window(event_loop: &EventLoop<()>) -> winit::window::Window { 38 | winit::window::WindowBuilder::new() 39 | .with_title(WINDOW_TITLE) 40 | .with_inner_size(winit::dpi::LogicalSize::new(WINDOW_WIDTH, WINDOW_HEIGHT)) 41 | .build(event_loop) 42 | .expect("Failed to create window.") 43 | } 44 | 45 | fn create_instance(entry: &ash::Entry) -> ash::Instance { 46 | 47 | let app_name = CString::new(WINDOW_TITLE).unwrap(); 48 | let engine_name = CString::new("Vulkan Engine").unwrap(); 49 | let app_info = vk::ApplicationInfo { 50 | s_type: vk::StructureType::APPLICATION_INFO, 51 | p_next: ptr::null(), 52 | p_application_name: app_name.as_ptr(), 53 | application_version: APPLICATION_VERSION, 54 | p_engine_name: engine_name.as_ptr(), 55 | engine_version: ENGINE_VERSION, 56 | api_version: API_VERSION, 57 | }; 58 | 59 | let extension_names = utility::platforms::required_extension_names(); 60 | 61 | let create_info = vk::InstanceCreateInfo { 62 | s_type: vk::StructureType::INSTANCE_CREATE_INFO, 63 | p_next: ptr::null(), 64 | flags: vk::InstanceCreateFlags::empty(), 65 | p_application_info: &app_info, 66 | pp_enabled_layer_names: ptr::null(), 67 | enabled_layer_count: 0, 68 | pp_enabled_extension_names: extension_names.as_ptr(), 69 | enabled_extension_count: extension_names.len() as u32, 70 | }; 71 | 72 | let instance: ash::Instance = unsafe { 73 | entry 74 | .create_instance(&create_info, None) 75 | .expect("Failed to create instance!") 76 | }; 77 | 78 | instance 79 | } 80 | 81 | fn draw_frame(&mut self) { 82 | // Drawing will be here 83 | } 84 | 85 | pub fn main_loop(mut self, event_loop: EventLoop<()>, window: Window) { 86 | 87 | event_loop.run(move |event, _, control_flow| { 88 | 89 | match event { 90 | | Event::WindowEvent { event, .. } => { 91 | match event { 92 | | WindowEvent::CloseRequested => { 93 | *control_flow = ControlFlow::Exit 94 | }, 95 | | WindowEvent::KeyboardInput { input, .. } => { 96 | match input { 97 | | KeyboardInput { virtual_keycode, state, .. } => { 98 | match (virtual_keycode, state) { 99 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 100 | *control_flow = ControlFlow::Exit 101 | }, 102 | | _ => {}, 103 | } 104 | }, 105 | } 106 | }, 107 | | _ => {}, 108 | } 109 | }, 110 | | Event::MainEventsCleared => { 111 | window.request_redraw(); 112 | }, 113 | | Event::RedrawRequested(_window_id) => { 114 | self.draw_frame(); 115 | }, 116 | _ => (), 117 | } 118 | 119 | }) 120 | } 121 | } 122 | 123 | impl Drop for VulkanApp { 124 | fn drop(&mut self) { 125 | unsafe { 126 | self.instance.destroy_instance(None); 127 | } 128 | } 129 | } 130 | 131 | fn main() { 132 | 133 | let event_loop = EventLoop::new(); 134 | let window = VulkanApp::init_window(&event_loop); 135 | 136 | let vulkan_app = VulkanApp::new(); 137 | vulkan_app.main_loop(event_loop, window); 138 | } 139 | -------------------------------------------------------------------------------- /src/tutorials/02_validation_layers.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::{utility, utility::constants::*, utility::debug::ValidationInfo}; 2 | 3 | use ash::version::EntryV1_0; 4 | use ash::version::InstanceV1_0; 5 | use ash::vk; 6 | use std::ffi::{CStr, CString}; 7 | use std::os::raw::c_void; 8 | use std::ptr; 9 | 10 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 11 | use winit::event_loop::{EventLoop, ControlFlow}; 12 | use winit::window::{Window, WindowBuilder}; 13 | 14 | // Constants 15 | const WINDOW_TITLE: &'static str = "02.Validation Layers"; 16 | const VALIDATION: ValidationInfo = ValidationInfo { 17 | is_enable: true, 18 | required_validation_layers: ["VK_LAYER_KHRONOS_validation"], 19 | }; 20 | 21 | /// the callback function used in Debug Utils. 22 | unsafe extern "system" fn vulkan_debug_utils_callback( 23 | message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, 24 | message_type: vk::DebugUtilsMessageTypeFlagsEXT, 25 | p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT, 26 | _p_user_data: *mut c_void, 27 | ) -> vk::Bool32 { 28 | let severity = match message_severity { 29 | vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => "[Verbose]", 30 | vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => "[Warning]", 31 | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => "[Error]", 32 | vk::DebugUtilsMessageSeverityFlagsEXT::INFO => "[Info]", 33 | _ => "[Unknown]", 34 | }; 35 | let types = match message_type { 36 | vk::DebugUtilsMessageTypeFlagsEXT::GENERAL => "[General]", 37 | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE => "[Performance]", 38 | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION => "[Validation]", 39 | _ => "[Unknown]", 40 | }; 41 | let message = CStr::from_ptr((*p_callback_data).p_message); 42 | println!("[Debug]{}{}{:?}", severity, types, message); 43 | 44 | vk::FALSE 45 | } 46 | 47 | struct VulkanApp { 48 | 49 | _entry: ash::Entry, 50 | instance: ash::Instance, 51 | debug_utils_loader: ash::extensions::ext::DebugUtils, 52 | debug_merssager: vk::DebugUtilsMessengerEXT, 53 | } 54 | 55 | impl VulkanApp { 56 | pub fn new() -> VulkanApp { 57 | 58 | // init vulkan stuff 59 | let entry = ash::Entry::new().unwrap(); 60 | let instance = VulkanApp::create_instance(&entry); 61 | let (debug_utils_loader, debug_merssager) = VulkanApp::setup_debug_utils(&entry, &instance); 62 | 63 | // cleanup(); the 'drop' function will take care of it. 64 | VulkanApp { 65 | 66 | _entry: entry, 67 | instance, 68 | debug_utils_loader, 69 | debug_merssager, 70 | } 71 | } 72 | 73 | fn init_window(event_loop: &EventLoop<()>) -> Window { 74 | WindowBuilder::new() 75 | .with_title(WINDOW_TITLE) 76 | .with_inner_size(winit::dpi::LogicalSize::new(WINDOW_WIDTH, WINDOW_HEIGHT)) 77 | .build(event_loop) 78 | .expect("Failed to create window.") 79 | } 80 | 81 | fn create_instance(entry: &ash::Entry) -> ash::Instance { 82 | if VALIDATION.is_enable && VulkanApp::check_validation_layer_support(entry) == false { 83 | panic!("Validation layers requested, but not available!"); 84 | } 85 | 86 | let app_name = CString::new(WINDOW_TITLE).unwrap(); 87 | let engine_name = CString::new("Vulkan Engine").unwrap(); 88 | let app_info = vk::ApplicationInfo { 89 | p_application_name: app_name.as_ptr(), 90 | s_type: vk::StructureType::APPLICATION_INFO, 91 | p_next: ptr::null(), 92 | application_version: APPLICATION_VERSION, 93 | p_engine_name: engine_name.as_ptr(), 94 | engine_version: ENGINE_VERSION, 95 | api_version: API_VERSION, // set api_version to vk_make_version!(2, 0, 92) to test if the p_next field in vk::InstanceCreateInfo works. 96 | }; 97 | 98 | // This create info used to debug issues in vk::createInstance and vk::destroyInstance. 99 | let debug_utils_create_info = populate_debug_messenger_create_info(); 100 | 101 | // VK_EXT debug utils has been requested here. 102 | let extension_names = utility::platforms::required_extension_names(); 103 | 104 | let requred_validation_layer_raw_names: Vec = VALIDATION 105 | .required_validation_layers 106 | .iter() 107 | .map(|layer_name| CString::new(*layer_name).unwrap()) 108 | .collect(); 109 | let enable_layer_names: Vec<*const i8> = requred_validation_layer_raw_names 110 | .iter() 111 | .map(|layer_name| layer_name.as_ptr()) 112 | .collect(); 113 | 114 | let create_info = vk::InstanceCreateInfo { 115 | s_type: vk::StructureType::INSTANCE_CREATE_INFO, 116 | p_next: if VALIDATION.is_enable { 117 | &debug_utils_create_info as *const vk::DebugUtilsMessengerCreateInfoEXT 118 | as *const c_void 119 | } else { 120 | ptr::null() 121 | }, 122 | flags: vk::InstanceCreateFlags::empty(), 123 | p_application_info: &app_info, 124 | pp_enabled_layer_names: if VALIDATION.is_enable { 125 | enable_layer_names.as_ptr() 126 | } else { 127 | ptr::null() 128 | }, 129 | enabled_layer_count: if VALIDATION.is_enable { 130 | enable_layer_names.len() 131 | } else { 132 | 0 133 | } as u32, 134 | pp_enabled_extension_names: extension_names.as_ptr(), 135 | enabled_extension_count: extension_names.len() as u32, 136 | }; 137 | 138 | let instance: ash::Instance = unsafe { 139 | entry 140 | .create_instance(&create_info, None) 141 | .expect("Failed to create Instance!") 142 | }; 143 | 144 | instance 145 | } 146 | 147 | fn check_validation_layer_support(entry: &ash::Entry) -> bool { 148 | // if support validation layer, then return true 149 | 150 | let layer_properties = entry 151 | .enumerate_instance_layer_properties() 152 | .expect("Failed to enumerate Instance Layers Properties!"); 153 | 154 | if layer_properties.len() <= 0 { 155 | eprintln!("No available layers."); 156 | return false; 157 | } else { 158 | println!("Instance Available Layers: "); 159 | for layer in layer_properties.iter() { 160 | let layer_name = utility::tools::vk_to_string(&layer.layer_name); 161 | println!("\t{}", layer_name); 162 | } 163 | } 164 | 165 | for required_layer_name in VALIDATION.required_validation_layers.iter() { 166 | let mut is_layer_found = false; 167 | 168 | for layer_property in layer_properties.iter() { 169 | let test_layer_name = utility::tools::vk_to_string(&layer_property.layer_name); 170 | if (*required_layer_name) == test_layer_name { 171 | is_layer_found = true; 172 | break; 173 | } 174 | } 175 | 176 | if is_layer_found == false { 177 | return false; 178 | } 179 | } 180 | 181 | true 182 | } 183 | 184 | fn setup_debug_utils( 185 | entry: &ash::Entry, 186 | instance: &ash::Instance, 187 | ) -> (ash::extensions::ext::DebugUtils, vk::DebugUtilsMessengerEXT) { 188 | let debug_utils_loader = ash::extensions::ext::DebugUtils::new(entry, instance); 189 | 190 | if VALIDATION.is_enable == false { 191 | (debug_utils_loader, ash::vk::DebugUtilsMessengerEXT::null()) 192 | } else { 193 | let messenger_ci = populate_debug_messenger_create_info(); 194 | 195 | let utils_messenger = unsafe { 196 | debug_utils_loader 197 | .create_debug_utils_messenger(&messenger_ci, None) 198 | .expect("Debug Utils Callback") 199 | }; 200 | 201 | (debug_utils_loader, utils_messenger) 202 | } 203 | } 204 | 205 | fn draw_frame(&mut self) { 206 | // Drawing will be here 207 | } 208 | 209 | pub fn main_loop(mut self, event_loop: EventLoop<()>, window: Window) { 210 | 211 | event_loop.run(move |event, _, control_flow| { 212 | 213 | match event { 214 | | Event::WindowEvent { event, .. } => { 215 | match event { 216 | | WindowEvent::CloseRequested => { 217 | *control_flow = ControlFlow::Exit 218 | }, 219 | | WindowEvent::KeyboardInput { input, .. } => { 220 | match input { 221 | | KeyboardInput { virtual_keycode, state, .. } => { 222 | match (virtual_keycode, state) { 223 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 224 | *control_flow = ControlFlow::Exit 225 | }, 226 | | _ => {}, 227 | } 228 | }, 229 | } 230 | }, 231 | | _ => {}, 232 | } 233 | }, 234 | | Event::MainEventsCleared => { 235 | window.request_redraw(); 236 | }, 237 | | Event::RedrawRequested(_window_id) => { 238 | self.draw_frame(); 239 | }, 240 | _ => (), 241 | } 242 | 243 | }) 244 | } 245 | } 246 | 247 | fn populate_debug_messenger_create_info() -> vk::DebugUtilsMessengerCreateInfoEXT { 248 | vk::DebugUtilsMessengerCreateInfoEXT { 249 | s_type: vk::StructureType::DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, 250 | p_next: ptr::null(), 251 | flags: vk::DebugUtilsMessengerCreateFlagsEXT::empty(), 252 | message_severity: vk::DebugUtilsMessageSeverityFlagsEXT::WARNING | 253 | // vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE | 254 | // vk::DebugUtilsMessageSeverityFlagsEXT::INFO | 255 | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR, 256 | message_type: vk::DebugUtilsMessageTypeFlagsEXT::GENERAL 257 | | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE 258 | | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION, 259 | pfn_user_callback: Some(vulkan_debug_utils_callback), 260 | p_user_data: ptr::null_mut(), 261 | } 262 | } 263 | 264 | impl Drop for VulkanApp { 265 | fn drop(&mut self) { 266 | unsafe { 267 | if VALIDATION.is_enable { 268 | self.debug_utils_loader 269 | .destroy_debug_utils_messenger(self.debug_merssager, None); 270 | } 271 | self.instance.destroy_instance(None); 272 | } 273 | } 274 | } 275 | 276 | fn main() { 277 | 278 | let event_loop = EventLoop::new(); 279 | let window = VulkanApp::init_window(&event_loop); 280 | 281 | let vulkan_app = VulkanApp::new(); 282 | vulkan_app.main_loop(event_loop, window); 283 | } 284 | -------------------------------------------------------------------------------- /src/tutorials/03_physical_device_selection.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::{ 2 | utility, // the mod define some fixed functions that have been learned before. 3 | utility::constants::*, 4 | utility::share, 5 | }; 6 | 7 | use ash::version::InstanceV1_0; 8 | use ash::vk; 9 | use ash::{vk_version_major, vk_version_minor, vk_version_patch}; 10 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 11 | use winit::event_loop::{EventLoop, ControlFlow}; 12 | 13 | // Constants 14 | const WINDOW_TITLE: &'static str = "03.Physical Device Selection"; 15 | 16 | struct QueueFamilyIndices { 17 | graphics_family: Option, 18 | } 19 | 20 | impl QueueFamilyIndices { 21 | pub fn is_complete(&self) -> bool { 22 | self.graphics_family.is_some() 23 | } 24 | } 25 | 26 | struct VulkanApp { 27 | 28 | // vulkan stuff 29 | _entry: ash::Entry, 30 | instance: ash::Instance, 31 | debug_utils_loader: ash::extensions::ext::DebugUtils, 32 | debug_merssager: vk::DebugUtilsMessengerEXT, 33 | _physical_device: vk::PhysicalDevice, 34 | } 35 | 36 | impl VulkanApp { 37 | pub fn new() -> VulkanApp { 38 | 39 | // init vulkan stuff 40 | let entry = ash::Entry::new().unwrap(); 41 | let instance = share::create_instance( 42 | &entry, 43 | WINDOW_TITLE, 44 | VALIDATION.is_enable, 45 | &VALIDATION.required_validation_layers.to_vec(), 46 | ); 47 | 48 | let (debug_utils_loader, debug_merssager) = 49 | utility::debug::setup_debug_utils(VALIDATION.is_enable, &entry, &instance); 50 | let physical_device = VulkanApp::pick_physical_device(&instance); 51 | 52 | // cleanup(); the 'drop' function will take care of it. 53 | VulkanApp { 54 | 55 | _entry: entry, 56 | instance, 57 | debug_utils_loader, 58 | debug_merssager, 59 | _physical_device: physical_device, 60 | } 61 | } 62 | 63 | fn pick_physical_device(instance: &ash::Instance) -> vk::PhysicalDevice { 64 | let physical_devices = unsafe { 65 | instance 66 | .enumerate_physical_devices() 67 | .expect("Failed to enumerate Physical Devices!") 68 | }; 69 | 70 | println!( 71 | "{} devices (GPU) found with vulkan support.", 72 | physical_devices.len() 73 | ); 74 | 75 | let mut result = None; 76 | for &physical_device in physical_devices.iter() { 77 | if VulkanApp::is_physical_device_suitable(instance, physical_device) { 78 | if result.is_none() { 79 | result = Some(physical_device) 80 | } 81 | } 82 | } 83 | 84 | match result { 85 | None => panic!("Failed to find a suitable GPU!"), 86 | Some(physical_device) => physical_device, 87 | } 88 | } 89 | 90 | fn is_physical_device_suitable( 91 | instance: &ash::Instance, 92 | physical_device: vk::PhysicalDevice, 93 | ) -> bool { 94 | let device_properties = unsafe { instance.get_physical_device_properties(physical_device) }; 95 | let device_features = unsafe { instance.get_physical_device_features(physical_device) }; 96 | let device_queue_families = 97 | unsafe { instance.get_physical_device_queue_family_properties(physical_device) }; 98 | 99 | let device_type = match device_properties.device_type { 100 | vk::PhysicalDeviceType::CPU => "Cpu", 101 | vk::PhysicalDeviceType::INTEGRATED_GPU => "Integrated GPU", 102 | vk::PhysicalDeviceType::DISCRETE_GPU => "Discrete GPU", 103 | vk::PhysicalDeviceType::VIRTUAL_GPU => "Virtual GPU", 104 | vk::PhysicalDeviceType::OTHER => "Unknown", 105 | _ => panic!(), 106 | }; 107 | 108 | let device_name = utility::tools::vk_to_string(&device_properties.device_name); 109 | println!( 110 | "\tDevice Name: {}, id: {}, type: {}", 111 | device_name, device_properties.device_id, device_type 112 | ); 113 | 114 | let major_version = vk_version_major!(device_properties.api_version); 115 | let minor_version = vk_version_minor!(device_properties.api_version); 116 | let patch_version = vk_version_patch!(device_properties.api_version); 117 | 118 | println!( 119 | "\tAPI Version: {}.{}.{}", 120 | major_version, minor_version, patch_version 121 | ); 122 | 123 | println!("\tSupport Queue Family: {}", device_queue_families.len()); 124 | println!("\t\tQueue Count | Graphics, Compute, Transfer, Sparse Binding"); 125 | for queue_family in device_queue_families.iter() { 126 | let is_graphics_support = if queue_family.queue_flags.contains(vk::QueueFlags::GRAPHICS) 127 | { 128 | "support" 129 | } else { 130 | "unsupport" 131 | }; 132 | let is_compute_support = if queue_family.queue_flags.contains(vk::QueueFlags::COMPUTE) { 133 | "support" 134 | } else { 135 | "unsupport" 136 | }; 137 | let is_transfer_support = if queue_family.queue_flags.contains(vk::QueueFlags::TRANSFER) 138 | { 139 | "support" 140 | } else { 141 | "unsupport" 142 | }; 143 | let is_sparse_support = if queue_family 144 | .queue_flags 145 | .contains(vk::QueueFlags::SPARSE_BINDING) 146 | { 147 | "support" 148 | } else { 149 | "unsupport" 150 | }; 151 | 152 | println!( 153 | "\t\t{}\t | {}, {}, {}, {}", 154 | queue_family.queue_count, 155 | is_graphics_support, 156 | is_compute_support, 157 | is_transfer_support, 158 | is_sparse_support 159 | ); 160 | } 161 | 162 | // there are plenty of features 163 | println!( 164 | "\tGeometry Shader support: {}", 165 | if device_features.geometry_shader == 1 { 166 | "Support" 167 | } else { 168 | "Unsupport" 169 | } 170 | ); 171 | 172 | let indices = VulkanApp::find_queue_family(instance, physical_device); 173 | 174 | return indices.is_complete(); 175 | } 176 | 177 | fn find_queue_family( 178 | instance: &ash::Instance, 179 | physical_device: vk::PhysicalDevice, 180 | ) -> QueueFamilyIndices { 181 | let queue_families = 182 | unsafe { instance.get_physical_device_queue_family_properties(physical_device) }; 183 | 184 | let mut queue_family_indices = QueueFamilyIndices { 185 | graphics_family: None, 186 | }; 187 | 188 | let mut index = 0; 189 | for queue_family in queue_families.iter() { 190 | if queue_family.queue_count > 0 191 | && queue_family.queue_flags.contains(vk::QueueFlags::GRAPHICS) 192 | { 193 | queue_family_indices.graphics_family = Some(index); 194 | } 195 | 196 | if queue_family_indices.is_complete() { 197 | break; 198 | } 199 | 200 | index += 1; 201 | } 202 | 203 | queue_family_indices 204 | } 205 | 206 | fn draw_frame(&mut self) { 207 | // Drawing will be here 208 | } 209 | } 210 | 211 | impl Drop for VulkanApp { 212 | fn drop(&mut self) { 213 | unsafe { 214 | if VALIDATION.is_enable { 215 | self.debug_utils_loader 216 | .destroy_debug_utils_messenger(self.debug_merssager, None); 217 | } 218 | self.instance.destroy_instance(None); 219 | } 220 | } 221 | } 222 | 223 | // Fix content ------------------------------------------------------------------------------- 224 | impl VulkanApp { 225 | pub fn main_loop(mut self, event_loop: EventLoop<()>, window: winit::window::Window) { 226 | 227 | event_loop.run(move |event, _, control_flow| { 228 | 229 | match event { 230 | | Event::WindowEvent { event, .. } => { 231 | match event { 232 | | WindowEvent::CloseRequested => { 233 | *control_flow = ControlFlow::Exit 234 | }, 235 | | WindowEvent::KeyboardInput { input, .. } => { 236 | match input { 237 | | KeyboardInput { virtual_keycode, state, .. } => { 238 | match (virtual_keycode, state) { 239 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 240 | *control_flow = ControlFlow::Exit 241 | }, 242 | | _ => {}, 243 | } 244 | }, 245 | } 246 | }, 247 | | _ => {}, 248 | } 249 | }, 250 | | Event::MainEventsCleared => { 251 | window.request_redraw(); 252 | }, 253 | | Event::RedrawRequested(_window_id) => { 254 | self.draw_frame(); 255 | }, 256 | _ => (), 257 | } 258 | 259 | }) 260 | } 261 | } 262 | 263 | fn main() { 264 | 265 | let event_loop = EventLoop::new(); 266 | let window = utility::window::init_window(&event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT); 267 | 268 | let vulkan_app = VulkanApp::new(); 269 | vulkan_app.main_loop(event_loop, window); 270 | } 271 | // ------------------------------------------------------------------------------------------- 272 | -------------------------------------------------------------------------------- /src/tutorials/04_logical_device.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::{ 2 | utility, // the mod define some fixed functions that have been learned before. 3 | utility::constants::*, 4 | utility::debug::ValidationInfo, 5 | utility::share, 6 | }; 7 | 8 | use ash::version::DeviceV1_0; 9 | use ash::version::InstanceV1_0; 10 | use ash::vk; 11 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 12 | use winit::event_loop::{EventLoop, ControlFlow}; 13 | 14 | use std::ffi::CString; 15 | use std::os::raw::c_char; 16 | use std::ptr; 17 | 18 | // Constants 19 | const WINDOW_TITLE: &'static str = "04.Logical Device"; 20 | 21 | struct QueueFamilyIndices { 22 | graphics_family: Option, 23 | } 24 | 25 | impl QueueFamilyIndices { 26 | pub fn is_complete(&self) -> bool { 27 | self.graphics_family.is_some() 28 | } 29 | } 30 | 31 | struct VulkanApp { 32 | _entry: ash::Entry, 33 | instance: ash::Instance, 34 | debug_utils_loader: ash::extensions::ext::DebugUtils, 35 | debug_merssager: vk::DebugUtilsMessengerEXT, 36 | _physical_device: vk::PhysicalDevice, 37 | device: ash::Device, // Logical Device 38 | _graphics_queue: vk::Queue, 39 | } 40 | 41 | impl VulkanApp { 42 | pub fn new() -> VulkanApp { 43 | // init vulkan stuff 44 | let entry = ash::Entry::new().unwrap(); 45 | let instance = share::create_instance( 46 | &entry, 47 | WINDOW_TITLE, 48 | VALIDATION.is_enable, 49 | &VALIDATION.required_validation_layers.to_vec(), 50 | ); 51 | let (debug_utils_loader, debug_merssager) = 52 | utility::debug::setup_debug_utils(VALIDATION.is_enable, &entry, &instance); 53 | let physical_device = VulkanApp::pick_physical_device(&instance); 54 | let (logical_device, graphics_queue) = 55 | VulkanApp::create_logical_device(&instance, physical_device, &VALIDATION); 56 | 57 | // cleanup(); the 'drop' function will take care of it. 58 | VulkanApp { 59 | _entry: entry, 60 | instance, 61 | debug_utils_loader, 62 | debug_merssager, 63 | _physical_device: physical_device, 64 | device: logical_device, 65 | _graphics_queue: graphics_queue, 66 | } 67 | } 68 | 69 | fn pick_physical_device(instance: &ash::Instance) -> vk::PhysicalDevice { 70 | let physical_devices = unsafe { 71 | instance 72 | .enumerate_physical_devices() 73 | .expect("Failed to enumerate Physical Devices!") 74 | }; 75 | 76 | let result = physical_devices.iter().find(|physical_device| { 77 | VulkanApp::is_physical_device_suitable(instance, **physical_device) 78 | }); 79 | 80 | match result { 81 | Some(p_physical_device) => *p_physical_device, 82 | None => panic!("Failed to find a suitable GPU!"), 83 | } 84 | } 85 | 86 | fn is_physical_device_suitable( 87 | instance: &ash::Instance, 88 | physical_device: vk::PhysicalDevice, 89 | ) -> bool { 90 | let _device_properties = 91 | unsafe { instance.get_physical_device_properties(physical_device) }; 92 | let _device_features = unsafe { instance.get_physical_device_features(physical_device) }; 93 | 94 | let indices = VulkanApp::find_queue_family(instance, physical_device); 95 | 96 | return indices.is_complete(); 97 | } 98 | 99 | fn create_logical_device( 100 | instance: &ash::Instance, 101 | physical_device: vk::PhysicalDevice, 102 | validation: &ValidationInfo, 103 | ) -> (ash::Device, vk::Queue) { 104 | let indices = VulkanApp::find_queue_family(instance, physical_device); 105 | 106 | let queue_priorities = [1.0_f32]; 107 | let queue_create_info = vk::DeviceQueueCreateInfo { 108 | s_type: vk::StructureType::DEVICE_QUEUE_CREATE_INFO, 109 | p_next: ptr::null(), 110 | flags: vk::DeviceQueueCreateFlags::empty(), 111 | queue_family_index: indices.graphics_family.unwrap(), 112 | p_queue_priorities: queue_priorities.as_ptr(), 113 | queue_count: queue_priorities.len() as u32, 114 | }; 115 | 116 | let physical_device_features = vk::PhysicalDeviceFeatures { 117 | ..Default::default() // default just enable no feature. 118 | }; 119 | 120 | let requred_validation_layer_raw_names: Vec = validation 121 | .required_validation_layers 122 | .iter() 123 | .map(|layer_name| CString::new(*layer_name).unwrap()) 124 | .collect(); 125 | let enable_layer_names: Vec<*const c_char> = requred_validation_layer_raw_names 126 | .iter() 127 | .map(|layer_name| layer_name.as_ptr()) 128 | .collect(); 129 | 130 | let device_create_info = vk::DeviceCreateInfo { 131 | s_type: vk::StructureType::DEVICE_CREATE_INFO, 132 | p_next: ptr::null(), 133 | flags: vk::DeviceCreateFlags::empty(), 134 | queue_create_info_count: 1, 135 | p_queue_create_infos: &queue_create_info, 136 | enabled_layer_count: if validation.is_enable { 137 | enable_layer_names.len() 138 | } else { 139 | 0 140 | } as u32, 141 | pp_enabled_layer_names: if validation.is_enable { 142 | enable_layer_names.as_ptr() 143 | } else { 144 | ptr::null() 145 | }, 146 | enabled_extension_count: 0, 147 | pp_enabled_extension_names: ptr::null(), 148 | p_enabled_features: &physical_device_features, 149 | }; 150 | 151 | let device: ash::Device = unsafe { 152 | instance 153 | .create_device(physical_device, &device_create_info, None) 154 | .expect("Failed to create logical Device!") 155 | }; 156 | 157 | let graphics_queue = unsafe { device.get_device_queue(indices.graphics_family.unwrap(), 0) }; 158 | 159 | (device, graphics_queue) 160 | } 161 | 162 | fn find_queue_family( 163 | instance: &ash::Instance, 164 | physical_device: vk::PhysicalDevice, 165 | ) -> QueueFamilyIndices { 166 | let queue_families = 167 | unsafe { instance.get_physical_device_queue_family_properties(physical_device) }; 168 | 169 | let mut queue_family_indices = QueueFamilyIndices { 170 | graphics_family: None, 171 | }; 172 | 173 | let mut index = 0; 174 | for queue_family in queue_families.iter() { 175 | if queue_family.queue_count > 0 176 | && queue_family.queue_flags.contains(vk::QueueFlags::GRAPHICS) 177 | { 178 | queue_family_indices.graphics_family = Some(index); 179 | } 180 | 181 | if queue_family_indices.is_complete() { 182 | break; 183 | } 184 | 185 | index += 1; 186 | } 187 | 188 | queue_family_indices 189 | } 190 | 191 | fn draw_frame(&mut self) { 192 | // Drawing will be here 193 | } 194 | } 195 | 196 | impl Drop for VulkanApp { 197 | fn drop(&mut self) { 198 | unsafe { 199 | self.device.destroy_device(None); 200 | 201 | if VALIDATION.is_enable { 202 | self.debug_utils_loader 203 | .destroy_debug_utils_messenger(self.debug_merssager, None); 204 | } 205 | self.instance.destroy_instance(None); 206 | } 207 | } 208 | } 209 | 210 | // Fix content ------------------------------------------------------------------------------- 211 | impl VulkanApp { 212 | pub fn main_loop(mut self, event_loop: EventLoop<()>, window: winit::window::Window) { 213 | 214 | event_loop.run(move |event, _, control_flow| { 215 | 216 | match event { 217 | | Event::WindowEvent { event, .. } => { 218 | match event { 219 | | WindowEvent::CloseRequested => { 220 | *control_flow = ControlFlow::Exit 221 | }, 222 | | WindowEvent::KeyboardInput { input, .. } => { 223 | match input { 224 | | KeyboardInput { virtual_keycode, state, .. } => { 225 | match (virtual_keycode, state) { 226 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 227 | *control_flow = ControlFlow::Exit 228 | }, 229 | | _ => {}, 230 | } 231 | }, 232 | } 233 | }, 234 | | _ => {}, 235 | } 236 | }, 237 | | Event::MainEventsCleared => { 238 | window.request_redraw(); 239 | }, 240 | | Event::RedrawRequested(_window_id) => { 241 | self.draw_frame(); 242 | }, 243 | _ => (), 244 | } 245 | 246 | }) 247 | } 248 | } 249 | 250 | 251 | fn main() { 252 | let event_loop = EventLoop::new(); 253 | let window = utility::window::init_window(&event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT); 254 | 255 | let vulkan_app = VulkanApp::new(); 256 | vulkan_app.main_loop(event_loop, window); 257 | } 258 | // ------------------------------------------------------------------------------------------- 259 | -------------------------------------------------------------------------------- /src/tutorials/05_window_surface.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::{ 2 | utility, // the mod define some fixed functions that have been learned before. 3 | utility::constants::*, 4 | utility::debug::ValidationInfo, 5 | utility::share, 6 | }; 7 | 8 | use ash::version::DeviceV1_0; 9 | use ash::version::InstanceV1_0; 10 | use ash::vk; 11 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 12 | use winit::event_loop::{EventLoop, ControlFlow}; 13 | 14 | use std::ffi::CString; 15 | use std::os::raw::c_char; 16 | use std::ptr; 17 | 18 | // Constants 19 | const WINDOW_TITLE: &'static str = "05.Window Surface"; 20 | 21 | struct QueueFamilyIndices { 22 | graphics_family: Option, 23 | present_family: Option, 24 | } 25 | 26 | impl QueueFamilyIndices { 27 | pub fn new() -> QueueFamilyIndices { 28 | QueueFamilyIndices { 29 | graphics_family: None, 30 | present_family: None, 31 | } 32 | } 33 | 34 | pub fn is_complete(&self) -> bool { 35 | self.graphics_family.is_some() && self.present_family.is_some() 36 | } 37 | } 38 | 39 | struct SurfaceStuff { 40 | surface_loader: ash::extensions::khr::Surface, 41 | surface: vk::SurfaceKHR, 42 | } 43 | 44 | struct VulkanApp { 45 | _entry: ash::Entry, 46 | instance: ash::Instance, 47 | surface_loader: ash::extensions::khr::Surface, 48 | surface: vk::SurfaceKHR, 49 | debug_utils_loader: ash::extensions::ext::DebugUtils, 50 | debug_merssager: vk::DebugUtilsMessengerEXT, 51 | _physical_device: vk::PhysicalDevice, 52 | device: ash::Device, 53 | _graphics_queue: vk::Queue, 54 | _present_queue: vk::Queue, 55 | } 56 | 57 | impl VulkanApp { 58 | pub fn new(window: &winit::window::Window) -> VulkanApp { 59 | 60 | let entry = ash::Entry::new().unwrap(); 61 | let instance = share::create_instance( 62 | &entry, 63 | WINDOW_TITLE, 64 | VALIDATION.is_enable, 65 | &VALIDATION.required_validation_layers.to_vec(), 66 | ); 67 | let (debug_utils_loader, debug_merssager) = 68 | utility::debug::setup_debug_utils(VALIDATION.is_enable, &entry, &instance); 69 | let surface_stuff = VulkanApp::create_surface(&entry, &instance, &window); 70 | let physical_device = VulkanApp::pick_physical_device(&instance, &surface_stuff); 71 | let (device, family_indices) = VulkanApp::create_logical_device( 72 | &instance, 73 | physical_device, 74 | &VALIDATION, 75 | &surface_stuff, 76 | ); 77 | let graphics_queue = 78 | unsafe { device.get_device_queue(family_indices.graphics_family.unwrap(), 0) }; 79 | let present_queue = 80 | unsafe { device.get_device_queue(family_indices.present_family.unwrap(), 0) }; 81 | 82 | // cleanup(); the 'drop' function will take care of it. 83 | VulkanApp { 84 | _entry: entry, 85 | instance, 86 | surface: surface_stuff.surface, 87 | surface_loader: surface_stuff.surface_loader, 88 | debug_utils_loader, 89 | debug_merssager, 90 | _physical_device: physical_device, 91 | device, 92 | _graphics_queue: graphics_queue, 93 | _present_queue: present_queue, 94 | } 95 | } 96 | 97 | fn create_surface( 98 | entry: &ash::Entry, 99 | instance: &ash::Instance, 100 | window: &winit::window::Window, 101 | ) -> SurfaceStuff { 102 | let surface = unsafe { 103 | utility::platforms::create_surface(entry, instance, window) 104 | .expect("Failed to create surface.") 105 | }; 106 | let surface_loader = ash::extensions::khr::Surface::new(entry, instance); 107 | 108 | SurfaceStuff { 109 | surface_loader, 110 | surface, 111 | } 112 | } 113 | 114 | fn pick_physical_device( 115 | instance: &ash::Instance, 116 | surface_stuff: &SurfaceStuff, 117 | ) -> vk::PhysicalDevice { 118 | let physical_devices = unsafe { 119 | instance 120 | .enumerate_physical_devices() 121 | .expect("Failed to enumerate Physical Devices!") 122 | }; 123 | 124 | let result = physical_devices.iter().find(|physical_device| { 125 | VulkanApp::is_physical_device_suitable(instance, **physical_device, surface_stuff) 126 | }); 127 | 128 | match result { 129 | Some(p_physical_device) => *p_physical_device, 130 | None => panic!("Failed to find a suitable GPU!"), 131 | } 132 | } 133 | 134 | fn is_physical_device_suitable( 135 | instance: &ash::Instance, 136 | physical_device: vk::PhysicalDevice, 137 | surface_stuff: &SurfaceStuff, 138 | ) -> bool { 139 | let _device_properties = 140 | unsafe { instance.get_physical_device_properties(physical_device) }; 141 | let _device_features = unsafe { instance.get_physical_device_features(physical_device) }; 142 | 143 | let indices = VulkanApp::find_queue_family(instance, physical_device, surface_stuff); 144 | 145 | return indices.is_complete(); 146 | } 147 | 148 | fn create_logical_device( 149 | instance: &ash::Instance, 150 | physical_device: vk::PhysicalDevice, 151 | validation: &ValidationInfo, 152 | surface_stuff: &SurfaceStuff, 153 | ) -> (ash::Device, QueueFamilyIndices) { 154 | let indices = VulkanApp::find_queue_family(instance, physical_device, surface_stuff); 155 | 156 | use std::collections::HashSet; 157 | let mut unique_queue_families = HashSet::new(); 158 | unique_queue_families.insert(indices.graphics_family.unwrap()); 159 | unique_queue_families.insert(indices.present_family.unwrap()); 160 | 161 | let queue_priorities = [1.0_f32]; 162 | let mut queue_create_infos = vec![]; 163 | for &queue_family in unique_queue_families.iter() { 164 | let queue_create_info = vk::DeviceQueueCreateInfo { 165 | s_type: vk::StructureType::DEVICE_QUEUE_CREATE_INFO, 166 | p_next: ptr::null(), 167 | flags: vk::DeviceQueueCreateFlags::empty(), 168 | queue_family_index: queue_family, 169 | p_queue_priorities: queue_priorities.as_ptr(), 170 | queue_count: queue_priorities.len() as u32, 171 | }; 172 | queue_create_infos.push(queue_create_info); 173 | } 174 | 175 | let physical_device_features = vk::PhysicalDeviceFeatures { 176 | ..Default::default() // default just enable no feature. 177 | }; 178 | 179 | let requred_validation_layer_raw_names: Vec = validation 180 | .required_validation_layers 181 | .iter() 182 | .map(|layer_name| CString::new(*layer_name).unwrap()) 183 | .collect(); 184 | let enable_layer_names: Vec<*const c_char> = requred_validation_layer_raw_names 185 | .iter() 186 | .map(|layer_name| layer_name.as_ptr()) 187 | .collect(); 188 | 189 | let device_create_info = vk::DeviceCreateInfo { 190 | s_type: vk::StructureType::DEVICE_CREATE_INFO, 191 | p_next: ptr::null(), 192 | flags: vk::DeviceCreateFlags::empty(), 193 | queue_create_info_count: queue_create_infos.len() as u32, 194 | p_queue_create_infos: queue_create_infos.as_ptr(), 195 | enabled_layer_count: if validation.is_enable { 196 | enable_layer_names.len() 197 | } else { 198 | 0 199 | } as u32, 200 | pp_enabled_layer_names: if validation.is_enable { 201 | enable_layer_names.as_ptr() 202 | } else { 203 | ptr::null() 204 | }, 205 | enabled_extension_count: 0, 206 | pp_enabled_extension_names: ptr::null(), 207 | p_enabled_features: &physical_device_features, 208 | }; 209 | 210 | let device: ash::Device = unsafe { 211 | instance 212 | .create_device(physical_device, &device_create_info, None) 213 | .expect("Failed to create logical device!") 214 | }; 215 | 216 | (device, indices) 217 | } 218 | 219 | fn find_queue_family( 220 | instance: &ash::Instance, 221 | physical_device: vk::PhysicalDevice, 222 | surface_stuff: &SurfaceStuff, 223 | ) -> QueueFamilyIndices { 224 | let queue_families = 225 | unsafe { instance.get_physical_device_queue_family_properties(physical_device) }; 226 | 227 | let mut queue_family_indices = QueueFamilyIndices::new(); 228 | 229 | let mut index = 0; 230 | for queue_family in queue_families.iter() { 231 | if queue_family.queue_count > 0 232 | && queue_family.queue_flags.contains(vk::QueueFlags::GRAPHICS) 233 | { 234 | queue_family_indices.graphics_family = Some(index); 235 | } 236 | 237 | let is_present_support = unsafe { 238 | surface_stuff 239 | .surface_loader 240 | .get_physical_device_surface_support( 241 | physical_device, 242 | index as u32, 243 | surface_stuff.surface, 244 | ) 245 | }; 246 | if queue_family.queue_count > 0 && is_present_support { 247 | queue_family_indices.present_family = Some(index); 248 | } 249 | 250 | if queue_family_indices.is_complete() { 251 | break; 252 | } 253 | 254 | index += 1; 255 | } 256 | 257 | queue_family_indices 258 | } 259 | 260 | fn draw_frame(&mut self) { 261 | // Drawing will be here 262 | } 263 | } 264 | 265 | impl Drop for VulkanApp { 266 | fn drop(&mut self) { 267 | unsafe { 268 | self.device.destroy_device(None); 269 | // FIXME: The program crash here. 270 | self.surface_loader.destroy_surface(self.surface, None); 271 | 272 | if VALIDATION.is_enable { 273 | self.debug_utils_loader 274 | .destroy_debug_utils_messenger(self.debug_merssager, None); 275 | } 276 | self.instance.destroy_instance(None); 277 | } 278 | } 279 | } 280 | 281 | // Fix content ------------------------------------------------------------------------------- 282 | impl VulkanApp { 283 | pub fn main_loop(mut self, event_loop: EventLoop<()>, window: winit::window::Window) { 284 | 285 | event_loop.run(move |event, _, control_flow| { 286 | 287 | match event { 288 | | Event::WindowEvent { event, .. } => { 289 | match event { 290 | | WindowEvent::CloseRequested => { 291 | *control_flow = ControlFlow::Exit 292 | }, 293 | | WindowEvent::KeyboardInput { input, .. } => { 294 | match input { 295 | | KeyboardInput { virtual_keycode, state, .. } => { 296 | match (virtual_keycode, state) { 297 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 298 | *control_flow = ControlFlow::Exit 299 | }, 300 | | _ => {}, 301 | } 302 | }, 303 | } 304 | }, 305 | | _ => {}, 306 | } 307 | }, 308 | | Event::MainEventsCleared => { 309 | window.request_redraw(); 310 | }, 311 | | Event::RedrawRequested(_window_id) => { 312 | self.draw_frame(); 313 | }, 314 | _ => (), 315 | } 316 | 317 | }) 318 | } 319 | } 320 | 321 | 322 | fn main() { 323 | let event_loop = EventLoop::new(); 324 | let window = utility::window::init_window(&event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT); 325 | 326 | let vulkan_app = VulkanApp::new(&window); 327 | vulkan_app.main_loop(event_loop, window); 328 | } 329 | // ------------------------------------------------------------------------------------------- 330 | -------------------------------------------------------------------------------- /src/tutorials/07_image_view.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::{ 2 | utility, // the mod define some fixed functions that have been learned before. 3 | utility::constants::*, 4 | utility::debug::*, 5 | utility::share, 6 | }; 7 | 8 | use ash::version::DeviceV1_0; 9 | use ash::version::InstanceV1_0; 10 | use ash::vk; 11 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 12 | use winit::event_loop::{EventLoop, ControlFlow}; 13 | 14 | use std::ptr; 15 | 16 | // Constants 17 | const WINDOW_TITLE: &'static str = "07.Image View"; 18 | 19 | struct VulkanApp { 20 | 21 | _entry: ash::Entry, 22 | instance: ash::Instance, 23 | surface_loader: ash::extensions::khr::Surface, 24 | surface: vk::SurfaceKHR, 25 | debug_utils_loader: ash::extensions::ext::DebugUtils, 26 | debug_merssager: vk::DebugUtilsMessengerEXT, 27 | 28 | _physical_device: vk::PhysicalDevice, 29 | device: ash::Device, 30 | 31 | _graphics_queue: vk::Queue, 32 | _present_queue: vk::Queue, 33 | 34 | swapchain_loader: ash::extensions::khr::Swapchain, 35 | swapchain: vk::SwapchainKHR, 36 | _swapchain_images: Vec, 37 | _swapchain_format: vk::Format, 38 | _swapchain_extent: vk::Extent2D, 39 | swapchain_imageviews: Vec, 40 | } 41 | 42 | impl VulkanApp { 43 | pub fn new(window: &winit::window::Window) -> VulkanApp { 44 | 45 | // init vulkan stuff 46 | let entry = ash::Entry::new().unwrap(); 47 | let instance = share::create_instance( 48 | &entry, 49 | WINDOW_TITLE, 50 | VALIDATION.is_enable, 51 | &VALIDATION.required_validation_layers.to_vec(), 52 | ); 53 | let surface_stuff = 54 | share::create_surface(&entry, &instance, &window, WINDOW_WIDTH, WINDOW_HEIGHT); 55 | let (debug_utils_loader, debug_merssager) = 56 | setup_debug_utils(VALIDATION.is_enable, &entry, &instance); 57 | let physical_device = 58 | share::pick_physical_device(&instance, &surface_stuff, &DEVICE_EXTENSIONS); 59 | let (device, family_indices) = share::create_logical_device( 60 | &instance, 61 | physical_device, 62 | &VALIDATION, 63 | &DEVICE_EXTENSIONS, 64 | &surface_stuff, 65 | ); 66 | let graphics_queue = 67 | unsafe { device.get_device_queue(family_indices.graphics_family.unwrap(), 0) }; 68 | let present_queue = 69 | unsafe { device.get_device_queue(family_indices.present_family.unwrap(), 0) }; 70 | let swapchain_stuff = share::create_swapchain( 71 | &instance, 72 | &device, 73 | physical_device, 74 | &window, 75 | &surface_stuff, 76 | &family_indices, 77 | ); 78 | let swapchain_imageviews = VulkanApp::create_image_views( 79 | &device, 80 | swapchain_stuff.swapchain_format, 81 | &swapchain_stuff.swapchain_images, 82 | ); 83 | 84 | // cleanup(); the 'drop' function will take care of it. 85 | VulkanApp { 86 | _entry: entry, 87 | instance, 88 | surface: surface_stuff.surface, 89 | surface_loader: surface_stuff.surface_loader, 90 | debug_utils_loader, 91 | debug_merssager, 92 | 93 | _physical_device: physical_device, 94 | device, 95 | 96 | _graphics_queue: graphics_queue, 97 | _present_queue: present_queue, 98 | 99 | swapchain_loader: swapchain_stuff.swapchain_loader, 100 | swapchain: swapchain_stuff.swapchain, 101 | _swapchain_format: swapchain_stuff.swapchain_format, 102 | _swapchain_images: swapchain_stuff.swapchain_images, 103 | _swapchain_extent: swapchain_stuff.swapchain_extent, 104 | swapchain_imageviews, 105 | } 106 | } 107 | 108 | fn create_image_views( 109 | device: &ash::Device, 110 | surface_format: vk::Format, 111 | images: &Vec, 112 | ) -> Vec { 113 | let mut swapchain_imageviews = vec![]; 114 | 115 | for &image in images.iter() { 116 | let imageview_create_info = vk::ImageViewCreateInfo { 117 | s_type: vk::StructureType::IMAGE_VIEW_CREATE_INFO, 118 | p_next: ptr::null(), 119 | flags: vk::ImageViewCreateFlags::empty(), 120 | view_type: vk::ImageViewType::TYPE_2D, 121 | format: surface_format, 122 | components: vk::ComponentMapping { 123 | r: vk::ComponentSwizzle::IDENTITY, 124 | g: vk::ComponentSwizzle::IDENTITY, 125 | b: vk::ComponentSwizzle::IDENTITY, 126 | a: vk::ComponentSwizzle::IDENTITY, 127 | }, 128 | subresource_range: vk::ImageSubresourceRange { 129 | aspect_mask: vk::ImageAspectFlags::COLOR, 130 | base_mip_level: 0, 131 | level_count: 1, 132 | base_array_layer: 0, 133 | layer_count: 1, 134 | }, 135 | image, 136 | }; 137 | 138 | let imageview = unsafe { 139 | device 140 | .create_image_view(&imageview_create_info, None) 141 | .expect("Failed to create Image View!") 142 | }; 143 | swapchain_imageviews.push(imageview); 144 | } 145 | 146 | swapchain_imageviews 147 | } 148 | 149 | fn draw_frame(&mut self) { 150 | // Drawing will be here 151 | } 152 | } 153 | 154 | impl Drop for VulkanApp { 155 | fn drop(&mut self) { 156 | unsafe { 157 | for &imageview in self.swapchain_imageviews.iter() { 158 | self.device.destroy_image_view(imageview, None); 159 | } 160 | 161 | self.swapchain_loader 162 | .destroy_swapchain(self.swapchain, None); 163 | self.device.destroy_device(None); 164 | self.surface_loader.destroy_surface(self.surface, None); 165 | 166 | if VALIDATION.is_enable { 167 | self.debug_utils_loader 168 | .destroy_debug_utils_messenger(self.debug_merssager, None); 169 | } 170 | self.instance.destroy_instance(None); 171 | } 172 | } 173 | } 174 | 175 | // Fix content ------------------------------------------------------------------------------- 176 | impl VulkanApp { 177 | pub fn main_loop(mut self, event_loop: EventLoop<()>, window: winit::window::Window) { 178 | 179 | event_loop.run(move |event, _, control_flow| { 180 | 181 | match event { 182 | | Event::WindowEvent { event, .. } => { 183 | match event { 184 | | WindowEvent::CloseRequested => { 185 | *control_flow = ControlFlow::Exit 186 | }, 187 | | WindowEvent::KeyboardInput { input, .. } => { 188 | match input { 189 | | KeyboardInput { virtual_keycode, state, .. } => { 190 | match (virtual_keycode, state) { 191 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 192 | *control_flow = ControlFlow::Exit 193 | }, 194 | | _ => {}, 195 | } 196 | }, 197 | } 198 | }, 199 | | _ => {}, 200 | } 201 | }, 202 | | Event::MainEventsCleared => { 203 | window.request_redraw(); 204 | }, 205 | | Event::RedrawRequested(_window_id) => { 206 | self.draw_frame(); 207 | }, 208 | _ => (), 209 | } 210 | 211 | }) 212 | } 213 | } 214 | 215 | 216 | fn main() { 217 | let event_loop = EventLoop::new(); 218 | let window = utility::window::init_window(&event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT); 219 | 220 | let vulkan_app = VulkanApp::new(&window); 221 | vulkan_app.main_loop(event_loop, window); 222 | } 223 | // ------------------------------------------------------------------------------------------- 224 | -------------------------------------------------------------------------------- /src/tutorials/08_graphics_pipeline.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::{ 2 | utility, // the mod define some fixed functions that have been learned before. 3 | utility::constants::*, 4 | utility::debug::*, 5 | utility::share, 6 | }; 7 | 8 | use ash::version::DeviceV1_0; 9 | use ash::version::InstanceV1_0; 10 | use ash::vk; 11 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 12 | use winit::event_loop::{EventLoop, ControlFlow}; 13 | 14 | // Constants 15 | const WINDOW_TITLE: &'static str = "08.Graphics Pipeline"; 16 | 17 | struct VulkanApp { 18 | 19 | _entry: ash::Entry, 20 | instance: ash::Instance, 21 | surface_loader: ash::extensions::khr::Surface, 22 | surface: vk::SurfaceKHR, 23 | debug_utils_loader: ash::extensions::ext::DebugUtils, 24 | debug_merssager: vk::DebugUtilsMessengerEXT, 25 | 26 | _physical_device: vk::PhysicalDevice, 27 | device: ash::Device, 28 | 29 | _graphics_queue: vk::Queue, 30 | _present_queue: vk::Queue, 31 | 32 | swapchain_loader: ash::extensions::khr::Swapchain, 33 | swapchain: vk::SwapchainKHR, 34 | _swapchain_images: Vec, 35 | _swapchain_format: vk::Format, 36 | _swapchain_extent: vk::Extent2D, 37 | swapchain_imageviews: Vec, 38 | } 39 | 40 | impl VulkanApp { 41 | pub fn new(window: &winit::window::Window) -> VulkanApp { 42 | 43 | let entry = ash::Entry::new().unwrap(); 44 | let instance = share::create_instance( 45 | &entry, 46 | WINDOW_TITLE, 47 | VALIDATION.is_enable, 48 | &VALIDATION.required_validation_layers.to_vec(), 49 | ); 50 | let surface_stuff = 51 | share::create_surface(&entry, &instance, &window, WINDOW_WIDTH, WINDOW_HEIGHT); 52 | let (debug_utils_loader, debug_merssager) = 53 | setup_debug_utils(VALIDATION.is_enable, &entry, &instance); 54 | let physical_device = 55 | share::pick_physical_device(&instance, &surface_stuff, &DEVICE_EXTENSIONS); 56 | let (device, family_indices) = share::create_logical_device( 57 | &instance, 58 | physical_device, 59 | &VALIDATION, 60 | &DEVICE_EXTENSIONS, 61 | &surface_stuff, 62 | ); 63 | let graphics_queue = 64 | unsafe { device.get_device_queue(family_indices.graphics_family.unwrap(), 0) }; 65 | let present_queue = 66 | unsafe { device.get_device_queue(family_indices.present_family.unwrap(), 0) }; 67 | let swapchain_stuff = share::create_swapchain( 68 | &instance, 69 | &device, 70 | physical_device, 71 | &window, 72 | &surface_stuff, 73 | &family_indices, 74 | ); 75 | let swapchain_imageviews = share::v1::create_image_views( 76 | &device, 77 | swapchain_stuff.swapchain_format, 78 | &swapchain_stuff.swapchain_images, 79 | ); 80 | let _graphics_pipeline = VulkanApp::create_graphics_pipeline(); 81 | 82 | // cleanup(); the 'drop' function will take care of it. 83 | VulkanApp { 84 | _entry: entry, 85 | instance, 86 | surface: surface_stuff.surface, 87 | surface_loader: surface_stuff.surface_loader, 88 | debug_utils_loader, 89 | debug_merssager, 90 | 91 | _physical_device: physical_device, 92 | device, 93 | 94 | _graphics_queue: graphics_queue, 95 | _present_queue: present_queue, 96 | 97 | swapchain_loader: swapchain_stuff.swapchain_loader, 98 | swapchain: swapchain_stuff.swapchain, 99 | _swapchain_format: swapchain_stuff.swapchain_format, 100 | _swapchain_images: swapchain_stuff.swapchain_images, 101 | _swapchain_extent: swapchain_stuff.swapchain_extent, 102 | swapchain_imageviews, 103 | } 104 | } 105 | 106 | fn create_graphics_pipeline() { 107 | // leave it empty right now 108 | } 109 | 110 | fn draw_frame(&mut self) { 111 | // Drawing will be here 112 | } 113 | } 114 | 115 | impl Drop for VulkanApp { 116 | fn drop(&mut self) { 117 | unsafe { 118 | for &imageview in self.swapchain_imageviews.iter() { 119 | self.device.destroy_image_view(imageview, None); 120 | } 121 | 122 | self.swapchain_loader 123 | .destroy_swapchain(self.swapchain, None); 124 | self.device.destroy_device(None); 125 | self.surface_loader.destroy_surface(self.surface, None); 126 | 127 | if VALIDATION.is_enable { 128 | self.debug_utils_loader 129 | .destroy_debug_utils_messenger(self.debug_merssager, None); 130 | } 131 | self.instance.destroy_instance(None); 132 | } 133 | } 134 | } 135 | 136 | // Fix content ------------------------------------------------------------------------------- 137 | impl VulkanApp { 138 | pub fn main_loop(mut self, event_loop: EventLoop<()>, window: winit::window::Window) { 139 | 140 | event_loop.run(move |event, _, control_flow| { 141 | 142 | match event { 143 | | Event::WindowEvent { event, .. } => { 144 | match event { 145 | | WindowEvent::CloseRequested => { 146 | *control_flow = ControlFlow::Exit 147 | }, 148 | | WindowEvent::KeyboardInput { input, .. } => { 149 | match input { 150 | | KeyboardInput { virtual_keycode, state, .. } => { 151 | match (virtual_keycode, state) { 152 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 153 | *control_flow = ControlFlow::Exit 154 | }, 155 | | _ => {}, 156 | } 157 | }, 158 | } 159 | }, 160 | | _ => {}, 161 | } 162 | }, 163 | | Event::MainEventsCleared => { 164 | window.request_redraw(); 165 | }, 166 | | Event::RedrawRequested(_window_id) => { 167 | self.draw_frame(); 168 | }, 169 | _ => (), 170 | } 171 | 172 | }) 173 | } 174 | } 175 | 176 | 177 | fn main() { 178 | let event_loop = EventLoop::new(); 179 | let window = utility::window::init_window(&event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT); 180 | 181 | let vulkan_app = VulkanApp::new(&window); 182 | vulkan_app.main_loop(event_loop, window); 183 | } 184 | // ------------------------------------------------------------------------------------------- 185 | 186 | -------------------------------------------------------------------------------- /src/tutorials/09_shader_modules.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::{ 2 | utility, // the mod define some fixed functions that have been learned before. 3 | utility::constants::*, 4 | utility::debug::*, 5 | utility::share, 6 | }; 7 | 8 | use ash::version::DeviceV1_0; 9 | use ash::version::InstanceV1_0; 10 | use ash::vk; 11 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 12 | use winit::event_loop::{EventLoop, ControlFlow}; 13 | 14 | use std::ffi::CString; 15 | use std::path::Path; 16 | use std::ptr; 17 | 18 | // Constants 19 | const WINDOW_TITLE: &'static str = "09.Shader Modules"; 20 | 21 | struct VulkanApp { 22 | 23 | _entry: ash::Entry, 24 | instance: ash::Instance, 25 | surface_loader: ash::extensions::khr::Surface, 26 | surface: vk::SurfaceKHR, 27 | debug_utils_loader: ash::extensions::ext::DebugUtils, 28 | debug_merssager: vk::DebugUtilsMessengerEXT, 29 | 30 | _physical_device: vk::PhysicalDevice, 31 | device: ash::Device, 32 | 33 | _graphics_queue: vk::Queue, 34 | _present_queue: vk::Queue, 35 | 36 | swapchain_loader: ash::extensions::khr::Swapchain, 37 | swapchain: vk::SwapchainKHR, 38 | _swapchain_images: Vec, 39 | _swapchain_format: vk::Format, 40 | _swapchain_extent: vk::Extent2D, 41 | swapchain_imageviews: Vec, 42 | } 43 | 44 | impl VulkanApp { 45 | pub fn new(window: &winit::window::Window) -> VulkanApp { 46 | 47 | // init vulkan stuff 48 | let entry = ash::Entry::new().unwrap(); 49 | let instance = share::create_instance( 50 | &entry, 51 | WINDOW_TITLE, 52 | VALIDATION.is_enable, 53 | &VALIDATION.required_validation_layers.to_vec(), 54 | ); 55 | let surface_stuff = 56 | share::create_surface(&entry, &instance, &window, WINDOW_WIDTH, WINDOW_HEIGHT); 57 | let (debug_utils_loader, debug_merssager) = 58 | setup_debug_utils(VALIDATION.is_enable, &entry, &instance); 59 | let physical_device = 60 | share::pick_physical_device(&instance, &surface_stuff, &DEVICE_EXTENSIONS); 61 | let (device, family_indices) = share::create_logical_device( 62 | &instance, 63 | physical_device, 64 | &VALIDATION, 65 | &DEVICE_EXTENSIONS, 66 | &surface_stuff, 67 | ); 68 | let graphics_queue = 69 | unsafe { device.get_device_queue(family_indices.graphics_family.unwrap(), 0) }; 70 | let present_queue = 71 | unsafe { device.get_device_queue(family_indices.present_family.unwrap(), 0) }; 72 | let swapchain_stuff = share::create_swapchain( 73 | &instance, 74 | &device, 75 | physical_device, 76 | &window, 77 | &surface_stuff, 78 | &family_indices, 79 | ); 80 | let swapchain_imageviews = share::v1::create_image_views( 81 | &device, 82 | swapchain_stuff.swapchain_format, 83 | &swapchain_stuff.swapchain_images, 84 | ); 85 | let _pipeline = VulkanApp::create_graphics_pipeline(&device); 86 | 87 | // cleanup(); the 'drop' function will take care of it. 88 | VulkanApp { 89 | _entry: entry, 90 | instance, 91 | surface: surface_stuff.surface, 92 | surface_loader: surface_stuff.surface_loader, 93 | debug_utils_loader, 94 | debug_merssager, 95 | 96 | _physical_device: physical_device, 97 | device, 98 | 99 | _graphics_queue: graphics_queue, 100 | _present_queue: present_queue, 101 | 102 | swapchain_loader: swapchain_stuff.swapchain_loader, 103 | swapchain: swapchain_stuff.swapchain, 104 | _swapchain_format: swapchain_stuff.swapchain_format, 105 | _swapchain_images: swapchain_stuff.swapchain_images, 106 | _swapchain_extent: swapchain_stuff.swapchain_extent, 107 | swapchain_imageviews, 108 | } 109 | } 110 | 111 | fn create_graphics_pipeline(device: &ash::Device) { 112 | let vert_shader_code = 113 | VulkanApp::read_shader_code(Path::new("shaders/spv/09-shader-base.vert.spv")); 114 | let frag_shader_code = 115 | VulkanApp::read_shader_code(Path::new("shaders/spv/09-shader-base.frag.spv")); 116 | 117 | let vert_shader_module = VulkanApp::create_shader_module(device, vert_shader_code); 118 | let frag_shader_module = VulkanApp::create_shader_module(device, frag_shader_code); 119 | 120 | let main_function_name = CString::new("main").unwrap(); // the beginning function name in shader code. 121 | 122 | let _shader_stages = [ 123 | vk::PipelineShaderStageCreateInfo { 124 | // Vertex Shader 125 | s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO, 126 | p_next: ptr::null(), 127 | flags: vk::PipelineShaderStageCreateFlags::empty(), 128 | module: vert_shader_module, 129 | p_name: main_function_name.as_ptr(), 130 | p_specialization_info: ptr::null(), 131 | stage: vk::ShaderStageFlags::VERTEX, 132 | }, 133 | vk::PipelineShaderStageCreateInfo { 134 | // Fragment Shader 135 | s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO, 136 | p_next: ptr::null(), 137 | flags: vk::PipelineShaderStageCreateFlags::empty(), 138 | module: frag_shader_module, 139 | p_name: main_function_name.as_ptr(), 140 | p_specialization_info: ptr::null(), 141 | stage: vk::ShaderStageFlags::FRAGMENT, 142 | }, 143 | ]; 144 | 145 | unsafe { 146 | device.destroy_shader_module(vert_shader_module, None); 147 | device.destroy_shader_module(frag_shader_module, None); 148 | } 149 | } 150 | 151 | fn create_shader_module(device: &ash::Device, code: Vec) -> vk::ShaderModule { 152 | let shader_module_create_info = vk::ShaderModuleCreateInfo { 153 | s_type: vk::StructureType::SHADER_MODULE_CREATE_INFO, 154 | p_next: ptr::null(), 155 | flags: vk::ShaderModuleCreateFlags::empty(), 156 | code_size: code.len(), 157 | p_code: code.as_ptr() as *const u32, 158 | }; 159 | 160 | unsafe { 161 | device 162 | .create_shader_module(&shader_module_create_info, None) 163 | .expect("Failed to create Shader Module!") 164 | } 165 | } 166 | 167 | fn read_shader_code(shader_path: &Path) -> Vec { 168 | use std::fs::File; 169 | use std::io::Read; 170 | 171 | let spv_file = File::open(shader_path) 172 | .expect(&format!("Failed to find spv file at {:?}", shader_path)); 173 | let bytes_code: Vec = spv_file.bytes().filter_map(|byte| byte.ok()).collect(); 174 | 175 | bytes_code 176 | } 177 | 178 | fn draw_frame(&mut self) { 179 | // Drawing will be here 180 | } 181 | } 182 | 183 | impl Drop for VulkanApp { 184 | fn drop(&mut self) { 185 | unsafe { 186 | for &imageview in self.swapchain_imageviews.iter() { 187 | self.device.destroy_image_view(imageview, None); 188 | } 189 | 190 | self.swapchain_loader 191 | .destroy_swapchain(self.swapchain, None); 192 | self.device.destroy_device(None); 193 | self.surface_loader.destroy_surface(self.surface, None); 194 | 195 | if VALIDATION.is_enable { 196 | self.debug_utils_loader 197 | .destroy_debug_utils_messenger(self.debug_merssager, None); 198 | } 199 | self.instance.destroy_instance(None); 200 | } 201 | } 202 | } 203 | 204 | impl VulkanApp { 205 | pub fn main_loop(mut self, event_loop: EventLoop<()>, window: winit::window::Window) { 206 | 207 | event_loop.run(move |event, _, control_flow| { 208 | 209 | match event { 210 | | Event::WindowEvent { event, .. } => { 211 | match event { 212 | | WindowEvent::CloseRequested => { 213 | *control_flow = ControlFlow::Exit 214 | }, 215 | | WindowEvent::KeyboardInput { input, .. } => { 216 | match input { 217 | | KeyboardInput { virtual_keycode, state, .. } => { 218 | match (virtual_keycode, state) { 219 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 220 | *control_flow = ControlFlow::Exit 221 | }, 222 | | _ => {}, 223 | } 224 | }, 225 | } 226 | }, 227 | | _ => {}, 228 | } 229 | }, 230 | | Event::MainEventsCleared => { 231 | window.request_redraw(); 232 | }, 233 | | Event::RedrawRequested(_window_id) => { 234 | self.draw_frame(); 235 | }, 236 | _ => (), 237 | } 238 | 239 | }) 240 | } 241 | } 242 | 243 | 244 | fn main() { 245 | let event_loop = EventLoop::new(); 246 | let window = utility::window::init_window(&event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT); 247 | 248 | let vulkan_app = VulkanApp::new(&window); 249 | vulkan_app.main_loop(event_loop, window); 250 | } 251 | // ------------------------------------------------------------------------------------------- 252 | -------------------------------------------------------------------------------- /src/tutorials/10_fixed_functions.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::{ 2 | utility, // the mod define some fixed functions that have been learned before. 3 | utility::constants::*, 4 | utility::debug::*, 5 | utility::share, 6 | }; 7 | 8 | use ash::version::DeviceV1_0; 9 | use ash::version::InstanceV1_0; 10 | use ash::vk; 11 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 12 | use winit::event_loop::{EventLoop, ControlFlow}; 13 | 14 | use std::ffi::CString; 15 | use std::path::Path; 16 | use std::ptr; 17 | 18 | // Constants 19 | const WINDOW_TITLE: &'static str = "10.Fixed Functions"; 20 | 21 | struct VulkanApp { 22 | 23 | _entry: ash::Entry, 24 | instance: ash::Instance, 25 | surface_loader: ash::extensions::khr::Surface, 26 | surface: vk::SurfaceKHR, 27 | debug_utils_loader: ash::extensions::ext::DebugUtils, 28 | debug_merssager: vk::DebugUtilsMessengerEXT, 29 | 30 | _physical_device: vk::PhysicalDevice, 31 | device: ash::Device, 32 | 33 | _graphics_queue: vk::Queue, 34 | _present_queue: vk::Queue, 35 | 36 | swapchain_loader: ash::extensions::khr::Swapchain, 37 | swapchain: vk::SwapchainKHR, 38 | _swapchain_images: Vec, 39 | _swapchain_format: vk::Format, 40 | _swapchain_extent: vk::Extent2D, 41 | swapchain_imageviews: Vec, 42 | 43 | pipeline_layout: vk::PipelineLayout, 44 | } 45 | 46 | impl VulkanApp { 47 | pub fn new(window: &winit::window::Window) -> VulkanApp { 48 | 49 | let entry = ash::Entry::new().unwrap(); 50 | let instance = share::create_instance( 51 | &entry, 52 | WINDOW_TITLE, 53 | VALIDATION.is_enable, 54 | &VALIDATION.required_validation_layers.to_vec(), 55 | ); 56 | let surface_stuff = 57 | share::create_surface(&entry, &instance, &window, WINDOW_WIDTH, WINDOW_HEIGHT); 58 | let (debug_utils_loader, debug_merssager) = 59 | setup_debug_utils(VALIDATION.is_enable, &entry, &instance); 60 | let physical_device = 61 | share::pick_physical_device(&instance, &surface_stuff, &DEVICE_EXTENSIONS); 62 | let (device, family_indices) = share::create_logical_device( 63 | &instance, 64 | physical_device, 65 | &VALIDATION, 66 | &DEVICE_EXTENSIONS, 67 | &surface_stuff, 68 | ); 69 | let graphics_queue = 70 | unsafe { device.get_device_queue(family_indices.graphics_family.unwrap(), 0) }; 71 | let present_queue = 72 | unsafe { device.get_device_queue(family_indices.present_family.unwrap(), 0) }; 73 | let swapchain_stuff = share::create_swapchain( 74 | &instance, 75 | &device, 76 | physical_device, 77 | &window, 78 | &surface_stuff, 79 | &family_indices, 80 | ); 81 | let swapchain_imageviews = share::v1::create_image_views( 82 | &device, 83 | swapchain_stuff.swapchain_format, 84 | &swapchain_stuff.swapchain_images, 85 | ); 86 | let pipeline_layout = 87 | VulkanApp::create_graphics_pipeline(&device, swapchain_stuff.swapchain_extent); 88 | 89 | // cleanup(); the 'drop' function will take care of it. 90 | VulkanApp { 91 | _entry: entry, 92 | instance, 93 | surface: surface_stuff.surface, 94 | surface_loader: surface_stuff.surface_loader, 95 | debug_utils_loader, 96 | debug_merssager, 97 | 98 | _physical_device: physical_device, 99 | device, 100 | 101 | _graphics_queue: graphics_queue, 102 | _present_queue: present_queue, 103 | 104 | swapchain_loader: swapchain_stuff.swapchain_loader, 105 | swapchain: swapchain_stuff.swapchain, 106 | _swapchain_format: swapchain_stuff.swapchain_format, 107 | _swapchain_images: swapchain_stuff.swapchain_images, 108 | _swapchain_extent: swapchain_stuff.swapchain_extent, 109 | swapchain_imageviews, 110 | 111 | pipeline_layout, 112 | } 113 | } 114 | 115 | fn create_graphics_pipeline( 116 | device: &ash::Device, 117 | swapchain_extent: vk::Extent2D, 118 | ) -> vk::PipelineLayout { 119 | let vert_shader_code = 120 | utility::tools::read_shader_code(Path::new("shaders/spv/09-shader-base.vert.spv")); 121 | let frag_shader_code = 122 | utility::tools::read_shader_code(Path::new("shaders/spv/09-shader-base.frag.spv")); 123 | 124 | let vert_shader_module = share::create_shader_module(device, vert_shader_code); 125 | let frag_shader_module = share::create_shader_module(device, frag_shader_code); 126 | 127 | let main_function_name = CString::new("main").unwrap(); // the beginning function name in shader code. 128 | 129 | let _shader_stages = [ 130 | vk::PipelineShaderStageCreateInfo { 131 | // Vertex Shader 132 | s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO, 133 | p_next: ptr::null(), 134 | flags: vk::PipelineShaderStageCreateFlags::empty(), 135 | module: vert_shader_module, 136 | p_name: main_function_name.as_ptr(), 137 | p_specialization_info: ptr::null(), 138 | stage: vk::ShaderStageFlags::VERTEX, 139 | }, 140 | vk::PipelineShaderStageCreateInfo { 141 | // Fragment Shader 142 | s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO, 143 | p_next: ptr::null(), 144 | flags: vk::PipelineShaderStageCreateFlags::empty(), 145 | module: frag_shader_module, 146 | p_name: main_function_name.as_ptr(), 147 | p_specialization_info: ptr::null(), 148 | stage: vk::ShaderStageFlags::FRAGMENT, 149 | }, 150 | ]; 151 | 152 | let _vertex_input_state_create_info = vk::PipelineVertexInputStateCreateInfo { 153 | s_type: vk::StructureType::PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, 154 | p_next: ptr::null(), 155 | flags: vk::PipelineVertexInputStateCreateFlags::empty(), 156 | vertex_attribute_description_count: 0, 157 | p_vertex_attribute_descriptions: ptr::null(), 158 | vertex_binding_description_count: 0, 159 | p_vertex_binding_descriptions: ptr::null(), 160 | }; 161 | let _vertex_input_assembly_state_info = vk::PipelineInputAssemblyStateCreateInfo { 162 | s_type: vk::StructureType::PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, 163 | flags: vk::PipelineInputAssemblyStateCreateFlags::empty(), 164 | p_next: ptr::null(), 165 | primitive_restart_enable: vk::FALSE, 166 | topology: vk::PrimitiveTopology::TRIANGLE_LIST, 167 | }; 168 | 169 | let viewports = [vk::Viewport { 170 | x: 0.0, 171 | y: 0.0, 172 | width: swapchain_extent.width as f32, 173 | height: swapchain_extent.height as f32, 174 | min_depth: 0.0, 175 | max_depth: 1.0, 176 | }]; 177 | 178 | let scissors = [vk::Rect2D { 179 | offset: vk::Offset2D { x: 0, y: 0 }, 180 | extent: swapchain_extent, 181 | }]; 182 | 183 | let _viewport_state_create_info = vk::PipelineViewportStateCreateInfo { 184 | s_type: vk::StructureType::PIPELINE_VIEWPORT_STATE_CREATE_INFO, 185 | p_next: ptr::null(), 186 | flags: vk::PipelineViewportStateCreateFlags::empty(), 187 | scissor_count: scissors.len() as u32, 188 | p_scissors: scissors.as_ptr(), 189 | viewport_count: viewports.len() as u32, 190 | p_viewports: viewports.as_ptr(), 191 | }; 192 | 193 | let _rasterization_statue_create_info = vk::PipelineRasterizationStateCreateInfo { 194 | s_type: vk::StructureType::PIPELINE_RASTERIZATION_STATE_CREATE_INFO, 195 | p_next: ptr::null(), 196 | flags: vk::PipelineRasterizationStateCreateFlags::empty(), 197 | depth_clamp_enable: vk::FALSE, 198 | cull_mode: vk::CullModeFlags::BACK, 199 | front_face: vk::FrontFace::CLOCKWISE, 200 | line_width: 1.0, 201 | polygon_mode: vk::PolygonMode::FILL, 202 | rasterizer_discard_enable: vk::FALSE, 203 | depth_bias_clamp: 0.0, 204 | depth_bias_constant_factor: 0.0, 205 | depth_bias_enable: vk::FALSE, 206 | depth_bias_slope_factor: 0.0, 207 | }; 208 | let _multisample_state_create_info = vk::PipelineMultisampleStateCreateInfo { 209 | s_type: vk::StructureType::PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, 210 | flags: vk::PipelineMultisampleStateCreateFlags::empty(), 211 | p_next: ptr::null(), 212 | rasterization_samples: vk::SampleCountFlags::TYPE_1, 213 | sample_shading_enable: vk::FALSE, 214 | min_sample_shading: 0.0, 215 | p_sample_mask: ptr::null(), 216 | alpha_to_one_enable: vk::FALSE, 217 | alpha_to_coverage_enable: vk::FALSE, 218 | }; 219 | 220 | let stencil_state = vk::StencilOpState { 221 | fail_op: vk::StencilOp::KEEP, 222 | pass_op: vk::StencilOp::KEEP, 223 | depth_fail_op: vk::StencilOp::KEEP, 224 | compare_op: vk::CompareOp::ALWAYS, 225 | compare_mask: 0, 226 | write_mask: 0, 227 | reference: 0, 228 | }; 229 | 230 | let _depth_state_create_info = vk::PipelineDepthStencilStateCreateInfo { 231 | s_type: vk::StructureType::PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, 232 | p_next: ptr::null(), 233 | flags: vk::PipelineDepthStencilStateCreateFlags::empty(), 234 | depth_test_enable: vk::FALSE, 235 | depth_write_enable: vk::FALSE, 236 | depth_compare_op: vk::CompareOp::LESS_OR_EQUAL, 237 | depth_bounds_test_enable: vk::FALSE, 238 | stencil_test_enable: vk::FALSE, 239 | front: stencil_state, 240 | back: stencil_state, 241 | max_depth_bounds: 1.0, 242 | min_depth_bounds: 0.0, 243 | }; 244 | 245 | let color_blend_attachment_states = [vk::PipelineColorBlendAttachmentState { 246 | blend_enable: vk::FALSE, 247 | color_write_mask: vk::ColorComponentFlags::all(), 248 | src_color_blend_factor: vk::BlendFactor::ONE, 249 | dst_color_blend_factor: vk::BlendFactor::ZERO, 250 | color_blend_op: vk::BlendOp::ADD, 251 | src_alpha_blend_factor: vk::BlendFactor::ONE, 252 | dst_alpha_blend_factor: vk::BlendFactor::ZERO, 253 | alpha_blend_op: vk::BlendOp::ADD, 254 | }]; 255 | 256 | let _color_blend_state = vk::PipelineColorBlendStateCreateInfo { 257 | s_type: vk::StructureType::PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 258 | p_next: ptr::null(), 259 | flags: vk::PipelineColorBlendStateCreateFlags::empty(), 260 | logic_op_enable: vk::FALSE, 261 | logic_op: vk::LogicOp::COPY, 262 | attachment_count: color_blend_attachment_states.len() as u32, 263 | p_attachments: color_blend_attachment_states.as_ptr(), 264 | blend_constants: [0.0, 0.0, 0.0, 0.0], 265 | }; 266 | 267 | // leaving the dynamic statue unconfigurated right now 268 | // let dynamic_state = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]; 269 | // let dynamic_state_info = vk::PipelineDynamicStateCreateInfo { 270 | // s_type: vk::StructureType::PIPELINE_DYNAMIC_STATE_CREATE_INFO, 271 | // p_next: ptr::null(), 272 | // flags: vk::PipelineDynamicStateCreateFlags::empty(), 273 | // dynamic_state_count: dynamic_state.len() as u32, 274 | // p_dynamic_states: dynamic_state.as_ptr(), 275 | // }; 276 | 277 | let pipeline_layout_create_info = vk::PipelineLayoutCreateInfo { 278 | s_type: vk::StructureType::PIPELINE_LAYOUT_CREATE_INFO, 279 | p_next: ptr::null(), 280 | flags: vk::PipelineLayoutCreateFlags::empty(), 281 | set_layout_count: 0, 282 | p_set_layouts: ptr::null(), 283 | push_constant_range_count: 0, 284 | p_push_constant_ranges: ptr::null(), 285 | }; 286 | 287 | let pipeline_layout = unsafe { 288 | device 289 | .create_pipeline_layout(&pipeline_layout_create_info, None) 290 | .expect("Failed to create pipeline layout!") 291 | }; 292 | 293 | unsafe { 294 | device.destroy_shader_module(vert_shader_module, None); 295 | device.destroy_shader_module(frag_shader_module, None); 296 | } 297 | 298 | pipeline_layout 299 | } 300 | 301 | fn draw_frame(&mut self) { 302 | // Drawing will be here 303 | } 304 | } 305 | 306 | impl Drop for VulkanApp { 307 | fn drop(&mut self) { 308 | unsafe { 309 | self.device 310 | .destroy_pipeline_layout(self.pipeline_layout, None); 311 | 312 | for &imageview in self.swapchain_imageviews.iter() { 313 | self.device.destroy_image_view(imageview, None); 314 | } 315 | 316 | self.swapchain_loader 317 | .destroy_swapchain(self.swapchain, None); 318 | self.device.destroy_device(None); 319 | self.surface_loader.destroy_surface(self.surface, None); 320 | 321 | if VALIDATION.is_enable { 322 | self.debug_utils_loader 323 | .destroy_debug_utils_messenger(self.debug_merssager, None); 324 | } 325 | self.instance.destroy_instance(None); 326 | } 327 | } 328 | } 329 | 330 | // Fix content ------------------------------------------------------------------------------- 331 | impl VulkanApp { 332 | pub fn main_loop(mut self, event_loop: EventLoop<()>, window: winit::window::Window) { 333 | 334 | event_loop.run(move |event, _, control_flow| { 335 | 336 | match event { 337 | | Event::WindowEvent { event, .. } => { 338 | match event { 339 | | WindowEvent::CloseRequested => { 340 | *control_flow = ControlFlow::Exit 341 | }, 342 | | WindowEvent::KeyboardInput { input, .. } => { 343 | match input { 344 | | KeyboardInput { virtual_keycode, state, .. } => { 345 | match (virtual_keycode, state) { 346 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 347 | *control_flow = ControlFlow::Exit 348 | }, 349 | | _ => {}, 350 | } 351 | }, 352 | } 353 | }, 354 | | _ => {}, 355 | } 356 | }, 357 | | Event::MainEventsCleared => { 358 | window.request_redraw(); 359 | }, 360 | | Event::RedrawRequested(_window_id) => { 361 | self.draw_frame(); 362 | }, 363 | _ => (), 364 | } 365 | 366 | }) 367 | } 368 | } 369 | 370 | 371 | fn main() { 372 | let event_loop = EventLoop::new(); 373 | let window = utility::window::init_window(&event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT); 374 | 375 | let vulkan_app = VulkanApp::new(&window); 376 | vulkan_app.main_loop(event_loop, window); 377 | } 378 | // ------------------------------------------------------------------------------------------- 379 | -------------------------------------------------------------------------------- /src/tutorials/12_graphics_pipeline_complete.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::{ 2 | utility, // the mod define some fixed functions that have been learned before. 3 | utility::constants::*, 4 | utility::debug::*, 5 | utility::share, 6 | }; 7 | 8 | use ash::version::DeviceV1_0; 9 | use ash::version::InstanceV1_0; 10 | use ash::vk; 11 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 12 | use winit::event_loop::{EventLoop, ControlFlow}; 13 | 14 | use std::ffi::CString; 15 | use std::path::Path; 16 | use std::ptr; 17 | 18 | // Constants 19 | const WINDOW_TITLE: &'static str = "12.Graphics Pipeline Complete"; 20 | 21 | struct VulkanApp { 22 | 23 | _entry: ash::Entry, 24 | instance: ash::Instance, 25 | surface_loader: ash::extensions::khr::Surface, 26 | surface: vk::SurfaceKHR, 27 | debug_utils_loader: ash::extensions::ext::DebugUtils, 28 | debug_merssager: vk::DebugUtilsMessengerEXT, 29 | 30 | _physical_device: vk::PhysicalDevice, 31 | device: ash::Device, 32 | 33 | _graphics_queue: vk::Queue, 34 | _present_queue: vk::Queue, 35 | 36 | swapchain_loader: ash::extensions::khr::Swapchain, 37 | swapchain: vk::SwapchainKHR, 38 | _swapchain_images: Vec, 39 | _swapchain_format: vk::Format, 40 | _swapchain_extent: vk::Extent2D, 41 | swapchain_imageviews: Vec, 42 | 43 | render_pass: vk::RenderPass, 44 | pipeline_layout: vk::PipelineLayout, 45 | graphics_pipeline: vk::Pipeline, 46 | } 47 | 48 | impl VulkanApp { 49 | pub fn new(window: &winit::window::Window) -> VulkanApp { 50 | 51 | let entry = ash::Entry::new().unwrap(); 52 | let instance = share::create_instance( 53 | &entry, 54 | WINDOW_TITLE, 55 | VALIDATION.is_enable, 56 | &VALIDATION.required_validation_layers.to_vec(), 57 | ); 58 | let surface_stuff = 59 | share::create_surface(&entry, &instance, &window, WINDOW_WIDTH, WINDOW_HEIGHT); 60 | let (debug_utils_loader, debug_merssager) = 61 | setup_debug_utils(VALIDATION.is_enable, &entry, &instance); 62 | let physical_device = 63 | share::pick_physical_device(&instance, &surface_stuff, &DEVICE_EXTENSIONS); 64 | let (device, family_indices) = share::create_logical_device( 65 | &instance, 66 | physical_device, 67 | &VALIDATION, 68 | &DEVICE_EXTENSIONS, 69 | &surface_stuff, 70 | ); 71 | let graphics_queue = 72 | unsafe { device.get_device_queue(family_indices.graphics_family.unwrap(), 0) }; 73 | let present_queue = 74 | unsafe { device.get_device_queue(family_indices.present_family.unwrap(), 0) }; 75 | let swapchain_stuff = share::create_swapchain( 76 | &instance, 77 | &device, 78 | physical_device, 79 | &window, 80 | &surface_stuff, 81 | &family_indices, 82 | ); 83 | let swapchain_imageviews = share::v1::create_image_views( 84 | &device, 85 | swapchain_stuff.swapchain_format, 86 | &swapchain_stuff.swapchain_images, 87 | ); 88 | let render_pass = share::v1::create_render_pass(&device, swapchain_stuff.swapchain_format); 89 | let (graphics_pipeline, pipeline_layout) = VulkanApp::create_graphics_pipeline( 90 | &device, 91 | render_pass, 92 | swapchain_stuff.swapchain_extent, 93 | ); 94 | 95 | // cleanup(); the 'drop' function will take care of it. 96 | VulkanApp { 97 | _entry: entry, 98 | instance, 99 | surface: surface_stuff.surface, 100 | surface_loader: surface_stuff.surface_loader, 101 | debug_utils_loader, 102 | debug_merssager, 103 | 104 | _physical_device: physical_device, 105 | device, 106 | 107 | _graphics_queue: graphics_queue, 108 | _present_queue: present_queue, 109 | 110 | swapchain_loader: swapchain_stuff.swapchain_loader, 111 | swapchain: swapchain_stuff.swapchain, 112 | _swapchain_format: swapchain_stuff.swapchain_format, 113 | _swapchain_images: swapchain_stuff.swapchain_images, 114 | _swapchain_extent: swapchain_stuff.swapchain_extent, 115 | swapchain_imageviews, 116 | 117 | pipeline_layout, 118 | render_pass, 119 | graphics_pipeline, 120 | } 121 | } 122 | 123 | fn create_graphics_pipeline( 124 | device: &ash::Device, 125 | render_pass: vk::RenderPass, 126 | swapchain_extent: vk::Extent2D, 127 | ) -> (vk::Pipeline, vk::PipelineLayout) { 128 | let vert_shader_code = 129 | utility::tools::read_shader_code(Path::new("shaders/spv/09-shader-base.vert.spv")); 130 | let frag_shader_code = 131 | utility::tools::read_shader_code(Path::new("shaders/spv/09-shader-base.frag.spv")); 132 | 133 | let vert_shader_module = share::create_shader_module(device, vert_shader_code); 134 | let frag_shader_module = share::create_shader_module(device, frag_shader_code); 135 | 136 | let main_function_name = CString::new("main").unwrap(); // the beginning function name in shader code. 137 | 138 | let shader_stages = [ 139 | vk::PipelineShaderStageCreateInfo { 140 | // Vertex Shader 141 | s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO, 142 | p_next: ptr::null(), 143 | flags: vk::PipelineShaderStageCreateFlags::empty(), 144 | module: vert_shader_module, 145 | p_name: main_function_name.as_ptr(), 146 | p_specialization_info: ptr::null(), 147 | stage: vk::ShaderStageFlags::VERTEX, 148 | }, 149 | vk::PipelineShaderStageCreateInfo { 150 | // Fragment Shader 151 | s_type: vk::StructureType::PIPELINE_SHADER_STAGE_CREATE_INFO, 152 | p_next: ptr::null(), 153 | flags: vk::PipelineShaderStageCreateFlags::empty(), 154 | module: frag_shader_module, 155 | p_name: main_function_name.as_ptr(), 156 | p_specialization_info: ptr::null(), 157 | stage: vk::ShaderStageFlags::FRAGMENT, 158 | }, 159 | ]; 160 | 161 | let vertex_input_state_create_info = vk::PipelineVertexInputStateCreateInfo { 162 | s_type: vk::StructureType::PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, 163 | p_next: ptr::null(), 164 | flags: vk::PipelineVertexInputStateCreateFlags::empty(), 165 | vertex_attribute_description_count: 0, 166 | p_vertex_attribute_descriptions: ptr::null(), 167 | vertex_binding_description_count: 0, 168 | p_vertex_binding_descriptions: ptr::null(), 169 | }; 170 | let vertex_input_assembly_state_info = vk::PipelineInputAssemblyStateCreateInfo { 171 | s_type: vk::StructureType::PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, 172 | flags: vk::PipelineInputAssemblyStateCreateFlags::empty(), 173 | p_next: ptr::null(), 174 | primitive_restart_enable: vk::FALSE, 175 | topology: vk::PrimitiveTopology::TRIANGLE_LIST, 176 | }; 177 | 178 | let viewports = [vk::Viewport { 179 | x: 0.0, 180 | y: 0.0, 181 | width: swapchain_extent.width as f32, 182 | height: swapchain_extent.height as f32, 183 | min_depth: 0.0, 184 | max_depth: 1.0, 185 | }]; 186 | 187 | let scissors = [vk::Rect2D { 188 | offset: vk::Offset2D { x: 0, y: 0 }, 189 | extent: swapchain_extent, 190 | }]; 191 | 192 | let viewport_state_create_info = vk::PipelineViewportStateCreateInfo { 193 | s_type: vk::StructureType::PIPELINE_VIEWPORT_STATE_CREATE_INFO, 194 | p_next: ptr::null(), 195 | flags: vk::PipelineViewportStateCreateFlags::empty(), 196 | scissor_count: scissors.len() as u32, 197 | p_scissors: scissors.as_ptr(), 198 | viewport_count: viewports.len() as u32, 199 | p_viewports: viewports.as_ptr(), 200 | }; 201 | 202 | let rasterization_statue_create_info = vk::PipelineRasterizationStateCreateInfo { 203 | s_type: vk::StructureType::PIPELINE_RASTERIZATION_STATE_CREATE_INFO, 204 | p_next: ptr::null(), 205 | flags: vk::PipelineRasterizationStateCreateFlags::empty(), 206 | depth_clamp_enable: vk::FALSE, 207 | cull_mode: vk::CullModeFlags::BACK, 208 | front_face: vk::FrontFace::CLOCKWISE, 209 | line_width: 1.0, 210 | polygon_mode: vk::PolygonMode::FILL, 211 | rasterizer_discard_enable: vk::FALSE, 212 | depth_bias_clamp: 0.0, 213 | depth_bias_constant_factor: 0.0, 214 | depth_bias_enable: vk::FALSE, 215 | depth_bias_slope_factor: 0.0, 216 | }; 217 | let multisample_state_create_info = vk::PipelineMultisampleStateCreateInfo { 218 | s_type: vk::StructureType::PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, 219 | flags: vk::PipelineMultisampleStateCreateFlags::empty(), 220 | p_next: ptr::null(), 221 | rasterization_samples: vk::SampleCountFlags::TYPE_1, 222 | sample_shading_enable: vk::FALSE, 223 | min_sample_shading: 0.0, 224 | p_sample_mask: ptr::null(), 225 | alpha_to_one_enable: vk::FALSE, 226 | alpha_to_coverage_enable: vk::FALSE, 227 | }; 228 | 229 | let stencil_state = vk::StencilOpState { 230 | fail_op: vk::StencilOp::KEEP, 231 | pass_op: vk::StencilOp::KEEP, 232 | depth_fail_op: vk::StencilOp::KEEP, 233 | compare_op: vk::CompareOp::ALWAYS, 234 | compare_mask: 0, 235 | write_mask: 0, 236 | reference: 0, 237 | }; 238 | 239 | let depth_state_create_info = vk::PipelineDepthStencilStateCreateInfo { 240 | s_type: vk::StructureType::PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, 241 | p_next: ptr::null(), 242 | flags: vk::PipelineDepthStencilStateCreateFlags::empty(), 243 | depth_test_enable: vk::FALSE, 244 | depth_write_enable: vk::FALSE, 245 | depth_compare_op: vk::CompareOp::LESS_OR_EQUAL, 246 | depth_bounds_test_enable: vk::FALSE, 247 | stencil_test_enable: vk::FALSE, 248 | front: stencil_state, 249 | back: stencil_state, 250 | max_depth_bounds: 1.0, 251 | min_depth_bounds: 0.0, 252 | }; 253 | 254 | let color_blend_attachment_states = [vk::PipelineColorBlendAttachmentState { 255 | blend_enable: vk::FALSE, 256 | color_write_mask: vk::ColorComponentFlags::all(), 257 | src_color_blend_factor: vk::BlendFactor::ONE, 258 | dst_color_blend_factor: vk::BlendFactor::ZERO, 259 | color_blend_op: vk::BlendOp::ADD, 260 | src_alpha_blend_factor: vk::BlendFactor::ONE, 261 | dst_alpha_blend_factor: vk::BlendFactor::ZERO, 262 | alpha_blend_op: vk::BlendOp::ADD, 263 | }]; 264 | 265 | let color_blend_state = vk::PipelineColorBlendStateCreateInfo { 266 | s_type: vk::StructureType::PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, 267 | p_next: ptr::null(), 268 | flags: vk::PipelineColorBlendStateCreateFlags::empty(), 269 | logic_op_enable: vk::FALSE, 270 | logic_op: vk::LogicOp::COPY, 271 | attachment_count: color_blend_attachment_states.len() as u32, 272 | p_attachments: color_blend_attachment_states.as_ptr(), 273 | blend_constants: [0.0, 0.0, 0.0, 0.0], 274 | }; 275 | 276 | // leaving the dynamic statue unconfigurated right now 277 | // let dynamic_state = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]; 278 | // let dynamic_state_info = vk::PipelineDynamicStateCreateInfo { 279 | // s_type: vk::StructureType::PIPELINE_DYNAMIC_STATE_CREATE_INFO, 280 | // p_next: ptr::null(), 281 | // flags: vk::PipelineDynamicStateCreateFlags::empty(), 282 | // dynamic_state_count: dynamic_state.len() as u32, 283 | // p_dynamic_states: dynamic_state.as_ptr(), 284 | // }; 285 | 286 | let pipeline_layout_create_info = vk::PipelineLayoutCreateInfo { 287 | s_type: vk::StructureType::PIPELINE_LAYOUT_CREATE_INFO, 288 | p_next: ptr::null(), 289 | flags: vk::PipelineLayoutCreateFlags::empty(), 290 | set_layout_count: 0, 291 | p_set_layouts: ptr::null(), 292 | push_constant_range_count: 0, 293 | p_push_constant_ranges: ptr::null(), 294 | }; 295 | 296 | let pipeline_layout = unsafe { 297 | device 298 | .create_pipeline_layout(&pipeline_layout_create_info, None) 299 | .expect("Failed to create pipeline layout!") 300 | }; 301 | 302 | let graphic_pipeline_create_infos = [vk::GraphicsPipelineCreateInfo { 303 | s_type: vk::StructureType::GRAPHICS_PIPELINE_CREATE_INFO, 304 | p_next: ptr::null(), 305 | flags: vk::PipelineCreateFlags::empty(), 306 | stage_count: shader_stages.len() as u32, 307 | p_stages: shader_stages.as_ptr(), 308 | p_vertex_input_state: &vertex_input_state_create_info, 309 | p_input_assembly_state: &vertex_input_assembly_state_info, 310 | p_tessellation_state: ptr::null(), 311 | p_viewport_state: &viewport_state_create_info, 312 | p_rasterization_state: &rasterization_statue_create_info, 313 | p_multisample_state: &multisample_state_create_info, 314 | p_depth_stencil_state: &depth_state_create_info, 315 | p_color_blend_state: &color_blend_state, 316 | p_dynamic_state: ptr::null(), 317 | layout: pipeline_layout, 318 | render_pass, 319 | subpass: 0, 320 | base_pipeline_handle: vk::Pipeline::null(), 321 | base_pipeline_index: -1, 322 | }]; 323 | 324 | let graphics_pipelines = unsafe { 325 | device 326 | .create_graphics_pipelines( 327 | vk::PipelineCache::null(), 328 | &graphic_pipeline_create_infos, 329 | None, 330 | ) 331 | .expect("Failed to create Graphics Pipeline!.") 332 | }; 333 | 334 | unsafe { 335 | device.destroy_shader_module(vert_shader_module, None); 336 | device.destroy_shader_module(frag_shader_module, None); 337 | } 338 | 339 | (graphics_pipelines[0], pipeline_layout) 340 | } 341 | 342 | fn draw_frame(&mut self) { 343 | // Drawing will be here 344 | } 345 | } 346 | 347 | impl Drop for VulkanApp { 348 | fn drop(&mut self) { 349 | unsafe { 350 | self.device.destroy_pipeline(self.graphics_pipeline, None); 351 | self.device 352 | .destroy_pipeline_layout(self.pipeline_layout, None); 353 | self.device.destroy_render_pass(self.render_pass, None); 354 | 355 | for &imageview in self.swapchain_imageviews.iter() { 356 | self.device.destroy_image_view(imageview, None); 357 | } 358 | 359 | self.swapchain_loader 360 | .destroy_swapchain(self.swapchain, None); 361 | self.device.destroy_device(None); 362 | self.surface_loader.destroy_surface(self.surface, None); 363 | 364 | if VALIDATION.is_enable { 365 | self.debug_utils_loader 366 | .destroy_debug_utils_messenger(self.debug_merssager, None); 367 | } 368 | self.instance.destroy_instance(None); 369 | } 370 | } 371 | } 372 | 373 | // Fix content ------------------------------------------------------------------------------- 374 | impl VulkanApp { 375 | pub fn main_loop(mut self, event_loop: EventLoop<()>, window: winit::window::Window) { 376 | 377 | event_loop.run(move |event, _, control_flow| { 378 | 379 | match event { 380 | | Event::WindowEvent { event, .. } => { 381 | match event { 382 | | WindowEvent::CloseRequested => { 383 | *control_flow = ControlFlow::Exit 384 | }, 385 | | WindowEvent::KeyboardInput { input, .. } => { 386 | match input { 387 | | KeyboardInput { virtual_keycode, state, .. } => { 388 | match (virtual_keycode, state) { 389 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 390 | *control_flow = ControlFlow::Exit 391 | }, 392 | | _ => {}, 393 | } 394 | }, 395 | } 396 | }, 397 | | _ => {}, 398 | } 399 | }, 400 | | Event::MainEventsCleared => { 401 | window.request_redraw(); 402 | }, 403 | | Event::RedrawRequested(_window_id) => { 404 | self.draw_frame(); 405 | }, 406 | _ => (), 407 | } 408 | 409 | }) 410 | } 411 | } 412 | 413 | 414 | fn main() { 415 | let event_loop = EventLoop::new(); 416 | let window = utility::window::init_window(&event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT); 417 | 418 | let vulkan_app = VulkanApp::new(&window); 419 | vulkan_app.main_loop(event_loop, window); 420 | } 421 | // ------------------------------------------------------------------------------------------- 422 | -------------------------------------------------------------------------------- /src/tutorials/13_framebuffers.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::{ 2 | utility, // the mod define some fixed functions that have been learned before. 3 | utility::constants::*, 4 | utility::debug::*, 5 | utility::share, 6 | }; 7 | 8 | use ash::version::DeviceV1_0; 9 | use ash::version::InstanceV1_0; 10 | use ash::vk; 11 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 12 | use winit::event_loop::{EventLoop, ControlFlow}; 13 | 14 | use std::ptr; 15 | 16 | // Constants 17 | const WINDOW_TITLE: &'static str = "13.Framebuffers"; 18 | 19 | struct VulkanApp { 20 | 21 | _entry: ash::Entry, 22 | instance: ash::Instance, 23 | surface_loader: ash::extensions::khr::Surface, 24 | surface: vk::SurfaceKHR, 25 | debug_utils_loader: ash::extensions::ext::DebugUtils, 26 | debug_merssager: vk::DebugUtilsMessengerEXT, 27 | 28 | _physical_device: vk::PhysicalDevice, 29 | device: ash::Device, 30 | 31 | _graphics_queue: vk::Queue, 32 | _present_queue: vk::Queue, 33 | 34 | swapchain_loader: ash::extensions::khr::Swapchain, 35 | swapchain: vk::SwapchainKHR, 36 | _swapchain_images: Vec, 37 | _swapchain_format: vk::Format, 38 | _swapchain_extent: vk::Extent2D, 39 | swapchain_imageviews: Vec, 40 | swapchain_framebuffers: Vec, 41 | 42 | render_pass: vk::RenderPass, 43 | pipeline_layout: vk::PipelineLayout, 44 | graphics_pipeline: vk::Pipeline, 45 | } 46 | 47 | impl VulkanApp { 48 | pub fn new(window: &winit::window::Window) -> VulkanApp { 49 | 50 | let entry = ash::Entry::new().unwrap(); 51 | let instance = share::create_instance( 52 | &entry, 53 | WINDOW_TITLE, 54 | VALIDATION.is_enable, 55 | &VALIDATION.required_validation_layers.to_vec(), 56 | ); 57 | let surface_stuff = 58 | share::create_surface(&entry, &instance, &window, WINDOW_WIDTH, WINDOW_HEIGHT); 59 | let (debug_utils_loader, debug_merssager) = 60 | setup_debug_utils(VALIDATION.is_enable, &entry, &instance); 61 | let physical_device = 62 | share::pick_physical_device(&instance, &surface_stuff, &DEVICE_EXTENSIONS); 63 | let (device, family_indices) = share::create_logical_device( 64 | &instance, 65 | physical_device, 66 | &VALIDATION, 67 | &DEVICE_EXTENSIONS, 68 | &surface_stuff, 69 | ); 70 | let graphics_queue = 71 | unsafe { device.get_device_queue(family_indices.graphics_family.unwrap(), 0) }; 72 | let present_queue = 73 | unsafe { device.get_device_queue(family_indices.present_family.unwrap(), 0) }; 74 | let swapchain_stuff = share::create_swapchain( 75 | &instance, 76 | &device, 77 | physical_device, 78 | &window, 79 | &surface_stuff, 80 | &family_indices, 81 | ); 82 | let swapchain_imageviews = share::v1::create_image_views( 83 | &device, 84 | swapchain_stuff.swapchain_format, 85 | &swapchain_stuff.swapchain_images, 86 | ); 87 | let render_pass = share::v1::create_render_pass(&device, swapchain_stuff.swapchain_format); 88 | let (graphics_pipeline, pipeline_layout) = share::v1::create_graphics_pipeline( 89 | &device, 90 | render_pass, 91 | swapchain_stuff.swapchain_extent, 92 | ); 93 | let swapchain_framebuffers = VulkanApp::create_framebuffers( 94 | &device, 95 | render_pass, 96 | &swapchain_imageviews, 97 | &swapchain_stuff.swapchain_extent, 98 | ); 99 | 100 | // cleanup(); the 'drop' function will take care of it. 101 | VulkanApp { 102 | _entry: entry, 103 | instance, 104 | surface: surface_stuff.surface, 105 | surface_loader: surface_stuff.surface_loader, 106 | debug_utils_loader, 107 | debug_merssager, 108 | 109 | _physical_device: physical_device, 110 | device, 111 | 112 | _graphics_queue: graphics_queue, 113 | _present_queue: present_queue, 114 | 115 | swapchain_loader: swapchain_stuff.swapchain_loader, 116 | swapchain: swapchain_stuff.swapchain, 117 | _swapchain_format: swapchain_stuff.swapchain_format, 118 | _swapchain_images: swapchain_stuff.swapchain_images, 119 | _swapchain_extent: swapchain_stuff.swapchain_extent, 120 | swapchain_imageviews, 121 | swapchain_framebuffers, 122 | 123 | pipeline_layout, 124 | render_pass, 125 | graphics_pipeline, 126 | } 127 | } 128 | 129 | fn create_framebuffers( 130 | device: &ash::Device, 131 | render_pass: vk::RenderPass, 132 | image_views: &Vec, 133 | swapchain_extent: &vk::Extent2D, 134 | ) -> Vec { 135 | let mut framebuffers = vec![]; 136 | 137 | for &image_view in image_views.iter() { 138 | let attachments = [image_view]; 139 | 140 | let framebuffer_create_info = vk::FramebufferCreateInfo { 141 | s_type: vk::StructureType::FRAMEBUFFER_CREATE_INFO, 142 | p_next: ptr::null(), 143 | flags: vk::FramebufferCreateFlags::empty(), 144 | render_pass, 145 | attachment_count: attachments.len() as u32, 146 | p_attachments: attachments.as_ptr(), 147 | width: swapchain_extent.width, 148 | height: swapchain_extent.height, 149 | layers: 1, 150 | }; 151 | 152 | let framebuffer = unsafe { 153 | device 154 | .create_framebuffer(&framebuffer_create_info, None) 155 | .expect("Failed to create Framebuffer!") 156 | }; 157 | 158 | framebuffers.push(framebuffer); 159 | } 160 | 161 | framebuffers 162 | } 163 | 164 | fn draw_frame(&mut self) { 165 | // Drawing will be here 166 | } 167 | } 168 | 169 | impl Drop for VulkanApp { 170 | fn drop(&mut self) { 171 | unsafe { 172 | for &framebuffer in self.swapchain_framebuffers.iter() { 173 | self.device.destroy_framebuffer(framebuffer, None); 174 | } 175 | 176 | self.device.destroy_pipeline(self.graphics_pipeline, None); 177 | self.device 178 | .destroy_pipeline_layout(self.pipeline_layout, None); 179 | self.device.destroy_render_pass(self.render_pass, None); 180 | 181 | for &imageview in self.swapchain_imageviews.iter() { 182 | self.device.destroy_image_view(imageview, None); 183 | } 184 | 185 | self.swapchain_loader 186 | .destroy_swapchain(self.swapchain, None); 187 | self.device.destroy_device(None); 188 | self.surface_loader.destroy_surface(self.surface, None); 189 | 190 | if VALIDATION.is_enable { 191 | self.debug_utils_loader 192 | .destroy_debug_utils_messenger(self.debug_merssager, None); 193 | } 194 | self.instance.destroy_instance(None); 195 | } 196 | } 197 | } 198 | 199 | // Fix content ------------------------------------------------------------------------------- 200 | impl VulkanApp { 201 | pub fn main_loop(mut self, event_loop: EventLoop<()>, window: winit::window::Window) { 202 | 203 | event_loop.run(move |event, _, control_flow| { 204 | 205 | match event { 206 | | Event::WindowEvent { event, .. } => { 207 | match event { 208 | | WindowEvent::CloseRequested => { 209 | *control_flow = ControlFlow::Exit 210 | }, 211 | | WindowEvent::KeyboardInput { input, .. } => { 212 | match input { 213 | | KeyboardInput { virtual_keycode, state, .. } => { 214 | match (virtual_keycode, state) { 215 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 216 | *control_flow = ControlFlow::Exit 217 | }, 218 | | _ => {}, 219 | } 220 | }, 221 | } 222 | }, 223 | | _ => {}, 224 | } 225 | }, 226 | | Event::MainEventsCleared => { 227 | window.request_redraw(); 228 | }, 229 | | Event::RedrawRequested(_window_id) => { 230 | self.draw_frame(); 231 | }, 232 | _ => (), 233 | } 234 | 235 | }) 236 | } 237 | } 238 | 239 | 240 | fn main() { 241 | let event_loop = EventLoop::new(); 242 | let window = utility::window::init_window(&event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT); 243 | 244 | let vulkan_app = VulkanApp::new(&window); 245 | vulkan_app.main_loop(event_loop, window); 246 | } 247 | // ------------------------------------------------------------------------------------------- 248 | -------------------------------------------------------------------------------- /src/tutorials/14_command_buffers.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::{ 2 | utility, // the mod define some fixed functions that have been learned before. 3 | utility::constants::*, 4 | utility::debug::*, 5 | utility::share, 6 | utility::structures::*, 7 | }; 8 | 9 | use ash::version::DeviceV1_0; 10 | use ash::version::InstanceV1_0; 11 | use ash::vk; 12 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 13 | use winit::event_loop::{EventLoop, ControlFlow}; 14 | 15 | use std::ptr; 16 | 17 | // Constants 18 | const WINDOW_TITLE: &'static str = "14.Command Buffers"; 19 | 20 | struct VulkanApp { 21 | 22 | _entry: ash::Entry, 23 | instance: ash::Instance, 24 | surface_loader: ash::extensions::khr::Surface, 25 | surface: vk::SurfaceKHR, 26 | debug_utils_loader: ash::extensions::ext::DebugUtils, 27 | debug_merssager: vk::DebugUtilsMessengerEXT, 28 | 29 | _physical_device: vk::PhysicalDevice, 30 | device: ash::Device, 31 | 32 | _graphics_queue: vk::Queue, 33 | _present_queue: vk::Queue, 34 | 35 | swapchain_loader: ash::extensions::khr::Swapchain, 36 | swapchain: vk::SwapchainKHR, 37 | _swapchain_images: Vec, 38 | _swapchain_format: vk::Format, 39 | _swapchain_extent: vk::Extent2D, 40 | swapchain_imageviews: Vec, 41 | swapchain_framebuffers: Vec, 42 | 43 | render_pass: vk::RenderPass, 44 | pipeline_layout: vk::PipelineLayout, 45 | graphics_pipeline: vk::Pipeline, 46 | 47 | command_pool: vk::CommandPool, 48 | _command_buffers: Vec, 49 | } 50 | 51 | impl VulkanApp { 52 | pub fn new(window: &winit::window::Window) -> VulkanApp { 53 | 54 | let entry = ash::Entry::new().unwrap(); 55 | let instance = share::create_instance( 56 | &entry, 57 | WINDOW_TITLE, 58 | VALIDATION.is_enable, 59 | &VALIDATION.required_validation_layers.to_vec(), 60 | ); 61 | let surface_stuff = 62 | share::create_surface(&entry, &instance, &window, WINDOW_WIDTH, WINDOW_HEIGHT); 63 | let (debug_utils_loader, debug_merssager) = 64 | setup_debug_utils(VALIDATION.is_enable, &entry, &instance); 65 | let physical_device = 66 | share::pick_physical_device(&instance, &surface_stuff, &DEVICE_EXTENSIONS); 67 | let (device, family_indices) = share::create_logical_device( 68 | &instance, 69 | physical_device, 70 | &VALIDATION, 71 | &DEVICE_EXTENSIONS, 72 | &surface_stuff, 73 | ); 74 | let graphics_queue = 75 | unsafe { device.get_device_queue(family_indices.graphics_family.unwrap(), 0) }; 76 | let present_queue = 77 | unsafe { device.get_device_queue(family_indices.present_family.unwrap(), 0) }; 78 | let swapchain_stuff = share::create_swapchain( 79 | &instance, 80 | &device, 81 | physical_device, 82 | &window, 83 | &surface_stuff, 84 | &family_indices, 85 | ); 86 | let swapchain_imageviews = share::v1::create_image_views( 87 | &device, 88 | swapchain_stuff.swapchain_format, 89 | &swapchain_stuff.swapchain_images, 90 | ); 91 | let render_pass = share::v1::create_render_pass(&device, swapchain_stuff.swapchain_format); 92 | let (graphics_pipeline, pipeline_layout) = share::v1::create_graphics_pipeline( 93 | &device, 94 | render_pass, 95 | swapchain_stuff.swapchain_extent, 96 | ); 97 | let swapchain_framebuffers = share::v1::create_framebuffers( 98 | &device, 99 | render_pass, 100 | &swapchain_imageviews, 101 | swapchain_stuff.swapchain_extent, 102 | ); 103 | let command_pool = VulkanApp::create_command_pool(&device, &family_indices); 104 | let command_buffers = VulkanApp::create_command_buffers( 105 | &device, 106 | command_pool, 107 | graphics_pipeline, 108 | &swapchain_framebuffers, 109 | render_pass, 110 | swapchain_stuff.swapchain_extent, 111 | ); 112 | 113 | // cleanup(); the 'drop' function will take care of it. 114 | VulkanApp { 115 | _entry: entry, 116 | instance, 117 | surface: surface_stuff.surface, 118 | surface_loader: surface_stuff.surface_loader, 119 | debug_utils_loader, 120 | debug_merssager, 121 | 122 | _physical_device: physical_device, 123 | device, 124 | 125 | _graphics_queue: graphics_queue, 126 | _present_queue: present_queue, 127 | 128 | swapchain_loader: swapchain_stuff.swapchain_loader, 129 | swapchain: swapchain_stuff.swapchain, 130 | _swapchain_format: swapchain_stuff.swapchain_format, 131 | _swapchain_images: swapchain_stuff.swapchain_images, 132 | _swapchain_extent: swapchain_stuff.swapchain_extent, 133 | swapchain_imageviews, 134 | swapchain_framebuffers, 135 | 136 | pipeline_layout, 137 | render_pass, 138 | graphics_pipeline, 139 | 140 | command_pool, 141 | _command_buffers: command_buffers, 142 | } 143 | } 144 | 145 | fn create_command_pool( 146 | device: &ash::Device, 147 | queue_families: &QueueFamilyIndices, 148 | ) -> vk::CommandPool { 149 | let command_pool_create_info = vk::CommandPoolCreateInfo { 150 | s_type: vk::StructureType::COMMAND_POOL_CREATE_INFO, 151 | p_next: ptr::null(), 152 | flags: vk::CommandPoolCreateFlags::empty(), 153 | queue_family_index: queue_families.graphics_family.unwrap(), 154 | }; 155 | 156 | unsafe { 157 | device 158 | .create_command_pool(&command_pool_create_info, None) 159 | .expect("Failed to create Command Pool!") 160 | } 161 | } 162 | 163 | fn create_command_buffers( 164 | device: &ash::Device, 165 | command_pool: vk::CommandPool, 166 | graphics_pipeline: vk::Pipeline, 167 | framebuffers: &Vec, 168 | render_pass: vk::RenderPass, 169 | surface_extent: vk::Extent2D, 170 | ) -> Vec { 171 | let command_buffer_allocate_info = vk::CommandBufferAllocateInfo { 172 | s_type: vk::StructureType::COMMAND_BUFFER_ALLOCATE_INFO, 173 | p_next: ptr::null(), 174 | command_buffer_count: framebuffers.len() as u32, 175 | command_pool, 176 | level: vk::CommandBufferLevel::PRIMARY, 177 | }; 178 | 179 | let command_buffers = unsafe { 180 | device 181 | .allocate_command_buffers(&command_buffer_allocate_info) 182 | .expect("Failed to allocate Command Buffers!") 183 | }; 184 | 185 | for (i, &command_buffer) in command_buffers.iter().enumerate() { 186 | let command_buffer_begin_info = vk::CommandBufferBeginInfo { 187 | s_type: vk::StructureType::COMMAND_BUFFER_BEGIN_INFO, 188 | p_next: ptr::null(), 189 | p_inheritance_info: ptr::null(), 190 | flags: vk::CommandBufferUsageFlags::SIMULTANEOUS_USE, 191 | }; 192 | 193 | unsafe { 194 | device 195 | .begin_command_buffer(command_buffer, &command_buffer_begin_info) 196 | .expect("Failed to begin recording Command Buffer at beginning!"); 197 | } 198 | 199 | let clear_values = [vk::ClearValue { 200 | color: vk::ClearColorValue { 201 | float32: [0.0, 0.0, 0.0, 1.0], 202 | }, 203 | }]; 204 | 205 | let render_pass_begin_info = vk::RenderPassBeginInfo { 206 | s_type: vk::StructureType::RENDER_PASS_BEGIN_INFO, 207 | p_next: ptr::null(), 208 | render_pass, 209 | framebuffer: framebuffers[i], 210 | render_area: vk::Rect2D { 211 | offset: vk::Offset2D { x: 0, y: 0 }, 212 | extent: surface_extent, 213 | }, 214 | clear_value_count: clear_values.len() as u32, 215 | p_clear_values: clear_values.as_ptr(), 216 | }; 217 | 218 | unsafe { 219 | device.cmd_begin_render_pass( 220 | command_buffer, 221 | &render_pass_begin_info, 222 | vk::SubpassContents::INLINE, 223 | ); 224 | device.cmd_bind_pipeline( 225 | command_buffer, 226 | vk::PipelineBindPoint::GRAPHICS, 227 | graphics_pipeline, 228 | ); 229 | device.cmd_draw(command_buffer, 3, 1, 0, 0); 230 | 231 | device.cmd_end_render_pass(command_buffer); 232 | 233 | device 234 | .end_command_buffer(command_buffer) 235 | .expect("Failed to record Command Buffer at Ending!"); 236 | } 237 | } 238 | 239 | command_buffers 240 | } 241 | 242 | fn draw_frame(&mut self) { 243 | // Drawing will be here 244 | } 245 | } 246 | 247 | impl Drop for VulkanApp { 248 | fn drop(&mut self) { 249 | unsafe { 250 | self.device.destroy_command_pool(self.command_pool, None); 251 | 252 | for &framebuffer in self.swapchain_framebuffers.iter() { 253 | self.device.destroy_framebuffer(framebuffer, None); 254 | } 255 | 256 | self.device.destroy_pipeline(self.graphics_pipeline, None); 257 | self.device 258 | .destroy_pipeline_layout(self.pipeline_layout, None); 259 | self.device.destroy_render_pass(self.render_pass, None); 260 | 261 | for &imageview in self.swapchain_imageviews.iter() { 262 | self.device.destroy_image_view(imageview, None); 263 | } 264 | 265 | self.swapchain_loader 266 | .destroy_swapchain(self.swapchain, None); 267 | self.device.destroy_device(None); 268 | self.surface_loader.destroy_surface(self.surface, None); 269 | 270 | if VALIDATION.is_enable { 271 | self.debug_utils_loader 272 | .destroy_debug_utils_messenger(self.debug_merssager, None); 273 | } 274 | self.instance.destroy_instance(None); 275 | } 276 | } 277 | } 278 | 279 | // Fix content ------------------------------------------------------------------------------- 280 | impl VulkanApp { 281 | pub fn main_loop(mut self, event_loop: EventLoop<()>, window: winit::window::Window) { 282 | 283 | event_loop.run(move |event, _, control_flow| { 284 | 285 | match event { 286 | | Event::WindowEvent { event, .. } => { 287 | match event { 288 | | WindowEvent::CloseRequested => { 289 | *control_flow = ControlFlow::Exit 290 | }, 291 | | WindowEvent::KeyboardInput { input, .. } => { 292 | match input { 293 | | KeyboardInput { virtual_keycode, state, .. } => { 294 | match (virtual_keycode, state) { 295 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 296 | *control_flow = ControlFlow::Exit 297 | }, 298 | | _ => {}, 299 | } 300 | }, 301 | } 302 | }, 303 | | _ => {}, 304 | } 305 | }, 306 | | Event::MainEventsCleared => { 307 | window.request_redraw(); 308 | }, 309 | | Event::RedrawRequested(_window_id) => { 310 | self.draw_frame(); 311 | }, 312 | _ => (), 313 | } 314 | 315 | }) 316 | } 317 | } 318 | 319 | 320 | fn main() { 321 | let event_loop = EventLoop::new(); 322 | let window = utility::window::init_window(&event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT); 323 | 324 | let vulkan_app = VulkanApp::new(&window); 325 | vulkan_app.main_loop(event_loop, window); 326 | } 327 | // ------------------------------------------------------------------------------------------- 328 | -------------------------------------------------------------------------------- /src/tutorials/15_hello_triangle.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::{ 2 | utility, // the mod define some fixed functions that have been learned before. 3 | utility::constants::*, 4 | utility::debug::*, 5 | utility::share, 6 | }; 7 | 8 | use ash::version::DeviceV1_0; 9 | use ash::version::InstanceV1_0; 10 | use ash::vk; 11 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 12 | use winit::event_loop::{EventLoop, ControlFlow}; 13 | 14 | use std::ptr; 15 | 16 | // Constants 17 | const WINDOW_TITLE: &'static str = "15.Hello Triangle"; 18 | const MAX_FRAMES_IN_FLIGHT: usize = 2; 19 | 20 | struct SyncObjects { 21 | image_available_semaphores: Vec, 22 | render_finished_semaphores: Vec, 23 | inflight_fences: Vec, 24 | } 25 | 26 | struct VulkanApp { 27 | window: winit::window::Window, 28 | // vulkan stuff 29 | _entry: ash::Entry, 30 | instance: ash::Instance, 31 | surface_loader: ash::extensions::khr::Surface, 32 | surface: vk::SurfaceKHR, 33 | debug_utils_loader: ash::extensions::ext::DebugUtils, 34 | debug_merssager: vk::DebugUtilsMessengerEXT, 35 | 36 | _physical_device: vk::PhysicalDevice, 37 | device: ash::Device, 38 | 39 | graphics_queue: vk::Queue, 40 | present_queue: vk::Queue, 41 | 42 | swapchain_loader: ash::extensions::khr::Swapchain, 43 | swapchain: vk::SwapchainKHR, 44 | _swapchain_images: Vec, 45 | _swapchain_format: vk::Format, 46 | _swapchain_extent: vk::Extent2D, 47 | swapchain_imageviews: Vec, 48 | swapchain_framebuffers: Vec, 49 | 50 | render_pass: vk::RenderPass, 51 | pipeline_layout: vk::PipelineLayout, 52 | graphics_pipeline: vk::Pipeline, 53 | 54 | command_pool: vk::CommandPool, 55 | command_buffers: Vec, 56 | 57 | image_available_semaphores: Vec, 58 | render_finished_semaphores: Vec, 59 | in_flight_fences: Vec, 60 | current_frame: usize, 61 | } 62 | 63 | impl VulkanApp { 64 | pub fn new(event_loop: &winit::event_loop::EventLoop<()>) -> VulkanApp { 65 | 66 | let window = utility::window::init_window(event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT); 67 | 68 | // init vulkan stuff 69 | let entry = ash::Entry::new().unwrap(); 70 | let instance = share::create_instance( 71 | &entry, 72 | WINDOW_TITLE, 73 | VALIDATION.is_enable, 74 | &VALIDATION.required_validation_layers.to_vec(), 75 | ); 76 | let surface_stuff = 77 | share::create_surface(&entry, &instance, &window, WINDOW_WIDTH, WINDOW_HEIGHT); 78 | let (debug_utils_loader, debug_merssager) = 79 | setup_debug_utils(VALIDATION.is_enable, &entry, &instance); 80 | let physical_device = 81 | share::pick_physical_device(&instance, &surface_stuff, &DEVICE_EXTENSIONS); 82 | let (device, family_indices) = share::create_logical_device( 83 | &instance, 84 | physical_device, 85 | &VALIDATION, 86 | &DEVICE_EXTENSIONS, 87 | &surface_stuff, 88 | ); 89 | let graphics_queue = 90 | unsafe { device.get_device_queue(family_indices.graphics_family.unwrap(), 0) }; 91 | let present_queue = 92 | unsafe { device.get_device_queue(family_indices.present_family.unwrap(), 0) }; 93 | let swapchain_stuff = share::create_swapchain( 94 | &instance, 95 | &device, 96 | physical_device, 97 | &window, 98 | &surface_stuff, 99 | &family_indices, 100 | ); 101 | let swapchain_imageviews = share::v1::create_image_views( 102 | &device, 103 | swapchain_stuff.swapchain_format, 104 | &swapchain_stuff.swapchain_images, 105 | ); 106 | let render_pass = VulkanApp::create_render_pass(&device, swapchain_stuff.swapchain_format); 107 | let (graphics_pipeline, pipeline_layout) = share::v1::create_graphics_pipeline( 108 | &device, 109 | render_pass, 110 | swapchain_stuff.swapchain_extent, 111 | ); 112 | let swapchain_framebuffers = share::v1::create_framebuffers( 113 | &device, 114 | render_pass, 115 | &swapchain_imageviews, 116 | swapchain_stuff.swapchain_extent, 117 | ); 118 | let command_pool = share::v1::create_command_pool(&device, &family_indices); 119 | let command_buffers = share::v1::create_command_buffers( 120 | &device, 121 | command_pool, 122 | graphics_pipeline, 123 | &swapchain_framebuffers, 124 | render_pass, 125 | swapchain_stuff.swapchain_extent, 126 | ); 127 | let sync_ojbects = VulkanApp::create_sync_objects(&device); 128 | 129 | // cleanup(); the 'drop' function will take care of it. 130 | VulkanApp { 131 | window, 132 | // vulkan stuff 133 | _entry: entry, 134 | instance, 135 | surface: surface_stuff.surface, 136 | surface_loader: surface_stuff.surface_loader, 137 | debug_utils_loader, 138 | debug_merssager, 139 | 140 | _physical_device: physical_device, 141 | device, 142 | 143 | graphics_queue, 144 | present_queue, 145 | 146 | swapchain_loader: swapchain_stuff.swapchain_loader, 147 | swapchain: swapchain_stuff.swapchain, 148 | _swapchain_format: swapchain_stuff.swapchain_format, 149 | _swapchain_images: swapchain_stuff.swapchain_images, 150 | _swapchain_extent: swapchain_stuff.swapchain_extent, 151 | swapchain_imageviews, 152 | swapchain_framebuffers, 153 | 154 | pipeline_layout, 155 | render_pass, 156 | graphics_pipeline, 157 | 158 | command_pool, 159 | command_buffers, 160 | 161 | image_available_semaphores: sync_ojbects.image_available_semaphores, 162 | render_finished_semaphores: sync_ojbects.render_finished_semaphores, 163 | in_flight_fences: sync_ojbects.inflight_fences, 164 | current_frame: 0, 165 | } 166 | } 167 | 168 | fn draw_frame(&mut self) { 169 | let wait_fences = [self.in_flight_fences[self.current_frame]]; 170 | 171 | let (image_index, _is_sub_optimal) = unsafe { 172 | self.device 173 | .wait_for_fences(&wait_fences, true, std::u64::MAX) 174 | .expect("Failed to wait for Fence!"); 175 | 176 | self.swapchain_loader 177 | .acquire_next_image( 178 | self.swapchain, 179 | std::u64::MAX, 180 | self.image_available_semaphores[self.current_frame], 181 | vk::Fence::null(), 182 | ) 183 | .expect("Failed to acquire next image.") 184 | }; 185 | 186 | let wait_semaphores = [self.image_available_semaphores[self.current_frame]]; 187 | let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; 188 | let signal_semaphores = [self.render_finished_semaphores[self.current_frame]]; 189 | 190 | let submit_infos = [vk::SubmitInfo { 191 | s_type: vk::StructureType::SUBMIT_INFO, 192 | p_next: ptr::null(), 193 | wait_semaphore_count: wait_semaphores.len() as u32, 194 | p_wait_semaphores: wait_semaphores.as_ptr(), 195 | p_wait_dst_stage_mask: wait_stages.as_ptr(), 196 | command_buffer_count: 1, 197 | p_command_buffers: &self.command_buffers[image_index as usize], 198 | signal_semaphore_count: signal_semaphores.len() as u32, 199 | p_signal_semaphores: signal_semaphores.as_ptr(), 200 | }]; 201 | 202 | unsafe { 203 | self.device 204 | .reset_fences(&wait_fences) 205 | .expect("Failed to reset Fence!"); 206 | 207 | self.device 208 | .queue_submit( 209 | self.graphics_queue, 210 | &submit_infos, 211 | self.in_flight_fences[self.current_frame], 212 | ) 213 | .expect("Failed to execute queue submit."); 214 | } 215 | 216 | let swapchains = [self.swapchain]; 217 | 218 | let present_info = vk::PresentInfoKHR { 219 | s_type: vk::StructureType::PRESENT_INFO_KHR, 220 | p_next: ptr::null(), 221 | wait_semaphore_count: 1, 222 | p_wait_semaphores: signal_semaphores.as_ptr(), 223 | swapchain_count: 1, 224 | p_swapchains: swapchains.as_ptr(), 225 | p_image_indices: &image_index, 226 | p_results: ptr::null_mut(), 227 | }; 228 | 229 | unsafe { 230 | self.swapchain_loader 231 | .queue_present(self.present_queue, &present_info) 232 | .expect("Failed to execute queue present."); 233 | } 234 | 235 | self.current_frame = (self.current_frame + 1) % MAX_FRAMES_IN_FLIGHT; 236 | } 237 | 238 | fn create_render_pass(device: &ash::Device, surface_format: vk::Format) -> vk::RenderPass { 239 | let color_attachment = vk::AttachmentDescription { 240 | format: surface_format, 241 | flags: vk::AttachmentDescriptionFlags::empty(), 242 | samples: vk::SampleCountFlags::TYPE_1, 243 | load_op: vk::AttachmentLoadOp::CLEAR, 244 | store_op: vk::AttachmentStoreOp::STORE, 245 | stencil_load_op: vk::AttachmentLoadOp::DONT_CARE, 246 | stencil_store_op: vk::AttachmentStoreOp::DONT_CARE, 247 | initial_layout: vk::ImageLayout::UNDEFINED, 248 | final_layout: vk::ImageLayout::PRESENT_SRC_KHR, 249 | }; 250 | 251 | let color_attachment_ref = vk::AttachmentReference { 252 | attachment: 0, 253 | layout: vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, 254 | }; 255 | 256 | let subpasses = [vk::SubpassDescription { 257 | color_attachment_count: 1, 258 | p_color_attachments: &color_attachment_ref, 259 | p_depth_stencil_attachment: ptr::null(), 260 | flags: vk::SubpassDescriptionFlags::empty(), 261 | pipeline_bind_point: vk::PipelineBindPoint::GRAPHICS, 262 | input_attachment_count: 0, 263 | p_input_attachments: ptr::null(), 264 | p_resolve_attachments: ptr::null(), 265 | preserve_attachment_count: 0, 266 | p_preserve_attachments: ptr::null(), 267 | }]; 268 | 269 | let render_pass_attachments = [color_attachment]; 270 | 271 | let subpass_dependencies = [vk::SubpassDependency { 272 | src_subpass: vk::SUBPASS_EXTERNAL, 273 | dst_subpass: 0, 274 | src_stage_mask: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, 275 | dst_stage_mask: vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, 276 | src_access_mask: vk::AccessFlags::empty(), 277 | dst_access_mask: vk::AccessFlags::COLOR_ATTACHMENT_WRITE, 278 | dependency_flags: vk::DependencyFlags::empty(), 279 | }]; 280 | 281 | let renderpass_create_info = vk::RenderPassCreateInfo { 282 | s_type: vk::StructureType::RENDER_PASS_CREATE_INFO, 283 | flags: vk::RenderPassCreateFlags::empty(), 284 | p_next: ptr::null(), 285 | attachment_count: render_pass_attachments.len() as u32, 286 | p_attachments: render_pass_attachments.as_ptr(), 287 | subpass_count: subpasses.len() as u32, 288 | p_subpasses: subpasses.as_ptr(), 289 | dependency_count: subpass_dependencies.len() as u32, 290 | p_dependencies: subpass_dependencies.as_ptr(), 291 | }; 292 | 293 | unsafe { 294 | device 295 | .create_render_pass(&renderpass_create_info, None) 296 | .expect("Failed to create render pass!") 297 | } 298 | } 299 | 300 | fn create_sync_objects(device: &ash::Device) -> SyncObjects { 301 | let mut sync_objects = SyncObjects { 302 | image_available_semaphores: vec![], 303 | render_finished_semaphores: vec![], 304 | inflight_fences: vec![], 305 | }; 306 | 307 | let semaphore_create_info = vk::SemaphoreCreateInfo { 308 | s_type: vk::StructureType::SEMAPHORE_CREATE_INFO, 309 | p_next: ptr::null(), 310 | flags: vk::SemaphoreCreateFlags::empty(), 311 | }; 312 | 313 | let fence_create_info = vk::FenceCreateInfo { 314 | s_type: vk::StructureType::FENCE_CREATE_INFO, 315 | p_next: ptr::null(), 316 | flags: vk::FenceCreateFlags::SIGNALED, 317 | }; 318 | 319 | for _ in 0..MAX_FRAMES_IN_FLIGHT { 320 | unsafe { 321 | let image_available_semaphore = device 322 | .create_semaphore(&semaphore_create_info, None) 323 | .expect("Failed to create Semaphore Object!"); 324 | let render_finished_semaphore = device 325 | .create_semaphore(&semaphore_create_info, None) 326 | .expect("Failed to create Semaphore Object!"); 327 | let inflight_fence = device 328 | .create_fence(&fence_create_info, None) 329 | .expect("Failed to create Fence Object!"); 330 | 331 | sync_objects 332 | .image_available_semaphores 333 | .push(image_available_semaphore); 334 | sync_objects 335 | .render_finished_semaphores 336 | .push(render_finished_semaphore); 337 | sync_objects.inflight_fences.push(inflight_fence); 338 | } 339 | } 340 | 341 | sync_objects 342 | } 343 | } 344 | 345 | impl Drop for VulkanApp { 346 | fn drop(&mut self) { 347 | unsafe { 348 | for i in 0..MAX_FRAMES_IN_FLIGHT { 349 | self.device 350 | .destroy_semaphore(self.image_available_semaphores[i], None); 351 | self.device 352 | .destroy_semaphore(self.render_finished_semaphores[i], None); 353 | self.device.destroy_fence(self.in_flight_fences[i], None); 354 | } 355 | 356 | self.device.destroy_command_pool(self.command_pool, None); 357 | 358 | for &framebuffer in self.swapchain_framebuffers.iter() { 359 | self.device.destroy_framebuffer(framebuffer, None); 360 | } 361 | 362 | self.device.destroy_pipeline(self.graphics_pipeline, None); 363 | self.device 364 | .destroy_pipeline_layout(self.pipeline_layout, None); 365 | self.device.destroy_render_pass(self.render_pass, None); 366 | 367 | for &imageview in self.swapchain_imageviews.iter() { 368 | self.device.destroy_image_view(imageview, None); 369 | } 370 | 371 | self.swapchain_loader 372 | .destroy_swapchain(self.swapchain, None); 373 | self.device.destroy_device(None); 374 | self.surface_loader.destroy_surface(self.surface, None); 375 | 376 | if VALIDATION.is_enable { 377 | self.debug_utils_loader 378 | .destroy_debug_utils_messenger(self.debug_merssager, None); 379 | } 380 | self.instance.destroy_instance(None); 381 | } 382 | } 383 | } 384 | 385 | // Fix content ------------------------------------------------------------------------------- 386 | impl VulkanApp { 387 | 388 | pub fn main_loop(mut self, event_loop: EventLoop<()>) { 389 | 390 | event_loop.run(move |event, _, control_flow| { 391 | 392 | match event { 393 | | Event::WindowEvent { event, .. } => { 394 | match event { 395 | | WindowEvent::CloseRequested => { 396 | *control_flow = ControlFlow::Exit 397 | }, 398 | | WindowEvent::KeyboardInput { input, .. } => { 399 | match input { 400 | | KeyboardInput { virtual_keycode, state, .. } => { 401 | match (virtual_keycode, state) { 402 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 403 | *control_flow = ControlFlow::Exit 404 | }, 405 | | _ => {}, 406 | } 407 | }, 408 | } 409 | }, 410 | | _ => {}, 411 | } 412 | }, 413 | | Event::MainEventsCleared => { 414 | self.window.request_redraw(); 415 | }, 416 | | Event::RedrawRequested(_window_id) => { 417 | self.draw_frame(); 418 | }, 419 | | Event::LoopDestroyed => { 420 | unsafe { 421 | self.device.device_wait_idle() 422 | .expect("Failed to wait device idle!") 423 | }; 424 | }, 425 | _ => (), 426 | } 427 | 428 | }) 429 | } 430 | } 431 | 432 | fn main() { 433 | 434 | let event_loop = EventLoop::new(); 435 | 436 | let vulkan_app = VulkanApp::new(&event_loop); 437 | vulkan_app.main_loop(event_loop); 438 | } 439 | // ------------------------------------------------------------------------------------------- 440 | -------------------------------------------------------------------------------- /src/tutorials/16_swap_chain_recreation.rs: -------------------------------------------------------------------------------- 1 | use vulkan_tutorial_rust::{ 2 | utility, // the mod define some fixed functions that have been learned before. 3 | utility::constants::*, 4 | utility::debug::*, 5 | utility::share, 6 | utility::structures::*, 7 | }; 8 | 9 | use ash::version::DeviceV1_0; 10 | use ash::version::InstanceV1_0; 11 | use ash::vk; 12 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 13 | use winit::event_loop::{EventLoop, ControlFlow}; 14 | 15 | use std::ptr; 16 | 17 | // Constants 18 | const WINDOW_TITLE: &'static str = "16.Swap Chain Recreation"; 19 | 20 | struct VulkanApp { 21 | window: winit::window::Window, 22 | 23 | // vulkan stuff 24 | _entry: ash::Entry, 25 | instance: ash::Instance, 26 | surface_loader: ash::extensions::khr::Surface, 27 | surface: vk::SurfaceKHR, 28 | debug_utils_loader: ash::extensions::ext::DebugUtils, 29 | debug_merssager: vk::DebugUtilsMessengerEXT, 30 | 31 | physical_device: vk::PhysicalDevice, 32 | device: ash::Device, 33 | 34 | queue_family: QueueFamilyIndices, 35 | graphics_queue: vk::Queue, 36 | present_queue: vk::Queue, 37 | 38 | swapchain_loader: ash::extensions::khr::Swapchain, 39 | swapchain: vk::SwapchainKHR, 40 | swapchain_images: Vec, 41 | swapchain_format: vk::Format, 42 | swapchain_extent: vk::Extent2D, 43 | swapchain_imageviews: Vec, 44 | swapchain_framebuffers: Vec, 45 | 46 | render_pass: vk::RenderPass, 47 | pipeline_layout: vk::PipelineLayout, 48 | graphics_pipeline: vk::Pipeline, 49 | 50 | command_pool: vk::CommandPool, 51 | command_buffers: Vec, 52 | 53 | image_available_semaphores: Vec, 54 | render_finished_semaphores: Vec, 55 | in_flight_fences: Vec, 56 | current_frame: usize, 57 | 58 | is_framebuffer_resized: bool, 59 | } 60 | 61 | impl VulkanApp { 62 | pub fn new(event_loop: &winit::event_loop::EventLoop<()>) -> VulkanApp { 63 | 64 | let window = utility::window::init_window(event_loop, WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT); 65 | 66 | let entry = ash::Entry::new().unwrap(); 67 | let instance = share::create_instance( 68 | &entry, 69 | WINDOW_TITLE, 70 | VALIDATION.is_enable, 71 | &VALIDATION.required_validation_layers.to_vec(), 72 | ); 73 | let surface_stuff = 74 | share::create_surface(&entry, &instance, &window, WINDOW_WIDTH, WINDOW_HEIGHT); 75 | let (debug_utils_loader, debug_merssager) = 76 | setup_debug_utils(VALIDATION.is_enable, &entry, &instance); 77 | let physical_device = 78 | share::pick_physical_device(&instance, &surface_stuff, &DEVICE_EXTENSIONS); 79 | let (device, queue_family) = share::create_logical_device( 80 | &instance, 81 | physical_device, 82 | &VALIDATION, 83 | &DEVICE_EXTENSIONS, 84 | &surface_stuff, 85 | ); 86 | let graphics_queue = 87 | unsafe { device.get_device_queue(queue_family.graphics_family.unwrap(), 0) }; 88 | let present_queue = 89 | unsafe { device.get_device_queue(queue_family.present_family.unwrap(), 0) }; 90 | let swapchain_stuff = share::create_swapchain( 91 | &instance, 92 | &device, 93 | physical_device, 94 | &window, 95 | &surface_stuff, 96 | &queue_family, 97 | ); 98 | let swapchain_imageviews = share::v1::create_image_views( 99 | &device, 100 | swapchain_stuff.swapchain_format, 101 | &swapchain_stuff.swapchain_images, 102 | ); 103 | let render_pass = share::v1::create_render_pass(&device, swapchain_stuff.swapchain_format); 104 | let (graphics_pipeline, pipeline_layout) = share::v1::create_graphics_pipeline( 105 | &device, 106 | render_pass, 107 | swapchain_stuff.swapchain_extent, 108 | ); 109 | let swapchain_framebuffers = share::v1::create_framebuffers( 110 | &device, 111 | render_pass, 112 | &swapchain_imageviews, 113 | swapchain_stuff.swapchain_extent, 114 | ); 115 | let command_pool = share::v1::create_command_pool(&device, &queue_family); 116 | let command_buffers = share::v1::create_command_buffers( 117 | &device, 118 | command_pool, 119 | graphics_pipeline, 120 | &swapchain_framebuffers, 121 | render_pass, 122 | swapchain_stuff.swapchain_extent, 123 | ); 124 | let sync_ojbects = share::v1::create_sync_objects(&device, MAX_FRAMES_IN_FLIGHT); 125 | 126 | // cleanup(); the 'drop' function will take care of it. 127 | VulkanApp { 128 | // winit stuff 129 | window, 130 | 131 | // vulkan stuff 132 | _entry: entry, 133 | instance, 134 | surface: surface_stuff.surface, 135 | surface_loader: surface_stuff.surface_loader, 136 | debug_utils_loader, 137 | debug_merssager, 138 | 139 | physical_device, 140 | device, 141 | 142 | queue_family, 143 | graphics_queue, 144 | present_queue, 145 | 146 | swapchain_loader: swapchain_stuff.swapchain_loader, 147 | swapchain: swapchain_stuff.swapchain, 148 | swapchain_format: swapchain_stuff.swapchain_format, 149 | swapchain_images: swapchain_stuff.swapchain_images, 150 | swapchain_extent: swapchain_stuff.swapchain_extent, 151 | swapchain_imageviews, 152 | swapchain_framebuffers, 153 | 154 | pipeline_layout, 155 | render_pass, 156 | graphics_pipeline, 157 | 158 | command_pool, 159 | command_buffers, 160 | 161 | image_available_semaphores: sync_ojbects.image_available_semaphores, 162 | render_finished_semaphores: sync_ojbects.render_finished_semaphores, 163 | in_flight_fences: sync_ojbects.inflight_fences, 164 | current_frame: 0, 165 | 166 | is_framebuffer_resized: false, 167 | } 168 | } 169 | 170 | fn draw_frame(&mut self) { 171 | let wait_fences = [self.in_flight_fences[self.current_frame]]; 172 | 173 | unsafe { 174 | self.device 175 | .wait_for_fences(&wait_fences, true, std::u64::MAX) 176 | .expect("Failed to wait for Fence!"); 177 | } 178 | 179 | let (image_index, _is_sub_optimal) = unsafe { 180 | let result = self.swapchain_loader.acquire_next_image( 181 | self.swapchain, 182 | std::u64::MAX, 183 | self.image_available_semaphores[self.current_frame], 184 | vk::Fence::null(), 185 | ); 186 | match result { 187 | Ok(image_index) => image_index, 188 | Err(vk_result) => match vk_result { 189 | vk::Result::ERROR_OUT_OF_DATE_KHR => { 190 | self.recreate_swapchain(); 191 | return; 192 | } 193 | _ => panic!("Failed to acquire Swap Chain Image!"), 194 | }, 195 | } 196 | }; 197 | 198 | let wait_semaphores = [self.image_available_semaphores[self.current_frame]]; 199 | let wait_stages = [vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT]; 200 | let signal_semaphores = [self.render_finished_semaphores[self.current_frame]]; 201 | 202 | let submit_infos = [vk::SubmitInfo { 203 | s_type: vk::StructureType::SUBMIT_INFO, 204 | p_next: ptr::null(), 205 | wait_semaphore_count: wait_semaphores.len() as u32, 206 | p_wait_semaphores: wait_semaphores.as_ptr(), 207 | p_wait_dst_stage_mask: wait_stages.as_ptr(), 208 | command_buffer_count: 1, 209 | p_command_buffers: &self.command_buffers[image_index as usize], 210 | signal_semaphore_count: signal_semaphores.len() as u32, 211 | p_signal_semaphores: signal_semaphores.as_ptr(), 212 | }]; 213 | 214 | unsafe { 215 | self.device 216 | .reset_fences(&wait_fences) 217 | .expect("Failed to reset Fence!"); 218 | 219 | self.device 220 | .queue_submit( 221 | self.graphics_queue, 222 | &submit_infos, 223 | self.in_flight_fences[self.current_frame], 224 | ) 225 | .expect("Failed to execute queue submit."); 226 | } 227 | 228 | let swapchains = [self.swapchain]; 229 | 230 | let present_info = vk::PresentInfoKHR { 231 | s_type: vk::StructureType::PRESENT_INFO_KHR, 232 | p_next: ptr::null(), 233 | wait_semaphore_count: 1, 234 | p_wait_semaphores: signal_semaphores.as_ptr(), 235 | swapchain_count: 1, 236 | p_swapchains: swapchains.as_ptr(), 237 | p_image_indices: &image_index, 238 | p_results: ptr::null_mut(), 239 | }; 240 | 241 | let result = unsafe { 242 | self.swapchain_loader 243 | .queue_present(self.present_queue, &present_info) 244 | }; 245 | let is_resized = match result { 246 | Ok(_) => self.is_framebuffer_resized, 247 | Err(vk_result) => match vk_result { 248 | vk::Result::ERROR_OUT_OF_DATE_KHR | vk::Result::SUBOPTIMAL_KHR => true, 249 | _ => panic!("Failed to execute queue present."), 250 | }, 251 | }; 252 | if is_resized { 253 | self.is_framebuffer_resized = false; 254 | self.recreate_swapchain(); 255 | } 256 | 257 | self.current_frame = (self.current_frame + 1) % MAX_FRAMES_IN_FLIGHT; 258 | } 259 | 260 | fn recreate_swapchain(&mut self) { 261 | // parameters ------------- 262 | let surface_suff = SurfaceStuff { 263 | surface_loader: self.surface_loader.clone(), 264 | surface: self.surface, 265 | screen_width: WINDOW_WIDTH, 266 | screen_height: WINDOW_HEIGHT, 267 | }; 268 | // ------------------------ 269 | 270 | unsafe { 271 | self.device 272 | .device_wait_idle() 273 | .expect("Failed to wait device idle!") 274 | }; 275 | self.cleanup_swapchain(); 276 | 277 | let swapchain_stuff = share::create_swapchain( 278 | &self.instance, 279 | &self.device, 280 | self.physical_device, 281 | &self.window, 282 | &surface_suff, 283 | &self.queue_family, 284 | ); 285 | self.swapchain_loader = swapchain_stuff.swapchain_loader; 286 | self.swapchain = swapchain_stuff.swapchain; 287 | self.swapchain_images = swapchain_stuff.swapchain_images; 288 | self.swapchain_format = swapchain_stuff.swapchain_format; 289 | self.swapchain_extent = swapchain_stuff.swapchain_extent; 290 | 291 | self.swapchain_imageviews = share::v1::create_image_views( 292 | &self.device, 293 | self.swapchain_format, 294 | &self.swapchain_images, 295 | ); 296 | self.render_pass = share::v1::create_render_pass(&self.device, self.swapchain_format); 297 | let (graphics_pipeline, pipeline_layout) = share::v1::create_graphics_pipeline( 298 | &self.device, 299 | self.render_pass, 300 | swapchain_stuff.swapchain_extent, 301 | ); 302 | self.graphics_pipeline = graphics_pipeline; 303 | self.pipeline_layout = pipeline_layout; 304 | 305 | self.swapchain_framebuffers = share::v1::create_framebuffers( 306 | &self.device, 307 | self.render_pass, 308 | &self.swapchain_imageviews, 309 | self.swapchain_extent, 310 | ); 311 | self.command_buffers = share::v1::create_command_buffers( 312 | &self.device, 313 | self.command_pool, 314 | self.graphics_pipeline, 315 | &self.swapchain_framebuffers, 316 | self.render_pass, 317 | self.swapchain_extent, 318 | ); 319 | } 320 | 321 | fn cleanup_swapchain(&self) { 322 | unsafe { 323 | self.device 324 | .free_command_buffers(self.command_pool, &self.command_buffers); 325 | for &framebuffer in self.swapchain_framebuffers.iter() { 326 | self.device.destroy_framebuffer(framebuffer, None); 327 | } 328 | self.device.destroy_pipeline(self.graphics_pipeline, None); 329 | self.device 330 | .destroy_pipeline_layout(self.pipeline_layout, None); 331 | self.device.destroy_render_pass(self.render_pass, None); 332 | for &image_view in self.swapchain_imageviews.iter() { 333 | self.device.destroy_image_view(image_view, None); 334 | } 335 | self.swapchain_loader 336 | .destroy_swapchain(self.swapchain, None); 337 | } 338 | } 339 | } 340 | 341 | impl Drop for VulkanApp { 342 | fn drop(&mut self) { 343 | unsafe { 344 | for i in 0..MAX_FRAMES_IN_FLIGHT { 345 | self.device 346 | .destroy_semaphore(self.image_available_semaphores[i], None); 347 | self.device 348 | .destroy_semaphore(self.render_finished_semaphores[i], None); 349 | self.device.destroy_fence(self.in_flight_fences[i], None); 350 | } 351 | 352 | self.cleanup_swapchain(); 353 | 354 | self.device.destroy_command_pool(self.command_pool, None); 355 | 356 | self.device.destroy_device(None); 357 | self.surface_loader.destroy_surface(self.surface, None); 358 | 359 | if VALIDATION.is_enable { 360 | self.debug_utils_loader 361 | .destroy_debug_utils_messenger(self.debug_merssager, None); 362 | } 363 | self.instance.destroy_instance(None); 364 | } 365 | } 366 | } 367 | 368 | // Fix content ------------------------------------------------------------------------------- 369 | impl VulkanApp { 370 | 371 | pub fn main_loop(mut self, event_loop: EventLoop<()>) { 372 | 373 | event_loop.run(move |event, _, control_flow| { 374 | 375 | match event { 376 | | Event::WindowEvent { event, .. } => { 377 | match event { 378 | | WindowEvent::CloseRequested => { 379 | *control_flow = ControlFlow::Exit 380 | }, 381 | | WindowEvent::KeyboardInput { input, .. } => { 382 | match input { 383 | | KeyboardInput { virtual_keycode, state, .. } => { 384 | match (virtual_keycode, state) { 385 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 386 | *control_flow = ControlFlow::Exit 387 | }, 388 | | _ => {}, 389 | } 390 | }, 391 | } 392 | }, 393 | | _ => {}, 394 | } 395 | }, 396 | | Event::MainEventsCleared => { 397 | self.window.request_redraw(); 398 | }, 399 | | Event::RedrawRequested(_window_id) => { 400 | self.draw_frame(); 401 | }, 402 | | Event::LoopDestroyed => { 403 | unsafe { 404 | self.device.device_wait_idle() 405 | .expect("Failed to wait device idle!") 406 | }; 407 | }, 408 | _ => (), 409 | } 410 | 411 | }) 412 | } 413 | } 414 | 415 | fn main() { 416 | 417 | let event_loop = EventLoop::new(); 418 | 419 | let vulkan_app = VulkanApp::new(&event_loop); 420 | vulkan_app.main_loop(event_loop); 421 | } 422 | // ------------------------------------------------------------------------------------------- 423 | -------------------------------------------------------------------------------- /src/utility/constants.rs: -------------------------------------------------------------------------------- 1 | use crate::utility::debug::ValidationInfo; 2 | use crate::utility::structures::DeviceExtension; 3 | use ash::vk_make_version; 4 | 5 | use std::os::raw::c_char; 6 | 7 | pub const APPLICATION_VERSION: u32 = vk_make_version!(1, 0, 0); 8 | pub const ENGINE_VERSION: u32 = vk_make_version!(1, 0, 0); 9 | pub const API_VERSION: u32 = vk_make_version!(1, 0, 92); 10 | 11 | pub const WINDOW_WIDTH: u32 = 800; 12 | pub const WINDOW_HEIGHT: u32 = 600; 13 | pub const VALIDATION: ValidationInfo = ValidationInfo { 14 | is_enable: true, 15 | required_validation_layers: ["VK_LAYER_KHRONOS_validation"], 16 | }; 17 | pub const DEVICE_EXTENSIONS: DeviceExtension = DeviceExtension { 18 | names: ["VK_KHR_swapchain"], 19 | }; 20 | pub const MAX_FRAMES_IN_FLIGHT: usize = 2; 21 | pub const IS_PAINT_FPS_COUNTER: bool = false; 22 | 23 | impl DeviceExtension { 24 | pub fn get_extensions_raw_names(&self) -> [*const c_char; 1] { 25 | [ 26 | // currently just enable the Swapchain extension. 27 | ash::extensions::khr::Swapchain::name().as_ptr(), 28 | ] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/utility/debug.rs: -------------------------------------------------------------------------------- 1 | use ash::version::EntryV1_0; 2 | use ash::vk; 3 | 4 | use std::ffi::CStr; 5 | use std::os::raw::c_void; 6 | use std::ptr; 7 | 8 | unsafe extern "system" fn vulkan_debug_utils_callback( 9 | message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, 10 | message_type: vk::DebugUtilsMessageTypeFlagsEXT, 11 | p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT, 12 | _p_user_data: *mut c_void, 13 | ) -> vk::Bool32 { 14 | let severity = match message_severity { 15 | vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => "[Verbose]", 16 | vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => "[Warning]", 17 | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => "[Error]", 18 | vk::DebugUtilsMessageSeverityFlagsEXT::INFO => "[Info]", 19 | _ => "[Unknown]", 20 | }; 21 | let types = match message_type { 22 | vk::DebugUtilsMessageTypeFlagsEXT::GENERAL => "[General]", 23 | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE => "[Performance]", 24 | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION => "[Validation]", 25 | _ => "[Unknown]", 26 | }; 27 | let message = CStr::from_ptr((*p_callback_data).p_message); 28 | println!("[Debug]{}{}{:?}", severity, types, message); 29 | 30 | vk::FALSE 31 | } 32 | 33 | pub struct ValidationInfo { 34 | pub is_enable: bool, 35 | pub required_validation_layers: [&'static str; 1], 36 | } 37 | 38 | pub fn check_validation_layer_support( 39 | entry: &ash::Entry, 40 | required_validation_layers: &Vec<&str>, 41 | ) -> bool { 42 | // if support validation layer, then return true 43 | 44 | let layer_properties = entry 45 | .enumerate_instance_layer_properties() 46 | .expect("Failed to enumerate Instance Layers Properties"); 47 | 48 | if layer_properties.len() <= 0 { 49 | eprintln!("No available layers."); 50 | return false; 51 | } 52 | 53 | for required_layer_name in required_validation_layers.iter() { 54 | let mut is_layer_found = false; 55 | 56 | for layer_property in layer_properties.iter() { 57 | let test_layer_name = super::tools::vk_to_string(&layer_property.layer_name); 58 | if (*required_layer_name) == test_layer_name { 59 | is_layer_found = true; 60 | break; 61 | } 62 | } 63 | 64 | if is_layer_found == false { 65 | return false; 66 | } 67 | } 68 | 69 | true 70 | } 71 | 72 | pub fn setup_debug_utils( 73 | is_enable_debug: bool, 74 | entry: &ash::Entry, 75 | instance: &ash::Instance, 76 | ) -> (ash::extensions::ext::DebugUtils, vk::DebugUtilsMessengerEXT) { 77 | let debug_utils_loader = ash::extensions::ext::DebugUtils::new(entry, instance); 78 | 79 | if is_enable_debug == false { 80 | (debug_utils_loader, ash::vk::DebugUtilsMessengerEXT::null()) 81 | } else { 82 | let messenger_ci = populate_debug_messenger_create_info(); 83 | 84 | let utils_messenger = unsafe { 85 | debug_utils_loader 86 | .create_debug_utils_messenger(&messenger_ci, None) 87 | .expect("Debug Utils Callback") 88 | }; 89 | 90 | (debug_utils_loader, utils_messenger) 91 | } 92 | } 93 | 94 | pub fn populate_debug_messenger_create_info() -> vk::DebugUtilsMessengerCreateInfoEXT { 95 | vk::DebugUtilsMessengerCreateInfoEXT { 96 | s_type: vk::StructureType::DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, 97 | p_next: ptr::null(), 98 | flags: vk::DebugUtilsMessengerCreateFlagsEXT::empty(), 99 | message_severity: vk::DebugUtilsMessageSeverityFlagsEXT::WARNING | 100 | // vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE | 101 | // vk::DebugUtilsMessageSeverityFlagsEXT::INFO | 102 | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR, 103 | message_type: vk::DebugUtilsMessageTypeFlagsEXT::GENERAL 104 | | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE 105 | | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION, 106 | pfn_user_callback: Some(vulkan_debug_utils_callback), 107 | p_user_data: ptr::null_mut(), 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/utility/fps_limiter.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::time::Duration; 3 | use std::time::Instant; 4 | 5 | const SAMPLE_COUNT: usize = 5; 6 | const SAMPLE_COUNT_FLOAT: f32 = SAMPLE_COUNT as f32; 7 | 8 | pub struct FPSLimiter { 9 | counter: Instant, 10 | frame_time_prefer: u32, // unit microseconds 11 | samples: [u32; SAMPLE_COUNT], 12 | current_frame: usize, 13 | delta_frame: u32, 14 | } 15 | 16 | impl FPSLimiter { 17 | pub fn new() -> FPSLimiter { 18 | const DEFAULT_PREFER_FPS: f32 = 60.0; 19 | 20 | FPSLimiter { 21 | counter: Instant::now(), 22 | frame_time_prefer: (1000_000.0_f32 / DEFAULT_PREFER_FPS) as u32, 23 | samples: [0; SAMPLE_COUNT], 24 | current_frame: 0, 25 | delta_frame: 0, 26 | } 27 | } 28 | 29 | pub fn set_prefer_fps(&mut self, prefer_fps: f32) { 30 | self.frame_time_prefer = (1000_000.0_f32 / prefer_fps) as u32; 31 | } 32 | 33 | /// Call this function in game loop to update its inner status. 34 | pub fn tick_frame(&mut self) { 35 | let time_elapsed = self.counter.elapsed(); 36 | self.counter = Instant::now(); 37 | 38 | self.delta_frame = time_elapsed.subsec_micros(); 39 | self.samples[self.current_frame] = self.delta_frame; 40 | self.current_frame = (self.current_frame + 1) % SAMPLE_COUNT; 41 | } 42 | 43 | // TODO: this function seems not work. 44 | pub fn keep_fps(&self) { 45 | if self.frame_time_prefer > self.delta_frame { 46 | let delay = Duration::from_micros((self.frame_time_prefer - self.delta_frame) as u64); 47 | 48 | thread::sleep(delay); 49 | } 50 | } 51 | 52 | /// Calculate the current FPS. 53 | pub fn fps(&self) -> f32 { 54 | let mut sum = 0_u32; 55 | self.samples.iter().for_each(|val| { 56 | sum += val; 57 | }); 58 | 59 | 1000_000.0_f32 / (sum as f32 / SAMPLE_COUNT_FLOAT) 60 | } 61 | 62 | /// Return current delta time in seconds 63 | /// this function ignore its second part, since the second is mostly zero. 64 | pub fn delta_time(&self) -> f32 { 65 | self.delta_frame as f32 / 1000_000.0_f32 // time in second 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/utility/mod.rs: -------------------------------------------------------------------------------- 1 | //! The utility mod define some fixed function using in this tutorial. 2 | //! Help to simplify the code. 3 | 4 | pub mod constants; 5 | pub mod debug; 6 | pub mod fps_limiter; 7 | pub mod platforms; 8 | pub mod share; 9 | pub mod structures; 10 | pub mod tools; 11 | pub mod window; 12 | -------------------------------------------------------------------------------- /src/utility/platforms.rs: -------------------------------------------------------------------------------- 1 | 2 | use ash::version::{EntryV1_0, InstanceV1_0}; 3 | use ash::vk; 4 | 5 | #[cfg(target_os = "windows")] 6 | use ash::extensions::khr::Win32Surface; 7 | #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] 8 | use ash::extensions::khr::XlibSurface; 9 | #[cfg(target_os = "macos")] 10 | use ash::extensions::mvk::MacOSSurface; 11 | 12 | use ash::extensions::ext::DebugUtils; 13 | use ash::extensions::khr::Surface; 14 | 15 | #[cfg(target_os = "macos")] 16 | use cocoa::appkit::{NSView, NSWindow}; 17 | #[cfg(target_os = "macos")] 18 | use cocoa::base::id as cocoa_id; 19 | #[cfg(target_os = "macos")] 20 | use metal::CoreAnimationLayer; 21 | #[cfg(target_os = "macos")] 22 | use objc::runtime::YES; 23 | 24 | // required extension ------------------------------------------------------ 25 | #[cfg(target_os = "macos")] 26 | pub fn required_extension_names() -> Vec<*const i8> { 27 | vec![ 28 | Surface::name().as_ptr(), 29 | MacOSSurface::name().as_ptr(), 30 | DebugUtils::name().as_ptr(), 31 | ] 32 | } 33 | 34 | #[cfg(all(windows))] 35 | pub fn required_extension_names() -> Vec<*const i8> { 36 | vec![ 37 | Surface::name().as_ptr(), 38 | Win32Surface::name().as_ptr(), 39 | DebugUtils::name().as_ptr(), 40 | ] 41 | } 42 | 43 | #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] 44 | pub fn required_extension_names() -> Vec<*const i8> { 45 | vec![ 46 | Surface::name().as_ptr(), 47 | XlibSurface::name().as_ptr(), 48 | DebugUtils::name().as_ptr(), 49 | ] 50 | } 51 | // ------------------------------------------------------------------------ 52 | 53 | // create surface --------------------------------------------------------- 54 | #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] 55 | pub unsafe fn create_surface( 56 | entry: &E, 57 | instance: &I, 58 | window: &winit::window::Window, 59 | ) -> Result { 60 | use std::ptr; 61 | use winit::platform::unix::WindowExtUnix; 62 | 63 | let x11_display = window.xlib_display().unwrap(); 64 | let x11_window = window.xlib_window().unwrap(); 65 | let x11_create_info = vk::XlibSurfaceCreateInfoKHR { 66 | s_type: vk::StructureType::XLIB_SURFACE_CREATE_INFO_KHR, 67 | p_next: ptr::null(), 68 | flags: Default::default(), 69 | window: x11_window as vk::Window, 70 | dpy: x11_display as *mut vk::Display, 71 | }; 72 | let xlib_surface_loader = XlibSurface::new(entry, instance); 73 | xlib_surface_loader.create_xlib_surface(&x11_create_info, None) 74 | } 75 | 76 | #[cfg(target_os = "macos")] 77 | pub unsafe fn create_surface( 78 | entry: &E, 79 | instance: &I, 80 | window: &winit::window::Window, 81 | ) -> Result { 82 | use std::mem; 83 | use std::os::raw::c_void; 84 | use std::ptr; 85 | use winit::platform::macos::WindowExtMacOS; 86 | 87 | let wnd: cocoa_id = mem::transmute(window.ns_window()); 88 | 89 | let layer = CoreAnimationLayer::new(); 90 | 91 | layer.set_edge_antialiasing_mask(0); 92 | layer.set_presents_with_transaction(false); 93 | layer.remove_all_animations(); 94 | 95 | let view = wnd.contentView(); 96 | 97 | layer.set_contents_scale(view.backingScaleFactor()); 98 | view.setLayer(mem::transmute(layer.as_ref())); 99 | view.setWantsLayer(YES); 100 | 101 | let create_info = vk::MacOSSurfaceCreateInfoMVK { 102 | s_type: vk::StructureType::MACOS_SURFACE_CREATE_INFO_M, 103 | p_next: ptr::null(), 104 | flags: Default::default(), 105 | p_view: window.ns_view() as *const c_void, 106 | }; 107 | 108 | let macos_surface_loader = MacOSSurface::new(entry, instance); 109 | macos_surface_loader.create_mac_os_surface_mvk(&create_info, None) 110 | } 111 | 112 | #[cfg(target_os = "windows")] 113 | pub unsafe fn create_surface( 114 | entry: &E, 115 | instance: &I, 116 | window: &winit::window::Window, 117 | ) -> Result { 118 | use std::os::raw::c_void; 119 | use std::ptr; 120 | use winapi::shared::windef::HWND; 121 | use winapi::um::libloaderapi::GetModuleHandleW; 122 | use winit::platform::windows::WindowExtWindows; 123 | 124 | let hwnd = window.hwnd() as HWND; 125 | let hinstance = GetModuleHandleW(ptr::null()) as *const c_void; 126 | let win32_create_info = vk::Win32SurfaceCreateInfoKHR { 127 | s_type: vk::StructureType::WIN32_SURFACE_CREATE_INFO_KHR, 128 | p_next: ptr::null(), 129 | flags: Default::default(), 130 | hinstance, 131 | hwnd: hwnd as *const c_void, 132 | }; 133 | let win32_surface_loader = Win32Surface::new(entry, instance); 134 | win32_surface_loader.create_win32_surface(&win32_create_info, None) 135 | } 136 | // ------------------------------------------------------------------------ 137 | -------------------------------------------------------------------------------- /src/utility/share/v2.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | 3 | use std::ptr; 4 | 5 | use super::*; 6 | 7 | pub fn create_descriptor_pool( 8 | device: &ash::Device, 9 | swapchain_images_size: usize, 10 | ) -> vk::DescriptorPool { 11 | let pool_sizes = [ 12 | vk::DescriptorPoolSize { 13 | // transform descriptor pool 14 | ty: vk::DescriptorType::UNIFORM_BUFFER, 15 | descriptor_count: swapchain_images_size as u32, 16 | }, 17 | vk::DescriptorPoolSize { 18 | // sampler descriptor pool 19 | ty: vk::DescriptorType::COMBINED_IMAGE_SAMPLER, 20 | descriptor_count: swapchain_images_size as u32, 21 | }, 22 | ]; 23 | 24 | let descriptor_pool_create_info = vk::DescriptorPoolCreateInfo { 25 | s_type: vk::StructureType::DESCRIPTOR_POOL_CREATE_INFO, 26 | p_next: ptr::null(), 27 | flags: vk::DescriptorPoolCreateFlags::empty(), 28 | max_sets: swapchain_images_size as u32, 29 | pool_size_count: pool_sizes.len() as u32, 30 | p_pool_sizes: pool_sizes.as_ptr(), 31 | }; 32 | 33 | unsafe { 34 | device 35 | .create_descriptor_pool(&descriptor_pool_create_info, None) 36 | .expect("Failed to create Descriptor Pool!") 37 | } 38 | } 39 | 40 | pub fn create_descriptor_sets( 41 | device: &ash::Device, 42 | descriptor_pool: vk::DescriptorPool, 43 | descriptor_set_layout: vk::DescriptorSetLayout, 44 | uniforms_buffers: &Vec, 45 | texture_image_view: vk::ImageView, 46 | texture_sampler: vk::Sampler, 47 | swapchain_images_size: usize, 48 | ) -> Vec { 49 | let mut layouts: Vec = vec![]; 50 | for _ in 0..swapchain_images_size { 51 | layouts.push(descriptor_set_layout); 52 | } 53 | 54 | let descriptor_set_allocate_info = vk::DescriptorSetAllocateInfo { 55 | s_type: vk::StructureType::DESCRIPTOR_SET_ALLOCATE_INFO, 56 | p_next: ptr::null(), 57 | descriptor_pool, 58 | descriptor_set_count: swapchain_images_size as u32, 59 | p_set_layouts: layouts.as_ptr(), 60 | }; 61 | 62 | let descriptor_sets = unsafe { 63 | device 64 | .allocate_descriptor_sets(&descriptor_set_allocate_info) 65 | .expect("Failed to allocate descriptor sets!") 66 | }; 67 | 68 | for (i, &descritptor_set) in descriptor_sets.iter().enumerate() { 69 | let descriptor_buffer_infos = [vk::DescriptorBufferInfo { 70 | buffer: uniforms_buffers[i], 71 | offset: 0, 72 | range: ::std::mem::size_of::() as u64, 73 | }]; 74 | 75 | let descriptor_image_infos = [vk::DescriptorImageInfo { 76 | sampler: texture_sampler, 77 | image_view: texture_image_view, 78 | image_layout: vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, 79 | }]; 80 | 81 | let descriptor_write_sets = [ 82 | vk::WriteDescriptorSet { 83 | // transform uniform 84 | s_type: vk::StructureType::WRITE_DESCRIPTOR_SET, 85 | p_next: ptr::null(), 86 | dst_set: descritptor_set, 87 | dst_binding: 0, 88 | dst_array_element: 0, 89 | descriptor_count: 1, 90 | descriptor_type: vk::DescriptorType::UNIFORM_BUFFER, 91 | p_image_info: ptr::null(), 92 | p_buffer_info: descriptor_buffer_infos.as_ptr(), 93 | p_texel_buffer_view: ptr::null(), 94 | }, 95 | vk::WriteDescriptorSet { 96 | // sampler uniform 97 | s_type: vk::StructureType::WRITE_DESCRIPTOR_SET, 98 | p_next: ptr::null(), 99 | dst_set: descritptor_set, 100 | dst_binding: 1, 101 | dst_array_element: 0, 102 | descriptor_count: 1, 103 | descriptor_type: vk::DescriptorType::COMBINED_IMAGE_SAMPLER, 104 | p_image_info: descriptor_image_infos.as_ptr(), 105 | p_buffer_info: ptr::null(), 106 | p_texel_buffer_view: ptr::null(), 107 | }, 108 | ]; 109 | 110 | unsafe { 111 | device.update_descriptor_sets(&descriptor_write_sets, &[]); 112 | } 113 | } 114 | 115 | descriptor_sets 116 | } 117 | 118 | pub fn create_descriptor_set_layout(device: &ash::Device) -> vk::DescriptorSetLayout { 119 | let ubo_layout_bindings = [ 120 | vk::DescriptorSetLayoutBinding { 121 | // transform uniform 122 | binding: 0, 123 | descriptor_type: vk::DescriptorType::UNIFORM_BUFFER, 124 | descriptor_count: 1, 125 | stage_flags: vk::ShaderStageFlags::VERTEX, 126 | p_immutable_samplers: ptr::null(), 127 | }, 128 | vk::DescriptorSetLayoutBinding { 129 | // sampler uniform 130 | binding: 1, 131 | descriptor_type: vk::DescriptorType::COMBINED_IMAGE_SAMPLER, 132 | descriptor_count: 1, 133 | stage_flags: vk::ShaderStageFlags::FRAGMENT, 134 | p_immutable_samplers: ptr::null(), 135 | }, 136 | ]; 137 | 138 | let ubo_layout_create_info = vk::DescriptorSetLayoutCreateInfo { 139 | s_type: vk::StructureType::DESCRIPTOR_SET_LAYOUT_CREATE_INFO, 140 | p_next: ptr::null(), 141 | flags: vk::DescriptorSetLayoutCreateFlags::empty(), 142 | binding_count: ubo_layout_bindings.len() as u32, 143 | p_bindings: ubo_layout_bindings.as_ptr(), 144 | }; 145 | 146 | unsafe { 147 | device 148 | .create_descriptor_set_layout(&ubo_layout_create_info, None) 149 | .expect("Failed to create Descriptor Set Layout!") 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/utility/structures.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | use cgmath::Matrix4; 3 | 4 | use memoffset::offset_of; 5 | 6 | pub struct DeviceExtension { 7 | pub names: [&'static str; 1], 8 | // pub raw_names: [*const i8; 1], 9 | } 10 | 11 | pub struct SurfaceStuff { 12 | pub surface_loader: ash::extensions::khr::Surface, 13 | pub surface: vk::SurfaceKHR, 14 | 15 | pub screen_width: u32, 16 | pub screen_height: u32, 17 | } 18 | pub struct SwapChainStuff { 19 | pub swapchain_loader: ash::extensions::khr::Swapchain, 20 | pub swapchain: vk::SwapchainKHR, 21 | pub swapchain_images: Vec, 22 | pub swapchain_format: vk::Format, 23 | pub swapchain_extent: vk::Extent2D, 24 | } 25 | 26 | pub struct SwapChainSupportDetail { 27 | pub capabilities: vk::SurfaceCapabilitiesKHR, 28 | pub formats: Vec, 29 | pub present_modes: Vec, 30 | } 31 | 32 | pub struct QueueFamilyIndices { 33 | pub graphics_family: Option, 34 | pub present_family: Option, 35 | } 36 | 37 | impl QueueFamilyIndices { 38 | pub fn new() -> QueueFamilyIndices { 39 | QueueFamilyIndices { 40 | graphics_family: None, 41 | present_family: None, 42 | } 43 | } 44 | 45 | pub fn is_complete(&self) -> bool { 46 | self.graphics_family.is_some() && self.present_family.is_some() 47 | } 48 | } 49 | 50 | pub struct SyncObjects { 51 | pub image_available_semaphores: Vec, 52 | pub render_finished_semaphores: Vec, 53 | pub inflight_fences: Vec, 54 | } 55 | 56 | #[repr(C)] 57 | #[derive(Clone, Debug, Copy)] 58 | pub struct UniformBufferObject { 59 | pub model: Matrix4, 60 | pub view: Matrix4, 61 | pub proj: Matrix4, 62 | } 63 | 64 | #[repr(C)] 65 | #[derive(Clone, Debug, Copy)] 66 | pub struct VertexV1 { 67 | pub pos: [f32; 2], 68 | pub color: [f32; 3], 69 | } 70 | impl VertexV1 { 71 | pub fn get_binding_description() -> [vk::VertexInputBindingDescription; 1] { 72 | [vk::VertexInputBindingDescription { 73 | binding: 0, 74 | stride: ::std::mem::size_of::() as u32, 75 | input_rate: vk::VertexInputRate::VERTEX, 76 | }] 77 | } 78 | 79 | pub fn get_attribute_descriptions() -> [vk::VertexInputAttributeDescription; 2] { 80 | [ 81 | vk::VertexInputAttributeDescription { 82 | binding: 0, 83 | location: 0, 84 | format: vk::Format::R32G32_SFLOAT, 85 | offset: offset_of!(VertexV1, pos) as u32, 86 | }, 87 | vk::VertexInputAttributeDescription { 88 | binding: 0, 89 | location: 1, 90 | format: vk::Format::R32G32B32_SFLOAT, 91 | offset: offset_of!(VertexV1, color) as u32, 92 | }, 93 | ] 94 | } 95 | } 96 | 97 | #[repr(C)] 98 | #[derive(Debug, Clone, Copy)] 99 | pub struct VertexV3 { 100 | pub pos: [f32; 4], 101 | pub color: [f32; 4], 102 | pub tex_coord: [f32; 2], 103 | } 104 | impl VertexV3 { 105 | pub fn get_binding_descriptions() -> [vk::VertexInputBindingDescription; 1] { 106 | [vk::VertexInputBindingDescription { 107 | binding: 0, 108 | stride: ::std::mem::size_of::() as u32, 109 | input_rate: vk::VertexInputRate::VERTEX, 110 | }] 111 | } 112 | 113 | pub fn get_attribute_descriptions() -> [vk::VertexInputAttributeDescription; 3] { 114 | [ 115 | vk::VertexInputAttributeDescription { 116 | binding: 0, 117 | location: 0, 118 | format: vk::Format::R32G32B32A32_SFLOAT, 119 | offset: offset_of!(Self, pos) as u32, 120 | }, 121 | vk::VertexInputAttributeDescription { 122 | binding: 0, 123 | location: 1, 124 | format: vk::Format::R32G32B32A32_SFLOAT, 125 | offset: offset_of!(Self, color) as u32, 126 | }, 127 | vk::VertexInputAttributeDescription { 128 | binding: 0, 129 | location: 2, 130 | format: vk::Format::R32G32_SFLOAT, 131 | offset: offset_of!(Self, tex_coord) as u32, 132 | }, 133 | ] 134 | } 135 | } 136 | 137 | pub const RECT_VERTICES_DATA: [VertexV1; 4] = [ 138 | VertexV1 { 139 | pos: [-0.5, -0.5], 140 | color: [1.0, 0.0, 0.0], 141 | }, 142 | VertexV1 { 143 | pos: [0.5, -0.5], 144 | color: [0.0, 1.0, 0.0], 145 | }, 146 | VertexV1 { 147 | pos: [0.5, 0.5], 148 | color: [0.0, 0.0, 1.0], 149 | }, 150 | VertexV1 { 151 | pos: [-0.5, 0.5], 152 | color: [1.0, 1.0, 1.0], 153 | }, 154 | ]; 155 | pub const RECT_INDICES_DATA: [u32; 6] = [0, 1, 2, 2, 3, 0]; 156 | -------------------------------------------------------------------------------- /src/utility/tools.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::os::raw::c_char; 3 | use std::path::Path; 4 | 5 | /// Helper function to convert [c_char; SIZE] to string 6 | pub fn vk_to_string(raw_string_array: &[c_char]) -> String { 7 | // Implementation 1 8 | // let end = '\0' as u8; 9 | // 10 | // let mut content: Vec = vec![]; 11 | // 12 | // for ch in raw_string_array.iter() { 13 | // let ch = (*ch) as u8; 14 | // 15 | // if ch != end { 16 | // content.push(ch); 17 | // } else { 18 | // break 19 | // } 20 | // } 21 | // 22 | // String::from_utf8(content) 23 | // .expect("Failed to convert vulkan raw string") 24 | 25 | // Implementation 2 26 | let raw_string = unsafe { 27 | let pointer = raw_string_array.as_ptr(); 28 | CStr::from_ptr(pointer) 29 | }; 30 | 31 | raw_string 32 | .to_str() 33 | .expect("Failed to convert vulkan raw string.") 34 | .to_owned() 35 | } 36 | 37 | pub fn read_shader_code(shader_path: &Path) -> Vec { 38 | use std::fs::File; 39 | use std::io::Read; 40 | 41 | let spv_file = 42 | File::open(shader_path).expect(&format!("Failed to find spv file at {:?}", shader_path)); 43 | let bytes_code: Vec = spv_file.bytes().filter_map(|byte| byte.ok()).collect(); 44 | 45 | bytes_code 46 | } 47 | -------------------------------------------------------------------------------- /src/utility/window.rs: -------------------------------------------------------------------------------- 1 | 2 | use winit::event::{Event, VirtualKeyCode, ElementState, KeyboardInput, WindowEvent}; 3 | use winit::event_loop::{EventLoop, ControlFlow}; 4 | 5 | 6 | const IS_PAINT_FPS_COUNTER: bool = true; 7 | 8 | pub fn init_window( 9 | event_loop: &EventLoop<()>, 10 | title: &str, 11 | width: u32, 12 | height: u32, 13 | ) -> winit::window::Window { 14 | winit::window::WindowBuilder::new() 15 | .with_title(title) 16 | .with_inner_size(winit::dpi::LogicalSize::new(width, height)) 17 | .build(event_loop) 18 | .expect("Failed to create window.") 19 | } 20 | 21 | pub trait VulkanApp { 22 | fn draw_frame(&mut self, delta_time: f32); 23 | fn recreate_swapchain(&mut self); 24 | fn cleanup_swapchain(&self); 25 | fn wait_device_idle(&self); 26 | fn resize_framebuffer(&mut self); 27 | fn window_ref(&self) -> &winit::window::Window; 28 | } 29 | 30 | pub struct ProgramProc { 31 | pub event_loop: EventLoop<()>, 32 | } 33 | 34 | impl ProgramProc { 35 | 36 | pub fn new() -> ProgramProc { 37 | // init window stuff 38 | let event_loop = EventLoop::new(); 39 | 40 | ProgramProc { event_loop } 41 | } 42 | 43 | pub fn main_loop(self, mut vulkan_app: A) { 44 | 45 | let mut tick_counter = super::fps_limiter::FPSLimiter::new(); 46 | 47 | self.event_loop.run(move |event, _, control_flow| { 48 | 49 | match event { 50 | | Event::WindowEvent { event, .. } => { 51 | match event { 52 | | WindowEvent::CloseRequested => { 53 | vulkan_app.wait_device_idle(); 54 | *control_flow = ControlFlow::Exit 55 | }, 56 | | WindowEvent::KeyboardInput { input, .. } => { 57 | match input { 58 | | KeyboardInput { virtual_keycode, state, .. } => { 59 | match (virtual_keycode, state) { 60 | | (Some(VirtualKeyCode::Escape), ElementState::Pressed) => { 61 | vulkan_app.wait_device_idle(); 62 | *control_flow = ControlFlow::Exit 63 | }, 64 | | _ => {}, 65 | } 66 | }, 67 | } 68 | }, 69 | | WindowEvent::Resized(_new_size) => { 70 | vulkan_app.wait_device_idle(); 71 | vulkan_app.resize_framebuffer(); 72 | }, 73 | | _ => {}, 74 | } 75 | }, 76 | | Event::MainEventsCleared => { 77 | vulkan_app.window_ref().request_redraw(); 78 | }, 79 | | Event::RedrawRequested(_window_id) => { 80 | let delta_time = tick_counter.delta_time(); 81 | vulkan_app.draw_frame(delta_time); 82 | 83 | if IS_PAINT_FPS_COUNTER { 84 | print!("FPS: {}\r", tick_counter.fps()); 85 | } 86 | 87 | tick_counter.tick_frame(); 88 | }, 89 | | Event::LoopDestroyed => { 90 | vulkan_app.wait_device_idle(); 91 | }, 92 | _ => (), 93 | } 94 | 95 | }) 96 | } 97 | 98 | } 99 | --------------------------------------------------------------------------------