use super::{UiBatch, UiImageBindGroups, UiMeta}; use crate::{prelude::UiCameraConfig, DefaultCameraView}; use bevy_ecs::{ prelude::*, system::{lifetimeless::*, SystemParamItem}, }; use bevy_render::{ render_graph::*, render_phase::*, render_resource::{CachedRenderPipelineId, LoadOp, Operations, RenderPassDescriptor}, renderer::*, view::*, }; use bevy_utils::FloatOrd; pub struct UiPassNode { ui_view_query: QueryState< ( &'static RenderPhase, &'static ViewTarget, Option<&'static UiCameraConfig>, ), With, >, default_camera_view_query: QueryState<&'static DefaultCameraView>, } impl UiPassNode { pub const IN_VIEW: &'static str = "view"; pub fn new(world: &mut World) -> Self { Self { ui_view_query: world.query_filtered(), default_camera_view_query: world.query(), } } } impl Node for UiPassNode { fn input(&self) -> Vec { vec![SlotInfo::new(UiPassNode::IN_VIEW, SlotType::Entity)] } fn update(&mut self, world: &mut World) { self.ui_view_query.update_archetypes(world); self.default_camera_view_query.update_archetypes(world); } fn run( &self, graph: &mut RenderGraphContext, render_context: &mut RenderContext, world: &World, ) -> Result<(), NodeRunError> { let input_view_entity = graph.get_input_entity(Self::IN_VIEW)?; let Ok((transparent_phase, target, camera_ui)) = self.ui_view_query.get_manual(world, input_view_entity) else { return Ok(()); }; if transparent_phase.items.is_empty() { return Ok(()); } // Don't render UI for cameras where it is explicitly disabled if matches!(camera_ui, Some(&UiCameraConfig { show_ui: false })) { return Ok(()); } // use the "default" view entity if it is defined let view_entity = if let Ok(default_view) = self .default_camera_view_query .get_manual(world, input_view_entity) { default_view.0 } else { input_view_entity }; let pass_descriptor = RenderPassDescriptor { label: Some("ui_pass"), color_attachments: &[Some(target.get_unsampled_color_attachment(Operations { load: LoadOp::Load, store: true, }))], depth_stencil_attachment: None, }; let draw_functions = world.resource::>(); let render_pass = render_context .command_encoder .begin_render_pass(&pass_descriptor); let mut draw_functions = draw_functions.write(); let mut tracked_pass = TrackedRenderPass::new(render_pass); for item in &transparent_phase.items { let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); draw_function.draw(world, &mut tracked_pass, view_entity, item); } Ok(()) } } pub struct TransparentUi { pub sort_key: FloatOrd, pub entity: Entity, pub pipeline: CachedRenderPipelineId, pub draw_function: DrawFunctionId, } impl PhaseItem for TransparentUi { type SortKey = FloatOrd; #[inline] fn sort_key(&self) -> Self::SortKey { self.sort_key } #[inline] fn draw_function(&self) -> DrawFunctionId { self.draw_function } } impl EntityPhaseItem for TransparentUi { #[inline] fn entity(&self) -> Entity { self.entity } } impl CachedRenderPipelinePhaseItem for TransparentUi { #[inline] fn cached_pipeline(&self) -> CachedRenderPipelineId { self.pipeline } } pub type DrawUi = ( SetItemPipeline, SetUiViewBindGroup<0>, SetUiTextureBindGroup<1>, DrawUiNode, ); pub struct SetUiViewBindGroup; impl EntityRenderCommand for SetUiViewBindGroup { type Param = (SRes, SQuery>); fn render<'w>( view: Entity, _item: Entity, (ui_meta, view_query): SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { let view_uniform = view_query.get(view).unwrap(); pass.set_bind_group( I, ui_meta.into_inner().view_bind_group.as_ref().unwrap(), &[view_uniform.offset], ); RenderCommandResult::Success } } pub struct SetUiTextureBindGroup; impl EntityRenderCommand for SetUiTextureBindGroup { type Param = (SRes, SQuery>); fn render<'w>( _view: Entity, item: Entity, (image_bind_groups, query_batch): SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { let batch = query_batch.get(item).unwrap(); let image_bind_groups = image_bind_groups.into_inner(); pass.set_bind_group(I, image_bind_groups.values.get(&batch.image).unwrap(), &[]); RenderCommandResult::Success } } pub struct DrawUiNode; impl EntityRenderCommand for DrawUiNode { type Param = (SRes, SQuery>); fn render<'w>( _view: Entity, item: Entity, (ui_meta, query_batch): SystemParamItem<'w, '_, Self::Param>, pass: &mut TrackedRenderPass<'w>, ) -> RenderCommandResult { let batch = query_batch.get(item).unwrap(); pass.set_vertex_buffer(0, ui_meta.into_inner().vertices.buffer().unwrap().slice(..)); pass.draw(batch.range.clone(), 0..1); RenderCommandResult::Success } }