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::<ClearColor>()` 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.
This commit is contained in:
Zicklag 2021-08-19 20:34:31 +00:00
parent 5fa0b5b498
commit 3faca93f7b
6 changed files with 59 additions and 7 deletions

View file

@ -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"

View file

@ -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

View file

@ -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<Input<KeyCode>>, mut clear_color: ResMut<ClearColor>) {
if input.just_pressed(KeyCode::Space) {
clear_color.0 = Color::PURPLE;
}
}

View file

@ -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::<ClearColor>();
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<ClearColor>, 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())
}
}
pub fn extract_core_pipeline_camera_phases(
mut commands: Commands,
active_cameras: Res<ActiveCameras>,

View file

@ -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::<ClearColor>().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,
},
}],

View file

@ -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::<ClearColor>().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,
},
}],