From 1076a8f2b58bf0d61ac6b09e821bf5744a498006 Mon Sep 17 00:00:00 2001 From: dataphract Date: Tue, 16 Nov 2021 03:37:48 +0000 Subject: [PATCH] Document the new pipelined renderer (#3094) This is a squash-and-rebase of @Ku95's documentation of the new renderer onto the latest `pipelined-rendering` branch. Original PR is #2884. Co-authored-by: dataphract Co-authored-by: Carter Anderson --- crates/bevy_ecs/src/system/function_system.rs | 9 +++ pipelined/bevy_gltf2/src/lib.rs | 7 +- pipelined/bevy_gltf2/src/loader.rs | 22 ++++-- pipelined/bevy_pbr2/src/bundle.rs | 5 +- pipelined/bevy_pbr2/src/lib.rs | 2 + pipelined/bevy_pbr2/src/light.rs | 8 +-- pipelined/bevy_pbr2/src/material.rs | 16 ++++- pipelined/bevy_render2/src/lib.rs | 27 +++++--- pipelined/bevy_render2/src/mesh/mesh/mod.rs | 53 +++++++++++--- pipelined/bevy_render2/src/mesh/mod.rs | 1 + pipelined/bevy_render2/src/mesh/shape/mod.rs | 6 +- .../bevy_render2/src/mesh/shape/uvsphere.rs | 2 +- pipelined/bevy_render2/src/render_asset.rs | 32 ++++++++- .../bevy_render2/src/render_component.rs | 27 +++++++- .../bevy_render2/src/render_graph/context.rs | 23 +++++++ .../bevy_render2/src/render_graph/edge.rs | 19 +++++ .../bevy_render2/src/render_graph/graph.rs | 69 +++++++++++++++++++ .../bevy_render2/src/render_graph/node.rs | 68 +++++++++++++++--- .../src/render_graph/node_slot.rs | 31 +++++++++ .../bevy_render2/src/render_phase/draw.rs | 54 +++++++++++++++ .../src/render_phase/draw_state.rs | 30 +++++++- .../bevy_render2/src/render_phase/mod.rs | 4 ++ .../src/render_resource/bind_group.rs | 8 +++ .../src/render_resource/pipeline.rs | 11 +++ .../src/render_resource/texture.rs | 27 ++++++++ pipelined/bevy_render2/src/renderer/mod.rs | 11 +++ .../src/renderer/render_device.rs | 29 ++++---- pipelined/bevy_render2/src/texture/image.rs | 41 +++++++++-- .../src/texture/image_texture_conversion.rs | 6 +- .../src/texture/image_texture_loader.rs | 2 +- pipelined/bevy_render2/src/texture/mod.rs | 1 + .../bevy_render2/src/texture/texture_cache.rs | 11 +++ pipelined/bevy_render2/src/view/window.rs | 2 +- 33 files changed, 592 insertions(+), 72 deletions(-) diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index 85109090a2..b3ac3af522 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -170,9 +170,18 @@ impl SystemState { } } +/// A trait for defining systems with a [`SystemParam`] associated type. +/// +/// This facilitates the creation of systems that are generic over some trait +/// and that use that trait's associated types as `SystemParam`s. pub trait RunSystem: Send + Sync + 'static { + /// The `SystemParam` type passed to the system when it runs. type Param: SystemParam; + + /// Runs the system. fn run(param: SystemParamItem); + + /// Creates a concrete instance of the system for the specified `World`. fn system(world: &mut World) -> ParamSystem { ParamSystem { run: Self::run, diff --git a/pipelined/bevy_gltf2/src/lib.rs b/pipelined/bevy_gltf2/src/lib.rs index 4290cc455a..39cd6a9d41 100644 --- a/pipelined/bevy_gltf2/src/lib.rs +++ b/pipelined/bevy_gltf2/src/lib.rs @@ -10,7 +10,7 @@ use bevy_reflect::TypeUuid; use bevy_render2::mesh::Mesh; use bevy_scene::Scene; -/// Adds support for GLTF file loading to Apps +/// Adds support for glTF file loading to the app. #[derive(Default)] pub struct GltfPlugin; @@ -24,6 +24,7 @@ impl Plugin for GltfPlugin { } } +/// Representation of a loaded glTF file. #[derive(Debug, TypeUuid)] #[uuid = "5c7d5f8a-f7b0-4e45-a09e-406c0372fea2"] pub struct Gltf { @@ -38,6 +39,8 @@ pub struct Gltf { pub default_scene: Option>, } +/// A glTF node with all of its child nodes, its [`GltfMesh`] and +/// [`Transform`](bevy_transform::prelude::Transform). #[derive(Debug, Clone, TypeUuid)] #[uuid = "dad74750-1fd6-460f-ac51-0a7937563865"] pub struct GltfNode { @@ -46,12 +49,14 @@ pub struct GltfNode { pub transform: bevy_transform::prelude::Transform, } +/// A glTF mesh, which may consists of multiple [`GtlfPrimitives`](GltfPrimitive). #[derive(Debug, Clone, TypeUuid)] #[uuid = "8ceaec9a-926a-4f29-8ee3-578a69f42315"] pub struct GltfMesh { pub primitives: Vec, } +/// Part of a [`GltfMesh`] that consists of a [`Mesh`] and an optional [`StandardMaterial`]. #[derive(Debug, Clone, TypeUuid)] #[uuid = "cbfca302-82fd-41cb-af77-cab6b3d50af1"] pub struct GltfPrimitive { diff --git a/pipelined/bevy_gltf2/src/loader.rs b/pipelined/bevy_gltf2/src/loader.rs index 9e15a7a4d9..560d221388 100644 --- a/pipelined/bevy_gltf2/src/loader.rs +++ b/pipelined/bevy_gltf2/src/loader.rs @@ -35,12 +35,12 @@ use wgpu::{AddressMode, FilterMode, PrimitiveTopology, SamplerDescriptor, Textur use crate::{Gltf, GltfNode}; -/// An error that occurs when loading a GLTF file +/// An error that occurs when loading a glTF file. #[derive(Error, Debug)] pub enum GltfError { #[error("unsupported primitive mode")] UnsupportedPrimitive { mode: Mode }, - #[error("invalid GLTF file: {0}")] + #[error("invalid glTF file: {0}")] Gltf(#[from] gltf::Error), #[error("binary blob is missing")] MissingBlob, @@ -56,7 +56,7 @@ pub enum GltfError { AssetIoError(#[from] AssetIoError), } -/// Loads meshes from GLTF files into Mesh assets +/// Loads glTF files with all of their data as their corresponding bevy representations. #[derive(Default)] pub struct GltfLoader; @@ -74,6 +74,7 @@ impl AssetLoader for GltfLoader { } } +/// Loads an entire glTF file. async fn load_gltf<'a, 'b>( bytes: &'a [u8], load_context: &'a mut LoadContext<'b>, @@ -265,7 +266,7 @@ async fn load_gltf<'a, 'b>( .into_iter() .filter_map(|res| { if let Err(err) = res.as_ref() { - warn!("Error loading GLTF texture: {}", err); + warn!("Error loading glTF texture: {}", err); } res.ok() }) @@ -320,6 +321,7 @@ async fn load_gltf<'a, 'b>( Ok(()) } +/// Loads a glTF texture as a bevy [`Image`] and returns it together with its label. async fn load_texture<'a>( gltf_texture: gltf::Texture<'a>, buffer_data: &[Vec], @@ -368,6 +370,7 @@ async fn load_texture<'a>( Ok((texture, texture_label(&gltf_texture))) } +/// Loads a glTF material as a bevy [`StandardMaterial`] and returns it. fn load_material(material: &Material, load_context: &mut LoadContext) -> Handle { let material_label = material_label(material); @@ -444,6 +447,7 @@ fn load_material(material: &Material, load_context: &mut LoadContext) -> Handle< ) } +/// Loads a glTF node. fn load_node( gltf_node: &gltf::Node, world_builder: &mut WorldChildBuilder, @@ -559,14 +563,17 @@ fn load_node( } } +/// Returns the label for the `mesh`. fn mesh_label(mesh: &gltf::Mesh) -> String { format!("Mesh{}", mesh.index()) } +/// Returns the label for the `mesh` and `primitive`. fn primitive_label(mesh: &gltf::Mesh, primitive: &Primitive) -> String { format!("Mesh{}/Primitive{}", mesh.index(), primitive.index()) } +/// Returns the label for the `material`. fn material_label(material: &gltf::Material) -> String { if let Some(index) = material.index() { format!("Material{}", index) @@ -575,18 +582,22 @@ fn material_label(material: &gltf::Material) -> String { } } +/// Returns the label for the `texture`. fn texture_label(texture: &gltf::Texture) -> String { format!("Texture{}", texture.index()) } +/// Returns the label for the `node`. fn node_label(node: &gltf::Node) -> String { format!("Node{}", node.index()) } +/// Returns the label for the `scene`. fn scene_label(scene: &gltf::Scene) -> String { format!("Scene{}", scene.index()) } +/// Extracts the texture sampler data from the glTF texture. fn texture_sampler<'a>(texture: &gltf::Texture) -> SamplerDescriptor<'a> { let gltf_sampler = texture.sampler(); @@ -631,6 +642,7 @@ fn texture_sampler<'a>(texture: &gltf::Texture) -> SamplerDescriptor<'a> { } } +/// Maps the texture address mode form glTF to wgpu. fn texture_address_mode(gltf_address_mode: &gltf::texture::WrappingMode) -> AddressMode { match gltf_address_mode { WrappingMode::ClampToEdge => AddressMode::ClampToEdge, @@ -639,6 +651,7 @@ fn texture_address_mode(gltf_address_mode: &gltf::texture::WrappingMode) -> Addr } } +/// Maps the primitive_topology form glTF to wgpu. fn get_primitive_topology(mode: Mode) -> Result { match mode { Mode::Points => Ok(PrimitiveTopology::PointList), @@ -658,6 +671,7 @@ fn alpha_mode(material: &Material) -> AlphaMode { } } +/// Loads the raw glTF buffer data for a specific glTF file. async fn load_buffers( gltf: &gltf::Gltf, load_context: &LoadContext<'_>, diff --git a/pipelined/bevy_pbr2/src/bundle.rs b/pipelined/bevy_pbr2/src/bundle.rs index 220e8291ee..8e93159ce0 100644 --- a/pipelined/bevy_pbr2/src/bundle.rs +++ b/pipelined/bevy_pbr2/src/bundle.rs @@ -8,6 +8,7 @@ use bevy_render2::{ }; use bevy_transform::components::{GlobalTransform, Transform}; +/// A component bundle for PBR entities with a [`Mesh`] and a [`StandardMaterial`]. #[derive(Bundle, Clone)] pub struct PbrBundle { pub mesh: Handle, @@ -56,7 +57,7 @@ impl CubemapVisibleEntities { } } -/// A component bundle for "point light" entities +/// A component bundle for [`PointLight`] entities. #[derive(Debug, Bundle, Default)] pub struct PointLightBundle { pub point_light: PointLight, @@ -66,7 +67,7 @@ pub struct PointLightBundle { pub global_transform: GlobalTransform, } -/// A component bundle for "directional light" entities +/// A component bundle for [`DirectionalLight`] entities. #[derive(Debug, Bundle, Default)] pub struct DirectionalLightBundle { pub directional_light: DirectionalLight, diff --git a/pipelined/bevy_pbr2/src/lib.rs b/pipelined/bevy_pbr2/src/lib.rs index b9d537aab2..0ef32aa3c1 100644 --- a/pipelined/bevy_pbr2/src/lib.rs +++ b/pipelined/bevy_pbr2/src/lib.rs @@ -27,6 +27,7 @@ use bevy_transform::TransformSystem; pub mod draw_3d_graph { pub mod node { + /// Label for the shadow pass node. pub const SHADOW_PASS: &str = "shadow_pass"; } } @@ -36,6 +37,7 @@ pub const PBR_SHADER_HANDLE: HandleUntyped = pub const SHADOW_SHADER_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 1836745567947005696); +/// Sets up the entire PBR infrastructure of bevy. #[derive(Default)] pub struct PbrPlugin; diff --git a/pipelined/bevy_pbr2/src/light.rs b/pipelined/bevy_pbr2/src/light.rs index f662781be0..65917d2aa5 100644 --- a/pipelined/bevy_pbr2/src/light.rs +++ b/pipelined/bevy_pbr2/src/light.rs @@ -145,11 +145,11 @@ impl Default for DirectionalLightShadowMap { } } -/// Ambient light. +/// An ambient light, which lights the entire scene equally. #[derive(Debug)] pub struct AmbientLight { pub color: Color, - /// A direct scale factor multiplied with `color` before being passed to the shader + /// A direct scale factor multiplied with `color` before being passed to the shader. pub brightness: f32, } @@ -162,9 +162,9 @@ impl Default for AmbientLight { } } -/// Add this component to make a `Mesh` not cast shadows +/// Add this component to make a [`Mesh`](bevy_render2::mesh::Mesh) not cast shadows. pub struct NotShadowCaster; -/// Add this component to make a `Mesh` not receive shadows +/// Add this component to make a [`Mesh`](bevy_render2::mesh::Mesh) not receive shadows. pub struct NotShadowReceiver; #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)] diff --git a/pipelined/bevy_pbr2/src/material.rs b/pipelined/bevy_pbr2/src/material.rs index 87a7622e30..3dacb60b21 100644 --- a/pipelined/bevy_pbr2/src/material.rs +++ b/pipelined/bevy_pbr2/src/material.rs @@ -15,7 +15,10 @@ use crevice::std140::{AsStd140, Std140}; use wgpu::{BindGroupDescriptor, BindGroupEntry, BindingResource}; /// A material with "standard" properties used in PBR lighting -/// Standard property values with pictures here https://google.github.io/filament/Material%20Properties.pdf +/// Standard property values with pictures here +/// . +/// +/// May be created directly from a [`Color`] or an [`Image`]. #[derive(Debug, Clone, TypeUuid)] #[uuid = "7494888b-c082-457b-aacf-517228cc0c22"] pub struct StandardMaterial { @@ -56,7 +59,7 @@ impl Default for StandardMaterial { emissive: Color::BLACK, emissive_texture: None, // This is the minimum the roughness is clamped to in shader code - // See https://google.github.io/filament/Filament.html#materialsystem/parameterization/ + // See // It's the minimum floating point value that won't be rounded down to 0 in the // calculations used. Although technically for 32-bit floats, 0.045 could be // used. @@ -66,7 +69,8 @@ impl Default for StandardMaterial { metallic: 0.01, metallic_roughness_texture: None, // Minimum real-world reflectance is 2%, most materials between 2-5% - // Expressed in a linear scale and equivalent to 4% reflectance see https://google.github.io/filament/Material%20Properties.pdf + // Expressed in a linear scale and equivalent to 4% reflectance see + // reflectance: 0.5, occlusion_texture: None, normal_map_texture: None, @@ -95,6 +99,7 @@ impl From> for StandardMaterial { } } +/// The GPU representation of the uniform data of a [`StandardMaterial`]. #[derive(Clone, Default, AsStd140)] pub struct StandardMaterialUniformData { /// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything @@ -117,6 +122,7 @@ pub struct StandardMaterialUniformData { pub alpha_cutoff: f32, } +/// This plugin adds the [`StandardMaterial`] asset to the app. pub struct StandardMaterialPlugin; impl Plugin for StandardMaterialPlugin { @@ -126,9 +132,13 @@ impl Plugin for StandardMaterialPlugin { } } +/// The GPU representation of a [`StandardMaterial`]. #[derive(Debug, Clone)] pub struct GpuStandardMaterial { + /// A buffer containing the [`StandardMaterialUniformData`] of the material. pub buffer: Buffer, + /// The bind group specifying how the [`StandardMaterialUniformData`] and + /// all the textures of the material are bound. pub bind_group: BindGroup, pub has_normal_map: bool, pub flags: StandardMaterialFlags, diff --git a/pipelined/bevy_render2/src/lib.rs b/pipelined/bevy_render2/src/lib.rs index 1e28acc6fe..59f6fc079a 100644 --- a/pipelined/bevy_render2/src/lib.rs +++ b/pipelined/bevy_render2/src/lib.rs @@ -29,28 +29,32 @@ use bevy_ecs::prelude::*; use std::ops::{Deref, DerefMut}; use wgpu::Backends; +/// Contains the default Bevy rendering backend based on wgpu. #[derive(Default)] pub struct RenderPlugin; -/// The names of the default App stages +/// The labels of the default App rendering stages. #[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)] pub enum RenderStage { - /// Extract data from "app world" and insert it into "render world". This step should be kept - /// as short as possible to increase the "pipelining potential" for running the next frame - /// while rendering the current frame. + /// Extract data from the "app world" and insert it into the "render world". + /// This step should be kept as short as possible to increase the "pipelining potential" for + /// running the next frame while rendering the current frame. Extract, - /// Prepare render resources from extracted data. + /// Prepare render resources from the extracted data for the GPU. Prepare, - /// Create Bind Groups that depend on Prepare data and queue up draw calls to run during the Render stage. + /// Create [`BindGroups`](crate::render_resource::BindGroup) that depend on + /// [`Prepare`](RenderStage::Prepare) data and queue up draw calls to run during the + /// [`Render`](RenderStage::Render) stage. Queue, // TODO: This could probably be moved in favor of a system ordering abstraction in Render or Queue - /// Sort RenderPhases here + /// Sort the [`RenderPhases`](crate::render_phase::RenderPhase) here. PhaseSort, - /// Actual rendering happens here. In most cases, only the render backend should insert resources here + /// Actual rendering happens here. + /// In most cases, only the render backend should insert resources here. Render, /// Cleanup render resources here. @@ -75,16 +79,17 @@ impl DerefMut for RenderWorld { } } -/// Label for the rendering sub-app +/// A Label for the rendering sub-app. #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)] pub struct RenderApp; /// A "scratch" world used to avoid allocating new worlds every frame when -// swapping out the Render World. +/// swapping out the [`RenderWorld`]. #[derive(Default)] struct ScratchRenderWorld(World); impl Plugin for RenderPlugin { + /// Initializes the renderer, sets up the [`RenderStage`](RenderStage) and creates the rendering sub-app. fn build(&self, app: &mut App) { let default_backend = if cfg!(not(target_arch = "wasm32")) { Backends::PRIMARY @@ -271,6 +276,8 @@ impl Plugin for RenderPlugin { } } +/// Executes the [`Extract`](RenderStage::Extract) stage of the renderer. +/// This updates the render world with the extracted ECS data of the current frame. fn extract(app_world: &mut World, render_app: &mut App) { let extract = render_app .schedule diff --git a/pipelined/bevy_render2/src/mesh/mesh/mod.rs b/pipelined/bevy_render2/src/mesh/mesh/mod.rs index 204af9445d..57614f378e 100644 --- a/pipelined/bevy_render2/src/mesh/mesh/mod.rs +++ b/pipelined/bevy_render2/src/mesh/mesh/mod.rs @@ -79,12 +79,13 @@ impl Mesh { } } + /// Returns the topology of the mesh. pub fn primitive_topology(&self) -> PrimitiveTopology { self.primitive_topology } /// Sets the data for a vertex attribute (position, normal etc.). The name will - /// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`] + /// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`]. pub fn set_attribute( &mut self, name: impl Into>, @@ -94,11 +95,12 @@ impl Mesh { self.attributes.insert(name.into(), values); } - /// Retrieve the data currently set behind a vertex attribute. + /// Retrieves the data currently set to the vertex attribute with the specified `name`. pub fn attribute(&self, name: impl Into>) -> Option<&VertexAttributeValues> { self.attributes.get(&name.into()) } + /// Retrieves the data currently set to the vertex attribute with the specified `name` mutably. pub fn attribute_mut( &mut self, name: impl Into>, @@ -106,21 +108,25 @@ impl Mesh { self.attributes.get_mut(&name.into()) } - /// Indices describe how triangles are constructed out of the vertex attributes. - /// They are only useful for the [`crate::pipeline::PrimitiveTopology`] variants that use - /// triangles + /// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the + /// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants + /// that use triangles. pub fn set_indices(&mut self, indices: Option) { self.indices = indices; } + /// Retrieves the vertex `indices` of the mesh. pub fn indices(&self) -> Option<&Indices> { self.indices.as_ref() } + /// Retrieves the vertex `indices` of the mesh mutably. pub fn indices_mut(&mut self) -> Option<&mut Indices> { self.indices.as_mut() } + /// Computes and returns the index data of the mesh as bytes. + /// This is used to transform the index data into a GPU friendly format. pub fn get_index_buffer_bytes(&self) -> Option<&[u8]> { self.indices.as_ref().map(|indices| match &indices { Indices::U16(indices) => cast_slice(&indices[..]), @@ -150,6 +156,10 @@ impl Mesh { // } // } + /// Counts all vertices of the mesh. + /// + /// # Panics + /// Panics if the attributes have different vertex counts. pub fn count_vertices(&self) -> usize { let mut vertex_count: Option = None; for (attribute_name, attribute_data) in self.attributes.iter() { @@ -164,6 +174,12 @@ impl Mesh { vertex_count.unwrap_or(0) } + /// Computes and returns the vertex data of the mesh as bytes. + /// Therefore the attributes are located in alphabetical order. + /// This is used to transform the vertex data into a GPU friendly format. + /// + /// # Panics + /// Panics if the attributes have different vertex counts. pub fn get_vertex_buffer_data(&self) -> Vec { let mut vertex_size = 0; for attribute_values in self.attributes.values() { @@ -197,6 +213,9 @@ impl Mesh { /// /// This can dramatically increase the vertex count, so make sure this is what you want. /// Does nothing if no [Indices] are set. + /// + /// # Panics + /// If the mesh has any other topology than [`PrimitiveTopology::TriangleList`]. pub fn duplicate_vertices(&mut self) { fn duplicate(values: &[T], indices: impl Iterator) -> Vec { indices.map(|i| values[i]).collect() @@ -248,7 +267,8 @@ impl Mesh { /// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a mesh. /// - /// Panics if [`Indices`] are set. + /// # Panics + /// Panics if [`Indices`] are set or [`Mesh::ATTRIBUTE_POSITION`] is not of type `float3`. /// Consider calling [Mesh::duplicate_vertices] or export your mesh with normal attributes. pub fn compute_flat_normals(&mut self) { if self.indices().is_some() { @@ -349,7 +369,8 @@ impl VertexFormatSize for wgpu::VertexFormat { } } -/// An array where each entry describes a property of a single vertex. +/// Contains an array where each entry describes a property of a single vertex. +/// Matches the [`VertexFormats`](VertexFormat). #[derive(Clone, Debug, EnumVariantMeta)] pub enum VertexAttributeValues { Float32(Vec), @@ -418,11 +439,12 @@ impl VertexAttributeValues { } } - /// Returns `true` if there are no vertices in this VertexAttributeValue + /// Returns `true` if there are no vertices in this VertexAttributeValue. pub fn is_empty(&self) -> bool { self.len() == 0 } + /// Returns the values as float triples if possible. fn as_float3(&self) -> Option<&[[f32; 3]]> { match self { VertexAttributeValues::Float32x3(values) => Some(values), @@ -502,7 +524,7 @@ impl From<&VertexAttributeValues> for VertexFormat { } } -/// An array of indices into the VertexAttributeValues for a mesh. +/// An array of indices into the [`VertexAttributeValues`] for a mesh. /// /// It describes the order in which the vertex attributes should be joined into faces. #[derive(Debug, Clone)] @@ -512,6 +534,7 @@ pub enum Indices { } impl Indices { + /// Returns an iterator over the indices. fn iter(&self) -> impl Iterator + '_ { match self { Indices::U16(vec) => IndicesIter::U16(vec.iter()), @@ -519,6 +542,7 @@ impl Indices { } } + /// Returns the number of indices. pub fn len(&self) -> usize { match self { Indices::U16(vec) => vec.len(), @@ -526,6 +550,7 @@ impl Indices { } } + /// Returns `true` if there are no indices. pub fn is_empty(&self) -> bool { match self { Indices::U16(vec) => vec.is_empty(), @@ -533,10 +558,13 @@ impl Indices { } } } + +/// An Iterator for the [`Indices`]. enum IndicesIter<'a> { U16(std::slice::Iter<'a, u16>), U32(std::slice::Iter<'a, u32>), } + impl Iterator for IndicesIter<'_> { type Item = usize; @@ -557,15 +585,20 @@ impl From<&Indices> for IndexFormat { } } +/// The GPU-representation of a [`Mesh`]. +/// Consists of a vertex data buffer and an optional index data buffer. #[derive(Debug, Clone)] pub struct GpuMesh { + /// Contains all attribute data for each vertex. pub vertex_buffer: Buffer, pub index_info: Option, pub has_tangents: bool, } +/// The index info of a [`GpuMesh`]. #[derive(Debug, Clone)] pub struct GpuIndexInfo { + /// Contains all index data of a mesh. pub buffer: Buffer, pub count: u32, pub index_format: IndexFormat, @@ -576,10 +609,12 @@ impl RenderAsset for Mesh { type PreparedAsset = GpuMesh; type Param = SRes; + /// Clones the mesh. fn extract_asset(&self) -> Self::ExtractedAsset { self.clone() } + /// Converts the extracted mesh a into [`GpuMesh`]. fn prepare_asset( mesh: Self::ExtractedAsset, render_device: &mut SystemParamItem, diff --git a/pipelined/bevy_render2/src/mesh/mod.rs b/pipelined/bevy_render2/src/mesh/mod.rs index e3d0665934..6740bd02ef 100644 --- a/pipelined/bevy_render2/src/mesh/mod.rs +++ b/pipelined/bevy_render2/src/mesh/mod.rs @@ -9,6 +9,7 @@ use crate::render_asset::RenderAssetPlugin; use bevy_app::{App, Plugin}; use bevy_asset::AddAsset; +/// Adds the [`Mesh`] as an asset and makes sure that they are extracted and prepared for the GPU. pub struct MeshPlugin; impl Plugin for MeshPlugin { diff --git a/pipelined/bevy_render2/src/mesh/shape/mod.rs b/pipelined/bevy_render2/src/mesh/shape/mod.rs index bae3330e4c..e2aa285d20 100644 --- a/pipelined/bevy_render2/src/mesh/shape/mod.rs +++ b/pipelined/bevy_render2/src/mesh/shape/mod.rs @@ -24,6 +24,7 @@ impl From for Mesh { } } +/// An axis-aligned box defined by its minimum and maximum point. #[derive(Debug, Copy, Clone)] pub struct Box { pub min_x: f32, @@ -37,6 +38,7 @@ pub struct Box { } impl Box { + /// Creates a new box centered at the origin with the supplied side lengths. pub fn new(x_length: f32, y_length: f32, z_length: f32) -> Box { Box { max_x: x_length / 2.0, @@ -118,7 +120,7 @@ impl From for Mesh { } } -/// A rectangle on the XY plane. +/// A rectangle on the XY plane centered at the origin. #[derive(Debug, Copy, Clone)] pub struct Quad { /// Full width and height of the rectangle. @@ -220,7 +222,7 @@ impl From for Mesh { } } -/// A square on the XZ plane. +/// A square on the XZ plane centered at the origin. #[derive(Debug, Copy, Clone)] pub struct Plane { /// The total side length of the square. diff --git a/pipelined/bevy_render2/src/mesh/shape/uvsphere.rs b/pipelined/bevy_render2/src/mesh/shape/uvsphere.rs index 6c21e1fa38..571c3123d9 100644 --- a/pipelined/bevy_render2/src/mesh/shape/uvsphere.rs +++ b/pipelined/bevy_render2/src/mesh/shape/uvsphere.rs @@ -3,7 +3,7 @@ use wgpu::PrimitiveTopology; use crate::mesh::{Indices, Mesh}; use std::f32::consts::PI; -/// A sphere made of sectors and stacks +/// A sphere made of sectors and stacks. #[allow(clippy::upper_case_acronyms)] #[derive(Debug, Clone, Copy)] pub struct UVSphere { diff --git a/pipelined/bevy_render2/src/render_asset.rs b/pipelined/bevy_render2/src/render_asset.rs index 93e4297215..6b259efd71 100644 --- a/pipelined/bevy_render2/src/render_asset.rs +++ b/pipelined/bevy_render2/src/render_asset.rs @@ -12,18 +12,38 @@ pub enum PrepareAssetError { RetryNextUpdate(E), } +/// Describes how an asset gets extracted and prepared for rendering. +/// +/// In the [`RenderStage::Extract`](crate::RenderStage::Extract) step the asset is transferred +/// from the "app world" into the "render world". +/// Therefore it is converted into a [`RenderAsset::ExtractedAsset`], which may be the same type +/// as the render asset itself. +/// +/// After that in the [`RenderStage::Prepare`](crate::RenderStage::Prepare) step the extracted asset +/// is transformed into its GPU-representation of type [`RenderAsset::PreparedAsset`]. pub trait RenderAsset: Asset { + /// The representation of the the asset in the "render world". type ExtractedAsset: Send + Sync + 'static; + /// The GPU-representation of the the asset. type PreparedAsset: Send + Sync + 'static; + /// Specifies all ECS data required by [`RenderAsset::prepare_asset`]. + /// For convenience use the [`lifetimeless`](bevy_ecs::system::lifetimeless) SystemParams. type Param: SystemParam; + /// Converts the asset into a [`RenderAsset::ExtractedAsset`]. fn extract_asset(&self) -> Self::ExtractedAsset; + /// Prepares the `extracted asset` for the GPU by transforming it into + /// a [`RenderAsset::PreparedAsset`]. Therefore ECS data may be accessed via the `param`. fn prepare_asset( extracted_asset: Self::ExtractedAsset, param: &mut SystemParamItem, ) -> Result>; } -/// Extracts assets into gpu-usable data +/// This plugin extracts the changed assets from the "app world" into the "render world" +/// and prepares them for the GPU. They can then be accessed from the [`RenderAssets`] resource. +/// +/// Therefore it sets up the [`RenderStage::Extract`](crate::RenderStage::Extract) and +/// [`RenderStage::Prepare`](crate::RenderStage::Prepare) steps for the specified [`RenderAsset`]. pub struct RenderAssetPlugin(PhantomData A>); impl Default for RenderAssetPlugin { @@ -45,6 +65,7 @@ impl Plugin for RenderAssetPlugin { } } +/// Temporarily stores the extracted and removed assets of the current frame. pub struct ExtractedAssets { extracted: Vec<(Handle, A::ExtractedAsset)>, removed: Vec>, @@ -59,8 +80,12 @@ impl Default for ExtractedAssets { } } +/// Stores all GPU representations ([`RenderAsset::PreparedAssets`](RenderAsset::PreparedAsset)) +/// of [`RenderAssets`](RenderAsset) as long as they exist. pub type RenderAssets = HashMap, ::PreparedAsset>; +/// This system extracts all crated or modified assets of the corresponding [`RenderAsset`] type +/// into the "render world". fn extract_render_asset( mut commands: Commands, mut events: EventReader>, @@ -97,6 +122,7 @@ fn extract_render_asset( }) } +/// Specifies all ECS data required by [`PrepareAssetSystem`]. pub type RenderAssetParams = ( SResMut>, SResMut>, @@ -105,6 +131,7 @@ pub type RenderAssetParams = ( ); // TODO: consider storing inside system? +/// All assets that should be prepared next frame. pub struct PrepareNextFrameAssets { assets: Vec<(Handle, A::ExtractedAsset)>, } @@ -117,10 +144,13 @@ impl Default for PrepareNextFrameAssets { } } +/// This system prepares all assets of the corresponding [`RenderAsset`] type +/// which where extracted this frame for the GPU. pub struct PrepareAssetSystem(PhantomData); impl RunSystem for PrepareAssetSystem { type Param = RenderAssetParams; + fn run( (mut extracted_assets, mut render_assets, mut prepare_next_frame, mut param): SystemParamItem, ) { diff --git a/pipelined/bevy_render2/src/render_component.rs b/pipelined/bevy_render2/src/render_component.rs index f123db86c3..6fd6141fb8 100644 --- a/pipelined/bevy_render2/src/render_component.rs +++ b/pipelined/bevy_render2/src/render_component.rs @@ -17,6 +17,7 @@ use bevy_ecs::{ use crevice::std140::AsStd140; use std::{marker::PhantomData, ops::Deref}; +/// Stores the index of a uniform inside of [`ComponentUniforms`]. pub struct DynamicUniformIndex { index: u32, marker: PhantomData, @@ -29,13 +30,28 @@ impl DynamicUniformIndex { } } +/// Describes how a component gets extracted for rendering. +/// +/// Therefore the component is transferred from the "app world" into the "render world" +/// in the [`RenderStage::Extract`](crate::RenderStage::Extract) step. pub trait ExtractComponent: Component { + /// ECS [`WorldQuery`] to fetch the components to extract. type Query: WorldQuery; + /// Filters the entities with additional constraints. type Filter: WorldQuery; + /// Defines how the component is transferred into the "render world". fn extract_component(item: QueryItem) -> Self; } -/// Extracts assets into gpu-usable data +/// This plugin prepares the components of the corresponding type for the GPU +/// by transforming them into uniforms. +/// +/// They can then be accessed from the [`ComponentUniforms`] resource. +/// For referencing the newly created uniforms a [`DynamicUniformIndex`] is inserted +/// for every processed entity. +/// +/// Therefore it sets up the [`RenderStage::Prepare`](crate::RenderStage::Prepare) step +/// for the specified [`ExtractComponent`]. pub struct UniformComponentPlugin(PhantomData C>); impl Default for UniformComponentPlugin { @@ -55,6 +71,7 @@ impl Plugin for UniformComponentPlugin { } } +/// Stores all uniforms of the component type. pub struct ComponentUniforms { uniforms: DynamicUniformVec, } @@ -83,6 +100,8 @@ impl Default for ComponentUniforms { } } +/// This system prepares all components of the corresponding component type. +/// They are transformed into uniforms and stored in the [`ComponentUniforms`] resource. fn prepare_uniform_components( mut commands: Commands, render_device: Res, @@ -107,6 +126,10 @@ fn prepare_uniform_components( .write_buffer(&render_device, &render_queue); } +/// This plugin extracts the components into the "render world". +/// +/// Therefore it sets up the [`RenderStage::Extract`](crate::RenderStage::Extract) step +/// for the specified [`ExtractComponent`]. pub struct ExtractComponentPlugin(PhantomData (C, F)>); impl Default for ExtractComponentPlugin { @@ -137,6 +160,7 @@ impl ExtractComponent for Handle { } } +/// This system extracts all components of the corresponding [`ExtractComponent`] type. pub struct ExtractComponentSystem(PhantomData); impl RunSystem for ExtractComponentSystem @@ -146,6 +170,7 @@ where { type Param = ( SCommands, + // the previous amount of extracted components Local<'static, usize>, SQuery<(Entity, C::Query), C::Filter>, ); diff --git a/pipelined/bevy_render2/src/render_graph/context.rs b/pipelined/bevy_render2/src/render_graph/context.rs index 26f784b8ea..ec300ac25e 100644 --- a/pipelined/bevy_render2/src/render_graph/context.rs +++ b/pipelined/bevy_render2/src/render_graph/context.rs @@ -6,11 +6,21 @@ use bevy_ecs::entity::Entity; use std::borrow::Cow; use thiserror::Error; +/// A command that signals the graph runner to run the sub graph corresponding to the `name` +/// with the specified `inputs` next. pub struct RunSubGraph { pub name: Cow<'static, str>, pub inputs: Vec, } +/// The context with all graph information required to run a [`Node`](super::Node). +/// This context is created for each node by the `RenderGraphRunner`. +/// +/// The slot input can be read from here and the outputs must be written back to the context for +/// passing them onto the next node. +/// +/// Sub graphs can be queued for running by adding a [`RunSubGraph`] command to the context. +/// After the node has finished running the graph runner is responsible for executing the sub graphs. pub struct RenderGraphContext<'a> { graph: &'a RenderGraph, node: &'a NodeState, @@ -20,6 +30,7 @@ pub struct RenderGraphContext<'a> { } impl<'a> RenderGraphContext<'a> { + /// Creates a new render graph context for the `node`. pub fn new( graph: &'a RenderGraph, node: &'a NodeState, @@ -35,19 +46,23 @@ impl<'a> RenderGraphContext<'a> { } } + /// Returns the input slot values for the node. #[inline] pub fn inputs(&self) -> &[SlotValue] { self.inputs } + /// Returns the [`SlotInfos`] of the inputs. pub fn input_info(&self) -> &SlotInfos { &self.node.input_slots } + /// Returns the [`SlotInfos`] of the outputs. pub fn output_info(&self) -> &SlotInfos { &self.node.output_slots } + /// Retrieves the input slot value referenced by the `label`. pub fn get_input(&self, label: impl Into) -> Result<&SlotValue, InputSlotError> { let label = label.into(); let index = self @@ -58,6 +73,7 @@ impl<'a> RenderGraphContext<'a> { } // TODO: should this return an Arc or a reference? + /// Retrieves the input slot value referenced by the `label` as a [`TextureView`]. pub fn get_input_texture( &self, label: impl Into, @@ -73,6 +89,7 @@ impl<'a> RenderGraphContext<'a> { } } + /// Retrieves the input slot value referenced by the `label` as a [`Sampler`]. pub fn get_input_sampler( &self, label: impl Into, @@ -88,6 +105,7 @@ impl<'a> RenderGraphContext<'a> { } } + /// Retrieves the input slot value referenced by the `label` as a [`Buffer`]. pub fn get_input_buffer(&self, label: impl Into) -> Result<&Buffer, InputSlotError> { let label = label.into(); match self.get_input(label.clone())? { @@ -100,6 +118,7 @@ impl<'a> RenderGraphContext<'a> { } } + /// Retrieves the input slot value referenced by the `label` as an [`Entity`]. pub fn get_input_entity(&self, label: impl Into) -> Result { let label = label.into(); match self.get_input(label.clone())? { @@ -112,6 +131,7 @@ impl<'a> RenderGraphContext<'a> { } } + /// Sets the output slot value referenced by the `label`. pub fn set_output( &mut self, label: impl Into, @@ -138,6 +158,7 @@ impl<'a> RenderGraphContext<'a> { Ok(()) } + /// Queues up a sub graph for execution after the node has finished running. pub fn run_sub_graph( &mut self, name: impl Into>, @@ -177,6 +198,8 @@ impl<'a> RenderGraphContext<'a> { Ok(()) } + /// Finishes the context for this [`Node`](super::Node) by + /// returning the sub graphs to run next. pub fn finish(self) -> Vec { self.run_sub_graphs } diff --git a/pipelined/bevy_render2/src/render_graph/edge.rs b/pipelined/bevy_render2/src/render_graph/edge.rs index e736797598..fce406f53d 100644 --- a/pipelined/bevy_render2/src/render_graph/edge.rs +++ b/pipelined/bevy_render2/src/render_graph/edge.rs @@ -1,13 +1,30 @@ use super::NodeId; +/// An edge, which connects two [`Nodes`](super::Node) in +/// a [`RenderGraph`](crate::render_graph::RenderGraph). +/// +/// They are used to describe the ordering (which node has to run first) +/// and may be of two kinds: [`NodeEdge`](Self::NodeEdge) and [`SlotEdge`](Self::SlotEdge). +/// +/// Edges are added via the render_graph::add_node_edge(output_node, input_node) and the +/// render_graph::add_slot_edge(output_node, output_slot, input_node, input_slot) methode. +/// +/// The former simply states that the `output_node` has to be run before the `input_node`, +/// while the later connects an output slot of the `output_node` +/// with an input slot of the `input_node` to pass additional data along. +/// For more information see [`SlotType`](super::SlotType). #[derive(Clone, Debug, Eq, PartialEq)] pub enum Edge { + /// An edge describing to ordering of both nodes (`output_node` before `input_node`) + /// and connecting the output slot at the `output_index` of the output_node + /// with the slot at the `input_index` of the `input_node`. SlotEdge { input_node: NodeId, input_index: usize, output_node: NodeId, output_index: usize, }, + /// An edge describing to ordering of both nodes (`output_node` before `input_node`). NodeEdge { input_node: NodeId, output_node: NodeId, @@ -15,6 +32,7 @@ pub enum Edge { } impl Edge { + /// Returns the id of the 'input_node'. pub fn get_input_node(&self) -> NodeId { match self { Edge::SlotEdge { input_node, .. } => *input_node, @@ -22,6 +40,7 @@ impl Edge { } } + /// Returns the id of the 'output_node'. pub fn get_output_node(&self) -> NodeId { match self { Edge::SlotEdge { output_node, .. } => *output_node, diff --git a/pipelined/bevy_render2/src/render_graph/graph.rs b/pipelined/bevy_render2/src/render_graph/graph.rs index 8a68c54ab2..53e8bacb26 100644 --- a/pipelined/bevy_render2/src/render_graph/graph.rs +++ b/pipelined/bevy_render2/src/render_graph/graph.rs @@ -9,6 +9,43 @@ use bevy_ecs::prelude::World; use bevy_utils::HashMap; use std::{borrow::Cow, fmt::Debug}; +/// The render graph configures the modular, parallel and re-usable render logic. +/// It is a retained and stateless (nodes itself my have their internal state) structure, +/// which can not be modified while it is executed by the graph runner. +/// +/// The `RenderGraphRunner` is responsible for executing the entire graph each frame. +/// +/// It consists of three main components: [`Nodes`](Node), [`Edges`](Edge) +/// and [`Slots`](super::SlotType). +/// +/// Nodes are responsible for generating draw calls and operating on input and output slots. +/// Edges specify the order of execution for nodes and connect input and output slots together. +/// Slots describe the render resources created or used by the nodes. +/// +/// Additionally a render graph can contain multiple sub graphs, which are run by the +/// corresponding nodes. Every render graph can have it’s own optional input node. +/// +/// ## Example +/// Here is a simple render graph example with two nodes connected by a node edge. +/// ``` +/// # use bevy_app::prelude::*; +/// # use bevy_ecs::prelude::World; +/// # use bevy_render2::render_graph::{RenderGraph, Node, RenderGraphContext, NodeRunError}; +/// # use bevy_render2::renderer::RenderContext; +/// # +/// # struct MyNode; +/// # +/// # impl Node for MyNode { +/// # fn run(&self, graph: &mut RenderGraphContext, render_context: &mut RenderContext, world: &World) -> Result<(), NodeRunError> { +/// # unimplemented!() +/// # } +/// # } +/// # +/// let mut graph = RenderGraph::default(); +/// graph.add_node("input_node", MyNode); +/// graph.add_node("output_node", MyNode); +/// graph.add_node_edge("output_node", "input_node").unwrap(); +/// ``` #[derive(Default)] pub struct RenderGraph { nodes: HashMap, @@ -18,8 +55,10 @@ pub struct RenderGraph { } impl RenderGraph { + /// The name of the [`GraphInputNode`] of this graph. Used to connect other nodes to it. pub const INPUT_NODE_NAME: &'static str = "GraphInputNode"; + /// Updates all nodes and sub graphs of the render graph. Should be called before executing it. pub fn update(&mut self, world: &mut World) { for node in self.nodes.values_mut() { node.node.update(world); @@ -30,6 +69,7 @@ impl RenderGraph { } } + /// Creates an [`GraphInputNode`] with the specified slots if not already present. pub fn set_input(&mut self, inputs: Vec) -> NodeId { if self.input_node.is_some() { panic!("Graph already has an input node"); @@ -40,11 +80,14 @@ impl RenderGraph { id } + /// Returns the [`NodeState`] of the input node of this graph.. #[inline] pub fn input_node(&self) -> Option<&NodeState> { self.input_node.and_then(|id| self.get_node_state(id).ok()) } + /// Adds the `node` with the `name` to the graph. + /// If the name is already present replaces it instead. pub fn add_node(&mut self, name: impl Into>, node: T) -> NodeId where T: Node, @@ -58,6 +101,7 @@ impl RenderGraph { id } + /// Retrieves the [`NodeState`] referenced by the `label`. pub fn get_node_state( &self, label: impl Into, @@ -69,6 +113,7 @@ impl RenderGraph { .ok_or(RenderGraphError::InvalidNode(label)) } + /// Retrieves the [`NodeState`] referenced by the `label` mutably. pub fn get_node_state_mut( &mut self, label: impl Into, @@ -80,6 +125,7 @@ impl RenderGraph { .ok_or(RenderGraphError::InvalidNode(label)) } + /// Retrieves the [`NodeId`] referenced by the `label`. pub fn get_node_id(&self, label: impl Into) -> Result { let label = label.into(); match label { @@ -92,6 +138,7 @@ impl RenderGraph { } } + /// Retrieves the [`Node`] referenced by the `label`. pub fn get_node(&self, label: impl Into) -> Result<&T, RenderGraphError> where T: Node, @@ -99,6 +146,7 @@ impl RenderGraph { self.get_node_state(label).and_then(|n| n.node()) } + /// Retrieves the [`Node`] referenced by the `label` mutably. pub fn get_node_mut( &mut self, label: impl Into, @@ -109,6 +157,8 @@ impl RenderGraph { self.get_node_state_mut(label).and_then(|n| n.node_mut()) } + /// Adds the [`Edge::SlotEdge`] to the graph. This guarantees that the `output_node` + /// is run before the `input_node` and also connects the `output_slot` to the `input_slot`. pub fn add_slot_edge( &mut self, output_node: impl Into, @@ -151,6 +201,8 @@ impl RenderGraph { Ok(()) } + /// Adds the [`Edge::NodeEdge`] to the graph. This guarantees that the `output_node` + /// is run before the `input_node`. pub fn add_node_edge( &mut self, output_node: impl Into, @@ -176,6 +228,8 @@ impl RenderGraph { Ok(()) } + /// Verifies that the edge is not already existing and + /// checks that slot edges are connected correctly. pub fn validate_edge(&mut self, edge: &Edge) -> Result<(), RenderGraphError> { if self.has_edge(edge) { return Err(RenderGraphError::EdgeAlreadyExists(edge.clone())); @@ -237,6 +291,7 @@ impl RenderGraph { Ok(()) } + /// Checks whether the `edge` already exists in the graph. pub fn has_edge(&self, edge: &Edge) -> bool { let output_node_state = self.get_node_state(edge.get_output_node()); let input_node_state = self.get_node_state(edge.get_input_node()); @@ -253,26 +308,32 @@ impl RenderGraph { false } + /// Returns an iterator over the [`NodeStates`](NodeState). pub fn iter_nodes(&self) -> impl Iterator { self.nodes.values() } + /// Returns an iterator over the [`NodeStates`](NodeState), that allows modifying each value. pub fn iter_nodes_mut(&mut self) -> impl Iterator { self.nodes.values_mut() } + /// Returns an iterator over the sub graphs. pub fn iter_sub_graphs(&self) -> impl Iterator { self.sub_graphs .iter() .map(|(name, graph)| (name.as_ref(), graph)) } + /// Returns an iterator over the sub graphs, that allows modifying each value. pub fn iter_sub_graphs_mut(&mut self) -> impl Iterator { self.sub_graphs .iter_mut() .map(|(name, graph)| (name.as_ref(), graph)) } + /// Returns an iterator over a tuple of the input edges and the corresponding output nodes + /// for the node referenced by the label. pub fn iter_node_inputs( &self, label: impl Into, @@ -288,6 +349,8 @@ impl RenderGraph { })) } + /// Returns an iterator over a tuple of the ouput edges and the corresponding input nodes + /// for the node referenced by the label. pub fn iter_node_outputs( &self, label: impl Into, @@ -301,14 +364,18 @@ impl RenderGraph { .map(move |(edge, input_node_id)| (edge, self.get_node_state(input_node_id).unwrap()))) } + /// Adds the `sub_graph` with the `name` to the graph. + /// If the name is already present replaces it instead. pub fn add_sub_graph(&mut self, name: impl Into>, sub_graph: RenderGraph) { self.sub_graphs.insert(name.into(), sub_graph); } + /// Retrieves the sub graph corresponding to the `name`. pub fn get_sub_graph(&self, name: impl AsRef) -> Option<&RenderGraph> { self.sub_graphs.get(name.as_ref()) } + /// Retrieves the sub graph corresponding to the `name` mutably. pub fn get_sub_graph_mut(&mut self, name: impl AsRef) -> Option<&mut RenderGraph> { self.sub_graphs.get_mut(name.as_ref()) } @@ -326,6 +393,8 @@ impl Debug for RenderGraph { } } +/// A [`Node`] which acts as an entry point for a [`RenderGraph`] with custom inputs. +/// It has the same input and output slots and simply copies them over when run. pub struct GraphInputNode { inputs: Vec, } diff --git a/pipelined/bevy_render2/src/render_graph/node.rs b/pipelined/bevy_render2/src/render_graph/node.rs index 5aefa90b7d..0562cef87b 100644 --- a/pipelined/bevy_render2/src/render_graph/node.rs +++ b/pipelined/bevy_render2/src/render_graph/node.rs @@ -11,6 +11,10 @@ use downcast_rs::{impl_downcast, Downcast}; use std::{borrow::Cow, fmt::Debug}; use thiserror::Error; +/// A [`Node`] identifier. +/// It automatically generates its own random uuid. +/// +/// This id is used to reference the node internally (edges, etc). #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct NodeId(Uuid); @@ -25,19 +29,37 @@ impl NodeId { } } +/// A render node that can be added to a [`RenderGraph`](super::RenderGraph). +/// +/// Nodes are the fundamental part of the graph and used to extend its functionality, by +/// generating draw calls and/or running subgraphs. +/// They are added via the render_graph::add_node(my_node) methode. +/// +/// To determine their position in the graph and ensure that all required dependencies (inputs) +/// are already executed, [`Edges`](Edge) are used. +/// +/// A node can produce outputs used as dependencies by other nodes. +/// Those inputs and outputs are called slots and are the default way of passing render data +/// inside the graph. For more information see [`SlotType`](super::SlotType). pub trait Node: Downcast + Send + Sync + 'static { + /// Specifies the required input slots for this node. + /// They will then be available during the run method inside the [`RenderContext`]. fn input(&self) -> Vec { Vec::new() } + /// Specifies the produced output slots for this node. + /// They can then be passed one inside [`RenderContext`] during the run method. fn output(&self) -> Vec { Vec::new() } - /// Update internal node state using the current render [`World`]. + /// Updates internal node state using the current render [`World`] prior to the run method. fn update(&mut self, _world: &mut World) {} - /// Run the graph node logic + /// Runs the graph node logic, issues draw calls, updates the output slots and + /// optionally queues up subgraphs for execution. The graph data, input and output values are + /// passed via the [`RenderGraphContext`]. fn run( &self, graph: &mut RenderGraphContext, @@ -58,6 +80,7 @@ pub enum NodeRunError { RunSubGraphError(#[from] RunSubGraphError), } +/// A collection of input and output [`Edges`](Edge) for a [`Node`]. #[derive(Debug)] pub struct Edges { pub id: NodeId, @@ -66,6 +89,7 @@ pub struct Edges { } impl Edges { + /// Adds an edge to the `input_edges` if it does not already exist. pub(crate) fn add_input_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> { if self.has_input_edge(&edge) { return Err(RenderGraphError::EdgeAlreadyExists(edge)); @@ -74,6 +98,7 @@ impl Edges { Ok(()) } + /// Adds an edge to the `output_edges` if it does not already exist. pub(crate) fn add_output_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> { if self.has_output_edge(&edge) { return Err(RenderGraphError::EdgeAlreadyExists(edge)); @@ -82,14 +107,18 @@ impl Edges { Ok(()) } + /// Checks whether the input edge already exists. pub fn has_input_edge(&self, edge: &Edge) -> bool { self.input_edges.contains(edge) } + /// Checks whether the output edge already exists. pub fn has_output_edge(&self, edge: &Edge) -> bool { self.output_edges.contains(edge) } + /// Searches the `input_edges` for a [`Edge::SlotEdge`], + /// which `input_index` matches the `index`; pub fn get_input_slot_edge(&self, index: usize) -> Result<&Edge, RenderGraphError> { self.input_edges .iter() @@ -106,6 +135,8 @@ impl Edges { }) } + /// Searches the `output_edges` for a [`Edge::SlotEdge`], + /// which `output_index` matches the `index`; pub fn get_output_slot_edge(&self, index: usize) -> Result<&Edge, RenderGraphError> { self.output_edges .iter() @@ -123,9 +154,14 @@ impl Edges { } } +/// The internal representation of a [`Node`], with all data required +/// by the [`RenderGraph`](super::RenderGraph). +/// +/// The `input_slots` and `output_slots` are provided by the `node`. pub struct NodeState { pub id: NodeId, pub name: Option>, + /// The name of the type that implements [`Node`]. pub type_name: &'static str, pub node: Box, pub input_slots: SlotInfos, @@ -140,6 +176,8 @@ impl Debug for NodeState { } impl NodeState { + /// Creates an [`NodeState`] without edges, but the `input_slots` and `output_slots` + /// are provided by the `node`. pub fn new(id: NodeId, node: T) -> Self where T: Node, @@ -159,6 +197,7 @@ impl NodeState { } } + /// Retrieves the [`Node`]. pub fn node(&self) -> Result<&T, RenderGraphError> where T: Node, @@ -168,6 +207,7 @@ impl NodeState { .ok_or(RenderGraphError::WrongNodeType) } + /// Retrieves the [`Node`] mutably. pub fn node_mut(&mut self) -> Result<&mut T, RenderGraphError> where T: Node, @@ -177,14 +217,7 @@ impl NodeState { .ok_or(RenderGraphError::WrongNodeType) } - pub fn validate_output_slots(&self) -> Result<(), RenderGraphError> { - for i in 0..self.output_slots.len() { - self.edges.get_output_slot_edge(i)?; - } - - Ok(()) - } - + /// Validates that each input slot corresponds to an input edge. pub fn validate_input_slots(&self) -> Result<(), RenderGraphError> { for i in 0..self.input_slots.len() { self.edges.get_input_slot_edge(i)?; @@ -192,8 +225,19 @@ impl NodeState { Ok(()) } + + /// Validates that each output slot corresponds to an output edge. + pub fn validate_output_slots(&self) -> Result<(), RenderGraphError> { + for i in 0..self.output_slots.len() { + self.edges.get_output_slot_edge(i)?; + } + + Ok(()) + } } +/// A [`NodeLabel`] is used to reference a [`NodeState`] by either its name or [`NodeId`] +/// inside the [`RenderGraph`](super::RenderGraph). #[derive(Debug, Clone, Eq, PartialEq)] pub enum NodeLabel { Id(NodeId), @@ -223,6 +267,10 @@ impl From for NodeLabel { NodeLabel::Id(value) } } + +/// A [`Node`] without any inputs, outputs and subgraphs, which does nothing when run. +/// Used (as a label) to bundle multiple dependencies into one inside +/// the [`RenderGraph`](super::RenderGraph). pub struct EmptyNode; impl Node for EmptyNode { diff --git a/pipelined/bevy_render2/src/render_graph/node_slot.rs b/pipelined/bevy_render2/src/render_graph/node_slot.rs index df4122863f..0789d62df0 100644 --- a/pipelined/bevy_render2/src/render_graph/node_slot.rs +++ b/pipelined/bevy_render2/src/render_graph/node_slot.rs @@ -3,15 +3,27 @@ use std::borrow::Cow; use crate::render_resource::{Buffer, Sampler, TextureView}; +/// A value passed between render [`Nodes`](super::Node). +/// Corresponds to the [SlotType] specified in the [`RenderGraph`](super::RenderGraph). +/// +/// Slots can have four different types of values: +/// [`Buffer`], [`TextureView`], [`Sampler`] and [`Entity`]. +/// +/// These values do not contain the actual render data, but only the ids to retrieve them. #[derive(Debug, Clone)] pub enum SlotValue { + /// A GPU-accessible [`Buffer`]. Buffer(Buffer), + /// A [`TextureView`] describes a texture used in a pipeline. TextureView(TextureView), + /// A texture [`Sampler`] defines how a pipeline will sample from a [`TextureView`]. Sampler(Sampler), + /// An entity from the ECS. Entity(Entity), } impl SlotValue { + /// Returns the [`SlotType`] of this value. pub fn slot_type(&self) -> SlotType { match self { SlotValue::Buffer(_) => SlotType::Buffer, @@ -46,14 +58,24 @@ impl From for SlotValue { } } +/// Describes the render resources created (output) or used (input) by +/// the render [`Nodes`](super::Node). +/// +/// This should not be confused with [`SlotValue`], which actually contains the passed data. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum SlotType { + /// A GPU-accessible [`Buffer`]. Buffer, + /// A [`TextureView`] describes a texture used in a pipeline. TextureView, + /// A texture [`Sampler`] defines how a pipeline will sample from a [`TextureView`]. Sampler, + /// An entity from the ECS. Entity, } +/// A [`SlotLabel`] is used to reference a slot by either its name or index +/// inside the [`RenderGraph`](super::RenderGraph). #[derive(Debug, Clone, Eq, PartialEq)] pub enum SlotLabel { Index(usize), @@ -90,6 +112,7 @@ impl From for SlotLabel { } } +/// The internal representation of a slot, which specifies its [`SlotType`] and name. #[derive(Clone, Debug)] pub struct SlotInfo { pub name: Cow<'static, str>, @@ -105,6 +128,8 @@ impl SlotInfo { } } +/// A collection of input or output [`SlotInfos`](SlotInfo) for +/// a [`NodeState`](super::NodeState). #[derive(Default, Debug)] pub struct SlotInfos { slots: Vec, @@ -119,28 +144,33 @@ impl> From for SlotInfos { } impl SlotInfos { + /// Returns the count of slots. #[inline] pub fn len(&self) -> usize { self.slots.len() } + /// Returns true if there are no slots. #[inline] pub fn is_empty(&self) -> bool { self.slots.is_empty() } + /// Retrieves the [`SlotInfo`] for the provided label. pub fn get_slot(&self, label: impl Into) -> Option<&SlotInfo> { let label = label.into(); let index = self.get_slot_index(&label)?; self.slots.get(index) } + /// Retrieves the [`SlotInfo`] for the provided label mutably. pub fn get_slot_mut(&mut self, label: impl Into) -> Option<&mut SlotInfo> { let label = label.into(); let index = self.get_slot_index(&label)?; self.slots.get_mut(index) } + /// Retrieves the index (inside input or output slots) of the slot for the provided label. pub fn get_slot_index(&self, label: impl Into) -> Option { let label = label.into(); match label { @@ -154,6 +184,7 @@ impl SlotInfos { } } + /// Returns an iterator over the slot infos. pub fn iter(&self) -> impl Iterator { self.slots.iter() } diff --git a/pipelined/bevy_render2/src/render_phase/draw.rs b/pipelined/bevy_render2/src/render_phase/draw.rs index 28522bfd19..4077c5d46f 100644 --- a/pipelined/bevy_render2/src/render_phase/draw.rs +++ b/pipelined/bevy_render2/src/render_phase/draw.rs @@ -15,7 +15,12 @@ use bevy_utils::HashMap; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use std::{any::TypeId, fmt::Debug, hash::Hash}; +/// A draw function which is used to draw a specific [`PhaseItem`]. +/// +/// They are the the general form of drawing items, whereas [`RenderCommands`](RenderCommand) +/// are more modular. pub trait Draw: Send + Sync + 'static { + /// Draws the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`]. fn draw<'w>( &mut self, world: &'w World, @@ -25,26 +30,39 @@ pub trait Draw: Send + Sync + 'static { ); } +/// An item which will be drawn to the screen. A phase item should be queued up for rendering +/// during the [`RenderStage::Queue`](crate::RenderStage::Queue) stage. +/// Afterwards it will be sorted and rendered automatically in the +/// [`RenderStage::PhaseSort`](crate::RenderStage::PhaseSort) stage and +/// [`RenderStage::Render`](crate::RenderStage::Render) stage, respectively. pub trait PhaseItem: Send + Sync + 'static { + /// The type used for ordering the items. The smallest values are drawn first. type SortKey: Ord; + /// Determines the order in which the items are drawn during the corresponding [`RenderPhase`]. fn sort_key(&self) -> Self::SortKey; + /// Specifies the [`Draw`] function used to render the item. fn draw_function(&self) -> DrawFunctionId; } // TODO: make this generic? +/// /// A [`Draw`] function identifier. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct DrawFunctionId(usize); +/// Stores all draw functions for the [`PhaseItem`] type. +/// For retrieval they are associated with their [`TypeId`]. pub struct DrawFunctionsInternal { pub draw_functions: Vec>>, pub indices: HashMap, } impl DrawFunctionsInternal

{ + /// Adds the [`Draw`] function and associates it to its own type. pub fn add>(&mut self, draw_function: T) -> DrawFunctionId { self.add_with::(draw_function) } + /// Adds the [`Draw`] function and associates it to the type `T` pub fn add_with>(&mut self, draw_function: D) -> DrawFunctionId { self.draw_functions.push(Box::new(draw_function)); let id = DrawFunctionId(self.draw_functions.len() - 1); @@ -52,15 +70,19 @@ impl DrawFunctionsInternal

{ id } + /// Retrieves the [`Draw`] function corresponding to the `id` mutably. pub fn get_mut(&mut self, id: DrawFunctionId) -> Option<&mut dyn Draw

> { self.draw_functions.get_mut(id.0).map(|f| &mut **f) } + /// Retrieves the id of the [`Draw`] function corresponding to their associated type `T`. pub fn get_id(&self) -> Option { self.indices.get(&TypeId::of::()).copied() } } +/// Stores all draw functions for the [`PhaseItem`] type hidden behind a reader-writer lock. +/// To access them the [`DrawFunctions::read`] and [`DrawFunctions::write`] methods are used. pub struct DrawFunctions { internal: RwLock>, } @@ -77,16 +99,42 @@ impl Default for DrawFunctions

{ } impl DrawFunctions

{ + /// Accesses the draw functions in read mode. pub fn read(&self) -> RwLockReadGuard<'_, DrawFunctionsInternal

> { self.internal.read() } + /// Accesses the draw functions in write mode. pub fn write(&self) -> RwLockWriteGuard<'_, DrawFunctionsInternal

> { self.internal.write() } } + +/// RenderCommand is a trait that runs an ECS query and produces one or more +/// [`TrackedRenderPass`] calls. Types implementing this trait can be composed (as tuples). +/// +/// They can be registered as a [`Draw`] function via the +/// [`AddRenderCommand::add_render_command`] method. +/// +/// # Example +/// The `DrawPbr` draw function is created from the following render command +/// tuple. Const generics are used to set specific bind group locations: +/// +/// ```ignore +/// pub type DrawPbr = ( +/// SetItemPipeline, +/// SetMeshViewBindGroup<0>, +/// SetStandardMaterialBindGroup<1>, +/// SetTransformBindGroup<2>, +/// DrawMesh, +/// ); +/// ``` pub trait RenderCommand { + /// Specifies all ECS data required by [`RenderCommand::render`]. + /// All parameters have to be read only. type Param: SystemParam; + + /// Renders the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`]. fn render<'w>( view: Entity, item: &P, @@ -165,6 +213,8 @@ macro_rules! render_command_tuple_impl { all_tuples!(render_command_tuple_impl, 0, 15, C); +/// Wraps a [`RenderCommand`] into a state so that it can be used as a [`Draw`] function. +/// Therefore the [`RenderCommand::Param`] is queried from the ECS and passed to the command. pub struct RenderCommandState> { state: SystemState, } @@ -181,6 +231,7 @@ impl + Send + Sync + 'static> Draw

for Rend where ::Fetch: ReadOnlySystemParamFetch, { + /// Prepares the ECS parameters for the wrapped [`RenderCommand`] and then renders it. fn draw<'w>( &mut self, world: &'w World, @@ -193,7 +244,10 @@ where } } +/// Registers a [`RenderCommand`] as a [`Draw`] function. +/// They are stored inside the [`DrawFunctions`] resource of the app. pub trait AddRenderCommand { + /// Adds the [`RenderCommand`] for the specified [`RenderPhase`](super::RenderPhase) to the app. fn add_render_command + Send + Sync + 'static>( &mut self, ) -> &mut Self diff --git a/pipelined/bevy_render2/src/render_phase/draw_state.rs b/pipelined/bevy_render2/src/render_phase/draw_state.rs index e6b2b86ba5..62c19706d1 100644 --- a/pipelined/bevy_render2/src/render_phase/draw_state.rs +++ b/pipelined/bevy_render2/src/render_phase/draw_state.rs @@ -5,7 +5,7 @@ use bevy_utils::tracing::debug; use std::ops::Range; use wgpu::{IndexFormat, RenderPass}; -/// Tracks the current pipeline state to ensure draw calls are valid. +/// Tracks the current [`TrackedRenderPipeline`] state to ensure draw calls are valid. #[derive(Debug, Default)] pub struct DrawState { pipeline: Option, @@ -83,12 +83,16 @@ impl DrawState { } } +/// A [`RenderPass`], which tracks the current pipeline state to ensure all draw calls are valid. +/// It is used to set the current [`RenderPipeline`], [`BindGroups`](BindGroup) and buffers. +/// After all requirements are specified, draw calls can be issued. pub struct TrackedRenderPass<'a> { pass: RenderPass<'a>, state: DrawState, } impl<'a> TrackedRenderPass<'a> { + /// Tracks the supplied render pass. pub fn new(pass: RenderPass<'a>) -> Self { Self { state: DrawState::default(), @@ -96,6 +100,9 @@ impl<'a> TrackedRenderPass<'a> { } } + /// Sets the active [`RenderPipeline`]. + /// + /// Subsequent draw calls will exhibit the behavior defined by the `pipeline`. pub fn set_render_pipeline(&mut self, pipeline: &'a RenderPipeline) { debug!("set pipeline: {:?}", pipeline); if self.state.is_pipeline_set(pipeline.id()) { @@ -105,6 +112,9 @@ impl<'a> TrackedRenderPass<'a> { self.state.set_pipeline(pipeline.id()); } + /// Sets the active [`BindGroup`] for a given bind group index. The bind group layout in the + /// active pipeline when any `draw()` function is called must match the layout + /// of this `bind group`. pub fn set_bind_group( &mut self, index: usize, @@ -132,6 +142,13 @@ impl<'a> TrackedRenderPass<'a> { .set_bind_group(index as usize, bind_group.id(), dynamic_uniform_indices); } + /// Assign a vertex buffer to a slot. + /// + /// Subsequent calls to [`TrackedRenderPass::draw`] and [`TrackedRenderPass::draw_indexed`] + /// will use the `buffer` as one of the source vertex buffers. + /// + /// The `slot` refers to the index of the matching descriptor in + /// [`VertexState::buffers`](crate::render_resource::VertexState::buffers). pub fn set_vertex_buffer(&mut self, index: usize, buffer_slice: BufferSlice<'a>) { let offset = buffer_slice.offset(); if self @@ -158,6 +175,10 @@ impl<'a> TrackedRenderPass<'a> { .set_vertex_buffer(index, buffer_slice.id(), offset); } + /// Sets the active index buffer. + /// + /// Subsequent calls to [`TrackedRenderPass::draw_indexed`] will use the `buffer` as + /// the source index buffer. pub fn set_index_buffer( &mut self, buffer_slice: BufferSlice<'a>, @@ -182,11 +203,18 @@ impl<'a> TrackedRenderPass<'a> { .set_index_buffer(buffer_slice.id(), offset, index_format); } + /// Draws primitives from the active vertex buffer(s). + /// + /// The active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`]. pub fn draw(&mut self, vertices: Range, instances: Range) { debug!("draw: {:?} {:?}", vertices, instances); self.pass.draw(vertices, instances); } + /// Draws indexed primitives using the active index buffer and the active vertex buffer(s). + /// + /// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the + /// active vertex buffers can be set with [`TrackedRenderPass::set_vertex_buffer`]. pub fn draw_indexed(&mut self, indices: Range, base_vertex: i32, instances: Range) { debug!( "draw indexed: {:?} {} {:?}", diff --git a/pipelined/bevy_render2/src/render_phase/mod.rs b/pipelined/bevy_render2/src/render_phase/mod.rs index ba2a1fb408..ff838f55c1 100644 --- a/pipelined/bevy_render2/src/render_phase/mod.rs +++ b/pipelined/bevy_render2/src/render_phase/mod.rs @@ -6,6 +6,7 @@ pub use draw_state::*; use bevy_ecs::prelude::Query; +/// A resource to collect and sort draw requests for specific [`PhaseItems`](PhaseItem). pub struct RenderPhase { pub items: Vec, } @@ -17,16 +18,19 @@ impl Default for RenderPhase { } impl RenderPhase { + /// Adds a [`PhaseItem`] to this render phase. #[inline] pub fn add(&mut self, item: I) { self.items.push(item); } + /// Sorts all of its [`PhaseItems`](PhaseItem). pub fn sort(&mut self) { self.items.sort_by_key(|d| d.sort_key()); } } +/// This system sorts all [`RenderPhases`](RenderPhase) for the [`PhaseItem`] type. pub fn sort_phase_system(mut render_phases: Query<&mut RenderPhase>) { for mut phase in render_phases.iter_mut() { phase.sort(); diff --git a/pipelined/bevy_render2/src/render_resource/bind_group.rs b/pipelined/bevy_render2/src/render_resource/bind_group.rs index 75e5262a85..6ee9633c2b 100644 --- a/pipelined/bevy_render2/src/render_resource/bind_group.rs +++ b/pipelined/bevy_render2/src/render_resource/bind_group.rs @@ -1,9 +1,16 @@ use bevy_reflect::Uuid; use std::{ops::Deref, sync::Arc}; +/// A [`BindGroup`] identifier. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] pub struct BindGroupId(Uuid); +/// Bind groups are responsible for binding render resources (e.g. buffers, textures, samplers) +/// to a [`TrackedRenderPass`](crate::render_phase::TrackedRenderPass). +/// This makes them accessible in the pipeline (shaders) as uniforms. +/// +/// May be converted from and dereferences to a wgpu [`BindGroup`](wgpu::BindGroup). +/// Can be created via [`RenderDevice::create_bind_group`](crate::renderer::RenderDevice::create_bind_group). #[derive(Clone, Debug)] pub struct BindGroup { id: BindGroupId, @@ -11,6 +18,7 @@ pub struct BindGroup { } impl BindGroup { + /// Returns the [`BindGroupId`]. #[inline] pub fn id(&self) -> BindGroupId { self.id diff --git a/pipelined/bevy_render2/src/render_resource/pipeline.rs b/pipelined/bevy_render2/src/render_resource/pipeline.rs index 1bf4a0e30f..f462c98c53 100644 --- a/pipelined/bevy_render2/src/render_resource/pipeline.rs +++ b/pipelined/bevy_render2/src/render_resource/pipeline.rs @@ -7,9 +7,14 @@ use wgpu::{ VertexAttribute, VertexStepMode, }; +/// A [`RenderPipeline`] identifier. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] pub struct RenderPipelineId(Uuid); +/// A RenderPipeline represents a graphics pipeline and its stages (shaders), bindings and vertex buffers. +/// +/// May be converted from and dereferences to a wgpu [`RenderPipeline`](wgpu::RenderPipeline). +/// Can be created via [`RenderDevice::create_render_pipeline`](crate::renderer::RenderDevice::create_render_pipeline). #[derive(Clone, Debug)] pub struct RenderPipeline { id: RenderPipelineId, @@ -41,9 +46,14 @@ impl Deref for RenderPipeline { } } +/// A [`ComputePipeline`] identifier. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] pub struct ComputePipelineId(Uuid); +/// A ComputePipeline represents a compute pipeline and its single shader stage. +/// +/// May be converted from and dereferences to a wgpu [`ComputePipeline`](wgpu::ComputePipeline). +/// Can be created via [`RenderDevice::create_compute_pipeline`](crate::renderer::RenderDevice::create_compute_pipeline). #[derive(Clone, Debug)] pub struct ComputePipeline { id: ComputePipelineId, @@ -51,6 +61,7 @@ pub struct ComputePipeline { } impl ComputePipeline { + /// Returns the [`ComputePipelineId`]. #[inline] pub fn id(&self) -> ComputePipelineId { self.id diff --git a/pipelined/bevy_render2/src/render_resource/texture.rs b/pipelined/bevy_render2/src/render_resource/texture.rs index 6baac16a17..047c042123 100644 --- a/pipelined/bevy_render2/src/render_resource/texture.rs +++ b/pipelined/bevy_render2/src/render_resource/texture.rs @@ -1,9 +1,14 @@ use bevy_utils::Uuid; use std::{ops::Deref, sync::Arc}; +/// A [`Texture`] identifier. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] pub struct TextureId(Uuid); +/// A GPU-accessible texture. +/// +/// May be converted from and dereferences to a wgpu [`Texture`](wgpu::Texture). +/// Can be created via [`RenderDevice::create_texture`](crate::renderer::RenderDevice::create_texture). #[derive(Clone, Debug)] pub struct Texture { id: TextureId, @@ -11,11 +16,13 @@ pub struct Texture { } impl Texture { + /// Returns the [`TextureId`]. #[inline] pub fn id(&self) -> TextureId { self.id } + /// Creates a view of this texture. pub fn create_view(&self, desc: &wgpu::TextureViewDescriptor) -> TextureView { TextureView::from(self.value.create_view(desc)) } @@ -39,12 +46,19 @@ impl Deref for Texture { } } +/// A [`TextureView`] identifier. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] pub struct TextureViewId(Uuid); +/// This type combines wgpu's [`TextureView`](wgpu::TextureView) and +/// [SurfaceTexture`](wgpu::SurfaceTexture) into the same interface. #[derive(Clone, Debug)] pub enum TextureViewValue { + /// The value is an actual wgpu [`TextureView`](wgpu::TextureView). TextureView(Arc), + + /// The value is a wgpu [`SurfaceTexture`](wgpu::SurfaceTexture), but dereferences to + /// a [`TextureView`](wgpu::TextureView). SurfaceTexture { // NOTE: The order of these fields is important because the view must be dropped before the // frame is dropped @@ -53,6 +67,10 @@ pub enum TextureViewValue { }, } +/// Describes a [`Texture`] with its associated metadata required by a pipeline or [`BindGroup`](super::BindGroup). +/// +/// May be converted from a [`TextureView`](wgpu::TextureView) or [`SurfaceTexture`](wgpu::SurfaceTexture) +/// or dereferences to a wgpu [`TextureView`](wgpu::TextureView). #[derive(Clone, Debug)] pub struct TextureView { id: TextureViewId, @@ -60,11 +78,13 @@ pub struct TextureView { } impl TextureView { + /// Returns the [`TextureViewId`]. #[inline] pub fn id(&self) -> TextureViewId { self.id } + /// Returns the [`SurfaceTexture`](wgpu::SurfaceTexture) of the texture view if it is of that type. #[inline] pub fn take_surface_texture(self) -> Option { match self.value { @@ -107,9 +127,15 @@ impl Deref for TextureView { } } +/// A [`Sampler`] identifier. #[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)] pub struct SamplerId(Uuid); +/// A Sampler defines how a pipeline will sample from a [`TextureView`]. +/// They define image filters (including anisotropy) and address (wrapping) modes, among other things. +/// +/// May be converted from and dereferences to a wgpu [`Sampler`](wgpu::Sampler). +/// Can be created via [`RenderDevice::create_sampler`](crate::renderer::RenderDevice::create_sampler). #[derive(Clone, Debug)] pub struct Sampler { id: SamplerId, @@ -117,6 +143,7 @@ pub struct Sampler { } impl Sampler { + /// Returns the [`SamplerId`]. #[inline] pub fn id(&self) -> SamplerId { self.id diff --git a/pipelined/bevy_render2/src/renderer/mod.rs b/pipelined/bevy_render2/src/renderer/mod.rs index 46e4fd855b..dab1295dba 100644 --- a/pipelined/bevy_render2/src/renderer/mod.rs +++ b/pipelined/bevy_render2/src/renderer/mod.rs @@ -13,6 +13,7 @@ use bevy_ecs::prelude::*; use std::sync::Arc; use wgpu::{CommandEncoder, DeviceDescriptor, Instance, Queue, RequestAdapterOptions}; +/// Updates the [`RenderGraph`] with all of its nodes and then runs it to render the entire frame. pub fn render_system(world: &mut World) { world.resource_scope(|world, mut graph: Mut| { graph.update(world); @@ -52,9 +53,15 @@ pub fn render_system(world: &mut World) { } } +/// This queue is used to enqueue tasks for the GPU to execute asynchronously. pub type RenderQueue = Arc; + +/// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`], +/// aswell as to create [`WindowSurfaces`](crate::view::window::WindowSurfaces). pub type RenderInstance = Instance; +/// Initializes the renderer by retrieving and preparing the GPU instance, device and queue +/// for the specified backend. pub async fn initialize_renderer( instance: &Instance, request_adapter_options: &RequestAdapterOptions<'_>, @@ -87,6 +94,10 @@ pub async fn initialize_renderer( (RenderDevice::from(device), queue) } +/// The context with all information required to interact with the GPU. +/// +/// The [`RenderDevice`] is used to create render resources and the +/// the [`CommandEncoder`] is used to record a series of GPU operations. pub struct RenderContext { pub render_device: RenderDevice, pub command_encoder: CommandEncoder, diff --git a/pipelined/bevy_render2/src/renderer/render_device.rs b/pipelined/bevy_render2/src/renderer/render_device.rs index 31ce0ece2b..dda70368aa 100644 --- a/pipelined/bevy_render2/src/renderer/render_device.rs +++ b/pipelined/bevy_render2/src/renderer/render_device.rs @@ -1,12 +1,12 @@ -use futures_lite::future; -use wgpu::util::DeviceExt; - use crate::render_resource::{ BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawRenderPipelineDescriptor, RenderPipeline, Sampler, Texture, }; +use futures_lite::future; use std::sync::Arc; +use wgpu::util::DeviceExt; +/// This GPU device is responsible for the creation of most rendering and compute resources. #[derive(Clone)] pub struct RenderDevice { device: Arc, @@ -19,7 +19,7 @@ impl From> for RenderDevice { } impl RenderDevice { - /// List all features that may be used with this device. + /// List all [`Features`](wgpu::Features) that may be used with this device. /// /// Functions may panic if you use unsupported features. #[inline] @@ -27,7 +27,7 @@ impl RenderDevice { self.device.features() } - /// List all limits that were requested of this device. + /// List all [`Limits`](wgpu::Limits) that were requested of this device. /// /// If any of these limits are exceeded, functions may panic. #[inline] @@ -35,7 +35,7 @@ impl RenderDevice { self.device.limits() } - /// Creates a shader module from either SPIR-V or WGSL source code. + /// Creates a [ShaderModule](wgpu::ShaderModule) from either SPIR-V or WGSL source code. #[inline] pub fn create_shader_module(&self, desc: &wgpu::ShaderModuleDescriptor) -> wgpu::ShaderModule { self.device.create_shader_module(desc) @@ -49,7 +49,7 @@ impl RenderDevice { self.device.poll(maintain) } - /// Creates an empty [`CommandEncoder`]. + /// Creates an empty [`CommandEncoder`](wgpu::CommandEncoder). #[inline] pub fn create_command_encoder( &self, @@ -58,7 +58,7 @@ impl RenderDevice { self.device.create_command_encoder(desc) } - /// Creates an empty [`RenderBundleEncoder`]. + /// Creates an empty [`RenderBundleEncoder`](wgpu::RenderBundleEncoder). #[inline] pub fn create_render_bundle_encoder( &self, @@ -67,14 +67,14 @@ impl RenderDevice { self.device.create_render_bundle_encoder(desc) } - /// Creates a new [`BindGroup`]. + /// Creates a new [`BindGroup`](wgpu::BindGroup). #[inline] pub fn create_bind_group(&self, desc: &wgpu::BindGroupDescriptor) -> BindGroup { let wgpu_bind_group = self.device.create_bind_group(desc); BindGroup::from(wgpu_bind_group) } - /// Creates a [`BindGroupLayout`]. + /// Creates a [`BindGroupLayout`](wgpu::BindGroupLayout). #[inline] pub fn create_bind_group_layout( &self, @@ -83,7 +83,7 @@ impl RenderDevice { BindGroupLayout::from(self.device.create_bind_group_layout(desc)) } - /// Creates a [`PipelineLayout`]. + /// Creates a [`PipelineLayout`](wgpu::PipelineLayout). #[inline] pub fn create_pipeline_layout( &self, @@ -115,7 +115,7 @@ impl RenderDevice { Buffer::from(wgpu_buffer) } - /// Creates a [`Buffer`]. + /// Creates a [`Buffer`] and initializes it with the specified data. pub fn create_buffer_with_data(&self, desc: &wgpu::util::BufferInitDescriptor) -> Buffer { let wgpu_buffer = self.device.create_buffer_init(desc); Buffer::from(wgpu_buffer) @@ -137,16 +137,17 @@ impl RenderDevice { Sampler::from(wgpu_sampler) } - /// Create a new [`SwapChain`] which targets `surface`. + /// Create a new [`SwapChain`](wgpu::SwapChain) which targets `surface`. /// /// # Panics /// - /// - A old [`SwapChainFrame`] is still alive referencing an old swapchain. + /// - A old [`SwapChainFrame`](wgpu::SwapChain) is still alive referencing an old swap chain. /// - Texture format requested is unsupported on the swap chain. pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) { surface.configure(&self.device, config) } + /// Returns the wgpu [`Device`](wgpu::Device). pub fn wgpu_device(&self) -> &wgpu::Device { &self.device } diff --git a/pipelined/bevy_render2/src/texture/image.rs b/pipelined/bevy_render2/src/texture/image.rs index 270a0824fc..30b7b84394 100644 --- a/pipelined/bevy_render2/src/texture/image.rs +++ b/pipelined/bevy_render2/src/texture/image.rs @@ -50,6 +50,11 @@ impl Default for Image { } impl Image { + /// Creates a new image from raw binary data and the corresponding metadata. + /// + /// # Panics + /// Panics if the length of the `data`, volume of the `size` and the size of the `format` + /// do not match. pub fn new( size: Extent3d, dimension: TextureDimension, @@ -71,6 +76,12 @@ impl Image { image } + /// Creates a new image from raw binary data and the corresponding metadata, by filling + /// the image data with the `pixel` data repeated multiple times. + /// + /// # Panics + /// Panics if the size of the `format` is not a multiple of the length of the `pixel` data. + /// do not match. pub fn new_fill( size: Extent3d, dimension: TextureDimension, @@ -98,10 +109,13 @@ impl Image { value } + /// Returns the aspect ratio (height/width) of a 2D image. pub fn aspect_2d(&self) -> f32 { self.texture_descriptor.size.height as f32 / self.texture_descriptor.size.width as f32 } + /// Resizes the image to the new size, by removing information or appending 0 to the `data`. + /// Does not properly resize the contents of the image, but only its internal `data` buffer. pub fn resize(&mut self, size: Extent3d) { self.texture_descriptor.size = size; self.data.resize( @@ -112,6 +126,9 @@ impl Image { /// Changes the `size`, asserting that the total number of data elements (pixels) remains the /// same. + /// + /// # Panics + /// Panics if the `new_size` does not have the same volume as to old one. pub fn reinterpret_size(&mut self, new_size: Extent3d) { assert!( new_size.volume() == self.texture_descriptor.size.volume(), @@ -123,9 +140,13 @@ impl Image { self.texture_descriptor.size = new_size; } - /// Takes a 2D texture containing vertically stacked images of the same size, and reinterprets + /// Takes a 2D image containing vertically stacked images of the same size, and reinterprets /// it as a 2D array texture, where each of the stacked images becomes one layer of the /// array. This is primarily for use with the `texture2DArray` shader uniform type. + /// + /// # Panics + /// Panics if the texture is not 2D, has more than one layers or is not evenly dividable into + /// the `layers`. pub fn reinterpret_stacked_2d_as_array(&mut self, layers: u32) { // Must be a stacked image, and the height must be divisible by layers. assert!(self.texture_descriptor.dimension == TextureDimension::D2); @@ -203,31 +224,39 @@ pub enum TextureError { ImageError(#[from] image::ImageError), } -/// Type of a raw image buffer +/// The type of a raw image buffer. pub enum ImageType<'a> { - /// Mime type of an image, for example `"image/png"` + /// The mime type of an image, for example `"image/png"`. MimeType(&'a str), - /// Extension of an image file, for example `"png"` + /// The extension of an image file, for example `"png"`. Extension(&'a str), } +/// Used to calculate the volume of an item. pub trait Volume { fn volume(&self) -> usize; } impl Volume for Extent3d { + /// Calculates the volume of the [`Extent3D`]. fn volume(&self) -> usize { (self.width * self.height * self.depth_or_array_layers) as usize } } +/// Information about the pixel size in bytes and the number of different components. pub struct PixelInfo { + /// The size of a component of a pixel in bytes. pub type_size: usize, + /// The amount of different components (color channels). pub num_components: usize, } +/// Extends the wgpu [`TextureFormat`] with information about the pixel. pub trait TextureFormatPixelInfo { + /// Returns the pixel information of the format. fn pixel_info(&self) -> PixelInfo; + /// Returns the size of a pixel of the format. fn pixel_size(&self) -> usize { let info = self.pixel_info(); info.type_size * info.num_components @@ -340,6 +369,8 @@ impl TextureFormatPixelInfo for TextureFormat { } } +/// The GPU-representation of an [`Image`]. +/// Consists of the [`Texture`], its [`TextureView`] and the corresponding [`Sampler`]. #[derive(Debug, Clone)] pub struct GpuImage { pub texture: Texture, @@ -352,10 +383,12 @@ impl RenderAsset for Image { type PreparedAsset = GpuImage; type Param = (SRes, SRes); + /// Clones the Image. fn extract_asset(&self) -> Self::ExtractedAsset { self.clone() } + /// Converts the extracted image into a [`GpuImage`]. fn prepare_asset( image: Self::ExtractedAsset, (render_device, render_queue): &mut SystemParamItem, diff --git a/pipelined/bevy_render2/src/texture/image_texture_conversion.rs b/pipelined/bevy_render2/src/texture/image_texture_conversion.rs index 85126f23c2..d3c243bad4 100644 --- a/pipelined/bevy_render2/src/texture/image_texture_conversion.rs +++ b/pipelined/bevy_render2/src/texture/image_texture_conversion.rs @@ -2,7 +2,7 @@ use crate::texture::{Image, TextureFormatPixelInfo}; use wgpu::{Extent3d, TextureDimension, TextureFormat}; // TODO: fix name? -/// Helper method to convert a `DynamicImage` to a `Texture` +/// Converts a [`DynamicImage`] to an [`Image`]. pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Image { use bevy_core::cast_slice; let width; @@ -125,8 +125,8 @@ pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Image { ) } -/// Helper method to convert a `Texture` to a `DynamicImage`. Not all `Texture` formats are -/// covered, it will return `None` if the format is not supported +/// Converts an [`Image`] to a [`DynamicImage`]. Not all [`TextureFormat`] are +/// covered, therefore it will return `None` if the format is unsupported. pub(crate) fn texture_to_image(texture: &Image) -> Option { match texture.texture_descriptor.format { TextureFormat::R8Unorm => image::ImageBuffer::from_raw( diff --git a/pipelined/bevy_render2/src/texture/image_texture_loader.rs b/pipelined/bevy_render2/src/texture/image_texture_loader.rs index c539d0a6b4..20e6c774cc 100644 --- a/pipelined/bevy_render2/src/texture/image_texture_loader.rs +++ b/pipelined/bevy_render2/src/texture/image_texture_loader.rs @@ -38,7 +38,7 @@ impl AssetLoader for ImageTextureLoader { } } -/// An error that occurs when loading a texture from a file +/// An error that occurs when loading a texture from a file. #[derive(Error, Debug)] pub struct FileTextureError { error: TextureError, diff --git a/pipelined/bevy_render2/src/texture/mod.rs b/pipelined/bevy_render2/src/texture/mod.rs index 59eb1f5dc1..1b99642a5f 100644 --- a/pipelined/bevy_render2/src/texture/mod.rs +++ b/pipelined/bevy_render2/src/texture/mod.rs @@ -18,6 +18,7 @@ use bevy_app::{App, Plugin}; use bevy_asset::AddAsset; // TODO: replace Texture names with Image names? +/// Adds the [`Image`] as an asset and makes sure that they are extracted and prepared for the GPU. pub struct ImagePlugin; impl Plugin for ImagePlugin { diff --git a/pipelined/bevy_render2/src/texture/texture_cache.rs b/pipelined/bevy_render2/src/texture/texture_cache.rs index 3b9ebe7b1a..cca353a1d5 100644 --- a/pipelined/bevy_render2/src/texture/texture_cache.rs +++ b/pipelined/bevy_render2/src/texture/texture_cache.rs @@ -6,6 +6,8 @@ use bevy_ecs::prelude::ResMut; use bevy_utils::HashMap; use wgpu::{TextureDescriptor, TextureViewDescriptor}; +/// The internal representation of a [`CachedTexture`] used to track whether it was recently used +/// and is currently taken. struct CachedTextureMeta { texture: Texture, default_view: TextureView, @@ -13,17 +15,24 @@ struct CachedTextureMeta { frames_since_last_use: usize, } +/// A cached GPU [`Texture`] with corresponding [`TextureView`]. +/// This is useful for textures that are created repeatedly (each frame) in the rendering process +/// to reduce the amount of GPU memory allocations. pub struct CachedTexture { pub texture: Texture, pub default_view: TextureView, } +/// This resource caches textures that are created repeatedly in the rendering process and +/// are only required for one frame. #[derive(Default)] pub struct TextureCache { textures: HashMap, Vec>, } impl TextureCache { + /// Retrieves a texture that matches the `descriptor`. If no matching one is found a new + /// [`CachedTexture`] is created. pub fn get( &mut self, render_device: &RenderDevice, @@ -72,6 +81,7 @@ impl TextureCache { } } + /// Updates the cache and only retains recently used textures. pub fn update(&mut self) { for textures in self.textures.values_mut() { for texture in textures.iter_mut() { @@ -84,6 +94,7 @@ impl TextureCache { } } +/// Updates the [`TextureCache`] to only retains recently used textures. pub fn update_texture_cache_system(mut texture_cache: ResMut) { texture_cache.update(); } diff --git a/pipelined/bevy_render2/src/view/window.rs b/pipelined/bevy_render2/src/view/window.rs index 00aea10aa1..6bc9dafc2a 100644 --- a/pipelined/bevy_render2/src/view/window.rs +++ b/pipelined/bevy_render2/src/view/window.rs @@ -11,7 +11,7 @@ use bevy_window::{RawWindowHandleWrapper, WindowId, Windows}; use std::ops::{Deref, DerefMut}; use wgpu::TextureFormat; -// Token to ensure a system runs on the main thread. +/// Token to ensure a system runs on the main thread. #[derive(Default)] pub struct NonSendMarker;