Don't ignore draw errors (#13240)

# Objective

- It's possible to have errors in a draw command, but these errors are
ignored

## Solution

- Return a result with the error

## Changelog

Renamed `RenderCommandResult::Failure` to `RenderCommandResult::Skip`
Added a `reason` string parameter to `RenderCommandResult::Failure`

## Migration Guide
If you were using `RenderCommandResult::Failure` to just ignore an error
and retry later, use `RenderCommandResult::Skip` instead.

This wasn't intentional, but this PR should also help with
https://github.com/bevyengine/bevy/issues/12660 since we can turn a few
unwraps into error messages now.

---------

Co-authored-by: Charlotte McElwain <charlotte.c.mcelwain@gmail.com>
This commit is contained in:
IceSentry 2024-07-22 15:22:30 -04:00 committed by GitHub
parent ee88d79d88
commit 3faca1e549
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 170 additions and 82 deletions

View file

@ -58,7 +58,7 @@ impl ViewNode for MainTransparentPass2dNode {
} }
if !transparent_phase.items.is_empty() { if !transparent_phase.items.is_empty() {
transparent_phase.render(&mut render_pass, world, view_entity); transparent_phase.render(&mut render_pass, world, view_entity)?;
} }
pass_span.end(&mut render_pass); pass_span.end(&mut render_pass);

View file

@ -12,6 +12,7 @@ use bevy_render::{
renderer::RenderContext, renderer::RenderContext,
view::{ViewDepthTexture, ViewTarget, ViewUniformOffset}, view::{ViewDepthTexture, ViewTarget, ViewUniformOffset},
}; };
use bevy_utils::tracing::error;
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
use bevy_utils::tracing::info_span; use bevy_utils::tracing::info_span;
@ -95,14 +96,18 @@ impl ViewNode for MainOpaquePass3dNode {
if !opaque_phase.is_empty() { if !opaque_phase.is_empty() {
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
let _opaque_main_pass_3d_span = info_span!("opaque_main_pass_3d").entered(); let _opaque_main_pass_3d_span = info_span!("opaque_main_pass_3d").entered();
opaque_phase.render(&mut render_pass, world, view_entity); if let Err(err) = opaque_phase.render(&mut render_pass, world, view_entity) {
error!("Error encountered while rendering the opaque phase {err:?}");
}
} }
// Alpha draws // Alpha draws
if !alpha_mask_phase.is_empty() { if !alpha_mask_phase.is_empty() {
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
let _alpha_mask_main_pass_3d_span = info_span!("alpha_mask_main_pass_3d").entered(); let _alpha_mask_main_pass_3d_span = info_span!("alpha_mask_main_pass_3d").entered();
alpha_mask_phase.render(&mut render_pass, world, view_entity); if let Err(err) = alpha_mask_phase.render(&mut render_pass, world, view_entity) {
error!("Error encountered while rendering the alpha mask phase {err:?}");
}
} }
// Skybox draw using a fullscreen triangle // Skybox draw using a fullscreen triangle

View file

@ -9,6 +9,7 @@ use bevy_render::{
renderer::RenderContext, renderer::RenderContext,
view::{ViewDepthTexture, ViewTarget}, view::{ViewDepthTexture, ViewTarget},
}; };
use bevy_utils::tracing::error;
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
use bevy_utils::tracing::info_span; use bevy_utils::tracing::info_span;
use std::ops::Range; use std::ops::Range;
@ -98,7 +99,11 @@ impl ViewNode for MainTransmissivePass3dNode {
} }
// render items in range // render items in range
transmissive_phase.render_range(&mut render_pass, world, view_entity, range); if let Err(err) =
transmissive_phase.render_range(&mut render_pass, world, view_entity, range)
{
error!("Error encountered while rendering the transmissive phase {err:?}");
}
} }
} else { } else {
let mut render_pass = let mut render_pass =
@ -108,7 +113,9 @@ impl ViewNode for MainTransmissivePass3dNode {
render_pass.set_camera_viewport(viewport); render_pass.set_camera_viewport(viewport);
} }
transmissive_phase.render(&mut render_pass, world, view_entity); if let Err(err) = transmissive_phase.render(&mut render_pass, world, view_entity) {
error!("Error encountered while rendering the transmissive phase {err:?}");
}
} }
} }

