From 3faca93f7bf83e4d6f81cffbac7f070809c07023 Mon Sep 17 00:00:00 2001 From: Zicklag Date: Thu, 19 Aug 2021 20:34:31 +0000 Subject: [PATCH] Add ClearColor Resource to Pipelined Renderer (#2631) # Objective - Allow the user to set the clear color when using the pipelined renderer ## Solution - Add a `ClearColor` resource that can be added to the world to configure the clear color ## Remaining Issues Currently the `ClearColor` resource is cloned from the app world to the render world every frame. There are two ways I can think of around this: 1. Figure out why `app_world.is_resource_changed::()` always returns `true` in the `extract` step and fix it so that we are only updating the resource when it changes 2. Require the users to add the `ClearColor` resource to the render sub-app instead of the parent app. This is currently sub-optimal until we have labled sub-apps, and probably a helper funciton on `App` such as `app.with_sub_app(RenderApp, |app| { ... })`. Even if we had that, I think it would be more than we want the user to have to think about. They shouldn't have to know about the render sub-app I don't think. I think the first option is the best, but I could really use some help figuring out the nuance of why `is_resource_changed` is always returning true in that context. --- Cargo.toml | 4 +++ examples/README.md | 1 + examples/window/clear_color_pipelined.rs | 25 +++++++++++++++++++ pipelined/bevy_core_pipeline/src/lib.rs | 24 +++++++++++++++++- .../bevy_core_pipeline/src/main_pass_2d.rs | 6 ++--- .../bevy_core_pipeline/src/main_pass_3d.rs | 6 ++--- 6 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 examples/window/clear_color_pipelined.rs diff --git a/Cargo.toml b/Cargo.toml index 94cf3d143d..d38ff3375f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -493,6 +493,10 @@ path = "examples/ui/ui.rs" name = "clear_color" path = "examples/window/clear_color.rs" +[[example]] +name = "clear_color_pipelined" +path = "examples/window/clear_color_pipelined.rs" + [[example]] name = "multiple_windows" path = "examples/window/multiple_windows.rs" diff --git a/examples/README.md b/examples/README.md index ae09301681..40e82fb112 100644 --- a/examples/README.md +++ b/examples/README.md @@ -254,6 +254,7 @@ Example | File | Description Example | File | Description --- | --- | --- `clear_color` | [`window/clear_color.rs`](./window/clear_color.rs) | Creates a solid color window +`clear_color_pipelined` | [`window/clear_color_pipelined.rs`](./window/clear_color_pipelined.rs) | Creates a solid color window with the pipelined renderer `multiple_windows` | [`window/multiple_windows.rs`](./window/multiple_windows.rs) | Creates two windows and cameras viewing the same mesh `scale_factor_override` | [`window/scale_factor_override.rs`](./window/scale_factor_override.rs) | Illustrates how to customize the default window settings `window_settings` | [`window/window_settings.rs`](./window/window_settings.rs) | Demonstrates customizing default window settings diff --git a/examples/window/clear_color_pipelined.rs b/examples/window/clear_color_pipelined.rs new file mode 100644 index 0000000000..a2b020119a --- /dev/null +++ b/examples/window/clear_color_pipelined.rs @@ -0,0 +1,25 @@ +use bevy::{ + core_pipeline::ClearColor, + prelude::*, + render2::{camera::OrthographicCameraBundle, color::Color}, + PipelinedDefaultPlugins, +}; + +fn main() { + App::new() + .insert_resource(ClearColor(Color::rgb(0.5, 0.5, 0.9))) + .add_plugins(PipelinedDefaultPlugins) + .add_startup_system(setup) + .add_system(change_clear_color) + .run(); +} + +fn setup(mut commands: Commands) { + commands.spawn_bundle(OrthographicCameraBundle::new_2d()); +} + +fn change_clear_color(input: Res>, mut clear_color: ResMut) { + if input.just_pressed(KeyCode::Space) { + clear_color.0 = Color::PURPLE; + } +} diff --git a/pipelined/bevy_core_pipeline/src/lib.rs b/pipelined/bevy_core_pipeline/src/lib.rs index 5101276733..554ac46fc9 100644 --- a/pipelined/bevy_core_pipeline/src/lib.rs +++ b/pipelined/bevy_core_pipeline/src/lib.rs @@ -10,6 +10,7 @@ use bevy_app::{App, Plugin}; use bevy_ecs::prelude::*; use bevy_render2::{ camera::{ActiveCameras, CameraPlugin}, + color::Color, render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType}, render_phase::{sort_phase_system, RenderPhase}, render_resource::{ @@ -19,9 +20,19 @@ use bevy_render2::{ renderer::RenderDevice, texture::TextureCache, view::{ExtractedView, ViewPlugin}, - RenderStage, + RenderStage, RenderWorld, }; +/// Resource that configures the clear color +#[derive(Clone, Debug)] +pub struct ClearColor(pub Color); + +impl Default for ClearColor { + fn default() -> Self { + Self(Color::rgb(0.4, 0.4, 0.4)) + } +} + // Plugins that contribute to the RenderGraph should use the following label conventions: // 1. Graph modules should have a NAME, input module, and node module (where relevant) // 2. The "top level" graph is the plugin module root. Just add things like `pub mod node` directly under the plugin module @@ -61,8 +72,11 @@ pub struct CorePipelinePlugin; impl Plugin for CorePipelinePlugin { fn build(&self, app: &mut App) { + app.init_resource::(); + let render_app = app.sub_app_mut(0); render_app + .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( @@ -154,6 +168,14 @@ pub struct ViewDepthTexture { pub view: TextureView, } +pub fn extract_clear_color(clear_color: 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()) + } +} + pub fn extract_core_pipeline_camera_phases( mut commands: Commands, active_cameras: Res, diff --git a/pipelined/bevy_core_pipeline/src/main_pass_2d.rs b/pipelined/bevy_core_pipeline/src/main_pass_2d.rs index 50f91bdb97..e21d2dac5f 100644 --- a/pipelined/bevy_core_pipeline/src/main_pass_2d.rs +++ b/pipelined/bevy_core_pipeline/src/main_pass_2d.rs @@ -1,7 +1,6 @@ -use crate::Transparent2dPhase; +use crate::{ClearColor, Transparent2dPhase}; use bevy_ecs::prelude::*; use bevy_render2::{ - color::Color, render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass}, render_resource::{LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor}, @@ -43,13 +42,14 @@ impl Node for MainPass2dNode { world: &World, ) -> Result<(), NodeRunError> { let color_attachment_texture = graph.get_input_texture(Self::IN_COLOR_ATTACHMENT)?; + let clear_color = world.get_resource::().unwrap(); let pass_descriptor = RenderPassDescriptor { label: Some("main_pass_2d"), color_attachments: &[RenderPassColorAttachment { view: color_attachment_texture, resolve_target: None, ops: Operations { - load: LoadOp::Clear(Color::rgb(0.4, 0.4, 0.4).into()), + load: LoadOp::Clear(clear_color.0.into()), store: true, }, }], diff --git a/pipelined/bevy_core_pipeline/src/main_pass_3d.rs b/pipelined/bevy_core_pipeline/src/main_pass_3d.rs index c561bac2f3..a3d0438d76 100644 --- a/pipelined/bevy_core_pipeline/src/main_pass_3d.rs +++ b/pipelined/bevy_core_pipeline/src/main_pass_3d.rs @@ -1,7 +1,6 @@ -use crate::Transparent3dPhase; +use crate::{ClearColor, Transparent3dPhase}; use bevy_ecs::prelude::*; use bevy_render2::{ - color::Color, render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass}, render_resource::{ @@ -48,6 +47,7 @@ impl Node for MainPass3dNode { world: &World, ) -> Result<(), NodeRunError> { let color_attachment_texture = graph.get_input_texture(Self::IN_COLOR_ATTACHMENT)?; + let clear_color = world.get_resource::().unwrap(); let depth_texture = graph.get_input_texture(Self::IN_DEPTH)?; let pass_descriptor = RenderPassDescriptor { label: Some("main_pass_3d"), @@ -55,7 +55,7 @@ impl Node for MainPass3dNode { view: color_attachment_texture, resolve_target: None, ops: Operations { - load: LoadOp::Clear(Color::rgb(0.4, 0.4, 0.4).into()), + load: LoadOp::Clear(clear_color.0.into()), store: true, }, }],