atagen

monolith

public
Star0
Watch0
bbb50b4f27f74f8acfdd998ddc5b5a75cc2696f0
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/render-vk/src/renderer.rsRust1220 lines39.7 KB
1
use crate::Error;
2
use crate::device::Shared;
3
use crate::dmabuf::{self, DmabufCaps, RENDER_USAGE, SAMPLE_USAGE, VulkanAllocator};
4
use crate::format::{BYTES_PER_PIXEL, SUPPORTED};
5
use crate::frame::{Op, VulkanFrame};
6
use crate::image::{
7
    ImageInner, Staging, VulkanImage, VulkanMapping, VulkanTarget, VulkanTexture, color_range,
8
};
9
use crate::pipeline::Pipelines;
10
use crate::sync::{FenceInner, VkFence};
11
use ash::vk;
12
use gpu_allocator::MemoryLocation;
13
use smithay::backend::allocator::Fourcc;
14
use smithay::backend::allocator::dmabuf::{Dmabuf, WeakDmabuf};
15
use smithay::backend::allocator::format::FormatSet;
16
use smithay::backend::renderer::ImportMemWl;
17
use smithay::backend::renderer::sync::SyncPoint;
18
use smithay::backend::renderer::{
19
    Bind, ContextId, DebugFlags, ExportMem, ImportDma, ImportDmaWl, ImportMem, Offscreen, Renderer,
20
    RendererSuper, Texture, TextureFilter,
21
};
22
use smithay::reexports::wayland_server::Resource;
23
use smithay::reexports::wayland_server::backend::ObjectId;
24
use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer;
25
use smithay::utils::{Buffer, Physical, Rectangle, Size, Transform};
26
use smithay::wayland::compositor::SurfaceData;
27
use smithay::wayland::shm::{shm_format_to_fourcc, with_buffer_contents};
28
use std::cell::Cell;
29
use std::collections::HashMap;
30
use std::fmt;
31
use std::marker::PhantomData;
32
use std::sync::{Arc, Mutex};
33
use std::time::{Duration, Instant};
34
35
struct Pending {
36
    fence: Arc<FenceInner>,
37
    cmd: vk::CommandBuffer,
38
    _keep: Vec<Arc<ImageInner>>,
39
    _staging: Option<Staging>,
40
}
41
42
pub struct VulkanRenderer {
43
    pending: Vec<Pending>,
44
    dmabuf_textures: HashMap<WeakDmabuf, VulkanTexture>,
45
    dmabuf_targets: HashMap<WeakDmabuf, Arc<ImageInner>>,
46
    pipelines: Pipelines,
47
    pool: vk::CommandPool,
48
    /// Command buffers whose work finished, reset and reused by `begin`.
49
    spare_cmds: Vec<vk::CommandBuffer>,
50
    samplers: [vk::Sampler; 4],
51
    min_filter: TextureFilter,
52
    mag_filter: TextureFilter,
53
    debug: DebugFlags,
54
    context: ContextId<VulkanTexture>,
55
    caps: Arc<DmabufCaps>,
56
    shared: Arc<Shared>,
57
    submissions: Cell<u64>,
58
    /// Shm uploads staged off the main thread, by buffer.
59
    staged: HashMap<ObjectId, (WlBuffer, StagedShm)>,
60
    stall: Cell<StallPhase>,
61
}
62
63
/// Where in every 1024 submissions the driver stalls. A phase is trusted
64
/// once two stalls land on it.
65
#[derive(Clone, Copy, Default)]
66
struct StallPhase {
67
    phase: Option<u64>,
68
    seen: Option<u64>,
69
}
70
71
impl StallPhase {
72
    fn saw(self, at: u64) -> Self {
73
        if self.seen == Some(at) {
74
            if self.phase != Some(at) {
75
                tracing::debug!(phase = at, "queue submissions stall periodically");
76
            }
77
            Self {
78
                phase: Some(at),
79
                seen: Some(at),
80
            }
81
        } else {
82
            Self {
83
                seen: Some(at),
84
                ..self
85
            }
86
        }
87
    }
88
}
89
90
/// A submission this slow is a driver stall rather than work.
91
const STALL: Duration = Duration::from_micros(1500);
92
/// How many submissions early a stall is absorbed. Each empty one costs
93
/// tens of microseconds.
94
const ABSORB_AHEAD: u64 = 8;
95
96
impl fmt::Debug for VulkanRenderer {
97
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98
        f.debug_struct("VulkanRenderer")
99
            .field("device", &self.shared.device.name)
100
            .finish()
101
    }
