├── .gitignore ├── bors.toml ├── src ├── 09_shader_base.frag ├── 09_shader_base.vert ├── 00_base_code.rs ├── 01_instance_creation.rs ├── 02_validation_layers.rs ├── 03_physical_device_selection.rs ├── 04_logical_device.rs ├── 05_window_surface.rs ├── 06_swap_chain_creation.rs ├── 07_image_views.rs ├── 08_graphics_pipeline.rs ├── 09_shader_modules.rs ├── 10_fixed_functions.rs ├── 11_render_passes.rs ├── 12_graphics_pipeline_complete.rs ├── 13_framebuffers.rs ├── 14_command_buffers.rs └── 15_hello_triangle.rs ├── README.md ├── appveyor.yml ├── .travis.yml ├── LICENSE-MIT ├── Cargo.toml └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | .vscode 4 | .idea 5 | *.iml 6 | 7 | -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | status = [ 2 | "continuous-integration/travis-ci/push", 3 | "continuous-integration/appveyor/branch" 4 | ] 5 | -------------------------------------------------------------------------------- /src/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 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gfx-hal-tutorial 2 | 3 | [![Join the chat at https://gitter.im/grovesNL/gfx-hal-tutorial](https://badges.gitter.im/grovesNL/gfx-hal-tutorial.svg)](https://gitter.im/grovesNL/gfx-hal-tutorial?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 4 | 5 | Reimplementing examples from [Vulkan Tutorial](https://vulkan-tutorial.com/) with Rust and [gfx-hal](https://vulkan-tutorial.com). 6 | 7 | ## License 8 | 9 | This project is licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or [MIT license](LICENSE-MIT), at your option. 10 | -------------------------------------------------------------------------------- /src/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 | 27 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | os: Visual Studio 2015 2 | 3 | environment: 4 | global: 5 | CHANNEL: stable 6 | CRATE_NAME: gfx-hal-tutorial 7 | TARGET: x86_64-pc-windows-msvc 8 | 9 | install: 10 | - curl -sSf -o rustup-init.exe https://win.rustup.rs 11 | - rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y 12 | - set PATH=%PATH%;C:\msys64\mingw64\bin;C:\Users\appveyor\.cargo\bin 13 | - rustc -Vv 14 | - cargo -V 15 | 16 | build_script: 17 | - cargo build --verbose --all --features dx12 --target %TARGET% 18 | 19 | branches: 20 | only: 21 | - staging 22 | - trying 23 | - master 24 | except: 25 | - staging.tmp 26 | 27 | notifications: 28 | - provider: Webhook 29 | url: https://webhooks.gitter.im/e/3b63eac0174975b2edc5 30 | on_build_success: false 31 | on_build_failure: true 32 | on_build_status_changed: true 33 | 34 | cache: 35 | - C:\Users\appveyor\.cargo 36 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | cache: 5 | cargo: true 6 | branches: 7 | only: 8 | - staging 9 | - trying 10 | - master 11 | except: 12 | - staging.tmp 13 | matrix: 14 | include: 15 | - os: osx 16 | osx_image: xcode9 17 | compiler: clang 18 | env: 19 | - JOB=metal 20 | - MACOSX_DEPLOYMENT_TARGET=10.9 21 | - os: linux 22 | compiler: gcc 23 | env: 24 | - JOB=vulkan 25 | - CXX=g++-5 26 | addons: 27 | apt: 28 | sources: 29 | - llvm-toolchain-precise 30 | - ubuntu-toolchain-r-test 31 | packages: 32 | - g++-5 33 | notifications: 34 | webhooks: 35 | urls: 36 | - https://webhooks.gitter.im/e/416d6e1e41c9dfaa71b7 37 | on_success: change 38 | on_failure: always 39 | on_start: never 40 | script: 41 | - if [[ $JOB == "metal" ]]; then cargo build --verbose --all --features metal; fi 42 | - if [[ $JOB == "vulkan" ]]; then cargo build --verbose --all --features vulkan; fi 43 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/00_base_code.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "dx12")] 2 | extern crate gfx_backend_dx12 as back; 3 | #[cfg(feature = "metal")] 4 | extern crate gfx_backend_metal as back; 5 | #[cfg(feature = "vulkan")] 6 | extern crate gfx_backend_vulkan as back; 7 | extern crate gfx_hal as hal; 8 | extern crate winit; 9 | 10 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 11 | 12 | static WINDOW_NAME: &str = "00_base_code"; 13 | 14 | fn main() { 15 | let mut application = HelloTriangleApplication::init(); 16 | application.run(); 17 | application.clean_up(); 18 | } 19 | 20 | struct WindowState { 21 | events_loop: EventsLoop, 22 | _window: Window, 23 | } 24 | 25 | struct HalState {} 26 | 27 | impl HalState { 28 | fn clean_up(self) {} 29 | } 30 | 31 | struct HelloTriangleApplication { 32 | hal_state: HalState, 33 | window_state: WindowState, 34 | } 35 | 36 | impl HelloTriangleApplication { 37 | pub fn init() -> HelloTriangleApplication { 38 | let window_state = HelloTriangleApplication::init_window(); 39 | let hal_state = HelloTriangleApplication::init_hal(); 40 | 41 | HelloTriangleApplication { 42 | hal_state, 43 | window_state, 44 | } 45 | } 46 | 47 | fn init_window() -> WindowState { 48 | let events_loop = EventsLoop::new(); 49 | let window_builder = WindowBuilder::new() 50 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 51 | .with_title(WINDOW_NAME.to_string()); 52 | let window = window_builder.build(&events_loop).unwrap(); 53 | WindowState { 54 | events_loop, 55 | _window: window, 56 | } 57 | } 58 | 59 | fn init_hal() -> HalState { 60 | HalState {} 61 | } 62 | 63 | fn main_loop(&mut self) { 64 | self.window_state 65 | .events_loop 66 | .run_forever(|event| match event { 67 | Event::WindowEvent { 68 | event: WindowEvent::CloseRequested, 69 | .. 70 | } => ControlFlow::Break, 71 | _ => ControlFlow::Continue, 72 | }); 73 | } 74 | 75 | fn run(&mut self) { 76 | self.main_loop(); 77 | } 78 | 79 | fn clean_up(self) { 80 | self.hal_state.clean_up(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gfx-hal-tutorial" 3 | version = "0.1.0" 4 | authors = ["Joshua Groves "] 5 | 6 | [features] 7 | default = [] 8 | metal = ["gfx-backend-metal"] 9 | dx12 = ["gfx-backend-dx12"] 10 | vulkan = ["gfx-backend-vulkan"] 11 | 12 | [dependencies] 13 | winit = "0.18" 14 | gfx-hal = { version = "0.1", git = "https://github.com/gfx-rs/gfx", branch = "master" } 15 | glsl-to-spirv = "0.1.6" 16 | log = "0.4.0" 17 | env_logger = "0.5.12" 18 | 19 | [dependencies.gfx-backend-vulkan] 20 | version = "0.1" 21 | git = "https://github.com/gfx-rs/gfx" 22 | branch = "master" 23 | optional = true 24 | 25 | [target.'cfg(target_os = "macos")'.dependencies.gfx-backend-metal] 26 | version = "0.1" 27 | git = "https://github.com/gfx-rs/gfx" 28 | branch = "master" 29 | optional = true 30 | 31 | [target.'cfg(windows)'.dependencies.gfx-backend-dx12] 32 | version = "0.1" 33 | git = "https://github.com/gfx-rs/gfx" 34 | branch = "master" 35 | optional = true 36 | 37 | [[bin]] 38 | name = "00_base_code" 39 | path = "src/00_base_code.rs" 40 | 41 | [[bin]] 42 | name = "01_instance_creation" 43 | path = "src/01_instance_creation.rs" 44 | 45 | [[bin]] 46 | name = "02_validation_layers" 47 | path = "src/02_validation_layers.rs" 48 | 49 | [[bin]] 50 | name = "03_physical_device_selection" 51 | path = "src/03_physical_device_selection.rs" 52 | 53 | [[bin]] 54 | name = "04_logical_device" 55 | path = "src/04_logical_device.rs" 56 | 57 | [[bin]] 58 | name = "05_window_surface" 59 | path = "src/05_window_surface.rs" 60 | 61 | [[bin]] 62 | name = "06_swap_chain_creation" 63 | path = "src/06_swap_chain_creation.rs" 64 | 65 | [[bin]] 66 | name = "07_image_views" 67 | path = "src/07_image_views.rs" 68 | 69 | [[bin]] 70 | name = "08_graphics_pipeline" 71 | path = "src/08_graphics_pipeline.rs" 72 | 73 | [[bin]] 74 | name = "09_shader_modules" 75 | path = "src/09_shader_modules.rs" 76 | 77 | [[bin]] 78 | name = "10_fixed_functions" 79 | path = "src/10_fixed_functions.rs" 80 | 81 | [[bin]] 82 | name = "11_render_passes" 83 | path = "src/11_render_passes.rs" 84 | 85 | [[bin]] 86 | name = "12_graphics_pipeline_complete" 87 | path = "src/12_graphics_pipeline_complete.rs" 88 | 89 | [[bin]] 90 | name = "13_framebuffers" 91 | path = "src/13_framebuffers.rs" 92 | 93 | [[bin]] 94 | name = "14_command_buffers" 95 | path = "src/14_command_buffers.rs" 96 | 97 | [[bin]] 98 | name = "15_hello_triangle" 99 | path = "src/15_hello_triangle.rs" 100 | 101 | -------------------------------------------------------------------------------- /src/01_instance_creation.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "dx12")] 2 | extern crate gfx_backend_dx12 as back; 3 | #[cfg(feature = "metal")] 4 | extern crate gfx_backend_metal as back; 5 | #[cfg(feature = "vulkan")] 6 | extern crate gfx_backend_vulkan as back; 7 | extern crate gfx_hal as hal; 8 | extern crate winit; 9 | 10 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 11 | 12 | static WINDOW_NAME: &str = "01_instance_creation"; 13 | 14 | fn main() { 15 | let mut application = HelloTriangleApplication::init(); 16 | application.run(); 17 | application.clean_up(); 18 | } 19 | 20 | struct WindowState { 21 | events_loop: EventsLoop, 22 | _window: Window, 23 | } 24 | 25 | struct HalState { 26 | _instance: back::Instance, 27 | } 28 | 29 | impl HalState { 30 | fn clean_up(self) {} 31 | } 32 | 33 | struct HelloTriangleApplication { 34 | hal_state: HalState, 35 | window_state: WindowState, 36 | } 37 | 38 | impl HelloTriangleApplication { 39 | pub fn init() -> HelloTriangleApplication { 40 | let window_state = HelloTriangleApplication::init_window(); 41 | let hal_state = HelloTriangleApplication::init_hal(); 42 | 43 | HelloTriangleApplication { 44 | hal_state, 45 | window_state, 46 | } 47 | } 48 | 49 | fn init_window() -> WindowState { 50 | let events_loop = EventsLoop::new(); 51 | let window_builder = WindowBuilder::new() 52 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 53 | .with_title(WINDOW_NAME.to_string()); 54 | let window = window_builder.build(&events_loop).unwrap(); 55 | WindowState { 56 | events_loop, 57 | _window: window, 58 | } 59 | } 60 | 61 | fn init_hal() -> HalState { 62 | let instance = HelloTriangleApplication::create_instance(); 63 | HalState { 64 | _instance: instance, 65 | } 66 | } 67 | 68 | fn create_instance() -> back::Instance { 69 | back::Instance::create(WINDOW_NAME, 1) 70 | } 71 | 72 | fn main_loop(&mut self) { 73 | self.window_state 74 | .events_loop 75 | .run_forever(|event| match event { 76 | Event::WindowEvent { 77 | event: WindowEvent::CloseRequested, 78 | .. 79 | } => ControlFlow::Break, 80 | _ => ControlFlow::Continue, 81 | }); 82 | } 83 | 84 | fn run(&mut self) { 85 | self.main_loop(); 86 | } 87 | 88 | fn clean_up(self) { 89 | self.hal_state.clean_up(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/02_validation_layers.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate winit; 10 | 11 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 12 | 13 | static WINDOW_NAME: &str = "02_validation_layers"; 14 | 15 | fn main() { 16 | // if building in debug mode, vulkan backend initializes standard validation layers 17 | // all we need to do is enable logging 18 | // run the program like so to print all logs of level 'warn' and above: 19 | // bash: RUST_LOG=warn && cargo run --bin 02_validation_layers --features vulkan 20 | // powershell: $env:RUST_LOG="warn"; cargo run --bin 02_validation_layers --features vulkan 21 | // see: https://docs.rs/env_logger/0.5.13/env_logger/ 22 | env_logger::init(); 23 | let mut application = HelloTriangleApplication::init(); 24 | application.run(); 25 | application.clean_up(); 26 | } 27 | 28 | struct WindowState { 29 | events_loop: EventsLoop, 30 | _window: Window, 31 | } 32 | 33 | struct HalState { 34 | _instance: back::Instance, 35 | } 36 | 37 | impl HalState { 38 | fn clean_up(self) {} 39 | } 40 | 41 | struct HelloTriangleApplication { 42 | hal_state: HalState, 43 | window_state: WindowState, 44 | } 45 | 46 | impl HelloTriangleApplication { 47 | pub fn init() -> HelloTriangleApplication { 48 | let window_state = HelloTriangleApplication::init_window(); 49 | let hal_state = HelloTriangleApplication::init_hal(); 50 | 51 | HelloTriangleApplication { 52 | hal_state, 53 | window_state, 54 | } 55 | } 56 | 57 | fn init_window() -> WindowState { 58 | let events_loop = EventsLoop::new(); 59 | let window_builder = WindowBuilder::new() 60 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 61 | .with_title(WINDOW_NAME.to_string()); 62 | let window = window_builder.build(&events_loop).unwrap(); 63 | WindowState { 64 | events_loop, 65 | _window: window, 66 | } 67 | } 68 | 69 | fn init_hal() -> HalState { 70 | let instance = HelloTriangleApplication::create_instance(); 71 | HalState { 72 | _instance: instance, 73 | } 74 | } 75 | 76 | fn create_instance() -> back::Instance { 77 | back::Instance::create(WINDOW_NAME, 1) 78 | } 79 | 80 | fn main_loop(&mut self) { 81 | self.window_state 82 | .events_loop 83 | .run_forever(|event| match event { 84 | Event::WindowEvent { 85 | event: WindowEvent::CloseRequested, 86 | .. 87 | } => ControlFlow::Break, 88 | _ => ControlFlow::Continue, 89 | }); 90 | } 91 | 92 | fn run(&mut self) { 93 | self.main_loop(); 94 | } 95 | 96 | fn clean_up(self) { 97 | self.hal_state.clean_up(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/03_physical_device_selection.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate winit; 10 | 11 | use hal::{queue, Adapter, Instance, QueueFamily}; 12 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 13 | 14 | static WINDOW_NAME: &str = "03_physical_device_selection"; 15 | 16 | fn main() { 17 | env_logger::init(); 18 | let mut application = HelloTriangleApplication::init(); 19 | application.run(); 20 | application.clean_up(); 21 | } 22 | 23 | struct WindowState { 24 | events_loop: EventsLoop, 25 | _window: Window, 26 | } 27 | 28 | struct HalState { 29 | _adapter: Adapter, 30 | _instance: back::Instance, 31 | } 32 | 33 | impl HalState { 34 | fn clean_up(self) {} 35 | } 36 | 37 | struct HelloTriangleApplication { 38 | hal_state: HalState, 39 | window_state: WindowState, 40 | } 41 | 42 | #[derive(Default)] 43 | struct QueueFamilyIds { 44 | graphics_family: Option, 45 | } 46 | 47 | impl QueueFamilyIds { 48 | fn is_complete(&self) -> bool { 49 | self.graphics_family.is_some() 50 | } 51 | } 52 | 53 | impl HelloTriangleApplication { 54 | pub fn init() -> HelloTriangleApplication { 55 | let window_state = HelloTriangleApplication::init_window(); 56 | let hal_state = HelloTriangleApplication::init_hal(); 57 | 58 | HelloTriangleApplication { 59 | hal_state, 60 | window_state, 61 | } 62 | } 63 | 64 | fn init_window() -> WindowState { 65 | let events_loop = EventsLoop::new(); 66 | let window_builder = WindowBuilder::new() 67 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 68 | .with_title(WINDOW_NAME.to_string()); 69 | let window = window_builder.build(&events_loop).unwrap(); 70 | WindowState { 71 | events_loop, 72 | _window: window, 73 | } 74 | } 75 | 76 | fn init_hal() -> HalState { 77 | let instance = HelloTriangleApplication::create_instance(); 78 | let adapter = HelloTriangleApplication::pick_adapter(&instance); 79 | HalState { 80 | _adapter: adapter, 81 | _instance: instance, 82 | } 83 | } 84 | 85 | fn create_instance() -> back::Instance { 86 | back::Instance::create(WINDOW_NAME, 1) 87 | } 88 | 89 | fn find_queue_families(adapter: &Adapter) -> QueueFamilyIds { 90 | let mut queue_family_ids = QueueFamilyIds::default(); 91 | 92 | for queue_family in &adapter.queue_families { 93 | if queue_family.max_queues() > 0 && queue_family.supports_graphics() { 94 | queue_family_ids.graphics_family = Some(queue_family.id()); 95 | } 96 | 97 | if queue_family_ids.is_complete() { 98 | break; 99 | } 100 | } 101 | 102 | queue_family_ids 103 | } 104 | 105 | fn is_adapter_suitable(adapter: &Adapter) -> bool { 106 | HelloTriangleApplication::find_queue_families(adapter).is_complete() 107 | } 108 | 109 | fn pick_adapter(instance: &back::Instance) -> Adapter { 110 | let adapters = instance.enumerate_adapters(); 111 | for adapter in adapters { 112 | if HelloTriangleApplication::is_adapter_suitable(&adapter) { 113 | return adapter; 114 | } 115 | } 116 | panic!("No suitable adapter"); 117 | } 118 | 119 | fn main_loop(&mut self) { 120 | self.window_state 121 | .events_loop 122 | .run_forever(|event| match event { 123 | Event::WindowEvent { 124 | event: WindowEvent::CloseRequested, 125 | .. 126 | } => ControlFlow::Break, 127 | _ => ControlFlow::Continue, 128 | }); 129 | } 130 | 131 | fn run(&mut self) { 132 | self.main_loop(); 133 | } 134 | 135 | fn clean_up(self) { 136 | self.hal_state.clean_up(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/04_logical_device.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate winit; 10 | 11 | use hal::{ 12 | queue, Adapter, Backend, Capability, Features, Gpu, Graphics, Instance, PhysicalDevice, 13 | QueueFamily, 14 | }; 15 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 16 | 17 | static WINDOW_NAME: &str = "04_logical_device"; 18 | 19 | fn main() { 20 | env_logger::init(); 21 | let mut application = HelloTriangleApplication::init(); 22 | application.run(); 23 | application.clean_up(); 24 | } 25 | 26 | struct WindowState { 27 | events_loop: EventsLoop, 28 | _window: Window, 29 | } 30 | 31 | struct HalState { 32 | _command_queues: Vec>, 33 | _device: ::Device, 34 | _adapter: Adapter, 35 | _instance: back::Instance, 36 | } 37 | 38 | impl HalState { 39 | fn clean_up(self) {} 40 | } 41 | 42 | struct HelloTriangleApplication { 43 | hal_state: HalState, 44 | window_state: WindowState, 45 | } 46 | 47 | #[derive(Default)] 48 | struct QueueFamilyIds { 49 | graphics_family: Option, 50 | } 51 | 52 | impl QueueFamilyIds { 53 | fn is_complete(&self) -> bool { 54 | self.graphics_family.is_some() 55 | } 56 | } 57 | 58 | impl HelloTriangleApplication { 59 | pub fn init() -> HelloTriangleApplication { 60 | let window_state = HelloTriangleApplication::init_window(); 61 | let hal_state = HelloTriangleApplication::init_hal(); 62 | 63 | HelloTriangleApplication { 64 | hal_state, 65 | window_state, 66 | } 67 | } 68 | 69 | fn init_window() -> WindowState { 70 | let events_loop = EventsLoop::new(); 71 | let window_builder = WindowBuilder::new() 72 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 73 | .with_title(WINDOW_NAME.to_string()); 74 | let window = window_builder.build(&events_loop).unwrap(); 75 | WindowState { 76 | events_loop, 77 | _window: window, 78 | } 79 | } 80 | 81 | fn init_hal() -> HalState { 82 | let instance = HelloTriangleApplication::create_instance(); 83 | let mut adapter = HelloTriangleApplication::pick_adapter(&instance); 84 | let (device, command_queues) = 85 | HelloTriangleApplication::create_device_with_graphics_queues(&mut adapter); 86 | HalState { 87 | _command_queues: command_queues, 88 | _device: device, 89 | _adapter: adapter, 90 | _instance: instance, 91 | } 92 | } 93 | 94 | fn create_instance() -> back::Instance { 95 | back::Instance::create(WINDOW_NAME, 1) 96 | } 97 | 98 | fn find_queue_families(adapter: &Adapter) -> QueueFamilyIds { 99 | let mut queue_family_ids = QueueFamilyIds::default(); 100 | 101 | for queue_family in &adapter.queue_families { 102 | if queue_family.max_queues() > 0 && queue_family.supports_graphics() { 103 | queue_family_ids.graphics_family = Some(queue_family.id()); 104 | } 105 | 106 | if queue_family_ids.is_complete() { 107 | break; 108 | } 109 | } 110 | 111 | queue_family_ids 112 | } 113 | 114 | fn is_adapter_suitable(adapter: &Adapter) -> bool { 115 | HelloTriangleApplication::find_queue_families(adapter).is_complete() 116 | } 117 | 118 | fn pick_adapter(instance: &back::Instance) -> Adapter { 119 | let adapters = instance.enumerate_adapters(); 120 | for adapter in adapters { 121 | if HelloTriangleApplication::is_adapter_suitable(&adapter) { 122 | return adapter; 123 | } 124 | } 125 | panic!("No suitable adapter"); 126 | } 127 | 128 | fn create_device_with_graphics_queues( 129 | adapter: &mut Adapter, 130 | ) -> ( 131 | ::Device, 132 | Vec>, 133 | ) { 134 | let family = adapter 135 | .queue_families 136 | .iter() 137 | .find(|family| Graphics::supported_by(family.queue_type()) && family.max_queues() > 0) 138 | .expect("Could not find a queue family supporting graphics."); 139 | 140 | // we only want to create a single queue 141 | let priorities = vec![1.0; 1]; 142 | let families = [(family, priorities.as_slice())]; 143 | 144 | let Gpu { device, mut queues } = unsafe { 145 | adapter 146 | .physical_device 147 | .open(&families, Features::empty()) 148 | .expect("Could not create device.") 149 | }; 150 | 151 | let mut queue_group = queues 152 | .take::(family.id()) 153 | .expect("Could not take ownership of relevant queue group."); 154 | 155 | let command_queues: Vec<_> = queue_group.queues.drain(..1).collect(); 156 | 157 | (device, command_queues) 158 | } 159 | 160 | fn main_loop(&mut self) { 161 | self.window_state 162 | .events_loop 163 | .run_forever(|event| match event { 164 | Event::WindowEvent { 165 | event: WindowEvent::CloseRequested, 166 | .. 167 | } => ControlFlow::Break, 168 | _ => ControlFlow::Continue, 169 | }); 170 | } 171 | 172 | fn run(&mut self) { 173 | self.main_loop(); 174 | } 175 | 176 | fn clean_up(self) { 177 | self.hal_state.clean_up(); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/05_window_surface.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate winit; 10 | 11 | use hal::{ 12 | queue, Adapter, Backend, Capability, Features, Gpu, Graphics, Instance, PhysicalDevice, 13 | QueueFamily, Surface, 14 | }; 15 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 16 | 17 | static WINDOW_NAME: &str = "05_window_surface"; 18 | 19 | fn main() { 20 | env_logger::init(); 21 | let mut application = HelloTriangleApplication::init(); 22 | application.run(); 23 | application.clean_up(); 24 | } 25 | 26 | struct WindowState { 27 | events_loop: EventsLoop, 28 | window: Window, 29 | } 30 | 31 | struct HalState { 32 | _command_queues: Vec>, 33 | _device: ::Device, 34 | _surface: ::Surface, 35 | _adapter: Adapter, 36 | _instance: back::Instance, 37 | } 38 | 39 | impl HalState { 40 | fn clean_up(self) {} 41 | } 42 | 43 | struct HelloTriangleApplication { 44 | hal_state: HalState, 45 | window_state: WindowState, 46 | } 47 | 48 | #[derive(Default)] 49 | struct QueueFamilyIds { 50 | graphics_family: Option, 51 | } 52 | 53 | impl QueueFamilyIds { 54 | fn is_complete(&self) -> bool { 55 | self.graphics_family.is_some() 56 | } 57 | } 58 | 59 | impl HelloTriangleApplication { 60 | pub fn init() -> HelloTriangleApplication { 61 | let window_state = HelloTriangleApplication::init_window(); 62 | let hal_state = HelloTriangleApplication::init_hal(&window_state.window); 63 | 64 | HelloTriangleApplication { 65 | hal_state, 66 | window_state, 67 | } 68 | } 69 | 70 | fn init_window() -> WindowState { 71 | let events_loop = EventsLoop::new(); 72 | let window_builder = WindowBuilder::new() 73 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 74 | .with_title(WINDOW_NAME.to_string()); 75 | let window = window_builder.build(&events_loop).unwrap(); 76 | WindowState { 77 | events_loop, 78 | window, 79 | } 80 | } 81 | 82 | fn init_hal(window: &Window) -> HalState { 83 | let instance = HelloTriangleApplication::create_instance(); 84 | let mut adapter = HelloTriangleApplication::pick_adapter(&instance); 85 | let surface = HelloTriangleApplication::create_surface(&instance, window); 86 | let (device, command_queues) = 87 | HelloTriangleApplication::create_device_with_graphics_queues(&mut adapter, &surface); 88 | 89 | HalState { 90 | _command_queues: command_queues, 91 | _device: device, 92 | _surface: surface, 93 | _adapter: adapter, 94 | _instance: instance, 95 | } 96 | } 97 | 98 | fn create_instance() -> back::Instance { 99 | back::Instance::create(WINDOW_NAME, 1) 100 | } 101 | 102 | fn find_queue_families(adapter: &Adapter) -> QueueFamilyIds { 103 | let mut queue_family_ids = QueueFamilyIds::default(); 104 | 105 | for queue_family in &adapter.queue_families { 106 | if queue_family.max_queues() > 0 && queue_family.supports_graphics() { 107 | queue_family_ids.graphics_family = Some(queue_family.id()); 108 | } 109 | 110 | if queue_family_ids.is_complete() { 111 | break; 112 | } 113 | } 114 | 115 | queue_family_ids 116 | } 117 | 118 | fn is_adapter_suitable(adapter: &Adapter) -> bool { 119 | HelloTriangleApplication::find_queue_families(adapter).is_complete() 120 | } 121 | 122 | fn pick_adapter(instance: &back::Instance) -> Adapter { 123 | let adapters = instance.enumerate_adapters(); 124 | for adapter in adapters { 125 | if HelloTriangleApplication::is_adapter_suitable(&adapter) { 126 | return adapter; 127 | } 128 | } 129 | panic!("No suitable adapter"); 130 | } 131 | 132 | fn create_surface( 133 | instance: &back::Instance, 134 | window: &Window, 135 | ) -> ::Surface { 136 | instance.create_surface(window) 137 | } 138 | 139 | // we have an additional check to make: make sure the queue family selected supports presentation to the surface we have created 140 | fn create_device_with_graphics_queues( 141 | adapter: &mut Adapter, 142 | surface: &::Surface, 143 | ) -> ( 144 | ::Device, 145 | Vec>, 146 | ) { 147 | let family = adapter 148 | .queue_families 149 | .iter() 150 | .find(|family| { 151 | Graphics::supported_by(family.queue_type()) 152 | && family.max_queues() > 0 153 | && surface.supports_queue_family(family) 154 | }) 155 | .expect("Could not find a queue family supporting graphics."); 156 | 157 | let priorities = vec![1.0; 1]; 158 | let families = [(family, priorities.as_slice())]; 159 | 160 | let Gpu { device, mut queues } = unsafe { 161 | adapter 162 | .physical_device 163 | .open(&families, Features::empty()) 164 | .expect("Could not create device.") 165 | }; 166 | 167 | let mut queue_group = queues 168 | .take::(family.id()) 169 | .expect("Could not take ownership of relevant queue group."); 170 | 171 | let command_queues: Vec<_> = queue_group.queues.drain(..1).collect(); 172 | 173 | (device, command_queues) 174 | } 175 | 176 | fn main_loop(&mut self) { 177 | self.window_state 178 | .events_loop 179 | .run_forever(|event| match event { 180 | Event::WindowEvent { 181 | event: WindowEvent::CloseRequested, 182 | .. 183 | } => ControlFlow::Break, 184 | _ => ControlFlow::Continue, 185 | }); 186 | } 187 | 188 | fn run(&mut self) { 189 | self.main_loop(); 190 | } 191 | 192 | fn clean_up(self) { 193 | self.hal_state.clean_up(); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/06_swap_chain_creation.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate winit; 10 | 11 | use hal::{ 12 | format, queue, Adapter, Backbuffer, Backend, Capability, Device, Features, Gpu, Graphics, 13 | Instance, PhysicalDevice, QueueFamily, Surface, SwapchainConfig, 14 | }; 15 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 16 | 17 | static WINDOW_NAME: &str = "06_swap_chain_creation"; 18 | 19 | fn main() { 20 | env_logger::init(); 21 | let mut application = HelloTriangleApplication::init(); 22 | application.run(); 23 | unsafe { 24 | application.clean_up(); 25 | } 26 | } 27 | 28 | struct WindowState { 29 | events_loop: EventsLoop, 30 | window: Window, 31 | } 32 | 33 | struct HalState { 34 | swapchain: ::Swapchain, 35 | _command_queues: Vec>, 36 | device: ::Device, 37 | _surface: ::Surface, 38 | _adapter: Adapter, 39 | _instance: back::Instance, 40 | } 41 | 42 | impl HalState { 43 | unsafe fn clean_up(self) { 44 | self.device.destroy_swapchain(self.swapchain); 45 | } 46 | } 47 | 48 | struct HelloTriangleApplication { 49 | hal_state: HalState, 50 | window_state: WindowState, 51 | } 52 | 53 | #[derive(Default)] 54 | struct QueueFamilyIds { 55 | graphics_family: Option, 56 | } 57 | 58 | impl QueueFamilyIds { 59 | fn is_complete(&self) -> bool { 60 | self.graphics_family.is_some() 61 | } 62 | } 63 | 64 | impl HelloTriangleApplication { 65 | pub fn init() -> HelloTriangleApplication { 66 | let window_state = HelloTriangleApplication::init_window(); 67 | let hal_state = HelloTriangleApplication::init_hal(&window_state.window); 68 | 69 | HelloTriangleApplication { 70 | hal_state, 71 | window_state, 72 | } 73 | } 74 | 75 | fn init_window() -> WindowState { 76 | let events_loop = EventsLoop::new(); 77 | let window_builder = WindowBuilder::new() 78 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 79 | .with_title(WINDOW_NAME.to_string()); 80 | let window = window_builder.build(&events_loop).unwrap(); 81 | WindowState { 82 | events_loop, 83 | window, 84 | } 85 | } 86 | 87 | fn init_hal(window: &Window) -> HalState { 88 | let instance = HelloTriangleApplication::create_instance(); 89 | let mut adapter = HelloTriangleApplication::pick_adapter(&instance); 90 | let mut surface = HelloTriangleApplication::create_surface(&instance, window); 91 | let (device, command_queues) = 92 | HelloTriangleApplication::create_device_with_graphics_queues(&mut adapter, &surface); 93 | let (swapchain, _backbuffer, _format) = 94 | HelloTriangleApplication::create_swap_chain(&adapter, &device, &mut surface, None); 95 | 96 | HalState { 97 | swapchain, 98 | _command_queues: command_queues, 99 | device, 100 | _surface: surface, 101 | _adapter: adapter, 102 | _instance: instance, 103 | } 104 | } 105 | 106 | fn create_instance() -> back::Instance { 107 | back::Instance::create(WINDOW_NAME, 1) 108 | } 109 | 110 | fn find_queue_families(adapter: &Adapter) -> QueueFamilyIds { 111 | let mut queue_family_ids = QueueFamilyIds::default(); 112 | 113 | for queue_family in &adapter.queue_families { 114 | if queue_family.max_queues() > 0 && queue_family.supports_graphics() { 115 | queue_family_ids.graphics_family = Some(queue_family.id()); 116 | } 117 | 118 | if queue_family_ids.is_complete() { 119 | break; 120 | } 121 | } 122 | 123 | queue_family_ids 124 | } 125 | 126 | fn is_adapter_suitable(adapter: &Adapter) -> bool { 127 | HelloTriangleApplication::find_queue_families(adapter).is_complete() 128 | } 129 | 130 | fn pick_adapter(instance: &back::Instance) -> Adapter { 131 | let adapters = instance.enumerate_adapters(); 132 | for adapter in adapters { 133 | if HelloTriangleApplication::is_adapter_suitable(&adapter) { 134 | return adapter; 135 | } 136 | } 137 | panic!("No suitable adapter"); 138 | } 139 | 140 | fn create_surface( 141 | instance: &back::Instance, 142 | window: &Window, 143 | ) -> ::Surface { 144 | instance.create_surface(window) 145 | } 146 | 147 | fn create_device_with_graphics_queues( 148 | adapter: &mut Adapter, 149 | surface: &::Surface, 150 | ) -> ( 151 | ::Device, 152 | Vec>, 153 | ) { 154 | let family = adapter 155 | .queue_families 156 | .iter() 157 | .find(|family| { 158 | Graphics::supported_by(family.queue_type()) 159 | && family.max_queues() > 0 160 | && surface.supports_queue_family(family) 161 | }) 162 | .expect("Could not find a queue family supporting graphics."); 163 | 164 | let priorities = vec![1.0; 1]; 165 | let families = [(family, priorities.as_slice())]; 166 | 167 | let Gpu { device, mut queues } = unsafe { 168 | adapter 169 | .physical_device 170 | .open(&families, Features::empty()) 171 | .expect("Could not create device.") 172 | }; 173 | 174 | let mut queue_group = queues 175 | .take::(family.id()) 176 | .expect("Could not take ownership of relevant queue group."); 177 | 178 | let command_queues: Vec<_> = queue_group.queues.drain(..1).collect(); 179 | 180 | (device, command_queues) 181 | } 182 | 183 | fn create_swap_chain( 184 | adapter: &Adapter, 185 | device: &::Device, 186 | surface: &mut ::Surface, 187 | previous_swapchain: Option<::Swapchain>, 188 | ) -> ( 189 | ::Swapchain, 190 | Backbuffer, 191 | format::Format, 192 | ) { 193 | let (caps, formats, _present_modes, _composite_alphas) = 194 | surface.compatibility(&adapter.physical_device); 195 | 196 | let format = formats.map_or(format::Format::Rgba8Srgb, |formats| { 197 | formats 198 | .iter() 199 | .find(|format| format.base_format().1 == format::ChannelType::Srgb) 200 | .map(|format| *format) 201 | .unwrap_or(formats[0]) 202 | }); 203 | 204 | // what should default extent be? 205 | let swap_config = SwapchainConfig::from_caps(&caps, format, caps.extents.end); 206 | let (swapchain, backbuffer) = unsafe { 207 | device 208 | .create_swapchain(surface, swap_config, previous_swapchain) 209 | .unwrap() 210 | }; 211 | 212 | (swapchain, backbuffer, format) 213 | } 214 | 215 | fn main_loop(&mut self) { 216 | self.window_state 217 | .events_loop 218 | .run_forever(|event| match event { 219 | Event::WindowEvent { 220 | event: WindowEvent::CloseRequested, 221 | .. 222 | } => ControlFlow::Break, 223 | _ => ControlFlow::Continue, 224 | }); 225 | } 226 | 227 | fn run(&mut self) { 228 | self.main_loop(); 229 | } 230 | 231 | unsafe fn clean_up(self) { 232 | self.hal_state.clean_up(); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/07_image_views.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate winit; 10 | 11 | use hal::{ 12 | format, image, queue, window, Adapter, Backbuffer, Backend, Capability, Device, Features, Gpu, 13 | Graphics, Instance, PhysicalDevice, QueueFamily, Surface, SwapchainConfig, 14 | }; 15 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 16 | 17 | static WINDOW_NAME: &str = "07_image_views"; 18 | 19 | fn main() { 20 | env_logger::init(); 21 | let mut application = HelloTriangleApplication::init(); 22 | application.run(); 23 | unsafe { 24 | application.clean_up(); 25 | } 26 | } 27 | 28 | struct WindowState { 29 | events_loop: EventsLoop, 30 | window: Window, 31 | } 32 | 33 | struct HalState { 34 | frame_images: Vec<( 35 | ::Image, 36 | ::ImageView, 37 | )>, 38 | _format: format::Format, 39 | swapchain: ::Swapchain, 40 | _command_queues: Vec>, 41 | device: ::Device, 42 | _surface: ::Surface, 43 | _adapter: Adapter, 44 | _instance: back::Instance, 45 | } 46 | 47 | impl HalState { 48 | unsafe fn clean_up(self) { 49 | let device = &self.device; 50 | 51 | for (_, image_view) in self.frame_images.into_iter() { 52 | device.destroy_image_view(image_view); 53 | } 54 | 55 | device.destroy_swapchain(self.swapchain); 56 | } 57 | } 58 | 59 | struct HelloTriangleApplication { 60 | hal_state: HalState, 61 | window_state: WindowState, 62 | } 63 | 64 | #[derive(Default)] 65 | struct QueueFamilyIds { 66 | graphics_family: Option, 67 | } 68 | 69 | impl QueueFamilyIds { 70 | fn is_complete(&self) -> bool { 71 | self.graphics_family.is_some() 72 | } 73 | } 74 | 75 | impl HelloTriangleApplication { 76 | pub fn init() -> HelloTriangleApplication { 77 | let window_state = HelloTriangleApplication::init_window(); 78 | let hal_state = unsafe { HelloTriangleApplication::init_hal(&window_state.window) }; 79 | 80 | HelloTriangleApplication { 81 | hal_state, 82 | window_state, 83 | } 84 | } 85 | 86 | fn init_window() -> WindowState { 87 | let events_loop = EventsLoop::new(); 88 | let window_builder = WindowBuilder::new() 89 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 90 | .with_title(WINDOW_NAME.to_string()); 91 | let window = window_builder.build(&events_loop).unwrap(); 92 | WindowState { 93 | events_loop, 94 | window, 95 | } 96 | } 97 | 98 | unsafe fn init_hal(window: &Window) -> HalState { 99 | let instance = HelloTriangleApplication::create_instance(); 100 | let mut adapter = HelloTriangleApplication::pick_adapter(&instance); 101 | let mut surface = HelloTriangleApplication::create_surface(&instance, window); 102 | let (device, command_queues) = 103 | HelloTriangleApplication::create_device_with_graphics_queues(&mut adapter, &surface); 104 | let (swapchain, backbuffer, format) = 105 | HelloTriangleApplication::create_swap_chain(&adapter, &device, &mut surface, None); 106 | let frame_images = 107 | HelloTriangleApplication::create_image_views(backbuffer, format, &device); 108 | 109 | HalState { 110 | frame_images, 111 | _format: format, 112 | swapchain, 113 | _command_queues: command_queues, 114 | device, 115 | _surface: surface, 116 | _adapter: adapter, 117 | _instance: instance, 118 | } 119 | } 120 | 121 | fn create_instance() -> back::Instance { 122 | back::Instance::create(WINDOW_NAME, 1) 123 | } 124 | 125 | fn find_queue_families(adapter: &Adapter) -> QueueFamilyIds { 126 | let mut queue_family_ids = QueueFamilyIds::default(); 127 | 128 | for queue_family in &adapter.queue_families { 129 | if queue_family.max_queues() > 0 && queue_family.supports_graphics() { 130 | queue_family_ids.graphics_family = Some(queue_family.id()); 131 | } 132 | 133 | if queue_family_ids.is_complete() { 134 | break; 135 | } 136 | } 137 | 138 | queue_family_ids 139 | } 140 | 141 | fn is_adapter_suitable(adapter: &Adapter) -> bool { 142 | HelloTriangleApplication::find_queue_families(adapter).is_complete() 143 | } 144 | 145 | fn pick_adapter(instance: &back::Instance) -> Adapter { 146 | let adapters = instance.enumerate_adapters(); 147 | for adapter in adapters { 148 | if HelloTriangleApplication::is_adapter_suitable(&adapter) { 149 | return adapter; 150 | } 151 | } 152 | panic!("No suitable adapter"); 153 | } 154 | 155 | fn create_surface( 156 | instance: &back::Instance, 157 | window: &Window, 158 | ) -> ::Surface { 159 | instance.create_surface(window) 160 | } 161 | 162 | fn create_device_with_graphics_queues( 163 | adapter: &mut Adapter, 164 | surface: &::Surface, 165 | ) -> ( 166 | ::Device, 167 | Vec>, 168 | ) { 169 | let family = adapter 170 | .queue_families 171 | .iter() 172 | .find(|family| { 173 | Graphics::supported_by(family.queue_type()) 174 | && family.max_queues() > 0 175 | && surface.supports_queue_family(family) 176 | }) 177 | .expect("Could not find a queue family supporting graphics."); 178 | 179 | let priorities = vec![1.0; 1]; 180 | let families = [(family, priorities.as_slice())]; 181 | 182 | let Gpu { device, mut queues } = unsafe { 183 | adapter 184 | .physical_device 185 | .open(&families, Features::empty()) 186 | .expect("Could not create device.") 187 | }; 188 | 189 | let mut queue_group = queues 190 | .take::(family.id()) 191 | .expect("Could not take ownership of relevant queue group."); 192 | 193 | let command_queues: Vec<_> = queue_group.queues.drain(..1).collect(); 194 | 195 | (device, command_queues) 196 | } 197 | 198 | fn create_swap_chain( 199 | adapter: &Adapter, 200 | device: &::Device, 201 | surface: &mut ::Surface, 202 | previous_swapchain: Option<::Swapchain>, 203 | ) -> ( 204 | ::Swapchain, 205 | Backbuffer, 206 | format::Format, 207 | ) { 208 | let (caps, formats, _present_modes, _composite_alphas) = 209 | surface.compatibility(&adapter.physical_device); 210 | 211 | let format = formats.map_or(format::Format::Rgba8Srgb, |formats| { 212 | formats 213 | .iter() 214 | .find(|format| format.base_format().1 == format::ChannelType::Srgb) 215 | .map(|format| *format) 216 | .unwrap_or(formats[0]) 217 | }); 218 | 219 | let swap_config = SwapchainConfig::from_caps(&caps, format, caps.extents.end); 220 | 221 | let (swapchain, backbuffer) = unsafe { 222 | device 223 | .create_swapchain(surface, swap_config, previous_swapchain) 224 | .unwrap() 225 | }; 226 | 227 | (swapchain, backbuffer, format) 228 | } 229 | 230 | unsafe fn create_image_views( 231 | backbuffer: Backbuffer, 232 | format: format::Format, 233 | device: &::Device, 234 | ) -> Vec<( 235 | ::Image, 236 | ::ImageView, 237 | )> { 238 | match backbuffer { 239 | window::Backbuffer::Images(images) => images 240 | .into_iter() 241 | .map(|image| { 242 | let image_view = match device.create_image_view( 243 | &image, 244 | image::ViewKind::D2, 245 | format, 246 | format::Swizzle::NO, 247 | image::SubresourceRange { 248 | aspects: format::Aspects::COLOR, 249 | levels: 0..1, 250 | layers: 0..1, 251 | }, 252 | ) { 253 | Ok(image_view) => image_view, 254 | Err(_) => panic!("Error creating image view for an image!"), 255 | }; 256 | 257 | (image, image_view) 258 | }) 259 | .collect(), 260 | // OpenGL case, where backbuffer is a framebuffer, not implemented currently 261 | _ => unimplemented!(), 262 | } 263 | } 264 | 265 | fn main_loop(&mut self) { 266 | self.window_state 267 | .events_loop 268 | .run_forever(|event| match event { 269 | Event::WindowEvent { 270 | event: WindowEvent::CloseRequested, 271 | .. 272 | } => ControlFlow::Break, 273 | _ => ControlFlow::Continue, 274 | }); 275 | } 276 | 277 | fn run(&mut self) { 278 | self.main_loop(); 279 | } 280 | 281 | unsafe fn clean_up(self) { 282 | self.hal_state.clean_up(); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /src/08_graphics_pipeline.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate winit; 10 | 11 | use hal::{ 12 | format, image, queue, window, Adapter, Backbuffer, Backend, Capability, Device, Features, Gpu, 13 | Graphics, Instance, PhysicalDevice, QueueFamily, Surface, SwapchainConfig, 14 | }; 15 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 16 | 17 | static WINDOW_NAME: &str = "08_graphics_pipeline"; 18 | 19 | fn main() { 20 | env_logger::init(); 21 | let mut application = HelloTriangleApplication::init(); 22 | application.run(); 23 | unsafe { 24 | application.clean_up(); 25 | } 26 | } 27 | 28 | struct WindowState { 29 | events_loop: EventsLoop, 30 | window: Window, 31 | } 32 | 33 | struct HalState { 34 | frame_images: Vec<( 35 | ::Image, 36 | ::ImageView, 37 | )>, 38 | _format: format::Format, 39 | swapchain: ::Swapchain, 40 | _command_queues: Vec>, 41 | device: ::Device, 42 | _surface: ::Surface, 43 | _adapter: Adapter, 44 | _instance: back::Instance, 45 | } 46 | 47 | struct HelloTriangleApplication { 48 | hal_state: HalState, 49 | window_state: WindowState, 50 | } 51 | 52 | impl HalState { 53 | unsafe fn clean_up(self) { 54 | let device = &self.device; 55 | 56 | for (_, image_view) in self.frame_images.into_iter() { 57 | device.destroy_image_view(image_view); 58 | } 59 | 60 | device.destroy_swapchain(self.swapchain); 61 | } 62 | } 63 | 64 | #[derive(Default)] 65 | struct QueueFamilyIds { 66 | graphics_family: Option, 67 | } 68 | 69 | impl QueueFamilyIds { 70 | fn is_complete(&self) -> bool { 71 | self.graphics_family.is_some() 72 | } 73 | } 74 | 75 | impl HelloTriangleApplication { 76 | pub fn init() -> HelloTriangleApplication { 77 | let window_state = HelloTriangleApplication::init_window(); 78 | let hal_state = unsafe { HelloTriangleApplication::init_hal(&window_state.window) }; 79 | 80 | HelloTriangleApplication { 81 | hal_state, 82 | window_state, 83 | } 84 | } 85 | 86 | fn init_window() -> WindowState { 87 | let events_loop = EventsLoop::new(); 88 | let window_builder = WindowBuilder::new() 89 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 90 | .with_title(WINDOW_NAME.to_string()); 91 | let window = window_builder.build(&events_loop).unwrap(); 92 | WindowState { 93 | events_loop, 94 | window, 95 | } 96 | } 97 | 98 | unsafe fn init_hal(window: &Window) -> HalState { 99 | let instance = HelloTriangleApplication::create_instance(); 100 | let mut adapter = HelloTriangleApplication::pick_adapter(&instance); 101 | let mut surface = HelloTriangleApplication::create_surface(&instance, window); 102 | let (device, command_queues) = 103 | HelloTriangleApplication::create_device_with_graphics_queues(&mut adapter, &surface); 104 | let (swapchain, backbuffer, format) = 105 | HelloTriangleApplication::create_swap_chain(&adapter, &device, &mut surface, None); 106 | let frame_images = 107 | HelloTriangleApplication::create_image_views(backbuffer, format, &device); 108 | 109 | HalState { 110 | frame_images, 111 | _format: format, 112 | swapchain, 113 | _command_queues: command_queues, 114 | device, 115 | _surface: surface, 116 | _adapter: adapter, 117 | _instance: instance, 118 | } 119 | } 120 | 121 | fn create_instance() -> back::Instance { 122 | back::Instance::create(WINDOW_NAME, 1) 123 | } 124 | 125 | fn find_queue_families(adapter: &Adapter) -> QueueFamilyIds { 126 | let mut queue_family_ids = QueueFamilyIds::default(); 127 | 128 | for queue_family in &adapter.queue_families { 129 | if queue_family.max_queues() > 0 && queue_family.supports_graphics() { 130 | queue_family_ids.graphics_family = Some(queue_family.id()); 131 | } 132 | 133 | if queue_family_ids.is_complete() { 134 | break; 135 | } 136 | } 137 | 138 | queue_family_ids 139 | } 140 | 141 | fn is_adapter_suitable(adapter: &Adapter) -> bool { 142 | HelloTriangleApplication::find_queue_families(adapter).is_complete() 143 | } 144 | 145 | fn pick_adapter(instance: &back::Instance) -> Adapter { 146 | let adapters = instance.enumerate_adapters(); 147 | for adapter in adapters { 148 | if HelloTriangleApplication::is_adapter_suitable(&adapter) { 149 | return adapter; 150 | } 151 | } 152 | panic!("No suitable adapter"); 153 | } 154 | 155 | fn create_surface( 156 | instance: &back::Instance, 157 | window: &Window, 158 | ) -> ::Surface { 159 | instance.create_surface(window) 160 | } 161 | 162 | fn create_device_with_graphics_queues( 163 | adapter: &mut Adapter, 164 | surface: &::Surface, 165 | ) -> ( 166 | ::Device, 167 | Vec>, 168 | ) { 169 | let family = adapter 170 | .queue_families 171 | .iter() 172 | .find(|family| { 173 | Graphics::supported_by(family.queue_type()) 174 | && family.max_queues() > 0 175 | && surface.supports_queue_family(family) 176 | }) 177 | .expect("Could not find a queue family supporting graphics."); 178 | 179 | let priorities = vec![1.0; 1]; 180 | let families = [(family, priorities.as_slice())]; 181 | 182 | let Gpu { device, mut queues } = unsafe { 183 | adapter 184 | .physical_device 185 | .open(&families, Features::empty()) 186 | .expect("Could not create device.") 187 | }; 188 | 189 | let mut queue_group = queues 190 | .take::(family.id()) 191 | .expect("Could not take ownership of relevant queue group."); 192 | 193 | let command_queues: Vec<_> = queue_group.queues.drain(..1).collect(); 194 | 195 | (device, command_queues) 196 | } 197 | 198 | fn create_swap_chain( 199 | adapter: &Adapter, 200 | device: &::Device, 201 | surface: &mut ::Surface, 202 | previous_swapchain: Option<::Swapchain>, 203 | ) -> ( 204 | ::Swapchain, 205 | Backbuffer, 206 | format::Format, 207 | ) { 208 | let (caps, formats, _present_modes, _composite_alphas) = 209 | surface.compatibility(&adapter.physical_device); 210 | 211 | let format = formats.map_or(format::Format::Rgba8Srgb, |formats| { 212 | formats 213 | .iter() 214 | .find(|format| format.base_format().1 == format::ChannelType::Srgb) 215 | .map(|format| *format) 216 | .unwrap_or(formats[0]) 217 | }); 218 | 219 | let swap_config = SwapchainConfig::from_caps(&caps, format, caps.extents.end); 220 | 221 | let (swapchain, backbuffer) = unsafe { 222 | device 223 | .create_swapchain(surface, swap_config, previous_swapchain) 224 | .unwrap() 225 | }; 226 | 227 | (swapchain, backbuffer, format) 228 | } 229 | 230 | unsafe fn create_image_views( 231 | backbuffer: Backbuffer, 232 | format: format::Format, 233 | device: &::Device, 234 | ) -> Vec<( 235 | ::Image, 236 | ::ImageView, 237 | )> { 238 | match backbuffer { 239 | window::Backbuffer::Images(images) => images 240 | .into_iter() 241 | .map(|image| { 242 | let image_view = match device.create_image_view( 243 | &image, 244 | image::ViewKind::D2, 245 | format, 246 | format::Swizzle::NO, 247 | image::SubresourceRange { 248 | aspects: format::Aspects::COLOR, 249 | levels: 0..1, 250 | layers: 0..1, 251 | }, 252 | ) { 253 | Ok(image_view) => image_view, 254 | Err(_) => panic!("Error creating image view for an image!"), 255 | }; 256 | 257 | (image, image_view) 258 | }) 259 | .collect(), 260 | _ => unimplemented!(), 261 | } 262 | } 263 | 264 | #[allow(dead_code)] 265 | fn create_graphics_pipeline() { 266 | // our goal is to fill out this entire struct 267 | // let desc = pso::GraphicsPipelineDesc { 268 | // shaders, 269 | // rasterizer, 270 | // vertex_buffers, 271 | // attributes, 272 | // input_assembler, 273 | // blender, 274 | // depth_stencil, 275 | // multisampling, 276 | // baked_states, 277 | // layout, 278 | // subpass, 279 | // flags, 280 | // parent, 281 | // }; 282 | 283 | // device.create_graphics_pipeline(desc, None); 284 | } 285 | 286 | fn main_loop(&mut self) { 287 | self.window_state 288 | .events_loop 289 | .run_forever(|event| match event { 290 | Event::WindowEvent { 291 | event: WindowEvent::CloseRequested, 292 | .. 293 | } => ControlFlow::Break, 294 | _ => ControlFlow::Continue, 295 | }); 296 | } 297 | 298 | fn run(&mut self) { 299 | self.main_loop(); 300 | } 301 | 302 | unsafe fn clean_up(self) { 303 | self.hal_state.clean_up(); 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /src/09_shader_modules.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate glsl_to_spirv; 10 | extern crate winit; 11 | 12 | use hal::{ 13 | format, image, queue, window, Adapter, Backbuffer, Backend, Capability, Device, Features, Gpu, 14 | Graphics, Instance, PhysicalDevice, QueueFamily, Surface, SwapchainConfig, 15 | }; 16 | use std::io::Read; 17 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 18 | 19 | static WINDOW_NAME: &str = "09_shader_modules"; 20 | 21 | fn main() { 22 | env_logger::init(); 23 | let mut application = HelloTriangleApplication::init(); 24 | application.run(); 25 | unsafe { 26 | application.clean_up(); 27 | } 28 | } 29 | 30 | struct WindowState { 31 | events_loop: EventsLoop, 32 | window: Window, 33 | } 34 | 35 | struct HalState { 36 | frame_images: Vec<( 37 | ::Image, 38 | ::ImageView, 39 | )>, 40 | _format: format::Format, 41 | swapchain: ::Swapchain, 42 | _command_queues: Vec>, 43 | device: ::Device, 44 | _surface: ::Surface, 45 | _adapter: Adapter, 46 | _instance: back::Instance, 47 | } 48 | 49 | impl HalState { 50 | unsafe fn clean_up(self) { 51 | let device = &self.device; 52 | 53 | for (_, image_view) in self.frame_images.into_iter() { 54 | device.destroy_image_view(image_view); 55 | } 56 | 57 | device.destroy_swapchain(self.swapchain); 58 | } 59 | } 60 | 61 | struct HelloTriangleApplication { 62 | hal_state: HalState, 63 | window_state: WindowState, 64 | } 65 | 66 | #[derive(Default)] 67 | struct QueueFamilyIds { 68 | graphics_family: Option, 69 | } 70 | 71 | impl QueueFamilyIds { 72 | fn is_complete(&self) -> bool { 73 | self.graphics_family.is_some() 74 | } 75 | } 76 | 77 | impl HelloTriangleApplication { 78 | pub fn init() -> HelloTriangleApplication { 79 | let window_state = HelloTriangleApplication::init_window(); 80 | let hal_state = unsafe { HelloTriangleApplication::init_hal(&window_state.window) }; 81 | 82 | HelloTriangleApplication { 83 | hal_state, 84 | window_state, 85 | } 86 | } 87 | 88 | fn init_window() -> WindowState { 89 | let events_loop = EventsLoop::new(); 90 | let window_builder = WindowBuilder::new() 91 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 92 | .with_title(WINDOW_NAME.to_string()); 93 | let window = window_builder.build(&events_loop).unwrap(); 94 | 95 | WindowState { 96 | events_loop, 97 | window, 98 | } 99 | } 100 | 101 | unsafe fn init_hal(window: &Window) -> HalState { 102 | let instance = HelloTriangleApplication::create_instance(); 103 | let mut adapter = HelloTriangleApplication::pick_adapter(&instance); 104 | let mut surface = HelloTriangleApplication::create_surface(&instance, window); 105 | let (device, command_queues) = 106 | HelloTriangleApplication::create_device_with_graphics_queues(&mut adapter, &surface); 107 | let (swapchain, backbuffer, format) = 108 | HelloTriangleApplication::create_swap_chain(&adapter, &device, &mut surface, None); 109 | let frame_images = 110 | HelloTriangleApplication::create_image_views(backbuffer, format, &device); 111 | HalState { 112 | frame_images, 113 | _format: format, 114 | swapchain, 115 | _command_queues: command_queues, 116 | device, 117 | _surface: surface, 118 | _adapter: adapter, 119 | _instance: instance, 120 | } 121 | } 122 | 123 | fn create_instance() -> back::Instance { 124 | back::Instance::create(WINDOW_NAME, 1) 125 | } 126 | 127 | fn find_queue_families(adapter: &Adapter) -> QueueFamilyIds { 128 | let mut queue_family_ids = QueueFamilyIds::default(); 129 | 130 | for queue_family in &adapter.queue_families { 131 | if queue_family.max_queues() > 0 && queue_family.supports_graphics() { 132 | queue_family_ids.graphics_family = Some(queue_family.id()); 133 | } 134 | 135 | if queue_family_ids.is_complete() { 136 | break; 137 | } 138 | } 139 | 140 | queue_family_ids 141 | } 142 | 143 | fn is_adapter_suitable(adapter: &Adapter) -> bool { 144 | HelloTriangleApplication::find_queue_families(adapter).is_complete() 145 | } 146 | 147 | fn pick_adapter(instance: &back::Instance) -> Adapter { 148 | let adapters = instance.enumerate_adapters(); 149 | for adapter in adapters { 150 | if HelloTriangleApplication::is_adapter_suitable(&adapter) { 151 | return adapter; 152 | } 153 | } 154 | panic!("No suitable adapter"); 155 | } 156 | 157 | fn create_surface( 158 | instance: &back::Instance, 159 | window: &Window, 160 | ) -> ::Surface { 161 | instance.create_surface(window) 162 | } 163 | 164 | fn create_device_with_graphics_queues( 165 | adapter: &mut Adapter, 166 | surface: &::Surface, 167 | ) -> ( 168 | ::Device, 169 | Vec>, 170 | ) { 171 | let family = adapter 172 | .queue_families 173 | .iter() 174 | .find(|family| { 175 | Graphics::supported_by(family.queue_type()) 176 | && family.max_queues() > 0 177 | && surface.supports_queue_family(family) 178 | }) 179 | .expect("Could not find a queue family supporting graphics."); 180 | 181 | let priorities = vec![1.0; 1]; 182 | let families = [(family, priorities.as_slice())]; 183 | 184 | let Gpu { device, mut queues } = unsafe { 185 | adapter 186 | .physical_device 187 | .open(&families, Features::empty()) 188 | .expect("Could not create device.") 189 | }; 190 | 191 | let mut queue_group = queues 192 | .take::(family.id()) 193 | .expect("Could not take ownership of relevant queue group."); 194 | 195 | let command_queues: Vec<_> = queue_group.queues.drain(..1).collect(); 196 | 197 | (device, command_queues) 198 | } 199 | 200 | fn create_swap_chain( 201 | adapter: &Adapter, 202 | device: &::Device, 203 | surface: &mut ::Surface, 204 | previous_swapchain: Option<::Swapchain>, 205 | ) -> ( 206 | ::Swapchain, 207 | Backbuffer, 208 | format::Format, 209 | ) { 210 | let (caps, formats, _present_modes, _composite_alphas) = 211 | surface.compatibility(&adapter.physical_device); 212 | 213 | let format = formats.map_or(format::Format::Rgba8Srgb, |formats| { 214 | formats 215 | .iter() 216 | .find(|format| format.base_format().1 == format::ChannelType::Srgb) 217 | .map(|format| *format) 218 | .unwrap_or(formats[0]) 219 | }); 220 | 221 | let swap_config = SwapchainConfig::from_caps(&caps, format, caps.extents.end); 222 | 223 | let (swapchain, backbuffer) = unsafe { 224 | device 225 | .create_swapchain(surface, swap_config, previous_swapchain) 226 | .unwrap() 227 | }; 228 | 229 | (swapchain, backbuffer, format) 230 | } 231 | 232 | unsafe fn create_image_views( 233 | backbuffer: Backbuffer, 234 | format: format::Format, 235 | device: &::Device, 236 | ) -> Vec<( 237 | ::Image, 238 | ::ImageView, 239 | )> { 240 | match backbuffer { 241 | window::Backbuffer::Images(images) => images 242 | .into_iter() 243 | .map(|image| { 244 | let image_view = match device.create_image_view( 245 | &image, 246 | image::ViewKind::D2, 247 | format, 248 | format::Swizzle::NO, 249 | image::SubresourceRange { 250 | aspects: format::Aspects::COLOR, 251 | levels: 0..1, 252 | layers: 0..1, 253 | }, 254 | ) { 255 | Ok(image_view) => image_view, 256 | Err(_) => panic!("Error creating image view for an image!"), 257 | }; 258 | 259 | (image, image_view) 260 | }) 261 | .collect(), 262 | _ => unimplemented!(), 263 | } 264 | } 265 | 266 | #[allow(dead_code)] 267 | fn create_graphics_pipeline(device: &::Device) { 268 | let vert_shader_code = glsl_to_spirv::compile( 269 | include_str!("09_shader_base.vert"), 270 | glsl_to_spirv::ShaderType::Vertex, 271 | ) 272 | .expect("Error compiling vertex shader code.") 273 | .bytes() 274 | .map(|b| b.unwrap()) 275 | .collect::>(); 276 | 277 | let frag_shader_code = glsl_to_spirv::compile( 278 | include_str!("09_shader_base.frag"), 279 | glsl_to_spirv::ShaderType::Fragment, 280 | ) 281 | .expect("Error compiling fragment shader code.") 282 | .bytes() 283 | .map(|b| b.unwrap()) 284 | .collect::>(); 285 | 286 | unsafe { 287 | let vert_shader_module = device 288 | .create_shader_module(&vert_shader_code) 289 | .expect("Error creating shader module."); 290 | let frag_shader_module = device 291 | .create_shader_module(&frag_shader_code) 292 | .expect("Error creating fragment module."); 293 | 294 | // our goal is to fill out this entire struct 295 | // let desc = pso::GraphicsPipelineDesc { 296 | // shaders, 297 | // rasterizer, 298 | // vertex_buffers, 299 | // attributes, 300 | // input_assembler, 301 | // blender, 302 | // depth_stencil, 303 | // multisampling, 304 | // baked_states, 305 | // layout, 306 | // subpass, 307 | // flags, 308 | // parent, 309 | // }; 310 | 311 | device.destroy_shader_module(vert_shader_module); 312 | device.destroy_shader_module(frag_shader_module); 313 | } 314 | 315 | // device.create_graphics_pipeline(desc, None); 316 | } 317 | 318 | fn main_loop(&mut self) { 319 | self.window_state 320 | .events_loop 321 | .run_forever(|event| match event { 322 | Event::WindowEvent { 323 | event: WindowEvent::CloseRequested, 324 | .. 325 | } => ControlFlow::Break, 326 | _ => ControlFlow::Continue, 327 | }); 328 | } 329 | 330 | fn run(&mut self) { 331 | self.main_loop(); 332 | } 333 | 334 | unsafe fn clean_up(self) { 335 | self.hal_state.clean_up(); 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /src/10_fixed_functions.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate glsl_to_spirv; 10 | extern crate winit; 11 | 12 | use hal::{ 13 | format, image, pso, queue, window, Adapter, Backbuffer, Backend, Capability, Device, Features, 14 | Gpu, Graphics, Instance, PhysicalDevice, Primitive, QueueFamily, Surface, SwapchainConfig, 15 | }; 16 | use std::io::Read; 17 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 18 | 19 | static WINDOW_NAME: &str = "10_fixed_functions"; 20 | 21 | fn main() { 22 | env_logger::init(); 23 | let mut application = HelloTriangleApplication::init(); 24 | application.run(); 25 | unsafe { 26 | application.clean_up(); 27 | } 28 | } 29 | 30 | struct WindowState { 31 | events_loop: EventsLoop, 32 | window: Window, 33 | } 34 | 35 | struct HalState { 36 | descriptor_set_layouts: Vec<::DescriptorSetLayout>, 37 | pipeline_layout: ::PipelineLayout, 38 | frame_images: Vec<( 39 | ::Image, 40 | ::ImageView, 41 | )>, 42 | _format: format::Format, 43 | swapchain: ::Swapchain, 44 | _command_queues: Vec>, 45 | device: ::Device, 46 | _surface: ::Surface, 47 | _adapter: Adapter, 48 | _instance: back::Instance, 49 | } 50 | 51 | struct HelloTriangleApplication { 52 | hal_state: HalState, 53 | window_state: WindowState, 54 | } 55 | 56 | impl HalState { 57 | unsafe fn clean_up(self) { 58 | let device = &self.device; 59 | 60 | for descriptor_set_layout in self.descriptor_set_layouts { 61 | device.destroy_descriptor_set_layout(descriptor_set_layout); 62 | } 63 | 64 | device.destroy_pipeline_layout(self.pipeline_layout); 65 | 66 | for (_, image_view) in self.frame_images.into_iter() { 67 | device.destroy_image_view(image_view); 68 | } 69 | 70 | device.destroy_swapchain(self.swapchain); 71 | } 72 | } 73 | 74 | #[derive(Default)] 75 | struct QueueFamilyIds { 76 | graphics_family: Option, 77 | } 78 | 79 | impl QueueFamilyIds { 80 | fn is_complete(&self) -> bool { 81 | self.graphics_family.is_some() 82 | } 83 | } 84 | 85 | impl HelloTriangleApplication { 86 | pub fn init() -> HelloTriangleApplication { 87 | let window_state = HelloTriangleApplication::init_window(); 88 | let hal_state = unsafe { HelloTriangleApplication::init_hal(&window_state.window) }; 89 | 90 | HelloTriangleApplication { 91 | hal_state, 92 | window_state, 93 | } 94 | } 95 | 96 | fn init_window() -> WindowState { 97 | let events_loop = EventsLoop::new(); 98 | let window_builder = WindowBuilder::new() 99 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 100 | .with_title(WINDOW_NAME.to_string()); 101 | let window = window_builder.build(&events_loop).unwrap(); 102 | 103 | WindowState { 104 | events_loop, 105 | window, 106 | } 107 | } 108 | 109 | unsafe fn init_hal(window: &Window) -> HalState { 110 | let instance = HelloTriangleApplication::create_instance(); 111 | let mut adapter = HelloTriangleApplication::pick_adapter(&instance); 112 | let mut surface = HelloTriangleApplication::create_surface(&instance, window); 113 | let (device, command_queues) = 114 | HelloTriangleApplication::create_device_with_graphics_queues(&mut adapter, &surface); 115 | let (swapchain, extent, backbuffer, format) = 116 | HelloTriangleApplication::create_swap_chain(&adapter, &device, &mut surface, None); 117 | let frame_images = 118 | HelloTriangleApplication::create_image_views(backbuffer, format, &device); 119 | let (descriptor_set_layouts, pipeline_layout) = 120 | HelloTriangleApplication::create_graphics_pipeline(&device, extent); 121 | 122 | HalState { 123 | descriptor_set_layouts, 124 | pipeline_layout, 125 | frame_images, 126 | _format: format, 127 | swapchain, 128 | _command_queues: command_queues, 129 | device, 130 | _surface: surface, 131 | _adapter: adapter, 132 | _instance: instance, 133 | } 134 | } 135 | 136 | fn create_instance() -> back::Instance { 137 | back::Instance::create(WINDOW_NAME, 1) 138 | } 139 | 140 | fn find_queue_families(adapter: &Adapter) -> QueueFamilyIds { 141 | let mut queue_family_ids = QueueFamilyIds::default(); 142 | 143 | for queue_family in &adapter.queue_families { 144 | if queue_family.max_queues() > 0 && queue_family.supports_graphics() { 145 | queue_family_ids.graphics_family = Some(queue_family.id()); 146 | } 147 | 148 | if queue_family_ids.is_complete() { 149 | break; 150 | } 151 | } 152 | 153 | queue_family_ids 154 | } 155 | 156 | fn is_adapter_suitable(adapter: &Adapter) -> bool { 157 | HelloTriangleApplication::find_queue_families(adapter).is_complete() 158 | } 159 | 160 | fn pick_adapter(instance: &back::Instance) -> Adapter { 161 | let adapters = instance.enumerate_adapters(); 162 | for adapter in adapters { 163 | if HelloTriangleApplication::is_adapter_suitable(&adapter) { 164 | return adapter; 165 | } 166 | } 167 | panic!("No suitable adapter"); 168 | } 169 | 170 | fn create_surface( 171 | instance: &back::Instance, 172 | window: &Window, 173 | ) -> ::Surface { 174 | instance.create_surface(window) 175 | } 176 | 177 | fn create_device_with_graphics_queues( 178 | adapter: &mut Adapter, 179 | surface: &::Surface, 180 | ) -> ( 181 | ::Device, 182 | Vec>, 183 | ) { 184 | let family = adapter 185 | .queue_families 186 | .iter() 187 | .find(|family| { 188 | Graphics::supported_by(family.queue_type()) 189 | && family.max_queues() > 0 190 | && surface.supports_queue_family(family) 191 | }) 192 | .expect("Could not find a queue family supporting graphics."); 193 | 194 | let priorities = vec![1.0; 1]; 195 | let families = [(family, priorities.as_slice())]; 196 | 197 | let Gpu { device, mut queues } = unsafe { 198 | adapter 199 | .physical_device 200 | .open(&families, Features::empty()) 201 | .expect("Could not create device.") 202 | }; 203 | 204 | let mut queue_group = queues 205 | .take::(family.id()) 206 | .expect("Could not take ownership of relevant queue group."); 207 | 208 | let command_queues: Vec<_> = queue_group.queues.drain(..1).collect(); 209 | 210 | (device, command_queues) 211 | } 212 | 213 | fn create_swap_chain( 214 | adapter: &Adapter, 215 | device: &::Device, 216 | surface: &mut ::Surface, 217 | previous_swapchain: Option<::Swapchain>, 218 | ) -> ( 219 | ::Swapchain, 220 | window::Extent2D, 221 | Backbuffer, 222 | format::Format, 223 | ) { 224 | let (caps, formats, _present_modes, _composite_alphas) = 225 | surface.compatibility(&adapter.physical_device); 226 | 227 | let format = formats.map_or(format::Format::Rgba8Srgb, |formats| { 228 | formats 229 | .iter() 230 | .find(|format| format.base_format().1 == format::ChannelType::Srgb) 231 | .map(|format| *format) 232 | .unwrap_or(formats[0]) 233 | }); 234 | 235 | let swap_config = SwapchainConfig::from_caps(&caps, format, caps.extents.end); 236 | let extent = swap_config.extent; 237 | let (swapchain, backbuffer) = unsafe { 238 | device 239 | .create_swapchain(surface, swap_config, previous_swapchain) 240 | .unwrap() 241 | }; 242 | 243 | (swapchain, extent, backbuffer, format) 244 | } 245 | 246 | unsafe fn create_image_views( 247 | backbuffer: Backbuffer, 248 | format: format::Format, 249 | device: &::Device, 250 | ) -> Vec<( 251 | ::Image, 252 | ::ImageView, 253 | )> { 254 | match backbuffer { 255 | window::Backbuffer::Images(images) => images 256 | .into_iter() 257 | .map(|image| { 258 | let image_view = match device.create_image_view( 259 | &image, 260 | image::ViewKind::D2, 261 | format, 262 | format::Swizzle::NO, 263 | image::SubresourceRange { 264 | aspects: format::Aspects::COLOR, 265 | levels: 0..1, 266 | layers: 0..1, 267 | }, 268 | ) { 269 | Ok(image_view) => image_view, 270 | Err(_) => panic!("Error creating image view for an image!"), 271 | }; 272 | 273 | (image, image_view) 274 | }) 275 | .collect(), 276 | _ => unimplemented!(), 277 | } 278 | } 279 | 280 | unsafe fn create_graphics_pipeline( 281 | device: &::Device, 282 | extent: window::Extent2D, 283 | ) -> ( 284 | Vec<::DescriptorSetLayout>, 285 | ::PipelineLayout, 286 | ) { 287 | let vert_shader_code = glsl_to_spirv::compile( 288 | include_str!("09_shader_base.vert"), 289 | glsl_to_spirv::ShaderType::Vertex, 290 | ) 291 | .expect("Error compiling vertex shader code.") 292 | .bytes() 293 | .map(|b| b.unwrap()) 294 | .collect::>(); 295 | 296 | let frag_shader_code = glsl_to_spirv::compile( 297 | include_str!("09_shader_base.frag"), 298 | glsl_to_spirv::ShaderType::Fragment, 299 | ) 300 | .expect("Error compiling fragment shader code.") 301 | .bytes() 302 | .map(|b| b.unwrap()) 303 | .collect::>(); 304 | 305 | let vert_shader_module = device 306 | .create_shader_module(&vert_shader_code) 307 | .expect("Error creating shader module."); 308 | let frag_shader_module = device 309 | .create_shader_module(&frag_shader_code) 310 | .expect("Error creating fragment module."); 311 | 312 | let (ds_layouts, pipeline_layout) = { 313 | let (vs_entry, fs_entry) = ( 314 | pso::EntryPoint:: { 315 | entry: "main", 316 | module: &vert_shader_module, 317 | specialization: hal::pso::Specialization { 318 | constants: &[], 319 | data: &[], 320 | }, 321 | }, 322 | pso::EntryPoint:: { 323 | entry: "main", 324 | module: &frag_shader_module, 325 | specialization: hal::pso::Specialization { 326 | constants: &[], 327 | data: &[], 328 | }, 329 | }, 330 | ); 331 | 332 | let _shaders = pso::GraphicsShaderSet { 333 | vertex: vs_entry, 334 | hull: None, 335 | domain: None, 336 | geometry: None, 337 | fragment: Some(fs_entry), 338 | }; 339 | 340 | let _rasterizer = pso::Rasterizer { 341 | depth_clamping: false, 342 | polygon_mode: pso::PolygonMode::Fill, 343 | cull_face: ::BACK, 344 | front_face: pso::FrontFace::Clockwise, 345 | depth_bias: None, 346 | conservative: false, 347 | }; 348 | 349 | // no need to set up vertex input format, as it is hardcoded 350 | let _vertex_buffers: Vec = Vec::new(); 351 | let _attributes: Vec = Vec::new(); 352 | 353 | let _input_assembler = pso::InputAssemblerDesc::new(Primitive::TriangleList); 354 | 355 | // implements optional blending description provided in vulkan-tutorial 356 | let _blender = { 357 | let blend_state = pso::BlendState::On { 358 | color: pso::BlendOp::Add { 359 | src: pso::Factor::One, 360 | dst: pso::Factor::Zero, 361 | }, 362 | alpha: pso::BlendOp::Add { 363 | src: pso::Factor::One, 364 | dst: pso::Factor::Zero, 365 | }, 366 | }; 367 | 368 | pso::BlendDesc { 369 | logic_op: Some(pso::LogicOp::Copy), 370 | targets: vec![pso::ColorBlendDesc(pso::ColorMask::ALL, blend_state)], 371 | } 372 | }; 373 | 374 | let _depth_stencil = pso::DepthStencilDesc { 375 | depth: pso::DepthTest::Off, 376 | depth_bounds: false, 377 | stencil: pso::StencilTest::Off, 378 | }; 379 | 380 | let _multisampling: Option = None; 381 | 382 | // viewports and scissors 383 | let _baked_states = pso::BakedStates { 384 | viewport: Some(pso::Viewport { 385 | rect: pso::Rect { 386 | x: 0, 387 | y: 0, 388 | w: extent.width as i16, 389 | h: extent.height as i16, 390 | }, 391 | depth: (0.0..1.0), 392 | }), 393 | scissor: Some(pso::Rect { 394 | x: 0, 395 | y: 0, 396 | w: extent.width as i16, 397 | h: extent.height as i16, 398 | }), 399 | blend_color: None, 400 | depth_bounds: None, 401 | }; 402 | 403 | // with HAL, user only needs to specify whether overall state is static or dynamic 404 | 405 | // pipeline layout 406 | let bindings = Vec::::new(); 407 | let immutable_samplers = Vec::<::Sampler>::new(); 408 | let ds_layouts: Vec<::DescriptorSetLayout> = vec![device 409 | .create_descriptor_set_layout(bindings, immutable_samplers) 410 | .unwrap()]; 411 | let push_constants = Vec::<(pso::ShaderStageFlags, std::ops::Range)>::new(); 412 | let layout = device 413 | .create_pipeline_layout(&ds_layouts, push_constants) 414 | .unwrap(); 415 | 416 | // our goal is to fill out this entire struct 417 | // let desc = pso::GraphicsPipelineDesc { 418 | // shaders, 419 | // rasterizer, 420 | // vertex_buffers, 421 | // attributes, 422 | // input_assembler, 423 | // blender, 424 | // depth_stencil, 425 | // multisampling, 426 | // baked_states, 427 | // layout, 428 | // subpass, 429 | // flags, 430 | // parent, 431 | // }; 432 | 433 | (ds_layouts, layout) 434 | }; 435 | 436 | device.destroy_shader_module(vert_shader_module); 437 | device.destroy_shader_module(frag_shader_module); 438 | 439 | (ds_layouts, pipeline_layout) 440 | } 441 | 442 | fn main_loop(&mut self) { 443 | self.window_state 444 | .events_loop 445 | .run_forever(|event| match event { 446 | Event::WindowEvent { 447 | event: WindowEvent::CloseRequested, 448 | .. 449 | } => ControlFlow::Break, 450 | _ => ControlFlow::Continue, 451 | }); 452 | } 453 | 454 | fn run(&mut self) { 455 | self.main_loop(); 456 | } 457 | 458 | unsafe fn clean_up(self) { 459 | self.hal_state.clean_up(); 460 | } 461 | } 462 | -------------------------------------------------------------------------------- /src/11_render_passes.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate glsl_to_spirv; 10 | extern crate winit; 11 | 12 | use hal::{ 13 | format, image, pass, pso, queue, window, Adapter, Backbuffer, Backend, Capability, Device, 14 | Features, Gpu, Graphics, Instance, PhysicalDevice, Primitive, QueueFamily, Surface, 15 | SwapchainConfig, 16 | }; 17 | use std::io::Read; 18 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 19 | 20 | static WINDOW_NAME: &str = "11_render_passes"; 21 | 22 | fn main() { 23 | env_logger::init(); 24 | let mut application = HelloTriangleApplication::init(); 25 | application.run(); 26 | unsafe { 27 | application.clean_up(); 28 | } 29 | } 30 | 31 | struct WindowState { 32 | events_loop: EventsLoop, 33 | window: Window, 34 | } 35 | 36 | struct HalState { 37 | descriptor_set_layouts: Vec<::DescriptorSetLayout>, 38 | pipeline_layout: ::PipelineLayout, 39 | render_pass: ::RenderPass, 40 | frame_images: Vec<( 41 | ::Image, 42 | ::ImageView, 43 | )>, 44 | _format: format::Format, 45 | swapchain: ::Swapchain, 46 | _command_queues: Vec>, 47 | device: ::Device, 48 | _surface: ::Surface, 49 | _adapter: Adapter, 50 | _instance: back::Instance, 51 | } 52 | 53 | impl HalState { 54 | unsafe fn clean_up(self) { 55 | let device = &self.device; 56 | 57 | for descriptor_set_layout in self.descriptor_set_layouts { 58 | device.destroy_descriptor_set_layout(descriptor_set_layout); 59 | } 60 | 61 | device.destroy_pipeline_layout(self.pipeline_layout); 62 | 63 | device.destroy_render_pass(self.render_pass); 64 | 65 | for (_, image_view) in self.frame_images.into_iter() { 66 | device.destroy_image_view(image_view); 67 | } 68 | 69 | device.destroy_swapchain(self.swapchain); 70 | } 71 | } 72 | 73 | struct HelloTriangleApplication { 74 | hal_state: HalState, 75 | window_state: WindowState, 76 | } 77 | 78 | #[derive(Default)] 79 | struct QueueFamilyIds { 80 | graphics_family: Option, 81 | } 82 | 83 | impl QueueFamilyIds { 84 | fn is_complete(&self) -> bool { 85 | self.graphics_family.is_some() 86 | } 87 | } 88 | 89 | impl HelloTriangleApplication { 90 | pub fn init() -> HelloTriangleApplication { 91 | let window_state = HelloTriangleApplication::init_window(); 92 | let hal_state = unsafe { HelloTriangleApplication::init_hal(&window_state.window) }; 93 | 94 | HelloTriangleApplication { 95 | hal_state, 96 | window_state, 97 | } 98 | } 99 | 100 | fn init_window() -> WindowState { 101 | let events_loop = EventsLoop::new(); 102 | let window_builder = WindowBuilder::new() 103 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 104 | .with_title(WINDOW_NAME.to_string()); 105 | let window = window_builder.build(&events_loop).unwrap(); 106 | 107 | WindowState { 108 | events_loop, 109 | window, 110 | } 111 | } 112 | 113 | unsafe fn init_hal(window: &Window) -> HalState { 114 | let instance = HelloTriangleApplication::create_instance(); 115 | let mut adapter = HelloTriangleApplication::pick_adapter(&instance); 116 | let mut surface = HelloTriangleApplication::create_surface(&instance, window); 117 | let (device, command_queues) = 118 | HelloTriangleApplication::create_device_with_graphics_queues(&mut adapter, &surface); 119 | let (swapchain, extent, backbuffer, format) = 120 | HelloTriangleApplication::create_swap_chain(&adapter, &device, &mut surface, None); 121 | let frame_images = 122 | HelloTriangleApplication::create_image_views(backbuffer, format, &device); 123 | let render_pass = HelloTriangleApplication::create_render_pass(&device, Some(format)); 124 | let (descriptor_set_layouts, pipeline_layout) = 125 | HelloTriangleApplication::create_graphics_pipeline(&device, extent); 126 | 127 | HalState { 128 | descriptor_set_layouts, 129 | pipeline_layout, 130 | render_pass, 131 | frame_images, 132 | _format: format, 133 | swapchain, 134 | _command_queues: command_queues, 135 | device, 136 | _surface: surface, 137 | _adapter: adapter, 138 | _instance: instance, 139 | } 140 | } 141 | 142 | fn create_instance() -> back::Instance { 143 | back::Instance::create(WINDOW_NAME, 1) 144 | } 145 | 146 | fn find_queue_families(adapter: &Adapter) -> QueueFamilyIds { 147 | let mut queue_family_ids = QueueFamilyIds::default(); 148 | 149 | for queue_family in &adapter.queue_families { 150 | if queue_family.max_queues() > 0 && queue_family.supports_graphics() { 151 | queue_family_ids.graphics_family = Some(queue_family.id()); 152 | } 153 | 154 | if queue_family_ids.is_complete() { 155 | break; 156 | } 157 | } 158 | 159 | queue_family_ids 160 | } 161 | 162 | fn is_adapter_suitable(adapter: &Adapter) -> bool { 163 | HelloTriangleApplication::find_queue_families(adapter).is_complete() 164 | } 165 | 166 | fn pick_adapter(instance: &back::Instance) -> Adapter { 167 | let adapters = instance.enumerate_adapters(); 168 | for adapter in adapters { 169 | if HelloTriangleApplication::is_adapter_suitable(&adapter) { 170 | return adapter; 171 | } 172 | } 173 | panic!("No suitable adapter"); 174 | } 175 | 176 | fn create_surface( 177 | instance: &back::Instance, 178 | window: &Window, 179 | ) -> ::Surface { 180 | instance.create_surface(window) 181 | } 182 | 183 | fn create_device_with_graphics_queues( 184 | adapter: &mut Adapter, 185 | surface: &::Surface, 186 | ) -> ( 187 | ::Device, 188 | Vec>, 189 | ) { 190 | let family = adapter 191 | .queue_families 192 | .iter() 193 | .find(|family| { 194 | Graphics::supported_by(family.queue_type()) 195 | && family.max_queues() > 0 196 | && surface.supports_queue_family(family) 197 | }) 198 | .expect("Could not find a queue family supporting graphics."); 199 | 200 | let priorities = vec![1.0; 1]; 201 | let families = [(family, priorities.as_slice())]; 202 | 203 | let Gpu { device, mut queues } = unsafe { 204 | adapter 205 | .physical_device 206 | .open(&families, Features::empty()) 207 | .expect("Could not create device.") 208 | }; 209 | 210 | let mut queue_group = queues 211 | .take::(family.id()) 212 | .expect("Could not take ownership of relevant queue group."); 213 | 214 | let command_queues: Vec<_> = queue_group.queues.drain(..1).collect(); 215 | 216 | (device, command_queues) 217 | } 218 | 219 | fn create_swap_chain( 220 | adapter: &Adapter, 221 | device: &::Device, 222 | surface: &mut ::Surface, 223 | previous_swapchain: Option<::Swapchain>, 224 | ) -> ( 225 | ::Swapchain, 226 | window::Extent2D, 227 | Backbuffer, 228 | format::Format, 229 | ) { 230 | let (caps, formats, _present_modes, _composite_alphas) = 231 | surface.compatibility(&adapter.physical_device); 232 | 233 | let format = formats.map_or(format::Format::Rgba8Srgb, |formats| { 234 | formats 235 | .iter() 236 | .find(|format| format.base_format().1 == format::ChannelType::Srgb) 237 | .map(|format| *format) 238 | .unwrap_or(formats[0]) 239 | }); 240 | 241 | let swap_config = SwapchainConfig::from_caps(&caps, format, caps.extents.end); 242 | let extent = swap_config.extent; 243 | let (swapchain, backbuffer) = unsafe { 244 | device 245 | .create_swapchain(surface, swap_config, previous_swapchain) 246 | .unwrap() 247 | }; 248 | 249 | (swapchain, extent, backbuffer, format) 250 | } 251 | 252 | unsafe fn create_image_views( 253 | backbuffer: Backbuffer, 254 | format: format::Format, 255 | device: &::Device, 256 | ) -> Vec<( 257 | ::Image, 258 | ::ImageView, 259 | )> { 260 | match backbuffer { 261 | window::Backbuffer::Images(images) => images 262 | .into_iter() 263 | .map(|image| { 264 | let image_view = match device.create_image_view( 265 | &image, 266 | image::ViewKind::D2, 267 | format, 268 | format::Swizzle::NO, 269 | image::SubresourceRange { 270 | aspects: format::Aspects::COLOR, 271 | levels: 0..1, 272 | layers: 0..1, 273 | }, 274 | ) { 275 | Ok(image_view) => image_view, 276 | Err(_) => panic!("Error creating image view for an image!"), 277 | }; 278 | 279 | (image, image_view) 280 | }) 281 | .collect(), 282 | _ => unimplemented!(), 283 | } 284 | } 285 | 286 | fn create_render_pass( 287 | device: &::Device, 288 | format: Option, 289 | ) -> ::RenderPass { 290 | let samples: u8 = 1; 291 | 292 | let ops = pass::AttachmentOps { 293 | load: pass::AttachmentLoadOp::Clear, 294 | store: pass::AttachmentStoreOp::Store, 295 | }; 296 | 297 | let stencil_ops = pass::AttachmentOps::DONT_CARE; 298 | 299 | let layouts = image::Layout::Undefined..image::Layout::Present; 300 | 301 | let color_attachment = pass::Attachment { 302 | format, 303 | samples, 304 | ops, 305 | stencil_ops, 306 | layouts, 307 | }; 308 | 309 | let color_attachment_ref: pass::AttachmentRef = (0, image::Layout::ColorAttachmentOptimal); 310 | 311 | // hal assumes pipeline bind point is GRAPHICS 312 | let subpass = pass::SubpassDesc { 313 | colors: &[color_attachment_ref], 314 | depth_stencil: None, 315 | inputs: &[], 316 | resolves: &[], 317 | preserves: &[], 318 | }; 319 | 320 | unsafe { 321 | device 322 | .create_render_pass(&[color_attachment], &[subpass], &[]) 323 | .unwrap() 324 | } 325 | } 326 | 327 | unsafe fn create_graphics_pipeline( 328 | device: &::Device, 329 | extent: window::Extent2D, 330 | ) -> ( 331 | Vec<::DescriptorSetLayout>, 332 | ::PipelineLayout, 333 | ) { 334 | let vert_shader_code = glsl_to_spirv::compile( 335 | include_str!("09_shader_base.vert"), 336 | glsl_to_spirv::ShaderType::Vertex, 337 | ) 338 | .expect("Error compiling vertex shader code.") 339 | .bytes() 340 | .map(|b| b.unwrap()) 341 | .collect::>(); 342 | 343 | let frag_shader_code = glsl_to_spirv::compile( 344 | include_str!("09_shader_base.frag"), 345 | glsl_to_spirv::ShaderType::Fragment, 346 | ) 347 | .expect("Error compiling fragment shader code.") 348 | .bytes() 349 | .map(|b| b.unwrap()) 350 | .collect::>(); 351 | 352 | let vert_shader_module = device 353 | .create_shader_module(&vert_shader_code) 354 | .expect("Error creating shader module."); 355 | let frag_shader_module = device 356 | .create_shader_module(&frag_shader_code) 357 | .expect("Error creating fragment module."); 358 | 359 | let (ds_layouts, pipeline_layout) = { 360 | let (vs_entry, fs_entry) = ( 361 | pso::EntryPoint:: { 362 | entry: "main", 363 | module: &vert_shader_module, 364 | specialization: hal::pso::Specialization { 365 | constants: &[], 366 | data: &[], 367 | }, 368 | }, 369 | pso::EntryPoint:: { 370 | entry: "main", 371 | module: &frag_shader_module, 372 | specialization: hal::pso::Specialization { 373 | constants: &[], 374 | data: &[], 375 | }, 376 | }, 377 | ); 378 | 379 | let _shaders = pso::GraphicsShaderSet { 380 | vertex: vs_entry, 381 | hull: None, 382 | domain: None, 383 | geometry: None, 384 | fragment: Some(fs_entry), 385 | }; 386 | 387 | let _rasterizer = pso::Rasterizer { 388 | depth_clamping: false, 389 | polygon_mode: pso::PolygonMode::Fill, 390 | cull_face: ::BACK, 391 | front_face: pso::FrontFace::Clockwise, 392 | depth_bias: None, 393 | conservative: false, 394 | }; 395 | 396 | let _vertex_buffers: Vec = Vec::new(); 397 | let _attributes: Vec = Vec::new(); 398 | 399 | let _input_assembler = pso::InputAssemblerDesc::new(Primitive::TriangleList); 400 | 401 | let _blender = { 402 | let blend_state = pso::BlendState::On { 403 | color: pso::BlendOp::Add { 404 | src: pso::Factor::One, 405 | dst: pso::Factor::Zero, 406 | }, 407 | alpha: pso::BlendOp::Add { 408 | src: pso::Factor::One, 409 | dst: pso::Factor::Zero, 410 | }, 411 | }; 412 | 413 | pso::BlendDesc { 414 | logic_op: Some(pso::LogicOp::Copy), 415 | targets: vec![pso::ColorBlendDesc(pso::ColorMask::ALL, blend_state)], 416 | } 417 | }; 418 | 419 | let _depth_stencil = pso::DepthStencilDesc { 420 | depth: pso::DepthTest::Off, 421 | depth_bounds: false, 422 | stencil: pso::StencilTest::Off, 423 | }; 424 | 425 | let _multisampling: Option = None; 426 | 427 | let _baked_states = pso::BakedStates { 428 | viewport: Some(pso::Viewport { 429 | rect: pso::Rect { 430 | x: 0, 431 | y: 0, 432 | w: extent.width as i16, 433 | h: extent.height as i16, 434 | }, 435 | depth: (0.0..1.0), 436 | }), 437 | scissor: Some(pso::Rect { 438 | x: 0, 439 | y: 0, 440 | w: extent.width as i16, 441 | h: extent.height as i16, 442 | }), 443 | blend_color: None, 444 | depth_bounds: None, 445 | }; 446 | 447 | let bindings = Vec::::new(); 448 | let immutable_samplers = Vec::<::Sampler>::new(); 449 | let ds_layouts: Vec<::DescriptorSetLayout> = vec![device 450 | .create_descriptor_set_layout(bindings, immutable_samplers) 451 | .unwrap()]; 452 | let push_constants = Vec::<(pso::ShaderStageFlags, std::ops::Range)>::new(); 453 | let layout = device 454 | .create_pipeline_layout(&ds_layouts, push_constants) 455 | .unwrap(); 456 | 457 | // our goal is to fill out this entire struct 458 | // let desc = pso::GraphicsPipelineDesc { 459 | // shaders, 460 | // rasterizer, 461 | // vertex_buffers, 462 | // attributes, 463 | // input_assembler, 464 | // blender, 465 | // depth_stencil, 466 | // multisampling, 467 | // baked_states, 468 | // layout, 469 | // subpass, 470 | // flags, 471 | // parent, 472 | // }; 473 | 474 | (ds_layouts, layout) 475 | }; 476 | 477 | device.destroy_shader_module(vert_shader_module); 478 | device.destroy_shader_module(frag_shader_module); 479 | 480 | (ds_layouts, pipeline_layout) 481 | } 482 | 483 | fn main_loop(&mut self) { 484 | self.window_state 485 | .events_loop 486 | .run_forever(|event| match event { 487 | Event::WindowEvent { 488 | event: WindowEvent::CloseRequested, 489 | .. 490 | } => ControlFlow::Break, 491 | _ => ControlFlow::Continue, 492 | }); 493 | } 494 | 495 | fn run(&mut self) { 496 | self.main_loop(); 497 | } 498 | 499 | unsafe fn clean_up(self) { 500 | self.hal_state.clean_up(); 501 | } 502 | } 503 | -------------------------------------------------------------------------------- /src/12_graphics_pipeline_complete.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate glsl_to_spirv; 10 | extern crate winit; 11 | 12 | use hal::{ 13 | format, image, pass, pso, queue, window, Adapter, Backbuffer, Backend, Capability, Device, 14 | Features, Gpu, Graphics, Instance, PhysicalDevice, Primitive, QueueFamily, Surface, 15 | SwapchainConfig, 16 | }; 17 | use std::io::Read; 18 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 19 | 20 | static WINDOW_NAME: &str = "12_graphics_pipeline_complete"; 21 | 22 | fn main() { 23 | env_logger::init(); 24 | let mut application = HelloTriangleApplication::init(); 25 | application.run(); 26 | unsafe { 27 | application.clean_up(); 28 | } 29 | } 30 | 31 | struct WindowState { 32 | events_loop: EventsLoop, 33 | window: Window, 34 | } 35 | 36 | struct HalState { 37 | gfx_pipeline: ::GraphicsPipeline, 38 | descriptor_set_layouts: Vec<::DescriptorSetLayout>, 39 | pipeline_layout: ::PipelineLayout, 40 | render_pass: ::RenderPass, 41 | frame_images: Vec<( 42 | ::Image, 43 | ::ImageView, 44 | )>, 45 | _format: format::Format, 46 | swapchain: ::Swapchain, 47 | _command_queues: Vec>, 48 | device: ::Device, 49 | _surface: ::Surface, 50 | _adapter: Adapter, 51 | _instance: back::Instance, 52 | } 53 | 54 | impl HalState { 55 | unsafe fn clean_up(self) { 56 | let device = &self.device; 57 | 58 | device.destroy_graphics_pipeline(self.gfx_pipeline); 59 | 60 | for descriptor_set_layout in self.descriptor_set_layouts { 61 | device.destroy_descriptor_set_layout(descriptor_set_layout); 62 | } 63 | 64 | device.destroy_pipeline_layout(self.pipeline_layout); 65 | 66 | device.destroy_render_pass(self.render_pass); 67 | 68 | for (_, image_view) in self.frame_images.into_iter() { 69 | device.destroy_image_view(image_view); 70 | } 71 | 72 | device.destroy_swapchain(self.swapchain); 73 | } 74 | } 75 | 76 | struct HelloTriangleApplication { 77 | hal_state: HalState, 78 | window_state: WindowState, 79 | } 80 | 81 | #[derive(Default)] 82 | struct QueueFamilyIds { 83 | graphics_family: Option, 84 | } 85 | 86 | impl QueueFamilyIds { 87 | fn is_complete(&self) -> bool { 88 | self.graphics_family.is_some() 89 | } 90 | } 91 | 92 | impl HelloTriangleApplication { 93 | pub fn init() -> HelloTriangleApplication { 94 | let window_state = HelloTriangleApplication::init_window(); 95 | let hal_state = unsafe { HelloTriangleApplication::init_hal(&window_state.window) }; 96 | 97 | HelloTriangleApplication { 98 | hal_state, 99 | window_state, 100 | } 101 | } 102 | 103 | fn init_window() -> WindowState { 104 | let events_loop = EventsLoop::new(); 105 | let window_builder = WindowBuilder::new() 106 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 107 | .with_title(WINDOW_NAME.to_string()); 108 | let window = window_builder.build(&events_loop).unwrap(); 109 | 110 | WindowState { 111 | events_loop: events_loop, 112 | window, 113 | } 114 | } 115 | 116 | unsafe fn init_hal(window: &Window) -> HalState { 117 | let instance = HelloTriangleApplication::create_instance(); 118 | let mut adapter = HelloTriangleApplication::pick_adapter(&instance); 119 | let mut surface = HelloTriangleApplication::create_surface(&instance, window); 120 | let (device, command_queues) = 121 | HelloTriangleApplication::create_device_with_graphics_queues(&mut adapter, &surface); 122 | let (swapchain, extent, backbuffer, format) = 123 | HelloTriangleApplication::create_swap_chain(&adapter, &device, &mut surface, None); 124 | let frame_images = 125 | HelloTriangleApplication::create_image_views(backbuffer, format, &device); 126 | let render_pass = HelloTriangleApplication::create_render_pass(&device, Some(format)); 127 | let (descriptor_set_layouts, pipeline_layout, gfx_pipeline) = 128 | HelloTriangleApplication::create_graphics_pipeline(&device, extent, &render_pass); 129 | 130 | HalState { 131 | gfx_pipeline, 132 | descriptor_set_layouts, 133 | pipeline_layout, 134 | render_pass, 135 | frame_images, 136 | _format: format, 137 | swapchain, 138 | _command_queues: command_queues, 139 | device, 140 | _surface: surface, 141 | _adapter: adapter, 142 | _instance: instance, 143 | } 144 | } 145 | 146 | fn create_instance() -> back::Instance { 147 | back::Instance::create(WINDOW_NAME, 1) 148 | } 149 | 150 | fn find_queue_families(adapter: &Adapter) -> QueueFamilyIds { 151 | let mut queue_family_ids = QueueFamilyIds::default(); 152 | 153 | for queue_family in &adapter.queue_families { 154 | if queue_family.max_queues() > 0 && queue_family.supports_graphics() { 155 | queue_family_ids.graphics_family = Some(queue_family.id()); 156 | } 157 | 158 | if queue_family_ids.is_complete() { 159 | break; 160 | } 161 | } 162 | 163 | queue_family_ids 164 | } 165 | 166 | fn is_adapter_suitable(adapter: &Adapter) -> bool { 167 | HelloTriangleApplication::find_queue_families(adapter).is_complete() 168 | } 169 | 170 | fn pick_adapter(instance: &back::Instance) -> Adapter { 171 | let adapters = instance.enumerate_adapters(); 172 | for adapter in adapters { 173 | if HelloTriangleApplication::is_adapter_suitable(&adapter) { 174 | return adapter; 175 | } 176 | } 177 | panic!("No suitable adapter"); 178 | } 179 | 180 | fn create_surface( 181 | instance: &back::Instance, 182 | window: &Window, 183 | ) -> ::Surface { 184 | instance.create_surface(window) 185 | } 186 | 187 | fn create_device_with_graphics_queues( 188 | adapter: &mut Adapter, 189 | surface: &::Surface, 190 | ) -> ( 191 | ::Device, 192 | Vec>, 193 | ) { 194 | let family = adapter 195 | .queue_families 196 | .iter() 197 | .find(|family| { 198 | Graphics::supported_by(family.queue_type()) 199 | && family.max_queues() > 0 200 | && surface.supports_queue_family(family) 201 | }) 202 | .expect("Could not find a queue family supporting graphics."); 203 | 204 | let priorities = vec![1.0; 1]; 205 | let families = [(family, priorities.as_slice())]; 206 | 207 | let Gpu { device, mut queues } = unsafe { 208 | adapter 209 | .physical_device 210 | .open(&families, Features::empty()) 211 | .expect("Could not create device.") 212 | }; 213 | 214 | let mut queue_group = queues 215 | .take::(family.id()) 216 | .expect("Could not take ownership of relevant queue group."); 217 | 218 | let command_queues: Vec<_> = queue_group.queues.drain(..1).collect(); 219 | 220 | (device, command_queues) 221 | } 222 | 223 | fn create_swap_chain( 224 | adapter: &Adapter, 225 | device: &::Device, 226 | surface: &mut ::Surface, 227 | previous_swapchain: Option<::Swapchain>, 228 | ) -> ( 229 | ::Swapchain, 230 | window::Extent2D, 231 | Backbuffer, 232 | format::Format, 233 | ) { 234 | let (caps, formats, _present_modes, _composite_alphas) = 235 | surface.compatibility(&adapter.physical_device); 236 | 237 | let format = formats.map_or(format::Format::Rgba8Srgb, |formats| { 238 | formats 239 | .iter() 240 | .find(|format| format.base_format().1 == format::ChannelType::Srgb) 241 | .map(|format| *format) 242 | .unwrap_or(formats[0]) 243 | }); 244 | 245 | let swap_config = SwapchainConfig::from_caps(&caps, format, caps.extents.end); 246 | let extent = swap_config.extent; 247 | let (swapchain, backbuffer) = unsafe { 248 | device 249 | .create_swapchain(surface, swap_config, previous_swapchain) 250 | .unwrap() 251 | }; 252 | 253 | (swapchain, extent, backbuffer, format) 254 | } 255 | 256 | unsafe fn create_image_views( 257 | backbuffer: Backbuffer, 258 | format: format::Format, 259 | device: &::Device, 260 | ) -> Vec<( 261 | ::Image, 262 | ::ImageView, 263 | )> { 264 | match backbuffer { 265 | window::Backbuffer::Images(images) => images 266 | .into_iter() 267 | .map(|image| { 268 | let image_view = match device.create_image_view( 269 | &image, 270 | image::ViewKind::D2, 271 | format, 272 | format::Swizzle::NO, 273 | image::SubresourceRange { 274 | aspects: format::Aspects::COLOR, 275 | levels: 0..1, 276 | layers: 0..1, 277 | }, 278 | ) { 279 | Ok(image_view) => image_view, 280 | Err(_) => panic!("Error creating image view for an image!"), 281 | }; 282 | 283 | (image, image_view) 284 | }) 285 | .collect(), 286 | _ => unimplemented!(), 287 | } 288 | } 289 | 290 | fn create_render_pass( 291 | device: &::Device, 292 | format: Option, 293 | ) -> ::RenderPass { 294 | let samples: u8 = 1; 295 | 296 | let ops = pass::AttachmentOps { 297 | load: pass::AttachmentLoadOp::Clear, 298 | store: pass::AttachmentStoreOp::Store, 299 | }; 300 | 301 | let stencil_ops = pass::AttachmentOps::DONT_CARE; 302 | 303 | let layouts = image::Layout::Undefined..image::Layout::Present; 304 | 305 | let color_attachment = pass::Attachment { 306 | format, 307 | samples, 308 | ops, 309 | stencil_ops, 310 | layouts, 311 | }; 312 | 313 | let color_attachment_ref: pass::AttachmentRef = (0, image::Layout::ColorAttachmentOptimal); 314 | 315 | let subpass = pass::SubpassDesc { 316 | colors: &[color_attachment_ref], 317 | depth_stencil: None, 318 | inputs: &[], 319 | resolves: &[], 320 | preserves: &[], 321 | }; 322 | 323 | unsafe { 324 | device 325 | .create_render_pass(&[color_attachment], &[subpass], &[]) 326 | .unwrap() 327 | } 328 | } 329 | 330 | unsafe fn create_graphics_pipeline( 331 | device: &::Device, 332 | extent: window::Extent2D, 333 | render_pass: &::RenderPass, 334 | ) -> ( 335 | Vec<::DescriptorSetLayout>, 336 | ::PipelineLayout, 337 | ::GraphicsPipeline, 338 | ) { 339 | let vert_shader_code = glsl_to_spirv::compile( 340 | include_str!("09_shader_base.vert"), 341 | glsl_to_spirv::ShaderType::Vertex, 342 | ) 343 | .expect("Error compiling vertex shader code.") 344 | .bytes() 345 | .map(|b| b.unwrap()) 346 | .collect::>(); 347 | 348 | let frag_shader_code = glsl_to_spirv::compile( 349 | include_str!("09_shader_base.frag"), 350 | glsl_to_spirv::ShaderType::Fragment, 351 | ) 352 | .expect("Error compiling fragment shader code.") 353 | .bytes() 354 | .map(|b| b.unwrap()) 355 | .collect::>(); 356 | 357 | let vert_shader_module = device 358 | .create_shader_module(&vert_shader_code) 359 | .expect("Error creating shader module."); 360 | let frag_shader_module = device 361 | .create_shader_module(&frag_shader_code) 362 | .expect("Error creating fragment module."); 363 | 364 | let (ds_layouts, pipeline_layout, gfx_pipeline) = { 365 | let (vs_entry, fs_entry) = ( 366 | pso::EntryPoint:: { 367 | entry: "main", 368 | module: &vert_shader_module, 369 | specialization: hal::pso::Specialization { 370 | constants: &[], 371 | data: &[], 372 | }, 373 | }, 374 | pso::EntryPoint:: { 375 | entry: "main", 376 | module: &frag_shader_module, 377 | specialization: hal::pso::Specialization { 378 | constants: &[], 379 | data: &[], 380 | }, 381 | }, 382 | ); 383 | 384 | let shaders = pso::GraphicsShaderSet { 385 | vertex: vs_entry, 386 | hull: None, 387 | domain: None, 388 | geometry: None, 389 | fragment: Some(fs_entry), 390 | }; 391 | 392 | let rasterizer = pso::Rasterizer { 393 | depth_clamping: false, 394 | polygon_mode: pso::PolygonMode::Fill, 395 | cull_face: ::BACK, 396 | front_face: pso::FrontFace::Clockwise, 397 | depth_bias: None, 398 | conservative: false, 399 | }; 400 | 401 | let vertex_buffers: Vec = Vec::new(); 402 | let attributes: Vec = Vec::new(); 403 | 404 | let input_assembler = pso::InputAssemblerDesc::new(Primitive::TriangleList); 405 | 406 | let blender = { 407 | let blend_state = pso::BlendState::On { 408 | color: pso::BlendOp::Add { 409 | src: pso::Factor::One, 410 | dst: pso::Factor::Zero, 411 | }, 412 | alpha: pso::BlendOp::Add { 413 | src: pso::Factor::One, 414 | dst: pso::Factor::Zero, 415 | }, 416 | }; 417 | 418 | pso::BlendDesc { 419 | logic_op: Some(pso::LogicOp::Copy), 420 | targets: vec![pso::ColorBlendDesc(pso::ColorMask::ALL, blend_state)], 421 | } 422 | }; 423 | 424 | let depth_stencil = pso::DepthStencilDesc { 425 | depth: pso::DepthTest::Off, 426 | depth_bounds: false, 427 | stencil: pso::StencilTest::Off, 428 | }; 429 | 430 | let multisampling: Option = None; 431 | 432 | let baked_states = pso::BakedStates { 433 | viewport: Some(pso::Viewport { 434 | rect: pso::Rect { 435 | x: 0, 436 | y: 0, 437 | w: extent.width as i16, 438 | h: extent.height as i16, 439 | }, 440 | depth: (0.0..1.0), 441 | }), 442 | scissor: Some(pso::Rect { 443 | x: 0, 444 | y: 0, 445 | w: extent.width as i16, 446 | h: extent.height as i16, 447 | }), 448 | blend_color: None, 449 | depth_bounds: None, 450 | }; 451 | 452 | let bindings = Vec::::new(); 453 | let immutable_samplers = Vec::<::Sampler>::new(); 454 | let ds_layouts: Vec<::DescriptorSetLayout> = vec![device 455 | .create_descriptor_set_layout(bindings, immutable_samplers) 456 | .unwrap()]; 457 | let push_constants = Vec::<(pso::ShaderStageFlags, std::ops::Range)>::new(); 458 | let layout = device 459 | .create_pipeline_layout(&ds_layouts, push_constants) 460 | .unwrap(); 461 | 462 | let subpass = pass::Subpass { 463 | index: 0, 464 | main_pass: render_pass, 465 | }; 466 | 467 | let flags = pso::PipelineCreationFlags::empty(); 468 | 469 | let parent = pso::BasePipeline::None; 470 | 471 | let gfx_pipeline = { 472 | let desc = pso::GraphicsPipelineDesc { 473 | shaders, 474 | rasterizer, 475 | vertex_buffers, 476 | attributes, 477 | input_assembler, 478 | blender, 479 | depth_stencil, 480 | multisampling, 481 | baked_states, 482 | layout: &layout, 483 | subpass, 484 | flags, 485 | parent, 486 | }; 487 | 488 | device 489 | .create_graphics_pipeline(&desc, None) 490 | .expect("failed to create graphics pipeline!") 491 | }; 492 | 493 | (ds_layouts, layout, gfx_pipeline) 494 | }; 495 | 496 | device.destroy_shader_module(vert_shader_module); 497 | device.destroy_shader_module(frag_shader_module); 498 | 499 | (ds_layouts, pipeline_layout, gfx_pipeline) 500 | } 501 | 502 | fn main_loop(&mut self) { 503 | self.window_state 504 | .events_loop 505 | .run_forever(|event| match event { 506 | Event::WindowEvent { 507 | event: WindowEvent::CloseRequested, 508 | .. 509 | } => ControlFlow::Break, 510 | _ => ControlFlow::Continue, 511 | }); 512 | } 513 | 514 | fn run(&mut self) { 515 | self.main_loop(); 516 | } 517 | 518 | unsafe fn clean_up(self) { 519 | self.hal_state.clean_up(); 520 | } 521 | } 522 | -------------------------------------------------------------------------------- /src/13_framebuffers.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate glsl_to_spirv; 10 | extern crate winit; 11 | 12 | use hal::{ 13 | format, image, pass, pso, queue, window, Adapter, Backbuffer, Backend, Capability, Device, 14 | Features, Gpu, Graphics, Instance, PhysicalDevice, Primitive, QueueFamily, Surface, 15 | SwapchainConfig, 16 | }; 17 | use std::io::Read; 18 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 19 | 20 | static WINDOW_NAME: &str = "13_framebuffers"; 21 | 22 | fn main() { 23 | env_logger::init(); 24 | let mut application = HelloTriangleApplication::init(); 25 | application.run(); 26 | unsafe { 27 | application.clean_up(); 28 | } 29 | } 30 | 31 | struct WindowState { 32 | events_loop: EventsLoop, 33 | window: Window, 34 | } 35 | 36 | struct HalState { 37 | swapchain_framebuffers: Vec<::Framebuffer>, 38 | gfx_pipeline: ::GraphicsPipeline, 39 | descriptor_set_layouts: Vec<::DescriptorSetLayout>, 40 | pipeline_layout: ::PipelineLayout, 41 | render_pass: ::RenderPass, 42 | frame_images: Vec<( 43 | ::Image, 44 | ::ImageView, 45 | )>, 46 | _format: format::Format, 47 | swapchain: ::Swapchain, 48 | _command_queues: Vec>, 49 | device: ::Device, 50 | _surface: ::Surface, 51 | _adapter: Adapter, 52 | _instance: back::Instance, 53 | } 54 | 55 | impl HalState { 56 | unsafe fn clean_up(self) { 57 | let device = &self.device; 58 | 59 | for framebuffer in self.swapchain_framebuffers { 60 | device.destroy_framebuffer(framebuffer); 61 | } 62 | 63 | device.destroy_graphics_pipeline(self.gfx_pipeline); 64 | 65 | for descriptor_set_layout in self.descriptor_set_layouts { 66 | device.destroy_descriptor_set_layout(descriptor_set_layout); 67 | } 68 | 69 | device.destroy_pipeline_layout(self.pipeline_layout); 70 | 71 | device.destroy_render_pass(self.render_pass); 72 | 73 | for (_, image_view) in self.frame_images.into_iter() { 74 | device.destroy_image_view(image_view); 75 | } 76 | 77 | device.destroy_swapchain(self.swapchain); 78 | } 79 | } 80 | 81 | struct HelloTriangleApplication { 82 | hal_state: HalState, 83 | window_state: WindowState, 84 | } 85 | 86 | #[derive(Default)] 87 | struct QueueFamilyIds { 88 | graphics_family: Option, 89 | } 90 | 91 | impl QueueFamilyIds { 92 | fn is_complete(&self) -> bool { 93 | self.graphics_family.is_some() 94 | } 95 | } 96 | 97 | impl HelloTriangleApplication { 98 | pub fn init() -> HelloTriangleApplication { 99 | let window_state = HelloTriangleApplication::init_window(); 100 | let hal_state = unsafe { HelloTriangleApplication::init_hal(&window_state.window) }; 101 | 102 | HelloTriangleApplication { 103 | hal_state, 104 | window_state, 105 | } 106 | } 107 | 108 | fn init_window() -> WindowState { 109 | let events_loop = EventsLoop::new(); 110 | let window_builder = WindowBuilder::new() 111 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 112 | .with_title(WINDOW_NAME.to_string()); 113 | let window = window_builder.build(&events_loop).unwrap(); 114 | 115 | WindowState { 116 | events_loop: events_loop, 117 | window, 118 | } 119 | } 120 | 121 | unsafe fn init_hal(window: &Window) -> HalState { 122 | let instance = HelloTriangleApplication::create_instance(); 123 | let mut adapter = HelloTriangleApplication::pick_adapter(&instance); 124 | let mut surface = HelloTriangleApplication::create_surface(&instance, window); 125 | let (device, command_queues) = 126 | HelloTriangleApplication::create_device_with_graphics_queues(&mut adapter, &surface); 127 | let (swapchain, extent, backbuffer, format) = 128 | HelloTriangleApplication::create_swap_chain(&adapter, &device, &mut surface, None); 129 | let frame_images = 130 | HelloTriangleApplication::create_image_views(backbuffer, format, &device); 131 | let render_pass = HelloTriangleApplication::create_render_pass(&device, Some(format)); 132 | let (descriptor_set_layouts, pipeline_layout, gfx_pipeline) = 133 | HelloTriangleApplication::create_graphics_pipeline(&device, extent, &render_pass); 134 | let swapchain_framebuffers = HelloTriangleApplication::create_framebuffers( 135 | &device, 136 | &render_pass, 137 | &frame_images, 138 | extent, 139 | ); 140 | 141 | HalState { 142 | swapchain_framebuffers, 143 | gfx_pipeline, 144 | descriptor_set_layouts, 145 | pipeline_layout, 146 | render_pass, 147 | frame_images, 148 | _format: format, 149 | swapchain, 150 | _command_queues: command_queues, 151 | device, 152 | _surface: surface, 153 | _adapter: adapter, 154 | _instance: instance, 155 | } 156 | } 157 | 158 | fn create_instance() -> back::Instance { 159 | back::Instance::create(WINDOW_NAME, 1) 160 | } 161 | 162 | fn find_queue_families(adapter: &Adapter) -> QueueFamilyIds { 163 | let mut queue_family_ids = QueueFamilyIds::default(); 164 | 165 | for queue_family in &adapter.queue_families { 166 | if queue_family.max_queues() > 0 && queue_family.supports_graphics() { 167 | queue_family_ids.graphics_family = Some(queue_family.id()); 168 | } 169 | 170 | if queue_family_ids.is_complete() { 171 | break; 172 | } 173 | } 174 | 175 | queue_family_ids 176 | } 177 | 178 | fn is_adapter_suitable(adapter: &Adapter) -> bool { 179 | HelloTriangleApplication::find_queue_families(adapter).is_complete() 180 | } 181 | 182 | fn pick_adapter(instance: &back::Instance) -> Adapter { 183 | let adapters = instance.enumerate_adapters(); 184 | for adapter in adapters { 185 | if HelloTriangleApplication::is_adapter_suitable(&adapter) { 186 | return adapter; 187 | } 188 | } 189 | panic!("No suitable adapter"); 190 | } 191 | 192 | fn create_surface( 193 | instance: &back::Instance, 194 | window: &Window, 195 | ) -> ::Surface { 196 | instance.create_surface(window) 197 | } 198 | 199 | fn create_device_with_graphics_queues( 200 | adapter: &mut Adapter, 201 | surface: &::Surface, 202 | ) -> ( 203 | ::Device, 204 | Vec>, 205 | ) { 206 | let family = adapter 207 | .queue_families 208 | .iter() 209 | .find(|family| { 210 | Graphics::supported_by(family.queue_type()) 211 | && family.max_queues() > 0 212 | && surface.supports_queue_family(family) 213 | }) 214 | .expect("Could not find a queue family supporting graphics."); 215 | 216 | let priorities = vec![1.0; 1]; 217 | let families = [(family, priorities.as_slice())]; 218 | 219 | let Gpu { device, mut queues } = unsafe { 220 | adapter 221 | .physical_device 222 | .open(&families, Features::empty()) 223 | .expect("Could not create device.") 224 | }; 225 | 226 | let mut queue_group = queues 227 | .take::(family.id()) 228 | .expect("Could not take ownership of relevant queue group."); 229 | 230 | let command_queues: Vec<_> = queue_group.queues.drain(..1).collect(); 231 | 232 | (device, command_queues) 233 | } 234 | 235 | fn create_swap_chain( 236 | adapter: &Adapter, 237 | device: &::Device, 238 | surface: &mut ::Surface, 239 | previous_swapchain: Option<::Swapchain>, 240 | ) -> ( 241 | ::Swapchain, 242 | window::Extent2D, 243 | Backbuffer, 244 | format::Format, 245 | ) { 246 | let (caps, formats, _present_modes, _composite_alphas) = 247 | surface.compatibility(&adapter.physical_device); 248 | 249 | let format = formats.map_or(format::Format::Rgba8Srgb, |formats| { 250 | formats 251 | .iter() 252 | .find(|format| format.base_format().1 == format::ChannelType::Srgb) 253 | .map(|format| *format) 254 | .unwrap_or(formats[0]) 255 | }); 256 | 257 | let swap_config = SwapchainConfig::from_caps(&caps, format, caps.extents.end); 258 | let extent = swap_config.extent; 259 | let (swapchain, backbuffer) = unsafe { 260 | device 261 | .create_swapchain(surface, swap_config, previous_swapchain) 262 | .unwrap() 263 | }; 264 | 265 | (swapchain, extent, backbuffer, format) 266 | } 267 | 268 | unsafe fn create_image_views( 269 | backbuffer: Backbuffer, 270 | format: format::Format, 271 | device: &::Device, 272 | ) -> Vec<( 273 | ::Image, 274 | ::ImageView, 275 | )> { 276 | match backbuffer { 277 | window::Backbuffer::Images(images) => images 278 | .into_iter() 279 | .map(|image| { 280 | let image_view = match device.create_image_view( 281 | &image, 282 | image::ViewKind::D2, 283 | format, 284 | format::Swizzle::NO, 285 | image::SubresourceRange { 286 | aspects: format::Aspects::COLOR, 287 | levels: 0..1, 288 | layers: 0..1, 289 | }, 290 | ) { 291 | Ok(image_view) => image_view, 292 | Err(_) => panic!("Error creating image view for an image!"), 293 | }; 294 | 295 | (image, image_view) 296 | }) 297 | .collect(), 298 | _ => unimplemented!(), 299 | } 300 | } 301 | 302 | fn create_render_pass( 303 | device: &::Device, 304 | format: Option, 305 | ) -> ::RenderPass { 306 | let samples: u8 = 1; 307 | 308 | let ops = pass::AttachmentOps { 309 | load: pass::AttachmentLoadOp::Clear, 310 | store: pass::AttachmentStoreOp::Store, 311 | }; 312 | 313 | let stencil_ops = pass::AttachmentOps::DONT_CARE; 314 | 315 | let layouts = image::Layout::Undefined..image::Layout::Present; 316 | 317 | let color_attachment = pass::Attachment { 318 | format, 319 | samples, 320 | ops, 321 | stencil_ops, 322 | layouts, 323 | }; 324 | 325 | let color_attachment_ref: pass::AttachmentRef = (0, image::Layout::ColorAttachmentOptimal); 326 | 327 | // hal assumes pipeline bind point is GRAPHICS 328 | let subpass = pass::SubpassDesc { 329 | colors: &[color_attachment_ref], 330 | depth_stencil: None, 331 | inputs: &[], 332 | resolves: &[], 333 | preserves: &[], 334 | }; 335 | 336 | unsafe { 337 | device 338 | .create_render_pass(&[color_attachment], &[subpass], &[]) 339 | .unwrap() 340 | } 341 | } 342 | 343 | unsafe fn create_graphics_pipeline( 344 | device: &::Device, 345 | extent: window::Extent2D, 346 | render_pass: &::RenderPass, 347 | ) -> ( 348 | Vec<::DescriptorSetLayout>, 349 | ::PipelineLayout, 350 | ::GraphicsPipeline, 351 | ) { 352 | let vert_shader_code = glsl_to_spirv::compile( 353 | include_str!("09_shader_base.vert"), 354 | glsl_to_spirv::ShaderType::Vertex, 355 | ) 356 | .expect("Error compiling vertex shader code.") 357 | .bytes() 358 | .map(|b| b.unwrap()) 359 | .collect::>(); 360 | 361 | let frag_shader_code = glsl_to_spirv::compile( 362 | include_str!("09_shader_base.frag"), 363 | glsl_to_spirv::ShaderType::Fragment, 364 | ) 365 | .expect("Error compiling fragment shader code.") 366 | .bytes() 367 | .map(|b| b.unwrap()) 368 | .collect::>(); 369 | 370 | let vert_shader_module = device 371 | .create_shader_module(&vert_shader_code) 372 | .expect("Error creating shader module."); 373 | let frag_shader_module = device 374 | .create_shader_module(&frag_shader_code) 375 | .expect("Error creating fragment module."); 376 | 377 | let (ds_layouts, pipeline_layout, gfx_pipeline) = { 378 | let (vs_entry, fs_entry) = ( 379 | pso::EntryPoint:: { 380 | entry: "main", 381 | module: &vert_shader_module, 382 | specialization: hal::pso::Specialization { 383 | constants: &[], 384 | data: &[], 385 | }, 386 | }, 387 | pso::EntryPoint:: { 388 | entry: "main", 389 | module: &frag_shader_module, 390 | specialization: hal::pso::Specialization { 391 | constants: &[], 392 | data: &[], 393 | }, 394 | }, 395 | ); 396 | 397 | let shaders = pso::GraphicsShaderSet { 398 | vertex: vs_entry, 399 | hull: None, 400 | domain: None, 401 | geometry: None, 402 | fragment: Some(fs_entry), 403 | }; 404 | 405 | let rasterizer = pso::Rasterizer { 406 | depth_clamping: false, 407 | polygon_mode: pso::PolygonMode::Fill, 408 | cull_face: ::BACK, 409 | front_face: pso::FrontFace::Clockwise, 410 | depth_bias: None, 411 | conservative: false, 412 | }; 413 | 414 | let vertex_buffers: Vec = Vec::new(); 415 | let attributes: Vec = Vec::new(); 416 | 417 | let input_assembler = pso::InputAssemblerDesc::new(Primitive::TriangleList); 418 | 419 | let blender = { 420 | let blend_state = pso::BlendState::On { 421 | color: pso::BlendOp::Add { 422 | src: pso::Factor::One, 423 | dst: pso::Factor::Zero, 424 | }, 425 | alpha: pso::BlendOp::Add { 426 | src: pso::Factor::One, 427 | dst: pso::Factor::Zero, 428 | }, 429 | }; 430 | 431 | pso::BlendDesc { 432 | logic_op: Some(pso::LogicOp::Copy), 433 | targets: vec![pso::ColorBlendDesc(pso::ColorMask::ALL, blend_state)], 434 | } 435 | }; 436 | 437 | let depth_stencil = pso::DepthStencilDesc { 438 | depth: pso::DepthTest::Off, 439 | depth_bounds: false, 440 | stencil: pso::StencilTest::Off, 441 | }; 442 | 443 | let multisampling: Option = None; 444 | 445 | let baked_states = pso::BakedStates { 446 | viewport: Some(pso::Viewport { 447 | rect: pso::Rect { 448 | x: 0, 449 | y: 0, 450 | w: extent.width as i16, 451 | h: extent.height as i16, 452 | }, 453 | depth: (0.0..1.0), 454 | }), 455 | scissor: Some(pso::Rect { 456 | x: 0, 457 | y: 0, 458 | w: extent.width as i16, 459 | h: extent.height as i16, 460 | }), 461 | blend_color: None, 462 | depth_bounds: None, 463 | }; 464 | 465 | let bindings = Vec::::new(); 466 | let immutable_samplers = Vec::<::Sampler>::new(); 467 | let ds_layouts: Vec<::DescriptorSetLayout> = vec![device 468 | .create_descriptor_set_layout(bindings, immutable_samplers) 469 | .unwrap()]; 470 | let push_constants = Vec::<(pso::ShaderStageFlags, std::ops::Range)>::new(); 471 | let layout = device 472 | .create_pipeline_layout(&ds_layouts, push_constants) 473 | .unwrap(); 474 | 475 | let subpass = pass::Subpass { 476 | index: 0, 477 | main_pass: render_pass, 478 | }; 479 | 480 | let flags = pso::PipelineCreationFlags::empty(); 481 | 482 | let parent = pso::BasePipeline::None; 483 | 484 | let gfx_pipeline = { 485 | let desc = pso::GraphicsPipelineDesc { 486 | shaders, 487 | rasterizer, 488 | vertex_buffers, 489 | attributes, 490 | input_assembler, 491 | blender, 492 | depth_stencil, 493 | multisampling, 494 | baked_states, 495 | layout: &layout, 496 | subpass, 497 | flags, 498 | parent, 499 | }; 500 | 501 | device 502 | .create_graphics_pipeline(&desc, None) 503 | .expect("failed to create graphics pipeline!") 504 | }; 505 | 506 | (ds_layouts, layout, gfx_pipeline) 507 | }; 508 | 509 | device.destroy_shader_module(vert_shader_module); 510 | device.destroy_shader_module(frag_shader_module); 511 | 512 | (ds_layouts, pipeline_layout, gfx_pipeline) 513 | } 514 | 515 | fn create_framebuffers( 516 | device: &::Device, 517 | render_pass: &::RenderPass, 518 | frame_images: &[( 519 | ::Image, 520 | ::ImageView, 521 | )], 522 | extent: window::Extent2D, 523 | ) -> Vec<::Framebuffer> { 524 | let mut swapchain_framebuffers: Vec<::Framebuffer> = Vec::new(); 525 | 526 | unsafe { 527 | for (_, image_view) in frame_images.iter() { 528 | swapchain_framebuffers.push( 529 | device 530 | .create_framebuffer( 531 | render_pass, 532 | vec![image_view], 533 | image::Extent { 534 | width: extent.width as _, 535 | height: extent.height as _, 536 | depth: 1, 537 | }, 538 | ) 539 | .expect("failed to create framebuffer!"), 540 | ); 541 | } 542 | } 543 | 544 | swapchain_framebuffers 545 | } 546 | 547 | fn main_loop(&mut self) { 548 | self.window_state 549 | .events_loop 550 | .run_forever(|event| match event { 551 | Event::WindowEvent { 552 | event: WindowEvent::CloseRequested, 553 | .. 554 | } => ControlFlow::Break, 555 | _ => ControlFlow::Continue, 556 | }); 557 | } 558 | 559 | fn run(&mut self) { 560 | self.main_loop(); 561 | } 562 | 563 | unsafe fn clean_up(self) { 564 | self.hal_state.clean_up(); 565 | } 566 | } 567 | -------------------------------------------------------------------------------- /src/14_command_buffers.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate glsl_to_spirv; 10 | extern crate winit; 11 | 12 | use hal::{ 13 | command, format, image, pass, pool, pso, queue, window, Adapter, Backbuffer, Backend, 14 | Capability, Device, Features, Gpu, Graphics, Instance, PhysicalDevice, Primitive, QueueFamily, 15 | Surface, SwapchainConfig, 16 | }; 17 | use std::io::Read; 18 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 19 | 20 | static WINDOW_NAME: &str = "14_command_buffers"; 21 | 22 | fn main() { 23 | env_logger::init(); 24 | let mut application = HelloTriangleApplication::init(); 25 | application.run(); 26 | unsafe { 27 | application.clean_up(); 28 | } 29 | } 30 | 31 | struct WindowState { 32 | events_loop: EventsLoop, 33 | window: Window, 34 | } 35 | 36 | struct HalState { 37 | _submission_command_buffers: 38 | Vec>, 39 | command_pool: pool::CommandPool, 40 | swapchain_framebuffers: Vec<::Framebuffer>, 41 | gfx_pipeline: ::GraphicsPipeline, 42 | descriptor_set_layouts: Vec<::DescriptorSetLayout>, 43 | pipeline_layout: ::PipelineLayout, 44 | render_pass: ::RenderPass, 45 | frame_images: Vec<( 46 | ::Image, 47 | ::ImageView, 48 | )>, 49 | _format: format::Format, 50 | swapchain: ::Swapchain, 51 | _command_queues: Vec>, 52 | device: ::Device, 53 | _surface: ::Surface, 54 | _adapter: Adapter, 55 | _instance: back::Instance, 56 | } 57 | 58 | impl HalState { 59 | unsafe fn clean_up(self) { 60 | let device = &self.device; 61 | 62 | device.destroy_command_pool(self.command_pool.into_raw()); 63 | 64 | for framebuffer in self.swapchain_framebuffers { 65 | device.destroy_framebuffer(framebuffer); 66 | } 67 | 68 | device.destroy_graphics_pipeline(self.gfx_pipeline); 69 | 70 | for descriptor_set_layout in self.descriptor_set_layouts { 71 | device.destroy_descriptor_set_layout(descriptor_set_layout); 72 | } 73 | 74 | device.destroy_pipeline_layout(self.pipeline_layout); 75 | 76 | device.destroy_render_pass(self.render_pass); 77 | 78 | for (_, image_view) in self.frame_images.into_iter() { 79 | device.destroy_image_view(image_view); 80 | } 81 | 82 | device.destroy_swapchain(self.swapchain); 83 | } 84 | } 85 | 86 | struct HelloTriangleApplication { 87 | hal_state: HalState, 88 | window_state: WindowState, 89 | } 90 | 91 | #[derive(Default)] 92 | struct QueueFamilyIds { 93 | graphics_family: Option, 94 | } 95 | 96 | impl QueueFamilyIds { 97 | fn is_complete(&self) -> bool { 98 | self.graphics_family.is_some() 99 | } 100 | } 101 | 102 | impl HelloTriangleApplication { 103 | pub fn init() -> HelloTriangleApplication { 104 | let window_state = HelloTriangleApplication::init_window(); 105 | let hal_state = unsafe { HelloTriangleApplication::init_hal(&window_state.window) }; 106 | 107 | HelloTriangleApplication { 108 | hal_state, 109 | window_state, 110 | } 111 | } 112 | 113 | fn init_window() -> WindowState { 114 | let events_loop = EventsLoop::new(); 115 | let window_builder = WindowBuilder::new() 116 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 117 | .with_title(WINDOW_NAME.to_string()); 118 | let window = window_builder.build(&events_loop).unwrap(); 119 | 120 | WindowState { 121 | events_loop, 122 | window, 123 | } 124 | } 125 | 126 | unsafe fn init_hal(window: &Window) -> HalState { 127 | let instance = HelloTriangleApplication::create_instance(); 128 | let mut adapter = HelloTriangleApplication::pick_adapter(&instance); 129 | let mut surface = HelloTriangleApplication::create_surface(&instance, window); 130 | let (device, command_queues, queue_type, qf_id) = 131 | HelloTriangleApplication::create_device_with_graphics_queues(&mut adapter, &surface); 132 | let (swapchain, extent, backbuffer, format) = 133 | HelloTriangleApplication::create_swap_chain(&adapter, &device, &mut surface, None); 134 | let frame_images = 135 | HelloTriangleApplication::create_image_views(backbuffer, format, &device); 136 | let render_pass = HelloTriangleApplication::create_render_pass(&device, Some(format)); 137 | let (descriptor_set_layouts, pipeline_layout, gfx_pipeline) = 138 | HelloTriangleApplication::create_graphics_pipeline(&device, extent, &render_pass); 139 | let swapchain_framebuffers = HelloTriangleApplication::create_framebuffers( 140 | &device, 141 | &render_pass, 142 | &frame_images, 143 | extent, 144 | ); 145 | let mut command_pool = 146 | HelloTriangleApplication::create_command_pool(&device, queue_type, qf_id); 147 | let submission_command_buffers = HelloTriangleApplication::create_command_buffers( 148 | &mut command_pool, 149 | &render_pass, 150 | &swapchain_framebuffers, 151 | extent, 152 | &gfx_pipeline, 153 | ); 154 | 155 | HalState { 156 | _submission_command_buffers: submission_command_buffers, 157 | command_pool, 158 | swapchain_framebuffers, 159 | gfx_pipeline, 160 | descriptor_set_layouts, 161 | pipeline_layout, 162 | render_pass, 163 | frame_images, 164 | _format: format, 165 | swapchain, 166 | _command_queues: command_queues, 167 | device, 168 | _surface: surface, 169 | _adapter: adapter, 170 | _instance: instance, 171 | } 172 | } 173 | 174 | fn create_instance() -> back::Instance { 175 | back::Instance::create(WINDOW_NAME, 1) 176 | } 177 | 178 | fn find_queue_families(adapter: &Adapter) -> QueueFamilyIds { 179 | let mut queue_family_ids = QueueFamilyIds::default(); 180 | 181 | for queue_family in &adapter.queue_families { 182 | if queue_family.max_queues() > 0 && queue_family.supports_graphics() { 183 | queue_family_ids.graphics_family = Some(queue_family.id()); 184 | } 185 | 186 | if queue_family_ids.is_complete() { 187 | break; 188 | } 189 | } 190 | 191 | queue_family_ids 192 | } 193 | 194 | fn is_adapter_suitable(adapter: &Adapter) -> bool { 195 | HelloTriangleApplication::find_queue_families(adapter).is_complete() 196 | } 197 | 198 | fn pick_adapter(instance: &back::Instance) -> Adapter { 199 | let adapters = instance.enumerate_adapters(); 200 | for adapter in adapters { 201 | if HelloTriangleApplication::is_adapter_suitable(&adapter) { 202 | return adapter; 203 | } 204 | } 205 | panic!("No suitable adapter"); 206 | } 207 | 208 | fn create_surface( 209 | instance: &back::Instance, 210 | window: &Window, 211 | ) -> ::Surface { 212 | instance.create_surface(window) 213 | } 214 | 215 | fn create_device_with_graphics_queues( 216 | adapter: &mut Adapter, 217 | surface: &::Surface, 218 | ) -> ( 219 | ::Device, 220 | Vec>, 221 | queue::QueueType, 222 | queue::family::QueueFamilyId, 223 | ) { 224 | let family = adapter 225 | .queue_families 226 | .iter() 227 | .find(|family| { 228 | Graphics::supported_by(family.queue_type()) 229 | && family.max_queues() > 0 230 | && surface.supports_queue_family(family) 231 | }) 232 | .expect("Could not find a queue family supporting graphics."); 233 | 234 | let priorities = vec![1.0; 1]; 235 | let families = [(family, priorities.as_slice())]; 236 | 237 | let Gpu { device, mut queues } = unsafe { 238 | adapter 239 | .physical_device 240 | .open(&families, Features::empty()) 241 | .expect("Could not create device.") 242 | }; 243 | 244 | let mut queue_group = queues 245 | .take::(family.id()) 246 | .expect("Could not take ownership of relevant queue group."); 247 | 248 | let command_queues: Vec<_> = queue_group.queues.drain(..1).collect(); 249 | 250 | (device, command_queues, family.queue_type(), family.id()) 251 | } 252 | 253 | fn create_swap_chain( 254 | adapter: &Adapter, 255 | device: &::Device, 256 | surface: &mut ::Surface, 257 | previous_swapchain: Option<::Swapchain>, 258 | ) -> ( 259 | ::Swapchain, 260 | window::Extent2D, 261 | Backbuffer, 262 | format::Format, 263 | ) { 264 | let (caps, formats, _present_modes, _composite_alphas) = 265 | surface.compatibility(&adapter.physical_device); 266 | 267 | let format = formats.map_or(format::Format::Rgba8Srgb, |formats| { 268 | formats 269 | .iter() 270 | .find(|format| format.base_format().1 == format::ChannelType::Srgb) 271 | .map(|format| *format) 272 | .unwrap_or(formats[0]) 273 | }); 274 | 275 | let swap_config = SwapchainConfig::from_caps(&caps, format, caps.extents.end); 276 | let extent = swap_config.extent; 277 | let (swapchain, backbuffer) = unsafe { 278 | device 279 | .create_swapchain(surface, swap_config, previous_swapchain) 280 | .unwrap() 281 | }; 282 | 283 | (swapchain, extent, backbuffer, format) 284 | } 285 | 286 | unsafe fn create_image_views( 287 | backbuffer: Backbuffer, 288 | format: format::Format, 289 | device: &::Device, 290 | ) -> Vec<( 291 | ::Image, 292 | ::ImageView, 293 | )> { 294 | match backbuffer { 295 | window::Backbuffer::Images(images) => images 296 | .into_iter() 297 | .map(|image| { 298 | let image_view = match device.create_image_view( 299 | &image, 300 | image::ViewKind::D2, 301 | format, 302 | format::Swizzle::NO, 303 | image::SubresourceRange { 304 | aspects: format::Aspects::COLOR, 305 | levels: 0..1, 306 | layers: 0..1, 307 | }, 308 | ) { 309 | Ok(image_view) => image_view, 310 | Err(_) => panic!("Error creating image view for an image!"), 311 | }; 312 | 313 | (image, image_view) 314 | }) 315 | .collect(), 316 | _ => unimplemented!(), 317 | } 318 | } 319 | 320 | fn create_render_pass( 321 | device: &::Device, 322 | format: Option, 323 | ) -> ::RenderPass { 324 | let samples: u8 = 1; 325 | 326 | let ops = pass::AttachmentOps { 327 | load: pass::AttachmentLoadOp::Clear, 328 | store: pass::AttachmentStoreOp::Store, 329 | }; 330 | 331 | let stencil_ops = pass::AttachmentOps::DONT_CARE; 332 | 333 | let layouts = image::Layout::Undefined..image::Layout::Present; 334 | 335 | let color_attachment = pass::Attachment { 336 | format, 337 | samples, 338 | ops, 339 | stencil_ops, 340 | layouts, 341 | }; 342 | 343 | let color_attachment_ref: pass::AttachmentRef = (0, image::Layout::ColorAttachmentOptimal); 344 | 345 | let subpass = pass::SubpassDesc { 346 | colors: &[color_attachment_ref], 347 | depth_stencil: None, 348 | inputs: &[], 349 | resolves: &[], 350 | preserves: &[], 351 | }; 352 | 353 | unsafe { 354 | device 355 | .create_render_pass(&[color_attachment], &[subpass], &[]) 356 | .unwrap() 357 | } 358 | } 359 | 360 | unsafe fn create_graphics_pipeline( 361 | device: &::Device, 362 | extent: window::Extent2D, 363 | render_pass: &::RenderPass, 364 | ) -> ( 365 | Vec<::DescriptorSetLayout>, 366 | ::PipelineLayout, 367 | ::GraphicsPipeline, 368 | ) { 369 | let vert_shader_code = glsl_to_spirv::compile( 370 | include_str!("09_shader_base.vert"), 371 | glsl_to_spirv::ShaderType::Vertex, 372 | ) 373 | .expect("Error compiling vertex shader code.") 374 | .bytes() 375 | .map(|b| b.unwrap()) 376 | .collect::>(); 377 | 378 | let frag_shader_code = glsl_to_spirv::compile( 379 | include_str!("09_shader_base.frag"), 380 | glsl_to_spirv::ShaderType::Fragment, 381 | ) 382 | .expect("Error compiling fragment shader code.") 383 | .bytes() 384 | .map(|b| b.unwrap()) 385 | .collect::>(); 386 | 387 | let vert_shader_module = device 388 | .create_shader_module(&vert_shader_code) 389 | .expect("Error creating shader module."); 390 | let frag_shader_module = device 391 | .create_shader_module(&frag_shader_code) 392 | .expect("Error creating fragment module."); 393 | 394 | let (ds_layouts, pipeline_layout, gfx_pipeline) = { 395 | let (vs_entry, fs_entry) = ( 396 | pso::EntryPoint:: { 397 | entry: "main", 398 | module: &vert_shader_module, 399 | specialization: hal::pso::Specialization { 400 | constants: &[], 401 | data: &[], 402 | }, 403 | }, 404 | pso::EntryPoint:: { 405 | entry: "main", 406 | module: &frag_shader_module, 407 | specialization: hal::pso::Specialization { 408 | constants: &[], 409 | data: &[], 410 | }, 411 | }, 412 | ); 413 | 414 | let shaders = pso::GraphicsShaderSet { 415 | vertex: vs_entry, 416 | hull: None, 417 | domain: None, 418 | geometry: None, 419 | fragment: Some(fs_entry), 420 | }; 421 | 422 | let rasterizer = pso::Rasterizer { 423 | depth_clamping: false, 424 | polygon_mode: pso::PolygonMode::Fill, 425 | cull_face: ::BACK, 426 | front_face: pso::FrontFace::Clockwise, 427 | depth_bias: None, 428 | conservative: false, 429 | }; 430 | 431 | let vertex_buffers: Vec = Vec::new(); 432 | let attributes: Vec = Vec::new(); 433 | 434 | let input_assembler = pso::InputAssemblerDesc::new(Primitive::TriangleList); 435 | 436 | let blender = { 437 | let blend_state = pso::BlendState::On { 438 | color: pso::BlendOp::Add { 439 | src: pso::Factor::One, 440 | dst: pso::Factor::Zero, 441 | }, 442 | alpha: pso::BlendOp::Add { 443 | src: pso::Factor::One, 444 | dst: pso::Factor::Zero, 445 | }, 446 | }; 447 | 448 | pso::BlendDesc { 449 | logic_op: Some(pso::LogicOp::Copy), 450 | targets: vec![pso::ColorBlendDesc(pso::ColorMask::ALL, blend_state)], 451 | } 452 | }; 453 | 454 | let depth_stencil = pso::DepthStencilDesc { 455 | depth: pso::DepthTest::Off, 456 | depth_bounds: false, 457 | stencil: pso::StencilTest::Off, 458 | }; 459 | 460 | let multisampling: Option = None; 461 | 462 | let baked_states = pso::BakedStates { 463 | viewport: Some(pso::Viewport { 464 | rect: pso::Rect { 465 | x: 0, 466 | y: 0, 467 | w: extent.width as i16, 468 | h: extent.height as i16, 469 | }, 470 | depth: (0.0..1.0), 471 | }), 472 | scissor: Some(pso::Rect { 473 | x: 0, 474 | y: 0, 475 | w: extent.width as i16, 476 | h: extent.height as i16, 477 | }), 478 | blend_color: None, 479 | depth_bounds: None, 480 | }; 481 | 482 | let bindings = Vec::::new(); 483 | let immutable_samplers = Vec::<::Sampler>::new(); 484 | let ds_layouts: Vec<::DescriptorSetLayout> = vec![device 485 | .create_descriptor_set_layout(bindings, immutable_samplers) 486 | .unwrap()]; 487 | let push_constants = Vec::<(pso::ShaderStageFlags, std::ops::Range)>::new(); 488 | let layout = device 489 | .create_pipeline_layout(&ds_layouts, push_constants) 490 | .unwrap(); 491 | 492 | let subpass = pass::Subpass { 493 | index: 0, 494 | main_pass: render_pass, 495 | }; 496 | 497 | let flags = pso::PipelineCreationFlags::empty(); 498 | 499 | let parent = pso::BasePipeline::None; 500 | 501 | let gfx_pipeline = { 502 | let desc = pso::GraphicsPipelineDesc { 503 | shaders, 504 | rasterizer, 505 | vertex_buffers, 506 | attributes, 507 | input_assembler, 508 | blender, 509 | depth_stencil, 510 | multisampling, 511 | baked_states, 512 | layout: &layout, 513 | subpass, 514 | flags, 515 | parent, 516 | }; 517 | 518 | device 519 | .create_graphics_pipeline(&desc, None) 520 | .expect("failed to create graphics pipeline!") 521 | }; 522 | 523 | (ds_layouts, layout, gfx_pipeline) 524 | }; 525 | 526 | device.destroy_shader_module(vert_shader_module); 527 | device.destroy_shader_module(frag_shader_module); 528 | 529 | (ds_layouts, pipeline_layout, gfx_pipeline) 530 | } 531 | 532 | fn create_framebuffers( 533 | device: &::Device, 534 | render_pass: &::RenderPass, 535 | frame_images: &[( 536 | ::Image, 537 | ::ImageView, 538 | )], 539 | extent: window::Extent2D, 540 | ) -> Vec<::Framebuffer> { 541 | let mut swapchain_framebuffers: Vec<::Framebuffer> = Vec::new(); 542 | 543 | unsafe { 544 | for (_, image_view) in frame_images.iter() { 545 | swapchain_framebuffers.push( 546 | device 547 | .create_framebuffer( 548 | render_pass, 549 | vec![image_view], 550 | image::Extent { 551 | width: extent.width as _, 552 | height: extent.height as _, 553 | depth: 1, 554 | }, 555 | ) 556 | .expect("failed to create framebuffer!"), 557 | ); 558 | } 559 | } 560 | 561 | swapchain_framebuffers 562 | } 563 | 564 | unsafe fn create_command_buffers<'a>( 565 | command_pool: &'a mut pool::CommandPool, 566 | render_pass: &::RenderPass, 567 | framebuffers: &[::Framebuffer], 568 | extent: window::Extent2D, 569 | pipeline: &::GraphicsPipeline, 570 | ) -> Vec> 571 | { 572 | // pre-allocating memory primary command buffers is not necessary: HAL handles automatically 573 | 574 | let mut submission_command_buffers: Vec< 575 | command::CommandBuffer, 576 | > = Vec::new(); 577 | 578 | for fb in framebuffers.iter() { 579 | // command buffer will be returned in 'recording' state 580 | // Shot: how many times a command buffer can be submitted; we want MultiShot (allow submission multiple times) 581 | // Level: command buffer type (primary or secondary) 582 | let mut command_buffer: command::CommandBuffer< 583 | back::Backend, 584 | Graphics, 585 | command::MultiShot, 586 | command::Primary, 587 | > = command_pool.acquire_command_buffer(); 588 | 589 | // allow_pending_resubmit to be true, per vulkan-tutorial 590 | command_buffer.begin(true); 591 | command_buffer.bind_graphics_pipeline(pipeline); 592 | { 593 | // begin render pass 594 | let render_area = pso::Rect { 595 | x: 0, 596 | y: 0, 597 | w: extent.width as _, 598 | h: extent.height as _, 599 | }; 600 | let clear_values = vec![command::ClearValue::Color(command::ClearColor::Float([ 601 | 0.0, 0.0, 0.0, 1.0, 602 | ]))]; 603 | 604 | let mut render_pass_inline_encoder = command_buffer.begin_render_pass_inline( 605 | render_pass, 606 | fb, 607 | render_area, 608 | clear_values.iter(), 609 | ); 610 | // HAL encoder draw command is best understood by seeing how it expands out: 611 | // vertex_count = vertices.end - vertices.start 612 | // instance_count = instances.end - instances.start 613 | // first_vertex = vertices.start 614 | // first_instance = instances.start 615 | render_pass_inline_encoder.draw(0..3, 0..1); 616 | } 617 | 618 | command_buffer.finish(); 619 | submission_command_buffers.push(command_buffer); 620 | } 621 | 622 | submission_command_buffers 623 | } 624 | 625 | unsafe fn create_command_pool( 626 | device: &::Device, 627 | queue_type: queue::QueueType, 628 | qf_id: queue::family::QueueFamilyId, 629 | ) -> pool::CommandPool { 630 | // raw command pool: a thin wrapper around command pools 631 | // strongly typed command pool: a safe wrapper around command pools, which ensures that only one command buffer is recorded at the same time from the current queue 632 | let raw_command_pool = device 633 | .create_command_pool(qf_id, pool::CommandPoolCreateFlags::empty()) 634 | .unwrap(); 635 | 636 | // safety check necessary before creating a strongly typed command pool 637 | assert_eq!(Graphics::supported_by(queue_type), true); 638 | pool::CommandPool::new(raw_command_pool) 639 | } 640 | 641 | fn main_loop(&mut self) { 642 | self.window_state 643 | .events_loop 644 | .run_forever(|event| match event { 645 | Event::WindowEvent { 646 | event: WindowEvent::CloseRequested, 647 | .. 648 | } => ControlFlow::Break, 649 | _ => ControlFlow::Continue, 650 | }); 651 | } 652 | 653 | fn run(&mut self) { 654 | self.main_loop(); 655 | } 656 | 657 | unsafe fn clean_up(self) { 658 | self.hal_state.clean_up(); 659 | } 660 | } 661 | -------------------------------------------------------------------------------- /src/15_hello_triangle.rs: -------------------------------------------------------------------------------- 1 | extern crate env_logger; 2 | #[cfg(feature = "dx12")] 3 | extern crate gfx_backend_dx12 as back; 4 | #[cfg(feature = "metal")] 5 | extern crate gfx_backend_metal as back; 6 | #[cfg(feature = "vulkan")] 7 | extern crate gfx_backend_vulkan as back; 8 | extern crate gfx_hal as hal; 9 | extern crate glsl_to_spirv; 10 | extern crate winit; 11 | 12 | use hal::{ 13 | command, format, image, pass, pool, pso, queue, window, Adapter, Backbuffer, Backend, 14 | Capability, Device, Features, Gpu, Graphics, Instance, PhysicalDevice, Primitive, QueueFamily, 15 | Surface, Swapchain, SwapchainConfig, 16 | }; 17 | use std::io::Read; 18 | use winit::{dpi, ControlFlow, Event, EventsLoop, Window, WindowBuilder, WindowEvent}; 19 | 20 | static WINDOW_NAME: &str = "15_hello_triangle"; 21 | const MAX_FRAMES_IN_FLIGHT: usize = 2; 22 | 23 | fn main() { 24 | env_logger::init(); 25 | let mut application = HelloTriangleApplication::init(); 26 | application.run(); 27 | unsafe { 28 | application.clean_up(); 29 | } 30 | } 31 | 32 | struct WindowState { 33 | events_loop: Option, 34 | window: Window, 35 | } 36 | 37 | struct HalState { 38 | in_flight_fences: Vec<::Fence>, 39 | render_finished_semaphores: Vec<::Semaphore>, 40 | image_available_semaphores: Vec<::Semaphore>, 41 | submission_command_buffers: 42 | Vec>, 43 | command_pool: pool::CommandPool, 44 | swapchain_framebuffers: Vec<::Framebuffer>, 45 | gfx_pipeline: ::GraphicsPipeline, 46 | descriptor_set_layouts: Vec<::DescriptorSetLayout>, 47 | pipeline_layout: ::PipelineLayout, 48 | render_pass: ::RenderPass, 49 | frame_images: Vec<( 50 | ::Image, 51 | ::ImageView, 52 | )>, 53 | _format: format::Format, 54 | swapchain: ::Swapchain, 55 | command_queues: Vec>, 56 | device: ::Device, 57 | _surface: ::Surface, 58 | _adapter: Adapter, 59 | _instance: back::Instance, 60 | } 61 | 62 | impl HalState { 63 | unsafe fn clean_up(self) { 64 | let device = &self.device; 65 | 66 | for fence in self.in_flight_fences { 67 | device.destroy_fence(fence) 68 | } 69 | 70 | for semaphore in self.render_finished_semaphores { 71 | device.destroy_semaphore(semaphore) 72 | } 73 | 74 | for semaphore in self.image_available_semaphores { 75 | device.destroy_semaphore(semaphore) 76 | } 77 | 78 | device.destroy_command_pool(self.command_pool.into_raw()); 79 | 80 | for framebuffer in self.swapchain_framebuffers { 81 | device.destroy_framebuffer(framebuffer); 82 | } 83 | 84 | device.destroy_graphics_pipeline(self.gfx_pipeline); 85 | 86 | for descriptor_set_layout in self.descriptor_set_layouts { 87 | device.destroy_descriptor_set_layout(descriptor_set_layout); 88 | } 89 | 90 | device.destroy_pipeline_layout(self.pipeline_layout); 91 | 92 | device.destroy_render_pass(self.render_pass); 93 | 94 | for (_, image_view) in self.frame_images.into_iter() { 95 | device.destroy_image_view(image_view); 96 | } 97 | 98 | device.destroy_swapchain(self.swapchain); 99 | } 100 | } 101 | 102 | struct HelloTriangleApplication { 103 | hal_state: HalState, 104 | window_state: WindowState, 105 | } 106 | 107 | #[derive(Default)] 108 | struct QueueFamilyIds { 109 | graphics_family: Option, 110 | } 111 | 112 | impl QueueFamilyIds { 113 | fn is_complete(&self) -> bool { 114 | self.graphics_family.is_some() 115 | } 116 | } 117 | 118 | impl HelloTriangleApplication { 119 | pub fn init() -> HelloTriangleApplication { 120 | let window_state = HelloTriangleApplication::init_window(); 121 | let hal_state = unsafe { HelloTriangleApplication::init_hal(&window_state.window) }; 122 | 123 | HelloTriangleApplication { 124 | hal_state, 125 | window_state, 126 | } 127 | } 128 | 129 | fn init_window() -> WindowState { 130 | let events_loop = EventsLoop::new(); 131 | let window_builder = WindowBuilder::new() 132 | .with_dimensions(dpi::LogicalSize::new(1024., 768.)) 133 | .with_title(WINDOW_NAME.to_string()); 134 | let window = window_builder.build(&events_loop).unwrap(); 135 | 136 | WindowState { 137 | events_loop: Some(events_loop), 138 | window, 139 | } 140 | } 141 | 142 | unsafe fn init_hal(window: &Window) -> HalState { 143 | let instance = HelloTriangleApplication::create_instance(); 144 | let mut adapter = HelloTriangleApplication::pick_adapter(&instance); 145 | let mut surface = HelloTriangleApplication::create_surface(&instance, window); 146 | let (device, command_queues, queue_type, qf_id) = 147 | HelloTriangleApplication::create_device_with_graphics_queues(&mut adapter, &surface); 148 | let (swapchain, extent, backbuffer, format) = 149 | HelloTriangleApplication::create_swap_chain(&adapter, &device, &mut surface, None); 150 | let frame_images = 151 | HelloTriangleApplication::create_image_views(backbuffer, format, &device); 152 | let render_pass = HelloTriangleApplication::create_render_pass(&device, Some(format)); 153 | let (descriptor_set_layouts, pipeline_layout, gfx_pipeline) = 154 | HelloTriangleApplication::create_graphics_pipeline(&device, extent, &render_pass); 155 | let swapchain_framebuffers = HelloTriangleApplication::create_framebuffers( 156 | &device, 157 | &render_pass, 158 | &frame_images, 159 | extent, 160 | ); 161 | let mut command_pool = 162 | HelloTriangleApplication::create_command_pool(&device, queue_type, qf_id); 163 | let submission_command_buffers = HelloTriangleApplication::create_command_buffers( 164 | &mut command_pool, 165 | &render_pass, 166 | &swapchain_framebuffers, 167 | extent, 168 | &gfx_pipeline, 169 | ); 170 | let (image_available_semaphores, render_finished_semaphores, in_flight_fences) = 171 | HelloTriangleApplication::create_sync_objects(&device); 172 | 173 | HalState { 174 | in_flight_fences, 175 | render_finished_semaphores, 176 | image_available_semaphores, 177 | submission_command_buffers, 178 | command_pool, 179 | swapchain_framebuffers, 180 | gfx_pipeline, 181 | descriptor_set_layouts, 182 | pipeline_layout, 183 | render_pass, 184 | frame_images, 185 | _format: format, 186 | swapchain, 187 | command_queues, 188 | device, 189 | _surface: surface, 190 | _adapter: adapter, 191 | _instance: instance, 192 | } 193 | } 194 | 195 | fn create_instance() -> back::Instance { 196 | back::Instance::create(WINDOW_NAME, 1) 197 | } 198 | 199 | fn find_queue_families(adapter: &Adapter) -> QueueFamilyIds { 200 | let mut queue_family_ids = QueueFamilyIds::default(); 201 | 202 | for queue_family in &adapter.queue_families { 203 | if queue_family.max_queues() > 0 && queue_family.supports_graphics() { 204 | queue_family_ids.graphics_family = Some(queue_family.id()); 205 | } 206 | 207 | if queue_family_ids.is_complete() { 208 | break; 209 | } 210 | } 211 | 212 | queue_family_ids 213 | } 214 | 215 | fn is_adapter_suitable(adapter: &Adapter) -> bool { 216 | HelloTriangleApplication::find_queue_families(adapter).is_complete() 217 | } 218 | 219 | fn pick_adapter(instance: &back::Instance) -> Adapter { 220 | let adapters = instance.enumerate_adapters(); 221 | for adapter in adapters { 222 | if HelloTriangleApplication::is_adapter_suitable(&adapter) { 223 | return adapter; 224 | } 225 | } 226 | panic!("No suitable adapter"); 227 | } 228 | 229 | fn create_surface( 230 | instance: &back::Instance, 231 | window: &Window, 232 | ) -> ::Surface { 233 | instance.create_surface(window) 234 | } 235 | 236 | fn create_device_with_graphics_queues( 237 | adapter: &mut Adapter, 238 | surface: &::Surface, 239 | ) -> ( 240 | ::Device, 241 | Vec>, 242 | queue::QueueType, 243 | queue::family::QueueFamilyId, 244 | ) { 245 | let family = adapter 246 | .queue_families 247 | .iter() 248 | .find(|family| { 249 | Graphics::supported_by(family.queue_type()) 250 | && family.max_queues() > 0 251 | && surface.supports_queue_family(family) 252 | }) 253 | .expect("Could not find a queue family supporting graphics."); 254 | 255 | let priorities = vec![1.0; 1]; 256 | let families = [(family, priorities.as_slice())]; 257 | 258 | let Gpu { device, mut queues } = unsafe { 259 | adapter 260 | .physical_device 261 | .open(&families, Features::empty()) 262 | .expect("Could not create device.") 263 | }; 264 | 265 | let mut queue_group = queues 266 | .take::(family.id()) 267 | .expect("Could not take ownership of relevant queue group."); 268 | 269 | let command_queues: Vec<_> = queue_group.queues.drain(..1).collect(); 270 | 271 | (device, command_queues, family.queue_type(), family.id()) 272 | } 273 | 274 | fn create_swap_chain( 275 | adapter: &Adapter, 276 | device: &::Device, 277 | surface: &mut ::Surface, 278 | previous_swapchain: Option<::Swapchain>, 279 | ) -> ( 280 | ::Swapchain, 281 | window::Extent2D, 282 | Backbuffer, 283 | format::Format, 284 | ) { 285 | let (caps, formats, _present_modes, _composite_alphas) = 286 | surface.compatibility(&adapter.physical_device); 287 | 288 | let format = formats.map_or(format::Format::Rgba8Srgb, |formats| { 289 | formats 290 | .iter() 291 | .find(|format| format.base_format().1 == format::ChannelType::Srgb) 292 | .map(|format| *format) 293 | .unwrap_or(formats[0]) 294 | }); 295 | 296 | let swap_config = SwapchainConfig::from_caps(&caps, format, caps.extents.end); 297 | let extent = swap_config.extent; 298 | let (swapchain, backbuffer) = unsafe { 299 | device 300 | .create_swapchain(surface, swap_config, previous_swapchain) 301 | .unwrap() 302 | }; 303 | 304 | (swapchain, extent, backbuffer, format) 305 | } 306 | 307 | unsafe fn create_image_views( 308 | backbuffer: Backbuffer, 309 | format: format::Format, 310 | device: &::Device, 311 | ) -> Vec<( 312 | ::Image, 313 | ::ImageView, 314 | )> { 315 | match backbuffer { 316 | window::Backbuffer::Images(images) => images 317 | .into_iter() 318 | .map(|image| { 319 | let image_view = match device.create_image_view( 320 | &image, 321 | image::ViewKind::D2, 322 | format, 323 | format::Swizzle::NO, 324 | image::SubresourceRange { 325 | aspects: format::Aspects::COLOR, 326 | levels: 0..1, 327 | layers: 0..1, 328 | }, 329 | ) { 330 | Ok(image_view) => image_view, 331 | Err(_) => panic!("Error creating image view for an image!"), 332 | }; 333 | 334 | (image, image_view) 335 | }) 336 | .collect(), 337 | _ => unimplemented!(), 338 | } 339 | } 340 | 341 | fn create_render_pass( 342 | device: &::Device, 343 | format: Option, 344 | ) -> ::RenderPass { 345 | let samples: u8 = 1; 346 | 347 | let ops = pass::AttachmentOps { 348 | load: pass::AttachmentLoadOp::Clear, 349 | store: pass::AttachmentStoreOp::Store, 350 | }; 351 | 352 | let stencil_ops = pass::AttachmentOps::DONT_CARE; 353 | 354 | let layouts = image::Layout::Undefined..image::Layout::Present; 355 | 356 | let color_attachment = pass::Attachment { 357 | format, 358 | samples, 359 | ops, 360 | stencil_ops, 361 | layouts, 362 | }; 363 | 364 | let color_attachment_ref: pass::AttachmentRef = (0, image::Layout::ColorAttachmentOptimal); 365 | 366 | // hal assumes pipeline bind point is GRAPHICS 367 | let subpass = pass::SubpassDesc { 368 | colors: &[color_attachment_ref], 369 | depth_stencil: None, 370 | inputs: &[], 371 | resolves: &[], 372 | preserves: &[], 373 | }; 374 | 375 | unsafe { 376 | device 377 | .create_render_pass(&[color_attachment], &[subpass], &[]) 378 | .unwrap() 379 | } 380 | } 381 | 382 | unsafe fn create_graphics_pipeline( 383 | device: &::Device, 384 | extent: window::Extent2D, 385 | render_pass: &::RenderPass, 386 | ) -> ( 387 | Vec<::DescriptorSetLayout>, 388 | ::PipelineLayout, 389 | ::GraphicsPipeline, 390 | ) { 391 | let vert_shader_code = glsl_to_spirv::compile( 392 | include_str!("09_shader_base.vert"), 393 | glsl_to_spirv::ShaderType::Vertex, 394 | ) 395 | .expect("Error compiling vertex shader code.") 396 | .bytes() 397 | .map(|b| b.unwrap()) 398 | .collect::>(); 399 | 400 | let frag_shader_code = glsl_to_spirv::compile( 401 | include_str!("09_shader_base.frag"), 402 | glsl_to_spirv::ShaderType::Fragment, 403 | ) 404 | .expect("Error compiling fragment shader code.") 405 | .bytes() 406 | .map(|b| b.unwrap()) 407 | .collect::>(); 408 | 409 | let vert_shader_module = device 410 | .create_shader_module(&vert_shader_code) 411 | .expect("Error creating shader module."); 412 | let frag_shader_module = device 413 | .create_shader_module(&frag_shader_code) 414 | .expect("Error creating fragment module."); 415 | 416 | let (descriptor_set_layouts, pipeline_layout, gfx_pipeline) = { 417 | let (vs_entry, fs_entry) = ( 418 | pso::EntryPoint:: { 419 | entry: "main", 420 | module: &vert_shader_module, 421 | specialization: hal::pso::Specialization { 422 | constants: &[], 423 | data: &[], 424 | }, 425 | }, 426 | pso::EntryPoint:: { 427 | entry: "main", 428 | module: &frag_shader_module, 429 | specialization: hal::pso::Specialization { 430 | constants: &[], 431 | data: &[], 432 | }, 433 | }, 434 | ); 435 | 436 | let shaders = pso::GraphicsShaderSet { 437 | vertex: vs_entry, 438 | hull: None, 439 | domain: None, 440 | geometry: None, 441 | fragment: Some(fs_entry), 442 | }; 443 | 444 | let rasterizer = pso::Rasterizer { 445 | depth_clamping: false, 446 | polygon_mode: pso::PolygonMode::Fill, 447 | cull_face: ::BACK, 448 | front_face: pso::FrontFace::Clockwise, 449 | depth_bias: None, 450 | conservative: false, 451 | }; 452 | 453 | let vertex_buffers: Vec = Vec::new(); 454 | let attributes: Vec = Vec::new(); 455 | 456 | let input_assembler = pso::InputAssemblerDesc::new(Primitive::TriangleList); 457 | 458 | let blender = { 459 | let blend_state = pso::BlendState::On { 460 | color: pso::BlendOp::Add { 461 | src: pso::Factor::One, 462 | dst: pso::Factor::Zero, 463 | }, 464 | alpha: pso::BlendOp::Add { 465 | src: pso::Factor::One, 466 | dst: pso::Factor::Zero, 467 | }, 468 | }; 469 | 470 | pso::BlendDesc { 471 | logic_op: Some(pso::LogicOp::Copy), 472 | targets: vec![pso::ColorBlendDesc(pso::ColorMask::ALL, blend_state)], 473 | } 474 | }; 475 | 476 | let depth_stencil = pso::DepthStencilDesc { 477 | depth: pso::DepthTest::Off, 478 | depth_bounds: false, 479 | stencil: pso::StencilTest::Off, 480 | }; 481 | 482 | let multisampling: Option = None; 483 | 484 | let baked_states = pso::BakedStates { 485 | viewport: Some(pso::Viewport { 486 | rect: pso::Rect { 487 | x: 0, 488 | y: 0, 489 | w: extent.width as i16, 490 | h: extent.height as i16, 491 | }, 492 | depth: (0.0..1.0), 493 | }), 494 | scissor: Some(pso::Rect { 495 | x: 0, 496 | y: 0, 497 | w: extent.width as i16, 498 | h: extent.height as i16, 499 | }), 500 | blend_color: None, 501 | depth_bounds: None, 502 | }; 503 | 504 | let bindings = Vec::::new(); 505 | let immutable_samplers = Vec::<::Sampler>::new(); 506 | let descriptor_set_layouts: Vec<::DescriptorSetLayout> = 507 | vec![device 508 | .create_descriptor_set_layout(bindings, immutable_samplers) 509 | .unwrap()]; 510 | let push_constants = Vec::<(pso::ShaderStageFlags, std::ops::Range)>::new(); 511 | let layout = device 512 | .create_pipeline_layout(&descriptor_set_layouts, push_constants) 513 | .unwrap(); 514 | 515 | let subpass = pass::Subpass { 516 | index: 0, 517 | main_pass: render_pass, 518 | }; 519 | 520 | let flags = pso::PipelineCreationFlags::empty(); 521 | 522 | let parent = pso::BasePipeline::None; 523 | 524 | let gfx_pipeline = { 525 | let desc = pso::GraphicsPipelineDesc { 526 | shaders, 527 | rasterizer, 528 | vertex_buffers, 529 | attributes, 530 | input_assembler, 531 | blender, 532 | depth_stencil, 533 | multisampling, 534 | baked_states, 535 | layout: &layout, 536 | subpass, 537 | flags, 538 | parent, 539 | }; 540 | 541 | device 542 | .create_graphics_pipeline(&desc, None) 543 | .expect("failed to create graphics pipeline!") 544 | }; 545 | 546 | (descriptor_set_layouts, layout, gfx_pipeline) 547 | }; 548 | 549 | device.destroy_shader_module(vert_shader_module); 550 | device.destroy_shader_module(frag_shader_module); 551 | 552 | (descriptor_set_layouts, pipeline_layout, gfx_pipeline) 553 | } 554 | 555 | fn create_framebuffers( 556 | device: &::Device, 557 | render_pass: &::RenderPass, 558 | frame_images: &[( 559 | ::Image, 560 | ::ImageView, 561 | )], 562 | extent: window::Extent2D, 563 | ) -> Vec<::Framebuffer> { 564 | let mut swapchain_framebuffers: Vec<::Framebuffer> = Vec::new(); 565 | 566 | unsafe { 567 | for (_, image_view) in frame_images.iter() { 568 | swapchain_framebuffers.push( 569 | device 570 | .create_framebuffer( 571 | render_pass, 572 | vec![image_view], 573 | image::Extent { 574 | width: extent.width as _, 575 | height: extent.height as _, 576 | depth: 1, 577 | }, 578 | ) 579 | .expect("failed to create framebuffer!"), 580 | ); 581 | } 582 | } 583 | 584 | swapchain_framebuffers 585 | } 586 | 587 | unsafe fn create_command_buffers<'a>( 588 | command_pool: &'a mut pool::CommandPool, 589 | render_pass: &::RenderPass, 590 | framebuffers: &[::Framebuffer], 591 | extent: window::Extent2D, 592 | pipeline: &::GraphicsPipeline, 593 | ) -> Vec> 594 | { 595 | let mut submission_command_buffers: Vec< 596 | command::CommandBuffer, 597 | > = Vec::new(); 598 | 599 | for fb in framebuffers.iter() { 600 | let mut command_buffer: command::CommandBuffer< 601 | back::Backend, 602 | Graphics, 603 | command::MultiShot, 604 | command::Primary, 605 | > = command_pool.acquire_command_buffer(); 606 | 607 | command_buffer.begin(true); 608 | command_buffer.bind_graphics_pipeline(pipeline); 609 | { 610 | // begin render pass 611 | let render_area = pso::Rect { 612 | x: 0, 613 | y: 0, 614 | w: extent.width as _, 615 | h: extent.height as _, 616 | }; 617 | let clear_values = vec![command::ClearValue::Color(command::ClearColor::Float([ 618 | 0.0, 0.0, 0.0, 1.0, 619 | ]))]; 620 | 621 | let mut render_pass_inline_encoder = command_buffer.begin_render_pass_inline( 622 | render_pass, 623 | fb, 624 | render_area, 625 | clear_values.iter(), 626 | ); 627 | 628 | render_pass_inline_encoder.draw(0..3, 0..1); 629 | } 630 | command_buffer.finish(); 631 | 632 | submission_command_buffers.push(command_buffer); 633 | } 634 | 635 | submission_command_buffers 636 | } 637 | 638 | unsafe fn create_command_pool( 639 | device: &::Device, 640 | queue_type: queue::QueueType, 641 | qf_id: queue::family::QueueFamilyId, 642 | ) -> pool::CommandPool { 643 | let raw_command_pool = device 644 | .create_command_pool(qf_id, pool::CommandPoolCreateFlags::empty()) 645 | .unwrap(); 646 | 647 | // safety check necessary before creating a strongly typed command pool 648 | assert_eq!(Graphics::supported_by(queue_type), true); 649 | pool::CommandPool::new(raw_command_pool) 650 | } 651 | 652 | unsafe fn draw_frame( 653 | device: &::Device, 654 | command_queues: &mut [queue::CommandQueue], 655 | swapchain: &mut ::Swapchain, 656 | submission_command_buffers: &[command::CommandBuffer< 657 | back::Backend, 658 | Graphics, 659 | command::MultiShot, 660 | command::Primary, 661 | >], 662 | image_available_semaphore: &::Semaphore, 663 | render_finished_semaphore: &::Semaphore, 664 | in_flight_fence: &::Fence, 665 | ) { 666 | device 667 | .wait_for_fence(in_flight_fence, std::u64::MAX) 668 | .unwrap(); 669 | device.reset_fence(in_flight_fence).unwrap(); 670 | 671 | let image_index = swapchain 672 | .acquire_image( 673 | std::u64::MAX, 674 | window::FrameSync::Semaphore(image_available_semaphore), 675 | ) 676 | .expect("could not acquire image!"); 677 | 678 | let i = image_index as usize; 679 | let submission = queue::Submission { 680 | command_buffers: &submission_command_buffers[i..i + 1], 681 | wait_semaphores: vec![( 682 | image_available_semaphore, 683 | pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT, 684 | )], 685 | signal_semaphores: vec![render_finished_semaphore], 686 | }; 687 | 688 | // recall we only made one queue 689 | command_queues[0].submit(submission, Some(in_flight_fence)); 690 | 691 | swapchain 692 | .present( 693 | &mut command_queues[0], 694 | image_index, 695 | vec![render_finished_semaphore], 696 | ) 697 | .expect("presentation failed!"); 698 | } 699 | 700 | fn create_sync_objects( 701 | device: &::Device, 702 | ) -> ( 703 | Vec<::Semaphore>, 704 | Vec<::Semaphore>, 705 | Vec<::Fence>, 706 | ) { 707 | let mut image_available_semaphores: Vec<::Semaphore> = Vec::new(); 708 | let mut render_finished_semaphores: Vec<::Semaphore> = Vec::new(); 709 | let mut in_flight_fences: Vec<::Fence> = Vec::new(); 710 | 711 | for _ in 0..MAX_FRAMES_IN_FLIGHT { 712 | image_available_semaphores.push(device.create_semaphore().unwrap()); 713 | render_finished_semaphores.push(device.create_semaphore().unwrap()); 714 | in_flight_fences.push(device.create_fence(true).unwrap()); 715 | } 716 | 717 | ( 718 | image_available_semaphores, 719 | render_finished_semaphores, 720 | in_flight_fences, 721 | ) 722 | } 723 | 724 | fn main_loop(&mut self) { 725 | let mut current_frame: usize = 0; 726 | 727 | let mut events_loop = self 728 | .window_state 729 | .events_loop 730 | .take() 731 | .expect("events_loop does not exist!"); 732 | events_loop.run_forever(|event| match event { 733 | Event::WindowEvent { 734 | event: WindowEvent::CloseRequested, 735 | .. 736 | } => { 737 | self.hal_state 738 | .device 739 | .wait_idle() 740 | .expect("Queues are not going idle!"); 741 | ControlFlow::Break 742 | } 743 | _ => { 744 | unsafe { 745 | HelloTriangleApplication::draw_frame( 746 | &self.hal_state.device, 747 | &mut self.hal_state.command_queues, 748 | &mut self.hal_state.swapchain, 749 | &self.hal_state.submission_command_buffers, 750 | &self.hal_state.image_available_semaphores[current_frame], 751 | &self.hal_state.render_finished_semaphores[current_frame], 752 | &self.hal_state.in_flight_fences[current_frame], 753 | ); 754 | } 755 | 756 | current_frame = (current_frame + 1) % MAX_FRAMES_IN_FLIGHT; 757 | ; 758 | ControlFlow::Continue 759 | } 760 | }); 761 | self.window_state.events_loop = Some(events_loop); 762 | } 763 | 764 | fn run(&mut self) { 765 | self.main_loop(); 766 | } 767 | 768 | unsafe fn clean_up(self) { 769 | self.hal_state.clean_up(); 770 | } 771 | } 772 | --------------------------------------------------------------------------------