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    entity::EntityHash,
8    prelude::*,
9    system::{lifetimeless::*, SystemParamItem},
10};
11use bevy_math::FloatOrd;
12use bevy_render::{
13    camera::ExtractedCamera,
14    diagnostic::RecordDiagnostics,
15    render_phase::*,
16    render_resource::{CachedRenderPipelineId, RenderPassDescriptor},
17    renderer::{RenderContext, ViewQuery},
18    sync_world::MainEntity,
19    view::*,
20};
21use indexmap::IndexMap;
22use tracing::error;
23
24pub fn ui_pass(
25    world: &World,
26    view: ViewQuery<&UiCameraView>,
27    ui_view_query: Query<(&ExtractedView, &UiViewTarget)>,
28    ui_view_target_query: Query<(&ViewTarget, &ExtractedCamera)>,
29    transparent_render_phases: Res<ViewSortedRenderPhases<TransparentUi>>,
30    mut ctx: RenderContext,
31) {
32    let ui_camera_view = view.into_inner();
33    let ui_view_entity = ui_camera_view.0;
34
35    let Ok((extracted_view, ui_view_target)) = ui_view_query.get(ui_view_entity) else {
36        return;
37    };
38
39    let Ok((target, camera)) = ui_view_target_query.get(ui_view_target.0) else {
40        return;
41    };
42
43    let Some(transparent_phase) =
44        transparent_render_phases.get(&extracted_view.retained_view_entity)
45    else {
46        return;
47    };
48
49    if transparent_phase.items.is_empty() {
50        return;
51    }
52
53    let diagnostics = ctx.diagnostic_recorder();
54    let diagnostics = diagnostics.as_deref();
55
56    let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
57        label: Some("ui"),
58        color_attachments: &[Some(target.get_unsampled_color_attachment())],
59        depth_stencil_attachment: None,
60        timestamp_writes: None,
61        occlusion_query_set: None,
62        multiview_mask: None,
63    });
64    let pass_span = diagnostics.pass_span(&mut render_pass, "ui");
65
66    if let Some(viewport) = camera.viewport.as_ref() {
67        render_pass.set_camera_viewport(viewport);
68    }
69
70    if let Err(err) = transparent_phase.render(&mut render_pass, world, ui_view_entity) {
71        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event src/render_pass.rs:71",
                        "bevy_ui_render::render_pass", ::tracing::Level::ERROR,
                        ::tracing_core::__macro_support::Option::Some("src/render_pass.rs"),
                        ::tracing_core::__macro_support::Option::Some(71u32),
                        ::tracing_core::__macro_support::Option::Some("bevy_ui_render::render_pass"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::ERROR <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::ERROR <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Error encountered while rendering the ui phase {0:?}",
                                                    err) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};error!("Error encountered while rendering the ui phase {err:?}");
