1use crate::app::strategies::framebuffer::{CoordinatorProxyPtr, DisplayId};
6use crate::app::{Config, MessageInternal};
7use crate::drawing::DisplayRotation;
8use crate::render::generic::{self, Backend};
9use crate::render::{Context as RenderContext, ContextInner};
10use crate::view::strategies::base::{ViewStrategy, ViewStrategyPtr};
11use crate::view::{
12 DisplayInfo, UserInputMessage, ViewAssistantContext, ViewAssistantPtr, ViewDetails,
13};
14use crate::{IntPoint, IntSize, Size, ViewKey, input};
15use anyhow::{Context, Error, bail, ensure};
16use async_trait::async_trait;
17use display_utils::{
18 BufferCollectionId, EventId, INVALID_LAYER_ID, ImageId as DisplayImageId, LayerId, PixelFormat,
19};
20use euclid::size2;
21use fidl_fuchsia_hardware_display::{
22 ConfigStamp, CoordinatorApplyConfig3Request, CoordinatorListenerRequest, CoordinatorProxy,
23 INVALID_CONFIG_STAMP_VALUE,
24};
25use fidl_fuchsia_hardware_display_types::{INVALID_DISP_ID, ImageBufferUsage, ImageMetadata};
26use fuchsia_async::{self as fasync};
27use fuchsia_framebuffer::sysmem::BufferCollectionAllocator;
28use fuchsia_framebuffer::{FrameSet, FrameUsage, ImageId};
29use fuchsia_trace::{duration, instant};
30use futures::channel::mpsc::UnboundedSender;
31use std::collections::{BTreeMap, BTreeSet, VecDeque};
32use std::sync::atomic::{AtomicU64, Ordering};
33use zx::{Event, HandleBased, MonotonicDuration, MonotonicInstant, Status};
34
35type WaitEvents = BTreeMap<ImageId, (Event, EventId)>;
36
37struct BusyImage {
38 stamp: ConfigStamp,
39 view_key: ViewKey,
40 image_id: DisplayImageId,
41 collection_id: BufferCollectionId,
42}
43
44#[derive(Default)]
45struct CollectionIdGenerator {}
46
47impl Iterator for CollectionIdGenerator {
48 type Item = BufferCollectionId;
49
50 fn next(&mut self) -> Option<BufferCollectionId> {
51 static NEXT_ID_VALUE: AtomicU64 = AtomicU64::new(100);
52 let value = NEXT_ID_VALUE.fetch_add(1, Ordering::Relaxed);
55 if value == 0 { None } else { Some(BufferCollectionId(value)) }
58 }
59}
60fn next_collection_id() -> BufferCollectionId {
61 CollectionIdGenerator::default().next().expect("collection_id")
62}
63
64#[derive(Default)]
65struct ImageIdGenerator {}
66
67impl Iterator for ImageIdGenerator {
68 type Item = u64;
69
70 fn next(&mut self) -> Option<u64> {
71 static NEXT_ID: AtomicU64 = AtomicU64::new(1);
72 let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
75 if id == 0 { None } else { Some(id) }
78 }
79}
80fn next_image_id() -> u64 {
81 ImageIdGenerator::default().next().expect("image_id")
82}
83
84async fn create_and_import_event(
85 coordinator: &CoordinatorProxy,
86) -> Result<(Event, EventId), Error> {
87 let event = Event::create();
88
89 let their_event = event.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
90 let event_id_value = event.koid()?.raw_koid();
91 let event_id = EventId(event_id_value);
92 coordinator.import_event(Event::from_handle(their_event.into_handle()), &event_id.into())?;
93 Ok((event, event_id))
94}
95
96fn size_from_info(info: &fidl_fuchsia_hardware_display::Info, mode_idx: usize) -> IntSize {
97 let mode = &info.modes[mode_idx];
98 size2(mode.active_area.width, mode.active_area.height).to_i32()
99}
100
101#[derive(Debug, Clone)]
102pub struct Display {
103 pub coordinator: CoordinatorProxyPtr,
104 pub display_id: DisplayId,
105 pub info: fidl_fuchsia_hardware_display::Info,
106 pub layer_id: LayerId,
107 pub mode_idx: usize,
108}
109
110impl Display {
111 pub async fn new(
112 coordinator: CoordinatorProxyPtr,
113 display_id: DisplayId,
114 info: fidl_fuchsia_hardware_display::Info,
115 ) -> Result<Self, Error> {
116 Ok(Self { coordinator, display_id, info, layer_id: INVALID_LAYER_ID, mode_idx: 0 })
117 }
118
119 pub fn set_mode(&mut self, mode_idx: usize) -> Result<(), Error> {
120 self.coordinator.set_display_mode(&self.info.id, &self.info.modes[mode_idx])?;
121 self.mode_idx = mode_idx;
122 Ok(())
123 }
124
125 pub async fn create_layer(&mut self) -> Result<(), Error> {
126 let result = self.coordinator.create_layer().await?;
127 match result {
128 Ok(layer_id) => {
129 self.layer_id = layer_id.into();
130 Ok(())
131 }
132 Err(status) => {
133 bail!("Display::new(): failed to create layer {}", Status::from_raw(status))
134 }
135 }
136 }
137
138 pub fn size(&self) -> IntSize {
139 size_from_info(&self.info, self.mode_idx)
140 }
141
142 pub fn pixel_format(&self) -> PixelFormat {
143 self.info.pixel_format[0].into()
144 }
145}
146
147struct DisplayResources {
148 pub frame_set: FrameSet,
149 pub image_indexes: BTreeMap<ImageId, u32>,
150 pub context: RenderContext,
151 pub wait_events: WaitEvents,
152 pub busy_images: VecDeque<BusyImage>,
153}
154
155const RENDER_FRAME_COUNT: usize = 2;
156
157pub(crate) struct DisplayDirectViewStrategy {
158 key: ViewKey,
159 display: Display,
160 app_sender: UnboundedSender<MessageInternal>,
161 display_rotation: DisplayRotation,
162 display_resources: Option<DisplayResources>,
163 drop_display_resources_task: Option<fasync::Task<()>>,
164 display_resource_release_delay: std::time::Duration,
165 vsync_phase: MonotonicInstant,
166 vsync_interval: MonotonicDuration,
167 mouse_cursor_position: Option<IntPoint>,
168 pub collection_id: BufferCollectionId,
169 render_frame_count: usize,
170 last_config_stamp: u64,
171 presented: Option<u64>,
172}
173
174impl DisplayDirectViewStrategy {
175 pub async fn new(
176 key: ViewKey,
177 coordinator: CoordinatorProxyPtr,
178 app_sender: UnboundedSender<MessageInternal>,
179 info: fidl_fuchsia_hardware_display::Info,
180 preferred_size: IntSize,
181 ) -> Result<ViewStrategyPtr, Error> {
182 let app_config = Config::get();
183 let collection_id = next_collection_id();
184 let render_frame_count = app_config.buffer_count.unwrap_or(RENDER_FRAME_COUNT);
185
186 let mode_idx = info
188 .modes
189 .iter()
190 .position(|mode| {
191 let size = size2(mode.active_area.width, mode.active_area.height).to_i32();
192 size == preferred_size
193 })
194 .unwrap_or(0);
195
196 let mut display = Display::new(coordinator, info.id.into(), info).await?;
197
198 if mode_idx != 0 {
199 display.set_mode(mode_idx)?;
200 }
201 display.create_layer().await?;
202
203 let display_resources = Self::allocate_display_resources(
204 collection_id,
205 display.size(),
206 display.pixel_format(),
207 render_frame_count,
208 &display,
209 )
210 .await?;
211
212 app_sender.unbounded_send(MessageInternal::Render(key)).expect("unbounded_send");
213 app_sender.unbounded_send(MessageInternal::Focus(key, true)).expect("unbounded_send");
214
215 Ok(Box::new(Self {
216 key,
217 display,
218 app_sender,
219 display_rotation: app_config.display_rotation,
220 display_resources: Some(display_resources),
221 drop_display_resources_task: None,
222 display_resource_release_delay: app_config.display_resource_release_delay,
223 vsync_phase: MonotonicInstant::get(),
224 vsync_interval: MonotonicDuration::from_millis(16),
225 mouse_cursor_position: None,
226 collection_id,
227 render_frame_count,
228 last_config_stamp: INVALID_CONFIG_STAMP_VALUE,
229 presented: None,
230 }))
231 }
232
233 fn make_context(
234 &mut self,
235 view_details: &ViewDetails,
236 image_id: Option<ImageId>,
237 ) -> ViewAssistantContext {
238 let time_now = MonotonicInstant::get();
239 let mut interval_offset = MonotonicDuration::from_nanos(
242 (self.vsync_phase.into_nanos() - time_now.into_nanos())
243 % self.vsync_interval.into_nanos(),
244 );
245 if interval_offset != MonotonicDuration::from_nanos(0) && self.vsync_phase < time_now {
248 interval_offset += self.vsync_interval;
249 }
250
251 let display_rotation = self.display_rotation;
252 let app_sender = self.app_sender.clone();
253 let mouse_cursor_position = self.mouse_cursor_position.clone();
254 let (image_index, actual_image_id) = image_id
255 .and_then(|available| {
256 Some((
257 *self.display_resources().image_indexes.get(&available).expect("image_index"),
258 available,
259 ))
260 })
261 .unwrap_or_default();
262
263 ViewAssistantContext {
264 key: view_details.key,
265 size: match display_rotation {
266 DisplayRotation::Deg0 | DisplayRotation::Deg180 => view_details.physical_size,
267 DisplayRotation::Deg90 | DisplayRotation::Deg270 => {
268 size2(view_details.physical_size.height, view_details.physical_size.width)
269 }
270 },
271 metrics: view_details.metrics,
272 presentation_time: time_now + interval_offset,
273 buffer_count: None,
274 image_id: actual_image_id,
275 image_index: image_index,
276 app_sender,
277 mouse_cursor_position,
278 display_info: Some(DisplayInfo::from(&self.display.info)),
279 }
280 }
281
282 async fn allocate_display_resources(
283 collection_id: BufferCollectionId,
284 size: IntSize,
285 pixel_format: display_utils::PixelFormat,
286 render_frame_count: usize,
287 display: &Display,
288 ) -> Result<DisplayResources, Error> {
289 let app_config = Config::get();
290 let use_spinel = app_config.use_spinel;
291
292 ensure!(use_spinel == false, "Spinel support is disabled");
293
294 let display_rotation = app_config.display_rotation;
295 let unsize = size.floor().to_u32();
296
297 let usage = if use_spinel { FrameUsage::Gpu } else { FrameUsage::Cpu };
298 let mut buffer_allocator = BufferCollectionAllocator::new(
299 unsize.width,
300 unsize.height,
301 pixel_format.into(),
302 usage,
303 render_frame_count,
304 )?;
305
306 buffer_allocator.set_name(100, "CarnelianDirect")?;
307
308 let context_token = buffer_allocator.duplicate_token().await?;
309 let context = RenderContext {
310 inner: ContextInner::Forma(generic::Forma::new_context(
311 context_token,
312 unsize,
313 display_rotation,
314 )),
315 };
316
317 let coordinator_token = buffer_allocator.duplicate_token().await?;
318 display
321 .coordinator
322 .import_buffer_collection(&collection_id.into(), coordinator_token)
323 .await?
324 .map_err(zx::Status::from_raw)?;
325 display
326 .coordinator
327 .set_buffer_collection_constraints(
328 &collection_id.into(),
329 &ImageBufferUsage {
330 tiling_type: fidl_fuchsia_hardware_display_types::IMAGE_TILING_TYPE_LINEAR,
331 },
332 )
333 .await?
334 .map_err(zx::Status::from_raw)?;
335
336 let buffers = buffer_allocator
337 .allocate_buffers(true)
338 .await
339 .context(format!("view: {:?} allocate_buffers", display.display_id))?;
340
341 ensure!(
342 buffers.settings.as_ref().unwrap().image_format_constraints.is_some(),
343 "No image format constraints"
344 );
345 ensure!(
346 buffers
347 .settings
348 .as_ref()
349 .unwrap()
350 .image_format_constraints
351 .as_ref()
352 .unwrap()
353 .pixel_format_modifier
354 .is_some(),
355 "Sysmem will always set pixel_format_modifier"
356 );
357 ensure!(
358 buffers.buffers.as_ref().unwrap().len() == render_frame_count,
359 "Buffers do not match frame count"
360 );
361
362 let image_tiling_type = match buffers
363 .settings
364 .as_ref()
365 .unwrap()
366 .image_format_constraints
367 .as_ref()
368 .unwrap()
369 .pixel_format_modifier
370 .as_ref()
371 .unwrap()
372 {
373 fidl_fuchsia_images2::PixelFormatModifier::IntelI915XTiled => 1,
374 fidl_fuchsia_images2::PixelFormatModifier::IntelI915YTiled => 2,
375 _ => fidl_fuchsia_hardware_display_types::IMAGE_TILING_TYPE_LINEAR,
376 };
377
378 let image_metadata = ImageMetadata {
379 dimensions: fidl_fuchsia_math::SizeU { width: unsize.width, height: unsize.height },
380 tiling_type: image_tiling_type,
381 };
382
383 let mut image_ids = BTreeSet::new();
384 let mut image_indexes = BTreeMap::new();
385 let mut wait_events = WaitEvents::new();
386 let buffer_count = buffers.buffers.as_ref().unwrap().len();
387 for index in 0..buffer_count as usize {
388 let uindex = index as u32;
389 let image_id = next_image_id();
390 let display_image_id = DisplayImageId(image_id);
391 display
392 .coordinator
393 .import_image(
394 &image_metadata,
395 &collection_id.into(),
396 uindex,
397 &display_image_id.into(),
398 )
399 .await
400 .context("FIDL coordinator import_image")?
401 .map_err(zx::Status::from_raw)
402 .context("import image error")?;
403
404 image_ids.insert(image_id as u64);
405 image_indexes.insert(image_id as u64, uindex);
406
407 let (event, event_id) = create_and_import_event(&display.coordinator).await?;
408 wait_events.insert(image_id as ImageId, (event, event_id));
409 }
410
411 let frame_set = FrameSet::new(collection_id, image_ids);
412
413 display.coordinator.set_layer_primary_config(&display.layer_id.into(), &image_metadata)?;
414
415 Ok(DisplayResources {
416 frame_set,
417 image_indexes,
418 context,
419 wait_events,
420 busy_images: VecDeque::new(),
421 })
422 }
423
424 async fn maybe_reallocate_display_resources(&mut self) -> Result<(), Error> {
425 if self.display_resources.is_none() {
426 instant!(
427 c"gfx",
428 c"DisplayDirectViewStrategy::allocate_display_resources",
429 fuchsia_trace::Scope::Process,
430 "" => ""
431 );
432 self.collection_id = next_collection_id();
433 self.presented = None;
434 self.display_resources = Some(
435 Self::allocate_display_resources(
436 self.collection_id,
437 self.display.size(),
438 self.display.pixel_format(),
439 self.render_frame_count,
440 &self.display,
441 )
442 .await?,
443 );
444 }
445 Ok(())
446 }
447
448 fn display_resources(&mut self) -> &mut DisplayResources {
449 self.display_resources.as_mut().expect("display_resources")
450 }
451
452 fn update_image(
453 &mut self,
454 view_details: &ViewDetails,
455 view_assistant: &mut ViewAssistantPtr,
456 image: u64,
457 ) {
458 instant!(
459 c"gfx",
460 c"DisplayDirectViewStrategy::update_image",
461 fuchsia_trace::Scope::Process,
462 "image" => format!("{}", image).as_str()
463 );
464 let (event, _) = self.display_resources().wait_events.get(&image).expect("wait event");
465 let buffer_ready_event =
466 event.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("duplicate_handle");
467 let direct_context = self.make_context(view_details, Some(image));
468
469 view_assistant
470 .render(&mut self.display_resources().context, buffer_ready_event, &direct_context)
471 .unwrap_or_else(|e| panic!("Update error: {:?}", e));
472 }
473
474 fn handle_vsync_parameters_changed(
475 &mut self,
476 phase: MonotonicInstant,
477 interval: MonotonicDuration,
478 ) {
479 self.vsync_phase = phase;
480 self.vsync_interval = interval;
481 }
482}
483
484#[async_trait(?Send)]
485impl ViewStrategy for DisplayDirectViewStrategy {
486 fn initial_metrics(&self) -> Size {
487 size2(1.0, 1.0)
488 }
489
490 fn initial_physical_size(&self) -> Size {
491 self.display.size().to_f32()
492 }
493
494 fn initial_logical_size(&self) -> Size {
495 self.display.size().to_f32()
496 }
497
498 fn create_view_assistant_context(&self, view_details: &ViewDetails) -> ViewAssistantContext {
499 ViewAssistantContext {
500 key: view_details.key,
501 size: match self.display_rotation {
502 DisplayRotation::Deg0 | DisplayRotation::Deg180 => view_details.physical_size,
503 DisplayRotation::Deg90 | DisplayRotation::Deg270 => {
504 size2(view_details.physical_size.height, view_details.physical_size.width)
505 }
506 },
507 metrics: view_details.metrics,
508 presentation_time: Default::default(),
509 buffer_count: None,
510 image_id: Default::default(),
511 image_index: Default::default(),
512 app_sender: self.app_sender.clone(),
513 mouse_cursor_position: self.mouse_cursor_position.clone(),
514 display_info: Some(DisplayInfo::from(&self.display.info)),
515 }
516 }
517
518 fn setup(&mut self, view_details: &ViewDetails, view_assistant: &mut ViewAssistantPtr) {
519 if let Some(available) = self.display_resources().frame_set.get_available_image() {
520 let direct_context = self.make_context(view_details, Some(available));
521 view_assistant
522 .setup(&direct_context)
523 .unwrap_or_else(|e| panic!("Setup error: {:?}", e));
524 self.display_resources().frame_set.return_image(available);
525 }
526 }
527
528 async fn render(
529 &mut self,
530 view_details: &ViewDetails,
531 view_assistant: &mut ViewAssistantPtr,
532 ) -> bool {
533 duration!("gfx", "DisplayDirectViewStrategy::update");
534 self.maybe_reallocate_display_resources()
535 .await
536 .expect("maybe_reallocate_display_resources");
537 if let Some(available) = self.display_resources().frame_set.get_available_image() {
538 self.update_image(view_details, view_assistant, available);
539 self.display_resources().frame_set.mark_prepared(available);
540 true
541 } else {
542 if self.render_frame_count == 1 {
543 if let Some(presented) = self.presented {
544 self.update_image(view_details, view_assistant, presented);
545 true
546 } else {
547 false
548 }
549 } else {
550 false
551 }
552 }
553 }
554
555 fn present(&mut self, view_details: &ViewDetails) {
556 duration!("gfx", "DisplayDirectViewStrategy::present");
557
558 if self.render_frame_count == 1 && self.presented.is_some() {
559 return;
560 }
561 if let Some(prepared) = self.display_resources().frame_set.prepared {
562 instant!(
563 c"gfx",
564 c"DisplayDirectViewStrategy::present",
565 fuchsia_trace::Scope::Process,
566 "prepared" => format!("{}", prepared).as_str()
567 );
568 let collection_id = self.collection_id;
569 let view_key = view_details.key;
570 self.display
571 .coordinator
572 .set_display_layers(
573 &self.display.display_id.into(),
574 &[self.display.layer_id.into()],
575 )
576 .expect("set_display_layers");
577
578 let (_, wait_event_id) =
579 *self.display_resources().wait_events.get(&prepared).expect("wait event");
580
581 let image_id = DisplayImageId(prepared);
582 self.display
583 .coordinator
584 .set_layer_image2(
585 &self.display.layer_id.into(),
586 &image_id.into(),
587 &wait_event_id.into(),
588 )
589 .expect("Frame::present() set_layer_image2");
590
591 self.last_config_stamp += 1;
592 let stamp = ConfigStamp { value: self.last_config_stamp };
593 let req = CoordinatorApplyConfig3Request { stamp: Some(stamp), ..Default::default() };
594
595 self.display.coordinator.apply_config3(req).expect("Frame::present() apply_config");
596
597 self.display_resources().busy_images.push_back(BusyImage {
598 stamp,
599 view_key,
600 image_id,
601 collection_id,
602 });
603
604 self.display_resources().frame_set.mark_presented(prepared);
605 self.presented = Some(prepared);
606 }
607 }
608
609 fn handle_focus(
610 &mut self,
611 view_details: &ViewDetails,
612 view_assistant: &mut ViewAssistantPtr,
613 focus: bool,
614 ) {
615 let mut direct_context = self.make_context(view_details, None);
616 view_assistant
617 .handle_focus_event(&mut direct_context, focus)
618 .unwrap_or_else(|e| panic!("handle_focus error: {:?}", e));
619 }
620
621 fn convert_user_input_message(
622 &mut self,
623 _view_details: &ViewDetails,
624 _message: UserInputMessage,
625 ) -> Result<Vec<crate::input::Event>, Error> {
626 bail!("convert_user_input_message not used for display_direct.")
627 }
628
629 fn inspect_event(&mut self, view_details: &ViewDetails, event: &crate::input::Event) {
630 match &event.event_type {
631 input::EventType::Mouse(mouse_event) => {
632 self.mouse_cursor_position = Some(mouse_event.location);
633 self.app_sender
634 .unbounded_send(MessageInternal::RequestRender(view_details.key))
635 .expect("unbounded_send");
636 }
637 _ => (),
638 };
639 }
640
641 fn image_freed(&mut self, image_id: u64, collection_id: u32) {
642 if BufferCollectionId(collection_id as u64) == self.collection_id {
643 instant!(
644 c"gfx",
645 c"DisplayDirectViewStrategy::image_freed",
646 fuchsia_trace::Scope::Process,
647 "image_freed" => format!("{}", image_id).as_str()
648 );
649 if let Some(display_resources) = self.display_resources.as_mut() {
650 display_resources.frame_set.mark_done_presenting(image_id);
651 }
652 }
653 }
654
655 fn ownership_changed(&mut self, owned: bool) {
656 if !owned {
657 let timer = fasync::Timer::new(fuchsia_async::MonotonicInstant::after(
658 self.display_resource_release_delay.into(),
659 ));
660 let timer_sender = self.app_sender.clone();
661 let task = fasync::Task::local(async move {
662 timer.await;
663 timer_sender
664 .unbounded_send(MessageInternal::DropDisplayResources)
665 .expect("unbounded_send");
666 });
667 self.drop_display_resources_task = Some(task);
668 } else {
669 self.drop_display_resources_task = None;
670 }
671 }
672
673 fn drop_display_resources(&mut self) {
674 let task = self.drop_display_resources_task.take();
675 if task.is_some() {
676 instant!(
677 c"gfx",
678 c"DisplayDirectViewStrategy::drop_display_resources",
679 fuchsia_trace::Scope::Process,
680 "" => ""
681 );
682 self.display_resources = None;
683 }
684 }
685
686 async fn handle_display_coordinator_listener_request(
687 &mut self,
688 event: CoordinatorListenerRequest,
689 ) {
690 match event {
691 CoordinatorListenerRequest::OnVsync {
692 timestamp, cookie, applied_config_stamp, ..
693 } => {
694 duration!("gfx", "DisplayDirectViewStrategy::OnVsync");
695 let vsync_interval = MonotonicDuration::from_nanos(
696 1_000_000_000_000 / self.display.info.modes[0].refresh_rate_millihertz as i64,
697 );
698 self.handle_vsync_parameters_changed(timestamp, vsync_interval);
699 if cookie.value != INVALID_DISP_ID {
700 self.display
701 .coordinator
702 .acknowledge_vsync(cookie.value)
703 .expect("acknowledge_vsync");
704 }
705
706 let signal_sender = self.app_sender.clone();
707
708 if let Some(display_resources) = self.display_resources.as_mut() {
714 let busy_images = &mut display_resources.busy_images;
715 while !busy_images.is_empty() {
716 let front = &busy_images.front().unwrap();
717 if applied_config_stamp.value <= front.stamp.value {
718 break;
719 }
720
721 signal_sender
722 .unbounded_send(MessageInternal::ImageFreed(
723 front.view_key,
724 front.image_id.0,
725 front.collection_id.0 as u32,
726 ))
727 .expect("unbounded_send");
728
729 busy_images.pop_front();
730 }
731 }
732
733 signal_sender
734 .unbounded_send(MessageInternal::Render(self.key))
735 .expect("unbounded_send");
736 }
737 CoordinatorListenerRequest::OnDisplaysChanged { .. } => {
738 eprintln!("Carnelian ignoring CoordinatorListenerRequest::OnDisplaysChanged");
739 }
740 CoordinatorListenerRequest::OnClientOwnershipChange { has_ownership, .. } => {
741 eprintln!(
742 "Carnelian ignoring CoordinatorListenerRequest::OnClientOwnershipChange (value: {})",
743 has_ownership
744 );
745 }
746 _ => (),
747 }
748 }
749
750 fn is_hosted_on_display(&self, display_id: DisplayId) -> bool {
751 self.display.display_id == display_id
752 }
753
754 fn close(&mut self) {
755 self.display
756 .coordinator
757 .release_buffer_collection(&self.collection_id.into())
758 .expect("release_buffer_collection");
759 }
760}