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 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 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 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 pass.set_vertex_buffer(0, vertices.slice(..));
267 pass.set_index_buffer(
269 indices.slice(..),
270 bevy_render::render_resource::IndexFormat::Uint32,
271 );
272 pass.draw_indexed(batch.range.clone(), 0, 0..1);
274 RenderCommandResult::Success
275 }
276}