72    }
73
74    pass_span.end(&mut render_pass);
75}
76
77pub struct TransparentUi {
78    pub sort_key: FloatOrd,
79    pub entity: (Entity, MainEntity),
80    pub pipeline: CachedRenderPipelineId,
81    pub draw_function: DrawFunctionId,
82    pub batch_range: Range<u32>,
83    pub extra_index: PhaseItemExtraIndex,
84    pub index: usize,
85    pub indexed: bool,
86}
87
88impl PhaseItem for TransparentUi {
89    #[inline]
90    fn entity(&self) -> Entity {
91        self.entity.0
92    }
93
94    fn main_entity(&self) -> MainEntity {
95        self.entity.1
96    }
97
98    #[inline]
99    fn draw_function(&self) -> DrawFunctionId {
100        self.draw_function
101    }
102
103    #[inline]
104    fn batch_range(&self) -> &Range<u32> {
105        &self.batch_range
106    }
107
108    #[inline]
109    fn batch_range_mut(&mut self) -> &mut Range<u32> {
110        &mut self.batch_range
111    }
112
113    #[inline]
114    fn extra_index(&self) -> PhaseItemExtraIndex {
115        self.extra_index.clone()
116    }
117
118    #[inline]
119    fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range<u32>, &mut PhaseItemExtraIndex) {
120        (&mut self.batch_range, &mut self.extra_index)
121    }
122}
123
124impl SortedPhaseItem for TransparentUi {
125    type SortKey = FloatOrd;
126
127    #[inline]
128    fn sort_key(&self) -> Self::SortKey {
129        self.sort_key
130    }
131
132    #[inline]
133    fn sort(items: &mut IndexMap<(Entity, MainEntity), TransparentUi, EntityHash>) {
134        items.sort_by_key(|_, value| value.sort_key());
135    }
136
137    fn recalculate_sort_keys(
138        _: &mut IndexMap<(Entity, MainEntity), Self, EntityHash>,
139        _: &ExtractedView,
140    ) {
141        // Sort keys are precalculated for UI phase items.
142    }
143
144    #[inline]
145    fn indexed(&self) -> bool {
146        self.indexed
147    }
148}
149
150impl CachedRenderPipelinePhaseItem for TransparentUi {
151    #[inline]
152    fn cached_pipeline(&self) -> CachedRenderPipelineId {
153        self.pipeline
154    }
155}
156
157pub type DrawUi = (
158    SetItemPipeline,
159    SetUiViewBindGroup<0>,
160    SetUiTextureBindGroup<1>,
161    DrawUiNode,
162);
163
164pub struct SetUiViewBindGroup<const I: usize>;
165impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiViewBindGroup<I> {
166    type Param = SRes<UiMeta>;
167    type ViewQuery = Read<ViewUniformOffset>;
168    type ItemQuery = ();
169
170    fn render<'w>(
171        _item: &P,
172        view_uniform: &'w ViewUniformOffset,
173        _entity: Option<()>,
174        ui_meta: SystemParamItem<'w, '_, Self::Param>,
175        pass: &mut TrackedRenderPass<'w>,
176    ) -> RenderCommandResult {
177        let Some(view_bind_group) = ui_meta.into_inner().view_bind_group.as_ref() else {
178            return RenderCommandResult::Failure("view_bind_group not available");
179        };
180        pass.set_bind_group(I, view_bind_group, &[view_uniform.offset]);
181        RenderCommandResult::Success
182    }
183}
184pub struct SetUiTextureBindGroup<const I: usize>;
185impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiTextureBindGroup<I> {
186    type Param = SRes<ImageNodeBindGroups>;
187    type ViewQuery = ();
188    type ItemQuery = Read<UiBatch>;
189
190    #[inline]
191    fn render<'w>(
192        _item: &P,
193        _view: (),
194        batch: Option<&'w UiBatch>,
195        image_bind_groups: SystemParamItem<'w, '_, Self::Param>,
196        pass: &mut TrackedRenderPass<'w>,
197    ) -> RenderCommandResult {
198        let image_bind_groups = image_bind_groups.into_inner();
199        let Some(batch) = batch else {
200            return RenderCommandResult::Skip;
201        };
202
203        pass.set_bind_group(I, image_bind_groups.values.get(&batch.image).unwrap(), &[]);
204        RenderCommandResult::Success
205    }
206}
207
208pub struct DrawUiNode;
209impl<P: PhaseItem> RenderCommand<P> for DrawUiNode {
210    type Param = SRes<UiMeta>;
211    type ViewQuery = ();
212    type ItemQuery = Read<UiBatch>;
213
214    #[inline]
215    fn render<'w>(
216        _item: &P,
217        _view: (),
218        batch: Option<&'w UiBatch>,
219        ui_meta: SystemParamItem<'w, '_, Self::Param>,
220        pass: &mut TrackedRenderPass<'w>,
221    ) -> RenderCommandResult {
222        let Some(batch) = batch else {
223            return RenderCommandResult::Skip;
224        };
225        let ui_meta = ui_meta.into_inner();
226        let Some(vertices) = ui_meta.vertices.buffer() else {
227            return RenderCommandResult::Failure("missing vertices to draw ui");
228        };
229        let Some(indices) = ui_meta.indices.buffer() else {
230            return RenderCommandResult::Failure("missing indices to draw ui");
231        };
232
233        // Store the vertices
234        pass.set_vertex_buffer(0, vertices.slice(..));
235        // Define how to "connect" the vertices
236        pass.set_index_buffer(
237            indices.slice(..),
238            bevy_render::render_resource::IndexFormat::Uint32,
239        );
240        // Draw the vertices
241        pass.draw_indexed(batch.range.clone(), 0, 0..1);
242        RenderCommandResult::Success
243    }
244}