1use std::cell::{Cell, Ref, RefCell, RefMut};
6use std::collections::HashMap;
7use std::env;
8use std::fs::create_dir_all;
9use std::rc::Rc;
10use std::sync::{Arc, Mutex};
11use std::time::{SystemTime, UNIX_EPOCH};
12
13use base::generic_channel::RoutedReceiver;
14use base::id::{PainterId, WebViewId};
15use bitflags::bitflags;
16use canvas_traits::webgl::WebGLThreads;
17use compositing_traits::{CompositorMsg, WebRenderExternalImageRegistry, WebViewTrait};
18use constellation_traits::EmbedderToConstellationMessage;
19use crossbeam_channel::Sender;
20use dpi::PhysicalSize;
21use embedder_traits::{
22 InputEventAndId, InputEventId, InputEventResult, ScreenshotCaptureError, Scroll, ShutdownState,
23 ViewportDetails, WebViewPoint, WebViewRect,
24};
25use euclid::{Scale, Size2D};
26use image::RgbaImage;
27use ipc_channel::ipc::{self};
28use log::debug;
29use profile_traits::mem::{
30 ProcessReports, ProfilerRegistration, Report, ReportKind, perform_memory_report,
31};
32use profile_traits::path;
33use profile_traits::time::{self as profile_time};
34use servo_geometry::DeviceIndependentPixel;
35use style_traits::CSSPixel;
36use webrender::{CaptureBits, MemoryReport};
37use webrender_api::units::{DevicePixel, DevicePoint, DeviceRect};
38
39use crate::InitialCompositorState;
40use crate::painter::Painter;
41use crate::webview_renderer::UnknownWebView;
42
43#[derive(Copy, Clone)]
45pub enum WebRenderDebugOption {
46 Profiler,
47 TextureCacheDebug,
48 RenderTargetDebug,
49}
50
51pub struct IOCompositor {
53 painters: Vec<Rc<RefCell<Painter>>>,
56
57 shutdown_state: Rc<Cell<ShutdownState>>,
60
61 compositor_receiver: RoutedReceiver<CompositorMsg>,
63
64 pub(crate) embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
66
67 time_profiler_chan: profile_time::ProfilerChan,
69
70 _mem_profiler_registration: ProfilerRegistration,
73}
74
75#[derive(Clone, Copy, Default, PartialEq)]
77pub(crate) struct RepaintReason(u8);
78
79bitflags! {
80 impl RepaintReason: u8 {
81 const ReadyForScreenshot = 1 << 0;
83 const ChangedAnimationState = 1 << 1;
85 const NewWebRenderFrame = 1 << 2;
87 const Resize = 1 << 3;
89 }
90}
91
92impl IOCompositor {
93 pub fn new(state: InitialCompositorState) -> Rc<RefCell<Self>> {
94 let registration = state.mem_profiler_chan.prepare_memory_reporting(
95 "compositor".into(),
96 state.compositor_proxy.clone(),
97 CompositorMsg::CollectMemoryReport,
98 );
99
100 let compositor = Rc::new(RefCell::new(IOCompositor {
101 painters: Default::default(),
102 shutdown_state: state.shutdown_state,
103 compositor_receiver: state.receiver,
104 embedder_to_constellation_sender: state.embedder_to_constellation_sender.clone(),
105 time_profiler_chan: state.time_profiler_chan,
106 _mem_profiler_registration: registration,
107 }));
108
109 let painter = Painter::new(
110 state.rendering_context,
111 state.compositor_proxy,
112 state.event_loop_waker,
113 state.refresh_driver,
114 state.shaders_path,
115 compositor.borrow().embedder_to_constellation_sender.clone(),
116 #[cfg(feature = "webxr")]
117 state.webxr_registry,
118 );
119
120 compositor
121 .borrow_mut()
122 .painters
123 .push(Rc::new(RefCell::new(painter)));
124
125 compositor
126 }
127
128 pub(crate) fn painter<'a>(&'a self) -> Ref<'a, Painter> {
129 self.painters[0].borrow()
130 }
131
132 pub(crate) fn painter_mut<'a>(&'a self) -> RefMut<'a, Painter> {
133 self.painters[0].borrow_mut()
134 }
135
136 pub fn painter_id(&self) -> PainterId {
137 self.painters[0].borrow().painter_id
138 }
139
140 pub fn deinit(&mut self) {
141 for painter in &self.painters {
142 painter.borrow_mut().deinit();
143 }
144 }
145
146 pub fn rendering_context_size(&self) -> Size2D<u32, DevicePixel> {
147 self.painter().rendering_context.size2d()
148 }
149
150 pub fn webgl_threads(&self) -> WebGLThreads {
151 self.painter().webgl_threads.clone()
152 }
153
154 pub fn webrender_external_images(&self) -> Arc<Mutex<WebRenderExternalImageRegistry>> {
155 self.painter().webrender_external_images.clone()
156 }
157
158 pub fn webxr_running(&self) -> bool {
159 #[cfg(feature = "webxr")]
160 {
161 self.painter().webxr_main_thread.running()
162 }
163 #[cfg(not(feature = "webxr"))]
164 {
165 false
166 }
167 }
168
169 #[cfg(feature = "webxr")]
170 pub fn webxr_main_thread_registry(&self) -> webxr_api::Registry {
171 self.painter().webxr_main_thread.registry()
172 }
173
174 #[cfg(feature = "webgpu")]
175 pub fn webgpu_image_map(&self) -> webgpu::canvas_context::WGPUImageMap {
176 self.painter().webgpu_image_map.clone()
177 }
178
179 pub fn webviews_needing_repaint(&self) -> Vec<WebViewId> {
180 self.painters
181 .iter()
182 .flat_map(|painter| painter.borrow().webviews_needing_repaint())
183 .collect()
184 }
185
186 pub fn finish_shutting_down(&self) {
187 while self.compositor_receiver.try_recv().is_ok() {}
190
191 if let Ok((sender, receiver)) = ipc::channel() {
193 self.time_profiler_chan
194 .send(profile_time::ProfilerMsg::Exit(sender));
195 let _ = receiver.recv();
196 }
197 }
198
199 fn handle_browser_message(&self, msg: CompositorMsg) {
200 trace_msg_from_constellation!(msg, "{msg:?}");
201
202 match self.shutdown_state() {
203 ShutdownState::NotShuttingDown => {},
204 ShutdownState::ShuttingDown => {
205 self.handle_browser_message_while_shutting_down(msg);
206 return;
207 },
208 ShutdownState::FinishedShuttingDown => {
209 return;
211 },
212 }
213
214 match msg {
215 CompositorMsg::CollectMemoryReport(sender) => {
216 self.collect_memory_report(sender);
217 },
218 CompositorMsg::ChangeRunningAnimationsState(
219 webview_id,
220 pipeline_id,
221 animation_state,
222 ) => {
223 self.painter_mut().change_running_animations_state(
224 webview_id,
225 pipeline_id,
226 animation_state,
227 );
228 },
229 CompositorMsg::CreateOrUpdateWebView(frame_tree) => {
230 self.painter_mut().set_frame_tree_for_webview(&frame_tree);
231 },
232 CompositorMsg::RemoveWebView(webview_id) => {
233 self.painter_mut().remove_webview(webview_id);
234 },
235 CompositorMsg::SetThrottled(webview_id, pipeline_id, throttled) => {
236 self.painter_mut()
237 .set_throttled(webview_id, pipeline_id, throttled);
238 },
239 CompositorMsg::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
240 self.painter_mut().notify_pipeline_exited(
241 webview_id,
242 pipeline_id,
243 pipeline_exit_source,
244 );
245 },
246 CompositorMsg::NewWebRenderFrameReady(..) => {
247 unreachable!("New WebRender frames should be handled in the caller.");
248 },
249 CompositorMsg::SendInitialTransaction(webview_id, pipeline_id) => {
250 self.painter_mut()
251 .send_initial_pipeline_transaction(webview_id, pipeline_id);
252 },
253 CompositorMsg::ScrollNodeByDelta(
254 webview_id,
255 pipeline_id,
256 offset,
257 external_scroll_id,
258 ) => {
259 self.painter_mut().scroll_node_by_delta(
260 webview_id,
261 pipeline_id,
262 offset,
263 external_scroll_id,
264 );
265 },
266 CompositorMsg::ScrollViewportByDelta(webview_id, delta) => {
267 self.painter_mut()
268 .scroll_viewport_by_delta(webview_id, delta);
269 },
270 CompositorMsg::UpdateEpoch {
271 webview_id,
272 pipeline_id,
273 epoch,
274 } => {
275 self.painter_mut()
276 .update_epoch(webview_id, pipeline_id, epoch);
277 },
278 CompositorMsg::SendDisplayList {
279 webview_id,
280 display_list_descriptor,
281 display_list_receiver,
282 } => {
283 self.painter_mut().handle_new_display_list(
284 webview_id,
285 display_list_descriptor,
286 display_list_receiver,
287 );
288 },
289 CompositorMsg::GenerateFrame => {
290 self.painter_mut().generate_frame_for_script();
291 },
292 CompositorMsg::GenerateImageKey(sender) => {
293 let _ = sender.send(self.painter().generate_image_key());
294 },
295 CompositorMsg::GenerateImageKeysForPipeline(pipeline_id) => {
296 let _ = self.embedder_to_constellation_sender.send(
297 EmbedderToConstellationMessage::SendImageKeysForPipeline(
298 pipeline_id,
299 self.painter().generate_image_keys(),
300 ),
301 );
302 },
303 CompositorMsg::UpdateImages(updates) => {
304 self.painter_mut().update_images(updates);
305 },
306 CompositorMsg::DelayNewFrameForCanvas(pipeline_id, canvas_epoch, image_keys) => self
307 .painter_mut()
308 .delay_new_frames_for_canvas(pipeline_id, canvas_epoch, image_keys),
309 CompositorMsg::AddFont(font_key, data, index) => {
310 self.painter_mut().add_font(font_key, data, index);
311 },
312 CompositorMsg::AddSystemFont(font_key, native_handle) => {
313 self.painter_mut().add_system_font(font_key, native_handle);
314 },
315 CompositorMsg::AddFontInstance(
316 font_instance_key,
317 font_key,
318 size,
319 flags,
320 variations,
321 ) => {
322 self.painter_mut().add_font_instance(
323 font_instance_key,
324 font_key,
325 size,
326 flags,
327 variations,
328 );
329 },
330 CompositorMsg::RemoveFonts(keys, instance_keys) => {
331 self.painter_mut().remove_fonts(keys, instance_keys);
332 },
333 CompositorMsg::GenerateFontKeys(
334 number_of_font_keys,
335 number_of_font_instance_keys,
336 result_sender,
337 _painter_id,
338 ) => {
339 let _ = result_sender.send(
340 self.painter_mut()
341 .generate_font_keys(number_of_font_keys, number_of_font_instance_keys),
342 );
343 },
344 CompositorMsg::Viewport(webview_id, viewport_description) => {
345 self.painter_mut()
346 .set_viewport_description(webview_id, viewport_description);
347 },
348 CompositorMsg::ScreenshotReadinessReponse(webview_id, pipelines_and_epochs) => {
349 self.painter()
350 .handle_screenshot_readiness_reply(webview_id, pipelines_and_epochs);
351 },
352 CompositorMsg::SendLCPCandidate(lcp_candidate, webview_id, pipeline_id, epoch) => {
353 self.painter_mut().append_lcp_candidate(
354 lcp_candidate,
355 webview_id,
356 pipeline_id,
357 epoch,
358 );
359 },
360 }
361 }
362
363 fn collect_memory_report(&self, sender: profile_traits::mem::ReportsChan) {
364 let mut memory_report = MemoryReport::default();
365 for painter in &self.painters {
366 memory_report += painter.borrow().report_memory();
367 }
368
369 let mut reports = vec![
370 Report {
371 path: path!["webrender", "fonts"],
372 kind: ReportKind::ExplicitJemallocHeapSize,
373 size: memory_report.fonts,
374 },
375 Report {
376 path: path!["webrender", "images"],
377 kind: ReportKind::ExplicitJemallocHeapSize,
378 size: memory_report.images,
379 },
380 Report {
381 path: path!["webrender", "display-list"],
382 kind: ReportKind::ExplicitJemallocHeapSize,
383 size: memory_report.display_list,
384 },
385 ];
386
387 perform_memory_report(|ops| {
388 reports.push(Report {
389 path: path!["compositor", "scroll-tree"],
390 kind: ReportKind::ExplicitJemallocHeapSize,
391 size: self
392 .painter()
393 .webview_renderers
394 .scroll_trees_memory_usage(ops),
395 });
396 });
397
398 sender.send(ProcessReports::new(reports));
399 }
400
401 fn handle_browser_message_while_shutting_down(&self, msg: CompositorMsg) {
411 match msg {
412 CompositorMsg::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
413 self.painter_mut().notify_pipeline_exited(
414 webview_id,
415 pipeline_id,
416 pipeline_exit_source,
417 );
418 },
419 CompositorMsg::GenerateImageKey(sender) => {
420 let _ = sender.send(self.painter().webrender_api.generate_image_key());
421 },
422 CompositorMsg::GenerateFontKeys(
423 number_of_font_keys,
424 number_of_font_instance_keys,
425 result_sender,
426 _painter_id,
427 ) => {
428 let _ = result_sender.send(
429 self.painter_mut()
430 .generate_font_keys(number_of_font_keys, number_of_font_instance_keys),
431 );
432 },
433 _ => {
434 debug!("Ignoring message ({:?} while shutting down", msg);
435 },
436 }
437 }
438
439 pub fn add_webview(&self, webview: Box<dyn WebViewTrait>, viewport_details: ViewportDetails) {
440 self.painter_mut().add_webview(webview, viewport_details);
441 }
442
443 pub fn show_webview(
444 &self,
445 webview_id: WebViewId,
446 hide_others: bool,
447 ) -> Result<(), UnknownWebView> {
448 self.painter_mut().show_webview(webview_id, hide_others)
449 }
450
451 pub fn hide_webview(&self, webview_id: WebViewId) -> Result<(), UnknownWebView> {
452 self.painter_mut().hide_webview(webview_id)
453 }
454
455 pub fn raise_webview_to_top(
456 &self,
457 webview_id: WebViewId,
458 hide_others: bool,
459 ) -> Result<(), UnknownWebView> {
460 self.painter_mut()
461 .raise_webview_to_top(webview_id, hide_others)
462 }
463
464 pub fn move_resize_webview(&self, webview_id: WebViewId, rect: DeviceRect) {
465 if self.shutdown_state() != ShutdownState::NotShuttingDown {
466 return;
467 }
468 self.painter_mut().move_resize_webview(webview_id, rect);
469 }
470
471 pub fn set_hidpi_scale_factor(
472 &self,
473 webview_id: WebViewId,
474 new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
475 ) {
476 if self.shutdown_state() != ShutdownState::NotShuttingDown {
477 return;
478 }
479 self.painter_mut()
480 .set_hidpi_scale_factor(webview_id, new_scale_factor);
481 }
482
483 pub fn resize_rendering_context(&self, new_size: PhysicalSize<u32>) {
484 if self.shutdown_state() != ShutdownState::NotShuttingDown {
485 return;
486 }
487 self.painter_mut().resize_rendering_context(new_size);
488 }
489
490 pub fn set_page_zoom(&self, webview_id: WebViewId, new_zoom: f32) {
491 if self.shutdown_state() != ShutdownState::NotShuttingDown {
492 return;
493 }
494 self.painter_mut().set_page_zoom(webview_id, new_zoom);
495 }
496
497 pub fn page_zoom(&self, webview_id: WebViewId) -> f32 {
498 self.painter().page_zoom(webview_id)
499 }
500
501 pub fn render(&self) {
503 self.painter_mut().render(&self.time_profiler_chan);
504 }
505
506 pub fn receiver(&self) -> &RoutedReceiver<CompositorMsg> {
508 &self.compositor_receiver
509 }
510
511 #[servo_tracing::instrument(skip_all)]
512 pub fn handle_messages(&self, mut messages: Vec<CompositorMsg>) {
513 let mut saw_webrender_frame_ready_for_painter = HashMap::new();
518 messages.retain(|message| match message {
519 CompositorMsg::NewWebRenderFrameReady(painter_id, _document_id, need_repaint) => {
520 self.painter().decrement_pending_frames();
521 *saw_webrender_frame_ready_for_painter
522 .entry(*painter_id)
523 .or_insert(*need_repaint) |= *need_repaint;
524
525 false
526 },
527 _ => true,
528 });
529
530 for message in messages {
531 self.handle_browser_message(message);
532 if self.shutdown_state() == ShutdownState::FinishedShuttingDown {
533 return;
534 }
535 }
536
537 for (_, repaint_needed) in saw_webrender_frame_ready_for_painter.iter() {
538 self.painter()
539 .handle_new_webrender_frame_ready(*repaint_needed);
540 }
541 }
542
543 #[servo_tracing::instrument(skip_all)]
544 pub fn perform_updates(&self) -> bool {
545 if self.shutdown_state() == ShutdownState::FinishedShuttingDown {
546 return false;
547 }
548
549 for painter in &self.painters {
550 painter.borrow_mut().perform_updates();
551 }
552
553 self.shutdown_state() != ShutdownState::FinishedShuttingDown
554 }
555
556 pub fn toggle_webrender_debug(&self, option: WebRenderDebugOption) {
557 for painter in &self.painters {
558 painter.borrow_mut().toggle_webrender_debug(option);
559 }
560 }
561
562 pub fn capture_webrender(&self) {
563 let capture_id = SystemTime::now()
564 .duration_since(UNIX_EPOCH)
565 .unwrap_or_default()
566 .as_secs()
567 .to_string();
568 let available_path = [env::current_dir(), Ok(env::temp_dir())]
569 .iter()
570 .filter_map(|val| {
571 val.as_ref()
572 .map(|dir| dir.join("webrender-captures").join(&capture_id))
573 .ok()
574 })
575 .find(|val| create_dir_all(val).is_ok());
576
577 let Some(capture_path) = available_path else {
578 eprintln!("Couldn't create a path for WebRender captures.");
579 return;
580 };
581
582 println!("Saving WebRender capture to {capture_path:?}");
583 self.painter()
584 .webrender_api
585 .save_capture(capture_path.clone(), CaptureBits::all());
586 }
587
588 pub fn notify_input_event(&self, webview_id: WebViewId, event: InputEventAndId) {
589 if self.shutdown_state() != ShutdownState::NotShuttingDown {
590 return;
591 }
592 self.painter_mut().notify_input_event(webview_id, event);
593 }
594
595 pub fn notify_scroll_event(&self, webview_id: WebViewId, scroll: Scroll, point: WebViewPoint) {
596 if self.shutdown_state() != ShutdownState::NotShuttingDown {
597 return;
598 }
599 self.painter_mut()
600 .notify_scroll_event(webview_id, scroll, point);
601 }
602
603 pub fn pinch_zoom(&self, webview_id: WebViewId, pinch_zoom_delta: f32, center: DevicePoint) {
604 if self.shutdown_state() != ShutdownState::NotShuttingDown {
605 return;
606 }
607 self.painter_mut()
608 .pinch_zoom(webview_id, pinch_zoom_delta, center);
609 }
610
611 pub fn device_pixels_per_page_pixel(
612 &self,
613 webview_id: WebViewId,
614 ) -> Scale<f32, CSSPixel, DevicePixel> {
615 self.painter_mut().device_pixels_per_page_pixel(webview_id)
616 }
617
618 pub(crate) fn shutdown_state(&self) -> ShutdownState {
619 self.shutdown_state.get()
620 }
621
622 pub fn request_screenshot(
623 &self,
624 webview_id: WebViewId,
625 rect: Option<WebViewRect>,
626 callback: Box<dyn FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static>,
627 ) {
628 self.painter()
629 .request_screenshot(webview_id, rect, callback);
630 }
631
632 pub fn notify_input_event_handled(
633 &self,
634 webview_id: WebViewId,
635 input_event_id: InputEventId,
636 result: InputEventResult,
637 ) {
638 self.painter_mut()
639 .notify_input_event_handled(webview_id, input_event_id, result);
640 }
641}