Commit 8956d017e879
tarball6 files changed+175-7
@@ -1,6 +1,5 @@
11
use crate::state::{ClientState, State};22
use smithay::backend::allocator::dmabuf::Dmabuf;3
-use smithay::backend::renderer::ImportDma;43
use smithay::backend::renderer::utils::{import_surface_tree, on_commit_buffer_handler};54
use smithay::desktop::{PopupKind, Window};65
use smithay::input::dnd::{DndGrabHandler, GrabType, Source};@@ -127,11 +126,7 @@ impl DmabufHandler for State {
127126
dmabuf: Dmabuf,128127
notifier: ImportNotifier,129128
) {130
- if self.renderer.import_dmabuf(&dmabuf, None).is_ok() {131
- let _ = notifier.successful::<State>();132
- } else {133
- notifier.failed();134
- }129
+ self.import_for(&dmabuf, notifier);135130
}136131
}137132
@@ -300,6 +295,9 @@ fn hold_until_readable(state: &mut State, surface: &WlSurface) {
300295
let (Some(dmabuf), Some(client)) = (dmabuf, surface.client()) else {301296
return;302297
};298
+ if let Some(blocker) = state.import_blocker(&dmabuf, client.clone()) {299
+ add_blocker(surface, blocker);300
+ }303301
if let Some(point) = acquire304302
&& let Ok((blocker, source)) = point.generate_blocker()305303
{@@ -0,0 +1,133 @@
1
+//! Dmabuf imports on a worker thread. A first import takes 15 to 27 ms on2
+//! NVIDIA, which on the main thread drops frames. Buffer creation answers3
+//! once the import is done, and commits of buffers still importing wait on4
+//! a blocker.5
+6
+use crate::state::State;7
+use monolith_render_vk::{DmabufImporter, VulkanTexture};8
+use smithay::backend::allocator::dmabuf::{Dmabuf, WeakDmabuf};9
+use smithay::reexports::calloop::LoopHandle;10
+use smithay::reexports::calloop::channel::{Event, channel};11
+use smithay::reexports::wayland_server::Client;12
+use smithay::wayland::compositor::{Blocker, BlockerState, CompositorHandler};13
+use smithay::wayland::dmabuf::ImportNotifier;14
+use std::collections::HashMap;15
+use std::sync::Arc;16
+use std::sync::atomic::{AtomicBool, Ordering};17
+use std::sync::mpsc;18
+19
+pub struct Imports {20
+ jobs: mpsc::Sender<Dmabuf>,21
+ waiting: HashMap<WeakDmabuf, Waiters>,22
+}23
+24
+#[derive(Default)]25
+struct Waiters {26
+ done: Arc<AtomicBool>,27
+ notifiers: Vec<ImportNotifier>,28
+ clients: Vec<Client>,29
+}30
+31
+pub struct ImportBlocker(Arc<AtomicBool>);32
+33
+impl Blocker for ImportBlocker {34
+ fn state(&self) -> BlockerState {35
+ if self.0.load(Ordering::Acquire) {36
+ BlockerState::Released37
+ } else {38
+ BlockerState::Pending39
+ }40
+ }41
+}42
+43
+type Done = (Dmabuf, Result<VulkanTexture, String>);44
+45
+impl Imports {46
+ pub fn new(47
+ handle: &LoopHandle<'static, State>,48
+ importer: DmabufImporter,49
+ ) -> std::io::Result<Self> {50
+ let (done, results) = channel::<Done>();51
+ let (jobs, queue) = mpsc::channel::<Dmabuf>();52
+ std::thread::Builder::new()53
+ .name("dmabuf-import".into())54
+ .spawn(move || {55
+ for buffer in queue {56
+ let result = importer.import(&buffer).map_err(|e| e.to_string());57
+ if done.send((buffer, result)).is_err() {58
+ break;59
+ }60
+ }61
+ })?;62
+ handle63
+ .insert_source(results, |event, _, state| {64
+ if let Event::Msg((buffer, result)) = event {65
+ state.imported(buffer, result);66
+ }67
+ })68
+ .map_err(|e| std::io::Error::other(e.to_string()))?;69
+ Ok(Self {70
+ jobs,71
+ waiting: HashMap::new(),72
+ })73
+ }74
+75
+ fn start(&mut self, buffer: &Dmabuf) -> &mut Waiters {76
+ let jobs = &self.jobs;77
+ self.waiting.entry(buffer.weak()).or_insert_with(|| {78
+ let _ = jobs.send(buffer.clone());79
+ Waiters::default()80
+ })81
+ }82
+}83
+84
+impl State {85
+ /// Answers a client's buffer creation once its dmabuf is imported.86
+ pub fn import_for(&mut self, buffer: &Dmabuf, notifier: ImportNotifier) {87
+ if self.renderer.has_dmabuf(buffer) {88
+ let _ = notifier.successful::<State>();89
+ return;90
+ }91
+ self.imports.start(buffer).notifiers.push(notifier);92
+ }93
+94
+ /// A blocker for a commit whose dmabuf the renderer has not imported yet.95
+ pub fn import_blocker(&mut self, buffer: &Dmabuf, client: Client) -> Option<ImportBlocker> {96
+ if self.renderer.has_dmabuf(buffer) {97
+ return None;98
+ }99
+ let waiters = self.imports.start(buffer);100
+ waiters.clients.push(client);101
+ Some(ImportBlocker(waiters.done.clone()))102
+ }103
+104
+ fn imported(&mut self, buffer: Dmabuf, result: Result<VulkanTexture, String>) {105
+ let Some(waiters) = self.imports.waiting.remove(&buffer.weak()) else {106
+ return;107
+ };108
+ let result = result.and_then(|t| {109
+ self.renderer110
+ .adopt_dmabuf(&buffer, t)111
+ .map_err(|e| e.to_string())112
+ });113
+ match result {114
+ Ok(()) => {115
+ for n in waiters.notifiers {116
+ let _ = n.successful::<State>();117
+ }118
+ }119
+ Err(e) => {120
+ tracing::warn!("dmabuf import failed: {e}");121
+ for n in waiters.notifiers {122
+ n.failed();123
+ }124
+ }125
+ }126
+ waiters.done.store(true, Ordering::Release);127
+ let dh = self.display.clone();128
+ for client in waiters.clients {129
+ self.client_compositor_state(&client)130
+ .blocker_cleared(self, &dh);131
+ }132
+ }133
+}@@ -7,6 +7,7 @@ mod drm;
77
mod focus;88
mod handlers;99
mod idle;10
+mod import;1011
mod input;1112
mod ipc;1213
mod launch;@@ -97,6 +97,7 @@ pub struct State {
9797
pub xwayland: Option<crate::xwayland::Xwayland>,9898
pub xwayland_shell_state: XWaylandShellState,9999
pub captures: crate::capture::Captures,100
+ pub imports: crate::import::Imports,100101
/// The render node clients get dmabufs for, known on bare metal.101102
pub render_node: Option<smithay::backend::drm::DrmNode>,102103
pub lock: crate::lock::Lock,@@ -177,6 +178,7 @@ impl State {
177178
let binds = Binds::new(&config);178179
let animation = config.animation.clone();179180
let primary_selection_state = PrimarySelectionState::new::<State>(&dh);181
+ let imports = crate::import::Imports::new(&handle, renderer.dmabuf_importer())?;180182
Ok(Self {181183
start: Instant::now(),182184
socket_name,@@ -214,6 +216,7 @@ impl State {
214216
xwayland: None,215217
xwayland_shell_state: XWaylandShellState::new::<State>(&dh),216218
captures: Default::default(),219
+ imports,217220
render_node: None,218221
lock: Default::default(),219222
inhibitors: HashSet::new(),@@ -18,4 +18,4 @@ pub use dmabuf::VulkanAllocator;
1818
pub use error::Error;1919
pub use frame::{Effect, Transition, VulkanFrame};2020
pub use image::{VulkanImage, VulkanMapping, VulkanTarget, VulkanTexture};21
-pub use renderer::VulkanRenderer;21
+pub use renderer::{DmabufImporter, VulkanRenderer};@@ -975,6 +975,39 @@ impl ImportDma for VulkanRenderer {
975975
976976
impl ImportDmaWl for VulkanRenderer {}977977
978
+/// Imports dmabufs on another thread. The textures it makes belong to the979
+/// renderer it came from, which takes them back with `adopt_dmabuf`.980
+#[derive(Clone)]981
+pub struct DmabufImporter {982
+ shared: Arc<Shared>,983
+ caps: Arc<DmabufCaps>,984
+}985
+986
+impl DmabufImporter {987
+ pub fn import(&self, buffer: &Dmabuf) -> Result<VulkanTexture, Error> {988
+ dmabuf::import(&self.shared, &self.caps, buffer, SAMPLE_USAGE).map(VulkanTexture)989
+ }990
+}991
+992
+impl VulkanRenderer {993
+ pub fn dmabuf_importer(&self) -> DmabufImporter {994
+ DmabufImporter {995
+ shared: self.shared.clone(),996
+ caps: self.caps.clone(),997
+ }998
+ }999
+1000
+ pub fn has_dmabuf(&self, buffer: &Dmabuf) -> bool {1001
+ self.dmabuf_textures.contains_key(&buffer.weak())1002
+ }1003
+1004
+ pub fn adopt_dmabuf(&mut self, buffer: &Dmabuf, texture: VulkanTexture) -> Result<(), Error> {1005
+ self.own(&texture.0)?;1006
+ self.dmabuf_textures.insert(buffer.weak(), texture);1007
+ Ok(())1008
+ }1009
+}1010
+9781011
impl ExportMem for VulkanRenderer {9791012
type TextureMapping = VulkanMapping;