├── .github └── main.workflow ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── mac-env.sh └── src ├── bin ├── 00_base_code.rs ├── 01_instance_creation.rs ├── 01_instance_creation.rs.diff ├── 02_validation_layers.rs ├── 02_validation_layers.rs.diff ├── 03_physical_device_selection.rs ├── 03_physical_device_selection.rs.diff ├── 04_logical_device.rs ├── 04_logical_device.rs.diff ├── 05_window_surface.rs ├── 05_window_surface.rs.diff ├── 06_swap_chain_creation.rs ├── 06_swap_chain_creation.rs.diff ├── 08_graphics_pipeline.rs ├── 08_graphics_pipeline.rs.diff ├── 09_shader_base.frag ├── 09_shader_base.vert ├── 09_shader_modules.rs ├── 09_shader_modules.rs.diff ├── 10_fixed_functions.rs ├── 10_fixed_functions.rs.diff ├── 11_render_passes.rs ├── 11_render_passes.rs.diff ├── 12_graphics_pipeline_complete.rs ├── 12_graphics_pipeline_complete.rs.diff ├── 13_framebuffers.rs ├── 13_framebuffers.rs.diff ├── 14_command_buffers.rs ├── 14_command_buffers.rs.diff ├── 15_hello_triangle.rs ├── 15_hello_triangle.rs.diff ├── 16_swap_chain_recreation.rs ├── 16_swap_chain_recreation.rs.diff ├── 17_shader_vertexbuffer.frag ├── 17_shader_vertexbuffer.vert ├── 17_shader_vertexbuffer.vert.diff ├── 18_vertex_buffer.rs ├── 18_vertex_buffer.rs.diff ├── 19_staging_buffer.rs ├── 19_staging_buffer.rs.diff ├── 20_index_buffer.rs ├── 20_index_buffer.rs.diff ├── 21_descriptor_layout_and_buffer.rs ├── 21_descriptor_layout_and_buffer.rs.diff ├── 21_shader_uniformbuffer.frag ├── 21_shader_uniformbuffer.vert ├── 21_shader_uniformbuffer.vert.diff └── diff.sh ├── main.rs └── shaders ├── compile.bat ├── compile.sh ├── shader.frag └── shader.vert /.github/main.workflow: -------------------------------------------------------------------------------- 1 | workflow "CI" { 2 | on = "push" 3 | resolves = ["Build & Lint"] 4 | } 5 | 6 | action "Build & Lint" { 7 | uses = "bwasty/rust-action@master" 8 | args = "cargo build && cargo clippy -- -D warnings -A clippy::ref_in_deref" 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | *.spv 4 | .idea/ -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vulkan-tutorial-rs" 3 | version = "0.1.0" 4 | authors = ["Benjamin Wasty "] 5 | autobins = true 6 | 7 | [dependencies] 8 | vulkano = "0.11.1" 9 | vulkano-shaders = "0.11.1" 10 | image = "0.20.1" 11 | vulkano-win = "0.11.1" 12 | winit = "0.18.0" 13 | cgmath = "0.17.0" 14 | 15 | # [[bin]] 16 | # name = "main" 17 | # path = "src/bin/00_base_code.rs" 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vulkan-tutorial-rs 2 | Rust version of https://github.com/Overv/VulkanTutorial using [Vulkano](http://vulkano.rs/). 3 | 4 | **Goal**: Rust port with code structure as similar as possible to the original C++, so the original tutorial can easily be followed (similar to [learn-opengl-rs](https://github.com/bwasty/learn-opengl-rs)). 5 | 6 | **Current State**: The chapters `Drawing a triangle` and `Vertex buffers` are complete. 7 | 8 | --- 9 | * [Introduction](#introduction) 10 | * [Overview](#overview) 11 | * [Development Environment](#development-environment) 12 | * [Drawing a triangle](#drawing-a-triangle) 13 | * [Setup](#setup) 14 | * [Base code](#base-code) 15 | * [General structure](#general-structure) 16 | * [Resource management](#resource-management) 17 | * [Integrating GLFW winit](#integrating-glfw-winit) 18 | * [Instance](#instance) 19 | * [Validation layers](#validation-layers) 20 | * [Physical devices and queue families](#physical-devices-and-queue-families) 21 | * [Logical device and queues](#logical-device-and-queues) 22 | * [Presentation](#presentation) 23 | * [Window surface](#window-surface) 24 | * [Swap chain](#swap-chain) 25 | * [Image views](#image-views) 26 | * [Graphics pipeline basics](#graphics-pipeline-basics) 27 | * [Introduction](#introduction-1) 28 | * [Shader Modules](#shader-modules) 29 | * [Fixed functions](#fixed-functions) 30 | * [Render passes](#render-passes) 31 | * [Conclusion](#conclusion) 32 | * [Drawing](#drawing) 33 | * [Framebuffers](#framebuffers) 34 | * [Command buffers](#command-buffers) 35 | * [Rendering and presentation](#rendering-and-presentation) 36 | * [Swapchain recreation](#swapchain-recreation) 37 | * [Vertex buffers](#vertex-buffers) 38 | * [Vertex input description](#vertex-input-description) 39 | * [Vertex buffer creation](#vertex-buffer-creation) 40 | * [Staging buffer](#staging-buffer) 41 | * [Index buffer](#index-buffer) 42 | * [Uniform buffers](#uniform-buffers) 43 | * [Texture mapping (TODO)](#texture-mapping-todo) 44 | * [Depth buffering (TODO)](#depth-buffering-todo) 45 | * [Loading models (TODO)](#loading-models-todo) 46 | * [Generating Mipmaps (TODO)](#generating-mipmaps-todo) 47 | * [Multisampling (TODO)](#multisampling-todo) 48 | 49 | ## Introduction 50 | This tutorial consists of the the ported code and notes about the differences between the original C++ and the Rust code. 51 | The [explanatory texts](https://vulkan-tutorial.com/Introduction) generally apply equally, although the Rust version is often shorter due to the use of [Vulkano](http://vulkano.rs/), a safe wrapper around the Vulkan API with some convenience functionality (the final triangle example is about 600 lines, compared to 950 lines in C++). 52 | 53 | If you prefer a lower-level API closer to the Vulkan C API, have a look at [Ash](https://github.com/MaikKlein/ash) and [vulkan-tutorial-rust](https://github.com/Usami-Renko/vulkan-tutorial-rust). 54 | 55 | ## Overview 56 | https://vulkan-tutorial.com/Overview 57 | 58 | (nothing to note here) 59 | 60 | ## Development Environment 61 | https://vulkan-tutorial.com/Development_environment 62 | 63 | Download the Vulkan SDK as described, but ignore everything about library and project setup. Instead, create a new Cargo project: 64 | ``` 65 | $ cargo new vulkan-tutorial-rs 66 | ``` 67 | Then add this to your `Cargo.toml`: 68 | ``` 69 | [dependencies] 70 | vulkano = "0.11.1" 71 | ``` 72 | 73 | On macOS, copy [mac-env.sh](mac-env.sh), adapt the `VULKAN_SDK` path if necessary and `source` the file in your terminal. See also [vulkano-rs/vulkano#macos-and-ios-specific-setup](https://github.com/vulkano-rs/vulkano#macos-and-ios-specific-setup). 74 | 75 | ## Drawing a triangle 76 | ### Setup 77 | #### Base code 78 | https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Base_code 79 | ##### General structure 80 | ```rust 81 | extern crate vulkano; 82 | 83 | struct HelloTriangleApplication { 84 | 85 | } 86 | 87 | impl HelloTriangleApplication { 88 | pub fn initialize() -> Self { 89 | Self { 90 | 91 | } 92 | } 93 | 94 | fn main_loop(&mut self) { 95 | 96 | } 97 | } 98 | 99 | fn main() { 100 | let mut app = HelloTriangleApplication::initialize(); 101 | app.main_loop(); 102 | } 103 | ``` 104 | 105 | ##### Resource management 106 | Vulkano handles calling `vkDestroyXXX`/`vkFreeXXX` in the `Drop` implementation of all wrapper objects, so we will skip all cleanup code. 107 | 108 | ##### Integrating ~GLFW~ winit 109 | Instead of GLFW we'll be using [winit](https://github.com/tomaka/winit), an alternative window managment library written in pure Rust. 110 | 111 | Add this to your Cargo.toml: 112 | ``` 113 | winit = "0.18.0" 114 | ``` 115 | And extend your main.rs: 116 | ```rust 117 | extern crate winit; 118 | 119 | use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize}; 120 | 121 | const WIDTH: u32 = 800; 122 | const HEIGHT: u32 = 600; 123 | 124 | struct HelloTriangleApplication { 125 | events_loop: EventsLoop, 126 | } 127 | ``` 128 | ```rust 129 | pub fn initialize() -> Self { 130 | let events_loop = Self::init_window(); 131 | 132 | Self { 133 | events_loop, 134 | } 135 | } 136 | 137 | fn init_window() -> EventsLoop { 138 | let events_loop = EventsLoop::new(); 139 | let _window = WindowBuilder::new() 140 | .with_title("Vulkan") 141 | .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) 142 | .build(&events_loop); 143 | events_loop 144 | } 145 | ``` 146 | ```rust 147 | fn main_loop(&mut self) { 148 | loop { 149 | let mut done = false; 150 | self.events_loop.poll_events(|ev| { 151 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 152 | done = true 153 | } 154 | }); 155 | if done { 156 | return; 157 | } 158 | } 159 | } 160 | ``` 161 | 162 | [Complete code](src/bin/00_base_code.rs) 163 | 164 | 165 | #### Instance 166 | https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Instance 167 | 168 | Cargo.toml: 169 | ``` 170 | vulkano-win = "0.11.1" 171 | ``` 172 | main.rs: 173 | ```rust 174 | extern crate vulkano_win; 175 | ``` 176 | ```rust 177 | use std::sync::Arc; 178 | use vulkano::instance::{ 179 | Instance, 180 | InstanceExtensions, 181 | ApplicationInfo, 182 | Version, 183 | }; 184 | ``` 185 | ```rust 186 | struct HelloTriangleApplication { 187 | instance: Option>, 188 | events_loop: EventsLoop, 189 | } 190 | ``` 191 | ```rust 192 | pub fn initialize() -> Self { 193 | let instance = Self::create_instance(); 194 | let events_loop = Self::init_window(); 195 | 196 | Self { 197 | instance, 198 | events_loop, 199 | } 200 | } 201 | ``` 202 | ```rust 203 | fn create_instance() -> Arc { 204 | let supported_extensions = InstanceExtensions::supported_by_core() 205 | .expect("failed to retrieve supported extensions"); 206 | println!("Supported extensions: {:?}", supported_extensions); 207 | 208 | let app_info = ApplicationInfo { 209 | application_name: Some("Hello Triangle".into()), 210 | application_version: Some(Version { major: 1, minor: 0, patch: 0 }), 211 | engine_name: Some("No Engine".into()), 212 | engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 213 | }; 214 | 215 | let required_extensions = vulkano_win::required_extensions(); 216 | Instance::new(Some(&app_info), &required_extensions, None) 217 | .expect("failed to create Vulkan instance") 218 | } 219 | ``` 220 | 221 | [Diff](src/bin/01_instance_creation.rs.diff) / [Complete code](src/bin/01_instance_creation.rs) 222 | 223 | #### Validation layers 224 | https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Validation_layers 225 | 226 | From here on we'll just link to the code instead of putting everything in the README: 227 | 228 | [Diff](src/bin/02_validation_layers.rs.diff) / [Complete code](src/bin/02_validation_layers.rs) 229 | 230 | 231 | #### Physical devices and queue families 232 | https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Physical_devices_and_queue_families 233 | 234 | [Diff](src/bin/03_physical_device_selection.rs.diff) / [Complete code](src/bin/03_physical_device_selection.rs) 235 | 236 | 237 | #### Logical device and queues 238 | https://vulkan-tutorial.com/Drawing_a_triangle/Setup/Logical_device_and_queues 239 | 240 | [Diff](src/bin/04_logical_device.rs.diff) / [Complete code](src/bin/04_logical_device.rs) 241 | 242 | ### Presentation 243 | 244 | #### Window surface 245 | https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Window_surface 246 | 247 | [Diff](src/bin/05_window_surface.rs.diff) / [Complete code](src/bin/05_window_surface.rs) 248 | 249 | #### Swap chain 250 | https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Swap_chain 251 | 252 | [Diff](src/bin/06_swap_chain_creation.rs.diff) / [Complete code](src/bin/06_swap_chain_creation.rs) 253 | 254 | #### Image views 255 | https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Image_views 256 | 257 | We're skipping this section because image views are handled by Vulkano and can be accessed via the `SwapchainImage`s created in the last section. 258 | 259 | ### Graphics pipeline basics 260 | #### Introduction 261 | https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics 262 | 263 | [Diff](src/bin/08_graphics_pipeline.rs.diff) / [Complete code](src/bin/08_graphics_pipeline.rs) 264 | 265 | #### Shader Modules 266 | https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Shader_modules 267 | 268 | Instead of compiling the shaders to SPIR-V manually and loading them at runtime, we'll use [vulkano_shaders](https://docs.rs/vulkano-shaders/0.11.1/vulkano_shaders) to do the same at compile-time. Loading them at runtime is also possible, but a bit more invovled - see the [runtime shader](https://github.com/vulkano-rs/vulkano/blob/master/examples/src/bin/runtime-shader/main.rs) example of Vulkano. 269 | 270 | [Diff](src/bin/09_shader_modules.rs.diff) / [Rust code](src/bin/09_shader_modules.rs) / [Vertex shader](src/bin/09_shader_base.vert) / [Fragment shader](src/bin/09_shader_base.frag) 271 | 272 | #### Fixed functions 273 | https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Fixed_functions 274 | 275 | [Diff](src/bin/10_fixed_functions.rs.diff) / [Complete code](src/bin/10_fixed_functions.rs) 276 | 277 | #### Render passes 278 | https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Render_passes 279 | 280 | [Diff](src/bin/11_render_passes.rs.diff) / [Complete code](src/bin/11_render_passes.rs) 281 | 282 | #### Conclusion 283 | https://vulkan-tutorial.com/Drawing_a_triangle/Graphics_pipeline_basics/Conclusion 284 | 285 | [Diff](src/bin/12_graphics_pipeline_complete.rs.diff) / [Complete code](src/bin/12_graphics_pipeline_complete.rs) 286 | 287 | 288 | ### Drawing 289 | #### Framebuffers 290 | https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Framebuffers 291 | 292 | [Diff](src/bin/13_framebuffers.rs.diff) / [Complete code](src/bin/13_framebuffers.rs) 293 | 294 | #### Command buffers 295 | https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Command_buffers 296 | 297 | We're skipping the first part because Vulkano maintains a [`StandardCommandPool`](https://docs.rs/vulkano/0.10.0/vulkano/command_buffer/pool/standard/struct.StandardCommandPool.html). 298 | 299 | [Diff](src/bin/14_command_buffers.rs.diff) / [Complete code](src/bin/14_command_buffers.rs) 300 | 301 | #### Rendering and presentation 302 | https://vulkan-tutorial.com/Drawing_a_triangle/Drawing/Rendering_and_presentation 303 | 304 | [Diff](src/bin/15_hello_triangle.rs.diff) / [Complete code](src/bin/15_hello_triangle.rs) 305 | 306 | ### Swapchain recreation 307 | https://vulkan-tutorial.com/Drawing_a_triangle/Swap_chain_recreation 308 | 309 | [Diff](src/bin/16_swap_chain_recreation.rs.diff) / [Complete code](src/bin/16_swap_chain_recreation.rs) 310 | 311 | ## Vertex buffers 312 | ### Vertex input description 313 | https://vulkan-tutorial.com/Vertex_buffers/Vertex_input_description 314 | 315 | [Vertex shader diff](src/bin/17_shader_vertexbuffer.vert.diff) / [Vertex shader](src/bin/17_shader_vertexbuffer.vert) 316 | 317 | (Rust code combined with next section, since this alone won't compile) 318 | 319 | ### Vertex buffer creation 320 | https://vulkan-tutorial.com/Vertex_buffers/Vertex_buffer_creation 321 | 322 | [Diff](src/bin/18_vertex_buffer.rs.diff) / [Complete code](src/bin/18_vertex_buffer.rs) 323 | 324 | ### Staging buffer 325 | https://vulkan-tutorial.com/Vertex_buffers/Staging_buffer 326 | 327 | We're just replacing `CpuAccessibleBuffer` with `ImmutableBuffer`, which uses a staging buffer internally. See [`vulkano::buffer`](https://docs.rs/vulkano/0.10.0/vulkano/buffer/index.html) for an overview of Vulkano's buffer types. 328 | 329 | [Diff](src/bin/19_staging_buffer.rs.diff) / [Complete code](src/bin/19_staging_buffer.rs) 330 | 331 | ### Index buffer 332 | https://vulkan-tutorial.com/Vertex_buffers/Index_buffer 333 | 334 | [Diff](src/bin/20_index_buffer.rs.diff) / [Complete code](src/bin/20_index_buffer.rs) 335 | 336 | ## Uniform buffers 337 | ### Uniform Buffer Object 338 | https://vulkan-tutorial.com/Uniform_buffers 339 | 340 | In this section we change the vertex shader to take a uniform buffer object consisting of a model, view, and projection matrix. 341 | The shader now outputs the final position as the result of multiplying these three matrices with the original vertex position. 342 | 343 | We add a new type of buffer, the CpuAccessibleBuffer, which allows us to update its contents without needing to rebuild 344 | the entire buffer. In order to actually be able to write to this buffer we need to specify its usage as a uniform buffer and 345 | also the destination of a memory transfer. 346 | 347 | Note that unlike the original tutorial we did **not** need to create any layout binding. This is handled internally by vulkano when creating 348 | a descriptor set, as we'll see in the next section. 349 | 350 | At this point our program will compile and run but immediately panic because we specify a binding in our shader but do not 351 | include a matching descriptor set. 352 | 353 | [Vertex Shader Diff](src/bin/21_shader_uniformbuffer.vert.diff) / [Vertex Shader](src/bin/21_shader_uniformbuffer.vert) 354 | 355 | [Diff](src/bin/21_descriptor_layout_and_buffer.rs.diff) / [Complete code](src/bin/21_descriptor_layout_and_buffer.rs) 356 | ## Texture mapping (*TODO*) 357 | ## Depth buffering (*TODO*) 358 | ## Loading models (*TODO*) 359 | ## Generating Mipmaps (*TODO*) 360 | ## Multisampling (*TODO*) 361 | -------------------------------------------------------------------------------- /mac-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export VULKAN_SDK=$HOME/vulkansdk-macos-1.1.92.1/macOS 3 | export PATH=$VULKAN_SDK/bin:$PATH 4 | export DYLD_LIBRARY_PATH=$VULKAN_SDK/lib:$DYLD_LIBRARY_PATH 5 | export VK_ICD_FILENAMES=$VULKAN_SDK/etc/vulkan/icd.d/MoltenVK_icd.json 6 | export VK_LAYER_PATH=$VULKAN_SDK/etc/vulkan/explicit_layer.d 7 | -------------------------------------------------------------------------------- /src/bin/00_base_code.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | extern crate winit; 3 | 4 | use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; 5 | 6 | const WIDTH: u32 = 800; 7 | const HEIGHT: u32 = 600; 8 | 9 | #[allow(unused)] 10 | struct HelloTriangleApplication { 11 | events_loop: EventsLoop, 12 | } 13 | 14 | impl HelloTriangleApplication { 15 | pub fn initialize() -> Self { 16 | let events_loop = Self::init_window(); 17 | 18 | Self { 19 | events_loop, 20 | } 21 | } 22 | 23 | fn init_window() -> EventsLoop { 24 | let events_loop = EventsLoop::new(); 25 | let _window = WindowBuilder::new() 26 | .with_title("Vulkan") 27 | .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) 28 | .build(&events_loop); 29 | events_loop 30 | } 31 | 32 | fn main_loop(&mut self) { 33 | loop { 34 | let mut done = false; 35 | self.events_loop.poll_events(|ev| { 36 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 37 | done = true 38 | } 39 | }); 40 | if done { 41 | return; 42 | } 43 | } 44 | } 45 | } 46 | 47 | fn main() { 48 | let mut app = HelloTriangleApplication::initialize(); 49 | app.main_loop(); 50 | } 51 | -------------------------------------------------------------------------------- /src/bin/01_instance_creation.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | extern crate vulkano_win; 3 | extern crate winit; 4 | 5 | use std::sync::Arc; 6 | 7 | use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; 8 | 9 | use vulkano::instance::{ 10 | Instance, 11 | InstanceExtensions, 12 | ApplicationInfo, 13 | Version, 14 | }; 15 | 16 | const WIDTH: u32 = 800; 17 | const HEIGHT: u32 = 600; 18 | 19 | #[allow(unused)] 20 | struct HelloTriangleApplication { 21 | instance: Arc, 22 | events_loop: EventsLoop, 23 | } 24 | 25 | impl HelloTriangleApplication { 26 | pub fn initialize() -> Self { 27 | let instance = Self::create_instance(); 28 | let events_loop = Self::init_window(); 29 | 30 | Self { 31 | instance, 32 | events_loop, 33 | } 34 | } 35 | 36 | fn init_window() -> EventsLoop { 37 | let events_loop = EventsLoop::new(); 38 | let _window_builder = WindowBuilder::new() 39 | .with_title("Vulkan") 40 | .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))); 41 | // .build(&self.events_loop.as_ref().unwrap()); 42 | events_loop 43 | } 44 | 45 | fn create_instance() -> Arc { 46 | let supported_extensions = InstanceExtensions::supported_by_core() 47 | .expect("failed to retrieve supported extensions"); 48 | println!("Supported extensions: {:?}", supported_extensions); 49 | 50 | let app_info = ApplicationInfo { 51 | application_name: Some("Hello Triangle".into()), 52 | application_version: Some(Version { major: 1, minor: 0, patch: 0 }), 53 | engine_name: Some("No Engine".into()), 54 | engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 55 | }; 56 | 57 | let required_extensions = vulkano_win::required_extensions(); 58 | Instance::new(Some(&app_info), &required_extensions, None) 59 | .expect("failed to create Vulkan instance") 60 | } 61 | 62 | #[allow(unused)] 63 | fn main_loop(&mut self) { 64 | loop { 65 | let mut done = false; 66 | self.events_loop.poll_events(|ev| { 67 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 68 | done = true 69 | } 70 | }); 71 | if done { 72 | return; 73 | } 74 | } 75 | } 76 | } 77 | 78 | fn main() { 79 | let mut _app = HelloTriangleApplication::initialize(); 80 | // app.main_loop(); 81 | } 82 | -------------------------------------------------------------------------------- /src/bin/01_instance_creation.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/00_base_code.rs 2 | +++ b/01_instance_creation.rs 3 | @@ -1,34 +1,65 @@ 4 | extern crate vulkano; 5 | +extern crate vulkano_win; 6 | extern crate winit; 7 | 8 | +use std::sync::Arc; 9 | + 10 | use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; 11 | 12 | +use vulkano::instance::{ 13 | + Instance, 14 | + InstanceExtensions, 15 | + ApplicationInfo, 16 | + Version, 17 | +}; 18 | + 19 | const WIDTH: u32 = 800; 20 | const HEIGHT: u32 = 600; 21 | 22 | #[allow(unused)] 23 | struct HelloTriangleApplication { 24 | + instance: Arc, 25 | events_loop: EventsLoop, 26 | } 27 | 28 | impl HelloTriangleApplication { 29 | pub fn initialize() -> Self { 30 | + let instance = Self::create_instance(); 31 | let events_loop = Self::init_window(); 32 | 33 | Self { 34 | + instance, 35 | events_loop, 36 | } 37 | } 38 | 39 | fn init_window() -> EventsLoop { 40 | let events_loop = EventsLoop::new(); 41 | - let _window = WindowBuilder::new() 42 | + let _window_builder = WindowBuilder::new() 43 | .with_title("Vulkan") 44 | - .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) 45 | - .build(&events_loop); 46 | + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))); 47 | + // .build(&self.events_loop.as_ref().unwrap()); 48 | events_loop 49 | } 50 | 51 | + fn create_instance() -> Arc { 52 | + let supported_extensions = InstanceExtensions::supported_by_core() 53 | + .expect("failed to retrieve supported extensions"); 54 | + println!("Supported extensions: {:?}", supported_extensions); 55 | + 56 | + let app_info = ApplicationInfo { 57 | + application_name: Some("Hello Triangle".into()), 58 | + application_version: Some(Version { major: 1, minor: 0, patch: 0 }), 59 | + engine_name: Some("No Engine".into()), 60 | + engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 61 | + }; 62 | + 63 | + let required_extensions = vulkano_win::required_extensions(); 64 | + Instance::new(Some(&app_info), &required_extensions, None) 65 | + .expect("failed to create Vulkan instance") 66 | + } 67 | + 68 | + #[allow(unused)] 69 | fn main_loop(&mut self) { 70 | loop { 71 | let mut done = false; 72 | @@ -45,6 +76,6 @@ impl HelloTriangleApplication { 73 | } 74 | 75 | fn main() { 76 | - let mut app = HelloTriangleApplication::initialize(); 77 | - app.main_loop(); 78 | + let mut _app = HelloTriangleApplication::initialize(); 79 | + // app.main_loop(); 80 | } 81 | -------------------------------------------------------------------------------- /src/bin/02_validation_layers.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | extern crate vulkano_win; 3 | extern crate winit; 4 | 5 | use std::sync::Arc; 6 | 7 | use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; 8 | 9 | use vulkano::instance::{ 10 | Instance, 11 | InstanceExtensions, 12 | ApplicationInfo, 13 | Version, 14 | layers_list, 15 | }; 16 | use vulkano::instance::debug::{DebugCallback, MessageTypes}; 17 | 18 | const WIDTH: u32 = 800; 19 | const HEIGHT: u32 = 600; 20 | 21 | const VALIDATION_LAYERS: &[&str] = &[ 22 | "VK_LAYER_LUNARG_standard_validation" 23 | ]; 24 | 25 | #[cfg(all(debug_assertions))] 26 | const ENABLE_VALIDATION_LAYERS: bool = true; 27 | #[cfg(not(debug_assertions))] 28 | const ENABLE_VALIDATION_LAYERS: bool = false; 29 | 30 | #[allow(unused)] 31 | struct HelloTriangleApplication { 32 | instance: Arc, 33 | debug_callback: Option, 34 | 35 | events_loop: EventsLoop, 36 | } 37 | 38 | impl HelloTriangleApplication { 39 | pub fn initialize() -> Self { 40 | let instance = Self::create_instance(); 41 | let debug_callback = Self::setup_debug_callback(&instance); 42 | 43 | let events_loop = Self::init_window(); 44 | 45 | Self { 46 | instance, 47 | debug_callback, 48 | 49 | events_loop, 50 | } 51 | } 52 | 53 | fn init_window() -> EventsLoop { 54 | let events_loop = EventsLoop::new(); 55 | let _window_builder = WindowBuilder::new() 56 | .with_title("Vulkan") 57 | .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))); 58 | // .build(&self.events_loop.as_ref().unwrap()); 59 | events_loop 60 | } 61 | 62 | fn create_instance() -> Arc { 63 | if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { 64 | println!("Validation layers requested, but not available!") 65 | } 66 | 67 | let supported_extensions = InstanceExtensions::supported_by_core() 68 | .expect("failed to retrieve supported extensions"); 69 | println!("Supported extensions: {:?}", supported_extensions); 70 | 71 | let app_info = ApplicationInfo { 72 | application_name: Some("Hello Triangle".into()), 73 | application_version: Some(Version { major: 1, minor: 0, patch: 0 }), 74 | engine_name: Some("No Engine".into()), 75 | engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 76 | }; 77 | 78 | let required_extensions = Self::get_required_extensions(); 79 | 80 | if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { 81 | Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) 82 | .expect("failed to create Vulkan instance") 83 | } else { 84 | Instance::new(Some(&app_info), &required_extensions, None) 85 | .expect("failed to create Vulkan instance") 86 | } 87 | } 88 | 89 | fn check_validation_layer_support() -> bool { 90 | let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); 91 | VALIDATION_LAYERS.iter() 92 | .all(|layer_name| layers.contains(&layer_name.to_string())) 93 | } 94 | 95 | fn get_required_extensions() -> InstanceExtensions { 96 | let mut extensions = vulkano_win::required_extensions(); 97 | if ENABLE_VALIDATION_LAYERS { 98 | // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano 99 | extensions.ext_debug_report = true; 100 | } 101 | 102 | extensions 103 | } 104 | 105 | fn setup_debug_callback(instance: &Arc) -> Option { 106 | if !ENABLE_VALIDATION_LAYERS { 107 | return None; 108 | } 109 | 110 | let msg_types = MessageTypes { 111 | error: true, 112 | warning: true, 113 | performance_warning: true, 114 | information: false, 115 | debug: true, 116 | }; 117 | DebugCallback::new(&instance, msg_types, |msg| { 118 | println!("validation layer: {:?}", msg.description); 119 | }).ok() 120 | } 121 | 122 | #[allow(unused)] 123 | fn main_loop(&mut self) { 124 | loop { 125 | let mut done = false; 126 | self.events_loop.poll_events(|ev| { 127 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 128 | done = true 129 | } 130 | }); 131 | if done { 132 | return; 133 | } 134 | } 135 | } 136 | } 137 | 138 | fn main() { 139 | let mut _app = HelloTriangleApplication::initialize(); 140 | // app.main_loop(); 141 | } 142 | -------------------------------------------------------------------------------- /src/bin/02_validation_layers.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/01_instance_creation.rs 2 | +++ b/02_validation_layers.rs 3 | @@ -11,24 +11,41 @@ use vulkano::instance::{ 4 | InstanceExtensions, 5 | ApplicationInfo, 6 | Version, 7 | + layers_list, 8 | }; 9 | +use vulkano::instance::debug::{DebugCallback, MessageTypes}; 10 | 11 | const WIDTH: u32 = 800; 12 | const HEIGHT: u32 = 600; 13 | 14 | +const VALIDATION_LAYERS: &[&str] = &[ 15 | + "VK_LAYER_LUNARG_standard_validation" 16 | +]; 17 | + 18 | +#[cfg(all(debug_assertions))] 19 | +const ENABLE_VALIDATION_LAYERS: bool = true; 20 | +#[cfg(not(debug_assertions))] 21 | +const ENABLE_VALIDATION_LAYERS: bool = false; 22 | + 23 | #[allow(unused)] 24 | struct HelloTriangleApplication { 25 | instance: Arc, 26 | + debug_callback: Option, 27 | + 28 | events_loop: EventsLoop, 29 | } 30 | 31 | impl HelloTriangleApplication { 32 | pub fn initialize() -> Self { 33 | let instance = Self::create_instance(); 34 | + let debug_callback = Self::setup_debug_callback(&instance); 35 | + 36 | let events_loop = Self::init_window(); 37 | 38 | Self { 39 | instance, 40 | + debug_callback, 41 | + 42 | events_loop, 43 | } 44 | } 45 | @@ -43,6 +60,10 @@ impl HelloTriangleApplication { 46 | } 47 | 48 | fn create_instance() -> Arc { 49 | + if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { 50 | + println!("Validation layers requested, but not available!") 51 | + } 52 | + 53 | let supported_extensions = InstanceExtensions::supported_by_core() 54 | .expect("failed to retrieve supported extensions"); 55 | println!("Supported extensions: {:?}", supported_extensions); 56 | @@ -54,9 +75,48 @@ impl HelloTriangleApplication { 57 | engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 58 | }; 59 | 60 | - let required_extensions = vulkano_win::required_extensions(); 61 | - Instance::new(Some(&app_info), &required_extensions, None) 62 | - .expect("failed to create Vulkan instance") 63 | + let required_extensions = Self::get_required_extensions(); 64 | + 65 | + if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { 66 | + Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) 67 | + .expect("failed to create Vulkan instance") 68 | + } else { 69 | + Instance::new(Some(&app_info), &required_extensions, None) 70 | + .expect("failed to create Vulkan instance") 71 | + } 72 | + } 73 | + 74 | + fn check_validation_layer_support() -> bool { 75 | + let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); 76 | + VALIDATION_LAYERS.iter() 77 | + .all(|layer_name| layers.contains(&layer_name.to_string())) 78 | + } 79 | + 80 | + fn get_required_extensions() -> InstanceExtensions { 81 | + let mut extensions = vulkano_win::required_extensions(); 82 | + if ENABLE_VALIDATION_LAYERS { 83 | + // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano 84 | + extensions.ext_debug_report = true; 85 | + } 86 | + 87 | + extensions 88 | + } 89 | + 90 | + fn setup_debug_callback(instance: &Arc) -> Option { 91 | + if !ENABLE_VALIDATION_LAYERS { 92 | + return None; 93 | + } 94 | + 95 | + let msg_types = MessageTypes { 96 | + error: true, 97 | + warning: true, 98 | + performance_warning: true, 99 | + information: false, 100 | + debug: true, 101 | + }; 102 | + DebugCallback::new(&instance, msg_types, |msg| { 103 | + println!("validation layer: {:?}", msg.description); 104 | + }).ok() 105 | } 106 | 107 | #[allow(unused)] 108 | -------------------------------------------------------------------------------- /src/bin/03_physical_device_selection.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | extern crate vulkano_win; 3 | extern crate winit; 4 | 5 | use std::sync::Arc; 6 | 7 | use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; 8 | 9 | use vulkano::instance::{ 10 | Instance, 11 | InstanceExtensions, 12 | ApplicationInfo, 13 | Version, 14 | layers_list, 15 | PhysicalDevice, 16 | }; 17 | use vulkano::instance::debug::{DebugCallback, MessageTypes}; 18 | 19 | const WIDTH: u32 = 800; 20 | const HEIGHT: u32 = 600; 21 | 22 | const VALIDATION_LAYERS: &[&str] = &[ 23 | "VK_LAYER_LUNARG_standard_validation" 24 | ]; 25 | 26 | #[cfg(all(debug_assertions))] 27 | const ENABLE_VALIDATION_LAYERS: bool = true; 28 | #[cfg(not(debug_assertions))] 29 | const ENABLE_VALIDATION_LAYERS: bool = false; 30 | 31 | struct QueueFamilyIndices { 32 | graphics_family: i32, 33 | } 34 | impl QueueFamilyIndices { 35 | fn new() -> Self { 36 | Self { graphics_family: -1 } 37 | } 38 | 39 | fn is_complete(&self) -> bool { 40 | self.graphics_family >= 0 41 | } 42 | } 43 | 44 | #[allow(unused)] 45 | struct HelloTriangleApplication { 46 | instance: Arc, 47 | debug_callback: Option, 48 | 49 | events_loop: EventsLoop, 50 | 51 | physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) 52 | } 53 | 54 | impl HelloTriangleApplication { 55 | pub fn initialize() -> Self { 56 | let instance = Self::create_instance(); 57 | let debug_callback = Self::setup_debug_callback(&instance); 58 | 59 | let events_loop = Self::init_window(); 60 | 61 | let physical_device_index = Self::pick_physical_device(&instance); 62 | 63 | Self { 64 | instance, 65 | debug_callback, 66 | 67 | events_loop, 68 | 69 | physical_device_index, 70 | } 71 | } 72 | 73 | fn init_window() -> EventsLoop { 74 | let events_loop = EventsLoop::new(); 75 | let _window_builder = WindowBuilder::new() 76 | .with_title("Vulkan") 77 | .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))); 78 | // .build(&self.events_loop.as_ref().unwrap()); 79 | events_loop 80 | } 81 | 82 | fn create_instance() -> Arc { 83 | if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { 84 | println!("Validation layers requested, but not available!") 85 | } 86 | 87 | let supported_extensions = InstanceExtensions::supported_by_core() 88 | .expect("failed to retrieve supported extensions"); 89 | println!("Supported extensions: {:?}", supported_extensions); 90 | 91 | let app_info = ApplicationInfo { 92 | application_name: Some("Hello Triangle".into()), 93 | application_version: Some(Version { major: 1, minor: 0, patch: 0 }), 94 | engine_name: Some("No Engine".into()), 95 | engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 96 | }; 97 | 98 | let required_extensions = Self::get_required_extensions(); 99 | 100 | if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { 101 | Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) 102 | .expect("failed to create Vulkan instance") 103 | } else { 104 | Instance::new(Some(&app_info), &required_extensions, None) 105 | .expect("failed to create Vulkan instance") 106 | } 107 | } 108 | 109 | fn check_validation_layer_support() -> bool { 110 | let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); 111 | VALIDATION_LAYERS.iter() 112 | .all(|layer_name| layers.contains(&layer_name.to_string())) 113 | } 114 | 115 | fn get_required_extensions() -> InstanceExtensions { 116 | let mut extensions = vulkano_win::required_extensions(); 117 | if ENABLE_VALIDATION_LAYERS { 118 | // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano 119 | extensions.ext_debug_report = true; 120 | } 121 | 122 | extensions 123 | } 124 | 125 | fn setup_debug_callback(instance: &Arc) -> Option { 126 | if !ENABLE_VALIDATION_LAYERS { 127 | return None; 128 | } 129 | 130 | let msg_types = MessageTypes { 131 | error: true, 132 | warning: true, 133 | performance_warning: true, 134 | information: false, 135 | debug: true, 136 | }; 137 | DebugCallback::new(&instance, msg_types, |msg| { 138 | println!("validation layer: {:?}", msg.description); 139 | }).ok() 140 | } 141 | 142 | fn pick_physical_device(instance: &Arc) -> usize { 143 | PhysicalDevice::enumerate(&instance) 144 | .position(|device| Self::is_device_suitable(&device)) 145 | .expect("failed to find a suitable GPU!") 146 | } 147 | 148 | fn is_device_suitable(device: &PhysicalDevice) -> bool { 149 | let indices = Self::find_queue_families(device); 150 | indices.is_complete() 151 | } 152 | 153 | fn find_queue_families(device: &PhysicalDevice) -> QueueFamilyIndices { 154 | let mut indices = QueueFamilyIndices::new(); 155 | // TODO: replace index with id to simplify? 156 | for (i, queue_family) in device.queue_families().enumerate() { 157 | if queue_family.supports_graphics() { 158 | indices.graphics_family = i as i32; 159 | } 160 | 161 | if indices.is_complete() { 162 | break; 163 | } 164 | } 165 | 166 | indices 167 | } 168 | 169 | #[allow(unused)] 170 | fn main_loop(&mut self) { 171 | loop { 172 | let mut done = false; 173 | self.events_loop.poll_events(|ev| { 174 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 175 | done = true 176 | } 177 | }); 178 | if done { 179 | return; 180 | } 181 | } 182 | } 183 | } 184 | 185 | fn main() { 186 | let mut _app = HelloTriangleApplication::initialize(); 187 | // app.main_loop(); 188 | } 189 | -------------------------------------------------------------------------------- /src/bin/03_physical_device_selection.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/02_validation_layers.rs 2 | +++ b/03_physical_device_selection.rs 3 | @@ -12,6 +12,7 @@ use vulkano::instance::{ 4 | ApplicationInfo, 5 | Version, 6 | layers_list, 7 | + PhysicalDevice, 8 | }; 9 | use vulkano::instance::debug::{DebugCallback, MessageTypes}; 10 | 11 | @@ -27,12 +28,27 @@ const ENABLE_VALIDATION_LAYERS: bool = true; 12 | #[cfg(not(debug_assertions))] 13 | const ENABLE_VALIDATION_LAYERS: bool = false; 14 | 15 | +struct QueueFamilyIndices { 16 | + graphics_family: i32, 17 | +} 18 | +impl QueueFamilyIndices { 19 | + fn new() -> Self { 20 | + Self { graphics_family: -1 } 21 | + } 22 | + 23 | + fn is_complete(&self) -> bool { 24 | + self.graphics_family >= 0 25 | + } 26 | +} 27 | + 28 | #[allow(unused)] 29 | struct HelloTriangleApplication { 30 | instance: Arc, 31 | debug_callback: Option, 32 | 33 | events_loop: EventsLoop, 34 | + 35 | + physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) 36 | } 37 | 38 | impl HelloTriangleApplication { 39 | @@ -42,11 +58,15 @@ impl HelloTriangleApplication { 40 | 41 | let events_loop = Self::init_window(); 42 | 43 | + let physical_device_index = Self::pick_physical_device(&instance); 44 | + 45 | Self { 46 | instance, 47 | debug_callback, 48 | 49 | events_loop, 50 | + 51 | + physical_device_index, 52 | } 53 | } 54 | 55 | @@ -119,6 +139,33 @@ impl HelloTriangleApplication { 56 | }).ok() 57 | } 58 | 59 | + fn pick_physical_device(instance: &Arc) -> usize { 60 | + PhysicalDevice::enumerate(&instance) 61 | + .position(|device| Self::is_device_suitable(&device)) 62 | + .expect("failed to find a suitable GPU!") 63 | + } 64 | + 65 | + fn is_device_suitable(device: &PhysicalDevice) -> bool { 66 | + let indices = Self::find_queue_families(device); 67 | + indices.is_complete() 68 | + } 69 | + 70 | + fn find_queue_families(device: &PhysicalDevice) -> QueueFamilyIndices { 71 | + let mut indices = QueueFamilyIndices::new(); 72 | + // TODO: replace index with id to simplify? 73 | + for (i, queue_family) in device.queue_families().enumerate() { 74 | + if queue_family.supports_graphics() { 75 | + indices.graphics_family = i as i32; 76 | + } 77 | + 78 | + if indices.is_complete() { 79 | + break; 80 | + } 81 | + } 82 | + 83 | + indices 84 | + } 85 | + 86 | #[allow(unused)] 87 | fn main_loop(&mut self) { 88 | loop { 89 | -------------------------------------------------------------------------------- /src/bin/04_logical_device.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | extern crate vulkano_win; 3 | extern crate winit; 4 | 5 | use std::sync::Arc; 6 | 7 | use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; 8 | 9 | use vulkano::instance::{ 10 | Instance, 11 | InstanceExtensions, 12 | ApplicationInfo, 13 | Version, 14 | layers_list, 15 | PhysicalDevice, 16 | }; 17 | use vulkano::instance::debug::{DebugCallback, MessageTypes}; 18 | use vulkano::device::{Device, DeviceExtensions, Queue, Features}; 19 | 20 | const WIDTH: u32 = 800; 21 | const HEIGHT: u32 = 600; 22 | 23 | const VALIDATION_LAYERS: &[&str] = &[ 24 | "VK_LAYER_LUNARG_standard_validation" 25 | ]; 26 | 27 | #[cfg(all(debug_assertions))] 28 | const ENABLE_VALIDATION_LAYERS: bool = true; 29 | #[cfg(not(debug_assertions))] 30 | const ENABLE_VALIDATION_LAYERS: bool = false; 31 | 32 | struct QueueFamilyIndices { 33 | graphics_family: i32, 34 | } 35 | impl QueueFamilyIndices { 36 | fn new() -> Self { 37 | Self { graphics_family: -1 } 38 | } 39 | 40 | fn is_complete(&self) -> bool { 41 | self.graphics_family >= 0 42 | } 43 | } 44 | 45 | #[allow(unused)] 46 | struct HelloTriangleApplication { 47 | instance: Arc, 48 | debug_callback: Option, 49 | 50 | events_loop: EventsLoop, 51 | 52 | physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) 53 | device: Arc, 54 | 55 | graphics_queue: Arc, 56 | } 57 | 58 | impl HelloTriangleApplication { 59 | pub fn initialize() -> Self { 60 | let instance = Self::create_instance(); 61 | let debug_callback = Self::setup_debug_callback(&instance); 62 | 63 | let events_loop = Self::init_window(); 64 | 65 | let physical_device_index = Self::pick_physical_device(&instance); 66 | let (device, graphics_queue) = Self::create_logical_device( 67 | &instance, physical_device_index); 68 | 69 | Self { 70 | instance, 71 | debug_callback, 72 | 73 | events_loop, 74 | 75 | physical_device_index, 76 | device, 77 | 78 | graphics_queue, 79 | } 80 | } 81 | 82 | fn init_window() -> EventsLoop { 83 | let events_loop = EventsLoop::new(); 84 | let _window_builder = WindowBuilder::new() 85 | .with_title("Vulkan") 86 | .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))); 87 | // .build(&self.events_loop.as_ref().unwrap()); 88 | events_loop 89 | } 90 | 91 | fn create_instance() -> Arc { 92 | if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { 93 | println!("Validation layers requested, but not available!") 94 | } 95 | 96 | let supported_extensions = InstanceExtensions::supported_by_core() 97 | .expect("failed to retrieve supported extensions"); 98 | println!("Supported extensions: {:?}", supported_extensions); 99 | 100 | let app_info = ApplicationInfo { 101 | application_name: Some("Hello Triangle".into()), 102 | application_version: Some(Version { major: 1, minor: 0, patch: 0 }), 103 | engine_name: Some("No Engine".into()), 104 | engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 105 | }; 106 | 107 | let required_extensions = Self::get_required_extensions(); 108 | 109 | if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { 110 | Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) 111 | .expect("failed to create Vulkan instance") 112 | } else { 113 | Instance::new(Some(&app_info), &required_extensions, None) 114 | .expect("failed to create Vulkan instance") 115 | } 116 | } 117 | 118 | fn check_validation_layer_support() -> bool { 119 | let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); 120 | VALIDATION_LAYERS.iter() 121 | .all(|layer_name| layers.contains(&layer_name.to_string())) 122 | } 123 | 124 | fn get_required_extensions() -> InstanceExtensions { 125 | let mut extensions = vulkano_win::required_extensions(); 126 | if ENABLE_VALIDATION_LAYERS { 127 | // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano 128 | extensions.ext_debug_report = true; 129 | } 130 | 131 | extensions 132 | } 133 | 134 | fn setup_debug_callback(instance: &Arc) -> Option { 135 | if !ENABLE_VALIDATION_LAYERS { 136 | return None; 137 | } 138 | 139 | let msg_types = MessageTypes { 140 | error: true, 141 | warning: true, 142 | performance_warning: true, 143 | information: false, 144 | debug: true, 145 | }; 146 | DebugCallback::new(&instance, msg_types, |msg| { 147 | println!("validation layer: {:?}", msg.description); 148 | }).ok() 149 | } 150 | 151 | fn pick_physical_device(instance: &Arc) -> usize { 152 | PhysicalDevice::enumerate(&instance) 153 | .position(|device| Self::is_device_suitable(&device)) 154 | .expect("failed to find a suitable GPU!") 155 | } 156 | 157 | fn is_device_suitable(device: &PhysicalDevice) -> bool { 158 | let indices = Self::find_queue_families(device); 159 | indices.is_complete() 160 | } 161 | 162 | fn find_queue_families(device: &PhysicalDevice) -> QueueFamilyIndices { 163 | let mut indices = QueueFamilyIndices::new(); 164 | // TODO: replace index with id to simplify? 165 | for (i, queue_family) in device.queue_families().enumerate() { 166 | if queue_family.supports_graphics() { 167 | indices.graphics_family = i as i32; 168 | } 169 | 170 | if indices.is_complete() { 171 | break; 172 | } 173 | } 174 | 175 | indices 176 | } 177 | 178 | fn create_logical_device( 179 | instance: &Arc, 180 | physical_device_index: usize, 181 | ) -> (Arc, Arc) { 182 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 183 | let indices = Self::find_queue_families(&physical_device); 184 | 185 | let queue_family = physical_device.queue_families() 186 | .nth(indices.graphics_family as usize).unwrap(); 187 | 188 | let queue_priority = 1.0; 189 | 190 | // NOTE: the tutorial recommends passing the validation layers as well 191 | // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that 192 | // for us internally. 193 | 194 | let (device, mut queues) = Device::new(physical_device, &Features::none(), &DeviceExtensions::none(), 195 | [(queue_family, queue_priority)].iter().cloned()) 196 | .expect("failed to create logical device!"); 197 | 198 | let graphics_queue = queues.next().unwrap(); 199 | 200 | (device, graphics_queue) 201 | } 202 | 203 | #[allow(unused)] 204 | fn main_loop(&mut self) { 205 | loop { 206 | let mut done = false; 207 | self.events_loop.poll_events(|ev| { 208 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 209 | done = true 210 | } 211 | }); 212 | if done { 213 | return; 214 | } 215 | } 216 | } 217 | } 218 | 219 | fn main() { 220 | let mut _app = HelloTriangleApplication::initialize(); 221 | // app.main_loop(); 222 | } 223 | -------------------------------------------------------------------------------- /src/bin/04_logical_device.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/03_physical_device_selection.rs 2 | +++ b/04_logical_device.rs 3 | @@ -15,6 +15,7 @@ use vulkano::instance::{ 4 | PhysicalDevice, 5 | }; 6 | use vulkano::instance::debug::{DebugCallback, MessageTypes}; 7 | +use vulkano::device::{Device, DeviceExtensions, Queue, Features}; 8 | 9 | const WIDTH: u32 = 800; 10 | const HEIGHT: u32 = 600; 11 | @@ -49,6 +50,9 @@ struct HelloTriangleApplication { 12 | events_loop: EventsLoop, 13 | 14 | physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) 15 | + device: Arc, 16 | + 17 | + graphics_queue: Arc, 18 | } 19 | 20 | impl HelloTriangleApplication { 21 | @@ -59,6 +63,8 @@ impl HelloTriangleApplication { 22 | let events_loop = Self::init_window(); 23 | 24 | let physical_device_index = Self::pick_physical_device(&instance); 25 | + let (device, graphics_queue) = Self::create_logical_device( 26 | + &instance, physical_device_index); 27 | 28 | Self { 29 | instance, 30 | @@ -67,6 +73,9 @@ impl HelloTriangleApplication { 31 | events_loop, 32 | 33 | physical_device_index, 34 | + device, 35 | + 36 | + graphics_queue, 37 | } 38 | } 39 | 40 | @@ -166,6 +175,31 @@ impl HelloTriangleApplication { 41 | indices 42 | } 43 | 44 | + fn create_logical_device( 45 | + instance: &Arc, 46 | + physical_device_index: usize, 47 | + ) -> (Arc, Arc) { 48 | + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 49 | + let indices = Self::find_queue_families(&physical_device); 50 | + 51 | + let queue_family = physical_device.queue_families() 52 | + .nth(indices.graphics_family as usize).unwrap(); 53 | + 54 | + let queue_priority = 1.0; 55 | + 56 | + // NOTE: the tutorial recommends passing the validation layers as well 57 | + // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that 58 | + // for us internally. 59 | + 60 | + let (device, mut queues) = Device::new(physical_device, &Features::none(), &DeviceExtensions::none(), 61 | + [(queue_family, queue_priority)].iter().cloned()) 62 | + .expect("failed to create logical device!"); 63 | + 64 | + let graphics_queue = queues.next().unwrap(); 65 | + 66 | + (device, graphics_queue) 67 | + } 68 | + 69 | #[allow(unused)] 70 | fn main_loop(&mut self) { 71 | loop { 72 | -------------------------------------------------------------------------------- /src/bin/05_window_surface.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | extern crate vulkano_win; 3 | extern crate winit; 4 | 5 | use std::sync::Arc; 6 | use std::collections::HashSet; 7 | 8 | use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; 9 | use vulkano_win::VkSurfaceBuild; 10 | 11 | use vulkano::instance::{ 12 | Instance, 13 | InstanceExtensions, 14 | ApplicationInfo, 15 | Version, 16 | layers_list, 17 | PhysicalDevice, 18 | }; 19 | use vulkano::instance::debug::{DebugCallback, MessageTypes}; 20 | use vulkano::device::{Device, DeviceExtensions, Queue, Features}; 21 | use vulkano::swapchain::{ 22 | Surface, 23 | }; 24 | 25 | const WIDTH: u32 = 800; 26 | const HEIGHT: u32 = 600; 27 | 28 | const VALIDATION_LAYERS: &[&str] = &[ 29 | "VK_LAYER_LUNARG_standard_validation" 30 | ]; 31 | 32 | #[cfg(all(debug_assertions))] 33 | const ENABLE_VALIDATION_LAYERS: bool = true; 34 | #[cfg(not(debug_assertions))] 35 | const ENABLE_VALIDATION_LAYERS: bool = false; 36 | 37 | struct QueueFamilyIndices { 38 | graphics_family: i32, 39 | present_family: i32, 40 | } 41 | impl QueueFamilyIndices { 42 | fn new() -> Self { 43 | Self { graphics_family: -1, present_family: -1 } 44 | } 45 | 46 | fn is_complete(&self) -> bool { 47 | self.graphics_family >= 0 && self.present_family >= 0 48 | } 49 | } 50 | 51 | #[allow(unused)] 52 | struct HelloTriangleApplication { 53 | instance: Arc, 54 | debug_callback: Option, 55 | 56 | events_loop: EventsLoop, 57 | surface: Arc>, 58 | 59 | physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) 60 | device: Arc, 61 | 62 | graphics_queue: Arc, 63 | present_queue: Arc, 64 | } 65 | 66 | impl HelloTriangleApplication { 67 | pub fn initialize() -> Self { 68 | let instance = Self::create_instance(); 69 | let debug_callback = Self::setup_debug_callback(&instance); 70 | let (events_loop, surface) = Self::create_surface(&instance); 71 | 72 | let physical_device_index = Self::pick_physical_device(&instance, &surface); 73 | let (device, graphics_queue, present_queue) = Self::create_logical_device( 74 | &instance, &surface, physical_device_index); 75 | 76 | Self { 77 | instance, 78 | debug_callback, 79 | 80 | events_loop, 81 | surface, 82 | 83 | physical_device_index, 84 | device, 85 | 86 | graphics_queue, 87 | present_queue, 88 | } 89 | } 90 | 91 | fn create_instance() -> Arc { 92 | if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { 93 | println!("Validation layers requested, but not available!") 94 | } 95 | 96 | let supported_extensions = InstanceExtensions::supported_by_core() 97 | .expect("failed to retrieve supported extensions"); 98 | println!("Supported extensions: {:?}", supported_extensions); 99 | 100 | let app_info = ApplicationInfo { 101 | application_name: Some("Hello Triangle".into()), 102 | application_version: Some(Version { major: 1, minor: 0, patch: 0 }), 103 | engine_name: Some("No Engine".into()), 104 | engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 105 | }; 106 | 107 | let required_extensions = Self::get_required_extensions(); 108 | 109 | if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { 110 | Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) 111 | .expect("failed to create Vulkan instance") 112 | } else { 113 | Instance::new(Some(&app_info), &required_extensions, None) 114 | .expect("failed to create Vulkan instance") 115 | } 116 | } 117 | 118 | fn check_validation_layer_support() -> bool { 119 | let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); 120 | VALIDATION_LAYERS.iter() 121 | .all(|layer_name| layers.contains(&layer_name.to_string())) 122 | } 123 | 124 | fn get_required_extensions() -> InstanceExtensions { 125 | let mut extensions = vulkano_win::required_extensions(); 126 | if ENABLE_VALIDATION_LAYERS { 127 | // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano 128 | extensions.ext_debug_report = true; 129 | } 130 | 131 | extensions 132 | } 133 | 134 | fn setup_debug_callback(instance: &Arc) -> Option { 135 | if !ENABLE_VALIDATION_LAYERS { 136 | return None; 137 | } 138 | 139 | let msg_types = MessageTypes { 140 | error: true, 141 | warning: true, 142 | performance_warning: true, 143 | information: false, 144 | debug: true, 145 | }; 146 | DebugCallback::new(&instance, msg_types, |msg| { 147 | println!("validation layer: {:?}", msg.description); 148 | }).ok() 149 | } 150 | 151 | fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { 152 | PhysicalDevice::enumerate(&instance) 153 | .position(|device| Self::is_device_suitable(surface, &device)) 154 | .expect("failed to find a suitable GPU!") 155 | } 156 | 157 | fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { 158 | let indices = Self::find_queue_families(surface, device); 159 | indices.is_complete() 160 | } 161 | 162 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 163 | let mut indices = QueueFamilyIndices::new(); 164 | // TODO: replace index with id to simplify? 165 | for (i, queue_family) in device.queue_families().enumerate() { 166 | if queue_family.supports_graphics() { 167 | indices.graphics_family = i as i32; 168 | } 169 | 170 | if surface.is_supported(queue_family).unwrap() { 171 | indices.present_family = i as i32; 172 | } 173 | 174 | if indices.is_complete() { 175 | break; 176 | } 177 | } 178 | 179 | indices 180 | } 181 | 182 | fn create_logical_device( 183 | instance: &Arc, 184 | surface: &Arc>, 185 | physical_device_index: usize, 186 | ) -> (Arc, Arc, Arc) { 187 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 188 | let indices = Self::find_queue_families(&surface, &physical_device); 189 | 190 | let families = [indices.graphics_family, indices.present_family]; 191 | use std::iter::FromIterator; 192 | let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); 193 | 194 | let queue_priority = 1.0; 195 | let queue_families = unique_queue_families.iter().map(|i| { 196 | (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) 197 | }); 198 | 199 | // NOTE: the tutorial recommends passing the validation layers as well 200 | // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that 201 | // for us internally. 202 | 203 | let (device, mut queues) = Device::new(physical_device, &Features::none(), 204 | &DeviceExtensions::none(), queue_families) 205 | .expect("failed to create logical device!"); 206 | 207 | let graphics_queue = queues.next().unwrap(); 208 | let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); 209 | 210 | (device, graphics_queue, present_queue) 211 | } 212 | 213 | fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { 214 | let events_loop = EventsLoop::new(); 215 | let surface = WindowBuilder::new() 216 | .with_title("Vulkan") 217 | .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) 218 | .build_vk_surface(&events_loop, instance.clone()) 219 | .expect("failed to create window surface!"); 220 | (events_loop, surface) 221 | } 222 | 223 | #[allow(unused)] 224 | fn main_loop(&mut self) { 225 | loop { 226 | let mut done = false; 227 | self.events_loop.poll_events(|ev| { 228 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 229 | done = true 230 | } 231 | }); 232 | if done { 233 | return; 234 | } 235 | } 236 | } 237 | } 238 | 239 | fn main() { 240 | let mut _app = HelloTriangleApplication::initialize(); 241 | // app.main_loop(); 242 | } 243 | -------------------------------------------------------------------------------- /src/bin/05_window_surface.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/04_logical_device.rs 2 | +++ b/05_window_surface.rs 3 | @@ -3,8 +3,10 @@ extern crate vulkano_win; 4 | extern crate winit; 5 | 6 | use std::sync::Arc; 7 | +use std::collections::HashSet; 8 | 9 | -use winit::{EventsLoop, WindowBuilder, dpi::LogicalSize, Event, WindowEvent}; 10 | +use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; 11 | +use vulkano_win::VkSurfaceBuild; 12 | 13 | use vulkano::instance::{ 14 | Instance, 15 | @@ -16,6 +18,9 @@ use vulkano::instance::{ 16 | }; 17 | use vulkano::instance::debug::{DebugCallback, MessageTypes}; 18 | use vulkano::device::{Device, DeviceExtensions, Queue, Features}; 19 | +use vulkano::swapchain::{ 20 | + Surface, 21 | +}; 22 | 23 | const WIDTH: u32 = 800; 24 | const HEIGHT: u32 = 600; 25 | @@ -31,14 +36,15 @@ const ENABLE_VALIDATION_LAYERS: bool = false; 26 | 27 | struct QueueFamilyIndices { 28 | graphics_family: i32, 29 | + present_family: i32, 30 | } 31 | impl QueueFamilyIndices { 32 | fn new() -> Self { 33 | - Self { graphics_family: -1 } 34 | + Self { graphics_family: -1, present_family: -1 } 35 | } 36 | 37 | fn is_complete(&self) -> bool { 38 | - self.graphics_family >= 0 39 | + self.graphics_family >= 0 && self.present_family >= 0 40 | } 41 | } 42 | 43 | @@ -48,46 +54,40 @@ struct HelloTriangleApplication { 44 | debug_callback: Option, 45 | 46 | events_loop: EventsLoop, 47 | + surface: Arc>, 48 | 49 | physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) 50 | device: Arc, 51 | 52 | graphics_queue: Arc, 53 | + present_queue: Arc, 54 | } 55 | 56 | impl HelloTriangleApplication { 57 | pub fn initialize() -> Self { 58 | let instance = Self::create_instance(); 59 | let debug_callback = Self::setup_debug_callback(&instance); 60 | + let (events_loop, surface) = Self::create_surface(&instance); 61 | 62 | - let events_loop = Self::init_window(); 63 | - 64 | - let physical_device_index = Self::pick_physical_device(&instance); 65 | - let (device, graphics_queue) = Self::create_logical_device( 66 | - &instance, physical_device_index); 67 | + let physical_device_index = Self::pick_physical_device(&instance, &surface); 68 | + let (device, graphics_queue, present_queue) = Self::create_logical_device( 69 | + &instance, &surface, physical_device_index); 70 | 71 | Self { 72 | instance, 73 | debug_callback, 74 | 75 | events_loop, 76 | + surface, 77 | 78 | physical_device_index, 79 | device, 80 | 81 | graphics_queue, 82 | + present_queue, 83 | } 84 | } 85 | 86 | - fn init_window() -> EventsLoop { 87 | - let events_loop = EventsLoop::new(); 88 | - let _window_builder = WindowBuilder::new() 89 | - .with_title("Vulkan") 90 | - .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))); 91 | - // .build(&self.events_loop.as_ref().unwrap()); 92 | - events_loop 93 | - } 94 | - 95 | fn create_instance() -> Arc { 96 | if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { 97 | println!("Validation layers requested, but not available!") 98 | @@ -148,18 +148,18 @@ impl HelloTriangleApplication { 99 | }).ok() 100 | } 101 | 102 | - fn pick_physical_device(instance: &Arc) -> usize { 103 | + fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { 104 | PhysicalDevice::enumerate(&instance) 105 | - .position(|device| Self::is_device_suitable(&device)) 106 | + .position(|device| Self::is_device_suitable(surface, &device)) 107 | .expect("failed to find a suitable GPU!") 108 | } 109 | 110 | - fn is_device_suitable(device: &PhysicalDevice) -> bool { 111 | - let indices = Self::find_queue_families(device); 112 | + fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { 113 | + let indices = Self::find_queue_families(surface, device); 114 | indices.is_complete() 115 | } 116 | 117 | - fn find_queue_families(device: &PhysicalDevice) -> QueueFamilyIndices { 118 | + fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 119 | let mut indices = QueueFamilyIndices::new(); 120 | // TODO: replace index with id to simplify? 121 | for (i, queue_family) in device.queue_families().enumerate() { 122 | @@ -167,6 +167,10 @@ impl HelloTriangleApplication { 123 | indices.graphics_family = i as i32; 124 | } 125 | 126 | + if surface.is_supported(queue_family).unwrap() { 127 | + indices.present_family = i as i32; 128 | + } 129 | + 130 | if indices.is_complete() { 131 | break; 132 | } 133 | @@ -177,27 +181,43 @@ impl HelloTriangleApplication { 134 | 135 | fn create_logical_device( 136 | instance: &Arc, 137 | + surface: &Arc>, 138 | physical_device_index: usize, 139 | - ) -> (Arc, Arc) { 140 | + ) -> (Arc, Arc, Arc) { 141 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 142 | - let indices = Self::find_queue_families(&physical_device); 143 | + let indices = Self::find_queue_families(&surface, &physical_device); 144 | 145 | - let queue_family = physical_device.queue_families() 146 | - .nth(indices.graphics_family as usize).unwrap(); 147 | + let families = [indices.graphics_family, indices.present_family]; 148 | + use std::iter::FromIterator; 149 | + let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); 150 | 151 | let queue_priority = 1.0; 152 | + let queue_families = unique_queue_families.iter().map(|i| { 153 | + (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) 154 | + }); 155 | 156 | // NOTE: the tutorial recommends passing the validation layers as well 157 | // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that 158 | // for us internally. 159 | 160 | - let (device, mut queues) = Device::new(physical_device, &Features::none(), &DeviceExtensions::none(), 161 | - [(queue_family, queue_priority)].iter().cloned()) 162 | + let (device, mut queues) = Device::new(physical_device, &Features::none(), 163 | + &DeviceExtensions::none(), queue_families) 164 | .expect("failed to create logical device!"); 165 | 166 | let graphics_queue = queues.next().unwrap(); 167 | + let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); 168 | + 169 | + (device, graphics_queue, present_queue) 170 | + } 171 | 172 | - (device, graphics_queue) 173 | + fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { 174 | + let events_loop = EventsLoop::new(); 175 | + let surface = WindowBuilder::new() 176 | + .with_title("Vulkan") 177 | + .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) 178 | + .build_vk_surface(&events_loop, instance.clone()) 179 | + .expect("failed to create window surface!"); 180 | + (events_loop, surface) 181 | } 182 | 183 | #[allow(unused)] 184 | -------------------------------------------------------------------------------- /src/bin/06_swap_chain_creation.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | extern crate vulkano_win; 3 | extern crate winit; 4 | 5 | use std::sync::Arc; 6 | use std::collections::HashSet; 7 | 8 | use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; 9 | use vulkano_win::VkSurfaceBuild; 10 | 11 | use vulkano::instance::{ 12 | Instance, 13 | InstanceExtensions, 14 | ApplicationInfo, 15 | Version, 16 | layers_list, 17 | PhysicalDevice, 18 | }; 19 | use vulkano::instance::debug::{DebugCallback, MessageTypes}; 20 | use vulkano::device::{Device, DeviceExtensions, Queue, Features}; 21 | use vulkano::swapchain::{ 22 | Surface, 23 | Capabilities, 24 | ColorSpace, 25 | SupportedPresentModes, 26 | PresentMode, 27 | Swapchain, 28 | CompositeAlpha, 29 | }; 30 | use vulkano::format::Format; 31 | use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; 32 | use vulkano::sync::SharingMode; 33 | 34 | const WIDTH: u32 = 800; 35 | const HEIGHT: u32 = 600; 36 | 37 | const VALIDATION_LAYERS: &[&str] = &[ 38 | "VK_LAYER_LUNARG_standard_validation" 39 | ]; 40 | 41 | /// Required device extensions 42 | fn device_extensions() -> DeviceExtensions { 43 | DeviceExtensions { 44 | khr_swapchain: true, 45 | .. vulkano::device::DeviceExtensions::none() 46 | } 47 | } 48 | 49 | #[cfg(all(debug_assertions))] 50 | const ENABLE_VALIDATION_LAYERS: bool = true; 51 | #[cfg(not(debug_assertions))] 52 | const ENABLE_VALIDATION_LAYERS: bool = false; 53 | 54 | struct QueueFamilyIndices { 55 | graphics_family: i32, 56 | present_family: i32, 57 | } 58 | impl QueueFamilyIndices { 59 | fn new() -> Self { 60 | Self { graphics_family: -1, present_family: -1 } 61 | } 62 | 63 | fn is_complete(&self) -> bool { 64 | self.graphics_family >= 0 && self.present_family >= 0 65 | } 66 | } 67 | 68 | #[allow(unused)] 69 | struct HelloTriangleApplication { 70 | instance: Arc, 71 | debug_callback: Option, 72 | 73 | events_loop: EventsLoop, 74 | surface: Arc>, 75 | 76 | physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) 77 | device: Arc, 78 | 79 | graphics_queue: Arc, 80 | present_queue: Arc, 81 | 82 | swap_chain: Arc>, 83 | swap_chain_images: Vec>>, 84 | } 85 | 86 | impl HelloTriangleApplication { 87 | pub fn initialize() -> Self { 88 | let instance = Self::create_instance(); 89 | let debug_callback = Self::setup_debug_callback(&instance); 90 | let (events_loop, surface) = Self::create_surface(&instance); 91 | 92 | let physical_device_index = Self::pick_physical_device(&instance, &surface); 93 | let (device, graphics_queue, present_queue) = Self::create_logical_device( 94 | &instance, &surface, physical_device_index); 95 | 96 | let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, 97 | &device, &graphics_queue, &present_queue); 98 | 99 | Self { 100 | instance, 101 | debug_callback, 102 | 103 | events_loop, 104 | surface, 105 | 106 | physical_device_index, 107 | device, 108 | 109 | graphics_queue, 110 | present_queue, 111 | 112 | swap_chain, 113 | swap_chain_images, 114 | } 115 | } 116 | 117 | fn create_instance() -> Arc { 118 | if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { 119 | println!("Validation layers requested, but not available!") 120 | } 121 | 122 | let supported_extensions = InstanceExtensions::supported_by_core() 123 | .expect("failed to retrieve supported extensions"); 124 | println!("Supported extensions: {:?}", supported_extensions); 125 | 126 | let app_info = ApplicationInfo { 127 | application_name: Some("Hello Triangle".into()), 128 | application_version: Some(Version { major: 1, minor: 0, patch: 0 }), 129 | engine_name: Some("No Engine".into()), 130 | engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 131 | }; 132 | 133 | let required_extensions = Self::get_required_extensions(); 134 | 135 | if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { 136 | Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) 137 | .expect("failed to create Vulkan instance") 138 | } else { 139 | Instance::new(Some(&app_info), &required_extensions, None) 140 | .expect("failed to create Vulkan instance") 141 | } 142 | } 143 | 144 | fn check_validation_layer_support() -> bool { 145 | let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); 146 | VALIDATION_LAYERS.iter() 147 | .all(|layer_name| layers.contains(&layer_name.to_string())) 148 | } 149 | 150 | fn get_required_extensions() -> InstanceExtensions { 151 | let mut extensions = vulkano_win::required_extensions(); 152 | if ENABLE_VALIDATION_LAYERS { 153 | // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano 154 | extensions.ext_debug_report = true; 155 | } 156 | 157 | extensions 158 | } 159 | 160 | fn setup_debug_callback(instance: &Arc) -> Option { 161 | if !ENABLE_VALIDATION_LAYERS { 162 | return None; 163 | } 164 | 165 | let msg_types = MessageTypes { 166 | error: true, 167 | warning: true, 168 | performance_warning: true, 169 | information: false, 170 | debug: true, 171 | }; 172 | DebugCallback::new(&instance, msg_types, |msg| { 173 | println!("validation layer: {:?}", msg.description); 174 | }).ok() 175 | } 176 | 177 | fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { 178 | PhysicalDevice::enumerate(&instance) 179 | .position(|device| Self::is_device_suitable(surface, &device)) 180 | .expect("failed to find a suitable GPU!") 181 | } 182 | 183 | fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { 184 | let indices = Self::find_queue_families(surface, device); 185 | let extensions_supported = Self::check_device_extension_support(device); 186 | 187 | let swap_chain_adequate = if extensions_supported { 188 | let capabilities = surface.capabilities(*device) 189 | .expect("failed to get surface capabilities"); 190 | !capabilities.supported_formats.is_empty() && 191 | capabilities.present_modes.iter().next().is_some() 192 | } else { 193 | false 194 | }; 195 | 196 | indices.is_complete() && extensions_supported && swap_chain_adequate 197 | } 198 | 199 | fn check_device_extension_support(device: &PhysicalDevice) -> bool { 200 | let available_extensions = DeviceExtensions::supported_by_device(*device); 201 | let device_extensions = device_extensions(); 202 | available_extensions.intersection(&device_extensions) == device_extensions 203 | } 204 | 205 | fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { 206 | // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be 207 | // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) 208 | *available_formats.iter() 209 | .find(|(format, color_space)| 210 | *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear 211 | ) 212 | .unwrap_or_else(|| &available_formats[0]) 213 | } 214 | 215 | fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { 216 | if available_present_modes.mailbox { 217 | PresentMode::Mailbox 218 | } else if available_present_modes.immediate { 219 | PresentMode::Immediate 220 | } else { 221 | PresentMode::Fifo 222 | } 223 | } 224 | 225 | fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { 226 | if let Some(current_extent) = capabilities.current_extent { 227 | return current_extent 228 | } else { 229 | let mut actual_extent = [WIDTH, HEIGHT]; 230 | actual_extent[0] = capabilities.min_image_extent[0] 231 | .max(capabilities.max_image_extent[0].min(actual_extent[0])); 232 | actual_extent[1] = capabilities.min_image_extent[1] 233 | .max(capabilities.max_image_extent[1].min(actual_extent[1])); 234 | actual_extent 235 | } 236 | } 237 | 238 | fn create_swap_chain( 239 | instance: &Arc, 240 | surface: &Arc>, 241 | physical_device_index: usize, 242 | device: &Arc, 243 | graphics_queue: &Arc, 244 | present_queue: &Arc, 245 | ) -> (Arc>, Vec>>) { 246 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 247 | let capabilities = surface.capabilities(physical_device) 248 | .expect("failed to get surface capabilities"); 249 | 250 | let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); 251 | let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); 252 | let extent = Self::choose_swap_extent(&capabilities); 253 | 254 | let mut image_count = capabilities.min_image_count + 1; 255 | if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { 256 | image_count = capabilities.max_image_count.unwrap(); 257 | } 258 | 259 | let image_usage = ImageUsage { 260 | color_attachment: true, 261 | .. ImageUsage::none() 262 | }; 263 | 264 | let indices = Self::find_queue_families(&surface, &physical_device); 265 | 266 | let sharing: SharingMode = if indices.graphics_family != indices.present_family { 267 | vec![graphics_queue, present_queue].as_slice().into() 268 | } else { 269 | graphics_queue.into() 270 | }; 271 | 272 | let (swap_chain, images) = Swapchain::new( 273 | device.clone(), 274 | surface.clone(), 275 | image_count, 276 | surface_format.0, // TODO: color space? 277 | extent, 278 | 1, // layers 279 | image_usage, 280 | sharing, 281 | capabilities.current_transform, 282 | CompositeAlpha::Opaque, 283 | present_mode, 284 | true, // clipped 285 | None, 286 | ).expect("failed to create swap chain!"); 287 | 288 | (swap_chain, images) 289 | } 290 | 291 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 292 | let mut indices = QueueFamilyIndices::new(); 293 | // TODO: replace index with id to simplify? 294 | for (i, queue_family) in device.queue_families().enumerate() { 295 | if queue_family.supports_graphics() { 296 | indices.graphics_family = i as i32; 297 | } 298 | 299 | if surface.is_supported(queue_family).unwrap() { 300 | indices.present_family = i as i32; 301 | } 302 | 303 | if indices.is_complete() { 304 | break; 305 | } 306 | } 307 | 308 | indices 309 | } 310 | 311 | fn create_logical_device( 312 | instance: &Arc, 313 | surface: &Arc>, 314 | physical_device_index: usize, 315 | ) -> (Arc, Arc, Arc) { 316 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 317 | let indices = Self::find_queue_families(&surface, &physical_device); 318 | 319 | let families = [indices.graphics_family, indices.present_family]; 320 | use std::iter::FromIterator; 321 | let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); 322 | 323 | let queue_priority = 1.0; 324 | let queue_families = unique_queue_families.iter().map(|i| { 325 | (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) 326 | }); 327 | 328 | // NOTE: the tutorial recommends passing the validation layers as well 329 | // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that 330 | // for us internally. 331 | 332 | let (device, mut queues) = Device::new(physical_device, &Features::none(), 333 | &device_extensions(), queue_families) 334 | .expect("failed to create logical device!"); 335 | 336 | let graphics_queue = queues.next().unwrap(); 337 | let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); 338 | 339 | (device, graphics_queue, present_queue) 340 | } 341 | 342 | fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { 343 | let events_loop = EventsLoop::new(); 344 | let surface = WindowBuilder::new() 345 | .with_title("Vulkan") 346 | .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) 347 | .build_vk_surface(&events_loop, instance.clone()) 348 | .expect("failed to create window surface!"); 349 | (events_loop, surface) 350 | } 351 | 352 | #[allow(unused)] 353 | fn main_loop(&mut self) { 354 | loop { 355 | let mut done = false; 356 | self.events_loop.poll_events(|ev| { 357 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 358 | done = true 359 | } 360 | }); 361 | if done { 362 | return; 363 | } 364 | } 365 | } 366 | } 367 | 368 | fn main() { 369 | let mut _app = HelloTriangleApplication::initialize(); 370 | // app.main_loop(); 371 | } 372 | -------------------------------------------------------------------------------- /src/bin/06_swap_chain_creation.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/05_window_surface.rs 2 | +++ b/06_swap_chain_creation.rs 3 | @@ -20,7 +20,16 @@ use vulkano::instance::debug::{DebugCallback, MessageTypes}; 4 | use vulkano::device::{Device, DeviceExtensions, Queue, Features}; 5 | use vulkano::swapchain::{ 6 | Surface, 7 | + Capabilities, 8 | + ColorSpace, 9 | + SupportedPresentModes, 10 | + PresentMode, 11 | + Swapchain, 12 | + CompositeAlpha, 13 | }; 14 | +use vulkano::format::Format; 15 | +use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; 16 | +use vulkano::sync::SharingMode; 17 | 18 | const WIDTH: u32 = 800; 19 | const HEIGHT: u32 = 600; 20 | @@ -29,6 +38,14 @@ const VALIDATION_LAYERS: &[&str] = &[ 21 | "VK_LAYER_LUNARG_standard_validation" 22 | ]; 23 | 24 | +/// Required device extensions 25 | +fn device_extensions() -> DeviceExtensions { 26 | + DeviceExtensions { 27 | + khr_swapchain: true, 28 | + .. vulkano::device::DeviceExtensions::none() 29 | + } 30 | +} 31 | + 32 | #[cfg(all(debug_assertions))] 33 | const ENABLE_VALIDATION_LAYERS: bool = true; 34 | #[cfg(not(debug_assertions))] 35 | @@ -61,6 +78,9 @@ struct HelloTriangleApplication { 36 | 37 | graphics_queue: Arc, 38 | present_queue: Arc, 39 | + 40 | + swap_chain: Arc>, 41 | + swap_chain_images: Vec>>, 42 | } 43 | 44 | impl HelloTriangleApplication { 45 | @@ -73,6 +93,9 @@ impl HelloTriangleApplication { 46 | let (device, graphics_queue, present_queue) = Self::create_logical_device( 47 | &instance, &surface, physical_device_index); 48 | 49 | + let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, 50 | + &device, &graphics_queue, &present_queue); 51 | + 52 | Self { 53 | instance, 54 | debug_callback, 55 | @@ -85,6 +108,9 @@ impl HelloTriangleApplication { 56 | 57 | graphics_queue, 58 | present_queue, 59 | + 60 | + swap_chain, 61 | + swap_chain_images, 62 | } 63 | } 64 | 65 | @@ -156,7 +182,110 @@ impl HelloTriangleApplication { 66 | 67 | fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { 68 | let indices = Self::find_queue_families(surface, device); 69 | - indices.is_complete() 70 | + let extensions_supported = Self::check_device_extension_support(device); 71 | + 72 | + let swap_chain_adequate = if extensions_supported { 73 | + let capabilities = surface.capabilities(*device) 74 | + .expect("failed to get surface capabilities"); 75 | + !capabilities.supported_formats.is_empty() && 76 | + capabilities.present_modes.iter().next().is_some() 77 | + } else { 78 | + false 79 | + }; 80 | + 81 | + indices.is_complete() && extensions_supported && swap_chain_adequate 82 | + } 83 | + 84 | + fn check_device_extension_support(device: &PhysicalDevice) -> bool { 85 | + let available_extensions = DeviceExtensions::supported_by_device(*device); 86 | + let device_extensions = device_extensions(); 87 | + available_extensions.intersection(&device_extensions) == device_extensions 88 | + } 89 | + 90 | + fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { 91 | + // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be 92 | + // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) 93 | + *available_formats.iter() 94 | + .find(|(format, color_space)| 95 | + *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear 96 | + ) 97 | + .unwrap_or_else(|| &available_formats[0]) 98 | + } 99 | + 100 | + fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { 101 | + if available_present_modes.mailbox { 102 | + PresentMode::Mailbox 103 | + } else if available_present_modes.immediate { 104 | + PresentMode::Immediate 105 | + } else { 106 | + PresentMode::Fifo 107 | + } 108 | + } 109 | + 110 | + fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { 111 | + if let Some(current_extent) = capabilities.current_extent { 112 | + return current_extent 113 | + } else { 114 | + let mut actual_extent = [WIDTH, HEIGHT]; 115 | + actual_extent[0] = capabilities.min_image_extent[0] 116 | + .max(capabilities.max_image_extent[0].min(actual_extent[0])); 117 | + actual_extent[1] = capabilities.min_image_extent[1] 118 | + .max(capabilities.max_image_extent[1].min(actual_extent[1])); 119 | + actual_extent 120 | + } 121 | + } 122 | + 123 | + fn create_swap_chain( 124 | + instance: &Arc, 125 | + surface: &Arc>, 126 | + physical_device_index: usize, 127 | + device: &Arc, 128 | + graphics_queue: &Arc, 129 | + present_queue: &Arc, 130 | + ) -> (Arc>, Vec>>) { 131 | + let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 132 | + let capabilities = surface.capabilities(physical_device) 133 | + .expect("failed to get surface capabilities"); 134 | + 135 | + let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); 136 | + let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); 137 | + let extent = Self::choose_swap_extent(&capabilities); 138 | + 139 | + let mut image_count = capabilities.min_image_count + 1; 140 | + if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { 141 | + image_count = capabilities.max_image_count.unwrap(); 142 | + } 143 | + 144 | + let image_usage = ImageUsage { 145 | + color_attachment: true, 146 | + .. ImageUsage::none() 147 | + }; 148 | + 149 | + let indices = Self::find_queue_families(&surface, &physical_device); 150 | + 151 | + let sharing: SharingMode = if indices.graphics_family != indices.present_family { 152 | + vec![graphics_queue, present_queue].as_slice().into() 153 | + } else { 154 | + graphics_queue.into() 155 | + }; 156 | + 157 | + let (swap_chain, images) = Swapchain::new( 158 | + device.clone(), 159 | + surface.clone(), 160 | + image_count, 161 | + surface_format.0, // TODO: color space? 162 | + extent, 163 | + 1, // layers 164 | + image_usage, 165 | + sharing, 166 | + capabilities.current_transform, 167 | + CompositeAlpha::Opaque, 168 | + present_mode, 169 | + true, // clipped 170 | + None, 171 | + ).expect("failed to create swap chain!"); 172 | + 173 | + (swap_chain, images) 174 | } 175 | 176 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 177 | @@ -201,7 +330,7 @@ impl HelloTriangleApplication { 178 | // for us internally. 179 | 180 | let (device, mut queues) = Device::new(physical_device, &Features::none(), 181 | - &DeviceExtensions::none(), queue_families) 182 | + &device_extensions(), queue_families) 183 | .expect("failed to create logical device!"); 184 | 185 | let graphics_queue = queues.next().unwrap(); 186 | -------------------------------------------------------------------------------- /src/bin/08_graphics_pipeline.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | extern crate vulkano_win; 3 | extern crate winit; 4 | 5 | use std::sync::Arc; 6 | use std::collections::HashSet; 7 | 8 | use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; 9 | use vulkano_win::VkSurfaceBuild; 10 | 11 | use vulkano::instance::{ 12 | Instance, 13 | InstanceExtensions, 14 | ApplicationInfo, 15 | Version, 16 | layers_list, 17 | PhysicalDevice, 18 | }; 19 | use vulkano::instance::debug::{DebugCallback, MessageTypes}; 20 | use vulkano::device::{Device, DeviceExtensions, Queue, Features}; 21 | use vulkano::swapchain::{ 22 | Surface, 23 | Capabilities, 24 | ColorSpace, 25 | SupportedPresentModes, 26 | PresentMode, 27 | Swapchain, 28 | CompositeAlpha, 29 | }; 30 | use vulkano::format::Format; 31 | use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; 32 | use vulkano::sync::SharingMode; 33 | 34 | const WIDTH: u32 = 800; 35 | const HEIGHT: u32 = 600; 36 | 37 | const VALIDATION_LAYERS: &[&str] = &[ 38 | "VK_LAYER_LUNARG_standard_validation" 39 | ]; 40 | 41 | /// Required device extensions 42 | fn device_extensions() -> DeviceExtensions { 43 | DeviceExtensions { 44 | khr_swapchain: true, 45 | .. vulkano::device::DeviceExtensions::none() 46 | } 47 | } 48 | 49 | #[cfg(all(debug_assertions))] 50 | const ENABLE_VALIDATION_LAYERS: bool = true; 51 | #[cfg(not(debug_assertions))] 52 | const ENABLE_VALIDATION_LAYERS: bool = false; 53 | 54 | struct QueueFamilyIndices { 55 | graphics_family: i32, 56 | present_family: i32, 57 | } 58 | impl QueueFamilyIndices { 59 | fn new() -> Self { 60 | Self { graphics_family: -1, present_family: -1 } 61 | } 62 | 63 | fn is_complete(&self) -> bool { 64 | self.graphics_family >= 0 && self.present_family >= 0 65 | } 66 | } 67 | 68 | #[allow(unused)] 69 | struct HelloTriangleApplication { 70 | instance: Arc, 71 | debug_callback: Option, 72 | 73 | events_loop: EventsLoop, 74 | surface: Arc>, 75 | 76 | physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) 77 | device: Arc, 78 | 79 | graphics_queue: Arc, 80 | present_queue: Arc, 81 | 82 | swap_chain: Arc>, 83 | swap_chain_images: Vec>>, 84 | } 85 | 86 | impl HelloTriangleApplication { 87 | pub fn initialize() -> Self { 88 | let instance = Self::create_instance(); 89 | let debug_callback = Self::setup_debug_callback(&instance); 90 | let (events_loop, surface) = Self::create_surface(&instance); 91 | 92 | let physical_device_index = Self::pick_physical_device(&instance, &surface); 93 | let (device, graphics_queue, present_queue) = Self::create_logical_device( 94 | &instance, &surface, physical_device_index); 95 | 96 | let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, 97 | &device, &graphics_queue, &present_queue); 98 | 99 | Self::create_graphics_pipeline(&device); 100 | 101 | Self { 102 | instance, 103 | debug_callback, 104 | 105 | events_loop, 106 | surface, 107 | 108 | physical_device_index, 109 | device, 110 | 111 | graphics_queue, 112 | present_queue, 113 | 114 | swap_chain, 115 | swap_chain_images, 116 | } 117 | } 118 | 119 | fn create_instance() -> Arc { 120 | if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { 121 | println!("Validation layers requested, but not available!") 122 | } 123 | 124 | let supported_extensions = InstanceExtensions::supported_by_core() 125 | .expect("failed to retrieve supported extensions"); 126 | println!("Supported extensions: {:?}", supported_extensions); 127 | 128 | let app_info = ApplicationInfo { 129 | application_name: Some("Hello Triangle".into()), 130 | application_version: Some(Version { major: 1, minor: 0, patch: 0 }), 131 | engine_name: Some("No Engine".into()), 132 | engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 133 | }; 134 | 135 | let required_extensions = Self::get_required_extensions(); 136 | 137 | if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { 138 | Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) 139 | .expect("failed to create Vulkan instance") 140 | } else { 141 | Instance::new(Some(&app_info), &required_extensions, None) 142 | .expect("failed to create Vulkan instance") 143 | } 144 | } 145 | 146 | fn check_validation_layer_support() -> bool { 147 | let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); 148 | VALIDATION_LAYERS.iter() 149 | .all(|layer_name| layers.contains(&layer_name.to_string())) 150 | } 151 | 152 | fn get_required_extensions() -> InstanceExtensions { 153 | let mut extensions = vulkano_win::required_extensions(); 154 | if ENABLE_VALIDATION_LAYERS { 155 | // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano 156 | extensions.ext_debug_report = true; 157 | } 158 | 159 | extensions 160 | } 161 | 162 | fn setup_debug_callback(instance: &Arc) -> Option { 163 | if !ENABLE_VALIDATION_LAYERS { 164 | return None; 165 | } 166 | 167 | let msg_types = MessageTypes { 168 | error: true, 169 | warning: true, 170 | performance_warning: true, 171 | information: false, 172 | debug: true, 173 | }; 174 | DebugCallback::new(&instance, msg_types, |msg| { 175 | println!("validation layer: {:?}", msg.description); 176 | }).ok() 177 | } 178 | 179 | fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { 180 | PhysicalDevice::enumerate(&instance) 181 | .position(|device| Self::is_device_suitable(surface, &device)) 182 | .expect("failed to find a suitable GPU!") 183 | } 184 | 185 | fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { 186 | let indices = Self::find_queue_families(surface, device); 187 | let extensions_supported = Self::check_device_extension_support(device); 188 | 189 | let swap_chain_adequate = if extensions_supported { 190 | let capabilities = surface.capabilities(*device) 191 | .expect("failed to get surface capabilities"); 192 | !capabilities.supported_formats.is_empty() && 193 | capabilities.present_modes.iter().next().is_some() 194 | } else { 195 | false 196 | }; 197 | 198 | indices.is_complete() && extensions_supported && swap_chain_adequate 199 | } 200 | 201 | fn check_device_extension_support(device: &PhysicalDevice) -> bool { 202 | let available_extensions = DeviceExtensions::supported_by_device(*device); 203 | let device_extensions = device_extensions(); 204 | available_extensions.intersection(&device_extensions) == device_extensions 205 | } 206 | 207 | fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { 208 | // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be 209 | // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) 210 | *available_formats.iter() 211 | .find(|(format, color_space)| 212 | *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear 213 | ) 214 | .unwrap_or_else(|| &available_formats[0]) 215 | } 216 | 217 | fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { 218 | if available_present_modes.mailbox { 219 | PresentMode::Mailbox 220 | } else if available_present_modes.immediate { 221 | PresentMode::Immediate 222 | } else { 223 | PresentMode::Fifo 224 | } 225 | } 226 | 227 | fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { 228 | if let Some(current_extent) = capabilities.current_extent { 229 | return current_extent 230 | } else { 231 | let mut actual_extent = [WIDTH, HEIGHT]; 232 | actual_extent[0] = capabilities.min_image_extent[0] 233 | .max(capabilities.max_image_extent[0].min(actual_extent[0])); 234 | actual_extent[1] = capabilities.min_image_extent[1] 235 | .max(capabilities.max_image_extent[1].min(actual_extent[1])); 236 | actual_extent 237 | } 238 | } 239 | 240 | fn create_swap_chain( 241 | instance: &Arc, 242 | surface: &Arc>, 243 | physical_device_index: usize, 244 | device: &Arc, 245 | graphics_queue: &Arc, 246 | present_queue: &Arc, 247 | ) -> (Arc>, Vec>>) { 248 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 249 | let capabilities = surface.capabilities(physical_device) 250 | .expect("failed to get surface capabilities"); 251 | 252 | let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); 253 | let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); 254 | let extent = Self::choose_swap_extent(&capabilities); 255 | 256 | let mut image_count = capabilities.min_image_count + 1; 257 | if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { 258 | image_count = capabilities.max_image_count.unwrap(); 259 | } 260 | 261 | let image_usage = ImageUsage { 262 | color_attachment: true, 263 | .. ImageUsage::none() 264 | }; 265 | 266 | let indices = Self::find_queue_families(&surface, &physical_device); 267 | 268 | let sharing: SharingMode = if indices.graphics_family != indices.present_family { 269 | vec![graphics_queue, present_queue].as_slice().into() 270 | } else { 271 | graphics_queue.into() 272 | }; 273 | 274 | let (swap_chain, images) = Swapchain::new( 275 | device.clone(), 276 | surface.clone(), 277 | image_count, 278 | surface_format.0, // TODO: color space? 279 | extent, 280 | 1, // layers 281 | image_usage, 282 | sharing, 283 | capabilities.current_transform, 284 | CompositeAlpha::Opaque, 285 | present_mode, 286 | true, // clipped 287 | None, 288 | ).expect("failed to create swap chain!"); 289 | 290 | (swap_chain, images) 291 | } 292 | 293 | fn create_graphics_pipeline(_device: &Arc) { 294 | 295 | } 296 | 297 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 298 | let mut indices = QueueFamilyIndices::new(); 299 | // TODO: replace index with id to simplify? 300 | for (i, queue_family) in device.queue_families().enumerate() { 301 | if queue_family.supports_graphics() { 302 | indices.graphics_family = i as i32; 303 | } 304 | 305 | if surface.is_supported(queue_family).unwrap() { 306 | indices.present_family = i as i32; 307 | } 308 | 309 | if indices.is_complete() { 310 | break; 311 | } 312 | } 313 | 314 | indices 315 | } 316 | 317 | fn create_logical_device( 318 | instance: &Arc, 319 | surface: &Arc>, 320 | physical_device_index: usize, 321 | ) -> (Arc, Arc, Arc) { 322 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 323 | let indices = Self::find_queue_families(&surface, &physical_device); 324 | 325 | let families = [indices.graphics_family, indices.present_family]; 326 | use std::iter::FromIterator; 327 | let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); 328 | 329 | let queue_priority = 1.0; 330 | let queue_families = unique_queue_families.iter().map(|i| { 331 | (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) 332 | }); 333 | 334 | // NOTE: the tutorial recommends passing the validation layers as well 335 | // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that 336 | // for us internally. 337 | 338 | let (device, mut queues) = Device::new(physical_device, &Features::none(), 339 | &device_extensions(), queue_families) 340 | .expect("failed to create logical device!"); 341 | 342 | let graphics_queue = queues.next().unwrap(); 343 | let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); 344 | 345 | (device, graphics_queue, present_queue) 346 | } 347 | 348 | fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { 349 | let events_loop = EventsLoop::new(); 350 | let surface = WindowBuilder::new() 351 | .with_title("Vulkan") 352 | .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) 353 | .build_vk_surface(&events_loop, instance.clone()) 354 | .expect("failed to create window surface!"); 355 | (events_loop, surface) 356 | } 357 | 358 | #[allow(unused)] 359 | fn main_loop(&mut self) { 360 | loop { 361 | let mut done = false; 362 | self.events_loop.poll_events(|ev| { 363 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 364 | done = true 365 | } 366 | }); 367 | if done { 368 | return; 369 | } 370 | } 371 | } 372 | } 373 | 374 | fn main() { 375 | let mut _app = HelloTriangleApplication::initialize(); 376 | // app.main_loop(); 377 | } 378 | -------------------------------------------------------------------------------- /src/bin/08_graphics_pipeline.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/06_swap_chain_creation.rs 2 | +++ b/08_graphics_pipeline.rs 3 | @@ -96,6 +96,8 @@ impl HelloTriangleApplication { 4 | let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, 5 | &device, &graphics_queue, &present_queue); 6 | 7 | + Self::create_graphics_pipeline(&device); 8 | + 9 | Self { 10 | instance, 11 | debug_callback, 12 | @@ -288,6 +290,10 @@ impl HelloTriangleApplication { 13 | (swap_chain, images) 14 | } 15 | 16 | + fn create_graphics_pipeline(_device: &Arc) { 17 | + 18 | + } 19 | + 20 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 21 | let mut indices = QueueFamilyIndices::new(); 22 | // TODO: replace index with id to simplify? 23 | -------------------------------------------------------------------------------- /src/bin/09_shader_base.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec3 fragColor; 5 | 6 | layout(location = 0) out vec4 outColor; 7 | 8 | void main() { 9 | outColor = vec4(fragColor, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /src/bin/09_shader_base.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | out gl_PerVertex { 5 | vec4 gl_Position; 6 | }; 7 | 8 | layout(location = 0) out vec3 fragColor; 9 | 10 | vec2 positions[3] = vec2[]( 11 | vec2(0.0, -0.5), 12 | vec2(0.5, 0.5), 13 | vec2(-0.5, 0.5) 14 | ); 15 | 16 | vec3 colors[3] = vec3[]( 17 | vec3(1.0, 0.0, 0.0), 18 | vec3(0.0, 1.0, 0.0), 19 | vec3(0.0, 0.0, 1.0) 20 | ); 21 | 22 | void main() { 23 | gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); 24 | fragColor = colors[gl_VertexIndex]; 25 | } 26 | -------------------------------------------------------------------------------- /src/bin/09_shader_modules.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | extern crate vulkano_win; 3 | extern crate winit; 4 | 5 | use std::sync::Arc; 6 | use std::collections::HashSet; 7 | 8 | use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; 9 | use vulkano_win::VkSurfaceBuild; 10 | 11 | use vulkano::instance::{ 12 | Instance, 13 | InstanceExtensions, 14 | ApplicationInfo, 15 | Version, 16 | layers_list, 17 | PhysicalDevice, 18 | }; 19 | use vulkano::instance::debug::{DebugCallback, MessageTypes}; 20 | use vulkano::device::{Device, DeviceExtensions, Queue, Features}; 21 | use vulkano::swapchain::{ 22 | Surface, 23 | Capabilities, 24 | ColorSpace, 25 | SupportedPresentModes, 26 | PresentMode, 27 | Swapchain, 28 | CompositeAlpha, 29 | }; 30 | use vulkano::format::Format; 31 | use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; 32 | use vulkano::sync::SharingMode; 33 | 34 | const WIDTH: u32 = 800; 35 | const HEIGHT: u32 = 600; 36 | 37 | const VALIDATION_LAYERS: &[&str] = &[ 38 | "VK_LAYER_LUNARG_standard_validation" 39 | ]; 40 | 41 | /// Required device extensions 42 | fn device_extensions() -> DeviceExtensions { 43 | DeviceExtensions { 44 | khr_swapchain: true, 45 | .. vulkano::device::DeviceExtensions::none() 46 | } 47 | } 48 | 49 | #[cfg(all(debug_assertions))] 50 | const ENABLE_VALIDATION_LAYERS: bool = true; 51 | #[cfg(not(debug_assertions))] 52 | const ENABLE_VALIDATION_LAYERS: bool = false; 53 | 54 | struct QueueFamilyIndices { 55 | graphics_family: i32, 56 | present_family: i32, 57 | } 58 | impl QueueFamilyIndices { 59 | fn new() -> Self { 60 | Self { graphics_family: -1, present_family: -1 } 61 | } 62 | 63 | fn is_complete(&self) -> bool { 64 | self.graphics_family >= 0 && self.present_family >= 0 65 | } 66 | } 67 | 68 | #[allow(unused)] 69 | struct HelloTriangleApplication { 70 | instance: Arc, 71 | debug_callback: Option, 72 | 73 | events_loop: EventsLoop, 74 | surface: Arc>, 75 | 76 | physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) 77 | device: Arc, 78 | 79 | graphics_queue: Arc, 80 | present_queue: Arc, 81 | 82 | swap_chain: Arc>, 83 | swap_chain_images: Vec>>, 84 | } 85 | 86 | impl HelloTriangleApplication { 87 | pub fn initialize() -> Self { 88 | let instance = Self::create_instance(); 89 | let debug_callback = Self::setup_debug_callback(&instance); 90 | let (events_loop, surface) = Self::create_surface(&instance); 91 | 92 | let physical_device_index = Self::pick_physical_device(&instance, &surface); 93 | let (device, graphics_queue, present_queue) = Self::create_logical_device( 94 | &instance, &surface, physical_device_index); 95 | 96 | let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, 97 | &device, &graphics_queue, &present_queue); 98 | 99 | Self::create_graphics_pipeline(&device); 100 | 101 | Self { 102 | instance, 103 | debug_callback, 104 | 105 | events_loop, 106 | surface, 107 | 108 | physical_device_index, 109 | device, 110 | 111 | graphics_queue, 112 | present_queue, 113 | 114 | swap_chain, 115 | swap_chain_images, 116 | } 117 | } 118 | 119 | fn create_instance() -> Arc { 120 | if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { 121 | println!("Validation layers requested, but not available!") 122 | } 123 | 124 | let supported_extensions = InstanceExtensions::supported_by_core() 125 | .expect("failed to retrieve supported extensions"); 126 | println!("Supported extensions: {:?}", supported_extensions); 127 | 128 | let app_info = ApplicationInfo { 129 | application_name: Some("Hello Triangle".into()), 130 | application_version: Some(Version { major: 1, minor: 0, patch: 0 }), 131 | engine_name: Some("No Engine".into()), 132 | engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 133 | }; 134 | 135 | let required_extensions = Self::get_required_extensions(); 136 | 137 | if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { 138 | Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) 139 | .expect("failed to create Vulkan instance") 140 | } else { 141 | Instance::new(Some(&app_info), &required_extensions, None) 142 | .expect("failed to create Vulkan instance") 143 | } 144 | } 145 | 146 | fn check_validation_layer_support() -> bool { 147 | let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); 148 | VALIDATION_LAYERS.iter() 149 | .all(|layer_name| layers.contains(&layer_name.to_string())) 150 | } 151 | 152 | fn get_required_extensions() -> InstanceExtensions { 153 | let mut extensions = vulkano_win::required_extensions(); 154 | if ENABLE_VALIDATION_LAYERS { 155 | // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano 156 | extensions.ext_debug_report = true; 157 | } 158 | 159 | extensions 160 | } 161 | 162 | fn setup_debug_callback(instance: &Arc) -> Option { 163 | if !ENABLE_VALIDATION_LAYERS { 164 | return None; 165 | } 166 | 167 | let msg_types = MessageTypes { 168 | error: true, 169 | warning: true, 170 | performance_warning: true, 171 | information: false, 172 | debug: true, 173 | }; 174 | DebugCallback::new(&instance, msg_types, |msg| { 175 | println!("validation layer: {:?}", msg.description); 176 | }).ok() 177 | } 178 | 179 | fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { 180 | PhysicalDevice::enumerate(&instance) 181 | .position(|device| Self::is_device_suitable(surface, &device)) 182 | .expect("failed to find a suitable GPU!") 183 | } 184 | 185 | fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { 186 | let indices = Self::find_queue_families(surface, device); 187 | let extensions_supported = Self::check_device_extension_support(device); 188 | 189 | let swap_chain_adequate = if extensions_supported { 190 | let capabilities = surface.capabilities(*device) 191 | .expect("failed to get surface capabilities"); 192 | !capabilities.supported_formats.is_empty() && 193 | capabilities.present_modes.iter().next().is_some() 194 | } else { 195 | false 196 | }; 197 | 198 | indices.is_complete() && extensions_supported && swap_chain_adequate 199 | } 200 | 201 | fn check_device_extension_support(device: &PhysicalDevice) -> bool { 202 | let available_extensions = DeviceExtensions::supported_by_device(*device); 203 | let device_extensions = device_extensions(); 204 | available_extensions.intersection(&device_extensions) == device_extensions 205 | } 206 | 207 | fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { 208 | // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be 209 | // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) 210 | *available_formats.iter() 211 | .find(|(format, color_space)| 212 | *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear 213 | ) 214 | .unwrap_or_else(|| &available_formats[0]) 215 | } 216 | 217 | fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { 218 | if available_present_modes.mailbox { 219 | PresentMode::Mailbox 220 | } else if available_present_modes.immediate { 221 | PresentMode::Immediate 222 | } else { 223 | PresentMode::Fifo 224 | } 225 | } 226 | 227 | fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { 228 | if let Some(current_extent) = capabilities.current_extent { 229 | return current_extent 230 | } else { 231 | let mut actual_extent = [WIDTH, HEIGHT]; 232 | actual_extent[0] = capabilities.min_image_extent[0] 233 | .max(capabilities.max_image_extent[0].min(actual_extent[0])); 234 | actual_extent[1] = capabilities.min_image_extent[1] 235 | .max(capabilities.max_image_extent[1].min(actual_extent[1])); 236 | actual_extent 237 | } 238 | } 239 | 240 | fn create_swap_chain( 241 | instance: &Arc, 242 | surface: &Arc>, 243 | physical_device_index: usize, 244 | device: &Arc, 245 | graphics_queue: &Arc, 246 | present_queue: &Arc, 247 | ) -> (Arc>, Vec>>) { 248 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 249 | let capabilities = surface.capabilities(physical_device) 250 | .expect("failed to get surface capabilities"); 251 | 252 | let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); 253 | let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); 254 | let extent = Self::choose_swap_extent(&capabilities); 255 | 256 | let mut image_count = capabilities.min_image_count + 1; 257 | if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { 258 | image_count = capabilities.max_image_count.unwrap(); 259 | } 260 | 261 | let image_usage = ImageUsage { 262 | color_attachment: true, 263 | .. ImageUsage::none() 264 | }; 265 | 266 | let indices = Self::find_queue_families(&surface, &physical_device); 267 | 268 | let sharing: SharingMode = if indices.graphics_family != indices.present_family { 269 | vec![graphics_queue, present_queue].as_slice().into() 270 | } else { 271 | graphics_queue.into() 272 | }; 273 | 274 | let (swap_chain, images) = Swapchain::new( 275 | device.clone(), 276 | surface.clone(), 277 | image_count, 278 | surface_format.0, // TODO: color space? 279 | extent, 280 | 1, // layers 281 | image_usage, 282 | sharing, 283 | capabilities.current_transform, 284 | CompositeAlpha::Opaque, 285 | present_mode, 286 | true, // clipped 287 | None, 288 | ).expect("failed to create swap chain!"); 289 | 290 | (swap_chain, images) 291 | } 292 | 293 | fn create_graphics_pipeline( 294 | device: &Arc, 295 | ) { 296 | mod vertex_shader { 297 | vulkano_shaders::shader! { 298 | ty: "vertex", 299 | path: "src/bin/09_shader_base.vert" 300 | } 301 | } 302 | 303 | mod fragment_shader { 304 | vulkano_shaders::shader! { 305 | ty: "fragment", 306 | path: "src/bin/09_shader_base.frag" 307 | } 308 | } 309 | 310 | let _vert_shader_module = vertex_shader::Shader::load(device.clone()) 311 | .expect("failed to create vertex shader module!"); 312 | let _frag_shader_module = fragment_shader::Shader::load(device.clone()) 313 | .expect("failed to create fragment shader module!"); 314 | } 315 | 316 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 317 | let mut indices = QueueFamilyIndices::new(); 318 | // TODO: replace index with id to simplify? 319 | for (i, queue_family) in device.queue_families().enumerate() { 320 | if queue_family.supports_graphics() { 321 | indices.graphics_family = i as i32; 322 | } 323 | 324 | if surface.is_supported(queue_family).unwrap() { 325 | indices.present_family = i as i32; 326 | } 327 | 328 | if indices.is_complete() { 329 | break; 330 | } 331 | } 332 | 333 | indices 334 | } 335 | 336 | fn create_logical_device( 337 | instance: &Arc, 338 | surface: &Arc>, 339 | physical_device_index: usize, 340 | ) -> (Arc, Arc, Arc) { 341 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 342 | let indices = Self::find_queue_families(&surface, &physical_device); 343 | 344 | let families = [indices.graphics_family, indices.present_family]; 345 | use std::iter::FromIterator; 346 | let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); 347 | 348 | let queue_priority = 1.0; 349 | let queue_families = unique_queue_families.iter().map(|i| { 350 | (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) 351 | }); 352 | 353 | // NOTE: the tutorial recommends passing the validation layers as well 354 | // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that 355 | // for us internally. 356 | 357 | let (device, mut queues) = Device::new(physical_device, &Features::none(), 358 | &device_extensions(), queue_families) 359 | .expect("failed to create logical device!"); 360 | 361 | let graphics_queue = queues.next().unwrap(); 362 | let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); 363 | 364 | (device, graphics_queue, present_queue) 365 | } 366 | 367 | fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { 368 | let events_loop = EventsLoop::new(); 369 | let surface = WindowBuilder::new() 370 | .with_title("Vulkan") 371 | .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) 372 | .build_vk_surface(&events_loop, instance.clone()) 373 | .expect("failed to create window surface!"); 374 | (events_loop, surface) 375 | } 376 | 377 | #[allow(unused)] 378 | fn main_loop(&mut self) { 379 | loop { 380 | let mut done = false; 381 | self.events_loop.poll_events(|ev| { 382 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 383 | done = true 384 | } 385 | }); 386 | if done { 387 | return; 388 | } 389 | } 390 | } 391 | } 392 | 393 | fn main() { 394 | let mut _app = HelloTriangleApplication::initialize(); 395 | // app.main_loop(); 396 | } 397 | -------------------------------------------------------------------------------- /src/bin/09_shader_modules.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/08_graphics_pipeline.rs 2 | +++ b/09_shader_modules.rs 3 | @@ -290,8 +290,27 @@ impl HelloTriangleApplication { 4 | (swap_chain, images) 5 | } 6 | 7 | - fn create_graphics_pipeline(_device: &Arc) { 8 | + fn create_graphics_pipeline( 9 | + device: &Arc, 10 | + ) { 11 | + mod vertex_shader { 12 | + vulkano_shaders::shader! { 13 | + ty: "vertex", 14 | + path: "src/bin/09_shader_base.vert" 15 | + } 16 | + } 17 | + 18 | + mod fragment_shader { 19 | + vulkano_shaders::shader! { 20 | + ty: "fragment", 21 | + path: "src/bin/09_shader_base.frag" 22 | + } 23 | + } 24 | 25 | + let _vert_shader_module = vertex_shader::Shader::load(device.clone()) 26 | + .expect("failed to create vertex shader module!"); 27 | + let _frag_shader_module = fragment_shader::Shader::load(device.clone()) 28 | + .expect("failed to create fragment shader module!"); 29 | } 30 | 31 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 32 | -------------------------------------------------------------------------------- /src/bin/10_fixed_functions.rs: -------------------------------------------------------------------------------- 1 | extern crate vulkano; 2 | extern crate vulkano_win; 3 | extern crate winit; 4 | 5 | use std::sync::Arc; 6 | use std::collections::HashSet; 7 | 8 | use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; 9 | use vulkano_win::VkSurfaceBuild; 10 | 11 | use vulkano::instance::{ 12 | Instance, 13 | InstanceExtensions, 14 | ApplicationInfo, 15 | Version, 16 | layers_list, 17 | PhysicalDevice, 18 | }; 19 | use vulkano::instance::debug::{DebugCallback, MessageTypes}; 20 | use vulkano::device::{Device, DeviceExtensions, Queue, Features}; 21 | use vulkano::swapchain::{ 22 | Surface, 23 | Capabilities, 24 | ColorSpace, 25 | SupportedPresentModes, 26 | PresentMode, 27 | Swapchain, 28 | CompositeAlpha, 29 | }; 30 | use vulkano::format::Format; 31 | use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; 32 | use vulkano::sync::SharingMode; 33 | use vulkano::pipeline::{ 34 | GraphicsPipeline, 35 | vertex::BufferlessDefinition, 36 | viewport::Viewport, 37 | }; 38 | 39 | const WIDTH: u32 = 800; 40 | const HEIGHT: u32 = 600; 41 | 42 | const VALIDATION_LAYERS: &[&str] = &[ 43 | "VK_LAYER_LUNARG_standard_validation" 44 | ]; 45 | 46 | /// Required device extensions 47 | fn device_extensions() -> DeviceExtensions { 48 | DeviceExtensions { 49 | khr_swapchain: true, 50 | .. vulkano::device::DeviceExtensions::none() 51 | } 52 | } 53 | 54 | #[cfg(all(debug_assertions))] 55 | const ENABLE_VALIDATION_LAYERS: bool = true; 56 | #[cfg(not(debug_assertions))] 57 | const ENABLE_VALIDATION_LAYERS: bool = false; 58 | 59 | struct QueueFamilyIndices { 60 | graphics_family: i32, 61 | present_family: i32, 62 | } 63 | impl QueueFamilyIndices { 64 | fn new() -> Self { 65 | Self { graphics_family: -1, present_family: -1 } 66 | } 67 | 68 | fn is_complete(&self) -> bool { 69 | self.graphics_family >= 0 && self.present_family >= 0 70 | } 71 | } 72 | 73 | #[allow(unused)] 74 | struct HelloTriangleApplication { 75 | instance: Arc, 76 | debug_callback: Option, 77 | 78 | events_loop: EventsLoop, 79 | surface: Arc>, 80 | 81 | physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) 82 | device: Arc, 83 | 84 | graphics_queue: Arc, 85 | present_queue: Arc, 86 | 87 | swap_chain: Arc>, 88 | swap_chain_images: Vec>>, 89 | } 90 | 91 | impl HelloTriangleApplication { 92 | pub fn initialize() -> Self { 93 | let instance = Self::create_instance(); 94 | let debug_callback = Self::setup_debug_callback(&instance); 95 | let (events_loop, surface) = Self::create_surface(&instance); 96 | 97 | let physical_device_index = Self::pick_physical_device(&instance, &surface); 98 | let (device, graphics_queue, present_queue) = Self::create_logical_device( 99 | &instance, &surface, physical_device_index); 100 | 101 | let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, 102 | &device, &graphics_queue, &present_queue); 103 | 104 | Self::create_graphics_pipeline(&device, swap_chain.dimensions()); 105 | 106 | Self { 107 | instance, 108 | debug_callback, 109 | 110 | events_loop, 111 | surface, 112 | 113 | physical_device_index, 114 | device, 115 | 116 | graphics_queue, 117 | present_queue, 118 | 119 | swap_chain, 120 | swap_chain_images, 121 | } 122 | } 123 | 124 | fn create_instance() -> Arc { 125 | if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { 126 | println!("Validation layers requested, but not available!") 127 | } 128 | 129 | let supported_extensions = InstanceExtensions::supported_by_core() 130 | .expect("failed to retrieve supported extensions"); 131 | println!("Supported extensions: {:?}", supported_extensions); 132 | 133 | let app_info = ApplicationInfo { 134 | application_name: Some("Hello Triangle".into()), 135 | application_version: Some(Version { major: 1, minor: 0, patch: 0 }), 136 | engine_name: Some("No Engine".into()), 137 | engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 138 | }; 139 | 140 | let required_extensions = Self::get_required_extensions(); 141 | 142 | if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { 143 | Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) 144 | .expect("failed to create Vulkan instance") 145 | } else { 146 | Instance::new(Some(&app_info), &required_extensions, None) 147 | .expect("failed to create Vulkan instance") 148 | } 149 | } 150 | 151 | fn check_validation_layer_support() -> bool { 152 | let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); 153 | VALIDATION_LAYERS.iter() 154 | .all(|layer_name| layers.contains(&layer_name.to_string())) 155 | } 156 | 157 | fn get_required_extensions() -> InstanceExtensions { 158 | let mut extensions = vulkano_win::required_extensions(); 159 | if ENABLE_VALIDATION_LAYERS { 160 | // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano 161 | extensions.ext_debug_report = true; 162 | } 163 | 164 | extensions 165 | } 166 | 167 | fn setup_debug_callback(instance: &Arc) -> Option { 168 | if !ENABLE_VALIDATION_LAYERS { 169 | return None; 170 | } 171 | 172 | let msg_types = MessageTypes { 173 | error: true, 174 | warning: true, 175 | performance_warning: true, 176 | information: false, 177 | debug: true, 178 | }; 179 | DebugCallback::new(&instance, msg_types, |msg| { 180 | println!("validation layer: {:?}", msg.description); 181 | }).ok() 182 | } 183 | 184 | fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { 185 | PhysicalDevice::enumerate(&instance) 186 | .position(|device| Self::is_device_suitable(surface, &device)) 187 | .expect("failed to find a suitable GPU!") 188 | } 189 | 190 | fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { 191 | let indices = Self::find_queue_families(surface, device); 192 | let extensions_supported = Self::check_device_extension_support(device); 193 | 194 | let swap_chain_adequate = if extensions_supported { 195 | let capabilities = surface.capabilities(*device) 196 | .expect("failed to get surface capabilities"); 197 | !capabilities.supported_formats.is_empty() && 198 | capabilities.present_modes.iter().next().is_some() 199 | } else { 200 | false 201 | }; 202 | 203 | indices.is_complete() && extensions_supported && swap_chain_adequate 204 | } 205 | 206 | fn check_device_extension_support(device: &PhysicalDevice) -> bool { 207 | let available_extensions = DeviceExtensions::supported_by_device(*device); 208 | let device_extensions = device_extensions(); 209 | available_extensions.intersection(&device_extensions) == device_extensions 210 | } 211 | 212 | fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { 213 | // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be 214 | // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) 215 | *available_formats.iter() 216 | .find(|(format, color_space)| 217 | *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear 218 | ) 219 | .unwrap_or_else(|| &available_formats[0]) 220 | } 221 | 222 | fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { 223 | if available_present_modes.mailbox { 224 | PresentMode::Mailbox 225 | } else if available_present_modes.immediate { 226 | PresentMode::Immediate 227 | } else { 228 | PresentMode::Fifo 229 | } 230 | } 231 | 232 | fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { 233 | if let Some(current_extent) = capabilities.current_extent { 234 | return current_extent 235 | } else { 236 | let mut actual_extent = [WIDTH, HEIGHT]; 237 | actual_extent[0] = capabilities.min_image_extent[0] 238 | .max(capabilities.max_image_extent[0].min(actual_extent[0])); 239 | actual_extent[1] = capabilities.min_image_extent[1] 240 | .max(capabilities.max_image_extent[1].min(actual_extent[1])); 241 | actual_extent 242 | } 243 | } 244 | 245 | fn create_swap_chain( 246 | instance: &Arc, 247 | surface: &Arc>, 248 | physical_device_index: usize, 249 | device: &Arc, 250 | graphics_queue: &Arc, 251 | present_queue: &Arc, 252 | ) -> (Arc>, Vec>>) { 253 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 254 | let capabilities = surface.capabilities(physical_device) 255 | .expect("failed to get surface capabilities"); 256 | 257 | let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); 258 | let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); 259 | let extent = Self::choose_swap_extent(&capabilities); 260 | 261 | let mut image_count = capabilities.min_image_count + 1; 262 | if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { 263 | image_count = capabilities.max_image_count.unwrap(); 264 | } 265 | 266 | let image_usage = ImageUsage { 267 | color_attachment: true, 268 | .. ImageUsage::none() 269 | }; 270 | 271 | let indices = Self::find_queue_families(&surface, &physical_device); 272 | 273 | let sharing: SharingMode = if indices.graphics_family != indices.present_family { 274 | vec![graphics_queue, present_queue].as_slice().into() 275 | } else { 276 | graphics_queue.into() 277 | }; 278 | 279 | let (swap_chain, images) = Swapchain::new( 280 | device.clone(), 281 | surface.clone(), 282 | image_count, 283 | surface_format.0, // TODO: color space? 284 | extent, 285 | 1, // layers 286 | image_usage, 287 | sharing, 288 | capabilities.current_transform, 289 | CompositeAlpha::Opaque, 290 | present_mode, 291 | true, // clipped 292 | None, 293 | ).expect("failed to create swap chain!"); 294 | 295 | (swap_chain, images) 296 | } 297 | 298 | fn create_graphics_pipeline( 299 | device: &Arc, 300 | swap_chain_extent: [u32; 2], 301 | ) { 302 | mod vertex_shader { 303 | vulkano_shaders::shader! { 304 | ty: "vertex", 305 | path: "src/bin/09_shader_base.vert" 306 | } 307 | } 308 | 309 | mod fragment_shader { 310 | vulkano_shaders::shader! { 311 | ty: "fragment", 312 | path: "src/bin/09_shader_base.frag" 313 | } 314 | } 315 | 316 | let vert_shader_module = vertex_shader::Shader::load(device.clone()) 317 | .expect("failed to create vertex shader module!"); 318 | let frag_shader_module = fragment_shader::Shader::load(device.clone()) 319 | .expect("failed to create fragment shader module!"); 320 | 321 | let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; 322 | let viewport = Viewport { 323 | origin: [0.0, 0.0], 324 | dimensions, 325 | depth_range: 0.0 .. 1.0, 326 | }; 327 | 328 | let _pipeline_builder = Arc::new(GraphicsPipeline::start() 329 | .vertex_input(BufferlessDefinition {}) 330 | .vertex_shader(vert_shader_module.main_entry_point(), ()) 331 | .triangle_list() 332 | .primitive_restart(false) 333 | .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport 334 | .fragment_shader(frag_shader_module.main_entry_point(), ()) 335 | .depth_clamp(false) 336 | // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... 337 | .polygon_mode_fill() // = default 338 | .line_width(1.0) // = default 339 | .cull_mode_back() 340 | .front_face_clockwise() 341 | // NOTE: no depth_bias here, but on pipeline::raster::Rasterization 342 | .blend_pass_through() // = default 343 | ); 344 | } 345 | 346 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 347 | let mut indices = QueueFamilyIndices::new(); 348 | // TODO: replace index with id to simplify? 349 | for (i, queue_family) in device.queue_families().enumerate() { 350 | if queue_family.supports_graphics() { 351 | indices.graphics_family = i as i32; 352 | } 353 | 354 | if surface.is_supported(queue_family).unwrap() { 355 | indices.present_family = i as i32; 356 | } 357 | 358 | if indices.is_complete() { 359 | break; 360 | } 361 | } 362 | 363 | indices 364 | } 365 | 366 | fn create_logical_device( 367 | instance: &Arc, 368 | surface: &Arc>, 369 | physical_device_index: usize, 370 | ) -> (Arc, Arc, Arc) { 371 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 372 | let indices = Self::find_queue_families(&surface, &physical_device); 373 | 374 | let families = [indices.graphics_family, indices.present_family]; 375 | use std::iter::FromIterator; 376 | let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); 377 | 378 | let queue_priority = 1.0; 379 | let queue_families = unique_queue_families.iter().map(|i| { 380 | (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) 381 | }); 382 | 383 | // NOTE: the tutorial recommends passing the validation layers as well 384 | // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that 385 | // for us internally. 386 | 387 | let (device, mut queues) = Device::new(physical_device, &Features::none(), 388 | &device_extensions(), queue_families) 389 | .expect("failed to create logical device!"); 390 | 391 | let graphics_queue = queues.next().unwrap(); 392 | let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); 393 | 394 | (device, graphics_queue, present_queue) 395 | } 396 | 397 | fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { 398 | let events_loop = EventsLoop::new(); 399 | let surface = WindowBuilder::new() 400 | .with_title("Vulkan") 401 | .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) 402 | .build_vk_surface(&events_loop, instance.clone()) 403 | .expect("failed to create window surface!"); 404 | (events_loop, surface) 405 | } 406 | 407 | #[allow(unused)] 408 | fn main_loop(&mut self) { 409 | loop { 410 | let mut done = false; 411 | self.events_loop.poll_events(|ev| { 412 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 413 | done = true 414 | } 415 | }); 416 | if done { 417 | return; 418 | } 419 | } 420 | } 421 | } 422 | 423 | fn main() { 424 | let mut _app = HelloTriangleApplication::initialize(); 425 | // app.main_loop(); 426 | } 427 | -------------------------------------------------------------------------------- /src/bin/10_fixed_functions.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/08_graphics_pipeline.rs 2 | +++ b/10_fixed_functions.rs 3 | @@ -30,6 +30,11 @@ use vulkano::swapchain::{ 4 | use vulkano::format::Format; 5 | use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; 6 | use vulkano::sync::SharingMode; 7 | +use vulkano::pipeline::{ 8 | + GraphicsPipeline, 9 | + vertex::BufferlessDefinition, 10 | + viewport::Viewport, 11 | +}; 12 | 13 | const WIDTH: u32 = 800; 14 | const HEIGHT: u32 = 600; 15 | @@ -96,7 +101,7 @@ impl HelloTriangleApplication { 16 | let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, 17 | &device, &graphics_queue, &present_queue); 18 | 19 | - Self::create_graphics_pipeline(&device); 20 | + Self::create_graphics_pipeline(&device, swap_chain.dimensions()); 21 | 22 | Self { 23 | instance, 24 | @@ -290,8 +295,52 @@ impl HelloTriangleApplication { 25 | (swap_chain, images) 26 | } 27 | 28 | - fn create_graphics_pipeline(_device: &Arc) { 29 | + fn create_graphics_pipeline( 30 | + device: &Arc, 31 | + swap_chain_extent: [u32; 2], 32 | + ) { 33 | + mod vertex_shader { 34 | + vulkano_shaders::shader! { 35 | + ty: "vertex", 36 | + path: "src/bin/09_shader_base.vert" 37 | + } 38 | + } 39 | + 40 | + mod fragment_shader { 41 | + vulkano_shaders::shader! { 42 | + ty: "fragment", 43 | + path: "src/bin/09_shader_base.frag" 44 | + } 45 | + } 46 | + 47 | + let vert_shader_module = vertex_shader::Shader::load(device.clone()) 48 | + .expect("failed to create vertex shader module!"); 49 | + let frag_shader_module = fragment_shader::Shader::load(device.clone()) 50 | + .expect("failed to create fragment shader module!"); 51 | + 52 | + let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; 53 | + let viewport = Viewport { 54 | + origin: [0.0, 0.0], 55 | + dimensions, 56 | + depth_range: 0.0 .. 1.0, 57 | + }; 58 | 59 | + let _pipeline_builder = Arc::new(GraphicsPipeline::start() 60 | + .vertex_input(BufferlessDefinition {}) 61 | + .vertex_shader(vert_shader_module.main_entry_point(), ()) 62 | + .triangle_list() 63 | + .primitive_restart(false) 64 | + .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport 65 | + .fragment_shader(frag_shader_module.main_entry_point(), ()) 66 | + .depth_clamp(false) 67 | + // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... 68 | + .polygon_mode_fill() // = default 69 | + .line_width(1.0) // = default 70 | + .cull_mode_back() 71 | + .front_face_clockwise() 72 | + // NOTE: no depth_bias here, but on pipeline::raster::Rasterization 73 | + .blend_pass_through() // = default 74 | + ); 75 | } 76 | 77 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 78 | -------------------------------------------------------------------------------- /src/bin/11_render_passes.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate vulkano; 3 | extern crate vulkano_win; 4 | extern crate winit; 5 | 6 | use std::sync::Arc; 7 | use std::collections::HashSet; 8 | 9 | use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; 10 | use vulkano_win::VkSurfaceBuild; 11 | 12 | use vulkano::instance::{ 13 | Instance, 14 | InstanceExtensions, 15 | ApplicationInfo, 16 | Version, 17 | layers_list, 18 | PhysicalDevice, 19 | }; 20 | use vulkano::instance::debug::{DebugCallback, MessageTypes}; 21 | use vulkano::device::{Device, DeviceExtensions, Queue, Features}; 22 | use vulkano::swapchain::{ 23 | Surface, 24 | Capabilities, 25 | ColorSpace, 26 | SupportedPresentModes, 27 | PresentMode, 28 | Swapchain, 29 | CompositeAlpha, 30 | }; 31 | use vulkano::format::Format; 32 | use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; 33 | use vulkano::sync::SharingMode; 34 | use vulkano::pipeline::{ 35 | GraphicsPipeline, 36 | vertex::BufferlessDefinition, 37 | viewport::Viewport, 38 | }; 39 | use vulkano::framebuffer::{ 40 | RenderPassAbstract, 41 | }; 42 | 43 | const WIDTH: u32 = 800; 44 | const HEIGHT: u32 = 600; 45 | 46 | const VALIDATION_LAYERS: &[&str] = &[ 47 | "VK_LAYER_LUNARG_standard_validation" 48 | ]; 49 | 50 | /// Required device extensions 51 | fn device_extensions() -> DeviceExtensions { 52 | DeviceExtensions { 53 | khr_swapchain: true, 54 | .. vulkano::device::DeviceExtensions::none() 55 | } 56 | } 57 | 58 | #[cfg(all(debug_assertions))] 59 | const ENABLE_VALIDATION_LAYERS: bool = true; 60 | #[cfg(not(debug_assertions))] 61 | const ENABLE_VALIDATION_LAYERS: bool = false; 62 | 63 | struct QueueFamilyIndices { 64 | graphics_family: i32, 65 | present_family: i32, 66 | } 67 | impl QueueFamilyIndices { 68 | fn new() -> Self { 69 | Self { graphics_family: -1, present_family: -1 } 70 | } 71 | 72 | fn is_complete(&self) -> bool { 73 | self.graphics_family >= 0 && self.present_family >= 0 74 | } 75 | } 76 | 77 | #[allow(unused)] 78 | struct HelloTriangleApplication { 79 | instance: Arc, 80 | debug_callback: Option, 81 | 82 | events_loop: EventsLoop, 83 | surface: Arc>, 84 | 85 | physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) 86 | device: Arc, 87 | 88 | graphics_queue: Arc, 89 | present_queue: Arc, 90 | 91 | swap_chain: Arc>, 92 | swap_chain_images: Vec>>, 93 | 94 | render_pass: Arc, 95 | } 96 | 97 | impl HelloTriangleApplication { 98 | pub fn initialize() -> Self { 99 | let instance = Self::create_instance(); 100 | let debug_callback = Self::setup_debug_callback(&instance); 101 | let (events_loop, surface) = Self::create_surface(&instance); 102 | 103 | let physical_device_index = Self::pick_physical_device(&instance, &surface); 104 | let (device, graphics_queue, present_queue) = Self::create_logical_device( 105 | &instance, &surface, physical_device_index); 106 | 107 | let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, 108 | &device, &graphics_queue, &present_queue); 109 | 110 | let render_pass = Self::create_render_pass(&device, swap_chain.format()); 111 | Self::create_graphics_pipeline(&device, swap_chain.dimensions()); 112 | 113 | Self { 114 | instance, 115 | debug_callback, 116 | 117 | events_loop, 118 | surface, 119 | 120 | physical_device_index, 121 | device, 122 | 123 | graphics_queue, 124 | present_queue, 125 | 126 | swap_chain, 127 | swap_chain_images, 128 | 129 | render_pass, 130 | } 131 | } 132 | 133 | fn create_instance() -> Arc { 134 | if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { 135 | println!("Validation layers requested, but not available!") 136 | } 137 | 138 | let supported_extensions = InstanceExtensions::supported_by_core() 139 | .expect("failed to retrieve supported extensions"); 140 | println!("Supported extensions: {:?}", supported_extensions); 141 | 142 | let app_info = ApplicationInfo { 143 | application_name: Some("Hello Triangle".into()), 144 | application_version: Some(Version { major: 1, minor: 0, patch: 0 }), 145 | engine_name: Some("No Engine".into()), 146 | engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 147 | }; 148 | 149 | let required_extensions = Self::get_required_extensions(); 150 | 151 | if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { 152 | Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) 153 | .expect("failed to create Vulkan instance") 154 | } else { 155 | Instance::new(Some(&app_info), &required_extensions, None) 156 | .expect("failed to create Vulkan instance") 157 | } 158 | } 159 | 160 | fn check_validation_layer_support() -> bool { 161 | let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); 162 | VALIDATION_LAYERS.iter() 163 | .all(|layer_name| layers.contains(&layer_name.to_string())) 164 | } 165 | 166 | fn get_required_extensions() -> InstanceExtensions { 167 | let mut extensions = vulkano_win::required_extensions(); 168 | if ENABLE_VALIDATION_LAYERS { 169 | // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano 170 | extensions.ext_debug_report = true; 171 | } 172 | 173 | extensions 174 | } 175 | 176 | fn setup_debug_callback(instance: &Arc) -> Option { 177 | if !ENABLE_VALIDATION_LAYERS { 178 | return None; 179 | } 180 | 181 | let msg_types = MessageTypes { 182 | error: true, 183 | warning: true, 184 | performance_warning: true, 185 | information: false, 186 | debug: true, 187 | }; 188 | DebugCallback::new(&instance, msg_types, |msg| { 189 | println!("validation layer: {:?}", msg.description); 190 | }).ok() 191 | } 192 | 193 | fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { 194 | PhysicalDevice::enumerate(&instance) 195 | .position(|device| Self::is_device_suitable(surface, &device)) 196 | .expect("failed to find a suitable GPU!") 197 | } 198 | 199 | fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { 200 | let indices = Self::find_queue_families(surface, device); 201 | let extensions_supported = Self::check_device_extension_support(device); 202 | 203 | let swap_chain_adequate = if extensions_supported { 204 | let capabilities = surface.capabilities(*device) 205 | .expect("failed to get surface capabilities"); 206 | !capabilities.supported_formats.is_empty() && 207 | capabilities.present_modes.iter().next().is_some() 208 | } else { 209 | false 210 | }; 211 | 212 | indices.is_complete() && extensions_supported && swap_chain_adequate 213 | } 214 | 215 | fn check_device_extension_support(device: &PhysicalDevice) -> bool { 216 | let available_extensions = DeviceExtensions::supported_by_device(*device); 217 | let device_extensions = device_extensions(); 218 | available_extensions.intersection(&device_extensions) == device_extensions 219 | } 220 | 221 | fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { 222 | // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be 223 | // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) 224 | *available_formats.iter() 225 | .find(|(format, color_space)| 226 | *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear 227 | ) 228 | .unwrap_or_else(|| &available_formats[0]) 229 | } 230 | 231 | fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { 232 | if available_present_modes.mailbox { 233 | PresentMode::Mailbox 234 | } else if available_present_modes.immediate { 235 | PresentMode::Immediate 236 | } else { 237 | PresentMode::Fifo 238 | } 239 | } 240 | 241 | fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { 242 | if let Some(current_extent) = capabilities.current_extent { 243 | return current_extent 244 | } else { 245 | let mut actual_extent = [WIDTH, HEIGHT]; 246 | actual_extent[0] = capabilities.min_image_extent[0] 247 | .max(capabilities.max_image_extent[0].min(actual_extent[0])); 248 | actual_extent[1] = capabilities.min_image_extent[1] 249 | .max(capabilities.max_image_extent[1].min(actual_extent[1])); 250 | actual_extent 251 | } 252 | } 253 | 254 | fn create_swap_chain( 255 | instance: &Arc, 256 | surface: &Arc>, 257 | physical_device_index: usize, 258 | device: &Arc, 259 | graphics_queue: &Arc, 260 | present_queue: &Arc, 261 | ) -> (Arc>, Vec>>) { 262 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 263 | let capabilities = surface.capabilities(physical_device) 264 | .expect("failed to get surface capabilities"); 265 | 266 | let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); 267 | let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); 268 | let extent = Self::choose_swap_extent(&capabilities); 269 | 270 | let mut image_count = capabilities.min_image_count + 1; 271 | if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { 272 | image_count = capabilities.max_image_count.unwrap(); 273 | } 274 | 275 | let image_usage = ImageUsage { 276 | color_attachment: true, 277 | .. ImageUsage::none() 278 | }; 279 | 280 | let indices = Self::find_queue_families(&surface, &physical_device); 281 | 282 | let sharing: SharingMode = if indices.graphics_family != indices.present_family { 283 | vec![graphics_queue, present_queue].as_slice().into() 284 | } else { 285 | graphics_queue.into() 286 | }; 287 | 288 | let (swap_chain, images) = Swapchain::new( 289 | device.clone(), 290 | surface.clone(), 291 | image_count, 292 | surface_format.0, // TODO: color space? 293 | extent, 294 | 1, // layers 295 | image_usage, 296 | sharing, 297 | capabilities.current_transform, 298 | CompositeAlpha::Opaque, 299 | present_mode, 300 | true, // clipped 301 | None, 302 | ).expect("failed to create swap chain!"); 303 | 304 | (swap_chain, images) 305 | } 306 | 307 | fn create_render_pass(device: &Arc, color_format: Format) -> Arc { 308 | Arc::new(single_pass_renderpass!(device.clone(), 309 | attachments: { 310 | color: { 311 | load: Clear, 312 | store: Store, 313 | format: color_format, 314 | samples: 1, 315 | } 316 | }, 317 | pass: { 318 | color: [color], 319 | depth_stencil: {} 320 | } 321 | ).unwrap()) 322 | } 323 | 324 | fn create_graphics_pipeline( 325 | device: &Arc, 326 | swap_chain_extent: [u32; 2], 327 | ) { 328 | mod vertex_shader { 329 | vulkano_shaders::shader! { 330 | ty: "vertex", 331 | path: "src/bin/09_shader_base.vert" 332 | } 333 | } 334 | 335 | mod fragment_shader { 336 | vulkano_shaders::shader! { 337 | ty: "fragment", 338 | path: "src/bin/09_shader_base.frag" 339 | } 340 | } 341 | 342 | let vert_shader_module = vertex_shader::Shader::load(device.clone()) 343 | .expect("failed to create vertex shader module!"); 344 | let frag_shader_module = fragment_shader::Shader::load(device.clone()) 345 | .expect("failed to create fragment shader module!"); 346 | 347 | let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; 348 | let viewport = Viewport { 349 | origin: [0.0, 0.0], 350 | dimensions, 351 | depth_range: 0.0 .. 1.0, 352 | }; 353 | 354 | let _pipeline_builder = Arc::new(GraphicsPipeline::start() 355 | .vertex_input(BufferlessDefinition {}) 356 | .vertex_shader(vert_shader_module.main_entry_point(), ()) 357 | .triangle_list() 358 | .primitive_restart(false) 359 | .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport 360 | .fragment_shader(frag_shader_module.main_entry_point(), ()) 361 | .depth_clamp(false) 362 | // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... 363 | .polygon_mode_fill() // = default 364 | .line_width(1.0) // = default 365 | .cull_mode_back() 366 | .front_face_clockwise() 367 | // NOTE: no depth_bias here, but on pipeline::raster::Rasterization 368 | .blend_pass_through() // = default 369 | ); 370 | } 371 | 372 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 373 | let mut indices = QueueFamilyIndices::new(); 374 | // TODO: replace index with id to simplify? 375 | for (i, queue_family) in device.queue_families().enumerate() { 376 | if queue_family.supports_graphics() { 377 | indices.graphics_family = i as i32; 378 | } 379 | 380 | if surface.is_supported(queue_family).unwrap() { 381 | indices.present_family = i as i32; 382 | } 383 | 384 | if indices.is_complete() { 385 | break; 386 | } 387 | } 388 | 389 | indices 390 | } 391 | 392 | fn create_logical_device( 393 | instance: &Arc, 394 | surface: &Arc>, 395 | physical_device_index: usize, 396 | ) -> (Arc, Arc, Arc) { 397 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 398 | let indices = Self::find_queue_families(&surface, &physical_device); 399 | 400 | let families = [indices.graphics_family, indices.present_family]; 401 | use std::iter::FromIterator; 402 | let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); 403 | 404 | let queue_priority = 1.0; 405 | let queue_families = unique_queue_families.iter().map(|i| { 406 | (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) 407 | }); 408 | 409 | // NOTE: the tutorial recommends passing the validation layers as well 410 | // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that 411 | // for us internally. 412 | 413 | let (device, mut queues) = Device::new(physical_device, &Features::none(), 414 | &device_extensions(), queue_families) 415 | .expect("failed to create logical device!"); 416 | 417 | let graphics_queue = queues.next().unwrap(); 418 | let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); 419 | 420 | (device, graphics_queue, present_queue) 421 | } 422 | 423 | fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { 424 | let events_loop = EventsLoop::new(); 425 | let surface = WindowBuilder::new() 426 | .with_title("Vulkan") 427 | .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) 428 | .build_vk_surface(&events_loop, instance.clone()) 429 | .expect("failed to create window surface!"); 430 | (events_loop, surface) 431 | } 432 | 433 | #[allow(unused)] 434 | fn main_loop(&mut self) { 435 | loop { 436 | let mut done = false; 437 | self.events_loop.poll_events(|ev| { 438 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 439 | done = true 440 | } 441 | }); 442 | if done { 443 | return; 444 | } 445 | } 446 | } 447 | } 448 | 449 | fn main() { 450 | let mut _app = HelloTriangleApplication::initialize(); 451 | // app.main_loop(); 452 | } 453 | -------------------------------------------------------------------------------- /src/bin/11_render_passes.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/10_fixed_functions.rs 2 | +++ b/11_render_passes.rs 3 | @@ -1,3 +1,4 @@ 4 | +#[macro_use] 5 | extern crate vulkano; 6 | extern crate vulkano_win; 7 | extern crate winit; 8 | @@ -35,6 +36,9 @@ use vulkano::pipeline::{ 9 | vertex::BufferlessDefinition, 10 | viewport::Viewport, 11 | }; 12 | +use vulkano::framebuffer::{ 13 | + RenderPassAbstract, 14 | +}; 15 | 16 | const WIDTH: u32 = 800; 17 | const HEIGHT: u32 = 600; 18 | @@ -86,6 +90,8 @@ struct HelloTriangleApplication { 19 | 20 | swap_chain: Arc>, 21 | swap_chain_images: Vec>>, 22 | + 23 | + render_pass: Arc, 24 | } 25 | 26 | impl HelloTriangleApplication { 27 | @@ -101,6 +107,7 @@ impl HelloTriangleApplication { 28 | let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, 29 | &device, &graphics_queue, &present_queue); 30 | 31 | + let render_pass = Self::create_render_pass(&device, swap_chain.format()); 32 | Self::create_graphics_pipeline(&device, swap_chain.dimensions()); 33 | 34 | Self { 35 | @@ -118,6 +125,8 @@ impl HelloTriangleApplication { 36 | 37 | swap_chain, 38 | swap_chain_images, 39 | + 40 | + render_pass, 41 | } 42 | } 43 | 44 | @@ -295,6 +304,23 @@ impl HelloTriangleApplication { 45 | (swap_chain, images) 46 | } 47 | 48 | + fn create_render_pass(device: &Arc, color_format: Format) -> Arc { 49 | + Arc::new(single_pass_renderpass!(device.clone(), 50 | + attachments: { 51 | + color: { 52 | + load: Clear, 53 | + store: Store, 54 | + format: color_format, 55 | + samples: 1, 56 | + } 57 | + }, 58 | + pass: { 59 | + color: [color], 60 | + depth_stencil: {} 61 | + } 62 | + ).unwrap()) 63 | + } 64 | + 65 | fn create_graphics_pipeline( 66 | device: &Arc, 67 | swap_chain_extent: [u32; 2], 68 | -------------------------------------------------------------------------------- /src/bin/12_graphics_pipeline_complete.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate vulkano; 3 | extern crate vulkano_win; 4 | extern crate winit; 5 | 6 | use std::sync::Arc; 7 | use std::collections::HashSet; 8 | 9 | use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; 10 | use vulkano_win::VkSurfaceBuild; 11 | 12 | use vulkano::instance::{ 13 | Instance, 14 | InstanceExtensions, 15 | ApplicationInfo, 16 | Version, 17 | layers_list, 18 | PhysicalDevice, 19 | }; 20 | use vulkano::instance::debug::{DebugCallback, MessageTypes}; 21 | use vulkano::device::{Device, DeviceExtensions, Queue, Features}; 22 | use vulkano::swapchain::{ 23 | Surface, 24 | Capabilities, 25 | ColorSpace, 26 | SupportedPresentModes, 27 | PresentMode, 28 | Swapchain, 29 | CompositeAlpha, 30 | }; 31 | use vulkano::format::Format; 32 | use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; 33 | use vulkano::sync::SharingMode; 34 | use vulkano::pipeline::{ 35 | GraphicsPipeline, 36 | vertex::BufferlessDefinition, 37 | viewport::Viewport, 38 | }; 39 | use vulkano::framebuffer::{ 40 | RenderPassAbstract, 41 | Subpass, 42 | }; 43 | use vulkano::descriptor::PipelineLayoutAbstract; 44 | 45 | const WIDTH: u32 = 800; 46 | const HEIGHT: u32 = 600; 47 | 48 | const VALIDATION_LAYERS: &[&str] = &[ 49 | "VK_LAYER_LUNARG_standard_validation" 50 | ]; 51 | 52 | /// Required device extensions 53 | fn device_extensions() -> DeviceExtensions { 54 | DeviceExtensions { 55 | khr_swapchain: true, 56 | .. vulkano::device::DeviceExtensions::none() 57 | } 58 | } 59 | 60 | #[cfg(all(debug_assertions))] 61 | const ENABLE_VALIDATION_LAYERS: bool = true; 62 | #[cfg(not(debug_assertions))] 63 | const ENABLE_VALIDATION_LAYERS: bool = false; 64 | 65 | struct QueueFamilyIndices { 66 | graphics_family: i32, 67 | present_family: i32, 68 | } 69 | impl QueueFamilyIndices { 70 | fn new() -> Self { 71 | Self { graphics_family: -1, present_family: -1 } 72 | } 73 | 74 | fn is_complete(&self) -> bool { 75 | self.graphics_family >= 0 && self.present_family >= 0 76 | } 77 | } 78 | 79 | type ConcreteGraphicsPipeline = GraphicsPipeline, Arc>; 80 | 81 | #[allow(unused)] 82 | struct HelloTriangleApplication { 83 | instance: Arc, 84 | debug_callback: Option, 85 | 86 | events_loop: EventsLoop, 87 | surface: Arc>, 88 | 89 | physical_device_index: usize, // can't store PhysicalDevice directly (lifetime issues) 90 | device: Arc, 91 | 92 | graphics_queue: Arc, 93 | present_queue: Arc, 94 | 95 | swap_chain: Arc>, 96 | swap_chain_images: Vec>>, 97 | 98 | render_pass: Arc, 99 | // NOTE: We need to the full type of 100 | // self.graphics_pipeline, because `BufferlessVertices` only 101 | // works when the concrete type of the graphics pipeline is visible 102 | // to the command buffer. 103 | graphics_pipeline: Arc, 104 | } 105 | 106 | impl HelloTriangleApplication { 107 | pub fn initialize() -> Self { 108 | let instance = Self::create_instance(); 109 | let debug_callback = Self::setup_debug_callback(&instance); 110 | let (events_loop, surface) = Self::create_surface(&instance); 111 | 112 | let physical_device_index = Self::pick_physical_device(&instance, &surface); 113 | let (device, graphics_queue, present_queue) = Self::create_logical_device( 114 | &instance, &surface, physical_device_index); 115 | 116 | let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, 117 | &device, &graphics_queue, &present_queue); 118 | 119 | let render_pass = Self::create_render_pass(&device, swap_chain.format()); 120 | let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); 121 | 122 | Self { 123 | instance, 124 | debug_callback, 125 | 126 | events_loop, 127 | surface, 128 | 129 | physical_device_index, 130 | device, 131 | 132 | graphics_queue, 133 | present_queue, 134 | 135 | swap_chain, 136 | swap_chain_images, 137 | 138 | render_pass, 139 | graphics_pipeline, 140 | } 141 | } 142 | 143 | fn create_instance() -> Arc { 144 | if ENABLE_VALIDATION_LAYERS && !Self::check_validation_layer_support() { 145 | println!("Validation layers requested, but not available!") 146 | } 147 | 148 | let supported_extensions = InstanceExtensions::supported_by_core() 149 | .expect("failed to retrieve supported extensions"); 150 | println!("Supported extensions: {:?}", supported_extensions); 151 | 152 | let app_info = ApplicationInfo { 153 | application_name: Some("Hello Triangle".into()), 154 | application_version: Some(Version { major: 1, minor: 0, patch: 0 }), 155 | engine_name: Some("No Engine".into()), 156 | engine_version: Some(Version { major: 1, minor: 0, patch: 0 }), 157 | }; 158 | 159 | let required_extensions = Self::get_required_extensions(); 160 | 161 | if ENABLE_VALIDATION_LAYERS && Self::check_validation_layer_support() { 162 | Instance::new(Some(&app_info), &required_extensions, VALIDATION_LAYERS.iter().cloned()) 163 | .expect("failed to create Vulkan instance") 164 | } else { 165 | Instance::new(Some(&app_info), &required_extensions, None) 166 | .expect("failed to create Vulkan instance") 167 | } 168 | } 169 | 170 | fn check_validation_layer_support() -> bool { 171 | let layers: Vec<_> = layers_list().unwrap().map(|l| l.name().to_owned()).collect(); 172 | VALIDATION_LAYERS.iter() 173 | .all(|layer_name| layers.contains(&layer_name.to_string())) 174 | } 175 | 176 | fn get_required_extensions() -> InstanceExtensions { 177 | let mut extensions = vulkano_win::required_extensions(); 178 | if ENABLE_VALIDATION_LAYERS { 179 | // TODO!: this should be ext_debug_utils (_report is deprecated), but that doesn't exist yet in vulkano 180 | extensions.ext_debug_report = true; 181 | } 182 | 183 | extensions 184 | } 185 | 186 | fn setup_debug_callback(instance: &Arc) -> Option { 187 | if !ENABLE_VALIDATION_LAYERS { 188 | return None; 189 | } 190 | 191 | let msg_types = MessageTypes { 192 | error: true, 193 | warning: true, 194 | performance_warning: true, 195 | information: false, 196 | debug: true, 197 | }; 198 | DebugCallback::new(&instance, msg_types, |msg| { 199 | println!("validation layer: {:?}", msg.description); 200 | }).ok() 201 | } 202 | 203 | fn pick_physical_device(instance: &Arc, surface: &Arc>) -> usize { 204 | PhysicalDevice::enumerate(&instance) 205 | .position(|device| Self::is_device_suitable(surface, &device)) 206 | .expect("failed to find a suitable GPU!") 207 | } 208 | 209 | fn is_device_suitable(surface: &Arc>, device: &PhysicalDevice) -> bool { 210 | let indices = Self::find_queue_families(surface, device); 211 | let extensions_supported = Self::check_device_extension_support(device); 212 | 213 | let swap_chain_adequate = if extensions_supported { 214 | let capabilities = surface.capabilities(*device) 215 | .expect("failed to get surface capabilities"); 216 | !capabilities.supported_formats.is_empty() && 217 | capabilities.present_modes.iter().next().is_some() 218 | } else { 219 | false 220 | }; 221 | 222 | indices.is_complete() && extensions_supported && swap_chain_adequate 223 | } 224 | 225 | fn check_device_extension_support(device: &PhysicalDevice) -> bool { 226 | let available_extensions = DeviceExtensions::supported_by_device(*device); 227 | let device_extensions = device_extensions(); 228 | available_extensions.intersection(&device_extensions) == device_extensions 229 | } 230 | 231 | fn choose_swap_surface_format(available_formats: &[(Format, ColorSpace)]) -> (Format, ColorSpace) { 232 | // NOTE: the 'preferred format' mentioned in the tutorial doesn't seem to be 233 | // queryable in Vulkano (no VK_FORMAT_UNDEFINED enum) 234 | *available_formats.iter() 235 | .find(|(format, color_space)| 236 | *format == Format::B8G8R8A8Unorm && *color_space == ColorSpace::SrgbNonLinear 237 | ) 238 | .unwrap_or_else(|| &available_formats[0]) 239 | } 240 | 241 | fn choose_swap_present_mode(available_present_modes: SupportedPresentModes) -> PresentMode { 242 | if available_present_modes.mailbox { 243 | PresentMode::Mailbox 244 | } else if available_present_modes.immediate { 245 | PresentMode::Immediate 246 | } else { 247 | PresentMode::Fifo 248 | } 249 | } 250 | 251 | fn choose_swap_extent(capabilities: &Capabilities) -> [u32; 2] { 252 | if let Some(current_extent) = capabilities.current_extent { 253 | return current_extent 254 | } else { 255 | let mut actual_extent = [WIDTH, HEIGHT]; 256 | actual_extent[0] = capabilities.min_image_extent[0] 257 | .max(capabilities.max_image_extent[0].min(actual_extent[0])); 258 | actual_extent[1] = capabilities.min_image_extent[1] 259 | .max(capabilities.max_image_extent[1].min(actual_extent[1])); 260 | actual_extent 261 | } 262 | } 263 | 264 | fn create_swap_chain( 265 | instance: &Arc, 266 | surface: &Arc>, 267 | physical_device_index: usize, 268 | device: &Arc, 269 | graphics_queue: &Arc, 270 | present_queue: &Arc, 271 | ) -> (Arc>, Vec>>) { 272 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 273 | let capabilities = surface.capabilities(physical_device) 274 | .expect("failed to get surface capabilities"); 275 | 276 | let surface_format = Self::choose_swap_surface_format(&capabilities.supported_formats); 277 | let present_mode = Self::choose_swap_present_mode(capabilities.present_modes); 278 | let extent = Self::choose_swap_extent(&capabilities); 279 | 280 | let mut image_count = capabilities.min_image_count + 1; 281 | if capabilities.max_image_count.is_some() && image_count > capabilities.max_image_count.unwrap() { 282 | image_count = capabilities.max_image_count.unwrap(); 283 | } 284 | 285 | let image_usage = ImageUsage { 286 | color_attachment: true, 287 | .. ImageUsage::none() 288 | }; 289 | 290 | let indices = Self::find_queue_families(&surface, &physical_device); 291 | 292 | let sharing: SharingMode = if indices.graphics_family != indices.present_family { 293 | vec![graphics_queue, present_queue].as_slice().into() 294 | } else { 295 | graphics_queue.into() 296 | }; 297 | 298 | let (swap_chain, images) = Swapchain::new( 299 | device.clone(), 300 | surface.clone(), 301 | image_count, 302 | surface_format.0, // TODO: color space? 303 | extent, 304 | 1, // layers 305 | image_usage, 306 | sharing, 307 | capabilities.current_transform, 308 | CompositeAlpha::Opaque, 309 | present_mode, 310 | true, // clipped 311 | None, 312 | ).expect("failed to create swap chain!"); 313 | 314 | (swap_chain, images) 315 | } 316 | 317 | fn create_render_pass(device: &Arc, color_format: Format) -> Arc { 318 | Arc::new(single_pass_renderpass!(device.clone(), 319 | attachments: { 320 | color: { 321 | load: Clear, 322 | store: Store, 323 | format: color_format, 324 | samples: 1, 325 | } 326 | }, 327 | pass: { 328 | color: [color], 329 | depth_stencil: {} 330 | } 331 | ).unwrap()) 332 | } 333 | 334 | fn create_graphics_pipeline( 335 | device: &Arc, 336 | swap_chain_extent: [u32; 2], 337 | render_pass: &Arc, 338 | ) -> Arc { 339 | mod vertex_shader { 340 | vulkano_shaders::shader! { 341 | ty: "vertex", 342 | path: "src/bin/09_shader_base.vert" 343 | } 344 | } 345 | 346 | mod fragment_shader { 347 | vulkano_shaders::shader! { 348 | ty: "fragment", 349 | path: "src/bin/09_shader_base.frag" 350 | } 351 | } 352 | 353 | let vert_shader_module = vertex_shader::Shader::load(device.clone()) 354 | .expect("failed to create vertex shader module!"); 355 | let frag_shader_module = fragment_shader::Shader::load(device.clone()) 356 | .expect("failed to create fragment shader module!"); 357 | 358 | let dimensions = [swap_chain_extent[0] as f32, swap_chain_extent[1] as f32]; 359 | let viewport = Viewport { 360 | origin: [0.0, 0.0], 361 | dimensions, 362 | depth_range: 0.0 .. 1.0, 363 | }; 364 | 365 | Arc::new(GraphicsPipeline::start() 366 | .vertex_input(BufferlessDefinition {}) 367 | .vertex_shader(vert_shader_module.main_entry_point(), ()) 368 | .triangle_list() 369 | .primitive_restart(false) 370 | .viewports(vec![viewport]) // NOTE: also sets scissor to cover whole viewport 371 | .fragment_shader(frag_shader_module.main_entry_point(), ()) 372 | .depth_clamp(false) 373 | // NOTE: there's an outcommented .rasterizer_discard() in Vulkano... 374 | .polygon_mode_fill() // = default 375 | .line_width(1.0) // = default 376 | .cull_mode_back() 377 | .front_face_clockwise() 378 | // NOTE: no depth_bias here, but on pipeline::raster::Rasterization 379 | .blend_pass_through() // = default 380 | .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) 381 | .build(device.clone()) 382 | .unwrap() 383 | ) 384 | } 385 | 386 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 387 | let mut indices = QueueFamilyIndices::new(); 388 | // TODO: replace index with id to simplify? 389 | for (i, queue_family) in device.queue_families().enumerate() { 390 | if queue_family.supports_graphics() { 391 | indices.graphics_family = i as i32; 392 | } 393 | 394 | if surface.is_supported(queue_family).unwrap() { 395 | indices.present_family = i as i32; 396 | } 397 | 398 | if indices.is_complete() { 399 | break; 400 | } 401 | } 402 | 403 | indices 404 | } 405 | 406 | fn create_logical_device( 407 | instance: &Arc, 408 | surface: &Arc>, 409 | physical_device_index: usize, 410 | ) -> (Arc, Arc, Arc) { 411 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 412 | let indices = Self::find_queue_families(&surface, &physical_device); 413 | 414 | let families = [indices.graphics_family, indices.present_family]; 415 | use std::iter::FromIterator; 416 | let unique_queue_families: HashSet<&i32> = HashSet::from_iter(families.iter()); 417 | 418 | let queue_priority = 1.0; 419 | let queue_families = unique_queue_families.iter().map(|i| { 420 | (physical_device.queue_families().nth(**i as usize).unwrap(), queue_priority) 421 | }); 422 | 423 | // NOTE: the tutorial recommends passing the validation layers as well 424 | // for legacy reasons (if ENABLE_VALIDATION_LAYERS is true). Vulkano handles that 425 | // for us internally. 426 | 427 | let (device, mut queues) = Device::new(physical_device, &Features::none(), 428 | &device_extensions(), queue_families) 429 | .expect("failed to create logical device!"); 430 | 431 | let graphics_queue = queues.next().unwrap(); 432 | let present_queue = queues.next().unwrap_or_else(|| graphics_queue.clone()); 433 | 434 | (device, graphics_queue, present_queue) 435 | } 436 | 437 | fn create_surface(instance: &Arc) -> (EventsLoop, Arc>) { 438 | let events_loop = EventsLoop::new(); 439 | let surface = WindowBuilder::new() 440 | .with_title("Vulkan") 441 | .with_dimensions(LogicalSize::new(f64::from(WIDTH), f64::from(HEIGHT))) 442 | .build_vk_surface(&events_loop, instance.clone()) 443 | .expect("failed to create window surface!"); 444 | (events_loop, surface) 445 | } 446 | 447 | #[allow(unused)] 448 | fn main_loop(&mut self) { 449 | loop { 450 | let mut done = false; 451 | self.events_loop.poll_events(|ev| { 452 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 453 | done = true 454 | } 455 | }); 456 | if done { 457 | return; 458 | } 459 | } 460 | } 461 | } 462 | 463 | fn main() { 464 | let mut _app = HelloTriangleApplication::initialize(); 465 | // app.main_loop(); 466 | } 467 | -------------------------------------------------------------------------------- /src/bin/12_graphics_pipeline_complete.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/11_render_passes.rs 2 | +++ b/12_graphics_pipeline_complete.rs 3 | @@ -38,7 +38,9 @@ use vulkano::pipeline::{ 4 | }; 5 | use vulkano::framebuffer::{ 6 | RenderPassAbstract, 7 | + Subpass, 8 | }; 9 | +use vulkano::descriptor::PipelineLayoutAbstract; 10 | 11 | const WIDTH: u32 = 800; 12 | const HEIGHT: u32 = 600; 13 | @@ -74,6 +76,8 @@ impl QueueFamilyIndices { 14 | } 15 | } 16 | 17 | +type ConcreteGraphicsPipeline = GraphicsPipeline, Arc>; 18 | + 19 | #[allow(unused)] 20 | struct HelloTriangleApplication { 21 | instance: Arc, 22 | @@ -92,6 +96,11 @@ struct HelloTriangleApplication { 23 | swap_chain_images: Vec>>, 24 | 25 | render_pass: Arc, 26 | + // NOTE: We need to the full type of 27 | + // self.graphics_pipeline, because `BufferlessVertices` only 28 | + // works when the concrete type of the graphics pipeline is visible 29 | + // to the command buffer. 30 | + graphics_pipeline: Arc, 31 | } 32 | 33 | impl HelloTriangleApplication { 34 | @@ -108,7 +117,7 @@ impl HelloTriangleApplication { 35 | &device, &graphics_queue, &present_queue); 36 | 37 | let render_pass = Self::create_render_pass(&device, swap_chain.format()); 38 | - Self::create_graphics_pipeline(&device, swap_chain.dimensions()); 39 | + let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); 40 | 41 | Self { 42 | instance, 43 | @@ -127,6 +136,7 @@ impl HelloTriangleApplication { 44 | swap_chain_images, 45 | 46 | render_pass, 47 | + graphics_pipeline, 48 | } 49 | } 50 | 51 | @@ -324,7 +334,8 @@ impl HelloTriangleApplication { 52 | fn create_graphics_pipeline( 53 | device: &Arc, 54 | swap_chain_extent: [u32; 2], 55 | - ) { 56 | + render_pass: &Arc, 57 | + ) -> Arc { 58 | mod vertex_shader { 59 | vulkano_shaders::shader! { 60 | ty: "vertex", 61 | @@ -351,7 +362,7 @@ impl HelloTriangleApplication { 62 | depth_range: 0.0 .. 1.0, 63 | }; 64 | 65 | - let _pipeline_builder = Arc::new(GraphicsPipeline::start() 66 | + Arc::new(GraphicsPipeline::start() 67 | .vertex_input(BufferlessDefinition {}) 68 | .vertex_shader(vert_shader_module.main_entry_point(), ()) 69 | .triangle_list() 70 | @@ -366,7 +377,10 @@ impl HelloTriangleApplication { 71 | .front_face_clockwise() 72 | // NOTE: no depth_bias here, but on pipeline::raster::Rasterization 73 | .blend_pass_through() // = default 74 | - ); 75 | + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) 76 | + .build(device.clone()) 77 | + .unwrap() 78 | + ) 79 | } 80 | 81 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 82 | -------------------------------------------------------------------------------- /src/bin/13_framebuffers.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/12_graphics_pipeline_complete.rs 2 | +++ b/13_framebuffers.rs 3 | @@ -39,6 +39,8 @@ use vulkano::pipeline::{ 4 | use vulkano::framebuffer::{ 5 | RenderPassAbstract, 6 | Subpass, 7 | + FramebufferAbstract, 8 | + Framebuffer, 9 | }; 10 | use vulkano::descriptor::PipelineLayoutAbstract; 11 | 12 | @@ -101,6 +103,8 @@ struct HelloTriangleApplication { 13 | // works when the concrete type of the graphics pipeline is visible 14 | // to the command buffer. 15 | graphics_pipeline: Arc, 16 | + 17 | + swap_chain_framebuffers: Vec>, 18 | } 19 | 20 | impl HelloTriangleApplication { 21 | @@ -119,6 +123,8 @@ impl HelloTriangleApplication { 22 | let render_pass = Self::create_render_pass(&device, swap_chain.format()); 23 | let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); 24 | 25 | + let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); 26 | + 27 | Self { 28 | instance, 29 | debug_callback, 30 | @@ -137,6 +143,8 @@ impl HelloTriangleApplication { 31 | 32 | render_pass, 33 | graphics_pipeline, 34 | + 35 | + swap_chain_framebuffers, 36 | } 37 | } 38 | 39 | @@ -383,6 +391,20 @@ impl HelloTriangleApplication { 40 | ) 41 | } 42 | 43 | + fn create_framebuffers( 44 | + swap_chain_images: &[Arc>], 45 | + render_pass: &Arc 46 | + ) -> Vec> { 47 | + swap_chain_images.iter() 48 | + .map(|image| { 49 | + let fba: Arc = Arc::new(Framebuffer::start(render_pass.clone()) 50 | + .add(image.clone()).unwrap() 51 | + .build().unwrap()); 52 | + fba 53 | + } 54 | + ).collect::>() 55 | + } 56 | + 57 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 58 | let mut indices = QueueFamilyIndices::new(); 59 | // TODO: replace index with id to simplify? 60 | -------------------------------------------------------------------------------- /src/bin/14_command_buffers.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/13_framebuffers.rs 2 | +++ b/14_command_buffers.rs 3 | @@ -34,6 +34,7 @@ use vulkano::sync::SharingMode; 4 | use vulkano::pipeline::{ 5 | GraphicsPipeline, 6 | vertex::BufferlessDefinition, 7 | + vertex::BufferlessVertices, 8 | viewport::Viewport, 9 | }; 10 | use vulkano::framebuffer::{ 11 | @@ -43,6 +44,11 @@ use vulkano::framebuffer::{ 12 | Framebuffer, 13 | }; 14 | use vulkano::descriptor::PipelineLayoutAbstract; 15 | +use vulkano::command_buffer::{ 16 | + AutoCommandBuffer, 17 | + AutoCommandBufferBuilder, 18 | + DynamicState, 19 | +}; 20 | 21 | const WIDTH: u32 = 800; 22 | const HEIGHT: u32 = 600; 23 | @@ -105,6 +111,8 @@ struct HelloTriangleApplication { 24 | graphics_pipeline: Arc, 25 | 26 | swap_chain_framebuffers: Vec>, 27 | + 28 | + command_buffers: Vec>, 29 | } 30 | 31 | impl HelloTriangleApplication { 32 | @@ -125,7 +133,7 @@ impl HelloTriangleApplication { 33 | 34 | let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); 35 | 36 | - Self { 37 | + let mut app = Self { 38 | instance, 39 | debug_callback, 40 | 41 | @@ -145,7 +153,12 @@ impl HelloTriangleApplication { 42 | graphics_pipeline, 43 | 44 | swap_chain_framebuffers, 45 | - } 46 | + 47 | + command_buffers: vec![], 48 | + }; 49 | + 50 | + app.create_command_buffers(); 51 | + app 52 | } 53 | 54 | fn create_instance() -> Arc { 55 | @@ -405,6 +418,26 @@ impl HelloTriangleApplication { 56 | ).collect::>() 57 | } 58 | 59 | + fn create_command_buffers(&mut self) { 60 | + let queue_family = self.graphics_queue.family(); 61 | + self.command_buffers = self.swap_chain_framebuffers.iter() 62 | + .map(|framebuffer| { 63 | + let vertices = BufferlessVertices { vertices: 3, instances: 1 }; 64 | + Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) 65 | + .unwrap() 66 | + .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) 67 | + .unwrap() 68 | + .draw(self.graphics_pipeline.clone(), &DynamicState::none(), 69 | + vertices, (), ()) 70 | + .unwrap() 71 | + .end_render_pass() 72 | + .unwrap() 73 | + .build() 74 | + .unwrap()) 75 | + }) 76 | + .collect(); 77 | + } 78 | + 79 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 80 | let mut indices = QueueFamilyIndices::new(); 81 | // TODO: replace index with id to simplify? 82 | -------------------------------------------------------------------------------- /src/bin/15_hello_triangle.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/14_command_buffers.rs 2 | +++ b/15_hello_triangle.rs 3 | @@ -27,10 +27,11 @@ use vulkano::swapchain::{ 4 | PresentMode, 5 | Swapchain, 6 | CompositeAlpha, 7 | + acquire_next_image, 8 | }; 9 | use vulkano::format::Format; 10 | use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; 11 | -use vulkano::sync::SharingMode; 12 | +use vulkano::sync::{SharingMode, GpuFuture}; 13 | use vulkano::pipeline::{ 14 | GraphicsPipeline, 15 | vertex::BufferlessDefinition, 16 | @@ -502,6 +503,8 @@ impl HelloTriangleApplication { 17 | #[allow(unused)] 18 | fn main_loop(&mut self) { 19 | loop { 20 | + self.draw_frame(); 21 | + 22 | let mut done = false; 23 | self.events_loop.poll_events(|ev| { 24 | if let Event::WindowEvent { event: WindowEvent::CloseRequested, .. } = ev { 25 | @@ -513,9 +516,24 @@ impl HelloTriangleApplication { 26 | } 27 | } 28 | } 29 | + 30 | + fn draw_frame(&mut self) { 31 | + let (image_index, acquire_future) = acquire_next_image(self.swap_chain.clone(), None).unwrap(); 32 | + 33 | + let command_buffer = self.command_buffers[image_index].clone(); 34 | + 35 | + let future = acquire_future 36 | + .then_execute(self.graphics_queue.clone(), command_buffer) 37 | + .unwrap() 38 | + .then_swapchain_present(self.present_queue.clone(), self.swap_chain.clone(), image_index) 39 | + .then_signal_fence_and_flush() 40 | + .unwrap(); 41 | + 42 | + future.wait(None).unwrap(); 43 | + } 44 | } 45 | 46 | fn main() { 47 | - let mut _app = HelloTriangleApplication::initialize(); 48 | - // app.main_loop(); 49 | + let mut app = HelloTriangleApplication::initialize(); 50 | + app.main_loop(); 51 | } 52 | -------------------------------------------------------------------------------- /src/bin/16_swap_chain_recreation.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/15_hello_triangle.rs 2 | +++ b/16_swap_chain_recreation.rs 3 | @@ -28,10 +28,11 @@ use vulkano::swapchain::{ 4 | Swapchain, 5 | CompositeAlpha, 6 | acquire_next_image, 7 | + AcquireError, 8 | }; 9 | use vulkano::format::Format; 10 | use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; 11 | -use vulkano::sync::{SharingMode, GpuFuture}; 12 | +use vulkano::sync::{self, SharingMode, GpuFuture}; 13 | use vulkano::pipeline::{ 14 | GraphicsPipeline, 15 | vertex::BufferlessDefinition, 16 | @@ -87,9 +88,9 @@ impl QueueFamilyIndices { 17 | 18 | type ConcreteGraphicsPipeline = GraphicsPipeline, Arc>; 19 | 20 | -#[allow(unused)] 21 | struct HelloTriangleApplication { 22 | instance: Arc, 23 | + #[allow(unused)] 24 | debug_callback: Option, 25 | 26 | events_loop: EventsLoop, 27 | @@ -114,6 +115,9 @@ struct HelloTriangleApplication { 28 | swap_chain_framebuffers: Vec>, 29 | 30 | command_buffers: Vec>, 31 | + 32 | + previous_frame_end: Option>, 33 | + recreate_swap_chain: bool, 34 | } 35 | 36 | impl HelloTriangleApplication { 37 | @@ -127,13 +131,15 @@ impl HelloTriangleApplication { 38 | &instance, &surface, physical_device_index); 39 | 40 | let (swap_chain, swap_chain_images) = Self::create_swap_chain(&instance, &surface, physical_device_index, 41 | - &device, &graphics_queue, &present_queue); 42 | + &device, &graphics_queue, &present_queue, None); 43 | 44 | let render_pass = Self::create_render_pass(&device, swap_chain.format()); 45 | let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); 46 | 47 | let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); 48 | 49 | + let previous_frame_end = Some(Self::create_sync_objects(&device)); 50 | + 51 | let mut app = Self { 52 | instance, 53 | debug_callback, 54 | @@ -156,6 +162,9 @@ impl HelloTriangleApplication { 55 | swap_chain_framebuffers, 56 | 57 | command_buffers: vec![], 58 | + 59 | + previous_frame_end, 60 | + recreate_swap_chain: false, 61 | }; 62 | 63 | app.create_command_buffers(); 64 | @@ -290,6 +299,7 @@ impl HelloTriangleApplication { 65 | device: &Arc, 66 | graphics_queue: &Arc, 67 | present_queue: &Arc, 68 | + old_swapchain: Option>>, 69 | ) -> (Arc>, Vec>>) { 70 | let physical_device = PhysicalDevice::from_index(&instance, physical_device_index).unwrap(); 71 | let capabilities = surface.capabilities(physical_device) 72 | @@ -330,7 +340,7 @@ impl HelloTriangleApplication { 73 | CompositeAlpha::Opaque, 74 | present_mode, 75 | true, // clipped 76 | - None, 77 | + old_swapchain.as_ref() 78 | ).expect("failed to create swap chain!"); 79 | 80 | (swap_chain, images) 81 | @@ -439,6 +449,10 @@ impl HelloTriangleApplication { 82 | .collect(); 83 | } 84 | 85 | + fn create_sync_objects(device: &Arc) -> Box { 86 | + Box::new(sync::now(device.clone())) as Box 87 | + } 88 | + 89 | fn find_queue_families(surface: &Arc>, device: &PhysicalDevice) -> QueueFamilyIndices { 90 | let mut indices = QueueFamilyIndices::new(); 91 | // TODO: replace index with id to simplify? 92 | @@ -518,18 +532,59 @@ impl HelloTriangleApplication { 93 | } 94 | 95 | fn draw_frame(&mut self) { 96 | - let (image_index, acquire_future) = acquire_next_image(self.swap_chain.clone(), None).unwrap(); 97 | + self.previous_frame_end.as_mut().unwrap().cleanup_finished(); 98 | + 99 | + if self.recreate_swap_chain { 100 | + self.recreate_swap_chain(); 101 | + self.recreate_swap_chain = false; 102 | + } 103 | + 104 | + let (image_index, acquire_future) = match acquire_next_image(self.swap_chain.clone(), None) { 105 | + Ok(r) => r, 106 | + Err(AcquireError::OutOfDate) => { 107 | + self.recreate_swap_chain = true; 108 | + return; 109 | + }, 110 | + Err(err) => panic!("{:?}", err) 111 | + }; 112 | 113 | let command_buffer = self.command_buffers[image_index].clone(); 114 | 115 | - let future = acquire_future 116 | + let future = self.previous_frame_end.take().unwrap() 117 | + .join(acquire_future) 118 | .then_execute(self.graphics_queue.clone(), command_buffer) 119 | .unwrap() 120 | .then_swapchain_present(self.present_queue.clone(), self.swap_chain.clone(), image_index) 121 | - .then_signal_fence_and_flush() 122 | - .unwrap(); 123 | + .then_signal_fence_and_flush(); 124 | + 125 | + match future { 126 | + Ok(future) => { 127 | + self.previous_frame_end = Some(Box::new(future) as Box<_>); 128 | + } 129 | + Err(vulkano::sync::FlushError::OutOfDate) => { 130 | + self.recreate_swap_chain = true; 131 | + self.previous_frame_end 132 | + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); 133 | + } 134 | + Err(e) => { 135 | + println!("{:?}", e); 136 | + self.previous_frame_end 137 | + = Some(Box::new(vulkano::sync::now(self.device.clone())) as Box<_>); 138 | + } 139 | + } 140 | + } 141 | 142 | - future.wait(None).unwrap(); 143 | + fn recreate_swap_chain(&mut self) { 144 | + let (swap_chain, images) = Self::create_swap_chain(&self.instance, &self.surface, self.physical_device_index, 145 | + &self.device, &self.graphics_queue, &self.present_queue, Some(self.swap_chain.clone())); 146 | + self.swap_chain = swap_chain; 147 | + self.swap_chain_images = images; 148 | + 149 | + self.render_pass = Self::create_render_pass(&self.device, self.swap_chain.format()); 150 | + self.graphics_pipeline = Self::create_graphics_pipeline(&self.device, self.swap_chain.dimensions(), 151 | + &self.render_pass); 152 | + self.swap_chain_framebuffers = Self::create_framebuffers(&self.swap_chain_images, &self.render_pass); 153 | + self.create_command_buffers(); 154 | } 155 | } 156 | 157 | -------------------------------------------------------------------------------- /src/bin/17_shader_vertexbuffer.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec3 fragColor; 5 | 6 | layout(location = 0) out vec4 outColor; 7 | 8 | void main() { 9 | outColor = vec4(fragColor, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /src/bin/17_shader_vertexbuffer.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | // NOTE: names must match the `Vertex` struct in Rust 5 | layout(location = 0) in vec2 pos; 6 | layout(location = 1) in vec3 color; 7 | 8 | layout(location = 0) out vec3 fragColor; 9 | 10 | out gl_PerVertex { 11 | vec4 gl_Position; 12 | }; 13 | 14 | void main() { 15 | gl_Position = vec4(pos, 0.0, 1.0); 16 | fragColor = color; 17 | } 18 | -------------------------------------------------------------------------------- /src/bin/17_shader_vertexbuffer.vert.diff: -------------------------------------------------------------------------------- 1 | --- a/09_shader_base.vert 2 | +++ b/17_shader_vertexbuffer.vert 3 | @@ -1,25 +1,17 @@ 4 | #version 450 5 | #extension GL_ARB_separate_shader_objects : enable 6 | 7 | -out gl_PerVertex { 8 | - vec4 gl_Position; 9 | -}; 10 | +// NOTE: names must match the `Vertex` struct in Rust 11 | +layout(location = 0) in vec2 pos; 12 | +layout(location = 1) in vec3 color; 13 | 14 | layout(location = 0) out vec3 fragColor; 15 | 16 | -vec2 positions[3] = vec2[]( 17 | - vec2(0.0, -0.5), 18 | - vec2(0.5, 0.5), 19 | - vec2(-0.5, 0.5) 20 | -); 21 | - 22 | -vec3 colors[3] = vec3[]( 23 | - vec3(1.0, 0.0, 0.0), 24 | - vec3(0.0, 1.0, 0.0), 25 | - vec3(0.0, 0.0, 1.0) 26 | -); 27 | +out gl_PerVertex { 28 | + vec4 gl_Position; 29 | +}; 30 | 31 | void main() { 32 | - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); 33 | - fragColor = colors[gl_VertexIndex]; 34 | + gl_Position = vec4(pos, 0.0, 1.0); 35 | + fragColor = color; 36 | } 37 | -------------------------------------------------------------------------------- /src/bin/18_vertex_buffer.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/16_swap_chain_recreation.rs 2 | +++ b/18_vertex_buffer.rs 3 | @@ -35,8 +35,7 @@ use vulkano::image::{ImageUsage, swapchain::SwapchainImage}; 4 | use vulkano::sync::{self, SharingMode, GpuFuture}; 5 | use vulkano::pipeline::{ 6 | GraphicsPipeline, 7 | - vertex::BufferlessDefinition, 8 | - vertex::BufferlessVertices, 9 | + GraphicsPipelineAbstract, 10 | viewport::Viewport, 11 | }; 12 | use vulkano::framebuffer::{ 13 | @@ -45,12 +44,16 @@ use vulkano::framebuffer::{ 14 | FramebufferAbstract, 15 | Framebuffer, 16 | }; 17 | -use vulkano::descriptor::PipelineLayoutAbstract; 18 | use vulkano::command_buffer::{ 19 | AutoCommandBuffer, 20 | AutoCommandBufferBuilder, 21 | DynamicState, 22 | }; 23 | +use vulkano::buffer::{ 24 | + cpu_access::CpuAccessibleBuffer, 25 | + BufferUsage, 26 | + BufferAccess, 27 | +}; 28 | 29 | const WIDTH: u32 = 800; 30 | const HEIGHT: u32 = 600; 31 | @@ -86,7 +89,25 @@ impl QueueFamilyIndices { 32 | } 33 | } 34 | 35 | -type ConcreteGraphicsPipeline = GraphicsPipeline, Arc>; 36 | +#[derive(Copy, Clone)] 37 | +struct Vertex { 38 | + pos: [f32; 2], 39 | + color: [f32; 3], 40 | +} 41 | +impl Vertex { 42 | + fn new(pos: [f32; 2], color: [f32; 3]) -> Self { 43 | + Self { pos, color } 44 | + } 45 | +} 46 | +impl_vertex!(Vertex, pos, color); 47 | + 48 | +fn vertices() -> [Vertex; 3] { 49 | + [ 50 | + Vertex::new([0.0, -0.5], [1.0, 1.0, 1.0]), 51 | + Vertex::new([0.5, 0.5], [0.0, 1.0, 0.0]), 52 | + Vertex::new([-0.5, 0.5], [0.0, 0.0, 1.]) 53 | + ] 54 | +} 55 | 56 | struct HelloTriangleApplication { 57 | instance: Arc, 58 | @@ -106,14 +127,11 @@ struct HelloTriangleApplication { 59 | swap_chain_images: Vec>>, 60 | 61 | render_pass: Arc, 62 | - // NOTE: We need to the full type of 63 | - // self.graphics_pipeline, because `BufferlessVertices` only 64 | - // works when the concrete type of the graphics pipeline is visible 65 | - // to the command buffer. 66 | - graphics_pipeline: Arc, 67 | + graphics_pipeline: Arc, 68 | 69 | swap_chain_framebuffers: Vec>, 70 | 71 | + vertex_buffer: Arc, 72 | command_buffers: Vec>, 73 | 74 | previous_frame_end: Option>, 75 | @@ -138,6 +156,8 @@ impl HelloTriangleApplication { 76 | 77 | let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); 78 | 79 | + let vertex_buffer = Self::create_vertex_buffer(&device); 80 | + 81 | let previous_frame_end = Some(Self::create_sync_objects(&device)); 82 | 83 | let mut app = Self { 84 | @@ -161,6 +181,7 @@ impl HelloTriangleApplication { 85 | 86 | swap_chain_framebuffers, 87 | 88 | + vertex_buffer, 89 | command_buffers: vec![], 90 | 91 | previous_frame_end, 92 | @@ -367,18 +388,18 @@ impl HelloTriangleApplication { 93 | device: &Arc, 94 | swap_chain_extent: [u32; 2], 95 | render_pass: &Arc, 96 | - ) -> Arc { 97 | + ) -> Arc { 98 | mod vertex_shader { 99 | vulkano_shaders::shader! { 100 | ty: "vertex", 101 | - path: "src/bin/09_shader_base.vert" 102 | + path: "src/bin/17_shader_vertexbuffer.vert" 103 | } 104 | } 105 | 106 | mod fragment_shader { 107 | vulkano_shaders::shader! { 108 | ty: "fragment", 109 | - path: "src/bin/09_shader_base.frag" 110 | + path: "src/bin/17_shader_vertexbuffer.frag" 111 | } 112 | } 113 | 114 | @@ -395,7 +416,7 @@ impl HelloTriangleApplication { 115 | }; 116 | 117 | Arc::new(GraphicsPipeline::start() 118 | - .vertex_input(BufferlessDefinition {}) 119 | + .vertex_input_single_buffer::() 120 | .vertex_shader(vert_shader_module.main_entry_point(), ()) 121 | .triangle_list() 122 | .primitive_restart(false) 123 | @@ -429,17 +450,21 @@ impl HelloTriangleApplication { 124 | ).collect::>() 125 | } 126 | 127 | + fn create_vertex_buffer(device: &Arc) -> Arc { 128 | + CpuAccessibleBuffer::from_iter(device.clone(), 129 | + BufferUsage::vertex_buffer(), vertices().iter().cloned()).unwrap() 130 | + } 131 | + 132 | fn create_command_buffers(&mut self) { 133 | let queue_family = self.graphics_queue.family(); 134 | self.command_buffers = self.swap_chain_framebuffers.iter() 135 | .map(|framebuffer| { 136 | - let vertices = BufferlessVertices { vertices: 3, instances: 1 }; 137 | Arc::new(AutoCommandBufferBuilder::primary_simultaneous_use(self.device.clone(), queue_family) 138 | .unwrap() 139 | .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) 140 | .unwrap() 141 | .draw(self.graphics_pipeline.clone(), &DynamicState::none(), 142 | - vertices, (), ()) 143 | + vec![self.vertex_buffer.clone()], (), ()) 144 | .unwrap() 145 | .end_render_pass() 146 | .unwrap() 147 | -------------------------------------------------------------------------------- /src/bin/19_staging_buffer.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/18_vertex_buffer.rs 2 | +++ b/19_staging_buffer.rs 3 | @@ -50,7 +50,7 @@ use vulkano::command_buffer::{ 4 | DynamicState, 5 | }; 6 | use vulkano::buffer::{ 7 | - cpu_access::CpuAccessibleBuffer, 8 | + immutable::ImmutableBuffer, 9 | BufferUsage, 10 | BufferAccess, 11 | }; 12 | @@ -156,7 +156,7 @@ impl HelloTriangleApplication { 13 | 14 | let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); 15 | 16 | - let vertex_buffer = Self::create_vertex_buffer(&device); 17 | + let vertex_buffer = Self::create_vertex_buffer(&graphics_queue); 18 | 19 | let previous_frame_end = Some(Self::create_sync_objects(&device)); 20 | 21 | @@ -450,9 +450,13 @@ impl HelloTriangleApplication { 22 | ).collect::>() 23 | } 24 | 25 | - fn create_vertex_buffer(device: &Arc) -> Arc { 26 | - CpuAccessibleBuffer::from_iter(device.clone(), 27 | - BufferUsage::vertex_buffer(), vertices().iter().cloned()).unwrap() 28 | + fn create_vertex_buffer(graphics_queue: &Arc) -> Arc { 29 | + let (buffer, future) = ImmutableBuffer::from_iter( 30 | + vertices().iter().cloned(), BufferUsage::vertex_buffer(), 31 | + graphics_queue.clone()) 32 | + .unwrap(); 33 | + future.flush().unwrap(); 34 | + buffer 35 | } 36 | 37 | fn create_command_buffers(&mut self) { 38 | -------------------------------------------------------------------------------- /src/bin/20_index_buffer.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/19_staging_buffer.rs 2 | +++ b/20_index_buffer.rs 3 | @@ -53,6 +53,7 @@ use vulkano::buffer::{ 4 | immutable::ImmutableBuffer, 5 | BufferUsage, 6 | BufferAccess, 7 | + TypedBufferAccess, 8 | }; 9 | 10 | const WIDTH: u32 = 800; 11 | @@ -101,14 +102,19 @@ impl Vertex { 12 | } 13 | impl_vertex!(Vertex, pos, color); 14 | 15 | -fn vertices() -> [Vertex; 3] { 16 | +fn vertices() -> [Vertex; 4] { 17 | [ 18 | - Vertex::new([0.0, -0.5], [1.0, 1.0, 1.0]), 19 | - Vertex::new([0.5, 0.5], [0.0, 1.0, 0.0]), 20 | - Vertex::new([-0.5, 0.5], [0.0, 0.0, 1.]) 21 | + Vertex::new([-0.5, -0.5], [1.0, 0.0, 0.0]), 22 | + Vertex::new([0.5, -0.5], [0.0, 1.0, 0.0]), 23 | + Vertex::new([0.5, 0.5], [0.0, 0.0, 1.0]), 24 | + Vertex::new([-0.5, 0.5], [1.0, 1.0, 1.0]) 25 | ] 26 | } 27 | 28 | +fn indices() -> [u16; 6] { 29 | + [0, 1, 2, 2, 3, 0] 30 | +} 31 | + 32 | struct HelloTriangleApplication { 33 | instance: Arc, 34 | #[allow(unused)] 35 | @@ -132,6 +138,7 @@ struct HelloTriangleApplication { 36 | swap_chain_framebuffers: Vec>, 37 | 38 | vertex_buffer: Arc, 39 | + index_buffer: Arc + Send + Sync>, 40 | command_buffers: Vec>, 41 | 42 | previous_frame_end: Option>, 43 | @@ -157,6 +164,7 @@ impl HelloTriangleApplication { 44 | let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); 45 | 46 | let vertex_buffer = Self::create_vertex_buffer(&graphics_queue); 47 | + let index_buffer = Self::create_index_buffer(&graphics_queue); 48 | 49 | let previous_frame_end = Some(Self::create_sync_objects(&device)); 50 | 51 | @@ -182,6 +190,7 @@ impl HelloTriangleApplication { 52 | swap_chain_framebuffers, 53 | 54 | vertex_buffer, 55 | + index_buffer, 56 | command_buffers: vec![], 57 | 58 | previous_frame_end, 59 | @@ -459,6 +468,15 @@ impl HelloTriangleApplication { 60 | buffer 61 | } 62 | 63 | + fn create_index_buffer(graphics_queue: &Arc) -> Arc + Send + Sync> { 64 | + let (buffer, future) = ImmutableBuffer::from_iter( 65 | + indices().iter().cloned(), BufferUsage::index_buffer(), 66 | + graphics_queue.clone()) 67 | + .unwrap(); 68 | + future.flush().unwrap(); 69 | + buffer 70 | + } 71 | + 72 | fn create_command_buffers(&mut self) { 73 | let queue_family = self.graphics_queue.family(); 74 | self.command_buffers = self.swap_chain_framebuffers.iter() 75 | @@ -467,8 +485,9 @@ impl HelloTriangleApplication { 76 | .unwrap() 77 | .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) 78 | .unwrap() 79 | - .draw(self.graphics_pipeline.clone(), &DynamicState::none(), 80 | - vec![self.vertex_buffer.clone()], (), ()) 81 | + .draw_indexed(self.graphics_pipeline.clone(), &DynamicState::none(), 82 | + vec![self.vertex_buffer.clone()], 83 | + self.index_buffer.clone(), (), ()) 84 | .unwrap() 85 | .end_render_pass() 86 | .unwrap() 87 | -------------------------------------------------------------------------------- /src/bin/21_descriptor_layout_and_buffer.rs.diff: -------------------------------------------------------------------------------- 1 | --- a/20_index_buffer.rs 2 | +++ b/21_descriptor_layout_and_buffer.rs 3 | @@ -2,9 +2,11 @@ 4 | extern crate vulkano; 5 | extern crate vulkano_win; 6 | extern crate winit; 7 | +extern crate cgmath; 8 | 9 | use std::sync::Arc; 10 | use std::collections::HashSet; 11 | +use std::time::Instant; 12 | 13 | use winit::{EventsLoop, WindowBuilder, Window, dpi::LogicalSize, Event, WindowEvent}; 14 | use vulkano_win::VkSurfaceBuild; 15 | @@ -54,6 +56,14 @@ use vulkano::buffer::{ 16 | BufferUsage, 17 | BufferAccess, 18 | TypedBufferAccess, 19 | + CpuAccessibleBuffer, 20 | +}; 21 | +use cgmath::{ 22 | + Rad, 23 | + Deg, 24 | + Matrix4, 25 | + Vector3, 26 | + Point3 27 | }; 28 | 29 | const WIDTH: u32 = 800; 30 | @@ -100,8 +110,18 @@ impl Vertex { 31 | Self { pos, color } 32 | } 33 | } 34 | + 35 | +#[allow(clippy:ref_in_deref)] 36 | impl_vertex!(Vertex, pos, color); 37 | 38 | +#[allow(dead_code)] 39 | +#[derive(Copy, Clone)] 40 | +struct UniformBufferObject { 41 | + model: Matrix4, 42 | + view: Matrix4, 43 | + proj: Matrix4, 44 | +} 45 | + 46 | fn vertices() -> [Vertex; 4] { 47 | [ 48 | Vertex::new([-0.5, -0.5], [1.0, 0.0, 0.0]), 49 | @@ -139,10 +159,17 @@ struct HelloTriangleApplication { 50 | 51 | vertex_buffer: Arc, 52 | index_buffer: Arc + Send + Sync>, 53 | + 54 | + #[allow(dead_code)] 55 | + uniform_buffers: Vec>>, 56 | + 57 | command_buffers: Vec>, 58 | 59 | previous_frame_end: Option>, 60 | recreate_swap_chain: bool, 61 | + 62 | + #[allow(dead_code)] 63 | + start_time: Instant, 64 | } 65 | 66 | impl HelloTriangleApplication { 67 | @@ -159,12 +186,16 @@ impl HelloTriangleApplication { 68 | &device, &graphics_queue, &present_queue, None); 69 | 70 | let render_pass = Self::create_render_pass(&device, swap_chain.format()); 71 | + 72 | let graphics_pipeline = Self::create_graphics_pipeline(&device, swap_chain.dimensions(), &render_pass); 73 | 74 | let swap_chain_framebuffers = Self::create_framebuffers(&swap_chain_images, &render_pass); 75 | 76 | + let start_time = Instant::now(); 77 | + 78 | let vertex_buffer = Self::create_vertex_buffer(&graphics_queue); 79 | let index_buffer = Self::create_index_buffer(&graphics_queue); 80 | + let uniform_buffers = Self::create_uniform_buffers(&device, swap_chain_images.len(), start_time, swap_chain.dimensions()); 81 | 82 | let previous_frame_end = Some(Self::create_sync_objects(&device)); 83 | 84 | @@ -191,10 +222,14 @@ impl HelloTriangleApplication { 85 | 86 | vertex_buffer, 87 | index_buffer, 88 | + uniform_buffers, 89 | + 90 | command_buffers: vec![], 91 | 92 | previous_frame_end, 93 | recreate_swap_chain: false, 94 | + 95 | + start_time 96 | }; 97 | 98 | app.create_command_buffers(); 99 | @@ -401,14 +436,14 @@ impl HelloTriangleApplication { 100 | mod vertex_shader { 101 | vulkano_shaders::shader! { 102 | ty: "vertex", 103 | - path: "src/bin/17_shader_vertexbuffer.vert" 104 | + path: "src/bin/21_shader_uniformbuffer.vert" 105 | } 106 | } 107 | 108 | mod fragment_shader { 109 | vulkano_shaders::shader! { 110 | ty: "fragment", 111 | - path: "src/bin/17_shader_vertexbuffer.frag" 112 | + path: "src/bin/21_shader_uniformbuffer.frag" 113 | } 114 | } 115 | 116 | @@ -477,6 +512,31 @@ impl HelloTriangleApplication { 117 | buffer 118 | } 119 | 120 | + fn create_uniform_buffers( 121 | + device: &Arc, 122 | + num_buffers: usize, 123 | + start_time: Instant, 124 | + dimensions_u32: [u32; 2] 125 | + ) -> Vec>> { 126 | + let mut buffers = Vec::new(); 127 | + 128 | + let dimensions = [dimensions_u32[0] as f32, dimensions_u32[1] as f32]; 129 | + 130 | + let uniform_buffer = Self::update_uniform_buffer(start_time, dimensions); 131 | + 132 | + for _ in 0..num_buffers { 133 | + let buffer = CpuAccessibleBuffer::from_data( 134 | + device.clone(), 135 | + BufferUsage::uniform_buffer_transfer_destination(), 136 | + uniform_buffer, 137 | + ).unwrap(); 138 | + 139 | + buffers.push(buffer); 140 | + } 141 | + 142 | + buffers 143 | + } 144 | + 145 | fn create_command_buffers(&mut self) { 146 | let queue_family = self.graphics_queue.family(); 147 | self.command_buffers = self.swap_chain_framebuffers.iter() 148 | @@ -485,9 +545,13 @@ impl HelloTriangleApplication { 149 | .unwrap() 150 | .begin_render_pass(framebuffer.clone(), false, vec![[0.0, 0.0, 0.0, 1.0].into()]) 151 | .unwrap() 152 | - .draw_indexed(self.graphics_pipeline.clone(), &DynamicState::none(), 153 | + .draw_indexed( 154 | + self.graphics_pipeline.clone(), 155 | + &DynamicState::none(), 156 | vec![self.vertex_buffer.clone()], 157 | - self.index_buffer.clone(), (), ()) 158 | + self.index_buffer.clone(), 159 | + (), 160 | + ()) 161 | .unwrap() 162 | .end_render_pass() 163 | .unwrap() 164 | @@ -622,6 +686,30 @@ impl HelloTriangleApplication { 165 | } 166 | } 167 | 168 | + fn update_uniform_buffer(start_time: Instant, dimensions: [f32; 2]) -> UniformBufferObject { 169 | + let duration = Instant::now().duration_since(start_time); 170 | + let elapsed = (duration.as_secs() * 1000) + u64::from(duration.subsec_millis()); 171 | + 172 | + let model = Matrix4::from_angle_z(Rad::from(Deg(elapsed as f32 * 0.180))); 173 | + 174 | + let view = Matrix4::look_at( 175 | + Point3::new(2.0, 2.0, 2.0), 176 | + Point3::new(0.0, 0.0, 0.0), 177 | + Vector3::new(0.0, 0.0, 1.0) 178 | + ); 179 | + 180 | + let mut proj = cgmath::perspective( 181 | + Rad::from(Deg(45.0)), 182 | + dimensions[0] as f32 / dimensions[1] as f32, 183 | + 0.1, 184 | + 10.0 185 | + ); 186 | + 187 | + proj.y.y *= -1.0; 188 | + 189 | + UniformBufferObject { model, view, proj } 190 | + } 191 | + 192 | fn recreate_swap_chain(&mut self) { 193 | let (swap_chain, images) = Self::create_swap_chain(&self.instance, &self.surface, self.physical_device_index, 194 | &self.device, &self.graphics_queue, &self.present_queue, Some(self.swap_chain.clone())); 195 | @@ -639,4 +727,4 @@ impl HelloTriangleApplication { 196 | fn main() { 197 | let mut app = HelloTriangleApplication::initialize(); 198 | app.main_loop(); 199 | -} 200 | +} 201 | \ No newline at end of file 202 | -------------------------------------------------------------------------------- /src/bin/21_shader_uniformbuffer.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec3 fragColor; 5 | 6 | layout(location = 0) out vec4 outColor; 7 | 8 | void main() { 9 | outColor = vec4(fragColor, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /src/bin/21_shader_uniformbuffer.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(binding = 0) uniform UniformBufferObject { 5 | mat4 model; 6 | mat4 view; 7 | mat4 proj; 8 | } ubo; 9 | 10 | // NOTE: names must match the `Vertex` struct in Rust 11 | layout(location = 0) in vec2 pos; 12 | layout(location = 1) in vec3 color; 13 | 14 | layout(location = 0) out vec3 fragColor; 15 | 16 | out gl_PerVertex { 17 | vec4 gl_Position; 18 | }; 19 | 20 | void main() { 21 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(pos, 0.0, 1.0); 22 | fragColor = color; 23 | } -------------------------------------------------------------------------------- /src/bin/21_shader_uniformbuffer.vert.diff: -------------------------------------------------------------------------------- 1 | --- a/17_shader_vertexbuffer.vert 2 | +++ b/21_shader_uniformbuffer.vert 3 | @@ -1,6 +1,12 @@ 4 | #version 450 5 | #extension GL_ARB_separate_shader_objects : enable 6 | 7 | +layout(binding = 0) uniform UniformBufferObject { 8 | + mat4 model; 9 | + mat4 view; 10 | + mat4 proj; 11 | +} ubo; 12 | + 13 | // NOTE: names must match the `Vertex` struct in Rust 14 | layout(location = 0) in vec2 pos; 15 | layout(location = 1) in vec3 color; 16 | @@ -12,6 +18,6 @@ out gl_PerVertex { 17 | }; 18 | 19 | void main() { 20 | - gl_Position = vec4(pos, 0.0, 1.0); 21 | + gl_Position = ubo.proj * ubo.view * ubo.model * vec4(pos, 0.0, 1.0); 22 | fragColor = color; 23 | -} 24 | +} 25 | \ No newline at end of file 26 | -------------------------------------------------------------------------------- /src/bin/diff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | git diff --color-words --no-index "$1" "$2" 3 | git diff --no-index "$1" "$2" | tail -n+3 > "$2".diff 4 | -------------------------------------------------------------------------------- /src/shaders/compile.bat: -------------------------------------------------------------------------------- 1 | glslangValidator -V shader.vert 2 | glslangValidator -V shader.frag 3 | -------------------------------------------------------------------------------- /src/shaders/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source ../../mac-env.sh 3 | glslangValidator -V shader.vert 4 | glslangValidator -V shader.frag 5 | -------------------------------------------------------------------------------- /src/shaders/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec3 fragColor; 5 | 6 | layout(location = 0) out vec4 outColor; 7 | 8 | void main() { 9 | outColor = vec4(fragColor, 1.0); 10 | } 11 | -------------------------------------------------------------------------------- /src/shaders/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | // NOTE: names must match the `Vertex` struct in Rust 5 | layout(location = 0) in vec2 pos; 6 | layout(location = 1) in vec3 color; 7 | 8 | layout(location = 0) out vec3 fragColor; 9 | 10 | out gl_PerVertex { 11 | vec4 gl_Position; 12 | }; 13 | 14 | void main() { 15 | gl_Position = vec4(pos, 0.0, 1.0); 16 | fragColor = color; 17 | } 18 | --------------------------------------------------------------------------------