compositing_traits/
lib.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! The interface to the `compositing` crate.
6
7use std::fmt::{Debug, Error, Formatter};
8
9use base::Epoch;
10use base::id::{PipelineId, RenderingGroupId, WebViewId};
11use crossbeam_channel::Sender;
12use embedder_traits::{AnimationState, EventLoopWaker, TouchEventResult};
13use log::warn;
14use malloc_size_of_derive::MallocSizeOf;
15use rustc_hash::FxHashMap;
16use smallvec::SmallVec;
17use strum_macros::IntoStaticStr;
18use webrender_api::{DocumentId, FontVariation};
19
20pub mod display_list;
21pub mod rendering_context;
22pub mod viewport_description;
23
24use std::sync::{Arc, Mutex};
25
26use base::generic_channel::{self, GenericCallback, GenericSender};
27use bitflags::bitflags;
28use display_list::CompositorDisplayListInfo;
29use embedder_traits::ScreenGeometry;
30use euclid::default::Size2D as UntypedSize2D;
31use ipc_channel::ipc::{self, IpcSharedMemory};
32use profile_traits::mem::{OpaqueSender, ReportsChan};
33use serde::{Deserialize, Serialize};
34pub use webrender_api::ExternalImageSource;
35use webrender_api::units::{LayoutVector2D, TexelRect};
36use webrender_api::{
37    BuiltDisplayList, BuiltDisplayListDescriptor, ExternalImage, ExternalImageData,
38    ExternalImageHandler, ExternalImageId, ExternalScrollId, FontInstanceFlags, FontInstanceKey,
39    FontKey, ImageData, ImageDescriptor, ImageKey, NativeFontHandle,
40    PipelineId as WebRenderPipelineId,
41};
42
43use crate::viewport_description::ViewportDescription;
44
45/// Sends messages to the compositor.
46#[derive(Clone)]
47pub struct CompositorProxy {
48    pub sender: Sender<Result<CompositorMsg, ipc_channel::Error>>,
49    /// Access to [`Self::sender`] that is possible to send across an IPC
50    /// channel. These messages are routed via the router thread to
51    /// [`Self::sender`].
52    pub cross_process_compositor_api: CrossProcessCompositorApi,
53    pub event_loop_waker: Box<dyn EventLoopWaker>,
54}
55
56impl OpaqueSender<CompositorMsg> for CompositorProxy {
57    fn send(&self, message: CompositorMsg) {
58        CompositorProxy::send(self, message)
59    }
60}
61
62impl CompositorProxy {
63    pub fn send(&self, msg: CompositorMsg) {
64        self.route_msg(Ok(msg))
65    }
66
67    /// Helper method to route a deserialized IPC message to the receiver.
68    ///
69    /// This method is a temporary solution, and will be removed when migrating
70    /// to `GenericChannel`.
71    pub fn route_msg(&self, msg: Result<CompositorMsg, ipc_channel::Error>) {
72        if let Err(err) = self.sender.send(msg) {
73            warn!("Failed to send response ({:?}).", err);
74        }
75        self.event_loop_waker.wake();
76    }
77}
78
79/// Messages from (or via) the constellation thread to the compositor.
80#[derive(Deserialize, IntoStaticStr, Serialize)]
81pub enum CompositorMsg {
82    /// Alerts the compositor that the given pipeline has changed whether it is running animations.
83    ChangeRunningAnimationsState(WebViewId, PipelineId, AnimationState),
84    /// Create or update a webview, given its frame tree.
85    CreateOrUpdateWebView(SendableFrameTree),
86    /// Remove a webview.
87    RemoveWebView(WebViewId),
88    /// Script has handled a touch event, and either prevented or allowed default actions.
89    TouchEventProcessed(WebViewId, TouchEventResult),
90    /// Set whether to use less resources by stopping animations.
91    SetThrottled(WebViewId, PipelineId, bool),
92    /// WebRender has produced a new frame. This message informs the compositor that
93    /// the frame is ready. It contains a bool to indicate if it needs to composite and the
94    /// `DocumentId` of the new frame.
95    NewWebRenderFrameReady(DocumentId, bool),
96    /// Script or the Constellation is notifying the renderer that a Pipeline has finished
97    /// shutting down. The renderer will not discard the Pipeline until both report that
98    /// they have fully shut it down, to avoid recreating it due to any subsequent
99    /// messages.
100    PipelineExited(WebViewId, PipelineId, PipelineExitSource),
101    /// Inform WebRender of the existence of this pipeline.
102    SendInitialTransaction(WebViewId, WebRenderPipelineId),
103    /// Perform a scroll operation.
104    SendScrollNode(
105        WebViewId,
106        WebRenderPipelineId,
107        LayoutVector2D,
108        ExternalScrollId,
109    ),
110    /// Update the rendering epoch of the given `Pipeline`.
111    UpdateEpoch {
112        /// The [`WebViewId`] that this display list belongs to.
113        webview_id: WebViewId,
114        /// The [`PipelineId`] of the `Pipeline` to update.
115        pipeline_id: PipelineId,
116        /// The new [`Epoch`] value.
117        epoch: Epoch,
118    },
119    /// Inform WebRender of a new display list for the given pipeline.
120    SendDisplayList {
121        /// The [`WebViewId`] that this display list belongs to.
122        webview_id: WebViewId,
123        /// A descriptor of this display list used to construct this display list from raw data.
124        display_list_descriptor: BuiltDisplayListDescriptor,
125        /// An [ipc::IpcBytesReceiver] used to send the raw data of the display list.
126        display_list_receiver: ipc::IpcBytesReceiver,
127    },
128    /// Ask the renderer to generate a frame for the current set of display lists that
129    /// have been sent to the renderer.
130    GenerateFrame,
131    /// Create a new image key. The result will be returned via the
132    /// provided channel sender.
133    GenerateImageKey(GenericSender<ImageKey>),
134    /// The same as the above but it will be forwarded to the pipeline instead
135    /// of send via a channel.
136    GenerateImageKeysForPipeline(PipelineId),
137    /// Perform a resource update operation.
138    UpdateImages(SmallVec<[ImageUpdate; 1]>),
139    /// Pause all pipeline display list processing for the given pipeline until the
140    /// following image updates have been received. This is used to ensure that canvas
141    /// elements have had a chance to update their rendering and send the image update to
142    /// the renderer before their associated display list is actually displayed.
143    DelayNewFrameForCanvas(PipelineId, Epoch, Vec<ImageKey>),
144
145    /// Generate a new batch of font keys which can be used to allocate
146    /// keys asynchronously.
147    GenerateFontKeys(
148        usize,
149        usize,
150        GenericSender<(Vec<FontKey>, Vec<FontInstanceKey>)>,
151        RenderingGroupId,
152    ),
153    /// Add a font with the given data and font key.
154    AddFont(FontKey, Arc<IpcSharedMemory>, u32),
155    /// Add a system font with the given font key and handle.
156    AddSystemFont(FontKey, NativeFontHandle),
157    /// Add an instance of a font with the given instance key.
158    AddFontInstance(
159        FontInstanceKey,
160        FontKey,
161        f32,
162        FontInstanceFlags,
163        Vec<FontVariation>,
164    ),
165    /// Remove the given font resources from our WebRender instance.
166    RemoveFonts(Vec<FontKey>, Vec<FontInstanceKey>),
167    /// Measure the current memory usage associated with the compositor.
168    /// The report must be sent on the provided channel once it's complete.
169    CollectMemoryReport(ReportsChan),
170    /// A top-level frame has parsed a viewport metatag and is sending the new constraints.
171    Viewport(WebViewId, ViewportDescription),
172    /// Let the compositor know that the given WebView is ready to have a screenshot taken
173    /// after the given pipeline's epochs have been rendered.
174    ScreenshotReadinessReponse(WebViewId, FxHashMap<PipelineId, Epoch>),
175}
176
177impl Debug for CompositorMsg {
178    fn fmt(&self, formatter: &mut Formatter) -> Result<(), Error> {
179        let string: &'static str = self.into();
180        write!(formatter, "{string}")
181    }
182}
183
184#[derive(Deserialize, Serialize)]
185pub struct SendableFrameTree {
186    pub pipeline: CompositionPipeline,
187    pub children: Vec<SendableFrameTree>,
188}
189
190/// The subset of the pipeline that is needed for layer composition.
191#[derive(Clone, Deserialize, Serialize)]
192pub struct CompositionPipeline {
193    pub id: PipelineId,
194    pub webview_id: WebViewId,
195}
196
197/// A mechanism to send messages from ScriptThread to the parent process' WebRender instance.
198#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
199pub struct CrossProcessCompositorApi(GenericCallback<CompositorMsg>);
200
201impl CrossProcessCompositorApi {
202    /// Create a new [`CrossProcessCompositorApi`] struct.
203    pub fn new(callback: GenericCallback<CompositorMsg>) -> Self {
204        CrossProcessCompositorApi(callback)
205    }
206
207    /// Create a new [`CrossProcessCompositorApi`] struct that does not have a listener on the other
208    /// end to use for unit testing.
209    pub fn dummy() -> Self {
210        let callback = GenericCallback::new(|_msg| ()).unwrap();
211        Self(callback)
212    }
213
214    /// Inform WebRender of the existence of this pipeline.
215    pub fn send_initial_transaction(&self, webview_id: WebViewId, pipeline: WebRenderPipelineId) {
216        if let Err(e) = self
217            .0
218            .send(CompositorMsg::SendInitialTransaction(webview_id, pipeline))
219        {
220            warn!("Error sending initial transaction: {}", e);
221        }
222    }
223
224    /// Perform a scroll operation.
225    pub fn send_scroll_node(
226        &self,
227        webview_id: WebViewId,
228        pipeline_id: WebRenderPipelineId,
229        point: LayoutVector2D,
230        scroll_id: ExternalScrollId,
231    ) {
232        if let Err(e) = self.0.send(CompositorMsg::SendScrollNode(
233            webview_id,
234            pipeline_id,
235            point,
236            scroll_id,
237        )) {
238            warn!("Error sending scroll node: {}", e);
239        }
240    }
241
242    pub fn delay_new_frame_for_canvas(
243        &self,
244        pipeline_id: PipelineId,
245        canvas_epoch: Epoch,
246        image_keys: Vec<ImageKey>,
247    ) {
248        if let Err(error) = self.0.send(CompositorMsg::DelayNewFrameForCanvas(
249            pipeline_id,
250            canvas_epoch,
251            image_keys,
252        )) {
253            warn!("Error delaying frames for canvas image updates {error:?}");
254        }
255    }
256
257    /// Inform the renderer that the rendering epoch has advanced. This typically happens after
258    /// a new display list is sent and/or canvas and animated images are updated.
259    pub fn update_epoch(&self, webview_id: WebViewId, pipeline_id: PipelineId, epoch: Epoch) {
260        if let Err(error) = self.0.send(CompositorMsg::UpdateEpoch {
261            webview_id,
262            pipeline_id,
263            epoch,
264        }) {
265            warn!("Error updating epoch for pipeline: {error:?}");
266        }
267    }
268
269    /// Inform WebRender of a new display list for the given pipeline.
270    pub fn send_display_list(
271        &self,
272        webview_id: WebViewId,
273        display_list_info: &CompositorDisplayListInfo,
274        list: BuiltDisplayList,
275    ) {
276        let (display_list_data, display_list_descriptor) = list.into_data();
277        let (display_list_sender, display_list_receiver) = ipc::bytes_channel().unwrap();
278        if let Err(e) = self.0.send(CompositorMsg::SendDisplayList {
279            webview_id,
280            display_list_descriptor,
281            display_list_receiver,
282        }) {
283            warn!("Error sending display list: {}", e);
284        }
285
286        let display_list_info_serialized =
287            bincode::serialize(&display_list_info).unwrap_or_default();
288        if let Err(error) = display_list_sender.send(&display_list_info_serialized) {
289            warn!("Error sending display list info: {error}");
290        }
291
292        if let Err(error) = display_list_sender.send(&display_list_data.items_data) {
293            warn!("Error sending display list items: {error}");
294        }
295        if let Err(error) = display_list_sender.send(&display_list_data.cache_data) {
296            warn!("Error sending display list cache data: {error}");
297        }
298        if let Err(error) = display_list_sender.send(&display_list_data.spatial_tree) {
299            warn!("Error sending display spatial tree: {error}");
300        }
301    }
302
303    /// Ask the Servo renderer to generate a new frame after having new display lists.
304    pub fn generate_frame(&self) {
305        if let Err(error) = self.0.send(CompositorMsg::GenerateFrame) {
306            warn!("Error generating frame: {error}");
307        }
308    }
309
310    /// Create a new image key. Blocks until the key is available.
311    pub fn generate_image_key_blocking(&self) -> Option<ImageKey> {
312        let (sender, receiver) = generic_channel::channel().unwrap();
313        self.0.send(CompositorMsg::GenerateImageKey(sender)).ok()?;
314        receiver.recv().ok()
315    }
316
317    /// Sends a message to the compositor for creating new image keys.
318    /// The compositor will then send a batch of keys over the constellation to the script_thread
319    /// and the appropriate pipeline.
320    pub fn generate_image_key_async(&self, pipeline_id: PipelineId) {
321        if let Err(e) = self
322            .0
323            .send(CompositorMsg::GenerateImageKeysForPipeline(pipeline_id))
324        {
325            warn!("Could not send image keys to Compositor {}", e);
326        }
327    }
328
329    pub fn add_image(
330        &self,
331        key: ImageKey,
332        descriptor: ImageDescriptor,
333        data: SerializableImageData,
334    ) {
335        self.update_images([ImageUpdate::AddImage(key, descriptor, data)].into());
336    }
337
338    pub fn update_image(
339        &self,
340        key: ImageKey,
341        descriptor: ImageDescriptor,
342        data: SerializableImageData,
343        epoch: Option<Epoch>,
344    ) {
345        self.update_images([ImageUpdate::UpdateImage(key, descriptor, data, epoch)].into());
346    }
347
348    pub fn delete_image(&self, key: ImageKey) {
349        self.update_images([ImageUpdate::DeleteImage(key)].into());
350    }
351
352    /// Perform an image resource update operation.
353    pub fn update_images(&self, updates: SmallVec<[ImageUpdate; 1]>) {
354        if let Err(e) = self.0.send(CompositorMsg::UpdateImages(updates)) {
355            warn!("error sending image updates: {}", e);
356        }
357    }
358
359    pub fn remove_unused_font_resources(
360        &self,
361        keys: Vec<FontKey>,
362        instance_keys: Vec<FontInstanceKey>,
363    ) {
364        if keys.is_empty() && instance_keys.is_empty() {
365            return;
366        }
367        let _ = self.0.send(CompositorMsg::RemoveFonts(keys, instance_keys));
368    }
369
370    pub fn add_font_instance(
371        &self,
372        font_instance_key: FontInstanceKey,
373        font_key: FontKey,
374        size: f32,
375        flags: FontInstanceFlags,
376        variations: Vec<FontVariation>,
377    ) {
378        let _x = self.0.send(CompositorMsg::AddFontInstance(
379            font_instance_key,
380            font_key,
381            size,
382            flags,
383            variations,
384        ));
385    }
386
387    pub fn add_font(&self, font_key: FontKey, data: Arc<IpcSharedMemory>, index: u32) {
388        let _ = self.0.send(CompositorMsg::AddFont(font_key, data, index));
389    }
390
391    pub fn add_system_font(&self, font_key: FontKey, handle: NativeFontHandle) {
392        let _ = self.0.send(CompositorMsg::AddSystemFont(font_key, handle));
393    }
394
395    pub fn fetch_font_keys(
396        &self,
397        number_of_font_keys: usize,
398        number_of_font_instance_keys: usize,
399        rendering_group_id: RenderingGroupId,
400    ) -> (Vec<FontKey>, Vec<FontInstanceKey>) {
401        let (sender, receiver) = generic_channel::channel().expect("Could not create IPC channel");
402        let _ = self.0.send(CompositorMsg::GenerateFontKeys(
403            number_of_font_keys,
404            number_of_font_instance_keys,
405            sender,
406            rendering_group_id,
407        ));
408        receiver.recv().unwrap()
409    }
410
411    pub fn viewport(&self, webview_id: WebViewId, description: ViewportDescription) {
412        let _ = self
413            .0
414            .send(CompositorMsg::Viewport(webview_id, description));
415    }
416
417    pub fn pipeline_exited(
418        &self,
419        webview_id: WebViewId,
420        pipeline_id: PipelineId,
421        source: PipelineExitSource,
422    ) {
423        let _ = self.0.send(CompositorMsg::PipelineExited(
424            webview_id,
425            pipeline_id,
426            source,
427        ));
428    }
429}
430
431/// This trait is used as a bridge between the different GL clients
432/// in Servo that handles WebRender ExternalImages and the WebRender
433/// ExternalImageHandler API.
434//
435/// This trait is used to notify lock/unlock messages and get the
436/// required info that WR needs.
437pub trait WebrenderExternalImageApi {
438    fn lock(&mut self, id: u64) -> (ExternalImageSource<'_>, UntypedSize2D<i32>);
439    fn unlock(&mut self, id: u64);
440}
441
442/// Type of Webrender External Image Handler.
443pub enum WebrenderImageHandlerType {
444    WebGL,
445    Media,
446    WebGPU,
447}
448
449/// List of Webrender external images to be shared among all external image
450/// consumers (WebGL, Media, WebGPU).
451/// It ensures that external image identifiers are unique.
452#[derive(Default)]
453pub struct WebrenderExternalImageRegistry {
454    /// Map of all generated external images.
455    external_images: FxHashMap<ExternalImageId, WebrenderImageHandlerType>,
456    /// Id generator for the next external image identifier.
457    next_image_id: u64,
458}
459
460impl WebrenderExternalImageRegistry {
461    pub fn next_id(&mut self, handler_type: WebrenderImageHandlerType) -> ExternalImageId {
462        self.next_image_id += 1;
463        let key = ExternalImageId(self.next_image_id);
464        self.external_images.insert(key, handler_type);
465        key
466    }
467
468    pub fn remove(&mut self, key: &ExternalImageId) {
469        self.external_images.remove(key);
470    }
471
472    pub fn get(&self, key: &ExternalImageId) -> Option<&WebrenderImageHandlerType> {
473        self.external_images.get(key)
474    }
475}
476
477/// WebRender External Image Handler implementation.
478pub struct WebrenderExternalImageHandlers {
479    /// WebGL handler.
480    webgl_handler: Option<Box<dyn WebrenderExternalImageApi>>,
481    /// Media player handler.
482    media_handler: Option<Box<dyn WebrenderExternalImageApi>>,
483    /// WebGPU handler.
484    webgpu_handler: Option<Box<dyn WebrenderExternalImageApi>>,
485    /// Webrender external images.
486    external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
487}
488
489impl WebrenderExternalImageHandlers {
490    pub fn new() -> (Self, Arc<Mutex<WebrenderExternalImageRegistry>>) {
491        let external_images = Arc::new(Mutex::new(WebrenderExternalImageRegistry::default()));
492        (
493            Self {
494                webgl_handler: None,
495                media_handler: None,
496                webgpu_handler: None,
497                external_images: external_images.clone(),
498            },
499            external_images,
500        )
501    }
502
503    pub fn set_handler(
504        &mut self,
505        handler: Box<dyn WebrenderExternalImageApi>,
506        handler_type: WebrenderImageHandlerType,
507    ) {
508        match handler_type {
509            WebrenderImageHandlerType::WebGL => self.webgl_handler = Some(handler),
510            WebrenderImageHandlerType::Media => self.media_handler = Some(handler),
511            WebrenderImageHandlerType::WebGPU => self.webgpu_handler = Some(handler),
512        }
513    }
514}
515
516impl ExternalImageHandler for WebrenderExternalImageHandlers {
517    /// Lock the external image. Then, WR could start to read the
518    /// image content.
519    /// The WR client should not change the image content until the
520    /// unlock() call.
521    fn lock(
522        &mut self,
523        key: ExternalImageId,
524        _channel_index: u8,
525        _is_composited: bool,
526    ) -> ExternalImage<'_> {
527        let external_images = self.external_images.lock().unwrap();
528        let handler_type = external_images
529            .get(&key)
530            .expect("Tried to get unknown external image");
531        match handler_type {
532            WebrenderImageHandlerType::WebGL => {
533                let (source, size) = self.webgl_handler.as_mut().unwrap().lock(key.0);
534                let texture_id = match source {
535                    ExternalImageSource::NativeTexture(b) => b,
536                    _ => panic!("Wrong type"),
537                };
538                ExternalImage {
539                    uv: TexelRect::new(0.0, size.height as f32, size.width as f32, 0.0),
540                    source: ExternalImageSource::NativeTexture(texture_id),
541                }
542            },
543            WebrenderImageHandlerType::Media => {
544                let (source, size) = self.media_handler.as_mut().unwrap().lock(key.0);
545                let texture_id = match source {
546                    ExternalImageSource::NativeTexture(b) => b,
547                    _ => panic!("Wrong type"),
548                };
549                ExternalImage {
550                    uv: TexelRect::new(0.0, size.height as f32, size.width as f32, 0.0),
551                    source: ExternalImageSource::NativeTexture(texture_id),
552                }
553            },
554            WebrenderImageHandlerType::WebGPU => {
555                let (source, size) = self.webgpu_handler.as_mut().unwrap().lock(key.0);
556                ExternalImage {
557                    uv: TexelRect::new(0.0, size.height as f32, size.width as f32, 0.0),
558                    source,
559                }
560            },
561        }
562    }
563
564    /// Unlock the external image. The WR should not read the image
565    /// content after this call.
566    fn unlock(&mut self, key: ExternalImageId, _channel_index: u8) {
567        let external_images = self.external_images.lock().unwrap();
568        let handler_type = external_images
569            .get(&key)
570            .expect("Tried to get unknown external image");
571        match handler_type {
572            WebrenderImageHandlerType::WebGL => self.webgl_handler.as_mut().unwrap().unlock(key.0),
573            WebrenderImageHandlerType::Media => self.media_handler.as_mut().unwrap().unlock(key.0),
574            WebrenderImageHandlerType::WebGPU => {
575                self.webgpu_handler.as_mut().unwrap().unlock(key.0)
576            },
577        };
578    }
579}
580
581#[derive(Deserialize, Serialize)]
582/// Serializable image updates that must be performed by WebRender.
583pub enum ImageUpdate {
584    /// Register a new image.
585    AddImage(ImageKey, ImageDescriptor, SerializableImageData),
586    /// Delete a previously registered image registration.
587    DeleteImage(ImageKey),
588    /// Update an existing image registration.
589    UpdateImage(
590        ImageKey,
591        ImageDescriptor,
592        SerializableImageData,
593        Option<Epoch>,
594    ),
595}
596
597impl Debug for ImageUpdate {
598    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
599        match self {
600            Self::AddImage(image_key, image_desc, _) => f
601                .debug_tuple("AddImage")
602                .field(image_key)
603                .field(image_desc)
604                .finish(),
605            Self::DeleteImage(image_key) => f.debug_tuple("DeleteImage").field(image_key).finish(),
606            Self::UpdateImage(image_key, image_desc, _, epoch) => f
607                .debug_tuple("UpdateImage")
608                .field(image_key)
609                .field(image_desc)
610                .field(epoch)
611                .finish(),
612        }
613    }
614}
615
616#[derive(Debug, Deserialize, Serialize)]
617/// Serialized `ImageData`. It contains IPC byte channel receiver to prevent from loading bytes too
618/// slow.
619pub enum SerializableImageData {
620    /// A simple series of bytes, provided by the embedding and owned by WebRender.
621    /// The format is stored out-of-band, currently in ImageDescriptor.
622    Raw(IpcSharedMemory),
623    /// An image owned by the embedding, and referenced by WebRender. This may
624    /// take the form of a texture or a heap-allocated buffer.
625    External(ExternalImageData),
626}
627
628impl From<SerializableImageData> for ImageData {
629    fn from(value: SerializableImageData) -> Self {
630        match value {
631            SerializableImageData::Raw(shared_memory) => ImageData::new(shared_memory.to_vec()),
632            SerializableImageData::External(image) => ImageData::External(image),
633        }
634    }
635}
636
637/// A trait that exposes the embedding layer's `WebView` to the Servo renderer.
638/// This is to prevent a dependency cycle between the renderer and the embedding
639/// layer.
640pub trait WebViewTrait {
641    fn id(&self) -> WebViewId;
642    fn rendering_group_id(&self) -> Option<RenderingGroupId>;
643    fn screen_geometry(&self) -> Option<ScreenGeometry>;
644    fn set_animating(&self, new_value: bool);
645}
646
647/// What entity is reporting that a `Pipeline` has exited. Only when all have
648/// done this will the renderer discard its details.
649#[derive(Clone, Copy, Default, Deserialize, PartialEq, Serialize)]
650pub struct PipelineExitSource(u8);
651
652bitflags! {
653    impl PipelineExitSource: u8 {
654        const Script = 1 << 0;
655        const Constellation = 1 << 1;
656    }
657}