atagen

monolith

public
Star0
Watch0
8ffa3fa4d4a9331f2638c1598a9f3c107e148296
1
ssh://brewery@pls.lobotomise.me:33/atagen/monolith.git
crates/monolith/src/commit.rsRust225 lines7.8 KB
1
use crate::state::{ClientState, State};
2
use smithay::backend::allocator::dmabuf::Dmabuf;
3
use smithay::backend::renderer::utils::{import_surface_tree, on_commit_buffer_handler};
4
use smithay::desktop::PopupKind;
5
use smithay::reexports::calloop::Interest;
6
use smithay::reexports::wayland_server::protocol::wl_buffer::WlBuffer;
7
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
8
use smithay::reexports::wayland_server::{Client, Resource};
9
use smithay::utils::{Rectangle, Transform};
10
use smithay::wayland::buffer::BufferHandler;
11
use smithay::wayland::compositor::{
12
    BufferAssignment, CompositorClientState, CompositorHandler, CompositorState, Damage,
13
    SurfaceAttributes, add_blocker, add_pre_commit_hook, get_parent, is_sync_subsurface,
14
    with_states,
15
};
16
use smithay::wayland::dmabuf::{
17
    DmabufGlobal, DmabufHandler, DmabufState, ImportNotifier, get_dmabuf,
18
};
19
use smithay::wayland::drm_syncobj::{DrmSyncobjCachedState, DrmSyncobjHandler, DrmSyncobjState};
20
use smithay::wayland::shm::{ShmHandler, ShmState, with_buffer_contents};
21
22
impl CompositorHandler for State {
23
    fn compositor_state(&mut self) -> &mut CompositorState {
24
        &mut self.protocols.compositor_state
25
    }
26
27
    fn new_surface(&mut self, surface: &WlSurface) {
28
        add_pre_commit_hook::<Self, _>(surface, |state, _dh, surface| {
29
            hold_until_readable(state, surface);
30
        });
31
    }
32
33
    fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
34
        if let Some(x) = client.get_data::<smithay::xwayland::XWaylandClientData>() {
35
            return &x.compositor_state;
36
        }
37
        &client
38
            .get_data::<ClientState>()
39
            .expect("every client has ClientState")
40
            .compositor_state
41
    }
42
43
    fn commit(&mut self, surface: &WlSurface) {
44
        on_commit_buffer_handler::<Self>(surface);
45
        // Importing at commit keeps first-time dmabuf imports, which take
46
        // milliseconds on NVIDIA, out of the render racing the next vblank.
47
        if let Err(e) = import_surface_tree(&mut self.renderer, surface) {
48
            tracing::debug!("early import failed: {e}");
49
        }
50
        if !is_sync_subsurface(surface) {
51
            let mut root = surface.clone();
52
            while let Some(parent) = get_parent(&root) {
53
                root = parent;
54
            }
55
            let mapped = self.wm.id_of(&root).and_then(|id| self.wm.window(id));
56
            let window = mapped.or_else(|| {
57
                self.wm
58
                    .unmapped
59
                    .iter()
60
                    .find(|w| crate::wm::root(w).as_ref() == Some(&root))
61
            });
62
            if let Some(window) = window {
63
                window.on_commit();
64
            }
65
            self.count_commit(&root);
66
            if let Some(id) = self.window_id(&root) {
67
                self.refresh_identity(id);
68
                if self.services.captures.wants_damage() {
69
                    self.services.captures.window_damaged(id);
70
                }
71
            }
72
        }
73
        if let Some(i) = self
74
            .wm
75
            .unmapped
76
            .iter()
77
            .position(|w| w.toplevel().is_some_and(|t| t.wl_surface() == surface))
78
        {
79
            let window = self.wm.unmapped.remove(i);
80
            let toplevel = window.toplevel().cloned();
81
            self.map_window(window, None);
82
            if let Some(t) = toplevel {
83
                t.send_configure();
84
            }
85
        }
86
        self.on_buffer(surface);
87
        self.wm.popups.commit(surface);
88
        if let Some(PopupKind::Xdg(popup)) = self.wm.popups.find_popup(surface)
89
            && !popup.is_initial_configure_sent()
90
        {
91
            let _ = popup.send_configure();
92
        }
93
        self.layer_commit(surface);
94
        self.dirty.redraw = true;
95
    }
