|
| 1 | +use std::{ |
| 2 | + cell::RefCell, |
| 3 | + os::unix::io::{AsFd, OwnedFd}, |
| 4 | + sync::Arc, |
| 5 | +}; |
| 6 | +use wayland_protocols::wp::linux_drm_syncobj::v1::server::{ |
| 7 | + wp_linux_drm_syncobj_manager_v1::{self, WpLinuxDrmSyncobjManagerV1}, |
| 8 | + wp_linux_drm_syncobj_surface_v1::{self, WpLinuxDrmSyncobjSurfaceV1}, |
| 9 | + wp_linux_drm_syncobj_timeline_v1::{self, WpLinuxDrmSyncobjTimelineV1}, |
| 10 | +}; |
| 11 | +use wayland_server::{ |
| 12 | + protocol::wl_surface::WlSurface, Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, |
| 13 | + Resource, Weak, |
| 14 | +}; |
| 15 | + |
| 16 | +use super::compositor::{self, with_states, BufferAssignment, Cacheable, SurfaceAttributes}; |
| 17 | + |
| 18 | +mod sync_point; |
| 19 | +pub use sync_point::{SyncPoint, Timeline}; |
| 20 | + |
| 21 | +#[derive(Default)] |
| 22 | +struct DrmSyncobjCachedState { |
| 23 | + acquire_point: Option<SyncPoint>, |
| 24 | + release_point: Option<SyncPoint>, |
| 25 | +} |
| 26 | + |
| 27 | +impl Cacheable for DrmSyncobjCachedState { |
| 28 | + fn commit(&mut self, _dh: &DisplayHandle) -> Self { |
| 29 | + Self { |
| 30 | + acquire_point: None, |
| 31 | + release_point: None, |
| 32 | + } |
| 33 | + } |
| 34 | + |
| 35 | + fn merge_into(self, into: &mut Self, _dh: &DisplayHandle) { |
| 36 | + if self.acquire_point.is_some() && self.release_point.is_some() { |
| 37 | + into.acquire_point = self.acquire_point; |
| 38 | + into.release_point = self.release_point; |
| 39 | + } else { |
| 40 | + into.acquire_point = None; |
| 41 | + into.release_point = None; |
| 42 | + } |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +pub struct DrmSyncobjState {} |
| 47 | + |
| 48 | +impl DrmSyncobjState { |
| 49 | + pub fn new<D: 'static>(display: &DisplayHandle) -> Self |
| 50 | + where |
| 51 | + D: GlobalDispatch<WpLinuxDrmSyncobjManagerV1, ()>, |
| 52 | + { |
| 53 | + display.create_global::<D, WpLinuxDrmSyncobjManagerV1, _>(1, ()); |
| 54 | + Self {} |
| 55 | + } |
| 56 | + |
| 57 | + // TODO new_with_filter |
| 58 | +} |
| 59 | + |
| 60 | +impl<D> GlobalDispatch<WpLinuxDrmSyncobjManagerV1, (), D> for DrmSyncobjState |
| 61 | +where |
| 62 | + D: Dispatch<WpLinuxDrmSyncobjManagerV1, ()>, |
| 63 | +{ |
| 64 | + fn bind( |
| 65 | + state: &mut D, |
| 66 | + dh: &DisplayHandle, |
| 67 | + client: &Client, |
| 68 | + resource: New<WpLinuxDrmSyncobjManagerV1>, |
| 69 | + _global_data: &(), |
| 70 | + data_init: &mut DataInit<'_, D>, |
| 71 | + ) { |
| 72 | + data_init.init::<_, _>(resource, ()); |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +fn commit_hook<D>(_: &mut D, _dh: &DisplayHandle, surface: &WlSurface) { |
| 77 | + compositor::with_states(&surface, |states| { |
| 78 | + let cached = &states.cached_state; |
| 79 | + let has_new_buffer = matches!( |
| 80 | + cached.pending::<SurfaceAttributes>().buffer, |
| 81 | + Some(BufferAssignment::NewBuffer(_)) |
| 82 | + ); |
| 83 | + // TODO what if syncobj surface is destroyed? |
| 84 | + if let Some(data) = states |
| 85 | + .data_map |
| 86 | + .get::<RefCell<Option<WpLinuxDrmSyncobjSurfaceV1>>>() |
| 87 | + { |
| 88 | + if let Some(syncobj_surface) = data.borrow().as_ref() { |
| 89 | + let pending = cached.pending::<DrmSyncobjCachedState>(); |
| 90 | + if pending.acquire_point.is_some() && !has_new_buffer { |
| 91 | + syncobj_surface.post_error( |
| 92 | + wp_linux_drm_syncobj_surface_v1::Error::NoBuffer as u32, |
| 93 | + "acquire point without buffer".to_string(), |
| 94 | + ); |
| 95 | + } else if pending.acquire_point.is_some() && pending.release_point.is_none() { |
| 96 | + syncobj_surface.post_error( |
| 97 | + wp_linux_drm_syncobj_surface_v1::Error::NoReleasePoint as u32, |
| 98 | + "acquire point without release point".to_string(), |
| 99 | + ); |
| 100 | + } else if pending.acquire_point.is_none() && pending.release_point.is_some() { |
| 101 | + syncobj_surface.post_error( |
| 102 | + wp_linux_drm_syncobj_surface_v1::Error::NoAcquirePoint as u32, |
| 103 | + "release point without acquire point".to_string(), |
| 104 | + ); |
| 105 | + } else if let (Some(acquire), Some(release)) = |
| 106 | + (pending.acquire_point.as_ref(), pending.release_point.as_ref()) |
| 107 | + { |
| 108 | + if acquire.timeline == release.timeline && acquire.point <= release.point { |
| 109 | + syncobj_surface.post_error( |
| 110 | + wp_linux_drm_syncobj_surface_v1::Error::ConflictingPoints as u32, |
| 111 | + format!( |
| 112 | + "release point '{}' is not greater than acquire point '{}'", |
| 113 | + release.point, acquire.point |
| 114 | + ), |
| 115 | + ); |
| 116 | + } |
| 117 | + } |
| 118 | + // TODO unsupported buffer error |
| 119 | + } |
| 120 | + } |
| 121 | + }); |
| 122 | +} |
| 123 | + |
| 124 | +impl<D> Dispatch<WpLinuxDrmSyncobjManagerV1, (), D> for DrmSyncobjState |
| 125 | +where |
| 126 | + D: Dispatch<WpLinuxDrmSyncobjSurfaceV1, DrmSyncobjSurfaceData>, |
| 127 | + D: Dispatch<WpLinuxDrmSyncobjTimelineV1, DrmSyncobjTimelineData>, |
| 128 | +{ |
| 129 | + fn request( |
| 130 | + state: &mut D, |
| 131 | + _client: &Client, |
| 132 | + resource: &WpLinuxDrmSyncobjManagerV1, |
| 133 | + request: wp_linux_drm_syncobj_manager_v1::Request, |
| 134 | + _data: &(), |
| 135 | + _dh: &DisplayHandle, |
| 136 | + data_init: &mut DataInit<'_, D>, |
| 137 | + ) { |
| 138 | + match request { |
| 139 | + wp_linux_drm_syncobj_manager_v1::Request::GetSurface { id, surface } => { |
| 140 | + let already_exists = with_states(&surface, |states| { |
| 141 | + states |
| 142 | + .data_map |
| 143 | + .get::<RefCell<Option<WpLinuxDrmSyncobjSurfaceV1>>>() |
| 144 | + .map(|v| v.borrow().is_some()) |
| 145 | + .unwrap_or(false) |
| 146 | + }); |
| 147 | + if already_exists { |
| 148 | + resource.post_error( |
| 149 | + wp_linux_drm_syncobj_manager_v1::Error::SurfaceExists as u32, |
| 150 | + "the surface already has a syncobj_surface object associated".to_string(), |
| 151 | + ); |
| 152 | + return; |
| 153 | + } |
| 154 | + let syncobj_surface = data_init.init::<_, _>( |
| 155 | + id, |
| 156 | + DrmSyncobjSurfaceData { |
| 157 | + surface: surface.downgrade(), |
| 158 | + }, |
| 159 | + ); |
| 160 | + with_states(&surface, |states| { |
| 161 | + states |
| 162 | + .data_map |
| 163 | + .insert_if_missing(|| RefCell::new(Some(syncobj_surface))) |
| 164 | + }); |
| 165 | + compositor::add_pre_commit_hook::<D, _>(&surface, commit_hook); |
| 166 | + } |
| 167 | + wp_linux_drm_syncobj_manager_v1::Request::ImportTimeline { id, fd } => { |
| 168 | + match Timeline::new(todo!(), fd.as_fd()) { |
| 169 | + Ok(timeline) => { |
| 170 | + data_init.init::<_, _>(id, DrmSyncobjTimelineData { timeline }); |
| 171 | + } |
| 172 | + Err(err) => {} |
| 173 | + } |
| 174 | + // TODO import, protocol error if it fails? On which GPU? |
| 175 | + } |
| 176 | + wp_linux_drm_syncobj_manager_v1::Request::Destroy => {} |
| 177 | + _ => unreachable!(), |
| 178 | + } |
| 179 | + } |
| 180 | +} |
| 181 | + |
| 182 | +pub struct DrmSyncobjSurfaceData { |
| 183 | + surface: Weak<WlSurface>, |
| 184 | +} |
| 185 | + |
| 186 | +impl<D> Dispatch<WpLinuxDrmSyncobjSurfaceV1, DrmSyncobjSurfaceData, D> for DrmSyncobjState { |
| 187 | + fn request( |
| 188 | + state: &mut D, |
| 189 | + _client: &Client, |
| 190 | + _resource: &WpLinuxDrmSyncobjSurfaceV1, |
| 191 | + request: wp_linux_drm_syncobj_surface_v1::Request, |
| 192 | + data: &DrmSyncobjSurfaceData, |
| 193 | + _dh: &DisplayHandle, |
| 194 | + data_init: &mut DataInit<'_, D>, |
| 195 | + ) { |
| 196 | + let Ok(surface) = data.surface.upgrade() else { |
| 197 | + return; |
| 198 | + }; |
| 199 | + match request { |
| 200 | + wp_linux_drm_syncobj_surface_v1::Request::Destroy => { |
| 201 | + // TODO |
| 202 | + } |
| 203 | + wp_linux_drm_syncobj_surface_v1::Request::SetAcquirePoint { |
| 204 | + timeline, |
| 205 | + point_hi, |
| 206 | + point_lo, |
| 207 | + } => { |
| 208 | + let sync_point = SyncPoint { |
| 209 | + timeline: timeline |
| 210 | + .data::<DrmSyncobjTimelineData>() |
| 211 | + .unwrap() |
| 212 | + .timeline |
| 213 | + .clone(), |
| 214 | + point: ((point_hi as u64) << 32) + (point_lo as u64), |
| 215 | + }; |
| 216 | + with_states(&surface, |states| { |
| 217 | + let mut cached_state = states.cached_state.pending::<DrmSyncobjCachedState>(); |
| 218 | + cached_state.acquire_point = Some(sync_point); |
| 219 | + }); |
| 220 | + } |
| 221 | + wp_linux_drm_syncobj_surface_v1::Request::SetReleasePoint { |
| 222 | + timeline, |
| 223 | + point_hi, |
| 224 | + point_lo, |
| 225 | + } => { |
| 226 | + let sync_point = SyncPoint { |
| 227 | + timeline: timeline |
| 228 | + .data::<DrmSyncobjTimelineData>() |
| 229 | + .unwrap() |
| 230 | + .timeline |
| 231 | + .clone(), |
| 232 | + point: ((point_hi as u64) << 32) + (point_lo as u64), |
| 233 | + }; |
| 234 | + with_states(&surface, |states| { |
| 235 | + let mut cached_state = states.cached_state.pending::<DrmSyncobjCachedState>(); |
| 236 | + cached_state.release_point = Some(sync_point); |
| 237 | + }); |
| 238 | + } |
| 239 | + _ => unreachable!(), |
| 240 | + } |
| 241 | + } |
| 242 | +} |
| 243 | + |
| 244 | +pub struct DrmSyncobjTimelineData { |
| 245 | + timeline: Timeline, |
| 246 | +} |
| 247 | + |
| 248 | +impl<D> Dispatch<WpLinuxDrmSyncobjTimelineV1, DrmSyncobjTimelineData, D> for DrmSyncobjState { |
| 249 | + fn request( |
| 250 | + state: &mut D, |
| 251 | + _client: &Client, |
| 252 | + _resource: &WpLinuxDrmSyncobjTimelineV1, |
| 253 | + request: wp_linux_drm_syncobj_timeline_v1::Request, |
| 254 | + _data: &DrmSyncobjTimelineData, |
| 255 | + _dh: &DisplayHandle, |
| 256 | + data_init: &mut DataInit<'_, D>, |
| 257 | + ) { |
| 258 | + match request { |
| 259 | + wp_linux_drm_syncobj_timeline_v1::Request::Destroy => {} |
| 260 | + _ => unreachable!(), |
| 261 | + } |
| 262 | + } |
| 263 | +} |
| 264 | + |
| 265 | +#[macro_export] |
| 266 | +macro_rules! delegate_drm_syncobj { |
| 267 | + ($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => { |
| 268 | + $crate::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ |
| 269 | + $crate::reexports::wayland_protocols::wp::linux_drm_syncobj::v1::server::wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1: () |
| 270 | + ] => $crate::wayland::drm_syncobj::DrmSyncobjState); |
| 271 | + $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ |
| 272 | + $crate::reexports::wayland_protocols::wp::linux_drm_syncobj::v1::server::wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1: () |
| 273 | + ] => $crate::wayland::drm_syncobj::DrmSyncobjState); |
| 274 | + $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ |
| 275 | + $crate::reexports::wayland_protocols::wp::linux_drm_syncobj::v1::server::wp_linux_drm_syncobj_surface_v1::WpLinuxDrmSyncobjSurfaceV1: $crate::wayland::drm_syncobj::DrmSyncobjSurfaceData |
| 276 | + ] => $crate::wayland::drm_syncobj::DrmSyncobjState); |
| 277 | + $crate::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [ |
| 278 | + $crate::reexports::wayland_protocols::wp::linux_drm_syncobj::v1::server::wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1: $crate::wayland::drm_syncobj::DrmSyncobjTimelineData |
| 279 | + ] => $crate::wayland::drm_syncobj::DrmSyncobjState); |
| 280 | + } |
| 281 | +} |
0 commit comments