├── .gitignore ├── LICENSE-MIT ├── Cargo.toml ├── src ├── lib.rs ├── reclamation.rs ├── backend.rs ├── escape.rs ├── init.rs ├── renderer.rs ├── upload.rs └── factory.rs ├── README.md └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 The Gfx Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gfx-render" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | license = "MIT/Apache-2.0" 6 | repository = "https://github.com/gfx-rs/gfx-render.git" 7 | documentation = "https://docs.rs/crate/gfx-render/0.1.0/gfx-render" 8 | 9 | [features] 10 | profile = ["flame"] 11 | [dependencies] 12 | crossbeam-channel = "0.1" 13 | failure = "0.1" 14 | flame = { version = "0.2", optional = true } 15 | gfx-hal = { version = "0.1", git = "https://github.com/gfx-rs/gfx", rev = "6cb2a800b", features = ["serde"] } 16 | gfx-memory = { git = "https://github.com/gfx-rs/gfx-memory", rev = "4f6e410" } 17 | log = "0.4" 18 | regex = { version = "1.0", optional = true } 19 | serde = { version = "1.0", optional = true } 20 | winit = "0.15" 21 | 22 | [target.'cfg(all(features = "serde", features = "regex"))'.dependencies] 23 | serde_regex = { version = "0.2" } 24 | 25 | [target.'cfg(not(target_os = "macos"))'.dependencies] 26 | gfx-backend-vulkan = { version = "0.1", optional = true, git = "https://github.com/gfx-rs/gfx", rev = "6cb2a800b" } 27 | 28 | [target.'cfg(target_os = "macos")'.dependencies] 29 | gfx-backend-metal = { version = "0.1", optional = true, git = "https://github.com/gfx-rs/gfx", rev = "6cb2a800b" } 30 | 31 | [target.'cfg(windows)'.dependencies] 32 | gfx-backend-dx12 = { version = "0.1", optional = true, git = "https://github.com/gfx-rs/gfx", rev = "6cb2a800b" } 33 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate crossbeam_channel; 2 | #[macro_use] 3 | extern crate failure; 4 | extern crate gfx_hal as hal; 5 | extern crate gfx_memory as mem; 6 | #[macro_use] 7 | extern crate log; 8 | #[cfg(feature = "regex")] 9 | extern crate regex; 10 | 11 | #[cfg(feature = "serde")] 12 | #[macro_use] 13 | extern crate serde; 14 | 15 | #[cfg(all(feature = "serde", feature = "regex"))] 16 | extern crate serde_regex; 17 | extern crate winit; 18 | 19 | #[cfg(feature = "profile")] 20 | extern crate flame; 21 | 22 | #[cfg(feature = "profile")] 23 | macro_rules! profile { 24 | ($name:tt) => { 25 | let guard = ::flame::start_guard(concat!("'", $name, "' at : ", line!())); 26 | }; 27 | } 28 | 29 | #[cfg(not(feature = "profile"))] 30 | macro_rules! profile { 31 | ($name:tt) => {}; 32 | } 33 | 34 | #[cfg(feature = "gfx-backend-vulkan")] 35 | pub extern crate gfx_backend_vulkan as vulkan; 36 | 37 | #[cfg(feature = "gfx-backend-dx12")] 38 | pub extern crate gfx_backend_dx12 as dx12; 39 | 40 | #[cfg(feature = "gfx-backend-metal")] 41 | pub extern crate gfx_backend_metal as metal; 42 | 43 | mod backend; 44 | mod escape; 45 | mod factory; 46 | mod init; 47 | mod reclamation; 48 | mod renderer; 49 | mod upload; 50 | 51 | pub use backend::BackendEx; 52 | pub use factory::{Buffer, Factory, Image, Item}; 53 | pub use init::{init, Config, MemoryConfig, AdapterPicker, QueuesPicker, FirstAdapter, adapter_picker, adapter_by_name, adapter_by_name_regex, queue_picker}; 54 | pub use renderer::{Render, Renderer, TargetId}; 55 | -------------------------------------------------------------------------------- /src/reclamation.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | struct ReclamationNode { 3 | items: Vec, 4 | } 5 | 6 | #[derive(Debug)] 7 | pub struct ReclamationQueue { 8 | offset: u64, 9 | queue: Vec>, 10 | cache: Vec>, 11 | } 12 | 13 | impl ReclamationQueue { 14 | pub fn new() -> Self { 15 | ReclamationQueue { 16 | offset: 0, 17 | queue: Vec::new(), 18 | cache: Vec::new(), 19 | } 20 | } 21 | 22 | fn grow(&mut self, current: u64) { 23 | if self.queue.is_empty() { 24 | self.offset = current; 25 | } 26 | 27 | for _ in self.queue.len()..(current - self.offset + 1) as usize { 28 | self.queue.push( 29 | self.cache 30 | .pop() 31 | .unwrap_or_else(|| ReclamationNode { items: Vec::new() }), 32 | ) 33 | } 34 | } 35 | 36 | pub fn push(&mut self, current: u64, item: I) { 37 | self.grow(current); 38 | self.queue[(current - self.offset) as usize] 39 | .items 40 | .push(item); 41 | } 42 | 43 | pub fn clear(&mut self, ongoing: u64, mut f: F) 44 | where 45 | F: FnMut(I), 46 | { 47 | use std::cmp::min; 48 | let clear_until = (ongoing - self.offset) as usize; 49 | let clear_until = min(clear_until, self.queue.len()); 50 | for mut node in self.queue.drain(..clear_until) { 51 | for item in node.items.drain(..) { 52 | f(item); 53 | } 54 | self.cache.push(node); 55 | } 56 | self.offset = ongoing; 57 | } 58 | } 59 | 60 | impl Drop for ReclamationQueue { 61 | fn drop(&mut self) { 62 | assert!(self.queue.is_empty()); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/backend.rs: -------------------------------------------------------------------------------- 1 | //! This module provide extension trait `BackendEx` that is implemented for some gfx backends: 2 | //! vulkan, dx12 and metal if corresponding feature is enabled. 3 | //! Also empty backend implements `BackendEx` if no backend-features enabled. 4 | //! 5 | 6 | use hal::{Backend, Instance}; 7 | use winit::Window; 8 | 9 | #[cfg(feature = "gfx-backend-vulkan")] 10 | use vulkan; 11 | 12 | #[cfg(feature = "gfx-backend-metal")] 13 | use metal; 14 | 15 | #[cfg(feature = "gfx-backend-dx12")] 16 | use dx12; 17 | 18 | /// Extend backend trait with initialization method and surface creation method. 19 | pub trait BackendEx: Backend { 20 | type Instance: Instance + Send + Sync; 21 | fn init() -> Self::Instance; 22 | fn create_surface(instance: &Self::Instance, window: &Window) -> Self::Surface; 23 | } 24 | 25 | #[cfg(feature = "gfx-backend-vulkan")] 26 | impl BackendEx for vulkan::Backend { 27 | type Instance = vulkan::Instance; 28 | fn init() -> Self::Instance { 29 | vulkan::Instance::create("gfx-render", 1) 30 | } 31 | fn create_surface(instance: &Self::Instance, window: &Window) -> Self::Surface { 32 | trace!("vulkan::Backend::create_surface"); 33 | instance.create_surface(window) 34 | } 35 | } 36 | 37 | #[cfg(feature = "gfx-backend-metal")] 38 | impl BackendEx for metal::Backend { 39 | type Instance = metal::Instance; 40 | fn init() -> Self::Instance { 41 | metal::Instance::create("gfx-render", 1) 42 | } 43 | fn create_surface(instance: &Self::Instance, window: &Window) -> Self::Surface { 44 | trace!("metal::Backend::create_surface"); 45 | instance.create_surface(window) 46 | } 47 | } 48 | 49 | #[cfg(feature = "gfx-backend-dx12")] 50 | impl BackendEx for dx12::Backend { 51 | type Instance = dx12::Instance; 52 | fn init() -> Self::Instance { 53 | dx12::Instance::create("gfx-render", 1) 54 | } 55 | fn create_surface(instance: &Self::Instance, window: &Window) -> Self::Surface { 56 | trace!("dx12::Backend::create_surface"); 57 | instance.create_surface(window) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Attention 2 | 3 | This project is discontinued in favor of [rendy](https://github.com/omni-viral/rendy). 4 | 5 | # gfx-render 6 | 7 | This crates adds basic functionality around [`gfx-hal`](https://github.com/gfx-rs/gfx) through two primary types `Factory` and `Renderer` that can be instantiated together by `init` function. 8 | 9 | `Factory`'s functionality: 10 | 1. Allocating wrapped buffers and images using [`gfx-memory`'s](https://github.com/gfx-rs/gfx-memory) `SmartAllocator`. 11 | Wrapper will release resource automatically on drop. 12 | Also supports manual deallocation with less overhead than automatic. 13 | 1. Preserving deallocated buffers and images until they are not references by GPU's in-progress commands. 14 | Simply by waiting for all jobs that were recording or in-progress at the moment of deallocation to complete. 15 | 1. Uploading data to buffers and images with method chosen based on memory properties. 16 | 1. Report features and limits of physical device and capabilities and formats for the surface. 17 | 1. Substitute `B::Device` in generic code. `Factory` implements `Device`. 18 | 19 | `Renderer`'s functionality: 20 | 1. Creating rendering targets. Currently only surfaces from `winit::Window`. But headless mode is planned. 21 | 1. Instantiating custom `Render` implementation for each target. Designed to be compatible with [`xfg` crate](https://github.com/omni-viral/xfg-rs) but not limiting to. 22 | 1. Kicking off rendering jobs and managing completion through fences. 23 | 24 | ## License 25 | 26 | [license]: #license 27 | 28 | This repository is licensed under either of 29 | 30 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 31 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 32 | 33 | at your option. 34 | 35 | ## Contributions 36 | 37 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 38 | 39 | ## What else 40 | 41 | Other crates that aim to simplify usage of [`gfx-hal`](https://github.com/gfx-rs/gfx): 42 | * [`gfx-memory`](https://github.com/gfx-rs/gfx-memory) - memory allocators. Used in `gfx-render` internally but `Factory` can give access to the underlying allocator. 43 | * [`gfx-chain`](https://github.com/omni-viral/gfx-chain) - automatic synchronization. Requires up front dependencies declaration. 44 | * [`gfx-mesh`](https://github.com/omni-viral/gfx-mesh) - create meshes from vertex and index data with easy-to-use API. Dependes on `gfx-render`. 45 | 46 | -------------------------------------------------------------------------------- /src/escape.rs: -------------------------------------------------------------------------------- 1 | //! Module provides wrapper for types that cannot be dropped silently. 2 | //! Usually such types are required to be returned to their creator. 3 | //! `Escape` wrapper help the user to do so by sending underlying value to the `Terminal` when it is dropped. 4 | //! Users are encouraged to dispose of the values manually while `Escape` be just a safety net. 5 | 6 | use crossbeam_channel::{unbounded, Receiver, Sender, TryIter, TryRecvError}; 7 | use std::mem::{forget, ManuallyDrop}; 8 | use std::ops::{Deref, DerefMut}; 9 | use std::ptr::read; 10 | 11 | /// Wraps value of any type and send it to the `Terminal` from which the wrapper was created. 12 | /// In case `Terminal` is already dropped then value will be cast into oblivion via `std::mem::forget`. 13 | #[derive(Debug, Clone)] 14 | pub struct Escape { 15 | value: ManuallyDrop, 16 | sender: Sender, 17 | } 18 | 19 | impl Escape { 20 | /// Unwrap the value. 21 | pub fn into_inner(escape: Self) -> T { 22 | Self::deconstruct(escape).0 23 | } 24 | 25 | fn deconstruct(mut escape: Self) -> (T, Sender) { 26 | unsafe { 27 | let value = read(&mut *escape.value); 28 | let sender = read(&mut escape.sender); 29 | forget(escape); 30 | (value, sender) 31 | } 32 | } 33 | } 34 | 35 | impl Deref for Escape { 36 | type Target = T; 37 | fn deref(&self) -> &T { 38 | &*self.value 39 | } 40 | } 41 | 42 | impl DerefMut for Escape { 43 | fn deref_mut(&mut self) -> &mut T { 44 | &mut *self.value 45 | } 46 | } 47 | 48 | impl Drop for Escape { 49 | fn drop(&mut self) { 50 | let value = unsafe { read(&mut *self.value) }; 51 | self.sender 52 | .send(value) 53 | .unwrap_or_else(|value| forget(value)); 54 | } 55 | } 56 | 57 | /// This types allows the user to create `Escape` wrappers. 58 | /// Receives values from dropped `Escape` instances that was created by this `Terminal`. 59 | #[derive(Debug)] 60 | pub struct Terminal { 61 | receiver: Receiver, 62 | sender: ManuallyDrop>, 63 | } 64 | 65 | impl Default for Terminal { 66 | fn default() -> Self { 67 | Self::new() 68 | } 69 | } 70 | 71 | impl Terminal { 72 | /// Create new `Terminal`. 73 | pub fn new() -> Self { 74 | let (sender, receiver) = unbounded(); 75 | Terminal { 76 | sender: ManuallyDrop::new(sender), 77 | receiver, 78 | } 79 | } 80 | 81 | /// Wrap the value. It will be yielded by iterator returned by `Terminal::drain` if `Escape` will be dropped. 82 | pub fn escape(&self, value: T) -> Escape { 83 | Escape { 84 | value: ManuallyDrop::new(value), 85 | sender: Sender::clone(&self.sender), 86 | } 87 | } 88 | 89 | /// Get iterator over values from dropped `Escape` instances that was created by this `Terminal`. 90 | pub fn drain(&mut self) -> TryIter { 91 | self.receiver.try_iter() 92 | } 93 | } 94 | 95 | impl Drop for Terminal { 96 | fn drop(&mut self) { 97 | unsafe { 98 | ManuallyDrop::drop(&mut self.sender); 99 | match self.receiver.try_recv() { 100 | Err(TryRecvError::Disconnected) => {} 101 | _ => { 102 | panic!("Terminal must be dropped after all `Escape`s"); 103 | } 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/init.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use hal::{ 4 | adapter::{Adapter, PhysicalDevice}, error::DeviceCreationError, queue::QueueFamily, Backend, 5 | Instance, 6 | }; 7 | 8 | use backend::BackendEx; 9 | use factory::Factory; 10 | use mem::SmartAllocator; 11 | use renderer::Renderer; 12 | 13 | #[cfg(feature = "regex")] 14 | use regex::Regex; 15 | 16 | const STAGING_TRESHOLD: usize = 32 * 1024; // 32kb 17 | 18 | /// Trait for picking adapter among available. 19 | pub trait AdapterPicker { 20 | fn pick_adapter(self, adapters: Vec>) -> Option>; 21 | } 22 | 23 | /// `AdapterPicker` that picks first available adapter. 24 | #[derive(Clone, Copy, Debug, Default)] 25 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 26 | pub struct FirstAdapter; 27 | 28 | impl AdapterPicker for FirstAdapter 29 | where 30 | B: Backend, 31 | { 32 | fn pick_adapter(self, mut adapters: Vec>) -> Option> { 33 | Some(adapters.remove(0)) 34 | } 35 | } 36 | 37 | pub struct AdapterFn(F, PhantomData); 38 | 39 | /// Pick adapter with given closure. 40 | pub fn adapter_picker(f: F) -> AdapterFn 41 | where 42 | B: Backend, 43 | F: FnOnce(Vec>) -> Option>, 44 | { 45 | AdapterFn(f, PhantomData) 46 | } 47 | 48 | impl AdapterPicker for AdapterFn 49 | where 50 | B: Backend, 51 | F: FnOnce(Vec>) -> Option>, 52 | { 53 | fn pick_adapter(self, adapters: Vec>) -> Option> { 54 | (self.0)(adapters) 55 | } 56 | } 57 | 58 | /// `AdapterPicker` which searches for adapter with matching name. 59 | #[derive(Clone, Copy, Debug)] 60 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 61 | pub struct AdapterByName(T); 62 | 63 | /// Choose adapter by name. 64 | pub fn adapter_by_name(name: T) -> AdapterByName 65 | where 66 | T: PartialEq 67 | { 68 | AdapterByName(name) 69 | } 70 | 71 | impl AdapterPicker for AdapterByName 72 | where 73 | B: Backend, 74 | T: PartialEq, 75 | { 76 | fn pick_adapter(self, adapters: Vec>) -> Option> { 77 | adapters 78 | .into_iter() 79 | .find(|adapter| self.0.eq(&adapter.info.name)) 80 | } 81 | } 82 | 83 | /// `AdapterPicker` which searches for adapter with matching name by regex. 84 | #[cfg(feature = "regex")] 85 | #[derive(Clone, Copy, Debug)] 86 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 87 | pub struct AdapterByNameRe(#[cfg_attr(feature = "serde", serde(with = "::serde_regex"))] Regex); 88 | 89 | /// Choose adapter by name matching regex. 90 | pub fn adapter_by_name_regex(name: Regex) -> AdapterByNameRe { 91 | AdapterByNameRe(name) 92 | } 93 | 94 | #[cfg(feature = "regex")] 95 | impl AdapterPicker for AdapterByNameRe 96 | where 97 | B: Backend, 98 | { 99 | fn pick_adapter(self, adapters: Vec>) -> Option> { 100 | adapters 101 | .into_iter() 102 | .find(|adapter| self.0.is_match(&adapter.info.name)) 103 | } 104 | } 105 | 106 | /// Trait for picking queue families among available. 107 | pub trait QueuesPicker { 108 | fn pick_queues(self, families: &[B::QueueFamily]) -> Vec<(&B::QueueFamily, usize)>; 109 | } 110 | 111 | pub struct QueueFn(F, PhantomData); 112 | 113 | /// Pick queues with given closure. 114 | pub fn queue_picker(f: F) -> QueueFn 115 | where 116 | B: Backend, 117 | F: FnOnce(&[B::QueueFamily]) -> Vec<(&B::QueueFamily, usize)>, 118 | { 119 | QueueFn(f, PhantomData) 120 | } 121 | 122 | impl QueuesPicker for QueueFn 123 | where 124 | B: Backend, 125 | F: FnOnce(&[B::QueueFamily]) -> Vec<(&B::QueueFamily, usize)>, 126 | { 127 | fn pick_queues(self, families: &[B::QueueFamily]) -> Vec<(&B::QueueFamily, usize)> { 128 | (self.0)(families) 129 | } 130 | } 131 | 132 | /// Configuration for memory allocator in `Factory`. 133 | #[derive(Clone, Copy, Debug)] 134 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 135 | pub struct MemoryConfig { 136 | /// Size of one chunk in arena-based allocator. 137 | arena_chunk_size: u64, 138 | 139 | /// Blocks per chunk in block-based allocator. 140 | blocks_per_chunk: usize, 141 | 142 | /// Smallest block size in block-based allocator. 143 | /// Any lesser allocation will be of this size. 144 | min_block_size: u64, 145 | 146 | /// Biggest block size in block-based allocator. 147 | /// Any bigger allocation will allocate directly from gpu. 148 | max_chunk_size: u64, 149 | } 150 | 151 | impl Default for MemoryConfig { 152 | fn default() -> Self { 153 | MemoryConfig { 154 | arena_chunk_size: 32, 155 | blocks_per_chunk: 32, 156 | min_block_size: 32, 157 | max_chunk_size: 1024 * 1024 * 32, 158 | } 159 | } 160 | } 161 | 162 | /// Configuration for initialization. 163 | #[derive(Clone, Copy, Debug, Default)] 164 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 165 | pub struct Config { 166 | /// `AdapterPicker` implementation. 167 | /// Will be used to pick from available adapters. 168 | adapter_picker: A, 169 | 170 | /// `QueuesPicker` implementation. 171 | /// Will be used to pick from available queue families. 172 | queues_picker: Q, 173 | 174 | /// Config for allocator. 175 | memory: MemoryConfig, 176 | } 177 | 178 | /// Possible errors during initialization 179 | #[derive(Clone, Debug, Fail)] 180 | pub enum InitError { 181 | #[fail(display = "Failed to choose from available adapters")] 182 | AdapterNotChosen, 183 | 184 | #[fail(display = "Failed to create logical device")] 185 | DeviceCreationError(#[cause] DeviceCreationError), 186 | } 187 | 188 | /// Init chosen backend. 189 | /// Creates `Factory` and `Renderer` instances. 190 | pub fn init(adapter_picker: A, queues_picker: Q, memory: MemoryConfig) -> Result<(Factory, Renderer), InitError> 191 | where 192 | B: BackendEx, 193 | R: Send + Sync + 'static, 194 | A: AdapterPicker, 195 | Q: QueuesPicker, 196 | { 197 | let instance = B::init(); 198 | let adapter = adapter_picker 199 | .pick_adapter(instance.enumerate_adapters()) 200 | .ok_or(InitError::AdapterNotChosen)?; 201 | trace!("Adapter {:#?}", adapter.info); 202 | 203 | trace!("Device features: {:#?}", adapter.physical_device.features()); 204 | trace!("Device limits: {:#?}", adapter.physical_device.limits()); 205 | 206 | let (device, queue_groups) = { 207 | trace!("Queue families: {:#?}", adapter.queue_families); 208 | 209 | let queues = queues_picker 210 | .pick_queues(&adapter.queue_families) 211 | .into_iter() 212 | .map(|(qf, count)| (qf, vec![1.0; count])) 213 | .collect::>(); 214 | let queues = queues 215 | .iter() 216 | .map(|&(qf, ref priorities)| (qf, priorities.as_slice())) 217 | .collect::>(); 218 | 219 | let mut gpu = adapter 220 | .physical_device 221 | .open(&queues) 222 | .map_err(InitError::DeviceCreationError)?; 223 | let queue_groups = queues 224 | .iter() 225 | .map(|&(qf, _)| { 226 | ( 227 | qf.id(), 228 | gpu.queues 229 | .take_raw(qf.id()) 230 | .expect("Family with id was requested"), 231 | ) 232 | }) 233 | .collect(); 234 | (gpu.device, queue_groups) 235 | }; 236 | trace!("Logical device created"); 237 | 238 | let allocator = SmartAllocator::::new( 239 | adapter.physical_device.memory_properties(), 240 | memory.arena_chunk_size, 241 | memory.blocks_per_chunk, 242 | memory.min_block_size, 243 | memory.max_chunk_size, 244 | ); 245 | trace!("Allocator created: {:#?}", allocator); 246 | 247 | let factory = Factory::new( 248 | instance, 249 | adapter.info, 250 | adapter.physical_device, 251 | device, 252 | allocator, 253 | STAGING_TRESHOLD, 254 | ); 255 | let renderer = Renderer::::new(queue_groups, adapter.queue_families); 256 | 257 | Ok((factory, renderer)) 258 | } 259 | -------------------------------------------------------------------------------- /src/renderer.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::marker::PhantomData; 3 | 4 | use failure::Error; 5 | 6 | use hal::device::WaitFor; 7 | use hal::queue::{QueueFamilyId, RawCommandQueue, RawSubmission}; 8 | use hal::window::Backbuffer; 9 | use hal::{Backend, Device as HalDevice}; 10 | 11 | use winit::Window; 12 | 13 | use backend::BackendEx; 14 | 15 | #[cfg(feature = "gfx-backend-metal")] 16 | use metal; 17 | 18 | use factory::Factory; 19 | 20 | pub trait Render { 21 | fn run( 22 | &mut self, 23 | fences: &mut Vec, 24 | queues: &mut HashMap>, 25 | factory: &mut Factory, 26 | data: &mut T, 27 | ) -> usize; 28 | 29 | fn dispose(self, factory: &mut Factory, data: &mut T) -> Backbuffer; 30 | } 31 | 32 | #[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] 33 | pub struct TargetId(u64); 34 | 35 | pub struct Renderer { 36 | autorelease: AutoreleasePool, 37 | targets: HashMap>, 38 | families: Families, 39 | counter: u64, 40 | } 41 | 42 | impl Renderer 43 | where 44 | B: BackendEx, 45 | { 46 | /// Dispose of the renderer. 47 | pub fn dispose(self, factory: &mut Factory, data: &mut T) 48 | where 49 | R: Render, 50 | { 51 | for (_, target) in self.targets { 52 | target.dispose(factory, data); 53 | } 54 | self.families.dispose(factory); 55 | } 56 | 57 | /// Creates new render target 58 | pub fn add_target(&mut self, window: &Window, factory: &mut Factory) -> TargetId { 59 | self.counter += 1; 60 | let id = TargetId(self.counter); 61 | debug_assert!(self.targets.get(&id).is_none()); 62 | 63 | let surface = factory.create_surface(window); 64 | 65 | let target = Target { 66 | surface, 67 | render: None, 68 | fences: Vec::new(), 69 | }; 70 | self.targets.insert(id, target); 71 | id 72 | } 73 | 74 | /// Remove render 75 | pub fn remove_target(&mut self, id: TargetId, factory: &mut Factory, data: &mut T) 76 | where 77 | R: Render, 78 | { 79 | if let Some(target) = self.targets.remove(&id) { 80 | target.dispose(factory, data); 81 | } 82 | } 83 | 84 | /// Add graph to the render 85 | pub fn set_render( 86 | &mut self, 87 | id: TargetId, 88 | factory: &mut Factory, 89 | data: &mut T, 90 | render: F, 91 | ) -> Result<(), Error> 92 | where 93 | F: FnOnce(&mut B::Surface, &[B::QueueFamily], &mut Factory, &mut T) -> Result, 94 | E: Into, 95 | R: Render, 96 | { 97 | let ref mut target = *self 98 | .targets 99 | .get_mut(&id) 100 | .ok_or(format_err!("No target with id {:#?}", id))?; 101 | 102 | target.wait(factory); 103 | target 104 | .render 105 | .take() 106 | .map(|render| render.dispose(factory, data)); 107 | target.render = Some( 108 | render(&mut target.surface, &self.families.families, factory, data) 109 | .map_err(|err| err.into().context("Failed to build render"))?, 110 | ); 111 | 112 | Ok(()) 113 | } 114 | 115 | /// Create new render system providing it with general queue group and surfaces to draw onto 116 | pub fn new( 117 | queues: HashMap>, 118 | families: Vec, 119 | ) -> Self 120 | where 121 | R: Send + Sync, 122 | { 123 | fn is_send_sync() {} 124 | is_send_sync::(); 125 | 126 | Renderer { 127 | autorelease: AutoreleasePool::new(), 128 | targets: HashMap::new(), 129 | counter: 0, 130 | families: Families { queues, families }, 131 | } 132 | } 133 | 134 | #[inline] 135 | pub fn run(&mut self, factory: &mut Factory, data: &mut T) 136 | where 137 | B: Backend, 138 | R: Render, 139 | { 140 | profile!("Renderer::run"); 141 | self.poll_uploads(factory); 142 | 143 | // Run targets 144 | for target in self.targets.values_mut() { 145 | target.run(&mut self.families, factory, data); 146 | } 147 | 148 | unsafe { 149 | // cleanup after finished jobs. 150 | factory.advance(); 151 | } 152 | 153 | self.autorelease.reset(); 154 | } 155 | 156 | #[inline] 157 | fn poll_uploads(&mut self, factory: &mut Factory) 158 | where 159 | B: Backend, 160 | { 161 | profile!("Renderer::poll_uploads"); 162 | 163 | let ref mut queues = self.families.queues; 164 | for (cbuf, fid) in factory.uploads() { 165 | profile!("command buffer"); 166 | 167 | let family = queues.get_mut(&fid).unwrap(); 168 | 169 | unsafe { 170 | family[0].submit_raw( 171 | RawSubmission { 172 | cmd_buffers: Some(cbuf), 173 | wait_semaphores: &[], 174 | signal_semaphores: &[], 175 | }, 176 | None, 177 | ); 178 | } 179 | } 180 | } 181 | } 182 | 183 | struct Target { 184 | surface: B::Surface, 185 | render: Option, 186 | fences: Vec, 187 | } 188 | 189 | impl Target 190 | where 191 | B: Backend, 192 | { 193 | #[inline] 194 | fn wait(&mut self, factory: &mut Factory) { 195 | profile!("Target::wait"); 196 | if !self.fences.is_empty() && !factory.wait_for_fences(&self.fences, WaitFor::All, !0) { 197 | panic!("Device lost or something"); 198 | } 199 | } 200 | 201 | #[inline] 202 | fn run(&mut self, families: &mut Families, factory: &mut Factory, data: &mut T) 203 | where 204 | R: Render, 205 | { 206 | profile!("Target::run"); 207 | self.wait(factory); 208 | 209 | if let Some(ref mut render) = self.render { 210 | render.run(&mut self.fences, &mut families.queues, factory, data); 211 | }; 212 | } 213 | 214 | fn dispose(mut self, factory: &mut Factory, _data: &mut T) 215 | where 216 | R: Render, 217 | { 218 | self.wait(factory); 219 | unimplemented!() 220 | } 221 | } 222 | 223 | struct Families { 224 | queues: HashMap>, 225 | families: Vec, 226 | } 227 | 228 | impl Families 229 | where 230 | B: Backend, 231 | { 232 | fn dispose(self, _factory: &mut Factory) { 233 | unimplemented!() 234 | } 235 | } 236 | 237 | struct AutoreleasePool { 238 | #[cfg(feature = "gfx-backend-metal")] 239 | autorelease: Option, 240 | _pd: PhantomData<*mut B>, 241 | } 242 | 243 | #[cfg(feature = "gfx-backend-metal")] 244 | impl AutoreleasePool { 245 | #[inline(always)] 246 | fn new() -> Self { 247 | use std::any::TypeId; 248 | AutoreleasePool { 249 | autorelease: { 250 | if TypeId::of::() == TypeId::of::() { 251 | Some(unsafe { metal::AutoreleasePool::new() }) 252 | } else { 253 | None 254 | } 255 | }, 256 | _pd: PhantomData, 257 | } 258 | } 259 | 260 | #[inline(always)] 261 | fn reset(&mut self) { 262 | profile!("Autorelease"); 263 | use std::any::TypeId; 264 | if TypeId::of::() == TypeId::of::() { 265 | unsafe { 266 | profile!("Autorelease::reset"); 267 | self.autorelease.as_mut().unwrap().reset(); 268 | } 269 | } 270 | } 271 | } 272 | 273 | #[cfg(not(feature = "gfx-backend-metal"))] 274 | impl AutoreleasePool { 275 | #[inline(always)] 276 | fn new() -> Self { 277 | AutoreleasePool { _pd: PhantomData } 278 | } 279 | 280 | #[inline(always)] 281 | fn reset(&mut self) {} 282 | } 283 | 284 | unsafe impl Send for AutoreleasePool {} 285 | unsafe impl Sync for AutoreleasePool {} 286 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/upload.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Borrow; 2 | use std::collections::{HashMap, VecDeque}; 3 | use std::fmt; 4 | use std::slice::from_raw_parts_mut; 5 | 6 | use hal::buffer; 7 | use hal::command::{BufferCopy, BufferImageCopy, CommandBufferFlags, RawCommandBuffer, RawLevel}; 8 | use hal::image; 9 | use hal::memory::{Barrier, Dependencies, Properties}; 10 | use hal::pool::{CommandPoolCreateFlags, RawCommandPool}; 11 | use hal::pso::PipelineStage; 12 | use hal::queue::QueueFamilyId; 13 | use hal::{Backend, Device}; 14 | 15 | use factory::{BufferCreationError, RelevantBuffer, RelevantImage}; 16 | use mem::{Block, Type}; 17 | 18 | #[derive(Debug)] 19 | struct FamilyDebug { 20 | cbuf: bool, 21 | free: usize, 22 | used: usize, 23 | } 24 | 25 | struct Family { 26 | pool: B::CommandPool, 27 | cbuf: Option, 28 | free: Vec, 29 | used: VecDeque<(B::CommandBuffer, u64)>, 30 | } 31 | 32 | impl fmt::Debug for Family 33 | where 34 | B: Backend, 35 | { 36 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 37 | fmt::Debug::fmt( 38 | &FamilyDebug { 39 | cbuf: self.cbuf.is_some(), 40 | free: self.free.len(), 41 | used: self.used.len(), 42 | }, 43 | fmt, 44 | ) 45 | } 46 | } 47 | 48 | /// Errors occurred during data uploading. 49 | #[derive(Clone, Debug, Fail)] 50 | pub enum Error { 51 | #[fail(display = "Failed to create staging buffer")] 52 | StagingCreationError(#[cause] BufferCreationError), 53 | 54 | #[fail(display = "Data is larger than buffer it should be uploaded to")] 55 | OutOfBounds, 56 | } 57 | 58 | impl From for Error { 59 | fn from(error: BufferCreationError) -> Self { 60 | Error::StagingCreationError(error) 61 | } 62 | } 63 | 64 | #[derive(Debug)] 65 | pub struct Upload { 66 | staging_threshold: usize, 67 | families: HashMap>, 68 | } 69 | 70 | impl Upload 71 | where 72 | B: Backend, 73 | { 74 | pub fn dispose(mut self, device: &B::Device) { 75 | self.clear(u64::max_value()); 76 | 77 | for (_, mut family) in self.families { 78 | family.free.clear(); 79 | assert!(family.used.is_empty()); 80 | unsafe { 81 | family 82 | .pool 83 | .free(family.cbuf.into_iter().chain(family.free).collect()); 84 | } 85 | device.destroy_command_pool(family.pool); 86 | } 87 | } 88 | 89 | pub fn new(staging_threshold: usize) -> Self { 90 | Upload { 91 | staging_threshold, 92 | families: HashMap::new(), 93 | } 94 | } 95 | 96 | /// # Safety 97 | /// 98 | /// `properties` must match those of `buffer`. 99 | /// `allocator` must return correct properties. 100 | pub unsafe fn upload_buffer( 101 | &mut self, 102 | device: &B::Device, 103 | allocator: A, 104 | buffer: &mut RelevantBuffer, 105 | properties: Properties, 106 | fid: QueueFamilyId, 107 | access: buffer::Access, 108 | offset: u64, 109 | data: &[u8], 110 | ) -> Result>, Error> 111 | where 112 | A: FnMut(Type, Properties, u64, buffer::Usage) 113 | -> Result<(RelevantBuffer, Properties), BufferCreationError>, 114 | { 115 | if buffer.size() < offset + data.len() as u64 { 116 | return Err(Error::OutOfBounds); 117 | } 118 | if properties.contains(Properties::CPU_VISIBLE) { 119 | update_cpu_visible_block::( 120 | device, 121 | properties.contains(Properties::COHERENT), 122 | buffer, 123 | offset, 124 | data, 125 | ); 126 | Ok(None) 127 | } else { 128 | self.upload_device_local_buffer(device, allocator, buffer, fid, access, offset, data) 129 | } 130 | } 131 | 132 | /// # Safety 133 | /// 134 | /// `allocator` must return correct properties. 135 | pub unsafe fn upload_image( 136 | &mut self, 137 | device: &B::Device, 138 | mut allocator: A, 139 | image: &mut RelevantImage, 140 | fid: QueueFamilyId, 141 | access: image::Access, 142 | layout: image::Layout, 143 | layers: image::SubresourceLayers, 144 | offset: image::Offset, 145 | extent: image::Extent, 146 | data_width: u32, 147 | data_height: u32, 148 | data: &[u8], 149 | ) -> Result, Error> 150 | where 151 | A: FnMut(Type, Properties, u64, buffer::Usage) 152 | -> Result<(RelevantBuffer, Properties), BufferCreationError>, 153 | { 154 | assert!(data_width >= extent.width); 155 | assert!(data_height >= extent.height); 156 | assert_eq!(layers.aspects.bits().count_ones(), 1); 157 | assert!(data_width as usize * data_height as usize * extent.depth as usize <= data.len()); 158 | 159 | let (staging, properties) = allocator( 160 | Type::ShortLived, 161 | Properties::CPU_VISIBLE, 162 | data.len() as u64, 163 | buffer::Usage::TRANSFER_SRC, 164 | )?; 165 | update_cpu_visible_block::( 166 | device, 167 | properties.contains(Properties::COHERENT), 168 | &staging, 169 | 0, 170 | data, 171 | ); 172 | 173 | let uploading_layout = if layout == image::Layout::General { 174 | image::Layout::General 175 | } else { 176 | image::Layout::TransferDstOptimal 177 | }; 178 | 179 | let cbuf = self.get_command_buffer(device, fid); 180 | cbuf.copy_buffer_to_image( 181 | staging.borrow(), 182 | (&*image).borrow(), 183 | uploading_layout, 184 | Some(BufferImageCopy { 185 | buffer_offset: 0, 186 | buffer_width: data_width, 187 | buffer_height: data_height, 188 | image_layers: layers.clone(), 189 | image_offset: offset, 190 | image_extent: extent, 191 | }), 192 | ); 193 | 194 | cbuf.pipeline_barrier( 195 | PipelineStage::TRANSFER..PipelineStage::TOP_OF_PIPE, 196 | Dependencies::empty(), 197 | Some(Barrier::Image { 198 | states: (image::Access::TRANSFER_WRITE, uploading_layout)..(access, layout), 199 | target: (&*image).borrow(), 200 | range: image::SubresourceRange { 201 | aspects: layers.aspects, 202 | levels: layers.level..layers.level, 203 | layers: layers.layers, 204 | }, 205 | }), 206 | ); 207 | Ok(staging) 208 | } 209 | 210 | #[inline] 211 | pub fn uploads( 212 | &mut self, 213 | frame: u64, 214 | ) -> impl Iterator { 215 | self.families.iter_mut().filter_map(move |(&fid, family)| { 216 | if let Some(mut cbuf) = family.cbuf.take() { 217 | cbuf.finish(); 218 | family.used.push_back((cbuf, frame)); 219 | Some((&mut family.used.back_mut().unwrap().0, fid)) 220 | } else { 221 | None 222 | } 223 | }) 224 | } 225 | 226 | pub fn clear(&mut self, ongoing: u64) { 227 | self.families.iter_mut().for_each(|(_, family)| { 228 | while let Some((mut cbuf, frame)) = family.used.pop_front() { 229 | if frame >= ongoing { 230 | family.used.push_front((cbuf, ongoing)); 231 | break; 232 | } 233 | cbuf.reset(true); 234 | family.free.push(cbuf); 235 | } 236 | }); 237 | } 238 | 239 | fn get_command_buffer<'a>( 240 | &'a mut self, 241 | device: &B::Device, 242 | fid: QueueFamilyId, 243 | ) -> &'a mut B::CommandBuffer { 244 | let Family { 245 | ref mut pool, 246 | ref mut cbuf, 247 | ref mut free, 248 | .. 249 | } = *self.families.entry(fid).or_insert(Family { 250 | pool: device.create_command_pool(fid, CommandPoolCreateFlags::RESET_INDIVIDUAL), 251 | cbuf: None, 252 | free: Vec::new(), 253 | used: VecDeque::new(), 254 | }); 255 | cbuf.get_or_insert_with(|| { 256 | let mut cbuf = free 257 | .pop() 258 | .unwrap_or_else(|| pool.allocate(1, RawLevel::Primary).remove(0)); 259 | cbuf.begin(CommandBufferFlags::ONE_TIME_SUBMIT, Default::default()); 260 | cbuf 261 | }) 262 | } 263 | 264 | /// # Safety 265 | /// 266 | /// `allocator` must return correct properties. 267 | unsafe fn upload_device_local_buffer( 268 | &mut self, 269 | device: &B::Device, 270 | mut allocator: A, 271 | buffer: &mut RelevantBuffer, 272 | fid: QueueFamilyId, 273 | access: buffer::Access, 274 | offset: u64, 275 | data: &[u8], 276 | ) -> Result>, Error> 277 | where 278 | A: FnMut(Type, Properties, u64, buffer::Usage) 279 | -> Result<(RelevantBuffer, Properties), BufferCreationError>, 280 | { 281 | if data.len() <= self.staging_threshold { 282 | let cbuf = self.get_command_buffer(device, fid); 283 | cbuf.update_buffer((&*buffer).borrow(), offset, data); 284 | 285 | cbuf.pipeline_barrier( 286 | PipelineStage::TRANSFER..PipelineStage::TOP_OF_PIPE, 287 | Dependencies::empty(), 288 | Some(Barrier::Buffer { 289 | states: buffer::Access::TRANSFER_WRITE..access, 290 | target: (&*buffer).borrow(), 291 | }), 292 | ); 293 | Ok(None) 294 | } else { 295 | let (staging, properties) = allocator( 296 | Type::ShortLived, 297 | Properties::CPU_VISIBLE, 298 | data.len() as u64, 299 | buffer::Usage::TRANSFER_SRC, 300 | )?; 301 | update_cpu_visible_block::( 302 | device, 303 | properties.contains(Properties::COHERENT), 304 | &staging, 305 | 0, 306 | data, 307 | ); 308 | let cbuf = self.get_command_buffer(device, fid); 309 | cbuf.copy_buffer( 310 | staging.borrow(), 311 | (&*buffer).borrow(), 312 | Some(BufferCopy { 313 | src: 0, 314 | dst: offset, 315 | size: data.len() as u64, 316 | }), 317 | ); 318 | cbuf.pipeline_barrier( 319 | PipelineStage::TRANSFER..PipelineStage::TOP_OF_PIPE, 320 | Dependencies::empty(), 321 | Some(Barrier::Buffer { 322 | states: buffer::Access::TRANSFER_WRITE..access, 323 | target: (&*buffer).borrow(), 324 | }), 325 | ); 326 | Ok(Some(staging)) 327 | } 328 | } 329 | } 330 | 331 | /// Update cpu-visible block. 332 | /// 333 | /// # Safety 334 | /// 335 | /// Caller must be sure that memory of the block has `CPU_VISIBLE` property. 336 | /// `coherent` argument must be set to `true` only if memory of the block has `COHERENT` property. 337 | /// 338 | pub unsafe fn update_cpu_visible_block( 339 | device: &B::Device, 340 | coherent: bool, 341 | buffer: &RelevantBuffer, 342 | offset: u64, 343 | data: &[u8], 344 | ) { 345 | let start = buffer.range().start + offset; 346 | let end = start + data.len() as u64; 347 | let range = start..end; 348 | debug_assert!( 349 | end <= buffer.range().end, 350 | "Checked in `Upload::upload` method" 351 | ); 352 | let ptr = device 353 | .map_memory(buffer.memory(), range.clone()) 354 | .expect("Expect to be mapped"); 355 | if !coherent { 356 | device.invalidate_mapped_memory_ranges(Some((buffer.memory(), range.clone()))); 357 | } 358 | let slice = from_raw_parts_mut(ptr, data.len()); 359 | slice.copy_from_slice(data); 360 | if !coherent { 361 | device.flush_mapped_memory_ranges(Some((buffer.memory(), range))); 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /src/factory.rs: -------------------------------------------------------------------------------- 1 | //! This module provide convenient `Factory` type that can be used for several tasks: 2 | //! 1. Creating new `Buffer`s and `Image`s. 3 | //! 2. Destroying `Buffer`s and `Image`s with additional safety. 4 | //! 3. Uploading data to `Buffer`s and `Image`s. 5 | //! 4. Creating `Surface`s and fetching their capabilities. 6 | //! 5. Fetching `Features` and `Limits` of the GPU. 7 | //! All this packet into sendable and shareable `Factory` type. 8 | //! 9 | 10 | use std::any::Any; 11 | use std::borrow::{Borrow, BorrowMut}; 12 | use std::fmt::Debug; 13 | use std::mem::ManuallyDrop; 14 | use std::ops::Range; 15 | 16 | use hal::{ 17 | buffer, device::{BindError, FramebufferError, OutOfMemory, ShaderError, WaitFor}, 18 | error::HostExecutionError, format::{Format, Swizzle}, image, mapping::{self, Reader, Writer}, 19 | memory::{Properties, Requirements}, pass::{Attachment, SubpassDependency, SubpassDesc}, 20 | pool::{CommandPool, CommandPoolCreateFlags}, pso, query::QueryType, 21 | queue::{QueueFamilyId, QueueGroup}, range::RangeArg, window, AdapterInfo, Backend, Device, 22 | Features, Limits, MemoryTypeId, PhysicalDevice, Surface, 23 | }; 24 | 25 | use mem::{Block, MemoryAllocator, MemoryError, SmartAllocator, SmartBlock, Type}; 26 | 27 | use winit::Window; 28 | 29 | use backend::BackendEx; 30 | use escape::{Escape, Terminal}; 31 | use reclamation::ReclamationQueue; 32 | use upload::{self, Upload}; 33 | 34 | /// Item that must be destructed manually 35 | #[derive(Debug)] 36 | pub struct RelevantItem { 37 | raw: T, 38 | block: B, 39 | } 40 | 41 | impl Borrow for RelevantItem { 42 | fn borrow(&self) -> &T { 43 | &self.raw 44 | } 45 | } 46 | 47 | impl BorrowMut for RelevantItem { 48 | fn borrow_mut(&mut self) -> &mut T { 49 | &mut self.raw 50 | } 51 | } 52 | 53 | impl Block for RelevantItem 54 | where 55 | T: Debug + Send + Sync, 56 | B: Block, 57 | { 58 | type Memory = B::Memory; 59 | fn memory(&self) -> &Self::Memory { 60 | self.block.memory() 61 | } 62 | fn range(&self) -> Range { 63 | self.block.range() 64 | } 65 | } 66 | 67 | /// Buffer that must be destructed manually 68 | pub type RelevantBuffer = 69 | RelevantItem<::Buffer, SmartBlock<::Memory>>; 70 | 71 | /// Image that must be destructed manually 72 | pub type RelevantImage = RelevantItem<::Image, SmartBlock<::Memory>>; 73 | 74 | /// Wrapper around raw gpu resource like `B::Buffer` or `B::Image` 75 | /// It will send raw resource back to the `Factory` if dropped. 76 | /// Destroying it manually with `Factory::destroy_*` is better performance-wise. 77 | #[derive(Debug)] 78 | pub struct Item { 79 | inner: Escape>, 80 | } 81 | 82 | impl Item { 83 | /// Get raw gpu resource. 84 | pub fn raw(&self) -> &T { 85 | &self.inner.raw 86 | } 87 | 88 | /// Get memory block to which resource is bound. 89 | pub fn block(&self) -> &B { 90 | &self.inner.block 91 | } 92 | 93 | /// Unwrap from inner `Escape` wrapper. 94 | /// Returned item must be disposed manually. 95 | /// It may panic or behave unpredictably when dropped. 96 | pub fn into_inner(self) -> RelevantItem { 97 | Escape::into_inner(self.inner) 98 | } 99 | } 100 | 101 | impl Borrow for Item { 102 | fn borrow(&self) -> &T { 103 | &self.inner.raw 104 | } 105 | } 106 | 107 | impl BorrowMut for Item { 108 | fn borrow_mut(&mut self) -> &mut T { 109 | &mut self.inner.raw 110 | } 111 | } 112 | 113 | impl Block for Item 114 | where 115 | T: Debug + Send + Sync, 116 | B: Block, 117 | { 118 | type Memory = B::Memory; 119 | fn memory(&self) -> &Self::Memory { 120 | self.inner.block.memory() 121 | } 122 | fn range(&self) -> Range { 123 | self.inner.block.range() 124 | } 125 | } 126 | 127 | /// Buffer type `Factory` creates 128 | pub type Buffer = Item<::Buffer, SmartBlock<::Memory>>; 129 | 130 | /// Image type `Factory` creates 131 | pub type Image = Item<::Image, SmartBlock<::Memory>>; 132 | 133 | /// Errors this factory can produce. 134 | #[derive(Clone, Debug, Fail)] 135 | pub enum FactoryError { 136 | /// Buffer creation error. 137 | #[fail(display = "Failed to create buffer")] 138 | BufferCreationError(#[cause] BufferCreationError), 139 | 140 | /// Image creation error. 141 | #[fail(display = "Failed to create image")] 142 | ImageCreationError(#[cause] ImageCreationError), 143 | 144 | /// Uploading error. 145 | #[fail(display = "Failed to upload data")] 146 | UploadError(#[cause] upload::Error), 147 | } 148 | 149 | impl From for FactoryError { 150 | fn from(error: BufferCreationError) -> Self { 151 | FactoryError::BufferCreationError(error) 152 | } 153 | } 154 | 155 | impl From for FactoryError { 156 | fn from(error: ImageCreationError) -> Self { 157 | FactoryError::ImageCreationError(error) 158 | } 159 | } 160 | 161 | impl From for FactoryError { 162 | fn from(error: upload::Error) -> Self { 163 | FactoryError::UploadError(error) 164 | } 165 | } 166 | 167 | /// Error occurred during buffer creation. 168 | #[derive(Clone, Debug, Fail)] 169 | pub enum BufferCreationError { 170 | /// Memory error reported from allocator. 171 | #[fail(display = "Failed to allocate memory")] 172 | MemoryError(#[cause] MemoryError), 173 | 174 | /// Creating error reported by driver. 175 | #[fail(display = "Failed to create buffer object")] 176 | CreatingError(#[cause] buffer::CreationError), 177 | } 178 | 179 | impl From for BufferCreationError { 180 | fn from(error: MemoryError) -> Self { 181 | BufferCreationError::MemoryError(error) 182 | } 183 | } 184 | 185 | impl From for BufferCreationError { 186 | fn from(error: buffer::CreationError) -> Self { 187 | BufferCreationError::CreatingError(error) 188 | } 189 | } 190 | 191 | /// Error occurred during image creation. 192 | #[derive(Clone, Debug, Fail)] 193 | pub enum ImageCreationError { 194 | /// Memory error reported from allocator. 195 | #[fail(display = "Failed to allocate memory")] 196 | MemoryError(#[cause] MemoryError), 197 | 198 | /// Creating error reported by driver. 199 | #[fail(display = "Failed to create image object")] 200 | CreatingError(#[cause] image::CreationError), 201 | } 202 | 203 | impl From for ImageCreationError { 204 | fn from(error: MemoryError) -> Self { 205 | ImageCreationError::MemoryError(error) 206 | } 207 | } 208 | 209 | impl From for ImageCreationError { 210 | fn from(error: image::CreationError) -> Self { 211 | ImageCreationError::CreatingError(error) 212 | } 213 | } 214 | 215 | /// `Factory` is a central type that wraps GPU device and responsible for: 216 | /// 1. Creating new `Buffer`s and `Image`s. 217 | /// 2. Destroying `Buffer`s and `Image`s with additional safety. 218 | /// 3. Uploading data to `Buffer`s and `Image`s. 219 | /// 4. Creating `Surface`s and fetching their capabilities. 220 | /// 5. Fetching `Features` and `Limits` of the GPU. 221 | /// 222 | pub struct Factory, D: Device = ::Device> { 223 | device: D, 224 | allocator: ManuallyDrop>, 225 | reclamation: ReclamationQueue>, 226 | current: u64, 227 | upload: ManuallyDrop>, 228 | buffers: Terminal>, 229 | images: Terminal>, 230 | instance: Box, 231 | physical_device: B::PhysicalDevice, 232 | info: AdapterInfo, 233 | } 234 | 235 | impl Drop for Factory 236 | where 237 | B: Backend, 238 | D: Device, 239 | { 240 | fn drop(&mut self) { 241 | use std::ptr::read; 242 | 243 | self.current = u64::max_value() - 1; 244 | debug!("Dispose of `Factory`"); 245 | unsafe { 246 | debug!("Dispose of uploader"); 247 | read(&mut *self.upload).dispose(&self.device); 248 | 249 | debug!("Advance to the end of times"); 250 | self.advance(); 251 | 252 | debug!("Dispose of allocator"); 253 | read(&mut *self.allocator) 254 | .dispose(&self.device) 255 | .expect("Allocator must be cleared"); 256 | } 257 | } 258 | } 259 | 260 | impl Factory 261 | where 262 | B: Backend, 263 | { 264 | /// Create new `Buffer`. Factory will allocate buffer from memory which has all requested properties and supports all requested usages. 265 | /// 266 | /// # Parameters 267 | /// `size` - size of buffer. Returned buffer _may_ be larger but never smaller. 268 | /// `properties` - memory properties required for buffer. 269 | /// `usage` - how buffer is supposed to be used. Caller must specify all usages and never use buffer otherwise. 270 | /// 271 | pub fn create_buffer( 272 | &mut self, 273 | size: u64, 274 | usage: buffer::Usage, 275 | properties: Properties, 276 | ) -> Result, BufferCreationError> { 277 | Ok(Item { 278 | inner: self.buffers.escape(create_relevant_buffer( 279 | &self.device, 280 | &mut self.allocator, 281 | size, 282 | usage, 283 | Type::General, 284 | properties, 285 | )?), 286 | }) 287 | } 288 | 289 | /// Create new `Image`. Factory will allocate buffer from memory which has all requested properties and supports all requested usages. 290 | /// 291 | /// # Parameters 292 | /// 293 | /// `kind` - image dimensions. 294 | /// `level` - number of mim-levels. 295 | /// `format` - format of the image. 296 | /// `properties` - memory properties required for buffer. 297 | /// `usage` - how buffer is supposed to be used. Caller must specify all usages and never use buffer otherwise. 298 | /// 299 | pub fn create_image( 300 | &mut self, 301 | kind: image::Kind, 302 | level: image::Level, 303 | format: Format, 304 | tiling: image::Tiling, 305 | storage_flags: image::StorageFlags, 306 | usage: image::Usage, 307 | properties: Properties, 308 | ) -> Result, ImageCreationError> { 309 | Ok(Item { 310 | inner: self.images.escape(create_relevant_image( 311 | &self.device, 312 | &mut self.allocator, 313 | kind, 314 | level, 315 | format, 316 | tiling, 317 | storage_flags, 318 | usage, 319 | Type::General, 320 | properties, 321 | )?), 322 | }) 323 | } 324 | 325 | /// Destroy `Buffer`. 326 | /// Factory will destroy this buffer after all commands referencing this buffer will complete. 327 | pub fn destroy_buffer(&mut self, buffer: Buffer) { 328 | self.reclamation 329 | .push(self.current, AnyItem::Buffer(buffer.into_inner())); 330 | } 331 | 332 | /// Destroy `Image` 333 | /// Factory will destroy this buffer after all commands referencing this image will complete. 334 | pub fn destroy_image(&mut self, image: Image) { 335 | self.reclamation 336 | .push(self.current, AnyItem::Image(image.into_inner())); 337 | } 338 | 339 | /// Destroy `RelevantBuffer` 340 | /// Factory will destroy this buffer after all commands referencing this buffer will complete. 341 | pub fn destroy_relevant_buffer(&mut self, buffer: RelevantBuffer) { 342 | self.reclamation.push(self.current, AnyItem::Buffer(buffer)); 343 | } 344 | 345 | /// Destroy `RelevantImage` 346 | /// Factory will destroy this buffer after all commands referencing this image will complete. 347 | pub fn destroy_relevant_image(&mut self, image: RelevantImage) { 348 | self.reclamation.push(self.current, AnyItem::Image(image)); 349 | } 350 | 351 | /// Upload data to the buffer. 352 | /// Factory will try to use most appropriate way to write data to the buffer. 353 | /// For cpu-visible buffers it will write via memory mapping. 354 | /// If size of the `data` is bigger than `staging_threshold` then it will perform staging. 355 | /// Otherwise it will write through command buffer directly. 356 | /// 357 | /// # Parameters 358 | /// `buffer` - where to upload data. It must be created with at least one of `TRANSFER_DST` usage or `CPU_VISIBLE` property. 359 | /// `offset` - write data to the buffer starting from this byte. 360 | /// `data` - data to upload. 361 | /// 362 | pub fn upload_buffer( 363 | &mut self, 364 | buffer: &mut Buffer, 365 | family: QueueFamilyId, 366 | access: buffer::Access, 367 | offset: u64, 368 | data: &[u8], 369 | ) -> Result<(), upload::Error> { 370 | let staging = unsafe { 371 | // Correct properties provided. 372 | let ref device = self.device; 373 | let ref mut allocator = self.allocator; 374 | let properties = allocator.properties(&buffer.block()); 375 | self.upload.upload_buffer( 376 | device, 377 | |alloc_type, properties, size, usage| { 378 | let buffer = create_relevant_buffer( 379 | device, allocator, size, usage, alloc_type, properties, 380 | )?; 381 | let properties = allocator.properties(&buffer.block); 382 | Ok((buffer, properties)) 383 | }, 384 | &mut buffer.inner, 385 | properties, 386 | family, 387 | access, 388 | offset, 389 | data, 390 | )? 391 | }; 392 | staging.map(|staging| { 393 | self.reclamation 394 | .push(self.current, AnyItem::Buffer(staging)); 395 | }); 396 | Ok(()) 397 | } 398 | 399 | /// Upload data to the image. 400 | /// Factory will use staging buffer to write data to the image. 401 | /// 402 | /// # Parameters 403 | /// 404 | /// `image` - where to upload. It must be created with `TRANSFER_DST` usage. 405 | /// `layout` - layout in which `Image` will be after uploading. 406 | /// `layers` - specific image subresources of the image used for the destination image data. 407 | /// `offset` - offsets in texels of the sub-region of the destination image data. 408 | /// `extent` - size in texels of the sub-region of the destination image data. 409 | /// `data` - data containing texels in image's format. 410 | pub fn upload_image( 411 | &mut self, 412 | image: &mut Image, 413 | family: QueueFamilyId, 414 | layout: image::Layout, 415 | access: image::Access, 416 | layers: image::SubresourceLayers, 417 | offset: image::Offset, 418 | extent: image::Extent, 419 | data_width: u32, 420 | data_height: u32, 421 | data: &[u8], 422 | ) -> Result<(), upload::Error> { 423 | let staging = unsafe { 424 | // Correct properties provided. 425 | let ref device = self.device; 426 | let ref mut allocator = self.allocator; 427 | self.upload.upload_image( 428 | device, 429 | |alloc_type, properties, size, usage| { 430 | let buffer = create_relevant_buffer( 431 | device, allocator, size, usage, alloc_type, properties, 432 | )?; 433 | let properties = allocator.properties(&buffer.block); 434 | Ok((buffer, properties)) 435 | }, 436 | &mut image.inner, 437 | family, 438 | access, 439 | layout, 440 | layers, 441 | offset, 442 | extent, 443 | data_width, 444 | data_height, 445 | data, 446 | )? 447 | }; 448 | self.reclamation 449 | .push(self.current, AnyItem::Buffer(staging)); 450 | Ok(()) 451 | } 452 | 453 | /// Create new `Surface`. 454 | /// 455 | /// # Parameters 456 | /// 457 | /// `window` - window handler which will be represented by new surface. 458 | pub(crate) fn create_surface(&mut self, window: &Window) -> B::Surface 459 | where 460 | B: BackendEx, 461 | { 462 | use std::any::TypeId; 463 | trace!("Factory<{:?}>::create_surface", TypeId::of::()); 464 | let surface = Any::downcast_ref::(&*self.instance).unwrap(); 465 | B::create_surface(surface, window) 466 | } 467 | 468 | /// Get capabilities and formats for surface. 469 | /// If formats are `None` then `Surface` has no preferences for formats. 470 | /// Otherwise `Swapchain` can be created only with one of formats returned by this function. 471 | /// 472 | /// # Parameters 473 | /// 474 | /// `surface` - surface object which capabilities and supported formats are retrieved. 475 | /// 476 | pub fn compatibility( 477 | &self, 478 | surface: &B::Surface, 479 | ) -> ( 480 | window::SurfaceCapabilities, 481 | Option>, 482 | Vec, 483 | ) { 484 | surface.compatibility(&self.physical_device) 485 | } 486 | 487 | /// Borrow both `Device` and `SmartAllocator` from the `Factory`. 488 | pub fn device_and_allocator(&mut self) -> (&B::Device, &mut SmartAllocator) { 489 | (self.device.borrow(), &mut self.allocator) 490 | } 491 | 492 | /// Get features supported by hardware. 493 | pub fn features(&self) -> Features { 494 | self.physical_device.features() 495 | } 496 | 497 | /// Get hardware specific limits. 498 | pub fn limits(&self) -> Limits { 499 | self.physical_device.limits() 500 | } 501 | 502 | /// Retrieve adapter info 503 | pub fn adapter_info(&self) -> &AdapterInfo { 504 | &self.info 505 | } 506 | 507 | /// Construct `Factory` from parts. 508 | pub(crate) fn new( 509 | instance: B::Instance, 510 | info: AdapterInfo, 511 | physical_device: B::PhysicalDevice, 512 | device: B::Device, 513 | allocator: SmartAllocator, 514 | staging_threshold: usize, 515 | ) -> Self 516 | where 517 | B: BackendEx, 518 | { 519 | Factory { 520 | instance: Box::new(instance), 521 | device: device.into(), 522 | allocator: ManuallyDrop::new(allocator), 523 | reclamation: ReclamationQueue::new(), 524 | current: 0, 525 | upload: ManuallyDrop::new(Upload::new(staging_threshold)), 526 | buffers: Terminal::new(), 527 | images: Terminal::new(), 528 | physical_device, 529 | info, 530 | } 531 | } 532 | 533 | /// Fetch command buffer with uploads recorded. 534 | pub(crate) fn uploads( 535 | &mut self, 536 | ) -> impl Iterator { 537 | self.upload.uploads(self.current) 538 | } 539 | 540 | /// Render system call this after waiting for last jobs. 541 | pub(crate) unsafe fn advance(&mut self) { 542 | for buffer in self.buffers.drain() { 543 | self.reclamation.push(self.current, AnyItem::Buffer(buffer)); 544 | } 545 | for image in self.images.drain() { 546 | self.reclamation.push(self.current, AnyItem::Image(image)); 547 | } 548 | let ref device = self.device; 549 | let ref mut allocator = self.allocator; 550 | self.reclamation.clear(self.current, |item| { 551 | item.destroy(device, allocator); 552 | }); 553 | self.upload.clear(self.current); 554 | self.current += 1; 555 | } 556 | } 557 | 558 | impl Borrow for Factory 559 | where 560 | B: Backend, 561 | D: Device, 562 | { 563 | fn borrow(&self) -> &D { 564 | &self.device 565 | } 566 | } 567 | 568 | impl BorrowMut for Factory 569 | where 570 | B: Backend, 571 | D: Device, 572 | { 573 | fn borrow_mut(&mut self) -> &mut D { 574 | &mut self.device 575 | } 576 | } 577 | 578 | impl Device for Factory 579 | where 580 | B: Backend, 581 | D: Device, 582 | { 583 | #[inline] 584 | fn allocate_memory( 585 | &self, 586 | memory_type: MemoryTypeId, 587 | size: u64, 588 | ) -> Result { 589 | self.device.allocate_memory(memory_type, size) 590 | } 591 | 592 | #[inline] 593 | fn free_memory(&self, memory: B::Memory) { 594 | self.device.free_memory(memory) 595 | } 596 | 597 | #[inline] 598 | fn create_command_pool( 599 | &self, 600 | family: QueueFamilyId, 601 | create_flags: CommandPoolCreateFlags, 602 | ) -> B::CommandPool { 603 | self.device.create_command_pool(family, create_flags) 604 | } 605 | 606 | #[inline] 607 | fn destroy_command_pool(&self, pool: B::CommandPool) { 608 | self.device.destroy_command_pool(pool) 609 | } 610 | 611 | #[inline] 612 | fn create_render_pass<'a, IA, IS, ID>( 613 | &self, 614 | attachments: IA, 615 | subpasses: IS, 616 | dependencies: ID, 617 | ) -> B::RenderPass 618 | where 619 | IA: IntoIterator, 620 | IA::Item: Borrow, 621 | IS: IntoIterator, 622 | IS::Item: Borrow>, 623 | ID: IntoIterator, 624 | ID::Item: Borrow, 625 | { 626 | self.device 627 | .create_render_pass(attachments, subpasses, dependencies) 628 | } 629 | 630 | #[inline] 631 | fn destroy_render_pass(&self, rp: B::RenderPass) { 632 | self.device.destroy_render_pass(rp) 633 | } 634 | 635 | #[inline] 636 | fn create_pipeline_layout( 637 | &self, 638 | set_layouts: IS, 639 | push_constant: IR, 640 | ) -> B::PipelineLayout 641 | where 642 | IS: IntoIterator, 643 | IS::Item: Borrow, 644 | IR: IntoIterator, 645 | IR::Item: Borrow<(pso::ShaderStageFlags, Range)>, 646 | { 647 | self.device 648 | .create_pipeline_layout(set_layouts, push_constant) 649 | } 650 | 651 | #[inline] 652 | fn destroy_pipeline_layout(&self, layout: B::PipelineLayout) { 653 | self.device.destroy_pipeline_layout(layout) 654 | } 655 | 656 | #[inline] 657 | fn destroy_graphics_pipeline(&self, pipeline: B::GraphicsPipeline) { 658 | self.device.destroy_graphics_pipeline(pipeline) 659 | } 660 | 661 | #[inline] 662 | fn destroy_compute_pipeline(&self, pipeline: B::ComputePipeline) { 663 | self.device.destroy_compute_pipeline(pipeline) 664 | } 665 | 666 | #[inline] 667 | fn create_framebuffer( 668 | &self, 669 | pass: &B::RenderPass, 670 | attachments: I, 671 | extent: image::Extent, 672 | ) -> Result 673 | where 674 | I: IntoIterator, 675 | I::Item: Borrow, 676 | { 677 | self.device.create_framebuffer(pass, attachments, extent) 678 | } 679 | 680 | #[inline] 681 | fn destroy_framebuffer(&self, buf: B::Framebuffer) { 682 | self.device.destroy_framebuffer(buf) 683 | } 684 | 685 | #[inline] 686 | fn create_shader_module(&self, spirv_data: &[u8]) -> Result { 687 | self.device.create_shader_module(spirv_data) 688 | } 689 | 690 | #[inline] 691 | fn destroy_shader_module(&self, shader: B::ShaderModule) { 692 | self.device.destroy_shader_module(shader) 693 | } 694 | 695 | #[inline] 696 | fn create_buffer( 697 | &self, 698 | size: u64, 699 | usage: buffer::Usage, 700 | ) -> Result { 701 | self.device.create_buffer(size, usage) 702 | } 703 | 704 | #[inline] 705 | fn get_buffer_requirements(&self, buf: &B::UnboundBuffer) -> Requirements { 706 | self.device.get_buffer_requirements(buf) 707 | } 708 | 709 | #[inline] 710 | fn bind_buffer_memory( 711 | &self, 712 | memory: &B::Memory, 713 | offset: u64, 714 | buf: B::UnboundBuffer, 715 | ) -> Result { 716 | self.device.bind_buffer_memory(memory, offset, buf) 717 | } 718 | 719 | #[inline] 720 | fn destroy_buffer(&self, buf: B::Buffer) { 721 | self.device.destroy_buffer(buf) 722 | } 723 | 724 | #[inline] 725 | fn create_buffer_view>( 726 | &self, 727 | buf: &B::Buffer, 728 | fmt: Option, 729 | range: R, 730 | ) -> Result { 731 | self.device.create_buffer_view(buf, fmt, range) 732 | } 733 | 734 | #[inline] 735 | fn destroy_buffer_view(&self, view: B::BufferView) { 736 | self.device.destroy_buffer_view(view) 737 | } 738 | 739 | #[inline] 740 | fn create_image( 741 | &self, 742 | kind: image::Kind, 743 | mip_levels: image::Level, 744 | format: Format, 745 | tiling: image::Tiling, 746 | usage: image::Usage, 747 | storage_flags: image::StorageFlags, 748 | ) -> Result { 749 | self.device 750 | .create_image(kind, mip_levels, format, tiling, usage, storage_flags) 751 | } 752 | 753 | #[inline] 754 | fn get_image_requirements(&self, image: &B::UnboundImage) -> Requirements { 755 | self.device.get_image_requirements(image) 756 | } 757 | 758 | #[inline] 759 | fn bind_image_memory( 760 | &self, 761 | memory: &B::Memory, 762 | offset: u64, 763 | image: B::UnboundImage, 764 | ) -> Result { 765 | self.device.bind_image_memory(memory, offset, image) 766 | } 767 | 768 | #[inline] 769 | fn destroy_image(&self, image: B::Image) { 770 | self.device.destroy_image(image) 771 | } 772 | 773 | #[inline] 774 | fn create_image_view( 775 | &self, 776 | image: &B::Image, 777 | view_kind: image::ViewKind, 778 | format: Format, 779 | swizzle: Swizzle, 780 | range: image::SubresourceRange, 781 | ) -> Result { 782 | self.device 783 | .create_image_view(image, view_kind, format, swizzle, range) 784 | } 785 | 786 | #[inline] 787 | fn destroy_image_view(&self, view: B::ImageView) { 788 | self.device.destroy_image_view(view) 789 | } 790 | 791 | #[inline] 792 | fn create_sampler(&self, info: image::SamplerInfo) -> B::Sampler { 793 | self.device.create_sampler(info) 794 | } 795 | 796 | #[inline] 797 | fn destroy_sampler(&self, sampler: B::Sampler) { 798 | self.device.destroy_sampler(sampler) 799 | } 800 | 801 | #[inline] 802 | fn create_descriptor_pool(&self, max_sets: usize, descriptor_ranges: I) -> B::DescriptorPool 803 | where 804 | I: IntoIterator, 805 | I::Item: Borrow, 806 | { 807 | self.device 808 | .create_descriptor_pool(max_sets, descriptor_ranges) 809 | } 810 | 811 | #[inline] 812 | fn destroy_descriptor_pool(&self, pool: B::DescriptorPool) { 813 | self.device.destroy_descriptor_pool(pool) 814 | } 815 | 816 | #[inline] 817 | fn create_descriptor_set_layout( 818 | &self, 819 | bindings: I, 820 | immutable_samplers: J, 821 | ) -> B::DescriptorSetLayout 822 | where 823 | I: IntoIterator, 824 | I::Item: Borrow, 825 | J: IntoIterator, 826 | J::Item: Borrow, 827 | { 828 | self.device 829 | .create_descriptor_set_layout(bindings, immutable_samplers) 830 | } 831 | 832 | #[inline] 833 | fn destroy_descriptor_set_layout(&self, layout: B::DescriptorSetLayout) { 834 | self.device.destroy_descriptor_set_layout(layout) 835 | } 836 | 837 | #[inline] 838 | fn write_descriptor_sets<'a, I, J>(&self, write_iter: I) 839 | where 840 | I: IntoIterator>, 841 | J: IntoIterator, 842 | J::Item: Borrow>, 843 | { 844 | self.device.write_descriptor_sets(write_iter) 845 | } 846 | 847 | #[inline] 848 | fn copy_descriptor_sets<'a, I>(&self, copy_iter: I) 849 | where 850 | I: IntoIterator, 851 | I::Item: Borrow>, 852 | { 853 | self.device.copy_descriptor_sets(copy_iter) 854 | } 855 | 856 | #[inline] 857 | fn map_memory(&self, memory: &B::Memory, range: R) -> Result<*mut u8, mapping::Error> 858 | where 859 | R: RangeArg, 860 | { 861 | self.device.map_memory(memory, range) 862 | } 863 | 864 | #[inline] 865 | fn flush_mapped_memory_ranges<'a, I, R>(&self, ranges: I) 866 | where 867 | I: IntoIterator, 868 | I::Item: Borrow<(&'a B::Memory, R)>, 869 | R: RangeArg, 870 | { 871 | self.device.flush_mapped_memory_ranges(ranges) 872 | } 873 | 874 | #[inline] 875 | fn invalidate_mapped_memory_ranges<'a, I, R>(&self, ranges: I) 876 | where 877 | I: IntoIterator, 878 | I::Item: Borrow<(&'a B::Memory, R)>, 879 | R: RangeArg, 880 | { 881 | self.device.invalidate_mapped_memory_ranges(ranges) 882 | } 883 | 884 | #[inline] 885 | fn unmap_memory(&self, memory: &B::Memory) { 886 | self.device.unmap_memory(memory) 887 | } 888 | 889 | #[inline] 890 | fn create_semaphore(&self) -> B::Semaphore { 891 | self.device.create_semaphore() 892 | } 893 | 894 | #[inline] 895 | fn destroy_semaphore(&self, semaphore: B::Semaphore) { 896 | self.device.destroy_semaphore(semaphore) 897 | } 898 | 899 | #[inline] 900 | fn create_fence(&self, signaled: bool) -> B::Fence { 901 | self.device.create_fence(signaled) 902 | } 903 | 904 | #[inline] 905 | fn get_fence_status(&self, fence: &B::Fence) -> bool { 906 | self.device.get_fence_status(fence) 907 | } 908 | 909 | #[inline] 910 | fn destroy_fence(&self, fence: B::Fence) { 911 | self.device.destroy_fence(fence) 912 | } 913 | 914 | #[inline] 915 | fn create_query_pool(&self, ty: QueryType, count: u32) -> B::QueryPool { 916 | self.device.create_query_pool(ty, count) 917 | } 918 | 919 | #[inline] 920 | fn destroy_query_pool(&self, pool: B::QueryPool) { 921 | self.device.destroy_query_pool(pool) 922 | } 923 | 924 | #[inline] 925 | fn create_swapchain( 926 | &self, 927 | surface: &mut B::Surface, 928 | config: window::SwapchainConfig, 929 | old_swapchain: Option, 930 | extent: &window::Extent2D, 931 | ) -> (B::Swapchain, window::Backbuffer) { 932 | self.device 933 | .create_swapchain(surface, config, old_swapchain, extent) 934 | } 935 | 936 | #[inline] 937 | fn destroy_swapchain(&self, swapchain: B::Swapchain) { 938 | self.device.destroy_swapchain(swapchain) 939 | } 940 | 941 | #[inline] 942 | fn wait_idle(&self) -> Result<(), HostExecutionError> { 943 | self.device.wait_idle() 944 | } 945 | 946 | #[inline] 947 | fn create_command_pool_typed( 948 | &self, 949 | group: &QueueGroup, 950 | flags: CommandPoolCreateFlags, 951 | max_buffers: usize, 952 | ) -> CommandPool { 953 | self.device 954 | .create_command_pool_typed(group, flags, max_buffers) 955 | } 956 | 957 | #[inline] 958 | fn create_graphics_pipeline<'a>( 959 | &self, 960 | desc: &pso::GraphicsPipelineDesc<'a, B>, 961 | ) -> Result { 962 | self.device.create_graphics_pipeline(desc) 963 | } 964 | 965 | #[inline] 966 | fn create_graphics_pipelines<'a, I>( 967 | &self, 968 | descs: I, 969 | ) -> Vec> 970 | where 971 | I: IntoIterator, 972 | I::Item: Borrow>, 973 | { 974 | self.device.create_graphics_pipelines(descs) 975 | } 976 | 977 | #[inline] 978 | fn create_compute_pipeline<'a>( 979 | &self, 980 | desc: &pso::ComputePipelineDesc<'a, B>, 981 | ) -> Result { 982 | self.device.create_compute_pipeline(desc) 983 | } 984 | 985 | #[inline] 986 | fn create_compute_pipelines<'a, I>( 987 | &self, 988 | descs: I, 989 | ) -> Vec> 990 | where 991 | I: IntoIterator, 992 | I::Item: Borrow>, 993 | { 994 | self.device.create_compute_pipelines(descs) 995 | } 996 | 997 | #[inline] 998 | fn acquire_mapping_reader<'a, T>( 999 | &self, 1000 | memory: &'a B::Memory, 1001 | range: Range, 1002 | ) -> Result, mapping::Error> 1003 | where 1004 | T: Copy, 1005 | { 1006 | self.device.acquire_mapping_reader(memory, range) 1007 | } 1008 | 1009 | #[inline] 1010 | fn release_mapping_reader<'a, T>(&self, reader: Reader<'a, B, T>) { 1011 | self.device.release_mapping_reader(reader) 1012 | } 1013 | 1014 | #[inline] 1015 | fn acquire_mapping_writer<'a, T>( 1016 | &self, 1017 | memory: &'a B::Memory, 1018 | range: Range, 1019 | ) -> Result, mapping::Error> 1020 | where 1021 | T: Copy, 1022 | { 1023 | self.device.acquire_mapping_writer(memory, range) 1024 | } 1025 | 1026 | #[inline] 1027 | fn release_mapping_writer<'a, T>(&self, writer: Writer<'a, B, T>) { 1028 | self.device.release_mapping_writer(writer) 1029 | } 1030 | 1031 | #[inline] 1032 | fn reset_fence(&self, fence: &B::Fence) { 1033 | self.device.reset_fence(fence) 1034 | } 1035 | 1036 | #[inline] 1037 | fn reset_fences(&self, fences: I) 1038 | where 1039 | I: IntoIterator, 1040 | I::Item: Borrow, 1041 | { 1042 | self.device.reset_fences(fences) 1043 | } 1044 | 1045 | #[inline] 1046 | fn wait_for_fence(&self, fence: &B::Fence, timeout_ms: u32) -> bool { 1047 | self.device.wait_for_fence(fence, timeout_ms) 1048 | } 1049 | 1050 | #[inline] 1051 | fn wait_for_fences(&self, fences: I, wait: WaitFor, timeout_ms: u32) -> bool 1052 | where 1053 | I: IntoIterator, 1054 | I::Item: Borrow, 1055 | { 1056 | self.device.wait_for_fences(fences, wait, timeout_ms) 1057 | } 1058 | 1059 | #[inline] 1060 | fn get_image_subresource_footprint( 1061 | &self, 1062 | image: &B::Image, 1063 | subresource: image::Subresource, 1064 | ) -> image::SubresourceFootprint { 1065 | self.device 1066 | .get_image_subresource_footprint(image, subresource) 1067 | } 1068 | } 1069 | 1070 | #[derive(Debug)] 1071 | enum AnyItem { 1072 | Buffer(RelevantBuffer), 1073 | Image(RelevantImage), 1074 | } 1075 | 1076 | impl AnyItem 1077 | where 1078 | B: Backend, 1079 | { 1080 | pub fn destroy(self, device: &B::Device, allocator: &mut SmartAllocator) { 1081 | match self { 1082 | AnyItem::Buffer(buffer) => { 1083 | device.destroy_buffer(buffer.raw); 1084 | allocator.free(device, buffer.block); 1085 | } 1086 | AnyItem::Image(image) => { 1087 | device.destroy_image(image.raw); 1088 | allocator.free(device, image.block); 1089 | } 1090 | } 1091 | } 1092 | } 1093 | 1094 | #[test] 1095 | #[allow(dead_code)] 1096 | fn factory_send_sync() { 1097 | fn is_send_sync() {} 1098 | fn for_any_backend() { 1099 | is_send_sync::>(); 1100 | } 1101 | } 1102 | 1103 | fn create_relevant_buffer( 1104 | device: &B::Device, 1105 | allocator: &mut SmartAllocator, 1106 | size: u64, 1107 | usage: buffer::Usage, 1108 | alloc_type: Type, 1109 | properties: Properties, 1110 | ) -> Result, BufferCreationError> 1111 | where 1112 | B: Backend, 1113 | { 1114 | // Create unbound buffer object. 1115 | let ubuf = device.create_buffer(size, usage)?; 1116 | 1117 | // Get memory requirements. 1118 | let reqs = device.get_buffer_requirements(&ubuf); 1119 | 1120 | // Allocate memory block. 1121 | let block = allocator.alloc(device, (alloc_type, properties), reqs)?; 1122 | 1123 | // Bind memory. Infallible unless unless bugged. 1124 | let raw = device 1125 | .bind_buffer_memory(block.memory(), block.range().start, ubuf) 1126 | .expect("Requirements must be satisfied"); 1127 | 1128 | Ok(RelevantItem { raw, block }) 1129 | } 1130 | 1131 | fn create_relevant_image( 1132 | device: &B::Device, 1133 | allocator: &mut SmartAllocator, 1134 | kind: image::Kind, 1135 | level: image::Level, 1136 | format: Format, 1137 | tiling: image::Tiling, 1138 | storage_flags: image::StorageFlags, 1139 | usage: image::Usage, 1140 | alloc_type: Type, 1141 | properties: Properties, 1142 | ) -> Result, ImageCreationError> 1143 | where 1144 | B: Backend, 1145 | { 1146 | // Create unbound image object. 1147 | let uimg = device.create_image(kind, level, format, tiling, usage, storage_flags)?; 1148 | 1149 | // Get memory requirements. 1150 | let reqs = device.get_image_requirements(&uimg); 1151 | 1152 | // Allocate memory block. 1153 | let block = allocator.alloc(device, (alloc_type, properties), reqs)?; 1154 | 1155 | // Bind memory. Infallible unless unless bugged. 1156 | let raw = device 1157 | .bind_image_memory(block.memory(), block.range().start, uimg) 1158 | .expect("Requirements must be satisfied"); 1159 | 1160 | Ok(RelevantItem { raw, block }) 1161 | } 1162 | --------------------------------------------------------------------------------