102
}
103
104
impl VulkanRenderer {
105
    pub fn new() -> Result<Self, Error> {
106
        Self::with_node(None)
107
    }
108
109
    /// A renderer on the GPU behind DRM device number `node`, so it can
110
    /// render into buffers that GPU scans out.
111
    pub fn for_node(node: u64) -> Result<Self, Error> {
112
        Self::with_node(Some(node))
113
    }
114
115
    fn with_node(node: Option<u64>) -> Result<Self, Error> {
116
        let shared = Arc::new(Shared::new(node)?);
117
        let d = &shared.device.device;
118
        let pool = unsafe {
119
            d.create_command_pool(
120
                &vk::CommandPoolCreateInfo::default()
121
                    .queue_family_index(shared.device.queue_family)
122
                    .flags(
123
                        vk::CommandPoolCreateFlags::TRANSIENT
124
                            | vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER,
125
                    ),
126
                None,
127
            )?
128
        };
129
        let filter = |f: TextureFilter| match f {
130
            TextureFilter::Linear => vk::Filter::LINEAR,
131
            TextureFilter::Nearest => vk::Filter::NEAREST,
132
        };
133
        let mut samplers = [vk::Sampler::null(); 4];
134
        for (i, s) in samplers.iter_mut().enumerate() {
135
            let (min, mag) = (sampler_filter(i >> 1), sampler_filter(i & 1));
136
            *s = unsafe {
137
                d.create_sampler(
138
                    &vk::SamplerCreateInfo::default()
139
                        .min_filter(filter(min))
140
                        .mag_filter(filter(mag))
141
                        .address_mode_u(vk::SamplerAddressMode::CLAMP_TO_EDGE)
142
                        .address_mode_v(vk::SamplerAddressMode::CLAMP_TO_EDGE)
143
                        .address_mode_w(vk::SamplerAddressMode::CLAMP_TO_EDGE),
144
                    None,
145
                )?
146
            };
147
        }
148
        Ok(Self {
149
            pending: Vec::new(),
150
            dmabuf_textures: HashMap::new(),
151
            dmabuf_targets: HashMap::new(),
152
            pipelines: Pipelines::new(&shared)?,
153
            pool,
154
            spare_cmds: Vec::new(),
155
            submissions: Cell::new(0),
156
            staged: HashMap::new(),
157
            stall: Cell::new(StallPhase {
158
                // Measured on an RTX 3060, see research/nvidia.md.
159
                phase: shared.device.name.contains("NVIDIA").then_some(1015),
160
                seen: None,
161
            }),
162
            samplers,
163
            min_filter: TextureFilter::Linear,
164
            mag_filter: TextureFilter::Linear,
165
            debug: DebugFlags::empty(),
166
            context: ContextId::new(),
167
            caps: Arc::new(DmabufCaps::query(&shared)),
168
            shared,
169
        })
170
    }
171
172
    /// Name of the physical device in use.
173
    pub fn device_name(&self) -> &str {
174
        &self.shared.device.name
175
    }
176
177
    /// Whether dmabufs can be imported, rendered into and allocated.
178
    pub fn supports_dmabuf(&self) -> bool {
179
        self.caps.render.iter().next().is_some()
180
    }
181
182
    /// Formats and modifiers this renderer can render into as dmabufs.
183
    pub fn render_formats(&self) -> FormatSet {
184
        self.caps.render.clone()
185
    }
186
187
    /// An allocator for render-capable dmabufs on this device.
188
    pub fn allocator(&self) -> VulkanAllocator {
189
        VulkanAllocator {
190
            shared: self.shared.clone(),
191
            caps: self.caps.clone(),
192
        }
193
    }
194
195
    pub(crate) fn shared(&self) -> &Arc<Shared> {
196
        &self.shared
197
    }
198
199
    pub(crate) fn context_id_ref(&self) -> &ContextId<VulkanTexture> {
200
        &self.context
201
    }
202
203
    fn sampler(&self) -> vk::Sampler {
204
        let idx = |f: TextureFilter| usize::from(f == TextureFilter::Nearest);
205
        self.samplers[(idx(self.min_filter) << 1) | idx(self.mag_filter)]
206
    }
207
208
    fn reap(&mut self) {
209
        let spare = &mut self.spare_cmds;
210
        self.pending.retain(|p| {
211
            if !p.fence.signaled() {
212
                return true;
213
            }
214
            spare.push(p.cmd);
215
            false
216
        });
217
        self.dmabuf_textures.retain(|k, _| !k.is_gone());
218
        self.dmabuf_targets.retain(|k, _| !k.is_gone());
219
    }
220
221
    fn begin(&mut self) -> Result<vk::CommandBuffer, Error> {
222
        let d = &self.shared.device.device;
223
        let cmd = match self.spare_cmds.pop() {
224
            Some(cmd) => {
225
                unsafe { d.reset_command_buffer(cmd, vk::CommandBufferResetFlags::empty())? };
226
                cmd
227
            }
228
            None => unsafe {
229
                d.allocate_command_buffers(
230
                    &vk::CommandBufferAllocateInfo::default()
231
                        .command_pool(self.pool)
232
                        .level(vk::CommandBufferLevel::PRIMARY)
233
                        .command_buffer_count(1),
234
                )?[0]
235
            },
236
        };
237
        unsafe {
238
            d.begin_command_buffer(
239
                cmd,
240
                &vk::CommandBufferBeginInfo::default()
241
                    .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
242
            )?
243
        };
244
        Ok(cmd)
245
    }
246
247
    #[profiling::function]
248
    fn end_and_submit(&self, cmd: vk::CommandBuffer) -> Result<Arc<FenceInner>, Error> {
249
        let d = &self.shared.device.device;
250
        {
251
            profiling::scope!("end_command_buffer");
252
            unsafe { d.end_command_buffer(cmd)? };
253
        }
254
        let fence = {
255
            profiling::scope!("create_fence");
256
            FenceInner::new(&self.shared)?
257
        };
258
        let cmds = [vk::CommandBufferSubmitInfo::default().command_buffer(cmd)];
259
        let submit = vk::SubmitInfo2::default().command_buffer_infos(&cmds);
260
        let queue = self
261
            .shared
262
            .device
263
            .queue
264
            .lock()
265
            .unwrap_or_else(|e| e.into_inner());
266
        let began = Instant::now();
267
        {
268
            profiling::scope!("queue_submit");
269
            unsafe { d.queue_submit2(*queue, &[submit], fence.fence())? };
270
        }
271
        let n = self.submissions.get();
272
        self.submissions.set(n + 1);
273
        if began.elapsed() > STALL {
274
            self.stall.set(self.stall.get().saw(n % 1024));
275
        }
276
        Ok(fence)
277
    }
278
279
    /// Spends an upcoming stalling submission on empty command buffers, so
280
    /// the stall lands now, in idle time, instead of in a frame. Call it
281
    /// right after a flip. NVIDIA stalls for milliseconds on every 1024th
282
    /// submission, see research/nvidia.md.
283
    pub fn absorb_submit_stall(&mut self) -> Result<(), Error> {
284
        self.reap();
285
        let Some(phase) = self.stall.get().phase else {
286
            return Ok(());
287
        };
288
        let ahead = (phase + 1024 - self.submissions.get() % 1024) % 1024;
289
        if ahead >= ABSORB_AHEAD {
290
            return Ok(());
291
        }
292
        for _ in 0..=ahead {
293
            let cmd = self.begin()?;
294
            let fence = self.end_and_submit(cmd)?;
295
            self.pending.push(Pending {
296
                fence,
297
                cmd,
298
                _keep: Vec::new(),
299
                _staging: None,
300
            });
301
        }
302
        Ok(())
303
    }
304
305
    /// Records, submits and waits for a short command buffer.
306
    fn one_shot(
307
        &mut self,
308
        record: impl FnOnce(&ash::Device, vk::CommandBuffer, u32),
309
    ) -> Result<(), Error> {
310
        let cmd = self.begin()?;
311
        record(
312
            &self.shared.device.device,
313
            cmd,
314
            self.shared.device.queue_family,
315
        );
316
        let fence = self.end_and_submit(cmd)?;
317
        let res = fence.wait();
318
        self.spare_cmds.push(cmd);
319
        Ok(res?)
320
    }
321
322
    #[profiling::function]
323
    pub(crate) fn submit(
324
        &mut self,
325
        target: &mut VulkanTarget<'_>,
326
        ops: Vec<Op>,
327
    ) -> Result<SyncPoint, Error> {
328
        self.reap();
329
        let image = target.0.clone();
330
        let mut sampled: Vec<Arc<ImageInner>> = Vec::new();
331
        for op in &ops {
332
            if let Op::Draw {
333
                texture: Some(t), ..
334
            } = op
335
                && !sampled.iter().any(|s| Arc::ptr_eq(s, &t.0))
336
            {
337
                sampled.push(t.0.clone());
338
            }
339
        }
340
        let mut pipelines = Vec::with_capacity(ops.len());
341
        for op in &ops {
342
            pipelines.push(match op {
343
                Op::Draw { kind, .. } => Some(self.pipelines.get(image.format, *kind)?),
344
                Op::Clear { .. } => None,
345
            });
346
        }
347
        let cmd = self.begin()?;
348
        let d = &self.shared.device.device;
349
        let family = self.shared.device.queue_family;
350
        let push = &self.shared.device.push_descriptor;
351
        let (w, h) = (image.width, image.height);
352
        let sampler = self.sampler();
353
        unsafe {
354
            for t in &sampled {
355
                acquire(d, cmd, family, t, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);
356
            }
357
            acquire(
358
                d,
359
                cmd,
360
                family,
361
                &image,
362
                vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
363
            );
364
            let attachments = [vk::RenderingAttachmentInfo::default()
365
                .image_view(image.view)
366
                .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
367
                .load_op(vk::AttachmentLoadOp::LOAD)
368
                .store_op(vk::AttachmentStoreOp::STORE)];
369
            let area = vk::Rect2D {
370
                offset: vk::Offset2D::default(),
371
                extent: vk::Extent2D {
372
                    width: w,
373
                    height: h,
374
                },
375
            };
376
            d.cmd_begin_rendering(
377
                cmd,
378
                &vk::RenderingInfo::default()
379
                    .render_area(area)
380
                    .layer_count(1)
381
                    .color_attachments(&attachments),
382
            );
383
            d.cmd_set_viewport(
384
                cmd,
385
                0,
386
                &[vk::Viewport {
387
                    x: 0.0,
388
                    y: 0.0,
389
                    width: w as f32,
390
                    height: h as f32,
391
                    min_depth: 0.0,
392
                    max_depth: 1.0,
393
                }],
394
            );
395
            let mut bound = vk::Pipeline::null();
396
            for (op, pipeline) in ops.iter().zip(pipelines) {
397
                match op {
398
                    Op::Clear { color, rects } => {
399
                        let clear = [vk::ClearAttachment {
400
                            aspect_mask: vk::ImageAspectFlags::COLOR,
401
                            color_attachment: 0,
402
                            clear_value: vk::ClearValue {
403
                                color: vk::ClearColorValue { float32: *color },
404
                            },
405
                        }];
406
                        let rects: Vec<_> = rects
407
                            .iter()
408
                            .map(|&rect| vk::ClearRect {
409
                                rect,
410
                                base_array_layer: 0,
411
                                layer_count: 1,
412
                            })
413
                            .collect();
414
                        d.cmd_clear_attachments(cmd, &clear, &rects);
415
                    }
416
                    Op::Draw {
417
                        texture,
418
                        push: constants,
419
                        scissors,
420
                        ..
421
                    } => {
422
                        let pipeline = pipeline.unwrap_or_default();
423
                        if pipeline != bound {
424
                            d.cmd_bind_pipeline(cmd, vk::PipelineBindPoint::GRAPHICS, pipeline);
425
                            bound = pipeline;
426
                        }
427
                        if let Some(t) = texture {
428
                            let info = [vk::DescriptorImageInfo::default()
429
                                .image_view(t.0.view)
430
                                .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)
431
                                .sampler(sampler)];
432
                            let write = vk::WriteDescriptorSet::default()
433
                                .dst_binding(0)
434
                                .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
435
                                .image_info(&info);
436
                            push.cmd_push_descriptor_set(
437
                                cmd,
438
                                vk::PipelineBindPoint::GRAPHICS,
439
                                self.pipelines.layout,
440
                                0,
441
                                &[write],
442
                            );
443
                        }
444
                        d.cmd_push_constants(
445
                            cmd,
446
                            self.pipelines.layout,
447
                            vk::ShaderStageFlags::VERTEX | vk::ShaderStageFlags::FRAGMENT,
448
                            0,
449
                            constants.bytes(),
450
                        );
451
                        for s in scissors {
452
                            d.cmd_set_scissor(cmd, 0, &[*s]);
453
                            d.cmd_draw(cmd, 4, 1, 0, 0);
454
                        }
455
                    }
456
                }
457
            }
458
            d.cmd_end_rendering(cmd);
459
            release(
460
                d,
461
                cmd,
462
                family,
463
                &image,
464
                vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
465
            );
466
            for t in &sampled {
467
                release(d, cmd, family, t, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);
468
            }
469
        }
470
        let fence = self.end_and_submit(cmd)?;
471
        sampled.push(image);
472
        self.pending.push(Pending {
473
            fence: fence.clone(),
474
            cmd,
475
            _keep: sampled,
476
            _staging: None,
477
        });
478
        Ok(SyncPoint::from(VkFence(fence)))
479
    }
