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