Run a clear pass on Windows without any Views (#3304)

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.
This commit is contained in:
Carter Anderson 2021-12-12 21:08:04 +00:00
parent 172f4d6d78
commit fe9b5003c5

View file

@ -1,15 +1,27 @@
use std::collections::HashSet;
use crate::ClearColor; use crate::ClearColor;
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
use bevy_render2::{ use bevy_render2::{
camera::ExtractedCamera,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo}, render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo},
render_resource::{LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor}, render_resource::{
LoadOp, Operations, RenderPassColorAttachment, RenderPassDepthStencilAttachment,
RenderPassDescriptor,
},
renderer::RenderContext, renderer::RenderContext,
view::{ExtractedView, ViewDepthTexture, ViewTarget}, view::{ExtractedView, ExtractedWindows, ViewDepthTexture, ViewTarget},
}; };
pub struct ClearPassNode { pub struct ClearPassNode {
query: query: QueryState<
QueryState<(&'static ViewTarget, Option<&'static ViewDepthTexture>), With<ExtractedView>>, (
&'static ViewTarget,
Option<&'static ViewDepthTexture>,
Option<&'static ExtractedCamera>,
),
With<ExtractedView>,
>,
} }
impl ClearPassNode { impl ClearPassNode {
@ -35,9 +47,17 @@ impl Node for ClearPassNode {
render_context: &mut RenderContext, render_context: &mut RenderContext,
world: &World, world: &World,
) -> Result<(), NodeRunError> { ) -> Result<(), NodeRunError> {
/* This gets all ViewTargets and ViewDepthTextures and clears its attachments */ let mut cleared_windows = HashSet::new();
for (target, depth) in self.query.iter_manual(world) {
let clear_color = world.get_resource::<ClearColor>().unwrap(); 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 { let pass_descriptor = RenderPassDescriptor {
label: Some("clear_pass"), label: Some("clear_pass"),
color_attachments: &[target.get_color_attachment(Operations { color_attachments: &[target.get_color_attachment(Operations {
@ -59,6 +79,33 @@ impl Node for ClearPassNode {
.begin_render_pass(&pass_descriptor); .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(()) Ok(())
} }
} }