1use tracing::warn;
5#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
6use wayland_server::protocol::wl_buffer;
7
8use crate::backend::{
9 SwapBuffersError,
10 allocator::{
11 Allocator,
12 dmabuf::{AnyError, Dmabuf, DmabufAllocator},
13 gbm::{GbmAllocator, GbmBufferFlags, GbmDevice},
14 },
15 drm::{CreateDrmNodeError, DrmNode},
16 egl::{EGLContext, EGLDisplay, Error as EGLError, context::ContextPriority},
17 renderer::{
18 Renderer, RendererSuper,
19 gles::{GlesError, GlesRenderer},
20 multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
21 },
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 Bind, ExportMem, ImportDma, ImportEgl, ImportMem,
30 multigpu::{Error as MultigpuError, MultiRenderer, MultiTexture, import_dmabuf_internal},
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<R: From<GlesRenderer> + Renderer<Error = GlesError> + Borrow<GlesRenderer>, A: AsFd + Clone + 'static>
157 GraphicsApi for GbmGlesBackend<R, A>
158{
159 type Device = GbmGlesDevice<R>;
160 type Error = Error;
161
162 fn enumerate(&self, list: &mut Vec<Self::Device>) -> Result<(), Self::Error> {
163 self.needs_enumeration.store(false, Ordering::SeqCst);
164
165 list.retain(|renderer| {
167 self.devices
168 .keys()
169 .any(|node| renderer.node.dev_id() == node.dev_id())
170 });
171
172 let new_renderers = self
174 .devices
175 .iter()
176 .filter(|(node, _)| {
177 !list
178 .iter()
179 .any(|renderer| renderer.node.dev_id() == node.dev_id())
180 })
181 .map(|(node, (display, gbm))| {
182 let renderer = if let Some(factory) = self.factory.as_ref() {
183 factory(display)?.into()
184 } else {
185 let context =
186 EGLContext::new_with_priority(display, self.context_priority.unwrap_or_default())
187 .map_err(Error::Egl)?;
188 unsafe { GlesRenderer::new(context).map_err(Error::Gl)? }.into()
189 };
190
191 Ok(GbmGlesDevice {
192 node: *node,
193 _display: display.clone(),
194 renderer,
195 allocator: Box::new(DmabufAllocator(gbm.clone())),
196 })
197 })
198 .flat_map(|x: Result<GbmGlesDevice<R>, Error>| match x {
199 Ok(x) => Some(x),
200 Err(x) => {
201 warn!("Skipping GbmDevice: {}", x);
202 None
203 }
204 })
205 .collect::<Vec<GbmGlesDevice<R>>>();
206 list.extend(new_renderers);
207
208 Ok(())
211 }
212
213 fn needs_enumeration(&self) -> bool {
214 self.needs_enumeration.load(Ordering::Acquire)
215 }
216
217 fn identifier() -> &'static str {
218 "gbm_gles"
219 }
220}
221
222impl<
224 T: GraphicsApi,
225 R: From<GlesRenderer> + Renderer<Error = GlesError> + Borrow<GlesRenderer>,
226 A: AsFd + Clone + 'static,
227> std::convert::From<GlesError> for MultiError<GbmGlesBackend<R, A>, T>
228where
229 T::Error: 'static,
230 <<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
231{
232 #[inline]
233 fn from(err: GlesError) -> MultiError<GbmGlesBackend<R, A>, T> {
234 MultiError::Render(err)
235 }
236}
237
238pub struct GbmGlesDevice<R> {
240 node: DrmNode,
241 renderer: R,
242 allocator: Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
243 _display: EGLDisplay,
244}
245
246impl<R: Renderer> fmt::Debug for GbmGlesDevice<R> {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 f.debug_struct("GbmGlesDevice")
249 .field("node", &self.node)
250 .field("renderer", &self.renderer)
251 .finish_non_exhaustive()
252 }
253}
254
255impl<R: Renderer + Borrow<GlesRenderer>> ApiDevice for GbmGlesDevice<R> {
256 type Renderer = R;
257
258 fn renderer(&self) -> &Self::Renderer {
259 &self.renderer
260 }
261 fn renderer_mut(&mut self) -> &mut Self::Renderer {
262 &mut self.renderer
263 }
264 fn allocator(&mut self) -> &mut dyn Allocator<Buffer = Dmabuf, Error = AnyError> {
265 self.allocator.as_mut()
266 }
267 fn node(&self) -> &DrmNode {
268 &self.node
269 }
270 fn can_do_cross_device_imports(&self) -> bool {
271 !self.renderer.borrow().is_software()
272 }
273}
274
275#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
276impl<R, A> ImportEgl for MultiRenderer<'_, '_, GbmGlesBackend<R, A>, GbmGlesBackend<R, A>>
277where
278 A: AsFd + Clone + 'static,
279 R: From<GlesRenderer>
280 + BorrowMut<GlesRenderer>
281 + Renderer<Error = GlesError>
282 + Bind<Dmabuf>
283 + ImportDma
284 + ImportMem
285 + ImportEgl
286 + ExportMem
287 + 'static,
288 R::TextureId: Clone + Send,
289{
290 fn bind_wl_display(&mut self, display: &wayland_server::DisplayHandle) -> Result<(), EGLError> {
291 self.render.renderer_mut().bind_wl_display(display)
292 }
293 fn unbind_wl_display(&mut self) {
294 self.render.renderer_mut().unbind_wl_display()
295 }
296 fn egl_reader(&self) -> Option<&EGLBufferReader> {
297 self.render.renderer().egl_reader()
298 }
299
300 #[profiling::function]
301 fn import_egl_buffer(
302 &mut self,
303 buffer: &wl_buffer::WlBuffer,
304 surface: Option<&crate::wayland::compositor::SurfaceData>,
305 damage: &[Rectangle<i32, BufferCoords>],
306 ) -> Result<Self::TextureId, Self::Error> {
307 if let Some(dmabuf) = Self::try_import_egl(self.render.renderer_mut(), buffer)
308 .ok()
309 .or_else(|| {
310 self.target
311 .as_mut()
312 .and_then(|renderer| Self::try_import_egl(renderer.device.renderer_mut(), buffer).ok())
313 })
314 .or_else(|| {
315 self.other_renderers
316 .iter_mut()
317 .find_map(|renderer| Self::try_import_egl(renderer.renderer_mut(), buffer).ok())
318 })
319 {
320 let texture = MultiTexture::from_surface(surface, dmabuf.size(), dmabuf.format());
321 let texture_ref = texture.0.clone();
322 let res = import_dmabuf_internal(
323 self.render,
324 self.target.as_mut().map(|target| &mut *target.device),
325 &mut self.other_renderers,
326 &dmabuf,
327 texture,
328 Some(damage),
329 );
330 if res.is_ok() {
331 if let Some(surface) = surface {
332 surface.data_map.insert_if_missing_threadsafe(|| texture_ref);
333 }
334 }
335 return res;
336 }
337
338 Err(MultigpuError::DeviceMissing)
339 }
340}
341
342#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
343impl<R, A> MultiRenderer<'_, '_, GbmGlesBackend<R, A>, GbmGlesBackend<R, A>>
344where
345 A: AsFd + Clone + 'static,
346 R: From<GlesRenderer>
347 + BorrowMut<GlesRenderer>
348 + Renderer<Error = GlesError>
349 + ImportDma
350 + ImportMem
351 + ImportEgl
352 + ExportMem
353 + 'static,
354{
355 #[profiling::function]
356 fn try_import_egl(
357 renderer: &mut R,
358 buffer: &wl_buffer::WlBuffer,
359 ) -> Result<Dmabuf, MultigpuError<GbmGlesBackend<R, A>, GbmGlesBackend<R, A>>> {
360 if !renderer
361 .borrow_mut()
362 .extensions
363 .iter()
364 .any(|ext| ext == "GL_OES_EGL_image")
365 {
366 return Err(MultigpuError::Render(GlesError::GLExtensionNotSupported(&[
367 "GL_OES_EGL_image",
368 ])));
369 }
370
371 if renderer.egl_reader().is_none() {
372 return Err(MultigpuError::Render(GlesError::EGLBufferAccessError(
373 crate::backend::egl::BufferAccessError::NotManaged(crate::backend::egl::EGLError::BadDisplay),
374 )));
375 }
376
377 unsafe {
378 renderer
379 .borrow_mut()
380 .egl_context()
381 .make_current()
382 .map_err(GlesError::from)
383 .map_err(MultigpuError::Render)?
384 };
385
386 let egl = renderer
387 .egl_reader()
388 .as_ref()
389 .unwrap()
390 .egl_buffer_contents(buffer)
391 .map_err(GlesError::EGLBufferAccessError)
392 .map_err(MultigpuError::Render)?;
393 renderer
394 .borrow_mut()
395 .egl_context()
396 .display()
397 .create_dmabuf_from_image(egl.image(0).unwrap(), egl.size, egl.y_inverted)
398 .map_err(GlesError::BindBufferEGLError)
399 .map_err(MultigpuError::Render)
400 }
401}