├── .gitignore ├── .gitlab-ci.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── Cargo.toml ├── camera.rs ├── index_buffer.rs ├── shaders │ ├── camera │ │ ├── shader.frag │ │ └── shader.vert │ ├── index_buffer │ │ ├── shader.frag │ │ └── shader.vert │ ├── texture │ │ ├── shader.frag │ │ └── shader.vert │ ├── triangle │ │ ├── shader.frag │ │ └── shader.vert │ ├── uniform_buffer │ │ ├── shader.frag │ │ └── shader.vert │ └── vert_buffer │ │ ├── shader.frag │ │ └── shader.vert ├── src │ └── lib.rs ├── texture.jpg ├── texture.rs ├── triangle.rs ├── uniform_buffer.rs └── vert_buffer.rs ├── plate-macros ├── Cargo.toml └── src │ └── lib.rs └── plate ├── Cargo.toml └── src ├── buffer.rs ├── command.rs ├── debug.rs ├── descriptor.rs ├── device.rs ├── image.rs ├── instance.rs ├── lib.rs ├── pipeline.rs ├── rendering.rs ├── shaders ├── shader.frag └── shader.vert ├── surface.rs ├── swapchain.rs └── sync.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: rust:latest 2 | 3 | cache: 4 | - key: $CI_COMMIT_REF_SLUG 5 | - key: 6 | files: 7 | - Cargo.lock 8 | paths: 9 | - target 10 | 11 | before_script: 12 | - apt-get -qq update && apt-get -qq install -y python cmake gcc git libvulkan-dev 13 | 14 | stages: 15 | - build 16 | - test 17 | 18 | cargo-build: 19 | stage: build 20 | script: 21 | - cargo build --verbose 22 | 23 | cargo-test: 24 | stage: test 25 | script: 26 | - cargo test --verbose 27 | - cargo test --examples --verbose 28 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "plate", 5 | "plate-macros", 6 | "examples", 7 | ] 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Pedro Casotti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # plate 2 | 3 | Rust library for writing simpler Vulkan code 4 | 5 | [![crates.io][crates-badge]][crates-url] 6 | [![build][build-badge]][build-url] 7 | [![docs][docs-badge]][docs-url] 8 | [![license][license-badge]][license-url] 9 | 10 | [crates-badge]: https://img.shields.io/crates/v/plate 11 | [crates-url]: https://crates.io/crates/plate 12 | [build-badge]: https://img.shields.io/gitlab/pipeline-status/pcasotti/plate 13 | [build-url]: https://gitlab.com/pcasotti/plate/-/pipelines 14 | [docs-badge]: https://img.shields.io/docsrs/plate 15 | [docs-url]: https://docs.rs/plate/0.5.0/plate/ 16 | [license-badge]: https://img.shields.io/crates/l/plate 17 | [license-url]: https://github.com/pcasotti/plate/blob/main/LICENSE 18 | 19 | ## Installation 20 | 21 | Add the library to your Cargo.toml file: 22 | ```toml 23 | [dependencies] 24 | plate = "0.5" 25 | ``` 26 | 27 | ## Example 28 | 29 | Example code is available in the examples directory. 30 | 31 | Use cargo to run the examples: 32 | ```shell 33 | cargo run --example triangle 34 | ``` 35 | 36 | ## Features 37 | 38 | - Easy initialization. 39 | - Easy to use index and vertex buffers. 40 | - Simple buffer creation and manipulation of data. 41 | - Automatic buffer padding to device limits. 42 | - Simple image creation. 43 | - Ergonomic descriptor creation. 44 | - Dynamic descriptor support. 45 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | plate = { path = "../plate" } 9 | winit = "0.27" 10 | vk-shader-macros = "0.2" 11 | glam = "0.21" 12 | image = "0.24" 13 | memoffset = "0.6" 14 | 15 | [[example]] 16 | name = "triangle" 17 | path = "triangle.rs" 18 | 19 | [[example]] 20 | name = "vert_buffer" 21 | path = "vert_buffer.rs" 22 | 23 | [[example]] 24 | name = "index_buffer" 25 | path = "index_buffer.rs" 26 | 27 | [[example]] 28 | name = "uniform_buffer" 29 | path = "uniform_buffer.rs" 30 | 31 | [[example]] 32 | name = "texture" 33 | path = "texture.rs" 34 | 35 | [[example]] 36 | name = "camera" 37 | path = "camera.rs" 38 | -------------------------------------------------------------------------------- /examples/camera.rs: -------------------------------------------------------------------------------- 1 | use plate::{VertexDescription, plate_macros}; 2 | 3 | #[repr(C)] 4 | #[derive(plate_macros::Vertex)] 5 | struct Vert { 6 | #[vertex(loc = 0, format = "R32G32B32_SFLOAT")] 7 | pos: glam::Vec3, 8 | #[vertex(loc = 1, format = "R32G32_SFLOAT")] 9 | uv: glam::Vec2, 10 | } 11 | 12 | #[repr(C)] 13 | struct Ubo { 14 | proj: glam::Mat4, 15 | view: glam::Mat4, 16 | } 17 | 18 | fn main() -> Result<(), Box> { 19 | let event_loop = winit::event_loop::EventLoop::new(); 20 | let window = winit::window::WindowBuilder::new().build(&event_loop)?; 21 | 22 | let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 23 | let mut e = examples::App::new(&device, &window)?; 24 | 25 | let set_layout = plate::DescriptorSetLayout::new( 26 | &device, 27 | &[ 28 | plate::LayoutBinding { 29 | binding: 0, 30 | ty: plate::DescriptorType::UNIFORM_BUFFER, 31 | stage: plate::ShaderStage::VERTEX, 32 | count: 1, 33 | }, 34 | plate::LayoutBinding { 35 | binding: 1, 36 | ty: plate::DescriptorType::COMBINED_IMAGE_SAMPLER, 37 | stage: plate::ShaderStage::FRAGMENT, 38 | count: 1, 39 | }, 40 | ], 41 | )?; 42 | let pipeline = plate::pipeline::Pipeline::new( 43 | &device, 44 | &e.render_pass, 45 | vk_shader_macros::include_glsl!("shaders/camera/shader.vert"), 46 | vk_shader_macros::include_glsl!("shaders/camera/shader.frag"), 47 | &plate::PipelineParameters { 48 | vertex_binding_descriptions: Vert::binding_descriptions(), 49 | vertex_attribute_descriptions: Vert::attribute_descriptions(), 50 | descriptor_set_layouts: &[&set_layout], 51 | ..Default::default() 52 | }, 53 | )?; 54 | 55 | let cmd_pool = plate::CommandPool::new(&device)?; 56 | let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 57 | 58 | let vertices = vec![ 59 | Vert { pos: glam::vec3(-0.5, -0.5, 0.5), uv: glam::vec2(1.0, 0.0) }, 60 | Vert { pos: glam::vec3(0.5, -0.5, 0.5), uv: glam::vec2(0.0, 0.0) }, 61 | Vert { pos: glam::vec3(0.5, 0.5, 0.5), uv: glam::vec2(0.0, 1.0) }, 62 | Vert { pos: glam::vec3(-0.5, 0.5, 0.5), uv: glam::vec2(1.0, 1.0) }, 63 | 64 | Vert { pos: glam::vec3(-0.5, -0.5, -0.5), uv: glam::vec2(1.0, 0.0) }, 65 | Vert { pos: glam::vec3(0.5, -0.5, -0.5), uv: glam::vec2(0.0, 0.0) }, 66 | Vert { pos: glam::vec3(0.5, 0.5, -0.5), uv: glam::vec2(0.0, 1.0) }, 67 | Vert { pos: glam::vec3(-0.5, 0.5, -0.5), uv: glam::vec2(1.0, 1.0) }, 68 | ]; 69 | let indices = vec![ 70 | 0, 1, 2, 2, 3, 0, 71 | 0, 1, 4, 4, 5, 1, 72 | 0, 3, 4, 4, 7, 3, 73 | 2, 3, 7, 7, 6, 2, 74 | 1, 2, 5, 5, 6, 2, 75 | 4, 5, 6, 6, 7, 4, 76 | ]; 77 | 78 | let vert_buffer = plate::VertexBuffer::new(&device, &vertices, &cmd_pool)?; 79 | let index_buffer = plate::IndexBuffer::new(&device, &indices, &cmd_pool)?; 80 | 81 | let descriptor_pool = plate::DescriptorPool::new( 82 | &device, 83 | &[ 84 | plate::PoolSize { 85 | ty: plate::DescriptorType::UNIFORM_BUFFER, 86 | count: 1, 87 | }, 88 | plate::PoolSize { 89 | ty: plate::DescriptorType::COMBINED_IMAGE_SAMPLER, 90 | count: 1, 91 | }, 92 | ], 93 | 2 94 | )?; 95 | 96 | let ubo: plate::Buffer = plate::Buffer::new( 97 | &device, 98 | 1, 99 | plate::BufferUsageFlags::UNIFORM_BUFFER, 100 | plate::SharingMode::EXCLUSIVE, 101 | plate::MemoryPropertyFlags::HOST_VISIBLE | plate::MemoryPropertyFlags::HOST_COHERENT, 102 | )?; 103 | 104 | let tex = image::open("examples/texture.jpg")?.flipv(); 105 | let image = plate::Texture::new(&device, &cmd_pool, tex.width(), tex.height(), &tex.to_rgba8().into_raw())?; 106 | let sampler = plate::Sampler::new( 107 | &device, 108 | &plate::SamplerParameters { 109 | address_mode: plate::SamplerAddress::CLAMP_TO_EDGE, 110 | ..Default::default() 111 | }, 112 | )?; 113 | 114 | let descriptor_set = plate::DescriptorAllocator::new(&device) 115 | .add_buffer_binding(0, plate::DescriptorType::UNIFORM_BUFFER, &ubo) 116 | .add_image_binding(1, plate::DescriptorType::COMBINED_IMAGE_SAMPLER, &image, &sampler, plate::ImageLayout::SHADER_READ_ONLY_OPTIMAL) 117 | .allocate(&set_layout, &descriptor_pool)?; 118 | 119 | let mut ubo = ubo.map()?; 120 | 121 | let fence = plate::Fence::new(&device, plate::FenceFlags::SIGNALED)?; 122 | let acquire_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 123 | let present_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 124 | 125 | event_loop.run(move |event, _, control_flow| { 126 | *control_flow = winit::event_loop::ControlFlow::Poll; 127 | match event { 128 | winit::event::Event::WindowEvent { event, window_id } if window_id == window.id() => { 129 | match event { 130 | winit::event::WindowEvent::CloseRequested => { 131 | *control_flow = winit::event_loop::ControlFlow::Exit 132 | } 133 | winit::event::WindowEvent::Resized(_) => e.recreate().unwrap(), 134 | _ => (), 135 | } 136 | } 137 | 138 | winit::event::Event::MainEventsCleared => window.request_redraw(), 139 | winit::event::Event::RedrawRequested(window_id) if window_id == window.id() => { 140 | fence.wait().unwrap(); 141 | fence.reset().unwrap(); 142 | 143 | let (i, _) = e.swapchain.next_image(&acquire_sem).unwrap(); 144 | 145 | ubo.write(&[Ubo { 146 | proj: glam::Mat4::perspective_lh(45f32.to_radians(), e.swapchain.aspect_ratio(), 0.1, 10.0), 147 | view: glam::Mat4::look_at_lh(glam::vec3(2.0, 2.0, 2.0), glam::Vec3::ZERO, glam::Vec3::NEG_Y), 148 | }]); 149 | 150 | cmd_buffer.record(plate::CommandBufferUsageFlags::empty(), || { 151 | e.render_pass.begin(&cmd_buffer, &e.framebuffers[i as usize]); 152 | 153 | pipeline.bind(&cmd_buffer, e.swapchain.extent()); 154 | vert_buffer.bind(&cmd_buffer); 155 | index_buffer.bind(&cmd_buffer); 156 | descriptor_set.bind(&cmd_buffer, &pipeline, 0, &[]).unwrap(); 157 | 158 | cmd_buffer.draw_indexed(indices.len() as u32, 1, 0, 0, 0); 159 | e.render_pass.end(&cmd_buffer); 160 | }).unwrap(); 161 | 162 | device.queue_submit( 163 | &cmd_buffer, 164 | plate::PipelineStage::COLOR_ATTACHMENT_OUTPUT, 165 | Some(&acquire_sem), 166 | Some(&present_sem), 167 | Some(&fence), 168 | ).unwrap(); 169 | 170 | e.swapchain.present(i, &present_sem).unwrap(); 171 | } 172 | 173 | winit::event::Event::LoopDestroyed => device.wait_idle().unwrap(), 174 | _ => (), 175 | } 176 | }) 177 | } 178 | -------------------------------------------------------------------------------- /examples/index_buffer.rs: -------------------------------------------------------------------------------- 1 | use plate::{VertexDescription, plate_macros}; 2 | 3 | #[repr(C)] 4 | #[derive(plate_macros::Vertex)] 5 | struct Vert { 6 | #[vertex(loc = 0, format = "R32G32_SFLOAT")] 7 | pos: glam::Vec2, 8 | #[vertex(loc = 1, format = "R32G32B32_SFLOAT")] 9 | color: glam::Vec3, 10 | } 11 | 12 | fn main() -> Result<(), Box> { 13 | let event_loop = winit::event_loop::EventLoop::new(); 14 | let window = winit::window::WindowBuilder::new().build(&event_loop)?; 15 | 16 | let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 17 | let mut e = examples::App::new(&device, &window)?; 18 | let pipeline = plate::pipeline::Pipeline::new( 19 | &device, 20 | &e.render_pass, 21 | vk_shader_macros::include_glsl!("shaders/index_buffer/shader.vert"), 22 | vk_shader_macros::include_glsl!("shaders/index_buffer/shader.frag"), 23 | &plate::PipelineParameters { 24 | vertex_binding_descriptions: Vert::binding_descriptions(), 25 | vertex_attribute_descriptions: Vert::attribute_descriptions(), 26 | ..Default::default() 27 | }, 28 | )?; 29 | 30 | let cmd_pool = plate::CommandPool::new(&device)?; 31 | let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 32 | 33 | let vertices = vec![ 34 | Vert { pos: glam::vec2(-0.5, -0.5), color: glam::vec3(1.0, 0.0, 0.0) }, 35 | Vert { pos: glam::vec2(0.5, -0.5), color: glam::vec3(0.0, 1.0, 0.0) }, 36 | Vert { pos: glam::vec2(0.5, 0.5), color: glam::vec3(0.0, 0.0, 1.0) }, 37 | Vert { pos: glam::vec2(-0.5, 0.5), color: glam::vec3(1.0, 1.0, 1.0) }, 38 | ]; 39 | let indices = vec![0, 1, 2, 2, 3, 0]; 40 | 41 | let vert_buffer = plate::VertexBuffer::new(&device, &vertices, &cmd_pool)?; 42 | let index_buffer = plate::IndexBuffer::new(&device, &indices, &cmd_pool)?; 43 | 44 | let fence = plate::Fence::new(&device, plate::FenceFlags::SIGNALED)?; 45 | let acquire_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 46 | let present_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 47 | 48 | event_loop.run(move |event, _, control_flow| { 49 | *control_flow = winit::event_loop::ControlFlow::Poll; 50 | match event { 51 | winit::event::Event::WindowEvent { event, window_id } if window_id == window.id() => { 52 | match event { 53 | winit::event::WindowEvent::CloseRequested => { 54 | *control_flow = winit::event_loop::ControlFlow::Exit 55 | } 56 | winit::event::WindowEvent::Resized(_) => e.recreate().unwrap(), 57 | _ => (), 58 | } 59 | } 60 | 61 | winit::event::Event::MainEventsCleared => window.request_redraw(), 62 | winit::event::Event::RedrawRequested(window_id) if window_id == window.id() => { 63 | fence.wait().unwrap(); 64 | fence.reset().unwrap(); 65 | 66 | let (i, _) = e.swapchain.next_image(&acquire_sem).unwrap(); 67 | 68 | cmd_buffer.record(plate::CommandBufferUsageFlags::empty(), || { 69 | e.render_pass.begin(&cmd_buffer, &e.framebuffers[i as usize]); 70 | pipeline.bind(&cmd_buffer, e.swapchain.extent()); 71 | vert_buffer.bind(&cmd_buffer); 72 | index_buffer.bind(&cmd_buffer); 73 | cmd_buffer.draw_indexed(indices.len() as u32, 1, 0, 0, 0); 74 | e.render_pass.end(&cmd_buffer); 75 | }).unwrap(); 76 | 77 | device.queue_submit( 78 | &cmd_buffer, 79 | plate::PipelineStage::COLOR_ATTACHMENT_OUTPUT, 80 | Some(&acquire_sem), 81 | Some(&present_sem), 82 | Some(&fence), 83 | ).unwrap(); 84 | 85 | e.swapchain.present(i, &present_sem).unwrap(); 86 | } 87 | 88 | winit::event::Event::LoopDestroyed => device.wait_idle().unwrap(), 89 | _ => (), 90 | } 91 | }) 92 | } 93 | -------------------------------------------------------------------------------- /examples/shaders/camera/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec2 fragTexCoord; 4 | 5 | layout(location = 0) out vec4 outColor; 6 | 7 | layout(binding = 1) uniform sampler2D tex; 8 | 9 | void main() { 10 | outColor = texture(tex, fragTexCoord); 11 | } 12 | -------------------------------------------------------------------------------- /examples/shaders/camera/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 0) uniform UniformBufferObject { 4 | mat4 proj; 5 | mat4 view; 6 | } ubo; 7 | 8 | layout(location = 0) in vec3 inPosition; 9 | layout(location = 1) in vec2 uv; 10 | 11 | layout(location = 0) out vec2 fragTexCoord; 12 | 13 | void main() { 14 | gl_Position = ubo.proj * ubo.view * vec4(inPosition, 1.0); 15 | fragTexCoord = uv; 16 | } 17 | -------------------------------------------------------------------------------- /examples/shaders/index_buffer/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec3 color; 4 | 5 | layout(location = 0) out vec4 out_color; 6 | 7 | void main() { 8 | out_color = vec4(color, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /examples/shaders/index_buffer/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec2 pos; 4 | layout(location = 1) in vec3 color; 5 | 6 | layout(location = 0) out vec3 out_color; 7 | 8 | void main() { 9 | gl_Position = vec4(pos, 0.0, 1.0); 10 | out_color = color; 11 | } 12 | -------------------------------------------------------------------------------- /examples/shaders/texture/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec2 fragTexCoord; 4 | 5 | layout(location = 0) out vec4 outColor; 6 | 7 | layout(binding = 1) uniform sampler2D tex; 8 | 9 | void main() { 10 | outColor = texture(tex, fragTexCoord); 11 | } 12 | -------------------------------------------------------------------------------- /examples/shaders/texture/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 0) uniform UniformBufferObject { 4 | mat4 model; 5 | } ubo; 6 | 7 | layout(location = 0) in vec2 inPosition; 8 | layout(location = 1) in vec2 uv; 9 | 10 | layout(location = 0) out vec2 fragTexCoord; 11 | 12 | void main() { 13 | gl_Position = ubo.model * vec4(inPosition, 0.0, 1.0); 14 | fragTexCoord = uv; 15 | } 16 | -------------------------------------------------------------------------------- /examples/shaders/triangle/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec3 fragColor; 4 | 5 | layout(location = 0) out vec4 outColor; 6 | 7 | void main() { 8 | outColor = vec4(fragColor, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /examples/shaders/triangle/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) out vec3 fragColor; 4 | 5 | vec2 positions[3] = vec2[]( 6 | vec2(0.0, -0.5), 7 | vec2(0.5, 0.5), 8 | vec2(-0.5, 0.5) 9 | ); 10 | 11 | vec3 colors[3] = vec3[]( 12 | vec3(1.0, 0.0, 0.0), 13 | vec3(0.0, 1.0, 0.0), 14 | vec3(0.0, 0.0, 1.0) 15 | ); 16 | 17 | void main() { 18 | gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); 19 | fragColor = colors[gl_VertexIndex]; 20 | } 21 | -------------------------------------------------------------------------------- /examples/shaders/uniform_buffer/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec3 fragColor; 4 | 5 | layout(location = 0) out vec4 outColor; 6 | 7 | void main() { 8 | outColor = vec4(fragColor, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /examples/shaders/uniform_buffer/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 0) uniform UniformBufferObject { 4 | mat4 model; 5 | } ubo; 6 | 7 | layout(location = 0) in vec2 inPosition; 8 | layout(location = 1) in vec3 inColor; 9 | 10 | layout(location = 0) out vec3 fragColor; 11 | 12 | void main() { 13 | gl_Position = ubo.model * vec4(inPosition, 0.0, 1.0); 14 | fragColor = inColor; 15 | } 16 | -------------------------------------------------------------------------------- /examples/shaders/vert_buffer/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec3 fragColor; 4 | 5 | layout(location = 0) out vec4 outColor; 6 | 7 | void main() { 8 | outColor = vec4(fragColor, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /examples/shaders/vert_buffer/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec2 inPosition; 4 | layout(location = 1) in vec3 inColor; 5 | 6 | layout(location = 0) out vec3 fragColor; 7 | 8 | void main() { 9 | gl_Position = vec4(inPosition, 0.0, 1.0); 10 | fragColor = inColor; 11 | } 12 | -------------------------------------------------------------------------------- /examples/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | pub struct App { 4 | device: Arc, 5 | pub swapchain: plate::Swapchain, 6 | pub render_pass: plate::RenderPass, 7 | pub framebuffers: Vec, 8 | depth_image: plate::Image, 9 | } 10 | 11 | impl App { 12 | pub fn new(device: &Arc, window: &winit::window::Window) -> Result { 13 | let swapchain = plate::swapchain::Swapchain::new(&device, &window)?; 14 | 15 | let depth_image = plate::Image::new( 16 | device, 17 | swapchain.extent().0, 18 | swapchain.extent().1, 19 | swapchain.depth_format, 20 | plate::ImageLayout::UNDEFINED, 21 | plate::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT, 22 | plate::ImageAspectFlags::DEPTH, 23 | )?; 24 | 25 | let color_attachment = plate::Attachment { 26 | format: swapchain.surface_format, 27 | load_op: plate::AttachmentLoadOp::CLEAR, 28 | store_op: plate::AttachmentStoreOp::STORE, 29 | initial_layout: plate::ImageLayout::UNDEFINED, 30 | final_layout: plate::ImageLayout::PRESENT_SRC_KHR, 31 | }; 32 | let depth_attachment = plate::Attachment { 33 | format: swapchain.depth_format, 34 | load_op: plate::AttachmentLoadOp::CLEAR, 35 | store_op: plate::AttachmentStoreOp::STORE, 36 | initial_layout: plate::ImageLayout::UNDEFINED, 37 | final_layout: plate::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 38 | }; 39 | 40 | let subpass = plate::SubpassDescription { 41 | color_attachments: &[plate::AttachmentReference { attachment: 0, layout: plate::ImageLayout::COLOR_ATTACHMENT_OPTIMAL }], 42 | depth_attachment: Some(plate::AttachmentReference { attachment: 1, layout: plate::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL }), 43 | ..Default::default() 44 | }; 45 | 46 | let dependency = plate::SubpassDependency { 47 | src_subpass: plate::Subpass::EXTERNAL, 48 | dst_subpass: plate::Subpass(0), 49 | src_stage_mask: plate::PipelineStage::COLOR_ATTACHMENT_OUTPUT | plate::PipelineStage::EARLY_FRAGMENT_TESTS, 50 | dst_stage_mask: plate::PipelineStage::COLOR_ATTACHMENT_OUTPUT | plate::PipelineStage::EARLY_FRAGMENT_TESTS, 51 | src_access_mask: plate::AccessFlags::NONE, 52 | dst_access_mask: plate::AccessFlags::COLOR_ATTACHMENT_WRITE | plate::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE, 53 | }; 54 | 55 | let render_pass = plate::RenderPass::new(&device, &[color_attachment, depth_attachment], &[subpass], &[dependency])?; 56 | 57 | let framebuffers = swapchain.images 58 | .iter() 59 | .map(|image| { 60 | plate::Framebuffer::new(&device, &render_pass, &[image, &depth_image], swapchain.extent().0, swapchain.extent().1) 61 | }) 62 | .collect::, _>>()?; 63 | 64 | Ok(Self { 65 | device: Arc::clone(&device), 66 | swapchain, 67 | render_pass, 68 | framebuffers, 69 | depth_image, 70 | }) 71 | } 72 | 73 | pub fn recreate(&mut self) -> Result<(), plate::Error> { 74 | self.depth_image = plate::Image::new( 75 | &self.device, 76 | self.swapchain.extent().0, 77 | self.swapchain.extent().1, 78 | self.swapchain.depth_format, 79 | plate::ImageLayout::UNDEFINED, 80 | plate::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT, 81 | plate::ImageAspectFlags::DEPTH, 82 | )?; 83 | 84 | self.framebuffers = self.swapchain.images 85 | .iter() 86 | .map(|image| { 87 | plate::Framebuffer::new(&self.device, &self.render_pass, &[image, &self.depth_image], self.swapchain.extent().0, self.swapchain.extent().1) 88 | }) 89 | .collect::, _>>()?; 90 | 91 | Ok(()) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /examples/texture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcasotti/plate/04a9fd12a073fe0e4a33e455643f3971ab8955fa/examples/texture.jpg -------------------------------------------------------------------------------- /examples/texture.rs: -------------------------------------------------------------------------------- 1 | use plate::{VertexDescription, plate_macros}; 2 | 3 | #[repr(C)] 4 | #[derive(plate_macros::Vertex)] 5 | struct Vert { 6 | #[vertex(loc = 0, format = "R32G32_SFLOAT")] 7 | pos: glam::Vec2, 8 | #[vertex(loc = 1, format = "R32G32_SFLOAT")] 9 | uv: glam::Vec2, 10 | } 11 | 12 | #[repr(C)] 13 | struct Ubo { 14 | model: glam::Mat4, 15 | } 16 | 17 | fn main() -> Result<(), Box> { 18 | let event_loop = winit::event_loop::EventLoop::new(); 19 | let window = winit::window::WindowBuilder::new().build(&event_loop)?; 20 | 21 | let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 22 | let mut e = examples::App::new(&device, &window)?; 23 | 24 | let set_layout = plate::DescriptorSetLayout::new( 25 | &device, 26 | &[ 27 | plate::LayoutBinding { 28 | binding: 0, 29 | ty: plate::DescriptorType::UNIFORM_BUFFER, 30 | stage: plate::ShaderStage::VERTEX, 31 | count: 1, 32 | }, 33 | plate::LayoutBinding { 34 | binding: 1, 35 | ty: plate::DescriptorType::COMBINED_IMAGE_SAMPLER, 36 | stage: plate::ShaderStage::FRAGMENT, 37 | count: 1, 38 | }, 39 | ], 40 | )?; 41 | let pipeline = plate::pipeline::Pipeline::new( 42 | &device, 43 | &e.render_pass, 44 | vk_shader_macros::include_glsl!("shaders/texture/shader.vert"), 45 | vk_shader_macros::include_glsl!("shaders/texture/shader.frag"), 46 | &plate::PipelineParameters { 47 | vertex_binding_descriptions: Vert::binding_descriptions(), 48 | vertex_attribute_descriptions: Vert::attribute_descriptions(), 49 | descriptor_set_layouts: &[&set_layout], 50 | ..Default::default() 51 | }, 52 | )?; 53 | 54 | let cmd_pool = plate::CommandPool::new(&device)?; 55 | let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 56 | 57 | let vertices = vec![ 58 | Vert { pos: glam::vec2(-0.5, -0.5), uv: glam::vec2(1.0, 0.0) }, 59 | Vert { pos: glam::vec2(0.5, -0.5), uv: glam::vec2(0.0, 0.0) }, 60 | Vert { pos: glam::vec2(0.5, 0.5), uv: glam::vec2(0.0, 1.0) }, 61 | Vert { pos: glam::vec2(-0.5, 0.5), uv: glam::vec2(1.0, 1.0) }, 62 | ]; 63 | let indices = vec![0, 1, 2, 2, 3, 0]; 64 | 65 | let vert_buffer = plate::VertexBuffer::new(&device, &vertices, &cmd_pool)?; 66 | let index_buffer = plate::IndexBuffer::new(&device, &indices, &cmd_pool)?; 67 | 68 | let descriptor_pool = plate::DescriptorPool::new( 69 | &device, 70 | &[ 71 | plate::PoolSize { 72 | ty: plate::DescriptorType::UNIFORM_BUFFER, 73 | count: 1, 74 | }, 75 | plate::PoolSize { 76 | ty: plate::DescriptorType::COMBINED_IMAGE_SAMPLER, 77 | count: 1, 78 | }, 79 | ], 80 | 2 81 | )?; 82 | 83 | let ubo: plate::Buffer = plate::Buffer::new( 84 | &device, 85 | 1, 86 | plate::BufferUsageFlags::UNIFORM_BUFFER, 87 | plate::SharingMode::EXCLUSIVE, 88 | plate::MemoryPropertyFlags::HOST_VISIBLE | plate::MemoryPropertyFlags::HOST_COHERENT, 89 | )?; 90 | 91 | let tex = image::open("examples/texture.jpg")?.flipv(); 92 | let image = plate::Texture::new(&device, &cmd_pool, tex.width(), tex.height(), &tex.to_rgba8().into_raw())?; 93 | let sampler = plate::Sampler::new(&device, &Default::default())?; 94 | 95 | let descriptor_set = plate::DescriptorAllocator::new(&device) 96 | .add_buffer_binding(0, plate::DescriptorType::UNIFORM_BUFFER, &ubo) 97 | .add_image_binding(1, plate::DescriptorType::COMBINED_IMAGE_SAMPLER, &image, &sampler, plate::ImageLayout::SHADER_READ_ONLY_OPTIMAL) 98 | .allocate(&set_layout, &descriptor_pool)?; 99 | 100 | let mut ubo = ubo.map()?; 101 | 102 | let fence = plate::Fence::new(&device, plate::FenceFlags::SIGNALED)?; 103 | let acquire_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 104 | let present_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 105 | 106 | let mut rot = 0.0; 107 | event_loop.run(move |event, _, control_flow| { 108 | *control_flow = winit::event_loop::ControlFlow::Poll; 109 | match event { 110 | winit::event::Event::WindowEvent { event, window_id } if window_id == window.id() => { 111 | match event { 112 | winit::event::WindowEvent::CloseRequested => { 113 | *control_flow = winit::event_loop::ControlFlow::Exit 114 | } 115 | winit::event::WindowEvent::Resized(_) => e.recreate().unwrap(), 116 | _ => (), 117 | } 118 | } 119 | 120 | winit::event::Event::MainEventsCleared => window.request_redraw(), 121 | winit::event::Event::RedrawRequested(window_id) if window_id == window.id() => { 122 | rot += 0.01; 123 | 124 | fence.wait().unwrap(); 125 | fence.reset().unwrap(); 126 | 127 | let (i, _) = e.swapchain.next_image(&acquire_sem).unwrap(); 128 | 129 | ubo.write(&[Ubo { model: glam::Mat4::from_rotation_z(rot) }]); 130 | 131 | cmd_buffer.record(plate::CommandBufferUsageFlags::empty(), || { 132 | e.render_pass.begin(&cmd_buffer, &e.framebuffers[i as usize]); 133 | 134 | pipeline.bind(&cmd_buffer, e.swapchain.extent()); 135 | vert_buffer.bind(&cmd_buffer); 136 | index_buffer.bind(&cmd_buffer); 137 | descriptor_set.bind(&cmd_buffer, &pipeline, 0, &[]).unwrap(); 138 | 139 | cmd_buffer.draw_indexed(indices.len() as u32, 1, 0, 0, 0); 140 | e.render_pass.end(&cmd_buffer); 141 | }).unwrap(); 142 | 143 | device.queue_submit( 144 | &cmd_buffer, 145 | plate::PipelineStage::COLOR_ATTACHMENT_OUTPUT, 146 | Some(&acquire_sem), 147 | Some(&present_sem), 148 | Some(&fence), 149 | ).unwrap(); 150 | 151 | e.swapchain.present(i, &present_sem).unwrap(); 152 | } 153 | 154 | winit::event::Event::LoopDestroyed => device.wait_idle().unwrap(), 155 | _ => (), 156 | } 157 | }) 158 | } 159 | -------------------------------------------------------------------------------- /examples/triangle.rs: -------------------------------------------------------------------------------- 1 | fn main() -> Result<(), Box> { 2 | let event_loop = winit::event_loop::EventLoop::new(); 3 | let window = winit::window::WindowBuilder::new().build(&event_loop)?; 4 | 5 | let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 6 | let mut e = examples::App::new(&device, &window)?; 7 | 8 | let pipeline = plate::pipeline::Pipeline::new( 9 | &device, 10 | &e.render_pass, 11 | vk_shader_macros::include_glsl!("shaders/triangle/shader.vert"), 12 | vk_shader_macros::include_glsl!("shaders/triangle/shader.frag"), 13 | &Default::default(), 14 | )?; 15 | 16 | let cmd_pool = plate::CommandPool::new(&device)?; 17 | let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 18 | 19 | let fence = plate::Fence::new(&device, plate::FenceFlags::SIGNALED)?; 20 | let acquire_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 21 | let present_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 22 | 23 | event_loop.run(move |event, _, control_flow| { 24 | *control_flow = winit::event_loop::ControlFlow::Poll; 25 | match event { 26 | winit::event::Event::WindowEvent { event, window_id } if window_id == window.id() => { 27 | match event { 28 | winit::event::WindowEvent::CloseRequested => { 29 | *control_flow = winit::event_loop::ControlFlow::Exit 30 | } 31 | winit::event::WindowEvent::Resized(_) => e.recreate().unwrap(), 32 | _ => (), 33 | } 34 | } 35 | 36 | winit::event::Event::MainEventsCleared => window.request_redraw(), 37 | winit::event::Event::RedrawRequested(window_id) if window_id == window.id() => { 38 | fence.wait().unwrap(); 39 | fence.reset().unwrap(); 40 | 41 | let (i, _) = e.swapchain.next_image(&acquire_sem).unwrap(); 42 | 43 | cmd_buffer.record(plate::CommandBufferUsageFlags::empty(), || { 44 | e.render_pass.begin(&cmd_buffer, &e.framebuffers[i as usize]); 45 | pipeline.bind(&cmd_buffer, e.swapchain.extent()); 46 | cmd_buffer.draw(3, 1, 0, 0); 47 | e.render_pass.end(&cmd_buffer); 48 | }).unwrap(); 49 | 50 | device.queue_submit( 51 | &cmd_buffer, 52 | plate::PipelineStage::COLOR_ATTACHMENT_OUTPUT, 53 | Some(&acquire_sem), 54 | Some(&present_sem), 55 | Some(&fence), 56 | ).unwrap(); 57 | 58 | e.swapchain.present(i, &present_sem).unwrap(); 59 | } 60 | 61 | winit::event::Event::LoopDestroyed => device.wait_idle().unwrap(), 62 | _ => (), 63 | } 64 | }) 65 | } 66 | -------------------------------------------------------------------------------- /examples/uniform_buffer.rs: -------------------------------------------------------------------------------- 1 | use plate::{VertexDescription, plate_macros}; 2 | 3 | #[repr(C)] 4 | #[derive(plate_macros::Vertex)] 5 | struct Vert { 6 | #[vertex(loc = 0, format = "R32G32_SFLOAT")] 7 | pos: glam::Vec2, 8 | #[vertex(loc = 1, format = "R32G32B32_SFLOAT")] 9 | color: glam::Vec3, 10 | } 11 | 12 | #[repr(C)] 13 | struct Ubo { 14 | model: glam::Mat4, 15 | } 16 | 17 | fn main() -> Result<(), Box> { 18 | let event_loop = winit::event_loop::EventLoop::new(); 19 | let window = winit::window::WindowBuilder::new().build(&event_loop)?; 20 | 21 | let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 22 | let mut e = examples::App::new(&device, &window)?; 23 | 24 | let set_layout = plate::DescriptorSetLayout::new( 25 | &device, 26 | &[plate::LayoutBinding { 27 | binding: 0, 28 | ty: plate::DescriptorType::UNIFORM_BUFFER, 29 | stage: plate::ShaderStage::VERTEX, 30 | count: 1, 31 | }], 32 | )?; 33 | let pipeline = plate::pipeline::Pipeline::new( 34 | &device, 35 | &e.render_pass, 36 | vk_shader_macros::include_glsl!("shaders/uniform_buffer/shader.vert"), 37 | vk_shader_macros::include_glsl!("shaders/uniform_buffer/shader.frag"), 38 | &plate::PipelineParameters { 39 | vertex_binding_descriptions: Vert::binding_descriptions(), 40 | vertex_attribute_descriptions: Vert::attribute_descriptions(), 41 | descriptor_set_layouts: &[&set_layout], 42 | ..Default::default() 43 | }, 44 | )?; 45 | 46 | let cmd_pool = plate::CommandPool::new(&device)?; 47 | let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 48 | 49 | let vertices = vec![ 50 | Vert { pos: glam::vec2(-0.5, -0.5), color: glam::vec3(1.0, 0.0, 0.0) }, 51 | Vert { pos: glam::vec2(0.5, -0.5), color: glam::vec3(0.0, 1.0, 0.0) }, 52 | Vert { pos: glam::vec2(0.5, 0.5), color: glam::vec3(0.0, 0.0, 1.0) }, 53 | Vert { pos: glam::vec2(-0.5, 0.5), color: glam::vec3(1.0, 1.0, 1.0) }, 54 | ]; 55 | let indices = vec![0, 1, 2, 2, 3, 0]; 56 | 57 | let vert_buffer = plate::VertexBuffer::new(&device, &vertices, &cmd_pool)?; 58 | let index_buffer = plate::IndexBuffer::new(&device, &indices, &cmd_pool)?; 59 | 60 | let descriptor_pool = plate::DescriptorPool::new( 61 | &device, 62 | &[plate::PoolSize { 63 | ty: plate::DescriptorType::UNIFORM_BUFFER, 64 | count: 1, 65 | }], 66 | 1 67 | )?; 68 | 69 | let ubo: plate::Buffer = plate::Buffer::new( 70 | &device, 71 | 1, 72 | plate::BufferUsageFlags::UNIFORM_BUFFER, 73 | plate::SharingMode::EXCLUSIVE, 74 | plate::MemoryPropertyFlags::HOST_VISIBLE | plate::MemoryPropertyFlags::HOST_COHERENT, 75 | )?; 76 | 77 | let descriptor_set = plate::DescriptorAllocator::new(&device) 78 | .add_buffer_binding(0, plate::DescriptorType::UNIFORM_BUFFER, &ubo) 79 | .allocate(&set_layout, &descriptor_pool)?; 80 | 81 | let mut ubo = ubo.map()?; 82 | 83 | let fence = plate::Fence::new(&device, plate::FenceFlags::SIGNALED)?; 84 | let acquire_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 85 | let present_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 86 | 87 | let mut rot = 0.0; 88 | event_loop.run(move |event, _, control_flow| { 89 | *control_flow = winit::event_loop::ControlFlow::Poll; 90 | match event { 91 | winit::event::Event::WindowEvent { event, window_id } if window_id == window.id() => { 92 | match event { 93 | winit::event::WindowEvent::CloseRequested => { 94 | *control_flow = winit::event_loop::ControlFlow::Exit 95 | } 96 | winit::event::WindowEvent::Resized(_) => e.recreate().unwrap(), 97 | _ => (), 98 | } 99 | } 100 | 101 | winit::event::Event::MainEventsCleared => window.request_redraw(), 102 | winit::event::Event::RedrawRequested(window_id) if window_id == window.id() => { 103 | rot += 0.01; 104 | 105 | fence.wait().unwrap(); 106 | fence.reset().unwrap(); 107 | 108 | let (i, _) = e.swapchain.next_image(&acquire_sem).unwrap(); 109 | 110 | ubo.write(&[Ubo { model: glam::Mat4::from_rotation_z(rot) }]); 111 | 112 | cmd_buffer.record(plate::CommandBufferUsageFlags::empty(), || { 113 | e.render_pass.begin(&cmd_buffer, &e.framebuffers[i as usize]); 114 | 115 | pipeline.bind(&cmd_buffer, e.swapchain.extent()); 116 | vert_buffer.bind(&cmd_buffer); 117 | index_buffer.bind(&cmd_buffer); 118 | descriptor_set.bind(&cmd_buffer, &pipeline, 0, &[]).unwrap(); 119 | 120 | cmd_buffer.draw_indexed(indices.len() as u32, 1, 0, 0, 0); 121 | e.render_pass.end(&cmd_buffer); 122 | }).unwrap(); 123 | 124 | device.queue_submit( 125 | &cmd_buffer, 126 | plate::PipelineStage::COLOR_ATTACHMENT_OUTPUT, 127 | Some(&acquire_sem), 128 | Some(&present_sem), 129 | Some(&fence), 130 | ).unwrap(); 131 | 132 | e.swapchain.present(i, &present_sem).unwrap(); 133 | } 134 | 135 | winit::event::Event::LoopDestroyed => device.wait_idle().unwrap(), 136 | _ => (), 137 | } 138 | }) 139 | } 140 | -------------------------------------------------------------------------------- /examples/vert_buffer.rs: -------------------------------------------------------------------------------- 1 | use plate::{VertexDescription, plate_macros}; 2 | 3 | #[repr(C)] 4 | #[derive(plate_macros::Vertex)] 5 | struct Vert { 6 | #[vertex(loc = 0, format = "R32G32_SFLOAT")] 7 | pos: glam::Vec2, 8 | #[vertex(loc = 1, format = "R32G32B32_SFLOAT")] 9 | color: glam::Vec3, 10 | } 11 | 12 | fn main() -> Result<(), Box> { 13 | let event_loop = winit::event_loop::EventLoop::new(); 14 | let window = winit::window::WindowBuilder::new().build(&event_loop)?; 15 | 16 | let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 17 | let mut e = examples::App::new(&device, &window)?; 18 | let pipeline = plate::pipeline::Pipeline::new( 19 | &device, 20 | &e.render_pass, 21 | vk_shader_macros::include_glsl!("shaders/vert_buffer/shader.vert"), 22 | vk_shader_macros::include_glsl!("shaders/vert_buffer/shader.frag"), 23 | &plate::PipelineParameters { 24 | vertex_binding_descriptions: Vert::binding_descriptions(), 25 | vertex_attribute_descriptions: Vert::attribute_descriptions(), 26 | ..Default::default() 27 | }, 28 | )?; 29 | 30 | let cmd_pool = plate::CommandPool::new(&device)?; 31 | let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 32 | 33 | let vertices = vec![ 34 | Vert { pos: glam::vec2(0.0, -0.5), color: glam::vec3(1.0, 0.0, 0.0) }, 35 | Vert { pos: glam::vec2(0.5, 0.5), color: glam::vec3(0.0, 1.0, 0.0) }, 36 | Vert { pos: glam::vec2(-0.5, 0.5), color: glam::vec3(0.0, 0.0, 1.0) }, 37 | ]; 38 | let vert_buffer = plate::VertexBuffer::new(&device, &vertices, &cmd_pool)?; 39 | 40 | let fence = plate::Fence::new(&device, plate::FenceFlags::SIGNALED)?; 41 | let acquire_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 42 | let present_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 43 | 44 | event_loop.run(move |event, _, control_flow| { 45 | *control_flow = winit::event_loop::ControlFlow::Poll; 46 | match event { 47 | winit::event::Event::WindowEvent { event, window_id } if window_id == window.id() => { 48 | match event { 49 | winit::event::WindowEvent::CloseRequested => { 50 | *control_flow = winit::event_loop::ControlFlow::Exit 51 | } 52 | winit::event::WindowEvent::Resized(_) => e.recreate().unwrap(), 53 | _ => (), 54 | } 55 | } 56 | 57 | winit::event::Event::MainEventsCleared => window.request_redraw(), 58 | winit::event::Event::RedrawRequested(window_id) if window_id == window.id() => { 59 | fence.wait().unwrap(); 60 | fence.reset().unwrap(); 61 | 62 | let (i, _) = e.swapchain.next_image(&acquire_sem).unwrap(); 63 | 64 | cmd_buffer.record(plate::CommandBufferUsageFlags::empty(), || { 65 | e.render_pass.begin(&cmd_buffer, &e.framebuffers[i as usize]); 66 | pipeline.bind(&cmd_buffer, e.swapchain.extent()); 67 | vert_buffer.bind(&cmd_buffer); 68 | cmd_buffer.draw(vertices.len() as u32, 1, 0, 0); 69 | e.render_pass.end(&cmd_buffer); 70 | }).unwrap(); 71 | 72 | device.queue_submit( 73 | &cmd_buffer, 74 | plate::PipelineStage::COLOR_ATTACHMENT_OUTPUT, 75 | Some(&acquire_sem), 76 | Some(&present_sem), 77 | Some(&fence), 78 | ).unwrap(); 79 | 80 | e.swapchain.present(i, &present_sem).unwrap(); 81 | } 82 | 83 | winit::event::Event::LoopDestroyed => device.wait_idle().unwrap(), 84 | _ => (), 85 | } 86 | }) 87 | } 88 | -------------------------------------------------------------------------------- /plate-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plate-macros" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = "Macros to use with the plate crate" 6 | repository = "https://github.com/pcasotti/plate" 7 | license = "MIT" 8 | readme = "../README.md" 9 | keywords = ["graphics", "rendering", "vulkan"] 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [dependencies] 15 | syn = "1.0" 16 | quote = "1.0" 17 | -------------------------------------------------------------------------------- /plate-macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | use quote::quote; 2 | 3 | #[proc_macro_derive(Vertex, attributes(vertex))] 4 | pub fn vert(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 5 | let ast = syn::parse_macro_input!(input as syn::DeriveInput); 6 | let ident = &ast.ident; 7 | 8 | let fields = if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data { 9 | if let syn::Fields::Named(syn::FieldsNamed { named, .. }) = fields { 10 | named 11 | } else { unimplemented!("unnamed field") } 12 | } else { unimplemented!("not struct") }; 13 | 14 | let attributed = fields.iter() 15 | .filter_map(|f| { 16 | let attr = f.attrs.iter() 17 | .find(|a| { 18 | a.path.segments.iter() 19 | .find(|s| s.ident == "vertex") 20 | .is_some() 21 | }); 22 | 23 | match attr { 24 | Some(a) => Some((f, a)), 25 | None => None, 26 | } 27 | }); 28 | 29 | let attribute_descriptions = attributed 30 | .map(|(f, attr)| { 31 | let ident = f.ident.as_ref().unwrap(); 32 | 33 | let meta = attr.parse_meta().unwrap(); 34 | let location_nv = if let syn::Meta::List(p) = &meta { 35 | p.nested.iter() 36 | .find_map(|nm| { 37 | if let syn::NestedMeta::Meta(m) = nm { 38 | if let syn::Meta::NameValue(nv) = m { 39 | match nv.path.segments.iter().any(|s| s.ident == "loc") { 40 | true => Some(nv), 41 | false => None, 42 | } 43 | } else { unimplemented!("not namevalue") } 44 | } else { unimplemented!("not nested meta") } 45 | }) 46 | .expect("no attr?") 47 | } else { unreachable!() }; 48 | let location = if let syn::Lit::Int(i) = &location_nv.lit { 49 | i 50 | } else { unimplemented!("not int") }; 51 | 52 | let format_nv = if let syn::Meta::List(p) = &meta { 53 | p.nested.iter() 54 | .find_map(|nm| { 55 | if let syn::NestedMeta::Meta(m) = nm { 56 | if let syn::Meta::NameValue(nv) = m { 57 | match nv.path.segments.iter().any(|s| s.ident == "format") { 58 | true => Some(nv), 59 | false => None, 60 | } 61 | } else { unimplemented!("not namevalue") } 62 | } else { unimplemented!("not nested meta") } 63 | }) 64 | .expect("no attr?") 65 | } else { unreachable!() }; 66 | let format = if let syn::Lit::Str(s) = &format_nv.lit { 67 | quote::format_ident!("{}", s.value()) 68 | } else { unimplemented!("not str") }; 69 | 70 | quote! { 71 | plate::VertexAttributeDescription::new(0, #location, plate::memoffset::offset_of!(Self, #ident) as u32, plate::Format::#format) 72 | } 73 | }); 74 | 75 | quote! { 76 | impl plate::VertexDescription for #ident { 77 | fn binding_descriptions() -> Vec { 78 | vec![ 79 | plate::VertexBindingDescription::new(0, std::mem::size_of::() as u32, plate::InputRate::VERTEX) 80 | ] 81 | } 82 | 83 | fn attribute_descriptions() -> Vec { 84 | vec![ 85 | #(#attribute_descriptions),* 86 | ] 87 | } 88 | } 89 | }.into() 90 | } 91 | 92 | #[cfg(test)] 93 | mod tests { 94 | #[test] 95 | fn it_works() { 96 | let result = 2 + 2; 97 | assert_eq!(result, 4); 98 | } 99 | 100 | //test not struct 101 | //unnamed field 102 | //non attributed field 103 | //wrong attribute field 104 | //other macros attributes 105 | } 106 | -------------------------------------------------------------------------------- /plate/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "plate" 3 | version = "0.5.0" 4 | edition = "2021" 5 | description = "Rust library for writing simpler Vulkan code" 6 | repository = "https://github.com/pcasotti/plate" 7 | license = "MIT" 8 | readme = "../README.md" 9 | keywords = ["graphics", "rendering", "vulkan"] 10 | 11 | [features] 12 | default = ["macros"] 13 | macros = ["dep:plate-macros", "dep:memoffset"] 14 | 15 | [dependencies] 16 | ash = { version = "0.37", features = ["linked"] } 17 | winit = "0.27" 18 | ash-window = "0.11" 19 | vk-shader-macros = "0.2" 20 | thiserror = "1.0" 21 | bitflags = "1.3" 22 | memoffset = { version = "0.6", optional = true } 23 | plate-macros = { version = "0.1", path = "../plate-macros", optional = true } 24 | -------------------------------------------------------------------------------- /plate/src/buffer.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi, marker, mem, sync::Arc}; 2 | 3 | use ash::vk; 4 | 5 | use crate::{Device, PipelineStage, command::*, Error, MemoryPropertyFlags}; 6 | 7 | pub use vk::BufferUsageFlags as BufferUsageFlags; 8 | pub use vk::SharingMode as SharingMode; 9 | 10 | /// A struct to hold a vertex buffer. 11 | pub struct VertexBuffer(Buffer); 12 | 13 | unsafe impl Send for VertexBuffer {} 14 | unsafe impl Sync for VertexBuffer {} 15 | 16 | impl VertexBuffer { 17 | /// Creates a new VertexBuffer with data from a slice. 18 | /// 19 | /// # Examples 20 | /// 21 | /// ```no_run 22 | /// struct Vertex(f32); 23 | /// # let event_loop = winit::event_loop::EventLoop::new(); 24 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 25 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 26 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 27 | /// let vertices = [Vertex(0.0), Vertex(1.0)]; 28 | /// let vertex_buffer = plate::VertexBuffer::new(&device, &vertices, &cmd_pool)?; 29 | /// # Ok::<(), Box>(()) 30 | /// ``` 31 | pub fn new(device: &Arc, data: &[T], cmd_pool: &CommandPool) -> Result { 32 | let size = (mem::size_of::() * data.len()) as u64; 33 | let staging = Buffer::new( 34 | device, 35 | data.len(), 36 | vk::BufferUsageFlags::TRANSFER_SRC, 37 | vk::SharingMode::EXCLUSIVE, 38 | vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, 39 | )?; 40 | 41 | let mut mapped = staging.map()?; 42 | mapped.write(data); 43 | let staging = mapped.unmap(); 44 | 45 | let buffer = Buffer::new( 46 | device, 47 | data.len(), 48 | vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::VERTEX_BUFFER, 49 | vk::SharingMode::EXCLUSIVE, 50 | vk::MemoryPropertyFlags::DEVICE_LOCAL, 51 | )?; 52 | 53 | staging.copy_to(&buffer, size, cmd_pool)?; 54 | 55 | Ok(Self(buffer)) 56 | } 57 | 58 | /// Binds the VertexBuffer. 59 | /// 60 | /// To be used when recording a command buffer, should be used after binding the pipeline. The 61 | /// bound pipeline should be created with the proper attribute and binding descriptions from 62 | /// this struct type T. 63 | /// 64 | /// # Examples 65 | /// 66 | /// ```no_run 67 | /// # struct Vertex(f32); 68 | /// # let event_loop = winit::event_loop::EventLoop::new(); 69 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 70 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 71 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 72 | /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 73 | /// # let vertices = [Vertex(0.0), Vertex(1.0)]; 74 | /// let vertex_buffer = plate::VertexBuffer::new(&device, &vertices, &cmd_pool)?; 75 | /// // cmd_buffer.record(.., || { 76 | /// // pipeline.bind(..); 77 | /// vertex_buffer.bind(&cmd_buffer); 78 | /// // })?; 79 | /// # Ok::<(), Box>(()) 80 | /// ``` 81 | pub fn bind(&self, command_buffer: &CommandBuffer) { 82 | let buffers = [self.0.buffer]; 83 | unsafe { self.0.device.cmd_bind_vertex_buffers(**command_buffer, 0, &buffers, &[0]) }; 84 | } 85 | } 86 | 87 | /// A struct to hold a index buffer. 88 | pub struct IndexBuffer(Buffer); 89 | 90 | unsafe impl Send for IndexBuffer {} 91 | unsafe impl Sync for IndexBuffer {} 92 | 93 | impl IndexBuffer { 94 | /// Creates a new IndexBuffer with data from a slice. 95 | /// 96 | /// # Examples 97 | /// 98 | /// ```no_run 99 | /// # let event_loop = winit::event_loop::EventLoop::new(); 100 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 101 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 102 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 103 | /// let indices = [0, 1, 2]; 104 | /// let index_buffer = plate::IndexBuffer::new(&device, &indices, &cmd_pool)?; 105 | /// # Ok::<(), Box>(()) 106 | /// ``` 107 | pub fn new(device: &Arc, data: &[u32], cmd_pool: &CommandPool) -> Result { 108 | let size = (mem::size_of::() * data.len()) as u64; 109 | let staging = Buffer::new( 110 | device, 111 | data.len(), 112 | vk::BufferUsageFlags::TRANSFER_SRC, 113 | vk::SharingMode::EXCLUSIVE, 114 | vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, 115 | )?; 116 | 117 | let mut mapped = staging.map()?; 118 | mapped.write(data); 119 | let staging = mapped.unmap(); 120 | 121 | let buffer = Buffer::new( 122 | device, 123 | data.len(), 124 | vk::BufferUsageFlags::TRANSFER_DST | vk::BufferUsageFlags::INDEX_BUFFER, 125 | vk::SharingMode::EXCLUSIVE, 126 | vk::MemoryPropertyFlags::DEVICE_LOCAL, 127 | )?; 128 | 129 | staging.copy_to(&buffer, size, cmd_pool)?; 130 | 131 | Ok(Self(buffer)) 132 | } 133 | 134 | /// Binds the IndexBuffer. 135 | /// 136 | /// To be used when recording a command buffer, should be used after binding the pipeline and a 137 | /// vertex buffer. Should also be used before a [`draw_indexed()`](crate::CommandBuffer::draw_indexed()) call, otherwise the draw call 138 | /// will not make use of the index buffer. 139 | /// 140 | /// # Examples 141 | /// 142 | /// ```no_run 143 | /// # let event_loop = winit::event_loop::EventLoop::new(); 144 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 145 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 146 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 147 | /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 148 | /// # let indices = [0, 1, 2]; 149 | /// let index_buffer = plate::IndexBuffer::new(&device, &indices, &cmd_pool)?; 150 | /// // cmd_buffer.record(.., || { 151 | /// // pipeline.bind(..); 152 | /// // vertex_buffer.bind(..); 153 | /// index_buffer.bind(&cmd_buffer); 154 | /// // cmd_buffer.draw_indexed(..); 155 | /// // })?; 156 | /// # Ok::<(), Box>(()) 157 | /// ``` 158 | pub fn bind(&self, command_buffer: &CommandBuffer) { 159 | unsafe { 160 | self.0.device.cmd_bind_index_buffer( 161 | **command_buffer, 162 | self.0.buffer, 163 | 0, 164 | vk::IndexType::UINT32, 165 | ) 166 | }; 167 | } 168 | } 169 | 170 | /// Represents a buffer with memory mapped in the host, capable of performing write operations. 171 | pub struct MappedBuffer { 172 | buffer: Buffer, 173 | mapped: *mut ffi::c_void, 174 | } 175 | 176 | unsafe impl Send for MappedBuffer {} 177 | unsafe impl Sync for MappedBuffer {} 178 | 179 | impl MappedBuffer { 180 | /// Unmaps the memory from the host and returns the inner [`Buffer`]. 181 | /// 182 | /// # Example 183 | /// 184 | /// ```no_run 185 | /// # let event_loop = winit::event_loop::EventLoop::new(); 186 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 187 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 188 | /// # let buffer: plate::Buffer = plate::Buffer::new( // .. 189 | /// # &device, 190 | /// # 2, 191 | /// # plate::BufferUsageFlags::UNIFORM_BUFFER, 192 | /// # plate::SharingMode::EXCLUSIVE, 193 | /// # plate::MemoryPropertyFlags::HOST_VISIBLE | plate::MemoryPropertyFlags::HOST_COHERENT, 194 | /// # )?; 195 | /// // Create a MappedBuffer by mapping an existing Buffer 196 | /// let mapped = buffer.map()?; 197 | /// // Unmap the memory from the host 198 | /// let buffer = mapped.unmap(); 199 | /// # Ok::<(), Box>(()) 200 | /// ``` 201 | pub fn unmap(self) -> Buffer { 202 | unsafe { self.buffer.device.unmap_memory(self.buffer.mem) }; 203 | self.buffer 204 | } 205 | 206 | /// Writes data from a slice in the mapped memory. 207 | /// 208 | /// The given slice must not have length greater than the `instance_count` parameter provided 209 | /// during the maped Buffer creation. 210 | /// 211 | /// # Panics 212 | /// 213 | /// Panics if the index is larger than the buffer capacity. 214 | /// 215 | /// # Example 216 | /// 217 | /// ```no_run 218 | /// # let event_loop = winit::event_loop::EventLoop::new(); 219 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 220 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 221 | /// # let buffer: plate::Buffer = plate::Buffer::new( // .. 222 | /// # &device, 223 | /// # 2, 224 | /// # plate::BufferUsageFlags::UNIFORM_BUFFER, 225 | /// # plate::SharingMode::EXCLUSIVE, 226 | /// # plate::MemoryPropertyFlags::HOST_VISIBLE | plate::MemoryPropertyFlags::HOST_COHERENT, 227 | /// # )?; 228 | /// let data = [1, 2, 3]; 229 | /// let mut mapped = buffer.map()?; 230 | /// mapped.write(&data); 231 | /// mapped.unmap(); 232 | /// # Ok::<(), Box>(()) 233 | /// ``` 234 | pub fn write(&mut self, data: &[T]) { 235 | self.write_index(data, 0); 236 | } 237 | 238 | /// Writes data from a slice into a specific index from the mapped memory. 239 | /// 240 | /// The index + the legth of the data provided must be within range of the 241 | /// `instance_count` para parameter provided during the mapepd Buffer creation. 242 | /// 243 | /// # Panics 244 | /// 245 | /// Panics if the length of the data + the index is larger than the buffer capacity. 246 | /// 247 | /// # Example 248 | /// 249 | /// ```no_run 250 | /// # let event_loop = winit::event_loop::EventLoop::new(); 251 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 252 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 253 | /// let buffer: plate::Buffer = plate::Buffer::new(&device, 4, // .. 254 | /// # plate::BufferUsageFlags::UNIFORM_BUFFER, 255 | /// # plate::SharingMode::EXCLUSIVE, 256 | /// # plate::MemoryPropertyFlags::HOST_VISIBLE | plate::MemoryPropertyFlags::HOST_COHERENT, 257 | /// # )?; 258 | /// // Map an existing buffer created with capacity for 4 instances 259 | /// let mut mapped = buffer.map()?; 260 | /// // Write the contents of `data` to the mapped memory, starting from the index 1 261 | /// let data = [1, 2, 3]; 262 | /// mapped.write_index(&data, 1); 263 | /// mapped.unmap(); 264 | /// # Ok::<(), Box>(()) 265 | /// ``` 266 | pub fn write_index(&mut self, data: &[T], index: usize) { 267 | assert!(data.len()+index <= self.buffer.instance_count); 268 | 269 | data.iter() 270 | .enumerate() 271 | .for_each(|(i, d)| { 272 | unsafe { 273 | (d as *const T as *const u8) 274 | .copy_to_nonoverlapping((self.mapped as *mut u8).offset(((i+index) * self.buffer.alignment_size) as isize), mem::size_of::()) 275 | } 276 | }); 277 | } 278 | 279 | /// Flushes a this Buffer mapped memory. 280 | /// 281 | /// # Example 282 | /// 283 | /// ```no_run 284 | /// # let event_loop = winit::event_loop::EventLoop::new(); 285 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 286 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 287 | /// let buffer: plate::Buffer = plate::Buffer::new(&device, 4, // .. 288 | /// # plate::BufferUsageFlags::UNIFORM_BUFFER, 289 | /// # plate::SharingMode::EXCLUSIVE, 290 | /// # plate::MemoryPropertyFlags::HOST_VISIBLE | plate::MemoryPropertyFlags::HOST_COHERENT, 291 | /// # )?; 292 | /// let mut mapped = buffer.map()?; 293 | /// mapped.flush()?; 294 | /// # Ok::<(), Box>(()) 295 | /// ``` 296 | pub fn flush(&self) -> Result<(), Error> { self.flush_index(0, self.buffer.instance_count) } 297 | 298 | /// Flushes a range of this Buffer mapped memory. 299 | /// 300 | /// # Panics 301 | /// 302 | /// Panics if the offset + size is greater than the Buffer capacity. 303 | /// 304 | /// # Example 305 | /// 306 | /// ```no_run 307 | /// # let event_loop = winit::event_loop::EventLoop::new(); 308 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 309 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 310 | /// let buffer: plate::Buffer = plate::Buffer::new(&device, 4, // .. 311 | /// # plate::BufferUsageFlags::UNIFORM_BUFFER, 312 | /// # plate::SharingMode::EXCLUSIVE, 313 | /// # plate::MemoryPropertyFlags::HOST_VISIBLE | plate::MemoryPropertyFlags::HOST_COHERENT, 314 | /// # )?; 315 | /// let mut mapped = buffer.map()?; 316 | /// mapped.flush_index(2, 1)?; 317 | /// # Ok::<(), Box>(()) 318 | /// ``` 319 | pub fn flush_index(&self, offset: usize, size: usize) -> Result<(), Error> { 320 | assert!(offset+size <= self.buffer.instance_count); 321 | 322 | let range = vk::MappedMemoryRange::builder() 323 | .memory(self.buffer.mem) 324 | .offset((self.buffer.alignment_size * offset) as u64) 325 | .size((self.buffer.alignment_size * size) as u64); 326 | 327 | Ok(unsafe { self.buffer.device.flush_mapped_memory_ranges(&[*range])? }) 328 | } 329 | } 330 | 331 | /// A struct containing a vk::Buffer. 332 | pub struct Buffer { 333 | device: Arc, 334 | buffer: vk::Buffer, 335 | mem: vk::DeviceMemory, 336 | pub(crate) instance_count: usize, 337 | pub(crate) alignment_size: usize, 338 | 339 | marker: marker::PhantomData, 340 | } 341 | 342 | unsafe impl Send for Buffer {} 343 | unsafe impl Sync for Buffer {} 344 | 345 | impl Drop for Buffer { 346 | fn drop(&mut self) { 347 | unsafe { 348 | self.device.destroy_buffer(self.buffer, None); 349 | self.device.free_memory(self.mem, None); 350 | } 351 | } 352 | } 353 | 354 | impl Buffer { 355 | /// Creates a Buffer\. 356 | /// 357 | /// # Examples 358 | /// 359 | /// ```no_run 360 | /// # let event_loop = winit::event_loop::EventLoop::new(); 361 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 362 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 363 | /// // Create a uniform buffer with capacity of 2 instances 364 | /// let buffer: plate::Buffer = plate::Buffer::new( 365 | /// &device, 366 | /// 2, 367 | /// plate::BufferUsageFlags::UNIFORM_BUFFER, 368 | /// plate::SharingMode::EXCLUSIVE, 369 | /// plate::MemoryPropertyFlags::HOST_VISIBLE | plate::MemoryPropertyFlags::HOST_COHERENT, 370 | /// )?; 371 | /// # Ok::<(), Box>(()) 372 | /// ``` 373 | pub fn new( 374 | device: &Arc, 375 | instance_count: usize, 376 | usage: BufferUsageFlags, 377 | sharing_mode: SharingMode, 378 | memory_properties: MemoryPropertyFlags, 379 | ) -> Result { 380 | let alignment_size = alignment::(device, usage); 381 | let size = alignment_size * instance_count; 382 | 383 | let buffer_info = vk::BufferCreateInfo::builder() 384 | .size(size as u64) 385 | .usage(usage) 386 | .sharing_mode(sharing_mode); 387 | 388 | let buffer = unsafe { device.create_buffer(&buffer_info, None)? }; 389 | 390 | let mem_requirements = unsafe { device.get_buffer_memory_requirements(buffer) }; 391 | let mem_type_index = device.memory_type_index(mem_requirements, memory_properties)?; 392 | 393 | let alloc_info = vk::MemoryAllocateInfo::builder() 394 | .allocation_size(mem_requirements.size) 395 | .memory_type_index(mem_type_index as u32); 396 | 397 | let mem = unsafe { device.allocate_memory(&alloc_info, None)? }; 398 | unsafe { device.bind_buffer_memory(buffer, mem, 0)? }; 399 | 400 | Ok(Self { 401 | device: Arc::clone(&device), 402 | buffer, 403 | mem, 404 | instance_count, 405 | alignment_size, 406 | 407 | marker: marker::PhantomData, 408 | }) 409 | } 410 | 411 | /// Maps the memory the host and returns a [`MappedBuffer`]. 412 | /// 413 | /// # Example 414 | /// 415 | /// ```no_run 416 | /// # let event_loop = winit::event_loop::EventLoop::new(); 417 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 418 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 419 | /// let buffer: plate::Buffer = plate::Buffer::new( // .. 420 | /// # &device, 421 | /// # 2, 422 | /// # plate::BufferUsageFlags::UNIFORM_BUFFER, 423 | /// # plate::SharingMode::EXCLUSIVE, 424 | /// # plate::MemoryPropertyFlags::HOST_VISIBLE | plate::MemoryPropertyFlags::HOST_COHERENT, 425 | /// # )?; 426 | /// let mapped = buffer.map()?; 427 | /// # Ok::<(), Box>(()) 428 | /// ``` 429 | pub fn map(self) -> Result, Error> { 430 | let mapped = unsafe { 431 | self.device.map_memory( 432 | self.mem, 433 | 0, 434 | vk::WHOLE_SIZE, 435 | vk::MemoryMapFlags::empty(), 436 | )? 437 | }; 438 | 439 | Ok(MappedBuffer { 440 | buffer: self, 441 | mapped, 442 | }) 443 | } 444 | 445 | pub(crate) fn copy_to(&self, target: &Buffer, size: vk::DeviceSize, cmd_pool: &CommandPool) -> Result<(), Error> { 446 | let command_buffer = cmd_pool.alloc_cmd_buffer(CommandBufferLevel::PRIMARY)?; 447 | command_buffer.record(CommandBufferUsageFlags::ONE_TIME_SUBMIT, || { 448 | let regions = [*vk::BufferCopy::builder().size(size)]; 449 | unsafe { 450 | self.device.cmd_copy_buffer( 451 | *command_buffer, 452 | self.buffer, 453 | target.buffer, 454 | ®ions, 455 | ) 456 | }; 457 | })?; 458 | 459 | self.device.queue_submit(&command_buffer, PipelineStage::empty(), None, None, None)?; 460 | Ok(unsafe { self.device.queue_wait_idle(self.device.queue.queue)? }) 461 | } 462 | 463 | pub(crate) fn copy_to_image(&self, image: vk::Image, width: u32, height: u32, cmd_pool: &CommandPool) -> Result<(), Error> { 464 | let cmd_buffer = cmd_pool.alloc_cmd_buffer(CommandBufferLevel::PRIMARY)?; 465 | cmd_buffer.record(CommandBufferUsageFlags::ONE_TIME_SUBMIT, || { 466 | let region = vk::BufferImageCopy::builder() 467 | .buffer_offset(0) 468 | .buffer_row_length(0) 469 | .buffer_image_height(0) 470 | .image_subresource(vk::ImageSubresourceLayers { 471 | aspect_mask: vk::ImageAspectFlags::COLOR, 472 | mip_level: 0, 473 | base_array_layer: 0, 474 | layer_count: 1, 475 | }) 476 | .image_offset(vk::Offset3D { x: 0, y: 0, z: 0 }) 477 | .image_extent(vk::Extent3D { 478 | width, 479 | height, 480 | depth: 1, 481 | }); 482 | 483 | unsafe { self.device.cmd_copy_buffer_to_image(*cmd_buffer, self.buffer, image, vk::ImageLayout::TRANSFER_DST_OPTIMAL, &[*region]) }; 484 | })?; 485 | 486 | self.device.queue_submit(&cmd_buffer, PipelineStage::empty(), None, None, None)?; 487 | Ok(unsafe { self.device.queue_wait_idle(self.device.queue.queue)? }) 488 | } 489 | 490 | pub(crate) fn descriptor_info(&self, offset: usize, range: usize) -> vk::DescriptorBufferInfo { 491 | *vk::DescriptorBufferInfo::builder() 492 | .buffer(self.buffer) 493 | .offset((self.alignment_size * offset) as u64) 494 | .range((self.alignment_size * range) as u64) 495 | } 496 | } 497 | 498 | fn alignment(device: &Arc, usage: BufferUsageFlags) -> usize { 499 | let limits = unsafe { device.instance.get_physical_device_properties(device.physical_device).limits }; 500 | let min_offset = if usage.contains(BufferUsageFlags::UNIFORM_BUFFER) { 501 | limits.min_uniform_buffer_offset_alignment.max(limits.non_coherent_atom_size) 502 | } else { 1 } as usize; 503 | 504 | let instance_size = mem::size_of::(); 505 | if min_offset > 0 { 506 | (instance_size + min_offset - 1) & !(min_offset - 1) 507 | } else { instance_size } 508 | } 509 | -------------------------------------------------------------------------------- /plate/src/command.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ash::vk; 4 | 5 | use crate::{Device, Error}; 6 | 7 | pub use vk::CommandBufferLevel as CommandBufferLevel; 8 | pub use vk::CommandBufferUsageFlags as CommandBufferUsageFlags; 9 | 10 | /// Holds a [`vk::CommandPool`], used to allocate [`CommandBuffers`](CommandBuffer). 11 | pub struct CommandPool { 12 | device: Arc, 13 | cmd_pool: vk::CommandPool, 14 | } 15 | 16 | impl std::ops::Deref for CommandPool { 17 | type Target = vk::CommandPool; 18 | 19 | fn deref(&self) -> &Self::Target { 20 | &self.cmd_pool 21 | } 22 | } 23 | 24 | impl Drop for CommandPool { 25 | fn drop(&mut self) { 26 | unsafe { 27 | self.device.destroy_command_pool(self.cmd_pool, None); 28 | } 29 | } 30 | } 31 | 32 | impl CommandPool { 33 | /// Creates a CommandPool. 34 | /// 35 | /// # Examples 36 | /// 37 | /// ```no_run 38 | /// # let event_loop = winit::event_loop::EventLoop::new(); 39 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 40 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 41 | /// let cmd_pool = plate::CommandPool::new(&device)?; 42 | /// # Ok::<(), Box>(()) 43 | /// ``` 44 | pub fn new(device: &Arc) -> Result { 45 | let pool_info = vk::CommandPoolCreateInfo::builder() 46 | .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER) 47 | .queue_family_index(device.queue.family); 48 | 49 | let cmd_pool = unsafe { device.create_command_pool(&pool_info, None)? }; 50 | 51 | Ok(Self { 52 | device: Arc::clone(&device), 53 | cmd_pool, 54 | }) 55 | } 56 | 57 | /// Allocates CommandBuffers. 58 | /// 59 | /// # Examples 60 | /// 61 | /// ```no_run 62 | /// # let event_loop = winit::event_loop::EventLoop::new(); 63 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 64 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 65 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 66 | /// let cmd_buffers = cmd_pool.alloc_cmd_buffers(plate::CommandBufferLevel::PRIMARY, 2)?; 67 | /// # Ok::<(), Box>(()) 68 | /// ``` 69 | pub fn alloc_cmd_buffers(&self, level: CommandBufferLevel, cmd_buffer_count: u32) -> Result, Error> { 70 | let alloc_info = vk::CommandBufferAllocateInfo::builder() 71 | .command_pool(self.cmd_pool) 72 | .level(level) 73 | .command_buffer_count(cmd_buffer_count); 74 | 75 | let cmd_buffers = unsafe { self.device.allocate_command_buffers(&alloc_info)? }; 76 | Ok(cmd_buffers.into_iter() 77 | .map(|cmd_buffer| CommandBuffer { device: Arc::clone(&self.device), cmd_buffer }) 78 | .collect()) 79 | } 80 | 81 | /// Allocates a single CommandBuffer. 82 | /// 83 | /// # Examples 84 | /// 85 | /// ```no_run 86 | /// # let event_loop = winit::event_loop::EventLoop::new(); 87 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 88 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 89 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 90 | /// let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 91 | /// # Ok::<(), Box>(()) 92 | /// ``` 93 | pub fn alloc_cmd_buffer(&self, level: CommandBufferLevel) -> Result { 94 | Ok(self.alloc_cmd_buffers(level, 1)?.swap_remove(0)) 95 | } 96 | } 97 | 98 | /// Used to send instructions to the GPU. 99 | pub struct CommandBuffer { 100 | device: Arc, 101 | cmd_buffer: vk::CommandBuffer, 102 | } 103 | 104 | impl std::ops::Deref for CommandBuffer { 105 | type Target = vk::CommandBuffer; 106 | 107 | fn deref(&self) -> &Self::Target { 108 | &self.cmd_buffer 109 | } 110 | } 111 | 112 | impl CommandBuffer { 113 | /// Records instructions in the given closure to this command buffer. 114 | /// 115 | /// Runs [`begin()`](Self::begin()), the closure then [`end()`](Self::end()). 116 | /// 117 | /// # Examples 118 | /// 119 | /// ```no_run 120 | /// # let event_loop = winit::event_loop::EventLoop::new(); 121 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 122 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 123 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 124 | /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 125 | /// cmd_buffer.record(plate::CommandBufferUsageFlags::empty(), || { 126 | /// // cmd_buffer.draw(..); 127 | /// })?; 128 | /// # Ok::<(), Box>(()) 129 | /// ``` 130 | pub fn record(&self, flags: CommandBufferUsageFlags, f: F) -> Result<(), Error> { 131 | self.begin(flags)?; 132 | f(); 133 | self.end()?; 134 | Ok(()) 135 | } 136 | 137 | /// Begin recording instructions to this command buffer. 138 | /// 139 | /// # Examples 140 | /// 141 | /// ```no_run 142 | /// # let event_loop = winit::event_loop::EventLoop::new(); 143 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 144 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 145 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 146 | /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 147 | /// cmd_buffer.begin(plate::CommandBufferUsageFlags::empty())?; 148 | /// // cmd_buffer.draw(..); 149 | /// # Ok::<(), Box>(()) 150 | /// ``` 151 | pub fn begin(&self, flags: CommandBufferUsageFlags) -> Result<(), Error> { 152 | let info = vk::CommandBufferBeginInfo::builder().flags(flags); 153 | unsafe { 154 | self.device.reset_command_buffer(self.cmd_buffer, vk::CommandBufferResetFlags::empty())?; 155 | self.device.begin_command_buffer(self.cmd_buffer, &info)?; 156 | } 157 | Ok(()) 158 | } 159 | 160 | /// Stop recording instructions to this command buffer. 161 | /// 162 | /// # Examples 163 | /// 164 | /// ```no_run 165 | /// # let event_loop = winit::event_loop::EventLoop::new(); 166 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 167 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 168 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 169 | /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 170 | /// // cmd_buffer.draw(..); 171 | /// cmd_buffer.end()?; 172 | /// # Ok::<(), Box>(()) 173 | /// ``` 174 | pub fn end(&self) -> Result<(), Error> { 175 | unsafe { self.device.end_command_buffer(self.cmd_buffer)? }; 176 | Ok(()) 177 | } 178 | 179 | /// Calls a [`draw`](ash::Device::cmd_draw()) command. 180 | /// 181 | /// To be used when recording a CommandBuffer. 182 | /// 183 | /// # Examples 184 | /// 185 | /// ```no_run 186 | /// # let event_loop = winit::event_loop::EventLoop::new(); 187 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 188 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 189 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 190 | /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 191 | /// cmd_buffer.record(plate::CommandBufferUsageFlags::empty(), || { 192 | /// cmd_buffer.draw(3, 1, 0, 0); 193 | /// })?; 194 | /// # Ok::<(), Box>(()) 195 | /// ``` 196 | pub fn draw(&self, vert_count: u32, instance_count: u32, first_vert: u32, first_instance: u32) { 197 | unsafe { self.device.cmd_draw(self.cmd_buffer, vert_count, instance_count, first_vert, first_instance) } 198 | } 199 | 200 | /// Calls a [`draw_indexed`](ash::Device::cmd_draw_indexed()) command. 201 | /// 202 | /// To be used when recording a CommandBuffer. 203 | /// 204 | /// # Examples 205 | /// 206 | /// ```no_run 207 | /// # let event_loop = winit::event_loop::EventLoop::new(); 208 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 209 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 210 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 211 | /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 212 | /// cmd_buffer.record(plate::CommandBufferUsageFlags::empty(), || { 213 | /// cmd_buffer.draw_indexed(3, 1, 0, 0, 0); 214 | /// })?; 215 | /// # Ok::<(), Box>(()) 216 | /// ``` 217 | pub fn draw_indexed(&self, index_count: u32, instance_count: u32, first_index: u32, vertex_offset: i32, first_instance: u32) { 218 | unsafe { self.device.cmd_draw_indexed(self.cmd_buffer, index_count, instance_count, first_index, vertex_offset, first_instance) } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /plate/src/debug.rs: -------------------------------------------------------------------------------- 1 | use std::ffi; 2 | 3 | use ash::{extensions::ext, vk}; 4 | 5 | use crate::Error; 6 | 7 | pub struct Debugger { 8 | debug_utils: ext::DebugUtils, 9 | debug_messenger: vk::DebugUtilsMessengerEXT, 10 | } 11 | 12 | impl Drop for Debugger { 13 | fn drop(&mut self) { 14 | unsafe { 15 | self.debug_utils 16 | .destroy_debug_utils_messenger(self.debug_messenger, None); 17 | } 18 | } 19 | } 20 | 21 | impl Debugger { 22 | pub fn new(entry: &ash::Entry, instance: &ash::Instance) -> Result { 23 | let debug_utils = ext::DebugUtils::new(entry, instance); 24 | 25 | let debug_messenger_info = debug_messenger_info(); 26 | 27 | let debug_messenger = 28 | unsafe { debug_utils.create_debug_utils_messenger(&debug_messenger_info, None)? }; 29 | 30 | Ok(Self { 31 | debug_utils, 32 | debug_messenger, 33 | }) 34 | } 35 | } 36 | 37 | pub fn debug_messenger_info() -> vk::DebugUtilsMessengerCreateInfoEXT { 38 | *vk::DebugUtilsMessengerCreateInfoEXT::builder() 39 | .message_severity( 40 | vk::DebugUtilsMessageSeverityFlagsEXT::WARNING 41 | | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR, 42 | ) 43 | .message_type( 44 | vk::DebugUtilsMessageTypeFlagsEXT::GENERAL 45 | | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION 46 | | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE, 47 | ) 48 | .pfn_user_callback(Some(debug_callback)) 49 | } 50 | 51 | unsafe extern "system" fn debug_callback( 52 | message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, 53 | message_type: vk::DebugUtilsMessageTypeFlagsEXT, 54 | p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT, 55 | _p_user_data: *mut ffi::c_void, 56 | ) -> vk::Bool32 { 57 | println!( 58 | "[Validation Layer][{:?}][{:?}] {:?}", 59 | message_severity, 60 | message_type, 61 | ffi::CStr::from_ptr((*p_callback_data).p_message) 62 | ); 63 | 64 | vk::FALSE 65 | } 66 | -------------------------------------------------------------------------------- /plate/src/descriptor.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ash::vk; 4 | 5 | use crate::{image::*, Buffer, CommandBuffer, Device, Error, Pipeline, ImageLayout}; 6 | 7 | pub use vk::DescriptorType; 8 | pub use vk::ShaderStageFlags as ShaderStage; 9 | 10 | /// Errors from the descriptor module. 11 | #[derive(thiserror::Error, Debug)] 12 | pub enum DescriptorError { 13 | /// The number of provided dynamic offsets is not equal the number of bound dynamic descriptors. 14 | #[error("The number of provided dynamic offsets is not equal the number of bound dynamic descriptors. Provided {actual} dynamic offsets, but expected {expected}")] 15 | DynamicOffsetOutOfBounds { 16 | actual: usize, 17 | expected: usize, 18 | }, 19 | } 20 | 21 | /// A Component for building a descriptor pool. 22 | /// 23 | /// Describes the amount of descriptors that can be allocated of a certain type. 24 | pub struct PoolSize { 25 | /// Type of the descriptor to be allocated 26 | pub ty: DescriptorType, 27 | /// Max ammount of descriptors of that type 28 | pub count: u32, 29 | } 30 | 31 | /// Implements the builder pattern for a [`DescriptorPool`]. 32 | /// 33 | /// # Example 34 | /// 35 | /// ```no_run 36 | /// # let event_loop = winit::event_loop::EventLoop::new(); 37 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 38 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 39 | /// // Create a DescriptorPool capable of allocating at most 2 DescriptorSets of type 40 | /// // UNIFORM_BUFFER or STORAGE_BUFFER 41 | /// let descriptor_pool = plate::DescriptorPool::builder() 42 | /// .add_size(plate::DescriptorType::UNIFORM_BUFFER, 2) 43 | /// .add_size(plate::DescriptorType::STORAGE_BUFFER, 2) 44 | /// .max_sets(2) 45 | /// .build(&device)?; 46 | /// # Ok::<(), Box>(()) 47 | /// ``` 48 | pub struct DescriptorPoolBuilder { 49 | sizes: Vec, 50 | max_sets: Option, 51 | } 52 | 53 | impl Default for DescriptorPoolBuilder { 54 | fn default() -> Self { 55 | Self { 56 | sizes: vec![], 57 | max_sets: None, 58 | } 59 | } 60 | } 61 | 62 | impl DescriptorPoolBuilder { 63 | /// Add a [`PoolSize`] component to the DescriptorPool. 64 | pub fn add_size(&mut self, ty: DescriptorType, count: u32) -> &mut Self { 65 | self.sizes.push(PoolSize { ty, count }); 66 | self 67 | } 68 | 69 | /// Set the maximum amount of DescriptorSets to be allocated from the DescriptorPool. 70 | pub fn max_sets(&mut self, max_sets: u32) -> &mut Self { 71 | self.max_sets = Some(max_sets); 72 | self 73 | } 74 | 75 | /// Builds A DescriptorPool from this builder. 76 | /// 77 | /// If the value of `max_sets` is not set, it will default to the sum of all available sets 78 | /// in the PoolSizes. 79 | pub fn build(&self, device: &Arc) -> Result { 80 | let max_sets = self 81 | .max_sets 82 | .unwrap_or(self.sizes.iter().map(|size| size.count).sum()); 83 | DescriptorPool::new(device, &self.sizes, max_sets) 84 | } 85 | } 86 | 87 | /// Holds a vk::DescriptorPool, used to allocate [`DescriptorSets`](DescriptorSet). 88 | pub struct DescriptorPool { 89 | device: Arc, 90 | pool: vk::DescriptorPool, 91 | } 92 | 93 | impl Drop for DescriptorPool { 94 | fn drop(&mut self) { 95 | unsafe { 96 | self.device.destroy_descriptor_pool(self.pool, None); 97 | } 98 | } 99 | } 100 | 101 | impl DescriptorPool { 102 | /// Creates a DescriptorPool from [`PoolSizes`](PoolSize) and `max_sets`. 103 | /// 104 | /// The `sizes` parameter represent the type of DescriptorSets to be allocated and `max_sets` 105 | /// is the maximum ammount of DescriptorSets to be allocated from this DescriptorPool. 106 | /// 107 | /// # Example 108 | /// 109 | /// ```no_run 110 | /// # let event_loop = winit::event_loop::EventLoop::new(); 111 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 112 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 113 | /// // Create a DescriptorPool capable of allocating 1 DescriptorSet of type UNIFORM_BUFFER 114 | /// let descriptor_pool = plate::DescriptorPool::new( 115 | /// &device, 116 | /// &[plate::PoolSize { 117 | /// ty: plate::DescriptorType::UNIFORM_BUFFER, 118 | /// count: 1, 119 | /// }], 120 | /// 1, 121 | /// )?; 122 | /// # Ok::<(), Box>(()) 123 | /// ``` 124 | pub fn new(device: &Arc, sizes: &[PoolSize], max_sets: u32) -> Result { 125 | let pool_sizes = sizes 126 | .iter() 127 | .map(|size| { 128 | *vk::DescriptorPoolSize::builder() 129 | .ty(size.ty) 130 | .descriptor_count(size.count) 131 | }) 132 | .collect::>(); 133 | 134 | let pool_info = vk::DescriptorPoolCreateInfo::builder() 135 | .pool_sizes(&pool_sizes) 136 | .max_sets(max_sets); 137 | 138 | let pool = unsafe { device.create_descriptor_pool(&pool_info, None)? }; 139 | 140 | Ok(Self { 141 | device: Arc::clone(&device), 142 | pool, 143 | }) 144 | } 145 | 146 | /// Returns a [`DescriptorPoolBuilder`] if you prefer to use the builder pattern. 147 | pub fn builder() -> DescriptorPoolBuilder { 148 | DescriptorPoolBuilder::default() 149 | } 150 | } 151 | 152 | /// Represents a binding from a descriptor set where the data will be accessible from the shader. 153 | pub struct LayoutBinding { 154 | /// The actual binding to access from the shader. 155 | pub binding: u32, 156 | /// The type of the descriptor. Must match one of the types in the [`DescriptorPool`]. 157 | pub ty: DescriptorType, 158 | /// The stage in which the data will be acessible. 159 | pub stage: ShaderStage, 160 | /// The ammount of descriptors to allocate. Must not exceed the maximum amount of that type 161 | /// described in the [`DescriptorPool`]. 162 | pub count: u32, 163 | } 164 | 165 | /// A DescriptorSetLayout indicates what descriptor types will be allocated from a [`DescriptorPool`]. 166 | pub struct DescriptorSetLayout { 167 | device: Arc, 168 | pub(crate) layout: vk::DescriptorSetLayout, 169 | } 170 | 171 | impl Drop for DescriptorSetLayout { 172 | fn drop(&mut self) { 173 | unsafe { 174 | self.device.destroy_descriptor_set_layout(self.layout, None); 175 | } 176 | } 177 | } 178 | 179 | impl DescriptorSetLayout { 180 | /// Creates a DescriptorSetLayout with the specified [`LayoutBindings`](LayoutBinding). 181 | /// 182 | /// # Examples 183 | /// 184 | /// ```no_run 185 | /// # let event_loop = winit::event_loop::EventLoop::new(); 186 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 187 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 188 | /// // Create a layout with an uniform buffer visible in the vertex shader at binding 0 189 | /// let set_layout = plate::DescriptorSetLayout::new( 190 | /// &device, 191 | /// &[plate::LayoutBinding { 192 | /// binding: 0, 193 | /// ty: plate::DescriptorType::UNIFORM_BUFFER, 194 | /// stage: plate::ShaderStage::VERTEX, 195 | /// count: 1, 196 | /// }], 197 | /// )?; 198 | /// # Ok::<(), Box>(()) 199 | /// ``` 200 | pub fn new(device: &Arc, bindings: &[LayoutBinding]) -> Result { 201 | let bindings = bindings 202 | .iter() 203 | .map(|binding| { 204 | *vk::DescriptorSetLayoutBinding::builder() 205 | .binding(binding.binding) 206 | .descriptor_type(binding.ty) 207 | .descriptor_count(binding.count) 208 | .stage_flags(binding.stage) 209 | }) 210 | .collect::>(); 211 | 212 | let layout_info = vk::DescriptorSetLayoutCreateInfo::builder().bindings(&bindings); 213 | 214 | let layout = unsafe { device.create_descriptor_set_layout(&layout_info, None)? }; 215 | 216 | Ok(Self { 217 | device: Arc::clone(&device), 218 | layout, 219 | }) 220 | } 221 | } 222 | 223 | enum WriteDescriptor { 224 | Buffer { 225 | binding: u32, 226 | ty: DescriptorType, 227 | info: [vk::DescriptorBufferInfo; 1], 228 | alignment: usize, 229 | }, 230 | Image { 231 | binding: u32, 232 | ty: DescriptorType, 233 | info: [vk::DescriptorImageInfo; 1], 234 | }, 235 | } 236 | 237 | /// Struct used to allocate a [`DescriptorSet`]. 238 | pub struct DescriptorAllocator { 239 | device: Arc, 240 | writes: Vec, 241 | } 242 | 243 | impl DescriptorAllocator { 244 | /// Creates a new DescriptorAllocator. 245 | /// 246 | /// # Examples 247 | /// 248 | /// ```no_run 249 | /// # let event_loop = winit::event_loop::EventLoop::new(); 250 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 251 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 252 | /// let allocator = plate::DescriptorAllocator::new(&device); 253 | /// # Ok::<(), Box>(()) 254 | /// ``` 255 | pub fn new(device: &Arc) -> Self { 256 | Self { 257 | device: Arc::clone(&device), 258 | writes: vec![], 259 | } 260 | } 261 | 262 | /// Binds a [`Buffer`] to a descriptor binding. 263 | /// 264 | /// # Examples 265 | /// 266 | /// ```no_run 267 | /// # let event_loop = winit::event_loop::EventLoop::new(); 268 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 269 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 270 | /// # let buffer: plate::Buffer = plate::Buffer::new( 271 | /// # &device, 272 | /// # 1, 273 | /// # plate::BufferUsageFlags::UNIFORM_BUFFER, 274 | /// # plate::SharingMode::EXCLUSIVE, 275 | /// # plate::MemoryPropertyFlags::HOST_VISIBLE | plate::MemoryPropertyFlags::HOST_VISIBLE, 276 | /// # )?; 277 | /// let allocator = plate::DescriptorAllocator::new(&device) 278 | /// .add_buffer_binding(0, plate::DescriptorType::UNIFORM_BUFFER, &buffer); 279 | /// # Ok::<(), Box>(()) 280 | /// ``` 281 | pub fn add_buffer_binding( 282 | &mut self, 283 | binding: u32, 284 | ty: DescriptorType, 285 | buffer: &Buffer, 286 | ) -> &mut Self { 287 | self.add_buffer_index_binding(binding, 0, buffer.instance_count, ty, buffer) 288 | } 289 | 290 | /// Binds a segment of a [`Buffer`] to a descriptor binding. 291 | /// 292 | /// Use the `index` and `instance_count` parameters to specify wich instances of the buffer to 293 | /// bind. 294 | /// 295 | /// # Examples 296 | /// 297 | /// ```no_run 298 | /// # let event_loop = winit::event_loop::EventLoop::new(); 299 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 300 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 301 | /// # let buffer: plate::Buffer = plate::Buffer::new( 302 | /// # &device, 303 | /// # 1, 304 | /// # plate::BufferUsageFlags::UNIFORM_BUFFER, 305 | /// # plate::SharingMode::EXCLUSIVE, 306 | /// # plate::MemoryPropertyFlags::HOST_VISIBLE | plate::MemoryPropertyFlags::HOST_VISIBLE, 307 | /// # )?; 308 | /// let allocator = plate::DescriptorAllocator::new(&device) 309 | /// .add_buffer_index_binding(0, 0, 1, plate::DescriptorType::UNIFORM_BUFFER, &buffer); 310 | /// # Ok::<(), Box>(()) 311 | /// ``` 312 | pub fn add_buffer_index_binding( 313 | &mut self, 314 | binding: u32, 315 | index: usize, 316 | instance_count: usize, 317 | ty: DescriptorType, 318 | buffer: &Buffer, 319 | ) -> &mut Self { 320 | let info = [buffer.descriptor_info(index, instance_count)]; 321 | let write = WriteDescriptor::Buffer { 322 | binding, 323 | ty, 324 | info, 325 | alignment: buffer.alignment_size, 326 | }; 327 | self.writes.push(write); 328 | self 329 | } 330 | 331 | /// Binds a [`Image`] to a descriptor binding. 332 | /// 333 | /// # Examples 334 | /// 335 | /// ```no_run 336 | /// # let event_loop = winit::event_loop::EventLoop::new(); 337 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 338 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 339 | /// # let image = plate::Image::new(&device, 0, 0, 340 | /// # plate::Format::UNDEFINED, plate::ImageLayout::UNDEFINED, 341 | /// # plate::ImageUsageFlags::empty(), plate::ImageAspectFlags::empty())?; 342 | /// # let sampler = plate::Sampler::new(&device, &Default::default())?; 343 | /// let allocator = plate::DescriptorAllocator::new(&device) 344 | /// .add_image_binding( 345 | /// 0, plate::DescriptorType::COMBINED_IMAGE_SAMPLER, 346 | /// &image, &sampler, 347 | /// plate::ImageLayout::SHADER_READ_ONLY_OPTIMAL 348 | /// ); 349 | /// # Ok::<(), Box>(()) 350 | /// ``` 351 | pub fn add_image_binding( 352 | &mut self, 353 | binding: u32, 354 | ty: DescriptorType, 355 | image: &Image, 356 | sampler: &Sampler, 357 | layout: ImageLayout, 358 | ) -> &mut Self { 359 | let info = [image.descriptor_info(sampler, layout)]; 360 | let write = WriteDescriptor::Image { 361 | binding, 362 | ty, 363 | info, 364 | }; 365 | self.writes.push(write); 366 | self 367 | } 368 | 369 | /// Allocates a [`DescriptorSet`] with the added bindings. 370 | /// 371 | /// The type and number of the bindings must match the provided DescriptorSetLayout and the 372 | /// DescriptorPool must have enought capacity for all of them. 373 | /// 374 | /// # Examples 375 | /// 376 | /// ```no_run 377 | /// # let event_loop = winit::event_loop::EventLoop::new(); 378 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 379 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 380 | /// # let buffer: plate::Buffer = plate::Buffer::new( 381 | /// # &device, 382 | /// # 1, 383 | /// # plate::BufferUsageFlags::UNIFORM_BUFFER, 384 | /// # plate::SharingMode::EXCLUSIVE, 385 | /// # plate::MemoryPropertyFlags::HOST_VISIBLE | plate::MemoryPropertyFlags::HOST_VISIBLE, 386 | /// # )?; 387 | /// # let layout = plate::DescriptorSetLayout::new(&device, &[])?; 388 | /// # let pool = plate::DescriptorPool::new(&device, &[], 2)?; 389 | /// // Allocate a DescriptorSet with a uniform buffer at binding 0 390 | /// let descriptor_set = plate::DescriptorAllocator::new(&device) 391 | /// .add_buffer_binding(0, plate::DescriptorType::UNIFORM_BUFFER, &buffer) 392 | /// .allocate(&layout, &pool)?; 393 | /// # Ok::<(), Box>(()) 394 | /// ``` 395 | pub fn allocate( 396 | &mut self, 397 | layout: &DescriptorSetLayout, 398 | pool: &DescriptorPool, 399 | ) -> Result { 400 | let layouts = [layout.layout]; 401 | let alloc_info = vk::DescriptorSetAllocateInfo::builder() 402 | .descriptor_pool(pool.pool) 403 | .set_layouts(&layouts); 404 | 405 | let set = unsafe { self.device.allocate_descriptor_sets(&alloc_info)?[0] }; 406 | 407 | let mut dynamic_sizes = vec![]; 408 | 409 | let writes = self 410 | .writes 411 | .iter_mut() 412 | .map(|write| match write { 413 | WriteDescriptor::Buffer { binding, ty, info, alignment } => { 414 | if (*ty == DescriptorType::UNIFORM_BUFFER_DYNAMIC) || (*ty == DescriptorType::STORAGE_BUFFER_DYNAMIC) { 415 | dynamic_sizes.push(*alignment as u32); 416 | } 417 | *vk::WriteDescriptorSet::builder() 418 | .dst_set(set) 419 | .dst_binding(*binding) 420 | .descriptor_type(*ty) 421 | .dst_array_element(0) 422 | .buffer_info(info) 423 | } 424 | WriteDescriptor::Image { binding, ty, info } => { 425 | *vk::WriteDescriptorSet::builder() 426 | .dst_set(set) 427 | .dst_binding(*binding) 428 | .descriptor_type(*ty) 429 | .dst_array_element(0) 430 | .image_info(info) 431 | } 432 | }) 433 | .collect::>(); 434 | 435 | unsafe { self.device.update_descriptor_sets(&writes, &[]) }; 436 | 437 | Ok(DescriptorSet { 438 | device: Arc::clone(&self.device), 439 | set, 440 | dynamic_sizes, 441 | }) 442 | } 443 | } 444 | 445 | /// Holds a vk::DescriptorSet. 446 | pub struct DescriptorSet { 447 | device: Arc, 448 | set: vk::DescriptorSet, 449 | dynamic_sizes: Vec, 450 | } 451 | 452 | impl DescriptorSet { 453 | /// Binds the DescriptorSet. 454 | /// 455 | /// To be used when recording a command buffer, should be used after binding the pipeline. The 456 | /// pipeline should be created with the same [`DescriptorSetLayout`] as this DescriptorSet. 457 | /// `dynamic_offsets` must have the same length as the number of dynamic descriptors in this 458 | /// set. 459 | /// 460 | /// # Examples 461 | /// 462 | /// ```no_run 463 | /// # struct Vertex(f32); 464 | /// # let event_loop = winit::event_loop::EventLoop::new(); 465 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 466 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 467 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 468 | /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 469 | /// # let layout = plate::DescriptorSetLayout::new(&device, &[])?; 470 | /// # let render_pass = plate::RenderPass::new(&device, &[], &[], &[])?; 471 | /// # let pipeline = plate::pipeline::Pipeline::new(&device, &render_pass, &[], &[], 472 | /// # &Default::default())?; 473 | /// # let pool = plate::DescriptorPool::new(&device, &[], 2)?; 474 | /// let descriptor_set = plate::DescriptorAllocator::new(&device).allocate(&layout, &pool)?; 475 | /// // cmd_buffer.record(.., || { 476 | /// // pipeline.bind(..); 477 | /// descriptor_set.bind(&cmd_buffer, &pipeline, 0, &[]); 478 | /// // })?; 479 | /// # Ok::<(), Box>(()) 480 | /// ``` 481 | pub fn bind(&self, cmd_buffer: &CommandBuffer, pipeline: &Pipeline, first_set: u32, dynamic_offsets: &[u32]) -> Result<(), Error> { 482 | if dynamic_offsets.len() != self.dynamic_sizes.len() { 483 | return Err(DescriptorError::DynamicOffsetOutOfBounds { actual: dynamic_offsets.len(), expected: self.dynamic_sizes.len() }.into()) 484 | } 485 | 486 | let dynamic_offsets = self.dynamic_sizes.iter() 487 | .enumerate() 488 | .map(|(i, s)| s * dynamic_offsets[i]) 489 | .collect::>(); 490 | 491 | unsafe { 492 | self.device.cmd_bind_descriptor_sets( 493 | **cmd_buffer, 494 | vk::PipelineBindPoint::GRAPHICS, 495 | pipeline.layout, 496 | first_set, 497 | &[self.set], 498 | &dynamic_offsets, 499 | ) 500 | }; 501 | 502 | Ok(()) 503 | } 504 | } 505 | -------------------------------------------------------------------------------- /plate/src/device.rs: -------------------------------------------------------------------------------- 1 | use std::{ops, sync::Arc}; 2 | 3 | use ash::{extensions::khr, vk}; 4 | 5 | use crate::{Instance, InstanceParameters, CommandBuffer, Semaphore, Fence, Error, MemoryPropertyFlags}; 6 | 7 | /// Errors from the device module. 8 | #[derive(thiserror::Error, Debug)] 9 | pub enum DeviceError { 10 | /// None of the available queue families are suitable. 11 | #[error("None of the available queue families are suitable")] 12 | QueueNotFound, 13 | /// The physical device memory properties does not support the requested flags. 14 | #[error("{0:?}")] 15 | MemoryTypeNotFound(MemoryPropertyFlags), 16 | /// None of the available physical devices match the requested options. 17 | #[error("No suitable device was found")] 18 | NoDeviceSuitable, 19 | } 20 | 21 | #[derive(Clone, Copy)] 22 | pub(crate) struct Queue { 23 | pub queue: vk::Queue, 24 | pub family: u32, 25 | } 26 | 27 | /// The Device is responsible for most of the vulkan operations. 28 | pub struct Device { 29 | device: ash::Device, 30 | pub(crate) instance: Instance, 31 | pub(crate) physical_device: vk::PhysicalDevice, 32 | pub(crate) queue: Queue, 33 | } 34 | 35 | impl Drop for Device { 36 | fn drop(&mut self) { 37 | unsafe { 38 | self.destroy_device(None); 39 | } 40 | } 41 | } 42 | 43 | impl ops::Deref for Device { 44 | type Target = ash::Device; 45 | 46 | fn deref(&self) -> &Self::Target { 47 | &self.device 48 | } 49 | } 50 | 51 | impl Device { 52 | /// Creates a Device and returns an [`Arc`] pointing to it. 53 | /// 54 | /// The [`DeviceParameters`] are used to find a physical device with the required features. If 55 | /// no device is from the preferred [`DeviceType`], it will default to whatever is available. 56 | /// The [`InstanceParameters`] are used to create the instance. 57 | /// 58 | /// # Exmaple 59 | /// 60 | /// ```no_run 61 | /// # let event_loop = winit::event_loop::EventLoop::new(); 62 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 63 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 64 | /// # Ok::<(), Box>(()) 65 | /// ``` 66 | pub fn new( 67 | params: &DeviceParameters, 68 | instance_params: &InstanceParameters, 69 | window: Option<&winit::window::Window> 70 | ) -> Result, Error> { 71 | let instance = Instance::new(window, instance_params)?; 72 | 73 | let devices = unsafe { instance.enumerate_physical_devices()? }; 74 | let physical_device = pick_device(&devices, &instance, params)?; 75 | 76 | let queue_properties = 77 | unsafe { instance.get_physical_device_queue_family_properties(physical_device) }; 78 | 79 | let mut graphics_family = None; 80 | queue_properties 81 | .iter() 82 | .enumerate() 83 | .map(|(i, properties)| { 84 | if graphics_family.is_none() 85 | && properties.queue_flags.contains(vk::QueueFlags::GRAPHICS) 86 | { 87 | graphics_family = Some(i); 88 | } 89 | Ok(()) 90 | }) 91 | .collect::>()?; 92 | 93 | let queue_family = graphics_family.ok_or(DeviceError::QueueNotFound)? as u32; 94 | 95 | let queue_infos = [*vk::DeviceQueueCreateInfo::builder() 96 | .queue_family_index(queue_family) 97 | .queue_priorities(&[0.0])]; 98 | 99 | let features = vk::PhysicalDeviceFeatures::builder(); 100 | let extensions = [khr::Swapchain::name().as_ptr()]; 101 | 102 | let mut draw_params = vk::PhysicalDeviceShaderDrawParametersFeatures::builder() 103 | .shader_draw_parameters(true); 104 | let device_info = vk::DeviceCreateInfo::builder() 105 | .queue_create_infos(&queue_infos) 106 | .enabled_features(&features) 107 | .enabled_extension_names(&extensions) 108 | .push_next(&mut draw_params); 109 | 110 | let device = unsafe { instance.create_device(physical_device, &device_info, None)? }; 111 | 112 | let queue = Queue { 113 | queue: unsafe { device.get_device_queue(queue_family, 0) }, 114 | family: queue_family, 115 | }; 116 | 117 | Ok(Arc::new(Self { 118 | device, 119 | instance, 120 | physical_device, 121 | queue, 122 | })) 123 | } 124 | 125 | /// Submit a [`CommandBuffer`] to be executed. 126 | /// 127 | /// # Examples 128 | /// 129 | /// ```no_run 130 | /// # let event_loop = winit::event_loop::EventLoop::new(); 131 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 132 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 133 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 134 | /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 135 | /// # let fence = plate::Fence::new(&device, plate::FenceFlags::SIGNALED)?; 136 | /// # let acquire_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 137 | /// # let present_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 138 | /// device.queue_submit( 139 | /// &cmd_buffer, 140 | /// plate::PipelineStage::COLOR_ATTACHMENT_OUTPUT, 141 | /// Some(&acquire_sem), 142 | /// Some(&present_sem), 143 | /// Some(&fence), 144 | /// )?; 145 | /// # Ok::<(), Box>(()) 146 | /// ``` 147 | pub fn queue_submit( 148 | &self, 149 | command_buffer: &CommandBuffer, 150 | wait_stage: PipelineStage, 151 | wait_semaphore: Option<&Semaphore>, 152 | signal_semaphore: Option<&Semaphore>, 153 | fence: Option<&Fence> 154 | ) -> Result<(), Error> { 155 | let wait_semaphores = match wait_semaphore { 156 | Some(s) => vec![**s], 157 | None => vec![], 158 | }; 159 | let signal_semaphores = match signal_semaphore { 160 | Some(s) => vec![**s], 161 | None => vec![], 162 | }; 163 | let fence = match fence { 164 | Some(f) => **f, 165 | None => vk::Fence::null(), 166 | }; 167 | let wait_stages = match wait_stage { 168 | vk::PipelineStageFlags::NONE => vec![], 169 | _ => vec![wait_stage], 170 | }; 171 | let command_buffers = [**command_buffer]; 172 | let submit_infos = [*vk::SubmitInfo::builder() 173 | .wait_semaphores(&wait_semaphores) 174 | .wait_dst_stage_mask(&wait_stages) 175 | .signal_semaphores(&signal_semaphores) 176 | .command_buffers(&command_buffers)]; 177 | 178 | Ok(unsafe { self.device.queue_submit(self.queue.queue, &submit_infos, fence)? }) 179 | } 180 | 181 | /// Wait for all device queues to be executed. 182 | /// 183 | /// #Examples 184 | /// 185 | /// ```no_run 186 | /// # let event_loop = winit::event_loop::EventLoop::new(); 187 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 188 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 189 | /// device.wait_idle()?; 190 | /// # Ok::<(), Box>(()) 191 | /// ``` 192 | pub fn wait_idle(&self) -> Result<(), Error> { 193 | Ok(unsafe { self.device.device_wait_idle()? }) 194 | } 195 | 196 | pub(crate) fn memory_type_index(&self, mem_requirements: vk::MemoryRequirements, memory_properties: MemoryPropertyFlags) -> Result { 197 | let mem_properties = unsafe { self.instance.get_physical_device_memory_properties(self.physical_device) }; 198 | mem_properties 199 | .memory_types 200 | .iter() 201 | .enumerate() 202 | .find(|(i, ty)| { 203 | mem_requirements.memory_type_bits & (1 << i) > 0 204 | && ty.property_flags.contains(memory_properties) 205 | }) 206 | .map(|(i, _)| i) 207 | .ok_or(DeviceError::MemoryTypeNotFound(memory_properties).into()) 208 | } 209 | } 210 | 211 | pub use vk::PhysicalDeviceType as DeviceType; 212 | pub use vk::PipelineStageFlags as PipelineStage; 213 | 214 | /// Parameters for physical device selection. 215 | pub struct DeviceParameters { 216 | /// Will prefer devices of this type. 217 | pub preferred_type: DeviceType, 218 | /// What features the device should support. 219 | pub features: DeviceFeatures, 220 | } 221 | 222 | impl Default for DeviceParameters { 223 | fn default() -> Self { 224 | Self { 225 | preferred_type: DeviceType::DISCRETE_GPU, 226 | features: DeviceFeatures::empty(), 227 | } 228 | } 229 | } 230 | 231 | fn pick_device( 232 | devices: &Vec, 233 | instance: &Instance, 234 | params: &DeviceParameters, 235 | ) -> Result { 236 | let devices = devices 237 | .iter() 238 | .filter(|device| { 239 | let features = unsafe { instance.get_physical_device_features(**device) }; 240 | features.contains(¶ms.features) 241 | }) 242 | .collect::>(); 243 | if devices.is_empty() { 244 | return Err(DeviceError::NoDeviceSuitable.into()); 245 | }; 246 | 247 | let preferred_device = devices 248 | .iter() 249 | .find(|device| { 250 | let properties = unsafe { instance.get_physical_device_properties(***device) }; 251 | properties.device_type == params.preferred_type 252 | }) 253 | .unwrap_or(&devices[0]); 254 | 255 | Ok(**preferred_device) 256 | } 257 | 258 | trait Contains { 259 | fn contains(&self, _: &T) -> bool; 260 | } 261 | 262 | impl Contains for vk::PhysicalDeviceFeatures { 263 | fn contains(&self, other: &DeviceFeatures) -> bool { 264 | (self.robust_buffer_access == 1) >= other.contains(DeviceFeatures::ROBUST_BUFFER_ACCESS) 265 | && (self.full_draw_index_uint32 == 1) 266 | >= other.contains(DeviceFeatures::FULL_DRAW_INDEX_UINT32) 267 | && (self.image_cube_array == 1) >= other.contains(DeviceFeatures::IMAGE_CUBE_ARRAY) 268 | && (self.independent_blend == 1) >= other.contains(DeviceFeatures::INDEPENDENT_BLEND) 269 | && (self.geometry_shader == 1) >= other.contains(DeviceFeatures::GEOMETRY_SHADER) 270 | && (self.tessellation_shader == 1) 271 | >= other.contains(DeviceFeatures::TESSELLATION_SHADER) 272 | && (self.sample_rate_shading == 1) 273 | >= other.contains(DeviceFeatures::SAMPLE_RATE_SHADING) 274 | && (self.dual_src_blend == 1) >= other.contains(DeviceFeatures::DUAL_SRC_BLEND) 275 | && (self.logic_op == 1) >= other.contains(DeviceFeatures::LOGIC_OP) 276 | && (self.multi_draw_indirect == 1) 277 | >= other.contains(DeviceFeatures::MULTI_DRAW_INDIRECT) 278 | && (self.draw_indirect_first_instance == 1) 279 | >= other.contains(DeviceFeatures::DRAW_INDIRECT_FIRST_INSTANCE) 280 | && (self.depth_clamp == 1) >= other.contains(DeviceFeatures::DEPTH_CLAMP) 281 | && (self.depth_bias_clamp == 1) >= other.contains(DeviceFeatures::DEPTH_BIAS_CLAMP) 282 | && (self.fill_mode_non_solid == 1) 283 | >= other.contains(DeviceFeatures::FILL_MODE_NON_SOLID) 284 | && (self.depth_bounds == 1) >= other.contains(DeviceFeatures::DEPTH_BOUNDS) 285 | && (self.wide_lines == 1) >= other.contains(DeviceFeatures::WIDE_LINES) 286 | && (self.large_points == 1) >= other.contains(DeviceFeatures::LARGE_POINTS) 287 | && (self.alpha_to_one == 1) >= other.contains(DeviceFeatures::ALPHA_TO_ONE) 288 | && (self.multi_viewport == 1) >= other.contains(DeviceFeatures::MULTI_VIEWPORT) 289 | && (self.sampler_anisotropy == 1) >= other.contains(DeviceFeatures::SAMPLER_ANISOTROPY) 290 | && (self.texture_compression_etc2 == 1) 291 | >= other.contains(DeviceFeatures::TEXTURE_COMPRESSION_ETC2) 292 | && (self.texture_compression_astc_ldr == 1) 293 | >= other.contains(DeviceFeatures::TEXTURE_COMPRESSION_ASTC_LDR) 294 | && (self.texture_compression_bc == 1) 295 | >= other.contains(DeviceFeatures::TEXTURE_COMPRESSION_BC) 296 | && (self.occlusion_query_precise == 1) 297 | >= other.contains(DeviceFeatures::OCCLUSION_QUERY_PRECISE) 298 | && (self.pipeline_statistics_query == 1) 299 | >= other.contains(DeviceFeatures::PIPELINE_STATISTICS_QUERY) 300 | && (self.vertex_pipeline_stores_and_atomics == 1) 301 | >= other.contains(DeviceFeatures::VERTEX_PIPELINE_STORES_AND_ATOMICS) 302 | && (self.fragment_stores_and_atomics == 1) 303 | >= other.contains(DeviceFeatures::FRAGMENT_STORES_AND_ATOMICS) 304 | && (self.shader_tessellation_and_geometry_point_size == 1) 305 | >= other.contains(DeviceFeatures::SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE) 306 | && (self.shader_image_gather_extended == 1) 307 | >= other.contains(DeviceFeatures::SHADER_IMAGE_GATHER_EXTENDED) 308 | && (self.shader_storage_image_extended_formats == 1) 309 | >= other.contains(DeviceFeatures::SHADER_STORAGE_IMAGE_EXTENDED_FORMATS) 310 | && (self.shader_storage_image_multisample == 1) 311 | >= other.contains(DeviceFeatures::SHADER_STORAGE_IMAGE_MULTISAMPLE) 312 | && (self.shader_storage_image_read_without_format == 1) 313 | >= other.contains(DeviceFeatures::SHADER_STORAGE_IMAGE_READ_WITHOUT_FORMAT) 314 | && (self.shader_storage_image_write_without_format == 1) 315 | >= other.contains(DeviceFeatures::SHADER_STORAGE_IMAGE_WRITE_WITHOUT_FORMAT) 316 | && (self.shader_uniform_buffer_array_dynamic_indexing == 1) 317 | >= other.contains(DeviceFeatures::SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING) 318 | && (self.shader_sampled_image_array_dynamic_indexing == 1) 319 | >= other.contains(DeviceFeatures::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING) 320 | && (self.shader_storage_buffer_array_dynamic_indexing == 1) 321 | >= other.contains(DeviceFeatures::SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING) 322 | && (self.shader_storage_image_array_dynamic_indexing == 1) 323 | >= other.contains(DeviceFeatures::SHADER_STORAGE_IMAGE_ARRAY_DYNAMIC_INDEXING) 324 | && (self.shader_clip_distance == 1) 325 | >= other.contains(DeviceFeatures::SHADER_CLIP_DISTANCE) 326 | && (self.shader_cull_distance == 1) 327 | >= other.contains(DeviceFeatures::SHADER_CULL_DISTANCE) 328 | && (self.shader_float64 == 1) >= other.contains(DeviceFeatures::SHADER_FLOAT64) 329 | && (self.shader_int64 == 1) >= other.contains(DeviceFeatures::SHADER_INT64) 330 | && (self.shader_int16 == 1) >= other.contains(DeviceFeatures::SHADER_INT16) 331 | && (self.shader_resource_residency == 1) 332 | >= other.contains(DeviceFeatures::SHADER_RESOURCE_RESIDENCY) 333 | && (self.shader_resource_min_lod == 1) 334 | >= other.contains(DeviceFeatures::SHADER_RESOURCE_MIN_LOD) 335 | && (self.sparse_binding == 1) >= other.contains(DeviceFeatures::SPARSE_BINDING) 336 | && (self.sparse_residency_buffer == 1) 337 | >= other.contains(DeviceFeatures::SPARSE_RESIDENCY_BUFFER) 338 | && (self.sparse_residency_image2_d == 1) 339 | >= other.contains(DeviceFeatures::SPARSE_RESIDENCY_IMAGE2_D) 340 | && (self.sparse_residency_image3_d == 1) 341 | >= other.contains(DeviceFeatures::SPARSE_RESIDENCY_IMAGE3_D) 342 | && (self.sparse_residency2_samples == 1) 343 | >= other.contains(DeviceFeatures::SPARSE_RESIDENCY2_SAMPLES) 344 | && (self.sparse_residency4_samples == 1) 345 | >= other.contains(DeviceFeatures::SPARSE_RESIDENCY4_SAMPLES) 346 | && (self.sparse_residency8_samples == 1) 347 | >= other.contains(DeviceFeatures::SPARSE_RESIDENCY8_SAMPLES) 348 | && (self.sparse_residency16_samples == 1) 349 | >= other.contains(DeviceFeatures::SPARSE_RESIDENCY16_SAMPLES) 350 | && (self.sparse_residency_aliased == 1) 351 | >= other.contains(DeviceFeatures::SPARSE_RESIDENCY_ALIASED) 352 | && (self.variable_multisample_rate == 1) 353 | >= other.contains(DeviceFeatures::VARIABLE_MULTISAMPLE_RATE) 354 | && (self.inherited_queries == 1) >= other.contains(DeviceFeatures::INHERITED_QUERIES) 355 | } 356 | } 357 | 358 | bitflags::bitflags! { 359 | /// 360 | pub struct DeviceFeatures: u64 { 361 | const ROBUST_BUFFER_ACCESS = 1 << 0; 362 | const FULL_DRAW_INDEX_UINT32 = 1 << 1; 363 | const IMAGE_CUBE_ARRAY = 1 << 2; 364 | const INDEPENDENT_BLEND = 1 << 3; 365 | const GEOMETRY_SHADER = 1 << 4; 366 | const TESSELLATION_SHADER = 1 << 5; 367 | const SAMPLE_RATE_SHADING = 1 << 6; 368 | const DUAL_SRC_BLEND = 1 << 7; 369 | const LOGIC_OP = 1 << 8; 370 | const MULTI_DRAW_INDIRECT = 1 << 9; 371 | const DRAW_INDIRECT_FIRST_INSTANCE = 1 << 10; 372 | const DEPTH_CLAMP = 1 << 11; 373 | const DEPTH_BIAS_CLAMP = 1 << 12; 374 | const FILL_MODE_NON_SOLID = 1 << 13; 375 | const DEPTH_BOUNDS = 1 << 14; 376 | const WIDE_LINES = 1 << 15; 377 | const LARGE_POINTS = 1 << 16; 378 | const ALPHA_TO_ONE = 1 << 17; 379 | const MULTI_VIEWPORT = 1 << 18; 380 | const SAMPLER_ANISOTROPY = 1 << 19; 381 | const TEXTURE_COMPRESSION_ETC2 = 1 << 20; 382 | const TEXTURE_COMPRESSION_ASTC_LDR = 1 << 21; 383 | const TEXTURE_COMPRESSION_BC = 1 << 22; 384 | const OCCLUSION_QUERY_PRECISE = 1 << 23; 385 | const PIPELINE_STATISTICS_QUERY = 1 << 24; 386 | const VERTEX_PIPELINE_STORES_AND_ATOMICS = 1 << 25; 387 | const FRAGMENT_STORES_AND_ATOMICS = 1 << 26; 388 | const SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE = 1 << 27; 389 | const SHADER_IMAGE_GATHER_EXTENDED = 1 << 28; 390 | const SHADER_STORAGE_IMAGE_EXTENDED_FORMATS = 1 << 29; 391 | const SHADER_STORAGE_IMAGE_MULTISAMPLE = 1 << 30; 392 | const SHADER_STORAGE_IMAGE_READ_WITHOUT_FORMAT = 1 << 31; 393 | const SHADER_STORAGE_IMAGE_WRITE_WITHOUT_FORMAT = 1 << 32; 394 | const SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING = 1 << 33; 395 | const SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING = 1 << 34; 396 | const SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING = 1 << 35; 397 | const SHADER_STORAGE_IMAGE_ARRAY_DYNAMIC_INDEXING = 1 << 36; 398 | const SHADER_CLIP_DISTANCE = 1 << 37; 399 | const SHADER_CULL_DISTANCE = 1 << 38; 400 | const SHADER_FLOAT64 = 1 << 39; 401 | const SHADER_INT64 = 1 << 40; 402 | const SHADER_INT16 = 1 << 41; 403 | const SHADER_RESOURCE_RESIDENCY = 1 << 42; 404 | const SHADER_RESOURCE_MIN_LOD = 1 << 43; 405 | const SPARSE_BINDING = 1 << 44; 406 | const SPARSE_RESIDENCY_BUFFER = 1 << 45; 407 | const SPARSE_RESIDENCY_IMAGE2_D = 1 << 46; 408 | const SPARSE_RESIDENCY_IMAGE3_D = 1 << 47; 409 | const SPARSE_RESIDENCY2_SAMPLES = 1 << 48; 410 | const SPARSE_RESIDENCY4_SAMPLES = 1 << 49; 411 | const SPARSE_RESIDENCY8_SAMPLES = 1 << 50; 412 | const SPARSE_RESIDENCY16_SAMPLES = 1 << 51; 413 | const SPARSE_RESIDENCY_ALIASED = 1 << 52; 414 | const VARIABLE_MULTISAMPLE_RATE = 1 << 53; 415 | const INHERITED_QUERIES = 1 << 54; 416 | } 417 | } 418 | 419 | #[cfg(test)] 420 | mod tests { 421 | use super::*; 422 | 423 | #[test] 424 | fn test_feature_contains() { 425 | let features_a = vk::PhysicalDeviceFeatures::builder().robust_buffer_access(true); 426 | let features_b = DeviceFeatures::ROBUST_BUFFER_ACCESS; 427 | let features_c = DeviceFeatures::empty(); 428 | let features_d = 429 | DeviceFeatures::ROBUST_BUFFER_ACCESS | DeviceFeatures::FULL_DRAW_INDEX_UINT32; 430 | let features_e = DeviceFeatures::FULL_DRAW_INDEX_UINT32; 431 | assert!(features_a.contains(&features_b)); 432 | assert!(features_a.contains(&features_c)); 433 | assert!(!features_a.contains(&features_d)); 434 | assert!(!features_a.contains(&features_e)); 435 | 436 | let features_a = vk::PhysicalDeviceFeatures::builder(); 437 | assert!(!features_a.contains(&features_b)); 438 | assert!(features_a.contains(&features_c)); 439 | assert!(!features_a.contains(&features_d)); 440 | assert!(!features_a.contains(&features_e)); 441 | } 442 | } 443 | -------------------------------------------------------------------------------- /plate/src/image.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ash::vk; 4 | use crate::{Buffer, Device, command::*, PipelineStage, Format, Error, MemoryPropertyFlags, ImageLayout}; 5 | pub use vk::Filter as Filter; 6 | pub use vk::SamplerAddressMode as SamplerAddressMode; 7 | pub use vk::ImageUsageFlags as ImageUsageFlags; 8 | pub use vk::ImageAspectFlags as ImageAspectFlags; 9 | 10 | /// Filter mode for a [`Sampler`]. 11 | /// 12 | /// Describes how to interpolate texels. 13 | pub struct SamplerFilter { 14 | /// How to handle magnified texels. 15 | pub min: Filter, 16 | /// How to handle minified texels. 17 | pub mag: Filter, 18 | } 19 | 20 | impl SamplerFilter { 21 | /// Linear filtering. 22 | pub const LINEAR: Self = Self { min: Filter::LINEAR, mag: Filter::LINEAR }; 23 | /// Nearest filtering. 24 | pub const NEAREST: Self = Self { min: Filter::NEAREST, mag: Filter::LINEAR }; 25 | /// Cubic filtering. 26 | pub const CUBIC_EXT: Self = Self { min: Filter::CUBIC_EXT, mag: Filter::CUBIC_EXT }; 27 | } 28 | 29 | /// How to address coordinates outside the image bounds. 30 | pub struct SamplerAddress { 31 | /// U coordinates address mode. 32 | pub u: SamplerAddressMode, 33 | /// V coordinates address mode. 34 | pub v: SamplerAddressMode, 35 | /// W coordinates address mode. 36 | pub w: SamplerAddressMode, 37 | } 38 | 39 | impl SamplerAddress { 40 | /// Repeat the image in all coordinates. 41 | pub const REPEAT: Self = Self::all(SamplerAddressMode::REPEAT); 42 | /// Repeat and mirror the image in all coordinates. 43 | pub const MIRRORED_REPEAT: Self = Self::all(SamplerAddressMode::MIRRORED_REPEAT); 44 | /// Repeat the edge color in all coordinates. 45 | pub const CLAMP_TO_EDGE: Self = Self::all(SamplerAddressMode::CLAMP_TO_EDGE); 46 | /// Repeat the border color (black) in all coordinates. 47 | pub const CLAMP_TO_BORDER: Self = Self::all(SamplerAddressMode::CLAMP_TO_BORDER); 48 | /// Repeat the oposite edge color in all coordinates. 49 | pub const MIRROR_CLAMP_TO_EDGE: Self = Self::all(SamplerAddressMode::MIRROR_CLAMP_TO_EDGE); 50 | 51 | const fn all(mode: SamplerAddressMode) -> Self { 52 | Self { u: mode, v: mode, w: mode } 53 | } 54 | } 55 | 56 | /// Optional parameters for [`Sampler creation`]. 57 | pub struct SamplerParameters { 58 | /// Filter mode for the sampler. 59 | pub filter: SamplerFilter, 60 | /// Address mode for the sampler. 61 | pub address_mode: SamplerAddress, 62 | } 63 | 64 | impl Default for SamplerParameters { 65 | fn default() -> Self { 66 | Self { 67 | filter: SamplerFilter::LINEAR, 68 | address_mode: SamplerAddress::REPEAT, 69 | } 70 | } 71 | } 72 | 73 | /// Describes how to sample a image texture. 74 | pub struct Sampler { 75 | device: Arc, 76 | sampler: vk::Sampler, 77 | } 78 | 79 | impl Drop for Sampler { 80 | fn drop(&mut self) { 81 | unsafe { 82 | self.device.destroy_sampler(self.sampler, None); 83 | } 84 | } 85 | } 86 | 87 | impl Sampler { 88 | /// Creates a Sampler. 89 | /// 90 | /// # Examples 91 | /// 92 | /// ```no_run 93 | /// # let event_loop = winit::event_loop::EventLoop::new(); 94 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 95 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 96 | /// let sampler = plate::Sampler::new(&device, &Default::default())?; 97 | /// # Ok::<(), Box>(()) 98 | /// ``` 99 | pub fn new(device: &Arc, params: &SamplerParameters) -> Result { 100 | let sampler_info = vk::SamplerCreateInfo::builder() 101 | .mag_filter(params.filter.min) 102 | .min_filter(params.filter.mag) 103 | .address_mode_u(params.address_mode.u) 104 | .address_mode_v(params.address_mode.v) 105 | .address_mode_w(params.address_mode.w) 106 | .anisotropy_enable(false) 107 | .max_anisotropy(1.0) 108 | .border_color(vk::BorderColor::INT_OPAQUE_BLACK) 109 | .unnormalized_coordinates(false) 110 | .compare_enable(false) 111 | .compare_op(vk::CompareOp::ALWAYS) 112 | .mipmap_mode(vk::SamplerMipmapMode::LINEAR) 113 | .mip_lod_bias(0.0) 114 | .min_lod(0.0) 115 | .max_lod(0.0); 116 | 117 | let sampler = unsafe { device.create_sampler(&sampler_info, None)? }; 118 | 119 | Ok(Self { 120 | device: Arc::clone(&device), 121 | sampler, 122 | }) 123 | } 124 | } 125 | 126 | /// Represents a 2 dimesional array of data. 127 | pub struct Image { 128 | device: Arc, 129 | image: vk::Image, 130 | mem: Option, 131 | pub(crate) view: vk::ImageView, 132 | /// The format of the image. 133 | pub format: Format, 134 | /// The width of the image. 135 | pub width: u32, 136 | /// The height of the image. 137 | pub height: u32, 138 | } 139 | 140 | impl Drop for Image { 141 | fn drop(&mut self) { 142 | unsafe { 143 | self.device.destroy_image_view(self.view, None); 144 | if let Some(mem) = self.mem { 145 | self.device.destroy_image(self.image, None); 146 | self.device.free_memory(mem, None); 147 | } 148 | } 149 | } 150 | } 151 | 152 | impl Image { 153 | /// Creates a Image. 154 | /// 155 | /// # Examples 156 | /// 157 | /// ```no_run 158 | /// # let event_loop = winit::event_loop::EventLoop::new(); 159 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 160 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 161 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 162 | /// # let (width, height) = (0, 0); 163 | /// let image = plate::Image::new( 164 | /// &device, 165 | /// width, 166 | /// height, 167 | /// plate::Format::R8G8B8A8_SRGB, 168 | /// plate::ImageLayout::UNDEFINED, 169 | /// plate::ImageUsageFlags::TRANSFER_DST | plate::ImageUsageFlags::SAMPLED, 170 | /// plate::ImageAspectFlags::COLOR, 171 | /// )?; 172 | /// # Ok::<(), Box>(()) 173 | /// ``` 174 | pub fn new(device: &Arc, width: u32, height: u32, format: Format, layout: ImageLayout, usage: ImageUsageFlags, image_aspect: ImageAspectFlags) -> Result { 175 | let image_info = vk::ImageCreateInfo::builder() 176 | .image_type(vk::ImageType::TYPE_2D) 177 | .extent(vk::Extent3D { 178 | width, 179 | height, 180 | depth: 1, 181 | }) 182 | .mip_levels(1) 183 | .array_layers(1) 184 | .format(format) 185 | .tiling(vk::ImageTiling::OPTIMAL) 186 | .initial_layout(ImageLayout::UNDEFINED) 187 | .usage(usage) 188 | .sharing_mode(vk::SharingMode::EXCLUSIVE) 189 | .samples(vk::SampleCountFlags::TYPE_1); 190 | 191 | let image = unsafe { device.create_image(&image_info, None)? }; 192 | 193 | let mem_requirements = unsafe { device.get_image_memory_requirements(image) }; 194 | let mem_type_index = device.memory_type_index(mem_requirements, MemoryPropertyFlags::DEVICE_LOCAL)?; 195 | 196 | let alloc_info = vk::MemoryAllocateInfo::builder() 197 | .allocation_size(mem_requirements.size) 198 | .memory_type_index(mem_type_index as u32); 199 | 200 | let mem = unsafe { device.allocate_memory(&alloc_info, None)? }; 201 | unsafe { device.bind_image_memory(image, mem, 0)? }; 202 | 203 | let cmd_pool = CommandPool::new(device)?; 204 | if layout != ImageLayout::UNDEFINED { 205 | transition_layout(device, image, &cmd_pool, vk::ImageLayout::UNDEFINED, layout)?; 206 | } 207 | 208 | Self::from_vk_image(device, image, Some(mem), width, height, format, image_aspect) 209 | } 210 | 211 | pub(crate) fn from_vk_image(device: &Arc, image: vk::Image, mem: Option, width: u32, height: u32, format: Format, image_aspect: ImageAspectFlags) -> Result { 212 | let view = Self::image_view(device, image, image_aspect, format)?; 213 | 214 | Ok(Self { 215 | device: Arc::clone(&device), 216 | image, 217 | mem, 218 | view, 219 | format, 220 | width, 221 | height, 222 | }) 223 | } 224 | 225 | pub(crate) fn descriptor_info(&self, sampler: &Sampler, layout: ImageLayout) -> vk::DescriptorImageInfo { 226 | *vk::DescriptorImageInfo::builder() 227 | .image_layout(layout) 228 | .image_view(self.view) 229 | .sampler(sampler.sampler) 230 | } 231 | 232 | fn image_view(device: &Arc, image: vk::Image, image_aspect: ImageAspectFlags, format: Format) -> Result { 233 | let components = vk::ComponentMapping { 234 | r: vk::ComponentSwizzle::IDENTITY, 235 | g: vk::ComponentSwizzle::IDENTITY, 236 | b: vk::ComponentSwizzle::IDENTITY, 237 | a: vk::ComponentSwizzle::IDENTITY, 238 | }; 239 | 240 | let subresource_range = *vk::ImageSubresourceRange::builder() 241 | .aspect_mask(image_aspect) 242 | .base_mip_level(0) 243 | .level_count(1) 244 | .base_array_layer(0) 245 | .layer_count(1); 246 | 247 | let view_info = vk::ImageViewCreateInfo::builder() 248 | .image(image) 249 | .view_type(vk::ImageViewType::TYPE_2D) 250 | .format(format) 251 | .components(components) 252 | .subresource_range(subresource_range); 253 | 254 | Ok(unsafe { device.create_image_view(&view_info, None)? }) 255 | } 256 | } 257 | 258 | /// Holds a [`Image`] with texture data in it. 259 | pub struct Texture(Image); 260 | 261 | impl std::ops::Deref for Texture { 262 | type Target = Image; 263 | 264 | fn deref(&self) -> &Self::Target { 265 | &self.0 266 | } 267 | } 268 | 269 | impl Texture { 270 | /// Creates a Texture from a &[u8]. 271 | /// 272 | /// # Examples 273 | /// 274 | /// ```no_run 275 | /// # let event_loop = winit::event_loop::EventLoop::new(); 276 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 277 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 278 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 279 | /// # let (width, height) = (0, 0); 280 | /// # let data = [0]; 281 | /// let image = plate::Texture::new(&device, &cmd_pool, width, height, &data)?; 282 | /// # Ok::<(), Box>(()) 283 | /// ``` 284 | pub fn new(device: &Arc, cmd_pool: &CommandPool, width: u32, height: u32, data: &[u8]) -> Result { 285 | let staging = Buffer::new( 286 | device, 287 | (width * height * 4) as usize, 288 | vk::BufferUsageFlags::TRANSFER_SRC, 289 | vk::SharingMode::EXCLUSIVE, 290 | vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, 291 | )?; 292 | 293 | let mut mapped = staging.map()?; 294 | mapped.write(data); 295 | let staging = mapped.unmap(); 296 | 297 | let image = Image::new( 298 | device, 299 | width, 300 | height, 301 | Format::R8G8B8A8_SRGB, 302 | ImageLayout::UNDEFINED, 303 | ImageUsageFlags::TRANSFER_DST | ImageUsageFlags::SAMPLED, 304 | ImageAspectFlags::COLOR, 305 | )?; 306 | 307 | transition_layout(device, image.image, cmd_pool, vk::ImageLayout::UNDEFINED, vk::ImageLayout::TRANSFER_DST_OPTIMAL)?; 308 | staging.copy_to_image(image.image, width, height, cmd_pool)?; 309 | transition_layout(device, image.image, cmd_pool, vk::ImageLayout::TRANSFER_DST_OPTIMAL, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)?; 310 | 311 | Ok(Self(image)) 312 | } 313 | } 314 | 315 | fn transition_layout(device: &Arc, image: vk::Image, cmd_pool: &CommandPool, old_layout: vk::ImageLayout, new_layout: vk::ImageLayout) -> Result<(), Error> { 316 | let (src_access, src_stage) = match old_layout { 317 | vk::ImageLayout::UNDEFINED => (vk::AccessFlags::empty(), vk::PipelineStageFlags::TOP_OF_PIPE), 318 | vk::ImageLayout::TRANSFER_DST_OPTIMAL => (vk::AccessFlags::TRANSFER_WRITE, vk::PipelineStageFlags::TRANSFER), 319 | _ => (vk::AccessFlags::empty(), vk::PipelineStageFlags::TOP_OF_PIPE), 320 | }; 321 | 322 | let (dst_access, dst_stage) = match new_layout { 323 | vk::ImageLayout::TRANSFER_DST_OPTIMAL => (vk::AccessFlags::TRANSFER_WRITE, vk::PipelineStageFlags::TRANSFER), 324 | vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL => (vk::AccessFlags::SHADER_READ, vk::PipelineStageFlags::FRAGMENT_SHADER), 325 | _ => (vk::AccessFlags::empty(), vk::PipelineStageFlags::BOTTOM_OF_PIPE), 326 | }; 327 | 328 | let cmd_buffer = cmd_pool.alloc_cmd_buffer(CommandBufferLevel::PRIMARY)?; 329 | cmd_buffer.record(CommandBufferUsageFlags::ONE_TIME_SUBMIT, || { 330 | let barrier = vk::ImageMemoryBarrier::builder() 331 | .old_layout(old_layout) 332 | .new_layout(new_layout) 333 | .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED) 334 | .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED) 335 | .image(image) 336 | .subresource_range(vk::ImageSubresourceRange { 337 | aspect_mask: vk::ImageAspectFlags::COLOR, 338 | base_mip_level: 0, 339 | base_array_layer: 0, 340 | layer_count: 1, 341 | level_count: 1, 342 | }) 343 | .src_access_mask(src_access) 344 | .dst_access_mask(dst_access); 345 | 346 | unsafe { device.cmd_pipeline_barrier( 347 | *cmd_buffer, 348 | src_stage, dst_stage, vk::DependencyFlags::empty(), 349 | &[], &[], &[*barrier] 350 | ) }; 351 | })?; 352 | 353 | device.queue_submit(&cmd_buffer, PipelineStage::empty(), None, None, None)?; 354 | Ok(unsafe { device.queue_wait_idle(device.queue.queue)? }) 355 | } 356 | -------------------------------------------------------------------------------- /plate/src/instance.rs: -------------------------------------------------------------------------------- 1 | use ash::vk; 2 | use std::ffi; 3 | 4 | use crate::{debug, Error, Debugger}; 5 | 6 | /// Errors from the instance module. 7 | #[derive(thiserror::Error, Debug)] 8 | pub enum InstanceError { 9 | /// Error trying to create a C string because of a nul byte. 10 | #[error("Error creating C string: {0}")] 11 | NulError(#[from] ffi::NulError), 12 | } 13 | 14 | /// Version of the Vulkan API. 15 | #[derive(Clone, Copy)] 16 | pub enum ApiVersion { 17 | /// API version 1.1. 18 | Type1_1, 19 | /// API version 1.2. 20 | Type1_2, 21 | /// API version 1.3. 22 | Type1_3, 23 | } 24 | 25 | impl Into for ApiVersion { 26 | fn into(self) -> u32 { 27 | use ApiVersion::*; 28 | match self { 29 | Type1_1 => vk::API_VERSION_1_1, 30 | Type1_2 => vk::API_VERSION_1_2, 31 | Type1_3 => vk::API_VERSION_1_3, 32 | } 33 | } 34 | } 35 | 36 | /// Optional parameters when creating an Instance. 37 | pub struct InstanceParameters { 38 | /// Application name. 39 | pub app_name: String, 40 | /// Application version. 41 | pub app_version: (u32, u32, u32, u32), 42 | /// Engine name. 43 | pub engine_name: String, 44 | /// Engine version. 45 | pub engine_version: (u32, u32, u32, u32), 46 | /// API version to be used. 47 | pub api_version: ApiVersion, 48 | /// Aditional vulkan layers to be enabled. 49 | pub extra_layers: Vec, 50 | /// Aditional vulkan extensions to be enabled. 51 | pub extra_extensions: Vec, 52 | /// Whether to use validation layers or not. 53 | pub enable_validation_layers: bool, 54 | } 55 | 56 | impl Default for InstanceParameters { 57 | fn default() -> Self { 58 | Self { 59 | app_name: "wvk".into(), 60 | app_version: (0, 1, 0, 0), 61 | engine_name: "wvk".into(), 62 | engine_version: (0, 1, 0, 0), 63 | api_version: ApiVersion::Type1_2, 64 | extra_layers: vec![], 65 | extra_extensions: vec![], 66 | enable_validation_layers: true, 67 | } 68 | } 69 | } 70 | 71 | pub(crate) struct Instance { 72 | instance: ash::Instance, 73 | pub(crate) entry: ash::Entry, 74 | #[allow(dead_code)] 75 | debugger: Option, 76 | } 77 | 78 | impl std::ops::Deref for Instance { 79 | type Target = ash::Instance; 80 | 81 | fn deref(&self) -> &Self::Target { 82 | &self.instance 83 | } 84 | } 85 | 86 | impl Drop for Instance { 87 | fn drop(&mut self) { 88 | unsafe { 89 | self.debugger = None; 90 | self.destroy_instance(None); 91 | } 92 | } 93 | } 94 | 95 | impl Instance { 96 | pub fn new( 97 | window: Option<&winit::window::Window>, 98 | params: &InstanceParameters, 99 | ) -> Result { 100 | let entry = ash::Entry::linked(); 101 | 102 | let app_name = ffi::CString::new(params.app_name.clone()).map_err(|e| InstanceError::from(e))?; 103 | let engine_name = ffi::CString::new(params.engine_name.clone()).map_err(|e| InstanceError::from(e))?; 104 | let app_info = vk::ApplicationInfo::builder() 105 | .application_name(&app_name) 106 | .application_version(vk::make_api_version( 107 | params.app_version.0, 108 | params.app_version.1, 109 | params.app_version.2, 110 | params.app_version.3, 111 | )) 112 | .engine_name(&engine_name) 113 | .engine_version(vk::make_api_version( 114 | params.engine_version.0, 115 | params.engine_version.1, 116 | params.engine_version.2, 117 | params.engine_version.3, 118 | )) 119 | .api_version(params.api_version.into()); 120 | 121 | let mut layers = if params.enable_validation_layers { 122 | vec!["VK_LAYER_KHRONOS_validation".into()] 123 | } else { vec![] }; 124 | params 125 | .extra_layers 126 | .iter() 127 | .for_each(|layer| layers.push(layer.clone())); 128 | let layers = layers 129 | .into_iter() 130 | .map(|layer| ffi::CString::new(layer)) 131 | .collect::, _>>().map_err(|e| InstanceError::from(e))?; 132 | let layers = layers 133 | .iter() 134 | .map(|layer| layer.as_ptr()) 135 | .collect::>(); 136 | 137 | let mut extensions = match window { 138 | Some(window) => ash_window::enumerate_required_extensions(window)?.to_vec(), 139 | None => vec![], 140 | }; 141 | if params.enable_validation_layers { 142 | extensions.push(ash::extensions::ext::DebugUtils::name().as_ptr()); 143 | } 144 | 145 | let extra_extensions = params 146 | .extra_extensions 147 | .iter() 148 | .map(|extension| ffi::CString::new(extension.clone())) 149 | .collect::, _>>().map_err(|e| InstanceError::from(e))?; 150 | extra_extensions 151 | .into_iter() 152 | .for_each(|extension| extensions.push(extension.as_ptr())); 153 | 154 | let mut debug_messenger_info = debug::debug_messenger_info(); 155 | 156 | let instance_info = vk::InstanceCreateInfo::builder() 157 | .application_info(&app_info) 158 | .enabled_extension_names(extensions.as_slice()) 159 | .enabled_layer_names(layers.as_slice()); 160 | 161 | let instance_info = if params.enable_validation_layers { 162 | instance_info.push_next(&mut debug_messenger_info) 163 | } else { instance_info }; 164 | 165 | let instance = unsafe { entry.create_instance(&instance_info, None)? }; 166 | 167 | let debugger = if params.enable_validation_layers { 168 | Some(Debugger::new(&entry, &instance)?) 169 | } else { None }; 170 | 171 | Ok(Self { 172 | instance, 173 | entry, 174 | debugger, 175 | }) 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /plate/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # plate 2 | //! 3 | //! Rust library for writing simpler Vulkan code 4 | //! 5 | //! ## Instalation 6 | //! 7 | //! Add the library to your Cargo.toml file: 8 | //! ```toml 9 | //! [dependencies] 10 | //! plate = "0.5" 11 | //! ``` 12 | 13 | pub mod buffer; 14 | pub use buffer::*; 15 | pub(crate) mod debug; 16 | pub(crate) use debug::*; 17 | pub mod descriptor; 18 | pub use descriptor::*; 19 | pub mod device; 20 | pub use device::*; 21 | pub mod instance; 22 | pub use instance::*; 23 | pub mod pipeline; 24 | pub use pipeline::*; 25 | pub(crate) mod surface; 26 | pub(crate) use surface::*; 27 | pub mod swapchain; 28 | pub use swapchain::*; 29 | pub mod command; 30 | pub use command::*; 31 | pub mod sync; 32 | pub use sync::*; 33 | pub mod image; 34 | pub use image::*; 35 | pub mod rendering; 36 | pub use rendering::*; 37 | 38 | pub use ash::vk; 39 | 40 | pub use ash::vk::Format; 41 | pub use ash::vk::MemoryPropertyFlags; 42 | 43 | #[derive(thiserror::Error, Debug)] 44 | pub enum Error { 45 | #[error("{0}")] 46 | VulkanError(#[from] ash::vk::Result), 47 | #[error("{0}")] 48 | DeviceError(#[from] DeviceError), 49 | #[error("{0}")] 50 | SwapchainError(#[from] SwapchainError), 51 | #[error("{0}")] 52 | InstanceError(#[from] InstanceError), 53 | #[error("{0}")] 54 | DescriptorError(#[from] DescriptorError), 55 | } 56 | 57 | #[cfg(feature = "macros")] 58 | pub use plate_macros; 59 | pub use memoffset; 60 | -------------------------------------------------------------------------------- /plate/src/pipeline.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi, sync::Arc}; 2 | 3 | use ash::vk; 4 | 5 | use crate::{DescriptorSetLayout, Device, Format, Error, CommandBuffer, RenderPass}; 6 | 7 | pub use vk::VertexInputRate as InputRate; 8 | pub use vk::FrontFace; 9 | pub use vk::CullModeFlags; 10 | 11 | /// Vertex binding information. 12 | /// 13 | /// Describes the size of a vertex and the binding to access it in the shader. 14 | pub struct VertexBindingDescription(vk::VertexInputBindingDescription); 15 | 16 | /// Vertex attribute information to pass to the shader. 17 | /// 18 | /// Describes the offset of a field of a vector, its format and the corresponding binding and 19 | /// location on the shader. 20 | pub struct VertexAttributeDescription(vk::VertexInputAttributeDescription); 21 | 22 | impl VertexBindingDescription { 23 | /// Creates a VertexBindingDescription. 24 | /// 25 | /// # Examples 26 | /// 27 | /// ```no_run 28 | /// struct Vertex(u32); 29 | /// let binding_description = plate::VertexBindingDescription::new( 30 | /// 0, 31 | /// std::mem::size_of::() as u32, 32 | /// plate::InputRate::VERTEX, 33 | /// ); 34 | /// # Ok::<(), Box>(()) 35 | /// ``` 36 | pub fn new(binding: u32, stride: u32, input_rate: InputRate) -> Self { 37 | Self( 38 | *ash::vk::VertexInputBindingDescription::builder() 39 | .binding(binding) 40 | .stride(stride) 41 | .input_rate(input_rate) 42 | ) 43 | } 44 | } 45 | 46 | impl VertexAttributeDescription { 47 | /// Creates a VertexAttributeDescription. 48 | /// 49 | /// # Examples 50 | /// 51 | /// ```no_run 52 | /// struct Vertex{ 53 | /// f1: u32, 54 | /// }; 55 | /// plate::VertexAttributeDescription::new( 56 | /// 0, 57 | /// 0, 58 | /// memoffset::offset_of!(Vertex, f1) as u32, 59 | /// plate::Format::R32_UINT, 60 | /// ); 61 | /// # Ok::<(), Box>(()) 62 | /// ``` 63 | pub fn new(binding: u32, location: u32, offset: u32, format: Format) -> Self { 64 | Self( 65 | *ash::vk::VertexInputAttributeDescription::builder() 66 | .binding(binding) 67 | .location(location) 68 | .format(format) 69 | .offset(offset) 70 | ) 71 | } 72 | } 73 | 74 | /// Trait for vertex structs, with binding and attribute descriptions. 75 | pub trait VertexDescription { 76 | /// Returns a Vec of BindingDescriptions corresponding to a vertex. 77 | fn binding_descriptions() -> Vec; 78 | /// Returns a Vec of AttributeDescriptions corresponding to the fields of a vertex. 79 | fn attribute_descriptions() -> Vec; 80 | } 81 | 82 | /// Aditional parameters for [`Pipeline`] creation. 83 | pub struct PipelineParameters<'a> { 84 | /// BindingDescriptions of the vertex to be used by the pipeline. 85 | pub vertex_binding_descriptions: Vec, 86 | /// AttributeDescriptions of the vertex to be used by the pipeline. 87 | pub vertex_attribute_descriptions: Vec, 88 | /// DescriptorSetLayouts to be used by the pipeline. 89 | pub descriptor_set_layouts: &'a [&'a DescriptorSetLayout], 90 | /// Direction of the vertices to consider front-facing. 91 | pub front_face: FrontFace, 92 | /// The orientation of triangles to cull. 93 | pub cull_mode: CullModeFlags, 94 | } 95 | 96 | impl<'a> Default for PipelineParameters<'_> { 97 | fn default() -> Self { 98 | Self { 99 | vertex_binding_descriptions: vec![], 100 | vertex_attribute_descriptions: vec![], 101 | descriptor_set_layouts: &[], 102 | front_face: FrontFace::COUNTER_CLOCKWISE, 103 | cull_mode: CullModeFlags::NONE, 104 | } 105 | } 106 | } 107 | 108 | /// A vulkan graphics pipeline 109 | /// 110 | /// The Pipeline is responsible for executing all the operations needed to transform the vertices 111 | /// of a scene into the pixels in the rendered image. 112 | pub struct Pipeline { 113 | device: Arc, 114 | pipeline: vk::Pipeline, 115 | pub(crate) layout: vk::PipelineLayout, 116 | vert_shader: vk::ShaderModule, 117 | frag_shader: vk::ShaderModule, 118 | } 119 | 120 | impl Drop for Pipeline { 121 | fn drop(&mut self) { 122 | unsafe { 123 | self.device.destroy_shader_module(self.vert_shader, None); 124 | self.device.destroy_shader_module(self.frag_shader, None); 125 | 126 | self.device.destroy_pipeline(self.pipeline, None); 127 | self.device.destroy_pipeline_layout(self.layout, None); 128 | } 129 | } 130 | } 131 | 132 | impl Pipeline { 133 | /// Creates a Pipeline. 134 | /// 135 | /// The vertex input data in the shaders must match the binding and attribute descriptions 136 | /// specified in `params`. 137 | /// 138 | /// # Examples 139 | /// 140 | /// ```no_run 141 | /// # let event_loop = winit::event_loop::EventLoop::new(); 142 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 143 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 144 | /// # let render_pass = plate::RenderPass::new(&device, &[], &[], &[])?; 145 | /// # let (vert_code, frag_code) = ([0], [0]); 146 | /// let pipeline = plate::pipeline::Pipeline::new( 147 | /// &device, 148 | /// &render_pass, 149 | /// &vert_code, 150 | /// &frag_code, 151 | /// &Default::default(), 152 | /// )?; 153 | /// # Ok::<(), Box>(()) 154 | /// ``` 155 | pub fn new( 156 | device: &Arc, 157 | render_pass: &RenderPass, 158 | vert_code: &[u32], 159 | frag_code: &[u32], 160 | params: &PipelineParameters, 161 | ) -> Result { 162 | let binding_descriptions: Vec<_> = params.vertex_binding_descriptions.iter().map(|b| b.0).collect(); 163 | let attribute_descriptions: Vec<_> = params.vertex_attribute_descriptions.iter().map(|a| a.0).collect(); 164 | 165 | let vert_shader_info = vk::ShaderModuleCreateInfo::builder().code(vert_code); 166 | let frag_shader_info = vk::ShaderModuleCreateInfo::builder().code(frag_code); 167 | 168 | let vert_shader = unsafe { 169 | device.create_shader_module(&vert_shader_info, None)? 170 | }; 171 | let frag_shader = unsafe { 172 | device.create_shader_module(&frag_shader_info, None)? 173 | }; 174 | 175 | let name = ffi::CString::new("main").expect("Should never fail to build \"main\" string"); 176 | 177 | let stage_infos = [ 178 | *vk::PipelineShaderStageCreateInfo::builder() 179 | .module(vert_shader) 180 | .stage(vk::ShaderStageFlags::VERTEX) 181 | .name(&name), 182 | *vk::PipelineShaderStageCreateInfo::builder() 183 | .module(frag_shader) 184 | .stage(vk::ShaderStageFlags::FRAGMENT) 185 | .name(&name), 186 | ]; 187 | 188 | let vertex_info = vk::PipelineVertexInputStateCreateInfo::builder() 189 | .vertex_binding_descriptions(&binding_descriptions) 190 | .vertex_attribute_descriptions(&attribute_descriptions); 191 | let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::builder() 192 | .topology(vk::PrimitiveTopology::TRIANGLE_LIST); 193 | 194 | let viewports = [vk::Viewport { 195 | x: 0.0, 196 | y: 0.0, 197 | width: 0.0, 198 | height: 0.0, 199 | min_depth: 0.0, 200 | max_depth: 1.0, 201 | }]; 202 | 203 | let scissors = [vk::Rect2D { 204 | offset: vk::Offset2D { x: 0, y: 0 }, 205 | extent: vk::Extent2D { 206 | width: 0, 207 | height: 0, 208 | }, 209 | }]; 210 | 211 | let viewport_state = vk::PipelineViewportStateCreateInfo::builder() 212 | .viewports(&viewports) 213 | .scissors(&scissors); 214 | 215 | let rasterization = vk::PipelineRasterizationStateCreateInfo::builder() 216 | .line_width(1.0) 217 | .front_face(params.front_face) 218 | .cull_mode(params.cull_mode) 219 | .polygon_mode(vk::PolygonMode::FILL); 220 | 221 | let multisampling = vk::PipelineMultisampleStateCreateInfo::builder() 222 | .sample_shading_enable(false) 223 | .rasterization_samples(vk::SampleCountFlags::TYPE_1); 224 | 225 | let color_blend_attachments = (0..render_pass.attachment_counts[0]).into_iter() 226 | .map(|_| { 227 | *vk::PipelineColorBlendAttachmentState::builder() 228 | .blend_enable(true) 229 | .src_color_blend_factor(vk::BlendFactor::SRC_ALPHA) 230 | .dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA) 231 | .color_blend_op(vk::BlendOp::ADD) 232 | .src_alpha_blend_factor(vk::BlendFactor::SRC_ALPHA) 233 | .dst_alpha_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA) 234 | .alpha_blend_op(vk::BlendOp::ADD) 235 | .color_write_mask(vk::ColorComponentFlags::RGBA) 236 | }) 237 | .collect::>(); 238 | 239 | let color_blend = 240 | vk::PipelineColorBlendStateCreateInfo::builder().attachments(&color_blend_attachments); 241 | 242 | let layouts = params.descriptor_set_layouts.into_iter() 243 | .map(|l| l.layout) 244 | .collect::>(); 245 | let layout_info = vk::PipelineLayoutCreateInfo::builder().set_layouts(&layouts); 246 | let layout = unsafe { 247 | device.create_pipeline_layout(&layout_info, None)? 248 | }; 249 | 250 | let dynamic_state = vk::PipelineDynamicStateCreateInfo::builder() 251 | .dynamic_states(&[vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]); 252 | 253 | let stencil_state = vk::PipelineDepthStencilStateCreateInfo::builder() 254 | .depth_test_enable(true) 255 | .depth_write_enable(true) 256 | .depth_compare_op(vk::CompareOp::LESS) 257 | .depth_bounds_test_enable(false) 258 | .stencil_test_enable(false); 259 | 260 | let pipeline_info = vk::GraphicsPipelineCreateInfo::builder() 261 | .stages(&stage_infos) 262 | .vertex_input_state(&vertex_info) 263 | .input_assembly_state(&input_assembly) 264 | .viewport_state(&viewport_state) 265 | .rasterization_state(&rasterization) 266 | .multisample_state(&multisampling) 267 | .color_blend_state(&color_blend) 268 | .layout(layout) 269 | .render_pass(render_pass.render_pass) 270 | .dynamic_state(&dynamic_state) 271 | .subpass(0) 272 | .depth_stencil_state(&stencil_state); 273 | 274 | let pipeline = match unsafe { device.create_graphics_pipelines(vk::PipelineCache::null(), &[*pipeline_info], None) } { 275 | Ok(p) => Ok(p[0]), 276 | Err((_, e)) => Err(e) 277 | }?; 278 | 279 | Ok(Self { 280 | device: Arc::clone(&device), 281 | pipeline, 282 | layout, 283 | vert_shader, 284 | frag_shader, 285 | }) 286 | } 287 | 288 | /// Binds the Pipeline. 289 | /// 290 | /// To be used when recording a command buffer. 291 | /// 292 | /// # Examples 293 | /// 294 | /// ```no_run 295 | /// # struct Vertex(f32); 296 | /// # let event_loop = winit::event_loop::EventLoop::new(); 297 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 298 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 299 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 300 | /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 301 | /// # let mut swapchain = plate::swapchain::Swapchain::new(&device, &window)?; 302 | /// # let render_pass = plate::RenderPass::new(&device, &[], &[], &[])?; 303 | /// # let pipeline = plate::pipeline::Pipeline::new(&device, &render_pass, &[], &[], 304 | /// # &Default::default())?; 305 | /// // cmd_buffer.record(.., || { 306 | /// pipeline.bind(&cmd_buffer, swapchain.extent()); 307 | /// // })?; 308 | /// # Ok::<(), Box>(()) 309 | /// ``` 310 | pub fn bind(&self, command_buffer: &CommandBuffer, extent: (u32, u32)) { 311 | unsafe { 312 | self.device.cmd_bind_pipeline(**command_buffer, vk::PipelineBindPoint::GRAPHICS, self.pipeline) 313 | } 314 | 315 | let viewports = [vk::Viewport { 316 | x: 0.0, 317 | y: 0.0, 318 | width: extent.0 as f32, 319 | height: extent.1 as f32, 320 | min_depth: 0.0, 321 | max_depth: 1.0, 322 | }]; 323 | unsafe { self.device.cmd_set_viewport(**command_buffer, 0, &viewports) }; 324 | 325 | let scissors = [vk::Rect2D { 326 | offset: vk::Offset2D { x: 0, y: 0 }, 327 | extent: vk::Extent2D { width: extent.0, height: extent.1 }, 328 | }]; 329 | unsafe { self.device.cmd_set_scissor(**command_buffer, 0, &scissors) }; 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /plate/src/rendering.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ash::vk; 4 | 5 | use crate::{Device, Error, Format, Image, CommandBuffer, PipelineStage}; 6 | 7 | pub use vk::AttachmentLoadOp; 8 | pub use vk::AttachmentStoreOp; 9 | pub use vk::ImageLayout; 10 | pub use vk::AccessFlags; 11 | 12 | /// Describes [`RenderPass`] Attachment. 13 | pub struct Attachment { 14 | /// Format of the image. 15 | pub format: Format, 16 | /// How the attachment is treated at the beginning of the subpass. 17 | pub load_op: AttachmentLoadOp, 18 | /// How the attachment is treated at the end of the subpass. 19 | pub store_op: AttachmentStoreOp, 20 | /// Layout of the image when the render pass begins. 21 | pub initial_layout: ImageLayout, 22 | /// Layout of the image when the render pass ends. 23 | pub final_layout: ImageLayout, 24 | } 25 | 26 | /// Describes an [`Attachment`] reference. 27 | #[derive(Copy, Clone)] 28 | pub struct AttachmentReference { 29 | /// Attachment index. 30 | pub attachment: u32, 31 | /// Layout of the atatchment during the subpass. 32 | pub layout: ImageLayout, 33 | } 34 | 35 | /// Describes the attachments of a subpass. 36 | pub struct SubpassDescription<'a> { 37 | /// Defines the input attachments. 38 | pub input_attachments: &'a [AttachmentReference], 39 | /// Defines the color attachments. 40 | pub color_attachments: &'a [AttachmentReference], 41 | /// Defines the depth attachment. 42 | pub depth_attachment: Option, 43 | /// The indices of attachments to preserve throughout the subpass. 44 | pub preserve_attachments: &'a [u32], 45 | /// Defines the resolve attachments. 46 | pub resolve_attachments: &'a [AttachmentReference], 47 | } 48 | 49 | impl<'a> Default for SubpassDescription<'a> { 50 | fn default() -> Self { 51 | Self { 52 | input_attachments: &[], 53 | color_attachments: &[], 54 | depth_attachment: None, 55 | preserve_attachments: &[], 56 | resolve_attachments: &[], 57 | } 58 | } 59 | } 60 | 61 | /// Subpass index. 62 | pub struct Subpass(pub u32); 63 | 64 | impl Subpass { 65 | /// Equivalent to [`vk::SUBPASS_EXTERNAL`], refers to outside of the render pass. 66 | pub const EXTERNAL: Self = Self(vk::SUBPASS_EXTERNAL); 67 | } 68 | 69 | /// Introduces a dependecy between two subpasses. 70 | pub struct SubpassDependency { 71 | /// First subpass. 72 | pub src_subpass: Subpass, 73 | /// Second subpass. 74 | pub dst_subpass: Subpass, 75 | /// Wait for first subpass commands to reach this stage. 76 | pub src_stage_mask: PipelineStage, 77 | /// Resume second subpass commands from this stage. 78 | pub dst_stage_mask: PipelineStage, 79 | /// Access scope of the first subpass. 80 | pub src_access_mask: AccessFlags, 81 | /// Access scope of the second subpass. 82 | pub dst_access_mask: AccessFlags, 83 | } 84 | 85 | /// Opaque handle to a [`vk::RenderPass`]. 86 | pub struct RenderPass { 87 | device: Arc, 88 | pub(crate) render_pass: vk::RenderPass, 89 | pub(crate) attachment_counts: Vec, 90 | clear_values: Vec, 91 | } 92 | 93 | impl Drop for RenderPass { 94 | fn drop(&mut self) { 95 | unsafe { 96 | self.device.destroy_render_pass(self.render_pass, None) 97 | } 98 | } 99 | } 100 | 101 | impl RenderPass { 102 | /// Creates a RenderPass. 103 | /// 104 | /// # Examples 105 | /// 106 | /// ```no_run 107 | /// # let event_loop = winit::event_loop::EventLoop::new(); 108 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 109 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 110 | /// # let attachments = []; 111 | /// # let subpasses = []; 112 | /// # let dependencies = []; 113 | /// // let attachments = [..]; 114 | /// // let subpasses = [..]; 115 | /// // let dependencies = [..]; 116 | /// let render_pass = plate::RenderPass::new(&device, &attachments, &subpasses, &dependencies)?; 117 | /// # Ok::<(), Box>(()) 118 | /// ``` 119 | pub fn new(device: &Arc, attachments: &[Attachment], subpasses: &[SubpassDescription], dependencies: &[SubpassDependency]) -> Result { 120 | let clear_values = attachments.iter() 121 | .map(|a| { 122 | match a.format { 123 | Format::D16_UNORM | Format::D32_SFLOAT | Format::D16_UNORM_S8_UINT | Format::D24_UNORM_S8_UINT | Format::D32_SFLOAT_S8_UINT => { 124 | vk::ClearValue { 125 | depth_stencil: vk::ClearDepthStencilValue { 126 | depth: 1.0, 127 | stencil: 0, 128 | } 129 | } 130 | } 131 | _ => { 132 | vk::ClearValue { 133 | color: vk::ClearColorValue { 134 | float32: [0.0, 0.0, 0.0, 1.0], 135 | } 136 | } 137 | } 138 | } 139 | }) 140 | .collect::>(); 141 | 142 | let atts = attachments.iter() 143 | .map(|a| { 144 | *vk::AttachmentDescription::builder() 145 | .format(a.format) 146 | .samples(vk::SampleCountFlags::TYPE_1) 147 | .load_op(a.load_op) 148 | .store_op(a.store_op) 149 | .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) 150 | .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) 151 | .initial_layout(a.initial_layout) 152 | .final_layout(a.final_layout) 153 | }) 154 | .collect::>(); 155 | 156 | let input_attachments = subpasses.iter() 157 | .map(|s| { 158 | s.input_attachments.iter() 159 | .map(|a| vk::AttachmentReference { attachment: a.attachment, layout: a.layout }) 160 | .collect::>() 161 | }) 162 | .collect::>(); 163 | let color_attachments = subpasses.iter() 164 | .map(|s| { 165 | s.color_attachments.iter() 166 | .map(|a| { 167 | vk::AttachmentReference { attachment: a.attachment, layout: a.layout } 168 | }) 169 | .collect::>() 170 | }) 171 | .collect::>(); 172 | let depth_attachments = subpasses.iter() 173 | .filter_map(|s| s.depth_attachment) 174 | .map(|d| vk::AttachmentReference { attachment: d.attachment, layout: d.layout }) 175 | .collect::>(); 176 | let preserve_attachments = subpasses.iter() 177 | .map(|s| s.preserve_attachments.clone()) 178 | .collect::>(); 179 | let resolve_attachments = subpasses.iter() 180 | .map(|s| { 181 | s.resolve_attachments.iter() 182 | .map(|a| vk::AttachmentReference { attachment: a.attachment, layout: a.layout }) 183 | .collect::>() 184 | }) 185 | .collect::>(); 186 | let subs = subpasses.iter() 187 | .enumerate() 188 | .map(|(i, _)| { 189 | let mut builder = vk::SubpassDescription::builder() 190 | .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS); 191 | if !depth_attachments.is_empty() { 192 | builder = builder.depth_stencil_attachment(&depth_attachments[i]) 193 | } 194 | if !input_attachments[i].is_empty() { 195 | builder = builder.input_attachments(&input_attachments[i]) 196 | } 197 | if !color_attachments[i].is_empty() { 198 | builder = builder.color_attachments(&color_attachments[i]) 199 | } 200 | if !preserve_attachments[i].is_empty() { 201 | builder = builder.preserve_attachments(&preserve_attachments[i]) 202 | } 203 | if !resolve_attachments[i].is_empty() { 204 | builder = builder.resolve_attachments(&resolve_attachments[i]) 205 | } 206 | *builder 207 | }) 208 | .collect::>(); 209 | 210 | let deps = dependencies.iter() 211 | .map(|d| { 212 | *vk::SubpassDependency::builder() 213 | .src_subpass(d.src_subpass.0) 214 | .dst_subpass(d.dst_subpass.0) 215 | .src_stage_mask(d.src_stage_mask) 216 | .dst_stage_mask(d.dst_stage_mask) 217 | .src_access_mask(d.src_access_mask) 218 | .dst_access_mask(d.dst_access_mask) 219 | .dependency_flags(vk::DependencyFlags::BY_REGION) 220 | }) 221 | .collect::>(); 222 | 223 | let render_pass_info = vk::RenderPassCreateInfo::builder() 224 | .attachments(&atts) 225 | .subpasses(&subs) 226 | .dependencies(&deps); 227 | 228 | let render_pass = unsafe { device.create_render_pass(&render_pass_info, None)? }; 229 | 230 | let attachment_counts = subpasses.iter() 231 | .map(|s| s.color_attachments.len()) 232 | .collect(); 233 | Ok(Self { 234 | device: Arc::clone(device), 235 | render_pass, 236 | attachment_counts, 237 | clear_values, 238 | }) 239 | } 240 | 241 | /// Begins the renderpass. 242 | /// 243 | /// `framebuffer` must contain the attachments described at render pass creation. To be used 244 | /// when recording a [`CommandBuffer`]. 245 | /// 246 | /// # Examples 247 | /// 248 | /// ```no_run 249 | /// # let event_loop = winit::event_loop::EventLoop::new(); 250 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 251 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 252 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 253 | /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 254 | /// # let render_pass = plate::RenderPass::new(&device, &[], &[], &[])?; 255 | /// # let framebuffer = plate::Framebuffer::new(&device, &render_pass, &[], 0, 0)?; 256 | /// // cmd_buffer.record(.., || { 257 | /// render_pass.begin(&cmd_buffer, &framebuffer); 258 | /// // })?; 259 | /// # Ok::<(), Box>(()) 260 | /// ``` 261 | pub fn begin(&self, cmd_buffer: &CommandBuffer, framebuffer: &Framebuffer) { 262 | let begin_info = vk::RenderPassBeginInfo::builder() 263 | .render_pass(self.render_pass) 264 | .framebuffer(framebuffer.framebuffer) 265 | .render_area(vk::Rect2D { 266 | offset: vk::Offset2D { x: 0, y: 0 }, 267 | extent: framebuffer.extent, 268 | }) 269 | .clear_values(&self.clear_values); 270 | 271 | unsafe { 272 | self.device.cmd_begin_render_pass( 273 | **cmd_buffer, 274 | &begin_info, 275 | vk::SubpassContents::INLINE, 276 | ) 277 | } 278 | } 279 | 280 | /// Ends the renderpass. 281 | /// 282 | /// # Examples 283 | /// 284 | /// ```no_run 285 | /// # let event_loop = winit::event_loop::EventLoop::new(); 286 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 287 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 288 | /// # let cmd_pool = plate::CommandPool::new(&device)?; 289 | /// # let cmd_buffer = cmd_pool.alloc_cmd_buffer(plate::CommandBufferLevel::PRIMARY)?; 290 | /// # let render_pass = plate::RenderPass::new(&device, &[], &[], &[])?; 291 | /// # let framebuffer = plate::Framebuffer::new(&device, &render_pass, &[], 0, 0)?; 292 | /// // cmd_buffer.record(.., || { 293 | /// render_pass.end(&cmd_buffer); 294 | /// // })?; 295 | /// # Ok::<(), Box>(()) 296 | /// ``` 297 | pub fn end(&self, cmd_buffer: &CommandBuffer) { 298 | unsafe { self.device.cmd_end_render_pass(**cmd_buffer) } 299 | } 300 | } 301 | 302 | /// Reference the attachments used by a [`RenderPass`]. 303 | pub struct Framebuffer { 304 | device: Arc, 305 | framebuffer: vk::Framebuffer, 306 | extent: vk::Extent2D, 307 | } 308 | 309 | impl Drop for Framebuffer { 310 | fn drop(&mut self) { 311 | unsafe { 312 | self.device.destroy_framebuffer(self.framebuffer, None) 313 | } 314 | } 315 | } 316 | 317 | impl Framebuffer { 318 | /// Creates a Framebuffer. 319 | /// 320 | /// # Examples 321 | /// 322 | /// ```no_run 323 | /// # let event_loop = winit::event_loop::EventLoop::new(); 324 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 325 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 326 | /// # let image = plate::Image::new(&device, 0, 0, 327 | /// # plate::Format::UNDEFINED, plate::ImageLayout::UNDEFINED, 328 | /// # plate::ImageUsageFlags::empty(), plate::ImageAspectFlags::empty())?; 329 | /// # let render_pass = plate::RenderPass::new(&device, &[], &[], &[])?; 330 | /// let framebuffer = plate::Framebuffer::new( 331 | /// &device, 332 | /// &render_pass, 333 | /// &[&image], 334 | /// image.width, 335 | /// image.height 336 | /// )?; 337 | /// # Ok::<(), Box>(()) 338 | /// ``` 339 | pub fn new(device: &Arc, render_pass: &RenderPass, attachments: &[&Image], width: u32, height: u32) -> Result { 340 | let attachments = attachments.iter() 341 | .map(|i| i.view) 342 | .collect::>(); 343 | 344 | let framebuffer_info = vk::FramebufferCreateInfo::builder() 345 | .render_pass(render_pass.render_pass) 346 | .attachments(&attachments) 347 | .width(width) 348 | .height(height) 349 | .layers(1); 350 | 351 | let framebuffer = unsafe { device.create_framebuffer(&framebuffer_info, None)? }; 352 | 353 | let extent = vk::Extent2D { 354 | width, 355 | height, 356 | }; 357 | 358 | Ok(Self { 359 | device: Arc::clone(device), 360 | framebuffer, 361 | extent, 362 | }) 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /plate/src/shaders/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec3 fragColor; 4 | 5 | layout(location = 0) out vec4 outColor; 6 | 7 | void main() { 8 | outColor = vec4(fragColor, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /plate/src/shaders/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(binding = 0) uniform Ubo { 4 | mat4 model; 5 | mat4 view; 6 | mat4 proj; 7 | } ubo; 8 | 9 | layout(location = 0) in vec3 pos; 10 | layout(location = 1) in vec3 color; 11 | 12 | layout(location = 0) out vec3 fragColor; 13 | 14 | void main() { 15 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(pos, 1.0); 16 | fragColor = color; 17 | } 18 | -------------------------------------------------------------------------------- /plate/src/surface.rs: -------------------------------------------------------------------------------- 1 | use ash::{extensions::khr, vk}; 2 | 3 | use crate::{Instance, Error}; 4 | 5 | /// An abstraction of the window to be used by Vulkan. 6 | pub(crate) struct Surface { 7 | pub surface_loader: khr::Surface, 8 | pub surface: vk::SurfaceKHR, 9 | } 10 | 11 | impl Drop for Surface { 12 | fn drop(&mut self) { 13 | unsafe { 14 | self.surface_loader.destroy_surface(self.surface, None); 15 | } 16 | } 17 | } 18 | 19 | impl Surface { 20 | pub fn new( 21 | instance: &Instance, 22 | window: &winit::window::Window, 23 | ) -> Result { 24 | let surface_loader = khr::Surface::new(&instance.entry, &instance); 25 | let surface = unsafe { ash_window::create_surface(&instance.entry, &instance, &window, None)? }; 26 | 27 | Ok(Self { 28 | surface_loader, 29 | surface, 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /plate/src/swapchain.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ash::{extensions::khr, vk}; 4 | 5 | use crate::{Device, sync::*, image::*, Format, Error, Surface}; 6 | 7 | /// Errors from the swapchain module. 8 | #[derive(thiserror::Error, Debug)] 9 | pub enum SwapchainError { 10 | /// None of the available image formats match the depth requirements. 11 | #[error("No suitable depth format is available")] 12 | NoSuitableDepthFormat, 13 | } 14 | 15 | /// The Swapchain is responsible for providing images to be rendered to the screen. 16 | pub struct Swapchain { 17 | device: Arc, 18 | #[allow(dead_code)] 19 | surface: Surface, 20 | 21 | swapchain_loader: khr::Swapchain, 22 | swapchain: vk::SwapchainKHR, 23 | 24 | extent: vk::Extent2D, 25 | 26 | pub images: Vec, 27 | pub surface_format: Format, 28 | pub depth_format: Format, 29 | } 30 | 31 | impl Drop for Swapchain { 32 | fn drop(&mut self) { 33 | unsafe { 34 | self.swapchain_loader 35 | .destroy_swapchain(self.swapchain, None); 36 | } 37 | } 38 | } 39 | 40 | impl Swapchain { 41 | /// Creates a Swapchain. 42 | /// 43 | /// # Examples 44 | /// 45 | /// ```no_run 46 | /// # struct Vertex(f32); 47 | /// # let event_loop = winit::event_loop::EventLoop::new(); 48 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 49 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 50 | /// let swapchain = plate::Swapchain::new(&device, &window)?; 51 | /// # Ok::<(), Box>(()) 52 | /// ``` 53 | pub fn new( 54 | device: &Arc, 55 | window: &winit::window::Window, 56 | ) -> Result { 57 | let surface = Surface::new(&device.instance, &window)?; 58 | 59 | let ( 60 | swapchain_loader, 61 | swapchain, 62 | extent, 63 | images, 64 | surface_format, 65 | depth_format, 66 | ) = Self::create_swapchain(device, &surface, window, None)?; 67 | 68 | Ok(Self { 69 | device: Arc::clone(&device), 70 | surface, 71 | swapchain_loader, 72 | swapchain, 73 | extent, 74 | images, 75 | surface_format, 76 | depth_format, 77 | }) 78 | } 79 | 80 | /// Recreates the swapchain. 81 | /// 82 | /// Sould be called if the window was resized or the surface format has changed. 83 | /// 84 | /// # Examples 85 | /// 86 | /// ```no_run 87 | /// # struct Vertex(f32); 88 | /// # let event_loop = winit::event_loop::EventLoop::new(); 89 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 90 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 91 | /// let mut swapchain = plate::Swapchain::new(&device, &window)?; 92 | /// swapchain.recreate(&window)?; 93 | /// # Ok::<(), Box>(()) 94 | /// ``` 95 | pub fn recreate(&mut self, window: &winit::window::Window) -> Result<(), Error> { 96 | self.device.wait_idle()?; 97 | 98 | let ( 99 | swapchain_loader, 100 | swapchain, 101 | extent, 102 | images, 103 | surface_format, 104 | depth_format, 105 | ) = Self::create_swapchain(&self.device, &self.surface, window, Some(self.swapchain))?; 106 | 107 | unsafe { 108 | self.swapchain_loader 109 | .destroy_swapchain(self.swapchain, None); 110 | } 111 | 112 | self.swapchain_loader = swapchain_loader; 113 | self.swapchain = swapchain; 114 | self.extent = extent; 115 | self.images = images; 116 | self.surface_format = surface_format; 117 | self.depth_format = depth_format; 118 | 119 | Ok(()) 120 | } 121 | 122 | /// Acquires the next available swapchain image. 123 | /// 124 | /// Returns the index of the next available image from the swapchain and whetherthe swapchain 125 | /// is suboptimal. Will signal the provided semaphore when done. 126 | /// 127 | /// # Examples 128 | /// 129 | /// ```no_run 130 | /// # let event_loop = winit::event_loop::EventLoop::new(); 131 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 132 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 133 | /// # let mut swapchain = plate::Swapchain::new(&device, &window)?; 134 | /// # let acquire_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 135 | /// let (image_index, _) = swapchain.next_image(&acquire_sem).unwrap(); 136 | /// # Ok::<(), Box>(()) 137 | /// ``` 138 | pub fn next_image(&self, semaphore: &Semaphore) -> Result<(u32, bool), Error> { 139 | Ok(unsafe { 140 | self.swapchain_loader.acquire_next_image( 141 | self.swapchain, 142 | u64::MAX, 143 | **semaphore, 144 | vk::Fence::null(), 145 | )? 146 | }) 147 | } 148 | 149 | /// Present the image at `image_index` to the screen. 150 | /// 151 | /// Will wait on wait_semaphore. 152 | /// 153 | /// # Examples 154 | /// 155 | /// ```no_run 156 | /// # let event_loop = winit::event_loop::EventLoop::new(); 157 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 158 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 159 | /// # let mut swapchain = plate::Swapchain::new(&device, &window)?; 160 | /// # let present_sem = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 161 | /// let image_index = 0; 162 | /// swapchain.present(image_index, &present_sem).unwrap(); 163 | /// # Ok::<(), Box>(()) 164 | /// ``` 165 | pub fn present(&self, image_index: u32, wait_semaphore: &Semaphore) -> Result { 166 | let swapchains = [self.swapchain]; 167 | let wait_semaphores = [**wait_semaphore]; 168 | let image_indices = [image_index]; 169 | 170 | let present_info = vk::PresentInfoKHR::builder() 171 | .wait_semaphores(&wait_semaphores) 172 | .swapchains(&swapchains) 173 | .image_indices(&image_indices); 174 | 175 | Ok(unsafe { self.swapchain_loader.queue_present(self.device.queue.queue, &present_info)? }) 176 | } 177 | 178 | /// Returns the aspect ration of the extent. 179 | /// 180 | /// # Examples 181 | /// 182 | /// ```no_run 183 | /// # struct Vertex(f32); 184 | /// # let event_loop = winit::event_loop::EventLoop::new(); 185 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 186 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 187 | /// # let mut swapchain = plate::Swapchain::new(&device, &window)?; 188 | /// let aspect_ratio = swapchain.aspect_ratio(); 189 | /// # Ok::<(), Box>(()) 190 | /// ``` 191 | pub fn aspect_ratio(&self) -> f32 { 192 | (self.extent.width as f32) / (self.extent.height as f32) 193 | } 194 | 195 | /// Returns the swapchain extent. 196 | /// 197 | /// # Examples 198 | /// 199 | /// ```no_run 200 | /// # let event_loop = winit::event_loop::EventLoop::new(); 201 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 202 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 203 | /// # let mut swapchain = plate::Swapchain::new(&device, &window)?; 204 | /// let (width, height) = swapchain.extent(); 205 | /// # Ok::<(), Box>(()) 206 | /// ``` 207 | pub fn extent(&self) -> (u32, u32) { 208 | (self.extent.width, self.extent.height) 209 | } 210 | 211 | fn create_swapchain( 212 | device: &Arc, 213 | surface: &Surface, 214 | window: &winit::window::Window, 215 | old_swapchain: Option, 216 | ) -> Result<( 217 | khr::Swapchain, 218 | vk::SwapchainKHR, 219 | vk::Extent2D, 220 | Vec, 221 | Format, 222 | Format, 223 | ), Error> { 224 | let surface_capabilities = unsafe { 225 | surface 226 | .surface_loader 227 | .get_physical_device_surface_capabilities( 228 | device.physical_device, 229 | surface.surface, 230 | )? 231 | }; 232 | let surface_formats = unsafe { 233 | surface 234 | .surface_loader 235 | .get_physical_device_surface_formats( 236 | device.physical_device, 237 | surface.surface, 238 | )? 239 | }; 240 | let present_modes = unsafe { 241 | surface 242 | .surface_loader 243 | .get_physical_device_surface_present_modes( 244 | device.physical_device, 245 | surface.surface, 246 | )? 247 | }; 248 | 249 | let image_format = surface_formats 250 | .iter() 251 | .find(|format| { 252 | format.format == vk::Format::R8G8B8A8_SRGB 253 | && format.color_space == vk::ColorSpaceKHR::SRGB_NONLINEAR 254 | }) 255 | .unwrap_or(&surface_formats[0]); 256 | 257 | let present_mode = *present_modes 258 | .iter() 259 | .find(|mode| **mode == vk::PresentModeKHR::FIFO) 260 | .unwrap_or(&present_modes[0]); 261 | 262 | let window_extent = window.inner_size(); 263 | let extent = vk::Extent2D { 264 | width: window_extent.width.clamp( 265 | surface_capabilities.min_image_extent.width, 266 | surface_capabilities.max_image_extent.width, 267 | ), 268 | height: window_extent.height.clamp( 269 | surface_capabilities.min_image_extent.height, 270 | surface_capabilities.max_image_extent.height, 271 | ), 272 | }; 273 | 274 | let queue_families = [device.queue.family]; 275 | 276 | let image_count = if surface_capabilities.max_image_count == 0 { 277 | surface_capabilities.min_image_count + 1 278 | } else { 279 | (surface_capabilities.min_image_count + 1).min(surface_capabilities.max_image_count) 280 | }; 281 | 282 | let mut swapchain_info = vk::SwapchainCreateInfoKHR::builder() 283 | .surface(surface.surface) 284 | .min_image_count(image_count) 285 | .image_format(image_format.format) 286 | .image_color_space(image_format.color_space) 287 | .image_extent(extent) 288 | .image_array_layers(1) 289 | .image_usage(vk::ImageUsageFlags::COLOR_ATTACHMENT) 290 | .image_sharing_mode(vk::SharingMode::EXCLUSIVE) 291 | .queue_family_indices(&queue_families) 292 | .pre_transform(surface_capabilities.current_transform) 293 | .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) 294 | .present_mode(present_mode) 295 | .clipped(true); 296 | 297 | match old_swapchain { 298 | Some(swapchain) => swapchain_info = swapchain_info.old_swapchain(swapchain), 299 | None => (), 300 | }; 301 | 302 | let swapchain_loader = khr::Swapchain::new(&device.instance, &device); 303 | let swapchain = unsafe { swapchain_loader.create_swapchain(&swapchain_info, None)? }; 304 | 305 | let images = unsafe { swapchain_loader.get_swapchain_images(swapchain)? }.into_iter() 306 | .map(|i| Image::from_vk_image(device, i, None, extent.width, extent.height, image_format.format, ImageAspectFlags::COLOR)) 307 | .collect::, _>>()?; 308 | 309 | let depth_format = [ 310 | vk::Format::D32_SFLOAT, 311 | vk::Format::D32_SFLOAT_S8_UINT, 312 | vk::Format::D24_UNORM_S8_UINT 313 | ].into_iter() 314 | .find(|format| { 315 | let props = unsafe { device.instance.get_physical_device_format_properties(device.physical_device, *format) }; 316 | props.optimal_tiling_features.contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT) 317 | }) 318 | .ok_or(SwapchainError::NoSuitableDepthFormat)?; 319 | 320 | Ok(( 321 | swapchain_loader, 322 | swapchain, 323 | extent, 324 | images, 325 | image_format.format, 326 | depth_format, 327 | )) 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /plate/src/sync.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ash::vk; 4 | 5 | use crate::{Device, Error}; 6 | 7 | pub use vk::FenceCreateFlags as FenceFlags; 8 | pub use vk::SemaphoreCreateFlags as SemaphoreFlags; 9 | 10 | /// Used to synchronize the host with the GPU. 11 | /// 12 | /// Some GPU operations can set the Fence to be signaled or unsignaled, the host can then wait on 13 | /// for these operation to finish accordingly. 14 | pub struct Fence { 15 | device: Arc, 16 | fence: vk::Fence, 17 | } 18 | 19 | impl Drop for Fence { 20 | fn drop(&mut self) { unsafe { self.device.destroy_fence(self.fence, None) } 21 | } 22 | } 23 | 24 | impl std::ops::Deref for Fence { 25 | type Target = vk::Fence; 26 | 27 | fn deref(&self) -> &Self::Target { 28 | &self.fence 29 | } 30 | } 31 | 32 | impl Fence { 33 | /// Creates a Fence. 34 | /// 35 | /// Eamples 36 | /// ```no_run 37 | /// # let event_loop = winit::event_loop::EventLoop::new(); 38 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 39 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 40 | /// let fence = plate::Fence::new(&device, plate::FenceFlags::SIGNALED)?; 41 | /// # Ok::<(), Box>(()) 42 | /// ``` 43 | pub fn new(device: &Arc, flags: FenceFlags) -> Result { 44 | let info = vk::FenceCreateInfo::builder().flags(flags); 45 | let fence = unsafe { device.create_fence(&info, None)? }; 46 | 47 | Ok(Self { 48 | device: Arc::clone(&device), 49 | fence, 50 | }) 51 | } 52 | 53 | /// Block until the Fence is signaled. 54 | /// 55 | /// Eamples 56 | /// ```no_run 57 | /// # let event_loop = winit::event_loop::EventLoop::new(); 58 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 59 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 60 | /// # let fence = plate::Fence::new(&device, plate::FenceFlags::SIGNALED)?; 61 | /// fence.wait()?; 62 | /// # Ok::<(), Box>(()) 63 | /// ``` 64 | pub fn wait(&self) -> Result<(), Error> { 65 | Ok(unsafe { self.device.wait_for_fences(&[self.fence], true, u64::MAX)? }) 66 | } 67 | 68 | /// Resets the state of the Fence to unsignaled. 69 | /// 70 | /// Eamples 71 | /// ```no_run 72 | /// # let event_loop = winit::event_loop::EventLoop::new(); 73 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 74 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 75 | /// # let fence = plate::Fence::new(&device, plate::FenceFlags::SIGNALED)?; 76 | /// fence.wait()?; 77 | /// # Ok::<(), Box>(()) 78 | /// ``` 79 | pub fn reset(&self) -> Result<(), Error> { 80 | Ok(unsafe { self.device.reset_fences(&[self.fence])? }) 81 | } 82 | } 83 | 84 | /// Used to synchronize the execution of GPU instructions. 85 | /// 86 | /// The GPU executes instructions in parallel, to make sure these instructions run at the correct 87 | /// order, some operations can wait on or set the state of Semaphores to be signaled or unsignaled. 88 | pub struct Semaphore { 89 | device: Arc, 90 | semaphore: vk::Semaphore, 91 | } 92 | 93 | impl Drop for Semaphore { 94 | fn drop(&mut self) { 95 | unsafe { self.device.destroy_semaphore(self.semaphore, None) } 96 | } 97 | } 98 | 99 | impl std::ops::Deref for Semaphore { 100 | type Target = vk::Semaphore; 101 | 102 | fn deref(&self) -> &Self::Target { 103 | &self.semaphore 104 | } 105 | } 106 | 107 | impl Semaphore { 108 | /// Creates a Semaphore. 109 | /// 110 | /// Eamples 111 | /// ```no_run 112 | /// # let event_loop = winit::event_loop::EventLoop::new(); 113 | /// # let window = winit::window::WindowBuilder::new().build(&event_loop)?; 114 | /// # let device = plate::Device::new(&Default::default(), &Default::default(), Some(&window))?; 115 | /// let semaphore = plate::Semaphore::new(&device, plate::SemaphoreFlags::empty())?; 116 | /// # Ok::<(), Box>(()) 117 | /// ``` 118 | pub fn new(device: &Arc, flags: SemaphoreFlags) -> Result { 119 | let info = vk::SemaphoreCreateInfo::builder().flags(flags); 120 | let semaphore = unsafe { device.create_semaphore(&info, None)? }; 121 | 122 | Ok(Self { 123 | device: Arc::clone(&device), 124 | semaphore, 125 | }) 126 | } 127 | } 128 | --------------------------------------------------------------------------------