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