Skip to main content

bevy_ui_render/
render_pass.rs

1use core::ops::Range;
2
3use super::{ImageNodeBindGroups, UiBatch, UiMeta, UiViewTarget};
4
5use crate::UiCameraView;
6use bevy_ecs::{
7    prelude::*,
8    system::{lifetimeless::*, SystemParamItem},
9};
10use bevy_math::FloatOrd;
11use bevy_render::{
12    camera::ExtractedCamera,
13    diagnostic::RecordDiagnostics,
14    render_graph::*,
15    render_phase::*,
16    render_resource::{CachedRenderPipelineId, RenderPassDescriptor},
17    renderer::*,
18    sync_world::MainEntity,
19    view::*,
20};
21use tracing::error;
22
23pub struct UiPassNode {
24    ui_view_query: QueryState<(&'static ExtractedView, &'static UiViewTarget)>,
25    ui_view_target_query: QueryState<(&'static ViewTarget, &'static ExtractedCamera)>,
26    ui_camera_view_query: QueryState<&'static UiCameraView>,
27}
28
29impl UiPassNode {
30    pub fn new(world: &mut World) -> Self {
31        Self {
32            ui_view_query: world.query_filtered(),
33            ui_view_target_query: world.query(),
34            ui_camera_view_query: world.query(),
35        }
36    }
37}
38
39impl Node for UiPassNode {
40    fn update(&mut self, world: &mut World) {
41        self.ui_view_query.update_archetypes(world);
42        self.ui_view_target_query.update_archetypes(world);
43        self.ui_camera_view_query.update_archetypes(world);
44    }
45
46    fn run(
47        &self,
48        graph: &mut RenderGraphContext,
49        render_context: &mut RenderContext,
50        world: &World,
51    ) -> Result<(), NodeRunError> {
52        // Extract the UI view.
53        let input_view_entity = graph.view_entity();
54
55        let Some(transparent_render_phases) =
56            world.get_resource::<ViewSortedRenderPhases<TransparentUi>>()
57        else {
58            return Ok(());
59        };
60
61        // Query the UI view components.
62        let Ok((view, ui_view_target)) = self.ui_view_query.get_manual(world, input_view_entity)
63        else {
64            return Ok(());
65        };
66
67        let Ok((target, camera)) = self
68            .ui_view_target_query
69            .get_manual(world, ui_view_target.0)
70        else {
71            return Ok(());
72        };
73
74        let Some(transparent_phase) = transparent_render_phases.get(&view.retained_view_entity)
75        else {
76            return Ok(());
77        };
78
79        if transparent_phase.items.is_empty() {
80            return Ok(());
81        }
82
83        let diagnostics = render_context.diagnostic_recorder();
84
85        // use the UI view entity if it is defined
86        let view_entity = if let Ok(ui_camera_view) = self
87            .ui_camera_view_query
88            .get_manual(world, input_view_entity)
89        {
90            ui_camera_view.0
91        } else {
92            input_view_entity
93        };
94        let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
95            label: Some("ui"),
96            color_attachments: &[Some(target.get_unsampled_color_attachment())],
97            depth_stencil_attachment: None,
98            timestamp_writes: None,
99            occlusion_query_set: None,
100        });
101        let pass_span = diagnostics.pass_span(&mut render_pass, "ui");
102
103        if let Some(viewport) = camera.viewport.as_ref() {
104            render_pass.set_camera_viewport(viewport);
105        }
106        if let Err(err) = transparent_phase.render(&mut render_pass, world, view_entity) {
107            error!("Error encountered while rendering the ui phase {err:?}");
108        }
109
110        pass_span.end(&mut render_pass);
111
112        Ok(())
113    }
114}
115
116pub struct TransparentUi {
117    pub sort_key: FloatOrd,
118    pub entity: (Entity, MainEntity),
119    pub pipeline: CachedRenderPipelineId,
120    pub draw_function: DrawFunctionId,
121    pub batch_range: Range<u32>,
122    pub extra_index: PhaseItemExtraIndex,
123    pub index: usize,
124    pub indexed: bool,
125}
126
127impl PhaseItem for TransparentUi {
128    #[inline]
129    fn entity(&self) -> Entity {
130        self.entity.0
131    }
132
133    fn main_entity(&self) -> MainEntity {
134        self.entity.1
135    }
136
137    #[inline]
138    fn draw_function(&self) -> DrawFunctionId {
139        self.draw_function
140    }
141
142    #[inline]
143    fn batch_range(&self) -> &Range<u32> {
144        &self.batch_range
145    }
146
147    #[inline]
148    fn batch_range_mut(&mut self) -> &mut Range<u32> {
149        &mut self.batch_range
150    }
151
152    #[inline]
153    fn extra_index(&self) -> PhaseItemExtraIndex {
154        self.extra_index.clone()
155    }
156
157    #[inline]
158    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
159        (&mut self.batch_range, &mut self.extra_index)
160    }
161}
162
163impl SortedPhaseItem for TransparentUi {
164    type SortKey = FloatOrd;
165
166    #[inline]
167    fn sort_key(&self) -> Self::SortKey {
168        self.sort_key
169    }
170
171    #[inline]
172    fn sort(items: &mut [Self]) {
173        items.sort_by_key(SortedPhaseItem::sort_key);
174    }
175
176    #[inline]
177    fn indexed(&self) -> bool {
178        self.indexed
179    }
180}
181
182impl CachedRenderPipelinePhaseItem for TransparentUi {
183    #[inline]
184    fn cached_pipeline(&self) -> CachedRenderPipelineId {
185        self.pipeline
186    }
187}
188
189pub type DrawUi = (
190    SetItemPipeline,
191    SetUiViewBindGroup<0>,
192    SetUiTextureBindGroup<1>,
193    DrawUiNode,
194);
195
196pub struct SetUiViewBindGroup<const I: usize>;
197impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiViewBindGroup<I> {
198    type Param = SRes<UiMeta>;
199    type ViewQuery = Read<ViewUniformOffset>;
200    type ItemQuery = ();
201
202    fn render<'w>(
203        _item: &P,
204        view_uniform: &'w ViewUniformOffset,
205        _entity: Option<()>,
206        ui_meta: SystemParamItem<'w, '_, Self::Param>,
207        pass: &mut TrackedRenderPass<'w>,
208    ) -> RenderCommandResult {
209        let Some(view_bind_group) = ui_meta.into_inner().view_bind_group.as_ref() else {
210            return RenderCommandResult::Failure("view_bind_group not available");
211        };
212        pass.set_bind_group(I, view_bind_group, &[view_uniform.offset]);
213        RenderCommandResult::Success
214    }
215}
216pub struct SetUiTextureBindGroup<const I: usize>;
217impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiTextureBindGroup<I> {
218    type Param = SRes<ImageNodeBindGroups>;
219    type ViewQuery = ();
220    type ItemQuery = Read<UiBatch>;
221
222    #[inline]
223    fn render<'w>(
224        _item: &P,
225        _view: (),
226        batch: Option<&'w UiBatch>,
227        image_bind_groups: SystemParamItem<'w, '_, Self::Param>,
228        pass: &mut TrackedRenderPass<'w>,
229    ) -> RenderCommandResult {
230        let image_bind_groups = image_bind_groups.into_inner();
231        let Some(batch) = batch else {
232            return RenderCommandResult::Skip;
233        };
234
235        pass.set_bind_group(I, image_bind_groups.values.get(&batch.image).unwrap(), &[]);
236        RenderCommandResult::Success
237    }
238}
239
240pub struct DrawUiNode;
241impl<P: PhaseItem> RenderCommand<P> for DrawUiNode {
242    type Param = SRes<UiMeta>;
243    type ViewQuery = ();
244    type ItemQuery = Read<UiBatch>;
245
246    #[inline]
247    fn render<'w>(
248        _item: &P,
249        _view: (),
250        batch: Option<&'w UiBatch>,
251        ui_meta: SystemParamItem<'w, '_, Self::Param>,
252        pass: &mut TrackedRenderPass<'w>,
253    ) -> RenderCommandResult {
254        let Some(batch) = batch else {
255            return RenderCommandResult::Skip;
256        };
257        let ui_meta = ui_meta.into_inner();
258        let Some(vertices) = ui_meta.vertices.buffer() else {
259            return RenderCommandResult::Failure("missing vertices to draw ui");
260        };
261        let Some(indices) = ui_meta.indices.buffer() else {
262            return RenderCommandResult::Failure("missing indices to draw ui");
263        };
264
265        // Store the vertices
266        pass.set_vertex_buffer(0, vertices.slice(..));
267        // Define how to "connect" the vertices
268        pass.set_index_buffer(
269            indices.slice(..),
270            bevy_render::render_resource::IndexFormat::Uint32,
271        );
272        // Draw the vertices
273        pass.draw_indexed(batch.range.clone(), 0, 0..1);
274        RenderCommandResult::Success
275    }
276}