96
}
97
98
impl BufferHandler for State {
99
    fn buffer_destroyed(&mut self, _buffer: &WlBuffer) {}
100
}
101
102
impl ShmHandler for State {
103
    fn shm_state(&self) -> &ShmState {
104
        &self.protocols.shm_state
105
    }
106
}
107
108
impl DmabufHandler for State {
109
    fn dmabuf_state(&mut self) -> &mut DmabufState {
110
        &mut self.protocols.dmabuf_state
111
    }
112
113
    fn dmabuf_imported(
114
        &mut self,
115
        _global: &DmabufGlobal,
116
        dmabuf: Dmabuf,
117
        notifier: ImportNotifier,
118
    ) {
119
        self.import_for(&dmabuf, notifier);
120
    }
121
}
122
123
impl DrmSyncobjHandler for State {
124
    fn drm_syncobj_state(&mut self) -> Option<&mut DrmSyncobjState> {
125
        self.backend.syncobj_mut()
126
    }
127
}
128
129
/// Holds a dmabuf commit until the buffer can be read without the renderer
130
/// stalling on the client's GPU work: until its explicit acquire point
131
/// signals, or for implicitly synced buffers until their writes finish.
132
fn hold_until_readable(state: &mut State, surface: &WlSurface) {
133
    stage_shm(state, surface);
134
    let (acquire, dmabuf) = with_states(surface, |s| {
135
        let acquire = s
136
            .cached_state
137
            .get::<DrmSyncobjCachedState>()
138
            .pending()
139
            .acquire_point
140
            .clone();
141
        let dmabuf = match s
142
            .cached_state
143
            .get::<SurfaceAttributes>()
144
            .pending()
145
            .buffer
146
            .as_ref()
147
        {
148
            Some(BufferAssignment::NewBuffer(buffer)) => get_dmabuf(buffer).cloned().ok(),
149
            _ => None,
150
        };
151
        (acquire, dmabuf)
152
    });
153
    let (Some(dmabuf), Some(client)) = (dmabuf, surface.client()) else {
154
        return;
155
    };
156
    if let Some(blocker) = state.import_blocker(&dmabuf, client.clone()) {
157
        add_blocker(surface, blocker);
158
    }
159
    if let Some(point) = acquire
160
        && let Ok((blocker, source)) = point.generate_blocker()
161
    {
162
        let inserted = state.core.handle.insert_source(source, move |_, _, state| {
163
            let dh = state.core.display.clone();
164
            state
165
                .client_compositor_state(&client)
166
                .blocker_cleared(state, &dh);
167
            Ok(())
168
        });
169
        if inserted.is_ok() {
170
            add_blocker(surface, blocker);
171
        }
172
        return;
173
    }
174
    if let Ok((blocker, source)) = dmabuf.generate_blocker(Interest::READ) {
175
        let inserted = state.core.handle.insert_source(source, move |_, _, state| {
176
            let dh = state.core.display.clone();
177
            state
178
                .client_compositor_state(&client)
179
                .blocker_cleared(state, &dh);
180
            Ok(())
181
        });
182
        if inserted.is_ok() {
183
            add_blocker(surface, blocker);
184
        }
185
    }
186
}
187
188
/// Copies a committed shm buffer's damage on the worker, holding the commit
189
/// until it is staged. Surface damage equals buffer damage at scale 1 with no
190
/// transform, otherwise the whole buffer is staged so it covers whatever the
191
/// import uploads.
192
fn stage_shm(state: &mut State, surface: &WlSurface) {
193
    let job = with_states(surface, |s| {
194
        let mut cached = s.cached_state.get::<SurfaceAttributes>();
195
        let attrs = cached.pending();
196
        let Some(BufferAssignment::NewBuffer(buffer)) = attrs.buffer.as_ref() else {
197
            return None;
198
        };
199
        if get_dmabuf(buffer).is_ok() {
200
            return None;
201
        }
202
        let plain = attrs.buffer_scale == 1 && attrs.buffer_transform == Transform::Normal.into();
203
        let region = attrs
204
            .damage
205
            .iter()
206
            .map(|d| match d {
207
                Damage::Buffer(r) => *r,
208
                Damage::Surface(r) => {
209
                    Rectangle::new((r.loc.x, r.loc.y).into(), (r.size.w, r.size.h).into())
210
                }
211
            })
212
            .reduce(|a, b| a.merge(b))
213
            .filter(|_| plain);
214
        Some((buffer.clone(), region))
215
    });
216
    let (Some((buffer, region)), Some(client)) = (job, surface.client()) else {
217
        return;
218
    };
219
    let Ok(size) = with_buffer_contents(&buffer, |_, _, d| (d.width, d.height)) else {
220
        return;
221
    };
222
    if let Some(blocker) = state.stage_blocker(&buffer, region, size, client) {
223
        add_blocker(surface, blocker);
224
    }
225
}