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 }
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 pass.set_vertex_buffer(0, vertices.slice(..));
235 pass.set_index_buffer(
237 indices.slice(..),
238 bevy_render::render_resource::IndexFormat::Uint32,
239 );
240 pass.draw_indexed(batch.range.clone(), 0, 0..1);
242 RenderCommandResult::Success
243 }
244}