mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 04:33:37 +00:00
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 <dataphract@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
14ce281904
commit
1076a8f2b5
33 changed files with 592 additions and 72 deletions
|
@ -170,9 +170,18 @@ impl<Param: SystemParam> SystemState<Param> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
pub trait RunSystem: Send + Sync + 'static {
|
||||||
|
/// The `SystemParam` type passed to the system when it runs.
|
||||||
type Param: SystemParam;
|
type Param: SystemParam;
|
||||||
|
|
||||||
|
/// Runs the system.
|
||||||
fn run(param: SystemParamItem<Self::Param>);
|
fn run(param: SystemParamItem<Self::Param>);
|
||||||
|
|
||||||
|
/// Creates a concrete instance of the system for the specified `World`.
|
||||||
fn system(world: &mut World) -> ParamSystem<Self::Param> {
|
fn system(world: &mut World) -> ParamSystem<Self::Param> {
|
||||||
ParamSystem {
|
ParamSystem {
|
||||||
run: Self::run,
|
run: Self::run,
|
||||||
|
|
|
@ -10,7 +10,7 @@ use bevy_reflect::TypeUuid;
|
||||||
use bevy_render2::mesh::Mesh;
|
use bevy_render2::mesh::Mesh;
|
||||||
use bevy_scene::Scene;
|
use bevy_scene::Scene;
|
||||||
|
|
||||||
/// Adds support for GLTF file loading to Apps
|
/// Adds support for glTF file loading to the app.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct GltfPlugin;
|
pub struct GltfPlugin;
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ impl Plugin for GltfPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Representation of a loaded glTF file.
|
||||||
#[derive(Debug, TypeUuid)]
|
#[derive(Debug, TypeUuid)]
|
||||||
#[uuid = "5c7d5f8a-f7b0-4e45-a09e-406c0372fea2"]
|
#[uuid = "5c7d5f8a-f7b0-4e45-a09e-406c0372fea2"]
|
||||||
pub struct Gltf {
|
pub struct Gltf {
|
||||||
|
@ -38,6 +39,8 @@ pub struct Gltf {
|
||||||
pub default_scene: Option<Handle<Scene>>,
|
pub default_scene: Option<Handle<Scene>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A glTF node with all of its child nodes, its [`GltfMesh`] and
|
||||||
|
/// [`Transform`](bevy_transform::prelude::Transform).
|
||||||
#[derive(Debug, Clone, TypeUuid)]
|
#[derive(Debug, Clone, TypeUuid)]
|
||||||
#[uuid = "dad74750-1fd6-460f-ac51-0a7937563865"]
|
#[uuid = "dad74750-1fd6-460f-ac51-0a7937563865"]
|
||||||
pub struct GltfNode {
|
pub struct GltfNode {
|
||||||
|
@ -46,12 +49,14 @@ pub struct GltfNode {
|
||||||
pub transform: bevy_transform::prelude::Transform,
|
pub transform: bevy_transform::prelude::Transform,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A glTF mesh, which may consists of multiple [`GtlfPrimitives`](GltfPrimitive).
|
||||||
#[derive(Debug, Clone, TypeUuid)]
|
#[derive(Debug, Clone, TypeUuid)]
|
||||||
#[uuid = "8ceaec9a-926a-4f29-8ee3-578a69f42315"]
|
#[uuid = "8ceaec9a-926a-4f29-8ee3-578a69f42315"]
|
||||||
pub struct GltfMesh {
|
pub struct GltfMesh {
|
||||||
pub primitives: Vec<GltfPrimitive>,
|
pub primitives: Vec<GltfPrimitive>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Part of a [`GltfMesh`] that consists of a [`Mesh`] and an optional [`StandardMaterial`].
|
||||||
#[derive(Debug, Clone, TypeUuid)]
|
#[derive(Debug, Clone, TypeUuid)]
|
||||||
#[uuid = "cbfca302-82fd-41cb-af77-cab6b3d50af1"]
|
#[uuid = "cbfca302-82fd-41cb-af77-cab6b3d50af1"]
|
||||||
pub struct GltfPrimitive {
|
pub struct GltfPrimitive {
|
||||||
|
|
|
@ -35,12 +35,12 @@ use wgpu::{AddressMode, FilterMode, PrimitiveTopology, SamplerDescriptor, Textur
|
||||||
|
|
||||||
use crate::{Gltf, GltfNode};
|
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)]
|
#[derive(Error, Debug)]
|
||||||
pub enum GltfError {
|
pub enum GltfError {
|
||||||
#[error("unsupported primitive mode")]
|
#[error("unsupported primitive mode")]
|
||||||
UnsupportedPrimitive { mode: Mode },
|
UnsupportedPrimitive { mode: Mode },
|
||||||
#[error("invalid GLTF file: {0}")]
|
#[error("invalid glTF file: {0}")]
|
||||||
Gltf(#[from] gltf::Error),
|
Gltf(#[from] gltf::Error),
|
||||||
#[error("binary blob is missing")]
|
#[error("binary blob is missing")]
|
||||||
MissingBlob,
|
MissingBlob,
|
||||||
|
@ -56,7 +56,7 @@ pub enum GltfError {
|
||||||
AssetIoError(#[from] AssetIoError),
|
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)]
|
#[derive(Default)]
|
||||||
pub struct GltfLoader;
|
pub struct GltfLoader;
|
||||||
|
|
||||||
|
@ -74,6 +74,7 @@ impl AssetLoader for GltfLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Loads an entire glTF file.
|
||||||
async fn load_gltf<'a, 'b>(
|
async fn load_gltf<'a, 'b>(
|
||||||
bytes: &'a [u8],
|
bytes: &'a [u8],
|
||||||
load_context: &'a mut LoadContext<'b>,
|
load_context: &'a mut LoadContext<'b>,
|
||||||
|
@ -265,7 +266,7 @@ async fn load_gltf<'a, 'b>(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|res| {
|
.filter_map(|res| {
|
||||||
if let Err(err) = res.as_ref() {
|
if let Err(err) = res.as_ref() {
|
||||||
warn!("Error loading GLTF texture: {}", err);
|
warn!("Error loading glTF texture: {}", err);
|
||||||
}
|
}
|
||||||
res.ok()
|
res.ok()
|
||||||
})
|
})
|
||||||
|
@ -320,6 +321,7 @@ async fn load_gltf<'a, 'b>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Loads a glTF texture as a bevy [`Image`] and returns it together with its label.
|
||||||
async fn load_texture<'a>(
|
async fn load_texture<'a>(
|
||||||
gltf_texture: gltf::Texture<'a>,
|
gltf_texture: gltf::Texture<'a>,
|
||||||
buffer_data: &[Vec<u8>],
|
buffer_data: &[Vec<u8>],
|
||||||
|
@ -368,6 +370,7 @@ async fn load_texture<'a>(
|
||||||
Ok((texture, texture_label(&gltf_texture)))
|
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<StandardMaterial> {
|
fn load_material(material: &Material, load_context: &mut LoadContext) -> Handle<StandardMaterial> {
|
||||||
let material_label = material_label(material);
|
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(
|
fn load_node(
|
||||||
gltf_node: &gltf::Node,
|
gltf_node: &gltf::Node,
|
||||||
world_builder: &mut WorldChildBuilder,
|
world_builder: &mut WorldChildBuilder,
|
||||||
|
@ -559,14 +563,17 @@ fn load_node(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the label for the `mesh`.
|
||||||
fn mesh_label(mesh: &gltf::Mesh) -> String {
|
fn mesh_label(mesh: &gltf::Mesh) -> String {
|
||||||
format!("Mesh{}", mesh.index())
|
format!("Mesh{}", mesh.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the label for the `mesh` and `primitive`.
|
||||||
fn primitive_label(mesh: &gltf::Mesh, primitive: &Primitive) -> String {
|
fn primitive_label(mesh: &gltf::Mesh, primitive: &Primitive) -> String {
|
||||||
format!("Mesh{}/Primitive{}", mesh.index(), primitive.index())
|
format!("Mesh{}/Primitive{}", mesh.index(), primitive.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the label for the `material`.
|
||||||
fn material_label(material: &gltf::Material) -> String {
|
fn material_label(material: &gltf::Material) -> String {
|
||||||
if let Some(index) = material.index() {
|
if let Some(index) = material.index() {
|
||||||
format!("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 {
|
fn texture_label(texture: &gltf::Texture) -> String {
|
||||||
format!("Texture{}", texture.index())
|
format!("Texture{}", texture.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the label for the `node`.
|
||||||
fn node_label(node: &gltf::Node) -> String {
|
fn node_label(node: &gltf::Node) -> String {
|
||||||
format!("Node{}", node.index())
|
format!("Node{}", node.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the label for the `scene`.
|
||||||
fn scene_label(scene: &gltf::Scene) -> String {
|
fn scene_label(scene: &gltf::Scene) -> String {
|
||||||
format!("Scene{}", scene.index())
|
format!("Scene{}", scene.index())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts the texture sampler data from the glTF texture.
|
||||||
fn texture_sampler<'a>(texture: &gltf::Texture) -> SamplerDescriptor<'a> {
|
fn texture_sampler<'a>(texture: &gltf::Texture) -> SamplerDescriptor<'a> {
|
||||||
let gltf_sampler = texture.sampler();
|
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 {
|
fn texture_address_mode(gltf_address_mode: &gltf::texture::WrappingMode) -> AddressMode {
|
||||||
match gltf_address_mode {
|
match gltf_address_mode {
|
||||||
WrappingMode::ClampToEdge => AddressMode::ClampToEdge,
|
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<PrimitiveTopology, GltfError> {
|
fn get_primitive_topology(mode: Mode) -> Result<PrimitiveTopology, GltfError> {
|
||||||
match mode {
|
match mode {
|
||||||
Mode::Points => Ok(PrimitiveTopology::PointList),
|
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(
|
async fn load_buffers(
|
||||||
gltf: &gltf::Gltf,
|
gltf: &gltf::Gltf,
|
||||||
load_context: &LoadContext<'_>,
|
load_context: &LoadContext<'_>,
|
||||||
|
|
|
@ -8,6 +8,7 @@ use bevy_render2::{
|
||||||
};
|
};
|
||||||
use bevy_transform::components::{GlobalTransform, Transform};
|
use bevy_transform::components::{GlobalTransform, Transform};
|
||||||
|
|
||||||
|
/// A component bundle for PBR entities with a [`Mesh`] and a [`StandardMaterial`].
|
||||||
#[derive(Bundle, Clone)]
|
#[derive(Bundle, Clone)]
|
||||||
pub struct PbrBundle {
|
pub struct PbrBundle {
|
||||||
pub mesh: Handle<Mesh>,
|
pub mesh: Handle<Mesh>,
|
||||||
|
@ -56,7 +57,7 @@ impl CubemapVisibleEntities {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A component bundle for "point light" entities
|
/// A component bundle for [`PointLight`] entities.
|
||||||
#[derive(Debug, Bundle, Default)]
|
#[derive(Debug, Bundle, Default)]
|
||||||
pub struct PointLightBundle {
|
pub struct PointLightBundle {
|
||||||
pub point_light: PointLight,
|
pub point_light: PointLight,
|
||||||
|
@ -66,7 +67,7 @@ pub struct PointLightBundle {
|
||||||
pub global_transform: GlobalTransform,
|
pub global_transform: GlobalTransform,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A component bundle for "directional light" entities
|
/// A component bundle for [`DirectionalLight`] entities.
|
||||||
#[derive(Debug, Bundle, Default)]
|
#[derive(Debug, Bundle, Default)]
|
||||||
pub struct DirectionalLightBundle {
|
pub struct DirectionalLightBundle {
|
||||||
pub directional_light: DirectionalLight,
|
pub directional_light: DirectionalLight,
|
||||||
|
|
|
@ -27,6 +27,7 @@ use bevy_transform::TransformSystem;
|
||||||
|
|
||||||
pub mod draw_3d_graph {
|
pub mod draw_3d_graph {
|
||||||
pub mod node {
|
pub mod node {
|
||||||
|
/// Label for the shadow pass node.
|
||||||
pub const SHADOW_PASS: &str = "shadow_pass";
|
pub const SHADOW_PASS: &str = "shadow_pass";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +37,7 @@ pub const PBR_SHADER_HANDLE: HandleUntyped =
|
||||||
pub const SHADOW_SHADER_HANDLE: HandleUntyped =
|
pub const SHADOW_SHADER_HANDLE: HandleUntyped =
|
||||||
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 1836745567947005696);
|
HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 1836745567947005696);
|
||||||
|
|
||||||
|
/// Sets up the entire PBR infrastructure of bevy.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct PbrPlugin;
|
pub struct PbrPlugin;
|
||||||
|
|
||||||
|
|
|
@ -145,11 +145,11 @@ impl Default for DirectionalLightShadowMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ambient light.
|
/// An ambient light, which lights the entire scene equally.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AmbientLight {
|
pub struct AmbientLight {
|
||||||
pub color: Color,
|
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,
|
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;
|
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;
|
pub struct NotShadowReceiver;
|
||||||
|
|
||||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)]
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel)]
|
||||||
|
|
|
@ -15,7 +15,10 @@ use crevice::std140::{AsStd140, Std140};
|
||||||
use wgpu::{BindGroupDescriptor, BindGroupEntry, BindingResource};
|
use wgpu::{BindGroupDescriptor, BindGroupEntry, BindingResource};
|
||||||
|
|
||||||
/// A material with "standard" properties used in PBR lighting
|
/// 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
|
||||||
|
/// <https://google.github.io/filament/Material%20Properties.pdf>.
|
||||||
|
///
|
||||||
|
/// May be created directly from a [`Color`] or an [`Image`].
|
||||||
#[derive(Debug, Clone, TypeUuid)]
|
#[derive(Debug, Clone, TypeUuid)]
|
||||||
#[uuid = "7494888b-c082-457b-aacf-517228cc0c22"]
|
#[uuid = "7494888b-c082-457b-aacf-517228cc0c22"]
|
||||||
pub struct StandardMaterial {
|
pub struct StandardMaterial {
|
||||||
|
@ -56,7 +59,7 @@ impl Default for StandardMaterial {
|
||||||
emissive: Color::BLACK,
|
emissive: Color::BLACK,
|
||||||
emissive_texture: None,
|
emissive_texture: None,
|
||||||
// This is the minimum the roughness is clamped to in shader code
|
// This is the minimum the roughness is clamped to in shader code
|
||||||
// See https://google.github.io/filament/Filament.html#materialsystem/parameterization/
|
// See <https://google.github.io/filament/Filament.html#materialsystem/parameterization/>
|
||||||
// It's the minimum floating point value that won't be rounded down to 0 in the
|
// 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
|
// calculations used. Although technically for 32-bit floats, 0.045 could be
|
||||||
// used.
|
// used.
|
||||||
|
@ -66,7 +69,8 @@ impl Default for StandardMaterial {
|
||||||
metallic: 0.01,
|
metallic: 0.01,
|
||||||
metallic_roughness_texture: None,
|
metallic_roughness_texture: None,
|
||||||
// Minimum real-world reflectance is 2%, most materials between 2-5%
|
// 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
|
||||||
|
// <https://google.github.io/filament/Material%20Properties.pdf>
|
||||||
reflectance: 0.5,
|
reflectance: 0.5,
|
||||||
occlusion_texture: None,
|
occlusion_texture: None,
|
||||||
normal_map_texture: None,
|
normal_map_texture: None,
|
||||||
|
@ -95,6 +99,7 @@ impl From<Handle<Image>> for StandardMaterial {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The GPU representation of the uniform data of a [`StandardMaterial`].
|
||||||
#[derive(Clone, Default, AsStd140)]
|
#[derive(Clone, Default, AsStd140)]
|
||||||
pub struct StandardMaterialUniformData {
|
pub struct StandardMaterialUniformData {
|
||||||
/// Doubles as diffuse albedo for non-metallic, specular for metallic and a mix for everything
|
/// 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,
|
pub alpha_cutoff: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This plugin adds the [`StandardMaterial`] asset to the app.
|
||||||
pub struct StandardMaterialPlugin;
|
pub struct StandardMaterialPlugin;
|
||||||
|
|
||||||
impl Plugin for StandardMaterialPlugin {
|
impl Plugin for StandardMaterialPlugin {
|
||||||
|
@ -126,9 +132,13 @@ impl Plugin for StandardMaterialPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The GPU representation of a [`StandardMaterial`].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GpuStandardMaterial {
|
pub struct GpuStandardMaterial {
|
||||||
|
/// A buffer containing the [`StandardMaterialUniformData`] of the material.
|
||||||
pub buffer: Buffer,
|
pub buffer: Buffer,
|
||||||
|
/// The bind group specifying how the [`StandardMaterialUniformData`] and
|
||||||
|
/// all the textures of the material are bound.
|
||||||
pub bind_group: BindGroup,
|
pub bind_group: BindGroup,
|
||||||
pub has_normal_map: bool,
|
pub has_normal_map: bool,
|
||||||
pub flags: StandardMaterialFlags,
|
pub flags: StandardMaterialFlags,
|
||||||
|
|
|
@ -29,28 +29,32 @@ use bevy_ecs::prelude::*;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use wgpu::Backends;
|
use wgpu::Backends;
|
||||||
|
|
||||||
|
/// Contains the default Bevy rendering backend based on wgpu.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct RenderPlugin;
|
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)]
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)]
|
||||||
pub enum RenderStage {
|
pub enum RenderStage {
|
||||||
/// Extract data from "app world" and insert it into "render world". This step should be kept
|
/// Extract data from the "app world" and insert it into the "render world".
|
||||||
/// as short as possible to increase the "pipelining potential" for running the next frame
|
/// This step should be kept as short as possible to increase the "pipelining potential" for
|
||||||
/// while rendering the current frame.
|
/// running the next frame while rendering the current frame.
|
||||||
Extract,
|
Extract,
|
||||||
|
|
||||||
/// Prepare render resources from extracted data.
|
/// Prepare render resources from the extracted data for the GPU.
|
||||||
Prepare,
|
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,
|
Queue,
|
||||||
|
|
||||||
// TODO: This could probably be moved in favor of a system ordering abstraction in Render or 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,
|
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,
|
Render,
|
||||||
|
|
||||||
/// Cleanup render resources here.
|
/// 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)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, AppLabel)]
|
||||||
pub struct RenderApp;
|
pub struct RenderApp;
|
||||||
|
|
||||||
/// A "scratch" world used to avoid allocating new worlds every frame when
|
/// A "scratch" world used to avoid allocating new worlds every frame when
|
||||||
// swapping out the Render World.
|
/// swapping out the [`RenderWorld`].
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct ScratchRenderWorld(World);
|
struct ScratchRenderWorld(World);
|
||||||
|
|
||||||
impl Plugin for RenderPlugin {
|
impl Plugin for RenderPlugin {
|
||||||
|
/// Initializes the renderer, sets up the [`RenderStage`](RenderStage) and creates the rendering sub-app.
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
let default_backend = if cfg!(not(target_arch = "wasm32")) {
|
let default_backend = if cfg!(not(target_arch = "wasm32")) {
|
||||||
Backends::PRIMARY
|
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) {
|
fn extract(app_world: &mut World, render_app: &mut App) {
|
||||||
let extract = render_app
|
let extract = render_app
|
||||||
.schedule
|
.schedule
|
||||||
|
|
|
@ -79,12 +79,13 @@ impl Mesh {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the topology of the mesh.
|
||||||
pub fn primitive_topology(&self) -> PrimitiveTopology {
|
pub fn primitive_topology(&self) -> PrimitiveTopology {
|
||||||
self.primitive_topology
|
self.primitive_topology
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the data for a vertex attribute (position, normal etc.). The name will
|
/// 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(
|
pub fn set_attribute(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<Cow<'static, str>>,
|
name: impl Into<Cow<'static, str>>,
|
||||||
|
@ -94,11 +95,12 @@ impl Mesh {
|
||||||
self.attributes.insert(name.into(), values);
|
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<Cow<'static, str>>) -> Option<&VertexAttributeValues> {
|
pub fn attribute(&self, name: impl Into<Cow<'static, str>>) -> Option<&VertexAttributeValues> {
|
||||||
self.attributes.get(&name.into())
|
self.attributes.get(&name.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the data currently set to the vertex attribute with the specified `name` mutably.
|
||||||
pub fn attribute_mut(
|
pub fn attribute_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<Cow<'static, str>>,
|
name: impl Into<Cow<'static, str>>,
|
||||||
|
@ -106,21 +108,25 @@ impl Mesh {
|
||||||
self.attributes.get_mut(&name.into())
|
self.attributes.get_mut(&name.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indices describe how triangles are constructed out of the vertex attributes.
|
/// Sets the vertex indices of the mesh. They describe how triangles are constructed out of the
|
||||||
/// They are only useful for the [`crate::pipeline::PrimitiveTopology`] variants that use
|
/// vertex attributes and are therefore only useful for the [`PrimitiveTopology`] variants
|
||||||
/// triangles
|
/// that use triangles.
|
||||||
pub fn set_indices(&mut self, indices: Option<Indices>) {
|
pub fn set_indices(&mut self, indices: Option<Indices>) {
|
||||||
self.indices = indices;
|
self.indices = indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the vertex `indices` of the mesh.
|
||||||
pub fn indices(&self) -> Option<&Indices> {
|
pub fn indices(&self) -> Option<&Indices> {
|
||||||
self.indices.as_ref()
|
self.indices.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the vertex `indices` of the mesh mutably.
|
||||||
pub fn indices_mut(&mut self) -> Option<&mut Indices> {
|
pub fn indices_mut(&mut self) -> Option<&mut Indices> {
|
||||||
self.indices.as_mut()
|
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]> {
|
pub fn get_index_buffer_bytes(&self) -> Option<&[u8]> {
|
||||||
self.indices.as_ref().map(|indices| match &indices {
|
self.indices.as_ref().map(|indices| match &indices {
|
||||||
Indices::U16(indices) => cast_slice(&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 {
|
pub fn count_vertices(&self) -> usize {
|
||||||
let mut vertex_count: Option<usize> = None;
|
let mut vertex_count: Option<usize> = None;
|
||||||
for (attribute_name, attribute_data) in self.attributes.iter() {
|
for (attribute_name, attribute_data) in self.attributes.iter() {
|
||||||
|
@ -164,6 +174,12 @@ impl Mesh {
|
||||||
vertex_count.unwrap_or(0)
|
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<u8> {
|
pub fn get_vertex_buffer_data(&self) -> Vec<u8> {
|
||||||
let mut vertex_size = 0;
|
let mut vertex_size = 0;
|
||||||
for attribute_values in self.attributes.values() {
|
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.
|
/// This can dramatically increase the vertex count, so make sure this is what you want.
|
||||||
/// Does nothing if no [Indices] are set.
|
/// Does nothing if no [Indices] are set.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// If the mesh has any other topology than [`PrimitiveTopology::TriangleList`].
|
||||||
pub fn duplicate_vertices(&mut self) {
|
pub fn duplicate_vertices(&mut self) {
|
||||||
fn duplicate<T: Copy>(values: &[T], indices: impl Iterator<Item = usize>) -> Vec<T> {
|
fn duplicate<T: Copy>(values: &[T], indices: impl Iterator<Item = usize>) -> Vec<T> {
|
||||||
indices.map(|i| values[i]).collect()
|
indices.map(|i| values[i]).collect()
|
||||||
|
@ -248,7 +267,8 @@ impl Mesh {
|
||||||
|
|
||||||
/// Calculates the [`Mesh::ATTRIBUTE_NORMAL`] of a 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.
|
/// Consider calling [Mesh::duplicate_vertices] or export your mesh with normal attributes.
|
||||||
pub fn compute_flat_normals(&mut self) {
|
pub fn compute_flat_normals(&mut self) {
|
||||||
if self.indices().is_some() {
|
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)]
|
#[derive(Clone, Debug, EnumVariantMeta)]
|
||||||
pub enum VertexAttributeValues {
|
pub enum VertexAttributeValues {
|
||||||
Float32(Vec<f32>),
|
Float32(Vec<f32>),
|
||||||
|
@ -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 {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.len() == 0
|
self.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the values as float triples if possible.
|
||||||
fn as_float3(&self) -> Option<&[[f32; 3]]> {
|
fn as_float3(&self) -> Option<&[[f32; 3]]> {
|
||||||
match self {
|
match self {
|
||||||
VertexAttributeValues::Float32x3(values) => Some(values),
|
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.
|
/// It describes the order in which the vertex attributes should be joined into faces.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -512,6 +534,7 @@ pub enum Indices {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Indices {
|
impl Indices {
|
||||||
|
/// Returns an iterator over the indices.
|
||||||
fn iter(&self) -> impl Iterator<Item = usize> + '_ {
|
fn iter(&self) -> impl Iterator<Item = usize> + '_ {
|
||||||
match self {
|
match self {
|
||||||
Indices::U16(vec) => IndicesIter::U16(vec.iter()),
|
Indices::U16(vec) => IndicesIter::U16(vec.iter()),
|
||||||
|
@ -519,6 +542,7 @@ impl Indices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the number of indices.
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Indices::U16(vec) => vec.len(),
|
Indices::U16(vec) => vec.len(),
|
||||||
|
@ -526,6 +550,7 @@ impl Indices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if there are no indices.
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Indices::U16(vec) => vec.is_empty(),
|
Indices::U16(vec) => vec.is_empty(),
|
||||||
|
@ -533,10 +558,13 @@ impl Indices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An Iterator for the [`Indices`].
|
||||||
enum IndicesIter<'a> {
|
enum IndicesIter<'a> {
|
||||||
U16(std::slice::Iter<'a, u16>),
|
U16(std::slice::Iter<'a, u16>),
|
||||||
U32(std::slice::Iter<'a, u32>),
|
U32(std::slice::Iter<'a, u32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for IndicesIter<'_> {
|
impl Iterator for IndicesIter<'_> {
|
||||||
type Item = usize;
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GpuMesh {
|
pub struct GpuMesh {
|
||||||
|
/// Contains all attribute data for each vertex.
|
||||||
pub vertex_buffer: Buffer,
|
pub vertex_buffer: Buffer,
|
||||||
pub index_info: Option<GpuIndexInfo>,
|
pub index_info: Option<GpuIndexInfo>,
|
||||||
pub has_tangents: bool,
|
pub has_tangents: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The index info of a [`GpuMesh`].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GpuIndexInfo {
|
pub struct GpuIndexInfo {
|
||||||
|
/// Contains all index data of a mesh.
|
||||||
pub buffer: Buffer,
|
pub buffer: Buffer,
|
||||||
pub count: u32,
|
pub count: u32,
|
||||||
pub index_format: IndexFormat,
|
pub index_format: IndexFormat,
|
||||||
|
@ -576,10 +609,12 @@ impl RenderAsset for Mesh {
|
||||||
type PreparedAsset = GpuMesh;
|
type PreparedAsset = GpuMesh;
|
||||||
type Param = SRes<RenderDevice>;
|
type Param = SRes<RenderDevice>;
|
||||||
|
|
||||||
|
/// Clones the mesh.
|
||||||
fn extract_asset(&self) -> Self::ExtractedAsset {
|
fn extract_asset(&self) -> Self::ExtractedAsset {
|
||||||
self.clone()
|
self.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts the extracted mesh a into [`GpuMesh`].
|
||||||
fn prepare_asset(
|
fn prepare_asset(
|
||||||
mesh: Self::ExtractedAsset,
|
mesh: Self::ExtractedAsset,
|
||||||
render_device: &mut SystemParamItem<Self::Param>,
|
render_device: &mut SystemParamItem<Self::Param>,
|
||||||
|
|
|
@ -9,6 +9,7 @@ use crate::render_asset::RenderAssetPlugin;
|
||||||
use bevy_app::{App, Plugin};
|
use bevy_app::{App, Plugin};
|
||||||
use bevy_asset::AddAsset;
|
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;
|
pub struct MeshPlugin;
|
||||||
|
|
||||||
impl Plugin for MeshPlugin {
|
impl Plugin for MeshPlugin {
|
||||||
|
|
|
@ -24,6 +24,7 @@ impl From<Cube> for Mesh {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An axis-aligned box defined by its minimum and maximum point.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct Box {
|
pub struct Box {
|
||||||
pub min_x: f32,
|
pub min_x: f32,
|
||||||
|
@ -37,6 +38,7 @@ pub struct Box {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
pub fn new(x_length: f32, y_length: f32, z_length: f32) -> Box {
|
||||||
Box {
|
Box {
|
||||||
max_x: x_length / 2.0,
|
max_x: x_length / 2.0,
|
||||||
|
@ -118,7 +120,7 @@ impl From<Box> for Mesh {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A rectangle on the XY plane.
|
/// A rectangle on the XY plane centered at the origin.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct Quad {
|
pub struct Quad {
|
||||||
/// Full width and height of the rectangle.
|
/// Full width and height of the rectangle.
|
||||||
|
@ -220,7 +222,7 @@ impl From<Quad> for Mesh {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A square on the XZ plane.
|
/// A square on the XZ plane centered at the origin.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct Plane {
|
pub struct Plane {
|
||||||
/// The total side length of the square.
|
/// The total side length of the square.
|
||||||
|
|
|
@ -3,7 +3,7 @@ use wgpu::PrimitiveTopology;
|
||||||
use crate::mesh::{Indices, Mesh};
|
use crate::mesh::{Indices, Mesh};
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
/// A sphere made of sectors and stacks
|
/// A sphere made of sectors and stacks.
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct UVSphere {
|
pub struct UVSphere {
|
||||||
|
|
|
@ -12,18 +12,38 @@ pub enum PrepareAssetError<E: Send + Sync + 'static> {
|
||||||
RetryNextUpdate(E),
|
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 {
|
pub trait RenderAsset: Asset {
|
||||||
|
/// The representation of the the asset in the "render world".
|
||||||
type ExtractedAsset: Send + Sync + 'static;
|
type ExtractedAsset: Send + Sync + 'static;
|
||||||
|
/// The GPU-representation of the the asset.
|
||||||
type PreparedAsset: Send + Sync + 'static;
|
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;
|
type Param: SystemParam;
|
||||||
|
/// Converts the asset into a [`RenderAsset::ExtractedAsset`].
|
||||||
fn extract_asset(&self) -> Self::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(
|
fn prepare_asset(
|
||||||
extracted_asset: Self::ExtractedAsset,
|
extracted_asset: Self::ExtractedAsset,
|
||||||
param: &mut SystemParamItem<Self::Param>,
|
param: &mut SystemParamItem<Self::Param>,
|
||||||
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>>;
|
) -> Result<Self::PreparedAsset, PrepareAssetError<Self::ExtractedAsset>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<A: RenderAsset>(PhantomData<fn() -> A>);
|
pub struct RenderAssetPlugin<A: RenderAsset>(PhantomData<fn() -> A>);
|
||||||
|
|
||||||
impl<A: RenderAsset> Default for RenderAssetPlugin<A> {
|
impl<A: RenderAsset> Default for RenderAssetPlugin<A> {
|
||||||
|
@ -45,6 +65,7 @@ impl<A: RenderAsset> Plugin for RenderAssetPlugin<A> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Temporarily stores the extracted and removed assets of the current frame.
|
||||||
pub struct ExtractedAssets<A: RenderAsset> {
|
pub struct ExtractedAssets<A: RenderAsset> {
|
||||||
extracted: Vec<(Handle<A>, A::ExtractedAsset)>,
|
extracted: Vec<(Handle<A>, A::ExtractedAsset)>,
|
||||||
removed: Vec<Handle<A>>,
|
removed: Vec<Handle<A>>,
|
||||||
|
@ -59,8 +80,12 @@ impl<A: RenderAsset> Default for ExtractedAssets<A> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stores all GPU representations ([`RenderAsset::PreparedAssets`](RenderAsset::PreparedAsset))
|
||||||
|
/// of [`RenderAssets`](RenderAsset) as long as they exist.
|
||||||
pub type RenderAssets<A> = HashMap<Handle<A>, <A as RenderAsset>::PreparedAsset>;
|
pub type RenderAssets<A> = HashMap<Handle<A>, <A as RenderAsset>::PreparedAsset>;
|
||||||
|
|
||||||
|
/// This system extracts all crated or modified assets of the corresponding [`RenderAsset`] type
|
||||||
|
/// into the "render world".
|
||||||
fn extract_render_asset<A: RenderAsset>(
|
fn extract_render_asset<A: RenderAsset>(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut events: EventReader<AssetEvent<A>>,
|
mut events: EventReader<AssetEvent<A>>,
|
||||||
|
@ -97,6 +122,7 @@ fn extract_render_asset<A: RenderAsset>(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Specifies all ECS data required by [`PrepareAssetSystem`].
|
||||||
pub type RenderAssetParams<R> = (
|
pub type RenderAssetParams<R> = (
|
||||||
SResMut<ExtractedAssets<R>>,
|
SResMut<ExtractedAssets<R>>,
|
||||||
SResMut<RenderAssets<R>>,
|
SResMut<RenderAssets<R>>,
|
||||||
|
@ -105,6 +131,7 @@ pub type RenderAssetParams<R> = (
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: consider storing inside system?
|
// TODO: consider storing inside system?
|
||||||
|
/// All assets that should be prepared next frame.
|
||||||
pub struct PrepareNextFrameAssets<A: RenderAsset> {
|
pub struct PrepareNextFrameAssets<A: RenderAsset> {
|
||||||
assets: Vec<(Handle<A>, A::ExtractedAsset)>,
|
assets: Vec<(Handle<A>, A::ExtractedAsset)>,
|
||||||
}
|
}
|
||||||
|
@ -117,10 +144,13 @@ impl<A: RenderAsset> Default for PrepareNextFrameAssets<A> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This system prepares all assets of the corresponding [`RenderAsset`] type
|
||||||
|
/// which where extracted this frame for the GPU.
|
||||||
pub struct PrepareAssetSystem<R: RenderAsset>(PhantomData<R>);
|
pub struct PrepareAssetSystem<R: RenderAsset>(PhantomData<R>);
|
||||||
|
|
||||||
impl<R: RenderAsset> RunSystem for PrepareAssetSystem<R> {
|
impl<R: RenderAsset> RunSystem for PrepareAssetSystem<R> {
|
||||||
type Param = RenderAssetParams<R>;
|
type Param = RenderAssetParams<R>;
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
(mut extracted_assets, mut render_assets, mut prepare_next_frame, mut param): SystemParamItem<Self::Param>,
|
(mut extracted_assets, mut render_assets, mut prepare_next_frame, mut param): SystemParamItem<Self::Param>,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ use bevy_ecs::{
|
||||||
use crevice::std140::AsStd140;
|
use crevice::std140::AsStd140;
|
||||||
use std::{marker::PhantomData, ops::Deref};
|
use std::{marker::PhantomData, ops::Deref};
|
||||||
|
|
||||||
|
/// Stores the index of a uniform inside of [`ComponentUniforms`].
|
||||||
pub struct DynamicUniformIndex<C: Component> {
|
pub struct DynamicUniformIndex<C: Component> {
|
||||||
index: u32,
|
index: u32,
|
||||||
marker: PhantomData<C>,
|
marker: PhantomData<C>,
|
||||||
|
@ -29,13 +30,28 @@ impl<C: Component> DynamicUniformIndex<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {
|
pub trait ExtractComponent: Component {
|
||||||
|
/// ECS [`WorldQuery`] to fetch the components to extract.
|
||||||
type Query: WorldQuery;
|
type Query: WorldQuery;
|
||||||
|
/// Filters the entities with additional constraints.
|
||||||
type Filter: WorldQuery;
|
type Filter: WorldQuery;
|
||||||
|
/// Defines how the component is transferred into the "render world".
|
||||||
fn extract_component(item: QueryItem<Self::Query>) -> Self;
|
fn extract_component(item: QueryItem<Self::Query>) -> 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<C>(PhantomData<fn() -> C>);
|
pub struct UniformComponentPlugin<C>(PhantomData<fn() -> C>);
|
||||||
|
|
||||||
impl<C> Default for UniformComponentPlugin<C> {
|
impl<C> Default for UniformComponentPlugin<C> {
|
||||||
|
@ -55,6 +71,7 @@ impl<C: Component + AsStd140 + Clone> Plugin for UniformComponentPlugin<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stores all uniforms of the component type.
|
||||||
pub struct ComponentUniforms<C: Component + AsStd140> {
|
pub struct ComponentUniforms<C: Component + AsStd140> {
|
||||||
uniforms: DynamicUniformVec<C>,
|
uniforms: DynamicUniformVec<C>,
|
||||||
}
|
}
|
||||||
|
@ -83,6 +100,8 @@ impl<C: Component + AsStd140> Default for ComponentUniforms<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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<C: Component>(
|
fn prepare_uniform_components<C: Component>(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
|
@ -107,6 +126,10 @@ fn prepare_uniform_components<C: Component>(
|
||||||
.write_buffer(&render_device, &render_queue);
|
.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<C, F = ()>(PhantomData<fn() -> (C, F)>);
|
pub struct ExtractComponentPlugin<C, F = ()>(PhantomData<fn() -> (C, F)>);
|
||||||
|
|
||||||
impl<C, F> Default for ExtractComponentPlugin<C, F> {
|
impl<C, F> Default for ExtractComponentPlugin<C, F> {
|
||||||
|
@ -137,6 +160,7 @@ impl<T: Asset> ExtractComponent for Handle<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This system extracts all components of the corresponding [`ExtractComponent`] type.
|
||||||
pub struct ExtractComponentSystem<C: ExtractComponent>(PhantomData<C>);
|
pub struct ExtractComponentSystem<C: ExtractComponent>(PhantomData<C>);
|
||||||
|
|
||||||
impl<C: ExtractComponent> RunSystem for ExtractComponentSystem<C>
|
impl<C: ExtractComponent> RunSystem for ExtractComponentSystem<C>
|
||||||
|
@ -146,6 +170,7 @@ where
|
||||||
{
|
{
|
||||||
type Param = (
|
type Param = (
|
||||||
SCommands,
|
SCommands,
|
||||||
|
// the previous amount of extracted components
|
||||||
Local<'static, usize>,
|
Local<'static, usize>,
|
||||||
SQuery<(Entity, C::Query), C::Filter>,
|
SQuery<(Entity, C::Query), C::Filter>,
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,11 +6,21 @@ use bevy_ecs::entity::Entity;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use thiserror::Error;
|
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 struct RunSubGraph {
|
||||||
pub name: Cow<'static, str>,
|
pub name: Cow<'static, str>,
|
||||||
pub inputs: Vec<SlotValue>,
|
pub inputs: Vec<SlotValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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> {
|
pub struct RenderGraphContext<'a> {
|
||||||
graph: &'a RenderGraph,
|
graph: &'a RenderGraph,
|
||||||
node: &'a NodeState,
|
node: &'a NodeState,
|
||||||
|
@ -20,6 +30,7 @@ pub struct RenderGraphContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RenderGraphContext<'a> {
|
impl<'a> RenderGraphContext<'a> {
|
||||||
|
/// Creates a new render graph context for the `node`.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
graph: &'a RenderGraph,
|
graph: &'a RenderGraph,
|
||||||
node: &'a NodeState,
|
node: &'a NodeState,
|
||||||
|
@ -35,19 +46,23 @@ impl<'a> RenderGraphContext<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the input slot values for the node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn inputs(&self) -> &[SlotValue] {
|
pub fn inputs(&self) -> &[SlotValue] {
|
||||||
self.inputs
|
self.inputs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the [`SlotInfos`] of the inputs.
|
||||||
pub fn input_info(&self) -> &SlotInfos {
|
pub fn input_info(&self) -> &SlotInfos {
|
||||||
&self.node.input_slots
|
&self.node.input_slots
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the [`SlotInfos`] of the outputs.
|
||||||
pub fn output_info(&self) -> &SlotInfos {
|
pub fn output_info(&self) -> &SlotInfos {
|
||||||
&self.node.output_slots
|
&self.node.output_slots
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the input slot value referenced by the `label`.
|
||||||
pub fn get_input(&self, label: impl Into<SlotLabel>) -> Result<&SlotValue, InputSlotError> {
|
pub fn get_input(&self, label: impl Into<SlotLabel>) -> Result<&SlotValue, InputSlotError> {
|
||||||
let label = label.into();
|
let label = label.into();
|
||||||
let index = self
|
let index = self
|
||||||
|
@ -58,6 +73,7 @@ impl<'a> RenderGraphContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should this return an Arc or a reference?
|
// 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(
|
pub fn get_input_texture(
|
||||||
&self,
|
&self,
|
||||||
label: impl Into<SlotLabel>,
|
label: impl Into<SlotLabel>,
|
||||||
|
@ -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(
|
pub fn get_input_sampler(
|
||||||
&self,
|
&self,
|
||||||
label: impl Into<SlotLabel>,
|
label: impl Into<SlotLabel>,
|
||||||
|
@ -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<SlotLabel>) -> Result<&Buffer, InputSlotError> {
|
pub fn get_input_buffer(&self, label: impl Into<SlotLabel>) -> Result<&Buffer, InputSlotError> {
|
||||||
let label = label.into();
|
let label = label.into();
|
||||||
match self.get_input(label.clone())? {
|
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<SlotLabel>) -> Result<Entity, InputSlotError> {
|
pub fn get_input_entity(&self, label: impl Into<SlotLabel>) -> Result<Entity, InputSlotError> {
|
||||||
let label = label.into();
|
let label = label.into();
|
||||||
match self.get_input(label.clone())? {
|
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(
|
pub fn set_output(
|
||||||
&mut self,
|
&mut self,
|
||||||
label: impl Into<SlotLabel>,
|
label: impl Into<SlotLabel>,
|
||||||
|
@ -138,6 +158,7 @@ impl<'a> RenderGraphContext<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Queues up a sub graph for execution after the node has finished running.
|
||||||
pub fn run_sub_graph(
|
pub fn run_sub_graph(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<Cow<'static, str>>,
|
name: impl Into<Cow<'static, str>>,
|
||||||
|
@ -177,6 +198,8 @@ impl<'a> RenderGraphContext<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finishes the context for this [`Node`](super::Node) by
|
||||||
|
/// returning the sub graphs to run next.
|
||||||
pub fn finish(self) -> Vec<RunSubGraph> {
|
pub fn finish(self) -> Vec<RunSubGraph> {
|
||||||
self.run_sub_graphs
|
self.run_sub_graphs
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,30 @@
|
||||||
use super::NodeId;
|
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)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum Edge {
|
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 {
|
SlotEdge {
|
||||||
input_node: NodeId,
|
input_node: NodeId,
|
||||||
input_index: usize,
|
input_index: usize,
|
||||||
output_node: NodeId,
|
output_node: NodeId,
|
||||||
output_index: usize,
|
output_index: usize,
|
||||||
},
|
},
|
||||||
|
/// An edge describing to ordering of both nodes (`output_node` before `input_node`).
|
||||||
NodeEdge {
|
NodeEdge {
|
||||||
input_node: NodeId,
|
input_node: NodeId,
|
||||||
output_node: NodeId,
|
output_node: NodeId,
|
||||||
|
@ -15,6 +32,7 @@ pub enum Edge {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Edge {
|
impl Edge {
|
||||||
|
/// Returns the id of the 'input_node'.
|
||||||
pub fn get_input_node(&self) -> NodeId {
|
pub fn get_input_node(&self) -> NodeId {
|
||||||
match self {
|
match self {
|
||||||
Edge::SlotEdge { input_node, .. } => *input_node,
|
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 {
|
pub fn get_output_node(&self) -> NodeId {
|
||||||
match self {
|
match self {
|
||||||
Edge::SlotEdge { output_node, .. } => *output_node,
|
Edge::SlotEdge { output_node, .. } => *output_node,
|
||||||
|
|
|
@ -9,6 +9,43 @@ use bevy_ecs::prelude::World;
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
use std::{borrow::Cow, fmt::Debug};
|
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)]
|
#[derive(Default)]
|
||||||
pub struct RenderGraph {
|
pub struct RenderGraph {
|
||||||
nodes: HashMap<NodeId, NodeState>,
|
nodes: HashMap<NodeId, NodeState>,
|
||||||
|
@ -18,8 +55,10 @@ pub struct RenderGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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";
|
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) {
|
pub fn update(&mut self, world: &mut World) {
|
||||||
for node in self.nodes.values_mut() {
|
for node in self.nodes.values_mut() {
|
||||||
node.node.update(world);
|
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<SlotInfo>) -> NodeId {
|
pub fn set_input(&mut self, inputs: Vec<SlotInfo>) -> NodeId {
|
||||||
if self.input_node.is_some() {
|
if self.input_node.is_some() {
|
||||||
panic!("Graph already has an input node");
|
panic!("Graph already has an input node");
|
||||||
|
@ -40,11 +80,14 @@ impl RenderGraph {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the [`NodeState`] of the input node of this graph..
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn input_node(&self) -> Option<&NodeState> {
|
pub fn input_node(&self) -> Option<&NodeState> {
|
||||||
self.input_node.and_then(|id| self.get_node_state(id).ok())
|
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<T>(&mut self, name: impl Into<Cow<'static, str>>, node: T) -> NodeId
|
pub fn add_node<T>(&mut self, name: impl Into<Cow<'static, str>>, node: T) -> NodeId
|
||||||
where
|
where
|
||||||
T: Node,
|
T: Node,
|
||||||
|
@ -58,6 +101,7 @@ impl RenderGraph {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the [`NodeState`] referenced by the `label`.
|
||||||
pub fn get_node_state(
|
pub fn get_node_state(
|
||||||
&self,
|
&self,
|
||||||
label: impl Into<NodeLabel>,
|
label: impl Into<NodeLabel>,
|
||||||
|
@ -69,6 +113,7 @@ impl RenderGraph {
|
||||||
.ok_or(RenderGraphError::InvalidNode(label))
|
.ok_or(RenderGraphError::InvalidNode(label))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the [`NodeState`] referenced by the `label` mutably.
|
||||||
pub fn get_node_state_mut(
|
pub fn get_node_state_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
label: impl Into<NodeLabel>,
|
label: impl Into<NodeLabel>,
|
||||||
|
@ -80,6 +125,7 @@ impl RenderGraph {
|
||||||
.ok_or(RenderGraphError::InvalidNode(label))
|
.ok_or(RenderGraphError::InvalidNode(label))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the [`NodeId`] referenced by the `label`.
|
||||||
pub fn get_node_id(&self, label: impl Into<NodeLabel>) -> Result<NodeId, RenderGraphError> {
|
pub fn get_node_id(&self, label: impl Into<NodeLabel>) -> Result<NodeId, RenderGraphError> {
|
||||||
let label = label.into();
|
let label = label.into();
|
||||||
match label {
|
match label {
|
||||||
|
@ -92,6 +138,7 @@ impl RenderGraph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the [`Node`] referenced by the `label`.
|
||||||
pub fn get_node<T>(&self, label: impl Into<NodeLabel>) -> Result<&T, RenderGraphError>
|
pub fn get_node<T>(&self, label: impl Into<NodeLabel>) -> Result<&T, RenderGraphError>
|
||||||
where
|
where
|
||||||
T: Node,
|
T: Node,
|
||||||
|
@ -99,6 +146,7 @@ impl RenderGraph {
|
||||||
self.get_node_state(label).and_then(|n| n.node())
|
self.get_node_state(label).and_then(|n| n.node())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the [`Node`] referenced by the `label` mutably.
|
||||||
pub fn get_node_mut<T>(
|
pub fn get_node_mut<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
label: impl Into<NodeLabel>,
|
label: impl Into<NodeLabel>,
|
||||||
|
@ -109,6 +157,8 @@ impl RenderGraph {
|
||||||
self.get_node_state_mut(label).and_then(|n| n.node_mut())
|
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(
|
pub fn add_slot_edge(
|
||||||
&mut self,
|
&mut self,
|
||||||
output_node: impl Into<NodeLabel>,
|
output_node: impl Into<NodeLabel>,
|
||||||
|
@ -151,6 +201,8 @@ impl RenderGraph {
|
||||||
Ok(())
|
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(
|
pub fn add_node_edge(
|
||||||
&mut self,
|
&mut self,
|
||||||
output_node: impl Into<NodeLabel>,
|
output_node: impl Into<NodeLabel>,
|
||||||
|
@ -176,6 +228,8 @@ impl RenderGraph {
|
||||||
Ok(())
|
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> {
|
pub fn validate_edge(&mut self, edge: &Edge) -> Result<(), RenderGraphError> {
|
||||||
if self.has_edge(edge) {
|
if self.has_edge(edge) {
|
||||||
return Err(RenderGraphError::EdgeAlreadyExists(edge.clone()));
|
return Err(RenderGraphError::EdgeAlreadyExists(edge.clone()));
|
||||||
|
@ -237,6 +291,7 @@ impl RenderGraph {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether the `edge` already exists in the graph.
|
||||||
pub fn has_edge(&self, edge: &Edge) -> bool {
|
pub fn has_edge(&self, edge: &Edge) -> bool {
|
||||||
let output_node_state = self.get_node_state(edge.get_output_node());
|
let output_node_state = self.get_node_state(edge.get_output_node());
|
||||||
let input_node_state = self.get_node_state(edge.get_input_node());
|
let input_node_state = self.get_node_state(edge.get_input_node());
|
||||||
|
@ -253,26 +308,32 @@ impl RenderGraph {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the [`NodeStates`](NodeState).
|
||||||
pub fn iter_nodes(&self) -> impl Iterator<Item = &NodeState> {
|
pub fn iter_nodes(&self) -> impl Iterator<Item = &NodeState> {
|
||||||
self.nodes.values()
|
self.nodes.values()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the [`NodeStates`](NodeState), that allows modifying each value.
|
||||||
pub fn iter_nodes_mut(&mut self) -> impl Iterator<Item = &mut NodeState> {
|
pub fn iter_nodes_mut(&mut self) -> impl Iterator<Item = &mut NodeState> {
|
||||||
self.nodes.values_mut()
|
self.nodes.values_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the sub graphs.
|
||||||
pub fn iter_sub_graphs(&self) -> impl Iterator<Item = (&str, &RenderGraph)> {
|
pub fn iter_sub_graphs(&self) -> impl Iterator<Item = (&str, &RenderGraph)> {
|
||||||
self.sub_graphs
|
self.sub_graphs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, graph)| (name.as_ref(), graph))
|
.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<Item = (&str, &mut RenderGraph)> {
|
pub fn iter_sub_graphs_mut(&mut self) -> impl Iterator<Item = (&str, &mut RenderGraph)> {
|
||||||
self.sub_graphs
|
self.sub_graphs
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|(name, graph)| (name.as_ref(), graph))
|
.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(
|
pub fn iter_node_inputs(
|
||||||
&self,
|
&self,
|
||||||
label: impl Into<NodeLabel>,
|
label: impl Into<NodeLabel>,
|
||||||
|
@ -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(
|
pub fn iter_node_outputs(
|
||||||
&self,
|
&self,
|
||||||
label: impl Into<NodeLabel>,
|
label: impl Into<NodeLabel>,
|
||||||
|
@ -301,14 +364,18 @@ impl RenderGraph {
|
||||||
.map(move |(edge, input_node_id)| (edge, self.get_node_state(input_node_id).unwrap())))
|
.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<Cow<'static, str>>, sub_graph: RenderGraph) {
|
pub fn add_sub_graph(&mut self, name: impl Into<Cow<'static, str>>, sub_graph: RenderGraph) {
|
||||||
self.sub_graphs.insert(name.into(), sub_graph);
|
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<str>) -> Option<&RenderGraph> {
|
pub fn get_sub_graph(&self, name: impl AsRef<str>) -> Option<&RenderGraph> {
|
||||||
self.sub_graphs.get(name.as_ref())
|
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<str>) -> Option<&mut RenderGraph> {
|
pub fn get_sub_graph_mut(&mut self, name: impl AsRef<str>) -> Option<&mut RenderGraph> {
|
||||||
self.sub_graphs.get_mut(name.as_ref())
|
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 {
|
pub struct GraphInputNode {
|
||||||
inputs: Vec<SlotInfo>,
|
inputs: Vec<SlotInfo>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,10 @@ use downcast_rs::{impl_downcast, Downcast};
|
||||||
use std::{borrow::Cow, fmt::Debug};
|
use std::{borrow::Cow, fmt::Debug};
|
||||||
use thiserror::Error;
|
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)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
pub struct NodeId(Uuid);
|
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 {
|
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<SlotInfo> {
|
fn input(&self) -> Vec<SlotInfo> {
|
||||||
Vec::new()
|
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<SlotInfo> {
|
fn output(&self) -> Vec<SlotInfo> {
|
||||||
Vec::new()
|
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) {}
|
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(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
graph: &mut RenderGraphContext,
|
graph: &mut RenderGraphContext,
|
||||||
|
@ -58,6 +80,7 @@ pub enum NodeRunError {
|
||||||
RunSubGraphError(#[from] RunSubGraphError),
|
RunSubGraphError(#[from] RunSubGraphError),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A collection of input and output [`Edges`](Edge) for a [`Node`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Edges {
|
pub struct Edges {
|
||||||
pub id: NodeId,
|
pub id: NodeId,
|
||||||
|
@ -66,6 +89,7 @@ pub struct Edges {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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> {
|
pub(crate) fn add_input_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> {
|
||||||
if self.has_input_edge(&edge) {
|
if self.has_input_edge(&edge) {
|
||||||
return Err(RenderGraphError::EdgeAlreadyExists(edge));
|
return Err(RenderGraphError::EdgeAlreadyExists(edge));
|
||||||
|
@ -74,6 +98,7 @@ impl Edges {
|
||||||
Ok(())
|
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> {
|
pub(crate) fn add_output_edge(&mut self, edge: Edge) -> Result<(), RenderGraphError> {
|
||||||
if self.has_output_edge(&edge) {
|
if self.has_output_edge(&edge) {
|
||||||
return Err(RenderGraphError::EdgeAlreadyExists(edge));
|
return Err(RenderGraphError::EdgeAlreadyExists(edge));
|
||||||
|
@ -82,14 +107,18 @@ impl Edges {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether the input edge already exists.
|
||||||
pub fn has_input_edge(&self, edge: &Edge) -> bool {
|
pub fn has_input_edge(&self, edge: &Edge) -> bool {
|
||||||
self.input_edges.contains(edge)
|
self.input_edges.contains(edge)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks whether the output edge already exists.
|
||||||
pub fn has_output_edge(&self, edge: &Edge) -> bool {
|
pub fn has_output_edge(&self, edge: &Edge) -> bool {
|
||||||
self.output_edges.contains(edge)
|
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> {
|
pub fn get_input_slot_edge(&self, index: usize) -> Result<&Edge, RenderGraphError> {
|
||||||
self.input_edges
|
self.input_edges
|
||||||
.iter()
|
.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> {
|
pub fn get_output_slot_edge(&self, index: usize) -> Result<&Edge, RenderGraphError> {
|
||||||
self.output_edges
|
self.output_edges
|
||||||
.iter()
|
.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 struct NodeState {
|
||||||
pub id: NodeId,
|
pub id: NodeId,
|
||||||
pub name: Option<Cow<'static, str>>,
|
pub name: Option<Cow<'static, str>>,
|
||||||
|
/// The name of the type that implements [`Node`].
|
||||||
pub type_name: &'static str,
|
pub type_name: &'static str,
|
||||||
pub node: Box<dyn Node>,
|
pub node: Box<dyn Node>,
|
||||||
pub input_slots: SlotInfos,
|
pub input_slots: SlotInfos,
|
||||||
|
@ -140,6 +176,8 @@ impl Debug for NodeState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeState {
|
impl NodeState {
|
||||||
|
/// Creates an [`NodeState`] without edges, but the `input_slots` and `output_slots`
|
||||||
|
/// are provided by the `node`.
|
||||||
pub fn new<T>(id: NodeId, node: T) -> Self
|
pub fn new<T>(id: NodeId, node: T) -> Self
|
||||||
where
|
where
|
||||||
T: Node,
|
T: Node,
|
||||||
|
@ -159,6 +197,7 @@ impl NodeState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the [`Node`].
|
||||||
pub fn node<T>(&self) -> Result<&T, RenderGraphError>
|
pub fn node<T>(&self) -> Result<&T, RenderGraphError>
|
||||||
where
|
where
|
||||||
T: Node,
|
T: Node,
|
||||||
|
@ -168,6 +207,7 @@ impl NodeState {
|
||||||
.ok_or(RenderGraphError::WrongNodeType)
|
.ok_or(RenderGraphError::WrongNodeType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the [`Node`] mutably.
|
||||||
pub fn node_mut<T>(&mut self) -> Result<&mut T, RenderGraphError>
|
pub fn node_mut<T>(&mut self) -> Result<&mut T, RenderGraphError>
|
||||||
where
|
where
|
||||||
T: Node,
|
T: Node,
|
||||||
|
@ -177,14 +217,7 @@ impl NodeState {
|
||||||
.ok_or(RenderGraphError::WrongNodeType)
|
.ok_or(RenderGraphError::WrongNodeType)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_output_slots(&self) -> Result<(), RenderGraphError> {
|
/// Validates that each input slot corresponds to an input edge.
|
||||||
for i in 0..self.output_slots.len() {
|
|
||||||
self.edges.get_output_slot_edge(i)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn validate_input_slots(&self) -> Result<(), RenderGraphError> {
|
pub fn validate_input_slots(&self) -> Result<(), RenderGraphError> {
|
||||||
for i in 0..self.input_slots.len() {
|
for i in 0..self.input_slots.len() {
|
||||||
self.edges.get_input_slot_edge(i)?;
|
self.edges.get_input_slot_edge(i)?;
|
||||||
|
@ -192,8 +225,19 @@ impl NodeState {
|
||||||
|
|
||||||
Ok(())
|
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)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum NodeLabel {
|
pub enum NodeLabel {
|
||||||
Id(NodeId),
|
Id(NodeId),
|
||||||
|
@ -223,6 +267,10 @@ impl From<NodeId> for NodeLabel {
|
||||||
NodeLabel::Id(value)
|
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;
|
pub struct EmptyNode;
|
||||||
|
|
||||||
impl Node for EmptyNode {
|
impl Node for EmptyNode {
|
||||||
|
|
|
@ -3,15 +3,27 @@ use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::render_resource::{Buffer, Sampler, TextureView};
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum SlotValue {
|
pub enum SlotValue {
|
||||||
|
/// A GPU-accessible [`Buffer`].
|
||||||
Buffer(Buffer),
|
Buffer(Buffer),
|
||||||
|
/// A [`TextureView`] describes a texture used in a pipeline.
|
||||||
TextureView(TextureView),
|
TextureView(TextureView),
|
||||||
|
/// A texture [`Sampler`] defines how a pipeline will sample from a [`TextureView`].
|
||||||
Sampler(Sampler),
|
Sampler(Sampler),
|
||||||
|
/// An entity from the ECS.
|
||||||
Entity(Entity),
|
Entity(Entity),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SlotValue {
|
impl SlotValue {
|
||||||
|
/// Returns the [`SlotType`] of this value.
|
||||||
pub fn slot_type(&self) -> SlotType {
|
pub fn slot_type(&self) -> SlotType {
|
||||||
match self {
|
match self {
|
||||||
SlotValue::Buffer(_) => SlotType::Buffer,
|
SlotValue::Buffer(_) => SlotType::Buffer,
|
||||||
|
@ -46,14 +58,24 @@ impl From<Entity> 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)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub enum SlotType {
|
pub enum SlotType {
|
||||||
|
/// 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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`SlotLabel`] is used to reference a slot by either its name or index
|
||||||
|
/// inside the [`RenderGraph`](super::RenderGraph).
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub enum SlotLabel {
|
pub enum SlotLabel {
|
||||||
Index(usize),
|
Index(usize),
|
||||||
|
@ -90,6 +112,7 @@ impl From<usize> for SlotLabel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The internal representation of a slot, which specifies its [`SlotType`] and name.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SlotInfo {
|
pub struct SlotInfo {
|
||||||
pub name: Cow<'static, str>,
|
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)]
|
#[derive(Default, Debug)]
|
||||||
pub struct SlotInfos {
|
pub struct SlotInfos {
|
||||||
slots: Vec<SlotInfo>,
|
slots: Vec<SlotInfo>,
|
||||||
|
@ -119,28 +144,33 @@ impl<T: IntoIterator<Item = SlotInfo>> From<T> for SlotInfos {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SlotInfos {
|
impl SlotInfos {
|
||||||
|
/// Returns the count of slots.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.slots.len()
|
self.slots.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if there are no slots.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.slots.is_empty()
|
self.slots.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the [`SlotInfo`] for the provided label.
|
||||||
pub fn get_slot(&self, label: impl Into<SlotLabel>) -> Option<&SlotInfo> {
|
pub fn get_slot(&self, label: impl Into<SlotLabel>) -> Option<&SlotInfo> {
|
||||||
let label = label.into();
|
let label = label.into();
|
||||||
let index = self.get_slot_index(&label)?;
|
let index = self.get_slot_index(&label)?;
|
||||||
self.slots.get(index)
|
self.slots.get(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the [`SlotInfo`] for the provided label mutably.
|
||||||
pub fn get_slot_mut(&mut self, label: impl Into<SlotLabel>) -> Option<&mut SlotInfo> {
|
pub fn get_slot_mut(&mut self, label: impl Into<SlotLabel>) -> Option<&mut SlotInfo> {
|
||||||
let label = label.into();
|
let label = label.into();
|
||||||
let index = self.get_slot_index(&label)?;
|
let index = self.get_slot_index(&label)?;
|
||||||
self.slots.get_mut(index)
|
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<SlotLabel>) -> Option<usize> {
|
pub fn get_slot_index(&self, label: impl Into<SlotLabel>) -> Option<usize> {
|
||||||
let label = label.into();
|
let label = label.into();
|
||||||
match label {
|
match label {
|
||||||
|
@ -154,6 +184,7 @@ impl SlotInfos {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator over the slot infos.
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &SlotInfo> {
|
pub fn iter(&self) -> impl Iterator<Item = &SlotInfo> {
|
||||||
self.slots.iter()
|
self.slots.iter()
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,12 @@ use bevy_utils::HashMap;
|
||||||
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use std::{any::TypeId, fmt::Debug, hash::Hash};
|
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<P: PhaseItem>: Send + Sync + 'static {
|
pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
|
||||||
|
/// Draws the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
|
||||||
fn draw<'w>(
|
fn draw<'w>(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: &'w World,
|
world: &'w World,
|
||||||
|
@ -25,26 +30,39 @@ pub trait Draw<P: PhaseItem>: 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 {
|
pub trait PhaseItem: Send + Sync + 'static {
|
||||||
|
/// The type used for ordering the items. The smallest values are drawn first.
|
||||||
type SortKey: Ord;
|
type SortKey: Ord;
|
||||||
|
/// Determines the order in which the items are drawn during the corresponding [`RenderPhase`].
|
||||||
fn sort_key(&self) -> Self::SortKey;
|
fn sort_key(&self) -> Self::SortKey;
|
||||||
|
/// Specifies the [`Draw`] function used to render the item.
|
||||||
fn draw_function(&self) -> DrawFunctionId;
|
fn draw_function(&self) -> DrawFunctionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make this generic?
|
// TODO: make this generic?
|
||||||
|
/// /// A [`Draw`] function identifier.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||||
pub struct DrawFunctionId(usize);
|
pub struct DrawFunctionId(usize);
|
||||||
|
|
||||||
|
/// Stores all draw functions for the [`PhaseItem`] type.
|
||||||
|
/// For retrieval they are associated with their [`TypeId`].
|
||||||
pub struct DrawFunctionsInternal<P: PhaseItem> {
|
pub struct DrawFunctionsInternal<P: PhaseItem> {
|
||||||
pub draw_functions: Vec<Box<dyn Draw<P>>>,
|
pub draw_functions: Vec<Box<dyn Draw<P>>>,
|
||||||
pub indices: HashMap<TypeId, DrawFunctionId>,
|
pub indices: HashMap<TypeId, DrawFunctionId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: PhaseItem> DrawFunctionsInternal<P> {
|
impl<P: PhaseItem> DrawFunctionsInternal<P> {
|
||||||
|
/// Adds the [`Draw`] function and associates it to its own type.
|
||||||
pub fn add<T: Draw<P>>(&mut self, draw_function: T) -> DrawFunctionId {
|
pub fn add<T: Draw<P>>(&mut self, draw_function: T) -> DrawFunctionId {
|
||||||
self.add_with::<T, T>(draw_function)
|
self.add_with::<T, T>(draw_function)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds the [`Draw`] function and associates it to the type `T`
|
||||||
pub fn add_with<T: 'static, D: Draw<P>>(&mut self, draw_function: D) -> DrawFunctionId {
|
pub fn add_with<T: 'static, D: Draw<P>>(&mut self, draw_function: D) -> DrawFunctionId {
|
||||||
self.draw_functions.push(Box::new(draw_function));
|
self.draw_functions.push(Box::new(draw_function));
|
||||||
let id = DrawFunctionId(self.draw_functions.len() - 1);
|
let id = DrawFunctionId(self.draw_functions.len() - 1);
|
||||||
|
@ -52,15 +70,19 @@ impl<P: PhaseItem> DrawFunctionsInternal<P> {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the [`Draw`] function corresponding to the `id` mutably.
|
||||||
pub fn get_mut(&mut self, id: DrawFunctionId) -> Option<&mut dyn Draw<P>> {
|
pub fn get_mut(&mut self, id: DrawFunctionId) -> Option<&mut dyn Draw<P>> {
|
||||||
self.draw_functions.get_mut(id.0).map(|f| &mut **f)
|
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<T: 'static>(&self) -> Option<DrawFunctionId> {
|
pub fn get_id<T: 'static>(&self) -> Option<DrawFunctionId> {
|
||||||
self.indices.get(&TypeId::of::<T>()).copied()
|
self.indices.get(&TypeId::of::<T>()).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<P: PhaseItem> {
|
pub struct DrawFunctions<P: PhaseItem> {
|
||||||
internal: RwLock<DrawFunctionsInternal<P>>,
|
internal: RwLock<DrawFunctionsInternal<P>>,
|
||||||
}
|
}
|
||||||
|
@ -77,16 +99,42 @@ impl<P: PhaseItem> Default for DrawFunctions<P> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: PhaseItem> DrawFunctions<P> {
|
impl<P: PhaseItem> DrawFunctions<P> {
|
||||||
|
/// Accesses the draw functions in read mode.
|
||||||
pub fn read(&self) -> RwLockReadGuard<'_, DrawFunctionsInternal<P>> {
|
pub fn read(&self) -> RwLockReadGuard<'_, DrawFunctionsInternal<P>> {
|
||||||
self.internal.read()
|
self.internal.read()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Accesses the draw functions in write mode.
|
||||||
pub fn write(&self) -> RwLockWriteGuard<'_, DrawFunctionsInternal<P>> {
|
pub fn write(&self) -> RwLockWriteGuard<'_, DrawFunctionsInternal<P>> {
|
||||||
self.internal.write()
|
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<P: PhaseItem> {
|
pub trait RenderCommand<P: PhaseItem> {
|
||||||
|
/// Specifies all ECS data required by [`RenderCommand::render`].
|
||||||
|
/// All parameters have to be read only.
|
||||||
type Param: SystemParam;
|
type Param: SystemParam;
|
||||||
|
|
||||||
|
/// Renders the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
|
||||||
fn render<'w>(
|
fn render<'w>(
|
||||||
view: Entity,
|
view: Entity,
|
||||||
item: &P,
|
item: &P,
|
||||||
|
@ -165,6 +213,8 @@ macro_rules! render_command_tuple_impl {
|
||||||
|
|
||||||
all_tuples!(render_command_tuple_impl, 0, 15, C);
|
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<P: PhaseItem, C: RenderCommand<P>> {
|
pub struct RenderCommandState<P: PhaseItem, C: RenderCommand<P>> {
|
||||||
state: SystemState<C::Param>,
|
state: SystemState<C::Param>,
|
||||||
}
|
}
|
||||||
|
@ -181,6 +231,7 @@ impl<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static> Draw<P> for Rend
|
||||||
where
|
where
|
||||||
<C::Param as SystemParam>::Fetch: ReadOnlySystemParamFetch,
|
<C::Param as SystemParam>::Fetch: ReadOnlySystemParamFetch,
|
||||||
{
|
{
|
||||||
|
/// Prepares the ECS parameters for the wrapped [`RenderCommand`] and then renders it.
|
||||||
fn draw<'w>(
|
fn draw<'w>(
|
||||||
&mut self,
|
&mut self,
|
||||||
world: &'w World,
|
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 {
|
pub trait AddRenderCommand {
|
||||||
|
/// Adds the [`RenderCommand`] for the specified [`RenderPhase`](super::RenderPhase) to the app.
|
||||||
fn add_render_command<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static>(
|
fn add_render_command<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> &mut Self
|
) -> &mut Self
|
||||||
|
|
|
@ -5,7 +5,7 @@ use bevy_utils::tracing::debug;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use wgpu::{IndexFormat, RenderPass};
|
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)]
|
#[derive(Debug, Default)]
|
||||||
pub struct DrawState {
|
pub struct DrawState {
|
||||||
pipeline: Option<RenderPipelineId>,
|
pipeline: Option<RenderPipelineId>,
|
||||||
|
@ -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> {
|
pub struct TrackedRenderPass<'a> {
|
||||||
pass: RenderPass<'a>,
|
pass: RenderPass<'a>,
|
||||||
state: DrawState,
|
state: DrawState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TrackedRenderPass<'a> {
|
impl<'a> TrackedRenderPass<'a> {
|
||||||
|
/// Tracks the supplied render pass.
|
||||||
pub fn new(pass: RenderPass<'a>) -> Self {
|
pub fn new(pass: RenderPass<'a>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: DrawState::default(),
|
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) {
|
pub fn set_render_pipeline(&mut self, pipeline: &'a RenderPipeline) {
|
||||||
debug!("set pipeline: {:?}", pipeline);
|
debug!("set pipeline: {:?}", pipeline);
|
||||||
if self.state.is_pipeline_set(pipeline.id()) {
|
if self.state.is_pipeline_set(pipeline.id()) {
|
||||||
|
@ -105,6 +112,9 @@ impl<'a> TrackedRenderPass<'a> {
|
||||||
self.state.set_pipeline(pipeline.id());
|
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(
|
pub fn set_bind_group(
|
||||||
&mut self,
|
&mut self,
|
||||||
index: usize,
|
index: usize,
|
||||||
|
@ -132,6 +142,13 @@ impl<'a> TrackedRenderPass<'a> {
|
||||||
.set_bind_group(index as usize, bind_group.id(), dynamic_uniform_indices);
|
.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>) {
|
pub fn set_vertex_buffer(&mut self, index: usize, buffer_slice: BufferSlice<'a>) {
|
||||||
let offset = buffer_slice.offset();
|
let offset = buffer_slice.offset();
|
||||||
if self
|
if self
|
||||||
|
@ -158,6 +175,10 @@ impl<'a> TrackedRenderPass<'a> {
|
||||||
.set_vertex_buffer(index, buffer_slice.id(), offset);
|
.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(
|
pub fn set_index_buffer(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer_slice: BufferSlice<'a>,
|
buffer_slice: BufferSlice<'a>,
|
||||||
|
@ -182,11 +203,18 @@ impl<'a> TrackedRenderPass<'a> {
|
||||||
.set_index_buffer(buffer_slice.id(), offset, index_format);
|
.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<u32>, instances: Range<u32>) {
|
pub fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {
|
||||||
debug!("draw: {:?} {:?}", vertices, instances);
|
debug!("draw: {:?} {:?}", vertices, instances);
|
||||||
self.pass.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<u32>, base_vertex: i32, instances: Range<u32>) {
|
pub fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {
|
||||||
debug!(
|
debug!(
|
||||||
"draw indexed: {:?} {} {:?}",
|
"draw indexed: {:?} {} {:?}",
|
||||||
|
|
|
@ -6,6 +6,7 @@ pub use draw_state::*;
|
||||||
|
|
||||||
use bevy_ecs::prelude::Query;
|
use bevy_ecs::prelude::Query;
|
||||||
|
|
||||||
|
/// A resource to collect and sort draw requests for specific [`PhaseItems`](PhaseItem).
|
||||||
pub struct RenderPhase<I: PhaseItem> {
|
pub struct RenderPhase<I: PhaseItem> {
|
||||||
pub items: Vec<I>,
|
pub items: Vec<I>,
|
||||||
}
|
}
|
||||||
|
@ -17,16 +18,19 @@ impl<I: PhaseItem> Default for RenderPhase<I> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: PhaseItem> RenderPhase<I> {
|
impl<I: PhaseItem> RenderPhase<I> {
|
||||||
|
/// Adds a [`PhaseItem`] to this render phase.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn add(&mut self, item: I) {
|
pub fn add(&mut self, item: I) {
|
||||||
self.items.push(item);
|
self.items.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sorts all of its [`PhaseItems`](PhaseItem).
|
||||||
pub fn sort(&mut self) {
|
pub fn sort(&mut self) {
|
||||||
self.items.sort_by_key(|d| d.sort_key());
|
self.items.sort_by_key(|d| d.sort_key());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This system sorts all [`RenderPhases`](RenderPhase) for the [`PhaseItem`] type.
|
||||||
pub fn sort_phase_system<I: PhaseItem>(mut render_phases: Query<&mut RenderPhase<I>>) {
|
pub fn sort_phase_system<I: PhaseItem>(mut render_phases: Query<&mut RenderPhase<I>>) {
|
||||||
for mut phase in render_phases.iter_mut() {
|
for mut phase in render_phases.iter_mut() {
|
||||||
phase.sort();
|
phase.sort();
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
use bevy_reflect::Uuid;
|
use bevy_reflect::Uuid;
|
||||||
use std::{ops::Deref, sync::Arc};
|
use std::{ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
/// A [`BindGroup`] identifier.
|
||||||
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
||||||
pub struct BindGroupId(Uuid);
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct BindGroup {
|
pub struct BindGroup {
|
||||||
id: BindGroupId,
|
id: BindGroupId,
|
||||||
|
@ -11,6 +18,7 @@ pub struct BindGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BindGroup {
|
impl BindGroup {
|
||||||
|
/// Returns the [`BindGroupId`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn id(&self) -> BindGroupId {
|
pub fn id(&self) -> BindGroupId {
|
||||||
self.id
|
self.id
|
||||||
|
|
|
@ -7,9 +7,14 @@ use wgpu::{
|
||||||
VertexAttribute, VertexStepMode,
|
VertexAttribute, VertexStepMode,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A [`RenderPipeline`] identifier.
|
||||||
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
||||||
pub struct RenderPipelineId(Uuid);
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RenderPipeline {
|
pub struct RenderPipeline {
|
||||||
id: RenderPipelineId,
|
id: RenderPipelineId,
|
||||||
|
@ -41,9 +46,14 @@ impl Deref for RenderPipeline {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`ComputePipeline`] identifier.
|
||||||
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
||||||
pub struct ComputePipelineId(Uuid);
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ComputePipeline {
|
pub struct ComputePipeline {
|
||||||
id: ComputePipelineId,
|
id: ComputePipelineId,
|
||||||
|
@ -51,6 +61,7 @@ pub struct ComputePipeline {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ComputePipeline {
|
impl ComputePipeline {
|
||||||
|
/// Returns the [`ComputePipelineId`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn id(&self) -> ComputePipelineId {
|
pub fn id(&self) -> ComputePipelineId {
|
||||||
self.id
|
self.id
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
use bevy_utils::Uuid;
|
use bevy_utils::Uuid;
|
||||||
use std::{ops::Deref, sync::Arc};
|
use std::{ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
/// A [`Texture`] identifier.
|
||||||
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
||||||
pub struct TextureId(Uuid);
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Texture {
|
pub struct Texture {
|
||||||
id: TextureId,
|
id: TextureId,
|
||||||
|
@ -11,11 +16,13 @@ pub struct Texture {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Texture {
|
impl Texture {
|
||||||
|
/// Returns the [`TextureId`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn id(&self) -> TextureId {
|
pub fn id(&self) -> TextureId {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a view of this texture.
|
||||||
pub fn create_view(&self, desc: &wgpu::TextureViewDescriptor) -> TextureView {
|
pub fn create_view(&self, desc: &wgpu::TextureViewDescriptor) -> TextureView {
|
||||||
TextureView::from(self.value.create_view(desc))
|
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)]
|
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
||||||
pub struct TextureViewId(Uuid);
|
pub struct TextureViewId(Uuid);
|
||||||
|
|
||||||
|
/// This type combines wgpu's [`TextureView`](wgpu::TextureView) and
|
||||||
|
/// [SurfaceTexture`](wgpu::SurfaceTexture) into the same interface.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum TextureViewValue {
|
pub enum TextureViewValue {
|
||||||
|
/// The value is an actual wgpu [`TextureView`](wgpu::TextureView).
|
||||||
TextureView(Arc<wgpu::TextureView>),
|
TextureView(Arc<wgpu::TextureView>),
|
||||||
|
|
||||||
|
/// The value is a wgpu [`SurfaceTexture`](wgpu::SurfaceTexture), but dereferences to
|
||||||
|
/// a [`TextureView`](wgpu::TextureView).
|
||||||
SurfaceTexture {
|
SurfaceTexture {
|
||||||
// NOTE: The order of these fields is important because the view must be dropped before the
|
// NOTE: The order of these fields is important because the view must be dropped before the
|
||||||
// frame is dropped
|
// 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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TextureView {
|
pub struct TextureView {
|
||||||
id: TextureViewId,
|
id: TextureViewId,
|
||||||
|
@ -60,11 +78,13 @@ pub struct TextureView {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureView {
|
impl TextureView {
|
||||||
|
/// Returns the [`TextureViewId`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn id(&self) -> TextureViewId {
|
pub fn id(&self) -> TextureViewId {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the [`SurfaceTexture`](wgpu::SurfaceTexture) of the texture view if it is of that type.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn take_surface_texture(self) -> Option<wgpu::SurfaceTexture> {
|
pub fn take_surface_texture(self) -> Option<wgpu::SurfaceTexture> {
|
||||||
match self.value {
|
match self.value {
|
||||||
|
@ -107,9 +127,15 @@ impl Deref for TextureView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`Sampler`] identifier.
|
||||||
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
||||||
pub struct SamplerId(Uuid);
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Sampler {
|
pub struct Sampler {
|
||||||
id: SamplerId,
|
id: SamplerId,
|
||||||
|
@ -117,6 +143,7 @@ pub struct Sampler {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sampler {
|
impl Sampler {
|
||||||
|
/// Returns the [`SamplerId`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn id(&self) -> SamplerId {
|
pub fn id(&self) -> SamplerId {
|
||||||
self.id
|
self.id
|
||||||
|
|
|
@ -13,6 +13,7 @@ use bevy_ecs::prelude::*;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use wgpu::{CommandEncoder, DeviceDescriptor, Instance, Queue, RequestAdapterOptions};
|
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) {
|
pub fn render_system(world: &mut World) {
|
||||||
world.resource_scope(|world, mut graph: Mut<RenderGraph>| {
|
world.resource_scope(|world, mut graph: Mut<RenderGraph>| {
|
||||||
graph.update(world);
|
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<Queue>;
|
pub type RenderQueue = Arc<Queue>;
|
||||||
|
|
||||||
|
/// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`],
|
||||||
|
/// aswell as to create [`WindowSurfaces`](crate::view::window::WindowSurfaces).
|
||||||
pub type RenderInstance = Instance;
|
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(
|
pub async fn initialize_renderer(
|
||||||
instance: &Instance,
|
instance: &Instance,
|
||||||
request_adapter_options: &RequestAdapterOptions<'_>,
|
request_adapter_options: &RequestAdapterOptions<'_>,
|
||||||
|
@ -87,6 +94,10 @@ pub async fn initialize_renderer(
|
||||||
(RenderDevice::from(device), queue)
|
(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 struct RenderContext {
|
||||||
pub render_device: RenderDevice,
|
pub render_device: RenderDevice,
|
||||||
pub command_encoder: CommandEncoder,
|
pub command_encoder: CommandEncoder,
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use futures_lite::future;
|
|
||||||
use wgpu::util::DeviceExt;
|
|
||||||
|
|
||||||
use crate::render_resource::{
|
use crate::render_resource::{
|
||||||
BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawRenderPipelineDescriptor,
|
BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawRenderPipelineDescriptor,
|
||||||
RenderPipeline, Sampler, Texture,
|
RenderPipeline, Sampler, Texture,
|
||||||
};
|
};
|
||||||
|
use futures_lite::future;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
|
/// This GPU device is responsible for the creation of most rendering and compute resources.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RenderDevice {
|
pub struct RenderDevice {
|
||||||
device: Arc<wgpu::Device>,
|
device: Arc<wgpu::Device>,
|
||||||
|
@ -19,7 +19,7 @@ impl From<Arc<wgpu::Device>> for RenderDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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.
|
/// Functions may panic if you use unsupported features.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -27,7 +27,7 @@ impl RenderDevice {
|
||||||
self.device.features()
|
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.
|
/// If any of these limits are exceeded, functions may panic.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -35,7 +35,7 @@ impl RenderDevice {
|
||||||
self.device.limits()
|
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]
|
#[inline]
|
||||||
pub fn create_shader_module(&self, desc: &wgpu::ShaderModuleDescriptor) -> wgpu::ShaderModule {
|
pub fn create_shader_module(&self, desc: &wgpu::ShaderModuleDescriptor) -> wgpu::ShaderModule {
|
||||||
self.device.create_shader_module(desc)
|
self.device.create_shader_module(desc)
|
||||||
|
@ -49,7 +49,7 @@ impl RenderDevice {
|
||||||
self.device.poll(maintain)
|
self.device.poll(maintain)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an empty [`CommandEncoder`].
|
/// Creates an empty [`CommandEncoder`](wgpu::CommandEncoder).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn create_command_encoder(
|
pub fn create_command_encoder(
|
||||||
&self,
|
&self,
|
||||||
|
@ -58,7 +58,7 @@ impl RenderDevice {
|
||||||
self.device.create_command_encoder(desc)
|
self.device.create_command_encoder(desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an empty [`RenderBundleEncoder`].
|
/// Creates an empty [`RenderBundleEncoder`](wgpu::RenderBundleEncoder).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn create_render_bundle_encoder(
|
pub fn create_render_bundle_encoder(
|
||||||
&self,
|
&self,
|
||||||
|
@ -67,14 +67,14 @@ impl RenderDevice {
|
||||||
self.device.create_render_bundle_encoder(desc)
|
self.device.create_render_bundle_encoder(desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`BindGroup`].
|
/// Creates a new [`BindGroup`](wgpu::BindGroup).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn create_bind_group(&self, desc: &wgpu::BindGroupDescriptor) -> BindGroup {
|
pub fn create_bind_group(&self, desc: &wgpu::BindGroupDescriptor) -> BindGroup {
|
||||||
let wgpu_bind_group = self.device.create_bind_group(desc);
|
let wgpu_bind_group = self.device.create_bind_group(desc);
|
||||||
BindGroup::from(wgpu_bind_group)
|
BindGroup::from(wgpu_bind_group)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a [`BindGroupLayout`].
|
/// Creates a [`BindGroupLayout`](wgpu::BindGroupLayout).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn create_bind_group_layout(
|
pub fn create_bind_group_layout(
|
||||||
&self,
|
&self,
|
||||||
|
@ -83,7 +83,7 @@ impl RenderDevice {
|
||||||
BindGroupLayout::from(self.device.create_bind_group_layout(desc))
|
BindGroupLayout::from(self.device.create_bind_group_layout(desc))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a [`PipelineLayout`].
|
/// Creates a [`PipelineLayout`](wgpu::PipelineLayout).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn create_pipeline_layout(
|
pub fn create_pipeline_layout(
|
||||||
&self,
|
&self,
|
||||||
|
@ -115,7 +115,7 @@ impl RenderDevice {
|
||||||
Buffer::from(wgpu_buffer)
|
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 {
|
pub fn create_buffer_with_data(&self, desc: &wgpu::util::BufferInitDescriptor) -> Buffer {
|
||||||
let wgpu_buffer = self.device.create_buffer_init(desc);
|
let wgpu_buffer = self.device.create_buffer_init(desc);
|
||||||
Buffer::from(wgpu_buffer)
|
Buffer::from(wgpu_buffer)
|
||||||
|
@ -137,16 +137,17 @@ impl RenderDevice {
|
||||||
Sampler::from(wgpu_sampler)
|
Sampler::from(wgpu_sampler)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new [`SwapChain`] which targets `surface`.
|
/// Create a new [`SwapChain`](wgpu::SwapChain) which targets `surface`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # 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.
|
/// - Texture format requested is unsupported on the swap chain.
|
||||||
pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {
|
pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {
|
||||||
surface.configure(&self.device, config)
|
surface.configure(&self.device, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the wgpu [`Device`](wgpu::Device).
|
||||||
pub fn wgpu_device(&self) -> &wgpu::Device {
|
pub fn wgpu_device(&self) -> &wgpu::Device {
|
||||||
&self.device
|
&self.device
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,11 @@ impl Default for Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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(
|
pub fn new(
|
||||||
size: Extent3d,
|
size: Extent3d,
|
||||||
dimension: TextureDimension,
|
dimension: TextureDimension,
|
||||||
|
@ -71,6 +76,12 @@ impl Image {
|
||||||
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(
|
pub fn new_fill(
|
||||||
size: Extent3d,
|
size: Extent3d,
|
||||||
dimension: TextureDimension,
|
dimension: TextureDimension,
|
||||||
|
@ -98,10 +109,13 @@ impl Image {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the aspect ratio (height/width) of a 2D image.
|
||||||
pub fn aspect_2d(&self) -> f32 {
|
pub fn aspect_2d(&self) -> f32 {
|
||||||
self.texture_descriptor.size.height as f32 / self.texture_descriptor.size.width as 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) {
|
pub fn resize(&mut self, size: Extent3d) {
|
||||||
self.texture_descriptor.size = size;
|
self.texture_descriptor.size = size;
|
||||||
self.data.resize(
|
self.data.resize(
|
||||||
|
@ -112,6 +126,9 @@ impl Image {
|
||||||
|
|
||||||
/// Changes the `size`, asserting that the total number of data elements (pixels) remains the
|
/// Changes the `size`, asserting that the total number of data elements (pixels) remains the
|
||||||
/// same.
|
/// 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) {
|
pub fn reinterpret_size(&mut self, new_size: Extent3d) {
|
||||||
assert!(
|
assert!(
|
||||||
new_size.volume() == self.texture_descriptor.size.volume(),
|
new_size.volume() == self.texture_descriptor.size.volume(),
|
||||||
|
@ -123,9 +140,13 @@ impl Image {
|
||||||
self.texture_descriptor.size = new_size;
|
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
|
/// 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.
|
/// 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) {
|
pub fn reinterpret_stacked_2d_as_array(&mut self, layers: u32) {
|
||||||
// Must be a stacked image, and the height must be divisible by layers.
|
// Must be a stacked image, and the height must be divisible by layers.
|
||||||
assert!(self.texture_descriptor.dimension == TextureDimension::D2);
|
assert!(self.texture_descriptor.dimension == TextureDimension::D2);
|
||||||
|
@ -203,31 +224,39 @@ pub enum TextureError {
|
||||||
ImageError(#[from] image::ImageError),
|
ImageError(#[from] image::ImageError),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type of a raw image buffer
|
/// The type of a raw image buffer.
|
||||||
pub enum ImageType<'a> {
|
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),
|
MimeType(&'a str),
|
||||||
/// Extension of an image file, for example `"png"`
|
/// The extension of an image file, for example `"png"`.
|
||||||
Extension(&'a str),
|
Extension(&'a str),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used to calculate the volume of an item.
|
||||||
pub trait Volume {
|
pub trait Volume {
|
||||||
fn volume(&self) -> usize;
|
fn volume(&self) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Volume for Extent3d {
|
impl Volume for Extent3d {
|
||||||
|
/// Calculates the volume of the [`Extent3D`].
|
||||||
fn volume(&self) -> usize {
|
fn volume(&self) -> usize {
|
||||||
(self.width * self.height * self.depth_or_array_layers) as 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 {
|
pub struct PixelInfo {
|
||||||
|
/// The size of a component of a pixel in bytes.
|
||||||
pub type_size: usize,
|
pub type_size: usize,
|
||||||
|
/// The amount of different components (color channels).
|
||||||
pub num_components: usize,
|
pub num_components: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extends the wgpu [`TextureFormat`] with information about the pixel.
|
||||||
pub trait TextureFormatPixelInfo {
|
pub trait TextureFormatPixelInfo {
|
||||||
|
/// Returns the pixel information of the format.
|
||||||
fn pixel_info(&self) -> PixelInfo;
|
fn pixel_info(&self) -> PixelInfo;
|
||||||
|
/// Returns the size of a pixel of the format.
|
||||||
fn pixel_size(&self) -> usize {
|
fn pixel_size(&self) -> usize {
|
||||||
let info = self.pixel_info();
|
let info = self.pixel_info();
|
||||||
info.type_size * info.num_components
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GpuImage {
|
pub struct GpuImage {
|
||||||
pub texture: Texture,
|
pub texture: Texture,
|
||||||
|
@ -352,10 +383,12 @@ impl RenderAsset for Image {
|
||||||
type PreparedAsset = GpuImage;
|
type PreparedAsset = GpuImage;
|
||||||
type Param = (SRes<RenderDevice>, SRes<RenderQueue>);
|
type Param = (SRes<RenderDevice>, SRes<RenderQueue>);
|
||||||
|
|
||||||
|
/// Clones the Image.
|
||||||
fn extract_asset(&self) -> Self::ExtractedAsset {
|
fn extract_asset(&self) -> Self::ExtractedAsset {
|
||||||
self.clone()
|
self.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts the extracted image into a [`GpuImage`].
|
||||||
fn prepare_asset(
|
fn prepare_asset(
|
||||||
image: Self::ExtractedAsset,
|
image: Self::ExtractedAsset,
|
||||||
(render_device, render_queue): &mut SystemParamItem<Self::Param>,
|
(render_device, render_queue): &mut SystemParamItem<Self::Param>,
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::texture::{Image, TextureFormatPixelInfo};
|
||||||
use wgpu::{Extent3d, TextureDimension, TextureFormat};
|
use wgpu::{Extent3d, TextureDimension, TextureFormat};
|
||||||
|
|
||||||
// TODO: fix name?
|
// 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 {
|
pub(crate) fn image_to_texture(dyn_img: image::DynamicImage) -> Image {
|
||||||
use bevy_core::cast_slice;
|
use bevy_core::cast_slice;
|
||||||
let width;
|
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
|
/// Converts an [`Image`] to a [`DynamicImage`]. Not all [`TextureFormat`] are
|
||||||
/// covered, it will return `None` if the format is not supported
|
/// covered, therefore it will return `None` if the format is unsupported.
|
||||||
pub(crate) fn texture_to_image(texture: &Image) -> Option<image::DynamicImage> {
|
pub(crate) fn texture_to_image(texture: &Image) -> Option<image::DynamicImage> {
|
||||||
match texture.texture_descriptor.format {
|
match texture.texture_descriptor.format {
|
||||||
TextureFormat::R8Unorm => image::ImageBuffer::from_raw(
|
TextureFormat::R8Unorm => image::ImageBuffer::from_raw(
|
||||||
|
|
|
@ -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)]
|
#[derive(Error, Debug)]
|
||||||
pub struct FileTextureError {
|
pub struct FileTextureError {
|
||||||
error: TextureError,
|
error: TextureError,
|
||||||
|
|
|
@ -18,6 +18,7 @@ use bevy_app::{App, Plugin};
|
||||||
use bevy_asset::AddAsset;
|
use bevy_asset::AddAsset;
|
||||||
|
|
||||||
// TODO: replace Texture names with Image names?
|
// 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;
|
pub struct ImagePlugin;
|
||||||
|
|
||||||
impl Plugin for ImagePlugin {
|
impl Plugin for ImagePlugin {
|
||||||
|
|
|
@ -6,6 +6,8 @@ use bevy_ecs::prelude::ResMut;
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
use wgpu::{TextureDescriptor, TextureViewDescriptor};
|
use wgpu::{TextureDescriptor, TextureViewDescriptor};
|
||||||
|
|
||||||
|
/// The internal representation of a [`CachedTexture`] used to track whether it was recently used
|
||||||
|
/// and is currently taken.
|
||||||
struct CachedTextureMeta {
|
struct CachedTextureMeta {
|
||||||
texture: Texture,
|
texture: Texture,
|
||||||
default_view: TextureView,
|
default_view: TextureView,
|
||||||
|
@ -13,17 +15,24 @@ struct CachedTextureMeta {
|
||||||
frames_since_last_use: usize,
|
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 struct CachedTexture {
|
||||||
pub texture: Texture,
|
pub texture: Texture,
|
||||||
pub default_view: TextureView,
|
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)]
|
#[derive(Default)]
|
||||||
pub struct TextureCache {
|
pub struct TextureCache {
|
||||||
textures: HashMap<wgpu::TextureDescriptor<'static>, Vec<CachedTextureMeta>>,
|
textures: HashMap<wgpu::TextureDescriptor<'static>, Vec<CachedTextureMeta>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextureCache {
|
impl TextureCache {
|
||||||
|
/// Retrieves a texture that matches the `descriptor`. If no matching one is found a new
|
||||||
|
/// [`CachedTexture`] is created.
|
||||||
pub fn get(
|
pub fn get(
|
||||||
&mut self,
|
&mut self,
|
||||||
render_device: &RenderDevice,
|
render_device: &RenderDevice,
|
||||||
|
@ -72,6 +81,7 @@ impl TextureCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Updates the cache and only retains recently used textures.
|
||||||
pub fn update(&mut self) {
|
pub fn update(&mut self) {
|
||||||
for textures in self.textures.values_mut() {
|
for textures in self.textures.values_mut() {
|
||||||
for texture in textures.iter_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<TextureCache>) {
|
pub fn update_texture_cache_system(mut texture_cache: ResMut<TextureCache>) {
|
||||||
texture_cache.update();
|
texture_cache.update();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use bevy_window::{RawWindowHandleWrapper, WindowId, Windows};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use wgpu::TextureFormat;
|
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)]
|
#[derive(Default)]
|
||||||
pub struct NonSendMarker;
|
pub struct NonSendMarker;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue