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 () =
111 provider_proxy.open_coordinator(payload).await?.map_err(zx::Status::err_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 displayed_config_stamp,
198 cookie,
199 control_handle: _,
200 } => {
201 inner.write().handle_vsync(
202 display_id.into(),
203 timestamp,
204 displayed_config_stamp,
205 cookie,
206 )?;
207 }
208 _ => continue,
209 }
210 }
211 Ok(())
212 }
213
214 pub async fn create_layer(&self) -> Result<LayerId> {
217 let layer_id = self.inner.write().next_free_layer_id()?;
218 self.proxy().create_layer(&layer_id.into()).await?.map_err(zx::Status::err_from_raw)?;
219 Ok(layer_id)
220 }
221
222 pub fn create_event(&self) -> Result<Event> {
225 let event = zx::Event::create();
226 let remote = event.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
227 let id = self.inner.write().next_free_event_id()?;
228
229 self.inner.read().proxy.import_event(zx::Event::from(remote), &id.into())?;
230 Ok(Event::new(id, event))
231 }
232
233 pub async fn commit_config(
236 &self,
237 configs: &[DisplayConfig],
238 ) -> std::result::Result<u64, ConfigError> {
239 let proxy = self.proxy();
240 for config in configs {
241 proxy.set_display_layers(
242 &config.id.into(),
243 &config.layers.iter().map(|l| l.id.into()).collect::<Vec<FidlLayerId>>(),
244 )?;
245 for layer in &config.layers {
246 match &layer.config {
247 LayerConfig::Color { color, display_destination } => {
248 let fidl_color = fidl_fuchsia_hardware_display_types::Color::from(color);
249 proxy.set_layer_color_config(
250 &layer.id.into(),
251 &fidl_color,
252 display_destination,
253 )?;
254 }
255 LayerConfig::Primary { image_id, image_metadata, unblock_event, alpha } => {
256 proxy.set_layer_primary_config(&layer.id.into(), &image_metadata)?;
257 if let Some(alpha_config) = alpha {
258 proxy.set_layer_primary_alpha(
259 &layer.id.into(),
260 alpha_config.mode,
261 alpha_config.val,
262 )?;
263 }
264 proxy.set_layer_image2(
265 &layer.id.into(),
266 &(*image_id).into(),
267 &unblock_event.unwrap_or(INVALID_EVENT_ID).into(),
268 )?;
269 }
270 }
271 }
272 }
273
274 let result = proxy.check_config().await?;
275 if result != display_types::ConfigResult::Ok {
276 return Err(ConfigError::invalid(result));
277 }
278
279 let config_stamp = self.inner.write().next_config_stamp().unwrap();
280 let payload = fidl_fuchsia_hardware_display::CoordinatorCommitConfigRequest {
281 stamp: Some(fidl_fuchsia_hardware_display::ConfigStamp { value: config_stamp }),
282 ..Default::default()
283 };
284 match proxy.commit_config(payload) {
285 Ok(()) => Ok(config_stamp),
286 Err(err) => Err(ConfigError::from(err)),
287 }
288 }
289
290 pub async fn get_recent_committed_config_stamp(&self) -> std::result::Result<u64, Error> {
293 let proxy = self.proxy();
294 let response = proxy.get_latest_committed_config_stamp().await?;
295 Ok(response.value)
296 }
297
298 pub(crate) async fn import_buffer_collection(
301 &self,
302 token: ClientEnd<fidl_fuchsia_sysmem2::BufferCollectionTokenMarker>,
303 ) -> Result<BufferCollectionId> {
304 let id = self.inner.write().next_free_collection_id()?;
305 let proxy = self.proxy();
306
307 proxy
309 .import_buffer_collection(&id.into(), token)
310 .await?
311 .map_err(zx::Status::err_from_raw)?;
312
313 proxy
317 .set_buffer_collection_constraints(
318 &id.into(),
319 &display_types::ImageBufferUsage { tiling_type: IMAGE_TILING_TYPE_LINEAR },
320 )
321 .await?
322 .map_err(zx::Status::err_from_raw)?;
323 Ok(id)
324 }
325
326 pub(crate) fn release_buffer_collection(&self, id: BufferCollectionId) -> Result<()> {
328 self.inner.read().proxy.release_buffer_collection(&id.into()).map_err(Error::from)
329 }
330
331 pub(crate) async fn import_image(
333 &self,
334 collection_id: BufferCollectionId,
335 image_id: ImageId,
336 image_metadata: display_types::ImageMetadata,
337 ) -> Result<()> {
338 self.proxy()
339 .import_image(
340 &image_metadata,
341 &collection_id.into(),
342 0, &image_id.into(),
344 )
345 .await?
346 .map_err(zx::Status::err_from_raw)?;
347 Ok(())
348 }
349}
350
351impl fmt::Debug for Coordinator {
354 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355 f.debug_struct("Coordinator").field("displays", &self.displays()).finish()
356 }
357}
358
359impl CoordinatorInner {
360 fn next_free_collection_id(&mut self) -> Result<BufferCollectionId> {
361 self.id_counter = self.id_counter.checked_add(1).ok_or(Error::IdsExhausted)?;
362 Ok(BufferCollectionId(self.id_counter))
363 }
364
365 fn next_free_event_id(&mut self) -> Result<EventId> {
366 self.id_counter = self.id_counter.checked_add(1).ok_or(Error::IdsExhausted)?;
367 Ok(EventId(self.id_counter))
368 }
369
370 fn next_free_layer_id(&mut self) -> Result<LayerId> {
371 self.id_counter = self.id_counter.checked_add(1).ok_or(Error::IdsExhausted)?;
372 Ok(LayerId(self.id_counter))
373 }
374
375 fn next_config_stamp(&mut self) -> Result<u64> {
376 self.stamp_counter = self.stamp_counter.checked_add(1).ok_or(Error::IdsExhausted)?;
377 Ok(self.stamp_counter)
378 }
379
380 fn handle_displays_changed(&self, _added: Vec<display::Info>, _removed: Vec<DisplayId>) {
381 }
384
385 fn handle_vsync(
386 &mut self,
387 display_id: DisplayId,
388 timestamp: zx::MonotonicInstant,
389 displayed_config_stamp: display::ConfigStamp,
390 cookie: display::VsyncAckCookie,
391 ) -> Result<()> {
392 if cookie.value != 0 {
393 self.proxy.acknowledge_vsync(cookie.value)?;
394 }
395
396 let mut listeners_to_remove = Vec::new();
397 for (pos, (sender, filter)) in self.vsync_listeners.iter().enumerate() {
398 if filter.as_ref().map_or(false, |id| *id != display_id) {
400 continue;
401 }
402 let payload = VsyncEvent { id: display_id, timestamp, config: displayed_config_stamp };
403 if let Err(e) = sender.unbounded_send(payload) {
404 if e.is_disconnected() {
405 listeners_to_remove.push(pos);
406 } else {
407 return Err(e.into());
408 }
409 }
410 }
411
412 listeners_to_remove.into_iter().for_each(|pos| {
414 self.vsync_listeners.swap_remove(pos);
415 });
416
417 Ok(())
418 }
419}
420
421async fn wait_for_initial_displays(
426 listener_requests: &mut display::CoordinatorListenerRequestStream,
427) -> Result<Vec<display::Info>> {
428 let mut stream = listener_requests.try_filter_map(|event| match event {
429 CoordinatorListenerRequest::OnDisplaysChanged { added, removed: _, control_handle: _ } => {
430 future::ok(Some(added))
431 }
432 _ => future::ok(None),
433 });
434 stream.try_next().await?.ok_or(Error::NoDisplays)
435}
436
437#[cfg(test)]
438mod tests {
439 use super::{Coordinator, DisplayId, VsyncEvent};
440 use anyhow::{Context, Result, format_err};
441 use display_mocks::{MockCoordinator, create_proxy_and_mock};
442 use fidl_fuchsia_hardware_display as display;
443 use fidl_fuchsia_hardware_display_types as display_types;
444 use fuchsia_async::TestExecutor;
445 use futures::task::Poll;
446 use futures::{FutureExt, StreamExt, pin_mut, select};
447 use googletest::{assert_that, expect_eq, expect_that, gtest, matchers};
448
449 async fn init_with_proxy_and_listener_requests(
450 coordinator_proxy: display::CoordinatorProxy,
451 listener_requests: display::CoordinatorListenerRequestStream,
452 ) -> Result<Coordinator> {
453 Coordinator::init_with_proxy_and_listener_requests(coordinator_proxy, listener_requests)
454 .await
455 .context("failed to initialize Coordinator")
456 }
457
458 async fn init_with_displays(
462 displays: &[display::Info],
463 ) -> Result<(Coordinator, MockCoordinator)> {
464 let (coordinator_proxy, listener_requests, mut mock) = create_proxy_and_mock()?;
465 mock.assign_displays(displays.to_vec())?;
466
467 Ok((
468 init_with_proxy_and_listener_requests(coordinator_proxy, listener_requests).await?,
469 mock,
470 ))
471 }
472
473 #[gtest]
474 #[fuchsia::test]
475 async fn test_init_fails_with_no_device_dir() {
476 let result = Coordinator::init(crate::types::ClientPriority(300)).await;
477 expect_that!(&result, matchers::err(matchers::anything()));
478 }
479
480 #[gtest]
481 #[fuchsia::test]
482 async fn test_init_with_no_displays() -> Result<()> {
483 let (coordinator_proxy, listener_requests, mut mock) = create_proxy_and_mock()?;
484 mock.assign_displays([].to_vec())?;
485
486 let coordinator =
487 init_with_proxy_and_listener_requests(coordinator_proxy, listener_requests).await?;
488 expect_that!(&coordinator.displays(), matchers::is_empty());
489
490 Ok(())
491 }
492
493 #[gtest]
497 #[fuchsia::test]
498 async fn test_init_with_displays() -> Result<()> {
499 let displays = [
500 display::Info {
501 id: display_types::DisplayId { value: 1 },
502 modes: Vec::new(),
503 pixel_format: Vec::new(),
504 manufacturer_name: "Foo".to_string(),
505 monitor_name: "what".to_string(),
506 monitor_serial: "".to_string(),
507 horizontal_size_mm: 0,
508 vertical_size_mm: 0,
509 using_fallback_size: false,
510 max_layer_count: 1,
511 },
512 display::Info {
513 id: display_types::DisplayId { value: 2 },
514 modes: Vec::new(),
515 pixel_format: Vec::new(),
516 manufacturer_name: "Bar".to_string(),
517 monitor_name: "who".to_string(),
518 monitor_serial: "".to_string(),
519 horizontal_size_mm: 0,
520 vertical_size_mm: 0,
521 using_fallback_size: false,
522 max_layer_count: 1,
523 },
524 ]
525 .to_vec();
526 let (coordinator_proxy, listener_requests, mut mock) = create_proxy_and_mock()?;
527 mock.assign_displays(displays.clone())?;
528
529 let coordinator =
530 init_with_proxy_and_listener_requests(coordinator_proxy, listener_requests).await?;
531 expect_eq!(coordinator.displays().len(), 2);
532 expect_eq!(coordinator.displays()[0].0, displays[0]);
533 expect_eq!(coordinator.displays()[1].0, displays[1]);
534
535 Ok(())
536 }
537
538 #[gtest]
539 #[test]
540 fn test_vsync_listener_single() -> Result<()> {
541 let mut executor = TestExecutor::new();
544 let (coordinator, mock) = executor.run_singlethreaded(init_with_displays(&[]))?;
545 let mut vsync = coordinator.add_vsync_listener(None)?;
546
547 const ID: DisplayId = DisplayId(1);
548 const STAMP: display::ConfigStamp = display::ConfigStamp { value: 1 };
549 let event_handlers = async {
550 select! {
551 event = vsync.next() => event.ok_or_else(|| format_err!("did not receive vsync event")),
552 result = coordinator.handle_events().fuse() => {
553 result.context("FIDL event handler failed")?;
554 Err(format_err!("FIDL event handler completed before client vsync event"))
555 },
556 }
557 };
558 pin_mut!(event_handlers);
559
560 mock.emit_vsync_event(ID.0, STAMP)?;
562 let vsync_event = executor.run_until_stalled(&mut event_handlers);
563 assert_that!(&vsync_event, matchers::matches_pattern!(Poll::Ready(matchers::anything())));
564 let Poll::Ready(result) = vsync_event else { unreachable!() };
565 let VsyncEvent { id, config, .. } = result.unwrap();
566 expect_eq!(id, ID);
567 expect_eq!(config, STAMP);
568
569 Ok(())
570 }
571
572 #[gtest]
573 #[test]
574 fn test_vsync_listener_multiple() -> Result<()> {
575 let mut executor = TestExecutor::new();
578 let (coordinator, mock) = executor.run_singlethreaded(init_with_displays(&[]))?;
579 let mut vsync = coordinator.add_vsync_listener(None)?;
580
581 let fidl_server = coordinator.handle_events().fuse();
582 pin_mut!(fidl_server);
583
584 const ID1: DisplayId = DisplayId(1);
585 const ID2: DisplayId = DisplayId(2);
586 const STAMP: display::ConfigStamp = display::ConfigStamp { value: 1 };
587
588 mock.emit_vsync_event(ID1.0, STAMP)?;
590 mock.emit_vsync_event(ID2.0, STAMP)?;
591 mock.emit_vsync_event(ID1.0, STAMP)?;
592
593 let fidl_server_result = executor.run_until_stalled(&mut fidl_server);
596 assert_that!(fidl_server_result, matchers::matches_pattern!(Poll::Pending));
597
598 let vsync_event = executor.run_until_stalled(&mut Box::pin(async { vsync.next().await }));
600 assert_that!(&vsync_event, matchers::matches_pattern!(Poll::Ready(matchers::anything())));
601 let Poll::Ready(Some(VsyncEvent { id, config, .. })) = vsync_event else { unreachable!() };
602 expect_eq!(id, ID1);
603 expect_eq!(config, STAMP);
604
605 let vsync_event = executor.run_until_stalled(&mut Box::pin(async { vsync.next().await }));
606 assert_that!(&vsync_event, matchers::matches_pattern!(Poll::Ready(matchers::anything())));
607 let Poll::Ready(Some(VsyncEvent { id, config, .. })) = vsync_event else { unreachable!() };
608 expect_eq!(id, ID2);
609 expect_eq!(config, STAMP);
610
611 let vsync_event = executor.run_until_stalled(&mut Box::pin(async { vsync.next().await }));
612 assert_that!(&vsync_event, matchers::matches_pattern!(Poll::Ready(matchers::anything())));
613 let Poll::Ready(Some(VsyncEvent { id, config, .. })) = vsync_event else { unreachable!() };
614 expect_eq!(id, ID1);
615 expect_eq!(config, STAMP);
616
617 Ok(())
618 }
619
620 #[gtest]
621 #[test]
622 fn test_vsync_listener_display_id_filter() -> Result<()> {
623 let mut executor = TestExecutor::new();
626 let (coordinator, mock) = executor.run_singlethreaded(init_with_displays(&[]))?;
627
628 const ID1: DisplayId = DisplayId(1);
629 const ID2: DisplayId = DisplayId(2);
630 const STAMP: display::ConfigStamp = display::ConfigStamp { value: 1 };
631
632 let mut vsync = coordinator.add_vsync_listener(Some(ID2))?;
634 let event_handlers = async {
635 select! {
636 event = vsync.next() => event.ok_or_else(|| format_err!("did not receive vsync event")),
637 result = coordinator.handle_events().fuse() => {
638 result.context("FIDL event handler failed")?;
639 Err(format_err!("FIDL event handler completed before client vsync event"))
640 },
641 }
642 };
643 pin_mut!(event_handlers);
644
645 mock.emit_vsync_event(ID1.0, STAMP)?;
647 let vsync_event = executor.run_until_stalled(&mut event_handlers);
648 assert_that!(vsync_event, matchers::matches_pattern!(Poll::Pending));
649
650 mock.emit_vsync_event(ID2.0, STAMP)?;
652 let vsync_event = executor.run_until_stalled(&mut event_handlers);
653 assert_that!(&vsync_event, matchers::matches_pattern!(Poll::Ready(matchers::anything())));
654 let Poll::Ready(result) = vsync_event else { unreachable!() };
655 let VsyncEvent { id, config, .. } = result.unwrap();
656 expect_eq!(id, ID2);
657 expect_eq!(config, STAMP);
658
659 Ok(())
660 }
661}