480
481
    /// Copies `region` of a full-image `data` into `image`, rows top-down or
482
    /// flipped.
483
    fn upload(
484
        &mut self,
485
        image: &Arc<ImageInner>,
486
        data: &[u8],
487
        region: Rectangle<i32, Buffer>,
488
        flipped: bool,
489
    ) -> Result<(), Error> {
490
        let width = image.width as usize;
491
        let rows = image.height as usize;
492
        let needed = width * rows * BYTES_PER_PIXEL;
493
        if data.len() < needed {
494
            return Err(Error::BufferTooSmall {
495
                needed,
496
                got: data.len(),
497
            });
498
        }
499
        let stride = width * BYTES_PER_PIXEL;
500
        self.upload_with(image, region, |y| {
501
            let from = if flipped { rows - 1 - y } else { y };
502
            let start = from * stride + region.loc.x as usize * BYTES_PER_PIXEL;
503
            &data[start..start + region.size.w as usize * BYTES_PER_PIXEL]
504
        })
505
    }
506
507
    /// Uploads `region` of `image`, taking each image row `y` in the region
508
    /// from `row(y)`. The CPU does not wait: the copy is ordered before later
509
    /// frames by its barrier, and its staging memory lives until its fence.
510
    #[profiling::function]
511
    fn upload_with<'a>(
