Skip to content

Commit

Permalink
ExtractResourcePlugin (bevyengine#3745)
Browse files Browse the repository at this point in the history
# Objective

- Add an `ExtractResourcePlugin` for convenience and consistency

## Solution

- Add an `ExtractResourcePlugin` similar to `ExtractComponentPlugin` but for ECS `Resource`s. The system that is executed simply clones the main world resource into a render world resource, if and only if the main world resource was either added or changed since the last execution of the system.
- Add an `ExtractResource` trait with a `fn extract_resource(res: &Self) -> Self` function. This is used by the `ExtractResourcePlugin` to extract the resource
- Add a derive macro for `ExtractResource` on a `Resource` with the `Clone` trait, that simply returns `res.clone()`
- Use `ExtractResourcePlugin` wherever both possible and appropriate
  • Loading branch information
superdump authored and ItsDoot committed Feb 1, 2023
1 parent 6f6f161 commit d259461
Show file tree
Hide file tree
Showing 21 changed files with 154 additions and 75 deletions.
30 changes: 7 additions & 23 deletions crates/bevy_core_pipeline/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use bevy_ecs::prelude::*;
use bevy_render::{
camera::{ActiveCamera, Camera2d, Camera3d, ExtractedCamera, RenderTarget},
color::Color,
extract_resource::{ExtractResource, ExtractResourcePlugin},
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
render_phase::{
batch_phase_system, sort_phase_system, BatchedPhaseItem, CachedRenderPipelinePhaseItem,
Expand All @@ -33,15 +34,15 @@ use bevy_render::{
renderer::RenderDevice,
texture::TextureCache,
view::{ExtractedView, Msaa, ViewDepthTexture},
RenderApp, RenderStage, RenderWorld,
RenderApp, RenderStage,
};
use bevy_utils::FloatOrd;

/// When used as a resource, sets the color that is used to clear the screen between frames.
///
/// This color appears as the "background" color for simple apps, when
/// there are portions of the screen with nothing rendered.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, ExtractResource)]
pub struct ClearColor(pub Color);

impl Default for ClearColor {
Expand All @@ -50,7 +51,7 @@ impl Default for ClearColor {
}
}

#[derive(Clone, Debug, Default)]
#[derive(Clone, Debug, Default, ExtractResource)]
pub struct RenderTargetClearColors {
colors: HashMap<RenderTarget, Color>,
}
Expand Down Expand Up @@ -113,7 +114,9 @@ pub enum CorePipelineRenderSystems {
impl Plugin for CorePipelinePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<ClearColor>()
.init_resource::<RenderTargetClearColors>();
.init_resource::<RenderTargetClearColors>()
.add_plugin(ExtractResourcePlugin::<ClearColor>::default())
.add_plugin(ExtractResourcePlugin::<RenderTargetClearColors>::default());

let render_app = match app.get_sub_app_mut(RenderApp) {
Ok(render_app) => render_app,
Expand All @@ -125,7 +128,6 @@ impl Plugin for CorePipelinePlugin {
.init_resource::<DrawFunctions<Opaque3d>>()
.init_resource::<DrawFunctions<AlphaMask3d>>()
.init_resource::<DrawFunctions<Transparent3d>>()
.add_system_to_stage(RenderStage::Extract, extract_clear_color)
.add_system_to_stage(RenderStage::Extract, extract_core_pipeline_camera_phases)
.add_system_to_stage(RenderStage::Prepare, prepare_core_views_system)
.add_system_to_stage(
Expand Down Expand Up @@ -347,24 +349,6 @@ impl CachedRenderPipelinePhaseItem for Transparent3d {
}
}

pub fn extract_clear_color(
clear_color: Res<ClearColor>,
clear_colors: Res<RenderTargetClearColors>,
mut render_world: ResMut<RenderWorld>,
) {
// If the clear color has changed
if clear_color.is_changed() {
// Update the clear color resource in the render world
render_world.insert_resource(clear_color.clone());
}

// If the clear color has changed
if clear_colors.is_changed() {
// Update the clear color resource in the render world
render_world.insert_resource(clear_colors.clone());
}
}

pub fn extract_core_pipeline_camera_phases(
mut commands: Commands,
active_2d: Res<ActiveCamera<Camera2d>>,
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_pbr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
use bevy_ecs::prelude::*;
use bevy_reflect::TypeUuid;
use bevy_render::{
extract_resource::ExtractResourcePlugin,
prelude::Color,
render_graph::RenderGraph,
render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions},
Expand Down Expand Up @@ -76,6 +77,7 @@ impl Plugin for PbrPlugin {
.init_resource::<GlobalVisiblePointLights>()
.init_resource::<DirectionalLightShadowMap>()
.init_resource::<PointLightShadowMap>()
.add_plugin(ExtractResourcePlugin::<AmbientLight>::default())
.add_system_to_stage(
CoreStage::PostUpdate,
// NOTE: Clusters need to have been added before update_clusters is run so
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_pbr/src/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use bevy_reflect::prelude::*;
use bevy_render::{
camera::{Camera, CameraProjection, OrthographicProjection},
color::Color,
extract_resource::ExtractResource,
prelude::Image,
primitives::{Aabb, CubemapFrusta, Frustum, Plane, Sphere},
render_resource::BufferBindingType,
Expand Down Expand Up @@ -170,7 +171,7 @@ impl Default for DirectionalLightShadowMap {
}

/// An ambient light, which lights the entire scene equally.
#[derive(Debug)]
#[derive(Clone, Debug, ExtractResource)]
pub struct AmbientLight {
pub color: Color,
/// A direct scale factor multiplied with `color` before being passed to the shader.
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ use bevy_ecs::{
world::FromWorld,
};
use bevy_render::{
extract_component::ExtractComponentPlugin,
mesh::{Mesh, MeshVertexBufferLayout},
render_asset::{RenderAsset, RenderAssetPlugin, RenderAssets},
render_component::ExtractComponentPlugin,
render_phase::{
AddRenderCommand, DrawFunctions, EntityRenderCommand, RenderCommandResult, RenderPhase,
SetItemPipeline, TrackedRenderPass,
Expand Down
32 changes: 11 additions & 21 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@ pub enum RenderLightSystems {
QueueShadows,
}

pub struct ExtractedAmbientLight {
color: Color,
brightness: f32,
}

#[derive(Component)]
pub struct ExtractedPointLight {
color: Color,
Expand All @@ -63,8 +58,6 @@ pub struct ExtractedPointLight {
shadow_normal_bias: f32,
}

pub type ExtractedPointLightShadowMap = PointLightShadowMap;

#[derive(Component)]
pub struct ExtractedDirectionalLight {
color: Color,
Expand All @@ -76,8 +69,6 @@ pub struct ExtractedDirectionalLight {
shadow_normal_bias: f32,
}

pub type ExtractedDirectionalLightShadowMap = DirectionalLightShadowMap;

#[derive(Copy, Clone, ShaderType, Default, Debug)]
pub struct GpuPointLight {
// The lower-right 2x2 values of the projection matrix 22 23 32 33
Expand Down Expand Up @@ -408,7 +399,6 @@ pub fn extract_clusters(mut commands: Commands, views: Query<(Entity, &Clusters)
#[allow(clippy::too_many_arguments)]
pub fn extract_lights(
mut commands: Commands,
ambient_light: Res<AmbientLight>,
point_light_shadow_map: Res<PointLightShadowMap>,
directional_light_shadow_map: Res<DirectionalLightShadowMap>,
global_point_lights: Res<GlobalVisiblePointLights>,
Expand All @@ -422,14 +412,14 @@ pub fn extract_lights(
)>,
mut previous_point_lights_len: Local<usize>,
) {
commands.insert_resource(ExtractedAmbientLight {
color: ambient_light.color,
brightness: ambient_light.brightness,
});
commands.insert_resource::<ExtractedPointLightShadowMap>(point_light_shadow_map.clone());
commands.insert_resource::<ExtractedDirectionalLightShadowMap>(
directional_light_shadow_map.clone(),
);
// NOTE: These shadow map resources are extracted here as they are used here too so this avoids
// races between scheduling of ExtractResourceSystems and this system.
if point_light_shadow_map.is_changed() {
commands.insert_resource(point_light_shadow_map.clone());
}
if directional_light_shadow_map.is_changed() {
commands.insert_resource(directional_light_shadow_map.clone());
}
// This is the point light shadow map texel size for one face of the cube as a distance of 1.0
// world unit from the light.
// point_light_texel_size = 2.0 * 1.0 * tan(PI / 4.0) / cube face width in texels
Expand Down Expand Up @@ -665,9 +655,9 @@ pub fn prepare_lights(
(Entity, &ExtractedView, &ExtractedClusterConfig),
With<RenderPhase<Transparent3d>>,
>,
ambient_light: Res<ExtractedAmbientLight>,
point_light_shadow_map: Res<ExtractedPointLightShadowMap>,
directional_light_shadow_map: Res<ExtractedDirectionalLightShadowMap>,
ambient_light: Res<AmbientLight>,
point_light_shadow_map: Res<PointLightShadowMap>,
directional_light_shadow_map: Res<DirectionalLightShadowMap>,
point_lights: Query<(Entity, &ExtractedPointLight)>,
directional_lights: Query<(Entity, &ExtractedDirectionalLight)>,
) {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ use bevy_ecs::{
use bevy_math::{Mat4, Vec2};
use bevy_reflect::TypeUuid;
use bevy_render::{
extract_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
mesh::{
skinning::{SkinnedMesh, SkinnedMeshInverseBindposes},
GpuBufferInfo, Mesh, MeshVertexBufferLayout,
},
render_asset::RenderAssets,
render_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin},
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass},
render_resource::*,
renderer::{RenderDevice, RenderQueue},
Expand Down
13 changes: 4 additions & 9 deletions crates/bevy_pbr/src/wireframe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use bevy_ecs::{prelude::*, reflect::ReflectComponent};
use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::{Reflect, TypeUuid};
use bevy_render::{
extract_resource::{ExtractResource, ExtractResourcePlugin},
mesh::{Mesh, MeshVertexBufferLayout},
render_asset::RenderAssets,
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
Expand Down Expand Up @@ -34,26 +35,20 @@ impl Plugin for WireframePlugin {
Shader::from_wgsl
);

app.init_resource::<WireframeConfig>();
app.init_resource::<WireframeConfig>()
.add_plugin(ExtractResourcePlugin::<WireframeConfig>::default());

if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
render_app
.add_render_command::<Opaque3d, DrawWireframes>()
.init_resource::<WireframePipeline>()
.init_resource::<SpecializedMeshPipelines<WireframePipeline>>()
.add_system_to_stage(RenderStage::Extract, extract_wireframes)
.add_system_to_stage(RenderStage::Extract, extract_wireframe_config)
.add_system_to_stage(RenderStage::Queue, queue_wireframes);
}
}
}

fn extract_wireframe_config(mut commands: Commands, wireframe_config: Res<WireframeConfig>) {
if wireframe_config.is_added() || wireframe_config.is_changed() {
commands.insert_resource(wireframe_config.into_inner().clone());
}
}

fn extract_wireframes(mut commands: Commands, query: Query<Entity, With<Wireframe>>) {
for entity in query.iter() {
commands.get_or_spawn(entity).insert(Wireframe);
Expand All @@ -65,7 +60,7 @@ fn extract_wireframes(mut commands: Commands, query: Query<Entity, With<Wirefram
#[reflect(Component, Default)]
pub struct Wireframe;

#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone, Default, ExtractResource)]
pub struct WireframeConfig {
/// Whether to show wireframes for all meshes. If `false`, only meshes with a [Wireframe] component will be rendered.
pub global: bool,
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ bevy_ecs = { path = "../bevy_ecs", version = "0.8.0-dev" }
bevy_encase_derive = { path = "../bevy_encase_derive", version = "0.8.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.8.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.8.0-dev", features = ["bevy"] }
bevy_render_macros = { path = "macros", version = "0.8.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.8.0-dev" }
bevy_window = { path = "../bevy_window", version = "0.8.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" }
Expand Down
19 changes: 19 additions & 0 deletions crates/bevy_render/macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "bevy_render_macros"
version = "0.8.0-dev"
edition = "2021"
description = "Derive implementations for bevy_render"
homepage = "https://bevyengine.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]

[lib]
proc-macro = true

[dependencies]
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.8.0-dev" }

syn = "1.0"
proc-macro2 = "1.0"
quote = "1.0"
26 changes: 26 additions & 0 deletions crates/bevy_render/macros/src/extract_resource.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, parse_quote, DeriveInput, Path};

pub fn derive_extract_resource(input: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(input as DeriveInput);
let bevy_render_path: Path = crate::bevy_render_path();

ast.generics
.make_where_clause()
.predicates
.push(parse_quote! { Self: Clone });

let struct_name = &ast.ident;
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();

TokenStream::from(quote! {
impl #impl_generics #bevy_render_path::extract_resource::ExtractResource for #struct_name #type_generics #where_clause {
type Source = Self;

fn extract_resource(source: &Self::Source) -> Self {
source.clone()
}
}
})
}
16 changes: 16 additions & 0 deletions crates/bevy_render/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
mod extract_resource;

use bevy_macro_utils::BevyManifest;
use proc_macro::TokenStream;

pub(crate) fn bevy_render_path() -> syn::Path {
BevyManifest::default()
.maybe_get_path("bevy_render")
// NOTE: If the derivation is within bevy_render, then we need to return 'crate'
.unwrap_or_else(|| BevyManifest::parse_str("crate"))
}

#[proc_macro_derive(ExtractResource)]
pub fn derive_extract_resource(input: TokenStream) -> TokenStream {
extract_resource::derive_extract_resource(input)
}
File renamed without changes.
46 changes: 46 additions & 0 deletions crates/bevy_render/src/extract_resource.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use std::marker::PhantomData;

use bevy_app::{App, Plugin};
use bevy_ecs::system::{Commands, Res, Resource};
pub use bevy_render_macros::ExtractResource;

use crate::{RenderApp, RenderStage};

/// Describes how a resource gets extracted for rendering.
///
/// Therefore the resource is transferred from the "main world" into the "render world"
/// in the [`RenderStage::Extract`](crate::RenderStage::Extract) step.
pub trait ExtractResource: Resource {
type Source: Resource;

/// Defines how the resource is transferred into the "render world".
fn extract_resource(source: &Self::Source) -> Self;
}

/// This plugin extracts the resources into the "render world".
///
/// Therefore it sets up the [`RenderStage::Extract`](crate::RenderStage::Extract) step
/// for the specified [`Resource`].
pub struct ExtractResourcePlugin<R: ExtractResource>(PhantomData<R>);

impl<R: ExtractResource> Default for ExtractResourcePlugin<R> {
fn default() -> Self {
Self(PhantomData)
}
}

impl<R: ExtractResource> Plugin for ExtractResourcePlugin<R> {
fn build(&self, app: &mut App) {
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
render_app.add_system_to_stage(RenderStage::Extract, extract_resource::<R>);
}
}
}

/// This system extracts the resource of the corresponding [`Resource`] type
/// by cloning it.
pub fn extract_resource<R: ExtractResource>(mut commands: Commands, resource: Res<R::Source>) {
if resource.is_changed() {
commands.insert_resource(R::extract_resource(resource.into_inner()));
}
}
3 changes: 2 additions & 1 deletion crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ extern crate core;

pub mod camera;
pub mod color;
pub mod extract_component;
pub mod extract_resource;
pub mod mesh;
pub mod primitives;
pub mod render_asset;
pub mod render_component;
pub mod render_graph;
pub mod render_phase;
pub mod render_resource;
Expand Down
Loading

0 comments on commit d259461

Please sign in to comment.