carnelian/view/strategies/
display_direct.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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        // NEXT_ID_VALUE only increments so it only requires atomicity, and we
53        // can use Relaxed order.
54        let value = NEXT_ID_VALUE.fetch_add(1, Ordering::Relaxed);
55        // fetch_add wraps on overflow, which we'll use as a signal
56        // that this generator is out of ids.
57        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        // NEXT_ID only increments so it only requires atomicity, and we can
73        // use Relaxed order.
74        let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
75        // fetch_add wraps on overflow, which we'll use as a signal
76        // that this generator is out of ids.
77        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        // Find first mode with the preferred size. Use preferred mode if not found.
187        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        // |interval_offset| is the offset from |time_now| to the next multiple
240        // of vsync interval after vsync phase, possibly negative if in the past.
241        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        // Unless |time_now| is exactly on the interval, adjust forward to the next
246        // vsync after |time_now|.
247        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        // Sysmem token channels serve both sysmem(1) and sysmem2, so we can convert here until
319        // display has an import_buffer_collection that takes a sysmem2 token.
320        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                // Busy images are stamped with monotonically increasing values (because the last
709                // stamp added to the deque is always greater than the previous).  So when we see a
710                // vsync stamp, all images with a *strictly-lesser* stamp are now available for
711                // reuse (images with an *equal* stamp are the ones currently displayed on-screen,
712                // so can't be reused yet).
713                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}