512
        &mut self,
513
        image: &Arc<ImageInner>,
514
        region: Rectangle<i32, Buffer>,
515
        row: impl Fn(usize) -> &'a [u8],
516
    ) -> Result<(), Error> {
517
        if image.external {
518
            return Err(Error::ForeignTexture);
519
        }
520
        self.reap();
521
        if region.size.w <= 0 || region.size.h <= 0 {
522
            return Ok(());
523
        }
524
        let staging = stage_rows(&self.shared, region, row)?;
525
        self.copy_staged(image, region, staging)
526
    }
527
528
    /// Copies staged pixels into `region` of `image` on the GPU.
529
    fn copy_staged(
530
        &mut self,
531
        image: &Arc<ImageInner>,
532
        region: Rectangle<i32, Buffer>,
533
        staging: Staging,
534
    ) -> Result<(), Error> {
535
        let (w, h) = (region.size.w as usize, region.size.h as usize);
536
        let copy = vk::BufferImageCopy::default()
537
            .buffer_row_length(w as u32)
538
            .image_subresource(
539
                vk::ImageSubresourceLayers::default()
540
                    .aspect_mask(vk::ImageAspectFlags::COLOR)
541
                    .layer_count(1),
542
            )
543
            .image_offset(vk::Offset3D {
544
                x: region.loc.x,
545
                y: region.loc.y,
546
                z: 0,
547
            })
548
            .image_extent(vk::Extent3D {
549
                width: w as u32,
550
                height: h as u32,
551
                depth: 1,
552
            });
553
        let cmd = self.begin()?;
554
        let d = &self.shared.device.device;
555
        let family = self.shared.device.queue_family;
556
        unsafe {
557
            acquire(d, cmd, family, image, vk::ImageLayout::TRANSFER_DST_OPTIMAL);
558
            d.cmd_copy_buffer_to_image(
559
                cmd,
560
                staging.buffer,
561
                image.image,
562
                vk::ImageLayout::TRANSFER_DST_OPTIMAL,
563
                &[copy],
564
            );
565
            acquire(
566
                d,
567
                cmd,
568
                family,
569
                image,
570
                vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
571
            );
572
        }
573
        let fence = self.end_and_submit(cmd)?;
574
        self.pending.push(Pending {
575
            fence,
576
            cmd,
577
            _keep: vec![image.clone()],
578
            _staging: Some(staging),
579
        });
580
        Ok(())
581
    }
582
583
    fn read_back(
584
        &mut self,
585
        image: &Arc<ImageInner>,
586
        region: Rectangle<i32, Buffer>,
587
        fourcc: Fourcc,
588
    ) -> Result<VulkanMapping, Error> {
589
        let region = region
590
            .intersection(Rectangle::from_size(
591
                (image.width as i32, image.height as i32).into(),
592
            ))
593
            .unwrap_or_default();
594
        let (w, h) = (region.size.w.max(0) as u32, region.size.h.max(0) as u32);
595
        let swap = needs_swizzle(image.fourcc, fourcc)?;
596
        let len = (w as usize * h as usize * BYTES_PER_PIXEL).max(BYTES_PER_PIXEL);
597
        let staging = Staging::new(
598
            &self.shared,
599
            len,
600
            vk::BufferUsageFlags::TRANSFER_DST,
601
            MemoryLocation::GpuToCpu,
602
        )?;
603
        if w > 0 && h > 0 {
604
            let copy = vk::BufferImageCopy::default()
605
                .image_subresource(
606
                    vk::ImageSubresourceLayers::default()
607
                        .aspect_mask(vk::ImageAspectFlags::COLOR)
608
                        .layer_count(1),
609
                )
610
                .image_offset(vk::Offset3D {
611
                    x: region.loc.x,
612
                    y: region.loc.y,
613
                    z: 0,
614
                })
615
                .image_extent(vk::Extent3D {
616
                    width: w,
617
                    height: h,
618
                    depth: 1,
619
                });
620
            let buffer = staging.buffer;
621
            let source = image.clone();
622
            self.one_shot(|d, cmd, family| unsafe {
623
                acquire(
624
                    d,
625
                    cmd,
626
                    family,
627
                    &source,
628
                    vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
629
                );
630
                d.cmd_copy_image_to_buffer(
631
                    cmd,
632
                    source.image,
633
                    vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
634
                    buffer,
635
                    &[copy],
636
                );
637
                release(
638
                    d,
639
                    cmd,
640
                    family,
641
                    &source,
642
                    vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
643
                );
644
            })?;
645
        }
646
        let mut data = staging.bytes()[..w as usize * h as usize * BYTES_PER_PIXEL].to_vec();
647
        if swap {
648
            for px in data.as_chunks_mut::<BYTES_PER_PIXEL>().0 {
649
                px.swap(0, 2);
650
            }
651
        }
652
        Ok(VulkanMapping {
653
            data,
654
            width: w,
655
            height: h,
656
            fourcc,
657
        })
658
    }
