atagen

monolith

public
Star0
Watch0

Commit de1e9f21296c

tarball

atagen <boss@atagen.co>2026-10-17 04:56 UTC

6 files changed+1249-1220
render-vk: split stall handling, shm import and barriers out of the renderer

renderer.rs mixed the smithay trait impls with three self-contained concerns. Stall handling and the shm import stay submodules so they keep access to the renderer's fields, while the barriers only touch an image and a command buffer and move to the crate root.
Added file: crates/render-vk/src/barrier.rs+97 additions
@@ -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, optionally
5+/// transferring ownership between queue families. Coarse, but correct for
6+/// 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 come
36+/// from the foreign queue family in the general layout, others continue from
37+/// 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+}
Modified file: crates/render-vk/src/lib.rs+1 additions
@@ -3,6 +3,7 @@
33 //! A frame records its draws as a list of operations and submits them in one
44 //! 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;
Deleted file: crates/render-vk/src/renderer.rs−1220 deletions
@@ -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 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-}
Added file: crates/render-vk/src/renderer/mod.rs+890 additions
@@ -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 can
78+ /// 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::TRANSIENT
92+ | 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.name
139+ }
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.shared
161+ }
162+
163+ pub(crate) fn context_id_ref(&self) -> &ContextId<VulkanTexture> {
164+ &self.context
165+ }
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+ false
180+ });
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+ cmd
191+ }
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 = self
225+ .shared
226+ .device
227+ .queue
228+ .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+ } = op
273+ && !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<_> = rects
345+ .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 or
420+ /// 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 region
446+ /// from `row(y)`. The CPU does not wait: the copy is ordered before later
447+ /// 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 = region
528+ .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::Linear
626+ } else {
627+ TextureFilter::Nearest
628+ }
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+ where
648+ '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.debug
673+ }
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+ where
682+ '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+ image
728+ }
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_ATTACHMENT
748+ | vk::ImageUsageFlags::SAMPLED
749+ | vk::ImageUsageFlags::TRANSFER_SRC
750+ | 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::SAMPLED
773+ | vk::ImageUsageFlags::TRANSFER_DST
774+ | 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 the
827+/// 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+}
Added file: crates/render-vk/src/renderer/shm.rs+189 additions
@@ -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 into
20+ /// 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_map
31+ .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 = cache
49+ .and_then(|c| c.0.lock().unwrap_or_else(|e| e.into_inner()).clone())
50+ .filter(|t| {
51+ t.0.fourcc == fourcc
52+ && t.size() == size
53+ && Arc::ptr_eq(&t.0.shared, &self.shared)
54+ });
55+ let (texture, region) = match reused {
56+ Some(t) => {
57+ let dirty = damage
58+ .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::SAMPLED
65+ | vk::ImageUsageFlags::TRANSFER_DST
66+ | 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 == fourcc
74+ && s.size == size
75+ && 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 the
121+/// 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 renderer
130+/// 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 the
161+ // `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+}
Added file: crates/render-vk/src/renderer/stall.rs+72 additions
@@ -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 trusted
6+/// 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+ ..self
35+ }
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 costs
43+/// tens of microseconds.
44+const ABSORB_AHEAD: u64 = 8;
45+
46+impl VulkanRenderer {
47+ /// Spends an upcoming stalling submission on empty command buffers, so
48+ /// the stall lands now, in idle time, instead of in a frame. Call it
49+ /// right after a flip. NVIDIA stalls for milliseconds on every 1024th
50+ /// 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+}