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