1use std::collections::HashSet;
2
3use crate::{
4 backend::{
5 allocator::{
6 dmabuf::{AsDmabuf, Dmabuf},
7 Buffer, Slot,
8 },
9 drm::Framebuffer,
10 renderer::{
11 damage::OutputDamageTracker,
12 element::{Element, Id, RenderElement, RenderElementStates},
13 sync::SyncPoint,
14 utils::{CommitCounter, DamageSet, DamageSnapshot, OpaqueRegions},
15 Bind, Blit, Color32F, Frame, Renderer,
16 },
17 },
18 output::OutputNoMode,
19 utils::{Buffer as BufferCoords, Physical, Point, Rectangle, Scale, Size, Transform},
20};
21
22use super::{DrmScanoutBuffer, ScanoutBuffer};
23
24pub struct RenderFrameResult<'a, B: Buffer, F: Framebuffer, E> {
41 pub is_empty: bool,
43 pub states: RenderElementStates,
45 pub primary_element: PrimaryPlaneElement<'a, B, F, E>,
47 pub overlay_elements: Vec<&'a E>,
49 pub cursor_element: Option<&'a E>,
53
54 pub(super) primary_plane_element_id: Id,
55 pub(super) supports_fencing: bool,
56}
57
58impl<B: Buffer, F: Framebuffer, E> RenderFrameResult<'_, B, F, E> {
59 pub fn needs_sync(&self) -> bool {
61 if let PrimaryPlaneElement::Swapchain(ref element) = self.primary_element {
62 !self.supports_fencing || !element.sync.is_exportable()
63 } else {
64 false
65 }
66 }
67}
68
69struct SwapchainElement<'a, 'b, B: Buffer> {
70 id: Id,
71 slot: &'a Slot<B>,
72 transform: Transform,
73 damage: &'b DamageSnapshot<i32, BufferCoords>,
74}
75
76impl<B: Buffer> Element for SwapchainElement<'_, '_, B> {
77 fn id(&self) -> &Id {
78 &self.id
79 }
80
81 fn current_commit(&self) -> CommitCounter {
82 self.damage.current_commit()
83 }
84
85 fn src(&self) -> Rectangle<f64, BufferCoords> {
86 Rectangle::from_size(self.slot.size()).to_f64()
87 }
88
89 fn geometry(&self, _scale: Scale<f64>) -> Rectangle<i32, Physical> {
90 Rectangle::from_size(self.slot.size().to_logical(1, self.transform).to_physical(1))
91 }
92
93 fn transform(&self) -> Transform {
94 self.transform
95 }
96
97 fn damage_since(&self, scale: Scale<f64>, commit: Option<CommitCounter>) -> DamageSet<i32, Physical> {
98 self.damage
99 .damage_since(commit)
100 .map(|d| {
101 d.into_iter()
102 .map(|d| d.to_logical(1, self.transform, &self.slot.size()).to_physical(1))
103 .collect()
104 })
105 .unwrap_or_else(|| DamageSet::from_slice(&[self.geometry(scale)]))
106 }
107
108 fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {
109 OpaqueRegions::from_slice(&[self.geometry(scale)])
110 }
111}
112
113enum FrameResultDamageElement<'a, 'b, E, B: Buffer> {
114 Element(&'a E),
115 Swapchain(SwapchainElement<'a, 'b, B>),
116}
117
118impl<E, B> Element for FrameResultDamageElement<'_, '_, E, B>
119where
120 E: Element,
121 B: Buffer,
122{
123 fn id(&self) -> &Id {
124 match self {
125 FrameResultDamageElement::Element(e) => e.id(),
126 FrameResultDamageElement::Swapchain(e) => e.id(),
127 }
128 }
129
130 fn current_commit(&self) -> CommitCounter {
131 match self {
132 FrameResultDamageElement::Element(e) => e.current_commit(),
133 FrameResultDamageElement::Swapchain(e) => e.current_commit(),
134 }
135 }
136
137 fn src(&self) -> Rectangle<f64, BufferCoords> {
138 match self {
139 FrameResultDamageElement::Element(e) => e.src(),
140 FrameResultDamageElement::Swapchain(e) => e.src(),
141 }
142 }
143
144 fn geometry(&self, scale: Scale<f64>) -> Rectangle<i32, Physical> {
145 match self {
146 FrameResultDamageElement::Element(e) => e.geometry(scale),
147 FrameResultDamageElement::Swapchain(e) => e.geometry(scale),
148 }
149 }
150
151 fn location(&self, scale: Scale<f64>) -> Point<i32, Physical> {
152 match self {
153 FrameResultDamageElement::Element(e) => e.location(scale),
154 FrameResultDamageElement::Swapchain(e) => e.location(scale),
155 }
156 }
157
158 fn transform(&self) -> Transform {
159 match self {
160 FrameResultDamageElement::Element(e) => e.transform(),
161 FrameResultDamageElement::Swapchain(e) => e.transform(),
162 }
163 }
164
165 fn damage_since(&self, scale: Scale<f64>, commit: Option<CommitCounter>) -> DamageSet<i32, Physical> {
166 match self {
167 FrameResultDamageElement::Element(e) => e.damage_since(scale, commit),
168 FrameResultDamageElement::Swapchain(e) => e.damage_since(scale, commit),
169 }
170 }
171
172 fn opaque_regions(&self, scale: Scale<f64>) -> OpaqueRegions<i32, Physical> {
173 match self {
174 FrameResultDamageElement::Element(e) => e.opaque_regions(scale),
175 FrameResultDamageElement::Swapchain(e) => e.opaque_regions(scale),
176 }
177 }
178}
179
180#[derive(Debug)]
181pub enum PrimaryPlaneElement<'a, B: Buffer, F: Framebuffer, E> {
183 Swapchain(PrimarySwapchainElement<B, F>),
186 Element(&'a E),
188}
189
190#[derive(Debug, thiserror::Error)]
192pub enum BlitFrameResultError<R: std::error::Error, E: std::error::Error> {
193 #[error(transparent)]
195 Rendering(R),
196 #[error(transparent)]
198 Export(E),
199}
200
201impl<B, F, E> RenderFrameResult<'_, B, F, E>
202where
203 B: Buffer,
204 F: Framebuffer,
205{
206 pub fn damage_from_age<'d>(
208 &self,
209 damage_tracker: &'d mut OutputDamageTracker,
210 age: usize,
211 filter: impl IntoIterator<Item = Id>,
212 ) -> Result<(Option<&'d Vec<Rectangle<i32, Physical>>>, RenderElementStates), OutputNoMode>
213 where
214 E: Element,
215 {
216 #[allow(clippy::mutable_key_type)]
217 let filter_ids: HashSet<Id> = filter.into_iter().collect();
218
219 let mut elements: Vec<FrameResultDamageElement<'_, '_, E, B>> =
220 Vec::with_capacity(usize::from(self.cursor_element.is_some()) + self.overlay_elements.len() + 1);
221 if let Some(cursor) = self.cursor_element {
222 if !filter_ids.contains(cursor.id()) {
223 elements.push(FrameResultDamageElement::Element(cursor));
224 }
225 }
226
227 elements.extend(
228 self.overlay_elements
229 .iter()
230 .filter(|e| !filter_ids.contains(e.id()))
231 .map(|e| FrameResultDamageElement::Element(*e)),
232 );
233
234 let primary_render_element = match &self.primary_element {
235 PrimaryPlaneElement::Swapchain(PrimarySwapchainElement {
236 slot,
237 transform,
238 damage,
239 ..
240 }) => FrameResultDamageElement::Swapchain(SwapchainElement {
241 id: self.primary_plane_element_id.clone(),
242 transform: *transform,
243 slot: match &slot.buffer {
244 ScanoutBuffer::Swapchain(slot) => slot,
245 _ => unreachable!(),
246 },
247 damage,
248 }),
249 PrimaryPlaneElement::Element(e) => FrameResultDamageElement::Element(*e),
250 };
251
252 elements.push(primary_render_element);
253
254 damage_tracker.damage_output(age, &elements)
255 }
256}
257
258impl<'a, B, F, E> RenderFrameResult<'a, B, F, E>
259where
260 B: Buffer + AsDmabuf,
261 <B as AsDmabuf>::Error: std::fmt::Debug,
262 F: Framebuffer,
263{
264 #[allow(clippy::too_many_arguments)]
266 pub fn blit_frame_result<R>(
267 &self,
268 size: impl Into<Size<i32, Physical>>,
269 transform: Transform,
270 scale: impl Into<Scale<f64>>,
271 renderer: &mut R,
272 framebuffer: &mut R::Framebuffer<'_>,
273 damage: impl IntoIterator<Item = Rectangle<i32, Physical>>,
274 filter: impl IntoIterator<Item = Id>,
275 ) -> Result<SyncPoint, BlitFrameResultError<R::Error, <B as AsDmabuf>::Error>>
276 where
277 R: Renderer + Bind<Dmabuf> + Blit,
278 R::TextureId: 'static,
279 E: Element + RenderElement<R>,
280 {
281 let size = size.into();
282 let scale = scale.into();
283 #[allow(clippy::mutable_key_type)]
284 let filter_ids: HashSet<Id> = filter.into_iter().collect();
285 let damage = damage.into_iter().collect::<Vec<_>>();
286
287 if damage.is_empty() {
289 return Ok(SyncPoint::signaled());
290 }
291
292 let mut opaque_regions: Vec<Rectangle<i32, Physical>> = Vec::new();
293
294 let mut elements_to_render: Vec<&'a E> =
295 Vec::with_capacity(usize::from(self.cursor_element.is_some()) + self.overlay_elements.len() + 1);
296
297 if let Some(cursor_element) = self.cursor_element.as_ref() {
298 if !filter_ids.contains(cursor_element.id()) {
299 elements_to_render.push(*cursor_element);
300 opaque_regions.extend(cursor_element.opaque_regions(scale));
301 }
302 }
303
304 for element in self
305 .overlay_elements
306 .iter()
307 .filter(|e| !filter_ids.contains(e.id()))
308 {
309 elements_to_render.push(element);
310 opaque_regions.extend(element.opaque_regions(scale));
311 }
312
313 let primary_dmabuf = match &self.primary_element {
314 PrimaryPlaneElement::Swapchain(PrimarySwapchainElement { slot, sync, .. }) => {
315 let dmabuf = match &slot.buffer {
316 ScanoutBuffer::Swapchain(slot) => slot.export().map_err(BlitFrameResultError::Export)?,
317 _ => unreachable!(),
318 };
319 let size = dmabuf.size();
320 let geometry = Rectangle::from_size(size.to_logical(1, Transform::Normal).to_physical(1));
321 opaque_regions.push(geometry);
322 Some((sync.clone(), dmabuf, geometry))
323 }
324 PrimaryPlaneElement::Element(e) => {
325 elements_to_render.push(*e);
326 opaque_regions.extend(e.opaque_regions(scale));
327 None
328 }
329 };
330
331 let clear_damage =
332 Rectangle::subtract_rects_many_in_place(damage.clone(), opaque_regions.iter().copied());
333
334 let mut sync: Option<SyncPoint> = None;
335 if !clear_damage.is_empty() {
336 tracing::trace!("clearing frame damage {:#?}", clear_damage);
337
338 let mut frame = renderer
339 .render(framebuffer, size, transform)
340 .map_err(BlitFrameResultError::Rendering)?;
341
342 frame
343 .clear(Color32F::BLACK, &clear_damage)
344 .map_err(BlitFrameResultError::Rendering)?;
345
346 sync = Some(frame.finish().map_err(BlitFrameResultError::Rendering)?);
347 }
348
349 if let Some((sync, mut dmabuf, geometry)) = primary_dmabuf {
351 let blit_damage = damage
352 .iter()
353 .filter_map(|d| d.intersection(geometry))
354 .collect::<Vec<_>>();
355
356 tracing::trace!("blitting frame with damage: {:#?}", blit_damage);
357
358 renderer.wait(&sync).map_err(BlitFrameResultError::Rendering)?;
359 let fb = renderer
360 .bind(&mut dmabuf)
361 .map_err(BlitFrameResultError::Rendering)?;
362 for rect in blit_damage {
363 renderer
364 .blit(
365 &fb,
366 framebuffer,
367 rect,
368 rect,
369 crate::backend::renderer::TextureFilter::Linear,
370 )
371 .map_err(BlitFrameResultError::Rendering)?;
372 }
373 }
374
375 if !elements_to_render.is_empty() {
377 tracing::trace!("drawing {} frame element(s)", elements_to_render.len());
378
379 let mut frame = renderer
380 .render(framebuffer, size, transform)
381 .map_err(BlitFrameResultError::Rendering)?;
382
383 for element in elements_to_render.iter().rev() {
384 let src = element.src();
385 let dst = element.geometry(scale);
386 let element_damage = damage
387 .iter()
388 .filter_map(|d| {
389 d.intersection(dst).map(|mut d| {
390 d.loc -= dst.loc;
391 d
392 })
393 })
394 .collect::<Vec<_>>();
395
396 if element_damage.is_empty() {
398 continue;
399 }
400
401 tracing::trace!("drawing frame element with damage: {:#?}", element_damage);
402
403 element
404 .draw(&mut frame, src, dst, &element_damage, &[])
405 .map_err(BlitFrameResultError::Rendering)?;
406 }
407
408 Ok(frame.finish().map_err(BlitFrameResultError::Rendering)?)
409 } else {
410 Ok(sync.unwrap_or_default())
411 }
412 }
413}
414
415impl<B: Buffer + std::fmt::Debug, F: Framebuffer + std::fmt::Debug, E: std::fmt::Debug> std::fmt::Debug
416 for RenderFrameResult<'_, B, F, E>
417{
418 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
419 f.debug_struct("RenderFrameResult")
420 .field("is_empty", &self.is_empty)
421 .field("states", &self.states)
422 .field("primary_element", &self.primary_element)
423 .field("overlay_elements", &self.overlay_elements)
424 .field("cursor_element", &self.cursor_element)
425 .finish()
426 }
427}
428
429#[derive(Debug)]
430pub struct PrimarySwapchainElement<B: Buffer, F: Framebuffer> {
432 pub(super) slot: DrmScanoutBuffer<B, F>,
434 pub sync: SyncPoint,
436 pub transform: Transform,
438 pub damage: DamageSnapshot<i32, BufferCoords>,
440}
441
442impl<B: Buffer, F: Framebuffer> PrimarySwapchainElement<B, F> {
443 #[inline]
445 pub fn buffer(&self) -> &B {
446 match &self.slot.buffer {
447 ScanoutBuffer::Swapchain(slot) => slot,
448 _ => unreachable!(),
449 }
450 }
451}