659
660
    fn own(&self, image: &Arc<ImageInner>) -> Result<(), Error> {
661
        if Arc::ptr_eq(&image.shared, &self.shared) {
662
            Ok(())
663
        } else {
664
            Err(Error::ForeignTexture)
665
        }
666
    }
667
}
668
669
impl Drop for VulkanRenderer {
670
    fn drop(&mut self) {
671
        let d = &self.shared.device.device;
672
        unsafe {
673
            let _ = d.device_wait_idle();
674
            for p in self.pending.drain(..) {
675
                d.free_command_buffers(self.pool, &[p.cmd]);
676
            }
677
            for s in self.samplers {
678
                d.destroy_sampler(s, None);
679
            }
680
            d.destroy_command_pool(self.pool, None);
681
        }
682
    }
683
}
684
685
fn sampler_filter(bit: usize) -> TextureFilter {
686
    if bit == 0 {
687
        TextureFilter::Linear
688
    } else {
689
        TextureFilter::Nearest
690
    }
691
}
692
693
/// A full barrier between all earlier and later work on the image, optionally
694
/// transferring ownership between queue families. Coarse, but correct for
695
/// every transition the renderer makes.
696
unsafe fn barrier(
697
    d: &ash::Device,
698
    cmd: vk::CommandBuffer,
699
    image: vk::Image,
700
    old: vk::ImageLayout,
701
    new: vk::ImageLayout,
702
    src_family: u32,
703
    dst_family: u32,
704
) {
705
    let b = [vk::ImageMemoryBarrier2::default()
706
        .src_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS)
707
        .src_access_mask(vk::AccessFlags2::MEMORY_WRITE)
708
        .dst_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS)
709
        .dst_access_mask(vk::AccessFlags2::MEMORY_READ | vk::AccessFlags2::MEMORY_WRITE)
710
        .old_layout(old)
711
        .new_layout(new)
712
        .src_queue_family_index(src_family)
713
        .dst_queue_family_index(dst_family)
714
        .image(image)
715
        .subresource_range(color_range())];
716
    unsafe {
717
        d.cmd_pipeline_barrier2(
718
            cmd,
719
            &vk::DependencyInfo::default().image_memory_barriers(&b),
720
        )
721
    };
