1use crate::gl::{
11 DmabufEgl, EGL_LINUX_DMA_BUF_EXT, Egl, EglImage, GL_TEXTURE_2D, dmabuf_image_attribs,
12 load_dmabuf_egl,
13};
14use crate::wl;
15use edgefirst_egl as egl;
16use std::collections::HashMap;
17use std::ffi::c_void;
18use std::sync::Arc;
19use wayland_client::{Connection, Proxy, protocol::wl_surface::WlSurface};
20
21pub trait DmabufImporter {
25 fn import(
29 &mut self,
30 key: &str,
31 frame: wl::DmabufFrame,
32 ) -> Option<(egui::TextureId, egui::Vec2)>;
33 fn forget(&mut self, key: &str);
35}
36
37const GL_TEXTURE_SWIZZLE_A: u32 = 0x8E45;
41const GL_ONE: i32 = 1;
42
43struct NativeTex {
45 image: EglImage,
46 tex: glow::Texture,
47 id: egui::TextureId,
48 size: egui::Vec2,
49}
50
51struct HostImporter<'a> {
55 egl: Option<DmabufEgl>,
56 gl: Arc<glow::Context>,
57 painter: &'a mut egui_glow::Painter,
58 cache: &'a mut HashMap<String, NativeTex>,
59}
60
61impl DmabufImporter for HostImporter<'_> {
62 fn import(
63 &mut self,
64 key: &str,
65 frame: wl::DmabufFrame,
66 ) -> Option<(egui::TextureId, egui::Vec2)> {
67 use glow::HasContext as _;
68 let egl = self.egl?;
69 let size = egui::vec2(frame.width as f32, frame.height as f32);
70 let attribs = dmabuf_image_attribs(&frame);
71 let image = unsafe {
73 (egl.create_image)(
74 egl.display,
75 std::ptr::null_mut(),
76 EGL_LINUX_DMA_BUF_EXT,
77 std::ptr::null_mut(),
78 attribs.as_ptr(),
79 )
80 };
81 if image.is_null() {
82 return None;
83 }
84
85 let ckey = key.to_string();
86 if let Some(nt) = self.cache.get_mut(&ckey) {
89 unsafe {
90 self.gl.bind_texture(GL_TEXTURE_2D, Some(nt.tex));
91 (egl.image_target)(GL_TEXTURE_2D, image);
92 self.gl.bind_texture(GL_TEXTURE_2D, None);
93 (egl.destroy_image)(egl.display, nt.image);
94 }
95 nt.image = image;
96 nt.size = size;
97 return Some((nt.id, nt.size));
98 }
99
100 let tex = unsafe {
102 let t = self.gl.create_texture().ok()?;
103 self.gl.bind_texture(GL_TEXTURE_2D, Some(t));
104 let lin = glow::LINEAR as i32;
105 let clamp = glow::CLAMP_TO_EDGE as i32;
106 self.gl
107 .tex_parameter_i32(GL_TEXTURE_2D, glow::TEXTURE_MIN_FILTER, lin);
108 self.gl
109 .tex_parameter_i32(GL_TEXTURE_2D, glow::TEXTURE_MAG_FILTER, lin);
110 self.gl
111 .tex_parameter_i32(GL_TEXTURE_2D, glow::TEXTURE_WRAP_S, clamp);
112 self.gl
113 .tex_parameter_i32(GL_TEXTURE_2D, glow::TEXTURE_WRAP_T, clamp);
114 self.gl
117 .tex_parameter_i32(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE);
118 (egl.image_target)(GL_TEXTURE_2D, image);
119 self.gl.bind_texture(GL_TEXTURE_2D, None);
120 t
121 };
122 let id = self.painter.register_native_texture(tex);
123 self.cache.insert(
124 ckey,
125 NativeTex {
126 image,
127 tex,
128 id,
129 size,
130 },
131 );
132 Some((id, size))
133 }
134
135 fn forget(&mut self, key: &str) {
136 use glow::HasContext as _;
137 let Some(egl) = self.egl else { return };
138 if let Some(nt) = self.cache.remove(key) {
139 self.painter.free_texture(nt.id);
140 unsafe {
141 self.gl.delete_texture(nt.tex);
142 (egl.destroy_image)(egl.display, nt.image);
143 }
144 }
145 }
146}
147
148pub struct Gpu {
151 egl: Egl,
152 display: egl::Display,
153 surface: egl::Surface,
154 context: egl::Context,
155 egl_window: wayland_egl::WlEglSurface,
156 painter: egui_glow::Painter,
157 dmabuf_egl: Option<DmabufEgl>,
159 dmabuf_tex: HashMap<String, NativeTex>,
161}
162
163impl Gpu {
164 pub fn new(conn: &Connection, surface: &WlSurface, pw: i32, ph: i32) -> Gpu {
167 let lib = unsafe { egl::DynamicInstance::<egl::EGL1_4>::load_required() }
168 .expect("libEGL not found");
169 let egl: Egl = lib;
170
171 let display_ptr = conn.backend().display_ptr() as *mut c_void;
172 let display = unsafe { egl.get_display(display_ptr).expect("eglGetDisplay") };
173 egl.initialize(display).expect("eglInitialize");
174 egl.bind_api(egl::OPENGL_ES_API).expect("eglBindAPI");
175
176 let attribs = [
177 egl::SURFACE_TYPE,
178 egl::WINDOW_BIT,
179 egl::RENDERABLE_TYPE,
180 egl::OPENGL_ES2_BIT,
181 egl::RED_SIZE,
182 8,
183 egl::GREEN_SIZE,
184 8,
185 egl::BLUE_SIZE,
186 8,
187 egl::ALPHA_SIZE,
188 8,
189 egl::NONE,
190 ];
191 let config = egl
192 .choose_first_config(display, &attribs)
193 .expect("eglChooseConfig")
194 .expect("no EGL config with alpha");
195
196 let ctx_attribs = [egl::CONTEXT_CLIENT_VERSION, 3, egl::NONE];
197 let context = egl
198 .create_context(display, config, None, &ctx_attribs)
199 .or_else(|_| {
200 let a = [egl::CONTEXT_CLIENT_VERSION, 2, egl::NONE];
201 egl.create_context(display, config, None, &a)
202 })
203 .expect("eglCreateContext");
204
205 let egl_window = wayland_egl::WlEglSurface::new(surface.id(), pw, ph).expect("wl_egl");
206 let egl_surface = unsafe {
207 egl.create_window_surface(
208 display,
209 config,
210 egl_window.ptr() as egl::NativeWindowType,
211 None,
212 )
213 .expect("eglCreateWindowSurface")
214 };
215 egl.make_current(display, Some(egl_surface), Some(egl_surface), Some(context))
216 .expect("eglMakeCurrent");
217
218 let gl = unsafe {
219 glow::Context::from_loader_function(|s| {
220 egl.get_proc_address(s)
221 .map_or(std::ptr::null(), |p| p as *const _)
222 })
223 };
224 let painter = egui_glow::Painter::new(Arc::new(gl), "", None, false).expect("egui_glow");
225 let dmabuf_egl = load_dmabuf_egl(&egl, display);
226 if dmabuf_egl.is_none() {
227 eprintln!("wlr-capture: EGL dma-buf import unavailable (GPU display disabled)");
228 }
229
230 Gpu {
231 egl,
232 display,
233 surface: egl_surface,
234 context,
235 egl_window,
236 painter,
237 dmabuf_egl,
238 dmabuf_tex: HashMap::new(),
239 }
240 }
241
242 pub fn resize(&self, pw: i32, ph: i32) {
245 self.egl_window.resize(pw, ph, 0, 0);
246 }
247
248 pub fn render(
252 &mut self,
253 egui_ctx: &egui::Context,
254 mut raw_input: egui::RawInput,
255 ppp: f32,
256 size_px: (u32, u32),
257 backdrop: [f32; 4],
258 mut run_ui: impl FnMut(&mut egui::Ui, &mut dyn DmabufImporter),
259 ) {
260 raw_input
265 .viewports
266 .entry(egui::ViewportId::ROOT)
267 .or_default()
268 .native_pixels_per_point = Some(ppp);
269
270 let (pw, ph) = size_px;
271 self.egl
272 .make_current(
273 self.display,
274 Some(self.surface),
275 Some(self.surface),
276 Some(self.context),
277 )
278 .ok();
279
280 let (prims, textures_delta) = {
283 let gl = self.painter.gl().clone();
284 let mut importer = HostImporter {
285 egl: self.dmabuf_egl,
286 gl,
287 painter: &mut self.painter,
288 cache: &mut self.dmabuf_tex,
289 };
290 let full = egui_ctx.run_ui(raw_input, |ui| run_ui(ui, &mut importer));
293 (egui_ctx.tessellate(full.shapes, ppp), full.textures_delta)
294 };
295
296 unsafe {
297 use glow::HasContext as _;
298 let gl = self.painter.gl();
299 gl.viewport(0, 0, pw as i32, ph as i32);
300 let [r, g, b, a] = backdrop;
301 gl.clear_color(r, g, b, a);
302 gl.clear(glow::COLOR_BUFFER_BIT);
303 }
304 self.painter
305 .paint_and_update_textures([pw, ph], ppp, &prims, &textures_delta);
306 self.egl.swap_buffers(self.display, self.surface).ok();
307 }
308}