1use anyhow::Context;
6use display_types::IMAGE_TILING_TYPE_LINEAR;
7
8use fidl::endpoints::ClientEnd;
9use fidl_fuchsia_hardware_display::{
10 self as display, CoordinatorListenerRequest, LayerId as FidlLayerId,
11};
12use fidl_fuchsia_hardware_display_types::{self as display_types};
13use fuchsia_async::{DurationExt as _, TimeoutExt as _};
14use fuchsia_component::client::Service;
15use fuchsia_sync::RwLock;
16use futures::channel::mpsc;
17use futures::{TryFutureExt, TryStreamExt, future};
18use std::fmt;
19use std::sync::Arc;
20use zx::{self as zx, HandleBased};
21
22use crate::INVALID_EVENT_ID;
23use crate::config::{DisplayConfig, LayerConfig};
24use crate::error::{ConfigError, Error, Result};
25use crate::types::{BufferCollectionId, DisplayId, DisplayInfo, Event, EventId, ImageId, LayerId};
26
27const TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(2);
28
29#[derive(Clone)]
32pub struct Coordinator {
33 inner: Arc<RwLock<CoordinatorInner>>,
34}
35
36struct CoordinatorInner {
37 displays: Vec<DisplayInfo>,
38 proxy: display::CoordinatorProxy,
39 listener_requests: Option<display::CoordinatorListenerRequestStream>,
40
41 vsync_listeners: Vec<(mpsc::UnboundedSender<VsyncEvent>, Option<DisplayId>)>,
43
44 id_counter: u64,
46
47 stamp_counter: u64,
49}
50
51#[derive(Debug)]
53pub struct VsyncEvent {
54 pub id: DisplayId,
56
57 pub timestamp: zx::MonotonicInstant,
59
60 pub config: display::ConfigStamp,
62}
63
64impl Coordinator {
65 pub async fn init() -> Result<Coordinator> {
83 let service_proxy = Service::open(display::ServiceMarker)
84 .context("failed to open display Service")
85 .map_err(Error::DeviceConnectionError)?
86 .watch_for_any()
87 .map_err(Error::DeviceConnectionError)
88 .on_timeout(TIMEOUT.after_now(), || Err(Error::DeviceNotFound))
89 .await?;
90
91 let provider_proxy = service_proxy
92 .connect_to_provider()
93 .context("failed to connect to FIDL provider")
94 .map_err(|x| Error::DeviceConnectionError(x.into()))?;
95
96 let (coordinator_proxy, coordinator_server_end) =
97 fidl::endpoints::create_proxy::<display::CoordinatorMarker>();
98 let (coordinator_listener_client_end, coordinator_listener_requests) =
99 fidl::endpoints::create_request_stream::<display::CoordinatorListenerMarker>();
100
101 let payload = display::ProviderOpenCoordinatorWithListenerForPrimaryRequest {
104 coordinator: Some(coordinator_server_end),
105 coordinator_listener: Some(coordinator_listener_client_end),
106 __source_breaking: fidl::marker::SourceBreaking,
107 };
108 let () = provider_proxy
109 .open_coordinator_with_listener_for_primary(payload)
110 .await?
111 .map_err(zx::Status::from_raw)?;
112
113 Self::init_with_proxy_and_listener_requests(
114 coordinator_proxy,
115 coordinator_listener_requests,
116 )
117 .await
118 }
119
120 pub async fn init_with_proxy_and_listener_requests(
130 coordinator_proxy: display::CoordinatorProxy,
131 mut listener_requests: display::CoordinatorListenerRequestStream,
132 ) -> Result<Coordinator> {
133 let displays = wait_for_initial_displays(&mut listener_requests)
134 .on_timeout(TIMEOUT.after_now(), || Err(Error::NoDisplays))
135 .await?
136 .into_iter()
137 .map(DisplayInfo)
138 .collect::<Vec<_>>();
139 Ok(Coordinator {
140 inner: Arc::new(RwLock::new(CoordinatorInner {
141 proxy: coordinator_proxy,
142 listener_requests: Some(listener_requests),
143 displays,
144 vsync_listeners: Vec::new(),
145 id_counter: 0,
146 stamp_counter: 0,
147 })),
148 })
149 }
150
151 pub fn displays(&self) -> Vec<DisplayInfo> {
153 self.inner.read().displays.clone()
154 }
155
156 pub fn proxy(&self) -> display::CoordinatorProxy {
161 self.inner.read().proxy.clone()
162 }
163
164 pub fn add_vsync_listener(
166 &self,
167 id: Option<DisplayId>,
168 ) -> Result<mpsc::UnboundedReceiver<VsyncEvent>> {
169 let (sender, receiver) = mpsc::unbounded::<VsyncEvent>();
171 self.inner.write().vsync_listeners.push((sender, id));
172 Ok(receiver)
173 }
174
175 pub async fn handle_events(&self) -> Result<()> {
181 let inner = self.inner.clone();
182 let mut events = inner.write().listener_requests.take().ok_or(Error::AlreadyRequested)?;
183 while let Some(msg) = events.try_next().await? {
184 match msg {
185 CoordinatorListenerRequest::OnDisplaysChanged {
186 added,
187 removed,
188 control_handle: _,
189 } => {
190 let removed =
191 removed.into_iter().map(|id| id.into()).collect::<Vec<DisplayId>>();
192 inner.read().handle_displays_changed(added, removed);
193 }
194 CoordinatorListenerRequest::OnVsync {
195 display_id,
196 timestamp,
197 applied_config_stamp,
198 cookie,
199 control_handle: _,
200 } => {
201 inner.write().handle_vsync(
202 display_id.into(),
203 timestamp,
204 applied_config_stamp,
205 cookie,
206 )?;
207 }
208 _ => continue,
209 }
210 }
211 Ok(())
212 }
213
214 pub async fn create_layer(&self) -> Result<LayerId> {
217 Ok(self.proxy().create_layer().await?.map_err(zx::Status::from_raw)?.into())
218 }
219
220 pub fn create_event(&self) -> Result<Event> {
223 let event = zx::Event::create();
224 let remote = event.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
225 let id = self.inner.write().next_free_event_id()?;
226
227 self.inner.read().proxy.import_event(zx::Event::from(remote), &id.into())?;
228 Ok(Event::new(id, event))
229 }
230
231 pub async fn apply_config(
234 &self,
235 configs: &[DisplayConfig],
236 ) -> std::result::Result<u64, ConfigError> {
237 let proxy = self.proxy();
238 for config in configs {
239 proxy.set_display_layers(
240 &config.id.into(),
241 &config.layers.iter().map(|l| l.id.into()).collect::<Vec<FidlLayerId>>(),
242 )?;
243 for layer in &config.layers {
244 match &layer.config {
245 LayerConfig::Color { color, display_destination } => {
246 let fidl_color = fidl_fuchsia_hardware_display_types::Color::from(color);
247 proxy.set_layer_color_config(
248 &layer.id.into(),
249 &fidl_color,
250 display_destination,
251 )?;
252 }
253 LayerConfig::Primary { image_id, image_metadata, unblock_event, alpha } => {
254 proxy.set_layer_primary_config(&layer.id.into(), &image_metadata)?;
255 if let Some(alpha_config) = alpha {
256 proxy.set_layer_primary_alpha(
257 &layer.id.into(),
258 alpha_config.mode,
259 alpha_config.val,
260 )?;
261 }
262 proxy.set_layer_image2(
263 &layer.id.into(),
264 &(*image_id).into(),
265 &unblock_event.unwrap_or(INVALID_EVENT_ID).into(),
266 )?;
267 }
268 }
269 }
270 }
271
272 let result = proxy.check_config().await?;
273 if result != display_types::ConfigResult::Ok {
274 return Err(ConfigError::invalid(result));
275 }
276
277 let config_stamp = self.inner.write().next_config_stamp().unwrap();
278 let payload = fidl_fuchsia_hardware_display::CoordinatorApplyConfig3Request {
279 stamp: Some(fidl_fuchsia_hardware_display::ConfigStamp { value: config_stamp }),
280 ..Default::default()
281 };
282 match proxy.apply_config3(payload) {
283 Ok(()) => Ok(config_stamp),
284 Err(err) => Err(ConfigError::from(err)),
285 }
286 }
287
288 pub async fn get_recent_applied_config_stamp(&self) -> std::result::Result<u64, Error> {
291 let proxy = self.proxy();
292 let response = proxy.get_latest_applied_config_stamp().await?;
293 Ok(response.value)
294 }
295
296 pub(crate) async fn import_buffer_collection(
299 &self,
300 token: ClientEnd<fidl_fuchsia_sysmem2::BufferCollectionTokenMarker>,
301 ) -> Result<BufferCollectionId> {
302 let id = self.inner.write().next_free_collection_id()?;
303 let proxy = self.proxy();
304
305 proxy.import_buffer_collection(&id.into(), token).await?.map_err(zx::Status::from_raw)?;
307
308 proxy
312 .set_buffer_collection_constraints(
313 &id.into(),
314 &display_types::ImageBufferUsage { tiling_type: IMAGE_TILING_TYPE_LINEAR },
315 )
316 .await?
317 .map_err(zx::Status::from_raw)?;
318 Ok(id)
319 }
320
321 pub(crate) fn release_buffer_collection(&self, id: BufferCollectionId) -> Result<()> {
323 self.inner.read().proxy.release_buffer_collection(&id.into()).map_err(Error::from)
324 }
325
326 pub(crate) async fn import_image(
328 &self,
329 collection_id: BufferCollectionId,
330 image_id: ImageId,
331 image_metadata: display_types::ImageMetadata,
332 ) -> Result<()> {
333 self.proxy()
334 .import_image(
335 &image_metadata,
336 &collection_id.into(),
337 0, &image_id.into(),
339 )
340 .await?
341 .map_err(zx::Status::from_raw)?;
342 Ok(())
343 }
344}
345
346impl fmt::Debug for Coordinator {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 f.debug_struct("Coordinator").field("displays", &self.displays()).finish()
351 }
352}
353
354impl CoordinatorInner {
355 fn next_free_collection_id(&mut self) -> Result<BufferCollectionId> {
356 self.id_counter = self.id_counter.checked_add(1).ok_or(Error::IdsExhausted)?;
357 Ok(BufferCollectionId(self.id_counter))
358 }
359
360 fn next_free_event_id(&mut self) -> Result<EventId> {
361 self.id_counter = self.id_counter.checked_add(1).ok_or(Error::IdsExhausted)?;
362 Ok(EventId(self.id_counter))
363 }
364
365 fn next_config_stamp(&mut self) -> Result<u64> {
366 self.stamp_counter = self.stamp_counter.checked_add(1).ok_or(Error::IdsExhausted)?;
367 Ok(self.stamp_counter)
368 }
369
370 fn handle_displays_changed(&self, _added: Vec<display::Info>, _removed: Vec<DisplayId>) {
371 }
374
375 fn handle_vsync(
376 &mut self,
377 display_id: DisplayId,
378 timestamp: zx::MonotonicInstant,
379 applied_config_stamp: display::ConfigStamp,
380 cookie: display::VsyncAckCookie,
381 ) -> Result<()> {
382 if cookie.value != 0 {
383 self.proxy.acknowledge_vsync(cookie.value)?;
384 }
385
386 let mut listeners_to_remove = Vec::new();
387 for (pos, (sender, filter)) in self.vsync_listeners.iter().enumerate() {
388 if filter.as_ref().map_or(false, |id| *id != display_id) {
390 continue;
391 }
392 let payload = VsyncEvent { id: display_id, timestamp, config: applied_config_stamp };
393 if let Err(e) = sender.unbounded_send(payload) {
394 if e.is_disconnected() {
395 listeners_to_remove.push(pos);
396 } else {
397 return Err(e.into());
398 }
399 }
400 }
401
402 listeners_to_remove.into_iter().for_each(|pos| {
404 self.vsync_listeners.swap_remove(pos);
405 });
406
407 Ok(())
408 }
409}
410
411async fn wait_for_initial_displays(
416 listener_requests: &mut display::CoordinatorListenerRequestStream,
417) -> Result<Vec<display::Info>> {
418 let mut stream = listener_requests.try_filter_map(|event| match event {
419 CoordinatorListenerRequest::OnDisplaysChanged { added, removed: _, control_handle: _ } => {
420 future::ok(Some(added))
421 }
422 _ => future::ok(None),
423 });
424 stream.try_next().await?.ok_or(Error::NoDisplays)
425}
426
427#[cfg(test)]
428mod tests {
429 use super::{Coordinator, DisplayId, VsyncEvent};
430 use anyhow::{Context, Result, format_err};
431 use assert_matches::assert_matches;
432 use display_mocks::{MockCoordinator, create_proxy_and_mock};
433 use fuchsia_async::TestExecutor;
434 use futures::task::Poll;
435 use futures::{FutureExt, StreamExt, pin_mut, select};
436 use {
437 fidl_fuchsia_hardware_display as display,
438 fidl_fuchsia_hardware_display_types as display_types,
439 };
440
441 async fn init_with_proxy_and_listener_requests(
442 coordinator_proxy: display::CoordinatorProxy,
443 listener_requests: display::CoordinatorListenerRequestStream,
444 ) -> Result<Coordinator> {
445 Coordinator::init_with_proxy_and_listener_requests(coordinator_proxy, listener_requests)
446 .await
447 .context("failed to initialize Coordinator")
448 }
449
450 async fn init_with_displays(
454 displays: &[display::Info],
455 ) -> Result<(Coordinator, MockCoordinator)> {
456 let (coordinator_proxy, listener_requests, mut mock) = create_proxy_and_mock()?;
457 mock.assign_displays(displays.to_vec())?;
458
459 Ok((
460 init_with_proxy_and_listener_requests(coordinator_proxy, listener_requests).await?,
461 mock,
462 ))
463 }
464
465 #[fuchsia::test]
466 async fn test_init_fails_with_no_device_dir() {
467 let result = Coordinator::init().await;
468 assert_matches!(result, Err(_));
469 }
470
471 #[fuchsia::test]
472 async fn test_init_with_no_displays() -> Result<()> {
473 let (coordinator_proxy, listener_requests, mut mock) = create_proxy_and_mock()?;
474 mock.assign_displays([].to_vec())?;
475
476 let coordinator =
477 init_with_proxy_and_listener_requests(coordinator_proxy, listener_requests).await?;
478 assert!(coordinator.displays().is_empty());
479
480 Ok(())
481 }
482
483 #[fuchsia::test]
487 async fn test_init_with_displays() -> Result<()> {
488 let displays = [
489 display::Info {
490 id: display_types::DisplayId { value: 1 },
491 modes: Vec::new(),
492 pixel_format: Vec::new(),
493 manufacturer_name: "Foo".to_string(),
494 monitor_name: "what".to_string(),
495 monitor_serial: "".to_string(),
496 horizontal_size_mm: 0,
497 vertical_size_mm: 0,
498 using_fallback_size: false,
499 },
500 display::Info {
501 id: display_types::DisplayId { value: 2 },
502 modes: Vec::new(),
503 pixel_format: Vec::new(),
504 manufacturer_name: "Bar".to_string(),
505 monitor_name: "who".to_string(),
506 monitor_serial: "".to_string(),
507 horizontal_size_mm: 0,
508 vertical_size_mm: 0,
509 using_fallback_size: false,
510 },
511 ]
512 .to_vec();
513 let (coordinator_proxy, listener_requests, mut mock) = create_proxy_and_mock()?;
514 mock.assign_displays(displays.clone())?;
515
516 let coordinator =
517 init_with_proxy_and_listener_requests(coordinator_proxy, listener_requests).await?;
518 assert_eq!(coordinator.displays().len(), 2);
519 assert_eq!(coordinator.displays()[0].0, displays[0]);
520 assert_eq!(coordinator.displays()[1].0, displays[1]);
521
522 Ok(())
523 }
524
525 #[test]
526 fn test_vsync_listener_single() -> Result<()> {
527 let mut executor = TestExecutor::new();
530 let (coordinator, mock) = executor.run_singlethreaded(init_with_displays(&[]))?;
531 let mut vsync = coordinator.add_vsync_listener(None)?;
532
533 const ID: DisplayId = DisplayId(1);
534 const STAMP: display::ConfigStamp = display::ConfigStamp { value: 1 };
535 let event_handlers = async {
536 select! {
537 event = vsync.next() => event.ok_or(format_err!("did not receive vsync event")),
538 result = coordinator.handle_events().fuse() => {
539 result.context("FIDL event handler failed")?;
540 Err(format_err!("FIDL event handler completed before client vsync event"))
541 },
542 }
543 };
544 pin_mut!(event_handlers);
545
546 mock.emit_vsync_event(ID.0, STAMP)?;
548 let vsync_event = executor.run_until_stalled(&mut event_handlers);
549 assert_matches!(
550 vsync_event,
551 Poll::Ready(Ok(VsyncEvent { id: ID, timestamp: _, config: STAMP }))
552 );
553
554 Ok(())
555 }
556
557 #[test]
558 fn test_vsync_listener_multiple() -> Result<()> {
559 let mut executor = TestExecutor::new();
562 let (coordinator, mock) = executor.run_singlethreaded(init_with_displays(&[]))?;
563 let mut vsync = coordinator.add_vsync_listener(None)?;
564
565 let fidl_server = coordinator.handle_events().fuse();
566 pin_mut!(fidl_server);
567
568 const ID1: DisplayId = DisplayId(1);
569 const ID2: DisplayId = DisplayId(2);
570 const STAMP: display::ConfigStamp = display::ConfigStamp { value: 1 };
571
572 mock.emit_vsync_event(ID1.0, STAMP)?;
574 mock.emit_vsync_event(ID2.0, STAMP)?;
575 mock.emit_vsync_event(ID1.0, STAMP)?;
576
577 let fidl_server_result = executor.run_until_stalled(&mut fidl_server);
580 assert_matches!(fidl_server_result, Poll::Pending);
581
582 let vsync_event = executor.run_until_stalled(&mut Box::pin(async { vsync.next().await }));
584 assert_matches!(
585 vsync_event,
586 Poll::Ready(Some(VsyncEvent { id: ID1, timestamp: _, config: STAMP }))
587 );
588
589 let vsync_event = executor.run_until_stalled(&mut Box::pin(async { vsync.next().await }));
590 assert_matches!(
591 vsync_event,
592 Poll::Ready(Some(VsyncEvent { id: ID2, timestamp: _, config: STAMP }))
593 );
594
595 let vsync_event = executor.run_until_stalled(&mut Box::pin(async { vsync.next().await }));
596 assert_matches!(
597 vsync_event,
598 Poll::Ready(Some(VsyncEvent { id: ID1, timestamp: _, config: STAMP }))
599 );
600
601 Ok(())
602 }
603
604 #[test]
605 fn test_vsync_listener_display_id_filter() -> Result<()> {
606 let mut executor = TestExecutor::new();
609 let (coordinator, mock) = executor.run_singlethreaded(init_with_displays(&[]))?;
610
611 const ID1: DisplayId = DisplayId(1);
612 const ID2: DisplayId = DisplayId(2);
613 const STAMP: display::ConfigStamp = display::ConfigStamp { value: 1 };
614
615 let mut vsync = coordinator.add_vsync_listener(Some(ID2))?;
617 let event_handlers = async {
618 select! {
619 event = vsync.next() => event.ok_or(format_err!("did not receive vsync event")),
620 result = coordinator.handle_events().fuse() => {
621 result.context("FIDL event handler failed")?;
622 Err(format_err!("FIDL event handler completed before client vsync event"))
623 },
624 }
625 };
626 pin_mut!(event_handlers);
627
628 mock.emit_vsync_event(ID1.0, STAMP)?;
630 let vsync_event = executor.run_until_stalled(&mut event_handlers);
631 assert_matches!(vsync_event, Poll::Pending);
632
633 mock.emit_vsync_event(ID2.0, STAMP)?;
635 let vsync_event = executor.run_until_stalled(&mut event_handlers);
636 assert_matches!(
637 vsync_event,
638 Poll::Ready(Ok(VsyncEvent { id: ID2, timestamp: _, config: STAMP }))
639 );
640
641 Ok(())
642 }
643}