722
}
723
724
/// Moves an image into `layout` for use on this queue. Dmabuf images come
725
/// from the foreign queue family in the general layout, others continue from
726
/// their tracked layout.
727
unsafe fn acquire(
728
    d: &ash::Device,
729
    cmd: vk::CommandBuffer,
730
    family: u32,
731
    image: &ImageInner,
732
    layout: vk::ImageLayout,
733
) {
734
    if image.external {
735
        unsafe {
736
            barrier(
737
                d,
738
                cmd,
739
                image.image,
740
                vk::ImageLayout::GENERAL,
741
                layout,
742
                vk::QUEUE_FAMILY_FOREIGN_EXT,
743
                family,
744
            )
745
        };
746
    } else {
747
        let old = image.layout();
748
        if old != layout {
749
            unsafe {
750
                barrier(
751
                    d,
752
                    cmd,
753
                    image.image,
754
                    old,
755
                    layout,
756
                    vk::QUEUE_FAMILY_IGNORED,
757
                    vk::QUEUE_FAMILY_IGNORED,
758
                )
759
            };
760
            image.set_layout(layout);
761
        }
762
    }
763
}
764
765
/// Hands a dmabuf image back to the foreign queue family after use.
766
unsafe fn release(
767
    d: &ash::Device,
768
    cmd: vk::CommandBuffer,
769
    family: u32,
770
    image: &ImageInner,
771
    layout: vk::ImageLayout,
772
) {
773
    if image.external {
774
        unsafe {
775
            barrier(
776
                d,
777
                cmd,
778
                image.image,
779
                layout,
780
                vk::ImageLayout::GENERAL,
781
                family,
782
                vk::QUEUE_FAMILY_FOREIGN_EXT,
783
            )
784
        };
785
    }
786
}
787
788
/// Whether reading `from` as `to` needs red and blue swapped.
789
fn needs_swizzle(from: Fourcc, to: Fourcc) -> Result<bool, Error> {
790
    let bgr = |f: Fourcc| match f {
791
        Fourcc::Argb8888 | Fourcc::Xrgb8888 => Ok(true),
792
        Fourcc::Abgr8888 | Fourcc::Xbgr8888 => Ok(false),
793
        f => Err(Error::UnsupportedFormat(f)),
794
    };
795
    Ok(bgr(from)? != bgr(to)?)
796
}
797
798
impl RendererSuper for VulkanRenderer {
799
    type Error = Error;
800
    type TextureId = VulkanTexture;
801
    type Framebuffer<'buffer> = VulkanTarget<'buffer>;
802
    type Frame<'frame, 'buffer>
803
        = VulkanFrame<'frame, 'buffer>
804
    where
805
        'buffer: 'frame,
806
        Self: 'frame;
807
}
808
809
impl Renderer for VulkanRenderer {
810
    fn context_id(&self) -> ContextId<VulkanTexture> {
811
        self.context.clone()
812
    }
813
814
    fn downscale_filter(&mut self, filter: TextureFilter) -> Result<(), Error> {
815
        self.min_filter = filter;
816
        Ok(())
817
    }
818
819
    fn upscale_filter(&mut self, filter: TextureFilter) -> Result<(), Error> {
820
        self.mag_filter = filter;
821
        Ok(())
822
    }
823
824
    fn set_debug_flags(&mut self, flags: DebugFlags) {
825
        self.debug = flags;
826
    }
827
828
    fn debug_flags(&self) -> DebugFlags {
829
        self.debug
830
    }
831
832
    fn render<'frame, 'buffer>(
833
        &'frame mut self,
834
        framebuffer: &'frame mut VulkanTarget<'buffer>,
835
        output_size: Size<i32, Physical>,
836
        dst_transform: Transform,
837
    ) -> Result<VulkanFrame<'frame, 'buffer>, Error>
838
    where
839
        'buffer: 'frame,
840
    {
841
        self.own(&framebuffer.0)?;
842
        self.reap();
843
        Ok(VulkanFrame::new(
844
            self,
845
            framebuffer,
846
            output_size,
847
            dst_transform,
848
        ))
849
    }
850
851
    fn wait(&mut self, sync: &SyncPoint) -> Result<(), Error> {
852
        sync.wait().map_err(|_| Error::Interrupted)
853
    }
854
855
    fn cleanup_texture_cache(&mut self) -> Result<(), Error> {
856
        self.reap();
857
        Ok(())
858
    }
859
860
    fn invalidate_caches(&mut self) -> Result<(), Error> {
861
        self.reap();
862
        self.dmabuf_textures.clear();
863
        self.dmabuf_targets.clear();
864
        Ok(())
865
    }
866
}
867
868
impl Bind<VulkanImage> for VulkanRenderer {
869
    fn bind<'a>(&mut self, target: &'a mut VulkanImage) -> Result<VulkanTarget<'a>, Error> {
870
        self.own(&target.0)?;
871
        Ok(VulkanTarget(target.0.clone(), PhantomData))
872
    }
873
}
874
875
impl Bind<Dmabuf> for VulkanRenderer {
876
    #[profiling::function]
877
    fn bind<'a>(&mut self, target: &'a mut Dmabuf) -> Result<VulkanTarget<'a>, Error> {
878
        let key = target.weak();
879
        let image = match self.dmabuf_targets.get(&key) {
880
            Some(image) => image.clone(),
881
            None => {
882
                let image = dmabuf::import(&self.shared, &self.caps, target, RENDER_USAGE)?;
883
                self.dmabuf_targets.insert(key, image.clone());
884
                image
885
            }
886
        };
887
        Ok(VulkanTarget(image, PhantomData))
888
    }
889
890
    fn supported_formats(&self) -> Option<FormatSet> {
891
        Some(self.caps.render.clone())
892
    }
