Commit de1e9f21296c
tarball6 files changed+1249-1220
@@ -0,0 +1,97 @@
1
+use crate::image::{ImageInner, color_range};2
+use ash::vk;3
+4
+/// A full barrier between all earlier and later work on the image, optionally5
+/// transferring ownership between queue families. Coarse, but correct for6
+/// every transition the renderer makes.7
+pub(crate) unsafe fn barrier(8
+ d: &ash::Device,9
+ cmd: vk::CommandBuffer,10
+ image: vk::Image,11
+ old: vk::ImageLayout,12
+ new: vk::ImageLayout,13
+ src_family: u32,14
+ dst_family: u32,15
+) {16
+ let b = [vk::ImageMemoryBarrier2::default()17
+ .src_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS)18
+ .src_access_mask(vk::AccessFlags2::MEMORY_WRITE)19
+ .dst_stage_mask(vk::PipelineStageFlags2::ALL_COMMANDS)20
+ .dst_access_mask(vk::AccessFlags2::MEMORY_READ | vk::AccessFlags2::MEMORY_WRITE)21
+ .old_layout(old)22
+ .new_layout(new)23
+ .src_queue_family_index(src_family)24
+ .dst_queue_family_index(dst_family)25
+ .image(image)26
+ .subresource_range(color_range())];27
+ unsafe {28
+ d.cmd_pipeline_barrier2(29
+ cmd,30
+ &vk::DependencyInfo::default().image_memory_barriers(&b),31
+ )32
+ };33
+}34
+35
+/// Moves an image into `layout` for use on this queue. Dmabuf images come36
+/// from the foreign queue family in the general layout, others continue from37
+/// their tracked layout.38
+pub(crate) unsafe fn acquire(39
+ d: &ash::Device,40
+ cmd: vk::CommandBuffer,41
+ family: u32,42
+ image: &ImageInner,43
+ layout: vk::ImageLayout,44
+) {45
+ if image.external {46
+ unsafe {47
+ barrier(48
+ d,49
+ cmd,50
+ image.image,51
+ vk::ImageLayout::GENERAL,52
+ layout,53
+ vk::QUEUE_FAMILY_FOREIGN_EXT,54
+ family,55
+ )56
+ };57
+ } else {58
+ let old = image.layout();59
+ if old != layout {60
+ unsafe {61
+ barrier(62
+ d,63
+ cmd,64
+ image.image,65
+ old,66
+ layout,67
+ vk::QUEUE_FAMILY_IGNORED,68
+ vk::QUEUE_FAMILY_IGNORED,69
+ )70
+ };71
+ image.set_layout(layout);72
+ }73
+ }74
+}75
+76
+/// Hands a dmabuf image back to the foreign queue family after use.77
+pub(crate) unsafe fn release(78
+ d: &ash::Device,79
+ cmd: vk::CommandBuffer,80
+ family: u32,81
+ image: &ImageInner,82
+ layout: vk::ImageLayout,83
+) {84
+ if image.external {85
+ unsafe {86
+ barrier(87
+ d,88
+ cmd,89
+ image.image,90
+ layout,91
+ vk::ImageLayout::GENERAL,92
+ family,93
+ vk::QUEUE_FAMILY_FOREIGN_EXT,94
+ )95
+ };96
+ }97
+}@@ -3,6 +3,7 @@
33
//! A frame records its draws as a list of operations and submits them in one44
//! command buffer when it finishes, returning a fence as its sync point.55
6
+mod barrier;67
mod device;78
mod dmabuf;89
mod error;@@ -1,1220 +0,0 @@
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 trusted64
-/// 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
- ..self85
- }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 costs93
-/// 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 can110
- /// 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::TRANSIENT124
- | 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.name175
- }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.shared197
- }198
-199
- pub(crate) fn context_id_ref(&self) -> &ContextId<VulkanTexture> {200
- &self.context201
- }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
- false216
- });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
- cmd227
- }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 = self261
- .shared262
- .device263
- .queue264
- .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, so280
- /// the stall lands now, in idle time, instead of in a frame. Call it281
- /// right after a flip. NVIDIA stalls for milliseconds on every 1024th282
- /// 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
- } = op335
- && !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<_> = rects407
- .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 or482
- /// 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 region508
- /// from `row(y)`. The CPU does not wait: the copy is ordered before later509
- /// 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 = region590
- .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::Linear688
- } else {689
- TextureFilter::Nearest690
- }691
-}692
-693
-/// A full barrier between all earlier and later work on the image, optionally694
-/// transferring ownership between queue families. Coarse, but correct for695
-/// 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 come725
-/// from the foreign queue family in the general layout, others continue from726
-/// 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
- where805
- '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.debug830
- }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
- where839
- '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
- image885
- }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_ATTACHMENT905
- | vk::ImageUsageFlags::SAMPLED906
- | vk::ImageUsageFlags::TRANSFER_SRC907
- | 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::SAMPLED930
- | vk::ImageUsageFlags::TRANSFER_DST931
- | 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 the984
-/// 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 into1051
- /// 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_map1062
- .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 = cache1080
- .and_then(|c| c.0.lock().unwrap_or_else(|e| e.into_inner()).clone())1081
- .filter(|t| {1082
- t.0.fourcc == fourcc1083
- && t.size() == size1084
- && Arc::ptr_eq(&t.0.shared, &self.shared)1085
- });1086
- let (texture, region) = match reused {1087
- Some(t) => {1088
- let dirty = damage1089
- .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::SAMPLED1096
- | vk::ImageUsageFlags::TRANSFER_DST1097
- | 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 == fourcc1105
- && s.size == size1106
- && 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 the1152
-/// 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 renderer1161
-/// 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 the1192
- // `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
-}@@ -0,0 +1,890 @@
1
+mod shm;2
+pub use shm::{ShmStager, StagedShm};3
+mod stall;4
+use shm::stage_rows;5
+use stall::{STALL, StallPhase};6
+7
+use crate::Error;8
+use crate::barrier::{acquire, release};9
+use crate::device::Shared;10
+use crate::dmabuf::{self, DmabufCaps, RENDER_USAGE, SAMPLE_USAGE, VulkanAllocator};11
+use crate::format::{BYTES_PER_PIXEL, SUPPORTED};12
+use crate::frame::{Op, VulkanFrame};13
+use crate::image::{ImageInner, Staging, VulkanImage, VulkanMapping, VulkanTarget, VulkanTexture};14
+use crate::pipeline::Pipelines;15
+use crate::sync::{FenceInner, VkFence};16
+use ash::vk;17
+use gpu_allocator::MemoryLocation;18
+use smithay::backend::allocator::Fourcc;19
+use smithay::backend::allocator::dmabuf::{Dmabuf, WeakDmabuf};20
+use smithay::backend::allocator::format::FormatSet;21
+use smithay::backend::renderer::sync::SyncPoint;22
+use smithay::backend::renderer::{23
+ Bind, ContextId, DebugFlags, ExportMem, ImportDma, ImportDmaWl, ImportMem, Offscreen, Renderer,24
+ RendererSuper, Texture, TextureFilter,25
+};26
+use smithay::reexports::wayland_server::backend::ObjectId;27
+use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer;28
+use smithay::utils::{Buffer, Physical, Rectangle, Size, Transform};29
+use std::cell::Cell;30
+use std::collections::HashMap;31
+use std::fmt;32
+use std::marker::PhantomData;33
+use std::sync::Arc;34
+use std::time::Instant;35
+36
+struct Pending {37
+ fence: Arc<FenceInner>,38
+ cmd: vk::CommandBuffer,39
+ _keep: Vec<Arc<ImageInner>>,40
+ _staging: Option<Staging>,41
+}42
+43
+pub struct VulkanRenderer {44
+ pending: Vec<Pending>,45
+ dmabuf_textures: HashMap<WeakDmabuf, VulkanTexture>,46
+ dmabuf_targets: HashMap<WeakDmabuf, Arc<ImageInner>>,47
+ pipelines: Pipelines,48
+ pool: vk::CommandPool,49
+ /// Command buffers whose work finished, reset and reused by `begin`.50
+ spare_cmds: Vec<vk::CommandBuffer>,51
+ samplers: [vk::Sampler; 4],52
+ min_filter: TextureFilter,53
+ mag_filter: TextureFilter,54
+ debug: DebugFlags,55
+ context: ContextId<VulkanTexture>,56
+ caps: Arc<DmabufCaps>,57
+ shared: Arc<Shared>,58
+ submissions: Cell<u64>,59
+ /// Shm uploads staged off the main thread, by buffer.60
+ staged: HashMap<ObjectId, (WlBuffer, StagedShm)>,61
+ stall: Cell<StallPhase>,62
+}63
+64
+impl fmt::Debug for VulkanRenderer {65
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {66
+ f.debug_struct("VulkanRenderer")67
+ .field("device", &self.shared.device.name)68
+ .finish()69
+ }70
+}71
+72
+impl VulkanRenderer {73
+ pub fn new() -> Result<Self, Error> {74
+ Self::with_node(None)75
+ }76
+77
+ /// A renderer on the GPU behind DRM device number `node`, so it can78
+ /// render into buffers that GPU scans out.79
+ pub fn for_node(node: u64) -> Result<Self, Error> {80
+ Self::with_node(Some(node))81
+ }82
+83
+ fn with_node(node: Option<u64>) -> Result<Self, Error> {84
+ let shared = Arc::new(Shared::new(node)?);85
+ let d = &shared.device.device;86
+ let pool = unsafe {87
+ d.create_command_pool(88
+ &vk::CommandPoolCreateInfo::default()89
+ .queue_family_index(shared.device.queue_family)90
+ .flags(91
+ vk::CommandPoolCreateFlags::TRANSIENT92
+ | vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER,93
+ ),94
+ None,95
+ )?96
+ };97
+ let filter = |f: TextureFilter| match f {98
+ TextureFilter::Linear => vk::Filter::LINEAR,99
+ TextureFilter::Nearest => vk::Filter::NEAREST,100
+ };101
+ let mut samplers = [vk::Sampler::null(); 4];102
+ for (i, s) in samplers.iter_mut().enumerate() {103
+ let (min, mag) = (sampler_filter(i >> 1), sampler_filter(i & 1));104
+ *s = unsafe {105
+ d.create_sampler(106
+ &vk::SamplerCreateInfo::default()107
+ .min_filter(filter(min))108
+ .mag_filter(filter(mag))109
+ .address_mode_u(vk::SamplerAddressMode::CLAMP_TO_EDGE)110
+ .address_mode_v(vk::SamplerAddressMode::CLAMP_TO_EDGE)111
+ .address_mode_w(vk::SamplerAddressMode::CLAMP_TO_EDGE),112
+ None,113
+ )?114
+ };115
+ }116
+ Ok(Self {117
+ pending: Vec::new(),118
+ dmabuf_textures: HashMap::new(),119
+ dmabuf_targets: HashMap::new(),120
+ pipelines: Pipelines::new(&shared)?,121
+ pool,122
+ spare_cmds: Vec::new(),123
+ submissions: Cell::new(0),124
+ staged: HashMap::new(),125
+ stall: Cell::new(StallPhase::for_device(&shared.device.name)),126
+ samplers,127
+ min_filter: TextureFilter::Linear,128
+ mag_filter: TextureFilter::Linear,129
+ debug: DebugFlags::empty(),130
+ context: ContextId::new(),131
+ caps: Arc::new(DmabufCaps::query(&shared)),132
+ shared,133
+ })134
+ }135
+136
+ /// Name of the physical device in use.137
+ pub fn device_name(&self) -> &str {138
+ &self.shared.device.name139
+ }140
+141
+ /// Whether dmabufs can be imported, rendered into and allocated.142
+ pub fn supports_dmabuf(&self) -> bool {143
+ self.caps.render.iter().next().is_some()144
+ }145
+146
+ /// Formats and modifiers this renderer can render into as dmabufs.147
+ pub fn render_formats(&self) -> FormatSet {148
+ self.caps.render.clone()149
+ }150
+151
+ /// An allocator for render-capable dmabufs on this device.152
+ pub fn allocator(&self) -> VulkanAllocator {153
+ VulkanAllocator {154
+ shared: self.shared.clone(),155
+ caps: self.caps.clone(),156
+ }157
+ }158
+159
+ pub(crate) fn shared(&self) -> &Arc<Shared> {160
+ &self.shared161
+ }162
+163
+ pub(crate) fn context_id_ref(&self) -> &ContextId<VulkanTexture> {164
+ &self.context165
+ }166
+167
+ fn sampler(&self) -> vk::Sampler {168
+ let idx = |f: TextureFilter| usize::from(f == TextureFilter::Nearest);169
+ self.samplers[(idx(self.min_filter) << 1) | idx(self.mag_filter)]170
+ }171
+172
+ fn reap(&mut self) {173
+ let spare = &mut self.spare_cmds;174
+ self.pending.retain(|p| {175
+ if !p.fence.signaled() {176
+ return true;177
+ }178
+ spare.push(p.cmd);179
+ false180
+ });181
+ self.dmabuf_textures.retain(|k, _| !k.is_gone());182
+ self.dmabuf_targets.retain(|k, _| !k.is_gone());183
+ }184
+185
+ fn begin(&mut self) -> Result<vk::CommandBuffer, Error> {186
+ let d = &self.shared.device.device;187
+ let cmd = match self.spare_cmds.pop() {188
+ Some(cmd) => {189
+ unsafe { d.reset_command_buffer(cmd, vk::CommandBufferResetFlags::empty())? };190
+ cmd191
+ }192
+ None => unsafe {193
+ d.allocate_command_buffers(194
+ &vk::CommandBufferAllocateInfo::default()195
+ .command_pool(self.pool)196
+ .level(vk::CommandBufferLevel::PRIMARY)197
+ .command_buffer_count(1),198
+ )?[0]199
+ },200
+ };201
+ unsafe {202
+ d.begin_command_buffer(203
+ cmd,204
+ &vk::CommandBufferBeginInfo::default()205
+ .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),206
+ )?207
+ };208
+ Ok(cmd)209
+ }210
+211
+ #[profiling::function]212
+ fn end_and_submit(&self, cmd: vk::CommandBuffer) -> Result<Arc<FenceInner>, Error> {213
+ let d = &self.shared.device.device;214
+ {215
+ profiling::scope!("end_command_buffer");216
+ unsafe { d.end_command_buffer(cmd)? };217
+ }218
+ let fence = {219
+ profiling::scope!("create_fence");220
+ FenceInner::new(&self.shared)?221
+ };222
+ let cmds = [vk::CommandBufferSubmitInfo::default().command_buffer(cmd)];223
+ let submit = vk::SubmitInfo2::default().command_buffer_infos(&cmds);224
+ let queue = self225
+ .shared226
+ .device227
+ .queue228
+ .lock()229
+ .unwrap_or_else(|e| e.into_inner());230
+ let began = Instant::now();231
+ {232
+ profiling::scope!("queue_submit");233
+ unsafe { d.queue_submit2(*queue, &[submit], fence.fence())? };234
+ }235
+ let n = self.submissions.get();236
+ self.submissions.set(n + 1);237
+ if began.elapsed() > STALL {238
+ self.stall.set(self.stall.get().saw(n % 1024));239
+ }240
+ Ok(fence)241
+ }242
+243
+ /// Records, submits and waits for a short command buffer.244
+ fn one_shot(245
+ &mut self,246
+ record: impl FnOnce(&ash::Device, vk::CommandBuffer, u32),247
+ ) -> Result<(), Error> {248
+ let cmd = self.begin()?;249
+ record(250
+ &self.shared.device.device,251
+ cmd,252
+ self.shared.device.queue_family,253
+ );254
+ let fence = self.end_and_submit(cmd)?;255
+ let res = fence.wait();256
+ self.spare_cmds.push(cmd);257
+ Ok(res?)258
+ }259
+260
+ #[profiling::function]261
+ pub(crate) fn submit(262
+ &mut self,263
+ target: &mut VulkanTarget<'_>,264
+ ops: Vec<Op>,265
+ ) -> Result<SyncPoint, Error> {266
+ self.reap();267
+ let image = target.0.clone();268
+ let mut sampled: Vec<Arc<ImageInner>> = Vec::new();269
+ for op in &ops {270
+ if let Op::Draw {271
+ texture: Some(t), ..272
+ } = op273
+ && !sampled.iter().any(|s| Arc::ptr_eq(s, &t.0))274
+ {275
+ sampled.push(t.0.clone());276
+ }277
+ }278
+ let mut pipelines = Vec::with_capacity(ops.len());279
+ for op in &ops {280
+ pipelines.push(match op {281
+ Op::Draw { kind, .. } => Some(self.pipelines.get(image.format, *kind)?),282
+ Op::Clear { .. } => None,283
+ });284
+ }285
+ let cmd = self.begin()?;286
+ let d = &self.shared.device.device;287
+ let family = self.shared.device.queue_family;288
+ let push = &self.shared.device.push_descriptor;289
+ let (w, h) = (image.width, image.height);290
+ let sampler = self.sampler();291
+ unsafe {292
+ for t in &sampled {293
+ acquire(d, cmd, family, t, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);294
+ }295
+ acquire(296
+ d,297
+ cmd,298
+ family,299
+ &image,300
+ vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,301
+ );302
+ let attachments = [vk::RenderingAttachmentInfo::default()303
+ .image_view(image.view)304
+ .image_layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)305
+ .load_op(vk::AttachmentLoadOp::LOAD)306
+ .store_op(vk::AttachmentStoreOp::STORE)];307
+ let area = vk::Rect2D {308
+ offset: vk::Offset2D::default(),309
+ extent: vk::Extent2D {310
+ width: w,311
+ height: h,312
+ },313
+ };314
+ d.cmd_begin_rendering(315
+ cmd,316
+ &vk::RenderingInfo::default()317
+ .render_area(area)318
+ .layer_count(1)319
+ .color_attachments(&attachments),320
+ );321
+ d.cmd_set_viewport(322
+ cmd,323
+ 0,324
+ &[vk::Viewport {325
+ x: 0.0,326
+ y: 0.0,327
+ width: w as f32,328
+ height: h as f32,329
+ min_depth: 0.0,330
+ max_depth: 1.0,331
+ }],332
+ );333
+ let mut bound = vk::Pipeline::null();334
+ for (op, pipeline) in ops.iter().zip(pipelines) {335
+ match op {336
+ Op::Clear { color, rects } => {337
+ let clear = [vk::ClearAttachment {338
+ aspect_mask: vk::ImageAspectFlags::COLOR,339
+ color_attachment: 0,340
+ clear_value: vk::ClearValue {341
+ color: vk::ClearColorValue { float32: *color },342
+ },343
+ }];344
+ let rects: Vec<_> = rects345
+ .iter()346
+ .map(|&rect| vk::ClearRect {347
+ rect,348
+ base_array_layer: 0,349
+ layer_count: 1,350
+ })351
+ .collect();352
+ d.cmd_clear_attachments(cmd, &clear, &rects);353
+ }354
+ Op::Draw {355
+ texture,356
+ push: constants,357
+ scissors,358
+ ..359
+ } => {360
+ let pipeline = pipeline.unwrap_or_default();361
+ if pipeline != bound {362
+ d.cmd_bind_pipeline(cmd, vk::PipelineBindPoint::GRAPHICS, pipeline);363
+ bound = pipeline;364
+ }365
+ if let Some(t) = texture {366
+ let info = [vk::DescriptorImageInfo::default()367
+ .image_view(t.0.view)368
+ .image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL)369
+ .sampler(sampler)];370
+ let write = vk::WriteDescriptorSet::default()371
+ .dst_binding(0)372
+ .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)373
+ .image_info(&info);374
+ push.cmd_push_descriptor_set(375
+ cmd,376
+ vk::PipelineBindPoint::GRAPHICS,377
+ self.pipelines.layout,378
+ 0,379
+ &[write],380
+ );381
+ }382
+ d.cmd_push_constants(383
+ cmd,384
+ self.pipelines.layout,385
+ vk::ShaderStageFlags::VERTEX | vk::ShaderStageFlags::FRAGMENT,386
+ 0,387
+ constants.bytes(),388
+ );389
+ for s in scissors {390
+ d.cmd_set_scissor(cmd, 0, &[*s]);391
+ d.cmd_draw(cmd, 4, 1, 0, 0);392
+ }393
+ }394
+ }395
+ }396
+ d.cmd_end_rendering(cmd);397
+ release(398
+ d,399
+ cmd,400
+ family,401
+ &image,402
+ vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,403
+ );404
+ for t in &sampled {405
+ release(d, cmd, family, t, vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);406
+ }407
+ }408
+ let fence = self.end_and_submit(cmd)?;409
+ sampled.push(image);410
+ self.pending.push(Pending {411
+ fence: fence.clone(),412
+ cmd,413
+ _keep: sampled,414
+ _staging: None,415
+ });416
+ Ok(SyncPoint::from(VkFence(fence)))417
+ }418
+419
+ /// Copies `region` of a full-image `data` into `image`, rows top-down or420
+ /// flipped.421
+ fn upload(422
+ &mut self,423
+ image: &Arc<ImageInner>,424
+ data: &[u8],425
+ region: Rectangle<i32, Buffer>,426
+ flipped: bool,427
+ ) -> Result<(), Error> {428
+ let width = image.width as usize;429
+ let rows = image.height as usize;430
+ let needed = width * rows * BYTES_PER_PIXEL;431
+ if data.len() < needed {432
+ return Err(Error::BufferTooSmall {433
+ needed,434
+ got: data.len(),435
+ });436
+ }437
+ let stride = width * BYTES_PER_PIXEL;438
+ self.upload_with(image, region, |y| {439
+ let from = if flipped { rows - 1 - y } else { y };440
+ let start = from * stride + region.loc.x as usize * BYTES_PER_PIXEL;441
+ &data[start..start + region.size.w as usize * BYTES_PER_PIXEL]442
+ })443
+ }444
+445
+ /// Uploads `region` of `image`, taking each image row `y` in the region446
+ /// from `row(y)`. The CPU does not wait: the copy is ordered before later447
+ /// frames by its barrier, and its staging memory lives until its fence.448
+ #[profiling::function]449
+ fn upload_with<'a>(450
+ &mut self,451
+ image: &Arc<ImageInner>,452
+ region: Rectangle<i32, Buffer>,453
+ row: impl Fn(usize) -> &'a [u8],454
+ ) -> Result<(), Error> {455
+ if image.external {456
+ return Err(Error::ForeignTexture);457
+ }458
+ self.reap();459
+ if region.size.w <= 0 || region.size.h <= 0 {460
+ return Ok(());461
+ }462
+ let staging = stage_rows(&self.shared, region, row)?;463
+ self.copy_staged(image, region, staging)464
+ }465
+466
+ /// Copies staged pixels into `region` of `image` on the GPU.467
+ fn copy_staged(468
+ &mut self,469
+ image: &Arc<ImageInner>,470
+ region: Rectangle<i32, Buffer>,471
+ staging: Staging,472
+ ) -> Result<(), Error> {473
+ let (w, h) = (region.size.w as usize, region.size.h as usize);474
+ let copy = vk::BufferImageCopy::default()475
+ .buffer_row_length(w as u32)476
+ .image_subresource(477
+ vk::ImageSubresourceLayers::default()478
+ .aspect_mask(vk::ImageAspectFlags::COLOR)479
+ .layer_count(1),480
+ )481
+ .image_offset(vk::Offset3D {482
+ x: region.loc.x,483
+ y: region.loc.y,484
+ z: 0,485
+ })486
+ .image_extent(vk::Extent3D {487
+ width: w as u32,488
+ height: h as u32,489
+ depth: 1,490
+ });491
+ let cmd = self.begin()?;492
+ let d = &self.shared.device.device;493
+ let family = self.shared.device.queue_family;494
+ unsafe {495
+ acquire(d, cmd, family, image, vk::ImageLayout::TRANSFER_DST_OPTIMAL);496
+ d.cmd_copy_buffer_to_image(497
+ cmd,498
+ staging.buffer,499
+ image.image,500
+ vk::ImageLayout::TRANSFER_DST_OPTIMAL,501
+ &[copy],502
+ );503
+ acquire(504
+ d,505
+ cmd,506
+ family,507
+ image,508
+ vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,509
+ );510
+ }511
+ let fence = self.end_and_submit(cmd)?;512
+ self.pending.push(Pending {513
+ fence,514
+ cmd,515
+ _keep: vec![image.clone()],516
+ _staging: Some(staging),517
+ });518
+ Ok(())519
+ }520
+521
+ fn read_back(522
+ &mut self,523
+ image: &Arc<ImageInner>,524
+ region: Rectangle<i32, Buffer>,525
+ fourcc: Fourcc,526
+ ) -> Result<VulkanMapping, Error> {527
+ let region = region528
+ .intersection(Rectangle::from_size(529
+ (image.width as i32, image.height as i32).into(),530
+ ))531
+ .unwrap_or_default();532
+ let (w, h) = (region.size.w.max(0) as u32, region.size.h.max(0) as u32);533
+ let swap = needs_swizzle(image.fourcc, fourcc)?;534
+ let len = (w as usize * h as usize * BYTES_PER_PIXEL).max(BYTES_PER_PIXEL);535
+ let staging = Staging::new(536
+ &self.shared,537
+ len,538
+ vk::BufferUsageFlags::TRANSFER_DST,539
+ MemoryLocation::GpuToCpu,540
+ )?;541
+ if w > 0 && h > 0 {542
+ let copy = vk::BufferImageCopy::default()543
+ .image_subresource(544
+ vk::ImageSubresourceLayers::default()545
+ .aspect_mask(vk::ImageAspectFlags::COLOR)546
+ .layer_count(1),547
+ )548
+ .image_offset(vk::Offset3D {549
+ x: region.loc.x,550
+ y: region.loc.y,551
+ z: 0,552
+ })553
+ .image_extent(vk::Extent3D {554
+ width: w,555
+ height: h,556
+ depth: 1,557
+ });558
+ let buffer = staging.buffer;559
+ let source = image.clone();560
+ self.one_shot(|d, cmd, family| unsafe {561
+ acquire(562
+ d,563
+ cmd,564
+ family,565
+ &source,566
+ vk::ImageLayout::TRANSFER_SRC_OPTIMAL,567
+ );568
+ d.cmd_copy_image_to_buffer(569
+ cmd,570
+ source.image,571
+ vk::ImageLayout::TRANSFER_SRC_OPTIMAL,572
+ buffer,573
+ &[copy],574
+ );575
+ release(576
+ d,577
+ cmd,578
+ family,579
+ &source,580
+ vk::ImageLayout::TRANSFER_SRC_OPTIMAL,581
+ );582
+ })?;583
+ }584
+ let mut data = staging.bytes()[..w as usize * h as usize * BYTES_PER_PIXEL].to_vec();585
+ if swap {586
+ for px in data.as_chunks_mut::<BYTES_PER_PIXEL>().0 {587
+ px.swap(0, 2);588
+ }589
+ }590
+ Ok(VulkanMapping {591
+ data,592
+ width: w,593
+ height: h,594
+ fourcc,595
+ })596
+ }597
+598
+ fn own(&self, image: &Arc<ImageInner>) -> Result<(), Error> {599
+ if Arc::ptr_eq(&image.shared, &self.shared) {600
+ Ok(())601
+ } else {602
+ Err(Error::ForeignTexture)603
+ }604
+ }605
+}606
+607
+impl Drop for VulkanRenderer {608
+ fn drop(&mut self) {609
+ let d = &self.shared.device.device;610
+ unsafe {611
+ let _ = d.device_wait_idle();612
+ for p in self.pending.drain(..) {613
+ d.free_command_buffers(self.pool, &[p.cmd]);614
+ }615
+ for s in self.samplers {616
+ d.destroy_sampler(s, None);617
+ }618
+ d.destroy_command_pool(self.pool, None);619
+ }620
+ }621
+}622
+623
+fn sampler_filter(bit: usize) -> TextureFilter {624
+ if bit == 0 {625
+ TextureFilter::Linear626
+ } else {627
+ TextureFilter::Nearest628
+ }629
+}630
+631
+/// Whether reading `from` as `to` needs red and blue swapped.632
+fn needs_swizzle(from: Fourcc, to: Fourcc) -> Result<bool, Error> {633
+ let bgr = |f: Fourcc| match f {634
+ Fourcc::Argb8888 | Fourcc::Xrgb8888 => Ok(true),635
+ Fourcc::Abgr8888 | Fourcc::Xbgr8888 => Ok(false),636
+ f => Err(Error::UnsupportedFormat(f)),637
+ };638
+ Ok(bgr(from)? != bgr(to)?)639
+}640
+641
+impl RendererSuper for VulkanRenderer {642
+ type Error = Error;643
+ type TextureId = VulkanTexture;644
+ type Framebuffer<'buffer> = VulkanTarget<'buffer>;645
+ type Frame<'frame, 'buffer>646
+ = VulkanFrame<'frame, 'buffer>647
+ where648
+ 'buffer: 'frame,649
+ Self: 'frame;650
+}651
+652
+impl Renderer for VulkanRenderer {653
+ fn context_id(&self) -> ContextId<VulkanTexture> {654
+ self.context.clone()655
+ }656
+657
+ fn downscale_filter(&mut self, filter: TextureFilter) -> Result<(), Error> {658
+ self.min_filter = filter;659
+ Ok(())660
+ }661
+662
+ fn upscale_filter(&mut self, filter: TextureFilter) -> Result<(), Error> {663
+ self.mag_filter = filter;664
+ Ok(())665
+ }666
+667
+ fn set_debug_flags(&mut self, flags: DebugFlags) {668
+ self.debug = flags;669
+ }670
+671
+ fn debug_flags(&self) -> DebugFlags {672
+ self.debug673
+ }674
+675
+ fn render<'frame, 'buffer>(676
+ &'frame mut self,677
+ framebuffer: &'frame mut VulkanTarget<'buffer>,678
+ output_size: Size<i32, Physical>,679
+ dst_transform: Transform,680
+ ) -> Result<VulkanFrame<'frame, 'buffer>, Error>681
+ where682
+ 'buffer: 'frame,683
+ {684
+ self.own(&framebuffer.0)?;685
+ self.reap();686
+ Ok(VulkanFrame::new(687
+ self,688
+ framebuffer,689
+ output_size,690
+ dst_transform,691
+ ))692
+ }693
+694
+ fn wait(&mut self, sync: &SyncPoint) -> Result<(), Error> {695
+ sync.wait().map_err(|_| Error::Interrupted)696
+ }697
+698
+ fn cleanup_texture_cache(&mut self) -> Result<(), Error> {699
+ self.reap();700
+ Ok(())701
+ }702
+703
+ fn invalidate_caches(&mut self) -> Result<(), Error> {704
+ self.reap();705
+ self.dmabuf_textures.clear();706
+ self.dmabuf_targets.clear();707
+ Ok(())708
+ }709
+}710
+711
+impl Bind<VulkanImage> for VulkanRenderer {712
+ fn bind<'a>(&mut self, target: &'a mut VulkanImage) -> Result<VulkanTarget<'a>, Error> {713
+ self.own(&target.0)?;714
+ Ok(VulkanTarget(target.0.clone(), PhantomData))715
+ }716
+}717
+718
+impl Bind<Dmabuf> for VulkanRenderer {719
+ #[profiling::function]720
+ fn bind<'a>(&mut self, target: &'a mut Dmabuf) -> Result<VulkanTarget<'a>, Error> {721
+ let key = target.weak();722
+ let image = match self.dmabuf_targets.get(&key) {723
+ Some(image) => image.clone(),724
+ None => {725
+ let image = dmabuf::import(&self.shared, &self.caps, target, RENDER_USAGE)?;726
+ self.dmabuf_targets.insert(key, image.clone());727
+ image728
+ }729
+ };730
+ Ok(VulkanTarget(image, PhantomData))731
+ }732
+733
+ fn supported_formats(&self) -> Option<FormatSet> {734
+ Some(self.caps.render.clone())735
+ }736
+}737
+738
+impl Offscreen<VulkanImage> for VulkanRenderer {739
+ fn create_buffer(740
+ &mut self,741
+ format: Fourcc,742
+ size: Size<i32, Buffer>,743
+ ) -> Result<VulkanImage, Error> {744
+ if size.w <= 0 || size.h <= 0 {745
+ return Err(Error::InvalidSize(size.w, size.h));746
+ }747
+ let usage = vk::ImageUsageFlags::COLOR_ATTACHMENT748
+ | vk::ImageUsageFlags::SAMPLED749
+ | vk::ImageUsageFlags::TRANSFER_SRC750
+ | vk::ImageUsageFlags::TRANSFER_DST;751
+ Ok(VulkanImage(ImageInner::create(752
+ &self.shared,753
+ format,754
+ size.w as u32,755
+ size.h as u32,756
+ usage,757
+ )?))758
+ }759
+}760
+761
+impl ImportMem for VulkanRenderer {762
+ fn import_memory(763
+ &mut self,764
+ data: &[u8],765
+ format: Fourcc,766
+ size: Size<i32, Buffer>,767
+ flipped: bool,768
+ ) -> Result<VulkanTexture, Error> {769
+ if size.w <= 0 || size.h <= 0 {770
+ return Err(Error::InvalidSize(size.w, size.h));771
+ }772
+ let usage = vk::ImageUsageFlags::SAMPLED773
+ | vk::ImageUsageFlags::TRANSFER_DST774
+ | vk::ImageUsageFlags::TRANSFER_SRC;775
+ let image = ImageInner::create(&self.shared, format, size.w as u32, size.h as u32, usage)?;776
+ self.upload(&image, data, Rectangle::from_size(size), flipped)?;777
+ Ok(VulkanTexture(image))778
+ }779
+780
+ fn update_memory(781
+ &mut self,782
+ texture: &VulkanTexture,783
+ data: &[u8],784
+ region: Rectangle<i32, Buffer>,785
+ ) -> Result<(), Error> {786
+ self.own(&texture.0)?;787
+ let Some(region) = region.intersection(Rectangle::from_size(texture.size())) else {788
+ return Ok(());789
+ };790
+ self.upload(&texture.0, data, region, false)791
+ }792
+793
+ fn mem_formats(&self) -> Box<dyn Iterator<Item = Fourcc>> {794
+ Box::new(SUPPORTED.into_iter())795
+ }796
+}797
+798
+impl ImportDma for VulkanRenderer {799
+ fn dmabuf_formats(&self) -> FormatSet {800
+ self.caps.sample.clone()801
+ }802
+803
+ #[profiling::function]804
+ fn import_dmabuf(805
+ &mut self,806
+ buffer: &Dmabuf,807
+ _damage: Option<&[Rectangle<i32, Buffer>]>,808
+ ) -> Result<VulkanTexture, Error> {809
+ let key = buffer.weak();810
+ if let Some(t) = self.dmabuf_textures.get(&key) {811
+ return Ok(t.clone());812
+ }813
+ let texture = VulkanTexture(dmabuf::import(814
+ &self.shared,815
+ &self.caps,816
+ buffer,817
+ SAMPLE_USAGE,818
+ )?);819
+ self.dmabuf_textures.insert(key, texture.clone());820
+ Ok(texture)821
+ }822
+}823
+824
+impl ImportDmaWl for VulkanRenderer {}825
+826
+/// Imports dmabufs on another thread. The textures it makes belong to the827
+/// renderer it came from, which takes them back with `adopt_dmabuf`.828
+#[derive(Clone)]829
+pub struct DmabufImporter {830
+ shared: Arc<Shared>,831
+ caps: Arc<DmabufCaps>,832
+}833
+834
+impl DmabufImporter {835
+ pub fn import(&self, buffer: &Dmabuf) -> Result<VulkanTexture, Error> {836
+ dmabuf::import(&self.shared, &self.caps, buffer, SAMPLE_USAGE).map(VulkanTexture)837
+ }838
+}839
+840
+impl VulkanRenderer {841
+ pub fn dmabuf_importer(&self) -> DmabufImporter {842
+ DmabufImporter {843
+ shared: self.shared.clone(),844
+ caps: self.caps.clone(),845
+ }846
+ }847
+848
+ pub fn has_dmabuf(&self, buffer: &Dmabuf) -> bool {849
+ self.dmabuf_textures.contains_key(&buffer.weak())850
+ }851
+852
+ pub fn adopt_dmabuf(&mut self, buffer: &Dmabuf, texture: VulkanTexture) -> Result<(), Error> {853
+ self.own(&texture.0)?;854
+ self.dmabuf_textures.insert(buffer.weak(), texture);855
+ Ok(())856
+ }857
+}858
+859
+impl ExportMem for VulkanRenderer {860
+ type TextureMapping = VulkanMapping;861
+862
+ fn copy_framebuffer(863
+ &mut self,864
+ target: &VulkanTarget<'_>,865
+ region: Rectangle<i32, Buffer>,866
+ format: Fourcc,867
+ ) -> Result<VulkanMapping, Error> {868
+ self.own(&target.0)?;869
+ let image = target.0.clone();870
+ self.read_back(&image, region, format)871
+ }872
+873
+ fn copy_texture(874
+ &mut self,875
+ texture: &VulkanTexture,876
+ region: Rectangle<i32, Buffer>,877
+ format: Fourcc,878
+ ) -> Result<VulkanMapping, Error> {879
+ self.own(&texture.0)?;880
+ self.read_back(&texture.0, region, format)881
+ }882
+883
+ fn can_read_texture(&mut self, texture: &VulkanTexture) -> Result<bool, Error> {884
+ Ok(texture.0.external || texture.0.layout() != vk::ImageLayout::UNDEFINED)885
+ }886
+887
+ fn map_texture<'a>(&mut self, mapping: &'a VulkanMapping) -> Result<&'a [u8], Error> {888
+ Ok(&mapping.data)889
+ }890
+}@@ -0,0 +1,189 @@
1
+use super::VulkanRenderer;2
+use crate::Error;3
+use crate::device::Shared;4
+use crate::format::BYTES_PER_PIXEL;5
+use crate::image::{ImageInner, Staging, VulkanTexture};6
+use ash::vk;7
+use gpu_allocator::MemoryLocation;8
+use smithay::backend::allocator::Fourcc;9
+use smithay::backend::renderer::ImportMemWl;10
+use smithay::backend::renderer::Texture;11
+use smithay::reexports::wayland_server::Resource;12
+use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer;13
+use smithay::utils::{Buffer, Rectangle, Size};14
+use smithay::wayland::compositor::SurfaceData;15
+use smithay::wayland::shm::{shm_format_to_fourcc, with_buffer_contents};16
+use std::sync::{Arc, Mutex};17
+18
+impl ImportMemWl for VulkanRenderer {19
+ /// Keeps one texture per surface and uploads only the damaged rows into20
+ /// it, straight from the client's shm pool.21
+ fn import_shm_buffer(22
+ &mut self,23
+ buffer: &WlBuffer,24
+ surface: Option<&SurfaceData>,25
+ damage: &[Rectangle<i32, Buffer>],26
+ ) -> Result<VulkanTexture, Error> {27
+ self.staged.retain(|_, (b, _)| b.is_alive());28
+ let staged = self.staged.remove(&buffer.id()).map(|(_, s)| s);29
+ let cache = surface.map(|s| {30
+ s.data_map31
+ .insert_if_missing_threadsafe(|| ShmCache(Mutex::new(None)));32
+ s.data_map.get::<ShmCache>().expect("just inserted")33
+ });34
+ with_buffer_contents(buffer, |ptr, len, data| {35
+ let fourcc = shm_format_to_fourcc(data.format).ok_or(Error::ShmAccess)?;36
+ let (w, h, stride, offset) = (37
+ data.width as usize,38
+ data.height as usize,39
+ data.stride as usize,40
+ data.offset as usize,41
+ );42
+ let line = w * BYTES_PER_PIXEL;43
+ if w == 0 || h == 0 || stride < line || offset + stride * (h - 1) + line > len {44
+ return Err(Error::ShmAccess);45
+ }46
+ let size = Size::from((data.width, data.height));47
+ let full = Rectangle::from_size(size);48
+ let reused = cache49
+ .and_then(|c| c.0.lock().unwrap_or_else(|e| e.into_inner()).clone())50
+ .filter(|t| {51
+ t.0.fourcc == fourcc52
+ && t.size() == size53
+ && Arc::ptr_eq(&t.0.shared, &self.shared)54
+ });55
+ let (texture, region) = match reused {56
+ Some(t) => {57
+ let dirty = damage58
+ .iter()59
+ .filter_map(|d| d.intersection(full))60
+ .reduce(|a, b| a.merge(b));61
+ (t, if damage.is_empty() { Some(full) } else { dirty })62
+ }63
+ None => {64
+ let usage = vk::ImageUsageFlags::SAMPLED65
+ | vk::ImageUsageFlags::TRANSFER_DST66
+ | vk::ImageUsageFlags::TRANSFER_SRC;67
+ let image =68
+ ImageInner::create(&self.shared, fourcc, w as u32, h as u32, usage)?;69
+ (VulkanTexture(image), Some(full))70
+ }71
+ };72
+ let staged = staged.filter(|s| {73
+ s.fourcc == fourcc74
+ && s.size == size75
+ && region.is_some_and(|r| s.region.contains_rect(r))76
+ });77
+ if let Some(s) = staged {78
+ tracing::trace!(region = ?s.region, "uploading a staged shm copy");79
+ self.copy_staged(&texture.0, s.region, s.staging)?;80
+ } else if let Some(region) = region {81
+ let x = region.loc.x as usize * BYTES_PER_PIXEL;82
+ let rw = region.size.w as usize * BYTES_PER_PIXEL;83
+ self.upload_with(&texture.0, region, |y| unsafe {84
+ std::slice::from_raw_parts(ptr.add(offset + y * stride + x), rw)85
+ })?;86
+ }87
+ if let Some(c) = cache {88
+ *c.0.lock().unwrap_or_else(|e| e.into_inner()) = Some(texture.clone());89
+ }90
+ Ok(texture)91
+ })92
+ .map_err(|_| Error::ShmAccess)?93
+ }94
+}95
+96
+/// The texture last imported for a surface's shm buffers.97
+struct ShmCache(Mutex<Option<VulkanTexture>>);98
+99
+/// Copies `region` of a full-image `row` source into new staging memory.100
+pub(super) fn stage_rows<'a>(101
+ shared: &Arc<Shared>,102
+ region: Rectangle<i32, Buffer>,103
+ row: impl Fn(usize) -> &'a [u8],104
+) -> Result<Staging, Error> {105
+ let (w, h) = (region.size.w as usize, region.size.h as usize);106
+ let line = w * BYTES_PER_PIXEL;107
+ let mut staging = Staging::new(108
+ shared,109
+ line * h,110
+ vk::BufferUsageFlags::TRANSFER_SRC,111
+ MemoryLocation::CpuToGpu,112
+ )?;113
+ let dst = staging.bytes_mut();114
+ for i in 0..h {115
+ dst[i * line..(i + 1) * line].copy_from_slice(row(region.loc.y as usize + i));116
+ }117
+ Ok(staging)118
+}119
+120
+/// Shm pixels copied off the main thread, waiting for the import of the121
+/// commit that shows them.122
+pub struct StagedShm {123
+ staging: Staging,124
+ region: Rectangle<i32, Buffer>,125
+ fourcc: Fourcc,126
+ size: Size<i32, Buffer>,127
+}128
+129
+/// Copies shm buffers into staging memory on another thread. The renderer130
+/// it came from takes the result back with `adopt_staged`.131
+#[derive(Clone)]132
+pub struct ShmStager {133
+ shared: Arc<Shared>,134
+}135
+136
+impl ShmStager {137
+ /// Stages `region` of `buffer`, all of it when the region is `None`.138
+ pub fn stage(139
+ &self,140
+ buffer: &WlBuffer,141
+ region: Option<Rectangle<i32, Buffer>>,142
+ ) -> Result<StagedShm, Error> {143
+ with_buffer_contents(buffer, |ptr, len, data| {144
+ let fourcc = shm_format_to_fourcc(data.format).ok_or(Error::ShmAccess)?;145
+ let (w, h, stride, offset) = (146
+ data.width as usize,147
+ data.height as usize,148
+ data.stride as usize,149
+ data.offset as usize,150
+ );151
+ let line = w * BYTES_PER_PIXEL;152
+ if w == 0 || h == 0 || stride < line || offset + stride * (h - 1) + line > len {153
+ return Err(Error::ShmAccess);154
+ }155
+ let size = Size::from((data.width, data.height));156
+ let full = Rectangle::from_size(size);157
+ let region = region.and_then(|r| r.intersection(full)).unwrap_or(full);158
+ let x = region.loc.x as usize * BYTES_PER_PIXEL;159
+ let rw = region.size.w as usize * BYTES_PER_PIXEL;160
+ // SAFETY: the bounds check above keeps every row inside the161
+ // `len` bytes smithay mapped at `ptr`.162
+ let staging = stage_rows(&self.shared, region, |y| unsafe {163
+ std::slice::from_raw_parts(ptr.add(offset + y * stride + x), rw)164
+ })?;165
+ Ok(StagedShm {166
+ staging,167
+ region,168
+ fourcc,169
+ size,170
+ })171
+ })172
+ .map_err(|_| Error::ShmAccess)?173
+ }174
+}175
+176
+impl VulkanRenderer {177
+ pub fn shm_stager(&self) -> ShmStager {178
+ ShmStager {179
+ shared: self.shared.clone(),180
+ }181
+ }182
+183
+ /// Hands back pixels a stager copied, used by the next import of `buffer`.184
+ pub fn adopt_staged(&mut self, buffer: &WlBuffer, staged: StagedShm) {185
+ if Arc::ptr_eq(&staged.staging.shared, &self.shared) {186
+ self.staged.insert(buffer.id(), (buffer.clone(), staged));187
+ }188
+ }189
+}@@ -0,0 +1,72 @@
1
+use super::{Pending, VulkanRenderer};2
+use crate::Error;3
+use std::time::Duration;4
+5
+/// Where in every 1024 submissions the driver stalls. A phase is trusted6
+/// once two stalls land on it.7
+#[derive(Clone, Copy, Default)]8
+pub(super) struct StallPhase {9
+ phase: Option<u64>,10
+ seen: Option<u64>,11
+}12
+13
+impl StallPhase {14
+ pub(super) fn for_device(name: &str) -> Self {15
+ Self {16
+ // Measured on an RTX 3060, see research/nvidia.md.17
+ phase: name.contains("NVIDIA").then_some(1015),18
+ seen: None,19
+ }20
+ }21
+22
+ pub(super) fn saw(self, at: u64) -> Self {23
+ if self.seen == Some(at) {24
+ if self.phase != Some(at) {25
+ tracing::debug!(phase = at, "queue submissions stall periodically");26
+ }27
+ Self {28
+ phase: Some(at),29
+ seen: Some(at),30
+ }31
+ } else {32
+ Self {33
+ seen: Some(at),34
+ ..self35
+ }36
+ }37
+ }38
+}39
+40
+/// A submission this slow is a driver stall rather than work.41
+pub(super) const STALL: Duration = Duration::from_micros(1500);42
+/// How many submissions early a stall is absorbed. Each empty one costs43
+/// tens of microseconds.44
+const ABSORB_AHEAD: u64 = 8;45
+46
+impl VulkanRenderer {47
+ /// Spends an upcoming stalling submission on empty command buffers, so48
+ /// the stall lands now, in idle time, instead of in a frame. Call it49
+ /// right after a flip. NVIDIA stalls for milliseconds on every 1024th50
+ /// submission, see research/nvidia.md.51
+ pub fn absorb_submit_stall(&mut self) -> Result<(), Error> {52
+ self.reap();53
+ let Some(phase) = self.stall.get().phase else {54
+ return Ok(());55
+ };56
+ let ahead = (phase + 1024 - self.submissions.get() % 1024) % 1024;57
+ if ahead >= ABSORB_AHEAD {58
+ return Ok(());59
+ }60
+ for _ in 0..=ahead {61
+ let cmd = self.begin()?;62
+ let fence = self.end_and_submit(cmd)?;63
+ self.pending.push(Pending {64
+ fence,65
+ cmd,66
+ _keep: Vec::new(),67
+ _staging: None,68
+ });69
+ }70
+ Ok(())71
+ }72
+}