diff --git a/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs b/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs index c5dafd45b7dff..8f0d42acabe26 100644 --- a/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs +++ b/crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs @@ -58,7 +58,7 @@ impl ViewNode for MainTransparentPass2dNode { } 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); diff --git a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs index dfd6fcc06feab..b51f36354340a 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs @@ -12,6 +12,7 @@ use bevy_render::{ renderer::RenderContext, view::{ViewDepthTexture, ViewTarget, ViewUniformOffset}, }; +use bevy_utils::tracing::error; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; @@ -95,14 +96,18 @@ impl ViewNode for MainOpaquePass3dNode { if !opaque_phase.is_empty() { #[cfg(feature = "trace")] 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 if !alpha_mask_phase.is_empty() { #[cfg(feature = "trace")] 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 diff --git a/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs index dadc4e0744793..f78083afb3816 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs @@ -9,6 +9,7 @@ use bevy_render::{ renderer::RenderContext, view::{ViewDepthTexture, ViewTarget}, }; +use bevy_utils::tracing::error; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; use std::ops::Range; @@ -98,7 +99,11 @@ impl ViewNode for MainTransmissivePass3dNode { } // 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 { let mut render_pass = @@ -108,7 +113,9 @@ impl ViewNode for MainTransmissivePass3dNode { 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:?}"); + } } } diff --git a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs index 4ab8a697e393d..4f0d3d0722f0e 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs @@ -9,6 +9,7 @@ use bevy_render::{ renderer::RenderContext, view::{ViewDepthTexture, ViewTarget}, }; +use bevy_utils::tracing::error; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; @@ -70,7 +71,9 @@ impl ViewNode for MainTransparentPass3dNode { 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); } diff --git a/crates/bevy_core_pipeline/src/deferred/node.rs b/crates/bevy_core_pipeline/src/deferred/node.rs index 21df5f4ed9f6a..44895aaeb8cd2 100644 --- a/crates/bevy_core_pipeline/src/deferred/node.rs +++ b/crates/bevy_core_pipeline/src/deferred/node.rs @@ -11,6 +11,7 @@ use bevy_render::{ renderer::RenderContext, view::ViewDepthTexture, }; +use bevy_utils::tracing::error; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; @@ -149,14 +150,23 @@ impl ViewNode for DeferredGBufferPrepassNode { { #[cfg(feature = "trace")] 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 if !alpha_mask_deferred_phase.is_empty() { #[cfg(feature = "trace")] 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); diff --git a/crates/bevy_core_pipeline/src/prepass/node.rs b/crates/bevy_core_pipeline/src/prepass/node.rs index 203581a2bf0d6..d362c36f6c21f 100644 --- a/crates/bevy_core_pipeline/src/prepass/node.rs +++ b/crates/bevy_core_pipeline/src/prepass/node.rs @@ -9,6 +9,7 @@ use bevy_render::{ renderer::RenderContext, view::{ViewDepthTexture, ViewUniformOffset}, }; +use bevy_utils::tracing::error; #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; @@ -125,14 +126,23 @@ impl ViewNode for PrepassNode { { #[cfg(feature = "trace")] 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 if !alpha_mask_prepass_phase.is_empty() { #[cfg(feature = "trace")] 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 diff --git a/crates/bevy_gizmos/src/lib.rs b/crates/bevy_gizmos/src/lib.rs index 8bab13a62b849..3bda59054d25e 100644 --- a/crates/bevy_gizmos/src/lib.rs +++ b/crates/bevy_gizmos/src/lib.rs @@ -540,7 +540,7 @@ impl RenderCommand

for SetLineGizmoBindGroup pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { let Some(uniform_index) = uniform_index else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; pass.set_bind_group( I, @@ -566,10 +566,10 @@ impl RenderCommand

for DrawLineGizmo { pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { let Some(handle) = handle else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; let Some(line_gizmo) = line_gizmos.into_inner().get(handle) else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; if line_gizmo.vertex_count < 2 { @@ -612,10 +612,10 @@ impl RenderCommand

for DrawLineJointGizmo { pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { let Some(handle) = handle else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; 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 { diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index ba25fa30cda69..6656bf25ba9be 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -456,10 +456,10 @@ impl RenderCommand

for SetMaterial let material_instances = material_instances.into_inner(); 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 { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; pass.set_bind_group(I, &material.bind_group, &[]); RenderCommandResult::Success diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index 436cbb51fa3b0..11e3f2dcfd0a2 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -1445,7 +1445,11 @@ impl Node for ShadowPassNode { let pass_span = 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); drop(render_pass); diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 56f1cd128949d..33997129a87a5 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -2197,12 +2197,11 @@ impl RenderCommand

for SetMeshBindGroup { is_morphed, has_motion_vector_prepass, ) else { - error!( + return RenderCommandResult::Failure( "The MeshBindGroups resource wasn't set in the render phase. \ 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(); @@ -2285,7 +2284,7 @@ impl RenderCommand

for DrawMesh { if !has_preprocess_bind_group || !preprocess_pipelines.pipelines_are_loaded(&pipeline_cache) { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; } } @@ -2295,13 +2294,13 @@ impl RenderCommand

for DrawMesh { let mesh_allocator = mesh_allocator.into_inner(); 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 { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; 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. @@ -2310,7 +2309,7 @@ impl RenderCommand

for DrawMesh { Some(index) => match indirect_parameters_buffer.buffer() { None => { warn!("Not rendering mesh because indirect parameters buffer wasn't present"); - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; } Some(buffer) => Some(( index as u64 * mem::size_of::() as u64, @@ -2331,7 +2330,7 @@ impl RenderCommand

for DrawMesh { } => { let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(&mesh_asset_id) else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format); diff --git a/crates/bevy_render/src/render_graph/node.rs b/crates/bevy_render/src/render_graph/node.rs index 0558f7c078164..8528457d420a1 100644 --- a/crates/bevy_render/src/render_graph/node.rs +++ b/crates/bevy_render/src/render_graph/node.rs @@ -3,6 +3,7 @@ use crate::{ Edge, InputSlotError, OutputSlotError, RenderGraphContext, RenderGraphError, RunSubGraphError, SlotInfo, SlotInfos, }, + render_phase::DrawError, renderer::RenderContext, }; pub use bevy_ecs::label::DynEq; @@ -97,6 +98,8 @@ pub enum NodeRunError { OutputSlotError(#[from] OutputSlotError), #[error("encountered an error when running a sub-graph")] 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`]. diff --git a/crates/bevy_render/src/render_phase/draw.rs b/crates/bevy_render/src/render_phase/draw.rs index 980899b64bc44..439392c098eff 100644 --- a/crates/bevy_render/src/render_phase/draw.rs +++ b/crates/bevy_render/src/render_phase/draw.rs @@ -2,7 +2,7 @@ use crate::render_phase::{PhaseItem, TrackedRenderPass}; use bevy_app::{App, SubApp}; use bevy_ecs::{ entity::Entity, - query::{QueryState, ROQueryItem, ReadOnlyQueryData}, + query::{QueryEntityError, QueryState, ROQueryItem, ReadOnlyQueryData}, system::{ReadOnlySystemParam, Resource, SystemParam, SystemParamItem, SystemState}, world::World, }; @@ -13,6 +13,7 @@ use std::{ hash::Hash, sync::{PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; +use thiserror::Error; /// A draw function used to draw [`PhaseItem`]s. /// @@ -34,7 +35,17 @@ pub trait Draw: Send + Sync + 'static { pass: &mut TrackedRenderPass<'w>, view: Entity, 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? @@ -212,7 +223,8 @@ pub trait RenderCommand { #[derive(Debug)] pub enum RenderCommandResult { Success, - Failure, + Skip, + Failure(&'static str), } macro_rules! render_command_tuple_impl { @@ -232,14 +244,22 @@ macro_rules! render_command_tuple_impl { ) -> RenderCommandResult { match maybe_entities { 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,)*)) => { - $(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 @@ -290,12 +310,23 @@ where pass: &mut TrackedRenderPass<'w>, view: Entity, item: &P, - ) { + ) -> Result<(), DrawError> { 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(); - // TODO: handle/log `RenderCommand` failure - C::render(item, view, entity, param, pass); + match C::render(item, view, entity, param, pass) { + RenderCommandResult::Success | RenderCommandResult::Skip => Ok(()), + RenderCommandResult::Failure(reason) => Err(DrawError::RenderCommandFailure(reason)), + } } } diff --git a/crates/bevy_render/src/render_phase/mod.rs b/crates/bevy_render/src/render_phase/mod.rs index 00e32d4ea3bd5..1bc1ee52ca687 100644 --- a/crates/bevy_render/src/render_phase/mod.rs +++ b/crates/bevy_render/src/render_phase/mod.rs @@ -314,7 +314,7 @@ where render_pass: &mut TrackedRenderPass<'w>, world: &'w World, view: Entity, - ) { + ) -> Result<(), DrawError> { { let draw_functions = world.resource::>(); let mut draw_functions = draw_functions.write(); @@ -323,9 +323,11 @@ where // locks. } - self.render_batchable_meshes(render_pass, world, view); - self.render_unbatchable_meshes(render_pass, world, view); - self.render_non_meshes(render_pass, world, view); + self.render_batchable_meshes(render_pass, world, view)?; + self.render_unbatchable_meshes(render_pass, world, view)?; + self.render_non_meshes(render_pass, world, view)?; + + Ok(()) } /// Renders all batchable meshes queued in this phase. @@ -334,7 +336,7 @@ where render_pass: &mut TrackedRenderPass<'w>, world: &'w World, view: Entity, - ) { + ) -> Result<(), DrawError> { let draw_functions = world.resource::>(); let mut draw_functions = draw_functions.write(); @@ -355,9 +357,11 @@ where 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. @@ -366,7 +370,7 @@ where render_pass: &mut TrackedRenderPass<'w>, world: &'w World, view: Entity, - ) { + ) -> Result<(), DrawError> { let draw_functions = world.resource::>(); let mut draw_functions = draw_functions.write(); @@ -412,9 +416,10 @@ where 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`]. @@ -425,7 +430,7 @@ where render_pass: &mut TrackedRenderPass<'w>, world: &'w World, view: Entity, - ) { + ) -> Result<(), DrawError> { let draw_functions = world.resource::>(); let mut draw_functions = draw_functions.write(); @@ -439,8 +444,10 @@ where 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 { @@ -769,8 +776,8 @@ where render_pass: &mut TrackedRenderPass<'w>, world: &'w World, view: Entity, - ) { - self.render_range(render_pass, world, view, ..); + ) -> Result<(), DrawError> { + 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. @@ -780,7 +787,7 @@ where world: &'w World, view: Entity, range: impl SliceIndex<[I], Output = [I]>, - ) { + ) -> Result<(), DrawError> { let items = self .items .get(range) @@ -798,10 +805,11 @@ where index += 1; } else { 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(); } } + Ok(()) } } @@ -1081,7 +1089,7 @@ impl RenderCommand

for SetItemPipeline { pass.set_render_pipeline(pipeline); RenderCommandResult::Success } else { - RenderCommandResult::Failure + RenderCommandResult::Skip } } } diff --git a/crates/bevy_sprite/src/mesh2d/material.rs b/crates/bevy_sprite/src/mesh2d/material.rs index 1ef8ca1eaa935..04f8fc7c41342 100644 --- a/crates/bevy_sprite/src/mesh2d/material.rs +++ b/crates/bevy_sprite/src/mesh2d/material.rs @@ -338,10 +338,10 @@ impl RenderCommand

let materials = materials.into_inner(); let material_instances = material_instances.into_inner(); let Some(material_instance) = material_instances.get(&item.entity()) else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; let Some(material2d) = materials.get(*material_instance) else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; pass.set_bind_group(I, &material2d.bind_group, &[]); RenderCommandResult::Success diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index b2b69c081eab3..2db6149c5fbd0 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -718,13 +718,13 @@ impl RenderCommand

for DrawMesh2d { let Some(RenderMesh2dInstance { mesh_asset_id, .. }) = render_mesh2d_instances.get(&item.entity()) else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; 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 { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..)); @@ -737,7 +737,7 @@ impl RenderCommand

for DrawMesh2d { } => { let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(mesh_asset_id) else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format); diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index d60efb0977eb2..96caf4964f192 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -805,7 +805,7 @@ impl RenderCommand

for SetSpriteTextureBindGrou ) -> RenderCommandResult { let image_bind_groups = image_bind_groups.into_inner(); let Some(batch) = batch else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; pass.set_bind_group( @@ -835,7 +835,7 @@ impl RenderCommand

for DrawSpriteBatch { ) -> RenderCommandResult { let sprite_meta = sprite_meta.into_inner(); let Some(batch) = batch else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; pass.set_index_buffer( diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index 85a3bd456c5de..7c9931b82f183 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -15,6 +15,7 @@ use bevy_render::{ renderer::*, view::*, }; +use bevy_utils::tracing::error; pub struct UiPassNode { ui_view_query: QueryState<(&'static ViewTarget, &'static ExtractedCamera), With>, @@ -80,7 +81,9 @@ impl Node for UiPassNode { if let Some(viewport) = camera.viewport.as_ref() { 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(()) } @@ -168,11 +171,10 @@ impl RenderCommand

for SetUiViewBindGroup { ui_meta: SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { - pass.set_bind_group( - I, - ui_meta.into_inner().view_bind_group.as_ref().unwrap(), - &[view_uniform.offset], - ); + let Some(view_bind_group) = ui_meta.into_inner().view_bind_group.as_ref() else { + return RenderCommandResult::Failure("view_bind_group not available"); + }; + pass.set_bind_group(I, view_bind_group, &[view_uniform.offset]); RenderCommandResult::Success } } @@ -192,7 +194,7 @@ impl RenderCommand

for SetUiTextureBindGroup ) -> RenderCommandResult { let image_bind_groups = image_bind_groups.into_inner(); let Some(batch) = batch else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; pass.set_bind_group(I, image_bind_groups.values.get(&batch.image).unwrap(), &[]); @@ -214,15 +216,21 @@ impl RenderCommand

for DrawUiNode { pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { 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"); + }; + // 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 pass.set_index_buffer( - ui_meta.indices.buffer().unwrap().slice(..), + indices.slice(..), 0, bevy_render::render_resource::IndexFormat::Uint32, ); diff --git a/crates/bevy_ui/src/render/ui_material_pipeline.rs b/crates/bevy_ui/src/render/ui_material_pipeline.rs index 49eee2ed25579..e15dc4223e456 100644 --- a/crates/bevy_ui/src/render/ui_material_pipeline.rs +++ b/crates/bevy_ui/src/render/ui_material_pipeline.rs @@ -292,10 +292,10 @@ impl RenderCommand

pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { let Some(material_handle) = material_handle else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; 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, &[]); RenderCommandResult::Success @@ -317,7 +317,7 @@ impl RenderCommand

for DrawUiMaterialNode { pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { let Some(batch) = batch else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; pass.set_vertex_buffer(0, ui_meta.into_inner().vertices.buffer().unwrap().slice(..)); diff --git a/examples/shader/shader_instancing.rs b/examples/shader/shader_instancing.rs index 9ae798b2a7877..e72c0fa15d856 100644 --- a/examples/shader/shader_instancing.rs +++ b/examples/shader/shader_instancing.rs @@ -264,18 +264,18 @@ impl RenderCommand

for DrawMeshInstanced { let Some(mesh_instance) = render_mesh_instances.render_mesh_queue_data(item.entity()) else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; 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 { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(&mesh_instance.mesh_asset_id) else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..)); @@ -289,7 +289,7 @@ impl RenderCommand

for DrawMeshInstanced { let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(&mesh_instance.mesh_asset_id) else { - return RenderCommandResult::Failure; + return RenderCommandResult::Skip; }; pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format);