893
}
894
895
impl Offscreen<VulkanImage> for VulkanRenderer {
896
    fn create_buffer(
897
        &mut self,
898
        format: Fourcc,
899
        size: Size<i32, Buffer>,
900
    ) -> Result<VulkanImage, Error> {
901
        if size.w <= 0 || size.h <= 0 {
902
            return Err(Error::InvalidSize(size.w, size.h));
903
        }
904
        let usage = vk::ImageUsageFlags::COLOR_ATTACHMENT
905
            | vk::ImageUsageFlags::SAMPLED
906
            | vk::ImageUsageFlags::TRANSFER_SRC
907
            | vk::ImageUsageFlags::TRANSFER_DST;
908
        Ok(VulkanImage(ImageInner::create(
909
            &self.shared,
910
            format,
911
            size.w as u32,
912
            size.h as u32,
913
            usage,
914
        )?))
915
    }
916
}
917
918
impl ImportMem for VulkanRenderer {
919
    fn import_memory(
920
        &mut self,
921
        data: &[u8],
922
        format: Fourcc,
923
        size: Size<i32, Buffer>,
924
        flipped: bool,
925
    ) -> Result<VulkanTexture, Error> {
926
        if size.w <= 0 || size.h <= 0 {
927
            return Err(Error::InvalidSize(size.w, size.h));
928
        }
929
        let usage = vk::ImageUsageFlags::SAMPLED
930
            | vk::ImageUsageFlags::TRANSFER_DST
931
            | vk::ImageUsageFlags::TRANSFER_SRC;
932
        let image = ImageInner::create(&self.shared, format, size.w as u32, size.h as u32, usage)?;
933
        self.upload(&image, data, Rectangle::from_size(size), flipped)?;
934
        Ok(VulkanTexture(image))
935
    }
936
937
    fn update_memory(
938
        &mut self,
939
        texture: &VulkanTexture,
940
        data: &[u8],
941
        region: Rectangle<i32, Buffer>,
942
    ) -> Result<(), Error> {
943
        self.own(&texture.0)?;
944
        let Some(region) = region.intersection(Rectangle::from_size(texture.size())) else {
945
            return Ok(());
946
        };
947
        self.upload(&texture.0, data, region, false)
948
    }
949
950
    fn mem_formats(&self) -> Box<dyn Iterator<Item = Fourcc>> {
951
        Box::new(SUPPORTED.into_iter())
952
    }
953
}
954
955
impl ImportDma for VulkanRenderer {
956
    fn dmabuf_formats(&self) -> FormatSet {
957
        self.caps.sample.clone()
958
    }
959
960
    #[profiling::function]
961
    fn import_dmabuf(
962
        &mut self,
963
        buffer: &Dmabuf,
964
        _damage: Option<&[Rectangle<i32, Buffer>]>,
965
    ) -> Result<VulkanTexture, Error> {
966
        let key = buffer.weak();
967
        if let Some(t) = self.dmabuf_textures.get(&key) {
968
            return Ok(t.clone());
969
        }
970
        let texture = VulkanTexture(dmabuf::import(
971
            &self.shared,
972
            &self.caps,
973
            buffer,
974
            SAMPLE_USAGE,
975
        )?);
976
        self.dmabuf_textures.insert(key, texture.clone());
977
        Ok(texture)
978
    }
979
}
980
981
impl ImportDmaWl for VulkanRenderer {}
982
983
/// Imports dmabufs on another thread. The textures it makes belong to the
984
/// renderer it came from, which takes them back with `adopt_dmabuf`.
985
#[derive(Clone)]
986
pub struct DmabufImporter {
987
    shared: Arc<Shared>,
988
    caps: Arc<DmabufCaps>,
989
}
990
991
impl DmabufImporter {
992
    pub fn import(&self, buffer: &Dmabuf) -> Result<VulkanTexture, Error> {
993
        dmabuf::import(&self.shared, &self.caps, buffer, SAMPLE_USAGE).map(VulkanTexture)
994
    }
995
}
996
997
impl VulkanRenderer {
998
    pub fn dmabuf_importer(&self) -> DmabufImporter {
999
        DmabufImporter {
1000
            shared: self.shared.clone(),
1001
            caps: self.caps.clone(),
1002
        }
1003
    }
1004
1005
    pub fn has_dmabuf(&self, buffer: &Dmabuf) -> bool {
1006
        self.dmabuf_textures.contains_key(&buffer.weak())
1007
    }
1008
1009
    pub fn adopt_dmabuf(&mut self, buffer: &Dmabuf, texture: VulkanTexture) -> Result<(), Error> {
1010
        self.own(&texture.0)?;
1011
        self.dmabuf_textures.insert(buffer.weak(), texture);
1012
        Ok(())
1013
    }
1014
}
1015
1016
impl ExportMem for VulkanRenderer {
1017
    type TextureMapping = VulkanMapping;
1018
1019
    fn copy_framebuffer(
1020
        &mut self,
1021
        target: &VulkanTarget<'_>,
1022
        region: Rectangle<i32, Buffer>,
1023
        format: Fourcc,
1024
    ) -> Result<VulkanMapping, Error> {
1025
        self.own(&target.0)?;
1026
        let image = target.0.clone();
1027
        self.read_back(&image, region, format)
1028
    }
1029
1030
    fn copy_texture(
1031
        &mut self,
1032
        texture: &VulkanTexture,
1033
        region: Rectangle<i32, Buffer>,
1034
        format: Fourcc,
1035
    ) -> Result<VulkanMapping, Error> {
1036
        self.own(&texture.0)?;
1037
        self.read_back(&texture.0, region, format)
1038
    }
1039
1040
    fn can_read_texture(&mut self, texture: &VulkanTexture) -> Result<bool, Error> {
1041
        Ok(texture.0.external || texture.0.layout() != vk::ImageLayout::UNDEFINED)
1042
    }
1043
1044
    fn map_texture<'a>(&mut self, mapping: &'a VulkanMapping) -> Result<&'a [u8], Error> {
1045
        Ok(&mapping.data)
1046
    }
1047
}
1048
1049
impl ImportMemWl for VulkanRenderer {
1050
    /// Keeps one texture per surface and uploads only the damaged rows into
1051
    /// it, straight from the client's shm pool.
1052
    fn import_shm_buffer(
1053
        &mut self,
1054
        buffer: &WlBuffer,
1055
        surface: Option<&SurfaceData>,
1056
        damage: &[Rectangle<i32, Buffer>],
1057
    ) -> Result<VulkanTexture, Error> {
1058
        self.staged.retain(|_, (b, _)| b.is_alive());
1059
        let staged = self.staged.remove(&buffer.id()).map(|(_, s)| s);
1060
        let cache = surface.map(|s| {
1061
            s.data_map
1062
                .insert_if_missing_threadsafe(|| ShmCache(Mutex::new(None)));
1063
            s.data_map.get::<ShmCache>().expect("just inserted")
1064
        });
1065
        with_buffer_contents(buffer, |ptr, len, data| {
1066
            let fourcc = shm_format_to_fourcc(data.format).ok_or(Error::ShmAccess)?;
1067
            let (w, h, stride, offset) = (
1068
                data.width as usize,
1069
                data.height as usize,
1070
                data.stride as usize,
1071
                data.offset as usize,
1072
            );
1073
            let line = w * BYTES_PER_PIXEL;
1074
            if w == 0 || h == 0 || stride < line || offset + stride * (h - 1) + line > len {
1075
                return Err(Error::ShmAccess);
1076
            }
1077
            let size = Size::from((data.width, data.height));
1078
            let full = Rectangle::from_size(size);
1079
            let reused = cache
1080
                .and_then(|c| c.0.lock().unwrap_or_else(|e| e.into_inner()).clone())
1081
                .filter(|t| {
1082
                    t.0.fourcc == fourcc
1083
                        && t.size() == size
1084
                        && Arc::ptr_eq(&t.0.shared, &self.shared)
1085
                });
1086
            let (texture, region) = match reused {
1087
                Some(t) => {
1088
                    let dirty = damage
1089
                        .iter()
1090
                        .filter_map(|d| d.intersection(full))
1091
                        .reduce(|a, b| a.merge(b));
1092
                    (t, if damage.is_empty() { Some(full) } else { dirty })
1093
                }
1094
                None => {
1095
                    let usage = vk::ImageUsageFlags::SAMPLED
1096
                        | vk::ImageUsageFlags::TRANSFER_DST
1097
                        | vk::ImageUsageFlags::TRANSFER_SRC;
1098
                    let image =
1099
                        ImageInner::create(&self.shared, fourcc, w as u32, h as u32, usage)?;
1100
                    (VulkanTexture(image), Some(full))
1101
                }
1102
            };
1103
            let staged = staged.filter(|s| {
1104
                s.fourcc == fourcc
1105
                    && s.size == size
1106
                    && region.is_some_and(|r| s.region.contains_rect(r))
1107
            });
1108
            if let Some(s) = staged {
1109
                tracing::trace!(region = ?s.region, "uploading a staged shm copy");
1110
                self.copy_staged(&texture.0, s.region, s.staging)?;
1111
            } else if let Some(region) = region {
1112
                let x = region.loc.x as usize * BYTES_PER_PIXEL;
1113
                let rw = region.size.w as usize * BYTES_PER_PIXEL;
1114
                self.upload_with(&texture.0, region, |y| unsafe {
1115
                    std::slice::from_raw_parts(ptr.add(offset + y * stride + x), rw)
1116
                })?;
1117
            }
1118
            if let Some(c) = cache {
1119
                *c.0.lock().unwrap_or_else(|e| e.into_inner()) = Some(texture.clone());
1120
            }
1121
            Ok(texture)
1122
        })
1123
        .map_err(|_| Error::ShmAccess)?
1124
    }
1125
}
1126
1127
/// The texture last imported for a surface's shm buffers.
1128
struct ShmCache(Mutex<Option<VulkanTexture>>);
1129
1130
/// Copies `region` of a full-image `row` source into new staging memory.
1131
fn stage_rows<'a>(
1132
    shared: &Arc<Shared>,