View file

@ -9,6 +9,7 @@ use bevy_render::{
renderer::RenderContext, renderer::RenderContext,
view::{ViewDepthTexture, ViewTarget}, view::{ViewDepthTexture, ViewTarget},
}; };
use bevy_utils::tracing::error;
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
use bevy_utils::tracing::info_span; use bevy_utils::tracing::info_span;
@ -70,7 +71,9 @@ impl ViewNode for MainTransparentPass3dNode {
render_pass.set_camera_viewport(viewport); render_pass.set_camera_viewport(viewport);
} }
transparent_phase.render(&mut render_pass, world, view_entity); if let Err(err) = transparent_phase.render(&mut render_pass, world, view_entity) {
error!("Error encountered while rendering the transparent phase {err:?}");
}
pass_span.end(&mut render_pass); pass_span.end(&mut render_pass);
} }

View file

@ -11,6 +11,7 @@ use bevy_render::{
renderer::RenderContext, renderer::RenderContext,
view::ViewDepthTexture, view::ViewDepthTexture,
}; };
use bevy_utils::tracing::error;
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
use bevy_utils::tracing::info_span; use bevy_utils::tracing::info_span;
@ -149,14 +150,23 @@ impl ViewNode for DeferredGBufferPrepassNode {
{ {
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
let _opaque_prepass_span = info_span!("opaque_deferred_prepass").entered(); let _opaque_prepass_span = info_span!("opaque_deferred_prepass").entered();
opaque_deferred_phase.render(&mut render_pass, world, view_entity); if let Err(err) = opaque_deferred_phase.render(&mut render_pass, world, view_entity)
{
error!("Error encountered while rendering the opaque deferred phase {err:?}");
}
} }
// Alpha masked draws // Alpha masked draws
if !alpha_mask_deferred_phase.is_empty() { if !alpha_mask_deferred_phase.is_empty() {
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
let _alpha_mask_deferred_span = info_span!("alpha_mask_deferred_prepass").entered(); let _alpha_mask_deferred_span = info_span!("alpha_mask_deferred_prepass").entered();
alpha_mask_deferred_phase.render(&mut render_pass, world, view_entity); if let Err(err) =
alpha_mask_deferred_phase.render(&mut render_pass, world, view_entity)
{
error!(
"Error encountered while rendering the alpha mask deferred phase {err:?}"
);
}
} }
drop(render_pass); drop(render_pass);

View file

@ -9,6 +9,7 @@ use bevy_render::{
renderer::RenderContext, renderer::RenderContext,
view::{ViewDepthTexture, ViewUniformOffset}, view::{ViewDepthTexture, ViewUniformOffset},
}; };
use bevy_utils::tracing::error;
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
use bevy_utils::tracing::info_span; use bevy_utils::tracing::info_span;
@ -125,14 +126,23 @@ impl ViewNode for PrepassNode {
{ {
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
let _opaque_prepass_span = info_span!("opaque_prepass").entered(); let _opaque_prepass_span = info_span!("opaque_prepass").entered();
opaque_prepass_phase.render(&mut render_pass, world, view_entity); if let Err(err) = opaque_prepass_phase.render(&mut render_pass, world, view_entity)
{
error!("Error encountered while rendering the opaque prepass phase {err:?}");
}
} }
// Alpha masked draws // Alpha masked draws
if !alpha_mask_prepass_phase.is_empty() { if !alpha_mask_prepass_phase.is_empty() {
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
let _alpha_mask_prepass_span = info_span!("alpha_mask_prepass").entered(); let _alpha_mask_prepass_span = info_span!("alpha_mask_prepass").entered();
alpha_mask_prepass_phase.render(&mut render_pass, world, view_entity); if let Err(err) =
alpha_mask_prepass_phase.render(&mut render_pass, world, view_entity)
{
error!(
"Error encountered while rendering the alpha mask prepass phase {err:?}"
);
}
} }
// Skybox draw using a fullscreen triangle // Skybox draw using a fullscreen triangle

View file

@ -540,7 +540,7 @@ impl<const I: usize, P: PhaseItem> RenderCommand<P> for SetLineGizmoBindGroup<I>
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
let Some(uniform_index) = uniform_index else { let Some(uniform_index) = uniform_index else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
pass.set_bind_group( pass.set_bind_group(
I, I,
@ -566,10 +566,10 @@ impl<P: PhaseItem> RenderCommand<P> for DrawLineGizmo {
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
let Some(handle) = handle else { let Some(handle) = handle else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
let Some(line_gizmo) = line_gizmos.into_inner().get(handle) else { let Some(line_gizmo) = line_gizmos.into_inner().get(handle) else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
if line_gizmo.vertex_count < 2 { if line_gizmo.vertex_count < 2 {
@ -612,10 +612,10 @@ impl<P: PhaseItem> RenderCommand<P> for DrawLineJointGizmo {
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
let Some(handle) = handle else { let Some(handle) = handle else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
let Some(line_gizmo) = line_gizmos.into_inner().get(handle) else { let Some(line_gizmo) = line_gizmos.into_inner().get(handle) else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
if line_gizmo.vertex_count <= 2 || !line_gizmo.strip { if line_gizmo.vertex_count <= 2 || !line_gizmo.strip {

View file

@ -456,10 +456,10 @@ impl<P: PhaseItem, M: Material, const I: usize> RenderCommand<P> for SetMaterial
let material_instances = material_instances.into_inner(); let material_instances = material_instances.into_inner();
let Some(material_asset_id) = material_instances.get(&item.entity()) else { let Some(material_asset_id) = material_instances.get(&item.entity()) else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
let Some(material) = materials.get(*material_asset_id) else { let Some(material) = materials.get(*material_asset_id) else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
pass.set_bind_group(I, &material.bind_group, &[]); pass.set_bind_group(I, &material.bind_group, &[]);
RenderCommandResult::Success RenderCommandResult::Success

View file

@ -1445,7 +1445,11 @@ impl Node for ShadowPassNode {
let pass_span = let pass_span =
diagnostics.pass_span(&mut render_pass, view_light.pass_name.clone()); diagnostics.pass_span(&mut render_pass, view_light.pass_name.clone());
shadow_phase.render(&mut render_pass, world, view_light_entity); if let Err(err) =
shadow_phase.render(&mut render_pass, world, view_light_entity)
{
error!("Error encountered while rendering the shadow phase {err:?}");
}
pass_span.end(&mut render_pass); pass_span.end(&mut render_pass);
drop(render_pass); drop(render_pass);

View file

@ -2261,12 +2261,11 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshBindGroup<I> {
is_morphed, is_morphed,
has_motion_vector_prepass, has_motion_vector_prepass,
) else { ) else {
error!( return RenderCommandResult::Failure(
"The MeshBindGroups resource wasn't set in the render phase. \ "The MeshBindGroups resource wasn't set in the render phase. \
It should be set by the prepare_mesh_bind_group system.\n\ It should be set by the prepare_mesh_bind_group system.\n\
This is a bevy bug! Please open an issue." This is a bevy bug! Please open an issue.",
); );
return RenderCommandResult::Failure;
}; };
let mut dynamic_offsets: [u32; 3] = Default::default(); let mut dynamic_offsets: [u32; 3] = Default::default();
@ -2349,7 +2348,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
if !has_preprocess_bind_group if !has_preprocess_bind_group
|| !preprocess_pipelines.pipelines_are_loaded(&pipeline_cache) || !preprocess_pipelines.pipelines_are_loaded(&pipeline_cache)
{ {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
} }
} }
@ -2359,13 +2358,13 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
let mesh_allocator = mesh_allocator.into_inner(); let mesh_allocator = mesh_allocator.into_inner();
let Some(mesh_asset_id) = mesh_instances.mesh_asset_id(item.entity()) else { let Some(mesh_asset_id) = mesh_instances.mesh_asset_id(item.entity()) else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
let Some(gpu_mesh) = meshes.get(mesh_asset_id) else { let Some(gpu_mesh) = meshes.get(mesh_asset_id) else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(&mesh_asset_id) else { let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(&mesh_asset_id) else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
// Calculate the indirect offset, and look up the buffer. // Calculate the indirect offset, and look up the buffer.
@ -2374,7 +2373,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
Some(index) => match indirect_parameters_buffer.buffer() { Some(index) => match indirect_parameters_buffer.buffer() {
None => { None => {
warn!("Not rendering mesh because indirect parameters buffer wasn't present"); warn!("Not rendering mesh because indirect parameters buffer wasn't present");
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
} }
Some(buffer) => Some(( Some(buffer) => Some((
index as u64 * mem::size_of::<IndirectParameters>() as u64, index as u64 * mem::size_of::<IndirectParameters>() as u64,
@ -2395,7 +2394,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
} => { } => {
let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(&mesh_asset_id) let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(&mesh_asset_id)
else { else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format); pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format);

View file

@ -3,6 +3,7 @@ use crate::{
Edge, InputSlotError, OutputSlotError, RenderGraphContext, RenderGraphError, Edge, InputSlotError, OutputSlotError, RenderGraphContext, RenderGraphError,
RunSubGraphError, SlotInfo, SlotInfos, RunSubGraphError, SlotInfo, SlotInfos,
}, },
render_phase::DrawError,
renderer::RenderContext, renderer::RenderContext,
}; };
pub use bevy_ecs::label::DynEq; pub use bevy_ecs::label::DynEq;
@ -97,6 +98,8 @@ pub enum NodeRunError {
OutputSlotError(#[from] OutputSlotError), OutputSlotError(#[from] OutputSlotError),
#[error("encountered an error when running a sub-graph")] #[error("encountered an error when running a sub-graph")]
RunSubGraphError(#[from] RunSubGraphError), RunSubGraphError(#[from] RunSubGraphError),
#[error("encountered an error when executing draw command")]
DrawError(#[from] DrawError),
} }
/// A collection of input and output [`Edges`](Edge) for a [`Node`]. /// A collection of input and output [`Edges`](Edge) for a [`Node`].

View file

@ -2,7 +2,7 @@ use crate::render_phase::{PhaseItem, TrackedRenderPass};
use bevy_app::{App, SubApp}; use bevy_app::{App, SubApp};
use bevy_ecs::{ use bevy_ecs::{
entity::Entity, entity::Entity,
query::{QueryState, ROQueryItem, ReadOnlyQueryData}, query::{QueryEntityError, QueryState, ROQueryItem, ReadOnlyQueryData},
system::{ReadOnlySystemParam, Resource, SystemParam, SystemParamItem, SystemState}, system::{ReadOnlySystemParam, Resource, SystemParam, SystemParamItem, SystemState},
world::World, world::World,
}; };
@ -13,6 +13,7 @@ use std::{
hash::Hash, hash::Hash,
sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}, sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard},
}; };
use thiserror::Error;
/// A draw function used to draw [`PhaseItem`]s. /// A draw function used to draw [`PhaseItem`]s.
/// ///
@ -34,7 +35,17 @@ pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
view: Entity, view: Entity,
item: &P, item: &P,
); ) -> Result<(), DrawError>;
}
#[derive(Error, Debug, PartialEq, Eq)]
pub enum DrawError {
#[error("Failed to execute render command {0:?}")]
RenderCommandFailure(&'static str),
#[error("Failed to get execute view query")]
InvalidViewQuery,
#[error("View entity not found")]
ViewEntityNotFound,
} }
// TODO: make this generic? // TODO: make this generic?
@ -212,7 +223,8 @@ pub trait RenderCommand<P: PhaseItem> {
#[derive(Debug)] #[derive(Debug)]
pub enum RenderCommandResult { pub enum RenderCommandResult {
Success, Success,
Failure, Skip,
Failure(&'static str),
} }
macro_rules! render_command_tuple_impl { macro_rules! render_command_tuple_impl {
@ -232,14 +244,22 @@ macro_rules! render_command_tuple_impl {
) -> RenderCommandResult { ) -> RenderCommandResult {
match maybe_entities { match maybe_entities {
None => { None => {
$(if let RenderCommandResult::Failure = $name::render(_item, $view, None, $name, _pass) { $(
return RenderCommandResult::Failure; match $name::render(_item, $view, None, $name, _pass) {
})* RenderCommandResult::Skip => return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) => return RenderCommandResult::Failure(reason),
_ => {},
}
)*
} }
Some(($($entity,)*)) => { Some(($($entity,)*)) => {
$(if let RenderCommandResult::Failure = $name::render(_item, $view, Some($entity), $name, _pass) { $(
return RenderCommandResult::Failure; match $name::render(_item, $view, Some($entity), $name, _pass) {
})* RenderCommandResult::Skip => return RenderCommandResult::Skip,
RenderCommandResult::Failure(reason) => return RenderCommandResult::Failure(reason),
_ => {},
}
)*
} }
} }
RenderCommandResult::Success RenderCommandResult::Success
@ -290,12 +310,23 @@ where
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
view: Entity, view: Entity,
item: &P, item: &P,
) { ) -> Result<(), DrawError> {
let param = self.state.get_manual(world); let param = self.state.get_manual(world);
let view = self.view.get_manual(world, view).unwrap(); let view = match self.view.get_manual(world, view) {
Ok(view) => view,
Err(err) => match err {
QueryEntityError::NoSuchEntity(_) => return Err(DrawError::ViewEntityNotFound),
QueryEntityError::QueryDoesNotMatch(_) | QueryEntityError::AliasedMutability(_) => {
return Err(DrawError::InvalidViewQuery)
}
},
};
let entity = self.entity.get_manual(world, item.entity()).ok(); let entity = self.entity.get_manual(world, item.entity()).ok();
// TODO: handle/log `RenderCommand` failure match C::render(item, view, entity, param, pass) {
C::render(item, view, entity, param, pass); RenderCommandResult::Success | RenderCommandResult::Skip => Ok(()),
RenderCommandResult::Failure(reason) => Err(DrawError::RenderCommandFailure(reason)),
}
} }
} }

View file

@ -314,7 +314,7 @@ where
render_pass: &mut TrackedRenderPass<'w>, render_pass: &mut TrackedRenderPass<'w>,
world: &'w World, world: &'w World,
view: Entity, view: Entity,
) { ) -> Result<(), DrawError> {
{ {
let draw_functions = world.resource::<DrawFunctions<BPI>>(); let draw_functions = world.resource::<DrawFunctions<BPI>>();
let mut draw_functions = draw_functions.write(); let mut draw_functions = draw_functions.write();
@ -323,9 +323,11 @@ where
// locks. // locks.
} }
self.render_batchable_meshes(render_pass, world, view); self.render_batchable_meshes(render_pass, world, view)?;
self.render_unbatchable_meshes(render_pass, world, view); self.render_unbatchable_meshes(render_pass, world, view)?;
self.render_non_meshes(render_pass, world, view); self.render_non_meshes(render_pass, world, view)?;
Ok(())
} }
/// Renders all batchable meshes queued in this phase. /// Renders all batchable meshes queued in this phase.
@ -334,7 +336,7 @@ where
render_pass: &mut TrackedRenderPass<'w>, render_pass: &mut TrackedRenderPass<'w>,
world: &'w World, world: &'w World,
view: Entity, view: Entity,
) { ) -> Result<(), DrawError> {
let draw_functions = world.resource::<DrawFunctions<BPI>>(); let draw_functions = world.resource::<DrawFunctions<BPI>>();
let mut draw_functions = draw_functions.write(); let mut draw_functions = draw_functions.write();
@ -355,9 +357,11 @@ where
continue; continue;
}; };
draw_function.draw(world, render_pass, view, &binned_phase_item); draw_function.draw(world, render_pass, view, &binned_phase_item)?;
} }
} }
Ok(())
} }
/// Renders all unbatchable meshes queued in this phase. /// Renders all unbatchable meshes queued in this phase.
@ -366,7 +370,7 @@ where
render_pass: &mut TrackedRenderPass<'w>, render_pass: &mut TrackedRenderPass<'w>,
world: &'w World, world: &'w World,
view: Entity, view: Entity,
) { ) -> Result<(), DrawError> {
let draw_functions = world.resource::<DrawFunctions<BPI>>(); let draw_functions = world.resource::<DrawFunctions<BPI>>();
let mut draw_functions = draw_functions.write(); let mut draw_functions = draw_functions.write();
@ -412,9 +416,10 @@ where
continue; continue;
}; };
draw_function.draw(world, render_pass, view, &binned_phase_item); draw_function.draw(world, render_pass, view, &binned_phase_item)?;
} }
} }
Ok(())
} }
/// Renders all objects of type [`BinnedRenderPhaseType::NonMesh`]. /// Renders all objects of type [`BinnedRenderPhaseType::NonMesh`].
@ -425,7 +430,7 @@ where
render_pass: &mut TrackedRenderPass<'w>, render_pass: &mut TrackedRenderPass<'w>,
world: &'w World, world: &'w World,
view: Entity, view: Entity,
) { ) -> Result<(), DrawError> {
let draw_functions = world.resource::<DrawFunctions<BPI>>(); let draw_functions = world.resource::<DrawFunctions<BPI>>();
let mut draw_functions = draw_functions.write(); let mut draw_functions = draw_functions.write();
@ -439,8 +444,10 @@ where
continue; continue;
}; };
draw_function.draw(world, render_pass, view, &binned_phase_item); draw_function.draw(world, render_pass, view, &binned_phase_item)?;
} }
Ok(())
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
@ -769,8 +776,8 @@ where
render_pass: &mut TrackedRenderPass<'w>, render_pass: &mut TrackedRenderPass<'w>,
world: &'w World, world: &'w World,
view: Entity, view: Entity,
) { ) -> Result<(), DrawError> {
self.render_range(render_pass, world, view, ..); self.render_range(render_pass, world, view, ..)
} }
/// Renders all [`PhaseItem`]s in the provided `range` (based on their index in `self.items`) using their corresponding draw functions. /// Renders all [`PhaseItem`]s in the provided `range` (based on their index in `self.items`) using their corresponding draw functions.
@ -780,7 +787,7 @@ where
world: &'w World, world: &'w World,
view: Entity, view: Entity,
range: impl SliceIndex<[I], Output = [I]>, range: impl SliceIndex<[I], Output = [I]>,
) { ) -> Result<(), DrawError> {
let items = self let items = self
.items .items
.get(range) .get(range)
@ -798,10 +805,11 @@ where
index += 1; index += 1;
} else { } else {
let draw_function = draw_functions.get_mut(item.draw_function()).unwrap(); let draw_function = draw_functions.get_mut(item.draw_function()).unwrap();
draw_function.draw(world, render_pass, view, item); draw_function.draw(world, render_pass, view, item)?;
index += batch_range.len(); index += batch_range.len();
} }
} }
Ok(())
} }
} }
@ -1081,7 +1089,7 @@ impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline {
pass.set_render_pipeline(pipeline); pass.set_render_pipeline(pipeline);
RenderCommandResult::Success RenderCommandResult::Success
} else { } else {
RenderCommandResult::Failure RenderCommandResult::Skip
} }
} }
} }

