├── rustfmt.toml ├── mesh ├── src │ ├── format.rs │ ├── lib.rs │ └── format │ │ └── obj.rs ├── Cargo.toml └── README.md ├── .gitignore ├── tests ├── src │ ├── gl.rs │ ├── dx12.rs │ ├── metal.rs │ └── vulkan.rs └── Cargo.toml ├── docs ├── logo.png ├── graph.md └── buffers.md ├── descriptor ├── src │ ├── lib.rs │ └── ranges.rs └── Cargo.toml ├── rendy ├── examples │ ├── sprite │ │ ├── logo.png │ │ ├── shader.vert │ │ └── shader.frag │ ├── quads │ │ ├── render.frag │ │ ├── render.vert │ │ └── bounce.comp │ ├── triangle │ │ ├── shader.frag │ │ └── shader.vert │ ├── init │ │ └── main.rs │ └── meshes │ │ ├── shader.vert │ │ └── shader.frag ├── src │ └── lib.rs └── Cargo.toml ├── texture ├── src │ ├── format.rs │ ├── lib.rs │ └── format │ │ └── palette.rs └── Cargo.toml ├── bors.toml ├── Cargo.toml ├── COPYING ├── frame ├── src │ ├── lib.rs │ ├── cirque │ │ └── command.rs │ └── frame.rs └── Cargo.toml ├── core ├── src │ ├── types │ │ └── mod.rs │ ├── slow.rs │ ├── lib.rs │ └── casts.rs └── Cargo.toml ├── chain ├── Cargo.toml └── src │ ├── lib.rs │ ├── node.rs │ ├── chain │ └── mod.rs │ ├── schedule │ ├── family.rs │ ├── submission.rs │ └── queue.rs │ └── resource.rs ├── license ├── APACHE └── MIT ├── graph ├── src │ ├── node │ │ └── render │ │ │ ├── mod.rs │ │ │ └── group │ │ │ └── mod.rs │ └── lib.rs └── Cargo.toml ├── factory ├── src │ └── lib.rs └── Cargo.toml ├── memory ├── src │ ├── lib.rs │ ├── block.rs │ ├── heaps │ │ ├── heap.rs │ │ └── memory_type.rs │ ├── allocator │ │ ├── mod.rs │ │ └── dedicated.rs │ ├── mapping │ │ ├── write.rs │ │ └── range.rs │ ├── memory.rs │ ├── util.rs │ └── utilization.rs └── Cargo.toml ├── command ├── Cargo.toml └── src │ ├── buffer │ ├── level.rs │ ├── reset.rs │ ├── state.rs │ ├── usage.rs │ └── submit.rs │ ├── family │ ├── submission.rs │ └── queue.rs │ ├── lib.rs │ ├── capability.rs │ ├── pool.rs │ └── fence.rs ├── resource ├── Cargo.toml └── src │ ├── sampler │ ├── mod.rs │ └── cache.rs │ ├── lib.rs │ ├── resources.rs │ ├── buffer.rs │ ├── set.rs │ └── escape.rs ├── wsi └── Cargo.toml ├── shader └── Cargo.toml ├── init └── Cargo.toml ├── Makefile ├── .travis.yml ├── CHANGELOG.md └── Jenkinsfile /rustfmt.toml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mesh/src/format.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "obj")] 2 | pub mod obj; 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | **/*.rs.bk 3 | **/Cargo.lock 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /tests/src/gl.rs: -------------------------------------------------------------------------------- 1 | pub use rendy::core::gl; 2 | 3 | fn main() {} 4 | -------------------------------------------------------------------------------- /tests/src/dx12.rs: -------------------------------------------------------------------------------- 1 | pub use rendy::core::dx12; 2 | 3 | fn main() {} 4 | -------------------------------------------------------------------------------- /tests/src/metal.rs: -------------------------------------------------------------------------------- 1 | pub use rendy::core::metal; 2 | 3 | fn main() {} 4 | -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakarumych/rendy/HEAD/docs/logo.png -------------------------------------------------------------------------------- /tests/src/vulkan.rs: -------------------------------------------------------------------------------- 1 | pub use rendy::core::vulkan; 2 | 3 | fn main() {} 4 | -------------------------------------------------------------------------------- /descriptor/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod allocator; 2 | mod ranges; 3 | 4 | pub use {allocator::*, ranges::*}; 5 | -------------------------------------------------------------------------------- /rendy/examples/sprite/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zakarumych/rendy/HEAD/rendy/examples/sprite/logo.png -------------------------------------------------------------------------------- /texture/src/format.rs: -------------------------------------------------------------------------------- 1 | //! Exports the image and palette modules if the features 2 | //! are enabled 3 | 4 | #[cfg(feature = "image")] 5 | pub mod image; 6 | #[cfg(feature = "palette")] 7 | pub mod palette; 8 | -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | # Test suite must pass on both Travis (if enabled) and Jenkins before merging into `master`. 2 | status = [ 3 | # "continuous-integration/travis-ci/push", 4 | "continuous-integration/jenkins/%", 5 | ] 6 | -------------------------------------------------------------------------------- /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/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/sprite/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 vec2 in_uv; 6 | layout(location = 0) out vec2 uv; 7 | 8 | void main() { 9 | uv = in_uv; 10 | gl_Position = vec4(pos, 1.0); 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 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "command", 5 | "descriptor", 6 | "factory", 7 | "frame", 8 | "init", 9 | "memory", 10 | "mesh", 11 | "rendy", 12 | "resource", 13 | "shader", 14 | "wsi", 15 | "chain", 16 | "graph", 17 | "core", 18 | "tests", 19 | "texture", 20 | ] 21 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /rendy/examples/sprite/shader.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec2 uv; 5 | layout(location = 0) out vec4 color; 6 | 7 | layout(set = 0, binding = 0) uniform texture2D colormap; 8 | layout(set = 0, binding = 1) uniform sampler colorsampler; 9 | 10 | 11 | void main() { 12 | color = texture(sampler2D(colormap, colorsampler), uv); 13 | } 14 | -------------------------------------------------------------------------------- /frame/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Everything that is frame related. 2 | 3 | #![warn( 4 | missing_debug_implementations, 5 | missing_copy_implementations, 6 | missing_docs, 7 | trivial_casts, 8 | trivial_numeric_casts, 9 | unused_extern_crates, 10 | unused_import_braces, 11 | unused_qualifications 12 | )] 13 | use rendy_command as command; 14 | use rendy_factory as factory; 15 | 16 | pub mod cirque; 17 | mod frame; 18 | 19 | pub use crate::frame::*; 20 | -------------------------------------------------------------------------------- /texture/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Texture creation and usage 3 | //! 4 | 5 | #![warn( 6 | missing_debug_implementations, 7 | missing_copy_implementations, 8 | trivial_casts, 9 | trivial_numeric_casts, 10 | unused_extern_crates, 11 | unused_import_braces, 12 | unused_qualifications 13 | )] 14 | 15 | use rendy_core as core; 16 | use rendy_factory as factory; 17 | use rendy_memory as memory; 18 | use rendy_resource as resource; 19 | 20 | mod format; 21 | pub mod pixel; 22 | mod texture; 23 | 24 | pub use crate::{format::*, pixel::Rgba8Unorm, texture::*}; 25 | -------------------------------------------------------------------------------- /core/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Types shared across rendy 3 | //! 4 | pub mod vertex; 5 | 6 | /// Set layout 7 | #[derive(Clone, Debug, Default)] 8 | pub struct SetLayout { 9 | /// Set layout bindings. 10 | pub bindings: Vec, 11 | } 12 | 13 | /// Pipeline layout 14 | #[derive(Clone, Debug)] 15 | pub struct Layout { 16 | /// Sets in pipeline layout. 17 | pub sets: Vec, 18 | 19 | /// Push constants in pipeline layout. 20 | pub push_constants: Vec<(crate::hal::pso::ShaderStageFlags, std::ops::Range)>, 21 | } 22 | -------------------------------------------------------------------------------- /chain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-chain" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-chain" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | categories = ["rendering"] 11 | description = "Rendy's node synchronization tool" 12 | 13 | [features] 14 | profiler = ["thread_profiler/thread_profiler"] 15 | 16 | [dependencies] 17 | rendy-core = { version = "0.5.1", path = "../core" } 18 | log = "0.4" 19 | thread_profiler = "0.3" 20 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /descriptor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-descriptor" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-descriptor" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | categories = ["rendering"] 11 | description = "Rendy's descriptor allocator" 12 | 13 | [dependencies] 14 | gfx-hal = { git = "https://github.com/gfx-rs/gfx", rev = "3641183231f16877d4ea2fbdb2ff208ce736d6c4" } 15 | log = "0.4" 16 | relevant = { version = "0.4", features = ["log"] } 17 | smallvec = "1.0" 18 | -------------------------------------------------------------------------------- /graph/src/node/render/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Advanced render pass node. 3 | //! Will replace render pass node when polished. 4 | //! 5 | 6 | mod group; 7 | mod pass; 8 | 9 | pub use self::{group::*, pass::*}; 10 | 11 | /// Result of draw preparation. 12 | #[derive(Clone, Copy, Debug)] 13 | #[must_use] 14 | pub enum PrepareResult { 15 | /// Force record draw commands. 16 | DrawRecord, 17 | 18 | /// Reuse draw commands. 19 | DrawReuse, 20 | } 21 | 22 | impl PrepareResult { 23 | fn force_record(&self) -> bool { 24 | match self { 25 | PrepareResult::DrawRecord => true, 26 | PrepareResult::DrawReuse => false, 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /factory/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Higher-level rendy interface. 2 | 3 | #![warn( 4 | missing_debug_implementations, 5 | missing_copy_implementations, 6 | missing_docs, 7 | trivial_casts, 8 | trivial_numeric_casts, 9 | unused_extern_crates, 10 | unused_import_braces, 11 | unused_qualifications 12 | )] 13 | use rendy_command as command; 14 | use rendy_core as core; 15 | use rendy_descriptor as descriptor; 16 | use rendy_memory as memory; 17 | use rendy_resource as resource; 18 | use rendy_wsi as wsi; 19 | 20 | mod barriers; 21 | mod blitter; 22 | mod config; 23 | mod factory; 24 | mod upload; 25 | 26 | pub use crate::{barriers::*, blitter::*, config::*, factory::*, upload::*}; 27 | -------------------------------------------------------------------------------- /tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-tests" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | description = "Rendy's basic tests" 9 | categories = ["rendering"] 10 | 11 | [dependencies] 12 | rendy = { version = "0.5.1", path = "../rendy", features = ["dx12", "gl", "metal", "vulkan"], default-features = false } 13 | 14 | [[bin]] 15 | name = "dx12" 16 | path = "src/dx12.rs" 17 | 18 | [[bin]] 19 | name = "gl" 20 | path = "src/gl.rs" 21 | 22 | [[bin]] 23 | name = "metal" 24 | path = "src/metal.rs" 25 | 26 | [[bin]] 27 | name = "vulkan" 28 | path = "src/vulkan.rs" 29 | -------------------------------------------------------------------------------- /rendy/examples/init/main.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Basic example initializes core type of the rendy - `Factory` and exits. 3 | //! 4 | 5 | use rendy::{factory::Config, init::AnyWindowedRendy}; 6 | 7 | fn main() { 8 | env_logger::Builder::from_default_env() 9 | .filter_module("init", log::LevelFilter::Trace) 10 | .init(); 11 | 12 | let config: Config = Default::default(); 13 | 14 | let event_loop = rendy::init::winit::event_loop::EventLoop::new(); 15 | let window = rendy::init::winit::window::WindowBuilder::new().with_title("Rendy example"); 16 | 17 | let rendy = AnyWindowedRendy::init_auto(&config, window, &event_loop).unwrap(); 18 | 19 | rendy::with_any_windowed_rendy!((rendy) (_, _, _, _) => {}); 20 | } 21 | -------------------------------------------------------------------------------- /memory/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! GPU memory management 2 | //! 3 | 4 | #![warn( 5 | missing_debug_implementations, 6 | missing_copy_implementations, 7 | missing_docs, 8 | trivial_casts, 9 | trivial_numeric_casts, 10 | unused_extern_crates, 11 | unused_import_braces, 12 | unused_qualifications 13 | )] 14 | mod allocator; 15 | mod block; 16 | mod heaps; 17 | mod mapping; 18 | mod memory; 19 | mod usage; 20 | mod util; 21 | mod utilization; 22 | 23 | pub use crate::{ 24 | allocator::*, 25 | block::Block, 26 | heaps::{Heaps, HeapsConfig, HeapsError, MemoryBlock}, 27 | mapping::{write::Write, Coherent, MappedRange, MaybeCoherent, NonCoherent}, 28 | memory::Memory, 29 | usage::*, 30 | utilization::*, 31 | }; 32 | -------------------------------------------------------------------------------- /command/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-command" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-command" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | categories = ["rendering"] 11 | description = "Rendy's queues and commands tools" 12 | 13 | [features] 14 | no-slow-safety-checks = ["rendy-core/no-slow-safety-checks"] 15 | profiler = ["thread_profiler/thread_profiler"] 16 | 17 | [dependencies] 18 | relevant = { version = "0.4.0", features = ["log", "backtrace"] } 19 | smallvec = "1.0" 20 | rendy-core = { version = "0.5.1", path = "../core" } 21 | thread_profiler = "0.3" 22 | -------------------------------------------------------------------------------- /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 | vec4 posvel = posvelbuff.posvel[gl_InstanceIndex]; 22 | vec2 pos = posvel.rg; 23 | vec2 vertex = vertices[gl_VertexIndex]; 24 | 25 | vec2 v = ((vertex + pos / 1.01) * 2.0) - vec2(1.0, 1.0); 26 | v.y = -v.y; 27 | 28 | frag_color = vec4(color.rgb, 1.0); 29 | gl_Position = vec4(v, 0.0, 1.0); 30 | } 31 | -------------------------------------------------------------------------------- /resource/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-resource" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-resource" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | categories = ["rendering"] 11 | description = "Rendy's resource manager" 12 | 13 | [features] 14 | no-slow-safety-checks = ["rendy-core/no-slow-safety-checks"] 15 | 16 | [dependencies] 17 | crossbeam-channel = "0.3" 18 | log = "0.4" 19 | relevant = { version = "0.4", features = ["log"] } 20 | rendy-descriptor = { version = "0.5.1", path = "../descriptor" } 21 | rendy-memory = { version = "0.5.2", path = "../memory" } 22 | rendy-core = { version = "0.5.1", path = "../core" } 23 | smallvec = "1.0" 24 | -------------------------------------------------------------------------------- /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 | #![warn( 10 | missing_debug_implementations, 11 | missing_copy_implementations, 12 | missing_docs, 13 | trivial_casts, 14 | trivial_numeric_casts, 15 | unused_extern_crates, 16 | unused_import_braces, 17 | unused_qualifications 18 | )] 19 | use rendy_command as command; 20 | use rendy_core as core; 21 | use rendy_factory as factory; 22 | use rendy_memory as memory; 23 | use rendy_resource as resource; 24 | 25 | mod format; 26 | mod mesh; 27 | 28 | pub use crate::{format::*, mesh::*}; 29 | pub use rendy_core::types::vertex::*; 30 | -------------------------------------------------------------------------------- /rendy/examples/meshes/shader.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec3 position; 5 | layout(location = 1) in vec4 color; 6 | layout(location = 2) in vec3 normal; 7 | // vec4[4] is used instead of mat4 due to spirv-cross bug for dx12 backend 8 | layout(location = 3) in vec4 model[4]; // per-instance. 9 | 10 | layout(set = 0, binding = 0) uniform Args { 11 | mat4 proj; 12 | mat4 view; 13 | }; 14 | 15 | layout(location = 0) out vec4 frag_pos; 16 | layout(location = 1) out vec3 frag_norm; 17 | layout(location = 2) out vec4 frag_color; 18 | 19 | void main() { 20 | mat4 model_mat = mat4(model[0], model[1], model[2], model[3]); 21 | frag_color = color; 22 | frag_norm = normalize((vec4(normal, 1.0) * model_mat).xyz); 23 | frag_pos = model_mat * vec4(position, 1.0); 24 | gl_Position = proj * view * frag_pos; 25 | } 26 | -------------------------------------------------------------------------------- /memory/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-memory" 3 | version = "0.5.2" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-memory" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | categories = ["rendering"] 11 | description = "Rendy's memory manager" 12 | 13 | [features] 14 | serde-1 = ["serde", "gfx-hal/serde"] 15 | 16 | [dependencies] 17 | gfx-hal = { git = "https://github.com/gfx-rs/gfx", rev = "3641183231f16877d4ea2fbdb2ff208ce736d6c4" } 18 | log = "0.4" 19 | hibitset = {version = "0.6", default-features = false} 20 | relevant = { version = "0.4", features = ["log"] } 21 | serde = { version = "1.0", optional = true, features = ["derive"] } 22 | smallvec = "1.0" 23 | slab = "0.4" 24 | colorful = "0.2" 25 | 26 | [dev-dependencies] 27 | rand = "0.7" 28 | -------------------------------------------------------------------------------- /core/src/slow.rs: -------------------------------------------------------------------------------- 1 | //! Macros that do run-time safety checks. These can be disabled, but this increases 2 | //! the risk of unsafe behavior. 3 | //! 4 | 5 | /// `assert!` that is exists only if `"no-slow-safety-checks"` feature is not enabled. 6 | #[macro_export] 7 | macro_rules! rendy_slow_assert { 8 | ($($tt:tt)*) => { 9 | with_slow_safety_checks!(assert!($($tt)*)); 10 | } 11 | } 12 | 13 | /// `assert_eq!` that is exists only if `"no-slow-safety-checks"` feature is not enabled. 14 | #[macro_export] 15 | macro_rules! rendy_slow_assert_eq { 16 | ($($tt:tt)*) => { 17 | with_slow_safety_checks!(assert_eq!($($tt)*)); 18 | } 19 | } 20 | 21 | /// `assert_ne!` that is exists only if `"no-slow-safety-checks"` feature is not enabled. 22 | #[macro_export] 23 | macro_rules! rendy_slow_assert_ne { 24 | ($($tt:tt)*) => { 25 | with_slow_safety_checks!(assert_ne!($($tt)*)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /wsi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-wsi" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-wsi" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | categories = ["rendering"] 11 | description = "Rendy's windowing support" 12 | 13 | [features] 14 | empty = ["rendy-core/empty"] 15 | dx12 = ["rendy-core/dx12"] 16 | gl = ["rendy-core/gl"] 17 | metal = ["rendy-core/metal"] 18 | vulkan = ["rendy-core/vulkan"] 19 | no-slow-safety-checks = ["rendy-core/no-slow-safety-checks"] 20 | 21 | [dependencies] 22 | rendy-memory = { version = "0.5.2", path = "../memory" } 23 | rendy-resource = { version = "0.5.1", path = "../resource" } 24 | rendy-core = { version = "0.5.1", path = "../core" } 25 | 26 | log = "0.4" 27 | relevant = { version = "0.4", features = ["log"] } 28 | smallvec = "1.0" 29 | -------------------------------------------------------------------------------- /shader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-shader" 3 | version = "0.5.1" 4 | authors = ["omni-viral ", "Walter Pearce "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-shader" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | categories = ["rendering"] 11 | description = "Rendy's shader compilation tool" 12 | 13 | [features] 14 | shader-compiler = ["shaderc"] 15 | spirv-reflection = [ "spirv-reflect" ] 16 | serde-1 = ["serde", "rendy-core/serde-1"] 17 | 18 | [dependencies] 19 | smallvec = "1.0" 20 | log = "0.4" 21 | rendy-factory = { version = "0.5.1", path = "../factory" } 22 | rendy-core = { version = "0.5.1", path = "../core" } 23 | shaderc = { version = "0.6", optional = true } 24 | serde = { version = "1.0", optional = true, features = ["derive"] } 25 | spirv-reflect = { version = "0.2.1", optional = true } 26 | -------------------------------------------------------------------------------- /frame/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-frame" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-frame" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | categories = ["rendering"] 11 | description = "Rendy's frame synchronization tool" 12 | 13 | [features] 14 | no-slow-safety-checks = ["rendy-core/no-slow-safety-checks"] 15 | profiler = ["thread_profiler/thread_profiler"] 16 | 17 | [dependencies] 18 | rendy-command = { version = "0.5.1", path = "../command" } 19 | rendy-factory = { version = "0.5.1", path = "../factory" } 20 | rendy-memory = { version = "0.5.2", path = "../memory" } 21 | rendy-resource = { version = "0.5.1", path = "../resource" } 22 | rendy-core = { version = "0.5.1", path = "../core" } 23 | 24 | either = "1.5" 25 | log = "0.4" 26 | relevant = { version = "0.4", features = ["log"] } 27 | smallvec = "1.0" 28 | thread_profiler = "0.3" 29 | -------------------------------------------------------------------------------- /init/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-init" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-init" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | categories = ["rendering"] 11 | description = "Rendy's easy initialization tool" 12 | 13 | [features] 14 | empty = ["rendy-core/empty"] 15 | dx12 = ["rendy-core/dx12"] 16 | gl = ["rendy-core/gl"] 17 | metal = ["rendy-core/metal"] 18 | vulkan = ["rendy-core/vulkan"] 19 | no-slow-safety-checks = ["rendy-core/no-slow-safety-checks"] 20 | 21 | [dependencies] 22 | rendy-command = { version = "0.5.1", path = "../command" } 23 | rendy-factory = { version = "0.5.1", path = "../factory" } 24 | rendy-core = { version = "0.5.1", path = "../core" } 25 | rendy-wsi = { version = "0.5.1", path = "../wsi" } 26 | winit = { version = "0.20.0-alpha4", optional = true, features = ["web-sys"] } 27 | log = "0.4" 28 | smallvec = "1.0" 29 | -------------------------------------------------------------------------------- /command/src/buffer/level.rs: -------------------------------------------------------------------------------- 1 | /// Command buffers of this level can be submitted to the command queues. 2 | #[derive(Clone, Copy, Debug, Default)] 3 | pub struct PrimaryLevel; 4 | 5 | /// Command buffers of this level can be executed as part of the primary buffers. 6 | #[derive(Clone, Copy, Debug, Default)] 7 | pub struct SecondaryLevel; 8 | 9 | /// Type-level buffer level flag. 10 | /// It defines whether buffer can be submitted to the command queues 11 | /// or executed as part of the primary buffers. 12 | pub trait Level: Copy + Default + std::fmt::Debug + 'static { 13 | /// Get raw level value for command buffer allocation. 14 | fn raw_level(&self) -> rendy_core::hal::command::Level; 15 | } 16 | 17 | impl Level for PrimaryLevel { 18 | fn raw_level(&self) -> rendy_core::hal::command::Level { 19 | rendy_core::hal::command::Level::Primary 20 | } 21 | } 22 | 23 | impl Level for SecondaryLevel { 24 | fn raw_level(&self) -> rendy_core::hal::command::Level { 25 | rendy_core::hal::command::Level::Secondary 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /texture/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-texture" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-texture" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | categories = ["rendering"] 11 | description = "Rendy's texture" 12 | 13 | [features] 14 | no-slow-safety-checks = ["rendy-core/no-slow-safety-checks"] 15 | serde-1 = ["serde", "rendy-core/serde-1"] 16 | profile = ["thread_profiler/thread_profiler"] 17 | 18 | [dependencies] 19 | rendy-memory = { version = "0.5.2", path = "../memory" } 20 | rendy-resource = { version = "0.5.1", path = "../resource" } 21 | rendy-factory = { version = "0.5.1", path = "../factory" } 22 | rendy-core = { version = "0.5.1", path = "../core" } 23 | 24 | serde = { version = "1.0", optional = true } 25 | image = { version = "0.22.0", optional = true } 26 | palette = { version = "0.4", optional = true } 27 | log = "0.4" 28 | thread_profiler = "0.3" 29 | -------------------------------------------------------------------------------- /chain/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate can derive the synchronization required 2 | //! for the dependency chain of the whole execution graph. 3 | 4 | #![warn( 5 | missing_debug_implementations, 6 | missing_copy_implementations, 7 | missing_docs, 8 | trivial_casts, 9 | trivial_numeric_casts, 10 | unused_extern_crates, 11 | unused_import_braces, 12 | unused_qualifications 13 | )] 14 | 15 | /// Unique resource id. 16 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 17 | pub struct Id(pub usize); 18 | 19 | mod chain; 20 | mod collect; 21 | mod node; 22 | mod resource; 23 | mod schedule; 24 | mod sync; 25 | 26 | pub use crate::{ 27 | chain::{Chain, Link, LinkNode}, 28 | collect::{collect, Chains, Unsynchronized}, 29 | node::{BufferState, ImageState, Node, State}, 30 | resource::{AccessFlags, Buffer, Image, Resource, UsageFlags}, 31 | schedule::{Family, Queue, QueueId, Schedule, Submission, SubmissionId}, 32 | sync::{sync, Barrier, Barriers, BufferBarriers, Guard, ImageBarriers, Signal, SyncData, Wait}, 33 | }; 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | RUST_BACKTRACE:=1 2 | RENDY_BACKEND:= 3 | 4 | ifeq ($(OS),Windows_NT) 5 | RENDY_BACKEND=vulkan 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 | fast: 17 | cd rendy && cargo build --all --examples --features "full $(RENDY_BACKEND) no-slow-safety-checks" 18 | 19 | build: 20 | cd rendy && cargo build --all --examples --features "full $(RENDY_BACKEND)" 21 | 22 | test: 23 | cd rendy && cargo test --all --features "full $(RENDY_BACKEND)" 24 | 25 | doc: 26 | cd rendy && cargo doc --all --features "full $(RENDY_BACKEND)" 27 | 28 | all: fast build test doc 29 | 30 | quads: 31 | cd rendy && cargo run --features "full $(RENDY_BACKEND)" --example quads 32 | 33 | triangle: 34 | cd rendy && cargo run --features "full $(RENDY_BACKEND)" --example triangle 35 | 36 | sprite: 37 | cd rendy && cargo run --features "full $(RENDY_BACKEND)" --example sprite 38 | 39 | meshes: 40 | cd rendy && cargo run --features "full $(RENDY_BACKEND)" --example meshes 41 | -------------------------------------------------------------------------------- /graph/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Framegraph implementation for Rendy engine. 2 | 3 | #![warn( 4 | missing_debug_implementations, 5 | missing_copy_implementations, 6 | missing_docs, 7 | trivial_casts, 8 | trivial_numeric_casts, 9 | unused_extern_crates, 10 | unused_import_braces, 11 | unused_qualifications 12 | )] 13 | use rendy_chain as chain; 14 | use rendy_command as command; 15 | use rendy_core as core; 16 | use rendy_factory as factory; 17 | use rendy_frame as frame; 18 | use rendy_memory as memory; 19 | use rendy_resource as resource; 20 | use rendy_wsi as wsi; 21 | 22 | /// Id of the buffer in graph. 23 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 24 | pub struct BufferId(usize); 25 | 26 | /// Id of the image (or target) in graph. 27 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 28 | pub struct ImageId(usize); 29 | 30 | /// Id of the node in graph. 31 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 32 | pub struct NodeId(usize); 33 | 34 | mod graph; 35 | mod node; 36 | 37 | pub use self::{graph::*, node::*}; 38 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /mesh/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-mesh" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-mesh" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | description = "Rendy's mesh" 11 | categories = ["rendering"] 12 | readme = "README.md" 13 | 14 | [features] 15 | obj = ["wavefront_obj"] 16 | serde-1 = ["serde", "serde_bytes", "smallvec/serde", "rendy-factory/serde-1"] 17 | no-slow-safety-checks = ["rendy-core/no-slow-safety-checks"] 18 | 19 | [dependencies] 20 | rendy-command = { version = "0.5.1", path = "../command" } 21 | rendy-memory = { version = "0.5.2", path = "../memory" } 22 | rendy-resource = { version = "0.5.1", path = "../resource" } 23 | rendy-factory = { version = "0.5.1", path = "../factory" } 24 | rendy-core = { version = "0.5.1", path = "../core" } 25 | 26 | serde = { version = "1.0", optional = true, features = ["derive"] } 27 | wavefront_obj = { version = "6.0", optional = true } 28 | smallvec = "1.0" 29 | serde_bytes = { version = "0.11", optional = true } 30 | 31 | log = "0.4.6" 32 | -------------------------------------------------------------------------------- /rendy/examples/meshes/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 in_pos; 7 | layout(location = 1) in vec3 frag_norm; 8 | layout(location = 2) in vec4 frag_color; 9 | layout(location = 0) out vec4 color; 10 | 11 | struct Light { 12 | vec3 pos; 13 | float pad; 14 | float intencity; 15 | }; 16 | 17 | layout(std140, set = 0, binding = 0) uniform Args { 18 | mat4 proj; 19 | mat4 view; 20 | int lights_count; 21 | int pad1; 22 | int pad2; 23 | int pad3; 24 | Light lights[32]; 25 | }; 26 | 27 | void main() { 28 | float acc = 0.0; 29 | 30 | vec3 frag_pos = in_pos.xyz / in_pos.w; 31 | 32 | for (int i = 0; i < lights_count; ++i) { 33 | vec3 v = lights[i].pos - frag_pos; 34 | float d = length(v); 35 | float l = lights[i].intencity / d / d; 36 | l *= max(0.0, dot(normalize(v), frag_norm)); 37 | acc += l; 38 | // acc += 0.5; 39 | // acc += lights[i].intencity; 40 | } 41 | acc = min(acc, 1.0); 42 | color = frag_color * vec4(acc, acc, acc, 1.0); 43 | // color = frag_color; 44 | } 45 | -------------------------------------------------------------------------------- /command/src/buffer/reset.rs: -------------------------------------------------------------------------------- 1 | use super::state::*; 2 | 3 | /// This flag specify that buffer can be reset individually. 4 | #[derive(Clone, Copy, Debug, Default)] 5 | pub struct IndividualReset; 6 | 7 | /// This flag specify that buffer cannot be reset individually. 8 | #[derive(Clone, Copy, Debug, Default)] 9 | pub struct NoIndividualReset; 10 | 11 | /// Specify flags required for command pool creation to allow individual buffer reset. 12 | pub trait Reset: Copy + Default + std::fmt::Debug + 'static { 13 | /// Get flags for reset parameter. 14 | fn flags(&self) -> rendy_core::hal::pool::CommandPoolCreateFlags; 15 | } 16 | 17 | impl Reset for IndividualReset { 18 | fn flags(&self) -> rendy_core::hal::pool::CommandPoolCreateFlags { 19 | rendy_core::hal::pool::CommandPoolCreateFlags::RESET_INDIVIDUAL 20 | } 21 | } 22 | 23 | impl Reset for NoIndividualReset { 24 | fn flags(&self) -> rendy_core::hal::pool::CommandPoolCreateFlags { 25 | rendy_core::hal::pool::CommandPoolCreateFlags::empty() 26 | } 27 | } 28 | 29 | /// States in which command buffer can de reset. 30 | pub trait Resettable {} 31 | impl Resettable for InitialState {} 32 | impl Resettable for RecordingState {} 33 | impl Resettable for ExecutableState {} 34 | impl Resettable for InvalidState {} 35 | -------------------------------------------------------------------------------- /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 size of the block. 20 | fn size(&self) -> u64 { 21 | let range = self.range(); 22 | range.end - range.start 23 | } 24 | 25 | /// Get mapping for the buffer range. 26 | /// Memory writes to the region performed by device become available for the host. 27 | fn map<'a>( 28 | &'a mut self, 29 | device: &B::Device, 30 | range: Range, 31 | ) -> Result, gfx_hal::device::MapError>; 32 | 33 | /// Release memory mapping. Must be called after successful `map` call. 34 | /// No-op if block is not mapped. 35 | fn unmap(&mut self, device: &B::Device); 36 | } 37 | -------------------------------------------------------------------------------- /memory/src/heaps/heap.rs: -------------------------------------------------------------------------------- 1 | use crate::utilization::*; 2 | 3 | #[derive(Debug)] 4 | pub(super) struct MemoryHeap { 5 | size: u64, 6 | used: u64, 7 | effective: u64, 8 | } 9 | 10 | impl MemoryHeap { 11 | pub(super) fn new(size: u64) -> Self { 12 | MemoryHeap { 13 | size, 14 | used: 0, 15 | effective: 0, 16 | } 17 | } 18 | 19 | pub(super) fn available(&self) -> u64 { 20 | if self.used > self.size { 21 | log::warn!("Heap size exceeded"); 22 | 0 23 | } else { 24 | self.size - self.used 25 | } 26 | } 27 | 28 | pub(super) fn allocated(&mut self, used: u64, effective: u64) { 29 | self.used += used; 30 | self.effective += effective; 31 | debug_assert!(self.used >= self.effective); 32 | } 33 | 34 | pub(super) fn freed(&mut self, used: u64, effective: u64) { 35 | self.used -= used; 36 | self.effective -= effective; 37 | debug_assert!(self.used >= self.effective); 38 | } 39 | 40 | pub(super) fn utilization(&self) -> MemoryHeapUtilization { 41 | MemoryHeapUtilization { 42 | utilization: MemoryUtilization { 43 | used: self.used, 44 | effective: self.effective, 45 | }, 46 | size: self.size, 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | compiler: clang 3 | cache: cargo 4 | 5 | env: 6 | global: RUSTFLAGS="-D warnings" RUST_LOG=info 7 | 8 | rust: stable 9 | 10 | matrix: 11 | include: 12 | # # Linux 64bit 13 | # - os: linux 14 | 15 | # # macOS 64bit 16 | # - env: MACOSX_DEPLOYMENT_TARGET=10.9 17 | # os: osx 18 | # osx_image: xcode10.2 19 | # # We need travis_wait here to ensure the build doesn't timeout due to 20 | # # shader compilation 21 | # script: travis_wait 30 make 22 | 23 | # iPhoneOS 64bit 24 | - env: TARGET=aarch64-apple-ios 25 | os: osx 26 | osx_image: xcode10.2 27 | # We need travis_wait here to ensure the build doesn't timeout due to 28 | # shader compilation 29 | script: travis_wait 45 make 30 | 31 | # # Windows 64bit 32 | # - os: windows 33 | # env: PATH=/c/Python37:/c/Python37/Scripts:$PATH 34 | 35 | branches: 36 | only: 37 | # Only build on master as sanity check 38 | - master 39 | # - staging 40 | # - trying 41 | 42 | before_install: 43 | - if [[ $TRAVIS_OS_NAME == "windows" ]]; then choco install make; choco install ninja; choco install python; fi 44 | - rustup self update 45 | - if [[ ! -z "$TARGET" ]]; then rustup target add $TARGET; fi 46 | 47 | # Set the default install command to nothing as we build during the script stage 48 | install: 49 | 50 | script: 51 | - make 52 | -------------------------------------------------------------------------------- /factory/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-factory" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-factory" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | categories = ["rendering"] 11 | description = "Rendy's factory tool" 12 | 13 | [features] 14 | serde-1 = [ 15 | "serde", 16 | "rendy-core/serde-1", 17 | "rendy-memory/serde-1", 18 | ] 19 | 20 | empty = ["rendy-core/empty"] 21 | dx12 = ["rendy-core/dx12"] 22 | gl = ["rendy-core/gl"] 23 | metal = ["rendy-core/metal"] 24 | no-slow-safety-checks = ["rendy-core/no-slow-safety-checks"] 25 | profiler = ["thread_profiler/thread_profiler"] 26 | 27 | [dependencies] 28 | rendy-memory = { version = "0.5.2", path = "../memory" } 29 | rendy-resource = { version = "0.5.1", path = "../resource" } 30 | rendy-command = { version = "0.5.1", path = "../command" } 31 | rendy-descriptor = { version = "0.5.1", path = "../descriptor" } 32 | rendy-wsi = { version = "0.5.1", path = "../wsi" } 33 | rendy-core = { version = "0.5.1", path = "../core" } 34 | 35 | either = "1.0" 36 | log = "0.4" 37 | parking_lot = "0.9" 38 | relevant = { version = "0.4", features = ["log"] } 39 | serde = { version = "1.0", optional = true, features = ["derive"] } 40 | smallvec = "1.0" 41 | thread_profiler = "0.3" 42 | -------------------------------------------------------------------------------- /graph/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-graph" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-graph" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | categories = ["rendering"] 11 | description = "Rendy's render graph" 12 | 13 | [features] 14 | empty = ["rendy-core/empty"] 15 | dx12 = ["rendy-core/dx12"] 16 | gl = ["rendy-core/gl"] 17 | metal = ["rendy-core/metal"] 18 | vulkan = ["rendy-core/vulkan"] 19 | no-slow-safety-checks = ["rendy-core/no-slow-safety-checks"] 20 | profiler = ["thread_profiler/thread_profiler"] 21 | 22 | [dependencies] 23 | rendy-chain = { version = "0.5.1", path = "../chain" } 24 | rendy-command = { version = "0.5.1", path = "../command" } 25 | rendy-descriptor = { version = "0.5.1", path = "../descriptor" } 26 | rendy-factory = { version = "0.5.1", path = "../factory" } 27 | rendy-frame = { version = "0.5.1", path = "../frame" } 28 | rendy-memory = { version = "0.5.2", path = "../memory" } 29 | rendy-resource = { version = "0.5.1", path = "../resource" } 30 | rendy-core = { version = "0.5.1", path = "../core" } 31 | rendy-wsi = { version = "0.5.1", path = "../wsi" } 32 | rendy-shader = { version = "0.5.1", path = "../shader" } 33 | 34 | either = "1.5" 35 | bitflags = "1.0" 36 | log = "0.4" 37 | relevant = { version = "0.4", features = ["log"] } 38 | serde = { version = "1.0", optional = true, features = ["derive"] } 39 | smallvec = "1.0" 40 | thread_profiler = "0.3" 41 | -------------------------------------------------------------------------------- /memory/src/allocator/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module provides `Allocator` trait and few allocators that implements the trait. 2 | 3 | mod dedicated; 4 | mod dynamic; 5 | mod linear; 6 | 7 | use crate::block::Block; 8 | 9 | pub use self::{ 10 | dedicated::{DedicatedAllocator, DedicatedBlock}, 11 | dynamic::{DynamicAllocator, DynamicBlock, DynamicConfig}, 12 | linear::{LinearAllocator, LinearBlock, LinearConfig}, 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: &B::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: &B::Device, block: Self::Block) -> u64; 50 | } 51 | -------------------------------------------------------------------------------- /resource/src/sampler/mod.rs: -------------------------------------------------------------------------------- 1 | //! Sampler creation-info and wrappers. 2 | 3 | mod cache; 4 | 5 | use { 6 | crate::core::{device_owned, Device, DeviceId}, 7 | relevant::Relevant, 8 | rendy_core::hal::{device::Device as _, image::SamplerDesc, Backend}, 9 | }; 10 | 11 | pub use crate::sampler::cache::SamplerCache; 12 | 13 | /// Generic sampler resource wrapper. 14 | #[derive(Debug)] 15 | pub struct Sampler { 16 | device: DeviceId, 17 | raw: B::Sampler, 18 | info: SamplerDesc, 19 | relevant: Relevant, 20 | } 21 | 22 | device_owned!(Sampler); 23 | 24 | impl Sampler 25 | where 26 | B: Backend, 27 | { 28 | /// Create new sampler. 29 | pub fn create( 30 | device: &Device, 31 | info: SamplerDesc, 32 | ) -> Result { 33 | // TODO: Check info is valid. 34 | let raw = unsafe { device.create_sampler(&info) }?; 35 | Ok(Sampler { 36 | device: device.id(), 37 | raw, 38 | info, 39 | relevant: Relevant, 40 | }) 41 | } 42 | 43 | /// Destroy sampler resource. 44 | pub unsafe fn dispose(self, device: &Device) { 45 | self.assert_device_owner(device); 46 | device.destroy_sampler(self.raw); 47 | self.relevant.dispose(); 48 | } 49 | 50 | /// Get reference to raw sampler resource. 51 | pub fn raw(&self) -> &B::Sampler { 52 | &self.raw 53 | } 54 | 55 | /// Get mutable reference to raw sampler resource. 56 | pub unsafe fn raw_mut(&mut self) -> &mut B::Sampler { 57 | &mut self.raw 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Crate that contains utility modules used by other rendy crates 2 | 3 | #![warn( 4 | missing_debug_implementations, 5 | missing_copy_implementations, 6 | missing_docs, 7 | trivial_casts, 8 | trivial_numeric_casts, 9 | unused_extern_crates, 10 | unused_import_braces, 11 | unused_qualifications 12 | )] 13 | 14 | pub use crate::{backend::*, casts::*, slow::*, wrap::*}; 15 | 16 | #[doc(inline)] 17 | pub use gfx_hal as hal; 18 | 19 | #[cfg(all( 20 | feature = "dx12", 21 | all(target_os = "windows", not(target_arch = "wasm32")) 22 | ))] 23 | #[doc(inline)] 24 | pub use gfx_backend_dx12 as dx12; 25 | 26 | #[cfg(feature = "gl")] 27 | #[doc(inline)] 28 | pub use gfx_backend_gl as gl; 29 | 30 | #[cfg(feature = "gfx-backend-empty")] 31 | #[doc(inline)] 32 | pub use gfx_backend_empty as empty; 33 | 34 | #[cfg(all( 35 | feature = "metal", 36 | any( 37 | all(not(target_arch = "wasm32"), target_os = "macos"), 38 | all(target_arch = "aarch64", target_os = "ios") 39 | ) 40 | ))] 41 | #[doc(inline)] 42 | pub use gfx_backend_metal as metal; 43 | 44 | #[cfg(all( 45 | feature = "vulkan", 46 | all( 47 | any( 48 | target_os = "windows", 49 | all(unix, not(any(target_os = "macos", target_os = "ios"))) 50 | ), 51 | not(target_arch = "wasm32") 52 | ) 53 | ))] 54 | #[doc(inline)] 55 | pub use gfx_backend_vulkan as vulkan; 56 | 57 | #[doc(inline)] 58 | pub use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; 59 | 60 | #[macro_use] 61 | mod backend; 62 | 63 | #[macro_use] 64 | mod features; 65 | mod casts; 66 | mod slow; 67 | pub mod types; 68 | mod wrap; 69 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.3.2 4 | 5 | * Add dyn group api to subpass builder ([#169]) 6 | * Removed uses of FnvHashMap for Rust standard library's HashMap ([#170]) 7 | * Fix potentially unsound Factory::drop ([#173]) 8 | * Check that surface presentation supported by the queue ([#177]) 9 | 10 | ## 0.3.1 11 | 12 | * Fix short-circuting problem in render pass 13 | 14 | ## 0.3 15 | 16 | * Remove waiting on zero fences ([#138]) 17 | * Raw string source shader support ([#141]) 18 | * Fix memory allocator asserts ([#155], [#156]) 19 | * Add frames in flight to graph context ([#146]) 20 | * Fix double-panic on resource destruction ([#150]) 21 | * Add more ways to construct DescriptorRanges ([#153]) 22 | * Reduce logging level ([#161]) 23 | * Allow render pass to use surface as an attachment ([#164]) 24 | * Add `premultiply_alpha` option to `ImageTextureConfig` ([#143]) 25 | * Mark potentially unsafe command recording methods ([#165]) 26 | 27 | [#138]: https://github.com/amethyst/rendy/pull/138 28 | [#141]: https://github.com/amethyst/rendy/pull/141 29 | [#143]: https://github.com/amethyst/rendy/pull/143 30 | [#146]: https://github.com/amethyst/rendy/pull/146 31 | [#150]: https://github.com/amethyst/rendy/pull/150 32 | [#153]: https://github.com/amethyst/rendy/pull/153 33 | [#155]: https://github.com/amethyst/rendy/pull/155 34 | [#156]: https://github.com/amethyst/rendy/pull/156 35 | [#161]: https://github.com/amethyst/rendy/pull/161 36 | [#164]: https://github.com/amethyst/rendy/pull/164 37 | [#165]: https://github.com/amethyst/rendy/pull/165 38 | [#169]: https://github.com/amethyst/rendy/pull/169 39 | [#170]: https://github.com/amethyst/rendy/pull/170 40 | [#173]: https://github.com/amethyst/rendy/pull/173 41 | [#177]: https://github.com/amethyst/rendy/pull/177 42 | -------------------------------------------------------------------------------- /command/src/buffer/state.rs: -------------------------------------------------------------------------------- 1 | use super::usage::{MultiShot, OutsideRenderPass}; 2 | 3 | /// Command buffer state in which all buffers start. 4 | /// Resetting also moves buffer to this state. 5 | #[derive(Clone, Copy, Debug, Default)] 6 | pub struct InitialState; 7 | 8 | /// Command buffer in recording state could be populated with commands. 9 | #[derive(Clone, Copy, Debug, Default)] 10 | pub struct RecordingState(pub U, pub P); 11 | 12 | /// Command buffer in executable state can be submitted. 13 | #[derive(Clone, Copy, Debug, Default)] 14 | pub struct ExecutableState(pub U, pub P); 15 | 16 | /// Command buffer in pending state are submitted to the device. 17 | /// Command buffer in pending state must never be invalidated or reset because device may read it at the moment. 18 | /// Proving device is done with buffer requires nontrivial strategies. 19 | /// Therefore moving buffer from pending state requires `unsafe` method. 20 | #[derive(Clone, Copy, Debug, Default)] 21 | pub struct PendingState(pub N); 22 | 23 | /// Command buffer in pending state are submitted to the device. 24 | /// Command buffer in pending state must never be invalidated or reset because device may read it at the moment. 25 | /// Proving device is done with buffer requires nontrivial strategies. 26 | /// Therefore moving buffer from pending state requires `unsafe` method. 27 | /// This type alias can be used for one-shot command buffers. 28 | pub type PendingOnceState = PendingState; 29 | 30 | /// One-shot buffers move to invalid state after execution. 31 | /// Invalidating any resource referenced in any command recorded to the buffer implicitly move it to the invalid state. 32 | #[derive(Clone, Copy, Debug, Default)] 33 | pub struct InvalidState; 34 | -------------------------------------------------------------------------------- /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 | if let Some(f) = self.flush.take() { 39 | f(); 40 | } 41 | } 42 | } 43 | 44 | impl<'a, T, F> Write for WriteFlush<'a, T, F> 45 | where 46 | T: Copy + 'a, 47 | F: FnOnce() + 'a, 48 | { 49 | /// # Safety 50 | /// 51 | /// [See doc comment for trait method](trait.Write#method.slice) 52 | unsafe fn slice(&mut self) -> &mut [T] { 53 | self.slice 54 | } 55 | } 56 | 57 | #[warn(dead_code)] 58 | #[derive(Debug)] 59 | pub(super) struct WriteCoherent<'a, T> { 60 | pub(super) slice: &'a mut [T], 61 | } 62 | 63 | impl<'a, T> Write for WriteCoherent<'a, T> 64 | where 65 | T: Copy + 'a, 66 | { 67 | /// # Safety 68 | /// 69 | /// [See doc comment for trait method](trait.Write#method.slice) 70 | unsafe fn slice(&mut self) -> &mut [T] { 71 | self.slice 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /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 | #![warn( 5 | missing_debug_implementations, 6 | missing_copy_implementations, 7 | missing_docs, 8 | trivial_casts, 9 | trivial_numeric_casts, 10 | unused_extern_crates, 11 | unused_import_braces, 12 | unused_qualifications 13 | )] 14 | use rendy_core as core; 15 | use rendy_descriptor as descriptor; 16 | use rendy_memory as memory; 17 | 18 | mod buffer; 19 | mod escape; 20 | mod image; 21 | mod set; 22 | 23 | mod resources; 24 | mod sampler; 25 | 26 | pub use crate::{buffer::*, escape::*, image::*, resources::*, sampler::*, set::*}; 27 | 28 | /// Error creating a resource. 29 | #[derive(Clone, Debug, PartialEq)] 30 | pub enum CreationError { 31 | /// Failed to create an object. 32 | Create(E), 33 | /// Failed to allocate memory. 34 | Allocate(memory::HeapsError), 35 | /// Failed to bind object memory. 36 | Bind(rendy_core::hal::device::BindError), 37 | } 38 | 39 | impl std::fmt::Display for CreationError 40 | where 41 | E: std::fmt::Debug, 42 | { 43 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 44 | match self { 45 | CreationError::Create(_err) => write!(fmt, "Failed to create object"), // Uncomment after gfx-0.4.1 std::fmt::Display::fmt(err, fmt), 46 | CreationError::Allocate(err) => write!(fmt, "Failed to create object: {}", err), 47 | CreationError::Bind(err) => write!(fmt, "Failed to create object: {:?}", err), 48 | } 49 | } 50 | } 51 | 52 | impl std::error::Error for CreationError 53 | where 54 | E: std::error::Error + 'static, 55 | { 56 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 57 | match self { 58 | CreationError::Create(err) => Some(err), 59 | CreationError::Allocate(err) => Some(err), 60 | CreationError::Bind(err) => Some(err), 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /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: rendy_core::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: rendy_core::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) -> rendy_core::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 | -------------------------------------------------------------------------------- /core/src/casts.rs: -------------------------------------------------------------------------------- 1 | //! Contains functions for casting 2 | use std::{any::TypeId, borrow::Cow}; 3 | 4 | /// Cast vec of some arbitrary type into vec of bytes. 5 | /// Can lead to UB if allocator changes. Use with caution. 6 | /// TODO: Replace with something safer. 7 | pub fn cast_vec(mut vec: Vec) -> Vec { 8 | let len = std::mem::size_of::() * vec.len(); 9 | let cap = std::mem::size_of::() * vec.capacity(); 10 | let ptr = vec.as_mut_ptr(); 11 | std::mem::forget(vec); 12 | unsafe { Vec::from_raw_parts(ptr as _, len, cap) } 13 | } 14 | 15 | /// Cast slice of some arbitrary type into slice of bytes. 16 | pub fn cast_slice(slice: &[T]) -> &[u8] { 17 | let len = std::mem::size_of::() * slice.len(); 18 | let ptr = slice.as_ptr(); 19 | unsafe { std::slice::from_raw_parts(ptr as _, len) } 20 | } 21 | 22 | /// Cast `cow` of some arbitrary type into `cow` of bytes. 23 | /// Can lead to UB if allocator changes. Use with caution. 24 | /// TODO: Replace with something safer. 25 | pub fn cast_cow(cow: Cow<'_, [T]>) -> Cow<'_, [u8]> { 26 | match cow { 27 | Cow::Borrowed(slice) => Cow::Borrowed(cast_slice(slice)), 28 | Cow::Owned(vec) => Cow::Owned(cast_vec(vec)), 29 | } 30 | } 31 | 32 | /// Casts identical types. 33 | /// Useful in generic environment where caller knows that two types are the same 34 | /// but Rust is not convinced. 35 | /// 36 | /// # Panics 37 | /// 38 | /// Panics if types are actually different. 39 | /// 40 | /// # Example 41 | /// 42 | /// ``` 43 | /// # extern crate rendy_core; 44 | /// # use rendy_core::identical_cast; 45 | /// # use std::any::TypeId; 46 | /// # fn foo() { 47 | /// if TypeId::of::() == TypeId::of::() { 48 | /// let value: T = identical_cast(42u32); 49 | /// } 50 | /// # } 51 | /// 52 | /// ``` 53 | pub fn identical_cast(value: T) -> U { 54 | assert_eq!(TypeId::of::(), TypeId::of::()); 55 | unsafe { 56 | // We know types are the same. 57 | let mut value = std::mem::ManuallyDrop::new(value); 58 | let ptr: *mut T = &mut *value; 59 | std::ptr::read(ptr as *mut U) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /mesh/README.md: -------------------------------------------------------------------------------- 1 | 2 | # `rendy-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 have semantics attached to field names. A common example is for a 9 | vertex to be composed of a position, normal, color and texture coordinate field. 10 | This crate provides traits and types to have semantics explicitly defined for a vertex at the type level. 11 | 12 | `Position`, `Normal`, `TexCoord` etc. are attributes that have unambiguous semantics. 13 | Users can define their own attribute types by implementing the `Attribute` trait. 14 | 15 | While the attribute type on its own is a trivial vertex format (with single attribute), complex vertex formats are created by composing attribute types. 16 | 17 | The `WithAttribute` trait allows to get formatting info for individual attributes defined in a vertex format. 18 | The `Query` trait allows to get formatting info for several attributes at once. 19 | 20 | `VertexFormat` queried from vertex formats can be used to build graphics pipelines and bind required vertex buffers from mesh to command buffer. 21 | 22 | To define a custom vertex format type, the `AsVertexFormat` trait must be implemented providing a `VertexFormat` associated constant. 23 | 24 | `WithAttribute` can be implemented also for all attributes and the `VertexFormat` associated constant in `AsVertexFormat` can be defined more clearly by utilizing the `WithAttribute` implementation. `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 | -------------------------------------------------------------------------------- /resource/src/sampler/cache.rs: -------------------------------------------------------------------------------- 1 | //! A cache to store and retrieve samplers 2 | 3 | use { 4 | super::Sampler, 5 | crate::escape::Handle, 6 | rendy_core::hal::{image::SamplerDesc, Backend}, 7 | std::{ 8 | collections::hash_map::{Entry, HashMap}, 9 | ops::{Deref, DerefMut}, 10 | }, 11 | }; 12 | 13 | /// Sampler cache holds handlers to created samplers. 14 | #[derive(Debug)] 15 | pub struct SamplerCache { 16 | samplers: HashMap>>, 17 | } 18 | 19 | impl Default for SamplerCache 20 | where 21 | B: Backend, 22 | { 23 | fn default() -> Self { 24 | SamplerCache { 25 | samplers: HashMap::default(), 26 | } 27 | } 28 | } 29 | 30 | impl SamplerCache 31 | where 32 | B: Backend, 33 | { 34 | /// Get sampler with specified paramters. 35 | /// Create new one using closure provided. 36 | pub fn get( 37 | &mut self, 38 | info: SamplerDesc, 39 | create: impl FnOnce() -> Result>, rendy_core::hal::device::AllocationError>, 40 | ) -> Result>, rendy_core::hal::device::AllocationError> { 41 | Ok(match self.samplers.entry(info) { 42 | Entry::Occupied(occupied) => occupied.get().clone(), 43 | Entry::Vacant(vacant) => { 44 | let sampler = create()?; 45 | vacant.insert(sampler).clone() 46 | } 47 | }) 48 | } 49 | 50 | /// Get sampler with specified paramters. 51 | /// Create new one using closure provided. 52 | /// Does not lock for writing if sampler exists. 53 | pub fn get_with_upgradable_lock( 54 | read: R, 55 | upgrade: U, 56 | info: SamplerDesc, 57 | create: impl FnOnce() -> Result>, rendy_core::hal::device::AllocationError>, 58 | ) -> Result>, rendy_core::hal::device::AllocationError> 59 | where 60 | R: Deref, 61 | W: DerefMut, 62 | U: FnOnce(R) -> W, 63 | { 64 | if let Some(sampler) = read.samplers.get(&info) { 65 | return Ok(sampler.clone()); 66 | } 67 | let sampler = create()?; 68 | { 69 | upgrade(read).samplers.insert(info, sampler.clone()); 70 | } 71 | Ok(sampler) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy-core" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy-core" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | categories = ["rendering"] 11 | description = "Rendy's utilities" 12 | 13 | [features] 14 | serde-1 = ["serde", "gfx-hal/serde"] 15 | 16 | # This list of features is common for many of rendy's crates 17 | # All other crates should transitively enable feature for rendy-core crate 18 | # and not rely on the feature being enabled for that crate directly. 19 | # To conditionally enable token trees `rendy-core::with_*` macro should be used instead of 20 | # `cfg` attributes. 21 | empty = ["gfx-backend-empty"] 22 | dx12 = ["gfx-backend-dx12"] 23 | metal = ["gfx-backend-metal"] 24 | gl = ["gfx-backend-gl"] 25 | vulkan = ["gfx-backend-vulkan"] 26 | no-slow-safety-checks = [] 27 | 28 | [dependencies] 29 | gfx-hal = { git = "https://github.com/gfx-rs/gfx", rev = "3641183231f16877d4ea2fbdb2ff208ce736d6c4" } 30 | gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", rev = "3641183231f16877d4ea2fbdb2ff208ce736d6c4", optional = true } 31 | gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "3641183231f16877d4ea2fbdb2ff208ce736d6c4", features = ["glutin"], optional = true } 32 | lazy_static = "1.0" 33 | log = "0.4" 34 | parking_lot = "0.9" 35 | serde = { version = "1.0", optional = true, features = ["derive"] } 36 | thread_profiler = "0.3" 37 | raw-window-handle = "0.3" 38 | 39 | [target.'cfg(all(target_os = "windows", not(target_arch = "wasm32")))'.dependencies] 40 | gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx", rev = "3641183231f16877d4ea2fbdb2ff208ce736d6c4", optional = true } 41 | 42 | [target.'cfg(any(all(not(target_arch = "wasm32"), target_os = "macos"), all(target_arch = "aarch64", target_os = "ios")))'.dependencies] 43 | gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx", rev = "3641183231f16877d4ea2fbdb2ff208ce736d6c4", optional = true } 44 | 45 | [target.'cfg(all(any(target_os = "windows", all(unix, not(any(target_os = "macos", target_os = "ios")))), not(target_arch = "wasm32")))'.dependencies] 46 | gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "3641183231f16877d4ea2fbdb2ff208ce736d6c4", features = ["x11"], optional = true } 47 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /frame/src/cirque/command.rs: -------------------------------------------------------------------------------- 1 | use { 2 | super::*, 3 | crate::command::{ 4 | Capability, CommandBuffer, CommandPool, ExecutableState, IndividualReset, InitialState, 5 | Level, MultiShot, NoSimultaneousUse, OutsideRenderPass, PendingState, PrimaryLevel, 6 | RenderPassRelation, Submit, 7 | }, 8 | }; 9 | 10 | /// 11 | pub type CommandCirque = Cirque< 12 | CommandBuffer, L, IndividualReset>, 13 | CommandBuffer, 14 | CommandBuffer>, L, IndividualReset>, 15 | >; 16 | 17 | /// 18 | pub type CommandCirqueRef<'a, B, C, P = OutsideRenderPass, L = PrimaryLevel> = CirqueRef< 19 | 'a, 20 | CommandBuffer, L, IndividualReset>, 21 | CommandBuffer, 22 | CommandBuffer>, L, IndividualReset>, 23 | >; 24 | 25 | /// 26 | pub type CommandInitialRef<'a, B, C, P = OutsideRenderPass, L = PrimaryLevel> = InitialRef< 27 | 'a, 28 | CommandBuffer, L, IndividualReset>, 29 | CommandBuffer, 30 | CommandBuffer>, L, IndividualReset>, 31 | >; 32 | 33 | /// 34 | pub type CommandReadyRef<'a, B, C, P = OutsideRenderPass, L = PrimaryLevel> = ReadyRef< 35 | 'a, 36 | CommandBuffer, L, IndividualReset>, 37 | CommandBuffer, 38 | CommandBuffer>, L, IndividualReset>, 39 | >; 40 | 41 | impl CommandCirque 42 | where 43 | B: rendy_core::hal::Backend, 44 | L: Level, 45 | C: Capability, 46 | P: RenderPassRelation, 47 | { 48 | /// Encode and submit. 49 | pub fn encode<'a>( 50 | &'a mut self, 51 | frames: &Frames, 52 | pool: &mut CommandPool, 53 | encode: impl FnOnce(CommandCirqueRef<'a, B, C, P, L>) -> CommandReadyRef<'a, B, C, P, L>, 54 | ) -> Submit { 55 | let cr = self.get( 56 | frames, 57 | || pool.allocate_buffers(1).pop().unwrap(), 58 | |pending| unsafe { pending.mark_complete() }, 59 | ); 60 | 61 | let ready = encode(cr); 62 | 63 | let mut slot = None; 64 | 65 | ready.finish(|executable| { 66 | let (submit, pending) = executable.submit(); 67 | slot = Some(submit); 68 | pending 69 | }); 70 | 71 | slot.unwrap() 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /texture/src/format/palette.rs: -------------------------------------------------------------------------------- 1 | //! Module that generates functions to load a single color into a 1x1 `Texture`. Which function 2 | //! to use depends on what color space the user is using. 3 | //! 4 | use crate::{pixel, texture::TextureBuilder}; 5 | use palette::{white_point::WhitePoint, Component}; 6 | 7 | macro_rules! define_load { 8 | (@swizzle NO) => { rendy_core::hal::format::Swizzle::NO }; 9 | (@swizzle ($r:ident, $g:ident, $b:ident, $a:ident)) => { 10 | rendy_core::hal::format::Swizzle( 11 | rendy_core::hal::format::Component::$r, 12 | rendy_core::hal::format::Component::$g, 13 | rendy_core::hal::format::Component::$b, 14 | rendy_core::hal::format::Component::$a, 15 | ) 16 | }; 17 | ($(pub fn $load_fn:ident<$($ty:ident: $where:path),*>($palette:ident) -> $pixel:ident $swizzle:tt;)*) => {$( 18 | /// Function to load texture from `palette` pixels. 19 | pub fn $load_fn<$($ty),*>( 20 | palette: palette::$palette<$($ty),*>, 21 | ) -> TextureBuilder<'static> 22 | where 23 | $($ty: $where),*, 24 | palette::$palette<$($ty),*>: Into, 25 | { 26 | TextureBuilder::new() 27 | .with_kind(rendy_core::hal::image::Kind::D2(1, 1, 1, 1)) 28 | .with_view_kind(rendy_core::hal::image::ViewKind::D2) 29 | .with_data_width(1) 30 | .with_data_height(1) 31 | .with_data(vec![palette.into()]) 32 | .with_swizzle(define_load!(@swizzle $swizzle)) 33 | } 34 | )*}; 35 | } 36 | 37 | define_load! { 38 | pub fn load_from_srgb(Srgb) -> Rgb8Srgb NO; 39 | pub fn load_from_srgba(Srgba) -> Rgba8Srgb NO; 40 | pub fn load_from_linear_rgb(LinSrgb) -> Rgb8Unorm NO; 41 | pub fn load_from_linear_rgba(LinSrgba) -> Rgba8Unorm NO; 42 | pub fn load_from_linear_rgb_u16(LinSrgb) -> Rgb16Unorm NO; 43 | pub fn load_from_linear_rgba_u16(LinSrgba) -> Rgba16Unorm NO; 44 | pub fn load_from_linear_rgb_f32(LinSrgb) -> Rgb32Sfloat NO; 45 | pub fn load_from_linear_rgba_f32(LinSrgba) -> Rgba32Sfloat NO; 46 | pub fn load_from_luma(SrgbLuma) -> R8Srgb (R, R, R, One); 47 | pub fn load_from_lumaa(SrgbLumaa) -> Rg8Srgb (R, R, R, G); 48 | pub fn load_from_linear_luma(LinLuma) -> R8Unorm (R, R, R, One); 49 | pub fn load_from_linear_lumaa(LinLumaa) -> Rg8Unorm (R, R, R, G); 50 | pub fn load_from_linear_luma_f32(LinLuma) -> R32Sfloat (R, R, R, One); 51 | pub fn load_from_linear_lumaa_f32(LinLumaa) -> Rg32Sfloat (R, R, R, G); 52 | } 53 | -------------------------------------------------------------------------------- /rendy/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Rendy's top level crate. 2 | //! Reexports all others. 3 | 4 | #![warn( 5 | missing_debug_implementations, 6 | missing_copy_implementations, 7 | missing_docs, 8 | trivial_casts, 9 | trivial_numeric_casts, 10 | unused_extern_crates, 11 | unused_import_braces, 12 | unused_qualifications 13 | )] 14 | 15 | #[doc(inline)] 16 | pub use rendy_core as core; 17 | 18 | pub use crate::core::hal; 19 | 20 | rendy_core::rendy_with_empty_backend! { pub use crate::core::empty; } 21 | rendy_core::rendy_with_dx12_backend! { pub use crate::core::dx12; } 22 | rendy_core::rendy_with_gl_backend! { pub use crate::core::gl; } 23 | rendy_core::rendy_with_metal_backend! { pub use crate::core::metal; } 24 | rendy_core::rendy_with_vulkan_backend! { pub use crate::core::vulkan; } 25 | 26 | #[cfg(feature = "command")] 27 | #[doc(inline)] 28 | pub use rendy_command as command; 29 | 30 | #[cfg(feature = "descriptor")] 31 | #[doc(inline)] 32 | pub use rendy_descriptor as descriptor; 33 | 34 | #[cfg(feature = "factory")] 35 | #[doc(inline)] 36 | pub use rendy_factory as factory; 37 | 38 | #[cfg(feature = "frame")] 39 | #[doc(inline)] 40 | pub use rendy_frame as frame; 41 | 42 | #[cfg(feature = "graph")] 43 | #[doc(inline)] 44 | pub use rendy_graph as graph; 45 | 46 | #[cfg(feature = "init")] 47 | #[doc(inline)] 48 | pub use rendy_init as init; 49 | 50 | #[cfg(feature = "memory")] 51 | #[doc(inline)] 52 | pub use rendy_memory as memory; 53 | 54 | #[cfg(feature = "mesh")] 55 | #[doc(inline)] 56 | pub use rendy_mesh as mesh; 57 | 58 | #[cfg(feature = "resource")] 59 | #[doc(inline)] 60 | pub use rendy_resource as resource; 61 | 62 | #[cfg(feature = "shader")] 63 | #[doc(inline)] 64 | pub use rendy_shader as shader; 65 | 66 | #[cfg(feature = "texture")] 67 | #[doc(inline)] 68 | pub use rendy_texture as texture; 69 | 70 | #[cfg(feature = "wsi")] 71 | #[doc(inline)] 72 | pub use rendy_wsi as wsi; 73 | 74 | /// Init rendy and execute code based on chosen backend 75 | #[cfg(feature = "init")] 76 | #[macro_export] 77 | macro_rules! with_any_rendy { 78 | (($rendy:expr) $(use $back:ident;)?($factory:pat, $families:pat) => $code:block) => {{ 79 | $crate::core::rendy_backend!(match ($rendy): $crate::init::AnyRendy { 80 | $(use $back;)?_($crate::init::Rendy { factory: $factory, families: $families }) => { $code } 81 | }) 82 | }} 83 | } 84 | 85 | /// Init rendy and execute code based on chosen backend 86 | #[cfg(feature = "init")] 87 | #[macro_export] 88 | macro_rules! with_any_windowed_rendy { 89 | (($rendy:expr) $(use $back:ident;)? ($factory:pat, $families:pat, $surface:pat, $window:pat) => $code:block) => {{ 90 | $crate::core::rendy_backend!(match ($rendy): $crate::init::AnyWindowedRendy { 91 | $(use $back;)?_($crate::init::WindowedRendy { factory: $factory, families: $families, surface: $surface, window: $window }) => { $code } 92 | }) 93 | }} 94 | } 95 | -------------------------------------------------------------------------------- /resource/src/resources.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::escape::{Escape, Handle, Terminal}, 3 | smallvec::SmallVec, 4 | std::collections::VecDeque, 5 | }; 6 | 7 | /// Resource usage epochs. 8 | #[doc(hidden)] 9 | #[derive(Clone, Debug)] 10 | pub struct Epochs { 11 | /// 2D array of epochs. 12 | pub values: SmallVec<[SmallVec<[u64; 8]>; 4]>, 13 | } 14 | 15 | impl Epochs { 16 | fn is_before(&self, other: &Self) -> bool { 17 | debug_assert_eq!(self.values.len(), other.values.len()); 18 | self.values.iter().zip(other.values.iter()).all(|(a, b)| { 19 | debug_assert_eq!(a.len(), b.len()); 20 | a.iter().zip(b.iter()).all(|(a, b)| a < b) 21 | }) 22 | } 23 | } 24 | 25 | /// Resource handler. 26 | #[derive(Debug)] 27 | pub struct ResourceTracker { 28 | terminal: Terminal, 29 | dropped: VecDeque<(Epochs, T)>, 30 | } 31 | 32 | impl Default for ResourceTracker { 33 | fn default() -> Self { 34 | ResourceTracker { 35 | terminal: Terminal::default(), 36 | dropped: VecDeque::default(), 37 | } 38 | } 39 | } 40 | 41 | impl ResourceTracker { 42 | /// Create new resource manager. 43 | pub fn new() -> Self { 44 | Self::default() 45 | } 46 | 47 | /// Wrap resource instance into `Escape`. 48 | pub fn escape(&self, resource: T) -> Escape 49 | where 50 | T: Sized, 51 | { 52 | Escape::escape(resource, &self.terminal) 53 | } 54 | 55 | /// Wrap resource instance into `Handle`. 56 | pub fn handle(&self, resource: T) -> Handle 57 | where 58 | T: Sized, 59 | { 60 | self.escape(resource).into() 61 | } 62 | 63 | /// Cleanup dropped resources. 64 | /// 65 | /// # Safety 66 | /// 67 | /// `next` epochs must contain epoch indices that aren't started yet 68 | /// `complete` epochs must contain epoch indices that are complete. 69 | /// Can be guaranteed with fence wait. 70 | /// 71 | pub fn cleanup(&mut self, mut dispose: impl FnMut(T), next: &Epochs, complete: &Epochs) { 72 | while let Some((epoch, resource)) = self.dropped.pop_front() { 73 | if !epoch.is_before(complete) { 74 | self.dropped.push_front((epoch, resource)); 75 | break; 76 | } 77 | 78 | dispose(resource); 79 | } 80 | 81 | self.dropped 82 | .extend(self.terminal.drain().map(|res| (next.clone(), res))); 83 | } 84 | 85 | /// Cleanup all dropped resources. 86 | /// 87 | /// # Safety 88 | /// 89 | /// All dropped resources must be unused. 90 | /// Can be guaranteed with device idle wait. 91 | /// 92 | pub fn dispose(&mut self, dispose: impl FnMut(T)) { 93 | self.dropped 94 | .drain(..) 95 | .map(|(_, res)| res) 96 | .chain(self.terminal.drain()) 97 | .for_each(dispose); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /command/src/family/submission.rs: -------------------------------------------------------------------------------- 1 | use crate::buffer::{NoSimultaneousUse, OutsideRenderPass, PrimaryLevel, Submit, Submittable}; 2 | 3 | #[allow(unused)] 4 | type NoWaits = std::iter::Empty<( 5 | &'static ::Semaphore, 6 | rendy_core::hal::pso::PipelineStage, 7 | )>; 8 | #[allow(unused)] 9 | type NoSignals = std::iter::Empty<&'static ::Semaphore>; 10 | #[allow(unused)] 11 | type NoSubmits = std::iter::Empty>; 12 | 13 | /// Command queue submission. 14 | #[derive(Debug)] 15 | pub struct Submission, C = NoSubmits, S = NoSignals> { 16 | /// Iterator over semaphores with stage flag to wait on. 17 | pub waits: W, 18 | 19 | /// Iterator over submittables. 20 | pub submits: C, 21 | 22 | /// Iterator over semaphores to signal. 23 | pub signals: S, 24 | 25 | /// Marker type for submission backend. 26 | pub marker: std::marker::PhantomData B>, 27 | } 28 | 29 | impl Submission 30 | where 31 | B: rendy_core::hal::Backend, 32 | { 33 | /// Create new empty submission. 34 | pub fn new() -> Self { 35 | Submission { 36 | waits: std::iter::empty(), 37 | submits: std::iter::empty(), 38 | signals: std::iter::empty(), 39 | marker: std::marker::PhantomData, 40 | } 41 | } 42 | } 43 | 44 | impl Submission, S> 45 | where 46 | B: rendy_core::hal::Backend, 47 | { 48 | /// Add submits to the submission. 49 | pub fn submits(self, submits: C) -> Submission 50 | where 51 | C: IntoIterator, 52 | C::Item: Submittable, 53 | { 54 | Submission { 55 | waits: self.waits, 56 | submits, 57 | signals: self.signals, 58 | marker: self.marker, 59 | } 60 | } 61 | } 62 | 63 | impl Submission, C, S> 64 | where 65 | B: rendy_core::hal::Backend, 66 | { 67 | /// Add waits to the submission. 68 | pub fn wait<'a, W, E>(self, waits: W) -> Submission 69 | where 70 | W: IntoIterator, 71 | E: std::borrow::Borrow + 'a, 72 | { 73 | Submission { 74 | waits, 75 | submits: self.submits, 76 | signals: self.signals, 77 | marker: self.marker, 78 | } 79 | } 80 | } 81 | 82 | impl Submission> 83 | where 84 | B: rendy_core::hal::Backend, 85 | { 86 | /// Add signals to the submission. 87 | pub fn signal<'a, S, E>(self, signals: S) -> Submission 88 | where 89 | S: IntoIterator, 90 | E: std::borrow::Borrow + 'a, 91 | { 92 | Submission { 93 | waits: self.waits, 94 | submits: self.submits, 95 | signals, 96 | marker: self.marker, 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /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 | use std::collections::HashMap; 8 | 9 | mod link; 10 | 11 | use std::ops::BitOr; 12 | 13 | use crate::{ 14 | resource::{Buffer, Image, Resource}, 15 | Id, 16 | }; 17 | 18 | pub use self::link::{Link, LinkNode}; 19 | 20 | /// This type corresponds to resource category. 21 | /// All resources from the same category must be accessed as permitted by links of the chain. 22 | #[derive(Clone, Debug)] 23 | pub struct Chain { 24 | links: Vec>, 25 | } 26 | 27 | impl Chain 28 | where 29 | R: Resource, 30 | { 31 | /// Get links slice 32 | pub fn links(&self) -> &[Link] { 33 | &self.links 34 | } 35 | 36 | /// Create new empty `Chain` 37 | pub fn new() -> Self { 38 | Chain { links: Vec::new() } 39 | } 40 | 41 | /// Get links slice 42 | pub fn last_link_mut(&mut self) -> Option<&mut Link> { 43 | self.links.last_mut() 44 | } 45 | 46 | /// Add new link to the chain. 47 | pub fn add_link(&mut self, link: Link) -> &mut Link { 48 | self.links.push(link); 49 | self.links.last_mut().unwrap() 50 | } 51 | 52 | // /// Get link by index. 53 | // pub(crate) fn link(&self, index: usize) -> &Link { 54 | // &self.links[index] 55 | // } 56 | 57 | // /// Get link by index. 58 | // pub(crate) fn link_mut(&mut self, index: usize) -> &mut Link { 59 | // &mut self.links[index] 60 | // } 61 | 62 | // /// Get link by index. 63 | // pub(crate) fn next_link(&self, index: usize) -> &Link { 64 | // let index = (index + 1) % self.links.len(); 65 | // self.link(index) 66 | // } 67 | 68 | // /// Get link by index. 69 | // pub(crate) fn next_link_mut(&mut self, index: usize) -> &mut Link { 70 | // let index = (index + 1) % self.links.len(); 71 | // self.link_mut(index) 72 | // } 73 | 74 | // /// Get link by index. 75 | // pub(crate) fn prev_link(&self, index: usize) -> &Link { 76 | // let index = (index + self.links.len() - 1) % self.links.len(); 77 | // self.link(index) 78 | // } 79 | 80 | // /// Get link by index. 81 | // pub(crate) fn prev_link_mut(&mut self, index: usize) -> &mut Link { 82 | // let index = (index + self.links.len() - 1) % self.links.len(); 83 | // self.link_mut(index) 84 | // } 85 | 86 | /// Get total usage. 87 | pub fn usage(&self) -> R::Usage { 88 | self.links 89 | .iter() 90 | .map(Link::usage) 91 | .fold(R::no_usage(), BitOr::bitor) 92 | } 93 | } 94 | 95 | /// Type alias for map of chains by id for buffers. 96 | pub(crate) type BufferChains = HashMap>; 97 | 98 | /// Type alias for map of chains by id for images. 99 | pub(crate) type ImageChains = HashMap>; 100 | -------------------------------------------------------------------------------- /command/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate revolves around command recording and submission. 2 | 3 | #![warn( 4 | missing_debug_implementations, 5 | missing_copy_implementations, 6 | missing_docs, 7 | trivial_casts, 8 | trivial_numeric_casts, 9 | unused_extern_crates, 10 | unused_import_braces, 11 | unused_qualifications 12 | )] 13 | 14 | macro_rules! family_owned { 15 | ($type:ident @ $getter:expr) => { 16 | #[allow(unused_qualifications)] 17 | impl $type 18 | where 19 | B: rendy_core::hal::Backend, 20 | { 21 | /// Get owner id. 22 | pub fn family_id(&self) -> $crate::FamilyId { 23 | ($getter)(self) 24 | } 25 | 26 | /// Assert specified family is owner. 27 | pub fn assert_family_owner(&self, family: &$crate::Family) { 28 | assert_eq!(self.family_id(), family.id(), "Resource is not owned by specified family"); 29 | } 30 | 31 | /// Assert specified device is owner. 32 | pub fn assert_device_owner(&self, device: &$crate::core::Device) { 33 | assert_eq!(self.family_id().device, device.id(), "Resource is not owned by specified device"); 34 | } 35 | 36 | /// Assert specified instance is owner. 37 | pub fn assert_instance_owner(&self, instance: &$crate::core::Instance) { 38 | assert_eq!(self.family_id().device.instance, instance.id(), "Resource is not owned by specified instance"); 39 | } 40 | } 41 | }; 42 | 43 | ($type:ident) => { 44 | family_owned!($type @ |s: &Self| s.family); 45 | }; 46 | 47 | (@NOCAP $type:ident @ $getter:expr) => { 48 | #[allow(unused_qualifications)] 49 | impl $type 50 | where 51 | B: rendy_core::hal::Backend, 52 | { 53 | /// Get owner id. 54 | pub fn family_id(&self) -> $crate::FamilyId { 55 | ($getter)(self) 56 | } 57 | 58 | /// Assert specified family is owner. 59 | pub fn assert_family_owner(&self, family: &$crate::Family) { 60 | assert_eq!(self.family_id(), family.id(), "Resource is not owned by specified family"); 61 | } 62 | 63 | /// Assert specified device is owner. 64 | pub fn assert_device_owner(&self, device: &$crate::core::Device) { 65 | assert_eq!(self.family_id().device, device.id(), "Resource is not owned by specified device"); 66 | } 67 | 68 | /// Assert specified instance is owner. 69 | pub fn assert_instance_owner(&self, instance: &$crate::core::Instance) { 70 | assert_eq!(self.family_id().device.instance, instance.id(), "Resource is not owned by specified instance"); 71 | } 72 | } 73 | }; 74 | 75 | (@NOCAP $type:ident) => { 76 | family_owned!(@NOCAP $type @ |s: &Self| s.family); 77 | }; 78 | } 79 | 80 | use rendy_core as core; 81 | 82 | mod buffer; 83 | mod capability; 84 | mod family; 85 | mod fence; 86 | mod pool; 87 | 88 | pub use crate::{buffer::*, capability::*, family::*, fence::*, pool::*}; 89 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /command/src/buffer/usage.rs: -------------------------------------------------------------------------------- 1 | use super::SecondaryLevel; 2 | 3 | /// Command buffer with this usage flag will move to invalid state after execution. 4 | /// Resubmitting will require reset and rerecording commands. 5 | #[derive(Clone, Copy, Debug, Default)] 6 | pub struct OneShot; 7 | 8 | /// Command buffer with this usage flag will move back to executable state after execution. 9 | #[derive(Clone, Copy, Debug, Default)] 10 | pub struct MultiShot(pub S); 11 | 12 | /// Additional flag that allows resubmission of a command buffer while it is still in a pending state. 13 | /// `Submit` can be submitted more than once. 14 | #[derive(Clone, Copy, Debug, Default)] 15 | pub struct SimultaneousUse; 16 | 17 | /// Additional flag that disallows resubmission of a command buffer while it is still in a pending state 18 | /// It must be completed, i.e. a fence must submitted with this buffer or later into the same queue 19 | /// and be waited on before buffer resubmission. 20 | /// `Submit` cannot be submitted more than once. 21 | #[derive(Clone, Copy, Debug, Default)] 22 | pub struct NoSimultaneousUse; 23 | 24 | /// Buffers with this usage flag must be secondary buffers executed entirely in render-pass. 25 | #[derive(Clone, Copy, Debug, Default)] 26 | pub struct RenderPassContinue; 27 | 28 | /// Primary buffers must has this flag as they cannot has `RenderPassContinue` flag. 29 | /// Secondary buffers with this usage flag cannot be executed as part of render-pass. 30 | #[derive(Clone, Copy, Debug, Default)] 31 | pub struct OutsideRenderPass; 32 | 33 | /// Type-level usage flags. 34 | /// It defines if buffer can be resubmitted without reset. 35 | /// Or even resubmitted while being executed. 36 | pub trait Usage: Copy + Default + std::fmt::Debug + 'static { 37 | /// Flags required to begin command buffer. 38 | fn flags(&self) -> rendy_core::hal::command::CommandBufferFlags; 39 | } 40 | 41 | impl Usage for OneShot { 42 | fn flags(&self) -> rendy_core::hal::command::CommandBufferFlags { 43 | rendy_core::hal::command::CommandBufferFlags::ONE_TIME_SUBMIT 44 | } 45 | } 46 | 47 | impl Usage for MultiShot { 48 | fn flags(&self) -> rendy_core::hal::command::CommandBufferFlags { 49 | rendy_core::hal::command::CommandBufferFlags::empty() 50 | } 51 | } 52 | 53 | impl Usage for MultiShot { 54 | fn flags(&self) -> rendy_core::hal::command::CommandBufferFlags { 55 | rendy_core::hal::command::CommandBufferFlags::SIMULTANEOUS_USE 56 | } 57 | } 58 | 59 | impl Usage for NoSimultaneousUse { 60 | fn flags(&self) -> rendy_core::hal::command::CommandBufferFlags { 61 | rendy_core::hal::command::CommandBufferFlags::empty() 62 | } 63 | } 64 | 65 | /// Trait implemented for type-level render pass relation flags. 66 | /// `RenderPassContinue` and `OutsideRenderPass`. 67 | pub trait RenderPassRelation: Copy + Default + std::fmt::Debug + 'static { 68 | /// Flags required to begin command buffer. 69 | fn flags(&self) -> rendy_core::hal::command::CommandBufferFlags; 70 | } 71 | 72 | impl RenderPassRelation for RenderPassContinue { 73 | fn flags(&self) -> rendy_core::hal::command::CommandBufferFlags { 74 | rendy_core::hal::command::CommandBufferFlags::RENDER_PASS_CONTINUE 75 | } 76 | } 77 | 78 | impl RenderPassRelation for OutsideRenderPass { 79 | fn flags(&self) -> rendy_core::hal::command::CommandBufferFlags { 80 | rendy_core::hal::command::CommandBufferFlags::empty() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /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: rendy_core::hal::queue::QueueFamilyId, 11 | queues: Vec>, 12 | } 13 | 14 | impl Family { 15 | /// Create new empty `Family` 16 | pub fn new(id: rendy_core::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) -> rendy_core::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/submission.rs: -------------------------------------------------------------------------------- 1 | use super::queue::QueueId; 2 | use crate::Id; 3 | use std::collections::HashMap; 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) -> rendy_core::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 | buffer_links: HashMap, 43 | image_links: HashMap, 44 | wait_factor: usize, 45 | submit_order: usize, 46 | sync: S, 47 | } 48 | 49 | impl Submission { 50 | /// Get id of the `Node`. 51 | pub fn node(&self) -> usize { 52 | self.node 53 | } 54 | 55 | /// Get id of the `Submission`. 56 | pub fn id(&self) -> SubmissionId { 57 | self.id 58 | } 59 | 60 | /// Get synchronization for `Submission`. 61 | pub fn sync(&self) -> &S { 62 | &self.sync 63 | } 64 | 65 | /// Get wait factor for `Submission` 66 | pub fn wait_factor(&self) -> usize { 67 | self.wait_factor 68 | } 69 | 70 | /// Get submit order for `Submission` 71 | pub fn submit_order(&self) -> usize { 72 | self.submit_order 73 | } 74 | 75 | /// Get link index for resource by id. 76 | pub fn buffer_link_index(&self, id: Id) -> usize { 77 | self.buffer_links[&id] 78 | } 79 | 80 | /// Set link index for given chain. 81 | pub fn set_buffer_link(&mut self, id: Id, link: usize) { 82 | assert!(self.buffer_links.insert(id, link).is_none()); 83 | } 84 | 85 | /// Get link index for resource by id. 86 | pub fn image_link_index(&self, id: Id) -> usize { 87 | self.image_links[&id] 88 | } 89 | 90 | /// Set link index for given chain. 91 | pub fn set_image_link(&mut self, id: Id, link: usize) { 92 | assert!(self.image_links.insert(id, link).is_none()); 93 | } 94 | 95 | /// Create new submission with specified pass. 96 | pub(crate) fn new( 97 | node: usize, 98 | wait_factor: usize, 99 | submit_order: usize, 100 | id: SubmissionId, 101 | sync: S, 102 | ) -> Self { 103 | Submission { 104 | node, 105 | buffer_links: HashMap::default(), 106 | image_links: HashMap::default(), 107 | id, 108 | wait_factor, 109 | submit_order, 110 | sync, 111 | } 112 | } 113 | 114 | /// Set synchronization to the `Submission`. 115 | pub fn set_sync(&self, sync: T) -> Submission { 116 | Submission { 117 | node: self.node, 118 | buffer_links: self.buffer_links.clone(), 119 | image_links: self.image_links.clone(), 120 | id: self.id, 121 | wait_factor: self.wait_factor, 122 | submit_order: self.submit_order, 123 | sync, 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /memory/src/mapping/range.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::util::fits_usize, 3 | std::{ 4 | mem::{align_of, size_of}, 5 | ops::Range, 6 | ptr::NonNull, 7 | slice::{from_raw_parts, from_raw_parts_mut}, 8 | }, 9 | }; 10 | 11 | /// Get sub-range of memory mapping. 12 | /// `range` and `fitting` is in memory object space. 13 | /// `ptr` points to the `range.start` offset from memory origin. 14 | /// returns pointer to `fitting.start` offset from memory origin 15 | /// if `fitting` is contained in `range`. 16 | pub(crate) fn mapped_fitting_range( 17 | ptr: NonNull, 18 | range: Range, 19 | fitting: Range, 20 | ) -> Option> { 21 | assert!( 22 | range.start < range.end, 23 | "Memory mapping region must have valid size" 24 | ); 25 | assert!( 26 | fitting.start < fitting.end, 27 | "Memory mapping region must have valid size" 28 | ); 29 | assert!(fits_usize(range.end - range.start)); 30 | assert!(usize::max_value() - (range.end - range.start) as usize >= ptr.as_ptr() as usize); 31 | 32 | if fitting.start < range.start || fitting.end > range.end { 33 | None 34 | } else { 35 | Some(unsafe { 36 | // for x > 0 and y >= 0: x + y > 0. No overflow due to checks above. 37 | NonNull::new_unchecked( 38 | (ptr.as_ptr() as usize + (fitting.start - range.start) as usize) as *mut u8, 39 | ) 40 | }) 41 | } 42 | } 43 | 44 | /// Get sub-range of memory mapping. 45 | /// `range` is in memory object space. 46 | /// `sub` is a range inside `range`. 47 | /// `ptr` points to the `range.start` offset from memory origin. 48 | /// returns pointer to the `range.starti + sub.start` offset from memory origin 49 | /// if `sub` fits in `range`. 50 | pub(crate) fn mapped_sub_range( 51 | ptr: NonNull, 52 | range: Range, 53 | sub: Range, 54 | ) -> Option<(NonNull, Range)> { 55 | let fitting = sub.start.checked_add(range.start)?..sub.end.checked_add(range.start)?; 56 | let ptr = mapped_fitting_range(ptr, range, fitting.clone())?; 57 | Some((ptr, fitting)) 58 | } 59 | 60 | /// # Safety 61 | /// 62 | /// User must ensure that: 63 | /// * this function won't create aliasing slices. 64 | /// * returned slice doesn't outlive mapping. 65 | /// * `T` Must be plain-old-data type compatible with data in mapped region. 66 | pub(crate) unsafe fn mapped_slice_mut<'a, T>(ptr: NonNull, size: usize) -> &'a mut [T] { 67 | assert_eq!( 68 | size % size_of::(), 69 | 0, 70 | "Range length must be multiple of element size" 71 | ); 72 | let offset = ptr.as_ptr() as usize; 73 | assert_eq!( 74 | offset % align_of::(), 75 | 0, 76 | "Range offset must be multiple of element alignment" 77 | ); 78 | assert!(usize::max_value() - size >= ptr.as_ptr() as usize); 79 | from_raw_parts_mut(ptr.as_ptr() as *mut T, size) 80 | } 81 | 82 | /// # Safety 83 | /// 84 | /// User must ensure that: 85 | /// * returned slice doesn't outlive mapping. 86 | /// * `T` Must be plain-old-data type compatible with data in mapped region. 87 | pub(crate) unsafe fn mapped_slice<'a, T>(ptr: NonNull, size: usize) -> &'a [T] { 88 | assert_eq!( 89 | size % size_of::(), 90 | 0, 91 | "Range length must be multiple of element size" 92 | ); 93 | let offset = ptr.as_ptr() as usize; 94 | assert_eq!( 95 | offset % align_of::(), 96 | 0, 97 | "Range offset must be multiple of element alignment" 98 | ); 99 | assert!(usize::max_value() - size >= ptr.as_ptr() as usize); 100 | from_raw_parts(ptr.as_ptr() as *const T, size) 101 | } 102 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent none 3 | stages { 4 | stage("Pull new images") { 5 | agent { 6 | label 'docker' 7 | } 8 | steps { 9 | sh 'docker pull amethystrs/builder-linux:stable' 10 | sh 'docker pull amethystrs/builder-linux:nightly' 11 | } 12 | } 13 | stage('Check Formatting') { 14 | environment { 15 | CARGO_HOME = '/home/jenkins/.cargo' 16 | RUSTUP_HOME = '/home/jenkins/.rustup' 17 | RUSTFLAGS = "-D warnings" 18 | } 19 | agent { 20 | label 'linux' 21 | } 22 | steps { 23 | echo 'Checking formatting...' 24 | sh '$CARGO_HOME/bin/cargo fmt -- --check' 25 | } 26 | } 27 | stage('Run Tests') { 28 | parallel { 29 | stage('Test on Linux / coverage') { 30 | agent { 31 | docker { 32 | image 'amethystrs/builder-linux:stable' 33 | args '--privileged' 34 | label 'docker' 35 | } 36 | } 37 | steps { 38 | withCredentials([string(credentialsId: 'codecov_token', variable: 'CODECOV_TOKEN')]) { 39 | echo 'Building to calculate coverage' 40 | sh 'cd rendy && cargo test --all --all-features' 41 | sh 'cd tests && cargo build --bin gl --bin vulkan' 42 | echo 'Calculating code coverage...' 43 | sh 'for file in target/debug/rendy*[^\\.d]; do mkdir -p \"target/cov/$(basename $file)\"; kcov --exclude-pattern=/.cargo,/usr/lib --verify \"target/cov/$(basename $file)\" \"$file\" || true; done' 44 | echo "Uploading coverage..." 45 | sh "curl -s https://codecov.io/bash | bash -s - -t $CODECOV_TOKEN" 46 | echo "Uploaded code coverage!" 47 | } 48 | } 49 | } 50 | stage("Test on Windows") { 51 | environment { 52 | CARGO_HOME = 'C:\\Users\\root\\.cargo' 53 | RUSTUP_HOME = 'C:\\Users\\root\\.rustup' 54 | } 55 | agent { 56 | label 'windows' 57 | } 58 | steps { 59 | bat 'C:\\Users\\root\\.cargo\\bin\\cargo update' 60 | echo 'Beginning tests...' 61 | // TODO: Once we support DX12, we should switch to it from vulkan for windows 62 | // FIXME: Can't test "full" because of problems with shaderc compilation on windows box 63 | bat 'cd rendy && C:\\Users\\root\\.cargo\\bin\\cargo test --all --no-default-features --features "base mesh-obj texture-image texture-palette spirv-reflection serde-1 dx12 gl vulkan"' 64 | bat 'cd tests && cargo build --bin dx12 --bin gl --bin vulkan' 65 | echo 'Tests done!' 66 | } 67 | } 68 | stage("Test on macOS") { 69 | environment { 70 | CARGO_HOME = '/Users/jenkins/.cargo' 71 | RUSTUP_HOME = '/Users/jenkins/.rustup' 72 | } 73 | agent { 74 | label 'mac' 75 | } 76 | steps { 77 | echo 'Beginning tests...' 78 | sh 'cd rendy && /Users/jenkins/.cargo/bin/cargo test --all --all-features' 79 | sh 'cd tests && /Users/jenkins/.cargo/bin/cargo build --bin gl --bin metal' 80 | echo 'Tests done!' 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /rendy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rendy" 3 | version = "0.5.1" 4 | authors = ["omni-viral "] 5 | edition = "2018" 6 | repository = "https://github.com/amethyst/rendy" 7 | license = "MIT OR Apache-2.0" 8 | documentation = "https://docs.rs/rendy" 9 | keywords = ["graphics", "gfx-hal", "rendy"] 10 | description = "Higher-level graphics abstrations based on gfx-hal" 11 | categories = ["rendering"] 12 | readme = "../README.md" 13 | 14 | [features] 15 | serde-1 = ["rendy-factory/serde-1", "rendy-mesh/serde-1", "rendy-texture/serde-1", "rendy-shader/serde-1", "rendy-core/serde-1"] 16 | 17 | # Rendy subcrates 18 | command = ["rendy-command"] 19 | descriptor = ["rendy-descriptor"] 20 | factory = ["rendy-factory", "command", "descriptor", "resource", "wsi"] 21 | frame = ["rendy-frame", "factory"] 22 | graph = ["rendy-graph", "frame"] 23 | init = ["rendy-init"] 24 | init-winit = ["rendy-init/winit"] 25 | memory = ["rendy-memory"] 26 | mesh = ["rendy-mesh", "factory"] 27 | shader = ["rendy-shader", "factory"] 28 | resource = ["rendy-resource", "memory"] 29 | texture = ["rendy-texture", "factory"] 30 | wsi = ["rendy-wsi"] 31 | 32 | # Common core features 33 | empty = ["rendy-core/empty"] 34 | dx12 = ["rendy-core/dx12"] 35 | gl = ["rendy-core/gl"] 36 | metal = ["rendy-core/metal"] 37 | vulkan = ["rendy-core/vulkan"] 38 | no-slow-safety-checks = ["rendy-core/no-slow-safety-checks"] 39 | profiler = ["thread_profiler/thread_profiler"] 40 | 41 | # Base feature enables all subcrates 42 | base = ["command", "descriptor", "factory", "frame", "graph", "init", "memory", "mesh", "shader", "resource", "texture", "wsi"] 43 | 44 | # Subcrate features relay. 45 | mesh-obj = ["mesh", "rendy-mesh/obj"] 46 | texture-image = ["texture", "rendy-texture/image"] 47 | texture-palette = ["texture", "rendy-texture/palette"] 48 | shader-compiler = ["rendy-shader/shader-compiler"] 49 | spirv-reflection = ["rendy-shader/spirv-reflection" ] 50 | 51 | # Full feature set - all listed features except rendy-core's. 52 | full = ["base", "mesh-obj", "texture-image", "texture-palette", "spirv-reflection", "shader-compiler"] 53 | 54 | # Default feature set includes all subcrates and few commonly used features. 55 | default = [ "base", "shader-compiler", "spirv-reflection" ] 56 | 57 | [dependencies] 58 | rendy-command = { version = "0.5.1", path = "../command", optional = true } 59 | rendy-descriptor = { version = "0.5.1", path = "../descriptor", optional = true } 60 | rendy-factory = { version = "0.5.1", path = "../factory", optional = true } 61 | rendy-frame = { version = "0.5.1", path = "../frame", optional = true } 62 | rendy-graph = { version = "0.5.1", path = "../graph", optional = true } 63 | rendy-init = { version = "0.5.1", path = "../init", optional = true } 64 | rendy-memory = { version = "0.5.2", path = "../memory", optional = true } 65 | rendy-mesh = { version = "0.5.1", path = "../mesh", optional = true } 66 | rendy-shader = { version = "0.5.1", path = "../shader", optional = true } 67 | rendy-resource = { version = "0.5.1", path = "../resource", optional = true } 68 | rendy-texture = { version = "0.5.1", path = "../texture", optional = true } 69 | rendy-core = { version = "0.5.1", path = "../core" } 70 | rendy-wsi = { version = "0.5.1", path = "../wsi", optional = true } 71 | 72 | thread_profiler = "0.3" 73 | 74 | [dev-dependencies] 75 | genmesh = "0.6" 76 | nalgebra = "0.18" 77 | env_logger = "0.6" 78 | lazy_static = "1.0" 79 | log = "0.4" 80 | palette = "0.4" 81 | rand = "0.7" 82 | 83 | [[example]] 84 | name = "init" 85 | required-features = ["base", "init-winit"] 86 | 87 | [[example]] 88 | name = "triangle" 89 | required-features = ["base", "init-winit", "shader-compiler"] 90 | 91 | [[example]] 92 | name = "sprite" 93 | required-features = ["base", "init-winit", "texture-image", "shader-compiler"] 94 | 95 | [[example]] 96 | name = "meshes" 97 | required-features = ["base", "init-winit", "shader-compiler"] 98 | 99 | [[example]] 100 | name = "quads" 101 | required-features = ["base", "init-winit", "shader-compiler"] 102 | 103 | [[example]] 104 | name = "source_shaders" 105 | required-features = ["base", "init-winit", "shader-compiler"] 106 | 107 | [package.metadata.docs.rs] 108 | features = ["full"] 109 | -------------------------------------------------------------------------------- /chain/src/schedule/queue.rs: -------------------------------------------------------------------------------- 1 | use super::submission::{Submission, SubmissionId}; 2 | 3 | /// Queue id. 4 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 5 | pub struct QueueId { 6 | /// Family id of the queue. 7 | pub family: rendy_core::hal::queue::QueueFamilyId, 8 | 9 | /// Index of the queue. 10 | pub index: usize, 11 | } 12 | 13 | impl QueueId { 14 | /// Create queue id from family id and index. 15 | pub fn new(family: rendy_core::hal::queue::QueueFamilyId, index: usize) -> Self { 16 | QueueId { 17 | family: family, 18 | index, 19 | } 20 | } 21 | 22 | /// Get family id. 23 | pub fn family(&self) -> rendy_core::hal::queue::QueueFamilyId { 24 | self.family 25 | } 26 | 27 | /// Get index within the family. 28 | pub fn index(&self) -> usize { 29 | self.index 30 | } 31 | } 32 | 33 | /// Instances of this type contains array of `Submission`s. 34 | /// Those submissions are expected to be submitted in order. 35 | #[derive(Clone, Debug)] 36 | pub struct Queue { 37 | id: QueueId, 38 | submissions: Vec>, 39 | } 40 | 41 | impl Queue { 42 | /// Create new queue with specified id. 43 | pub fn new(id: QueueId) -> Self { 44 | Queue { 45 | id, 46 | submissions: Vec::new(), 47 | } 48 | } 49 | 50 | /// Get id of the queue. 51 | pub fn id(&self) -> QueueId { 52 | self.id 53 | } 54 | 55 | /// Iterate over immutable references to each submission in this queue 56 | pub fn iter(&self) -> impl Iterator> { 57 | self.submissions.iter() 58 | } 59 | 60 | /// Iterate over mutable references to each submission in this queue 61 | pub fn iter_mut(&mut self) -> impl Iterator> { 62 | self.submissions.iter_mut() 63 | } 64 | 65 | // /// Iterate over mutable references to each submission in this queue 66 | // pub fn into_iter(self) -> QueueIntoIter { 67 | // QueueIntoIter { 68 | // qid: self.id, 69 | // iter: self.submissions.into_iter().enumerate(), 70 | // } 71 | // } 72 | 73 | /// Get the number of submissions in queue. 74 | pub fn len(&self) -> usize { 75 | self.submissions.len() 76 | } 77 | 78 | /// Get reference to `Submission` instance by id. 79 | /// 80 | /// # Panic 81 | /// 82 | /// This function will panic if requested submission isn't part of this queue. 83 | /// 84 | pub fn submission(&self, sid: SubmissionId) -> Option<&Submission> { 85 | assert_eq!(self.id, sid.queue()); 86 | self.submissions.get(sid.index()) 87 | } 88 | 89 | /// Get mutable reference to `Submission` instance by id. 90 | /// 91 | /// # Panic 92 | /// 93 | /// This function will panic if requested submission isn't part of this queue. 94 | /// 95 | pub fn submission_mut(&mut self, sid: SubmissionId) -> Option<&mut Submission> { 96 | assert_eq!(self.id, sid.queue()); 97 | self.submissions.get_mut(sid.index()) 98 | } 99 | 100 | // /// Get reference to last `Submission` instance. 101 | // pub fn last_submission(&self) -> Option<&Submission> { 102 | // self.submissions.last() 103 | // } 104 | 105 | // /// Get mutable reference to last `Submission` instance. 106 | // pub fn last_submission_mut(&mut self) -> Option<&mut Submission> { 107 | // self.submissions.last_mut() 108 | // } 109 | 110 | /// Add `Submission` instance to the end of queue. 111 | /// Returns id of the added submission. 112 | pub fn add_submission( 113 | &mut self, 114 | node: usize, 115 | wait_factor: usize, 116 | submit_order: usize, 117 | sync: S, 118 | ) -> SubmissionId { 119 | let sid = SubmissionId::new(self.id, self.submissions.len()); 120 | self.submissions 121 | .push(Submission::new(node, wait_factor, submit_order, sid, sync)); 122 | sid 123 | } 124 | 125 | /// Add `Submission` instance to the end of queue. 126 | /// Check that submission has correct id. 127 | pub fn add_submission_checked(&mut self, submission: Submission) { 128 | assert_eq!( 129 | submission.id(), 130 | SubmissionId::new(self.id, self.submissions.len()) 131 | ); 132 | self.submissions.push(submission); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /command/src/buffer/submit.rs: -------------------------------------------------------------------------------- 1 | use { 2 | super::{ 3 | level::PrimaryLevel, 4 | state::{ExecutableState, InvalidState, PendingState}, 5 | usage::{MultiShot, NoSimultaneousUse, OneShot, OutsideRenderPass, SimultaneousUse}, 6 | CommandBuffer, 7 | }, 8 | crate::family::FamilyId, 9 | }; 10 | 11 | /// Structure contains command buffer ready for submission. 12 | #[derive(Debug)] 13 | pub struct Submit< 14 | B: rendy_core::hal::Backend, 15 | S = NoSimultaneousUse, 16 | L = PrimaryLevel, 17 | P = OutsideRenderPass, 18 | > { 19 | raw: std::ptr::NonNull, 20 | family: FamilyId, 21 | simultaneous: S, 22 | level: L, 23 | pass_continue: P, 24 | } 25 | 26 | unsafe impl Send for Submit 27 | where 28 | B: rendy_core::hal::Backend, 29 | B::CommandBuffer: Send + Sync, 30 | FamilyId: Send, 31 | S: Send, 32 | L: Send, 33 | P: Send, 34 | { 35 | } 36 | 37 | unsafe impl Sync for Submit 38 | where 39 | B: rendy_core::hal::Backend, 40 | B::CommandBuffer: Send + Sync, 41 | S: Sync, 42 | L: Sync, 43 | P: Sync, 44 | { 45 | } 46 | 47 | /// Submittable object. 48 | /// Values that implement this trait can be submitted to the queues 49 | /// or executed as part of primary buffers (in case of `Submittable`). 50 | pub unsafe trait Submittable { 51 | /// Get family that this submittable is belong to. 52 | fn family(&self) -> FamilyId; 53 | 54 | /// Get raw command buffer. 55 | /// This function is intended for submitting command buffer into raw queue. 56 | /// 57 | /// # Safety 58 | /// 59 | /// This function returns unbound reference to the raw command buffer. 60 | /// The actual lifetime of the command buffer is tied to the original `CommandBuffer` wrapper. 61 | /// `CommandBuffer` must not destroy raw command buffer or give access to it before submitted command is complete so 62 | /// using this funcion to submit command buffer into queue must be valid. 63 | unsafe fn raw<'a>(self) -> &'a B::CommandBuffer; 64 | } 65 | 66 | unsafe impl Submittable for Submit 67 | where 68 | B: rendy_core::hal::Backend, 69 | { 70 | fn family(&self) -> FamilyId { 71 | self.family 72 | } 73 | 74 | unsafe fn raw<'a>(self) -> &'a B::CommandBuffer { 75 | &*self.raw.as_ptr() 76 | } 77 | } 78 | 79 | unsafe impl<'a, B, L, P> Submittable for &'a Submit 80 | where 81 | B: rendy_core::hal::Backend, 82 | { 83 | fn family(&self) -> FamilyId { 84 | self.family 85 | } 86 | 87 | unsafe fn raw<'b>(self) -> &'b B::CommandBuffer { 88 | &*self.raw.as_ptr() 89 | } 90 | } 91 | 92 | impl CommandBuffer, L, R> 93 | where 94 | B: rendy_core::hal::Backend, 95 | P: Copy, 96 | L: Copy, 97 | { 98 | /// Produce `Submit` object that can be used to populate submission. 99 | pub fn submit_once( 100 | self, 101 | ) -> ( 102 | Submit, 103 | CommandBuffer, L, R>, 104 | ) { 105 | let pass_continue = self.state.1; 106 | let level = self.level; 107 | 108 | let buffer = unsafe { self.change_state(|_| PendingState(InvalidState)) }; 109 | 110 | let submit = Submit { 111 | raw: buffer.raw, 112 | family: buffer.family, 113 | pass_continue, 114 | simultaneous: NoSimultaneousUse, 115 | level, 116 | }; 117 | 118 | (submit, buffer) 119 | } 120 | } 121 | 122 | impl CommandBuffer, P>, L, R> 123 | where 124 | B: rendy_core::hal::Backend, 125 | P: Copy, 126 | S: Copy, 127 | L: Copy, 128 | { 129 | /// Produce `Submit` object that can be used to populate submission. 130 | pub fn submit( 131 | self, 132 | ) -> ( 133 | Submit, 134 | CommandBuffer, P>>, L, R>, 135 | ) { 136 | let MultiShot(simultaneous) = self.state.0; 137 | let pass_continue = self.state.1; 138 | let level = self.level; 139 | 140 | let buffer = unsafe { self.change_state(|state| PendingState(state)) }; 141 | 142 | let submit = Submit { 143 | raw: buffer.raw, 144 | family: buffer.family, 145 | pass_continue, 146 | simultaneous, 147 | level, 148 | }; 149 | 150 | (submit, buffer) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /memory/src/utilization.rs: -------------------------------------------------------------------------------- 1 | use { 2 | colorful::{core::color_string::CString, Color, Colorful as _}, 3 | gfx_hal::memory::Properties, 4 | }; 5 | 6 | /// Memory utilization stats. 7 | #[derive(Clone, Copy, Debug)] 8 | pub struct MemoryUtilization { 9 | /// Total number of bytes allocated. 10 | pub used: u64, 11 | /// Effective number bytes allocated. 12 | pub effective: u64, 13 | } 14 | 15 | /// Memory utilization of one heap. 16 | #[derive(Clone, Copy, Debug)] 17 | pub struct MemoryHeapUtilization { 18 | /// Utilization. 19 | pub utilization: MemoryUtilization, 20 | 21 | /// Memory heap size. 22 | pub size: u64, 23 | } 24 | 25 | /// Memory utilization of one type. 26 | #[derive(Clone, Copy, Debug)] 27 | pub struct MemoryTypeUtilization { 28 | /// Utilization. 29 | pub utilization: MemoryUtilization, 30 | 31 | /// Memory type info. 32 | pub properties: Properties, 33 | 34 | /// Index of heap this memory type uses. 35 | pub heap_index: usize, 36 | } 37 | 38 | /// Total memory utilization. 39 | #[derive(Clone, Debug)] 40 | pub struct TotalMemoryUtilization { 41 | /// Utilization by types. 42 | pub types: Vec, 43 | 44 | /// Utilization by heaps. 45 | pub heaps: Vec, 46 | } 47 | 48 | impl std::fmt::Display for TotalMemoryUtilization { 49 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 50 | const MB: u64 = 1024 * 1024; 51 | 52 | writeln!(fmt, "!!! Memory utilization !!!")?; 53 | for (index, heap) in self.heaps.iter().enumerate() { 54 | let size = heap.size; 55 | let MemoryUtilization { used, effective } = heap.utilization; 56 | let usage_basis_points = used * 10000 / size; 57 | let fill = if usage_basis_points > 10000 { 58 | // Shouldn't happen, but just in case. 59 | 50 60 | } else { 61 | (usage_basis_points / 200) as usize 62 | }; 63 | let effective_basis_points = if used > 0 { 64 | effective * 10000 / used 65 | } else { 66 | 10000 67 | }; 68 | 69 | let line = ("|".repeat(fill) + &(" ".repeat(50 - fill))) 70 | .gradient_with_color(Color::Green, Color::Red); 71 | writeln!( 72 | fmt, 73 | "Heap {}:\n{:6} / {:<6} or{} {{ effective:{} }} [{}]", 74 | format!("{}", index).magenta(), 75 | format!("{}MB", used / MB), 76 | format!("{}MB", size / MB), 77 | format_basis_points(usage_basis_points), 78 | format_basis_points_inverted(effective_basis_points), 79 | line 80 | )?; 81 | 82 | for ty in self.types.iter().filter(|ty| ty.heap_index == index) { 83 | let properties = ty.properties; 84 | let MemoryUtilization { used, effective } = ty.utilization; 85 | let usage_basis_points = used * 10000 / size; 86 | let effective_basis_points = if used > 0 { 87 | effective * 10000 / used 88 | } else { 89 | 0 90 | }; 91 | 92 | writeln!( 93 | fmt, 94 | " {:>6} or{} {{ effective:{} }} | {:?}", 95 | format!("{}MB", used / MB), 96 | format_basis_points(usage_basis_points), 97 | format_basis_points_inverted(effective_basis_points), 98 | properties, 99 | )?; 100 | } 101 | } 102 | 103 | Ok(()) 104 | } 105 | } 106 | 107 | fn format_basis_points(basis_points: u64) -> CString { 108 | debug_assert!(basis_points <= 10000); 109 | let s = format!("{:>3}.{:02}%", basis_points / 100, basis_points % 100); 110 | if basis_points > 7500 { 111 | s.red() 112 | } else if basis_points > 5000 { 113 | s.yellow() 114 | } else if basis_points > 2500 { 115 | s.green() 116 | } else if basis_points > 100 { 117 | s.blue() 118 | } else { 119 | s.white() 120 | } 121 | } 122 | 123 | fn format_basis_points_inverted(basis_points: u64) -> CString { 124 | debug_assert!(basis_points <= 10000); 125 | let s = format!("{:>3}.{:02}%", basis_points / 100, basis_points % 100); 126 | if basis_points > 9900 { 127 | s.white() 128 | } else if basis_points > 7500 { 129 | s.blue() 130 | } else if basis_points > 5000 { 131 | s.green() 132 | } else if basis_points > 2500 { 133 | s.yellow() 134 | } else { 135 | s.red() 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /resource/src/buffer.rs: -------------------------------------------------------------------------------- 1 | //! Buffer usage, creation-info and wrappers. 2 | 3 | pub use rendy_core::hal::buffer::*; 4 | 5 | use { 6 | crate::{ 7 | core::{device_owned, Device, DeviceId}, 8 | memory::{Block, Heaps, MappedRange, MemoryBlock, MemoryUsage}, 9 | CreationError, 10 | }, 11 | relevant::Relevant, 12 | rendy_core::hal::{device::Device as _, Backend}, 13 | }; 14 | 15 | /// Buffer info. 16 | #[derive(Clone, Copy, Debug)] 17 | pub struct BufferInfo { 18 | /// Buffer size. 19 | pub size: u64, 20 | 21 | /// Buffer usage flags. 22 | pub usage: Usage, 23 | } 24 | 25 | /// Generic buffer resource wrapper. 26 | /// 27 | /// # Parameters 28 | /// 29 | /// `B` - raw image type. 30 | #[derive(Debug)] 31 | pub struct Buffer { 32 | device: DeviceId, 33 | raw: B::Buffer, 34 | block: MemoryBlock, 35 | info: BufferInfo, 36 | relevant: Relevant, 37 | } 38 | 39 | device_owned!(Buffer); 40 | /// Alias for the error to create a buffer. 41 | pub type BufferCreationError = CreationError; 42 | 43 | impl Buffer 44 | where 45 | B: Backend, 46 | { 47 | /// Create buffer, allocate memory block for it and bind. 48 | /// 49 | /// # Safety 50 | /// 51 | /// In order to guarantee that `Heap::allocate` will return 52 | /// memory range owned by this `Device`, 53 | /// this `Heaps` instance must always be used with this `Device` instance. 54 | /// 55 | /// Otherwise usage of hal methods must be always valid. 56 | pub unsafe fn create( 57 | device: &Device, 58 | heaps: &mut Heaps, 59 | info: BufferInfo, 60 | memory_usage: impl MemoryUsage, 61 | ) -> Result { 62 | log::trace!("{:#?}@{:#?}", info, memory_usage); 63 | assert_ne!(info.size, 0); 64 | 65 | let mut buf = device 66 | .create_buffer(info.size, info.usage) 67 | .map_err(CreationError::Create)?; 68 | let reqs = device.get_buffer_requirements(&buf); 69 | let block = heaps 70 | .allocate( 71 | device, 72 | reqs.type_mask as u32, 73 | memory_usage, 74 | reqs.size, 75 | reqs.alignment, 76 | ) 77 | .map_err(CreationError::Allocate)?; 78 | 79 | device 80 | .bind_buffer_memory(block.memory(), block.range().start, &mut buf) 81 | .map_err(CreationError::Bind)?; 82 | 83 | Ok(Buffer { 84 | device: device.id(), 85 | raw: buf, 86 | block, 87 | info, 88 | relevant: Relevant, 89 | }) 90 | } 91 | 92 | /// Dispose of buffer resource. 93 | /// Deallocate memory block. 94 | pub unsafe fn dispose(self, device: &Device, heaps: &mut Heaps) { 95 | self.assert_device_owner(device); 96 | device.destroy_buffer(self.raw); 97 | heaps.free(device, self.block); 98 | self.relevant.dispose(); 99 | } 100 | 101 | /// Get reference to raw buffer resource 102 | pub fn raw(&self) -> &B::Buffer { 103 | &self.raw 104 | } 105 | 106 | /// Get mutable reference to raw buffer resource 107 | pub unsafe fn raw_mut(&mut self) -> &mut B::Buffer { 108 | &mut self.raw 109 | } 110 | 111 | /// Get reference to memory block occupied by buffer. 112 | pub fn block(&self) -> &MemoryBlock { 113 | &self.block 114 | } 115 | 116 | /// Get mutable reference to memory block occupied by buffer. 117 | pub unsafe fn block_mut(&mut self) -> &mut MemoryBlock { 118 | &mut self.block 119 | } 120 | 121 | /// Get buffer info. 122 | pub fn info(&self) -> &BufferInfo { 123 | &self.info 124 | } 125 | 126 | /// Check if this buffer could is bound to CPU visible memory and therefore mappable. 127 | /// If this function returns `false` `map` will always return `InvalidAccess`. 128 | /// 129 | /// [`map`]: #method.map 130 | /// [`InvalidAccess`]: https://docs.rs/gfx-hal/0.1/rendy_core::hal/mapping/enum.Error.html#InvalidAccess 131 | pub fn visible(&self) -> bool { 132 | self.block 133 | .properties() 134 | .contains(rendy_core::hal::memory::Properties::CPU_VISIBLE) 135 | } 136 | 137 | /// Map range of the buffer to the CPU accessible memory. 138 | pub fn map<'a>( 139 | &'a mut self, 140 | device: &Device, 141 | range: std::ops::Range, 142 | ) -> Result, rendy_core::hal::device::MapError> { 143 | self.block.map(device, range) 144 | } 145 | 146 | /// Get buffer info. 147 | pub fn size(&self) -> u64 { 148 | self.info().size 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /docs/graph.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | This document gives an overview of the Framegraph system in Rendy. 4 | 5 | ## What is a Framegraph 6 | 7 | Let's start at the end for this explanation. What you see on your monitor is the end result of a complicated series of transformations to data as it makes its way from your running application to your monitor. 8 | 9 | This series of transformations is often referred to as a _rendering pipeline_; note that this is separate from another meaning of _rendering pipeline_ which is used to encompass the GPU hardware pipeline (all shader stages and everything in between). 10 | 11 | These `rendering pipelines_ can be very simple, or they can be very complex. When they are very complex, it can be useful to add a layer of abstraction to make them easier to conceptualize. 12 | 13 | One way of doing this is with a _framegraph_ (an alternate name sometimes used is _render graph_). Consider a simple rendering pipeline that looks like this: 14 | 15 | ``` 16 | ┌────────────┐ ┌────────────┐ 17 | │ Previous │ │ Compute │ 18 | │ Frame │───▶│ Average │────────────────────────────────┐ 19 | │ │ │ Brightness │ │ 20 | └────────────┘ └────────────┘ │ 21 | ▼ 22 | ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ 23 | │ Depth │ │ │ │ │ │ │ │ │ │ │ 24 | │ Pre-Pass │───▶│ PBR Shader │────▶│ Tonemap │ ────▶│ Dither │────▶│ Lens Flare │───▶│ Output │ 25 | │ │ │ │ │ │ │ │ │ │ │ │ 26 | └────────────┘ └────────────┘ └────────────┘ └────────────┘ └────────────┘ └────────────┘ 27 | ▲ 28 | ┌────────────┐ │ 29 | │ Shadow Map │ │ 30 | │ Pass │───────────┘ 31 | │ │ 32 | └────────────┘ 33 | ``` 34 | 35 | This could be applied to one model in your frame. Expand this out to much more complex pipelines, across complex scenes with many models, and you can see how it can get complicated. 36 | 37 | ### Synchronization 38 | 39 | Aside from providing a higher-level view of your graphics pipeline, a render graph can also handle _synchronization_. Imagine you have 3 shaders, A, B, and C. C requires both A and B to have processed a pixel before it can process it. 40 | 41 | A graph can abstract the synchronization primitives needed to ensure C doesn't get the data before both A and B are done. 42 | 43 | ### Ordering 44 | 45 | Similar to synchronization, a render graph can provide ordering. If you have shaders A -> B -> C, you may not want them to ever run in the order B -> C -> A. 46 | 47 | ### Managed Primitives 48 | 49 | Beneath the graph, you have all the primitives you would normally find: 50 | 51 | * Buffers 52 | * Semaphores 53 | * Images 54 | * Swap Chains 55 | * etc... 56 | 57 | The render graph just wraps them up neatly into a 58 | 59 | ## Render Group 60 | 61 | A render group is a collection of pipelines (in the Vulkan sense of the word) that are processed in a `Subpass`. 62 | 63 | ## Subpass 64 | 65 | A `Subpass` is a child of a `Pass`. Currently, a `Pass` can have only one `Subpass`. More may be allowed in the future, but one is always required. They are an opportunity for performance optimization when the output of one render group is used directly as the input to another one; not sampled, but literally passing the fragment. 66 | 67 | An additional benefit to having and using `Subpasses` now is that the API will not require breaking changes to support more than one `Subpass` per `Pass`. 68 | 69 | ## RenderPass 70 | 71 | In Rendy, a `RenderPass` is a combination of a `Pass` and a `Node`, described below. 72 | 73 | ## Pass 74 | 75 | A `Pass`, currently can have only one `Subpass`, and a `Pass` will usually belong to a `Node`. 76 | 77 | ## Node 78 | 79 | A `Node` contains 0 or more things that provide a set of self-contained (i.e. internally synchronized) submissions to a queue each frame. This is _usually_ a `RenderPass`, but does not have to be. The intended usage of a `Node` is that it should only consist of one thing, though it can technically contain more. -------------------------------------------------------------------------------- /command/src/capability.rs: -------------------------------------------------------------------------------- 1 | //! Capability module docs. 2 | 3 | pub use rendy_core::hal::queue::QueueType; 4 | 5 | /// Capable of transfer only. 6 | #[derive(Clone, Copy, Debug)] 7 | pub struct Transfer; 8 | 9 | /// Capable of either compute or graphics commands execution. 10 | #[derive(Clone, Copy, Debug)] 11 | pub struct Execute; 12 | 13 | /// Capable of compute commands execution. 14 | #[derive(Clone, Copy, Debug)] 15 | pub struct Compute; 16 | 17 | /// Capable of graphics command execution. 18 | #[derive(Clone, Copy, Debug)] 19 | pub struct Graphics; 20 | 21 | /// Capable of any commands execution. 22 | #[derive(Clone, Copy, Debug)] 23 | pub struct General; 24 | 25 | /// Abstract capability specifier. 26 | pub trait Capability: Copy + std::fmt::Debug + 'static { 27 | /// Try to create capability instance from queue_type. 28 | /// Instance will be created if all required queue_type set. 29 | fn from_queue_type(queue_type: QueueType) -> Option; 30 | 31 | /// Convert into `QueueType` 32 | fn into_queue_type(self) -> QueueType; 33 | } 34 | 35 | impl Capability for QueueType { 36 | fn from_queue_type(queue_type: QueueType) -> Option { 37 | Some(queue_type) 38 | } 39 | 40 | fn into_queue_type(self) -> QueueType { 41 | self 42 | } 43 | } 44 | 45 | impl Capability for Transfer { 46 | fn from_queue_type(_queue_type: QueueType) -> Option { 47 | Some(Transfer) 48 | } 49 | 50 | fn into_queue_type(self) -> QueueType { 51 | QueueType::Transfer 52 | } 53 | } 54 | 55 | impl Capability for Execute { 56 | fn from_queue_type(queue_type: QueueType) -> Option { 57 | match queue_type { 58 | QueueType::Transfer => None, 59 | _ => Some(Execute), 60 | } 61 | } 62 | 63 | fn into_queue_type(self) -> QueueType { 64 | QueueType::General 65 | } 66 | } 67 | 68 | impl Capability for Compute { 69 | fn from_queue_type(queue_type: QueueType) -> Option { 70 | match queue_type { 71 | QueueType::Compute | QueueType::General => Some(Compute), 72 | _ => None, 73 | } 74 | } 75 | 76 | fn into_queue_type(self) -> QueueType { 77 | QueueType::Compute 78 | } 79 | } 80 | 81 | impl Capability for Graphics { 82 | fn from_queue_type(queue_type: QueueType) -> Option { 83 | match queue_type { 84 | QueueType::Graphics | QueueType::General => Some(Graphics), 85 | _ => None, 86 | } 87 | } 88 | 89 | fn into_queue_type(self) -> QueueType { 90 | QueueType::Graphics 91 | } 92 | } 93 | 94 | impl Capability for General { 95 | fn from_queue_type(queue_type: QueueType) -> Option { 96 | match queue_type { 97 | QueueType::General => Some(General), 98 | _ => None, 99 | } 100 | } 101 | 102 | fn into_queue_type(self) -> QueueType { 103 | QueueType::General 104 | } 105 | } 106 | 107 | /// Check if capability supported. 108 | pub trait Supports: Capability { 109 | /// Check runtime capability. 110 | fn supports(&self) -> Option; 111 | 112 | /// Assert capability. 113 | fn assert(&self) { 114 | assert!(self.supports().is_some()); 115 | } 116 | } 117 | 118 | impl Supports for Transfer { 119 | fn supports(&self) -> Option { 120 | Some(Transfer) 121 | } 122 | } 123 | 124 | impl Supports for Compute { 125 | fn supports(&self) -> Option { 126 | Some(Transfer) 127 | } 128 | } 129 | 130 | impl Supports for Graphics { 131 | fn supports(&self) -> Option { 132 | Some(Transfer) 133 | } 134 | } 135 | 136 | impl Supports for General { 137 | fn supports(&self) -> Option { 138 | Some(Transfer) 139 | } 140 | } 141 | 142 | impl Supports for Compute { 143 | fn supports(&self) -> Option { 144 | Some(Execute) 145 | } 146 | } 147 | 148 | impl Supports for Graphics { 149 | fn supports(&self) -> Option { 150 | Some(Execute) 151 | } 152 | } 153 | 154 | impl Supports for General { 155 | fn supports(&self) -> Option { 156 | Some(Execute) 157 | } 158 | } 159 | 160 | impl Supports for Compute { 161 | fn supports(&self) -> Option { 162 | Some(Compute) 163 | } 164 | } 165 | 166 | impl Supports for General { 167 | fn supports(&self) -> Option { 168 | Some(Compute) 169 | } 170 | } 171 | 172 | impl Supports for Graphics { 173 | fn supports(&self) -> Option { 174 | Some(Graphics) 175 | } 176 | } 177 | 178 | impl Supports for General { 179 | fn supports(&self) -> Option { 180 | Some(Graphics) 181 | } 182 | } 183 | 184 | impl Supports for QueueType 185 | where 186 | C: Capability, 187 | { 188 | fn supports(&self) -> Option { 189 | C::from_queue_type(*self) 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /resource/src/set.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::{ 3 | core::{device_owned, Device, DeviceId}, 4 | descriptor, 5 | escape::Handle, 6 | }, 7 | relevant::Relevant, 8 | rendy_core::hal::{device::Device as _, pso::DescriptorSetLayoutBinding, Backend}, 9 | smallvec::SmallVec, 10 | }; 11 | 12 | /// Descriptor set layout info. 13 | #[derive(Clone, Debug)] 14 | pub struct DescriptorSetInfo { 15 | /// Bindings. 16 | pub bindings: Vec, 17 | } 18 | 19 | impl DescriptorSetInfo { 20 | /// Get descriptor ranges of the layout. 21 | pub fn ranges(&self) -> descriptor::DescriptorRanges { 22 | descriptor::DescriptorRanges::from_bindings(&self.bindings) 23 | } 24 | } 25 | 26 | /// Generic descriptor set layout resource wrapper. 27 | #[derive(Debug)] 28 | pub struct DescriptorSetLayout { 29 | device: DeviceId, 30 | raw: B::DescriptorSetLayout, 31 | info: DescriptorSetInfo, 32 | relevant: Relevant, 33 | } 34 | 35 | device_owned!(DescriptorSetLayout); 36 | 37 | impl DescriptorSetLayout 38 | where 39 | B: Backend, 40 | { 41 | /// Create new descriptor set layout 42 | pub unsafe fn create( 43 | device: &Device, 44 | info: DescriptorSetInfo, 45 | ) -> Result { 46 | let raw = device 47 | .create_descriptor_set_layout(&info.bindings, std::iter::empty::())?; 48 | 49 | Ok(DescriptorSetLayout { 50 | device: device.id(), 51 | raw, 52 | info, 53 | relevant: Relevant, 54 | }) 55 | } 56 | 57 | /// Destroy descriptor set layout resource. 58 | pub unsafe fn dispose(self, device: &Device) { 59 | self.assert_device_owner(device); 60 | device.destroy_descriptor_set_layout(self.raw); 61 | self.relevant.dispose(); 62 | } 63 | 64 | /// Get reference to raw descriptor set layout resource. 65 | pub fn raw(&self) -> &B::DescriptorSetLayout { 66 | &self.raw 67 | } 68 | 69 | /// Get mutable reference to raw descriptor set layout resource. 70 | pub unsafe fn raw_mut(&mut self) -> &mut B::DescriptorSetLayout { 71 | &mut self.raw 72 | } 73 | 74 | /// Get descriptor set layout info. 75 | pub fn info(&self) -> &DescriptorSetInfo { 76 | &self.info 77 | } 78 | } 79 | 80 | /// Generic descriptor set resource wrapper. 81 | #[derive(Debug)] 82 | pub struct DescriptorSet { 83 | device: DeviceId, 84 | set: descriptor::DescriptorSet, 85 | layout: Handle>, 86 | relevant: Relevant, 87 | } 88 | 89 | device_owned!(DescriptorSet); 90 | 91 | impl DescriptorSet 92 | where 93 | B: Backend, 94 | { 95 | /// Create new descriptor set. 96 | pub unsafe fn create( 97 | device: &Device, 98 | allocator: &mut descriptor::DescriptorAllocator, 99 | layout: Handle>, 100 | ) -> Result { 101 | let mut sets = SmallVec::<[_; 1]>::new(); 102 | 103 | allocator.allocate(device, layout.raw(), layout.info().ranges(), 1, &mut sets)?; 104 | 105 | assert_eq!(sets.len() as u32, 1); 106 | Ok(DescriptorSet { 107 | device: device.id(), 108 | set: sets.swap_remove(0), 109 | layout: layout.clone(), 110 | relevant: Relevant, 111 | }) 112 | } 113 | 114 | /// Create new descriptor sets. 115 | pub unsafe fn create_many( 116 | device: &Device, 117 | allocator: &mut descriptor::DescriptorAllocator, 118 | layout: Handle>, 119 | count: u32, 120 | extend: &mut impl Extend, 121 | ) -> Result<(), rendy_core::hal::device::OutOfMemory> { 122 | let mut sets = SmallVec::<[_; 32]>::new(); 123 | 124 | allocator.allocate( 125 | device, 126 | layout.raw(), 127 | layout.info().ranges(), 128 | count, 129 | &mut sets, 130 | )?; 131 | 132 | assert_eq!(sets.len() as u32, count); 133 | 134 | extend.extend(sets.into_iter().map(|set| DescriptorSet { 135 | device: device.id(), 136 | set, 137 | layout: layout.clone(), 138 | relevant: Relevant, 139 | })); 140 | 141 | Ok(()) 142 | } 143 | 144 | /// Destroy descriptor set resource. 145 | pub unsafe fn dispose(self, allocator: &mut descriptor::DescriptorAllocator) { 146 | allocator.free(Some(self.set)); 147 | self.relevant.dispose(); 148 | } 149 | 150 | /// Get reference to raw descriptor set resource. 151 | pub fn raw(&self) -> &B::DescriptorSet { 152 | self.set.raw() 153 | } 154 | 155 | /// Get mutable reference to raw descriptor set resource. 156 | pub unsafe fn raw_mut(&mut self) -> &mut B::DescriptorSet { 157 | self.set.raw_mut() 158 | } 159 | 160 | /// Get layout of descriptor set. 161 | pub fn layout(&mut self) -> &Handle> { 162 | &self.layout 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /command/src/pool.rs: -------------------------------------------------------------------------------- 1 | //! CommandPool module docs. 2 | 3 | use { 4 | crate::{buffer::*, capability::*, core::Device, family::FamilyId}, 5 | rendy_core::hal::{device::Device as _, pool::CommandPool as _, Backend}, 6 | }; 7 | 8 | /// Simple pool wrapper. 9 | /// Doesn't provide any guarantees. 10 | /// Wraps raw buffers into `CommandCommand buffer`. 11 | #[derive(Debug)] 12 | pub struct CommandPool { 13 | raw: B::CommandPool, 14 | capability: C, 15 | reset: R, 16 | family: FamilyId, 17 | relevant: relevant::Relevant, 18 | } 19 | 20 | family_owned!(CommandPool); 21 | 22 | impl CommandPool 23 | where 24 | B: Backend, 25 | R: Reset, 26 | { 27 | /// Create command pool associated with the family. 28 | /// Command buffers created from the pool could be submitted to the queues of the family. 29 | /// 30 | /// # Safety 31 | /// 32 | /// Family must belong to specified device. 33 | /// Family must have specified capability. 34 | pub unsafe fn create( 35 | family: FamilyId, 36 | capability: C, 37 | device: &Device, 38 | ) -> Result 39 | where 40 | R: Reset, 41 | C: Capability, 42 | { 43 | let reset = R::default(); 44 | let raw = device.create_command_pool( 45 | rendy_core::hal::queue::QueueFamilyId(family.index), 46 | reset.flags(), 47 | )?; 48 | Ok(CommandPool::from_raw(raw, capability, reset, family)) 49 | } 50 | 51 | /// Wrap raw command pool. 52 | /// 53 | /// # Safety 54 | /// 55 | /// * `raw` must be valid command pool handle. 56 | /// * The command pool must be created for specified `family` index. 57 | /// * `capability` must be subset of capabilites of the `family` the pool was created for. 58 | /// * if `reset` is `IndividualReset` the pool must be created with individual command buffer reset flag set. 59 | pub unsafe fn from_raw(raw: B::CommandPool, capability: C, reset: R, family: FamilyId) -> Self { 60 | CommandPool { 61 | raw, 62 | capability, 63 | reset, 64 | family, 65 | relevant: relevant::Relevant, 66 | } 67 | } 68 | 69 | /// Allocate new command buffers. 70 | pub fn allocate_buffers( 71 | &mut self, 72 | count: usize, 73 | ) -> Vec> 74 | where 75 | L: Level, 76 | C: Capability, 77 | { 78 | let level = L::default(); 79 | 80 | let buffers = unsafe { self.raw.allocate_vec(count, level.raw_level()) }; 81 | 82 | buffers 83 | .into_iter() 84 | .map(|raw| unsafe { 85 | CommandBuffer::from_raw( 86 | raw, 87 | self.capability, 88 | InitialState, 89 | level, 90 | self.reset, 91 | self.family, 92 | ) 93 | }) 94 | .collect() 95 | } 96 | 97 | /// Free buffers. 98 | /// Buffers must be in droppable state. 99 | /// TODO: Validate buffers were allocated from this pool. 100 | pub unsafe fn free_buffers( 101 | &mut self, 102 | buffers: impl IntoIterator>, 103 | ) { 104 | let buffers = buffers 105 | .into_iter() 106 | .map(|buffer| buffer.into_raw()) 107 | .collect::>(); 108 | 109 | self.raw.free(buffers); 110 | } 111 | 112 | /// Reset all buffers of this pool. 113 | /// 114 | /// # Safety 115 | /// 116 | /// All buffers allocated from this pool must be marked reset. 117 | /// See [`CommandBuffer::mark_reset`](struct.Command buffer.html#method.mark_reset) 118 | pub unsafe fn reset(&mut self) { 119 | rendy_core::hal::pool::CommandPool::reset(&mut self.raw, false); 120 | } 121 | 122 | /// Dispose of command pool. 123 | /// 124 | /// # Safety 125 | /// 126 | /// All buffers allocated from this pool must be [freed](#method.free_buffers). 127 | pub unsafe fn dispose(self, device: &Device) { 128 | self.assert_device_owner(device); 129 | device.destroy_command_pool(self.raw); 130 | self.relevant.dispose(); 131 | } 132 | 133 | /// Convert capability level 134 | pub fn with_queue_type(self) -> CommandPool 135 | where 136 | C: Capability, 137 | { 138 | CommandPool { 139 | raw: self.raw, 140 | capability: self.capability.into_queue_type(), 141 | reset: self.reset, 142 | family: self.family, 143 | relevant: self.relevant, 144 | } 145 | } 146 | 147 | /// Convert capability level 148 | pub fn with_capability(self) -> Result, Self> 149 | where 150 | C: Supports, 151 | { 152 | if let Some(capability) = self.capability.supports() { 153 | Ok(CommandPool { 154 | raw: self.raw, 155 | capability, 156 | reset: self.reset, 157 | family: self.family, 158 | relevant: self.relevant, 159 | }) 160 | } else { 161 | Err(self) 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /frame/src/frame.rs: -------------------------------------------------------------------------------- 1 | //! Frame module docs. 2 | 3 | use crate::{command::Fence, 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(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 11 | #[allow(missing_copy_implementations)] 12 | pub struct Frame { 13 | index: u64, 14 | } 15 | 16 | impl Frame { 17 | /// Create frame with specific index. 18 | pub fn with_index(index: u64) -> Self { 19 | Frame { index } 20 | } 21 | 22 | /// Get frame index. 23 | pub fn index(&self) -> u64 { 24 | self.index 25 | } 26 | } 27 | 28 | /// Proof that frame is complete. 29 | #[derive(Debug)] 30 | #[allow(missing_copy_implementations)] 31 | pub struct CompleteFrame { 32 | index: u64, 33 | } 34 | 35 | impl CompleteFrame { 36 | /// Get frame index. 37 | pub fn index(&self) -> u64 { 38 | self.index 39 | } 40 | } 41 | 42 | /// Complete - next frame range. 43 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 44 | pub struct FramesRange { 45 | next: u64, 46 | complete_upper_bound: u64, 47 | } 48 | 49 | impl FramesRange { 50 | /// Check if given frame is. 51 | pub fn is_complete(&self, frame: Frame) -> bool { 52 | self.complete_upper_bound > frame.index 53 | } 54 | 55 | /// Check if given frame is. 56 | pub fn complete(&self, frame: Frame) -> Option { 57 | if self.complete_upper_bound > frame.index { 58 | Some(CompleteFrame { index: frame.index }) 59 | } else { 60 | None 61 | } 62 | } 63 | 64 | /// Get next frame 65 | pub fn next(&self) -> Frame { 66 | Frame { index: self.next } 67 | } 68 | } 69 | 70 | /// Timeline of frames, complete, pending and next. 71 | #[derive(Debug)] 72 | pub struct Frames { 73 | pending: std::collections::VecDeque>, 74 | next: u64, 75 | } 76 | 77 | impl Frames 78 | where 79 | B: rendy_core::hal::Backend, 80 | { 81 | /// Create new `Frames` instance. 82 | pub fn new() -> Self { 83 | Frames { 84 | pending: Default::default(), 85 | next: 0, 86 | } 87 | } 88 | 89 | /// Get next frame reference. 90 | pub fn next(&self) -> Frame { 91 | Frame { index: self.next } 92 | } 93 | 94 | /// Advance to the next frame. 95 | /// All fences of the next frame must be queued. 96 | pub fn advance(&mut self, fences: Fences) { 97 | assert!(fences.iter().all(Fence::is_submitted)); 98 | self.pending.push_back(fences); 99 | self.next += 1; 100 | } 101 | 102 | /// Get upper bound of complete frames. 103 | /// All frames with index less than result of this function are complete. 104 | pub fn complete_upper_bound(&self) -> u64 { 105 | debug_assert!(self.pending.len() as u64 <= self.next); 106 | self.next - self.pending.len() as u64 107 | } 108 | 109 | /// Check if given frame is. 110 | pub fn is_complete(&self, frame: Frame) -> bool { 111 | self.complete_upper_bound() > frame.index 112 | } 113 | 114 | /// Check if frame with specified index is complete. 115 | pub fn complete(&self, frame: Frame) -> Option { 116 | if self.complete_upper_bound() > frame.index { 117 | Some(CompleteFrame { index: frame.index }) 118 | } else { 119 | None 120 | } 121 | } 122 | 123 | /// Wait for completion of the frames until specified (inclusive) 124 | /// Returns proof. 125 | /// 126 | /// # Parameters 127 | /// 128 | /// `target` - frame that must complete. 129 | /// `factory` - The factory. 130 | /// 131 | /// # Panics 132 | /// 133 | /// This function will panic if `target` is greater than or equal to next frame. 134 | pub fn wait_complete( 135 | &mut self, 136 | target: Frame, 137 | factory: &Factory, 138 | free: impl FnMut(Fences), 139 | ) -> CompleteFrame { 140 | assert!(target.index <= self.next); 141 | if let Some(complete) = self.complete(target) { 142 | complete 143 | } else { 144 | // n - p <= t 145 | // p - n + t + 1 >= 1 146 | // count >= 1 147 | let count = self.pending.len() - (self.next - target.index - 1) as usize; 148 | let ready = factory.wait_for_fences( 149 | self.pending.iter_mut().take(count).flatten(), 150 | rendy_core::hal::device::WaitFor::All, 151 | !0, 152 | ); 153 | assert_eq!(ready, Ok(true)); 154 | self.pending.drain(..count).for_each(free); 155 | CompleteFrame { 156 | index: target.index, 157 | } 158 | } 159 | } 160 | 161 | /// Dispose of the `Frames` 162 | pub fn dispose(mut self, factory: &mut Factory) { 163 | let ready = factory.wait_for_fences( 164 | self.pending.iter_mut().flatten(), 165 | rendy_core::hal::device::WaitFor::All, 166 | !0, 167 | ); 168 | assert_eq!(ready, Ok(true)); 169 | 170 | self.pending 171 | .drain(..) 172 | .flatten() 173 | .for_each(|fence| factory.destroy_fence(fence)); 174 | } 175 | 176 | /// Get range of frame indices in this form: 177 | /// `upper bound of finished frames .. next frame`. 178 | pub fn range(&self) -> FramesRange { 179 | FramesRange { 180 | next: self.next, 181 | complete_upper_bound: self.complete_upper_bound(), 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /memory/src/allocator/dedicated.rs: -------------------------------------------------------------------------------- 1 | use std::{ops::Range, ptr::NonNull}; 2 | 3 | use { 4 | crate::{ 5 | allocator::{Allocator, Kind}, 6 | block::Block, 7 | mapping::{mapped_fitting_range, MappedRange}, 8 | memory::*, 9 | }, 10 | gfx_hal::{device::Device as _, Backend}, 11 | }; 12 | 13 | /// Memory block allocated from `DedicatedAllocator` 14 | #[derive(Debug)] 15 | pub struct DedicatedBlock { 16 | memory: Memory, 17 | mapping: Option<(NonNull, Range)>, 18 | } 19 | 20 | unsafe impl Send for DedicatedBlock where B: Backend {} 21 | unsafe impl Sync for DedicatedBlock where B: Backend {} 22 | 23 | impl DedicatedBlock 24 | where 25 | B: Backend, 26 | { 27 | /// Get inner memory. 28 | /// Panics if mapped. 29 | pub fn unwrap_memory(self) -> Memory { 30 | assert!(self.mapping.is_none()); 31 | self.memory 32 | } 33 | 34 | /// Make unmapped block. 35 | pub fn from_memory(memory: Memory) -> Self { 36 | DedicatedBlock { 37 | memory, 38 | mapping: None, 39 | } 40 | } 41 | } 42 | 43 | impl Block for DedicatedBlock 44 | where 45 | B: Backend, 46 | { 47 | #[inline] 48 | fn properties(&self) -> gfx_hal::memory::Properties { 49 | self.memory.properties() 50 | } 51 | 52 | #[inline] 53 | fn memory(&self) -> &B::Memory { 54 | self.memory.raw() 55 | } 56 | 57 | #[inline] 58 | fn range(&self) -> Range { 59 | 0..self.memory.size() 60 | } 61 | 62 | fn map<'a>( 63 | &'a mut self, 64 | device: &B::Device, 65 | range: Range, 66 | ) -> Result, gfx_hal::device::MapError> { 67 | assert!( 68 | range.start < range.end, 69 | "Memory mapping region must have valid size" 70 | ); 71 | 72 | if !self.memory.host_visible() { 73 | //TODO: invalid access error 74 | return Err(gfx_hal::device::MapError::MappingFailed); 75 | } 76 | 77 | unsafe { 78 | if let Some(ptr) = self 79 | .mapping 80 | .clone() 81 | .and_then(|mapping| mapped_fitting_range(mapping.0, mapping.1, range.clone())) 82 | { 83 | Ok(MappedRange::from_raw(&self.memory, ptr, range)) 84 | } else { 85 | self.unmap(device); 86 | let ptr = device.map_memory(self.memory.raw(), range.clone())?; 87 | let ptr = NonNull::new(ptr).expect("Memory mapping shouldn't return nullptr"); 88 | let mapping = MappedRange::from_raw(&self.memory, ptr, range); 89 | self.mapping = Some((mapping.ptr(), mapping.range())); 90 | Ok(mapping) 91 | } 92 | } 93 | } 94 | 95 | fn unmap(&mut self, device: &B::Device) { 96 | if self.mapping.take().is_some() { 97 | unsafe { 98 | // trace!("Unmap memory: {:#?}", self.memory); 99 | device.unmap_memory(self.memory.raw()); 100 | } 101 | } 102 | } 103 | } 104 | 105 | /// Dedicated memory allocator that uses memory object per allocation requested. 106 | /// 107 | /// This allocator suites best huge allocations. 108 | /// From 32 MiB when GPU has 4-8 GiB memory total. 109 | /// 110 | /// `Heaps` use this allocator when none of sub-allocators bound to the memory type 111 | /// can handle size required. 112 | /// TODO: Check if resource prefers dedicated memory. 113 | #[derive(Debug)] 114 | pub struct DedicatedAllocator { 115 | memory_type: gfx_hal::MemoryTypeId, 116 | memory_properties: gfx_hal::memory::Properties, 117 | used: u64, 118 | } 119 | 120 | impl DedicatedAllocator { 121 | /// Get properties required by the allocator. 122 | pub fn properties_required() -> gfx_hal::memory::Properties { 123 | gfx_hal::memory::Properties::empty() 124 | } 125 | 126 | /// Create new `LinearAllocator` 127 | /// for `memory_type` with `memory_properties` specified 128 | pub fn new( 129 | memory_type: gfx_hal::MemoryTypeId, 130 | memory_properties: gfx_hal::memory::Properties, 131 | ) -> Self { 132 | DedicatedAllocator { 133 | memory_type, 134 | memory_properties, 135 | used: 0, 136 | } 137 | } 138 | } 139 | 140 | impl Allocator for DedicatedAllocator 141 | where 142 | B: Backend, 143 | { 144 | type Block = DedicatedBlock; 145 | 146 | fn kind() -> Kind { 147 | Kind::Dedicated 148 | } 149 | 150 | #[inline] 151 | fn alloc( 152 | &mut self, 153 | device: &B::Device, 154 | size: u64, 155 | _align: u64, 156 | ) -> Result<(DedicatedBlock, u64), gfx_hal::device::AllocationError> { 157 | let memory = unsafe { 158 | Memory::from_raw( 159 | device.allocate_memory(self.memory_type, size)?, 160 | size, 161 | self.memory_properties, 162 | ) 163 | }; 164 | 165 | self.used += size; 166 | 167 | Ok((DedicatedBlock::from_memory(memory), size)) 168 | } 169 | 170 | #[inline] 171 | fn free(&mut self, device: &B::Device, mut block: DedicatedBlock) -> u64 { 172 | block.unmap(device); 173 | let size = block.memory.size(); 174 | self.used -= size; 175 | unsafe { 176 | device.free_memory(block.memory.into_raw()); 177 | } 178 | size 179 | } 180 | } 181 | 182 | impl Drop for DedicatedAllocator { 183 | fn drop(&mut self) { 184 | if self.used > 0 { 185 | log::error!("Not all allocation from DedicatedAllocator was freed"); 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /mesh/src/format/obj.rs: -------------------------------------------------------------------------------- 1 | //! Loading mesh data from obj format. 2 | 3 | use log::trace; 4 | use { 5 | crate::{mesh::MeshBuilder, Normal, Position, TexCoord}, 6 | wavefront_obj::obj, 7 | }; 8 | 9 | /// Object loading error.Option 10 | #[derive(Debug, PartialEq)] 11 | pub enum ObjError { 12 | /// The passed bytes were improper UTF-8 data. 13 | Utf8(std::str::Utf8Error), 14 | /// Parsing of the obj failed. 15 | Parse(wavefront_obj::ParseError), 16 | } 17 | 18 | impl std::error::Error for ObjError {} 19 | impl std::fmt::Display for ObjError { 20 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 21 | match self { 22 | ObjError::Utf8(e) => write!(f, "{}", e), 23 | ObjError::Parse(e) => write!( 24 | f, 25 | "Error parsing object file at line {}: {}", 26 | e.line_number, e.message 27 | ), 28 | } 29 | } 30 | } 31 | 32 | /// Load mesh data from obj. 33 | pub fn load_from_obj( 34 | bytes: &[u8], 35 | ) -> Result, Option)>, ObjError> { 36 | let string = std::str::from_utf8(bytes).map_err(ObjError::Utf8)?; 37 | obj::parse(string) 38 | .and_then(load_from_data) 39 | .map_err(ObjError::Parse) 40 | } 41 | 42 | fn load_from_data( 43 | obj_set: obj::ObjSet, 44 | ) -> Result, Option)>, wavefront_obj::ParseError> { 45 | // Takes a list of objects that contain geometries that contain shapes that contain 46 | // vertex/texture/normal indices into the main list of vertices, and converts to 47 | // MeshBuilders with Position, Normal, TexCoord. 48 | trace!("Loading mesh"); 49 | let mut objects = vec![]; 50 | 51 | for object in obj_set.objects { 52 | for geometry in &object.geometry { 53 | let mut builder = MeshBuilder::new(); 54 | 55 | let mut indices = Vec::new(); 56 | 57 | geometry.shapes.iter().for_each(|shape| { 58 | if let obj::Primitive::Triangle(v1, v2, v3) = shape.primitive { 59 | indices.push(v1); 60 | indices.push(v2); 61 | indices.push(v3); 62 | } 63 | }); 64 | // We can't use the vertices directly because we have per face normals and not per vertex normals in most obj files 65 | // TODO: Compress duplicates and return indices for indexbuffer. 66 | let positions = indices 67 | .iter() 68 | .map(|index| { 69 | let vertex: obj::Normal = object.vertices[index.0]; 70 | Position([vertex.x as f32, vertex.y as f32, vertex.z as f32]) 71 | }) 72 | .collect::>(); 73 | 74 | trace!("Loading normals"); 75 | let normals = indices 76 | .iter() 77 | .map(|index| { 78 | index 79 | .2 80 | .map(|i| { 81 | let normal: obj::Normal = object.normals[i]; 82 | Normal([normal.x as f32, normal.y as f32, normal.z as f32]) 83 | }) 84 | .unwrap_or(Normal([0.0, 0.0, 0.0])) 85 | }) 86 | .collect::>(); 87 | 88 | let tex_coords = indices 89 | .iter() 90 | .map(|index| { 91 | index 92 | .1 93 | .map(|i| { 94 | let tvertex: obj::TVertex = object.tex_vertices[i]; 95 | TexCoord([tvertex.u as f32, tvertex.v as f32]) 96 | }) 97 | .unwrap_or(TexCoord([0.0, 0.0])) 98 | }) 99 | .collect::>(); 100 | 101 | // builder.set_indices(indices.iter().map(|i| i.0 as u16).collect::>()); 102 | 103 | debug_assert!(&normals.len() == &positions.len()); 104 | debug_assert!(&tex_coords.len() == &positions.len()); 105 | 106 | builder.add_vertices(positions); 107 | builder.add_vertices(normals); 108 | builder.add_vertices(tex_coords); 109 | 110 | // TODO: Add Material loading 111 | objects.push((builder, geometry.material_name.clone())) 112 | } 113 | } 114 | trace!("Loaded mesh"); 115 | Ok(objects) 116 | } 117 | 118 | #[cfg(test)] 119 | mod test { 120 | use super::*; 121 | 122 | #[test] 123 | fn test_load_from_obj() { 124 | let quad = b"v -1.000000 -1.000000 1.000000\nv 1.000000 -1.000000 1.000000\nv -1.000000 1.000000 1.000000\nv 1.000000 1.000000 1.000000\nv -1.000000 1.000000 -1.000000\nv 1.000000 1.000000 -1.000000\nv -1.000000 -1.000000 -1.000000\nv 1.000000 -1.000000 -1.000000\n 125 | vt 0.000000 0.000000\nvt 1.000000 0.000000\nvt 0.000000 1.000000\nvt 1.000000 1.000000\n 126 | vn 0.000000 0.000000 1.000000\nvn 0.000000 1.000000 0.000000\nvn 0.000000 0.000000 -1.000000\nvn 0.000000 -1.000000 0.000000\nvn 1.000000 0.000000 0.000000\nvn -1.000000 0.000000 0.000000\n 127 | s 1 128 | f 1/1/1 2/2/1 3/3/1\nf 3/3/1 2/2/1 4/4/1 129 | s 2 130 | f 3/1/2 4/2/2 5/3/2\nf 5/3/2 4/2/2 6/4/2 131 | s 3 132 | f 5/4/3 6/3/3 7/2/3\nf 7/2/3 6/3/3 8/1/3 133 | s 4 134 | f 7/1/4 8/2/4 1/3/4\nf 1/3/4 8/2/4 2/4/4 135 | s 5 136 | f 2/1/5 8/2/5 4/3/5\nf 4/3/5 8/2/5 6/4/5 137 | s 6 138 | f 7/1/6 1/2/6 5/3/6\nf 5/3/6 1/2/6 3/4/6 139 | "; 140 | let result = load_from_obj(quad).ok().unwrap(); 141 | // dbg!(& result); 142 | assert_eq!(result.len(), 1); 143 | 144 | // When compressed into unique vertices there should be 4 vertices per side of the quad 145 | // assert!() 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /descriptor/src/ranges.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | cmp::Ordering, 3 | ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}, 4 | }; 5 | 6 | pub use gfx_hal::pso::{DescriptorRangeDesc, DescriptorSetLayoutBinding, DescriptorType}; 7 | 8 | const DESCRIPTOR_TYPES_COUNT: usize = 11; 9 | 10 | const DESCRIPTOR_TYPES: [DescriptorType; DESCRIPTOR_TYPES_COUNT] = [ 11 | DescriptorType::Sampler, 12 | DescriptorType::CombinedImageSampler, 13 | DescriptorType::SampledImage, 14 | DescriptorType::StorageImage, 15 | DescriptorType::UniformTexelBuffer, 16 | DescriptorType::StorageTexelBuffer, 17 | DescriptorType::UniformBuffer, 18 | DescriptorType::StorageBuffer, 19 | DescriptorType::UniformBufferDynamic, 20 | DescriptorType::StorageBufferDynamic, 21 | DescriptorType::InputAttachment, 22 | ]; 23 | 24 | /// Number of descriptors per type. 25 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 26 | pub struct DescriptorRanges { 27 | counts: [u32; DESCRIPTOR_TYPES_COUNT], 28 | } 29 | 30 | impl DescriptorRanges { 31 | /// Create new instance without descriptors. 32 | pub fn zero() -> Self { 33 | DescriptorRanges { 34 | counts: [0; DESCRIPTOR_TYPES_COUNT], 35 | } 36 | } 37 | 38 | /// Add a single layout binding. 39 | /// Useful when created with `DescriptorRanges::zero()`. 40 | pub fn add_binding(&mut self, binding: DescriptorSetLayoutBinding) { 41 | self.counts[binding.ty as usize] += binding.count as u32; 42 | } 43 | 44 | /// Iterate through ranges yelding 45 | /// descriptor types and their amount. 46 | pub fn iter(&self) -> DescriptorRangesIter<'_> { 47 | DescriptorRangesIter { 48 | counts: &self.counts, 49 | index: 0, 50 | } 51 | } 52 | 53 | /// Read as slice. 54 | pub fn counts(&self) -> &[u32] { 55 | &self.counts 56 | } 57 | 58 | /// Read or write as slice. 59 | pub fn counts_mut(&mut self) -> &mut [u32] { 60 | &mut self.counts 61 | } 62 | 63 | /// Calculate ranges from bindings. 64 | pub fn from_bindings(bindings: &[DescriptorSetLayoutBinding]) -> Self { 65 | let mut descs = Self::zero(); 66 | 67 | for binding in bindings { 68 | descs.counts[binding.ty as usize] += binding.count as u32; 69 | } 70 | 71 | descs 72 | } 73 | 74 | /// Calculate ranges from bindings, specified with an iterator. 75 | pub fn from_binding_iter(bindings: I) -> Self 76 | where 77 | I: Iterator, 78 | { 79 | let mut descs = Self::zero(); 80 | 81 | for binding in bindings { 82 | descs.counts[binding.ty as usize] += binding.count as u32; 83 | } 84 | 85 | descs 86 | } 87 | } 88 | 89 | impl PartialOrd for DescriptorRanges { 90 | fn partial_cmp(&self, other: &Self) -> Option { 91 | let mut ord = self.counts[0].partial_cmp(&other.counts[0])?; 92 | for i in 1..DESCRIPTOR_TYPES_COUNT { 93 | match (ord, self.counts[i].partial_cmp(&other.counts[i])?) { 94 | (Ordering::Less, Ordering::Greater) | (Ordering::Greater, Ordering::Less) => { 95 | return None; 96 | } 97 | (Ordering::Equal, new) => ord = new, 98 | _ => (), 99 | } 100 | } 101 | Some(ord) 102 | } 103 | } 104 | 105 | impl Add for DescriptorRanges { 106 | type Output = Self; 107 | fn add(mut self, rhs: Self) -> Self { 108 | self += rhs; 109 | self 110 | } 111 | } 112 | 113 | impl AddAssign for DescriptorRanges { 114 | fn add_assign(&mut self, rhs: Self) { 115 | for i in 0..DESCRIPTOR_TYPES_COUNT { 116 | self.counts[i] += rhs.counts[i]; 117 | } 118 | } 119 | } 120 | 121 | impl Sub for DescriptorRanges { 122 | type Output = Self; 123 | fn sub(mut self, rhs: Self) -> Self { 124 | self -= rhs; 125 | self 126 | } 127 | } 128 | 129 | impl SubAssign for DescriptorRanges { 130 | fn sub_assign(&mut self, rhs: Self) { 131 | for i in 0..DESCRIPTOR_TYPES_COUNT { 132 | self.counts[i] -= rhs.counts[i]; 133 | } 134 | } 135 | } 136 | 137 | impl Mul for DescriptorRanges { 138 | type Output = Self; 139 | fn mul(mut self, rhs: u32) -> Self { 140 | self *= rhs; 141 | self 142 | } 143 | } 144 | 145 | impl MulAssign for DescriptorRanges { 146 | fn mul_assign(&mut self, rhs: u32) { 147 | for i in 0..DESCRIPTOR_TYPES_COUNT { 148 | self.counts[i] *= rhs; 149 | } 150 | } 151 | } 152 | 153 | impl<'a> IntoIterator for &'a DescriptorRanges { 154 | type Item = DescriptorRangeDesc; 155 | type IntoIter = DescriptorRangesIter<'a>; 156 | 157 | fn into_iter(self) -> DescriptorRangesIter<'a> { 158 | self.iter() 159 | } 160 | } 161 | 162 | /// Iterator over descriptor ranges. 163 | pub struct DescriptorRangesIter<'a> { 164 | counts: &'a [u32; DESCRIPTOR_TYPES_COUNT], 165 | index: u8, 166 | } 167 | 168 | impl<'a> Iterator for DescriptorRangesIter<'a> { 169 | type Item = DescriptorRangeDesc; 170 | 171 | fn next(&mut self) -> Option { 172 | loop { 173 | let index = self.index as usize; 174 | if index >= DESCRIPTOR_TYPES_COUNT { 175 | return None; 176 | } else { 177 | self.index += 1; 178 | if self.counts[index] > 0 { 179 | return Some(DescriptorRangeDesc { 180 | count: self.counts[index] as usize, 181 | ty: DESCRIPTOR_TYPES[index], 182 | }); 183 | } 184 | } 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /graph/src/node/render/group/mod.rs: -------------------------------------------------------------------------------- 1 | mod simple; 2 | 3 | pub use self::simple::*; 4 | 5 | use { 6 | crate::{ 7 | command::{QueueId, RenderPassEncoder}, 8 | factory::Factory, 9 | graph::GraphContext, 10 | node::{ 11 | render::{pass::SubpassBuilder, PrepareResult}, 12 | BufferAccess, DescBuilder, ImageAccess, NodeBuffer, NodeImage, 13 | }, 14 | BufferId, ImageId, NodeId, 15 | }, 16 | rendy_core::hal::Backend, 17 | }; 18 | 19 | /// Descriptor for render group 20 | pub trait RenderGroupDesc: std::fmt::Debug { 21 | /// Make render group builder. 22 | fn builder(self) -> DescBuilder 23 | where 24 | Self: Sized, 25 | { 26 | DescBuilder { 27 | desc: self, 28 | buffers: Vec::new(), 29 | images: Vec::new(), 30 | dependencies: Vec::new(), 31 | marker: std::marker::PhantomData, 32 | } 33 | } 34 | 35 | /// Get buffers used by the group. Empty by default. 36 | fn buffers(&self) -> Vec { 37 | Vec::new() 38 | } 39 | 40 | /// Get images used by the group. Empty by default. 41 | fn images(&self) -> Vec { 42 | Vec::new() 43 | } 44 | 45 | /// Number of color output images. One by default. 46 | fn colors(&self) -> usize { 47 | 1 48 | } 49 | 50 | /// Is depth image used. True by default. 51 | fn depth(&self) -> bool { 52 | true 53 | } 54 | 55 | /// Build render group. 56 | fn build<'a>( 57 | self, 58 | ctx: &GraphContext, 59 | factory: &mut Factory, 60 | queue: QueueId, 61 | aux: &T, 62 | framebuffer_width: u32, 63 | framebuffer_height: u32, 64 | subpass: rendy_core::hal::pass::Subpass<'_, B>, 65 | buffers: Vec, 66 | images: Vec, 67 | ) -> Result>, rendy_core::hal::pso::CreationError>; 68 | } 69 | 70 | /// One or more graphics pipelines to be called in subpass. 71 | pub trait RenderGroup: std::fmt::Debug + Send + Sync { 72 | /// Prepare resources and data for rendering. 73 | fn prepare( 74 | &mut self, 75 | factory: &Factory, 76 | queue: QueueId, 77 | index: usize, 78 | subpass: rendy_core::hal::pass::Subpass<'_, B>, 79 | aux: &T, 80 | ) -> PrepareResult; 81 | 82 | /// Record commands. 83 | fn draw_inline( 84 | &mut self, 85 | encoder: RenderPassEncoder<'_, B>, 86 | index: usize, 87 | subpass: rendy_core::hal::pass::Subpass<'_, B>, 88 | aux: &T, 89 | ); 90 | 91 | /// Free all resources and destroy group instance. 92 | fn dispose(self: Box, factory: &mut Factory, aux: &T); 93 | } 94 | 95 | /// Builder fror render group. 96 | pub trait RenderGroupBuilder: std::fmt::Debug { 97 | /// Make subpass from render group. 98 | fn into_subpass(self) -> SubpassBuilder 99 | where 100 | Self: Sized + 'static, 101 | { 102 | SubpassBuilder::new().with_group(self) 103 | } 104 | 105 | /// Number of color output images. 106 | fn colors(&self) -> usize; 107 | 108 | /// Is depth image used. 109 | fn depth(&self) -> bool; 110 | 111 | /// Get buffers used by the group 112 | fn buffers(&self) -> Vec<(BufferId, BufferAccess)>; 113 | 114 | /// Get images used by the group 115 | fn images(&self) -> Vec<(ImageId, ImageAccess)>; 116 | 117 | /// Get nodes this group depends on. 118 | fn dependencies(&self) -> Vec; 119 | 120 | /// Build render group instance. 121 | fn build<'a>( 122 | self: Box, 123 | ctx: &GraphContext, 124 | factory: &mut Factory, 125 | queue: QueueId, 126 | aux: &T, 127 | framebuffer_width: u32, 128 | framebuffer_height: u32, 129 | subpass: rendy_core::hal::pass::Subpass<'_, B>, 130 | buffers: Vec, 131 | images: Vec, 132 | ) -> Result>, rendy_core::hal::pso::CreationError>; 133 | } 134 | 135 | impl RenderGroupBuilder for DescBuilder 136 | where 137 | B: Backend, 138 | T: ?Sized, 139 | D: RenderGroupDesc, 140 | { 141 | fn colors(&self) -> usize { 142 | self.desc.colors() 143 | } 144 | 145 | fn depth(&self) -> bool { 146 | self.desc.depth() 147 | } 148 | 149 | fn buffers(&self) -> Vec<(BufferId, BufferAccess)> { 150 | self.buffers 151 | .iter() 152 | .cloned() 153 | .zip(self.desc.buffers()) 154 | .collect() 155 | } 156 | 157 | fn images(&self) -> Vec<(ImageId, ImageAccess)> { 158 | self.images 159 | .iter() 160 | .cloned() 161 | .zip(self.desc.images()) 162 | .collect() 163 | } 164 | 165 | fn dependencies(&self) -> Vec { 166 | self.dependencies.clone() 167 | } 168 | 169 | fn build<'a>( 170 | self: Box, 171 | ctx: &GraphContext, 172 | factory: &mut Factory, 173 | queue: QueueId, 174 | aux: &T, 175 | framebuffer_width: u32, 176 | framebuffer_height: u32, 177 | subpass: rendy_core::hal::pass::Subpass<'_, B>, 178 | buffers: Vec, 179 | images: Vec, 180 | ) -> Result>, rendy_core::hal::pso::CreationError> { 181 | self.desc.build( 182 | ctx, 183 | factory, 184 | queue, 185 | aux, 186 | framebuffer_width, 187 | framebuffer_height, 188 | subpass, 189 | buffers, 190 | images, 191 | ) 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /resource/src/escape.rs: -------------------------------------------------------------------------------- 1 | //! This module provides wrapper for types that cannot be dropped silently. 2 | //! Usually such types are required to be returned to their creator, 3 | //! for example many Vulkan resources must be destroyed by the same 4 | //! Vulkan instance that created them. Because they need some outside 5 | //! context to be destroyed, Rust's `Drop` trait alone cannot handle them. 6 | //! The `Escape` wrapper helps the user handle these values by sending the 7 | //! underlying value to a `Terminal` when it is dropped. The user can 8 | //! then remove those values from the `Terminal` elsewhere in the program 9 | //! and destroy them however necessary. 10 | //! 11 | //! Users are encouraged to dispose of values manually while using `Escape` 12 | //! as just a safety net. 13 | 14 | use { 15 | crossbeam_channel::{Receiver, Sender, TryRecvError}, 16 | std::{ 17 | iter::repeat, 18 | mem::ManuallyDrop, 19 | ops::{Deref, DerefMut}, 20 | ptr::{drop_in_place, read}, 21 | sync::Arc, 22 | }, 23 | }; 24 | 25 | /// Allows values to "escape" dropping by sending them to the `Terminal`. 26 | #[derive(Debug)] 27 | pub struct Escape { 28 | value: ManuallyDrop, 29 | sender: Sender, 30 | } 31 | 32 | impl Escape { 33 | /// Escape value. 34 | pub fn escape(value: T, terminal: &Terminal) -> Self { 35 | Escape { 36 | value: ManuallyDrop::new(value), 37 | sender: Sender::clone(&terminal.sender), 38 | } 39 | } 40 | 41 | /// Unwrap escaping value. 42 | /// This will effectivly prevent it from escaping. 43 | pub fn unescape(escape: Self) -> T { 44 | unsafe { 45 | // Prevent ` as Drop>::drop` from being executed. 46 | let mut escape = ManuallyDrop::new(escape); 47 | 48 | // Release value from `ManuallyDrop`. 49 | let value = read(&mut *escape.value); 50 | 51 | // Drop sender. If it panics - value will be dropped. 52 | // Relevant values are allowed to be dropped due to panic. 53 | drop_in_place(&mut escape.sender); 54 | value 55 | } 56 | } 57 | 58 | /// Share escaped value. 59 | pub fn share(escape: Self) -> Handle { 60 | escape.into() 61 | } 62 | } 63 | 64 | impl Deref for Escape { 65 | type Target = T; 66 | fn deref(&self) -> &T { 67 | &*self.value 68 | } 69 | } 70 | 71 | impl DerefMut for Escape { 72 | fn deref_mut(&mut self) -> &mut T { 73 | &mut *self.value 74 | } 75 | } 76 | 77 | impl Drop for Escape { 78 | fn drop(&mut self) { 79 | unsafe { 80 | // Read value from `ManuallyDrop` wrapper and send it over the channel. 81 | match self.sender.send(read(&mut *self.value)) { 82 | Ok(_) => {} 83 | Err(_) => { 84 | log::error!("`Escape` was dropped after a `Terminal`?"); 85 | } 86 | } 87 | } 88 | } 89 | } 90 | 91 | /// This types allows the user to create `Escape` wrappers. 92 | /// Receives values from dropped `Escape` instances that was created by this `Terminal`. 93 | #[derive(Debug)] 94 | pub struct Terminal { 95 | receiver: Receiver, 96 | sender: ManuallyDrop>, 97 | } 98 | 99 | impl Default for Terminal { 100 | fn default() -> Self { 101 | Self::new() 102 | } 103 | } 104 | 105 | impl Terminal { 106 | /// Create new `Terminal`. 107 | pub fn new() -> Self { 108 | let (sender, receiver) = crossbeam_channel::unbounded(); 109 | Terminal { 110 | sender: ManuallyDrop::new(sender), 111 | receiver, 112 | } 113 | } 114 | 115 | /// Wrap the value. It will be yielded by iterator returned by `Terminal::drain` if `Escape` will be dropped. 116 | pub fn escape(&self, value: T) -> Escape { 117 | Escape::escape(value, &self) 118 | } 119 | 120 | // /// Check if `Escape` will send value to this `Terminal`. 121 | // pub fn owns(&self, escape: &Escape) -> bool { 122 | // *self.sender == escape.sender 123 | // } 124 | 125 | /// Get iterator over values from dropped `Escape` instances that was created by this `Terminal`. 126 | pub fn drain(&mut self) -> impl Iterator + '_ { 127 | repeat(()).scan(&mut self.receiver, move |receiver, ()| { 128 | // trace!("Drain escape"); 129 | if !receiver.is_empty() { 130 | receiver.recv().ok() 131 | } else { 132 | None 133 | } 134 | }) 135 | } 136 | } 137 | 138 | impl Drop for Terminal { 139 | fn drop(&mut self) { 140 | unsafe { 141 | ManuallyDrop::drop(&mut self.sender); 142 | match self.receiver.try_recv() { 143 | Err(TryRecvError::Disconnected) => {} 144 | _ => { 145 | log::error!("Terminal must be dropped after all `Escape`s"); 146 | } 147 | } 148 | } 149 | } 150 | } 151 | 152 | /// Allows values to "escape" dropping by sending them to the `Terminal`. 153 | /// Permit sharing unlike [`Escape`] 154 | /// 155 | /// [`Escape`]: ./struct.Escape.html 156 | #[derive(Debug)] 157 | pub struct Handle { 158 | inner: Arc>, 159 | } 160 | 161 | impl Clone for Handle { 162 | fn clone(&self) -> Self { 163 | Handle { 164 | inner: self.inner.clone(), 165 | } 166 | } 167 | } 168 | 169 | impl From> for Handle { 170 | fn from(value: Escape) -> Self { 171 | Handle { 172 | inner: Arc::new(value), 173 | } 174 | } 175 | } 176 | 177 | impl Deref for Handle { 178 | type Target = T; 179 | 180 | fn deref(&self) -> &T { 181 | &**self.inner 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /docs/buffers.md: -------------------------------------------------------------------------------- 1 | # Vulkan Buffers 2 | 3 | This is a brief overview of Vulkan Buffers and how they are used within Rendy. 4 | 5 | *Please note this is not intended to be a tutorial on Vulkan. It covers only enough to understand code written in Rendy* 6 | 7 | ## Buffers Overview 8 | 9 | A buffer is just an area of memory in which to store data. The goal with buffers in Vulkan, in general, is to make data available to the GPU. An important concept to remember is that when working with a graphics device, it will have its own memory (RAM), that is separate from the RAM your system uses. 10 | 11 | ### Memory Visibility and Coherency 12 | 13 | Can the CPU see the contents of the GPU's memory? Can the GPU access the system RAM? Good questions! The answer is, it depends. A graphics device will have capabilities, and it is up to you, intrepid programmer, to figure out what those are and code accordingly. 14 | 15 | A piece of memory is referred to as a `heap`. A `heap` has a size in bytes, and a location. The location can be local to the graphics device, or not local. In most systems, there will be two `heaps`: one on the Vulkan device, and one on the system used by the CPU. When you want to use some memory from a `heap`, you allocate it as a certain type of memory. Each type has different properties; a summary is below. 16 | 17 | *VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT*: If this is set, memory allocated with this type is the most efficient for device access. This bit _will only be set if and only if the heap has the `VK_MEMORY_HEAP_DEVICE_LOCAL_BIT` set as well_. 18 | 19 | *VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT*: If this is set, the memory is visible to the `host`; the system with the CPU. 20 | 21 | *VK_MEMORY_PROPERTY_HOST_COHERENT_BIT*: If this is set, host writes or device writes become visible to each other without explicit flush commands 22 | 23 | *VK_MEMORY_PROPERTY_HOST_CACHED_BIT*: If this is set, the memory is cached on the `host`. _This memory may not be host coherent, but reads from cached memory are usually faster than uncached memory_. This means that cached memory may not reflect the latest writes to it. 24 | 25 | *VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT*: If this is set, the memory is visible to the device only. Despite its name, whatever memory is behind this allocation may or may not be lazily allocated. This memory may only be used for VkImages, and only if the VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT is set _on the image_. An image that may or may not be displayed could use this, so that the memory is only allocated when it is actually shown. 26 | 27 | ### Why So Many? 28 | 29 | Vulkan is meant to be cross-platform, and used not just for graphics, but for compute as well. We often think of a graphics engine being meant for a desktop-style system with one graphics card, but there are many other configurations, such as: 30 | 31 | 1. A server with multiple graphics cards meant for highly parallelizable computation (machine learning, neural net training, etc) 32 | 2. CPUs with the GPUs embedded on the chip, also known as SOCs (System on a Chip) 33 | 3. Devices with Unified Memory Access (UMA) where memory is non-local to _both_ the host and the device (weird, huh?) 34 | 4. A GPU with no memory 35 | 5. A desktop system with 2 or more graphics cards 36 | 37 | ### Allocation and Deallocation 38 | 39 | When working with Vulkan, the programmer (that's you!), is responsible for requesting and freeing memory. This means it is possible to leak memory, even when using a safe language such as Rust. 40 | 41 | ## Rendy 42 | 43 | Memory management is an area where Rendy provides an abstraction layer. It has a memory manager called `Heaps`. You can find this module at `rendy/memory`. You will most often work with memory via the `Factory`. Using this module, you can request and free memory of various types and Rendy will handle the details. 44 | 45 | ### Buffer Example 46 | 47 | Let's look at an example: 48 | 49 | ```rust 50 | let buffer = factory 51 | .create_buffer( 52 | BufferInfo { 53 | size: buffer_frame_size(align) * frames as u64, 54 | usage: rendy_core::hal::buffer::Usage::UNIFORM 55 | | rendy_core::hal::buffer::Usage::INDIRECT 56 | | rendy_core::hal::buffer::Usage::VERTEX, 57 | }, 58 | Dynamic, 59 | ) 60 | .unwrap(); 61 | ``` 62 | 63 | This creates a buffer with a size large enough to hold the data for multiple frames in our game, can store indirect draw commands, and can store vertex data. It also has the Rendy type of `Dynamic`, which means it can be used to send data back and forth between the CPU and GPU (bidirectional) rather than just one direction (unidirectional). 64 | 65 | ### Rendy Memory Types 66 | 67 | Below is a summary of the memory types offered by Rendy. These contain a combination of flags most useful for their purpose; in some cases, they are differentiated only by usage. That is, the underlying Vulkan flags may be the same, but the Rendy type differs in order to be specific about usage. 68 | 69 | The code for these can be found in `rendy/memory/usage.rs`. 70 | 71 | 1. Dynamic 72 | * Host visible 73 | * _Prefers_ memory with fast GPU access 74 | * Used for data that will be frequently updated from the system side, such as uniform buffers, indirect calls, or instance rate vertex buffers 75 | 2. Upload 76 | * Host visible 77 | * _Prefers_ not device local, not cached 78 | * CPU to GPU data flow with mapping 79 | * Used for staging data before copying to `Data` memory 80 | 3. Download 81 | * Host visible 82 | * _Prefers_ not device local, cached 83 | * Used for copying data from `Data` memory to the host 84 | 4. Data 85 | * Device local and _not_ lazily allocated 86 | * Avoids memory that is also visible to the system 87 | * Used for render targets and persistent resources 88 | 89 | ## Useful Links 90 | 91 | [Usage Types in rendy_core::hal](https://docs.rs/gfx-hal/0.1.0/rendy_core::hal/buffer/struct.Usage.html) 92 | [Vulkan Vertex Buffers](https://vulkan-tutorial.com/Vertex_buffers) 93 | [Vulkan Indirect Draw](https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/vkCmdDrawIndirect.html) 94 | -------------------------------------------------------------------------------- /command/src/family/queue.rs: -------------------------------------------------------------------------------- 1 | use { 2 | super::{submission::*, QueueId}, 3 | crate::{buffer::Submittable, fence::*}, 4 | rendy_core::hal::{queue::CommandQueue, Backend}, 5 | }; 6 | 7 | /// Command queue wrapper. 8 | #[derive(Debug)] 9 | pub struct Queue { 10 | raw: B::CommandQueue, 11 | id: QueueId, 12 | next_epoch: u64, 13 | } 14 | 15 | family_owned!(@NOCAP Queue @ |q: &Self| q.id.family); 16 | 17 | impl Queue 18 | where 19 | B: Backend, 20 | { 21 | pub(super) fn new(raw: B::CommandQueue, id: QueueId) -> Self { 22 | Queue { 23 | id, 24 | raw, 25 | next_epoch: 0, 26 | } 27 | } 28 | 29 | /// Id of the queue. 30 | pub fn id(&self) -> QueueId { 31 | self.id 32 | } 33 | 34 | /// Get raw command queue. 35 | pub fn raw(&mut self) -> &mut impl CommandQueue { 36 | &mut self.raw 37 | } 38 | 39 | /// Returns next queue epoch. 40 | pub fn next_epoch(&self) -> u64 { 41 | self.next_epoch 42 | } 43 | 44 | /// Submit commands to the queue of the family. 45 | /// Fence must be submitted. 46 | pub unsafe fn submit<'a>( 47 | &mut self, 48 | submissions: impl IntoIterator< 49 | Item = Submission< 50 | B, 51 | impl IntoIterator< 52 | Item = ( 53 | &'a (impl std::borrow::Borrow + 'a), 54 | rendy_core::hal::pso::PipelineStage, 55 | ), 56 | >, 57 | impl IntoIterator>, 58 | impl IntoIterator + 'a)>, 59 | >, 60 | >, 61 | fence: Option<&mut Fence>, 62 | ) { 63 | assert!(fence.as_ref().map_or(true, |f| f.is_unsignaled())); 64 | 65 | let mut submissions = submissions.into_iter().peekable(); 66 | if submissions.peek().is_none() && fence.is_some() { 67 | self.raw.submit( 68 | rendy_core::hal::queue::Submission { 69 | command_buffers: std::iter::empty::<&'a B::CommandBuffer>(), 70 | wait_semaphores: std::iter::empty::<(&'a B::Semaphore, _)>(), 71 | signal_semaphores: std::iter::empty::<&'a B::Semaphore>(), 72 | }, 73 | fence.as_ref().map(|f| f.raw()), 74 | ); 75 | } else { 76 | let family = self.id.family; 77 | while let Some(submission) = submissions.next() { 78 | self.raw.submit( 79 | rendy_core::hal::queue::Submission { 80 | command_buffers: submission.submits.into_iter().map(|submit| { 81 | assert_eq!(submit.family(), family); 82 | submit.raw() 83 | }), 84 | wait_semaphores: submission.waits.into_iter().map(|w| (w.0.borrow(), w.1)), 85 | signal_semaphores: submission.signals.into_iter().map(|s| s.borrow()), 86 | }, 87 | submissions 88 | .peek() 89 | .map_or(fence.as_ref().map(|f| f.raw()), |_| None), 90 | ); 91 | } 92 | } 93 | 94 | if let Some(fence) = fence { 95 | fence.mark_submitted(FenceEpoch { 96 | queue: self.id, 97 | epoch: self.next_epoch, 98 | }); 99 | self.next_epoch += 1; 100 | } 101 | } 102 | 103 | /// Submit commands to the queue of the family. 104 | /// Fence must be submitted. 105 | /// This version uses raw fence and doesn't increment epoch. 106 | pub unsafe fn submit_raw_fence<'a>( 107 | &mut self, 108 | submissions: impl IntoIterator< 109 | Item = Submission< 110 | B, 111 | impl IntoIterator< 112 | Item = ( 113 | &'a (impl std::borrow::Borrow + 'a), 114 | rendy_core::hal::pso::PipelineStage, 115 | ), 116 | >, 117 | impl IntoIterator>, 118 | impl IntoIterator + 'a)>, 119 | >, 120 | >, 121 | fence: Option<&B::Fence>, 122 | ) { 123 | let mut submissions = submissions.into_iter().peekable(); 124 | if submissions.peek().is_none() && fence.is_some() { 125 | self.raw.submit( 126 | rendy_core::hal::queue::Submission { 127 | command_buffers: std::iter::empty::<&'a B::CommandBuffer>(), 128 | wait_semaphores: std::iter::empty::<(&'a B::Semaphore, _)>(), 129 | signal_semaphores: std::iter::empty::<&'a B::Semaphore>(), 130 | }, 131 | fence, 132 | ); 133 | } else { 134 | let family = self.id.family; 135 | while let Some(submission) = submissions.next() { 136 | self.raw.submit( 137 | rendy_core::hal::queue::Submission { 138 | command_buffers: submission.submits.into_iter().map(|submit| { 139 | assert_eq!(submit.family(), family); 140 | submit.raw() 141 | }), 142 | wait_semaphores: submission.waits.into_iter().map(|w| (w.0.borrow(), w.1)), 143 | signal_semaphores: submission.signals.into_iter().map(|s| s.borrow()), 144 | }, 145 | submissions.peek().map_or(fence, |_| None), 146 | ); 147 | } 148 | } 149 | } 150 | 151 | /// Wait for queue to finish all pending commands. 152 | pub fn wait_idle(&self) -> Result<(), rendy_core::hal::device::OutOfMemory> { 153 | self.raw.wait_idle() 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /memory/src/heaps/memory_type.rs: -------------------------------------------------------------------------------- 1 | use { 2 | super::{BlockFlavor, HeapsConfig}, 3 | crate::{allocator::*, usage::MemoryUsage, utilization::*}, 4 | gfx_hal::memory::Properties, 5 | }; 6 | 7 | #[derive(Debug)] 8 | pub(super) struct MemoryType { 9 | heap_index: usize, 10 | properties: Properties, 11 | dedicated: DedicatedAllocator, 12 | linear: Option>, 13 | dynamic: Option>, 14 | // chunk: Option, 15 | used: u64, 16 | effective: u64, 17 | } 18 | 19 | impl MemoryType 20 | where 21 | B: gfx_hal::Backend, 22 | { 23 | pub(super) fn new( 24 | memory_type: gfx_hal::MemoryTypeId, 25 | heap_index: usize, 26 | properties: Properties, 27 | config: HeapsConfig, 28 | ) -> Self { 29 | MemoryType { 30 | properties, 31 | heap_index, 32 | dedicated: DedicatedAllocator::new(memory_type, properties), 33 | linear: if properties.contains(Properties::CPU_VISIBLE) { 34 | config 35 | .linear 36 | .map(|config| LinearAllocator::new(memory_type, properties, config)) 37 | } else { 38 | None 39 | }, 40 | dynamic: config 41 | .dynamic 42 | .map(|config| DynamicAllocator::new(memory_type, properties, config)), 43 | used: 0, 44 | effective: 0, 45 | } 46 | } 47 | 48 | pub(super) fn properties(&self) -> Properties { 49 | self.properties 50 | } 51 | 52 | pub(super) fn heap_index(&self) -> usize { 53 | self.heap_index 54 | } 55 | 56 | pub(super) fn alloc( 57 | &mut self, 58 | device: &B::Device, 59 | usage: impl MemoryUsage, 60 | size: u64, 61 | align: u64, 62 | ) -> Result<(BlockFlavor, u64), gfx_hal::device::AllocationError> { 63 | let (block, allocated) = self.alloc_impl(device, usage, size, align)?; 64 | self.effective += block.size(); 65 | self.used += allocated; 66 | Ok((block, allocated)) 67 | } 68 | 69 | fn alloc_impl( 70 | &mut self, 71 | device: &B::Device, 72 | usage: impl MemoryUsage, 73 | size: u64, 74 | align: u64, 75 | ) -> Result<(BlockFlavor, u64), gfx_hal::device::AllocationError> { 76 | match (self.dynamic.as_mut(), self.linear.as_mut()) { 77 | (Some(dynamic), Some(linear)) => { 78 | if dynamic.max_allocation() >= size 79 | && usage.allocator_fitness(Kind::Dynamic) 80 | > usage.allocator_fitness(Kind::Linear) 81 | { 82 | dynamic 83 | .alloc(device, size, align) 84 | .map(|(block, size)| (BlockFlavor::Dynamic(block), size)) 85 | } else if linear.max_allocation() >= size 86 | && usage.allocator_fitness(Kind::Linear) > 0 87 | { 88 | linear 89 | .alloc(device, size, align) 90 | .map(|(block, size)| (BlockFlavor::Linear(block), size)) 91 | } else { 92 | self.dedicated 93 | .alloc(device, size, align) 94 | .map(|(block, size)| (BlockFlavor::Dedicated(block), size)) 95 | } 96 | } 97 | (Some(dynamic), None) => { 98 | if dynamic.max_allocation() >= size && usage.allocator_fitness(Kind::Dynamic) > 0 { 99 | dynamic 100 | .alloc(device, size, align) 101 | .map(|(block, size)| (BlockFlavor::Dynamic(block), size)) 102 | } else { 103 | self.dedicated 104 | .alloc(device, size, align) 105 | .map(|(block, size)| (BlockFlavor::Dedicated(block), size)) 106 | } 107 | } 108 | (None, Some(linear)) => { 109 | if linear.max_allocation() >= size && usage.allocator_fitness(Kind::Linear) > 0 { 110 | linear 111 | .alloc(device, size, align) 112 | .map(|(block, size)| (BlockFlavor::Linear(block), size)) 113 | } else { 114 | self.dedicated 115 | .alloc(device, size, align) 116 | .map(|(block, size)| (BlockFlavor::Dedicated(block), size)) 117 | } 118 | } 119 | (None, None) => self 120 | .dedicated 121 | .alloc(device, size, align) 122 | .map(|(block, size)| (BlockFlavor::Dedicated(block), size)), 123 | } 124 | } 125 | 126 | pub(super) fn free(&mut self, device: &B::Device, block: BlockFlavor) -> u64 { 127 | match block { 128 | BlockFlavor::Dedicated(block) => self.dedicated.free(device, block), 129 | BlockFlavor::Linear(block) => self.linear.as_mut().unwrap().free(device, block), 130 | BlockFlavor::Dynamic(block) => self.dynamic.as_mut().unwrap().free(device, block), 131 | } 132 | } 133 | 134 | pub(super) fn dispose(self, device: &B::Device) { 135 | log::trace!("Dispose memory allocators"); 136 | 137 | if let Some(linear) = self.linear { 138 | linear.dispose(device); 139 | log::trace!("Linear allocator disposed"); 140 | } 141 | if let Some(dynamic) = self.dynamic { 142 | dynamic.dispose(); 143 | log::trace!("Dynamic allocator disposed"); 144 | } 145 | } 146 | 147 | pub(super) fn utilization(&self) -> MemoryTypeUtilization { 148 | MemoryTypeUtilization { 149 | utilization: MemoryUtilization { 150 | used: self.used, 151 | effective: self.effective, 152 | }, 153 | properties: self.properties, 154 | heap_index: self.heap_index, 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /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 rendy_core::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 rendy_core::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 41 | | Self::COLOR_ATTACHMENT_WRITE 42 | | Self::DEPTH_STENCIL_ATTACHMENT_WRITE 43 | | Self::TRANSFER_WRITE 44 | | Self::HOST_WRITE 45 | | Self::MEMORY_WRITE, 46 | ) 47 | } 48 | } 49 | 50 | /// Trait to abstract of specific usage flags. 51 | pub trait UsageFlags: Copy + Debug + BitOr + BitOrAssign + 'static {} 52 | 53 | impl UsageFlags for rendy_core::hal::buffer::Usage {} 54 | impl UsageFlags for rendy_core::hal::image::Usage {} 55 | 56 | /// Abstracts resource types that uses different usage flags and layouts types. 57 | pub trait Resource: 'static { 58 | /// Access flags for resource type. 59 | type Access: AccessFlags; 60 | 61 | /// Usage flags type for the resource. 62 | type Usage: UsageFlags; 63 | 64 | /// Layout type for the resource. 65 | type Layout: Copy + Debug + 'static; 66 | 67 | /// Empty usage. 68 | fn no_usage() -> Self::Usage; 69 | 70 | /// Layout suitable for specified accesses. 71 | fn layout_for(access: Self::Access) -> Self::Layout; 72 | } 73 | 74 | /// Buffer resource type. 75 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 76 | pub struct Buffer; 77 | 78 | impl Resource for Buffer { 79 | type Access = rendy_core::hal::buffer::Access; 80 | type Usage = rendy_core::hal::buffer::Usage; 81 | type Layout = (); 82 | 83 | fn no_usage() -> Self::Usage { 84 | rendy_core::hal::buffer::Usage::empty() 85 | } 86 | 87 | fn layout_for(_access: rendy_core::hal::buffer::Access) {} 88 | } 89 | 90 | /// Image resource type. 91 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 92 | pub struct Image; 93 | 94 | impl Resource for Image { 95 | type Access = rendy_core::hal::image::Access; 96 | 97 | type Usage = rendy_core::hal::image::Usage; 98 | 99 | type Layout = rendy_core::hal::image::Layout; 100 | 101 | fn no_usage() -> Self::Usage { 102 | rendy_core::hal::image::Usage::empty() 103 | } 104 | 105 | fn layout_for(access: rendy_core::hal::image::Access) -> rendy_core::hal::image::Layout { 106 | let mut acc = None; 107 | if access.contains(rendy_core::hal::image::Access::INPUT_ATTACHMENT_READ) { 108 | acc = Some(common_layout( 109 | acc, 110 | rendy_core::hal::image::Layout::ShaderReadOnlyOptimal, 111 | )); 112 | } 113 | if access.contains(rendy_core::hal::image::Access::COLOR_ATTACHMENT_READ) { 114 | acc = Some(common_layout( 115 | acc, 116 | rendy_core::hal::image::Layout::ColorAttachmentOptimal, 117 | )); 118 | } 119 | if access.contains(rendy_core::hal::image::Access::COLOR_ATTACHMENT_WRITE) { 120 | acc = Some(common_layout( 121 | acc, 122 | rendy_core::hal::image::Layout::ColorAttachmentOptimal, 123 | )); 124 | } 125 | if access.contains(rendy_core::hal::image::Access::DEPTH_STENCIL_ATTACHMENT_READ) { 126 | acc = Some(common_layout( 127 | acc, 128 | rendy_core::hal::image::Layout::DepthStencilReadOnlyOptimal, 129 | )); 130 | } 131 | if access.contains(rendy_core::hal::image::Access::DEPTH_STENCIL_ATTACHMENT_WRITE) { 132 | acc = Some(common_layout( 133 | acc, 134 | rendy_core::hal::image::Layout::DepthStencilAttachmentOptimal, 135 | )); 136 | } 137 | if access.contains(rendy_core::hal::image::Access::TRANSFER_READ) { 138 | acc = Some(common_layout( 139 | acc, 140 | rendy_core::hal::image::Layout::TransferSrcOptimal, 141 | )); 142 | } 143 | if access.contains(rendy_core::hal::image::Access::TRANSFER_WRITE) { 144 | acc = Some(common_layout( 145 | acc, 146 | rendy_core::hal::image::Layout::TransferDstOptimal, 147 | )); 148 | } 149 | acc.unwrap_or(rendy_core::hal::image::Layout::General) 150 | } 151 | } 152 | 153 | fn common_layout( 154 | acc: Option, 155 | layout: rendy_core::hal::image::Layout, 156 | ) -> rendy_core::hal::image::Layout { 157 | match (acc, layout) { 158 | (None, layout) => layout, 159 | (Some(left), right) if left == right => left, 160 | ( 161 | Some(rendy_core::hal::image::Layout::DepthStencilReadOnlyOptimal), 162 | rendy_core::hal::image::Layout::DepthStencilAttachmentOptimal, 163 | ) => rendy_core::hal::image::Layout::DepthStencilAttachmentOptimal, 164 | ( 165 | Some(rendy_core::hal::image::Layout::DepthStencilAttachmentOptimal), 166 | rendy_core::hal::image::Layout::DepthStencilReadOnlyOptimal, 167 | ) => rendy_core::hal::image::Layout::DepthStencilAttachmentOptimal, 168 | (Some(_), _) => rendy_core::hal::image::Layout::General, 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /command/src/fence.rs: -------------------------------------------------------------------------------- 1 | use { 2 | crate::{ 3 | core::{device_owned, Device, DeviceId}, 4 | family::QueueId, 5 | }, 6 | rendy_core::hal::{device::Device as _, Backend}, 7 | }; 8 | 9 | /// Queue epoch is the point in particluar queue timeline when fence is submitted. 10 | #[derive(Clone, Copy, Debug)] 11 | pub struct FenceEpoch { 12 | /// Queue that signals fence. 13 | pub queue: QueueId, 14 | 15 | /// Queue epoch counter. 16 | pub epoch: u64, 17 | } 18 | 19 | #[derive(Clone, Copy, Debug)] 20 | enum FenceState { 21 | Unsignaled, 22 | Signaled, 23 | Submitted(FenceEpoch), 24 | } 25 | 26 | /// Fence wrapper. 27 | #[derive(Debug)] 28 | pub struct Fence { 29 | device: DeviceId, 30 | raw: B::Fence, 31 | state: FenceState, 32 | } 33 | 34 | device_owned!(Fence); 35 | 36 | impl Fence 37 | where 38 | B: Backend, 39 | { 40 | /// Create new fence in signaled or unsignaled state. 41 | pub fn new( 42 | device: &Device, 43 | signaled: bool, 44 | ) -> Result { 45 | let raw = device.raw().create_fence(false)?; 46 | Ok(Fence { 47 | device: device.id(), 48 | raw, 49 | state: if signaled { 50 | FenceState::Signaled 51 | } else { 52 | FenceState::Unsignaled 53 | }, 54 | }) 55 | } 56 | 57 | /// Check if fence was submitted. 58 | pub fn is_submitted(&self) -> bool { 59 | match self.state { 60 | FenceState::Submitted(_) => true, 61 | _ => false, 62 | } 63 | } 64 | 65 | /// Check if fence is signaled. 66 | pub fn is_signaled(&self) -> bool { 67 | match self.state { 68 | FenceState::Signaled => true, 69 | _ => false, 70 | } 71 | } 72 | 73 | /// Check if fence is unsignaled. 74 | /// It can be submitted as well. 75 | pub fn is_unsignaled(&self) -> bool { 76 | !self.is_signaled() 77 | } 78 | 79 | /// Panics if signaled or submitted. 80 | /// Becomes `Submitted` after. 81 | pub(crate) fn mark_submitted(&mut self, epoch: FenceEpoch) { 82 | match self.state { 83 | FenceState::Unsignaled => { 84 | self.state = FenceState::Submitted(epoch); 85 | } 86 | _ => panic!("Must be Unsignaled"), 87 | } 88 | } 89 | 90 | /// Reset signaled fence. 91 | /// Panics if not signaled. 92 | /// Becomes unsigneled. 93 | pub fn reset( 94 | &mut self, 95 | device: &Device, 96 | ) -> Result<(), rendy_core::hal::device::OutOfMemory> { 97 | self.assert_device_owner(device); 98 | match self.state { 99 | FenceState::Signaled => { 100 | unsafe { device.reset_fence(&self.raw) }?; 101 | self.state = FenceState::Unsignaled; 102 | Ok(()) 103 | } 104 | _ => panic!("Must be signaled"), 105 | } 106 | } 107 | 108 | /// Mark signaled fence as reset. 109 | /// Panics if not signaled. 110 | /// Becomes unsigneled. 111 | /// Fence must be reset using raw fence value. 112 | pub unsafe fn mark_reset(&mut self) { 113 | match self.state { 114 | FenceState::Signaled => { 115 | self.state = FenceState::Unsignaled; 116 | } 117 | _ => panic!("Must be signaled"), 118 | } 119 | } 120 | 121 | /// Mark fence as signaled. 122 | /// Panics if not submitted. 123 | /// Fence must be checked to be signaled using raw fence value. 124 | pub unsafe fn mark_signaled(&mut self) -> FenceEpoch { 125 | match self.state { 126 | FenceState::Submitted(epoch) => { 127 | self.state = FenceState::Signaled; 128 | epoch 129 | } 130 | _ => panic!("Must be submitted"), 131 | } 132 | } 133 | 134 | /// Wait for fence to become signaled. 135 | /// Panics if not submitted. 136 | /// Returns submission epoch on success. 137 | pub fn wait_signaled( 138 | &mut self, 139 | device: &Device, 140 | timeout_ns: u64, 141 | ) -> Result, rendy_core::hal::device::OomOrDeviceLost> { 142 | self.assert_device_owner(device); 143 | 144 | match self.state { 145 | FenceState::Submitted(epoch) => { 146 | if unsafe { device.wait_for_fence(&self.raw, timeout_ns) }? { 147 | self.state = FenceState::Signaled; 148 | Ok(Some(epoch)) 149 | } else { 150 | Ok(None) 151 | } 152 | } 153 | _ => panic!("Must be submitted"), 154 | } 155 | } 156 | 157 | /// Check if fence has became signaled. 158 | /// Panics if not submitted. 159 | /// Returns submission epoch on success. 160 | pub fn check_signaled( 161 | &mut self, 162 | device: &Device, 163 | ) -> Result, rendy_core::hal::device::DeviceLost> { 164 | self.assert_device_owner(device); 165 | 166 | match self.state { 167 | FenceState::Submitted(epoch) => { 168 | if unsafe { device.get_fence_status(&self.raw) }? { 169 | self.state = FenceState::Signaled; 170 | Ok(Some(epoch)) 171 | } else { 172 | Ok(None) 173 | } 174 | } 175 | _ => panic!("Must be submitted"), 176 | } 177 | } 178 | 179 | /// Get raw fence reference. 180 | /// Use `mark_*` functions to reflect stage changes. 181 | pub fn raw(&self) -> &B::Fence { 182 | &self.raw 183 | } 184 | 185 | /// Get submission epoch. 186 | /// Panics if not submitted. 187 | pub fn epoch(&self) -> FenceEpoch { 188 | match self.state { 189 | FenceState::Submitted(epoch) => epoch, 190 | _ => panic!("Must be submitted"), 191 | } 192 | } 193 | 194 | /// Unwrap raw fence value. 195 | /// Panics if submitted. 196 | pub fn into_inner(self) -> B::Fence { 197 | match self.state { 198 | FenceState::Signaled | FenceState::Unsignaled => self.raw, 199 | _ => panic!("Submitted fence must be waited upon before destroying"), 200 | } 201 | } 202 | } 203 | --------------------------------------------------------------------------------