wrench.rs (22419B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 6 use crate::blob; 7 use crossbeam::sync::chase_lev; 8 #[cfg(windows)] 9 use dwrote; 10 #[cfg(all(unix, not(target_os = "android")))] 11 use font_loader::system_fonts; 12 use winit::event_loop::EventLoopProxy; 13 use std::collections::HashMap; 14 use std::path::{Path, PathBuf}; 15 use std::sync::{Arc, Mutex}; 16 use std::sync::mpsc::Receiver; 17 use std::time::Instant; 18 use webrender::{api::*, CompositorConfig}; 19 use webrender::render_api::*; 20 use webrender::api::units::*; 21 use webrender::{DebugFlags, RenderResults, ShaderPrecacheFlags, LayerCompositor}; 22 use crate::{WindowWrapper, NotifierEvent}; 23 24 #[derive(Clone)] 25 pub struct DisplayList { 26 pub pipeline: PipelineId, 27 pub payload: BuiltDisplayList, 28 /// Whether to request that the transaction is presented to the window. 29 /// 30 /// The transaction will be presented if it contains at least one display list 31 /// with present set to true. 32 pub present: bool, 33 /// If set to true, send the transaction after adding this display list to it. 34 pub send_transaction: bool, 35 /// If set to true, the pipeline will be rendered off-screen. Only snapshotted 36 /// stacking contexts will be kept. 37 pub render_offscreen: bool, 38 } 39 40 // TODO(gw): This descriptor matches what we currently support for fonts 41 // but is quite a mess. We should at least document and 42 // use better types for things like the style and stretch. 43 #[derive(Debug, Clone, Hash, PartialEq, Eq)] 44 pub enum FontDescriptor { 45 Path { path: PathBuf, font_index: u32 }, 46 Family { name: String }, 47 Properties { 48 family: String, 49 weight: u32, 50 style: u32, 51 stretch: u32, 52 }, 53 } 54 55 struct NotifierData { 56 events_loop_proxy: Option<EventLoopProxy<()>>, 57 frames_notified: u32, 58 timing_receiver: chase_lev::Stealer<std::time::Instant>, 59 verbose: bool, 60 } 61 62 impl NotifierData { 63 fn new( 64 events_loop_proxy: Option<EventLoopProxy<()>>, 65 timing_receiver: chase_lev::Stealer<std::time::Instant>, 66 verbose: bool, 67 ) -> Self { 68 NotifierData { 69 events_loop_proxy, 70 frames_notified: 0, 71 timing_receiver, 72 verbose, 73 } 74 } 75 } 76 77 struct Notifier(Arc<Mutex<NotifierData>>); 78 79 impl Notifier { 80 fn update(&self, check_document: bool) { 81 let mut data = self.0.lock(); 82 let data = data.as_mut().unwrap(); 83 if check_document { 84 match data.timing_receiver.steal() { 85 chase_lev::Steal::Data(last_timing) => { 86 data.frames_notified += 1; 87 if data.verbose && data.frames_notified == 600 { 88 let elapsed = Instant::now() - last_timing; 89 println!( 90 "frame latency (consider queue depth here): {:3.6} ms", 91 elapsed.as_secs_f64() * 1000. 92 ); 93 data.frames_notified = 0; 94 } 95 } 96 _ => { 97 println!("Notified of frame, but no frame was ready?"); 98 } 99 } 100 } 101 102 if let Some(ref _elp) = data.events_loop_proxy { 103 #[cfg(not(target_os = "android"))] 104 let _ = _elp.send_event(()); 105 } 106 } 107 } 108 109 impl RenderNotifier for Notifier { 110 fn clone(&self) -> Box<dyn RenderNotifier> { 111 Box::new(Notifier(self.0.clone())) 112 } 113 114 fn wake_up(&self, _composite_needed: bool) { 115 self.update(false); 116 } 117 118 fn new_frame_ready(&self, _: DocumentId, _: FramePublishId, params: &FrameReadyParams) { 119 self.update(!params.scrolled); 120 } 121 } 122 123 pub trait WrenchThing { 124 fn next_frame(&mut self); 125 fn prev_frame(&mut self); 126 fn do_frame(&mut self, _: &mut Wrench) -> u32; 127 } 128 129 impl WrenchThing for CapturedDocument { 130 fn next_frame(&mut self) {} 131 fn prev_frame(&mut self) {} 132 fn do_frame(&mut self, wrench: &mut Wrench) -> u32 { 133 if let Some(root_pipeline_id) = self.root_pipeline_id.take() { 134 // skip the first frame - to not overwrite the loaded one 135 let mut txn = Transaction::new(); 136 txn.set_root_pipeline(root_pipeline_id); 137 wrench.api.send_transaction(self.document_id, txn); 138 } else { 139 wrench.refresh(); 140 } 141 0 142 } 143 } 144 145 pub struct CapturedSequence { 146 root: PathBuf, 147 frame: usize, 148 frame_set: Vec<(u32, u32)>, 149 } 150 151 impl CapturedSequence { 152 pub fn new(root: PathBuf, scene_start: u32, frame_start: u32) -> Self { 153 // Build set of a scene and frame IDs. 154 let mut scene = scene_start; 155 let mut frame = frame_start; 156 let mut frame_set = Vec::new(); 157 while Self::scene_root(&root, scene).as_path().is_dir() { 158 while Self::frame_root(&root, scene, frame).as_path().is_dir() { 159 frame_set.push((scene, frame)); 160 frame += 1; 161 } 162 scene += 1; 163 frame = 1; 164 } 165 166 assert!(!frame_set.is_empty()); 167 168 Self { 169 root, 170 frame: 0, 171 frame_set, 172 } 173 } 174 175 fn scene_root(root: &Path, scene: u32) -> PathBuf { 176 let path = format!("scenes/{:05}", scene); 177 root.join(path) 178 } 179 180 fn frame_root(root: &Path, scene: u32, frame: u32) -> PathBuf { 181 let path = format!("scenes/{:05}/frames/{:05}", scene, frame); 182 root.join(path) 183 } 184 } 185 186 impl WrenchThing for CapturedSequence { 187 fn next_frame(&mut self) { 188 if self.frame + 1 < self.frame_set.len() { 189 self.frame += 1; 190 } 191 } 192 193 fn prev_frame(&mut self) { 194 if self.frame > 0 { 195 self.frame -= 1; 196 } 197 } 198 199 fn do_frame(&mut self, wrench: &mut Wrench) -> u32 { 200 let mut documents = wrench.api.load_capture(self.root.clone(), Some(self.frame_set[self.frame])); 201 println!("loaded {:?} from {:?}", 202 documents.iter().map(|cd| cd.document_id).collect::<Vec<_>>(), 203 self.frame_set[self.frame]); 204 let captured = documents.swap_remove(0); 205 wrench.document_id = captured.document_id; 206 self.frame as u32 207 } 208 } 209 210 pub struct Wrench { 211 window_size: DeviceIntSize, 212 213 pub renderer: webrender::Renderer, 214 pub api: RenderApi, 215 pub document_id: DocumentId, 216 pub root_pipeline_id: PipelineId, 217 218 window_title_to_set: Option<String>, 219 220 graphics_api: webrender::GraphicsApiInfo, 221 222 pub rebuild_display_lists: bool, 223 224 pub frame_start_sender: chase_lev::Worker<Instant>, 225 226 pub callbacks: Arc<Mutex<blob::BlobCallbacks>>, 227 } 228 229 impl Wrench { 230 #[allow(clippy::too_many_arguments)] 231 pub fn new( 232 window: &mut WindowWrapper, 233 proxy: Option<EventLoopProxy<()>>, 234 shader_override_path: Option<PathBuf>, 235 use_optimized_shaders: bool, 236 size: DeviceIntSize, 237 do_rebuild: bool, 238 no_subpixel_aa: bool, 239 verbose: bool, 240 no_scissor: bool, 241 no_batch: bool, 242 precache_shaders: bool, 243 dump_shader_source: Option<String>, 244 notifier: Option<Box<dyn RenderNotifier>>, 245 layer_compositor: Option<Box<dyn LayerCompositor>>, 246 ) -> Self { 247 println!("Shader override path: {:?}", shader_override_path); 248 249 let mut debug_flags = DebugFlags::ECHO_DRIVER_MESSAGES; 250 debug_flags.set(DebugFlags::DISABLE_BATCHING, no_batch); 251 debug_flags.set(DebugFlags::MISSING_SNAPSHOT_PINK, true); 252 let callbacks = Arc::new(Mutex::new(blob::BlobCallbacks::new())); 253 254 let precache_flags = if precache_shaders { 255 ShaderPrecacheFlags::FULL_COMPILE 256 } else { 257 ShaderPrecacheFlags::empty() 258 }; 259 260 let compositor_config = match layer_compositor { 261 Some(compositor) => CompositorConfig::Layer { compositor }, 262 None => CompositorConfig::default(), 263 }; 264 265 let opts = webrender::WebRenderOptions { 266 resource_override_path: shader_override_path, 267 use_optimized_shaders, 268 enable_subpixel_aa: !no_subpixel_aa, 269 debug_flags, 270 enable_clear_scissor: no_scissor.then_some(false), 271 max_recorded_profiles: 16, 272 precache_flags, 273 blob_image_handler: Some(Box::new(blob::CheckerboardRenderer::new(callbacks.clone()))), 274 testing: true, 275 max_internal_texture_size: Some(8196), // Needed for rawtest::test_resize_image. 276 allow_advanced_blend_equation: window.is_software(), 277 dump_shader_source, 278 // SWGL doesn't support the GL_ALWAYS depth comparison function used by 279 // `clear_caches_with_quads`, but scissored clears work well. 280 clear_caches_with_quads: !window.is_software(), 281 compositor_config, 282 enable_debugger: true, 283 precise_radial_gradients: true, 284 precise_conic_gradients: true, 285 precise_linear_gradients: window.is_software(), 286 ..Default::default() 287 }; 288 289 // put an Awakened event into the queue to kick off the first frame 290 if let Some(ref _elp) = proxy { 291 #[cfg(not(target_os = "android"))] 292 let _ = _elp.send_event(()); 293 } 294 295 let (timing_sender, timing_receiver) = chase_lev::deque(); 296 let notifier = notifier.unwrap_or_else(|| { 297 let data = Arc::new(Mutex::new(NotifierData::new(proxy, timing_receiver, verbose))); 298 Box::new(Notifier(data)) 299 }); 300 301 let (renderer, sender) = webrender::create_webrender_instance( 302 window.clone_gl(), 303 notifier, 304 opts, 305 None, 306 ).unwrap(); 307 308 let api = sender.create_api(); 309 let document_id = api.add_document(size); 310 311 let graphics_api = renderer.get_graphics_api_info(); 312 313 let mut wrench = Wrench { 314 window_size: size, 315 316 renderer, 317 api, 318 document_id, 319 window_title_to_set: None, 320 321 rebuild_display_lists: do_rebuild, 322 323 root_pipeline_id: PipelineId(0, 0), 324 325 graphics_api, 326 frame_start_sender: timing_sender, 327 328 callbacks, 329 }; 330 331 wrench.set_title("start"); 332 let mut txn = Transaction::new(); 333 txn.set_root_pipeline(wrench.root_pipeline_id); 334 wrench.api.send_transaction(wrench.document_id, txn); 335 336 wrench 337 } 338 339 pub fn set_quality_settings(&mut self, settings: QualitySettings) { 340 let mut txn = Transaction::new(); 341 txn.set_quality_settings(settings); 342 self.api.send_transaction(self.document_id, txn); 343 } 344 345 pub fn layout_simple_ascii( 346 &mut self, 347 font_key: FontKey, 348 instance_key: FontInstanceKey, 349 text: &str, 350 size: f32, 351 origin: LayoutPoint, 352 flags: FontInstanceFlags, 353 ) -> (Vec<u32>, Vec<LayoutPoint>, LayoutRect) { 354 // Map the string codepoints to glyph indices in this font. 355 // Just drop any glyph that isn't present in this font. 356 let indices: Vec<u32> = self.api 357 .get_glyph_indices(font_key, text) 358 .iter() 359 .filter_map(|idx| *idx) 360 .collect(); 361 362 // Retrieve the metrics for each glyph. 363 let metrics = self.api.get_glyph_dimensions(instance_key, indices.clone()); 364 365 let mut bounding_rect = LayoutRect::zero(); 366 let mut positions = Vec::new(); 367 368 let mut cursor = origin; 369 let direction = if flags.contains(FontInstanceFlags::TRANSPOSE) { 370 LayoutVector2D::new( 371 0.0, 372 if flags.contains(FontInstanceFlags::FLIP_Y) { -1.0 } else { 1.0 }, 373 ) 374 } else { 375 LayoutVector2D::new( 376 if flags.contains(FontInstanceFlags::FLIP_X) { -1.0 } else { 1.0 }, 377 0.0, 378 ) 379 }; 380 for metric in metrics { 381 positions.push(cursor); 382 383 if let Some(GlyphDimensions { left, top, width, height, advance }) = metric { 384 let glyph_rect = LayoutRect::from_origin_and_size( 385 LayoutPoint::new(cursor.x + left as f32, cursor.y - top as f32), 386 LayoutSize::new(width as f32, height as f32) 387 ); 388 bounding_rect = bounding_rect.union(&glyph_rect); 389 cursor += direction * advance; 390 } else { 391 // Extract the advances from the metrics. The get_glyph_dimensions API 392 // has a limitation that it can't currently get dimensions for non-renderable 393 // glyphs (e.g. spaces), so just use a rough estimate in that case. 394 let space_advance = size / 3.0; 395 cursor += direction * space_advance; 396 } 397 } 398 399 // The platform font implementations don't always handle 400 // the exact dimensions used when subpixel AA is enabled 401 // on glyphs. As a workaround, inflate the bounds by 402 // 2 pixels on either side, to give a slightly less 403 // tight fitting bounding rect. 404 let bounding_rect = bounding_rect.inflate(2.0, 2.0); 405 406 (indices, positions, bounding_rect) 407 } 408 409 pub fn set_title(&mut self, extra: &str) { 410 self.window_title_to_set = Some(format!( 411 "Wrench: {} - {} - {}", 412 extra, 413 self.graphics_api.renderer, 414 self.graphics_api.version 415 )); 416 } 417 418 pub fn take_title(&mut self) -> Option<String> { 419 self.window_title_to_set.take() 420 } 421 422 pub fn should_rebuild_display_lists(&self) -> bool { 423 self.rebuild_display_lists 424 } 425 426 pub fn window_size_f32(&self) -> LayoutSize { 427 LayoutSize::new( 428 self.window_size.width as f32, 429 self.window_size.height as f32, 430 ) 431 } 432 433 #[cfg(target_os = "windows")] 434 pub fn font_key_from_native_handle(&mut self, descriptor: &NativeFontHandle) -> FontKey { 435 let key = self.api.generate_font_key(); 436 let mut txn = Transaction::new(); 437 txn.add_native_font(key, descriptor.clone()); 438 self.api.send_transaction(self.document_id, txn); 439 key 440 } 441 442 #[cfg(target_os = "windows")] 443 pub fn font_key_from_name(&mut self, font_name: &str) -> FontKey { 444 self.font_key_from_properties( 445 font_name, 446 dwrote::FontWeight::Regular.to_u32(), 447 dwrote::FontStyle::Normal.to_u32(), 448 dwrote::FontStretch::Normal.to_u32(), 449 ) 450 } 451 452 #[cfg(target_os = "windows")] 453 pub fn font_key_from_properties( 454 &mut self, 455 family: &str, 456 weight: u32, 457 style: u32, 458 stretch: u32, 459 ) -> FontKey { 460 let weight = dwrote::FontWeight::from_u32(weight); 461 let style = dwrote::FontStyle::from_u32(style); 462 let stretch = dwrote::FontStretch::from_u32(stretch); 463 let desc = dwrote::FontDescriptor { 464 family_name: family.to_owned(), 465 weight, 466 style, 467 stretch, 468 }; 469 let system_fc = dwrote::FontCollection::system(); 470 #[allow(deprecated)] 471 if let Some(font) = system_fc.get_font_from_descriptor(&desc) { 472 let face = font.create_font_face(); 473 #[allow(deprecated)] 474 let files = face.get_files(); 475 if files.len() == 1 { 476 #[allow(deprecated)] 477 if let Some(path) = files[0].get_font_file_path() { 478 return self.font_key_from_native_handle(&NativeFontHandle { 479 path, 480 index: face.get_index(), 481 }); 482 } 483 } 484 } 485 panic!("failed loading font from properties {:?}", desc) 486 } 487 488 #[cfg(all(unix, not(target_os = "android")))] 489 pub fn font_key_from_properties( 490 &mut self, 491 family: &str, 492 _weight: u32, 493 _style: u32, 494 _stretch: u32, 495 ) -> FontKey { 496 let property = system_fonts::FontPropertyBuilder::new() 497 .family(family) 498 .build(); 499 let (font, index) = system_fonts::get(&property).unwrap(); 500 self.font_key_from_bytes(font, index as u32) 501 } 502 503 #[cfg(target_os = "android")] 504 pub fn font_key_from_properties( 505 &mut self, 506 _family: &str, 507 _weight: u32, 508 _style: u32, 509 _stretch: u32, 510 ) -> FontKey { 511 unimplemented!() 512 } 513 514 #[cfg(all(unix, not(target_os = "android")))] 515 pub fn font_key_from_name(&mut self, font_name: &str) -> FontKey { 516 let property = system_fonts::FontPropertyBuilder::new() 517 .family(font_name) 518 .build(); 519 let (font, index) = system_fonts::get(&property).unwrap(); 520 self.font_key_from_bytes(font, index as u32) 521 } 522 523 #[cfg(target_os = "android")] 524 pub fn font_key_from_name(&mut self, _font_name: &str) -> FontKey { 525 unimplemented!() 526 } 527 528 pub fn font_key_from_bytes(&mut self, bytes: Vec<u8>, index: u32) -> FontKey { 529 let key = self.api.generate_font_key(); 530 let mut txn = Transaction::new(); 531 txn.add_raw_font(key, bytes, index); 532 self.api.send_transaction(self.document_id, txn); 533 key 534 } 535 536 pub fn add_font_instance(&mut self, 537 font_key: FontKey, 538 size: f32, 539 flags: FontInstanceFlags, 540 render_mode: Option<FontRenderMode>, 541 synthetic_italics: SyntheticItalics, 542 ) -> FontInstanceKey { 543 let key = self.api.generate_font_instance_key(); 544 let mut txn = Transaction::new(); 545 let mut options: FontInstanceOptions = Default::default(); 546 options.flags |= flags; 547 if let Some(render_mode) = render_mode { 548 options.render_mode = render_mode; 549 } 550 options.synthetic_italics = synthetic_italics; 551 txn.add_font_instance(key, font_key, size, Some(options), None, Vec::new()); 552 self.api.send_transaction(self.document_id, txn); 553 key 554 } 555 556 #[allow(dead_code)] 557 pub fn delete_font_instance(&mut self, key: FontInstanceKey) { 558 let mut txn = Transaction::new(); 559 txn.delete_font_instance(key); 560 self.api.send_transaction(self.document_id, txn); 561 } 562 563 pub fn update(&mut self, dim: DeviceIntSize) { 564 if dim != self.window_size { 565 self.window_size = dim; 566 } 567 } 568 569 pub fn begin_frame(&mut self) { 570 self.frame_start_sender.push(Instant::now()); 571 } 572 573 pub fn send_lists( 574 &mut self, 575 frame_number: &mut u32, 576 display_lists: Vec<DisplayList>, 577 scroll_offsets: &HashMap<ExternalScrollId, Vec<SampledScrollOffset>>, 578 ) { 579 let mut txn = Transaction::new(); 580 let mut present = false; 581 for display_list in display_lists { 582 present |= display_list.present; 583 584 txn.set_display_list( 585 Epoch(*frame_number), 586 (display_list.pipeline, display_list.payload), 587 ); 588 589 if display_list.render_offscreen { 590 txn.render_offscreen(display_list.pipeline); 591 } 592 593 if display_list.send_transaction { 594 for (id, offsets) in scroll_offsets { 595 txn.set_scroll_offsets(*id, offsets.clone()); 596 } 597 598 let tracked = false; 599 txn.generate_frame(0, present, tracked, RenderReasons::TESTING); 600 self.api.send_transaction(self.document_id, txn); 601 txn = Transaction::new(); 602 603 present = false; 604 *frame_number += 1; 605 } 606 } 607 608 // The last display lists should be have send_transaction set to true. 609 assert!(txn.is_empty()); 610 } 611 612 pub fn get_frame_profiles( 613 &mut self, 614 ) -> (Vec<webrender::CpuProfile>, Vec<webrender::GpuProfile>) { 615 self.renderer.get_frame_profiles() 616 } 617 618 pub fn render(&mut self) -> RenderResults { 619 self.renderer.update(); 620 let _ = self.renderer.flush_pipeline_info(); 621 self.renderer 622 .render(self.window_size, 0) 623 .expect("errors encountered during render!") 624 } 625 626 pub fn refresh(&mut self) { 627 self.begin_frame(); 628 let mut txn = Transaction::new(); 629 let present = true; 630 let tracked = false; 631 txn.generate_frame(0, present, tracked, RenderReasons::TESTING); 632 self.api.send_transaction(self.document_id, txn); 633 } 634 635 pub fn show_onscreen_help(&mut self) { 636 let help_lines = [ 637 "Esc - Quit", 638 "H - Toggle help", 639 "R - Toggle recreating display items each frame", 640 "P - Toggle profiler", 641 "O - Toggle showing intermediate targets", 642 "I - Toggle showing texture caches", 643 "B - Toggle showing alpha primitive rects", 644 "V - Toggle showing overdraw", 645 "G - Toggle showing gpu cache updates", 646 "S - Toggle compact profiler", 647 "Q - Toggle GPU queries for time and samples", 648 "M - Trigger memory pressure event", 649 "T - Save CPU profile to a file", 650 "C - Save a capture to captures/wrench/", 651 "X - Do a hit test at the current cursor position", 652 "Y - Clear all caches", 653 ]; 654 655 let color_and_offset = [(ColorF::BLACK, 2.0), (ColorF::WHITE, 0.0)]; 656 self.renderer.device.begin_frame(); // next line might compile shaders: 657 let dr = self.renderer.debug_renderer().unwrap(); 658 659 for co in &color_and_offset { 660 let x = 15.0 + co.1; 661 let mut y = 15.0 + co.1 + dr.line_height(); 662 for line in &help_lines { 663 dr.add_text(x, y, line, co.0.into(), None); 664 y += dr.line_height(); 665 } 666 } 667 self.renderer.device.end_frame(); 668 } 669 670 pub fn shut_down(self, rx: Receiver<NotifierEvent>) { 671 self.api.shut_down(true); 672 673 loop { 674 match rx.recv() { 675 Ok(NotifierEvent::ShutDown) => { break; } 676 Ok(_) => {} 677 Err(e) => { panic!("Did not shut down properly: {:?}.", e); } 678 } 679 } 680 681 self.renderer.deinit(); 682 } 683 }