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