1use std::cell::Cell;
6use std::collections::hash_map::Entry;
7use std::rc::Rc;
8use std::sync::Arc;
9
10use base::Epoch;
11use base::cross_process_instant::CrossProcessInstant;
12use base::id::{PainterId, PipelineId, WebViewId};
13use compositing_traits::display_list::{CompositorDisplayListInfo, ScrollType};
14use compositing_traits::largest_contentful_paint_candidate::LCPCandidate;
15use compositing_traits::rendering_context::RenderingContext;
16use compositing_traits::viewport_description::ViewportDescription;
17use compositing_traits::{
18 ImageUpdate, PipelineExitSource, SendableFrameTree, WebRenderExternalImageHandlers,
19 WebRenderImageHandlerType, WebViewTrait,
20};
21use constellation_traits::{EmbedderToConstellationMessage, PaintMetricEvent};
22use crossbeam_channel::Sender;
23use dpi::PhysicalSize;
24use embedder_traits::{
25 CompositorHitTestResult, InputEvent, InputEventAndId, InputEventId, InputEventResult,
26 ScreenshotCaptureError, Scroll, ViewportDetails, WebViewPoint, WebViewRect,
27};
28use euclid::{Point2D, Rect, Scale, Size2D};
29use gleam::gl::RENDERER;
30use image::RgbaImage;
31use ipc_channel::ipc::{IpcBytesReceiver, IpcSharedMemory};
32use log::{debug, info, warn};
33use media::WindowGLContext;
34use profile_traits::time::{ProfilerCategory, ProfilerChan};
35use profile_traits::time_profile;
36use rustc_hash::{FxHashMap, FxHashSet};
37use servo_config::{opts, pref};
38use servo_geometry::DeviceIndependentPixel;
39use smallvec::SmallVec;
40use style_traits::CSSPixel;
41use webrender::{
42 MemoryReport, ONE_TIME_USAGE_HINT, RenderApi, ShaderPrecacheFlags, Transaction, UploadMethod,
43};
44use webrender_api::units::{
45 DevicePixel, DevicePoint, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D,
46 WorldPoint,
47};
48use webrender_api::{
49 self, BuiltDisplayList, BuiltDisplayListDescriptor, ColorF, DirtyRect, DisplayListPayload,
50 DocumentId, Epoch as WebRenderEpoch, ExternalScrollId, FontInstanceFlags, FontInstanceKey,
51 FontInstanceOptions, FontKey, FontVariation, ImageKey, NativeFontHandle,
52 PipelineId as WebRenderPipelineId, PropertyBinding, ReferenceFrameKind, RenderReasons,
53 SampledScrollOffset, SpaceAndClipInfo, SpatialId, TransformStyle,
54};
55use wr_malloc_size_of::MallocSizeOfOps;
56
57use crate::IOCompositor;
58use crate::compositor::{RepaintReason, WebRenderDebugOption};
59use crate::largest_contentful_paint_calculator::LargestContentfulPaintCalculator;
60use crate::refresh_driver::{AnimationRefreshDriverObserver, BaseRefreshDriver};
61use crate::render_notifier::RenderNotifier;
62use crate::screenshot::ScreenshotTaker;
63use crate::webrender_external_images::WebGLExternalImages;
64use crate::webview_renderer::{PinchZoomResult, ScrollResult, UnknownWebView, WebViewRenderer};
65
66pub(crate) struct Painter {
76 pub(crate) rendering_context: Rc<dyn RenderingContext>,
78
79 pub(crate) painter_id: PainterId,
81
82 pub(crate) webview_renderers: FxHashMap<WebViewId, WebViewRenderer>,
84
85 pub(crate) needs_repaint: Cell<RepaintReason>,
87
88 pub(crate) pending_frames: Cell<usize>,
90
91 refresh_driver: Rc<BaseRefreshDriver>,
93
94 animation_refresh_driver_observer: Rc<AnimationRefreshDriverObserver>,
96
97 pub(crate) webrender_api: RenderApi,
99
100 pub(crate) webrender_document: DocumentId,
102
103 pub(crate) webrender: Option<webrender::Renderer>,
105
106 webrender_gl: Rc<dyn gleam::gl::Gl>,
108
109 pub(crate) last_mouse_move_position: Option<DevicePoint>,
112
113 pub(crate) screenshot_taker: ScreenshotTaker,
115
116 pub(crate) frame_delayer: FrameDelayer,
120
121 embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
123
124 lcp_calculator: LargestContentfulPaintCalculator,
126}
127
128impl Drop for Painter {
129 fn drop(&mut self) {
130 self.webrender_api.stop_render_backend();
131 self.webrender_api.shut_down(true);
132 }
133}
134
135impl Painter {
136 pub(crate) fn new(
137 rendering_context: Rc<dyn RenderingContext>,
138 compositor: &IOCompositor,
139 ) -> Self {
140 let webrender_gl = rendering_context.gleam_gl_api();
141
142 if let Err(err) = rendering_context.make_current() {
144 warn!("Failed to make the rendering context current: {:?}", err);
145 }
146 debug_assert_eq!(webrender_gl.get_error(), gleam::gl::NO_ERROR,);
147
148 let id_manager = compositor.webrender_external_image_id_manager();
149 let mut external_image_handlers = Box::new(WebRenderExternalImageHandlers::new(id_manager));
150
151 let image_handler = Box::new(WebGLExternalImages::new(
153 compositor.webgl_threads(),
154 rendering_context.clone(),
155 compositor.swap_chains.clone(),
156 compositor.busy_webgl_contexts_map.clone(),
157 ));
158 external_image_handlers.set_handler(image_handler, WebRenderImageHandlerType::WebGl);
159
160 #[cfg(feature = "webgpu")]
161 external_image_handlers.set_handler(
162 Box::new(webgpu::WebGpuExternalImages::new(
163 compositor.webgpu_image_map(),
164 )),
165 WebRenderImageHandlerType::WebGpu,
166 );
167
168 WindowGLContext::initialize_image_handler(&mut external_image_handlers);
169
170 let embedder_to_constellation_sender = compositor.embedder_to_constellation_sender.clone();
171 let refresh_driver = Rc::new(BaseRefreshDriver::new(
172 compositor.event_loop_waker.clone_box(),
173 rendering_context.refresh_driver(),
174 ));
175 let animation_refresh_driver_observer = Rc::new(AnimationRefreshDriverObserver::new(
176 embedder_to_constellation_sender.clone(),
177 ));
178
179 rendering_context.prepare_for_rendering();
180 let clear_color = servo_config::pref!(shell_background_color_rgba);
181 let clear_color = ColorF::new(
182 clear_color[0] as f32,
183 clear_color[1] as f32,
184 clear_color[2] as f32,
185 clear_color[3] as f32,
186 );
187
188 let upload_method = if webrender_gl.get_string(RENDERER).starts_with("ANGLE") {
191 UploadMethod::Immediate
192 } else {
193 UploadMethod::PixelBuffer(ONE_TIME_USAGE_HINT)
194 };
195 let worker_threads = std::thread::available_parallelism()
196 .map(|i| i.get())
197 .unwrap_or(pref!(threadpools_fallback_worker_num) as usize)
198 .min(pref!(threadpools_webrender_workers_max).max(1) as usize);
199 let workers = Some(Arc::new(
200 rayon::ThreadPoolBuilder::new()
201 .num_threads(worker_threads)
202 .thread_name(|idx| format!("WRWorker#{}", idx))
203 .build()
204 .expect("Unable to initialize WebRender worker pool."),
205 ));
206
207 let painter_id = PainterId::next();
208 let (mut webrender, webrender_api_sender) = webrender::create_webrender_instance(
209 webrender_gl.clone(),
210 Box::new(RenderNotifier::new(
211 painter_id,
212 compositor.compositor_proxy.clone(),
213 )),
214 webrender::WebRenderOptions {
215 use_optimized_shaders: true,
220 resource_override_path: opts::get().shaders_path.clone(),
221 debug_flags: webrender::DebugFlags::empty(),
222 precache_flags: if pref!(gfx_precache_shaders) {
223 ShaderPrecacheFlags::FULL_COMPILE
224 } else {
225 ShaderPrecacheFlags::empty()
226 },
227 enable_aa: pref!(gfx_text_antialiasing_enabled),
228 enable_subpixel_aa: pref!(gfx_subpixel_text_antialiasing_enabled),
229 allow_texture_swizzling: pref!(gfx_texture_swizzling_enabled),
230 clear_color,
231 upload_method,
232 workers,
233 size_of_op: Some(servo_allocator::usable_size),
234 namespace_alloc_by_client: true,
237 shared_font_namespace: Some(painter_id.into()),
238 ..Default::default()
239 },
240 None,
241 )
242 .expect("Unable to initialize WebRender.");
243
244 webrender.set_external_image_handler(external_image_handlers);
245
246 let webrender_api = webrender_api_sender.create_api_by_client(painter_id.into());
247 let webrender_document = webrender_api.add_document(rendering_context.size2d().to_i32());
248
249 let gl_renderer = webrender_gl.get_string(gleam::gl::RENDERER);
250 let gl_version = webrender_gl.get_string(gleam::gl::VERSION);
251 info!("Running on {gl_renderer} with OpenGL version {gl_version}");
252
253 let painter = Painter {
254 painter_id,
255 embedder_to_constellation_sender,
256 webview_renderers: Default::default(),
257 rendering_context,
258 needs_repaint: Cell::default(),
259 pending_frames: Default::default(),
260 screenshot_taker: Default::default(),
261 refresh_driver,
262 animation_refresh_driver_observer,
263 webrender: Some(webrender),
264 webrender_api,
265 webrender_document,
266 webrender_gl,
267 last_mouse_move_position: None,
268 frame_delayer: Default::default(),
269 lcp_calculator: LargestContentfulPaintCalculator::new(),
270 };
271 painter.assert_gl_framebuffer_complete();
272 painter.clear_background();
273 painter
274 }
275
276 pub(crate) fn perform_updates(&mut self) {
277 if let Err(err) = self.rendering_context.make_current() {
279 warn!("Failed to make the rendering context current: {:?}", err);
280 }
281
282 let mut need_zoom = false;
283 let scroll_offset_updates: Vec<_> = self
284 .webview_renderers
285 .values_mut()
286 .filter_map(|webview_renderer| {
287 let (zoom, scroll_result) = webview_renderer
288 .process_pending_scroll_and_pinch_zoom_events(&self.webrender_api);
289 need_zoom = need_zoom || (zoom == PinchZoomResult::DidPinchZoom);
290 scroll_result
291 })
292 .collect();
293
294 self.send_zoom_and_scroll_offset_updates(need_zoom, scroll_offset_updates);
295 }
296
297 pub(crate) fn deinit(&mut self) {
298 if let Err(error) = self.rendering_context.make_current() {
299 warn!("Failed to make the rendering context current: {error:?}");
300 }
301 if let Some(webrender) = self.webrender.take() {
302 webrender.deinit();
303 }
304 self.lcp_calculator.clear();
305 }
306
307 #[track_caller]
308 fn assert_no_gl_error(&self) {
309 debug_assert_eq!(self.webrender_gl.get_error(), gleam::gl::NO_ERROR);
310 }
311
312 #[track_caller]
313 fn assert_gl_framebuffer_complete(&self) {
314 debug_assert_eq!(
315 (
316 self.webrender_gl.get_error(),
317 self.webrender_gl
318 .check_frame_buffer_status(gleam::gl::FRAMEBUFFER)
319 ),
320 (gleam::gl::NO_ERROR, gleam::gl::FRAMEBUFFER_COMPLETE)
321 );
322 }
323
324 pub(crate) fn webview_renderer(&self, webview_id: WebViewId) -> Option<&WebViewRenderer> {
325 self.webview_renderers.get(&webview_id)
326 }
327
328 pub(crate) fn webview_renderer_mut(
329 &mut self,
330 webview_id: WebViewId,
331 ) -> Option<&mut WebViewRenderer> {
332 self.webview_renderers.get_mut(&webview_id)
333 }
334
335 pub(crate) fn has_pending_frames(&self) -> bool {
339 self.pending_frames.get() != 0 || self.frame_delayer.pending_frame
340 }
341
342 pub(crate) fn set_needs_repaint(&self, reason: RepaintReason) {
343 let mut needs_repaint = self.needs_repaint.get();
344 needs_repaint.insert(reason);
345 self.needs_repaint.set(needs_repaint);
346 }
347
348 pub(crate) fn needs_repaint(&self) -> bool {
349 let repaint_reason = self.needs_repaint.get();
350 if repaint_reason.is_empty() {
351 return false;
352 }
353
354 !self.refresh_driver.wait_to_paint()
355 }
356
357 pub(crate) fn animation_callbacks_running(&self) -> bool {
359 self.webview_renderers
360 .values()
361 .any(WebViewRenderer::animation_callbacks_running)
362 }
363
364 pub(crate) fn animating_webviews(&self) -> Vec<WebViewId> {
365 self.webview_renderers
366 .values()
367 .filter_map(|webview_renderer| {
368 if webview_renderer.animating() {
369 Some(webview_renderer.id)
370 } else {
371 None
372 }
373 })
374 .collect()
375 }
376
377 pub(crate) fn send_to_constellation(&self, message: EmbedderToConstellationMessage) {
378 if let Err(error) = self.embedder_to_constellation_sender.send(message) {
379 warn!("Could not send message to constellation ({error:?})");
380 }
381 }
382
383 #[servo_tracing::instrument(skip_all)]
384 pub(crate) fn render(&mut self, time_profiler_channel: &ProfilerChan) {
385 let refresh_driver = self.refresh_driver.clone();
386 refresh_driver.notify_will_paint(self);
387
388 if let Err(err) = self.rendering_context.make_current() {
389 warn!("Failed to make the rendering context current: {:?}", err);
390 }
391 self.assert_no_gl_error();
392
393 self.rendering_context.prepare_for_rendering();
394
395 time_profile!(
396 ProfilerCategory::Compositing,
397 None,
398 time_profiler_channel.clone(),
399 || {
400 if let Some(webrender) = self.webrender.as_mut() {
401 webrender.update();
402 }
403
404 self.clear_background();
407 if let Some(webrender) = self.webrender.as_mut() {
408 let size = self.rendering_context.size2d().to_i32();
409 webrender.render(size, 0 ).ok();
410 }
411 }
412 );
413
414 self.needs_repaint.set(RepaintReason::empty());
417
418 self.screenshot_taker.maybe_take_screenshots(self);
419 self.send_pending_paint_metrics_messages_after_composite();
420 }
421
422 fn clear_background(&self) {
423 self.assert_gl_framebuffer_complete();
424
425 let color = servo_config::pref!(shell_background_color_rgba);
429 self.webrender_gl.clear_color(
430 color[0] as f32,
431 color[1] as f32,
432 color[2] as f32,
433 color[3] as f32,
434 );
435 self.webrender_gl.clear(gleam::gl::COLOR_BUFFER_BIT);
436 }
437
438 fn send_pending_paint_metrics_messages_after_composite(&mut self) {
446 let paint_time = CrossProcessInstant::now();
447 for webview_renderer in self.webview_renderers.values() {
448 for (pipeline_id, pipeline) in webview_renderer.pipelines.iter() {
449 let Some(current_epoch) = self
450 .webrender
451 .as_ref()
452 .and_then(|wr| wr.current_epoch(self.webrender_document, pipeline_id.into()))
453 else {
454 continue;
455 };
456
457 match pipeline.first_paint_metric.get() {
458 PaintMetricState::Seen(epoch, first_reflow) if epoch <= current_epoch => {
463 assert!(epoch <= current_epoch);
464 #[cfg(feature = "tracing")]
465 tracing::info!(
466 name: "FirstPaint",
467 servo_profiling = true,
468 epoch = ?epoch,
469 paint_time = ?paint_time,
470 pipeline_id = ?pipeline_id,
471 );
472
473 self.send_to_constellation(EmbedderToConstellationMessage::PaintMetric(
474 *pipeline_id,
475 PaintMetricEvent::FirstPaint(paint_time, first_reflow),
476 ));
477
478 pipeline.first_paint_metric.set(PaintMetricState::Sent);
479 },
480 _ => {},
481 }
482
483 match pipeline.first_contentful_paint_metric.get() {
484 PaintMetricState::Seen(epoch, first_reflow) if epoch <= current_epoch => {
485 #[cfg(feature = "tracing")]
486 tracing::info!(
487 name: "FirstContentfulPaint",
488 servo_profiling = true,
489 epoch = ?epoch,
490 paint_time = ?paint_time,
491 pipeline_id = ?pipeline_id,
492 );
493 self.send_to_constellation(EmbedderToConstellationMessage::PaintMetric(
494 *pipeline_id,
495 PaintMetricEvent::FirstContentfulPaint(paint_time, first_reflow),
496 ));
497 pipeline
498 .first_contentful_paint_metric
499 .set(PaintMetricState::Sent);
500 },
501 _ => {},
502 }
503
504 match pipeline.largest_contentful_paint_metric.get() {
505 PaintMetricState::Seen(epoch, _) if epoch <= current_epoch => {
506 if let Some(lcp) = self
507 .lcp_calculator
508 .calculate_largest_contentful_paint(paint_time, pipeline_id.into())
509 {
510 #[cfg(feature = "tracing")]
511 tracing::info!(
512 name: "LargestContentfulPaint",
513 servo_profiling = true,
514 paint_time = ?paint_time,
515 area = ?lcp.area,
516 lcp_type = ?lcp.lcp_type,
517 pipeline_id = ?pipeline_id,
518 );
519 self.send_to_constellation(
520 EmbedderToConstellationMessage::PaintMetric(
521 *pipeline_id,
522 PaintMetricEvent::LargestContentfulPaint(
523 lcp.paint_time,
524 lcp.area,
525 lcp.lcp_type,
526 ),
527 ),
528 );
529 }
530 pipeline
531 .largest_contentful_paint_metric
532 .set(PaintMetricState::Sent);
533 },
534 _ => {},
535 }
536 }
537 }
538 }
539
540 pub(crate) fn generate_frame(&self, transaction: &mut Transaction, reason: RenderReasons) {
542 transaction.generate_frame(0, true , false , reason);
543 self.pending_frames.set(self.pending_frames.get() + 1);
544 }
545
546 pub(crate) fn hit_test_at_point_with_api_and_document(
547 webrender_api: &RenderApi,
548 webrender_document: DocumentId,
549 point: DevicePoint,
550 ) -> Vec<CompositorHitTestResult> {
551 let world_point = WorldPoint::from_untyped(point.to_untyped());
553 let results = webrender_api.hit_test(webrender_document, world_point);
554
555 results
556 .items
557 .iter()
558 .map(|item| {
559 let pipeline_id = item.pipeline.into();
560 let external_scroll_id = ExternalScrollId(item.tag.0, item.pipeline);
561 CompositorHitTestResult {
562 pipeline_id,
563 point_in_viewport: Point2D::from_untyped(item.point_in_viewport.to_untyped()),
564 external_scroll_id,
565 }
566 })
567 .collect()
568 }
569
570 pub(crate) fn send_transaction(&mut self, transaction: Transaction) {
571 let _ = self.rendering_context.make_current();
572 self.webrender_api
573 .send_transaction(self.webrender_document, transaction);
574 }
575
576 fn send_root_pipeline_display_list_in_transaction(&self, transaction: &mut Transaction) {
580 let root_pipeline = WebRenderPipelineId(0, 1);
584 transaction.set_root_pipeline(root_pipeline);
585
586 let mut builder = webrender_api::DisplayListBuilder::new(root_pipeline);
587 builder.begin();
588
589 let root_reference_frame = SpatialId::root_reference_frame(root_pipeline);
590
591 let viewport_size = self.rendering_context.size2d().to_f32().to_untyped();
592 let viewport_rect = LayoutRect::from_origin_and_size(
593 LayoutPoint::zero(),
594 LayoutSize::from_untyped(viewport_size),
595 );
596
597 let root_clip_id = builder.define_clip_rect(root_reference_frame, viewport_rect);
598 let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]);
599 for webview_renderer in self.webview_renderers.values() {
600 if webview_renderer.hidden() {
601 continue;
602 }
603 let Some(pipeline_id) = webview_renderer.root_pipeline_id else {
604 continue;
605 };
606
607 let pinch_zoom_transform = webview_renderer.pinch_zoom().transform().to_untyped();
608 let device_pixels_per_page_pixel_not_including_pinch_zoom = webview_renderer
609 .device_pixels_per_page_pixel_not_including_pinch_zoom()
610 .get();
611
612 let transform = LayoutTransform::scale(
613 device_pixels_per_page_pixel_not_including_pinch_zoom,
614 device_pixels_per_page_pixel_not_including_pinch_zoom,
615 1.0,
616 )
617 .then(&LayoutTransform::from_untyped(
618 &pinch_zoom_transform.to_3d(),
619 ));
620
621 let webview_reference_frame = builder.push_reference_frame(
622 LayoutPoint::zero(),
623 root_reference_frame,
624 TransformStyle::Flat,
625 PropertyBinding::Value(transform),
626 ReferenceFrameKind::Transform {
627 is_2d_scale_translation: true,
628 should_snap: true,
629 paired_with_perspective: false,
630 },
631 webview_renderer.id.into(),
632 );
633
634 let scaled_webview_rect = webview_renderer.rect /
635 webview_renderer.device_pixels_per_page_pixel_not_including_pinch_zoom();
636 builder.push_iframe(
637 LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
638 LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
639 &SpaceAndClipInfo {
640 spatial_id: webview_reference_frame,
641 clip_chain_id,
642 },
643 pipeline_id.into(),
644 true,
645 );
646 }
647
648 let built_display_list = builder.end();
649
650 transaction.set_display_list(WebRenderEpoch(0), built_display_list);
654 self.update_transaction_with_all_scroll_offsets(transaction);
655 }
656
657 fn send_root_pipeline_display_list(&mut self) {
661 let mut transaction = Transaction::new();
662 self.send_root_pipeline_display_list_in_transaction(&mut transaction);
663 self.generate_frame(&mut transaction, RenderReasons::SCENE);
664 self.send_transaction(transaction);
665 }
666
667 fn update_transaction_with_all_scroll_offsets(&self, transaction: &mut Transaction) {
676 for webview_renderer in self.webview_renderers.values() {
677 for details in webview_renderer.pipelines.values() {
678 for node in details.scroll_tree.nodes.iter() {
679 let (Some(offset), Some(external_id)) = (node.offset(), node.external_id())
680 else {
681 continue;
682 };
683 if offset == LayoutVector2D::zero() {
685 continue;
686 }
687 transaction.set_scroll_offsets(
688 external_id,
689 vec![SampledScrollOffset {
690 offset,
691 generation: 0,
692 }],
693 );
694 }
695 }
696 }
697 }
698
699 fn send_zoom_and_scroll_offset_updates(
700 &mut self,
701 need_zoom: bool,
702 scroll_offset_updates: Vec<ScrollResult>,
703 ) {
704 if !need_zoom && scroll_offset_updates.is_empty() {
705 return;
706 }
707
708 let mut transaction = Transaction::new();
709 if need_zoom {
710 self.send_root_pipeline_display_list_in_transaction(&mut transaction);
711 }
712 for update in scroll_offset_updates {
713 transaction.set_scroll_offsets(
714 update.external_scroll_id,
715 vec![SampledScrollOffset {
716 offset: update.offset,
717 generation: 0,
718 }],
719 );
720 }
721
722 self.generate_frame(&mut transaction, RenderReasons::APZ);
723 self.send_transaction(transaction);
724 }
725
726 pub(crate) fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) {
727 let Some(webrender) = self.webrender.as_mut() else {
728 return;
729 };
730 let mut flags = webrender.get_debug_flags();
731 let flag = match option {
732 WebRenderDebugOption::Profiler => {
733 webrender::DebugFlags::PROFILER_DBG |
734 webrender::DebugFlags::GPU_TIME_QUERIES |
735 webrender::DebugFlags::GPU_SAMPLE_QUERIES
736 },
737 WebRenderDebugOption::TextureCacheDebug => webrender::DebugFlags::TEXTURE_CACHE_DBG,
738 WebRenderDebugOption::RenderTargetDebug => webrender::DebugFlags::RENDER_TARGET_DBG,
739 };
740 flags.toggle(flag);
741 webrender.set_debug_flags(flags);
742
743 let mut txn = Transaction::new();
744 self.generate_frame(&mut txn, RenderReasons::TESTING);
745 self.send_transaction(txn);
746 }
747
748 pub(crate) fn decrement_pending_frames(&self) {
749 self.pending_frames.set(self.pending_frames.get() - 1);
750 }
751
752 pub(crate) fn report_memory(&self) -> MemoryReport {
753 self.webrender_api
754 .report_memory(MallocSizeOfOps::new(servo_allocator::usable_size, None))
755 }
756
757 pub(crate) fn change_running_animations_state(
758 &mut self,
759 webview_id: WebViewId,
760 pipeline_id: PipelineId,
761 animation_state: embedder_traits::AnimationState,
762 ) {
763 let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
764 return;
765 };
766 if !webview_renderer.change_pipeline_running_animations_state(pipeline_id, animation_state)
767 {
768 return;
769 }
770 if !self
771 .animation_refresh_driver_observer
772 .notify_animation_state_changed(webview_renderer)
773 {
774 return;
775 }
776
777 self.refresh_driver
778 .add_observer(self.animation_refresh_driver_observer.clone());
779 }
780
781 pub(crate) fn set_frame_tree_for_webview(&mut self, frame_tree: &SendableFrameTree) {
782 debug!("{}: Setting frame tree for webview", frame_tree.pipeline.id);
783
784 let webview_id = frame_tree.pipeline.webview_id;
785 let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
786 warn!(
787 "Attempted to set frame tree on unknown WebView (perhaps closed?): {webview_id:?}"
788 );
789 return;
790 };
791
792 webview_renderer.set_frame_tree(frame_tree);
793 self.send_root_pipeline_display_list();
794 }
795
796 pub(crate) fn remove_webview(&mut self, webview_id: WebViewId) {
797 if self.webview_renderers.remove(&webview_id).is_none() {
798 warn!("Tried removing unknown WebView: {webview_id:?}");
799 return;
800 };
801
802 self.send_root_pipeline_display_list();
803 }
804
805 pub(crate) fn set_throttled(
806 &mut self,
807 webview_id: WebViewId,
808 pipeline_id: PipelineId,
809 throttled: bool,
810 ) {
811 let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
812 return;
813 };
814 if !webview_renderer.set_throttled(pipeline_id, throttled) {
815 return;
816 }
817
818 if self
819 .animation_refresh_driver_observer
820 .notify_animation_state_changed(webview_renderer)
821 {
822 self.refresh_driver
823 .add_observer(self.animation_refresh_driver_observer.clone());
824 }
825 }
826
827 pub(crate) fn notify_pipeline_exited(
828 &mut self,
829 webview_id: WebViewId,
830 pipeline_id: PipelineId,
831 pipeline_exit_source: PipelineExitSource,
832 ) {
833 debug!("Compositor got pipeline exited: {webview_id:?} {pipeline_id:?}",);
834 if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
835 webview_renderer.pipeline_exited(pipeline_id, pipeline_exit_source);
836 }
837 self.lcp_calculator
838 .remove_lcp_candidates_for_pipeline(pipeline_id.into());
839 }
840
841 pub(crate) fn send_initial_pipeline_transaction(
842 &mut self,
843 webview_id: WebViewId,
844 pipeline_id: WebRenderPipelineId,
845 ) {
846 let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
847 return warn!("Could not find WebView for incoming display list");
848 };
849
850 let starting_epoch = Epoch(0);
851 let details = webview_renderer.ensure_pipeline_details(pipeline_id.into());
852 details.display_list_epoch = Some(starting_epoch);
853
854 let mut txn = Transaction::new();
855 txn.set_display_list(starting_epoch.into(), (pipeline_id, Default::default()));
856
857 self.generate_frame(&mut txn, RenderReasons::SCENE);
858 self.send_transaction(txn);
859 }
860
861 pub(crate) fn scroll_node_by_delta(
862 &mut self,
863 webview_id: WebViewId,
864 pipeline_id: WebRenderPipelineId,
865 offset: LayoutVector2D,
866 external_scroll_id: webrender_api::ExternalScrollId,
867 ) {
868 let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
869 return;
870 };
871
872 let pipeline_id = pipeline_id.into();
873 let Some(pipeline_details) = webview_renderer.pipelines.get_mut(&pipeline_id) else {
874 return;
875 };
876
877 let Some(offset) = pipeline_details
878 .scroll_tree
879 .set_scroll_offset_for_node_with_external_scroll_id(
880 external_scroll_id,
881 offset,
882 ScrollType::Script,
883 )
884 else {
885 warn!("Could not scroll node with id: {external_scroll_id:?}");
888 return;
889 };
890
891 let mut transaction = Transaction::new();
892 transaction.set_scroll_offsets(
893 external_scroll_id,
894 vec![SampledScrollOffset {
895 offset,
896 generation: 0,
897 }],
898 );
899
900 self.generate_frame(&mut transaction, RenderReasons::APZ);
901 self.send_transaction(transaction);
902 }
903
904 pub(crate) fn scroll_viewport_by_delta(
905 &mut self,
906 webview_id: WebViewId,
907 delta: LayoutVector2D,
908 ) {
909 let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
910 return;
911 };
912 let (pinch_zoom_result, scroll_results) = webview_renderer.scroll_viewport_by_delta(delta);
913 self.send_zoom_and_scroll_offset_updates(
914 pinch_zoom_result == PinchZoomResult::DidPinchZoom,
915 scroll_results,
916 );
917 }
918
919 pub(crate) fn update_epoch(
920 &mut self,
921 webview_id: WebViewId,
922 pipeline_id: PipelineId,
923 epoch: Epoch,
924 ) {
925 let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
926 return warn!("Could not find WebView for Epoch update.");
927 };
928 webview_renderer
929 .ensure_pipeline_details(pipeline_id)
930 .display_list_epoch = Some(Epoch(epoch.0));
931 }
932
933 pub(crate) fn handle_new_display_list(
934 &mut self,
935 webview_id: WebViewId,
936 display_list_descriptor: BuiltDisplayListDescriptor,
937 display_list_receiver: IpcBytesReceiver,
938 ) {
939 let display_list_info = match display_list_receiver.recv() {
941 Ok(display_list_info) => display_list_info,
942 Err(error) => {
943 return warn!("Could not receive display list info: {error}");
944 },
945 };
946 let display_list_info: CompositorDisplayListInfo =
947 match bincode::deserialize(&display_list_info) {
948 Ok(display_list_info) => display_list_info,
949 Err(error) => {
950 return warn!("Could not deserialize display list info: {error}");
951 },
952 };
953 let items_data = match display_list_receiver.recv() {
954 Ok(display_list_data) => display_list_data,
955 Err(error) => {
956 return warn!("Could not receive WebRender display list items data: {error}");
957 },
958 };
959 let cache_data = match display_list_receiver.recv() {
960 Ok(display_list_data) => display_list_data,
961 Err(error) => {
962 return warn!("Could not receive WebRender display list cache data: {error}");
963 },
964 };
965 let spatial_tree = match display_list_receiver.recv() {
966 Ok(display_list_data) => display_list_data,
967 Err(error) => {
968 return warn!("Could not receive WebRender display list spatial tree: {error}.");
969 },
970 };
971 let built_display_list = BuiltDisplayList::from_data(
972 DisplayListPayload {
973 items_data,
974 cache_data,
975 spatial_tree,
976 },
977 display_list_descriptor,
978 );
979 let _span =
980 profile_traits::trace_span!("ScriptToCompositorMsg::BuiltDisplayList",).entered();
981 let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
982 return warn!("Could not find WebView for incoming display list");
983 };
984
985 let old_scale = webview_renderer.device_pixels_per_page_pixel();
986 let pipeline_id = display_list_info.pipeline_id;
987 let details = webview_renderer.ensure_pipeline_details(pipeline_id.into());
988
989 details.install_new_scroll_tree(display_list_info.scroll_tree);
990 details.viewport_scale = Some(display_list_info.viewport_details.hidpi_scale_factor);
991
992 let epoch = display_list_info.epoch.into();
993 let first_reflow = display_list_info.first_reflow;
994 if details.first_paint_metric.get() == PaintMetricState::Waiting {
995 details
996 .first_paint_metric
997 .set(PaintMetricState::Seen(epoch, first_reflow));
998 }
999
1000 if details.first_contentful_paint_metric.get() == PaintMetricState::Waiting &&
1001 display_list_info.is_contentful
1002 {
1003 details
1004 .first_contentful_paint_metric
1005 .set(PaintMetricState::Seen(epoch, first_reflow));
1006 }
1007
1008 let mut transaction = Transaction::new();
1009 let is_root_pipeline = Some(pipeline_id.into()) == webview_renderer.root_pipeline_id;
1010 if is_root_pipeline && old_scale != webview_renderer.device_pixels_per_page_pixel() {
1011 self.send_root_pipeline_display_list_in_transaction(&mut transaction);
1012 }
1013
1014 transaction.set_display_list(epoch, (pipeline_id, built_display_list));
1015
1016 self.update_transaction_with_all_scroll_offsets(&mut transaction);
1017 self.send_transaction(transaction);
1018 }
1019
1020 pub(crate) fn generate_frame_for_script(&mut self) {
1021 self.frame_delayer.set_pending_frame(true);
1022
1023 if !self.frame_delayer.needs_new_frame() {
1024 return;
1025 }
1026
1027 let mut transaction = Transaction::new();
1028 self.generate_frame(&mut transaction, RenderReasons::SCENE);
1029 self.send_transaction(transaction);
1030
1031 let waiting_pipelines = self.frame_delayer.take_waiting_pipelines();
1032
1033 self.send_to_constellation(
1034 EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
1035 waiting_pipelines,
1036 ),
1037 );
1038
1039 self.frame_delayer.set_pending_frame(false);
1040 self.screenshot_taker
1041 .prepare_screenshot_requests_for_render(self)
1042 }
1043
1044 pub(crate) fn generate_image_key(&self) -> ImageKey {
1045 self.webrender_api.generate_image_key()
1046 }
1047
1048 pub(crate) fn generate_image_keys(&self) -> Vec<ImageKey> {
1049 (0..pref!(image_key_batch_size))
1050 .map(|_| self.webrender_api.generate_image_key())
1051 .collect()
1052 }
1053
1054 pub(crate) fn update_images(&mut self, updates: SmallVec<[ImageUpdate; 1]>) {
1055 let mut txn = Transaction::new();
1056 for update in updates {
1057 match update {
1058 ImageUpdate::AddImage(key, desc, data) => {
1059 txn.add_image(key, desc, data.into(), None)
1060 },
1061 ImageUpdate::DeleteImage(key) => {
1062 txn.delete_image(key);
1063 self.frame_delayer.delete_image(key);
1064 },
1065 ImageUpdate::UpdateImage(key, desc, data, epoch) => {
1066 if let Some(epoch) = epoch {
1067 self.frame_delayer.update_image(key, epoch);
1068 }
1069 txn.update_image(key, desc, data.into(), &DirtyRect::All)
1070 },
1071 }
1072 }
1073
1074 if self.frame_delayer.needs_new_frame() {
1075 self.frame_delayer.set_pending_frame(false);
1076 self.generate_frame(&mut txn, RenderReasons::SCENE);
1077 let waiting_pipelines = self.frame_delayer.take_waiting_pipelines();
1078
1079 self.send_to_constellation(
1080 EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
1081 waiting_pipelines,
1082 ),
1083 );
1084
1085 self.screenshot_taker
1086 .prepare_screenshot_requests_for_render(&*self);
1087 }
1088
1089 self.send_transaction(txn);
1090 }
1091
1092 pub(crate) fn delay_new_frames_for_canvas(
1093 &mut self,
1094 pipeline_id: PipelineId,
1095 canvas_epoch: Epoch,
1096 image_keys: Vec<ImageKey>,
1097 ) {
1098 self.frame_delayer
1099 .add_delay(pipeline_id, canvas_epoch, image_keys);
1100 }
1101
1102 pub(crate) fn add_font(&mut self, font_key: FontKey, data: Arc<IpcSharedMemory>, index: u32) {
1103 let mut transaction = Transaction::new();
1104 transaction.add_raw_font(font_key, (**data).into(), index);
1105 self.send_transaction(transaction);
1106 }
1107
1108 pub(crate) fn add_system_font(&mut self, font_key: FontKey, native_handle: NativeFontHandle) {
1109 let mut transaction = Transaction::new();
1110 transaction.add_native_font(font_key, native_handle);
1111 self.send_transaction(transaction);
1112 }
1113
1114 pub(crate) fn add_font_instance(
1115 &mut self,
1116 instance_key: FontInstanceKey,
1117 font_key: FontKey,
1118 size: f32,
1119 flags: FontInstanceFlags,
1120 variations: Vec<FontVariation>,
1121 ) {
1122 let variations = if pref!(layout_variable_fonts_enabled) {
1123 variations
1124 } else {
1125 vec![]
1126 };
1127
1128 let mut transaction = Transaction::new();
1129
1130 let font_instance_options = FontInstanceOptions {
1131 flags,
1132 ..Default::default()
1133 };
1134 transaction.add_font_instance(
1135 instance_key,
1136 font_key,
1137 size,
1138 Some(font_instance_options),
1139 None,
1140 variations,
1141 );
1142
1143 self.send_transaction(transaction);
1144 }
1145
1146 pub(crate) fn remove_fonts(&mut self, keys: Vec<FontKey>, instance_keys: Vec<FontInstanceKey>) {
1147 let mut transaction = Transaction::new();
1148
1149 for instance in instance_keys.into_iter() {
1150 transaction.delete_font_instance(instance);
1151 }
1152 for key in keys.into_iter() {
1153 transaction.delete_font(key);
1154 }
1155
1156 self.send_transaction(transaction);
1157 }
1158
1159 pub(crate) fn generate_font_keys(
1162 &self,
1163 number_of_font_keys: usize,
1164 number_of_font_instance_keys: usize,
1165 ) -> (Vec<FontKey>, Vec<FontInstanceKey>) {
1166 let font_keys = (0..number_of_font_keys)
1167 .map(|_| self.webrender_api.generate_font_key())
1168 .collect();
1169 let font_instance_keys = (0..number_of_font_instance_keys)
1170 .map(|_| self.webrender_api.generate_font_instance_key())
1171 .collect();
1172 (font_keys, font_instance_keys)
1173 }
1174
1175 pub(crate) fn set_viewport_description(
1176 &mut self,
1177 webview_id: WebViewId,
1178 viewport_description: ViewportDescription,
1179 ) {
1180 if let Some(webview) = self.webview_renderers.get_mut(&webview_id) {
1181 webview.set_viewport_description(viewport_description);
1182 }
1183 }
1184
1185 pub(crate) fn handle_screenshot_readiness_reply(
1186 &self,
1187 webview_id: WebViewId,
1188 expected_epochs: FxHashMap<PipelineId, Epoch>,
1189 ) {
1190 self.screenshot_taker
1191 .handle_screenshot_readiness_reply(webview_id, expected_epochs, self);
1192 }
1193
1194 pub(crate) fn add_webview(
1195 &mut self,
1196 webview: Box<dyn WebViewTrait>,
1197 viewport_details: ViewportDetails,
1198 ) {
1199 self.webview_renderers
1200 .entry(webview.id())
1201 .or_insert(WebViewRenderer::new(
1202 webview,
1203 viewport_details,
1204 self.embedder_to_constellation_sender.clone(),
1205 self.refresh_driver.clone(),
1206 self.webrender_document,
1207 ));
1208 }
1209
1210 pub(crate) fn set_webview_hidden(
1211 &mut self,
1212 webview_id: WebViewId,
1213 hidden: bool,
1214 ) -> Result<(), UnknownWebView> {
1215 debug!("Setting WebView visiblity for {webview_id:?} to hidden={hidden}");
1216 let Some(webview_renderer) = self.webview_renderer_mut(webview_id) else {
1217 return Err(UnknownWebView(webview_id));
1218 };
1219 if !webview_renderer.set_hidden(hidden) {
1220 return Ok(());
1221 }
1222 self.send_root_pipeline_display_list();
1223 Ok(())
1224 }
1225
1226 pub(crate) fn set_hidpi_scale_factor(
1227 &mut self,
1228 webview_id: WebViewId,
1229 new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1230 ) {
1231 let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
1232 return;
1233 };
1234 if !webview_renderer.set_hidpi_scale_factor(new_scale_factor) {
1235 return;
1236 }
1237
1238 self.send_root_pipeline_display_list();
1239 self.set_needs_repaint(RepaintReason::Resize);
1240 }
1241
1242 pub(crate) fn resize_rendering_context(&mut self, new_size: PhysicalSize<u32>) {
1243 if self.rendering_context.size() == new_size {
1244 return;
1245 }
1246
1247 self.rendering_context.resize(new_size);
1248
1249 let new_size = Size2D::new(new_size.width as f32, new_size.height as f32);
1250 let new_viewport_rect = Rect::from(new_size).to_box2d();
1251 for webview_renderer in self.webview_renderers.values_mut() {
1252 webview_renderer.set_rect(new_viewport_rect);
1253 }
1254
1255 let mut transaction = Transaction::new();
1256 transaction.set_document_view(new_viewport_rect.to_i32());
1257 self.send_transaction(transaction);
1258
1259 self.send_root_pipeline_display_list();
1260 self.set_needs_repaint(RepaintReason::Resize);
1261 }
1262
1263 pub(crate) fn set_page_zoom(&mut self, webview_id: WebViewId, new_zoom: f32) {
1264 if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1265 webview_renderer.set_page_zoom(Scale::new(new_zoom));
1266 }
1267 }
1268
1269 pub(crate) fn page_zoom(&self, webview_id: WebViewId) -> f32 {
1270 self.webview_renderers
1271 .get(&webview_id)
1272 .map(|webview_renderer| webview_renderer.page_zoom.get())
1273 .unwrap_or_default()
1274 }
1275
1276 pub(crate) fn notify_input_event(&mut self, webview_id: WebViewId, event: InputEventAndId) {
1277 if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1278 match &event.event {
1279 InputEvent::MouseMove(event) => {
1280 if !event.is_compatibility_event_for_touch {
1282 let event_point = event
1283 .point
1284 .as_device_point(webview_renderer.device_pixels_per_page_pixel());
1285 self.last_mouse_move_position = Some(event_point);
1286 }
1287 },
1288 InputEvent::MouseLeftViewport(_) => {
1289 self.last_mouse_move_position = None;
1290 },
1291 _ => {},
1292 }
1293
1294 webview_renderer.notify_input_event(&self.webrender_api, &self.needs_repaint, event);
1295 }
1296 self.disable_lcp_calculation_for_webview(webview_id);
1297 }
1298
1299 pub(crate) fn notify_scroll_event(
1300 &mut self,
1301 webview_id: WebViewId,
1302 scroll: Scroll,
1303 point: WebViewPoint,
1304 ) {
1305 if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1306 webview_renderer.notify_scroll_event(scroll, point);
1307 }
1308 self.disable_lcp_calculation_for_webview(webview_id);
1309 }
1310
1311 pub(crate) fn pinch_zoom(
1312 &mut self,
1313 webview_id: WebViewId,
1314 pinch_zoom_delta: f32,
1315 center: DevicePoint,
1316 ) {
1317 if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1318 webview_renderer.adjust_pinch_zoom(pinch_zoom_delta, center);
1319 }
1320 }
1321
1322 pub(crate) fn device_pixels_per_page_pixel(
1323 &self,
1324 webview_id: WebViewId,
1325 ) -> Scale<f32, CSSPixel, DevicePixel> {
1326 self.webview_renderers
1327 .get(&webview_id)
1328 .map(WebViewRenderer::device_pixels_per_page_pixel)
1329 .unwrap_or_default()
1330 }
1331
1332 pub(crate) fn request_screenshot(
1333 &self,
1334 webview_id: WebViewId,
1335 rect: Option<WebViewRect>,
1336 callback: Box<dyn FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static>,
1337 ) {
1338 let Some(webview) = self.webview_renderers.get(&webview_id) else {
1339 return;
1340 };
1341
1342 let rect = rect.map(|rect| rect.as_device_rect(webview.device_pixels_per_page_pixel()));
1343 self.screenshot_taker
1344 .request_screenshot(webview_id, rect, callback);
1345 self.send_to_constellation(EmbedderToConstellationMessage::RequestScreenshotReadiness(
1346 webview_id,
1347 ));
1348 }
1349
1350 pub(crate) fn notify_input_event_handled(
1351 &mut self,
1352 webview_id: WebViewId,
1353 input_event_id: InputEventId,
1354 result: InputEventResult,
1355 ) {
1356 let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) else {
1357 warn!("Handled input event for unknown webview: {webview_id}");
1358 return;
1359 };
1360 webview_renderer.notify_input_event_handled(
1361 &self.webrender_api,
1362 &self.needs_repaint,
1363 input_event_id,
1364 result,
1365 );
1366 }
1367
1368 pub(crate) fn refresh_cursor(&self) {
1369 let Some(last_mouse_move_position) = self.last_mouse_move_position else {
1370 return;
1371 };
1372
1373 let Some(hit_test_result) = Self::hit_test_at_point_with_api_and_document(
1374 &self.webrender_api,
1375 self.webrender_document,
1376 last_mouse_move_position,
1377 )
1378 .first()
1379 .cloned() else {
1380 return;
1381 };
1382
1383 if let Err(error) = self.embedder_to_constellation_sender.send(
1384 EmbedderToConstellationMessage::RefreshCursor(hit_test_result.pipeline_id),
1385 ) {
1386 warn!("Sending event to constellation failed ({:?}).", error);
1387 }
1388 }
1389
1390 pub(crate) fn handle_new_webrender_frame_ready(&self, repaint_needed: bool) {
1391 if repaint_needed {
1392 self.refresh_cursor()
1393 }
1394
1395 if repaint_needed || self.animation_callbacks_running() {
1396 self.set_needs_repaint(RepaintReason::NewWebRenderFrame);
1397 }
1398
1399 if !repaint_needed {
1403 self.screenshot_taker
1404 .maybe_trigger_paint_for_screenshot(self);
1405 }
1406 }
1407
1408 pub(crate) fn webviews_needing_repaint(&self) -> Vec<WebViewId> {
1409 if self.needs_repaint() {
1410 self.webview_renderers
1411 .values()
1412 .map(|webview_renderer| webview_renderer.id)
1413 .collect()
1414 } else {
1415 Vec::new()
1416 }
1417 }
1418
1419 pub(crate) fn scroll_trees_memory_usage(
1420 &self,
1421 ops: &mut malloc_size_of::MallocSizeOfOps,
1422 ) -> usize {
1423 self.webview_renderers
1424 .values()
1425 .map(|renderer| renderer.scroll_trees_memory_usage(ops))
1426 .sum::<usize>()
1427 }
1428
1429 pub(crate) fn append_lcp_candidate(
1430 &mut self,
1431 lcp_candidate: LCPCandidate,
1432 webview_id: WebViewId,
1433 pipeline_id: PipelineId,
1434 epoch: Epoch,
1435 ) {
1436 if self
1437 .lcp_calculator
1438 .append_lcp_candidate(webview_id, pipeline_id.into(), lcp_candidate)
1439 {
1440 if let Some(webview_renderer) = self.webview_renderers.get_mut(&webview_id) {
1441 webview_renderer
1442 .ensure_pipeline_details(pipeline_id)
1443 .largest_contentful_paint_metric
1444 .set(PaintMetricState::Seen(epoch.into(), false));
1445 }
1446 };
1447 }
1448
1449 fn disable_lcp_calculation_for_webview(&mut self, webview_id: WebViewId) {
1451 self.lcp_calculator.add_to_disabled_lcp_webviews(webview_id);
1452 }
1453}
1454
1455#[derive(Default)]
1465pub(crate) struct FrameDelayer {
1466 image_epochs: FxHashMap<ImageKey, Epoch>,
1470 pending_canvas_images: FxHashMap<ImageKey, Epoch>,
1472 pub(crate) pending_frame: bool,
1474 waiting_pipelines: FxHashSet<PipelineId>,
1477}
1478
1479impl FrameDelayer {
1480 pub(crate) fn delete_image(&mut self, image_key: ImageKey) {
1481 self.image_epochs.remove(&image_key);
1482 self.pending_canvas_images.remove(&image_key);
1483 }
1484
1485 pub(crate) fn update_image(&mut self, image_key: ImageKey, epoch: Epoch) {
1486 self.image_epochs.insert(image_key, epoch);
1487 let Entry::Occupied(entry) = self.pending_canvas_images.entry(image_key) else {
1488 return;
1489 };
1490 if *entry.get() <= epoch {
1491 entry.remove();
1492 }
1493 }
1494
1495 pub(crate) fn add_delay(
1496 &mut self,
1497 pipeline_id: PipelineId,
1498 canvas_epoch: Epoch,
1499 image_keys: Vec<ImageKey>,
1500 ) {
1501 for image_key in image_keys.into_iter() {
1502 if self
1505 .image_epochs
1506 .get(&image_key)
1507 .is_some_and(|epoch_seen| *epoch_seen >= canvas_epoch)
1508 {
1509 continue;
1510 }
1511 self.pending_canvas_images.insert(image_key, canvas_epoch);
1512 }
1513 self.waiting_pipelines.insert(pipeline_id);
1514 }
1515
1516 pub(crate) fn needs_new_frame(&self) -> bool {
1517 self.pending_frame && self.pending_canvas_images.is_empty()
1518 }
1519
1520 pub(crate) fn set_pending_frame(&mut self, value: bool) {
1521 self.pending_frame = value;
1522 }
1523
1524 pub(crate) fn take_waiting_pipelines(&mut self) -> Vec<PipelineId> {
1525 self.waiting_pipelines.drain().collect()
1526 }
1527}
1528
1529#[derive(Clone, Copy, PartialEq)]
1534pub(crate) enum PaintMetricState {
1535 Waiting,
1537 Seen(WebRenderEpoch, bool ),
1540 Sent,
1542}