1133
    region: Rectangle<i32, Buffer>,
1134
    row: impl Fn(usize) -> &'a [u8],
1135
) -> Result<Staging, Error> {
1136
    let (w, h) = (region.size.w as usize, region.size.h as usize);
1137
    let line = w * BYTES_PER_PIXEL;
1138
    let mut staging = Staging::new(
1139
        shared,
1140
        line * h,
1141
        vk::BufferUsageFlags::TRANSFER_SRC,
1142
        MemoryLocation::CpuToGpu,
1143
    )?;
1144
    let dst = staging.bytes_mut();
1145
    for i in 0..h {
1146
        dst[i * line..(i + 1) * line].copy_from_slice(row(region.loc.y as usize + i));
1147
    }
1148
    Ok(staging)
1149
}
1150
1151
/// Shm pixels copied off the main thread, waiting for the import of the
1152
/// commit that shows them.
1153
pub struct StagedShm {
1154
    staging: Staging,
1155
    region: Rectangle<i32, Buffer>,
1156
    fourcc: Fourcc,
1157
    size: Size<i32, Buffer>,
1158
}
1159
1160
/// Copies shm buffers into staging memory on another thread. The renderer
1161
/// it came from takes the result back with `adopt_staged`.
1162
#[derive(Clone)]
1163
pub struct ShmStager {
1164
    shared: Arc<Shared>,
1165
}
1166
1167
impl ShmStager {
1168
    /// Stages `region` of `buffer`, all of it when the region is `None`.
1169
    pub fn stage(
1170
        &self,
1171
        buffer: &WlBuffer,
1172
        region: Option<Rectangle<i32, Buffer>>,
1173
    ) -> Result<StagedShm, Error> {
1174
        with_buffer_contents(buffer, |ptr, len, data| {
1175
            let fourcc = shm_format_to_fourcc(data.format).ok_or(Error::ShmAccess)?;
1176
            let (w, h, stride, offset) = (
1177
                data.width as usize,
1178
                data.height as usize,
1179
                data.stride as usize,
1180
                data.offset as usize,
1181
            );
1182
            let line = w * BYTES_PER_PIXEL;
1183
            if w == 0 || h == 0 || stride < line || offset + stride * (h - 1) + line > len {
1184
                return Err(Error::ShmAccess);
1185
            }
1186
            let size = Size::from((data.width, data.height));
1187
            let full = Rectangle::from_size(size);
1188
            let region = region.and_then(|r| r.intersection(full)).unwrap_or(full);
1189
            let x = region.loc.x as usize * BYTES_PER_PIXEL;
1190
            let rw = region.size.w as usize * BYTES_PER_PIXEL;
1191
            // SAFETY: the bounds check above keeps every row inside the
1192
            // `len` bytes smithay mapped at `ptr`.
1193
            let staging = stage_rows(&self.shared, region, |y| unsafe {
1194
                std::slice::from_raw_parts(ptr.add(offset + y * stride + x), rw)
1195
            })?;
1196
            Ok(StagedShm {
1197
                staging,
1198
                region,
1199
                fourcc,
1200
                size,
1201
            })
1202
        })
1203
        .map_err(|_| Error::ShmAccess)?
1204
    }
1205
}
1206
1207
impl VulkanRenderer {
1208
    pub fn shm_stager(&self) -> ShmStager {
1209
        ShmStager {
1210
            shared: self.shared.clone(),
1211
        }
1212
    }
1213
1214
    /// Hands back pixels a stager copied, used by the next import of `buffer`.
1215
    pub fn adopt_staged(&mut self, buffer: &WlBuffer, staged: StagedShm) {
1216
        if Arc::ptr_eq(&staged.staging.shared, &self.shared) {
1217
            self.staged.insert(buffer.id(), (buffer.clone(), staged));
1218
        }
1219
    }
1220
}