carnelian/view/strategies/
flatland.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.
4use crate::app::{Config, MessageInternal};
5use crate::drawing::DisplayRotation;
6use crate::geometry::UintSize;
7use crate::input::flatland::{FlatlandMouseInputHandler, FlatlandTouchInputHandler};
8use crate::input::key3::KeyboardInputHandler;
9use crate::render::generic::{self, Backend};
10use crate::render::{self, ContextInner};
11use crate::view::strategies::base::{FlatlandParams, ViewStrategy, ViewStrategyPtr};
12use crate::view::{UserInputMessage, ViewAssistantContext, ViewAssistantPtr, ViewDetails, ViewKey};
13use crate::Size;
14use anyhow::{ensure, Context, Error, Result};
15use async_trait::async_trait;
16use async_utils::hanging_get::client::HangingGetStream;
17use display_utils::BufferCollectionId as DisplayBufferCollectionId;
18use euclid::size2;
19use fidl::endpoints::{create_endpoints, create_proxy, create_request_stream};
20use fidl_fuchsia_images2::PixelFormat;
21use fidl_fuchsia_ui_composition as flatland;
22use fidl_fuchsia_ui_views::ViewRef;
23use fuchsia_async::{self as fasync, OnSignals};
24use fuchsia_component::client::connect_to_protocol;
25use fuchsia_framebuffer::sysmem::BufferCollectionAllocator;
26use fuchsia_framebuffer::{FrameSet, FrameUsage, ImageId};
27use fuchsia_scenic::BufferCollectionTokenPair;
28use fuchsia_trace::{duration, instant};
29use futures::channel::mpsc::UnboundedSender;
30use futures::prelude::*;
31use futures::{StreamExt, TryStreamExt};
32use std::collections::{BTreeMap, BTreeSet, HashMap};
33use zx::{self as zx, Event, HandleBased, MonotonicInstant, Signals};
34
35fn setup_handle_flatland_events(
36    event_stream: flatland::FlatlandEventStream,
37    view_key: ViewKey,
38    app_sender: UnboundedSender<MessageInternal>,
39) {
40    fasync::Task::local(
41        event_stream
42            .try_for_each(move |event| {
43                match event {
44                    flatland::FlatlandEvent::OnNextFrameBegin { values } => {
45                        app_sender
46                            .unbounded_send(MessageInternal::FlatlandOnNextFrameBegin(
47                                view_key, values,
48                            ))
49                            .expect("unbounded_send");
50                    }
51                    flatland::FlatlandEvent::OnFramePresented { frame_presented_info } => {
52                        app_sender
53                            .unbounded_send(MessageInternal::FlatlandOnFramePresented(
54                                view_key,
55                                frame_presented_info,
56                            ))
57                            .expect("unbounded_send");
58                    }
59                    flatland::FlatlandEvent::OnError { error } => {
60                        app_sender
61                            .unbounded_send(MessageInternal::FlatlandOnError(view_key, error))
62                            .expect("unbounded_send");
63                    }
64                };
65                future::ok(())
66            })
67            .unwrap_or_else(|e| eprintln!("error listening for Flatland Events: {:?}", e)),
68    )
69    .detach();
70}
71
72fn duplicate_import_token(
73    token: &fidl_fuchsia_ui_composition::BufferCollectionImportToken,
74) -> Result<fidl_fuchsia_ui_composition::BufferCollectionImportToken, Error> {
75    let value = token.value.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
76    Ok(fidl_fuchsia_ui_composition::BufferCollectionImportToken { value })
77}
78
79struct Plumber {
80    pub size: UintSize,
81    pub collection_id: u32,
82    pub frame_set: FrameSet,
83    pub image_indexes: BTreeMap<ImageId, u32>,
84    pub context: render::Context,
85}
86
87impl Plumber {
88    async fn new(
89        flatland: &flatland::FlatlandProxy,
90        allocator: &flatland::AllocatorProxy,
91        size: UintSize,
92        pixel_format: PixelFormat,
93        buffer_count: usize,
94        collection_id: u32,
95        first_image_id: u64,
96    ) -> Result<Plumber, Error> {
97        let use_spinel = Config::get().use_spinel;
98
99        ensure!(use_spinel == false, "Spinel support is disabled");
100
101        let usage = if use_spinel { FrameUsage::Gpu } else { FrameUsage::Cpu };
102        let mut buffer_allocator = BufferCollectionAllocator::new(
103            size.width,
104            size.height,
105            pixel_format,
106            usage,
107            buffer_count,
108        )?;
109
110        buffer_allocator.set_name(100, "CarnelianSurface")?;
111
112        let context_token = buffer_allocator.duplicate_token().await?;
113        let mut context = render::Context {
114            inner: ContextInner::Forma(generic::Forma::new_context(
115                context_token,
116                size,
117                DisplayRotation::Deg0,
118            )),
119        };
120
121        let sysmem_buffer_collection_token = buffer_allocator.duplicate_token().await?;
122        let buffer_tokens = BufferCollectionTokenPair::new();
123        let args = flatland::RegisterBufferCollectionArgs {
124            export_token: Some(buffer_tokens.export_token),
125            // A sysmem token channel serves both sysmem(1) and sysmem2, so we can convert here
126            // until flatland has a field for a sysmem2 token.
127            buffer_collection_token2: Some(sysmem_buffer_collection_token),
128            ..Default::default()
129        };
130
131        allocator
132            .register_buffer_collection(args)
133            .await
134            .expect("fidl error")
135            .expect("error registering buffer collection");
136
137        let buffers = buffer_allocator.allocate_buffers(true).await.context("allocate_buffers")?;
138
139        let blend_mode = if Config::get().needs_blending {
140            flatland::BlendMode::SrcOver
141        } else {
142            flatland::BlendMode::Src
143        };
144        let mut image_ids = BTreeSet::new();
145        let mut image_indexes = BTreeMap::new();
146        let buffer_count = buffers.buffers.as_ref().unwrap().len();
147        for index in 0..buffer_count as usize {
148            let image_id = index + first_image_id as usize;
149            image_ids.insert(image_id as u64);
150            let uindex = index as u32;
151            image_indexes.insert(image_id as u64, uindex);
152            let image_props = flatland::ImageProperties {
153                size: Some(fidl_fuchsia_math::SizeU { width: size.width, height: size.height }),
154                ..Default::default()
155            };
156            let flatland_image_id = flatland::ContentId { value: image_id as u64 };
157            let import_token = duplicate_import_token(&buffer_tokens.import_token)?;
158            flatland.create_image(&flatland_image_id, import_token, uindex, &image_props)?;
159            flatland
160                .set_image_destination_size(
161                    &flatland_image_id,
162                    &fidl_fuchsia_math::SizeU { width: size.width, height: size.height },
163                )
164                .expect("fidl error");
165            flatland
166                .set_image_blending_function(&flatland_image_id, blend_mode)
167                .expect("fidl error");
168            // Get all the images at this point, since if we wait until we need them for
169            // rendering, Forma's private connection to sysmem has closed and it fails.
170            // https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=83543
171            context.get_image(index as u32);
172        }
173
174        let frame_set = FrameSet::new(DisplayBufferCollectionId(collection_id as u64), image_ids);
175        Ok(Plumber { size, collection_id, frame_set, image_indexes, context })
176    }
177
178    pub fn enter_retirement(&mut self, _flatland: &flatland::FlatlandProxy) {}
179}
180
181const RENDER_BUFFER_COUNT: usize = 3;
182const DEFAULT_PRESENT_INTERVAL: i64 = (1_000_000_000.0 / 60.0) as i64;
183const TRANSFORM_ID: flatland::TransformId = flatland::TransformId { value: 1 };
184
185#[derive(Clone, Copy)]
186struct PresentationTime {
187    presentation_time: i64,
188    latch_point: i64,
189}
190
191pub(crate) struct FlatlandViewStrategy {
192    flatland: flatland::FlatlandProxy,
193    allocator: flatland::AllocatorProxy,
194    view_key: ViewKey,
195    last_presentation_time: i64,
196    future_presentation_times: Vec<PresentationTime>,
197    custom_render_offset: Option<i64>,
198    present_interval: i64,
199    // The number of `Flatland.Present()` calls we're authorized to make.  Initial value is 1.
200    // Decremented whenever `Present()` is called, and incremented whenever Scenic authorizes us to.
201    num_presents_allowed: usize,
202    // Only used for tracing, does not affect behavior.
203    pending_present_count: usize,
204    render_timer_scheduled: bool,
205    missed_frame: bool,
206    app_sender: UnboundedSender<MessageInternal>,
207    next_buffer_collection: u32,
208    plumber: Option<Plumber>,
209    retiring_plumbers: Vec<Plumber>,
210    next_image_id: u64,
211    previous_present_release_event: Option<Event>,
212    current_present_release_event: Option<Event>,
213    flatland_mouse_input_handlers: HashMap<u32, FlatlandMouseInputHandler>,
214    flatland_touch_input_handler: FlatlandTouchInputHandler,
215    keyboard_handler: KeyboardInputHandler,
216}
217
218impl FlatlandViewStrategy {
219    pub(crate) async fn new(
220        key: ViewKey,
221        flatland_params: FlatlandParams,
222        app_sender: UnboundedSender<MessageInternal>,
223    ) -> Result<ViewStrategyPtr, Error> {
224        let flatland = connect_to_protocol::<flatland::FlatlandMarker>()?;
225        if let Some(debug_name) = flatland_params.debug_name {
226            flatland.set_debug_name(&debug_name)?;
227        }
228
229        // Add trace event with the Flatland channel's koid, so that it can be correlated with a
230        // Flatland session within Scenic.
231        {
232            use fidl::endpoints::Proxy;
233            use zx::AsHandleRef;
234
235            let koid = flatland.as_channel().get_koid().unwrap().raw_koid();
236            instant!(
237                c"gfx",
238                c"FlatlandViewStrategy::new",
239                fuchsia_trace::Scope::Process,
240                "flatland_koid" => koid
241            );
242        }
243
244        flatland.create_transform(&TRANSFORM_ID)?;
245        flatland.set_root_transform(&TRANSFORM_ID)?;
246        setup_handle_flatland_events(flatland.take_event_stream(), key, app_sender.clone());
247        let allocator = connect_to_protocol::<flatland::AllocatorMarker>()?;
248        Self::create_parent_viewport_watcher(
249            &flatland,
250            app_sender.clone(),
251            key,
252            flatland_params.args.view_creation_token.expect("view_creation_token"),
253        )?;
254
255        let strat = Self {
256            flatland,
257            allocator,
258            view_key: key,
259            app_sender: app_sender,
260            last_presentation_time: fasync::MonotonicInstant::now().into_nanos(),
261            future_presentation_times: Vec::new(),
262            custom_render_offset: None,
263            present_interval: DEFAULT_PRESENT_INTERVAL,
264            num_presents_allowed: 1,
265            pending_present_count: 0,
266            render_timer_scheduled: false,
267            missed_frame: false,
268            plumber: None,
269            retiring_plumbers: Vec::new(),
270            next_buffer_collection: 1,
271            next_image_id: 1,
272            previous_present_release_event: None,
273            current_present_release_event: None,
274            flatland_mouse_input_handlers: HashMap::new(),
275            flatland_touch_input_handler: FlatlandTouchInputHandler::default(),
276            keyboard_handler: KeyboardInputHandler::new(),
277        };
278
279        Ok(Box::new(strat))
280    }
281
282    fn create_parent_viewport_watcher(
283        flatland: &flatland::FlatlandProxy,
284        app_sender: UnboundedSender<MessageInternal>,
285        view_key: ViewKey,
286        view_creation_token: fidl_fuchsia_ui_views::ViewCreationToken,
287    ) -> Result<(), Error> {
288        let (parent_viewport_watcher, server_end) =
289            create_proxy::<flatland::ParentViewportWatcherMarker>();
290
291        let viewref_pair = fuchsia_scenic::ViewRefPair::new()?;
292        let view_ref = fuchsia_scenic::duplicate_view_ref(&viewref_pair.view_ref)?;
293
294        // Use CreateView2 if input events are expected.
295        let input = Config::get().input;
296        if input {
297            let view_identity = fidl_fuchsia_ui_views::ViewIdentityOnCreation::from(viewref_pair);
298            let mut view_bound_protocols = flatland::ViewBoundProtocols::default();
299
300            let (touch_client, touch_server) = create_endpoints();
301            let (mouse_client, mouse_server) = create_endpoints();
302            let (view_ref_focused_client, view_ref_focused_server) = create_endpoints();
303
304            view_bound_protocols.touch_source = Some(touch_server);
305            view_bound_protocols.mouse_source = Some(mouse_server);
306            view_bound_protocols.view_ref_focused = Some(view_ref_focused_server);
307
308            flatland.create_view2(
309                view_creation_token,
310                view_identity,
311                view_bound_protocols,
312                server_end,
313            )?;
314
315            let touch_proxy = touch_client.into_proxy();
316            let touch_sender = app_sender.clone();
317
318            fasync::Task::local(async move {
319                let mut events: Vec<fidl_fuchsia_ui_pointer::TouchResponse> = Vec::new();
320                loop {
321                    let result = touch_proxy.watch(&events).await;
322                    match result {
323                        Ok(returned_events) => {
324                            events = returned_events
325                                .iter()
326                                .map(|event| fidl_fuchsia_ui_pointer::TouchResponse {
327                                    trace_flow_id: event
328                                        .pointer_sample
329                                        .as_ref()
330                                        .and(event.trace_flow_id),
331                                    response_type: event.pointer_sample.as_ref().and_then(|_| {
332                                        Some(fidl_fuchsia_ui_pointer::TouchResponseType::Yes)
333                                    }),
334                                    ..Default::default()
335                                })
336                                .collect();
337                            touch_sender
338                                .unbounded_send(MessageInternal::UserInputMessage(
339                                    view_key,
340                                    UserInputMessage::FlatlandTouchEvents(returned_events),
341                                ))
342                                .expect("failed to send MessageInternal.");
343                        }
344                        Err(fidl::Error::ClientChannelClosed { .. }) => {
345                            println!("touch event connection closed.");
346                            return;
347                        }
348                        Err(fidl_error) => {
349                            println!("touch event connection closed error: {:?}", fidl_error);
350                            return;
351                        }
352                    }
353                }
354            })
355            .detach();
356
357            let mouse_proxy = mouse_client.into_proxy();
358            let mouse_sender = app_sender.clone();
359
360            fasync::Task::local(async move {
361                loop {
362                    let result = mouse_proxy.watch().await;
363                    match result {
364                        Ok(returned_events) => {
365                            mouse_sender
366                                .unbounded_send(MessageInternal::UserInputMessage(
367                                    view_key,
368                                    UserInputMessage::FlatlandMouseEvents(returned_events),
369                                ))
370                                .expect("failed to send MessageInternal.");
371                        }
372                        Err(fidl::Error::ClientChannelClosed { .. }) => {
373                            println!("mouse event connection closed.");
374                            return;
375                        }
376                        Err(fidl_error) => {
377                            println!("mouse event connection closed error: {:?}", fidl_error);
378                            return;
379                        }
380                    }
381                }
382            })
383            .detach();
384
385            let view_ref_focused_proxy = view_ref_focused_client.into_proxy();
386            let view_ref_focused_sender = app_sender.clone();
387
388            fasync::Task::local(async move {
389                loop {
390                    let result = view_ref_focused_proxy.watch().await;
391                    match result {
392                        Ok(msg) => {
393                            view_ref_focused_sender
394                                .unbounded_send(MessageInternal::Focus(
395                                    view_key,
396                                    msg.focused.unwrap_or(false),
397                                ))
398                                .expect("failed to send MessageInternal.");
399                        }
400                        Err(fidl::Error::ClientChannelClosed { .. }) => {
401                            println!("ViewRefFocused connection closed.");
402                            return;
403                        }
404                        Err(fidl_error) => {
405                            println!("ViewRefFocused connection closed error: {:?}", fidl_error);
406                            return;
407                        }
408                    }
409                }
410            })
411            .detach();
412
413            Self::listen_for_key_events(view_ref, &app_sender, view_key)?;
414        } else {
415            flatland.create_view(view_creation_token, server_end)?;
416        }
417
418        let sender = app_sender;
419        fasync::Task::local(async move {
420            let mut layout_info_stream = HangingGetStream::new(
421                parent_viewport_watcher,
422                flatland::ParentViewportWatcherProxy::get_layout,
423            );
424
425            while let Some(result) = layout_info_stream.next().await {
426                match result {
427                    Ok(layout_info) => {
428                        if let Some(fidl_fuchsia_math::SizeU { width, height }) =
429                            layout_info.logical_size
430                        {
431                            sender
432                                .unbounded_send(MessageInternal::SizeChanged(
433                                    view_key,
434                                    size2(width, height).to_f32(),
435                                ))
436                                .expect("failed to send MessageInternal.");
437                        }
438                        if let Some(fidl_fuchsia_math::VecF { x, y }) =
439                            layout_info.device_pixel_ratio
440                        {
441                            sender
442                                .unbounded_send(MessageInternal::MetricsChanged(
443                                    view_key,
444                                    size2(x, y),
445                                ))
446                                .expect("failed to send MessageInternal.");
447                        }
448                    }
449                    Err(fidl::Error::ClientChannelClosed { .. }) => {
450                        println!("graph link connection closed.");
451                        return; // from spawned task closure
452                    }
453                    Err(fidl_error) => {
454                        println!("graph link GetLayout() error: {:?}", fidl_error);
455                        return; // from spawned task closure
456                    }
457                }
458            }
459        })
460        .detach();
461        Ok(())
462    }
463
464    fn do_present(
465        flatland: &flatland::FlatlandProxy,
466        _app_sender: &UnboundedSender<MessageInternal>,
467        _key: ViewKey,
468        presentation_time: i64,
469        release_event: Option<Event>,
470    ) {
471        flatland
472            .present(flatland::PresentArgs {
473                requested_presentation_time: Some(presentation_time),
474                acquire_fences: None,
475                release_fences: release_event.and_then(|release_event| Some(vec![release_event])),
476                unsquashable: Some(true),
477                ..Default::default()
478            })
479            .expect("present error");
480    }
481
482    fn make_view_assistant_context_with_time(
483        view_details: &ViewDetails,
484        image_id: ImageId,
485        image_index: u32,
486        app_sender: UnboundedSender<MessageInternal>,
487        presentation_time: MonotonicInstant,
488    ) -> ViewAssistantContext {
489        ViewAssistantContext {
490            key: view_details.key,
491            size: view_details.logical_size,
492            metrics: view_details.metrics,
493            presentation_time,
494            buffer_count: None,
495            image_id,
496            image_index,
497            app_sender,
498            mouse_cursor_position: None,
499            display_info: None,
500        }
501    }
502
503    fn make_view_assistant_context(
504        view_details: &ViewDetails,
505        image_id: ImageId,
506        image_index: u32,
507        app_sender: UnboundedSender<MessageInternal>,
508    ) -> ViewAssistantContext {
509        Self::make_view_assistant_context_with_time(
510            view_details,
511            image_id,
512            image_index,
513            app_sender,
514            MonotonicInstant::get(),
515        )
516    }
517
518    async fn create_plumber(&mut self, size: UintSize) -> Result<(), Error> {
519        let buffer_collection_id = self.next_buffer_collection;
520        self.next_buffer_collection = self.next_buffer_collection.wrapping_add(1);
521        let next_image_id = self.next_image_id;
522        self.next_image_id = self.next_image_id.wrapping_add(RENDER_BUFFER_COUNT as u64);
523        self.plumber = Some(
524            Plumber::new(
525                &self.flatland,
526                &self.allocator,
527                size.to_u32(),
528                fidl_fuchsia_images2::PixelFormat::B8G8R8A8,
529                RENDER_BUFFER_COUNT,
530                buffer_collection_id,
531                next_image_id,
532            )
533            .await
534            .expect("VmoPlumber::new"),
535        );
536        Ok(())
537    }
538
539    fn next_presentation_time(&self) -> PresentationTime {
540        let now = fasync::MonotonicInstant::now().into_nanos();
541        let legal_next = PresentationTime {
542            presentation_time: self.last_presentation_time + self.present_interval,
543            latch_point: now,
544        };
545        // Find a future presentation time that is at least half an interval past
546        // the last presentation time and has a latch point that is in the future.
547        let earliest_presentation_time = self.last_presentation_time + self.present_interval / 2;
548        let next = self
549            .future_presentation_times
550            .iter()
551            .find(|t| t.presentation_time >= earliest_presentation_time && t.latch_point > now)
552            .unwrap_or(&legal_next);
553        *next
554    }
555
556    fn schedule_render_timer(&mut self) {
557        if !self.render_timer_scheduled && !self.missed_frame {
558            let presentation_time = self.next_presentation_time();
559            // Conservative render offset to prefer throughput over low-latency.
560            // TODO: allow applications that prefer low-latency to control this
561            // dynamically.
562            const DEFAULT_RENDER_OFFSET_DELTA: i64 = 1_000_000; // 1ms
563            let render_offset = self
564                .custom_render_offset
565                .unwrap_or_else(|| self.present_interval - DEFAULT_RENDER_OFFSET_DELTA);
566            let render_time = presentation_time.latch_point - render_offset;
567            let timer =
568                fasync::Timer::new(fuchsia_async::MonotonicInstant::from_nanos(render_time));
569            let timer_sender = self.app_sender.clone();
570            let key = self.view_key;
571            fasync::Task::local(async move {
572                timer.await;
573                timer_sender.unbounded_send(MessageInternal::Render(key)).expect("unbounded_send");
574            })
575            .detach();
576            self.render_timer_scheduled = true;
577        }
578    }
579
580    fn render_to_image_from_plumber(
581        &mut self,
582        view_details: &ViewDetails,
583        view_assistant: &mut ViewAssistantPtr,
584    ) -> bool {
585        let presentation_time = self.next_presentation_time();
586        let plumber = self.plumber.as_mut().expect("plumber");
587        if let Some(available) = plumber.frame_set.get_available_image() {
588            duration!(c"gfx", c"FlatlandViewStrategy::render.render_to_image");
589            let available_index = plumber.image_indexes.get(&available).expect("index for image");
590            let render_context = Self::make_view_assistant_context_with_time(
591                view_details,
592                available,
593                *available_index,
594                self.app_sender.clone(),
595                MonotonicInstant::from_nanos(presentation_time.presentation_time),
596            );
597            let buffer_ready_event = Event::create();
598            view_assistant
599                .render(&mut plumber.context, buffer_ready_event, &render_context)
600                .unwrap_or_else(|e| panic!("Update error: {:?}", e));
601            plumber.frame_set.mark_prepared(available);
602            let key = view_details.key;
603            let collection_id = plumber.collection_id;
604            let release_event = Event::create();
605            let local_release_event =
606                release_event.duplicate_handle(zx::Rights::SAME_RIGHTS).expect("duplicate_handle");
607            let app_sender = self.app_sender.clone();
608            fasync::Task::local(async move {
609                let signals = OnSignals::new(&local_release_event, Signals::EVENT_SIGNALED);
610                signals.await.expect("to wait");
611                app_sender
612                    .unbounded_send(MessageInternal::ImageFreed(key, available, collection_id))
613                    .expect("unbounded_send");
614            })
615            .detach();
616            self.current_present_release_event = Some(release_event);
617
618            let image_id = flatland::ContentId { value: available };
619            self.flatland.set_content(&TRANSFORM_ID, &image_id).expect("fidl error");
620
621            // Image is guaranteed to be presented at this point.
622            plumber.frame_set.mark_presented(available);
623            true
624        } else {
625            instant!(
626                c"gfx",
627                c"FlatlandViewStrategy::no_available_image",
628                fuchsia_trace::Scope::Process
629            );
630            self.missed_frame = true;
631            false
632        }
633    }
634
635    fn retry_missed_frame(&mut self) {
636        if self.missed_frame {
637            self.missed_frame = false;
638            self.render_requested();
639        }
640    }
641
642    fn listen_for_key_events(
643        view_ref: ViewRef,
644        app_sender: &UnboundedSender<MessageInternal>,
645        key: ViewKey,
646    ) -> Result<(), Error> {
647        let keyboard = connect_to_protocol::<fidl_fuchsia_ui_input3::KeyboardMarker>()
648            .context("Failed to connect to Keyboard service")?;
649
650        let (listener_client_end, mut listener_stream) =
651            create_request_stream::<fidl_fuchsia_ui_input3::KeyboardListenerMarker>();
652
653        let event_sender = app_sender.clone();
654
655        fasync::Task::local(async move {
656            keyboard.add_listener(view_ref, listener_client_end).await.expect("add_listener");
657
658            while let Some(event) =
659                listener_stream.try_next().await.expect("Failed to get next key event")
660            {
661                match event {
662                    fidl_fuchsia_ui_input3::KeyboardListenerRequest::OnKeyEvent {
663                        event,
664                        responder,
665                        ..
666                    } => {
667                        // Carnelian always considers key events handled. In the future, there
668                        // might be a use case that requires letting view assistants change
669                        // this behavior but none currently exists.
670                        responder
671                            .send(fidl_fuchsia_ui_input3::KeyEventStatus::Handled)
672                            .expect("send");
673                        event_sender
674                            .unbounded_send(MessageInternal::UserInputMessage(
675                                key,
676                                UserInputMessage::ScenicKeyEvent(event),
677                            ))
678                            .expect("unbounded_send");
679                    }
680                }
681            }
682        })
683        .detach();
684        Ok(())
685    }
686}
687
688#[async_trait(?Send)]
689impl ViewStrategy for FlatlandViewStrategy {
690    fn initial_metrics(&self) -> Size {
691        size2(1.0, 1.0)
692    }
693
694    fn create_view_assistant_context(&self, view_details: &ViewDetails) -> ViewAssistantContext {
695        ViewAssistantContext {
696            key: view_details.key,
697            size: view_details.logical_size,
698            metrics: view_details.metrics,
699            presentation_time: Default::default(),
700            buffer_count: None,
701            image_id: Default::default(),
702            image_index: Default::default(),
703            app_sender: self.app_sender.clone(),
704            mouse_cursor_position: None,
705            display_info: None,
706        }
707    }
708
709    fn setup(&mut self, view_details: &ViewDetails, view_assistant: &mut ViewAssistantPtr) {
710        duration!(c"gfx", c"FlatlandViewStrategy::setup");
711        let render_context =
712            Self::make_view_assistant_context(view_details, 0, 0, self.app_sender.clone());
713        view_assistant.setup(&render_context).unwrap_or_else(|e| panic!("Setup error: {:?}", e));
714        self.custom_render_offset = view_assistant.get_render_offset();
715        self.render_requested();
716    }
717
718    async fn render(
719        &mut self,
720        view_details: &ViewDetails,
721        view_assistant: &mut ViewAssistantPtr,
722    ) -> bool {
723        let size = view_details.physical_size.floor().to_u32();
724        duration!(c"gfx", c"FlatlandViewStrategy::render",
725            "width" => size.width,
726            "height" => size.height);
727
728        self.render_timer_scheduled = false;
729        if size.width > 0 && size.height > 0 {
730            if self.num_presents_allowed == 0 {
731                instant!(c"gfx", c"FlatlandViewStrategy::present_is_not_allowed",
732                    fuchsia_trace::Scope::Process,
733                    "counts" => format!("{} pending {} allowed",
734                        self.pending_present_count,
735                        self.num_presents_allowed).as_str());
736                self.missed_frame = true;
737                return false;
738            }
739
740            if self.plumber.is_none() {
741                duration!(c"gfx", c"FlatlandViewStrategy::render.create_plumber");
742                self.create_plumber(size).await.expect("create_plumber");
743            } else {
744                let current_size = self.plumber.as_ref().expect("plumber").size;
745                if current_size != size {
746                    duration!(c"gfx", c"FlatlandViewStrategy::render.create_plumber");
747                    let retired_plumber = self.plumber.take().expect("plumber");
748                    self.retiring_plumbers.push(retired_plumber);
749                    self.create_plumber(size).await.expect("create_plumber");
750                }
751            }
752            self.render_to_image_from_plumber(view_details, view_assistant)
753        } else {
754            true
755        }
756    }
757
758    fn present(&mut self, _view_details: &ViewDetails) {
759        duration!(c"gfx", c"FlatlandViewStrategy::present");
760        if !self.missed_frame {
761            assert!(self.num_presents_allowed > 0);
762            let release_event = self.previous_present_release_event.take();
763            let presentation_time = self.next_presentation_time();
764            Self::do_present(
765                &self.flatland,
766                &self.app_sender,
767                self.view_key,
768                presentation_time.presentation_time,
769                release_event,
770            );
771            self.last_presentation_time = presentation_time.presentation_time;
772            self.pending_present_count += 1;
773            self.num_presents_allowed -= 1;
774            self.previous_present_release_event = self.current_present_release_event.take();
775        }
776    }
777
778    fn present_done(
779        &mut self,
780        _view_details: &ViewDetails,
781        _view_assistant: &mut ViewAssistantPtr,
782        info: fidl_fuchsia_scenic_scheduling::FramePresentedInfo,
783    ) {
784        let num_presents_handled = info.presentation_infos.len();
785        assert!(self.pending_present_count >= num_presents_handled);
786        self.pending_present_count -= num_presents_handled;
787        instant!(
788            c"gfx",
789            c"FlatlandViewStrategy::present_done",
790            fuchsia_trace::Scope::Process,
791            "presents" => format!("{} handled", num_presents_handled).as_str()
792        );
793    }
794
795    fn handle_focus(
796        &mut self,
797        view_details: &ViewDetails,
798        view_assistant: &mut ViewAssistantPtr,
799        focus: bool,
800    ) {
801        let mut render_context =
802            Self::make_view_assistant_context(view_details, 0, 0, self.app_sender.clone());
803        view_assistant
804            .handle_focus_event(&mut render_context, focus)
805            .unwrap_or_else(|e| panic!("handle_focus error: {:?}", e));
806    }
807
808    fn convert_user_input_message(
809        &mut self,
810        _view_details: &ViewDetails,
811        message: UserInputMessage,
812    ) -> Result<Vec<crate::input::Event>, Error> {
813        match message {
814            UserInputMessage::ScenicKeyEvent(key_event) => {
815                let converted_events = self.keyboard_handler.handle_key_event(&key_event);
816                Ok(converted_events)
817            }
818            UserInputMessage::FlatlandMouseEvents(mouse_events) => {
819                let mut events = Vec::new();
820                for mouse_event in &mouse_events {
821                    if let Some(pointer_sample) = mouse_event.pointer_sample.as_ref() {
822                        let device_id = pointer_sample.device_id.expect("device_id");
823                        let handler_entry = self
824                            .flatland_mouse_input_handlers
825                            .entry(device_id)
826                            .or_insert_with(|| FlatlandMouseInputHandler::new(device_id));
827                        events.extend(handler_entry.handle_mouse_events(&mouse_events));
828                    }
829                }
830                Ok(events)
831            }
832            UserInputMessage::FlatlandTouchEvents(touch_events) => {
833                Ok(self.flatland_touch_input_handler.handle_events(&touch_events))
834            }
835        }
836    }
837
838    fn image_freed(&mut self, image_id: u64, collection_id: u32) {
839        instant!(
840            c"gfx",
841            c"FlatlandViewStrategy::image_freed",
842            fuchsia_trace::Scope::Process,
843            "image_freed" => format!("{} in {}", image_id, collection_id).as_str()
844        );
845
846        self.retry_missed_frame();
847
848        if let Some(plumber) = self.plumber.as_mut() {
849            if plumber.collection_id == collection_id {
850                plumber.frame_set.mark_done_presenting(image_id);
851                return;
852            }
853        }
854
855        for retired_plumber in &mut self.retiring_plumbers {
856            if retired_plumber.collection_id == collection_id {
857                retired_plumber.frame_set.mark_done_presenting(image_id);
858                if retired_plumber.frame_set.no_images_in_use() {
859                    retired_plumber.enter_retirement(&self.flatland);
860                }
861            }
862        }
863
864        self.retiring_plumbers.retain(|plumber| !plumber.frame_set.no_images_in_use());
865    }
866
867    fn render_requested(&mut self) {
868        self.schedule_render_timer();
869    }
870
871    fn handle_on_next_frame_begin(
872        &mut self,
873        info: &fidl_fuchsia_ui_composition::OnNextFrameBeginValues,
874    ) {
875        self.num_presents_allowed += info.additional_present_credits.unwrap_or(0) as usize;
876        let future_presentation_infos =
877            info.future_presentation_infos.as_ref().expect("future_presentation_infos");
878        let present_intervals = future_presentation_infos.len();
879        instant!(
880            c"gfx",
881            c"FlatlandViewStrategy::handle_on_next_frame_begin",
882            fuchsia_trace::Scope::Process,
883            "counts" => format!("{} present_intervals", present_intervals).as_str()
884        );
885        if present_intervals > 0 {
886            let times: Vec<_> = future_presentation_infos
887                .iter()
888                .filter_map(|info| info.presentation_time)
889                .collect();
890            let average_interval: i64 =
891                times.as_slice().windows(2).map(|slice| slice[1] - slice[0]).sum::<i64>()
892                    / present_intervals as i64;
893            self.present_interval = average_interval;
894        } else {
895            self.present_interval = DEFAULT_PRESENT_INTERVAL;
896        }
897        self.future_presentation_times.splice(
898            ..,
899            future_presentation_infos.iter().map(|t| PresentationTime {
900                presentation_time: t.presentation_time.expect("presentation_time"),
901                latch_point: t.latch_point.expect("latch_point"),
902            }),
903        );
904
905        // Now that we have incremented `self.num_presents_allowed`, a missed frame might succeed.
906        self.retry_missed_frame();
907    }
908}