View file

@ -338,10 +338,10 @@ impl<P: PhaseItem, M: Material2d, const I: usize> RenderCommand<P>
let materials = materials.into_inner(); let materials = materials.into_inner();
let material_instances = material_instances.into_inner(); let material_instances = material_instances.into_inner();
let Some(material_instance) = material_instances.get(&item.entity()) else { let Some(material_instance) = material_instances.get(&item.entity()) else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
let Some(material2d) = materials.get(*material_instance) else { let Some(material2d) = materials.get(*material_instance) else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
pass.set_bind_group(I, &material2d.bind_group, &[]); pass.set_bind_group(I, &material2d.bind_group, &[]);
RenderCommandResult::Success RenderCommandResult::Success

View file

@ -718,13 +718,13 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh2d {
let Some(RenderMesh2dInstance { mesh_asset_id, .. }) = let Some(RenderMesh2dInstance { mesh_asset_id, .. }) =
render_mesh2d_instances.get(&item.entity()) render_mesh2d_instances.get(&item.entity())
else { else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
let Some(gpu_mesh) = meshes.get(*mesh_asset_id) else { let Some(gpu_mesh) = meshes.get(*mesh_asset_id) else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(mesh_asset_id) else { let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(mesh_asset_id) else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..)); pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..));
@ -737,7 +737,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMesh2d {
} => { } => {
let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(mesh_asset_id) let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(mesh_asset_id)
else { else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format); pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format);

View file

@ -804,7 +804,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetSpriteTextureBindGrou
) -> RenderCommandResult { ) -> RenderCommandResult {
let image_bind_groups = image_bind_groups.into_inner(); let image_bind_groups = image_bind_groups.into_inner();
let Some(batch) = batch else { let Some(batch) = batch else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
pass.set_bind_group( pass.set_bind_group(
@ -834,7 +834,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawSpriteBatch {
) -> RenderCommandResult { ) -> RenderCommandResult {
let sprite_meta = sprite_meta.into_inner(); let sprite_meta = sprite_meta.into_inner();
let Some(batch) = batch else { let Some(batch) = batch else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
pass.set_index_buffer( pass.set_index_buffer(

View file

@ -15,6 +15,7 @@ use bevy_render::{
renderer::*, renderer::*,
view::*, view::*,
}; };
use bevy_utils::tracing::error;
pub struct UiPassNode { pub struct UiPassNode {
ui_view_query: QueryState<(&'static ViewTarget, &'static ExtractedCamera), With<ExtractedView>>, ui_view_query: QueryState<(&'static ViewTarget, &'static ExtractedCamera), With<ExtractedView>>,
@ -80,7 +81,9 @@ impl Node for UiPassNode {
if let Some(viewport) = camera.viewport.as_ref() { if let Some(viewport) = camera.viewport.as_ref() {
render_pass.set_camera_viewport(viewport); render_pass.set_camera_viewport(viewport);
} }
transparent_phase.render(&mut render_pass, world, view_entity); if let Err(err) = transparent_phase.render(&mut render_pass, world, view_entity) {
error!("Error encountered while rendering the ui phase {err:?}");
}
Ok(()) Ok(())
} }
@ -168,11 +171,10 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiViewBindGroup<I> {
ui_meta: SystemParamItem<'w, '_, Self::Param>, ui_meta: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
pass.set_bind_group( let Some(view_bind_group) = ui_meta.into_inner().view_bind_group.as_ref() else {
I, return RenderCommandResult::Failure("view_bind_group not available");
ui_meta.into_inner().view_bind_group.as_ref().unwrap(), };
&[view_uniform.offset], pass.set_bind_group(I, view_bind_group, &[view_uniform.offset]);
);
RenderCommandResult::Success RenderCommandResult::Success
} }
} }
@ -192,7 +194,7 @@ impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiTextureBindGroup<I>
) -> RenderCommandResult { ) -> RenderCommandResult {
let image_bind_groups = image_bind_groups.into_inner(); let image_bind_groups = image_bind_groups.into_inner();
let Some(batch) = batch else { let Some(batch) = batch else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
pass.set_bind_group(I, image_bind_groups.values.get(&batch.image).unwrap(), &[]); pass.set_bind_group(I, image_bind_groups.values.get(&batch.image).unwrap(), &[]);
@ -214,15 +216,21 @@ impl<P: PhaseItem> RenderCommand<P> for DrawUiNode {
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
let Some(batch) = batch else { let Some(batch) = batch else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
};
let ui_meta = ui_meta.into_inner();
let Some(vertices) = ui_meta.vertices.buffer() else {
return RenderCommandResult::Failure("missing vertices to draw ui");
};
let Some(indices) = ui_meta.indices.buffer() else {
return RenderCommandResult::Failure("missing indices to draw ui");
}; };
let ui_meta = ui_meta.into_inner();
// Store the vertices // Store the vertices
pass.set_vertex_buffer(0, ui_meta.vertices.buffer().unwrap().slice(..)); pass.set_vertex_buffer(0, vertices.slice(..));
// Define how to "connect" the vertices // Define how to "connect" the vertices
pass.set_index_buffer( pass.set_index_buffer(
ui_meta.indices.buffer().unwrap().slice(..), indices.slice(..),
0, 0,
bevy_render::render_resource::IndexFormat::Uint32, bevy_render::render_resource::IndexFormat::Uint32,
); );

View file

@ -292,10 +292,10 @@ impl<P: PhaseItem, M: UiMaterial, const I: usize> RenderCommand<P>
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
let Some(material_handle) = material_handle else { let Some(material_handle) = material_handle else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
let Some(material) = materials.into_inner().get(material_handle.material) else { let Some(material) = materials.into_inner().get(material_handle.material) else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
pass.set_bind_group(I, &material.bind_group, &[]); pass.set_bind_group(I, &material.bind_group, &[]);
RenderCommandResult::Success RenderCommandResult::Success
@ -317,7 +317,7 @@ impl<P: PhaseItem, M: UiMaterial> RenderCommand<P> for DrawUiMaterialNode<M> {
pass: &mut TrackedRenderPass<'w>, pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult { ) -> RenderCommandResult {
let Some(batch) = batch else { let Some(batch) = batch else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
pass.set_vertex_buffer(0, ui_meta.into_inner().vertices.buffer().unwrap().slice(..)); pass.set_vertex_buffer(0, ui_meta.into_inner().vertices.buffer().unwrap().slice(..));

View file

@ -263,18 +263,18 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMeshInstanced {
let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(item.entity()) let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(item.entity())
else { else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
let Some(gpu_mesh) = meshes.into_inner().get(mesh_instance.mesh_asset_id) else { let Some(gpu_mesh) = meshes.into_inner().get(mesh_instance.mesh_asset_id) else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
let Some(instance_buffer) = instance_buffer else { let Some(instance_buffer) = instance_buffer else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
let Some(vertex_buffer_slice) = let Some(vertex_buffer_slice) =
mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id) mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id)
else { else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..)); pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..));
@ -288,7 +288,7 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMeshInstanced {
let Some(index_buffer_slice) = let Some(index_buffer_slice) =
mesh_allocator.mesh_index_slice(&mesh_instance.mesh_asset_id) mesh_allocator.mesh_index_slice(&mesh_instance.mesh_asset_id)
else { else {
return RenderCommandResult::Failure; return RenderCommandResult::Skip;
}; };
pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format); pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format);