chore: refresco desde el monorepo (pin tawasuyu.git v0.2.0)
1use std::{fmt, fs::File, os::unix::io::OwnedFd, path::PathBuf};
2
3use clap::{Args, Parser, Subcommand, ValueEnum};
4use smithay::{
5 backend::{
6 allocator::{
7 dmabuf::{AnyError, Dmabuf, DmabufAllocator},
8 dumb::DumbAllocator,
9 gbm::{GbmAllocator, GbmBufferFlags, GbmDevice},
10 vulkan::{ImageUsageFlags, VulkanAllocator},
11 Allocator, Fourcc, Modifier,
12 },
13 drm::{DrmDeviceFd, DrmNode},
14 egl::{EGLContext, EGLDevice, EGLDisplay},
15 renderer::{
16 gles::{GlesRenderbuffer, GlesRenderer},
17 Bind, Color32F, ExportMem, Frame, ImportDma, Offscreen, Renderer,
18 },
19 vulkan::{version::Version, Instance, PhysicalDevice},
20 },
21 utils::{DeviceFd, Rectangle, Transform},
22};
23use tracing::info;
24
25#[derive(Parser, Debug)]
26#[command(author, version, about, long_about = None)]
27struct Cli {
28 #[command(subcommand)]
29 subcommand: Subcommands,
30}
31
32#[derive(Subcommand, Debug)]
33enum Subcommands {
34 /// Intersect formats to find usable combinations
35 #[command(arg_required_else_help = true)]
36 Formats {
37 /// Devices that should be able to render a format
38 #[arg(short, long)]
39 render: Vec<String>,
40 /// Devices that should be able to sample from a format
41 #[arg(short, long)]
42 sample: Vec<String>,
43 },
44 /// Test exporting and importing buffers with various apis and devices
45 Test(TestArgs),
46}
47
48#[derive(Args, Debug)]
49struct TestArgs {
50 /// Allocator used for creating the buffer
51 #[arg(short, long)]
52 allocator: AllocatorType,
53 /// Usage flags for allocating the image, when using the vulkan api
54 #[arg(short, long, value_parser = usage_flags_from_string, required_if_eq("allocator", "vulkan"))]
55 usage_flags: Option<ImageUsageFlags>,
56
57 /// Exporting device node
58 #[arg(short, long, required_if_eq_any([("allocator", "dumb"), ("allocator", "gbm"), ("allocator", "vulkan")]))]
59 export: Option<String>,
60 /// Importing device node (may be equal to export)
61 #[arg(short, long)]
62 import: String,
63 /// Renderer used to draw into buffer before exporting
64 #[arg(short = 'r', long)]
65 export_renderer: Option<RendererType>,
66 #[arg(short = 't', long)]
67 import_renderer: Option<RendererType>,
68 #[arg(short, long, requires("export_renderer"), requires("import_renderer"))]
69 dump: Option<String>,
70
71 #[arg(long, default_value_t = 256)]
72 width: u32,
73 #[arg(long, default_value_t = 256)]
74 height: u32,
75 #[arg(short, long, default_value = "Abgr8888", value_parser = fourcc_from_string)]
76 fourcc: Fourcc,
77 #[arg(short, long, default_value = "Linear", value_parser = modifier_from_string)]
78 modifier: Modifier,
79}
80
81#[derive(ValueEnum, Debug, Clone, Copy)]
82pub enum AllocatorType {
83 #[value(name = "dumb")]
84 DumbBuffer,
85 #[value(name = "gbm")]
86 Gbm,
87 #[value(name = "vulkan")]
88 Vulkan,
89}
90
91#[derive(ValueEnum, Debug, Clone, Copy)]
92pub enum RendererType {
93 #[value(name = "gles")]
94 Gles,
95}
96
97impl fmt::Display for RendererType {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 f.write_str("Gles")
100 }
101}
102
103fn main() {
104 let args = Cli::parse();
105
106 if let Ok(env_filter) = tracing_subscriber::EnvFilter::try_from_default_env() {
107 tracing_subscriber::fmt().with_env_filter(env_filter).init();
108 } else {
109 tracing_subscriber::fmt().init();
110 }
111
112 match args.subcommand {
113 Subcommands::Formats { render, sample } => format_test(render, sample),
114 Subcommands::Test(args) => buffer_test(args),
115 }
116}
117
118fn format_test(render: Vec<String>, sample: Vec<String>) {
119 for format in render
120 .iter()
121 .map(|path| {
122 let path = PathBuf::from(&path);
123 let device = EGLDevice::enumerate()
124 .expect("Failed to enumerate EGL devices")
125 .find(|device| {
126 device.drm_device_path().ok().as_ref() == Some(&path)
127 || device.render_device_path().ok().as_ref() == Some(&path)
128 })
129 .expect("Unable to find egl device");
130 let display = unsafe { EGLDisplay::new(device).expect("Failed to create EGL display") };
131 display.dmabuf_render_formats().clone()
132 })
133 .chain(sample.iter().map(|path| {
134 let path = PathBuf::from(&path);
135 let device = EGLDevice::enumerate()
136 .expect("Failed to enumerate EGL devices")
137 .find(|device| {
138 device.drm_device_path().ok().as_ref() == Some(&path)
139 || device.render_device_path().ok().as_ref() == Some(&path)
140 })
141 .expect("Unable to find egl device");
142 let display = unsafe { EGLDisplay::new(device).expect("Failed to create EGL display") };
143 display.dmabuf_texture_formats().clone()
144 }))
145 .fold(None, |set, formats| match set {
146 None => Some(formats),
147 Some(set) => Some(set.intersection(&formats).copied().collect()),
148 })
149 .unwrap_or_default()
150 {
151 println!(
152 "Format: Fourcc {} / Modifier {}",
153 format.code,
154 Into::<u64>::into(format.modifier)
155 )
156 }
157}
158
159fn open_device(path: &str) -> DrmDeviceFd {
160 let file = File::options()
161 .read(true)
162 .write(true)
163 .open(path)
164 .expect("Failed to open device node");
165 DrmDeviceFd::new(DeviceFd::from(Into::<OwnedFd>::into(file)))
166}
167
168fn buffer_test(args: TestArgs) {
169 // 1. create allocator
170 let path = args
171 .export
172 .expect("Dumb buffer allocator requires an export device");
173 info!("Export device: {}", path);
174 let mut allocator = match args.allocator {
175 AllocatorType::DumbBuffer => {
176 let fd = open_device(&path);
177 let dumb_allocator = DumbAllocator::new(fd);
178 Box::new(DmabufAllocator(dumb_allocator)) as Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>
179 }
180 AllocatorType::Gbm => {
181 let fd = open_device(&path);
182 let gbm = GbmDevice::new(fd).expect("Failed to init gbm device");
183 let gbm_allocator = GbmAllocator::new(gbm, GbmBufferFlags::RENDERING);
184 Box::new(DmabufAllocator(gbm_allocator)) as Box<_>
185 }
186 AllocatorType::Vulkan => {
187 let node = DrmNode::from_path(&path).expect("Failed to find drm node");
188 let instance =
189 Instance::new(Version::VERSION_1_2, None).expect("Unable to create vulkan instance");
190 let physical_device = PhysicalDevice::enumerate(&instance)
191 .expect("Failed to enumerate physical devices")
192 .filter(|phd| phd.has_device_extension(c"VK_EXT_physical_device_drm"))
193 .find(|phd| {
194 phd.primary_node().unwrap() == Some(node) || phd.render_node().unwrap() == Some(node)
195 })
196 .expect("Unable to find physical device");
197 Box::new(DmabufAllocator(
198 VulkanAllocator::new(&physical_device, args.usage_flags.unwrap())
199 .expect("Failed to create vulkan allocator"),
200 )) as Box<_>
201 }
202 };
203
204 // 2. alloc buffer
205 let mut buffer = allocator
206 .create_buffer(args.width, args.height, args.fourcc, &[args.modifier])
207 .expect("Failed to allocate buffer");
208
209 // 3. render into buffer on src device
210 match args.export_renderer {
211 None => {}
212 Some(RendererType::Gles) => {
213 let path = PathBuf::from(&path);
214 let device = EGLDevice::enumerate()
215 .expect("Failed to enumerate EGL devices")
216 .find(|device| {
217 device.drm_device_path().ok().as_ref() == Some(&path)
218 || device.render_device_path().ok().as_ref() == Some(&path)
219 })
220 .expect("Unable to find egl device");
221 let display = unsafe { EGLDisplay::new(device).expect("Failed to create EGL display") };
222
223 let context = EGLContext::new(&display).expect("Failed to create EGL context");
224 let mut renderer = unsafe { GlesRenderer::new(context).expect("Failed to init GL ES renderer") };
225
226 render_into(&mut renderer, &mut buffer, args.width as i32, args.height as i32);
227 }
228 }
229
230 // 4. import / render buffer on dst device
231 let path = PathBuf::from(args.import);
232 info!("Import device: {}", path.display());
233 match args.import_renderer {
234 None => {
235 let device = EGLDevice::enumerate()
236 .expect("Failed to enumerate EGL devices")
237 .find(|device| {
238 device.drm_device_path().ok().as_ref() == Some(&path)
239 || device.render_device_path().ok().as_ref() == Some(&path)
240 })
241 .expect("Unable to find egl device");
242 let display = unsafe { EGLDisplay::new(device).expect("Failed to create EGL display") };
243
244 display
245 .create_image_from_dmabuf(&buffer)
246 .expect("Failed to import dmabuf");
247 }
248 Some(RendererType::Gles) => {
249 let device = EGLDevice::enumerate()
250 .expect("Failed to enumerate EGL devices")
251 .find(|device| {
252 device.drm_device_path().ok().as_ref() == Some(&path)
253 || device.render_device_path().ok().as_ref() == Some(&path)
254 })
255 .expect("Unable to find egl device");
256 let display = unsafe { EGLDisplay::new(device).expect("Failed to create EGL display") };
257
258 let context = EGLContext::new(&display).expect("Failed to create EGL context");
259 let mut renderer = unsafe { GlesRenderer::new(context).expect("Failed to init GL ES renderer") };
260
261 render_from::<_, GlesRenderbuffer>(
262 &mut renderer,
263 buffer,
264 args.width as i32,
265 args.height as i32,
266 args.dump,
267 );
268 }
269 }
270}
271
272fn render_into<R, T>(renderer: &mut R, buffer: &mut T, w: i32, h: i32)
273where
274 R: Renderer + Bind<T>,
275{
276 // Bind it as a framebuffer
277 let mut framebuffer = renderer.bind(buffer).expect("Failed to bind dmabuf");
278
279 let mut frame = renderer
280 .render(&mut framebuffer, (w, h).into(), Transform::Normal)
281 .expect("Failed to create render frame");
282 frame
283 .clear(
284 Color32F::new(1.0, 0.0, 0.0, 1.0),
285 &[Rectangle::from_size((w / 2, h / 2).into())],
286 )
287 .expect("Render error");
288 frame
289 .clear(
290 Color32F::new(0.0, 1.0, 0.0, 1.0),
291 &[Rectangle::new((w / 2, 0).into(), (w / 2, h / 2).into())],
292 )
293 .expect("Render error");
294 frame
295 .clear(
296 Color32F::new(0.0, 0.0, 1.0, 1.0),
297 &[Rectangle::new((0, h / 2).into(), (w / 2, h / 2).into())],
298 )
299 .expect("Render error");
300 frame
301 .clear(
302 Color32F::new(1.0, 1.0, 0.0, 1.0),
303 &[Rectangle::new((w / 2, h / 2).into(), (w / 2, h / 2).into())],
304 )
305 .expect("Render error");
306 frame
307 .finish()
308 .expect("Failed to finish render frame")
309 .wait()
310 .expect("Synchronization error");
311}
312
313fn render_from<R, T>(renderer: &mut R, buffer: Dmabuf, w: i32, h: i32, dump: Option<String>)
314where
315 R: Renderer + ImportDma + ExportMem + Offscreen<T>,
316{
317 let texture = renderer
318 .import_dmabuf(&buffer, None)
319 .expect("Failed to import dmabuf");
320 let mut offscreen = Offscreen::<T>::create_buffer(renderer, Fourcc::Abgr8888, (w, h).into())
321 .expect("Failed to create offscreen buffer");
322 let mut framebuffer = renderer
323 .bind(&mut offscreen)
324 .expect("Failed to bind offscreen buffer");
325 let mut frame = renderer
326 .render(&mut framebuffer, (w, h).into(), Transform::Normal)
327 .expect("Failed to create render frame");
328 frame
329 .render_texture_at(
330 &texture,
331 (0, 0).into(),
332 1,
333 1.,
334 Transform::Normal,
335 &[Rectangle::from_size((w, h).into())],
336 &[],
337 1.0,
338 )
339 .expect("Failed to sample dmabuf");
340 frame
341 .finish()
342 .expect("Failed to finish render frame")
343 .wait()
344 .expect("Synchronization error");
345
346 if let Some(path) = dump {
347 let mapping = renderer
348 .copy_framebuffer(
349 &framebuffer,
350 Rectangle::from_size((w, h).into()),
351 Fourcc::Abgr8888,
352 )
353 .expect("Failed to map framebuffer");
354 let copy = renderer.map_texture(&mapping).expect("Failed to read mapping");
355 image::save_buffer(path, copy, w as u32, h as u32, image::ColorType::Rgba8)
356 .expect("Failed to save image");
357 }
358}
359
360fn fourcc_from_string(name: &str) -> Result<Fourcc, &'static str> {
361 Ok(match name {
362 "Abgr1555" => Fourcc::Abgr1555,
363 "Abgr16161616f" => Fourcc::Abgr16161616f,
364 "Abgr2101010" => Fourcc::Abgr2101010,
365 "Abgr4444" => Fourcc::Abgr4444,
366 "Abgr8888" => Fourcc::Abgr8888,
367 "Argb1555" => Fourcc::Argb1555,
368 "Argb16161616f" => Fourcc::Argb16161616f,
369 "Argb2101010" => Fourcc::Argb2101010,
370 "Argb4444" => Fourcc::Argb4444,
371 "Argb8888" => Fourcc::Argb8888,
372 "Axbxgxrx106106106106" => Fourcc::Axbxgxrx106106106106,
373 "Ayuv" => Fourcc::Ayuv,
374 "Bgr233" => Fourcc::Bgr233,
375 "Bgr565" => Fourcc::Bgr565,
376 "Bgr565_a8" => Fourcc::Bgr565_a8,
377 "Bgr888" => Fourcc::Bgr888,
378 "Bgr888_a8" => Fourcc::Bgr888_a8,
379 "Bgra1010102" => Fourcc::Bgra1010102,
380 "Bgra4444" => Fourcc::Bgra4444,
381 "Bgra5551" => Fourcc::Bgra5551,
382 "Bgra8888" => Fourcc::Bgra8888,
383 "Bgrx1010102" => Fourcc::Bgrx1010102,
384 "Bgrx4444" => Fourcc::Bgrx4444,
385 "Bgrx5551" => Fourcc::Bgrx5551,
386 "Bgrx8888" => Fourcc::Bgrx8888,
387 "Bgrx8888_a8" => Fourcc::Bgrx8888_a8,
388 "Big_endian" => Fourcc::Big_endian,
389 "C8" => Fourcc::C8,
390 "Gr1616" => Fourcc::Gr1616,
391 "Gr88" => Fourcc::Gr88,
392 "Nv12" => Fourcc::Nv12,
393 "Nv15" => Fourcc::Nv15,
394 "Nv16" => Fourcc::Nv16,
395 "Nv21" => Fourcc::Nv21,
396 "Nv24" => Fourcc::Nv24,
397 "Nv42" => Fourcc::Nv42,
398 "Nv61" => Fourcc::Nv61,
399 "P010" => Fourcc::P010,
400 "P012" => Fourcc::P012,
401 "P016" => Fourcc::P016,
402 "P210" => Fourcc::P210,
403 "Q401" => Fourcc::Q401,
404 "Q410" => Fourcc::Q410,
405 "R16" => Fourcc::R16,
406 "R8" => Fourcc::R8,
407 "Rg1616" => Fourcc::Rg1616,
408 "Rg88" => Fourcc::Rg88,
409 "Rgb332" => Fourcc::Rgb332,
410 "Rgb565" => Fourcc::Rgb565,
411 "Rgb565_a8" => Fourcc::Rgb565_a8,
412 "Rgb888" => Fourcc::Rgb888,
413 "Rgb888_a8" => Fourcc::Rgb888_a8,
414 "Rgba1010102" => Fourcc::Rgba1010102,
415 "Rgba4444" => Fourcc::Rgba4444,
416 "Rgba5551" => Fourcc::Rgba5551,
417 "Rgba8888" => Fourcc::Rgba8888,
418 "Rgbx1010102" => Fourcc::Rgbx1010102,
419 "Rgbx4444" => Fourcc::Rgbx4444,
420 "Rgbx5551" => Fourcc::Rgbx5551,
421 "Rgbx8888" => Fourcc::Rgbx8888,
422 "Rgbx8888_a8" => Fourcc::Rgbx8888_a8,
423 "Uyvy" => Fourcc::Uyvy,
424 "Vuy101010" => Fourcc::Vuy101010,
425 "Vuy888" => Fourcc::Vuy888,
426 "Vyuy" => Fourcc::Vyuy,
427 "X0l0" => Fourcc::X0l0,
428 "X0l2" => Fourcc::X0l2,
429 "Xbgr1555" => Fourcc::Xbgr1555,
430 "Xbgr16161616f" => Fourcc::Xbgr16161616f,
431 "Xbgr2101010" => Fourcc::Xbgr2101010,
432 "Xbgr4444" => Fourcc::Xbgr4444,
433 "Xbgr8888" => Fourcc::Xbgr8888,
434 "Xbgr8888_a8" => Fourcc::Xbgr8888_a8,
435 "Xrgb1555" => Fourcc::Xrgb1555,
436 "Xrgb16161616f" => Fourcc::Xrgb16161616f,
437 "Xrgb2101010" => Fourcc::Xrgb2101010,
438 "Xrgb4444" => Fourcc::Xrgb4444,
439 "Xrgb8888" => Fourcc::Xrgb8888,
440 "Xrgb8888_a8" => Fourcc::Xrgb8888_a8,
441 "Xvyu12_16161616" => Fourcc::Xvyu12_16161616,
442 "Xvyu16161616" => Fourcc::Xvyu16161616,
443 "Xvyu2101010" => Fourcc::Xvyu2101010,
444 "Xyuv8888" => Fourcc::Xyuv8888,
445 "Y0l0" => Fourcc::Y0l0,
446 "Y0l2" => Fourcc::Y0l2,
447 "Y210" => Fourcc::Y210,
448 "Y212" => Fourcc::Y212,
449 "Y216" => Fourcc::Y216,
450 "Y410" => Fourcc::Y410,
451 "Y412" => Fourcc::Y412,
452 "Y416" => Fourcc::Y416,
453 "Yuv410" => Fourcc::Yuv410,
454 "Yuv411" => Fourcc::Yuv411,
455 "Yuv420" => Fourcc::Yuv420,
456 "Yuv420_10bit" => Fourcc::Yuv420_10bit,
457 "Yuv420_8bit" => Fourcc::Yuv420_8bit,
458 "Yuv422" => Fourcc::Yuv422,
459 "Yuv444" => Fourcc::Yuv444,
460 "Yuyv" => Fourcc::Yuyv,
461 "Yvu410" => Fourcc::Yvu410,
462 "Yvu411" => Fourcc::Yvu411,
463 "Yvu420" => Fourcc::Yvu420,
464 "Yvu422" => Fourcc::Yvu422,
465 "Yvu444" => Fourcc::Yvu444,
466 "Yvyu" => Fourcc::Yvyu,
467 _ => {
468 return Err("Unknown pixel format");
469 }
470 })
471}
472
473fn modifier_from_string(modifier: &str) -> Result<Modifier, std::num::ParseIntError> {
474 Ok(match modifier {
475 "Invalid" => Modifier::Invalid,
476 "Linear" => Modifier::Linear,
477 x => Modifier::from(str::parse::<u64>(x)?),
478 })
479}
480
481fn usage_flags_from_string(flags: &str) -> Result<ImageUsageFlags, String> {
482 let flags = flags
483 .split('+')
484 .map(|f| {
485 Ok(match f.to_lowercase().trim() {
486 "all" => ImageUsageFlags::all(),
487 "empty" | "none" => ImageUsageFlags::empty(),
488 "color_attachment" => ImageUsageFlags::COLOR_ATTACHMENT,
489 "sampled" => ImageUsageFlags::SAMPLED,
490 "transfer_src" => ImageUsageFlags::TRANSFER_SRC,
491 "transfer_dst" => ImageUsageFlags::TRANSFER_DST,
492 x => {
493 return Err(format!("Unknown Usage Flag: {}", x));
494 }
495 })
496 })
497 .collect::<Result<Vec<ImageUsageFlags>, String>>()?;
498 Ok(flags
499 .into_iter()
500 .fold(ImageUsageFlags::empty(), |akk, val| akk | val))
501}