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