Skip to content

Commit

Permalink
Run a clear pass on Windows without any Views (#3304)
Browse files Browse the repository at this point in the history
Fixes #3043 

`surface_texture.present()` will cause panics if no work is done on a given frame. "Views" are how we queue up work. Without any cameras, no work is produced. This adds a "clear pass" for windows without views, which ensures we clear windows (thus doing work) every frame.

This is a "quick fix". It can be made much cleaner once we make "render targets" a concept and move some responsibilities around. Then we just clear the "render target" once instead of clearing "views". I _might_ have time to tackle that work prior to 0.6, but I doubt it. If "render targets" don't make it in to 0.6, they will be one of the first things I tackle after release.
  • Loading branch information
cart committed Dec 12, 2021
1 parent 172f4d6 commit fe9b500
Showing 1 changed file with 54 additions and 7 deletions.
61 changes: 54 additions & 7 deletions pipelined/bevy_core_pipeline/src/clear_pass.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
use std::collections::HashSet;

use crate::ClearColor;
use bevy_ecs::prelude::*;
use bevy_render2::{
camera::ExtractedCamera,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo},
render_resource::{LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor},
render_resource::{
LoadOp, Operations, RenderPassColorAttachment, RenderPassDepthStencilAttachment,
RenderPassDescriptor,
},
renderer::RenderContext,
view::{ExtractedView, ViewDepthTexture, ViewTarget},
view::{ExtractedView, ExtractedWindows, ViewDepthTexture, ViewTarget},
};

pub struct ClearPassNode {
query:
QueryState<(&'static ViewTarget, Option<&'static ViewDepthTexture>), With<ExtractedView>>,
query: QueryState<
(
&'static ViewTarget,
Option<&'static ViewDepthTexture>,
Option<&'static ExtractedCamera>,
),
With<ExtractedView>,
>,
}

impl ClearPassNode {
Expand All @@ -35,9 +47,17 @@ impl Node for ClearPassNode {
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
/* This gets all ViewTargets and ViewDepthTextures and clears its attachments */
for (target, depth) in self.query.iter_manual(world) {
let clear_color = world.get_resource::<ClearColor>().unwrap();
let mut cleared_windows = HashSet::new();
let clear_color = world.get_resource::<ClearColor>().unwrap();

// This gets all ViewTargets and ViewDepthTextures and clears its attachments
// TODO: This has the potential to clear the same target multiple times, if there
// are multiple views drawing to the same target. This should be fixed when we make
// clearing happen on "render targets" instead of "views" (see the TODO below for more context).
for (target, depth, camera) in self.query.iter_manual(world) {
if let Some(camera) = camera {
cleared_windows.insert(camera.window_id);
}
let pass_descriptor = RenderPassDescriptor {
label: Some("clear_pass"),
color_attachments: &[target.get_color_attachment(Operations {
Expand All @@ -59,6 +79,33 @@ impl Node for ClearPassNode {
.begin_render_pass(&pass_descriptor);
}

// TODO: This is a hack to ensure we don't call present() on frames without any work,
// which will cause panics. The real fix here is to clear "render targets" directly
// instead of "views". This should be removed once full RenderTargets are implemented.
let windows = world.get_resource::<ExtractedWindows>().unwrap();
for window in windows.values() {
// skip windows that have already been cleared
if cleared_windows.contains(&window.id) {
continue;
}
let pass_descriptor = RenderPassDescriptor {
label: Some("clear_pass"),
color_attachments: &[RenderPassColorAttachment {
view: window.swap_chain_texture.as_ref().unwrap(),
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(clear_color.0.into()),
store: true,
},
}],
depth_stencil_attachment: None,
};

render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
}

Ok(())
}
}

0 comments on commit fe9b500

Please sign in to comment.