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