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 #[macro_use]
6 extern crate clap;
7 #[macro_use]
8 extern crate log;
9 #[macro_use]
10 extern crate serde;
11 #[macro_use]
12 extern crate tracy_rs;
13 
14 mod angle;
15 mod blob;
16 mod egl;
17 mod parse_function;
18 mod perf;
19 mod png;
20 mod premultiply;
21 mod rawtest;
22 mod reftest;
23 mod test_invalidation;
24 mod wrench;
25 mod yaml_frame_reader;
26 mod yaml_helper;
27 
28 use gleam::gl;
29 #[cfg(feature = "software")]
30 use gleam::gl::Gl;
31 use crate::perf::PerfHarness;
32 use crate::png::save_flipped;
33 use crate::rawtest::RawtestHarness;
34 use crate::reftest::{ReftestHarness, ReftestOptions};
35 use std::fs;
36 #[cfg(feature = "headless")]
37 use std::ffi::CString;
38 #[cfg(feature = "headless")]
39 use std::mem;
40 use std::os::raw::c_void;
41 use std::path::{Path, PathBuf};
42 use std::process;
43 use std::ptr;
44 use std::rc::Rc;
45 #[cfg(feature = "software")]
46 use std::slice;
47 use std::sync::mpsc::{channel, Sender, Receiver};
48 use webrender::DebugFlags;
49 use webrender::api::*;
50 use webrender::render_api::*;
51 use webrender::api::units::*;
52 use winit::dpi::{LogicalPosition, LogicalSize};
53 use winit::VirtualKeyCode;
54 use crate::wrench::{CapturedSequence, Wrench, WrenchThing};
55 use crate::yaml_frame_reader::YamlFrameReader;
56 
57 pub const PLATFORM_DEFAULT_FACE_NAME: &str = "Arial";
58 
59 pub static mut CURRENT_FRAME_NUMBER: u32 = 0;
60 
61 #[cfg(feature = "headless")]
62 pub struct HeadlessContext {
63     width: i32,
64     height: i32,
65     _context: osmesa_sys::OSMesaContext,
66     _buffer: Vec<u32>,
67 }
68 
69 #[cfg(not(feature = "headless"))]
70 pub struct HeadlessContext {
71     width: i32,
72     height: i32,
73 }
74 
75 impl HeadlessContext {
76     #[cfg(feature = "headless")]
new(width: i32, height: i32) -> Self77     fn new(width: i32, height: i32) -> Self {
78         let mut attribs = Vec::new();
79 
80         attribs.push(osmesa_sys::OSMESA_PROFILE);
81         attribs.push(osmesa_sys::OSMESA_CORE_PROFILE);
82         attribs.push(osmesa_sys::OSMESA_CONTEXT_MAJOR_VERSION);
83         attribs.push(3);
84         attribs.push(osmesa_sys::OSMESA_CONTEXT_MINOR_VERSION);
85         attribs.push(3);
86         attribs.push(osmesa_sys::OSMESA_DEPTH_BITS);
87         attribs.push(24);
88         attribs.push(0);
89 
90         let context =
91             unsafe { osmesa_sys::OSMesaCreateContextAttribs(attribs.as_ptr(), ptr::null_mut()) };
92 
93         assert!(!context.is_null());
94 
95         let mut buffer = vec![0; (width * height) as usize];
96 
97         unsafe {
98             let ret = osmesa_sys::OSMesaMakeCurrent(
99                 context,
100                 buffer.as_mut_ptr() as *mut _,
101                 gl::UNSIGNED_BYTE,
102                 width,
103                 height,
104             );
105             assert!(ret != 0);
106         };
107 
108         HeadlessContext {
109             width,
110             height,
111             _context: context,
112             _buffer: buffer,
113         }
114     }
115 
116     #[cfg(not(feature = "headless"))]
new(width: i32, height: i32) -> Self117     fn new(width: i32, height: i32) -> Self {
118         HeadlessContext { width, height }
119     }
120 
121     #[cfg(feature = "headless")]
get_proc_address(s: &str) -> *const c_void122     fn get_proc_address(s: &str) -> *const c_void {
123         let c_str = CString::new(s).expect("Unable to create CString");
124         unsafe { mem::transmute(osmesa_sys::OSMesaGetProcAddress(c_str.as_ptr())) }
125     }
126 
127     #[cfg(not(feature = "headless"))]
get_proc_address(_: &str) -> *const c_void128     fn get_proc_address(_: &str) -> *const c_void {
129         ptr::null() as *const _
130     }
131 }
132 
133 #[cfg(not(feature = "software"))]
134 mod swgl {
135     pub struct Context;
136 }
137 
138 pub enum WindowWrapper {
139     WindowedContext(glutin::WindowedContext<glutin::PossiblyCurrent>, Rc<dyn gl::Gl>, Option<swgl::Context>),
140     Angle(winit::Window, angle::Context, Rc<dyn gl::Gl>, Option<swgl::Context>),
141     Headless(HeadlessContext, Rc<dyn gl::Gl>, Option<swgl::Context>),
142 }
143 
144 pub struct HeadlessEventIterater;
145 
146 impl WindowWrapper {
147     #[cfg(feature = "software")]
upload_software_to_native(&self)148     fn upload_software_to_native(&self) {
149         match *self {
150             WindowWrapper::Headless(..) => return,
151             _ => {}
152         }
153         let swgl = match self.software_gl() {
154             Some(swgl) => swgl,
155             None => return,
156         };
157         swgl.finish();
158         let gl = self.native_gl();
159         let tex = gl.gen_textures(1)[0];
160         gl.bind_texture(gl::TEXTURE_2D, tex);
161         let (data_ptr, w, h, stride) = swgl.get_color_buffer(0, true);
162         assert!(stride == w * 4);
163         let buffer = unsafe { slice::from_raw_parts(data_ptr as *const u8, w as usize * h as usize * 4) };
164         gl.tex_image_2d(gl::TEXTURE_2D, 0, gl::RGBA8 as gl::GLint, w, h, 0, gl::BGRA, gl::UNSIGNED_BYTE, Some(buffer));
165         let fb = gl.gen_framebuffers(1)[0];
166         gl.bind_framebuffer(gl::READ_FRAMEBUFFER, fb);
167         gl.framebuffer_texture_2d(gl::READ_FRAMEBUFFER, gl::COLOR_ATTACHMENT0, gl::TEXTURE_2D, tex, 0);
168         gl.blit_framebuffer(0, 0, w, h, 0, 0, w, h, gl::COLOR_BUFFER_BIT, gl::NEAREST);
169         gl.delete_framebuffers(&[fb]);
170         gl.delete_textures(&[tex]);
171         gl.finish();
172     }
173 
174     #[cfg(not(feature = "software"))]
upload_software_to_native(&self)175     fn upload_software_to_native(&self) {
176     }
177 
swap_buffers(&self)178     fn swap_buffers(&self) {
179         match *self {
180             WindowWrapper::WindowedContext(ref windowed_context, _, _) => {
181                 windowed_context.swap_buffers().unwrap()
182             }
183             WindowWrapper::Angle(_, ref context, _, _) => context.swap_buffers().unwrap(),
184             WindowWrapper::Headless(_, _, _) => {}
185         }
186     }
187 
get_inner_size(&self) -> DeviceIntSize188     fn get_inner_size(&self) -> DeviceIntSize {
189         fn inner_size(window: &winit::Window) -> DeviceIntSize {
190             let size = window
191                 .get_inner_size()
192                 .unwrap()
193                 .to_physical(window.get_hidpi_factor());
194             DeviceIntSize::new(size.width as i32, size.height as i32)
195         }
196         match *self {
197             WindowWrapper::WindowedContext(ref windowed_context, ..) => {
198                 inner_size(windowed_context.window())
199             }
200             WindowWrapper::Angle(ref window, ..) => inner_size(window),
201             WindowWrapper::Headless(ref context, ..) => DeviceIntSize::new(context.width, context.height),
202         }
203     }
204 
hidpi_factor(&self) -> f32205     fn hidpi_factor(&self) -> f32 {
206         match *self {
207             WindowWrapper::WindowedContext(ref windowed_context, ..) => {
208                 windowed_context.window().get_hidpi_factor() as f32
209             }
210             WindowWrapper::Angle(ref window, ..) => window.get_hidpi_factor() as f32,
211             WindowWrapper::Headless(..) => 1.0,
212         }
213     }
214 
resize(&mut self, size: DeviceIntSize)215     fn resize(&mut self, size: DeviceIntSize) {
216         match *self {
217             WindowWrapper::WindowedContext(ref mut windowed_context, ..) => {
218                 windowed_context.window()
219                     .set_inner_size(LogicalSize::new(size.width as f64, size.height as f64))
220             },
221             WindowWrapper::Angle(ref mut window, ..) => {
222                 window.set_inner_size(LogicalSize::new(size.width as f64, size.height as f64))
223             },
224             WindowWrapper::Headless(..) => unimplemented!(), // requites Glutin update
225         }
226     }
227 
set_title(&mut self, title: &str)228     fn set_title(&mut self, title: &str) {
229         match *self {
230             WindowWrapper::WindowedContext(ref windowed_context, ..) => {
231                 windowed_context.window().set_title(title)
232             }
233             WindowWrapper::Angle(ref window, ..) => window.set_title(title),
234             WindowWrapper::Headless(..) => (),
235         }
236     }
237 
software_gl(&self) -> Option<&swgl::Context>238     pub fn software_gl(&self) -> Option<&swgl::Context> {
239         match *self {
240             WindowWrapper::WindowedContext(_, _, ref swgl) |
241             WindowWrapper::Angle(_, _, _, ref swgl) |
242             WindowWrapper::Headless(_, _, ref swgl) => swgl.as_ref(),
243         }
244     }
245 
native_gl(&self) -> &dyn gl::Gl246     pub fn native_gl(&self) -> &dyn gl::Gl {
247         match *self {
248             WindowWrapper::WindowedContext(_, ref gl, _) |
249             WindowWrapper::Angle(_, _, ref gl, _) |
250             WindowWrapper::Headless(_, ref gl, _) => &**gl,
251         }
252     }
253 
254     #[cfg(feature = "software")]
gl(&self) -> &dyn gl::Gl255     pub fn gl(&self) -> &dyn gl::Gl {
256         if let Some(swgl) = self.software_gl() {
257             swgl
258         } else {
259             self.native_gl()
260         }
261     }
262 
is_software(&self) -> bool263     pub fn is_software(&self) -> bool {
264         self.software_gl().is_some()
265     }
266 
267     #[cfg(not(feature = "software"))]
gl(&self) -> &dyn gl::Gl268     pub fn gl(&self) -> &dyn gl::Gl {
269         self.native_gl()
270     }
271 
clone_gl(&self) -> Rc<dyn gl::Gl>272     pub fn clone_gl(&self) -> Rc<dyn gl::Gl> {
273         match *self {
274             WindowWrapper::WindowedContext(_, ref gl, ref swgl) |
275             WindowWrapper::Angle(_, _, ref gl, ref swgl) |
276             WindowWrapper::Headless(_, ref gl, ref swgl) => {
277                 match swgl {
278                     #[cfg(feature = "software")]
279                     Some(ref swgl) => Rc::new(swgl.clone()),
280                     None => gl.clone(),
281                     #[cfg(not(feature = "software"))]
282                     _ => panic!(),
283                 }
284             }
285         }
286     }
287 
288 
289     #[cfg(feature = "software")]
update_software(&self, dim: DeviceIntSize)290     fn update_software(&self, dim: DeviceIntSize) {
291         if let Some(swgl) = self.software_gl() {
292             swgl.init_default_framebuffer(0, 0, dim.width, dim.height, 0, std::ptr::null_mut());
293         }
294     }
295 
296     #[cfg(not(feature = "software"))]
update_software(&self, _dim: DeviceIntSize)297     fn update_software(&self, _dim: DeviceIntSize) {
298     }
299 
update(&self, wrench: &mut Wrench)300     fn update(&self, wrench: &mut Wrench) {
301         let dim = self.get_inner_size();
302         self.update_software(dim);
303         wrench.update(dim);
304     }
305 }
306 
307 #[cfg(feature = "software")]
make_software_context() -> swgl::Context308 fn make_software_context() -> swgl::Context {
309     let ctx = swgl::Context::create();
310     ctx.make_current();
311     ctx
312 }
313 
314 #[cfg(not(feature = "software"))]
make_software_context() -> swgl::Context315 fn make_software_context() -> swgl::Context {
316     panic!("software feature not enabled")
317 }
318 
make_window( size: DeviceIntSize, vsync: bool, events_loop: &Option<winit::EventsLoop>, angle: bool, gl_request: glutin::GlRequest, software: bool, ) -> WindowWrapper319 fn make_window(
320     size: DeviceIntSize,
321     vsync: bool,
322     events_loop: &Option<winit::EventsLoop>,
323     angle: bool,
324     gl_request: glutin::GlRequest,
325     software: bool,
326 ) -> WindowWrapper {
327     let sw_ctx = if software {
328         Some(make_software_context())
329     } else {
330         None
331     };
332 
333     let wrapper = match *events_loop {
334         Some(ref events_loop) => {
335             let context_builder = glutin::ContextBuilder::new()
336                 .with_gl(gl_request)
337                 .with_vsync(vsync);
338             let window_builder = winit::WindowBuilder::new()
339                 .with_title("WRench")
340                 .with_multitouch()
341                 .with_dimensions(LogicalSize::new(size.width as f64, size.height as f64));
342 
343             if angle {
344                 let (_window, _context) = angle::Context::with_window(
345                     window_builder, context_builder, events_loop
346                 ).unwrap();
347 
348                 unsafe {
349                     _context
350                         .make_current()
351                         .expect("unable to make context current!");
352                 }
353 
354                 let gl = match _context.get_api() {
355                     glutin::Api::OpenGl => unsafe {
356                         gl::GlFns::load_with(|symbol| _context.get_proc_address(symbol) as *const _)
357                     },
358                     glutin::Api::OpenGlEs => unsafe {
359                         gl::GlesFns::load_with(|symbol| _context.get_proc_address(symbol) as *const _)
360                     },
361                     glutin::Api::WebGl => unimplemented!(),
362                 };
363 
364                 WindowWrapper::Angle(_window, _context, gl, sw_ctx)
365             } else {
366                 let windowed_context = context_builder
367                     .build_windowed(window_builder, events_loop)
368                     .unwrap();
369 
370                 let windowed_context = unsafe {
371                     windowed_context
372                         .make_current()
373                         .expect("unable to make context current!")
374                 };
375 
376                 let gl = match windowed_context.get_api() {
377                     glutin::Api::OpenGl => unsafe {
378                         gl::GlFns::load_with(
379                             |symbol| windowed_context.get_proc_address(symbol) as *const _
380                         )
381                     },
382                     glutin::Api::OpenGlEs => unsafe {
383                         gl::GlesFns::load_with(
384                             |symbol| windowed_context.get_proc_address(symbol) as *const _
385                         )
386                     },
387                     glutin::Api::WebGl => unimplemented!(),
388                 };
389 
390                 WindowWrapper::WindowedContext(windowed_context, gl, sw_ctx)
391             }
392         }
393         None => {
394             let gl = match sw_ctx {
395                 #[cfg(feature = "software")]
396                 Some(ref sw_ctx) => Rc::new(sw_ctx.clone()),
397                 None => {
398                     match gl::GlType::default() {
399                         gl::GlType::Gl => unsafe {
400                             gl::GlFns::load_with(|symbol| {
401                                 HeadlessContext::get_proc_address(symbol) as *const _
402                             })
403                         },
404                         gl::GlType::Gles => unsafe {
405                             gl::GlesFns::load_with(|symbol| {
406                                 HeadlessContext::get_proc_address(symbol) as *const _
407                             })
408                         },
409                     }
410                 }
411                 #[cfg(not(feature = "software"))]
412                 _ => panic!(),
413             };
414             WindowWrapper::Headless(HeadlessContext::new(size.width, size.height), gl, sw_ctx)
415         }
416     };
417 
418     let gl = wrapper.gl();
419 
420     gl.clear_color(0.3, 0.0, 0.0, 1.0);
421 
422     let gl_version = gl.get_string(gl::VERSION);
423     let gl_renderer = gl.get_string(gl::RENDERER);
424 
425     println!("OpenGL version {}, {}", gl_version, gl_renderer);
426     println!(
427         "hidpi factor: {}",
428         wrapper.hidpi_factor()
429     );
430 
431     wrapper
432 }
433 
434 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
435 pub enum NotifierEvent {
436     WakeUp {
437         composite_needed: bool,
438     },
439     ShutDown,
440 }
441 
442 struct Notifier {
443     tx: Sender<NotifierEvent>,
444 }
445 
446 // setup a notifier so we can wait for frames to be finished
447 impl RenderNotifier for Notifier {
clone(&self) -> Box<dyn RenderNotifier>448     fn clone(&self) -> Box<dyn RenderNotifier> {
449         Box::new(Notifier {
450             tx: self.tx.clone(),
451         })
452     }
453 
wake_up( &self, composite_needed: bool, )454     fn wake_up(
455         &self,
456         composite_needed: bool,
457     ) {
458         let msg = NotifierEvent::WakeUp {
459             composite_needed,
460         };
461         self.tx.send(msg).unwrap();
462     }
463 
shut_down(&self)464     fn shut_down(&self) {
465         self.tx.send(NotifierEvent::ShutDown).unwrap();
466     }
467 
new_frame_ready(&self, _: DocumentId, _scrolled: bool, composite_needed: bool, _render_time: Option<u64>)468     fn new_frame_ready(&self,
469                        _: DocumentId,
470                        _scrolled: bool,
471                        composite_needed: bool,
472                        _render_time: Option<u64>) {
473         // TODO(gw): Refactor wrench so that it can take advantage of cases
474         //           where no composite is required when appropriate.
475         self.wake_up(composite_needed);
476     }
477 }
478 
create_notifier() -> (Box<dyn RenderNotifier>, Receiver<NotifierEvent>)479 fn create_notifier() -> (Box<dyn RenderNotifier>, Receiver<NotifierEvent>) {
480     let (tx, rx) = channel();
481     (Box::new(Notifier { tx: tx }), rx)
482 }
483 
rawtest(mut wrench: Wrench, window: &mut WindowWrapper, rx: Receiver<NotifierEvent>)484 fn rawtest(mut wrench: Wrench, window: &mut WindowWrapper, rx: Receiver<NotifierEvent>) {
485     RawtestHarness::new(&mut wrench, window, &rx).run();
486     wrench.shut_down(rx);
487 }
488 
reftest<'a>( mut wrench: Wrench, window: &mut WindowWrapper, subargs: &clap::ArgMatches<'a>, rx: Receiver<NotifierEvent> ) -> usize489 fn reftest<'a>(
490     mut wrench: Wrench,
491     window: &mut WindowWrapper,
492     subargs: &clap::ArgMatches<'a>,
493     rx: Receiver<NotifierEvent>
494 ) -> usize {
495     let dim = window.get_inner_size();
496     let base_manifest = if cfg!(target_os = "android") {
497         Path::new("/sdcard/wrench/reftests/reftest.list")
498     } else {
499         Path::new("reftests/reftest.list")
500     };
501     let specific_reftest = subargs.value_of("REFTEST").map(|x| Path::new(x));
502     let mut reftest_options = ReftestOptions::default();
503     if let Some(allow_max_diff) = subargs.value_of("fuzz_tolerance") {
504         reftest_options.allow_max_difference = allow_max_diff.parse().unwrap_or(1);
505         reftest_options.allow_num_differences = dim.width as usize * dim.height as usize;
506     }
507     let num_failures = ReftestHarness::new(&mut wrench, window, &rx)
508         .run(base_manifest, specific_reftest, &reftest_options);
509     wrench.shut_down(rx);
510     num_failures
511 }
512 
main()513 fn main() {
514     #[cfg(feature = "env_logger")]
515     env_logger::init();
516 
517     #[cfg(target_os = "macos")]
518     {
519         use core_foundation::{self as cf, base::TCFType};
520         let i = cf::bundle::CFBundle::main_bundle().info_dictionary();
521         let mut i = unsafe { i.to_mutable() };
522         i.set(
523             cf::string::CFString::new("NSSupportsAutomaticGraphicsSwitching"),
524             cf::boolean::CFBoolean::true_value().into_CFType(),
525         );
526     }
527 
528     let args_yaml = load_yaml!("args.yaml");
529     let clap = clap::App::from_yaml(args_yaml)
530         .setting(clap::AppSettings::ArgRequiredElseHelp);
531 
532     // On android devices, attempt to read command line arguments
533     // from a text file located at /sdcard/wrench/args.
534     let args = if cfg!(target_os = "android") {
535         // get full backtraces by default because it's hard to request
536         // externally on android
537         std::env::set_var("RUST_BACKTRACE", "full");
538 
539         let mut args = vec!["wrench".to_string()];
540 
541         if let Ok(wrench_args) = fs::read_to_string("/sdcard/wrench/args") {
542             for line in wrench_args.lines() {
543                 if line.starts_with("env: ") {
544                     let envvar = &line[5..];
545                     if let Some(ix) = envvar.find('=') {
546                         std::env::set_var(&envvar[0..ix], &envvar[ix + 1..]);
547                     } else {
548                         std::env::set_var(envvar, "");
549                     }
550                     continue;
551                 }
552                 for arg in line.split_whitespace() {
553                     args.push(arg.to_string());
554                 }
555             }
556         }
557 
558         clap.get_matches_from(&args)
559     } else {
560         clap.get_matches()
561     };
562 
563     // handle some global arguments
564     let res_path = args.value_of("shaders").map(|s| PathBuf::from(s));
565     let size = args.value_of("size")
566         .map(|s| if s == "720p" {
567             DeviceIntSize::new(1280, 720)
568         } else if s == "1080p" {
569             DeviceIntSize::new(1920, 1080)
570         } else if s == "4k" {
571             DeviceIntSize::new(3840, 2160)
572         } else {
573             let x = s.find('x').expect(
574                 "Size must be specified exactly as 720p, 1080p, 4k, or width x height",
575             );
576             let w = s[0 .. x].parse::<i32>().expect("Invalid size width");
577             let h = s[x + 1 ..].parse::<i32>().expect("Invalid size height");
578             DeviceIntSize::new(w, h)
579         })
580         .unwrap_or(DeviceIntSize::new(1920, 1080));
581     let chase_primitive = match args.value_of("chase") {
582         Some(s) => {
583             match s.find(',') {
584                 Some(_) => {
585                     let items = s
586                         .split(',')
587                         .map(|s| s.parse::<f32>().unwrap())
588                         .collect::<Vec<_>>();
589                     let rect = LayoutRect::from_origin_and_size(
590                         LayoutPoint::new(items[0], items[1]),
591                         LayoutSize::new(items[2], items[3]),
592                     );
593                     webrender::ChasePrimitive::LocalRect(rect)
594                 }
595                 None => {
596                     let id = s.parse::<usize>().unwrap();
597                     webrender::ChasePrimitive::Id(webrender::PrimitiveDebugId(id))
598                 }
599             }
600         },
601         None => webrender::ChasePrimitive::Nothing,
602     };
603 
604     let dump_shader_source = args.value_of("dump_shader_source").map(String::from);
605 
606     let mut events_loop = if args.is_present("headless") {
607         None
608     } else {
609         Some(winit::EventsLoop::new())
610     };
611 
612     let gl_request = match args.value_of("renderer") {
613         Some("es3") => {
614             glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (3, 0))
615         }
616         Some("gl3") => {
617             glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2))
618         }
619         Some("default") | None => {
620             glutin::GlRequest::GlThenGles {
621                 opengl_version: (3, 2),
622                 opengles_version: (3, 0),
623             }
624         }
625         Some(api) => {
626             panic!("Unexpected renderer string {}", api);
627         }
628     };
629 
630     let software = args.is_present("software");
631 
632     let mut window = make_window(
633         size,
634         args.is_present("vsync"),
635         &events_loop,
636         args.is_present("angle"),
637         gl_request,
638         software,
639     );
640     let dim = window.get_inner_size();
641 
642     let needs_frame_notifier = ["perf", "reftest", "png", "rawtest", "test_invalidation"]
643         .iter()
644         .any(|s| args.subcommand_matches(s).is_some());
645     let (notifier, rx) = if needs_frame_notifier {
646         let (notifier, rx) = create_notifier();
647         (Some(notifier), Some(rx))
648     } else {
649         (None, None)
650     };
651 
652     let mut wrench = Wrench::new(
653         &mut window,
654         events_loop.as_mut().map(|el| el.create_proxy()),
655         res_path,
656         !args.is_present("use_unoptimized_shaders"),
657         dim,
658         args.is_present("rebuild"),
659         args.is_present("no_subpixel_aa"),
660         args.is_present("verbose"),
661         args.is_present("no_scissor"),
662         args.is_present("no_batch"),
663         args.is_present("precache"),
664         args.is_present("slow_subpixel"),
665         chase_primitive,
666         dump_shader_source,
667         notifier,
668     );
669 
670     if let Some(ui_str) = args.value_of("profiler_ui") {
671         wrench.renderer.set_profiler_ui(&ui_str);
672     }
673 
674     window.update(&mut wrench);
675 
676     if let Some(window_title) = wrench.take_title() {
677         if !cfg!(windows) {
678             window.set_title(&window_title);
679         }
680     }
681 
682     if let Some(subargs) = args.subcommand_matches("show") {
683         let no_block = args.is_present("no_block");
684         let no_batch = args.is_present("no_batch");
685         render(
686             &mut wrench,
687             &mut window,
688             size,
689             &mut events_loop,
690             subargs,
691             no_block,
692             no_batch,
693         );
694     } else if let Some(subargs) = args.subcommand_matches("png") {
695         let surface = match subargs.value_of("surface") {
696             Some("screen") | None => png::ReadSurface::Screen,
697             Some("gpu-cache") => png::ReadSurface::GpuCache,
698             _ => panic!("Unknown surface argument value")
699         };
700         let output_path = subargs.value_of("OUTPUT").map(PathBuf::from);
701         let reader = YamlFrameReader::new_from_args(subargs);
702         png::png(&mut wrench, surface, &mut window, reader, rx.unwrap(), output_path);
703     } else if let Some(subargs) = args.subcommand_matches("reftest") {
704         // Exit with an error code in order to ensure the CI job fails.
705         process::exit(reftest(wrench, &mut window, subargs, rx.unwrap()) as _);
706     } else if let Some(_) = args.subcommand_matches("rawtest") {
707         rawtest(wrench, &mut window, rx.unwrap());
708         return;
709     } else if let Some(subargs) = args.subcommand_matches("perf") {
710         // Perf mode wants to benchmark the total cost of drawing
711         // a new displaty list each frame.
712         wrench.rebuild_display_lists = true;
713 
714         let as_csv = subargs.is_present("csv");
715         let auto_filename = subargs.is_present("auto-filename");
716 
717         let warmup_frames = subargs.value_of("warmup_frames").map(|s| s.parse().unwrap());
718         let sample_count = subargs.value_of("sample_count").map(|s| s.parse().unwrap());
719 
720         let harness = PerfHarness::new(&mut wrench,
721                                        &mut window,
722                                        rx.unwrap(),
723                                        warmup_frames,
724                                        sample_count);
725 
726         let benchmark = match subargs.value_of("benchmark") {
727             Some(path) => path,
728             None => "benchmarks/benchmarks.list"
729         };
730         println!("Benchmark: {}", benchmark);
731         let base_manifest = Path::new(benchmark);
732 
733         let mut filename = subargs.value_of("filename").unwrap().to_string();
734         if auto_filename {
735             let timestamp = chrono::Local::now().format("%Y-%m-%d-%H-%M-%S");
736             filename.push_str(
737                 &format!("/wrench-perf-{}.{}",
738                             timestamp,
739                             if as_csv { "csv" } else { "json" }));
740         }
741         harness.run(base_manifest, &filename, as_csv);
742         return;
743     } else if let Some(_) = args.subcommand_matches("test_invalidation") {
744         let harness = test_invalidation::TestHarness::new(
745             &mut wrench,
746             &mut window,
747             rx.unwrap(),
748         );
749 
750         harness.run();
751     } else if let Some(subargs) = args.subcommand_matches("compare_perf") {
752         let first_filename = subargs.value_of("first_filename").unwrap();
753         let second_filename = subargs.value_of("second_filename").unwrap();
754         perf::compare(first_filename, second_filename);
755         return;
756     } else if let Some(_) = args.subcommand_matches("test_init") {
757         // Wrench::new() unwraps the Renderer initialization, so if
758         // we reach this point then we have initialized successfully.
759         println!("Initialization successful");
760     } else {
761         panic!("Should never have gotten here! {:?}", args);
762     };
763 
764     wrench.renderer.deinit();
765 
766     // On android force-exit the process otherwise it stays running forever.
767     #[cfg(target_os = "android")]
768     process::exit(0);
769 }
770 
render<'a>( wrench: &mut Wrench, window: &mut WindowWrapper, size: DeviceIntSize, events_loop: &mut Option<winit::EventsLoop>, subargs: &clap::ArgMatches<'a>, no_block: bool, no_batch: bool, )771 fn render<'a>(
772     wrench: &mut Wrench,
773     window: &mut WindowWrapper,
774     size: DeviceIntSize,
775     events_loop: &mut Option<winit::EventsLoop>,
776     subargs: &clap::ArgMatches<'a>,
777     no_block: bool,
778     no_batch: bool,
779 ) {
780     let input_path = subargs.value_of("INPUT").map(PathBuf::from).unwrap();
781 
782     // If the input is a directory, we are looking at a capture.
783     let mut thing = if input_path.join("scenes").as_path().is_dir() {
784         let scene_id = subargs.value_of("scene-id").map(|z| z.parse::<u32>().unwrap());
785         let frame_id = subargs.value_of("frame-id").map(|z| z.parse::<u32>().unwrap());
786         Box::new(CapturedSequence::new(
787             input_path,
788             scene_id.unwrap_or(1),
789             frame_id.unwrap_or(1),
790         ))
791     } else if input_path.as_path().is_dir() {
792         let mut documents = wrench.api.load_capture(input_path, None);
793         println!("loaded {:?}", documents.iter().map(|cd| cd.document_id).collect::<Vec<_>>());
794         let captured = documents.swap_remove(0);
795         wrench.document_id = captured.document_id;
796         Box::new(captured) as Box<dyn WrenchThing>
797     } else {
798         let extension = input_path
799             .extension()
800             .expect("Tried to render with an unknown file type.")
801             .to_str()
802             .expect("Tried to render with an unknown file type.");
803 
804         match extension {
805             "yaml" => Box::new(YamlFrameReader::new_from_args(subargs)) as Box<dyn WrenchThing>,
806             _ => panic!("Tried to render with an unknown file type."),
807         }
808     };
809 
810     let mut show_help = false;
811     let mut do_loop = false;
812     let mut cursor_position = WorldPoint::zero();
813 
814     window.update(wrench);
815     thing.do_frame(wrench);
816 
817     if let Some(fb_size) = wrench.renderer.device_size() {
818         window.resize(fb_size);
819     }
820 
821     let mut debug_flags = DebugFlags::empty();
822     debug_flags.set(DebugFlags::DISABLE_BATCHING, no_batch);
823 
824     // Default the profile overlay on for android.
825     if cfg!(target_os = "android") {
826         debug_flags.toggle(DebugFlags::PROFILER_DBG);
827         wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
828     }
829 
830     let mut body = |wrench: &mut Wrench, events: Vec<winit::Event>| {
831         let mut do_frame = false;
832         let mut do_render = false;
833 
834         for event in events {
835             match event {
836                 winit::Event::Awakened => {
837                     do_render = true;
838                 }
839                 winit::Event::WindowEvent { event, .. } => match event {
840                     winit::WindowEvent::CloseRequested => {
841                         return winit::ControlFlow::Break;
842                     }
843                     winit::WindowEvent::Refresh |
844                     winit::WindowEvent::Focused(..) => {
845                         do_render = true;
846                     }
847                     winit::WindowEvent::CursorMoved { position: LogicalPosition { x, y }, .. } => {
848                         cursor_position = WorldPoint::new(x as f32, y as f32);
849                         wrench.renderer.set_cursor_position(
850                             DeviceIntPoint::new(
851                                 cursor_position.x.round() as i32,
852                                 cursor_position.y.round() as i32,
853                             ),
854                         );
855                         do_render = true;
856                     }
857                     winit::WindowEvent::KeyboardInput {
858                         input: winit::KeyboardInput {
859                             state: winit::ElementState::Pressed,
860                             virtual_keycode: Some(vk),
861                             ..
862                         },
863                         ..
864                     } => match vk {
865                         VirtualKeyCode::Escape => {
866                             return winit::ControlFlow::Break;
867                         }
868                         VirtualKeyCode::B => {
869                             debug_flags.toggle(DebugFlags::INVALIDATION_DBG);
870                             wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
871                             do_render = true;
872                         }
873                         VirtualKeyCode::P => {
874                             debug_flags.toggle(DebugFlags::PROFILER_DBG);
875                             wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
876                             do_render = true;
877                         }
878                         VirtualKeyCode::O => {
879                             debug_flags.toggle(DebugFlags::RENDER_TARGET_DBG);
880                             wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
881                             do_render = true;
882                         }
883                         VirtualKeyCode::I => {
884                             debug_flags.toggle(DebugFlags::TEXTURE_CACHE_DBG);
885                             wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
886                             do_render = true;
887                         }
888                         VirtualKeyCode::D => {
889                             debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG);
890                             wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
891                             do_render = true;
892                         }
893                         VirtualKeyCode::Q => {
894                             debug_flags.toggle(DebugFlags::GPU_TIME_QUERIES | DebugFlags::GPU_SAMPLE_QUERIES);
895                             wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
896                             do_render = true;
897                         }
898                         VirtualKeyCode::V => {
899                             debug_flags.toggle(DebugFlags::SHOW_OVERDRAW);
900                             wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
901                             do_render = true;
902                         }
903                         VirtualKeyCode::G => {
904                             debug_flags.toggle(DebugFlags::GPU_CACHE_DBG);
905                             wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
906 
907                             // force scene rebuild to see the full set of used GPU cache entries
908                             let mut txn = Transaction::new();
909                             txn.set_root_pipeline(wrench.root_pipeline_id);
910                             wrench.api.send_transaction(wrench.document_id, txn);
911 
912                             do_frame = true;
913                         }
914                         VirtualKeyCode::M => {
915                             wrench.api.notify_memory_pressure();
916                             do_render = true;
917                         }
918                         VirtualKeyCode::L => {
919                             do_loop = !do_loop;
920                             do_render = true;
921                         }
922                         VirtualKeyCode::Left => {
923                             thing.prev_frame();
924                             do_frame = true;
925                         }
926                         VirtualKeyCode::Right => {
927                             thing.next_frame();
928                             do_frame = true;
929                         }
930                         VirtualKeyCode::H => {
931                             show_help = !show_help;
932                             do_render = true;
933                         }
934                         VirtualKeyCode::C => {
935                             let path = PathBuf::from("../captures/wrench");
936                             wrench.api.save_capture(path, CaptureBits::all());
937                         }
938                         VirtualKeyCode::X => {
939                             let results = wrench.api.hit_test(
940                                 wrench.document_id,
941                                 None,
942                                 cursor_position,
943                             );
944 
945                             println!("Hit test results:");
946                             for item in &results.items {
947                                 println!("  • {:?}", item);
948                             }
949                             println!("");
950                         }
951                         VirtualKeyCode::Z => {
952                             debug_flags.toggle(DebugFlags::ZOOM_DBG);
953                             wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
954                             do_render = true;
955                         }
956                         VirtualKeyCode::Y => {
957                             println!("Clearing all caches...");
958                             wrench.api.send_debug_cmd(DebugCommand::ClearCaches(ClearCache::all()));
959                             do_frame = true;
960                         }
961                         _ => {}
962                     }
963                     _ => {}
964                 },
965                 _ => {}
966             }
967         }
968 
969         window.update(wrench);
970 
971         if do_frame {
972             let frame_num = thing.do_frame(wrench);
973             unsafe {
974                 CURRENT_FRAME_NUMBER = frame_num;
975             }
976         }
977 
978         if do_render {
979             if show_help {
980                 wrench.show_onscreen_help();
981             }
982 
983             wrench.render();
984             window.upload_software_to_native();
985             window.swap_buffers();
986 
987             if do_loop {
988                 thing.next_frame();
989             }
990         }
991 
992         winit::ControlFlow::Continue
993     };
994 
995     match *events_loop {
996         None => {
997             while body(wrench, vec![winit::Event::Awakened]) == winit::ControlFlow::Continue {}
998             let fb_rect = FramebufferIntSize::new(size.width, size.height).into();
999             let pixels = wrench.renderer.read_pixels_rgba8(fb_rect);
1000             save_flipped("screenshot.png", pixels, size);
1001         }
1002         Some(ref mut events_loop) => {
1003             // We want to ensure that we:
1004             //
1005             // (a) Block the thread when no events are present (for CPU/battery purposes)
1006             // (b) Don't lag the input events by having the event queue back up.
1007             loop {
1008                 let mut pending_events = Vec::new();
1009 
1010                 // Block the thread until at least one event arrives
1011                 // On Android, we are generally profiling when running
1012                 // wrench, and don't want to block on UI events.
1013                 if !no_block && cfg!(not(target_os = "android")) {
1014                     events_loop.run_forever(|event| {
1015                         pending_events.push(event);
1016                         winit::ControlFlow::Break
1017                     });
1018                 }
1019 
1020                 // Collect any other pending events that are also available
1021                 events_loop.poll_events(|event| {
1022                     pending_events.push(event);
1023                 });
1024 
1025                 // Ensure there is at least one event present so that the
1026                 // frame gets rendered.
1027                 if pending_events.is_empty() {
1028                     pending_events.push(winit::Event::Awakened);
1029                 }
1030 
1031                 // Process all of those pending events in the next vsync period
1032                 if body(wrench, pending_events) == winit::ControlFlow::Break {
1033                     break;
1034                 }
1035             }
1036         }
1037     }
1038 }
1039