diff --git a/crates/bevy_core_pipeline/src/lib.rs b/crates/bevy_core_pipeline/src/lib.rs index 328b6d7011466..31bb9077c1ef2 100644 --- a/crates/bevy_core_pipeline/src/lib.rs +++ b/crates/bevy_core_pipeline/src/lib.rs @@ -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, @@ -33,7 +34,7 @@ use bevy_render::{ renderer::RenderDevice, texture::TextureCache, view::{ExtractedView, Msaa, ViewDepthTexture}, - RenderApp, RenderStage, RenderWorld, + RenderApp, RenderStage, }; use bevy_utils::FloatOrd; @@ -41,7 +42,7 @@ use bevy_utils::FloatOrd; /// /// 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 { @@ -50,7 +51,7 @@ impl Default for ClearColor { } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, ExtractResource)] pub struct RenderTargetClearColors { colors: HashMap, } @@ -113,7 +114,9 @@ pub enum CorePipelineRenderSystems { impl Plugin for CorePipelinePlugin { fn build(&self, app: &mut App) { app.init_resource::() - .init_resource::(); + .init_resource::() + .add_plugin(ExtractResourcePlugin::::default()) + .add_plugin(ExtractResourcePlugin::::default()); let render_app = match app.get_sub_app_mut(RenderApp) { Ok(render_app) => render_app, @@ -125,7 +128,6 @@ impl Plugin for CorePipelinePlugin { .init_resource::>() .init_resource::>() .init_resource::>() - .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( @@ -347,24 +349,6 @@ impl CachedRenderPipelinePhaseItem for Transparent3d { } } -pub fn extract_clear_color( - clear_color: Res, - clear_colors: Res, - mut render_world: ResMut, -) { - // 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>, diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 1c191e6b0a041..83585694ba659 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -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}, @@ -76,6 +77,7 @@ impl Plugin for PbrPlugin { .init_resource::() .init_resource::() .init_resource::() + .add_plugin(ExtractResourcePlugin::::default()) .add_system_to_stage( CoreStage::PostUpdate, // NOTE: Clusters need to have been added before update_clusters is run so diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index a433403400bb0..c8e58b31db59f 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -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, @@ -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. diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index b0d20dde6e25a..b0df8d0116613 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -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, diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 82c3855e76a8e..57a1b825b56d1 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -45,11 +45,6 @@ pub enum RenderLightSystems { QueueShadows, } -pub struct ExtractedAmbientLight { - color: Color, - brightness: f32, -} - #[derive(Component)] pub struct ExtractedPointLight { color: Color, @@ -63,8 +58,6 @@ pub struct ExtractedPointLight { shadow_normal_bias: f32, } -pub type ExtractedPointLightShadowMap = PointLightShadowMap; - #[derive(Component)] pub struct ExtractedDirectionalLight { color: Color, @@ -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 @@ -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, point_light_shadow_map: Res, directional_light_shadow_map: Res, global_point_lights: Res, @@ -422,14 +412,14 @@ pub fn extract_lights( )>, mut previous_point_lights_len: Local, ) { - commands.insert_resource(ExtractedAmbientLight { - color: ambient_light.color, - brightness: ambient_light.brightness, - }); - commands.insert_resource::(point_light_shadow_map.clone()); - commands.insert_resource::( - 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 @@ -665,9 +655,9 @@ pub fn prepare_lights( (Entity, &ExtractedView, &ExtractedClusterConfig), With>, >, - ambient_light: Res, - point_light_shadow_map: Res, - directional_light_shadow_map: Res, + ambient_light: Res, + point_light_shadow_map: Res, + directional_light_shadow_map: Res, point_lights: Query<(Entity, &ExtractedPointLight)>, directional_lights: Query<(Entity, &ExtractedDirectionalLight)>, ) { diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index befc255daf1c3..857b2b9fbefc2 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -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}, diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index b8116c6f1fe6d..e2abbec39e34e 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -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}, @@ -34,7 +35,8 @@ impl Plugin for WireframePlugin { Shader::from_wgsl ); - app.init_resource::(); + app.init_resource::() + .add_plugin(ExtractResourcePlugin::::default()); if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app @@ -42,18 +44,11 @@ impl Plugin for WireframePlugin { .init_resource::() .init_resource::>() .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) { - 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>) { for entity in query.iter() { commands.get_or_spawn(entity).insert(Wireframe); @@ -65,7 +60,7 @@ fn extract_wireframes(mut commands: Commands, query: Query 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() + } + } + }) +} diff --git a/crates/bevy_render/macros/src/lib.rs b/crates/bevy_render/macros/src/lib.rs new file mode 100644 index 0000000000000..0079780b8e027 --- /dev/null +++ b/crates/bevy_render/macros/src/lib.rs @@ -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) +} diff --git a/crates/bevy_render/src/render_component.rs b/crates/bevy_render/src/extract_component.rs similarity index 100% rename from crates/bevy_render/src/render_component.rs rename to crates/bevy_render/src/extract_component.rs diff --git a/crates/bevy_render/src/extract_resource.rs b/crates/bevy_render/src/extract_resource.rs new file mode 100644 index 0000000000000..5db3be904b263 --- /dev/null +++ b/crates/bevy_render/src/extract_resource.rs @@ -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(PhantomData); + +impl Default for ExtractResourcePlugin { + fn default() -> Self { + Self(PhantomData) + } +} + +impl Plugin for ExtractResourcePlugin { + 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::); + } + } +} + +/// This system extracts the resource of the corresponding [`Resource`] type +/// by cloning it. +pub fn extract_resource(mut commands: Commands, resource: Res) { + if resource.is_changed() { + commands.insert_resource(R::extract_resource(resource.into_inner())); + } +} diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index cb0d4b49eeff4..1dadbf149a6a9 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -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; diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 0403abb46136e..80de0df9378e5 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -10,6 +10,7 @@ pub use window::*; use crate::{ camera::ExtractedCamera, + extract_resource::{ExtractResource, ExtractResourcePlugin}, prelude::Image, render_asset::RenderAssets, render_resource::{DynamicUniformBuffer, ShaderType, Texture, TextureView}, @@ -27,12 +28,14 @@ pub struct ViewPlugin; impl Plugin for ViewPlugin { fn build(&self, app: &mut App) { - app.init_resource::().add_plugin(VisibilityPlugin); + app.init_resource::() + // NOTE: windows.is_changed() handles cases where a window was resized + .add_plugin(ExtractResourcePlugin::::default()) + .add_plugin(VisibilityPlugin); if let Ok(render_app) = app.get_sub_app_mut(RenderApp) { render_app .init_resource::() - .add_system_to_stage(RenderStage::Extract, extract_msaa) .add_system_to_stage(RenderStage::Prepare, prepare_view_uniforms) .add_system_to_stage( RenderStage::Prepare, @@ -42,7 +45,7 @@ impl Plugin for ViewPlugin { } } -#[derive(Clone)] +#[derive(Clone, ExtractResource)] /// Configuration resource for [Multi-Sample Anti-Aliasing](https://en.wikipedia.org/wiki/Multisample_anti-aliasing). /// /// # Example @@ -70,11 +73,6 @@ impl Default for Msaa { } } -pub fn extract_msaa(mut commands: Commands, msaa: Res) { - // NOTE: windows.is_changed() handles cases where a window was resized - commands.insert_resource(msaa.clone()); -} - #[derive(Component)] pub struct ExtractedView { pub projection: Mat4, diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 77cb3c4ebe92f..bd938c91dbd96 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -12,9 +12,9 @@ use bevy_ecs::{ }; use bevy_log::error; 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, diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index dee3d52134162..de9376ea9cbc8 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -7,9 +7,9 @@ use bevy_ecs::{ use bevy_math::{Mat4, Vec2}; use bevy_reflect::{Reflect, TypeUuid}; use bevy_render::{ + extract_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin}, mesh::{GpuBufferInfo, Mesh, MeshVertexBufferLayout}, render_asset::RenderAssets, - render_component::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin}, render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass}, render_resource::*, renderer::{RenderDevice, RenderQueue}, diff --git a/examples/shader/compute_shader_game_of_life.rs b/examples/shader/compute_shader_game_of_life.rs index d45f9baf5bf76..e9cd037a9c723 100644 --- a/examples/shader/compute_shader_game_of_life.rs +++ b/examples/shader/compute_shader_game_of_life.rs @@ -7,6 +7,7 @@ use bevy::{ core_pipeline::node::MAIN_PASS_DEPENDENCIES, prelude::*, render::{ + extract_resource::{ExtractResource, ExtractResourcePlugin}, render_asset::RenderAssets, render_graph::{self, RenderGraph}, render_resource::*, @@ -66,10 +67,12 @@ pub struct GameOfLifeComputePlugin; impl Plugin for GameOfLifeComputePlugin { fn build(&self, app: &mut App) { + // Extract the game of life image resource from the main world into the render world + // for operation on by the compute shader and display on the sprite. + app.add_plugin(ExtractResourcePlugin::::default()); let render_app = app.sub_app_mut(RenderApp); render_app .init_resource::() - .add_system_to_stage(RenderStage::Extract, extract_game_of_life_image) .add_system_to_stage(RenderStage::Queue, queue_bind_group); let mut render_graph = render_app.world.resource_mut::(); @@ -80,15 +83,11 @@ impl Plugin for GameOfLifeComputePlugin { } } -#[derive(Deref)] +#[derive(Clone, Deref, ExtractResource)] struct GameOfLifeImage(Handle); struct GameOfLifeImageBindGroup(BindGroup); -fn extract_game_of_life_image(mut commands: Commands, image: Res) { - commands.insert_resource(GameOfLifeImage(image.clone())); -} - fn queue_bind_group( mut commands: Commands, pipeline: Res, diff --git a/examples/shader/shader_defs.rs b/examples/shader/shader_defs.rs index f96579663ef70..4997051b0c36a 100644 --- a/examples/shader/shader_defs.rs +++ b/examples/shader/shader_defs.rs @@ -8,9 +8,9 @@ use bevy::{ }, prelude::*, render::{ + extract_component::{ExtractComponent, ExtractComponentPlugin}, mesh::MeshVertexBufferLayout, render_asset::RenderAssets, - render_component::{ExtractComponent, ExtractComponentPlugin}, render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline}, render_resource::{ PipelineCache, RenderPipelineDescriptor, SpecializedMeshPipeline, diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 3dce17c00587d..52d641ed2bffa 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -7,9 +7,9 @@ use bevy::{ pbr::{MeshPipeline, MeshPipelineKey, MeshUniform, SetMeshBindGroup, SetMeshViewBindGroup}, prelude::*, render::{ + extract_component::{ExtractComponent, ExtractComponentPlugin}, mesh::{GpuBufferInfo, MeshVertexBufferLayout}, render_asset::RenderAssets, - render_component::{ExtractComponent, ExtractComponentPlugin}, render_phase::{ AddRenderCommand, DrawFunctions, EntityRenderCommand, RenderCommandResult, RenderPhase, SetItemPipeline, TrackedRenderPass, diff --git a/tools/publish.sh b/tools/publish.sh index e2677c5d70ff3..b20be5daff5d1 100644 --- a/tools/publish.sh +++ b/tools/publish.sh @@ -22,6 +22,7 @@ crates=( bevy_transform bevy_window bevy_encase_derive + bevy_render/macros bevy_render bevy_core_pipeline bevy_input