├── .gitignore ├── .travis.yml ├── COPYING ├── Cargo.toml ├── Makefile ├── README.md ├── bors.toml ├── chain ├── Cargo.toml └── src │ ├── chain │ ├── link.rs │ └── mod.rs │ ├── collect.rs │ ├── lib.rs │ ├── node.rs │ ├── resource.rs │ ├── schedule │ ├── family.rs │ ├── mod.rs │ ├── queue.rs │ └── submission.rs │ └── sync.rs ├── command ├── Cargo.toml └── src │ ├── buffer │ ├── encoder.rs │ ├── level.rs │ ├── mod.rs │ ├── reset.rs │ ├── state.rs │ ├── submit.rs │ └── usage.rs │ ├── capability.rs │ ├── family.rs │ ├── lib.rs │ └── pool.rs ├── docs └── public_api_overview.svg ├── factory ├── Cargo.toml └── src │ ├── config.rs │ ├── factory.rs │ └── lib.rs ├── frame ├── Cargo.toml └── src │ ├── cirque │ ├── command.rs │ └── mod.rs │ ├── frame.rs │ └── lib.rs ├── graph ├── Cargo.toml └── src │ ├── graph │ └── mod.rs │ ├── lib.rs │ └── node │ ├── mod.rs │ ├── present.rs │ └── render.rs ├── license ├── APACHE └── MIT ├── memory ├── Cargo.toml └── src │ ├── allocator │ ├── dedicated.rs │ ├── dynamic.rs │ ├── linear.rs │ └── mod.rs │ ├── block.rs │ ├── heaps.rs │ ├── lib.rs │ ├── mapping │ ├── mod.rs │ ├── range.rs │ └── write.rs │ ├── memory.rs │ ├── usage.rs │ └── util.rs ├── mesh ├── Cargo.toml ├── README.md └── src │ ├── format.rs │ ├── format │ └── obj.rs │ ├── lib.rs │ ├── mesh.rs │ └── vertex.rs ├── rendy ├── Cargo.toml ├── examples │ ├── init │ │ └── main.rs │ ├── quads │ │ ├── bounce.comp │ │ ├── main.rs │ │ ├── render.frag │ │ └── render.vert │ └── triangle │ │ ├── main.rs │ │ ├── shader.frag │ │ └── shader.vert └── src │ └── lib.rs ├── resource ├── Cargo.toml └── src │ ├── buffer │ ├── mod.rs │ └── usage.rs │ ├── escape.rs │ ├── image │ ├── mod.rs │ └── usage.rs │ ├── lib.rs │ └── resources.rs ├── rustfmt.toml ├── shader ├── Cargo.toml └── src │ └── lib.rs ├── texture ├── Cargo.toml └── src │ ├── lib.rs │ ├── pixel.rs │ └── texture.rs ├── util ├── Cargo.toml └── src │ └── lib.rs └── wsi ├── Cargo.toml └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | **/*.rs.bk 3 | **/Cargo.lock 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: 3 | cargo: true 4 | directories: 5 | - $HOME/deps 6 | 7 | compiler: clang 8 | 9 | matrix: 10 | include: 11 | # Linux 64bit 12 | - os: linux 13 | rust: stable 14 | - os: linux 15 | rust: nightly 16 | 17 | # macOS 64bit 18 | - env: MACOSX_DEPLOYMENT_TARGET=10.9 19 | os: osx 20 | rust: stable 21 | osx_image: xcode9 22 | - env: MACOSX_DEPLOYMENT_TARGET=10.9 23 | os: osx 24 | rust: nightly 25 | osx_image: xcode9 26 | 27 | # iPhoneOS 64bit 28 | - env: TARGET=aarch64-apple-ios 29 | os: osx 30 | osx_image: xcode9 31 | rust: nightly 32 | 33 | branches: 34 | only: 35 | - staging 36 | - trying 37 | - master 38 | 39 | script: 40 | - make 41 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2018 The Rendy Project Developers 2 | 3 | Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | copied, modified, or distributed except according to those terms. 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "command", 5 | "factory", 6 | "frame", 7 | "memory", 8 | "mesh", 9 | "rendy", 10 | "resource", 11 | "shader", 12 | "wsi", 13 | "chain", 14 | "graph", 15 | "util", 16 | "texture", 17 | ] 18 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | RUST_BACKTRACE:=1 2 | RENDY_BACKEND:= 3 | 4 | ifeq ($(OS),Windows_NT) 5 | RENDY_BACKEND=dx12 6 | else 7 | UNAME_S:=$(shell uname -s) 8 | ifeq ($(UNAME_S),Linux) 9 | RENDY_BACKEND=vulkan 10 | endif 11 | ifeq ($(UNAME_S),Darwin) 12 | RENDY_BACKEND=metal 13 | endif 14 | endif 15 | 16 | build: 17 | cd rendy && cargo build --all --examples --features "full $(RENDY_BACKEND)" 18 | 19 | test: 20 | cd rendy && cargo test --all --features "full $(RENDY_BACKEND)" 21 | 22 | doc: 23 | cd rendy && cargo doc --all --features "full $(RENDY_BACKEND)" 24 | 25 | all: build test doc 26 | 27 | quads: 28 | cd rendy && cargo run --features "full $(RENDY_BACKEND)" --example quads 29 | 30 | triangle: 31 | cd rendy && cargo run --features "full $(RENDY_BACKEND)" --example triangle 32 | 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Rendy 3 | 4 | [![Build Status][s1]][tc] 5 | [![Crates.io][s2]][ci] 6 | [![docs page][docs-badge]][docs] 7 | [![MIT/Apache][s3]][li] 8 | ![Lines of Code][s4] 9 | 10 | [s1]: https://travis-ci.org/rustgd/rendy.svg?branch=master 11 | [s2]: https://img.shields.io/crates/v/rendy.svg 12 | [docs-badge]: https://img.shields.io/badge/docs-website-blue.svg 13 | [docs]: https://rustgd.github.io/rendy-docs/rendy/index.html 14 | [s3]: https://img.shields.io/badge/license-MIT%2FApache-blue.svg 15 | [s4]: https://tokei.rs/b1/github/rustgd/rendy?category=code 16 | [tc]: https://travis-ci.org/rustgd/rendy 17 | [ci]: https://crates.io/crates/rendy/ 18 | [li]: COPYING 19 | 20 | Yet another [`Vulkan`] based rendering engine. 21 | Actually it is based on [`gfx-hal`] that mimics [`Vulkan`] API. 22 | 23 | ## Features 24 | 25 | Most importantly `rendy` features safer API by checking important states and invariants. 26 | It checks invariants statically using marker types and dynamically with stored values. 27 | 28 | ### Capability 29 | 30 | Queue family capability defines what operation queues of the family supports. 31 | `rendy` provides simple mechanism to prevent recording unsupported commands. 32 | Capability level can be stored statically by marking `Family` type with one of capability types: `Transfer`, `Graphics`, `Compute` or `General` (`Graphics` and `Compute` combined). 33 | Alternatively `Capability` type can be used instead of marker type, this way actual capability level can be checked dynamically. 34 | 35 | ### Command buffer 36 | 37 | `rendy` provides handy wrapper named `CommandBuffer`. In contrast to raw counterpart this wrapper 38 | encodes crutial information about its state directly into type level. 39 | This means user can't accidentially: 40 | * record command unsupported by queue family it belongs to. 41 | * record command when command buffer is not in recording state. 42 | * record render pass command outside renderpass. 43 | * forget to finish recording buffer before submitting. 44 | * resubmit command buffer which was created for one time use. 45 | * record execution of primary buffer into secondary buffer. 46 | * etc 47 | 48 | ### Memory manager 49 | 50 | `rendy`'s memory manager is called `Heaps`. 51 | `Heaps` provides convenient methods to sub-allocate device-visible memory based on usage and visibility requirements. It also handles mapping for specific usage types. 52 | **It is possible for [`gfx-hal`] to adopt VMA. In which case `rendy` will use it** 53 | 54 | ### Framegraph 55 | 56 | `rendy`'s framegraph allow writing rendering code in simple modular style. 57 | Making it much easier to composite complex frame from simple parts. 58 | User defines nodes which declare buffers and images it reads and writes. 59 | Framegraph takes responsibility for resource allocation and execution synchronization. 60 | User is responsible only for intra-node synchronization. 61 | 62 | ### Cirques 63 | 64 | This hybrid of circus and queue simplifies synchronizing host access to resources. 65 | `Cirque` allocates copies of the resource from resource spicific allocator 66 | (e.g. `CommandPool` for `CommandBuffer`s, `Factory` for `Buffer`s) 67 | and gives access to the unused copy. 68 | 69 | ### CPU-GPU data flow - ***Not yet implemented*** 70 | 71 | Rendy can help to send data between device and host. 72 | `Factory` can upload data to the device local memory choosing most appropriate technique for that. 73 | * Memory mapping will be used if device local memory happens to be cpu-visible. 74 | * Relatively small data will be uploaded directly to buffers. 75 | * Staging buffer will be used for bigger uploads or any image uploads. 76 | `Factoy` will automatically insert synchronization commands according to user request. 77 | 78 | ### Layouts - ***Not yet implemented. More experiments required*** 79 | 80 | Pipelines and descriptor sets has declarative nature and it is much easier to define them declaratively. 81 | `rendy` provides `DescriptorSet` trait. 82 | Deriving it will automatically generate code necessary for set creation, writing and binding. 83 | Deriving `GraphicsPipeline` trait will generate code for graphics pipeline creation and usage. 84 | Similar `ComputePipeline` trait exists for compute pipelines. 85 | 86 | #### Example 87 | 88 | ```rust 89 | #[derive(DescritorSet)] 90 | struct Example { 91 | /// This field will be associated with binding 1 of type `VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER`. 92 | /// Actual `Buffer` will be allocated and kept updated by `Set`. 93 | #[descriptor(UniformBlock)] 94 | transform: mat4, 95 | 96 | /// This field will be associated with binding 2 of type `VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE`. 97 | /// `ImageView` will be fetched from `Texture` which implements `Borrow`. 98 | #[descriptor(SampledImage)] 99 | texture: Texture, 100 | 101 | /// Raw Vulkan objects can be used as well. 102 | /// But this field will make binding of `Set` to command buffer to require unsafe operation 103 | /// since it is user job to ensure that this raw image view is valid during command buffer execution. 104 | #[descriptor(unsafe, SampledImage)] 105 | foo: RawImageView, 106 | } 107 | ``` 108 | 109 | ### Shader reflection - ***Not yet implemented*** 110 | 111 | `rendy` will use `spirv-relfect` or similiar crate to read layout information directly from shaders 112 | and use it to automatically populate descriptors and set index/vertex buffers based on registered data sources. 113 | 114 | ### Modularity 115 | 116 | Most of the features provided by rendy can be used independently from others. 117 | This helps to keep API clean and hopefuly sound. 118 | Top-level umbrela crate `rendy` has feature for each subcrates so that they could be enabled separately (subcrate will also enable its depenencies). 119 | 120 | ## Why another renderer 121 | 122 | There is no fully-featured modern renderers written in Rust. So this project aims to be the one of the first. 123 | `rendy` will be used by [`Amethyst`] project. 124 | 125 | ## License 126 | 127 | Licensed under either of 128 | 129 | * Apache License, Version 2.0, ([license/APACHE](license/APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 130 | * MIT license ([license/MIT](license/MIT) or http://opensource.org/licenses/MIT) 131 | 132 | at your option. 133 | 134 | [`gfx-hal`]: https://github.com/gfx-rs/gfx 135 | [`gfx-memory`]: https://github.com/gfx-rs/gfx-memory 136 | [`gfx-render`]: https://github.com/gfx-rs/gfx-render 137 | [`gfx-mesh`]: https://github.com/omni-viral/gfx-mesh 138 | [`gfx-texture`]: https://github.com/omni-viral/gfx-texture 139 | [`xfg`]: https://github.com/omni-viral/xfg-rs 140 | [`Vulkan`]: https://www.khronos.org/vulkan/ 141 | [`Vulkan`-portability]: https://www.khronos.org/vulkan/portability-initiative 142 | [`Amethyst`]: https://github.com/amethyst/amethyst 143 | -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | status = [ 2 | "continuous-integration/travis-ci/push" 3 | ] 4 | # Uncomment this to use a two hour timeout. 5 | # The default is one hour. 6 | #timeout_sec = 7200 7 | -------------------------------------------------------------------------------- /chain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-chain" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | gfx-hal = { git = "https://github.com/gfx-rs/gfx" } 9 | fnv = "1.0" 10 | log = "0.4" 11 | -------------------------------------------------------------------------------- /chain/src/chain/link.rs: -------------------------------------------------------------------------------- 1 | 2 | use crate::{ 3 | node::State, 4 | resource::{AccessFlags, Resource}, 5 | schedule::{QueueId, SubmissionId}, 6 | }; 7 | 8 | /// State of the link associated with queue. 9 | /// Contains submissions range, combined access and stages bits by submissions from the range. 10 | #[derive(Clone, Copy, Debug)] 11 | pub struct LinkQueueState { 12 | pub first: usize, 13 | pub last: usize, 14 | pub access: R::Access, 15 | pub stages: gfx_hal::pso::PipelineStage, 16 | } 17 | 18 | impl LinkQueueState 19 | where 20 | R: Resource, 21 | { 22 | fn new(node: &LinkNode) -> Self { 23 | LinkQueueState { 24 | first: node.sid.index(), 25 | last: node.sid.index(), 26 | access: node.state.access, 27 | stages: node.state.stages, 28 | } 29 | } 30 | 31 | fn push(&mut self, node: &LinkNode) { 32 | assert!(self.last < node.sid.index()); 33 | self.access |= node.state.access; 34 | self.stages |= node.state.stages; 35 | self.last = node.sid.index(); 36 | } 37 | } 38 | 39 | /// This type defines what states resource are at some point in time when commands recorded into 40 | /// corresponding submissions are executed. 41 | /// Those commands doesn't required to perform actions with all access types declared by the link. 42 | /// But performing actions with access types not declared by the link is prohibited. 43 | #[derive(Clone, Debug)] 44 | pub struct Link { 45 | /// Combination of all accesses. 46 | access: R::Access, 47 | 48 | /// Combination of all usages. 49 | usage: R::Usage, 50 | 51 | /// Common layout for all submissions. 52 | layout: R::Layout, 53 | 54 | /// Combination of all stages. 55 | stages: gfx_hal::pso::PipelineStage, 56 | 57 | /// Number of queues involved. 58 | queue_count: usize, 59 | 60 | /// State per queue. 61 | queues: Vec>>, 62 | 63 | /// Family of queues. 64 | family: gfx_hal::queue::QueueFamilyId, 65 | } 66 | 67 | /// Node for the link. 68 | #[derive(Debug)] 69 | pub struct LinkNode { 70 | /// Submission id of the node. 71 | pub sid: SubmissionId, 72 | 73 | /// Resource state of the node. 74 | pub state: State, 75 | } 76 | 77 | impl Link 78 | where 79 | R: Resource, 80 | { 81 | /// Create new link with first attached submission. 82 | /// 83 | /// # Parameters 84 | /// 85 | /// `access` - Access flags performed in submission. 86 | /// `usage` - Usage flags required by submission. 87 | pub fn new(node: LinkNode) -> Self { 88 | let mut link = Link { 89 | access: node.state.access, 90 | usage: node.state.usage, 91 | layout: node.state.layout, 92 | stages: node.state.stages, 93 | queue_count: 1, 94 | queues: Vec::new(), 95 | family: node.sid.family(), 96 | }; 97 | link.ensure_queue(node.sid.queue().index()); 98 | link.queues[node.sid.queue().index()] = Some(LinkQueueState::new(&node)); 99 | link 100 | } 101 | 102 | fn ensure_queue(&mut self, index: usize) { 103 | if index >= self.queues.len() { 104 | let reserve = index - self.queues.len() + 1; 105 | self.queues.reserve(reserve); 106 | while index >= self.queues.len() { 107 | self.queues.push(None); 108 | } 109 | } 110 | } 111 | 112 | /// Get queue family that owns the resource at the link. 113 | /// All associated submissions must be from the same queue family. 114 | pub fn family(&self) -> gfx_hal::queue::QueueFamilyId { 115 | self.family 116 | } 117 | 118 | /// Get usage. 119 | pub fn submission_state(&self, sid: SubmissionId) -> State { 120 | assert_eq!(sid.family(), self.family); 121 | let lqs = self.queues[sid.queue().index()].as_ref().unwrap(); 122 | State { 123 | access: lqs.access, 124 | layout: self.layout, 125 | stages: lqs.stages, 126 | usage: self.usage, 127 | } 128 | } 129 | 130 | /// Get usage. 131 | pub fn state(&self) -> State { 132 | State { 133 | access: self.access, 134 | layout: self.layout, 135 | stages: self.stages, 136 | usage: self.usage, 137 | } 138 | } 139 | 140 | /// Get access. 141 | pub fn access(&self) -> R::Access { 142 | self.access 143 | } 144 | 145 | /// Get layout. 146 | pub fn layout(&self) -> R::Layout { 147 | self.layout 148 | } 149 | 150 | /// Get usage. 151 | pub fn usage(&self) -> R::Usage { 152 | self.usage 153 | } 154 | 155 | // /// Get usage. 156 | // pub fn stages(&self) -> gfx_hal::pso::PipelineStage { 157 | // self.stages 158 | // } 159 | 160 | /// Check if the link is associated with only one queue. 161 | pub fn single_queue(&self) -> bool { 162 | self.queue_count == 1 163 | } 164 | 165 | /// Check if the given state and submission are compatible with link. 166 | /// If compatible then the submission can be associated with the link. 167 | pub fn compatible(&self, node: &LinkNode) -> bool { 168 | // If queue the same and states are compatible. 169 | self.family == node.sid.family() && !(self.access | node.state.access).exclusive() 170 | } 171 | 172 | /// Insert submission with specified state to the link. 173 | /// It must be compatible. 174 | /// Associating submission with the link will allow the submission 175 | /// to be executed in parallel with other submissions associated with this link. 176 | /// Unless other chains disallow. 177 | /// 178 | /// # Panics 179 | /// 180 | /// This function will panic if `state` and `sid` are not compatible. 181 | /// E.g. `Link::compatible` didn't return `true` for the arguments. 182 | /// 183 | pub fn add_node(&mut self, node: LinkNode) { 184 | assert_eq!(self.family, node.sid.family()); 185 | self.ensure_queue(node.sid.queue().index()); 186 | 187 | self.access |= node.state.access; 188 | self.usage |= node.state.usage; 189 | self.stages |= node.state.stages; 190 | 191 | match &mut self.queues[node.sid.queue().index()] { 192 | &mut Some(ref mut queue) => { 193 | queue.push(&node); 194 | } 195 | slot @ &mut None => { 196 | self.queue_count += 1; 197 | *slot = Some(LinkQueueState::new(&node)); 198 | } 199 | } 200 | } 201 | 202 | // /// Check if ownership transfer is required between those links. 203 | // pub fn transfer_required(&self, next: &Self) -> bool { 204 | // self.family != next.family 205 | // } 206 | 207 | /// Iterate over queues. 208 | pub fn queues(&self) -> impl Iterator)> { 209 | let family = self.family; 210 | self.queues 211 | .iter() 212 | .enumerate() 213 | .filter_map(move |(index, queue)| { 214 | queue 215 | .as_ref() 216 | .map(move |queue| (QueueId::new(family, index), queue)) 217 | }) 218 | } 219 | 220 | /// Get particular queue 221 | pub fn queue(&self, qid: QueueId) -> &LinkQueueState { 222 | assert_eq!(qid.family(), self.family); 223 | self.queues[qid.index()].as_ref().unwrap() 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /chain/src/chain/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! This module defines types to reason about what resources referenced in what submissions. 3 | //! How commands from those submissions access resources. 4 | //! This information allows to derive synchronization required. 5 | //! 6 | 7 | mod link; 8 | 9 | use std::ops::BitOr; 10 | 11 | use crate::{ 12 | resource::{Buffer, Image, Resource}, 13 | Id, 14 | }; 15 | 16 | pub use self::link::{Link, LinkNode}; 17 | 18 | /// This type corresponds to resource category. 19 | /// All resources from the same category must be accessed as permitted by links of the chain. 20 | #[derive(Clone, Debug)] 21 | pub struct Chain { 22 | links: Vec>, 23 | } 24 | 25 | impl Chain 26 | where 27 | R: Resource, 28 | { 29 | /// Get links slice 30 | pub fn links(&self) -> &[Link] { 31 | &self.links 32 | } 33 | 34 | /// Create new empty `Chain` 35 | pub fn new() -> Self { 36 | Chain { links: Vec::new() } 37 | } 38 | 39 | /// Get links slice 40 | pub fn last_link_mut(&mut self) -> Option<&mut Link> { 41 | self.links.last_mut() 42 | } 43 | 44 | /// Add new link to the chain. 45 | pub fn add_link(&mut self, link: Link) -> &mut Link { 46 | self.links.push(link); 47 | self.links.last_mut().unwrap() 48 | } 49 | 50 | // /// Get link by index. 51 | // pub(crate) fn link(&self, index: usize) -> &Link { 52 | // &self.links[index] 53 | // } 54 | 55 | // /// Get link by index. 56 | // pub(crate) fn link_mut(&mut self, index: usize) -> &mut Link { 57 | // &mut self.links[index] 58 | // } 59 | 60 | // /// Get link by index. 61 | // pub(crate) fn next_link(&self, index: usize) -> &Link { 62 | // let index = (index + 1) % self.links.len(); 63 | // self.link(index) 64 | // } 65 | 66 | // /// Get link by index. 67 | // pub(crate) fn next_link_mut(&mut self, index: usize) -> &mut Link { 68 | // let index = (index + 1) % self.links.len(); 69 | // self.link_mut(index) 70 | // } 71 | 72 | // /// Get link by index. 73 | // pub(crate) fn prev_link(&self, index: usize) -> &Link { 74 | // let index = (index + self.links.len() - 1) % self.links.len(); 75 | // self.link(index) 76 | // } 77 | 78 | // /// Get link by index. 79 | // pub(crate) fn prev_link_mut(&mut self, index: usize) -> &mut Link { 80 | // let index = (index + self.links.len() - 1) % self.links.len(); 81 | // self.link_mut(index) 82 | // } 83 | 84 | /// Get total usage. 85 | pub fn usage(&self) -> R::Usage { 86 | self.links 87 | .iter() 88 | .map(Link::usage) 89 | .fold(R::no_usage(), BitOr::bitor) 90 | } 91 | } 92 | 93 | /// Type alias for map of chains by id for buffers. 94 | pub(crate) type BufferChains = fnv::FnvHashMap>; 95 | 96 | /// Type alias for map of chains by id for images. 97 | pub(crate) type ImageChains = fnv::FnvHashMap>; 98 | -------------------------------------------------------------------------------- /chain/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate can derive synchronization required 2 | //! for the dependency chain of the whole execution graph. 3 | 4 | #![forbid(overflowing_literals)] 5 | #![deny(missing_copy_implementations)] 6 | #![deny(missing_debug_implementations)] 7 | #![deny(missing_docs)] 8 | #![deny(intra_doc_link_resolution_failure)] 9 | #![deny(path_statements)] 10 | #![deny(trivial_bounds)] 11 | #![deny(type_alias_bounds)] 12 | #![deny(unconditional_recursion)] 13 | #![deny(unions_with_drop_fields)] 14 | #![deny(while_true)] 15 | #![deny(unused)] 16 | #![deny(bad_style)] 17 | #![deny(future_incompatible)] 18 | #![deny(rust_2018_compatibility)] 19 | #![deny(rust_2018_idioms)] 20 | 21 | /// Unique resource id. 22 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 23 | pub struct Id(pub usize); 24 | 25 | mod chain; 26 | mod collect; 27 | mod node; 28 | mod resource; 29 | mod schedule; 30 | mod sync; 31 | 32 | pub use crate::{ 33 | chain::{Chain, Link, LinkNode}, 34 | collect::{collect, Chains, Unsynchronized}, 35 | node::{Node, State, BufferState, ImageState}, 36 | resource::{AccessFlags, Buffer, Image, Resource, UsageFlags}, 37 | schedule::{Family, Queue, QueueId, Schedule, Submission, SubmissionId}, 38 | sync::{sync, SyncData, Barrier, Barriers, BufferBarriers, ImageBarriers, Guard, Wait, Signal}, 39 | }; 40 | -------------------------------------------------------------------------------- /chain/src/node.rs: -------------------------------------------------------------------------------- 1 | use std::collections::hash_map::{HashMap, Iter as HashMapIter}; 2 | 3 | use crate::{ 4 | resource::{Buffer, Image, Resource}, 5 | Id, 6 | }; 7 | 8 | /// State in which node uses resource and usage flags. 9 | #[derive(Clone, Copy, Debug)] 10 | pub struct State { 11 | /// Access performed by the node. 12 | pub access: R::Access, 13 | 14 | /// Optional layout in which node can use resource. 15 | pub layout: R::Layout, 16 | 17 | /// Stages at which resource is accessed. 18 | pub stages: gfx_hal::pso::PipelineStage, 19 | 20 | /// Usage flags required for resource. 21 | pub usage: R::Usage, 22 | } 23 | 24 | /// Type alias for `State` 25 | pub type BufferState = State; 26 | 27 | /// Type alias for `State` 28 | pub type ImageState = State; 29 | 30 | /// Description of node. 31 | #[derive(Clone, Debug)] 32 | pub struct Node { 33 | /// Id of the node. 34 | pub id: usize, 35 | 36 | /// Family required to execute the node. 37 | pub family: gfx_hal::queue::QueueFamilyId, 38 | 39 | /// Dependencies of the node. 40 | /// Those are indices of other nodes in array. 41 | pub dependencies: Vec, 42 | 43 | /// Buffer category ids and required state. 44 | pub buffers: HashMap>, 45 | 46 | /// Image category ids and required state. 47 | pub images: HashMap>, 48 | } 49 | 50 | impl Node { 51 | /// Get family on which this node will be executed. 52 | pub fn family(&self) -> gfx_hal::queue::QueueFamilyId { 53 | self.family 54 | } 55 | 56 | /// Get indices of nodes this node depends on. 57 | pub fn dependencies(&self) -> &[usize] { 58 | &self.dependencies 59 | } 60 | 61 | /// Get iterator to buffer states this node accesses. 62 | pub fn buffers(&self) -> HashMapIter<'_, Id, State> { 63 | self.buffers.iter() 64 | } 65 | 66 | /// Get iterator to image states this node accesses. 67 | pub fn images(&self) -> HashMapIter<'_, Id, State> { 68 | self.images.iter() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /chain/src/resource.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fmt::Debug, 3 | ops::{BitOr, BitOrAssign}, 4 | }; 5 | 6 | /// Trait to abstract of specific access flags. 7 | pub trait AccessFlags: Copy + Debug + BitOr + BitOrAssign + 'static { 8 | /// Get flags value with no flags set. 9 | fn empty() -> Self; 10 | 11 | /// Check if this access must be exclusive. 12 | /// 13 | /// Basically this checks if all flags are known read flags. 14 | fn exclusive(&self) -> bool; 15 | } 16 | 17 | impl AccessFlags for gfx_hal::buffer::Access { 18 | #[inline] 19 | fn empty() -> Self { 20 | Self::empty() 21 | } 22 | 23 | #[inline] 24 | fn exclusive(&self) -> bool { 25 | self.intersects( 26 | Self::SHADER_WRITE | Self::TRANSFER_WRITE | Self::HOST_WRITE | Self::MEMORY_WRITE 27 | ) 28 | } 29 | } 30 | 31 | impl AccessFlags for gfx_hal::image::Access { 32 | #[inline] 33 | fn empty() -> Self { 34 | Self::empty() 35 | } 36 | 37 | #[inline] 38 | fn exclusive(&self) -> bool { 39 | self.intersects( 40 | Self::SHADER_WRITE | Self::COLOR_ATTACHMENT_WRITE | Self::DEPTH_STENCIL_ATTACHMENT_WRITE | Self::TRANSFER_WRITE | Self::HOST_WRITE | Self::MEMORY_WRITE 41 | ) 42 | } 43 | } 44 | 45 | /// Trait to abstract of specific usage flags. 46 | pub trait UsageFlags: Copy + Debug + BitOr + BitOrAssign + 'static {} 47 | 48 | impl UsageFlags for gfx_hal::buffer::Usage {} 49 | impl UsageFlags for gfx_hal::image::Usage {} 50 | 51 | /// Abstracts resource types that uses different usage flags and layouts types. 52 | pub trait Resource: 'static { 53 | /// Access flags for resource type. 54 | type Access: AccessFlags; 55 | 56 | /// Usage flags type for the resource. 57 | type Usage: UsageFlags; 58 | 59 | /// Layout type for the resource. 60 | type Layout: Copy + Debug + 'static; 61 | 62 | /// Empty usage. 63 | fn no_usage() -> Self::Usage; 64 | 65 | /// Layout suitable for specified accesses. 66 | fn layout_for(access: Self::Access) -> Self::Layout; 67 | } 68 | 69 | /// Buffer resource type. 70 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 71 | pub struct Buffer; 72 | 73 | impl Resource for Buffer { 74 | type Access = gfx_hal::buffer::Access; 75 | type Usage = gfx_hal::buffer::Usage; 76 | type Layout = (); 77 | 78 | fn no_usage() -> Self::Usage { 79 | gfx_hal::buffer::Usage::empty() 80 | } 81 | 82 | fn layout_for(_access: gfx_hal::buffer::Access) {} 83 | } 84 | 85 | /// Image resource type. 86 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 87 | pub struct Image; 88 | 89 | impl Resource for Image { 90 | 91 | type Access = gfx_hal::image::Access; 92 | 93 | type Usage = gfx_hal::image::Usage; 94 | 95 | type Layout = gfx_hal::image::Layout; 96 | 97 | fn no_usage() -> Self::Usage { 98 | gfx_hal::image::Usage::empty() 99 | } 100 | 101 | fn layout_for(access: gfx_hal::image::Access) -> gfx_hal::image::Layout { 102 | let mut acc = None; 103 | if access.contains(gfx_hal::image::Access::INPUT_ATTACHMENT_READ) { 104 | acc = Some(common_layout(acc, gfx_hal::image::Layout::ShaderReadOnlyOptimal)); 105 | } 106 | if access.contains(gfx_hal::image::Access::COLOR_ATTACHMENT_READ) { 107 | acc = Some(common_layout(acc, gfx_hal::image::Layout::ColorAttachmentOptimal)); 108 | } 109 | if access.contains(gfx_hal::image::Access::COLOR_ATTACHMENT_WRITE) { 110 | acc = Some(common_layout(acc, gfx_hal::image::Layout::ColorAttachmentOptimal)); 111 | } 112 | if access.contains(gfx_hal::image::Access::DEPTH_STENCIL_ATTACHMENT_READ) { 113 | acc = Some(common_layout(acc, gfx_hal::image::Layout::DepthStencilReadOnlyOptimal)); 114 | } 115 | if access.contains(gfx_hal::image::Access::DEPTH_STENCIL_ATTACHMENT_WRITE) { 116 | acc = Some(common_layout(acc, gfx_hal::image::Layout::DepthStencilAttachmentOptimal)); 117 | } 118 | if access.contains(gfx_hal::image::Access::TRANSFER_READ) { 119 | acc = Some(common_layout(acc, gfx_hal::image::Layout::TransferSrcOptimal)); 120 | } 121 | if access.contains(gfx_hal::image::Access::TRANSFER_WRITE) { 122 | acc = Some(common_layout(acc, gfx_hal::image::Layout::TransferDstOptimal)); 123 | } 124 | acc.unwrap_or(gfx_hal::image::Layout::General) 125 | } 126 | } 127 | 128 | 129 | fn common_layout(acc: Option, layout: gfx_hal::image::Layout) -> gfx_hal::image::Layout { 130 | match (acc, layout) { 131 | (None, layout) => layout, 132 | (Some(left), right) if left == right => left, 133 | ( 134 | Some(gfx_hal::image::Layout::DepthStencilReadOnlyOptimal), 135 | gfx_hal::image::Layout::DepthStencilAttachmentOptimal, 136 | ) => gfx_hal::image::Layout::DepthStencilAttachmentOptimal, 137 | ( 138 | Some(gfx_hal::image::Layout::DepthStencilAttachmentOptimal), 139 | gfx_hal::image::Layout::DepthStencilReadOnlyOptimal, 140 | ) => gfx_hal::image::Layout::DepthStencilAttachmentOptimal, 141 | (Some(_), _) => gfx_hal::image::Layout::General, 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /chain/src/schedule/family.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | queue::{Queue, QueueId}, 3 | submission::{Submission, SubmissionId}, 4 | }; 5 | 6 | /// Instances of this type contains array of `Queue`s. 7 | /// All contained queues has identical capabilities. 8 | #[derive(Clone, Debug)] 9 | pub struct Family { 10 | id: gfx_hal::queue::QueueFamilyId, 11 | queues: Vec>, 12 | } 13 | 14 | impl Family { 15 | /// Create new empty `Family` 16 | pub fn new(id: gfx_hal::queue::QueueFamilyId) -> Self { 17 | Family { 18 | id, 19 | queues: Vec::default(), 20 | } 21 | } 22 | 23 | /// Get id of the family. 24 | pub fn id(&self) -> gfx_hal::queue::QueueFamilyId { 25 | self.id 26 | } 27 | 28 | /// Get reference to `Queue` instance by the id. 29 | /// 30 | /// # Panic 31 | /// 32 | /// This function will panic if requested queue isn't part of this family. 33 | /// 34 | pub fn queue(&self, qid: QueueId) -> Option<&Queue> { 35 | assert_eq!(self.id, qid.family()); 36 | self.queues.get(qid.index()) 37 | } 38 | 39 | /// Get mutable reference to `Queue` instance by the id. 40 | /// 41 | /// # Panic 42 | /// 43 | /// This function will panic if requested queue isn't part of this family. 44 | /// 45 | pub fn queue_mut(&mut self, qid: QueueId) -> Option<&mut Queue> { 46 | assert_eq!(self.id, qid.family()); 47 | self.queues.get_mut(qid.index()) 48 | } 49 | 50 | /// Get mutable reference to `Queue` instance by the id. 51 | /// This function will grow queues array if index is out of bounds. 52 | /// 53 | /// # Panic 54 | /// 55 | /// This function will panic if requested queue isn't part of this family. 56 | /// 57 | pub fn ensure_queue(&mut self, qid: QueueId) -> &mut Queue { 58 | assert_eq!(self.id, qid.family()); 59 | let len = self.queues.len(); 60 | self.queues 61 | .extend((len..qid.index() + 1).map(|i| Queue::new(QueueId::new(qid.family(), i)))); 62 | &mut self.queues[qid.index()] 63 | } 64 | 65 | /// Get reference to `Submission` instance by id. 66 | /// 67 | /// # Panic 68 | /// 69 | /// This function will panic if requested submission isn't part of this family. 70 | /// 71 | pub fn submission(&self, sid: SubmissionId) -> Option<&Submission> { 72 | assert_eq!(self.id, sid.family()); 73 | self.queue(sid.queue()) 74 | .and_then(|queue| queue.submission(sid)) 75 | } 76 | 77 | /// Get mutable reference to `Submission` instance by id. 78 | /// 79 | /// # Panic 80 | /// 81 | /// This function will panic if requested submission isn't part of this family. 82 | /// 83 | pub fn submission_mut(&mut self, sid: SubmissionId) -> Option<&mut Submission> { 84 | assert_eq!(self.id, sid.family()); 85 | self.queue_mut(sid.queue()) 86 | .and_then(|queue| queue.submission_mut(sid)) 87 | } 88 | 89 | /// Iterate over queues. 90 | pub fn iter(&self) -> impl Iterator> { 91 | self.queues.iter() 92 | } 93 | 94 | /// Iterate over queues. 95 | pub fn iter_mut(&mut self) -> impl Iterator> { 96 | self.queues.iter_mut() 97 | } 98 | 99 | /// The number of queues in this schedule. 100 | pub fn queue_count(&self) -> usize { 101 | self.queues.len() 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /chain/src/schedule/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! This module defines types for execution hierarchy. 3 | //! 4 | //! `Submission` is a piece of work that can be recorded into single primary command buffer. 5 | //! `Submission` contains references to links and semaphores required to wait/signal. 6 | //! `Queue` contains array of `Submission`'s. User is expected to submission corresponding command buffers in the order. 7 | //! `Queue`'s are grouped into `Family`. All queues from one `Family` has identical capabilities. 8 | //! `Schedule` is a set or `Family` instances. 9 | //! 10 | 11 | mod family; 12 | mod queue; 13 | mod submission; 14 | 15 | use std::ops::{Index, IndexMut}; 16 | 17 | #[allow(unreachable_pub)] 18 | pub use self::{ 19 | family::Family, 20 | queue::{Queue, QueueId}, 21 | submission::{Submission, SubmissionId}, 22 | }; 23 | 24 | /// Whole passes schedule. 25 | #[derive(Clone, Debug)] 26 | pub struct Schedule { 27 | map: fnv::FnvHashMap>, 28 | ordered: Vec, 29 | } 30 | 31 | impl Schedule { 32 | /// Create new empty `Schedule` 33 | pub fn new() -> Self { 34 | Schedule { 35 | map: fnv::FnvHashMap::default(), 36 | ordered: Vec::new(), 37 | } 38 | } 39 | 40 | /// Get total number of submissions. 41 | pub fn total(&self) -> usize { 42 | self.ordered.len() 43 | } 44 | 45 | /// Iterate over submissions in ordered they must be submitted. 46 | pub fn ordered(&self) -> impl Iterator> { 47 | let ref map = self.map; 48 | 49 | self.ordered 50 | .iter() 51 | .map(move |&sid| map[&sid.family()].submission(sid).unwrap()) 52 | } 53 | 54 | /// The number of families in this schedule. 55 | pub fn family_count(&self) -> usize { 56 | self.map.len() 57 | } 58 | 59 | /// The number of queues in this schedule. 60 | pub fn queue_count(&self) -> usize { 61 | self.map.iter().map(|x| x.1.queue_count()).sum() 62 | } 63 | 64 | /// Iterate over immutable references to families in this schedule. 65 | pub fn iter(&self) -> impl Iterator> { 66 | self.map.values() 67 | } 68 | 69 | /// Iterate over mutable references to families in this schedule 70 | pub fn iter_mut(&mut self) -> impl Iterator> { 71 | self.map.values_mut() 72 | } 73 | 74 | /// Get reference to `Family` instance by the id. 75 | pub fn family(&self, fid: gfx_hal::queue::QueueFamilyId) -> Option<&Family> { 76 | self.map.get(&fid) 77 | } 78 | 79 | /// Get mutable reference to `Family` instance by the id. 80 | pub fn family_mut(&mut self, fid: gfx_hal::queue::QueueFamilyId) -> Option<&mut Family> { 81 | self.map.get_mut(&fid) 82 | } 83 | 84 | /// Get reference to `Queue` instance by the id. 85 | pub fn queue(&self, qid: QueueId) -> Option<&Queue> { 86 | self.family(qid.family()) 87 | .and_then(|family| family.queue(qid)) 88 | } 89 | 90 | /// Get mutable reference to `Queue` instance by the id. 91 | pub fn queue_mut(&mut self, qid: QueueId) -> Option<&mut Queue> { 92 | self.family_mut(qid.family()) 93 | .and_then(|family| family.queue_mut(qid)) 94 | } 95 | 96 | /// Get reference to `Submission` instance by id. 97 | pub fn submission(&self, sid: SubmissionId) -> Option<&Submission> { 98 | self.queue(sid.queue()) 99 | .and_then(|queue| queue.submission(sid)) 100 | } 101 | 102 | /// Get reference to `Submission` instance by id. 103 | pub fn submission_mut(&mut self, sid: SubmissionId) -> Option<&mut Submission> { 104 | self.queue_mut(sid.queue()) 105 | .and_then(|queue| queue.submission_mut(sid)) 106 | } 107 | 108 | /// Get mutable reference to `Family` instance by the id. 109 | /// This function will add empty `Family` if id is not present. 110 | pub fn ensure_family(&mut self, fid: gfx_hal::queue::QueueFamilyId) -> &mut Family { 111 | self.map.entry(fid).or_insert_with(|| Family::new(fid)) 112 | } 113 | 114 | /// Get mutable reference to `Queue` instance by the id. 115 | /// This function will grow queues array if index is out of bounds. 116 | pub fn ensure_queue(&mut self, qid: QueueId) -> &mut Queue { 117 | self.ensure_family(qid.family()).ensure_queue(qid) 118 | } 119 | 120 | /// Set queue to the schedule. 121 | pub(crate) fn set_queue(&mut self, queue: Queue) { 122 | let qid = queue.id(); 123 | *self.ensure_queue(qid) = queue; 124 | } 125 | 126 | /// Make ordered. 127 | pub fn build_order(&mut self) { 128 | let mut oredered: Vec = Vec::new(); 129 | 130 | { 131 | let submissions = self.iter().flat_map(|family| { 132 | family.iter().flat_map(|queue| { 133 | queue.iter() 134 | }) 135 | }); 136 | 137 | for submission in submissions { 138 | if submission.submit_order() == !0 { 139 | continue; 140 | } 141 | let len = oredered.len(); 142 | if len <= submission.submit_order() { 143 | oredered.extend((len .. submission.submit_order() + 1).map(|_| submission.id())); 144 | } else { 145 | oredered[submission.submit_order()] = submission.id(); 146 | } 147 | } 148 | } 149 | 150 | log::debug!("Schedule order: {:#?}", oredered); 151 | self.ordered = oredered; 152 | } 153 | } 154 | 155 | impl Index for Schedule { 156 | type Output = Queue; 157 | 158 | fn index(&self, qid: QueueId) -> &Queue { 159 | self.queue(qid).unwrap() 160 | } 161 | } 162 | 163 | impl IndexMut for Schedule { 164 | fn index_mut(&mut self, qid: QueueId) -> &mut Queue { 165 | self.queue_mut(qid).unwrap() 166 | } 167 | } 168 | 169 | impl Index for Schedule { 170 | type Output = Submission; 171 | 172 | fn index(&self, sid: SubmissionId) -> &Submission { 173 | self.submission(sid).unwrap() 174 | } 175 | } 176 | 177 | impl IndexMut for Schedule { 178 | fn index_mut(&mut self, sid: SubmissionId) -> &mut Submission { 179 | self.submission_mut(sid).unwrap() 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /chain/src/schedule/queue.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | submission::{Submission, SubmissionId}, 3 | }; 4 | 5 | /// Queue id. 6 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 7 | pub struct QueueId { 8 | /// Family id of the queue. 9 | pub family: gfx_hal::queue::QueueFamilyId, 10 | 11 | /// Index of the queue. 12 | pub index: usize, 13 | } 14 | 15 | impl QueueId { 16 | /// Create queue id from family id and index. 17 | pub fn new(family: gfx_hal::queue::QueueFamilyId, index: usize) -> Self { 18 | QueueId { 19 | family: family, 20 | index, 21 | } 22 | } 23 | 24 | /// Get family id. 25 | pub fn family(&self) -> gfx_hal::queue::QueueFamilyId { 26 | self.family 27 | } 28 | 29 | /// Get index within the family. 30 | pub fn index(&self) -> usize { 31 | self.index 32 | } 33 | } 34 | 35 | /// Instances of this type contains array of `Submission`s. 36 | /// Those submissions are expected to be submitted in order. 37 | #[derive(Clone, Debug)] 38 | pub struct Queue { 39 | id: QueueId, 40 | submissions: Vec>, 41 | } 42 | 43 | impl Queue { 44 | /// Create new queue with specified id. 45 | pub fn new(id: QueueId) -> Self { 46 | Queue { 47 | id, 48 | submissions: Vec::new(), 49 | } 50 | } 51 | 52 | /// Get id of the queue. 53 | pub fn id(&self) -> QueueId { 54 | self.id 55 | } 56 | 57 | /// Iterate over immutable references to each submission in this queue 58 | pub fn iter(&self) -> impl Iterator> { 59 | self.submissions.iter() 60 | } 61 | 62 | /// Iterate over mutable references to each submission in this queue 63 | pub fn iter_mut(&mut self) -> impl Iterator> { 64 | self.submissions.iter_mut() 65 | } 66 | 67 | // /// Iterate over mutable references to each submission in this queue 68 | // pub fn into_iter(self) -> QueueIntoIter { 69 | // QueueIntoIter { 70 | // qid: self.id, 71 | // iter: self.submissions.into_iter().enumerate(), 72 | // } 73 | // } 74 | 75 | /// Get the number of submissions in queue. 76 | pub fn len(&self) -> usize { 77 | self.submissions.len() 78 | } 79 | 80 | /// Get reference to `Submission` instance by id. 81 | /// 82 | /// # Panic 83 | /// 84 | /// This function will panic if requested submission isn't part of this queue. 85 | /// 86 | pub fn submission(&self, sid: SubmissionId) -> Option<&Submission> { 87 | assert_eq!(self.id, sid.queue()); 88 | self.submissions.get(sid.index()) 89 | } 90 | 91 | /// Get mutable reference to `Submission` instance by id. 92 | /// 93 | /// # Panic 94 | /// 95 | /// This function will panic if requested submission isn't part of this queue. 96 | /// 97 | pub fn submission_mut(&mut self, sid: SubmissionId) -> Option<&mut Submission> { 98 | assert_eq!(self.id, sid.queue()); 99 | self.submissions.get_mut(sid.index()) 100 | } 101 | 102 | // /// Get reference to last `Submission` instance. 103 | // pub fn last_submission(&self) -> Option<&Submission> { 104 | // self.submissions.last() 105 | // } 106 | 107 | // /// Get mutable reference to last `Submission` instance. 108 | // pub fn last_submission_mut(&mut self) -> Option<&mut Submission> { 109 | // self.submissions.last_mut() 110 | // } 111 | 112 | /// Add `Submission` instance to the end of queue. 113 | /// Returns id of the added submission. 114 | pub fn add_submission( 115 | &mut self, 116 | node: usize, 117 | wait_factor: usize, 118 | submit_order: usize, 119 | sync: S, 120 | ) -> SubmissionId { 121 | let sid = SubmissionId::new(self.id, self.submissions.len()); 122 | self.submissions 123 | .push(Submission::new(node, wait_factor, submit_order, sid, sync)); 124 | sid 125 | } 126 | 127 | /// Add `Submission` instance to the end of queue. 128 | /// Check that submission has correct id. 129 | pub fn add_submission_checked(&mut self, submission: Submission) { 130 | assert_eq!( 131 | submission.id(), 132 | SubmissionId::new(self.id, self.submissions.len()) 133 | ); 134 | self.submissions.push(submission); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /chain/src/schedule/submission.rs: -------------------------------------------------------------------------------- 1 | 2 | use super::queue::QueueId; 3 | use crate::Id; 4 | 5 | /// Submission id. 6 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 7 | pub struct SubmissionId { 8 | /// Queue id of the submission. 9 | pub queue: QueueId, 10 | 11 | /// Index of the queue. 12 | pub index: usize, 13 | } 14 | 15 | impl SubmissionId { 16 | /// Create new id from queue id and index. 17 | pub fn new(queue: QueueId, index: usize) -> Self { 18 | SubmissionId { queue, index } 19 | } 20 | 21 | /// Get family id. 22 | pub fn family(&self) -> gfx_hal::queue::QueueFamilyId { 23 | self.queue.family() 24 | } 25 | 26 | /// Get queue id. 27 | pub fn queue(&self) -> QueueId { 28 | self.queue 29 | } 30 | 31 | /// Get index. 32 | pub fn index(&self) -> usize { 33 | self.index 34 | } 35 | } 36 | 37 | /// This type corresponds to commands that should be recorded into single primary command buffer. 38 | #[derive(Clone, Debug)] 39 | pub struct Submission { 40 | node: usize, 41 | id: SubmissionId, 42 | resource_links: fnv::FnvHashMap, 43 | wait_factor: usize, 44 | submit_order: usize, 45 | sync: S, 46 | } 47 | 48 | impl Submission { 49 | /// Get id of the `Node`. 50 | pub fn node(&self) -> usize { 51 | self.node 52 | } 53 | 54 | /// Get id of the `Submission`. 55 | pub fn id(&self) -> SubmissionId { 56 | self.id 57 | } 58 | 59 | /// Get synchronization for `Submission`. 60 | pub fn sync(&self) -> &S { 61 | &self.sync 62 | } 63 | 64 | /// Get wait factor for `Submission` 65 | pub fn wait_factor(&self) -> usize { 66 | self.wait_factor 67 | } 68 | 69 | /// Get submit order for `Submission` 70 | pub fn submit_order(&self) -> usize { 71 | self.submit_order 72 | } 73 | 74 | /// Get link index for buffer by id. 75 | pub fn resource_link_index(&self, id: Id) -> usize { 76 | self.resource_links[&id] 77 | } 78 | 79 | /// Create new submission with specified pass. 80 | pub(crate) fn new( 81 | node: usize, 82 | wait_factor: usize, 83 | submit_order: usize, 84 | id: SubmissionId, 85 | sync: S, 86 | ) -> Self { 87 | Submission { 88 | node, 89 | resource_links: fnv::FnvHashMap::default(), 90 | id, 91 | wait_factor, 92 | submit_order, 93 | sync, 94 | } 95 | } 96 | 97 | /// Set synchronization to the `Submission`. 98 | pub fn set_sync(&self, sync: T) -> Submission { 99 | Submission { 100 | node: self.node, 101 | resource_links: self.resource_links.clone(), 102 | id: self.id, 103 | wait_factor: self.wait_factor, 104 | submit_order: self.submit_order, 105 | sync, 106 | } 107 | } 108 | 109 | /// Set link index for given chain. 110 | pub fn set_link(&mut self, id: Id, link: usize) { 111 | self.resource_links.insert(id, link); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /command/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-command" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | gfx-hal = { git = "https://github.com/gfx-rs/gfx" } 9 | derivative = "1.0" 10 | failure = "0.1" 11 | relevant = "0.2" 12 | smallvec = "0.6" 13 | -------------------------------------------------------------------------------- /command/src/buffer/level.rs: -------------------------------------------------------------------------------- 1 | 2 | /// Command buffers of this level can be submitted to the command queues. 3 | #[derive(Clone, Copy, Debug, Default)] 4 | pub struct PrimaryLevel; 5 | 6 | /// Command buffers of this level can be executed as part of the primary buffers. 7 | #[derive(Clone, Copy, Debug, Default)] 8 | pub struct SecondaryLevel; 9 | 10 | /// Type-level buffer level flag. 11 | /// It defines whether buffer can be submitted to the command queues 12 | /// or executed as part of the primary buffers. 13 | pub trait Level: Copy + Default + std::fmt::Debug + 'static { 14 | /// Get raw level value for command buffer allocation. 15 | fn raw_level(&self) -> gfx_hal::command::RawLevel; 16 | } 17 | 18 | impl Level for PrimaryLevel { 19 | fn raw_level(&self) -> gfx_hal::command::RawLevel { 20 | gfx_hal::command::RawLevel::Primary 21 | } 22 | } 23 | 24 | impl Level for SecondaryLevel { 25 | fn raw_level(&self) -> gfx_hal::command::RawLevel { 26 | gfx_hal::command::RawLevel::Secondary 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /command/src/buffer/mod.rs: -------------------------------------------------------------------------------- 1 | //! Command buffer module docs. 2 | 3 | mod encoder; 4 | mod level; 5 | mod reset; 6 | mod state; 7 | mod submit; 8 | mod usage; 9 | 10 | use crate::{ 11 | capability::{Capability, Supports}, 12 | }; 13 | 14 | pub use self::{ 15 | level::*, 16 | reset::*, 17 | state::*, 18 | submit::*, 19 | usage::*, 20 | encoder::*, 21 | }; 22 | 23 | /// Command buffer wrapper. 24 | /// This wrapper defines state with usage, level and ability to be individually reset at type level. 25 | /// This way many methods become safe. 26 | #[derive(derivative::Derivative)] 27 | #[derivative(Debug)] 28 | pub struct CommandBuffer { 29 | #[derivative(Debug = "ignore")] 30 | raw: Box, 31 | capability: C, 32 | state: S, 33 | level: L, 34 | reset: R, 35 | family: gfx_hal::queue::QueueFamilyId, 36 | } 37 | 38 | impl CommandBuffer 39 | where 40 | B: gfx_hal::Backend, 41 | { 42 | /// Wrap raw buffer handle. 43 | /// 44 | /// # Safety 45 | /// 46 | /// * `raw` must be valid command buffer handle. 47 | /// * `capability` must be subset of `family` capability. 48 | /// * `state` must represent actual state buffer currently in. 49 | /// * command buffer must be allocated with specified `level`. 50 | /// * If `reset` is `IndividualReset` then buffer must be allocated from pool created with `IndividualReset` marker. 51 | /// * command buffer must be allocated from pool created for `family`. 52 | pub(crate) unsafe fn from_raw( 53 | raw: B::CommandBuffer, 54 | capability: C, 55 | state: S, 56 | level: L, 57 | reset: R, 58 | family: gfx_hal::queue::QueueFamilyId, 59 | ) -> Self { 60 | CommandBuffer { 61 | raw: Box::new(raw), 62 | capability, 63 | state, 64 | level, 65 | reset, 66 | family, 67 | } 68 | } 69 | 70 | /// Change state of the command buffer. 71 | /// 72 | /// # Safety 73 | /// 74 | /// * This method must be used only to reflect state changed due to raw handle usage. 75 | pub unsafe fn change_state(self, f: impl FnOnce(S) -> U) -> CommandBuffer { 76 | CommandBuffer { 77 | raw: self.raw, 78 | capability: self.capability, 79 | state: f(self.state), 80 | level: self.level, 81 | reset: self.reset, 82 | family: self.family, 83 | } 84 | } 85 | 86 | /// Get buffers capability. 87 | pub fn capability(&self) -> C 88 | where 89 | C: Capability, 90 | { 91 | self.capability 92 | } 93 | 94 | /// Get buffers family. 95 | pub fn family(&self) -> gfx_hal::queue::QueueFamilyId { 96 | self.family 97 | } 98 | 99 | /// Convert capability level. 100 | pub fn with_queue_type(self) -> CommandBuffer 101 | where 102 | C: Capability, 103 | { 104 | CommandBuffer { 105 | raw: self.raw, 106 | capability: self.capability.into_queue_type(), 107 | state: self.state, 108 | level: self.level, 109 | reset: self.reset, 110 | family: self.family, 111 | } 112 | } 113 | 114 | /// Convert capability level. 115 | pub fn with_capability(self) -> Result, Self> 116 | where 117 | C: Supports, 118 | { 119 | if let Some(capability) = self.capability.supports() { 120 | Ok(CommandBuffer { 121 | raw: self.raw, 122 | capability: capability, 123 | state: self.state, 124 | level: self.level, 125 | reset: self.reset, 126 | family: self.family, 127 | }) 128 | } else { 129 | Err(self) 130 | } 131 | } 132 | } 133 | 134 | impl CommandBuffer 135 | where 136 | B: gfx_hal::Backend, 137 | { 138 | /// Begin recording command buffer. 139 | /// 140 | /// # Parameters 141 | /// 142 | /// `usage` - specifies usage of the command buffer. Possible types are `OneShot`, `MultiShot`. 143 | pub fn begin( 144 | mut self, 145 | ) -> CommandBuffer, L, R> 146 | where 147 | U: Usage, 148 | P: RenderPassRelation, 149 | { 150 | let usage = U::default(); 151 | let pass_continue = P::default(); 152 | unsafe { 153 | gfx_hal::command::RawCommandBuffer::begin( 154 | self.raw(), 155 | usage.flags() | pass_continue.flags(), 156 | gfx_hal::command::CommandBufferInheritanceInfo::default(), 157 | ); 158 | 159 | self.change_state(|_| RecordingState(usage, pass_continue)) 160 | } 161 | } 162 | } 163 | 164 | impl<'a, B, C, U, P, L, R> CommandBuffer, L, R> 165 | where 166 | B: gfx_hal::Backend, 167 | { 168 | /// Finish recording command buffer. 169 | pub fn finish(mut self) -> CommandBuffer, L, R> { 170 | unsafe { 171 | gfx_hal::command::RawCommandBuffer::finish( 172 | &mut*self.raw, 173 | ); 174 | 175 | self.change_state(|s| ExecutableState(s.0, s.1)) 176 | } 177 | } 178 | } 179 | 180 | impl CommandBuffer, L, R> 181 | where 182 | B: gfx_hal::Backend, 183 | { 184 | /// Mark command buffer as complete. 185 | /// 186 | /// # Safety 187 | /// 188 | /// * Commands recoreded to this buffer must be complete. 189 | /// Normally command buffer moved to `PendingState` when [`Submit`] object is created. 190 | /// To ensure that [submitted] commands are complete one can [wait] for the [`Fence`] specified 191 | /// when [submitting] created [`Submit`] object or in later submission to the same queue. 192 | /// 193 | /// [`Submit`]: struct.Submit 194 | /// [wait]: ..gfx_hal/device/trait.Device.html#method.wait_for_fences 195 | /// [`Fence`]: ..gfx_hal/trait.Backend.html#associatedtype.Fence 196 | /// [submitted]: ..gfx_hal/queue/struct.CommandQueue.html#method.submit 197 | /// [submitting]: ..gfx_hal/queue/struct.CommandQueue.html#method.submit 198 | pub unsafe fn mark_complete(self) -> CommandBuffer { 199 | self.change_state(|PendingState(state)| state) 200 | } 201 | } 202 | 203 | impl CommandBuffer 204 | where 205 | B: gfx_hal::Backend, 206 | S: Resettable, 207 | { 208 | /// Reset command buffer. 209 | pub fn reset(self) -> CommandBuffer { 210 | unsafe { self.change_state(|_| InitialState) } 211 | } 212 | } 213 | 214 | impl CommandBuffer 215 | where 216 | B: gfx_hal::Backend, 217 | S: Resettable, 218 | { 219 | /// Mark command buffer as reset. 220 | /// 221 | /// # Safety 222 | /// 223 | /// * This function must be used only to reflect command buffer being reset implicitly. 224 | /// For instance: 225 | /// * [`CommandPool::reset`](struct.CommandPool.html#method.reset) on pool from which the command buffer was allocated. 226 | /// * Raw handle usage. 227 | pub unsafe fn mark_reset(self) -> CommandBuffer { 228 | self.change_state(|_| InitialState) 229 | } 230 | } 231 | 232 | impl CommandBuffer 233 | where 234 | B: gfx_hal::Backend, 235 | S: Resettable, 236 | { 237 | /// Dispose of command buffer wrapper releasing raw comman buffer value. 238 | pub unsafe fn into_raw(self) -> B::CommandBuffer { 239 | *self.raw 240 | } 241 | 242 | /// Get raw command buffer handle. 243 | pub fn raw(&mut self) -> &mut B::CommandBuffer { 244 | &mut self.raw 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /command/src/buffer/reset.rs: -------------------------------------------------------------------------------- 1 | 2 | use super::{ 3 | state::*, 4 | usage::*, 5 | }; 6 | 7 | /// This flag specify that buffer can be reset individually. 8 | #[derive(Clone, Copy, Debug, Default)] 9 | pub struct IndividualReset; 10 | 11 | /// This flag specify that buffer cannot be reset individually. 12 | #[derive(Clone, Copy, Debug, Default)] 13 | pub struct NoIndividualReset; 14 | 15 | /// Specify flags required for command pool creation to allow individual buffer reset. 16 | pub trait Reset: Copy + Default + std::fmt::Debug + 'static { 17 | /// Get flags for reset parameter. 18 | fn flags(&self) -> gfx_hal::pool::CommandPoolCreateFlags; 19 | } 20 | 21 | impl Reset for IndividualReset { 22 | fn flags(&self) -> gfx_hal::pool::CommandPoolCreateFlags { 23 | gfx_hal::pool::CommandPoolCreateFlags::RESET_INDIVIDUAL 24 | } 25 | } 26 | 27 | impl Reset for NoIndividualReset { 28 | fn flags(&self) -> gfx_hal::pool::CommandPoolCreateFlags { 29 | gfx_hal::pool::CommandPoolCreateFlags::empty() 30 | } 31 | } 32 | 33 | /// States in which command buffer can de reset. 34 | pub trait Resettable: Copy + Default + std::fmt::Debug + 'static {} 35 | impl Resettable for InitialState {} 36 | impl Resettable for RecordingState where U: Usage, P: Copy + Default + std::fmt::Debug + 'static {} 37 | impl Resettable for ExecutableState where U: Usage, P: Copy + Default + std::fmt::Debug + 'static {} 38 | impl Resettable for InvalidState {} 39 | -------------------------------------------------------------------------------- /command/src/buffer/state.rs: -------------------------------------------------------------------------------- 1 | 2 | use super::usage::{MultiShot, OutsideRenderPass}; 3 | 4 | /// Command buffer state in which all buffers start. 5 | /// Resetting also moves buffer to this state. 6 | #[derive(Clone, Copy, Debug, Default)] 7 | pub struct InitialState; 8 | 9 | /// Command buffer in recording state could be populated with commands. 10 | #[derive(Clone, Copy, Debug, Default)] 11 | pub struct RecordingState(pub U, pub P); 12 | 13 | /// Command buffer in executable state can be submitted. 14 | #[derive(Clone, Copy, Debug, Default)] 15 | pub struct ExecutableState(pub U, pub P); 16 | 17 | /// Command buffer in pending state are submitted to the device. 18 | /// Command buffer in pending state must never be invalidated or reset because device may read it at the moment. 19 | /// Proving device is done with buffer requires nontrivial strategies. 20 | /// Therefore moving buffer from pending state requires `unsafe` method. 21 | #[derive(Clone, Copy, Debug, Default)] 22 | pub struct PendingState(pub N); 23 | 24 | /// One-shot buffers move to invalid state after execution. 25 | /// Invalidating any resource referenced in any command recorded to the buffer implicitly move it to the invalid state. 26 | #[derive(Clone, Copy, Debug, Default)] 27 | pub struct InvalidState; 28 | 29 | -------------------------------------------------------------------------------- /command/src/buffer/submit.rs: -------------------------------------------------------------------------------- 1 | 2 | use super::{ 3 | CommandBuffer, 4 | level::PrimaryLevel, 5 | state::{ExecutableState, PendingState, InvalidState}, 6 | usage::{OneShot, MultiShot, SimultaneousUse, NoSimultaneousUse, OutsideRenderPass}, 7 | }; 8 | 9 | /// Structure contains command buffer ready for submission. 10 | #[derive(derivative::Derivative)] 11 | #[derivative(Debug)] 12 | pub struct Submit { 13 | #[derivative(Debug = "ignore")] 14 | raw: *const B::CommandBuffer, 15 | family: gfx_hal::queue::QueueFamilyId, 16 | pass_continue: P, 17 | simultaneous: S, 18 | level: L, 19 | } 20 | 21 | unsafe impl Send for Submit 22 | where 23 | B: gfx_hal::Backend, 24 | B::CommandBuffer: Sync, 25 | {} 26 | unsafe impl Sync for Submit 27 | where 28 | B: gfx_hal::Backend, 29 | B::CommandBuffer: Sync, 30 | {} 31 | 32 | /// Submittable object. 33 | /// Values that implement this trait can be submitted to the queues 34 | /// or executed as part of primary buffers (in case of `Submittable`). 35 | pub unsafe trait Submittable { 36 | /// Get family that this submittable is belong to. 37 | fn family(&self) -> gfx_hal::queue::QueueFamilyId; 38 | 39 | /// Get raw command buffer. 40 | /// 41 | /// # Safety 42 | /// 43 | /// The command buffer is returned as raw pointer 44 | /// because its lifetime is not tied to `Submittable` instance lifetime 45 | /// but rather to original `CommandBuffer`. 46 | /// The command buffer cannot be freed before commands are complete 47 | /// which cannot be done before they are submitted. 48 | /// Dereferencing this pointer to perform submission is totally safe. 49 | /// On the other hand calling `CommandBuffer::mark_complete` (which must be done so buffer may be freed) 50 | /// before this pointer used for submission is considered an error. 51 | fn raw(self) -> *const B::CommandBuffer; 52 | } 53 | 54 | unsafe impl Submittable for Submit 55 | where 56 | B: gfx_hal::Backend, 57 | { 58 | fn family(&self) -> gfx_hal::queue::QueueFamilyId { 59 | self.family 60 | } 61 | 62 | fn raw(self) -> *const B::CommandBuffer { 63 | self.raw 64 | } 65 | } 66 | 67 | unsafe impl<'a, B, L, P> Submittable for &'a Submit 68 | where 69 | B: gfx_hal::Backend, 70 | { 71 | fn family(&self) -> gfx_hal::queue::QueueFamilyId { 72 | self.family 73 | } 74 | 75 | fn raw(self) -> *const B::CommandBuffer { 76 | self.raw 77 | } 78 | } 79 | 80 | impl CommandBuffer, L, R> 81 | where 82 | B: gfx_hal::Backend, 83 | P: Copy, 84 | L: Copy, 85 | { 86 | /// Produce `Submit` object that can be used to populate submission. 87 | pub fn submit_once( 88 | self, 89 | ) -> ( 90 | Submit, 91 | CommandBuffer, L, R>, 92 | ) { 93 | let pass_continue = self.state.1; 94 | let level = self.level; 95 | 96 | let buffer = unsafe { self.change_state(|_| PendingState(InvalidState)) }; 97 | 98 | let submit = Submit { 99 | raw: &*buffer.raw, 100 | family: buffer.family, 101 | pass_continue, 102 | simultaneous: NoSimultaneousUse, 103 | level, 104 | }; 105 | 106 | (submit, buffer) 107 | } 108 | } 109 | 110 | impl CommandBuffer, P>, L, R> 111 | where 112 | B: gfx_hal::Backend, 113 | P: Copy, 114 | S: Copy, 115 | L: Copy, 116 | { 117 | /// Produce `Submit` object that can be used to populate submission. 118 | pub fn submit( 119 | self, 120 | ) -> ( 121 | Submit, 122 | CommandBuffer, P>>, L, R>, 123 | ) { 124 | let MultiShot(simultaneous) = self.state.0; 125 | let pass_continue = self.state.1; 126 | let level = self.level; 127 | 128 | let buffer = unsafe { self.change_state(|state| PendingState(state)) }; 129 | 130 | let submit = Submit { 131 | raw: &*buffer.raw, 132 | family: buffer.family, 133 | pass_continue, 134 | simultaneous, 135 | level, 136 | }; 137 | 138 | (submit, buffer) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /command/src/buffer/usage.rs: -------------------------------------------------------------------------------- 1 | 2 | use super::{SecondaryLevel}; 3 | 4 | /// Command buffer with this usage flag will move to invalid state after execution. 5 | /// Resubmitting will require reset and rerecording commands. 6 | #[derive(Clone, Copy, Debug, Default)] 7 | pub struct OneShot; 8 | 9 | /// Command buffer with this usage flag will move back to executable state after execution. 10 | #[derive(Clone, Copy, Debug, Default)] 11 | pub struct MultiShot(pub S); 12 | 13 | /// Additional flag that allows resubmission of a command buffer while it is still in a pending state. 14 | /// `Submit` can be submitted more than once. 15 | #[derive(Clone, Copy, Debug, Default)] 16 | pub struct SimultaneousUse; 17 | 18 | /// Additional flag that disallows resubmission of a command buffer while it is still in a pending state 19 | /// It must be completed, i.e. a fence must submitted with this buffer or later into the same queue 20 | /// and be waited on before buffer resubmission. 21 | /// `Submit` cannot be submitted more than once. 22 | #[derive(Clone, Copy, Debug, Default)] 23 | pub struct NoSimultaneousUse; 24 | 25 | /// Buffers with this usage flag must be secondary buffers executed entirely in render-pass. 26 | #[derive(Clone, Copy, Debug, Default)] 27 | pub struct RenderPassContinue; 28 | 29 | /// Primary buffers must has this flag as they cannot has `RenderPassContinue` flag. 30 | /// Secondary buffers with this usage flag cannot be executed as part of render-pass. 31 | #[derive(Clone, Copy, Debug, Default)] 32 | pub struct OutsideRenderPass; 33 | 34 | /// Type-level usage flags. 35 | /// It defines if buffer can be resubmitted without reset. 36 | /// Or even resubmitted while being executed. 37 | pub trait Usage: Copy + Default + std::fmt::Debug + 'static { 38 | /// Flags required to begin command buffer. 39 | fn flags(&self) -> gfx_hal::command::CommandBufferFlags; 40 | } 41 | 42 | impl Usage for OneShot { 43 | fn flags(&self) -> gfx_hal::command::CommandBufferFlags { 44 | gfx_hal::command::CommandBufferFlags::ONE_TIME_SUBMIT 45 | } 46 | } 47 | 48 | impl Usage for MultiShot { 49 | fn flags(&self) -> gfx_hal::command::CommandBufferFlags { 50 | gfx_hal::command::CommandBufferFlags::empty() 51 | } 52 | } 53 | 54 | impl Usage for MultiShot { 55 | fn flags(&self) -> gfx_hal::command::CommandBufferFlags { 56 | gfx_hal::command::CommandBufferFlags::SIMULTANEOUS_USE 57 | } 58 | } 59 | 60 | impl Usage for NoSimultaneousUse { 61 | fn flags(&self) -> gfx_hal::command::CommandBufferFlags { 62 | gfx_hal::command::CommandBufferFlags::empty() 63 | } 64 | } 65 | 66 | /// Trait implemented for type-level render pass relation flags. 67 | /// `RenderPassContinue` and `OutsideRenderPass`. 68 | pub trait RenderPassRelation: Copy + Default + std::fmt::Debug { 69 | /// Flags required to begin command buffer. 70 | fn flags(&self) -> gfx_hal::command::CommandBufferFlags; 71 | } 72 | 73 | impl RenderPassRelation for RenderPassContinue { 74 | fn flags(&self) -> gfx_hal::command::CommandBufferFlags { 75 | gfx_hal::command::CommandBufferFlags::RENDER_PASS_CONTINUE 76 | } 77 | } 78 | 79 | impl RenderPassRelation for OutsideRenderPass { 80 | fn flags(&self) -> gfx_hal::command::CommandBufferFlags { 81 | gfx_hal::command::CommandBufferFlags::empty() 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /command/src/capability.rs: -------------------------------------------------------------------------------- 1 | //! Capability module docs. 2 | 3 | /// Capable of transfer only. 4 | #[derive(Clone, Copy, Debug)] 5 | pub struct Transfer; 6 | 7 | /// Capable of either compute or graphics commands execution. 8 | #[derive(Clone, Copy, Debug)] 9 | pub struct Execute; 10 | 11 | /// Capable of compute commands execution. 12 | #[derive(Clone, Copy, Debug)] 13 | pub struct Compute; 14 | 15 | /// Capable of graphics command execution. 16 | #[derive(Clone, Copy, Debug)] 17 | pub struct Graphics; 18 | 19 | /// Capable of any commands execution. 20 | #[derive(Clone, Copy, Debug)] 21 | pub struct General; 22 | 23 | /// Abstract capability specifier. 24 | pub trait Capability: Copy + std::fmt::Debug + 'static { 25 | /// Try to create capability instance from queue_type. 26 | /// Instance will be created if all required queue_type set. 27 | fn from_queue_type(queue_type: gfx_hal::QueueType) -> Option; 28 | 29 | /// Convert into `gfx_hal::QueueType` 30 | fn into_queue_type(self) -> gfx_hal::QueueType; 31 | } 32 | 33 | impl Capability for gfx_hal::QueueType { 34 | fn from_queue_type(queue_type: gfx_hal::QueueType) -> Option { 35 | Some(queue_type) 36 | } 37 | 38 | fn into_queue_type(self) -> gfx_hal::QueueType { 39 | self 40 | } 41 | } 42 | 43 | impl Capability for Transfer { 44 | fn from_queue_type(_queue_type: gfx_hal::QueueType) -> Option { 45 | Some(Transfer) 46 | } 47 | 48 | fn into_queue_type(self) -> gfx_hal::QueueType { 49 | gfx_hal::QueueType::Transfer 50 | } 51 | } 52 | 53 | impl Capability for Execute { 54 | fn from_queue_type(queue_type: gfx_hal::QueueType) -> Option { 55 | match queue_type { 56 | gfx_hal::QueueType::Transfer => None, 57 | _ => Some(Execute), 58 | } 59 | } 60 | 61 | fn into_queue_type(self) -> gfx_hal::QueueType { 62 | gfx_hal::QueueType::General 63 | } 64 | } 65 | 66 | impl Capability for Compute { 67 | fn from_queue_type(queue_type: gfx_hal::QueueType) -> Option { 68 | match queue_type { 69 | gfx_hal::QueueType::Compute | gfx_hal::QueueType::General => Some(Compute), 70 | _ => None 71 | } 72 | } 73 | 74 | fn into_queue_type(self) -> gfx_hal::QueueType { 75 | gfx_hal::QueueType::Compute 76 | } 77 | } 78 | 79 | impl Capability for Graphics { 80 | fn from_queue_type(queue_type: gfx_hal::QueueType) -> Option { 81 | match queue_type { 82 | gfx_hal::QueueType::Graphics | gfx_hal::QueueType::General => Some(Graphics), 83 | _ => None 84 | } 85 | } 86 | 87 | fn into_queue_type(self) -> gfx_hal::QueueType { 88 | gfx_hal::QueueType::Graphics 89 | } 90 | } 91 | 92 | impl Capability for General { 93 | fn from_queue_type(queue_type: gfx_hal::QueueType) -> Option { 94 | match queue_type { 95 | gfx_hal::QueueType::General => Some(General), 96 | _ => None 97 | } 98 | } 99 | 100 | fn into_queue_type(self) -> gfx_hal::QueueType { 101 | gfx_hal::QueueType::General 102 | } 103 | } 104 | 105 | /// Check if capability supported. 106 | pub trait Supports: Capability { 107 | /// Check runtime capability. 108 | fn supports(&self) -> Option; 109 | 110 | /// Assert capability. 111 | fn assert(&self) { 112 | assert!(self.supports().is_some()); 113 | } 114 | } 115 | 116 | impl Supports for Transfer { 117 | fn supports(&self) -> Option { 118 | Some(Transfer) 119 | } 120 | } 121 | 122 | impl Supports for Compute { 123 | fn supports(&self) -> Option { 124 | Some(Transfer) 125 | } 126 | } 127 | 128 | impl Supports for Graphics { 129 | fn supports(&self) -> Option { 130 | Some(Transfer) 131 | } 132 | } 133 | 134 | impl Supports for General { 135 | fn supports(&self) -> Option { 136 | Some(Transfer) 137 | } 138 | } 139 | 140 | impl Supports for Compute { 141 | fn supports(&self) -> Option { 142 | Some(Execute) 143 | } 144 | } 145 | 146 | impl Supports for Graphics { 147 | fn supports(&self) -> Option { 148 | Some(Execute) 149 | } 150 | } 151 | 152 | impl Supports for General { 153 | fn supports(&self) -> Option { 154 | Some(Execute) 155 | } 156 | } 157 | 158 | impl Supports for Compute { 159 | fn supports(&self) -> Option { 160 | Some(Compute) 161 | } 162 | } 163 | 164 | impl Supports for General { 165 | fn supports(&self) -> Option { 166 | Some(Compute) 167 | } 168 | } 169 | 170 | impl Supports for Graphics { 171 | fn supports(&self) -> Option { 172 | Some(Graphics) 173 | } 174 | } 175 | 176 | impl Supports for General { 177 | fn supports(&self) -> Option { 178 | Some(Graphics) 179 | } 180 | } 181 | 182 | impl Supports for gfx_hal::QueueType 183 | where 184 | C: Capability, 185 | { 186 | fn supports(&self) -> Option { 187 | C::from_queue_type(*self) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /command/src/family.rs: -------------------------------------------------------------------------------- 1 | //! Family module docs. 2 | 3 | use crate::{ 4 | buffer::{Reset, Submittable}, 5 | capability::{Capability, Supports}, 6 | pool::CommandPool, 7 | }; 8 | 9 | /// Command queue submission. 10 | #[derive(Debug)] 11 | pub struct Submission { 12 | /// Iterator over semaphores with stage flag to wait on. 13 | pub waits: W, 14 | 15 | /// Iterator over semaphores to signal. 16 | pub signals: S, 17 | 18 | /// Iterator over submittables. 19 | pub submits: C, 20 | } 21 | 22 | /// Family of the command queues. 23 | /// Queues from one family can share resources and execute command buffers associated with the family. 24 | /// All queues of the family have same capabilities. 25 | #[derive(derivative::Derivative)] 26 | #[derivative(Debug)] 27 | pub struct Family { 28 | index: gfx_hal::queue::QueueFamilyId, 29 | #[derivative(Debug = "ignore")] queues: Vec, 30 | // min_image_transfer_granularity: gfx_hal::image::Extent, 31 | capability: C, 32 | relevant: relevant::Relevant, 33 | } 34 | 35 | impl Family 36 | where 37 | B: gfx_hal::Backend, 38 | { 39 | /// Query queue family from device. 40 | /// 41 | /// # Safety 42 | /// 43 | /// This function shouldn't be used more then once with the same parameters. 44 | /// Raw queue handle queried from device can make `Family` usage invalid. 45 | /// `family` must be one of the family indices used during `device` creation. 46 | /// `properties` must be the properties retuned for queue family from physical device. 47 | pub unsafe fn from_device( 48 | queues: &mut gfx_hal::queue::Queues, 49 | index: gfx_hal::queue::QueueFamilyId, 50 | count: usize, 51 | family: &impl gfx_hal::queue::QueueFamily, 52 | ) -> Self { 53 | Family { 54 | index, 55 | queues: { 56 | let queues = queues.take_raw(index).expect(""); 57 | assert_eq!(queues.len(), count); 58 | queues 59 | }, 60 | // min_image_transfer_granularity: properties.min_image_transfer_granularity, 61 | capability: family.queue_type(), 62 | relevant: relevant::Relevant, 63 | } 64 | } 65 | } 66 | 67 | impl Family 68 | where 69 | B: gfx_hal::Backend, 70 | { 71 | /// Get id of the family. 72 | pub fn index(&self) -> gfx_hal::queue::QueueFamilyId { 73 | self.index 74 | } 75 | 76 | /// Get queues of the family. 77 | pub fn queues(&self) -> &[impl gfx_hal::queue::RawCommandQueue] { 78 | &self.queues 79 | } 80 | 81 | /// Get queues of the family. 82 | pub fn queues_mut(&mut self) -> &mut [impl gfx_hal::queue::RawCommandQueue] { 83 | &mut self.queues 84 | } 85 | 86 | /// Submit commands to the queue of the family. 87 | pub unsafe fn submit<'a>(&mut self, 88 | queue: usize, 89 | submissions: impl IntoIterator + 'a), gfx_hal::pso::PipelineStage)>, 91 | impl IntoIterator>, 92 | impl IntoIterator + 'a)>, 93 | >>, 94 | fence: Option<&B::Fence>, 95 | ) { 96 | let mut submissions = submissions.into_iter().peekable(); 97 | if submissions.peek().is_none() && fence.is_some() { 98 | gfx_hal::queue::RawCommandQueue::submit( 99 | &mut self.queues[queue], 100 | gfx_hal::queue::Submission { 101 | command_buffers: std::iter::empty::<&'a B::CommandBuffer>(), 102 | wait_semaphores: std::iter::empty::<(&'a B::Semaphore, _)>(), 103 | signal_semaphores: std::iter::empty::<&'a B::Semaphore>(), 104 | }, 105 | fence, 106 | ); 107 | } else { 108 | let index = self.index; 109 | while let Some(submission) = submissions.next() { 110 | gfx_hal::queue::RawCommandQueue::submit( 111 | &mut self.queues[queue], 112 | gfx_hal::queue::Submission { 113 | command_buffers: submission.submits.into_iter().map(|submit| { 114 | assert_eq!(submit.family(), index); 115 | unsafe { 116 | &*submit.raw() 117 | } 118 | }), 119 | wait_semaphores: submission.waits.into_iter().map(|w| (w.0.borrow(), w.1)), 120 | signal_semaphores: submission.signals.into_iter().map(|s| s.borrow()), 121 | }, 122 | submissions.peek().map_or(fence, |_| None), 123 | ); 124 | } 125 | } 126 | } 127 | 128 | /// Create command pool associated with the family. 129 | /// Command buffers created from the pool could be submitted to the queues of the family. 130 | pub fn create_pool( 131 | &self, 132 | device: &impl gfx_hal::Device, 133 | ) -> Result, gfx_hal::device::OutOfMemory> 134 | where 135 | R: Reset, 136 | C: Capability, 137 | { 138 | let reset = R::default(); 139 | let pool = unsafe { 140 | // Is this family belong to specified device. 141 | let raw = device.create_command_pool( 142 | self.index, 143 | reset.flags(), 144 | )?; 145 | 146 | CommandPool::from_raw(raw, self.capability, reset, self.index) 147 | }; 148 | 149 | Ok(pool) 150 | } 151 | 152 | /// Get family capability. 153 | pub fn capability(&self) -> C 154 | where 155 | C: Capability 156 | { 157 | self.capability 158 | } 159 | 160 | /// Dispose of queue family container. 161 | pub fn dispose(self) { 162 | for queue in self.queues { 163 | gfx_hal::queue::RawCommandQueue::wait_idle(&queue) 164 | .unwrap(); 165 | } 166 | 167 | self.relevant.dispose(); 168 | } 169 | 170 | /// Convert capability from type-level to value-level. 171 | pub fn with_queue_type(self) -> Family 172 | where 173 | C: Capability, 174 | { 175 | Family { 176 | index: self.index, 177 | queues: self.queues, 178 | // min_image_transfer_granularity: self.min_image_transfer_granularity, 179 | capability: self.capability.into_queue_type(), 180 | relevant: self.relevant, 181 | } 182 | } 183 | 184 | /// Convert capability into type-level one. 185 | /// 186 | pub fn with_capability(self) -> Result, Self> 187 | where 188 | C: Supports, 189 | { 190 | if let Some(capability) = self.capability.supports() { 191 | Ok(Family { 192 | index: self.index, 193 | queues: self.queues, 194 | // min_image_transfer_granularity: self.min_image_transfer_granularity, 195 | capability, 196 | relevant: self.relevant, 197 | }) 198 | } else { 199 | Err(self) 200 | } 201 | } 202 | } 203 | 204 | /// Query queue families from device. 205 | /// 206 | /// # Safety 207 | /// 208 | /// This function shouldn't be used more then once with same parameters. 209 | /// Raw queue handle queried from device can make returned `Family` usage invalid. 210 | /// `families` iterator must yeild unique family indices with queue count used during `device` creation. 211 | /// `properties` must contain properties retuned for queue family from physical device for each family index yielded by `families`. 212 | pub unsafe fn families_from_device( 213 | queues: &mut gfx_hal::queue::Queues, 214 | families: impl IntoIterator, 215 | queue_types: &[impl gfx_hal::queue::QueueFamily], 216 | ) -> Vec> 217 | where 218 | B: gfx_hal::Backend, 219 | { 220 | families 221 | .into_iter() 222 | .map(|(index, count)| { 223 | Family::from_device(queues, index, count, &queue_types[index.0]) 224 | }).collect() 225 | } 226 | -------------------------------------------------------------------------------- /command/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate revolves around command recording and submission. 2 | 3 | #![forbid(overflowing_literals)] 4 | #![deny(missing_copy_implementations)] 5 | #![deny(missing_debug_implementations)] 6 | #![deny(missing_docs)] 7 | #![deny(intra_doc_link_resolution_failure)] 8 | #![deny(path_statements)] 9 | #![deny(trivial_bounds)] 10 | #![deny(type_alias_bounds)] 11 | #![deny(unconditional_recursion)] 12 | #![deny(unions_with_drop_fields)] 13 | #![deny(while_true)] 14 | #![deny(unused)] 15 | #![deny(bad_style)] 16 | #![deny(future_incompatible)] 17 | #![deny(rust_2018_compatibility)] 18 | #![deny(rust_2018_idioms)] 19 | #![allow(unused_unsafe)] 20 | 21 | mod buffer; 22 | mod capability; 23 | mod family; 24 | mod pool; 25 | 26 | pub use crate::{ 27 | buffer::*, 28 | capability::*, 29 | family::*, 30 | pool::*, 31 | }; 32 | -------------------------------------------------------------------------------- /command/src/pool.rs: -------------------------------------------------------------------------------- 1 | //! CommandPool module docs. 2 | 3 | use crate::{buffer::*, capability::*}; 4 | 5 | /// Simple pool wrapper. 6 | /// Doesn't provide any guarantees. 7 | /// Wraps raw buffers into `CommandCommand buffer`. 8 | #[derive(derivative::Derivative)] 9 | #[derivative(Debug)] 10 | pub struct CommandPool { 11 | #[derivative(Debug = "ignore")]raw: B::CommandPool, 12 | capability: C, 13 | reset: R, 14 | family: gfx_hal::queue::QueueFamilyId, 15 | relevant: relevant::Relevant, 16 | } 17 | 18 | impl CommandPool 19 | where 20 | B: gfx_hal::Backend, 21 | R: Reset, 22 | { 23 | /// Wrap raw command pool. 24 | /// 25 | /// # Safety 26 | /// 27 | /// * `raw` must be valid command pool handle. 28 | /// * The command pool must be created for specified `family` index. 29 | /// * `capability` must be subset of capabilites of the `family` the pool was created for. 30 | /// * if `reset` is `IndividualReset` the pool must be created with individual command buffer reset flag set. 31 | pub unsafe fn from_raw( 32 | raw: B::CommandPool, 33 | capability: C, 34 | reset: R, 35 | family: gfx_hal::queue::QueueFamilyId, 36 | ) -> Self { 37 | CommandPool { 38 | raw, 39 | capability, 40 | reset, 41 | family, 42 | relevant: relevant::Relevant, 43 | } 44 | } 45 | 46 | /// Allocate new command buffers. 47 | pub fn allocate_buffers( 48 | &mut self, 49 | count: usize, 50 | ) -> Vec> 51 | where 52 | L: Level, 53 | C: Capability, 54 | { 55 | let level = L::default(); 56 | 57 | let buffers = gfx_hal::pool::RawCommandPool::allocate_vec( 58 | &mut self.raw, 59 | count, 60 | level.raw_level(), 61 | ); 62 | 63 | buffers 64 | .into_iter() 65 | .map(|raw| unsafe { 66 | CommandBuffer::from_raw( 67 | raw, 68 | self.capability, 69 | InitialState, 70 | level, 71 | self.reset, 72 | self.family, 73 | ) 74 | }).collect() 75 | } 76 | 77 | /// Free buffers. 78 | /// Buffers must be in droppable state. 79 | /// TODO: Validate buffers were allocated from this pool. 80 | pub unsafe fn free_buffers( 81 | &mut self, 82 | buffers: impl IntoIterator>, 83 | ) { 84 | let buffers = buffers 85 | .into_iter() 86 | .map(|buffer| unsafe { buffer.into_raw() }) 87 | .collect::>(); 88 | 89 | unsafe { 90 | gfx_hal::pool::RawCommandPool::free(&mut self.raw, buffers); 91 | } 92 | } 93 | 94 | /// Reset all buffers of this pool. 95 | /// 96 | /// # Safety 97 | /// 98 | /// All buffers allocated from this pool must be marked reset. 99 | /// See [`CommandBuffer::mark_reset`](struct.Command buffer.html#method.mark_reset) 100 | pub unsafe fn reset(&mut self) { 101 | gfx_hal::pool::RawCommandPool::reset(&mut self.raw); 102 | } 103 | 104 | /// Dispose of command pool. 105 | /// 106 | /// # Safety 107 | /// 108 | /// * All buffers allocated from this pool must be [freed](#method.free_buffers). 109 | pub unsafe fn dispose(self, device: &impl gfx_hal::Device) { 110 | device.destroy_command_pool(self.raw); 111 | self.relevant.dispose(); 112 | } 113 | 114 | /// Convert capability level 115 | pub fn with_queue_type(self) -> CommandPool 116 | where 117 | C: Capability, 118 | { 119 | CommandPool { 120 | raw: self.raw, 121 | capability: self.capability.into_queue_type(), 122 | reset: self.reset, 123 | family: self.family, 124 | relevant: self.relevant, 125 | } 126 | } 127 | 128 | /// Convert capability level 129 | pub fn with_capability(self) -> Result, Self> 130 | where 131 | C: Supports, 132 | { 133 | if let Some(capability) = self.capability.supports() { 134 | Ok(CommandPool { 135 | raw: self.raw, 136 | capability, 137 | reset: self.reset, 138 | family: self.family, 139 | relevant: self.relevant, 140 | }) 141 | } else { 142 | Err(self) 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /factory/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-factory" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | 7 | [features] 8 | empty = ["gfx-backend-empty", "rendy-wsi/gfx-backend-empty"] 9 | dx12 = ["gfx-backend-dx12", "rendy-wsi/gfx-backend-dx12"] 10 | metal = ["gfx-backend-metal", "rendy-wsi/gfx-backend-metal"] 11 | vulkan = ["gfx-backend-vulkan", "rendy-wsi/gfx-backend-vulkan"] 12 | serde-1 = [ 13 | "serde", 14 | "rendy-memory/serde-1", 15 | "gfx-hal/serde", 16 | ] 17 | 18 | [dependencies] 19 | rendy-memory = { path = "../memory" } 20 | rendy-resource = { path = "../resource" } 21 | rendy-command = { path = "../command" } 22 | rendy-wsi = { path = "../wsi" } 23 | 24 | gfx-hal = { git = "https://github.com/gfx-rs/gfx" } 25 | gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", optional = true } 26 | gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx", optional = true } 27 | gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx", optional = true } 28 | gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", optional = true } 29 | 30 | derivative = "1.0" 31 | failure = "0.1" 32 | log = "0.4" 33 | parking_lot = "0.7" 34 | relevant = "0.2" 35 | serde = { version = "1.0", optional = true, features = ["derive"] } 36 | smallvec = "0.6" 37 | winit = "0.18" 38 | -------------------------------------------------------------------------------- /factory/src/config.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::min; 2 | 3 | use crate::memory::{LinearConfig, DynamicConfig, HeapsConfig}; 4 | 5 | /// Factory initialization config. 6 | #[derive(Clone, derivative::Derivative)] 7 | #[derivative(Debug, Default)] 8 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 9 | pub struct Config { 10 | /// Config for memory::Heaps. 11 | pub heaps: H, 12 | 13 | /// Config for queue families. 14 | pub queues: Q, 15 | } 16 | 17 | /// Queues configuration. 18 | pub unsafe trait QueuesConfigure { 19 | /// Slice of priorities. 20 | type Priorities: AsRef<[f32]>; 21 | 22 | /// Iterator over families to create. 23 | type Families: IntoIterator; 24 | 25 | /// Configure. 26 | fn configure(self, families: &[impl gfx_hal::queue::QueueFamily]) -> Self::Families; 27 | } 28 | 29 | /// QueuePicker that picks first graphics queue family. 30 | /// If possible it checks that queues of the family are capabile of presenting. 31 | #[derive(Clone, Copy, Debug, Default)] 32 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 33 | pub struct OneGraphicsQueue; 34 | 35 | unsafe impl QueuesConfigure for OneGraphicsQueue { 36 | type Priorities = [f32; 1]; 37 | type Families = Option<(gfx_hal::queue::QueueFamilyId, [f32; 1])>; 38 | fn configure(self, families: &[impl gfx_hal::queue::QueueFamily]) -> Option<(gfx_hal::queue::QueueFamilyId, [f32; 1])> { 39 | families 40 | .iter() 41 | .find(|f| f.supports_graphics() && f.max_queues() > 0) 42 | .map(|f| (f.id(), [1.0])) 43 | } 44 | } 45 | 46 | /// Saved config for queues. 47 | #[derive(Clone, Debug)] 48 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 49 | pub struct SavedQueueConfig(Vec<(gfx_hal::queue::QueueFamilyId, Vec)>); 50 | 51 | unsafe impl QueuesConfigure for SavedQueueConfig { 52 | type Priorities = Vec; 53 | type Families = Vec<(gfx_hal::queue::QueueFamilyId, Vec)>; 54 | fn configure(self, _: &[impl gfx_hal::queue::QueueFamily]) -> Vec<(gfx_hal::queue::QueueFamilyId, Vec)> { 55 | self.0 56 | } 57 | } 58 | 59 | /// Heaps configuration. 60 | pub unsafe trait HeapsConfigure { 61 | /// Iterator over memory types. 62 | type Types: IntoIterator; 63 | 64 | /// Iterator over heaps. 65 | type Heaps: IntoIterator; 66 | 67 | /// Configure. 68 | fn configure( 69 | self, 70 | properties: &gfx_hal::adapter::MemoryProperties, 71 | ) -> (Self::Types, Self::Heaps); 72 | } 73 | 74 | /// Basic heaps config. 75 | #[derive(Clone, Copy, Debug, Default)] 76 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 77 | pub struct BasicHeapsConfigure; 78 | 79 | unsafe impl HeapsConfigure for BasicHeapsConfigure { 80 | type Types = Vec<(gfx_hal::memory::Properties, u32, HeapsConfig)>; 81 | type Heaps = Vec; 82 | 83 | fn configure( 84 | self, 85 | properties: &gfx_hal::adapter::MemoryProperties, 86 | ) -> (Self::Types, Self::Heaps) { 87 | let types = properties.memory_types.iter() 88 | .map(|mt| { 89 | let config = HeapsConfig { 90 | linear: if mt 91 | .properties 92 | .contains(gfx_hal::memory::Properties::CPU_VISIBLE) 93 | { 94 | Some(LinearConfig { 95 | linear_size: min( 96 | 256 * 1024 * 1024, 97 | properties.memory_heaps[mt.heap_index as usize] / 8, 98 | ), 99 | }) 100 | } else { 101 | None 102 | }, 103 | dynamic: Some(DynamicConfig { 104 | max_block_size: min( 105 | 32 * 1024 * 1024, 106 | properties.memory_heaps[mt.heap_index as usize] / 8, 107 | ), 108 | block_size_granularity: min( 109 | 256, 110 | properties.memory_heaps[mt.heap_index as usize] / 1024, 111 | ), 112 | blocks_per_chunk: 64, 113 | }), 114 | }; 115 | 116 | (mt.properties, mt.heap_index as u32, config) 117 | }).collect(); 118 | 119 | let heaps = properties.memory_heaps.iter() 120 | .cloned() 121 | .collect(); 122 | 123 | (types, heaps) 124 | } 125 | } 126 | 127 | /// Saved config for heaps. 128 | #[derive(Clone, Debug)] 129 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 130 | pub struct SavedHeapsConfig { 131 | types: Vec<(gfx_hal::memory::Properties, u32, HeapsConfig)>, 132 | heaps: Vec, 133 | } 134 | 135 | unsafe impl HeapsConfigure for SavedHeapsConfig { 136 | type Types = Vec<(gfx_hal::memory::Properties, u32, HeapsConfig)>; 137 | type Heaps = Vec; 138 | 139 | fn configure( 140 | self, 141 | _properties: &gfx_hal::adapter::MemoryProperties, 142 | ) -> (Self::Types, Self::Heaps) { 143 | (self.types, self.heaps) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /factory/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | //! Higher-level rendy interface. 3 | 4 | #![forbid(overflowing_literals)] 5 | #![deny(missing_copy_implementations)] 6 | #![deny(missing_debug_implementations)] 7 | #![deny(missing_docs)] 8 | #![deny(intra_doc_link_resolution_failure)] 9 | #![deny(path_statements)] 10 | #![deny(trivial_bounds)] 11 | #![deny(type_alias_bounds)] 12 | #![deny(unconditional_recursion)] 13 | #![deny(unions_with_drop_fields)] 14 | #![deny(while_true)] 15 | #![deny(unused)] 16 | #![deny(bad_style)] 17 | #![deny(future_incompatible)] 18 | #![deny(rust_2018_compatibility)] 19 | #![deny(rust_2018_idioms)] 20 | #![allow(unused_unsafe)] 21 | 22 | use rendy_command as command; 23 | use rendy_memory as memory; 24 | use rendy_resource as resource; 25 | use rendy_wsi as wsi; 26 | 27 | mod factory; 28 | mod config; 29 | 30 | pub use crate::{ 31 | config::{ 32 | BasicHeapsConfigure, Config, HeapsConfigure, OneGraphicsQueue, QueuesConfigure, 33 | SavedHeapsConfig, SavedQueueConfig, 34 | }, 35 | factory::Factory, 36 | }; 37 | -------------------------------------------------------------------------------- /frame/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-frame" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | derivative = "1.0" 9 | either = "1.5" 10 | gfx-hal = { git = "https://github.com/gfx-rs/gfx" } 11 | failure = "0.1" 12 | log = "0.4" 13 | relevant = "0.2" 14 | smallvec = "0.6" 15 | rendy-command = { path = "../command" } 16 | rendy-factory = { path = "../factory" } 17 | rendy-memory = { path = "../memory" } 18 | rendy-resource = { path = "../resource" } 19 | -------------------------------------------------------------------------------- /frame/src/cirque/command.rs: -------------------------------------------------------------------------------- 1 | 2 | use { 3 | crate::command::{ 4 | CommandBuffer, CommandPool, 5 | InitialState, ExecutableState, PendingState, 6 | MultiShot, NoSimultaneousUse, OutsideRenderPass, PrimaryLevel, 7 | Submit, Encoder, Level, Capability, IndividualReset, 8 | }, 9 | super::*, 10 | }; 11 | 12 | /// 13 | pub type CommandCirque = Cirque< 14 | CommandBuffer, L, IndividualReset>, 15 | CommandBuffer, 16 | CommandBuffer>, L, IndividualReset>, 17 | >; 18 | 19 | impl CommandCirque 20 | where 21 | B: gfx_hal::Backend, 22 | L: Level, 23 | C: Capability, 24 | { 25 | /// Encode and submit. 26 | pub fn encode_submit( 27 | &mut self, 28 | frames: std::ops::Range, 29 | force: bool, 30 | pool: &mut CommandPool, 31 | encode: impl FnOnce(Encoder<'_, B, C, L>, usize) 32 | ) -> Submit { 33 | let init = |initial: CommandBuffer<_, _, InitialState, _, _>, index| -> CommandBuffer<_, _, ExecutableState, _, _> { 34 | let mut recording = initial.begin(); 35 | encode(recording.encoder(), index); 36 | recording.finish() 37 | }; 38 | 39 | let cr = self.get( 40 | frames, 41 | |_| pool.allocate_buffers(1).pop().unwrap(), 42 | |pending, _| unsafe { 43 | pending.mark_complete() 44 | }, 45 | ); 46 | 47 | let ready = if force { 48 | cr.or_init(init) 49 | } else { 50 | cr.or_reset(|executable, _| executable.reset()) 51 | .init(init) 52 | }; 53 | 54 | let mut slot = None; 55 | 56 | ready.finish(|executable, _| { 57 | let (submit, pending) = executable.submit(); 58 | slot = Some(submit); 59 | pending 60 | }); 61 | 62 | slot.unwrap() 63 | } 64 | } -------------------------------------------------------------------------------- /frame/src/cirque/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | //! Ring buffers for using with frames. 3 | 4 | mod command; 5 | 6 | use std::collections::VecDeque; 7 | pub use self::command::*; 8 | 9 | /// Reference to one of the values in the `Cirque`. 10 | /// It can be in either initial or ready state. 11 | #[derive(Debug)] 12 | pub enum CirqueRef<'a, T, I = T, P = T> { 13 | /// Reference to value in initial state. 14 | Initial(InitialRef<'a, T, I, P>), 15 | 16 | /// Reference to value in ready state. 17 | Ready(ReadyRef<'a, T, I, P>), 18 | } 19 | 20 | impl<'a, T, I, P> CirqueRef<'a, T, I, P> { 21 | /// Init if not in ready state. 22 | pub fn or_init(self, init: impl FnOnce(I, usize) -> T) -> ReadyRef<'a, T, I, P> { 23 | match self { 24 | CirqueRef::Initial(initial) => initial.init(init), 25 | CirqueRef::Ready(ready) => ready, 26 | } 27 | } 28 | 29 | /// Reset if not in initial state. 30 | pub fn or_reset(self, reset: impl FnOnce(T, usize) -> I) -> InitialRef<'a, T, I, P> { 31 | match self { 32 | CirqueRef::Initial(initial) => initial, 33 | CirqueRef::Ready(ready) => ready.reset(reset), 34 | } 35 | } 36 | } 37 | 38 | /// Reference to new value in the `Cirque`. 39 | /// It is in initial state. 40 | #[derive(Debug)] 41 | pub struct InitialRef<'a, T, I = T, P = T> { 42 | cirque: &'a mut Cirque, 43 | value: I, 44 | frame: u64, 45 | index: usize, 46 | } 47 | 48 | impl<'a, T, I, P> InitialRef<'a, T, I, P> { 49 | /// Init value. 50 | pub fn init(self, init: impl FnOnce(I, usize) -> T) -> ReadyRef<'a, T, I, P> { 51 | ReadyRef { 52 | cirque: self.cirque, 53 | value: init(self.value, self.index), 54 | frame: self.frame, 55 | index: self.index, 56 | } 57 | } 58 | } 59 | 60 | /// Reference to value in the `Cirque`. 61 | /// It is in ready state. 62 | #[derive(Debug)] 63 | pub struct ReadyRef<'a, T, I = T, P = T> { 64 | cirque: &'a mut Cirque, 65 | value: T, 66 | frame: u64, 67 | index: usize, 68 | } 69 | 70 | impl<'a, T, I, P> ReadyRef<'a, T, I, P> { 71 | /// Init value. 72 | pub fn reset(self, reset: impl FnOnce(T, usize) -> I) -> InitialRef<'a, T, I, P> { 73 | InitialRef { 74 | cirque: self.cirque, 75 | value: reset(self.value, self.index), 76 | frame: self.frame, 77 | index: self.index, 78 | } 79 | } 80 | 81 | /// Finish using this value. 82 | pub fn finish(self, finish: impl FnOnce(T, usize) -> P) { 83 | self.cirque.pending.push_back((finish(self.value, self.index), self.index, self.frame)) 84 | } 85 | } 86 | 87 | /// Resource cirque. 88 | /// It simplifies using multiple resources 89 | /// when same resource cannot be used simulteneously. 90 | #[derive(Debug, derivative::Derivative)] 91 | #[derivative(Default(bound = ""))] 92 | pub struct Cirque { 93 | pending: VecDeque<(P, usize, u64)>, 94 | ready: VecDeque<(T, usize)>, 95 | marker: std::marker::PhantomData I>, 96 | counter: usize, 97 | } 98 | 99 | impl Cirque { 100 | /// Create new empty `Cirque` 101 | pub fn new() -> Self { 102 | Self::default() 103 | } 104 | 105 | /// Dispose of the `Cirque`. 106 | pub fn dispose( 107 | mut self, 108 | mut dispose: impl FnMut(either::Either, usize), 109 | ) { 110 | self.pending.drain(..).for_each(|(value, index, _)| dispose(either::Right(value), index)); 111 | self.ready.drain(..).for_each(|(value, index)| dispose(either::Left(value), index)); 112 | } 113 | 114 | /// Get `CirqueRef` for specified frames range. 115 | /// Allocate new instance in initial state if no ready values exist. 116 | pub fn get( 117 | &mut self, 118 | frames: std::ops::Range, 119 | alloc: impl FnOnce(usize) -> I, 120 | complete: impl Fn(P, usize) -> T, 121 | ) -> CirqueRef<'_, T, I, P> { 122 | while let Some((value, index, frame)) = self.pending.pop_front() { 123 | if frame > frames.start { 124 | self.pending.push_back((value, index, frame)); 125 | break; 126 | } 127 | self.ready.push_back((complete(value, index), index)); 128 | } 129 | if let Some((value, index)) = self.ready.pop_front() { 130 | CirqueRef::Ready(ReadyRef { 131 | cirque: self, 132 | value, 133 | frame: frames.end, 134 | index, 135 | }) 136 | } else { 137 | self.counter += 1; 138 | let index = self.counter - 1; 139 | let value = alloc(index); 140 | CirqueRef::Initial(InitialRef { 141 | index, 142 | cirque: self, 143 | value, 144 | frame: frames.end, 145 | }) 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /frame/src/frame.rs: -------------------------------------------------------------------------------- 1 | //! Frame module docs. 2 | 3 | use crate::factory::Factory; 4 | 5 | /// Fences collection. 6 | pub type Fences = smallvec::SmallVec<[::Fence; 8]>; 7 | 8 | /// Single frame rendering task. 9 | /// Command buffers can be submitted as part of the `Frame`. 10 | #[derive(Debug)] 11 | #[allow(missing_copy_implementations)] 12 | pub struct Frame { 13 | index: u64, 14 | } 15 | 16 | impl Frame { 17 | /// Get frame index. 18 | pub fn index(&self) -> u64 { 19 | self.index 20 | } 21 | } 22 | 23 | /// Proof that frame is complete. 24 | #[derive(Debug)] 25 | #[allow(missing_copy_implementations)] 26 | pub struct CompleteFrame { 27 | index: u64, 28 | } 29 | 30 | impl CompleteFrame { 31 | /// Get frame index. 32 | pub fn index(&self) -> u64 { 33 | self.index 34 | } 35 | } 36 | 37 | /// Timeline of frames, complete, pending and next. 38 | #[derive(Debug)] 39 | pub struct Frames { 40 | pending: std::collections::VecDeque>, 41 | next: Frame, 42 | } 43 | 44 | impl Frames 45 | where 46 | B: gfx_hal::Backend, 47 | { 48 | /// Create new `Frames` instance. 49 | pub fn new() -> Self { 50 | Frames { 51 | pending: Default::default(), 52 | next: Frame { 53 | index: 0, 54 | }, 55 | } 56 | } 57 | 58 | /// Get next frame reference. 59 | pub fn next(&self) -> &Frame { 60 | &self.next 61 | } 62 | 63 | /// Advance to the next frame. 64 | /// All fences of the next frame must be queued. 65 | pub unsafe fn advance(&mut self, fences: Fences) { 66 | self.pending 67 | .push_back(fences); 68 | self.next.index += 1; 69 | } 70 | 71 | /// Get upper bound of complete frames. 72 | pub fn complete_upper_bound(&self) -> u64 { 73 | debug_assert!(self.pending.len() as u64 <= self.next.index); 74 | self.next.index - self.pending.len() as u64 75 | } 76 | 77 | /// Check if frame with specified index is complete. 78 | pub fn complete(&self, index: u64) -> Option { 79 | if self.complete_upper_bound() > index { 80 | Some(CompleteFrame { index }) 81 | } else { 82 | None 83 | } 84 | } 85 | 86 | /// Wait for completion of the frames until specified (inclusive) 87 | /// Returns proof. 88 | /// 89 | /// # Parameters 90 | /// 91 | /// `target` - last index of frame that must complete. 92 | /// `factory` - The factory. 93 | /// 94 | /// # Panics 95 | /// 96 | /// This function will panic if `target` is greater than or equal to next frame. 97 | pub fn wait_complete(&mut self, target: u64, factory: &Factory, free: impl FnMut(Fences)) -> CompleteFrame { 98 | assert!(target <= self.next.index()); 99 | if let Some(complete) = self.complete(target) { 100 | complete 101 | } else { 102 | // n - p <= t 103 | // p - n + t + 1 >= 1 104 | // count >= 1 105 | let count = self.pending.len() - (self.next.index() - target - 1) as usize; 106 | let ready = factory.wait_for_fences( 107 | self.pending.iter().take(count).flatten(), 108 | gfx_hal::device::WaitFor::All, 109 | !0, 110 | ); 111 | assert_eq!(ready, Ok(true)); 112 | self.pending 113 | .drain(..count) 114 | .for_each(free); 115 | CompleteFrame { index: target } 116 | } 117 | } 118 | 119 | /// Dispose of the `Frames` 120 | pub fn dispose(mut self, factory: &mut Factory) { 121 | let ready = factory.wait_for_fences( 122 | self.pending.iter().flatten(), 123 | gfx_hal::device::WaitFor::All, 124 | !0, 125 | ); 126 | assert_eq!(ready, Ok(true)); 127 | 128 | self.pending.drain(..).flatten().for_each(|fence| unsafe { // Waited before destroying 129 | factory.destroy_fence(fence) 130 | }); 131 | } 132 | 133 | /// Get range of frame indices in this form: 134 | /// `upper bound of finished frames .. next frame`. 135 | pub fn range(&self) -> std::ops::Range { 136 | self.complete_upper_bound() .. self.next.index 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /frame/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | //! Everything that is frame related. 3 | 4 | #![forbid(overflowing_literals)] 5 | #![deny(missing_copy_implementations)] 6 | #![deny(missing_debug_implementations)] 7 | #![deny(missing_docs)] 8 | #![deny(intra_doc_link_resolution_failure)] 9 | #![deny(path_statements)] 10 | #![deny(trivial_bounds)] 11 | #![deny(type_alias_bounds)] 12 | #![deny(unconditional_recursion)] 13 | #![deny(unions_with_drop_fields)] 14 | #![deny(while_true)] 15 | #![deny(unused)] 16 | #![deny(bad_style)] 17 | #![deny(future_incompatible)] 18 | #![deny(rust_2018_compatibility)] 19 | #![deny(rust_2018_idioms)] 20 | #![allow(unused_unsafe)] 21 | 22 | use rendy_command as command; 23 | use rendy_factory as factory; 24 | 25 | pub mod cirque; 26 | mod frame; 27 | 28 | pub use crate::frame::*; 29 | -------------------------------------------------------------------------------- /graph/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-graph" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | 7 | [features] 8 | empty = ["gfx-backend-empty", "rendy-wsi/gfx-backend-empty", "rendy-factory/gfx-backend-empty"] 9 | dx12 = ["gfx-backend-dx12", "rendy-wsi/gfx-backend-dx12", "rendy-factory/gfx-backend-dx12"] 10 | metal = ["gfx-backend-metal", "rendy-wsi/gfx-backend-metal", "rendy-factory/gfx-backend-metal"] 11 | vulkan = ["gfx-backend-vulkan", "rendy-wsi/gfx-backend-vulkan", "rendy-factory/gfx-backend-vulkan"] 12 | 13 | [dependencies] 14 | rendy-chain = { path = "../chain" } 15 | rendy-command = { path = "../command" } 16 | rendy-factory = { path = "../factory" } 17 | rendy-frame = { path = "../frame" } 18 | rendy-memory = { path = "../memory" } 19 | rendy-resource = { path = "../resource" } 20 | rendy-wsi = { path = "../wsi" } 21 | 22 | gfx-hal = { git = "https://github.com/gfx-rs/gfx" } 23 | gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", optional = true } 24 | gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx", optional = true } 25 | gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx", optional = true } 26 | gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", optional = true } 27 | 28 | either = "1.5" 29 | bitflags = "1.0" 30 | derivative = "1.0" 31 | failure = "0.1" 32 | log = "0.4" 33 | relevant = "0.2" 34 | serde = { version = "1.0", optional = true, features = ["derive"] } 35 | smallvec = "0.6" 36 | winit = "0.18" 37 | -------------------------------------------------------------------------------- /graph/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Framegraph implementation for Rendy engine. 2 | 3 | #![forbid(overflowing_literals)] 4 | #![deny(missing_copy_implementations)] 5 | #![deny(missing_debug_implementations)] 6 | #![deny(missing_docs)] 7 | #![deny(intra_doc_link_resolution_failure)] 8 | #![deny(path_statements)] 9 | #![deny(trivial_bounds)] 10 | #![deny(type_alias_bounds)] 11 | #![deny(unconditional_recursion)] 12 | #![deny(unions_with_drop_fields)] 13 | #![deny(while_true)] 14 | #![deny(unused)] 15 | #![deny(bad_style)] 16 | #![deny(future_incompatible)] 17 | #![deny(rust_2018_compatibility)] 18 | #![deny(rust_2018_idioms)] 19 | #![allow(unused_unsafe)] 20 | 21 | use rendy_chain as chain; 22 | use rendy_command as command; 23 | use rendy_factory as factory; 24 | use rendy_frame as frame; 25 | use rendy_memory as memory; 26 | use rendy_resource as resource; 27 | use rendy_wsi as wsi; 28 | 29 | /// Id of the buffer in graph. 30 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 31 | pub struct BufferId(usize); 32 | 33 | /// Id of the image (or target) in graph. 34 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 35 | pub struct ImageId(usize); 36 | 37 | /// Id of the node in graph. 38 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 39 | pub struct NodeId(usize); 40 | 41 | mod graph; 42 | mod node; 43 | 44 | pub use self::{graph::*, node::*}; 45 | -------------------------------------------------------------------------------- /graph/src/node/present.rs: -------------------------------------------------------------------------------- 1 | //! Defines present node. 2 | 3 | use crate::{ 4 | chain::QueueId, 5 | command::{CommandPool, CommandBuffer, ExecutableState, PendingState, MultiShot, SimultaneousUse, Family, Submission, Submit}, 6 | factory::Factory, 7 | frame::Frames, 8 | wsi::{Surface, Target}, 9 | node::{AnyNodeDesc, AnyNode, NodeImage, NodeBuffer, NodeBuilder, ImageAccess, gfx_acquire_barriers, gfx_release_barriers}, 10 | }; 11 | 12 | #[derive(Debug)] 13 | struct ForImage { 14 | acquire: B::Semaphore, 15 | release: B::Semaphore, 16 | submit: Submit, 17 | buffer: CommandBuffer>>>, 18 | } 19 | 20 | /// Node that presents images to the surface. 21 | #[derive(Debug)] 22 | pub struct PresentNode { 23 | per_image: Vec>, 24 | free: Option, 25 | target: Target, 26 | pool: CommandPool, 27 | } 28 | 29 | impl PresentNode 30 | where 31 | B: gfx_hal::Backend, 32 | { 33 | /// Node builder. 34 | pub fn builder(surface: Surface) -> NodeBuilder { 35 | PresentDesc::new(surface).builder() 36 | } 37 | } 38 | 39 | /// Presentation node description. 40 | #[derive(Debug)] 41 | pub struct PresentDesc { 42 | surface: Surface, 43 | } 44 | 45 | impl PresentDesc 46 | where 47 | B: gfx_hal::Backend, 48 | { 49 | /// Create present builder 50 | pub fn new( 51 | surface: Surface, 52 | ) -> Self { 53 | PresentDesc { 54 | surface, 55 | } 56 | } 57 | } 58 | 59 | impl AnyNodeDesc for PresentDesc 60 | where 61 | B: gfx_hal::Backend, 62 | T: ?Sized, 63 | { 64 | fn family(&self, families: &[Family]) -> Option { 65 | families.get(0).map(Family::index) 66 | } 67 | 68 | fn images(&self) -> Vec { 69 | vec![ImageAccess { 70 | access: gfx_hal::image::Access::TRANSFER_READ, 71 | layout: gfx_hal::image::Layout::TransferSrcOptimal, 72 | usage: gfx_hal::image::Usage::TRANSFER_SRC, 73 | stages: gfx_hal::pso::PipelineStage::TRANSFER, 74 | }] 75 | } 76 | 77 | fn build<'a>( 78 | self: Box, 79 | factory: &mut Factory, 80 | _aux: &mut T, 81 | family: gfx_hal::queue::QueueFamilyId, 82 | buffers: &mut [NodeBuffer<'a, B>], 83 | images: &mut [NodeImage<'a, B>], 84 | ) -> Result>, failure::Error> { 85 | assert_eq!(buffers.len(), 0); 86 | assert_eq!(images.len(), 1); 87 | 88 | let ref input_image = images[0]; 89 | let target = factory.create_target(self.surface, 3, gfx_hal::image::Usage::TRANSFER_DST)?; 90 | let mut pool = factory.create_command_pool(family)?; 91 | 92 | let per_image = match target.backbuffer() { 93 | gfx_hal::Backbuffer::Images(target_images) => { 94 | let buffers = pool.allocate_buffers(target_images.len()); 95 | target_images.iter().zip(buffers).map(|(target_image, buf_initial)| { 96 | let mut buf_recording = buf_initial.begin::, _>(); 97 | let mut encoder = buf_recording.encoder(); 98 | { 99 | let (stages, barriers) = gfx_acquire_barriers(None, Some(input_image)); 100 | log::info!("Acquire {:?} : {:#?}", stages, barriers); 101 | encoder.pipeline_barrier( 102 | stages, 103 | gfx_hal::memory::Dependencies::empty(), 104 | barriers, 105 | ); 106 | } 107 | encoder.copy_image( 108 | input_image.image.raw(), 109 | input_image.layout, 110 | &target_image, 111 | gfx_hal::image::Layout::TransferDstOptimal, 112 | Some(gfx_hal::command::ImageCopy { 113 | src_subresource: gfx_hal::image::SubresourceLayers { 114 | aspects: gfx_hal::format::Aspects::COLOR, 115 | level: 0, 116 | layers: 0..1, 117 | }, 118 | src_offset: gfx_hal::image::Offset::ZERO, 119 | dst_subresource: gfx_hal::image::SubresourceLayers { 120 | aspects: gfx_hal::format::Aspects::COLOR, 121 | level: 0, 122 | layers: 0..1, 123 | }, 124 | dst_offset: gfx_hal::image::Offset::ZERO, 125 | extent: gfx_hal::image::Extent { 126 | width: target.extent().width, 127 | height: target.extent().height, 128 | depth: 1, 129 | }, 130 | }), 131 | ); 132 | { 133 | let (stages, barriers) = gfx_release_barriers(None, Some(input_image)); 134 | log::info!("Release {:?} : {:#?}", stages, barriers); 135 | encoder.pipeline_barrier( 136 | stages, 137 | gfx_hal::memory::Dependencies::empty(), 138 | barriers, 139 | ); 140 | } 141 | 142 | let (submit, buffer) = buf_recording.finish().submit(); 143 | 144 | ForImage { 145 | submit, 146 | buffer, 147 | acquire: factory.create_semaphore().unwrap(), 148 | release: factory.create_semaphore().unwrap(), 149 | } 150 | }).collect() 151 | } 152 | _ => unimplemented!(), 153 | }; 154 | 155 | Ok(Box::new(PresentNode { 156 | free: Some(factory.create_semaphore().unwrap()), 157 | target, 158 | pool, 159 | per_image, 160 | })) 161 | } 162 | } 163 | 164 | impl AnyNode for PresentNode 165 | where 166 | B: gfx_hal::Backend, 167 | T: ?Sized, 168 | { 169 | unsafe fn run<'a>( 170 | &mut self, 171 | factory: &mut Factory, 172 | _aux: &mut T, 173 | _frames: &Frames, 174 | qid: QueueId, 175 | waits: &[(&'a B::Semaphore, gfx_hal::pso::PipelineStage)], 176 | signals: &[&'a B::Semaphore], 177 | fence: Option<&B::Fence>, 178 | ) { 179 | let acquire = self.free.take().unwrap(); 180 | unsafe { 181 | let next = self.target.next_image(&acquire).unwrap(); 182 | log::trace!("Present: {:#?}", next); 183 | let ref mut for_image = self.per_image[next[0] as usize]; 184 | self.free = Some(std::mem::replace(&mut for_image.acquire, acquire)); 185 | 186 | let family = factory.family_mut(qid.family()); 187 | 188 | family.submit( 189 | qid.index(), 190 | Some(Submission { 191 | waits: waits.iter().cloned().chain(Some((&for_image.acquire, gfx_hal::pso::PipelineStage::TRANSFER))), 192 | signals: signals.iter().cloned().chain(Some(&for_image.release)), 193 | submits: Some(&for_image.submit), 194 | }), 195 | fence, 196 | ); 197 | 198 | next.present(&mut family.queues_mut()[qid.index()], Some(&for_image.release)).unwrap(); 199 | } 200 | } 201 | 202 | unsafe fn dispose(self: Box, _factory: &mut Factory, _aux: &mut T) { 203 | unimplemented!() 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /license/APACHE: -------------------------------------------------------------------------------- 1 | Copyright 2018 The Rendy project developers 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /license/MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 The Rendy 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. -------------------------------------------------------------------------------- /memory/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-memory" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | 7 | [features] 8 | serde-1 = ["serde"] 9 | 10 | [dependencies] 11 | gfx-hal = { git = "https://github.com/gfx-rs/gfx" } 12 | derivative = "1.0" 13 | failure = "0.1" 14 | log = "0.4" 15 | hibitset = "0.5" 16 | relevant = "0.2" 17 | serde = { version = "1.0", optional = true, features = ["derive"] } 18 | smallvec = "0.6" 19 | veclist = "0.1" 20 | 21 | [dev-dependencies] 22 | rand = "0.5" 23 | -------------------------------------------------------------------------------- /memory/src/allocator/dedicated.rs: -------------------------------------------------------------------------------- 1 | use std::{ops::Range, ptr::NonNull}; 2 | 3 | use crate::{ 4 | allocator::{Allocator, Kind}, 5 | block::Block, 6 | mapping::{mapped_fitting_range, MappedRange}, 7 | memory::*, 8 | }; 9 | 10 | /// Memory block allocated from `DedicatedAllocator` 11 | #[derive(Debug)] 12 | pub struct DedicatedBlock { 13 | memory: Memory, 14 | mapping: Option<(NonNull, Range)>, 15 | } 16 | 17 | unsafe impl Send for DedicatedBlock where B: gfx_hal::Backend, {} 18 | unsafe impl Sync for DedicatedBlock where B: gfx_hal::Backend, {} 19 | 20 | impl DedicatedBlock 21 | where 22 | B: gfx_hal::Backend, 23 | { 24 | /// Get inner memory. 25 | /// Panics if mapped. 26 | pub fn unwrap_memory(self) -> Memory { 27 | assert!(self.mapping.is_none()); 28 | self.memory 29 | } 30 | 31 | /// Make unmapped block. 32 | pub fn from_memory(memory: Memory) -> Self { 33 | DedicatedBlock { 34 | memory, 35 | mapping: None, 36 | } 37 | } 38 | } 39 | 40 | impl Block for DedicatedBlock 41 | where 42 | B: gfx_hal::Backend, 43 | { 44 | #[inline] 45 | fn properties(&self) -> gfx_hal::memory::Properties { 46 | self.memory.properties() 47 | } 48 | 49 | #[inline] 50 | fn memory(&self) -> &B::Memory { 51 | self.memory.raw() 52 | } 53 | 54 | #[inline] 55 | fn range(&self) -> Range { 56 | 0..self.memory.size() 57 | } 58 | 59 | fn map<'a>( 60 | &'a mut self, 61 | device: &impl gfx_hal::Device, 62 | range: Range, 63 | ) -> Result, gfx_hal::mapping::Error> { 64 | assert!( 65 | range.start <= range.end, 66 | "Memory mapping region must have valid size" 67 | ); 68 | 69 | unsafe { 70 | if let Some(ptr) = self 71 | .mapping 72 | .clone() 73 | .and_then(|mapping| mapped_fitting_range(mapping.0, mapping.1, range.clone())) 74 | { 75 | Ok(MappedRange::from_raw(&self.memory, ptr, range)) 76 | } else { 77 | self.unmap(device); 78 | let mapping = MappedRange::new(&self.memory, device, range.clone())?; 79 | self.mapping = Some((mapping.ptr(), mapping.range())); 80 | Ok(mapping) 81 | } 82 | } 83 | } 84 | 85 | fn unmap(&mut self, device: &impl gfx_hal::Device) { 86 | if self.mapping.take().is_some() { 87 | unsafe { 88 | // trace!("Unmap memory: {:#?}", self.memory); 89 | device.unmap_memory(self.memory.raw()); 90 | } 91 | } 92 | } 93 | } 94 | 95 | /// Dedicated memory allocator that uses memory object per allocation requested. 96 | /// 97 | /// This allocator suites best huge allocations. 98 | /// From 32 MiB when GPU has 4-8 GiB memory total. 99 | /// 100 | /// `Heaps` use this allocator when none of sub-allocators bound to the memory type 101 | /// can handle size required. 102 | /// TODO: Check if resource prefers dedicated memory. 103 | #[derive(Debug)] 104 | pub struct DedicatedAllocator { 105 | memory_type: gfx_hal::MemoryTypeId, 106 | memory_properties: gfx_hal::memory::Properties, 107 | used: u64, 108 | } 109 | 110 | impl DedicatedAllocator { 111 | /// Get properties required by the allocator. 112 | pub fn properties_required() -> gfx_hal::memory::Properties { 113 | gfx_hal::memory::Properties::empty() 114 | } 115 | 116 | /// Create new `LinearAllocator` 117 | /// for `memory_type` with `memory_properties` specified 118 | pub fn new(memory_type: gfx_hal::MemoryTypeId, memory_properties: gfx_hal::memory::Properties) -> Self { 119 | DedicatedAllocator { 120 | memory_type, 121 | memory_properties, 122 | used: 0, 123 | } 124 | } 125 | } 126 | 127 | impl Allocator for DedicatedAllocator 128 | where 129 | B: gfx_hal::Backend, 130 | { 131 | type Block = DedicatedBlock; 132 | 133 | fn kind() -> Kind { 134 | Kind::Dedicated 135 | } 136 | 137 | #[inline] 138 | fn alloc( 139 | &mut self, 140 | device: &impl gfx_hal::Device, 141 | size: u64, 142 | _align: u64, 143 | ) -> Result<(DedicatedBlock, u64), gfx_hal::device::AllocationError> { 144 | let memory = unsafe { 145 | Memory::from_raw( 146 | device.allocate_memory( 147 | self.memory_type, 148 | size, 149 | )?, 150 | size, 151 | self.memory_properties, 152 | ) 153 | }; 154 | 155 | self.used += size; 156 | 157 | Ok((DedicatedBlock::from_memory(memory), size)) 158 | } 159 | 160 | #[inline] 161 | fn free(&mut self, device: &impl gfx_hal::Device, mut block: DedicatedBlock) -> u64 { 162 | block.unmap(device); 163 | let size = block.memory.size(); 164 | self.used -= size; 165 | unsafe { 166 | device.free_memory(block.memory.into_raw()); 167 | } 168 | size 169 | } 170 | } 171 | 172 | impl Drop for DedicatedAllocator { 173 | fn drop(&mut self) { 174 | assert_eq!(self.used, 0); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /memory/src/allocator/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module provides `Allocator` trait and few allocators that implements the trait. 2 | 3 | mod linear; 4 | mod dedicated; 5 | mod dynamic; 6 | 7 | use crate::block::Block; 8 | 9 | pub use self::{ 10 | linear::{LinearAllocator, LinearBlock, LinearConfig}, 11 | dedicated::{DedicatedAllocator, DedicatedBlock}, 12 | dynamic::{DynamicAllocator, DynamicBlock, DynamicConfig}, 13 | }; 14 | 15 | /// Allocator kind. 16 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 17 | pub enum Kind { 18 | /// Memory object per allocation. 19 | Dedicated, 20 | 21 | /// General purpose allocator. 22 | Dynamic, 23 | 24 | /// Allocates linearly. 25 | /// Fast and low overhead. 26 | /// Suitable for one-time-use allocations. 27 | Linear, 28 | } 29 | 30 | /// Allocator trait implemented for various allocators. 31 | pub trait Allocator { 32 | /// Block type returned by allocator. 33 | type Block: Block; 34 | 35 | /// Get allocator kind. 36 | fn kind() -> Kind; 37 | 38 | /// Allocate block of memory. 39 | /// On success returns allocated block and amount of memory consumed from device. 40 | fn alloc( 41 | &mut self, 42 | device: &impl gfx_hal::Device, 43 | size: u64, 44 | align: u64, 45 | ) -> Result<(Self::Block, u64), gfx_hal::device::AllocationError>; 46 | 47 | /// Free block of memory. 48 | /// Returns amount of memory returned to the device. 49 | fn free(&mut self, device: &impl gfx_hal::Device, block: Self::Block) -> u64; 50 | } 51 | -------------------------------------------------------------------------------- /memory/src/block.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Range; 2 | 3 | use crate::mapping::MappedRange; 4 | 5 | /// Block that owns a `Range` of the `Memory`. 6 | /// Implementor must ensure that there can't be any other blocks 7 | /// with overlapping range (either through type system or safety notes for unsafe functions). 8 | /// Provides access to safe memory range mapping. 9 | pub trait Block { 10 | /// Get memory properties of the block. 11 | fn properties(&self) -> gfx_hal::memory::Properties; 12 | 13 | /// Get raw memory object. 14 | fn memory(&self) -> &B::Memory; 15 | 16 | /// Get memory range owned by this block. 17 | fn range(&self) -> Range; 18 | 19 | /// Get mapping for the buffer range. 20 | /// Memory writes to the region performed by device become available for the host. 21 | fn map<'a>( 22 | &'a mut self, 23 | device: &impl gfx_hal::Device, 24 | range: Range, 25 | ) -> Result, gfx_hal::mapping::Error>; 26 | 27 | /// Release memory mapping. Must be called after successful `map` call. 28 | /// No-op if block is not mapped. 29 | fn unmap(&mut self, device: &impl gfx_hal::Device); 30 | } 31 | -------------------------------------------------------------------------------- /memory/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! GPU memory management 2 | //! 3 | #![forbid(overflowing_literals)] 4 | #![deny(missing_copy_implementations)] 5 | #![deny(missing_debug_implementations)] 6 | #![deny(missing_docs)] 7 | #![deny(intra_doc_link_resolution_failure)] 8 | #![deny(path_statements)] 9 | #![deny(trivial_bounds)] 10 | #![deny(type_alias_bounds)] 11 | #![deny(unconditional_recursion)] 12 | #![deny(unions_with_drop_fields)] 13 | #![deny(while_true)] 14 | #![deny(unused)] 15 | #![deny(bad_style)] 16 | #![deny(future_incompatible)] 17 | #![deny(rust_2018_compatibility)] 18 | #![deny(rust_2018_idioms)] 19 | #![allow(unused_unsafe)] 20 | 21 | mod allocator; 22 | mod block; 23 | mod heaps; 24 | mod mapping; 25 | mod memory; 26 | mod util; 27 | mod usage; 28 | 29 | pub use crate::{ 30 | allocator::*, 31 | block::Block, 32 | heaps::{Heaps, HeapsConfig, MemoryBlock, HeapsError}, 33 | mapping::{write::Write, Coherent, MappedRange, MaybeCoherent, NonCoherent}, 34 | memory::Memory, 35 | usage::*, 36 | }; 37 | -------------------------------------------------------------------------------- /memory/src/mapping/mod.rs: -------------------------------------------------------------------------------- 1 | mod range; 2 | pub(crate) mod write; 3 | 4 | use std::{ops::Range, ptr::NonNull}; 5 | 6 | use crate::{ 7 | memory::Memory, 8 | util::fits_usize, 9 | }; 10 | 11 | pub(crate) use self::range::{ 12 | mapped_fitting_range, mapped_slice, mapped_slice_mut, mapped_sub_range, 13 | }; 14 | use self::write::{Write, WriteCoherent, WriteFlush}; 15 | 16 | /// Non-coherent marker. 17 | #[derive(Clone, Copy, Debug)] 18 | pub struct NonCoherent; 19 | 20 | /// Coherent marker. 21 | #[derive(Clone, Copy, Debug)] 22 | pub struct Coherent; 23 | 24 | /// Value that contains either coherent marker or non-coherent marker. 25 | #[derive(Clone, Copy, Debug)] 26 | pub struct MaybeCoherent(bool); 27 | 28 | /// Represents range of the memory mapped to the host. 29 | /// Provides methods for safer host access to the memory. 30 | #[derive(Debug)] 31 | pub struct MappedRange<'a, B: gfx_hal::Backend, C = MaybeCoherent> { 32 | /// Memory object that is mapped. 33 | memory: &'a Memory, 34 | 35 | /// Pointer to range mapped memory. 36 | ptr: NonNull, 37 | 38 | /// Range of mapped memory. 39 | range: Range, 40 | 41 | /// Coherency marker 42 | coherent: C, 43 | } 44 | 45 | impl<'a, B> MappedRange<'a, B> 46 | where 47 | B: gfx_hal::Backend, 48 | { 49 | /// Map range of memory. 50 | /// 51 | /// # Safety 52 | /// 53 | /// * Only one range for the given memory object can be mapped. 54 | /// * Memory object must be not mapped. 55 | /// * Memory object must be created with device specified. 56 | pub unsafe fn new( 57 | memory: &'a Memory, 58 | device: &impl gfx_hal::Device, 59 | range: Range, 60 | ) -> Result { 61 | assert!( 62 | range.start <= range.end, 63 | "Memory mapping region must have valid size" 64 | ); 65 | assert!( 66 | fits_usize(range.end - range.start), 67 | "Range length must fit in usize" 68 | ); 69 | assert!(memory.host_visible()); 70 | 71 | let ptr = device.map_memory( 72 | memory.raw(), 73 | range.clone(), 74 | )?; 75 | assert!( 76 | (ptr as usize).wrapping_neg() >= (range.end - range.start) as usize, 77 | "Resulting pointer value + range length must fit in usize. Pointer: {:p}, range {:?}", ptr, range, 78 | ); 79 | 80 | Ok(Self::from_raw( 81 | memory, 82 | NonNull::new_unchecked(ptr as *mut u8), 83 | range, 84 | )) 85 | } 86 | 87 | /// Construct mapped range from raw mapping 88 | /// 89 | /// # Safety 90 | /// 91 | /// `memory` `range` must be mapped to host memory region pointer by `ptr`. 92 | pub unsafe fn from_raw(memory: &'a Memory, ptr: NonNull, range: Range) -> Self { 93 | MappedRange { 94 | ptr, 95 | range, 96 | memory, 97 | coherent: MaybeCoherent(memory.host_coherent()), 98 | } 99 | } 100 | 101 | /// Get pointer to beginning of memory region. 102 | pub fn ptr(&self) -> NonNull { 103 | self.ptr 104 | } 105 | 106 | /// Get mapped range. 107 | pub fn range(&self) -> Range { 108 | self.range.clone() 109 | } 110 | 111 | /// Fetch readable slice of sub-range to be read. 112 | /// Invalidating range if memory is not coherent. 113 | /// `range.end - range.start` must be multiple of `size_of::()`. 114 | /// `mapping offset + range.start` must be multiple of `align_of::()`. 115 | /// 116 | /// # Safety 117 | /// 118 | /// * Caller must ensure that device won't write to the memory region until the borrowing ends. 119 | /// * `T` Must be plain-old-data type with memory layout compatible with data written by the device. 120 | pub unsafe fn read<'b, T>( 121 | &'b mut self, 122 | device: &impl gfx_hal::Device, 123 | range: Range, 124 | ) -> Result<&'b [T], gfx_hal::mapping::Error> 125 | where 126 | 'a: 'b, 127 | T: Copy, 128 | { 129 | let (ptr, range) = mapped_sub_range(self.ptr, self.range.clone(), range) 130 | .ok_or_else(|| gfx_hal::mapping::Error::OutOfBounds)?; 131 | 132 | if self.coherent.0 { 133 | device.invalidate_mapped_memory_ranges( 134 | Some((self.memory.raw(), self.range.clone())) 135 | )?; 136 | } 137 | 138 | let slice = mapped_slice::(ptr, range); 139 | Ok(slice) 140 | } 141 | 142 | /// Fetch writer to the sub-region. 143 | /// This writer will flush data on drop if written at least once. 144 | /// 145 | /// # Safety 146 | /// 147 | /// * Caller must ensure that device won't write to or read from the memory region. 148 | pub unsafe fn write<'b, T: 'b>( 149 | &'b mut self, 150 | device: &'b impl gfx_hal::Device, 151 | range: Range, 152 | ) -> Result + 'b, gfx_hal::mapping::Error> 153 | where 154 | 'a: 'b, 155 | T: Copy, 156 | { 157 | let (ptr, range) = mapped_sub_range(self.ptr, self.range.clone(), range) 158 | .ok_or_else(|| gfx_hal::mapping::Error::OutOfBounds)?; 159 | 160 | if !self.coherent.0 { 161 | device.invalidate_mapped_memory_ranges( 162 | Some((self.memory.raw(), self.range.clone())) 163 | )?; 164 | } 165 | 166 | let slice = mapped_slice_mut::(ptr, range.clone()); 167 | 168 | let ref memory = self.memory; 169 | 170 | Ok(WriteFlush { 171 | slice, 172 | flush: if !self.coherent.0 { 173 | Some(move || unsafe { 174 | device.flush_mapped_memory_ranges(Some((memory.raw(), range))) 175 | .expect("Should flush successfully"); 176 | }) 177 | } else { 178 | None 179 | } 180 | }) 181 | } 182 | 183 | /// Convert into mapped range with statically known coherency. 184 | pub fn coherent(self) -> Result, MappedRange<'a, B, NonCoherent>> { 185 | if self.coherent.0 { 186 | Ok(MappedRange { 187 | memory: self.memory, 188 | ptr: self.ptr, 189 | range: self.range, 190 | coherent: Coherent, 191 | }) 192 | } else { 193 | Err(MappedRange { 194 | memory: self.memory, 195 | ptr: self.ptr, 196 | range: self.range, 197 | coherent: NonCoherent, 198 | }) 199 | } 200 | } 201 | } 202 | 203 | impl<'a, B> From> for MappedRange<'a, B> 204 | where 205 | B: gfx_hal::Backend, 206 | { 207 | fn from(range: MappedRange<'a, B, Coherent>) -> Self { 208 | MappedRange { 209 | memory: range.memory, 210 | ptr: range.ptr, 211 | range: range.range, 212 | coherent: MaybeCoherent(true), 213 | } 214 | } 215 | } 216 | 217 | impl<'a, B> From> for MappedRange<'a, B> 218 | where 219 | B: gfx_hal::Backend, 220 | { 221 | fn from(range: MappedRange<'a, B, NonCoherent>) -> Self { 222 | MappedRange { 223 | memory: range.memory, 224 | ptr: range.ptr, 225 | range: range.range, 226 | coherent: MaybeCoherent(false), 227 | } 228 | } 229 | } 230 | 231 | impl<'a, B> MappedRange<'a, B, Coherent> 232 | where 233 | B: gfx_hal::Backend, 234 | { 235 | /// Fetch writer to the sub-region. 236 | /// 237 | /// # Safety 238 | /// 239 | /// * Caller must ensure that device won't write to or read from the memory region. 240 | pub unsafe fn write<'b, U: 'b>( 241 | &'b mut self, 242 | range: Range, 243 | ) -> Result + 'b, gfx_hal::mapping::Error> 244 | where 245 | U: Copy, 246 | { 247 | let (ptr, range) = mapped_sub_range(self.ptr, self.range.clone(), range) 248 | .ok_or_else(|| gfx_hal::mapping::Error::OutOfBounds)?; 249 | 250 | let slice = mapped_slice_mut::(ptr, range.clone()); 251 | 252 | Ok(WriteCoherent { slice }) 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /memory/src/mapping/range.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | mem::{align_of, size_of}, 3 | ops::Range, 4 | ptr::NonNull, 5 | slice::{from_raw_parts, from_raw_parts_mut}, 6 | }; 7 | 8 | /// Get sub-range of memory mapping. 9 | /// `range` is in memory object space. 10 | pub(crate) fn mapped_fitting_range( 11 | ptr: NonNull, 12 | range: Range, 13 | fitting: Range, 14 | ) -> Option> { 15 | assert!( 16 | range.start <= range.end, 17 | "Memory mapping region must have valid size" 18 | ); 19 | assert!( 20 | fitting.start <= fitting.end, 21 | "Memory mapping region must have valid size" 22 | ); 23 | debug_assert!(range.start <= range.end); 24 | 25 | if fitting.start < range.start || fitting.end > range.end { 26 | None 27 | } else { 28 | Some(unsafe { 29 | // for x > 0 and y >= 0: x + y > 0. No overlapping due to assertions in `new` and checks above. 30 | NonNull::new_unchecked( 31 | (ptr.as_ptr() as usize + (fitting.start - range.start) as usize) as *mut u8, 32 | ) 33 | }) 34 | } 35 | } 36 | 37 | /// Get sub-range of memory mapping. 38 | /// `range` is in mapping object space. 39 | pub(crate) fn mapped_sub_range( 40 | ptr: NonNull, 41 | range: Range, 42 | sub: Range, 43 | ) -> Option<(NonNull, Range)> { 44 | let fitting = sub.start.checked_add(range.start)?..sub.end.checked_add(range.start)?; 45 | let ptr = mapped_fitting_range(ptr, range, fitting.clone())?; 46 | Some((ptr, fitting)) 47 | } 48 | 49 | /// # Safety 50 | /// 51 | /// User must ensure that: 52 | /// * this function won't create aliasing slices. 53 | /// * returned slice doesn't outlive mapping. 54 | pub(crate) unsafe fn mapped_slice_mut<'a, T>( 55 | ptr: NonNull, 56 | range: Range, 57 | ) -> &'a mut [T] { 58 | let size = (range.end - range.start) as usize; 59 | assert_eq!( 60 | size % size_of::(), 61 | 0, 62 | "Range length must be multiple of element size" 63 | ); 64 | let offset = ptr.as_ptr() as usize; 65 | assert_eq!(offset % align_of::(), 0, "Range offset must be multiple of element alignment"); 66 | from_raw_parts_mut(ptr.as_ptr() as *mut T, size) 67 | } 68 | 69 | /// # Safety 70 | /// 71 | /// User must ensure that: 72 | /// * returned slice doesn't outlive mapping. 73 | pub(crate) unsafe fn mapped_slice<'a, T>( 74 | ptr: NonNull, 75 | range: Range, 76 | ) -> &'a [T] { 77 | let size = (range.end - range.start) as usize; 78 | assert_eq!( 79 | size % size_of::(), 80 | 0, 81 | "Range length must be multiple of element size" 82 | ); 83 | let offset = ptr.as_ptr() as usize; 84 | assert_eq!(offset % align_of::(), 0, "Range offset must be multiple of element alignment"); 85 | from_raw_parts(ptr.as_ptr() as *const T, size) 86 | } 87 | -------------------------------------------------------------------------------- /memory/src/mapping/write.rs: -------------------------------------------------------------------------------- 1 | use std::ptr::copy_nonoverlapping; 2 | 3 | /// Trait for memory region suitable for host writes. 4 | pub trait Write { 5 | /// Get mutable slice of `T` bound to mapped range. 6 | /// 7 | /// # Safety 8 | /// 9 | /// * Returned slice should not be read. 10 | unsafe fn slice(&mut self) -> &mut [T]; 11 | 12 | /// Write data into mapped memory sub-region. 13 | /// 14 | /// # Panic 15 | /// 16 | /// Panics if `data.len()` is greater than this sub-region len. 17 | fn write(&mut self, data: &[T]) { 18 | unsafe { 19 | let slice = self.slice(); 20 | assert!(data.len() <= slice.len()); 21 | copy_nonoverlapping(data.as_ptr(), slice.as_mut_ptr(), data.len()); 22 | } 23 | } 24 | } 25 | 26 | #[derive(Debug)] 27 | pub(super) struct WriteFlush<'a, T, F: FnOnce() + 'a> { 28 | pub(super) slice: &'a mut [T], 29 | pub(super) flush: Option, 30 | } 31 | 32 | impl<'a, T, F> Drop for WriteFlush<'a, T, F> 33 | where 34 | T: 'a, 35 | F: FnOnce() + 'a, 36 | { 37 | fn drop(&mut self) { 38 | self.flush.take().map(|f| f()); 39 | } 40 | } 41 | 42 | impl<'a, T, F> Write for WriteFlush<'a, T, F> 43 | where 44 | T: Copy + 'a, 45 | F: FnOnce() + 'a, 46 | { 47 | /// # Safety 48 | /// 49 | /// [See doc comment for trait method](trait.Write#method.slice) 50 | unsafe fn slice(&mut self) -> &mut [T] { 51 | self.slice 52 | } 53 | } 54 | 55 | #[warn(dead_code)] 56 | #[derive(Debug)] 57 | pub(super) struct WriteCoherent<'a, T> { 58 | pub(super) slice: &'a mut [T], 59 | } 60 | 61 | impl<'a, T> Write for WriteCoherent<'a, T> 62 | where 63 | T: Copy + 'a, 64 | { 65 | /// # Safety 66 | /// 67 | /// [See doc comment for trait method](trait.Write#method.slice) 68 | unsafe fn slice(&mut self) -> &mut [T] { 69 | self.slice 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /memory/src/memory.rs: -------------------------------------------------------------------------------- 1 | // use std::fmt; 2 | 3 | /// Memory object wrapper. 4 | /// Contains size and properties of the memory. 5 | #[derive(Debug)] 6 | pub struct Memory { 7 | raw: B::Memory, 8 | size: u64, 9 | properties: gfx_hal::memory::Properties, 10 | relevant: relevant::Relevant, 11 | } 12 | 13 | impl Memory 14 | where 15 | B: gfx_hal::Backend, 16 | { 17 | /// Get memory properties. 18 | pub fn properties(&self) -> gfx_hal::memory::Properties { 19 | self.properties 20 | } 21 | 22 | /// Get memory size. 23 | pub fn size(&self) -> u64 { 24 | self.size 25 | } 26 | 27 | /// Get raw memory. 28 | pub fn raw(&self) -> &B::Memory { 29 | &self.raw 30 | } 31 | 32 | /// Unwrap raw memory. 33 | pub fn into_raw(self) -> B::Memory { 34 | self.relevant.dispose(); 35 | self.raw 36 | } 37 | 38 | /// Create memory from raw object. 39 | /// 40 | /// # Safety 41 | /// 42 | /// TODO: 43 | pub unsafe fn from_raw( 44 | raw: B::Memory, 45 | size: u64, 46 | properties: gfx_hal::memory::Properties, 47 | ) -> Self { 48 | Memory { 49 | properties, 50 | raw, 51 | size, 52 | relevant: relevant::Relevant, 53 | } 54 | } 55 | 56 | /// Check if this memory is host-visible and can be mapped. 57 | /// `memory.host_visible()` is equivalent to `memory.properties().contains(Properties::CPU_VISIBLE)` 58 | pub fn host_visible(&self) -> bool { 59 | self.properties 60 | .contains(gfx_hal::memory::Properties::CPU_VISIBLE) 61 | } 62 | 63 | /// Check if this memory is host-coherent and doesn't require invalidating or flushing. 64 | /// `memory.host_coherent()` is equivalent to `memory.properties().contains(Properties::COHERENT)` 65 | pub fn host_coherent(&self) -> bool { 66 | self.properties 67 | .contains(gfx_hal::memory::Properties::COHERENT) 68 | } 69 | } 70 | 71 | // pub(crate) fn memory_ptr_fmt( 72 | // memory: &*const Memory, 73 | // fmt: &mut fmt::Formatter<'_>, 74 | // ) -> Result<(), fmt::Error> { 75 | // unsafe { 76 | // if fmt.alternate() { 77 | // write!(fmt, "*const {:#?}", **memory) 78 | // } else { 79 | // write!(fmt, "*const {:?}", **memory) 80 | // } 81 | // } 82 | // } 83 | -------------------------------------------------------------------------------- /memory/src/usage.rs: -------------------------------------------------------------------------------- 1 | //! Defines usage types for memory bocks. 2 | //! See `Usage` and implementations for details. 3 | 4 | use crate::allocator::Kind; 5 | 6 | /// Memory usage trait. 7 | pub trait MemoryUsage: Copy + std::fmt::Debug { 8 | /// Get set of properties required for the usage. 9 | fn properties_required(&self) -> gfx_hal::memory::Properties; 10 | 11 | /// Get comparable fitness value for memory properties. 12 | /// 13 | /// # Panics 14 | /// 15 | /// This function will panic if properties set doesn't contain required properties. 16 | fn memory_fitness(&self, properties: gfx_hal::memory::Properties) -> u32; 17 | 18 | /// Get comparable fitness value for memory allocator. 19 | fn allocator_fitness(&self, kind: Kind) -> u32; 20 | } 21 | 22 | /// Full speed GPU access. 23 | /// Optimal for render targets and persistent resources. 24 | /// Avoid memory with host access. 25 | #[derive(Clone, Copy, Debug)] 26 | pub struct Data; 27 | 28 | impl MemoryUsage for Data { 29 | 30 | fn properties_required(&self) -> gfx_hal::memory::Properties { 31 | gfx_hal::memory::Properties::DEVICE_LOCAL 32 | } 33 | 34 | #[inline] 35 | fn memory_fitness(&self, properties: gfx_hal::memory::Properties) -> u32 { 36 | assert!(properties.contains(gfx_hal::memory::Properties::DEVICE_LOCAL)); 37 | 0 | ((!properties.contains(gfx_hal::memory::Properties::CPU_VISIBLE)) as u32) << 3 38 | | ((!properties.contains(gfx_hal::memory::Properties::LAZILY_ALLOCATED)) as u32) << 2 39 | | ((!properties.contains(gfx_hal::memory::Properties::CPU_CACHED)) as u32) << 1 40 | | ((!properties.contains(gfx_hal::memory::Properties::COHERENT)) as u32) << 0 41 | } 42 | 43 | fn allocator_fitness(&self, kind: Kind) -> u32 { 44 | match kind { 45 | Kind::Dedicated => 1, 46 | Kind::Dynamic => 2, 47 | Kind::Linear => 0, 48 | } 49 | } 50 | } 51 | 52 | /// CPU to GPU data flow with update commands. 53 | /// Used for dynamic buffer data, typically constant buffers. 54 | /// Host access is guaranteed. 55 | /// Prefers memory with fast GPU access. 56 | #[derive(Clone, Copy, Debug)] 57 | pub struct Dynamic; 58 | 59 | impl MemoryUsage for Dynamic { 60 | 61 | fn properties_required(&self) -> gfx_hal::memory::Properties { 62 | gfx_hal::memory::Properties::CPU_VISIBLE 63 | } 64 | 65 | #[inline] 66 | fn memory_fitness(&self, properties: gfx_hal::memory::Properties) -> u32 { 67 | assert!(properties.contains(gfx_hal::memory::Properties::CPU_VISIBLE)); 68 | assert!(!properties.contains(gfx_hal::memory::Properties::LAZILY_ALLOCATED)); 69 | 70 | 0 | (properties.contains(gfx_hal::memory::Properties::DEVICE_LOCAL) as u32) << 2 71 | | (properties.contains(gfx_hal::memory::Properties::COHERENT) as u32) << 1 72 | | ((!properties.contains(gfx_hal::memory::Properties::CPU_CACHED)) as u32) << 0 73 | } 74 | 75 | fn allocator_fitness(&self, kind: Kind) -> u32 { 76 | match kind { 77 | Kind::Dedicated => 1, 78 | Kind::Dynamic => 2, 79 | Kind::Linear => 0, 80 | } 81 | } 82 | } 83 | 84 | /// CPU to GPU data flow with mapping. 85 | /// Used for staging data before copying to the `Data` memory. 86 | /// Host access is guaranteed. 87 | #[derive(Clone, Copy, Debug)] 88 | pub struct Upload; 89 | 90 | impl MemoryUsage for Upload { 91 | 92 | fn properties_required(&self) -> gfx_hal::memory::Properties { 93 | gfx_hal::memory::Properties::CPU_VISIBLE 94 | } 95 | 96 | #[inline] 97 | fn memory_fitness(&self, properties: gfx_hal::memory::Properties) -> u32 { 98 | assert!(properties.contains(gfx_hal::memory::Properties::CPU_VISIBLE)); 99 | assert!(!properties.contains(gfx_hal::memory::Properties::LAZILY_ALLOCATED)); 100 | 101 | 0 | ((!properties.contains(gfx_hal::memory::Properties::DEVICE_LOCAL)) as u32) << 2 102 | | (properties.contains(gfx_hal::memory::Properties::COHERENT) as u32) << 1 103 | | ((!properties.contains(gfx_hal::memory::Properties::CPU_CACHED)) as u32) << 0 104 | } 105 | 106 | fn allocator_fitness(&self, kind: Kind) -> u32 { 107 | match kind { 108 | Kind::Dedicated => 0, 109 | Kind::Dynamic => 1, 110 | Kind::Linear => 2, 111 | } 112 | } 113 | } 114 | 115 | /// GPU to CPU data flow with mapping. 116 | /// Used for copying data from `Data` memory to be read by the host. 117 | /// Host access is guaranteed. 118 | #[derive(Clone, Copy, Debug)] 119 | pub struct Download; 120 | 121 | impl MemoryUsage for Download { 122 | 123 | fn properties_required(&self) -> gfx_hal::memory::Properties { 124 | gfx_hal::memory::Properties::CPU_VISIBLE 125 | } 126 | 127 | #[inline] 128 | fn memory_fitness(&self, properties: gfx_hal::memory::Properties) -> u32 { 129 | assert!(properties.contains(gfx_hal::memory::Properties::CPU_VISIBLE)); 130 | assert!(!properties.contains(gfx_hal::memory::Properties::LAZILY_ALLOCATED)); 131 | 132 | 0 | ((!properties.contains(gfx_hal::memory::Properties::DEVICE_LOCAL)) as u32) << 2 133 | | (properties.contains(gfx_hal::memory::Properties::CPU_CACHED) as u32) << 1 134 | | (properties.contains(gfx_hal::memory::Properties::COHERENT) as u32) << 0 135 | } 136 | 137 | fn allocator_fitness(&self, kind: Kind) -> u32 { 138 | match kind { 139 | Kind::Dedicated => 0, 140 | Kind::Dynamic => 1, 141 | Kind::Linear => 2, 142 | } 143 | } 144 | } 145 | 146 | /// Well-known memory usage types. 147 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 148 | pub enum MemoryUsageValue { 149 | /// See [`Data`] 150 | /// 151 | /// [`Data`]: struct.Data.html 152 | Data, 153 | 154 | /// See [`Dynamic`] 155 | /// 156 | /// [`Dynamic`]: struct.Dynamic.html 157 | Dynamic, 158 | 159 | /// See [`Upload`] 160 | /// 161 | /// [`Upload`]: struct.Upload.html 162 | Upload, 163 | 164 | /// See [`Download`] 165 | /// 166 | /// [`Download`]: struct.Download.html 167 | Download, 168 | } 169 | 170 | 171 | /// Memory usage trait. 172 | impl MemoryUsage for MemoryUsageValue { 173 | fn properties_required(&self) -> gfx_hal::memory::Properties { 174 | match self { 175 | MemoryUsageValue::Data => Data.properties_required(), 176 | MemoryUsageValue::Dynamic => Dynamic.properties_required(), 177 | MemoryUsageValue::Upload => Upload.properties_required(), 178 | MemoryUsageValue::Download => Download.properties_required(), 179 | } 180 | } 181 | 182 | fn memory_fitness(&self, properties: gfx_hal::memory::Properties) -> u32 { 183 | match self { 184 | MemoryUsageValue::Data => Data.memory_fitness(properties), 185 | MemoryUsageValue::Dynamic => Dynamic.memory_fitness(properties), 186 | MemoryUsageValue::Upload => Upload.memory_fitness(properties), 187 | MemoryUsageValue::Download => Download.memory_fitness(properties), 188 | } 189 | } 190 | 191 | fn allocator_fitness(&self, kind: Kind) -> u32 { 192 | match self { 193 | MemoryUsageValue::Data => Data.allocator_fitness(kind), 194 | MemoryUsageValue::Dynamic => Dynamic.allocator_fitness(kind), 195 | MemoryUsageValue::Upload => Upload.allocator_fitness(kind), 196 | MemoryUsageValue::Download => Download.allocator_fitness(kind), 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /memory/src/util.rs: -------------------------------------------------------------------------------- 1 | pub(crate) fn aligned(value: u64, align: u64) -> u64 { 2 | debug_assert_ne!(align, 0); 3 | debug_assert_eq!(align.count_ones(), 1); 4 | if value == 0 { 5 | 0 6 | } else { 7 | 1u64 + ((value - 1u64) | (align - 1u64)) 8 | } 9 | } 10 | 11 | pub(crate) trait IntegerFitting { 12 | fn fits_usize(self) -> bool; 13 | fn fits_isize(self) -> bool; 14 | 15 | fn usize_fits(value: usize) -> bool; 16 | fn isize_fits(value: isize) -> bool; 17 | } 18 | 19 | #[cfg(any(target_pointer_width = "16", target_pointer_width = "32"))] 20 | impl IntegerFitting for u64 { 21 | fn fits_usize(self) -> bool { 22 | self <= usize::max_value() as u64 23 | } 24 | fn fits_isize(self) -> bool { 25 | self <= isize::max_value() as u64 26 | } 27 | fn usize_fits(_value: usize) -> bool { 28 | true 29 | } 30 | fn isize_fits(value: isize) -> bool { 31 | value >= 0 32 | } 33 | } 34 | 35 | #[cfg(target_pointer_width = "64")] 36 | impl IntegerFitting for u64 { 37 | fn fits_usize(self) -> bool { 38 | true 39 | } 40 | fn fits_isize(self) -> bool { 41 | self <= isize::max_value() as u64 42 | } 43 | fn usize_fits(_value: usize) -> bool { 44 | true 45 | } 46 | fn isize_fits(value: isize) -> bool { 47 | value >= 0 48 | } 49 | } 50 | 51 | #[cfg(not(any( 52 | target_pointer_width = "16", 53 | target_pointer_width = "32", 54 | target_pointer_width = "64" 55 | )))] 56 | impl IntegerFitting for u64 { 57 | fn fits_usize(self) -> bool { 58 | true 59 | } 60 | fn fits_isize(self) -> bool { 61 | true 62 | } 63 | fn usize_fits(value: usize) -> bool { 64 | value <= u64::max_value() as usize 65 | } 66 | fn isize_fits(value: isize) -> bool { 67 | value >= 0 && value <= u64::max_value() as isize 68 | } 69 | } 70 | 71 | #[cfg(target_pointer_width = "16")] 72 | impl IntegerFitting for u32 { 73 | fn fits_usize(self) -> bool { 74 | self <= usize::max_value() as u32 75 | } 76 | fn fits_isize(self) -> bool { 77 | self <= isize::max_value() as u32 78 | } 79 | fn usize_fits(_value: usize) -> bool { 80 | true 81 | } 82 | fn isize_fits(value: isize) -> bool { 83 | value >= 0 84 | } 85 | } 86 | 87 | #[cfg(target_pointer_width = "32")] 88 | impl IntegerFitting for u32 { 89 | fn fits_usize(self) -> bool { 90 | true 91 | } 92 | fn fits_isize(self) -> bool { 93 | self <= isize::max_value() as u32 94 | } 95 | fn usize_fits(_value: usize) -> bool { 96 | true 97 | } 98 | fn isize_fits(value: isize) -> bool { 99 | value >= 0 100 | } 101 | } 102 | 103 | #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))] 104 | impl IntegerFitting for u32 { 105 | fn fits_usize(self) -> bool { 106 | true 107 | } 108 | fn fits_isize(self) -> bool { 109 | true 110 | } 111 | fn usize_fits(value: usize) -> bool { 112 | value <= u32::max_value() as usize 113 | } 114 | fn isize_fits(value: isize) -> bool { 115 | value >= 0 && value <= u32::max_value() as isize 116 | } 117 | } 118 | 119 | pub(crate) fn fits_usize(value: T) -> bool { 120 | value.fits_usize() 121 | } 122 | 123 | pub(crate) fn fits_u32(value: usize) -> bool { 124 | u32::usize_fits(value) 125 | } 126 | -------------------------------------------------------------------------------- /mesh/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-mesh" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | description = "Crate for rendy to create and use meshes with vertex semantic" 6 | keywords = ["graphics", "rendy"] 7 | readme = "README.md" 8 | license = "MIT/Apache-2.0" 9 | repository = "https://github.com/rustgd/rendy.git" 10 | documentation = "https://docs.rs/crate/rendy-mesh/0.1.0" 11 | edition = "2018" 12 | 13 | [features] 14 | obj = ["wavefront_obj"] 15 | serde-1 = ["serde", "gfx-hal/serde", "smallvec/serde", "rendy-factory/serde"] 16 | 17 | [dependencies] 18 | rendy-memory = { path = "../memory" } 19 | rendy-command = { path = "../command" } 20 | rendy-resource = { path = "../resource" } 21 | rendy-factory = { path = "../factory" } 22 | rendy-util = { path = "../util" } 23 | 24 | gfx-hal = { git = "https://github.com/gfx-rs/gfx" } 25 | 26 | failure = "0.1" 27 | serde = { version = "1.0", optional = true, features = ["derive"] } 28 | wavefront_obj = { version = "5.1", optional = true } 29 | smallvec = { version = "0.6" } 30 | -------------------------------------------------------------------------------- /mesh/README.md: -------------------------------------------------------------------------------- 1 | 2 | # `gfx-mesh` 3 | 4 | Helper crate for `gfx-hal` to create and use meshes with vertex semantics. 5 | 6 | # Vertex semantics 7 | 8 | Vertex formats usually has semantics attached to field names. 9 | This crate provides traits and types to have semantics explicitly defined on the type level. 10 | 11 | `Position`, `Normal`, `TexCoord` etc. are attributes that have unambiguous semantics. 12 | Users can define their own attribute types by implementing the `Attribute` trait. 13 | 14 | While the attribute type on its own is a trivial vertex format (with single attribute), complex vertex formats are created by composing attribute types. 15 | 16 | The `WithAttribute` trait allows to get formatting info for individual attributes defined in a vertex format. 17 | The `Query` trait allows to get formatting info for several attributes at once. 18 | 19 | `VertexFormat` queried from vertex formats can be used to build graphics pipelines and bind required vertex buffers from mesh to command buffer. 20 | 21 | To define a custom vertex format type, the `AsVertexFormat` trait must be implemented providing a `VertexFormat` associated constant. 22 | 23 | `WithAttribute` can be implemented also for all attributes and `VertexFormat` associated constant in `AsVertexFormat` can be defined more clearly utilizing `WithAttribute` implementation. 24 | `Query` is automatically implemented. 25 | 26 | # Mesh 27 | 28 | `Mesh` is a collection of vertex buffers and optionally an index buffer together with vertex formats of the buffers and index type. Also there is a primitive type specified which defines how vertices form primitives (lines, triangles etc). 29 | To create instances of `Mesh` you need to use `MeshBuilder`. 30 | 31 | 1. Fill `MeshBuilder` with typed vertex data. 32 | 1. Provide the index data. 33 | 1. Set the primitive type (Triangles list by default). 34 | 1. Call `MeshBuilder::build`. It uses `Factory` from `gfx-render` to create buffers and upload data. 35 | 36 | Here is your fresh new `Mesh`. Or an `Error` from `gfx-render`. 37 | 38 | To bind vertex buffers to a command buffer use `Mesh::bind` with a sorted array of `VertexFormat`s (the same that was used to setup the graphics pipeline). 39 | -------------------------------------------------------------------------------- /mesh/src/format.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "obj")] 2 | pub mod obj; 3 | -------------------------------------------------------------------------------- /mesh/src/format/obj.rs: -------------------------------------------------------------------------------- 1 | //! Loading mesh data from obj format. 2 | 3 | use { 4 | crate::{ 5 | vertex::{PosNormTex, Position, Normal, TexCoord}, 6 | mesh::MeshBuilder, 7 | }, 8 | wavefront_obj::obj, 9 | }; 10 | 11 | /// Load mesh data from obj. 12 | pub fn load_from_obj(bytes: Vec, _: ()) -> Result, failure::Error> { 13 | let string = String::from_utf8(bytes)?; 14 | let set = obj::parse(string) 15 | .map_err(|e| failure::format_err!("Error during parsing obj-file at line '{}': {}", e.line_number, e.message))?; 16 | let posnormtex = from_data(set); 17 | Ok( 18 | MeshBuilder::new() 19 | .with_vertices(posnormtex) 20 | ) 21 | } 22 | 23 | fn convert( 24 | object: &obj::Object, 25 | vi: obj::VertexIndex, 26 | ti: Option, 27 | ni: Option, 28 | ) -> PosNormTex { 29 | PosNormTex { 30 | position: { 31 | let vertex: obj::Vertex = object.vertices[vi]; 32 | Position([vertex.x as f32, vertex.y as f32, vertex.z as f32]) 33 | }, 34 | normal: ni 35 | .map(|i| { 36 | let normal: obj::Normal = object.normals[i]; 37 | Normal([normal.x as f32, normal.y as f32, normal.z as f32]) 38 | }) 39 | .unwrap_or(Normal([0.0, 0.0, 0.0])), 40 | tex_coord: ti 41 | .map(|i| { 42 | let tvertex: obj::TVertex = object.tex_vertices[i]; 43 | TexCoord([tvertex.u as f32, tvertex.v as f32]) 44 | }) 45 | .unwrap_or(TexCoord([0.0, 0.0])), 46 | } 47 | } 48 | 49 | fn convert_primitive(object: &obj::Object, prim: &obj::Primitive) -> Option<[PosNormTex; 3]> { 50 | match *prim { 51 | obj::Primitive::Triangle(v1, v2, v3) => Some([ 52 | convert(object, v1.0, v1.1, v1.2), 53 | convert(object, v2.0, v2.1, v2.2), 54 | convert(object, v3.0, v3.1, v3.2), 55 | ]), 56 | _ => None, 57 | } 58 | } 59 | 60 | fn from_data(obj_set: obj::ObjSet) -> Vec { 61 | // Takes a list of objects that contain geometries that contain shapes that contain 62 | // vertex/texture/normal indices into the main list of vertices, and converts to a 63 | // flat vec of `PosNormTex` objects. 64 | // TODO: Doesn't differentiate between objects in a `*.obj` file, treats 65 | // them all as a single mesh. 66 | let vertices = obj_set.objects.iter().flat_map(|object| { 67 | object.geometry.iter().flat_map(move |geometry| { 68 | geometry 69 | .shapes 70 | .iter() 71 | .filter_map(move |s| convert_primitive(object, &s.primitive)) 72 | }) 73 | }); 74 | 75 | let mut result = Vec::new(); 76 | for vvv in vertices { 77 | result.push(vvv[0]); 78 | result.push(vvv[1]); 79 | result.push(vvv[2]); 80 | } 81 | result 82 | } 83 | -------------------------------------------------------------------------------- /mesh/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! This crates provides means to deal with vertex buffers and meshes. 3 | //! 4 | //! `Attribute` and `VertexFormat` allow vertex structure to declare semantics. 5 | //! `Mesh` can be created from typed vertex structures and provides mechanism to bind 6 | //! vertex attributes required by shader interface. 7 | //! 8 | 9 | #![forbid(overflowing_literals)] 10 | #![deny(missing_copy_implementations)] 11 | #![deny(missing_debug_implementations)] 12 | #![deny(missing_docs)] 13 | #![deny(intra_doc_link_resolution_failure)] 14 | #![deny(path_statements)] 15 | #![deny(trivial_bounds)] 16 | #![deny(type_alias_bounds)] 17 | #![deny(unconditional_recursion)] 18 | #![deny(unions_with_drop_fields)] 19 | #![deny(while_true)] 20 | #![deny(unused)] 21 | #![deny(bad_style)] 22 | #![deny(future_incompatible)] 23 | #![deny(rust_2018_compatibility)] 24 | #![deny(rust_2018_idioms)] 25 | #![allow(unused_unsafe)] 26 | 27 | use rendy_factory as factory; 28 | use rendy_resource as resource; 29 | use rendy_util as util; 30 | 31 | mod format; 32 | mod mesh; 33 | mod vertex; 34 | 35 | pub use crate::{ 36 | mesh::{Bind, Incompatible, IndexBuffer, Indices, Mesh, MeshBuilder, VertexBuffer}, 37 | vertex::{ 38 | AsAttribute, AsVertex, Attribute, Color, Normal, PosColor, PosNorm, PosNormTangTex, PosNormTex, 39 | PosTex, Position, Query, Tangent, TexCoord, VertexFormat, WithAttribute, 40 | }, 41 | format::*, 42 | }; 43 | -------------------------------------------------------------------------------- /rendy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | 7 | [features] 8 | empty = ["rendy-factory/empty", "rendy-graph/empty", "rendy-wsi/empty", "gfx-backend-empty"] 9 | dx12 = ["rendy-factory/dx12", "rendy-graph/dx12", "rendy-wsi/dx12", "gfx-backend-dx12"] 10 | metal = ["rendy-factory/metal", "rendy-graph/metal", "rendy-wsi/metal", "gfx-backend-metal"] 11 | vulkan = ["rendy-factory/vulkan", "rendy-graph/vulkan", "rendy-wsi/vulkan", "gfx-backend-vulkan"] 12 | serde-1 = ["gfx-hal/serde", "rendy-factory/serde-1", "rendy-mesh/serde-1", "rendy-texture/serde-1"] 13 | 14 | command = ["rendy-command"] 15 | factory = ["rendy-factory", "command", "resource", "wsi"] 16 | frame = ["rendy-frame", "factory"] 17 | graph = ["rendy-graph", "frame"] 18 | memory = ["rendy-memory"] 19 | mesh = ["rendy-mesh", "factory", "util"] 20 | shader = ["rendy-shader", "factory"] 21 | resource = ["rendy-resource", "memory"] 22 | texture = ["rendy-texture", "factory", "util"] 23 | util = ["rendy-util"] 24 | wsi = ["rendy-wsi"] 25 | 26 | full = ["graph", "mesh", "texture", "shader"] 27 | default = ["full"] 28 | 29 | [dependencies] 30 | rendy-command = { path = "../command", optional = true } 31 | rendy-factory = { path = "../factory", optional = true } 32 | rendy-frame = { path = "../frame", optional = true } 33 | rendy-graph = { path = "../graph", optional = true } 34 | rendy-memory = { path = "../memory", optional = true } 35 | rendy-mesh = { path = "../mesh", optional = true } 36 | rendy-shader = { path = "../shader", optional = true } 37 | rendy-resource = { path = "../resource", optional = true } 38 | rendy-texture = { path = "../texture", optional = true } 39 | rendy-util = { path = "../util", optional = true } 40 | rendy-wsi = { path = "../wsi", optional = true } 41 | 42 | gfx-hal = { git = "https://github.com/gfx-rs/gfx" } 43 | gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", optional = true } 44 | gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx", optional = true } 45 | gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx", optional = true } 46 | gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", optional = true } 47 | 48 | [dev-dependencies] 49 | env_logger = "0.5" 50 | failure = "0.1" 51 | lazy_static = "1.0" 52 | log = "0.4" 53 | winit = "0.18" 54 | palette = "0.4" 55 | rand = "0.6" 56 | -------------------------------------------------------------------------------- /rendy/examples/init/main.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Basic example initializes core type of the rendy - `Factory` and exits. 3 | //! 4 | 5 | #![forbid(overflowing_literals)] 6 | #![deny(missing_copy_implementations)] 7 | #![deny(missing_debug_implementations)] 8 | #![deny(missing_docs)] 9 | #![deny(intra_doc_link_resolution_failure)] 10 | #![deny(path_statements)] 11 | #![deny(trivial_bounds)] 12 | #![deny(type_alias_bounds)] 13 | #![deny(unconditional_recursion)] 14 | #![deny(unions_with_drop_fields)] 15 | #![deny(while_true)] 16 | #![deny(unused)] 17 | #![deny(bad_style)] 18 | #![deny(future_incompatible)] 19 | #![deny(rust_2018_compatibility)] 20 | #![deny(rust_2018_idioms)] 21 | #![allow(unused_unsafe)] 22 | 23 | use rendy::{ 24 | factory::{Config, Factory}, 25 | }; 26 | 27 | #[cfg(feature = "dx12")] 28 | type Backend = rendy::dx12::Backend; 29 | 30 | #[cfg(feature = "metal")] 31 | type Backend = rendy::metal::Backend; 32 | 33 | #[cfg(feature = "vulkan")] 34 | type Backend = rendy::vulkan::Backend; 35 | 36 | #[cfg(any(feature = "dx12", feature = "metal", feature = "vulkan"))] 37 | fn main() { 38 | env_logger::Builder::from_default_env() 39 | .filter_level(log::LevelFilter::Info) 40 | .filter_module("init", log::LevelFilter::Trace) 41 | .init(); 42 | 43 | let config: Config = Default::default(); 44 | 45 | let factory: Factory = Factory::new(config).unwrap(); 46 | 47 | factory.dispose(); 48 | } 49 | 50 | #[cfg(not(any(feature = "dx12", feature = "metal", feature = "vulkan")))] 51 | fn main() { 52 | panic!("Specify feature: { dx12, metal, vulkan }"); 53 | } 54 | -------------------------------------------------------------------------------- /rendy/examples/quads/bounce.comp: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; 4 | // layout(set = 0, binding = 0, rgba32f) uniform imageBuffer posvelbuff; 5 | 6 | layout(std430, set = 0, binding = 0) buffer _ { 7 | vec4 posvel[]; 8 | } posvelbuff; 9 | 10 | void main() { 11 | float delta_time = 0.00001; 12 | int index = int(gl_GlobalInvocationID.x) + 1024 * int(gl_GlobalInvocationID.y); 13 | vec4 posvel = posvelbuff.posvel[index]; 14 | vec2 pos = posvel.rg; 15 | vec2 vel = posvel.ba; 16 | pos += vel + (delta_time * delta_time) / 2; 17 | vel.y -= delta_time; 18 | 19 | if (pos.y < 0.0) { 20 | pos.y = -pos.y; 21 | vel.y *= -1.0; 22 | } 23 | 24 | if (pos.y > 1.0) { 25 | pos.y = 2.0 - pos.y; 26 | vel.y *= -1.0; 27 | } 28 | 29 | if (pos.x < 0.0) { 30 | pos.x = -pos.x; 31 | vel.x *= -1.0; 32 | } 33 | 34 | if (pos.x > 1.0) { 35 | pos.x = 2.0 - pos.x; 36 | vel.x *= -1.0; 37 | } 38 | 39 | vel = clamp(vel, vec2(-0.02, -0.02), vec2(0.02, 0.02)); 40 | 41 | posvelbuff.posvel[index] = vec4(pos, vel); 42 | } 43 | -------------------------------------------------------------------------------- /rendy/examples/quads/render.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(early_fragment_tests) in; 5 | 6 | layout(location = 0) in vec4 frag_color; 7 | layout(location = 0) out vec4 color; 8 | 9 | void main() { 10 | color = frag_color; 11 | } 12 | -------------------------------------------------------------------------------- /rendy/examples/quads/render.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec4 color; 5 | layout(location = 0) out vec4 frag_color; 6 | 7 | layout(std430, set = 0, binding = 0) buffer _ { 8 | vec4 posvel[]; 9 | } posvelbuff; 10 | 11 | vec2 vertices[6] = { 12 | vec2(0.00, 0.00), 13 | vec2(0.00, 0.01), 14 | vec2(0.01, 0.01), 15 | vec2(0.00, 0.00), 16 | vec2(0.01, 0.01), 17 | vec2(0.01, 0.00), 18 | }; 19 | 20 | void main() { 21 | int index = gl_InstanceIndex; 22 | vec4 posvel = posvelbuff.posvel[index]; 23 | vec2 pos = posvel.rg; 24 | vec2 vertex = vertices[gl_VertexIndex]; 25 | 26 | vec2 v = ((vertex + pos / 1.01) * 2.0) - vec2(1.0, 1.0); 27 | v.y = -v.y; 28 | 29 | frag_color = vec4(color.rgb, 1.0); 30 | gl_Position = vec4(v, 0.0, 1.0); 31 | } 32 | -------------------------------------------------------------------------------- /rendy/examples/triangle/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(early_fragment_tests) in; 5 | 6 | layout(location = 0) in vec4 frag_color; 7 | layout(location = 0) out vec4 color; 8 | 9 | void main() { 10 | color = frag_color; 11 | } 12 | -------------------------------------------------------------------------------- /rendy/examples/triangle/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec3 pos; 5 | layout(location = 1) in vec4 color; 6 | layout(location = 0) out vec4 frag_color; 7 | 8 | void main() { 9 | frag_color = color; 10 | gl_Position = vec4(pos, 1.0); 11 | } 12 | -------------------------------------------------------------------------------- /rendy/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | //! Rendy's top level crate. 3 | //! Reexports all others. 4 | 5 | #![forbid(overflowing_literals)] 6 | #![deny(missing_copy_implementations)] 7 | #![deny(missing_debug_implementations)] 8 | #![deny(missing_docs)] 9 | #![deny(intra_doc_link_resolution_failure)] 10 | #![deny(path_statements)] 11 | #![deny(trivial_bounds)] 12 | #![deny(type_alias_bounds)] 13 | #![deny(unconditional_recursion)] 14 | #![deny(unions_with_drop_fields)] 15 | #![deny(while_true)] 16 | #![deny(unused)] 17 | #![deny(bad_style)] 18 | #![deny(future_incompatible)] 19 | #![deny(rust_2018_compatibility)] 20 | #![deny(rust_2018_idioms)] 21 | #![allow(unused_unsafe)] 22 | 23 | #[cfg(feature = "command")] 24 | #[doc(inline)] 25 | pub use rendy_command as command; 26 | 27 | #[cfg(feature = "factory")] 28 | #[doc(inline)] 29 | pub use rendy_factory as factory; 30 | 31 | #[cfg(feature = "frame")] 32 | #[doc(inline)] 33 | pub use rendy_frame as frame; 34 | 35 | #[cfg(feature = "graph")] 36 | #[doc(inline)] 37 | pub use rendy_graph as graph; 38 | 39 | #[cfg(feature = "memory")] 40 | #[doc(inline)] 41 | pub use rendy_memory as memory; 42 | 43 | #[cfg(feature = "mesh")] 44 | #[doc(inline)] 45 | pub use rendy_mesh as mesh; 46 | 47 | #[cfg(feature = "resource")] 48 | #[doc(inline)] 49 | pub use rendy_resource as resource; 50 | 51 | #[cfg(feature = "shader")] 52 | #[doc(inline)] 53 | pub use rendy_shader as shader; 54 | 55 | #[cfg(feature = "util")] 56 | #[doc(inline)] 57 | pub use rendy_util as util; 58 | 59 | #[cfg(feature = "wsi")] 60 | #[doc(inline)] 61 | pub use rendy_wsi as wsi; 62 | 63 | 64 | pub use gfx_hal as hal; 65 | 66 | #[cfg(feature = "gfx-backend-empty")] 67 | pub use gfx_backend_empty as empty; 68 | 69 | #[cfg(feature = "gfx-backend-dx12")] 70 | pub use gfx_backend_dx12 as dx12; 71 | 72 | #[cfg(feature = "gfx-backend-metal")] 73 | pub use gfx_backend_metal as metal; 74 | 75 | #[cfg(feature = "gfx-backend-vulkan")] 76 | pub use gfx_backend_vulkan as vulkan; 77 | 78 | -------------------------------------------------------------------------------- /resource/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-resource" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | gfx-hal = { git = "https://github.com/gfx-rs/gfx" } 9 | crossbeam-channel = "0.2" 10 | derivative = "1.0" 11 | failure = "0.1" 12 | log = "0.4" 13 | relevant = "0.2" 14 | rendy-memory = { path = "../memory" } 15 | -------------------------------------------------------------------------------- /resource/src/buffer/mod.rs: -------------------------------------------------------------------------------- 1 | //! Buffer usage, creation-info and wrappers. 2 | 3 | mod usage; 4 | 5 | pub use self::usage::*; 6 | 7 | use crate::{ 8 | escape::{Escape, KeepAlive}, 9 | memory::{Block, MemoryBlock}, 10 | }; 11 | 12 | /// Buffer info. 13 | #[derive(Clone, Copy, Debug)] 14 | pub struct Info { 15 | /// Buffer size. 16 | pub size: u64, 17 | 18 | /// Buffer usage flags. 19 | pub usage: gfx_hal::buffer::Usage, 20 | } 21 | 22 | /// Generic buffer object wrapper. 23 | /// 24 | /// # Parameters 25 | /// 26 | /// `T` - type of the memory object of memory block. 27 | /// `B` - raw buffer type. 28 | #[derive(Debug)] 29 | pub struct Buffer { 30 | pub(crate) escape: Escape>, 31 | pub(crate) info: Info, 32 | } 33 | 34 | #[derive(Debug)] 35 | pub(crate) struct Inner { 36 | pub(crate) block: MemoryBlock, 37 | pub(crate) raw: B::Buffer, 38 | pub(crate) relevant: relevant::Relevant, 39 | } 40 | 41 | impl Buffer 42 | where 43 | B: gfx_hal::Backend, 44 | { 45 | /// Creates [`KeepAlive`] handler to extend buffer lifetime. 46 | /// 47 | /// [`KeepAlive`]: struct.KeepAlive.html 48 | pub fn keep_alive(&self) -> KeepAlive { 49 | Escape::keep_alive(&self.escape) 50 | } 51 | 52 | /// Get buffers memory [`Block`]. 53 | /// 54 | /// [`Block`]: ../memory/trait.Block.html 55 | pub fn block(&self) -> &impl Block { 56 | &self.escape.block 57 | } 58 | 59 | /// Get buffers memory [`Block`]. 60 | /// 61 | /// [`Block`]: ../memory/trait.Block.html 62 | pub fn block_mut(&mut self) -> &mut impl Block { 63 | &mut self.escape.block 64 | } 65 | 66 | /// Get raw buffer handle. 67 | /// 68 | /// # Safety 69 | /// 70 | /// Raw buffer handler should not be usage to violate this object valid usage. 71 | pub fn raw(&self) -> &B::Buffer { 72 | &self.escape.raw 73 | } 74 | 75 | /// Get buffer info. 76 | pub fn info(&self) -> &Info { 77 | &self.info 78 | } 79 | 80 | /// Get buffer info. 81 | pub fn size(&self) -> u64 { 82 | self.info.size 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /resource/src/buffer/usage.rs: -------------------------------------------------------------------------------- 1 | 2 | use crate::memory::{Data, Download, Dynamic, MemoryUsage, Upload}; 3 | 4 | /// Usage trait that must implemented by usage types. 5 | /// This trait provides a way to convert type-level usage to the value-level flags. 6 | pub trait Usage: std::fmt::Debug { 7 | /// Suggested memory usage type. 8 | type MemoryUsage: MemoryUsage; 9 | 10 | /// Convert usage to the flags. 11 | fn flags(&self) -> gfx_hal::buffer::Usage; 12 | 13 | /// Get suggested memory usage. 14 | fn memory(&self) -> Self::MemoryUsage; 15 | } 16 | 17 | impl Usage for (gfx_hal::buffer::Usage, M) 18 | where 19 | M: MemoryUsage, 20 | { 21 | type MemoryUsage = M; 22 | 23 | fn flags(&self) -> gfx_hal::buffer::Usage { 24 | self.0 25 | } 26 | 27 | fn memory(&self) -> M { 28 | self.1 29 | } 30 | } 31 | 32 | /// Type that specify that buffer is intended to be used as vertex buffer. 33 | /// It implies `TRANSFER_DST` because device-local, host-invisible memory should be used 34 | /// and transfer is left the only way to fill the buffer. 35 | #[derive(Clone, Copy, Debug)] 36 | pub struct VertexBuffer; 37 | 38 | impl Usage for VertexBuffer { 39 | type MemoryUsage = Data; 40 | 41 | fn flags(&self) -> gfx_hal::buffer::Usage { 42 | gfx_hal::buffer::Usage::TRANSFER_DST | gfx_hal::buffer::Usage::VERTEX 43 | } 44 | 45 | fn memory(&self) -> Data { 46 | Data 47 | } 48 | } 49 | 50 | /// Type that specify that buffer is intended to be used as index buffer. 51 | /// It implies `TRANSFER_DST` because device-local, host-invisible memory should be used 52 | /// and transfer is left the only way to fill the buffer. 53 | #[derive(Clone, Copy, Debug)] 54 | pub struct IndexBuffer; 55 | 56 | impl Usage for IndexBuffer { 57 | type MemoryUsage = Data; 58 | 59 | fn flags(&self) -> gfx_hal::buffer::Usage { 60 | gfx_hal::buffer::Usage::TRANSFER_DST | gfx_hal::buffer::Usage::INDEX 61 | } 62 | 63 | fn memory(&self) -> Data { 64 | Data 65 | } 66 | } 67 | 68 | /// Type that specify that buffer is intended to be used as uniform buffer. 69 | /// Host visible memory required and device-local preferred. 70 | #[derive(Clone, Copy, Debug)] 71 | pub struct UniformBuffer; 72 | 73 | impl Usage for UniformBuffer { 74 | type MemoryUsage = Dynamic; 75 | 76 | fn flags(&self) -> gfx_hal::buffer::Usage { 77 | gfx_hal::buffer::Usage::UNIFORM 78 | } 79 | 80 | fn memory(&self) -> Dynamic { 81 | Dynamic 82 | } 83 | } 84 | 85 | /// Type that specify that buffer is intended to be used as staging buffer for data uploads. 86 | #[derive(Clone, Copy, Debug)] 87 | pub struct UploadBuffer; 88 | 89 | impl Usage for UploadBuffer { 90 | type MemoryUsage = Upload; 91 | 92 | fn flags(&self) -> gfx_hal::buffer::Usage { 93 | gfx_hal::buffer::Usage::TRANSFER_SRC 94 | } 95 | 96 | fn memory(&self) -> Upload { 97 | Upload 98 | } 99 | } 100 | 101 | /// Type that specify that buffer is intended to be used as staging buffer for data downloads. 102 | #[derive(Clone, Copy, Debug)] 103 | pub struct DownloadBuffer; 104 | 105 | impl Usage for DownloadBuffer { 106 | type MemoryUsage = Download; 107 | 108 | fn flags(&self) -> gfx_hal::buffer::Usage { 109 | gfx_hal::buffer::Usage::TRANSFER_DST 110 | } 111 | 112 | fn memory(&self) -> Download { 113 | Download 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /resource/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 std::{ 7 | iter::repeat, 8 | mem::{forget, ManuallyDrop}, 9 | ops::{Deref, DerefMut}, 10 | ptr::read, 11 | }; 12 | 13 | #[derive(Debug)] 14 | struct Inner { 15 | value: ManuallyDrop>, 16 | sender: crossbeam_channel::Sender, 17 | } 18 | 19 | unsafe impl Send for Inner {} 20 | unsafe impl Sync for Inner {} 21 | 22 | impl Inner { 23 | /// Unwrap the value. 24 | 25 | fn into_inner(self) -> T { 26 | self.deconstruct().0 27 | } 28 | 29 | fn deconstruct(mut self) -> (T, crossbeam_channel::Sender) { 30 | unsafe { 31 | let value = read(&mut *self.value); 32 | let sender = read(&mut self.sender); 33 | forget(self); 34 | (value.into_inner(), sender) 35 | } 36 | } 37 | } 38 | 39 | impl Drop for Inner { 40 | fn drop(&mut self) { 41 | let value = unsafe { 42 | // `self.value` cannot be accessed after this function. 43 | // `ManuallyDrop` will prevent `self.value` from dropping. 44 | read(&mut *self.value) 45 | }; 46 | self.sender.send(value.into_inner()) 47 | } 48 | } 49 | 50 | /// Values of `KeepAlive` keeps resources from destroying. 51 | /// 52 | /// # Example 53 | /// 54 | /// ``` 55 | /// # extern crate rendy_resource; 56 | /// # use rendy_resource::*; 57 | /// 58 | /// fn foo(buffer: Buffer) { 59 | /// let kp: KeepAlive = buffer.keep_alive(); 60 | /// 61 | /// // `kp` keeps this buffer from being destroyed. 62 | /// // It still can be referenced by command buffer on used by GPU. 63 | /// drop(buffer); 64 | /// 65 | /// // If there is no `KeepAlive` instances created from this buffer 66 | /// // then it can be destrouyed after this line. 67 | /// drop(kp); 68 | /// } 69 | /// ``` 70 | #[derive(Clone, derivative::Derivative)] 71 | #[derivative(Debug)] 72 | pub struct KeepAlive(#[derivative(Debug = "ignore")] std::sync::Arc); 73 | 74 | /// Wraps value of any type and send it to the `Terminal` from which the wrapper was created. 75 | /// In case `Terminal` is already dropped then value will be cast into oblivion via `std::mem::forget`. 76 | #[derive(Debug)] 77 | pub struct Escape { 78 | inner: std::sync::Arc>, 79 | } 80 | 81 | impl Escape { 82 | pub fn keep_alive(escape: &Self) -> KeepAlive 83 | where 84 | T: Send + Sync + 'static, 85 | { 86 | KeepAlive(escape.inner.clone() as _) 87 | } 88 | 89 | /// Try to avoid channel sending if resource is not references elsewhere. 90 | pub fn dispose(escape: Self) -> Option { 91 | std::sync::Arc::try_unwrap(escape.inner) 92 | .ok() 93 | .map(Inner::into_inner) 94 | } 95 | } 96 | 97 | impl Deref for Escape { 98 | type Target = T; 99 | fn deref(&self) -> &T { 100 | unsafe { 101 | // Only `Escape` has access to `T`. 102 | // `KeepAlive` doesn't access `T`. 103 | // `Inner` only access `T` when dropped. 104 | &*self.inner.value.get() 105 | } 106 | } 107 | } 108 | 109 | impl DerefMut for Escape { 110 | fn deref_mut(&mut self) -> &mut T { 111 | unsafe { 112 | // Only `Escape` has access to `T`. 113 | // `KeepAlive` doesn't access `T`. 114 | // `Inner` only access `T` when dropped. 115 | &mut *self.inner.value.get() 116 | } 117 | } 118 | } 119 | 120 | /// This types allows the user to create `Escape` wrappers. 121 | /// Receives values from dropped `Escape` instances that was created by this `Terminal`. 122 | #[derive(Debug)] 123 | pub struct Terminal { 124 | receiver: crossbeam_channel::Receiver, 125 | sender: ManuallyDrop>, 126 | } 127 | 128 | impl Default for Terminal { 129 | fn default() -> Self { 130 | Self::new() 131 | } 132 | } 133 | 134 | impl Terminal { 135 | /// Create new `Terminal`. 136 | pub fn new() -> Self { 137 | let (sender, receiver) = crossbeam_channel::unbounded(); 138 | Terminal { 139 | sender: ManuallyDrop::new(sender), 140 | receiver, 141 | } 142 | } 143 | 144 | /// Wrap the value. It will be yielded by iterator returned by `Terminal::drain` if `Escape` will be dropped. 145 | pub fn escape(&self, value: T) -> Escape { 146 | let inner = std::sync::Arc::new(Inner { 147 | value: ManuallyDrop::new(value.into()), 148 | sender: crossbeam_channel::Sender::clone(&self.sender), 149 | }); 150 | 151 | Escape { 152 | inner, 153 | } 154 | } 155 | 156 | // /// Check if `Escape` will send value to this `Terminal`. 157 | // pub fn owns(&self, escape: &Escape) -> bool { 158 | // *self.sender == escape.sender 159 | // } 160 | 161 | /// Get iterator over values from dropped `Escape` instances that was created by this `Terminal`. 162 | pub fn drain<'a>(&'a mut self) -> impl Iterator + 'a { 163 | repeat(()).scan((), move |&mut (), ()| { 164 | // trace!("Drain escape"); 165 | if !self.receiver.is_empty() { 166 | self.receiver.recv() 167 | } else { 168 | None 169 | } 170 | }) 171 | } 172 | } 173 | 174 | impl Drop for Terminal { 175 | fn drop(&mut self) { 176 | unsafe { 177 | ManuallyDrop::drop(&mut self.sender); 178 | match self.receiver.recv() { 179 | None => {} 180 | Some(_) => { 181 | error!("Terminal must be dropped after all `Escape`s"); 182 | } 183 | } 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /resource/src/image/mod.rs: -------------------------------------------------------------------------------- 1 | //! Image usage, format, kind, extent, creation-info and wrappers. 2 | 3 | mod usage; 4 | 5 | pub use self::usage::*; 6 | 7 | use crate::{ 8 | escape::{Escape, KeepAlive}, 9 | memory::{Block, MemoryBlock}, 10 | }; 11 | 12 | /// Image info. 13 | #[derive(Clone, Copy, Debug)] 14 | pub struct Info { 15 | /// Kind of the image. 16 | pub kind: gfx_hal::image::Kind, 17 | 18 | /// Image mip-level count. 19 | pub levels: gfx_hal::image::Level, 20 | 21 | /// Image format. 22 | pub format: gfx_hal::format::Format, 23 | 24 | /// Image tiling mode. 25 | pub tiling: gfx_hal::image::Tiling, 26 | 27 | /// Image view capabilities. 28 | pub view_caps: gfx_hal::image::ViewCapabilities, 29 | 30 | /// Image usage flags. 31 | pub usage: gfx_hal::image::Usage, 32 | } 33 | 34 | /// Generic image object wrapper. 35 | /// 36 | /// # Parameters 37 | /// 38 | /// `T` - type of the memory object of memory block. 39 | /// `B` - raw image type. 40 | #[derive(Debug)] 41 | pub struct Image { 42 | pub(super) escape: Escape>, 43 | pub(super) info: Info, 44 | } 45 | 46 | #[derive(Debug)] 47 | pub(super) struct Inner { 48 | pub(super) block: MemoryBlock, 49 | pub(super) raw: B::Image, 50 | pub(super) relevant: relevant::Relevant, 51 | } 52 | 53 | impl Image 54 | where 55 | B: gfx_hal::Backend, 56 | { 57 | /// Creates [`KeepAlive`] handler to extend image lifetime. 58 | /// 59 | /// [`KeepAlive`]: struct.KeepAlive.html 60 | pub fn keep_alive(&self) -> KeepAlive { 61 | Escape::keep_alive(&self.escape) 62 | } 63 | 64 | /// Get images memory [`Block`]. 65 | /// 66 | /// [`Block`]: ../memory/trait.Block.html 67 | pub fn block(&self) -> &impl Block { 68 | &self.escape.block 69 | } 70 | 71 | /// Get images memory [`Block`]. 72 | /// 73 | /// [`Block`]: ../memory/trait.Block.html 74 | pub fn block_mut(&mut self) -> &mut impl Block { 75 | &mut self.escape.block 76 | } 77 | 78 | /// Get raw image handle. 79 | /// 80 | /// # Safety 81 | /// 82 | /// Raw image handler should not be usage to violate this object valid usage. 83 | pub fn raw(&self) -> &B::Image { 84 | &self.escape.raw 85 | } 86 | 87 | /// Get image [`Info`]. 88 | /// 89 | /// [`Info`]: struct.Info.html 90 | pub fn info(&self) -> Info { 91 | self.info 92 | } 93 | 94 | /// Get [`Kind`] of the image. 95 | /// 96 | /// [`Kind`]: ../gfx-hal/image/struct.Kind.html 97 | pub fn kind(&self) -> gfx_hal::image::Kind { 98 | self.info.kind 99 | } 100 | 101 | /// Get [`Format`] of the image. 102 | /// 103 | /// [`Format`]: ../gfx-hal/format/struct.Format.html 104 | pub fn format(&self) -> gfx_hal::format::Format { 105 | self.info.format 106 | } 107 | 108 | /// Get levels count of the image. 109 | pub fn levels(&self) -> u8 { 110 | self.info.levels 111 | } 112 | 113 | /// Get layers count of the image. 114 | pub fn layers(&self) -> u16 { 115 | self.info.kind.num_layers() 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /resource/src/image/usage.rs: -------------------------------------------------------------------------------- 1 | use crate::memory::{Data, MemoryUsage}; 2 | 3 | /// Usage trait that must implemented by usage types. 4 | /// This trait provides a way to convert type-level usage to the value-level flags. 5 | pub trait Usage: std::fmt::Debug { 6 | /// Suggested memory usage type. 7 | type MemoryUsage: MemoryUsage; 8 | 9 | /// Convert usage to the flags. 10 | fn flags(&self) -> gfx_hal::image::Usage; 11 | 12 | /// Get suggested memory usage. 13 | fn memory(&self) -> Self::MemoryUsage; 14 | } 15 | 16 | impl Usage for (gfx_hal::image::Usage, M) 17 | where 18 | M: MemoryUsage, 19 | { 20 | type MemoryUsage = M; 21 | 22 | fn flags(&self) -> gfx_hal::image::Usage { 23 | self.0 24 | } 25 | 26 | fn memory(&self) -> M { 27 | self.1 28 | } 29 | } 30 | 31 | /// Type that specify that image is intended to be used as texture. 32 | /// It implies `TRANSFER_DST` because device-local, host-invisible memory should be used 33 | /// and transfer is left the only way to fill the buffer. 34 | #[derive(Clone, Copy, Debug)] 35 | pub struct Texture; 36 | 37 | impl Usage for Texture { 38 | type MemoryUsage = Data; 39 | 40 | fn flags(&self) -> gfx_hal::image::Usage { 41 | gfx_hal::image::Usage::TRANSFER_DST | gfx_hal::image::Usage::SAMPLED 42 | } 43 | 44 | fn memory(&self) -> Data { 45 | Data 46 | } 47 | } 48 | 49 | /// Type that specify that image is intended to be used as render target and storage image. 50 | #[derive(Clone, Copy, Debug)] 51 | pub struct RenderTargetStorage; 52 | 53 | impl Usage for RenderTargetStorage { 54 | type MemoryUsage = Data; 55 | 56 | fn flags(&self) -> gfx_hal::image::Usage { 57 | gfx_hal::image::Usage::COLOR_ATTACHMENT | gfx_hal::image::Usage::STORAGE 58 | } 59 | 60 | fn memory(&self) -> Data { 61 | Data 62 | } 63 | } 64 | 65 | /// Type that specify that image is intended to be used as render target and sampled image. 66 | #[derive(Clone, Copy, Debug)] 67 | pub struct RenderTargetSampled; 68 | 69 | impl Usage for RenderTargetSampled { 70 | type MemoryUsage = Data; 71 | 72 | fn flags(&self) -> gfx_hal::image::Usage { 73 | gfx_hal::image::Usage::COLOR_ATTACHMENT | gfx_hal::image::Usage::SAMPLED 74 | } 75 | 76 | fn memory(&self) -> Data { 77 | Data 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /resource/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provide methods to create/destroy and otherwise manage device resources. 2 | //! Primarily focus on buffers and images. 3 | 4 | #![forbid(overflowing_literals)] 5 | #![deny(missing_copy_implementations)] 6 | #![deny(missing_debug_implementations)] 7 | #![deny(missing_docs)] 8 | #![deny(intra_doc_link_resolution_failure)] 9 | #![deny(path_statements)] 10 | #![deny(trivial_bounds)] 11 | #![deny(type_alias_bounds)] 12 | #![deny(unconditional_recursion)] 13 | #![deny(unions_with_drop_fields)] 14 | #![deny(while_true)] 15 | #![deny(unused)] 16 | #![deny(bad_style)] 17 | #![deny(future_incompatible)] 18 | #![deny(rust_2018_compatibility)] 19 | #![deny(rust_2018_idioms)] 20 | #![allow(unused_unsafe)] 21 | 22 | #[macro_use] extern crate derivative; 23 | #[macro_use] extern crate log; 24 | use rendy_memory as memory; 25 | 26 | mod escape; 27 | mod resources; 28 | 29 | pub mod buffer; 30 | pub mod image; 31 | 32 | pub use crate::{ 33 | escape::KeepAlive, 34 | buffer::Buffer, 35 | image::Image, 36 | resources::Resources, 37 | }; 38 | -------------------------------------------------------------------------------- /resource/src/resources.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::max; 2 | 3 | use crate::{ 4 | buffer, 5 | escape::{Escape, Terminal}, 6 | image, 7 | memory::{Block, Heaps}, 8 | }; 9 | 10 | /// Resource manager. 11 | /// It can be used to create and destroy resources such as buffers and images. 12 | #[derive(Debug, Derivative)] 13 | #[derivative(Default(bound = ""))] 14 | pub struct Resources { 15 | buffers: Terminal>, 16 | images: Terminal>, 17 | 18 | dropped_buffers: Vec>, 19 | dropped_images: Vec>, 20 | } 21 | 22 | impl Resources 23 | where 24 | B: gfx_hal::Backend, 25 | { 26 | /// Create new `Resources` instance. 27 | pub fn new() -> Self { 28 | Default::default() 29 | } 30 | 31 | /// Create a buffer and bind to the memory that support intended usage. 32 | pub fn create_buffer( 33 | &self, 34 | device: &impl gfx_hal::Device, 35 | heaps: &mut Heaps, 36 | align: u64, 37 | size: u64, 38 | usage: impl buffer::Usage, 39 | ) -> Result, failure::Error> { 40 | #[derive(Debug)] struct CreateBuffer<'a> { 41 | align: &'a dyn std::fmt::Debug, 42 | size: &'a dyn std::fmt::Debug, 43 | usage: &'a dyn std::fmt::Debug, 44 | }; 45 | log::trace!("{:#?}", CreateBuffer { 46 | align: &align, 47 | size: &size, 48 | usage: &usage, 49 | }); 50 | 51 | let buf = unsafe { 52 | device.create_buffer(size, usage.flags()) 53 | }?; 54 | let reqs = unsafe { 55 | device.get_buffer_requirements(&buf) 56 | }; 57 | let block = heaps.allocate( 58 | device, 59 | reqs.type_mask as u32, 60 | usage.memory(), 61 | reqs.size, 62 | max(reqs.alignment, align), 63 | )?; 64 | 65 | let buf = unsafe { 66 | device.bind_buffer_memory(block.memory(), block.range().start, buf) 67 | }?; 68 | 69 | Ok(buffer::Buffer { 70 | escape: self.buffers.escape(buffer::Inner { 71 | raw: buf, 72 | block, 73 | relevant: relevant::Relevant, 74 | }), 75 | info: buffer::Info { 76 | size, 77 | usage: usage.flags(), 78 | } 79 | }) 80 | } 81 | 82 | /// Destroy buffer. 83 | /// Buffer can be dropped but this method reduces overhead. 84 | pub fn destroy_buffer(&mut self, buffer: buffer::Buffer) { 85 | Escape::dispose(buffer.escape) 86 | .map(|inner| self.dropped_buffers.push(inner)); 87 | } 88 | 89 | /// Drop inner buffer representation. 90 | /// 91 | /// # Safety 92 | /// 93 | /// Device must not attempt to use the buffer. 94 | unsafe fn actually_destroy_buffer( 95 | inner: buffer::Inner, 96 | device: &impl gfx_hal::Device, 97 | heaps: &mut Heaps, 98 | ) { 99 | device.destroy_buffer(inner.raw); 100 | heaps.free(device, inner.block); 101 | inner.relevant.dispose(); 102 | } 103 | 104 | /// Create an image and bind to the memory that support intended usage. 105 | pub fn create_image( 106 | &self, 107 | device: &impl gfx_hal::Device, 108 | heaps: &mut Heaps, 109 | align: u64, 110 | kind: gfx_hal::image::Kind, 111 | levels: gfx_hal::image::Level, 112 | format: gfx_hal::format::Format, 113 | tiling: gfx_hal::image::Tiling, 114 | view_caps: gfx_hal::image::ViewCapabilities, 115 | usage: impl image::Usage, 116 | ) -> Result, failure::Error> { 117 | #[derive(Debug)] struct CreateImage<'a> { 118 | align: &'a dyn std::fmt::Debug, 119 | kind: &'a dyn std::fmt::Debug, 120 | levels: &'a dyn std::fmt::Debug, 121 | format: &'a dyn std::fmt::Debug, 122 | tiling: &'a dyn std::fmt::Debug, 123 | view_caps: &'a dyn std::fmt::Debug, 124 | usage: &'a dyn std::fmt::Debug, 125 | }; 126 | log::trace!("{:#?}", CreateImage { 127 | align: &align, 128 | kind: &kind, 129 | levels: &levels, 130 | format: &format, 131 | tiling: &tiling, 132 | view_caps: &view_caps, 133 | usage: &usage, 134 | }); 135 | 136 | let img = unsafe { 137 | device.create_image( 138 | kind, 139 | levels, 140 | format, 141 | tiling, 142 | usage.flags(), 143 | view_caps, 144 | ) 145 | }?; 146 | let reqs = unsafe { 147 | device.get_image_requirements(&img) 148 | }; 149 | let block = heaps.allocate( 150 | device, 151 | reqs.type_mask as u32, 152 | usage.memory(), 153 | reqs.size, 154 | max(reqs.alignment, align), 155 | )?; 156 | 157 | let img = unsafe { 158 | device 159 | .bind_image_memory(block.memory(), block.range().start, img) 160 | }?; 161 | 162 | Ok(image::Image { 163 | escape: self.images.escape(image::Inner { 164 | raw: img, 165 | block, 166 | relevant: relevant::Relevant, 167 | }), 168 | info: image::Info { 169 | kind, 170 | levels, 171 | format, 172 | tiling, 173 | view_caps, 174 | usage: usage.flags(), 175 | }, 176 | }) 177 | } 178 | 179 | /// Destroy image. 180 | /// Image can be dropped but this method reduces overhead. 181 | pub fn destroy_image( 182 | &mut self, 183 | image: image::Image, 184 | ) { 185 | Escape::dispose(image.escape) 186 | .map(|inner| self.dropped_images.push(inner)); 187 | } 188 | 189 | /// Drop inner image representation. 190 | /// 191 | /// # Safety 192 | /// 193 | /// Device must not attempt to use the image. 194 | unsafe fn actually_destroy_image( 195 | inner: image::Inner, 196 | device: &impl gfx_hal::Device, 197 | heaps: &mut Heaps, 198 | ) { 199 | device.destroy_image(inner.raw); 200 | heaps.free(device, inner.block); 201 | inner.relevant.dispose(); 202 | } 203 | 204 | /// Recycle dropped resources. 205 | /// 206 | /// # Safety 207 | /// 208 | /// Device must not attempt to use previously dropped buffers and images. 209 | pub unsafe fn cleanup(&mut self, device: &impl gfx_hal::Device, heaps: &mut Heaps) { 210 | log::trace!("Cleanup resources"); 211 | for buffer in self.dropped_buffers.drain(..) { 212 | Self::actually_destroy_buffer(buffer, device, heaps); 213 | } 214 | 215 | for image in self.dropped_images.drain(..) { 216 | Self::actually_destroy_image(image, device, heaps); 217 | } 218 | 219 | self.dropped_buffers.extend(self.buffers.drain()); 220 | self.dropped_images.extend(self.images.drain()); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rustgd/rendy/ff05ccb646704ff7de82a0ec1442c3e215ab36e9/rustfmt.toml -------------------------------------------------------------------------------- /shader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-shader" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | failure = "0.1" 9 | gfx-hal = { git = "https://github.com/gfx-rs/gfx" } 10 | rendy-factory = { version = "0.1", path = "../factory" } 11 | shaderc = "0.3" 12 | -------------------------------------------------------------------------------- /shader/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | //! Shader compilation. 3 | 4 | #![forbid(overflowing_literals)] 5 | #![deny(missing_copy_implementations)] 6 | #![deny(missing_debug_implementations)] 7 | #![deny(missing_docs)] 8 | #![deny(intra_doc_link_resolution_failure)] 9 | #![deny(path_statements)] 10 | #![deny(trivial_bounds)] 11 | #![deny(type_alias_bounds)] 12 | #![deny(unconditional_recursion)] 13 | #![deny(unions_with_drop_fields)] 14 | #![deny(while_true)] 15 | #![deny(unused)] 16 | #![deny(bad_style)] 17 | #![deny(future_incompatible)] 18 | #![deny(rust_2018_compatibility)] 19 | #![deny(rust_2018_idioms)] 20 | #![allow(unused_unsafe)] 21 | 22 | pub use shaderc::{self, ShaderKind, SourceLanguage}; 23 | 24 | macro_rules! vk_make_version { 25 | ($major: expr, $minor: expr, $patch: expr) => ((($major as u32) << 22) | (($minor as u32) << 12) | $patch as u32) 26 | } 27 | 28 | /// Interface to create shader modules from shaders. 29 | /// Implemented for static shaders via [`compile_to_spirv!`] macro. 30 | /// 31 | pub trait Shader { 32 | /// Get spirv bytecode. 33 | fn spirv(&self) -> Result, failure::Error>; 34 | 35 | /// Create shader module. 36 | fn module(&self, factory: &rendy_factory::Factory) -> Result 37 | where 38 | B: gfx_hal::Backend, 39 | { 40 | gfx_hal::Device::create_shader_module(factory.device(), &self.spirv()?) 41 | .map_err(Into::into) 42 | } 43 | } 44 | 45 | /// Dynamic shader. 46 | #[derive(Clone, Copy, Debug)] 47 | pub struct ShaderInfo { 48 | path: P, 49 | kind: ShaderKind, 50 | lang: SourceLanguage, 51 | entry: E, 52 | } 53 | 54 | impl ShaderInfo { 55 | 56 | /// New dynamic shader. 57 | pub fn new(path: P, kind: ShaderKind, lang: SourceLanguage, entry: E) -> Self { 58 | ShaderInfo { 59 | path: path.into(), 60 | kind, 61 | lang, 62 | entry: entry.into(), 63 | } 64 | } 65 | } 66 | 67 | impl Shader for ShaderInfo 68 | where 69 | P: AsRef + std::fmt::Debug, 70 | E: AsRef, 71 | { 72 | fn spirv(&self) -> Result, failure::Error> { 73 | let code = std::fs::read_to_string(&self.path)?; 74 | 75 | let artifact = shaderc::Compiler::new() 76 | .ok_or_else(|| failure::format_err!("Failed to init Shaderc"))? 77 | .compile_into_spirv( 78 | &code, 79 | self.kind, 80 | self.path.as_ref().to_str().ok_or_else(|| failure::format_err!("'{:?}' is not valid UTF-8 string", self.path))?, 81 | self.entry.as_ref(), 82 | Some({ 83 | let mut ops = shaderc::CompileOptions::new().ok_or_else(|| failure::format_err!("Failed to init Shaderc"))?; 84 | ops.set_target_env(shaderc::TargetEnv::Vulkan, vk_make_version!(1, 0, 0)); 85 | ops.set_source_language(self.lang); 86 | ops.set_optimization_level(shaderc::OptimizationLevel::Performance); 87 | ops 88 | }).as_ref(), 89 | )?; 90 | 91 | Ok(std::borrow::Cow::Owned(artifact.as_binary_u8().into())) 92 | } 93 | } 94 | 95 | /// Shader info with static data. 96 | pub type StaticShaderInfo = ShaderInfo<&'static str, &'static str>; 97 | -------------------------------------------------------------------------------- /texture/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-texture" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | description = "Crate for rendy to create and use textures" 6 | keywords = ["graphics", "rendy"] 7 | readme = "README.md" 8 | license = "MIT/Apache-2.0" 9 | repository = "https://github.com/rustgd/rendy.git" 10 | documentation = "https://docs.rs/crate/rendy-texture/0.1.0" 11 | edition = "2018" 12 | 13 | [features] 14 | serde-1 = ["serde", "gfx-hal/serde", "rendy-factory/serde"] 15 | 16 | [dependencies] 17 | rendy-memory = { path = "../memory" } 18 | rendy-resource = { path = "../resource" } 19 | rendy-factory = { path = "../factory" } 20 | rendy-util = { path = "../util" } 21 | 22 | gfx-hal = { git = "https://github.com/gfx-rs/gfx" } 23 | 24 | derivative = "1.0" 25 | failure = "0.1" 26 | serde = { version = "1.0", optional = true } 27 | -------------------------------------------------------------------------------- /texture/src/lib.rs: -------------------------------------------------------------------------------- 1 | 2 | //! 3 | //! Texture creation and usage. 4 | //! 5 | //! 6 | 7 | use rendy_factory as factory; 8 | use rendy_resource as resource; 9 | use rendy_util as util; 10 | 11 | pub mod pixel; 12 | mod texture; 13 | 14 | pub use crate::{ 15 | pixel::{ 16 | Rgba8Unorm, 17 | }, 18 | texture::*, 19 | }; 20 | -------------------------------------------------------------------------------- /texture/src/pixel.rs: -------------------------------------------------------------------------------- 1 | //! Typed pixel formats. 2 | 3 | #[derive(Clone, Copy, Debug, Default)] pub struct Unorm; 4 | #[derive(Clone, Copy, Debug, Default)] pub struct Inorm; 5 | #[derive(Clone, Copy, Debug, Default)] pub struct Uint; 6 | #[derive(Clone, Copy, Debug, Default)] pub struct Int; 7 | #[derive(Clone, Copy, Debug, Default)] pub struct Uscaled; 8 | #[derive(Clone, Copy, Debug, Default)] pub struct Iscaled; 9 | #[derive(Clone, Copy, Debug, Default)] pub struct Srgb; 10 | #[derive(Clone, Copy, Debug, Default)] pub struct Float; 11 | 12 | #[derive(Clone, Copy, Debug, Default)] pub struct _8; 13 | #[derive(Clone, Copy, Debug, Default)] pub struct _16; 14 | #[derive(Clone, Copy, Debug, Default)] pub struct _32; 15 | #[derive(Clone, Copy, Debug, Default)] pub struct _64; 16 | 17 | /// Size of the channel. 18 | pub trait ChannelSize { 19 | /// Channel representation. 20 | const SIZE: u32; 21 | } 22 | 23 | impl ChannelSize for _8 { const SIZE: u32 = 1; } 24 | impl ChannelSize for _16 { const SIZE: u32 = 2; } 25 | impl ChannelSize for _32 { const SIZE: u32 = 4; } 26 | impl ChannelSize for _64 { const SIZE: u32 = 8; } 27 | 28 | /// Channel representation. 29 | pub trait ChannelRepr { 30 | /// Channel representation. 31 | type Repr: Sized + std::fmt::Debug + Default + Copy + Send + Sync + 'static; 32 | } 33 | 34 | macro_rules! impl_channel_repr { 35 | ($($type:ident * $size:ident = $repr:ident;)*) => { 36 | $( 37 | impl ChannelRepr<$size> for $type { type Repr = $repr; } 38 | )* 39 | }; 40 | } 41 | 42 | impl_channel_repr! { 43 | Unorm * _8 = u8; 44 | Inorm * _8 = u8; 45 | Uint * _8 = u8; 46 | Int * _8 = u8; 47 | Uscaled * _8 = u8; 48 | Iscaled * _8 = u8; 49 | Srgb * _8 = u8; 50 | 51 | Unorm * _16 = u16; 52 | Inorm * _16 = u16; 53 | Uint * _16 = u16; 54 | Int * _16 = u16; 55 | Uscaled * _16 = u16; 56 | Iscaled * _16 = u16; 57 | Srgb * _16 = u16; 58 | 59 | Unorm * _32 = u32; 60 | Inorm * _32 = u32; 61 | Uint * _32 = u32; 62 | Int * _32 = u32; 63 | Uscaled * _32 = u32; 64 | Iscaled * _32 = u32; 65 | Srgb * _32 = u32; 66 | Float * _32 = f32; 67 | 68 | Unorm * _64 = u64; 69 | Inorm * _64 = u64; 70 | Uint * _64 = u64; 71 | Int * _64 = u64; 72 | Uscaled * _64 = u64; 73 | Iscaled * _64 = u64; 74 | Srgb * _64 = u64; 75 | Float * _64 = f64; 76 | } 77 | 78 | #[derive(Clone, Copy, Debug, Default)] pub struct R; 79 | #[derive(Clone, Copy, Debug, Default)] pub struct Rg; 80 | #[derive(Clone, Copy, Debug, Default)] pub struct Rgb; 81 | #[derive(Clone, Copy, Debug, Default)] pub struct Rgba; 82 | #[derive(Clone, Copy, Debug, Default)] pub struct Bgr; 83 | #[derive(Clone, Copy, Debug, Default)] pub struct Bgra; 84 | #[derive(Clone, Copy, Debug, Default)] pub struct Abgr; 85 | 86 | /// Pixel representation. 87 | pub trait PixelRepr { 88 | /// Pixel representation. 89 | type Repr: Sized + std::fmt::Debug + Default + Copy + Send + Sync + 'static; 90 | } 91 | 92 | macro_rules! num_channels { 93 | (R) => { 1 }; 94 | (Rg) => { 2 }; 95 | (Rgb) => { 3 }; 96 | (Rgba) => { 4 }; 97 | (Bgr) => { 3 }; 98 | (Bgra) => { 4 }; 99 | (Abgr) => { 4 }; 100 | } 101 | 102 | macro_rules! impl_pixel_repr { 103 | ($($channels:ident;)*) => { 104 | $( 105 | impl PixelRepr for $channels 106 | where 107 | S: ChannelSize, 108 | T: ChannelRepr, 109 | { 110 | type Repr = [>::Repr; num_channels!($channels)]; 111 | } 112 | )* 113 | }; 114 | } 115 | 116 | impl_pixel_repr! { 117 | R; 118 | Rg; 119 | Rgb; 120 | Rgba; 121 | Bgr; 122 | Bgra; 123 | Abgr; 124 | } 125 | 126 | /// One pixel 127 | #[derive(derivative::Derivative)] 128 | #[derivative(Clone(bound = ""), Copy(bound = ""), Debug(bound = ""), Default(bound = ""))] 129 | #[repr(transparent)] 130 | pub struct Pixel where C: PixelRepr { 131 | repr: >::Repr, 132 | } 133 | 134 | /// Pixel trait. 135 | pub trait AsPixel: Copy + std::fmt::Debug + Default + Send + Sync + 'static { 136 | /// Name of the pixel type. 137 | const NAME: &'static str; 138 | 139 | /// Size of the pixel. 140 | const SIZE: u32; 141 | 142 | /// Pixel format. 143 | const FORMAT: gfx_hal::format::Format; 144 | } 145 | 146 | macro_rules! impl_pixel { 147 | ($($alias:ident = $channels:ident $size:ident $type:ident;)*) => { 148 | $( 149 | pub type $alias = Pixel<$channels, $size, $type>; 150 | 151 | impl AsPixel for $alias { 152 | const NAME: &'static str = stringify!($alias); 153 | const SIZE: u32 = num_channels!($channels) * <$size as ChannelSize>::SIZE; 154 | const FORMAT: gfx_hal::format::Format = gfx_hal::format::Format::$alias; 155 | } 156 | )* 157 | }; 158 | } 159 | 160 | impl_pixel! { 161 | R8Unorm = R _8 Unorm; 162 | R8Inorm = R _8 Inorm; 163 | R8Uscaled = R _8 Uscaled; 164 | R8Iscaled = R _8 Iscaled; 165 | R8Uint = R _8 Uint; 166 | R8Int = R _8 Int; 167 | R8Srgb = R _8 Srgb; 168 | Rg8Unorm = Rg _8 Unorm; 169 | Rg8Inorm = Rg _8 Inorm; 170 | Rg8Uscaled = Rg _8 Uscaled; 171 | Rg8Iscaled = Rg _8 Iscaled; 172 | Rg8Uint = Rg _8 Uint; 173 | Rg8Int = Rg _8 Int; 174 | Rg8Srgb = Rg _8 Srgb; 175 | Rgb8Unorm = Rgb _8 Unorm; 176 | Rgb8Inorm = Rgb _8 Inorm; 177 | Rgb8Uscaled = Rgb _8 Uscaled; 178 | Rgb8Iscaled = Rgb _8 Iscaled; 179 | Rgb8Uint = Rgb _8 Uint; 180 | Rgb8Int = Rgb _8 Int; 181 | Rgb8Srgb = Rgb _8 Srgb; 182 | Bgr8Unorm = Bgr _8 Unorm; 183 | Bgr8Inorm = Bgr _8 Inorm; 184 | Bgr8Uscaled = Bgr _8 Uscaled; 185 | Bgr8Iscaled = Bgr _8 Iscaled; 186 | Bgr8Uint = Bgr _8 Uint; 187 | Bgr8Int = Bgr _8 Int; 188 | Bgr8Srgb = Bgr _8 Srgb; 189 | Rgba8Unorm = Rgba _8 Unorm; 190 | Rgba8Inorm = Rgba _8 Inorm; 191 | Rgba8Uscaled = Rgba _8 Uscaled; 192 | Rgba8Iscaled = Rgba _8 Iscaled; 193 | Rgba8Uint = Rgba _8 Uint; 194 | Rgba8Int = Rgba _8 Int; 195 | Rgba8Srgb = Rgba _8 Srgb; 196 | Bgra8Unorm = Bgra _8 Unorm; 197 | Bgra8Inorm = Bgra _8 Inorm; 198 | Bgra8Uscaled = Bgra _8 Uscaled; 199 | Bgra8Iscaled = Bgra _8 Iscaled; 200 | Bgra8Uint = Bgra _8 Uint; 201 | Bgra8Int = Bgra _8 Int; 202 | Bgra8Srgb = Bgra _8 Srgb; 203 | Abgr8Unorm = Abgr _8 Unorm; 204 | Abgr8Inorm = Abgr _8 Inorm; 205 | Abgr8Uscaled = Abgr _8 Uscaled; 206 | Abgr8Iscaled = Abgr _8 Iscaled; 207 | Abgr8Uint = Abgr _8 Uint; 208 | Abgr8Int = Abgr _8 Int; 209 | Abgr8Srgb = Abgr _8 Srgb; 210 | R16Unorm = R _16 Unorm; 211 | R16Inorm = R _16 Inorm; 212 | R16Uscaled = R _16 Uscaled; 213 | R16Iscaled = R _16 Iscaled; 214 | R16Uint = R _16 Uint; 215 | R16Int = R _16 Int; 216 | // R16Float = R _16 Float; 217 | Rg16Unorm = Rg _16 Unorm; 218 | Rg16Inorm = Rg _16 Inorm; 219 | Rg16Uscaled = Rg _16 Uscaled; 220 | Rg16Iscaled = Rg _16 Iscaled; 221 | Rg16Uint = Rg _16 Uint; 222 | Rg16Int = Rg _16 Int; 223 | // Rg16Float = Rg _16 Float; 224 | Rgb16Unorm = Rgb _16 Unorm; 225 | Rgb16Inorm = Rgb _16 Inorm; 226 | Rgb16Uscaled = Rgb _16 Uscaled; 227 | Rgb16Iscaled = Rgb _16 Iscaled; 228 | Rgb16Uint = Rgb _16 Uint; 229 | Rgb16Int = Rgb _16 Int; 230 | // Rgb16Float = Rgb _16 Float; 231 | Rgba16Unorm = Rgba _16 Unorm; 232 | Rgba16Inorm = Rgba _16 Inorm; 233 | Rgba16Uscaled = Rgba _16 Uscaled; 234 | Rgba16Iscaled = Rgba _16 Iscaled; 235 | Rgba16Uint = Rgba _16 Uint; 236 | Rgba16Int = Rgba _16 Int; 237 | // Rgba16Float = Rgba _16 Float; 238 | R32Uint = R _32 Uint; 239 | R32Int = R _32 Int; 240 | R32Float = R _32 Float; 241 | Rg32Uint = Rg _32 Uint; 242 | Rg32Int = Rg _32 Int; 243 | Rg32Float = Rg _32 Float; 244 | Rgb32Uint = Rgb _32 Uint; 245 | Rgb32Int = Rgb _32 Int; 246 | Rgb32Float = Rgb _32 Float; 247 | Rgba32Uint = Rgba _32 Uint; 248 | Rgba32Int = Rgba _32 Int; 249 | Rgba32Float = Rgba _32 Float; 250 | R64Uint = R _64 Uint; 251 | R64Int = R _64 Int; 252 | R64Float = R _64 Float; 253 | Rg64Uint = Rg _64 Uint; 254 | Rg64Int = Rg _64 Int; 255 | Rg64Float = Rg _64 Float; 256 | Rgb64Uint = Rgb _64 Uint; 257 | Rgb64Int = Rgb _64 Int; 258 | Rgb64Float = Rgb _64 Float; 259 | Rgba64Uint = Rgba _64 Uint; 260 | Rgba64Int = Rgba _64 Int; 261 | Rgba64Float = Rgba _64 Float; 262 | } 263 | -------------------------------------------------------------------------------- /texture/src/texture.rs: -------------------------------------------------------------------------------- 1 | 2 | use crate::{ 3 | pixel::AsPixel, 4 | resource::image::{Image, Texture as TextureUsage}, 5 | factory::Factory, 6 | util::cast_cow, 7 | }; 8 | 9 | /// Static image. 10 | /// Can be loaded from various of formats. 11 | #[derive(Debug)] 12 | pub struct Texture { 13 | image: Image, 14 | } 15 | 16 | #[derive(Clone, Debug)] 17 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 18 | pub struct TextureBuilder<'a> { 19 | kind: gfx_hal::image::Kind, 20 | format: gfx_hal::format::Format, 21 | data: std::borrow::Cow<'a, [u8]>, 22 | data_width: u32, 23 | data_height: u32, 24 | } 25 | 26 | impl<'a> TextureBuilder<'a> { 27 | /// New empty builder. 28 | pub fn new() -> Self { 29 | TextureBuilder { 30 | kind: gfx_hal::image::Kind::D1(0, 0), 31 | format: gfx_hal::format::Format::Rgba8Unorm, 32 | data: std::borrow::Cow::Borrowed(&[]), 33 | data_width: 0, 34 | data_height: 0, 35 | } 36 | } 37 | 38 | /// Set pixel data. 39 | pub fn with_data(mut self, data: impl Into>) -> Self { 40 | self.set_data(data); 41 | self 42 | } 43 | 44 | /// Set pixel data. 45 | pub fn set_data(&mut self, data: impl Into>) -> &mut Self { 46 | self.data = cast_cow(data.into()); 47 | self.format = P::FORMAT; 48 | self 49 | } 50 | 51 | /// Set pixel data width. 52 | pub fn with_data_width(mut self, data_width: u32) -> Self { 53 | self.set_data_width(data_width); 54 | self 55 | } 56 | 57 | /// Set pixel data width. 58 | pub fn set_data_width(&mut self, data_width: u32) -> &mut Self { 59 | self.data_width = data_width; 60 | self 61 | } 62 | 63 | /// Set pixel data height. 64 | pub fn with_data_height(mut self, data_height: u32) -> Self { 65 | self.set_data_height(data_height); 66 | self 67 | } 68 | 69 | /// Set pixel data height. 70 | pub fn set_data_height(&mut self, data_height: u32) -> &mut Self { 71 | self.data_height = data_height; 72 | self 73 | } 74 | 75 | /// Set image extent. 76 | pub fn with_kind(mut self, kind: gfx_hal::image::Kind) -> Self { 77 | self.set_kind(kind); 78 | self 79 | } 80 | 81 | /// Set image kind. 82 | pub fn set_kind(&mut self, kind: gfx_hal::image::Kind) -> &mut Self { 83 | self.kind = kind; 84 | self 85 | } 86 | 87 | /// Build texture. 88 | pub fn build( 89 | &self, 90 | family: gfx_hal::queue::QueueFamilyId, 91 | access: gfx_hal::image::Access, 92 | layout: gfx_hal::image::Layout, 93 | factory: &mut Factory, 94 | ) -> Result, failure::Error> 95 | where 96 | B: gfx_hal::Backend, 97 | { 98 | let mut image = factory.create_image( 99 | 256, 100 | self.kind, 101 | 1, 102 | self.format, 103 | gfx_hal::image::Tiling::Optimal, 104 | gfx_hal::image::ViewCapabilities::empty(), 105 | TextureUsage, 106 | )?; 107 | 108 | factory.upload_image( 109 | &mut image, 110 | self.data_width, 111 | self.data_height, 112 | gfx_hal::image::SubresourceLayers { 113 | aspects: self.format.surface_desc().aspects, 114 | level: 0, 115 | layers: 0 .. 1, 116 | }, 117 | gfx_hal::image::Offset::ZERO, 118 | self.kind.extent(), 119 | &self.data, 120 | family, 121 | access, 122 | layout, 123 | )?; 124 | 125 | Ok(Texture { 126 | image, 127 | }) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-util" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | -------------------------------------------------------------------------------- /util/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! 3 | 4 | #![forbid(overflowing_literals)] 5 | #![deny(missing_copy_implementations)] 6 | #![deny(missing_debug_implementations)] 7 | #![deny(missing_docs)] 8 | #![deny(intra_doc_link_resolution_failure)] 9 | #![deny(path_statements)] 10 | #![deny(trivial_bounds)] 11 | #![deny(type_alias_bounds)] 12 | #![deny(unconditional_recursion)] 13 | #![deny(unions_with_drop_fields)] 14 | #![deny(while_true)] 15 | #![deny(unused)] 16 | #![deny(bad_style)] 17 | #![deny(future_incompatible)] 18 | #![deny(rust_2018_compatibility)] 19 | #![deny(rust_2018_idioms)] 20 | 21 | use std::borrow::Cow; 22 | 23 | /// Chech if slice o f ordered values is sorted. 24 | pub fn is_slice_sorted(slice: &[T]) -> bool { 25 | is_slice_sorted_by_key(slice, |i| i) 26 | } 27 | 28 | /// Check if slice is sorted using ordered key and key extractor 29 | pub fn is_slice_sorted_by_key<'a, T, K: Ord>(slice: &'a [T], f: impl Fn(&'a T) -> K) -> bool { 30 | if let Some((first, slice)) = slice.split_first() { 31 | let mut cmp = f(first); 32 | for item in slice { 33 | let item = f(item); 34 | if cmp > item { 35 | return false; 36 | } 37 | cmp = item; 38 | } 39 | } 40 | true 41 | } 42 | 43 | /// Cast vec of some arbitrary type into vec of bytes. 44 | pub fn cast_vec(mut vec: Vec) -> Vec { 45 | use std::mem; 46 | 47 | let raw_len = mem::size_of::() * vec.len(); 48 | let len = raw_len; 49 | 50 | let cap = mem::size_of::() * vec.capacity(); 51 | 52 | let ptr = vec.as_mut_ptr(); 53 | mem::forget(vec); 54 | unsafe { Vec::from_raw_parts(ptr as _, len, cap) } 55 | } 56 | 57 | /// Cast slice of some arbitrary type into slice of bytes. 58 | pub fn cast_slice(slice: &[T]) -> &[u8] { 59 | use std::{mem, slice::from_raw_parts}; 60 | 61 | let raw_len = mem::size_of::() * slice.len(); 62 | let len = raw_len; 63 | 64 | let ptr = slice.as_ptr(); 65 | unsafe { from_raw_parts(ptr as _, len) } 66 | } 67 | 68 | /// Cast `cow` of some arbitrary type into `cow` of bytes. 69 | pub fn cast_cow(cow: Cow<'_, [T]>) -> Cow<'_, [u8]> 70 | where 71 | T: Clone, 72 | { 73 | match cow { 74 | Cow::Borrowed(slice) => Cow::Borrowed(cast_slice(slice)), 75 | Cow::Owned(vec) => Cow::Owned(cast_vec(vec)), 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /wsi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-wsi" 3 | version = "0.1.0" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | 7 | [features] 8 | empty = ["gfx-backend-empty"] 9 | dx12 = ["gfx-backend-dx12"] 10 | metal = ["gfx-backend-metal"] 11 | vulkan = ["gfx-backend-vulkan"] 12 | 13 | [dependencies] 14 | gfx-hal = { git = "https://github.com/gfx-rs/gfx" } 15 | gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", optional = true } 16 | gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx", optional = true } 17 | gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx", optional = true } 18 | gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", optional = true } 19 | 20 | derivative = "1.0" 21 | failure = "0.1" 22 | log = "0.4" 23 | relevant = "0.2" 24 | smallvec = "0.6" 25 | winit = "0.18" 26 | --------------------------------------------------------------------------------