1use tracing::warn;
5#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
6use wayland_server::protocol::wl_buffer;
7
8use crate::backend::{
9 allocator::{
10 dmabuf::{AnyError, Dmabuf, DmabufAllocator},
11 gbm::{GbmAllocator, GbmBufferFlags, GbmDevice},
12 Allocator,
13 },
14 drm::{CreateDrmNodeError, DrmNode},
15 egl::{context::ContextPriority, EGLContext, EGLDisplay, Error as EGLError},
16 renderer::{
17 gles::{GlesError, GlesRenderer},
18 multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
19 Renderer, RendererSuper,
20 },
21 SwapBuffersError,
22};
23#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
24use crate::{
25 backend::{
26 allocator::Buffer as BufferTrait,
27 egl::display::EGLBufferReader,
28 renderer::{
29 multigpu::{Error as MultigpuError, MultiRenderer, MultiTexture},
30 Bind, ExportMem, ImportDma, ImportEgl, ImportMem,
31 },
32 },
33 utils::{Buffer as BufferCoords, Rectangle},
34};
35#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
36use std::borrow::BorrowMut;
37use std::{
38 borrow::Borrow,
39 collections::HashMap,
40 fmt,
41 os::unix::prelude::AsFd,
42 sync::atomic::{AtomicBool, Ordering},
43};
44
45#[derive(Debug, thiserror::Error)]
47pub enum Error {
48 #[error(transparent)]
50 Egl(#[from] EGLError),
51 #[error(transparent)]
53 Gl(#[from] GlesError),
54 #[error(transparent)]
56 DrmNode(#[from] CreateDrmNodeError),
57}
58
59impl From<Error> for SwapBuffersError {
60 #[inline]
61 fn from(err: Error) -> SwapBuffersError {
62 match err {
63 x @ Error::DrmNode(_) | x @ Error::Egl(_) => SwapBuffersError::ContextLost(Box::new(x)),
64 Error::Gl(x) => x.into(),
65 }
66 }
67}
68
69type Factory = Box<dyn Fn(&EGLDisplay) -> Result<GlesRenderer, Error>>;
70
71pub struct GbmGlesBackend<R, A: AsFd + 'static> {
73 devices: HashMap<DrmNode, (EGLDisplay, GbmAllocator<A>)>,
74 factory: Option<Factory>,
75 context_priority: Option<ContextPriority>,
76 allocator_flags: GbmBufferFlags,
77 needs_enumeration: AtomicBool,
78 _renderer: std::marker::PhantomData<R>,
79}
80
81impl<R, A: AsFd + fmt::Debug + 'static> fmt::Debug for GbmGlesBackend<R, A> {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 f.debug_struct("GbmGlesBackend")
84 .field("devices", &self.devices)
85 .finish()
86 }
87}
88
89impl<R, A: AsFd + 'static> Default for GbmGlesBackend<R, A> {
90 #[inline]
91 fn default() -> Self {
92 GbmGlesBackend {
93 devices: HashMap::new(),
94 factory: None,
95 context_priority: None,
96 allocator_flags: GbmBufferFlags::RENDERING,
97 needs_enumeration: AtomicBool::new(true),
98 _renderer: std::marker::PhantomData,
99 }
100 }
101}
102
103impl<R, A: AsFd + Clone + Send + 'static> GbmGlesBackend<R, A> {
104 pub fn with_factory<F>(factory: F) -> Self
106 where
107 F: Fn(&EGLDisplay) -> Result<GlesRenderer, Error> + 'static,
108 {
109 Self {
110 factory: Some(Box::new(factory)),
111 ..Default::default()
112 }
113 }
114
115 pub fn with_context_priority(priority: ContextPriority) -> Self {
121 Self {
122 context_priority: Some(priority),
123 ..Default::default()
124 }
125 }
126
127 pub fn set_allocator_flags(&mut self, flags: GbmBufferFlags) {
132 self.allocator_flags = flags;
133 }
134
135 pub fn add_node(&mut self, node: DrmNode, gbm: GbmDevice<A>) -> Result<(), EGLError> {
137 if self.devices.contains_key(&node) {
138 return Ok(());
139 }
140
141 let allocator = GbmAllocator::new(gbm.clone(), self.allocator_flags);
142 self.devices
143 .insert(node, (unsafe { EGLDisplay::new(gbm)? }, allocator));
144 self.needs_enumeration.store(true, Ordering::SeqCst);
145 Ok(())
146 }
147
148 pub fn remove_node(&mut self, node: &DrmNode) {
150 if self.devices.remove(node).is_some() {
151 self.needs_enumeration.store(true, Ordering::SeqCst);
152 }
153 }
154}
155
156impl<
157 R: From<GlesRenderer> + Renderer<Error = GlesError> + Borrow<GlesRenderer>,
158 A: AsFd + Clone + 'static,
159 > GraphicsApi for GbmGlesBackend<R, A>
160{
161 type Device = GbmGlesDevice<R>;
162 type Error = Error;
163
164 fn enumerate(&self, list: &mut Vec<Self::Device>) -> Result<(), Self::Error> {
165 self.needs_enumeration.store(false, Ordering::SeqCst);
166
167 list.retain(|renderer| {
169 self.devices
170 .keys()
171 .any(|node| renderer.node.dev_id() == node.dev_id())
172 });
173
174 let new_renderers = self
176 .devices
177 .iter()
178 .filter(|(node, _)| {
179 !list
180 .iter()
181 .any(|renderer| renderer.node.dev_id() == node.dev_id())
182 })
183 .map(|(node, (display, gbm))| {
184 let renderer = if let Some(factory) = self.factory.as_ref() {
185 factory(display)?.into()
186 } else {
187 let context =
188 EGLContext::new_with_priority(display, self.context_priority.unwrap_or_default())
189 .map_err(Error::Egl)?;
190 unsafe { GlesRenderer::new(context).map_err(Error::Gl)? }.into()
191 };
192
193 Ok(GbmGlesDevice {
194 node: *node,
195 _display: display.clone(),
196 renderer,
197 allocator: Box::new(DmabufAllocator(gbm.clone())),
198 })
199 })
200 .flat_map(|x: Result<GbmGlesDevice<R>, Error>| match x {
201 Ok(x) => Some(x),
202 Err(x) => {
203 warn!("Skipping GbmDevice: {}", x);
204 None
205 }
206 })
207 .collect::<Vec<GbmGlesDevice<R>>>();
208 list.extend(new_renderers);
209
210 Ok(())
213 }
214
215 fn needs_enumeration(&self) -> bool {
216 self.needs_enumeration.load(Ordering::Acquire)
217 }
218
219 fn identifier() -> &'static str {
220 "gbm_gles"
221 }
222}
223
224impl<
226 T: GraphicsApi,
227 R: From<GlesRenderer> + Renderer<Error = GlesError> + Borrow<GlesRenderer>,
228 A: AsFd + Clone + 'static,
229 > std::convert::From<GlesError> for MultiError<GbmGlesBackend<R, A>, T>
230where
231 T::Error: 'static,
232 <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
233{
234 #[inline]
235 fn from(err: GlesError) -> MultiError<GbmGlesBackend<R, A>, T> {
236 MultiError::Render(err)
237 }
238}
239
240pub struct GbmGlesDevice<R> {
242 node: DrmNode,
243 renderer: R,
244 allocator: Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
245 _display: EGLDisplay,
246}
247
248impl<R: Renderer> fmt::Debug for GbmGlesDevice<R> {
249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250 f.debug_struct("GbmGlesDevice")
251 .field("node", &self.node)
252 .field("renderer", &self.renderer)
253 .finish_non_exhaustive()
254 }
255}
256
257impl<R: Renderer + Borrow<GlesRenderer>> ApiDevice for GbmGlesDevice<R> {
258 type Renderer = R;
259
260 fn renderer(&self) -> &Self::Renderer {
261 &self.renderer
262 }
263 fn renderer_mut(&mut self) -> &mut Self::Renderer {
264 &mut self.renderer
265 }
266 fn allocator(&mut self) -> &mut dyn Allocator<Buffer = Dmabuf, Error = AnyError> {
267 self.allocator.as_mut()
268 }
269 fn node(&self) -> &DrmNode {
270 &self.node
271 }
272 fn can_do_cross_device_imports(&self) -> bool {
273 !self.renderer.borrow().is_software()
274 }
275}
276
277#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
278impl<R, A> ImportEgl for MultiRenderer<'_, '_, GbmGlesBackend<R, A>, GbmGlesBackend<R, A>>
279where
280 A: AsFd + Clone + 'static,
281 R: From<GlesRenderer>
282 + BorrowMut<GlesRenderer>
283 + Renderer<Error = GlesError>
284 + Bind<Dmabuf>
285 + ImportDma
286 + ImportMem
287 + ImportEgl
288 + ExportMem
289 + 'static,
290 R::TextureId: Clone + Send,
291{
292 fn bind_wl_display(&mut self, display: &wayland_server::DisplayHandle) -> Result<(), EGLError> {
293 self.render.renderer_mut().bind_wl_display(display)
294 }
295 fn unbind_wl_display(&mut self) {
296 self.render.renderer_mut().unbind_wl_display()
297 }
298 fn egl_reader(&self) -> Option<&EGLBufferReader> {
299 self.render.renderer().egl_reader()
300 }
301
302 #[profiling::function]
303 fn import_egl_buffer(
304 &mut self,
305 buffer: &wl_buffer::WlBuffer,
306 surface: Option<&crate::wayland::compositor::SurfaceData>,
307 damage: &[Rectangle<i32, BufferCoords>],
308 ) -> Result<Self::TextureId, Self::Error> {
309 if let Some(dmabuf) = Self::try_import_egl(self.render.renderer_mut(), buffer)
310 .ok()
311 .or_else(|| {
312 self.target
313 .as_mut()
314 .and_then(|renderer| Self::try_import_egl(renderer.device.renderer_mut(), buffer).ok())
315 })
316 .or_else(|| {
317 self.other_renderers
318 .iter_mut()
319 .find_map(|renderer| Self::try_import_egl(renderer.renderer_mut(), buffer).ok())
320 })
321 {
322 let texture = MultiTexture::from_surface(surface, dmabuf.size(), dmabuf.format());
323 let texture_ref = texture.0.clone();
324 let res = self.import_dmabuf_internal(&dmabuf, texture, Some(damage));
325 if res.is_ok() {
326 if let Some(surface) = surface {
327 surface.data_map.insert_if_missing_threadsafe(|| texture_ref);
328 }
329 }
330 return res;
331 }
332
333 Err(MultigpuError::DeviceMissing)
334 }
335}
336
337#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
338impl<R, A> MultiRenderer<'_, '_, GbmGlesBackend<R, A>, GbmGlesBackend<R, A>>
339where
340 A: AsFd + Clone + 'static,
341 R: From<GlesRenderer>
342 + BorrowMut<GlesRenderer>
343 + Renderer<Error = GlesError>
344 + ImportDma
345 + ImportMem
346 + ImportEgl
347 + ExportMem
348 + 'static,
349{
350 #[profiling::function]
351 fn try_import_egl(
352 renderer: &mut R,
353 buffer: &wl_buffer::WlBuffer,
354 ) -> Result<Dmabuf, MultigpuError<GbmGlesBackend<R, A>, GbmGlesBackend<R, A>>> {
355 if !renderer
356 .borrow_mut()
357 .extensions
358 .iter()
359 .any(|ext| ext == "GL_OES_EGL_image")
360 {
361 return Err(MultigpuError::Render(GlesError::GLExtensionNotSupported(&[
362 "GL_OES_EGL_image",
363 ])));
364 }
365
366 if renderer.egl_reader().is_none() {
367 return Err(MultigpuError::Render(GlesError::EGLBufferAccessError(
368 crate::backend::egl::BufferAccessError::NotManaged(crate::backend::egl::EGLError::BadDisplay),
369 )));
370 }
371
372 unsafe {
373 renderer
374 .borrow_mut()
375 .egl_context()
376 .make_current()
377 .map_err(GlesError::from)
378 .map_err(MultigpuError::Render)?
379 };
380
381 let egl = renderer
382 .egl_reader()
383 .as_ref()
384 .unwrap()
385 .egl_buffer_contents(buffer)
386 .map_err(GlesError::EGLBufferAccessError)
387 .map_err(MultigpuError::Render)?;
388 renderer
389 .borrow_mut()
390 .egl_context()
391 .display()
392 .create_dmabuf_from_image(egl.image(0).unwrap(), egl.size, egl.y_inverted)
393 .map_err(GlesError::BindBufferEGLError)
394 .map_err(MultigpuError::Render)
395 }
396}