tawasuyu

tawasuyu/mirada

Sovereign tiling Wayland compositor + window manager in Rust, on Llimphi.

Rust 50Markdown 30TOML 211282 Definitions

⎇ main vendor/smithay/examples/buffer_test.rs Rust · 501 lines · 17.9 KB History Who touched what raw

chore: refresco desde el monorepo (pin tawasuyu.git v0.2.0) a91da3fea · Sergio ·

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}