boilerplate.rs (10739B)
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 use gleam::gl; 6 use glutin; 7 use std::env; 8 use std::path::PathBuf; 9 use webrender; 10 use winit; 11 use winit::platform::run_return::EventLoopExtRunReturn; 12 use webrender::{DebugFlags, ShaderPrecacheFlags}; 13 use webrender::api::*; 14 use webrender::render_api::*; 15 use webrender::api::units::*; 16 17 struct Notifier { 18 events_proxy: winit::event_loop::EventLoopProxy<()>, 19 } 20 21 impl Notifier { 22 fn new(events_proxy: winit::event_loop::EventLoopProxy<()>) -> Notifier { 23 Notifier { events_proxy } 24 } 25 } 26 27 impl RenderNotifier for Notifier { 28 fn clone(&self) -> Box<dyn RenderNotifier> { 29 Box::new(Notifier { 30 events_proxy: self.events_proxy.clone(), 31 }) 32 } 33 34 fn wake_up(&self, _composite_needed: bool) { 35 #[cfg(not(target_os = "android"))] 36 let _ = self.events_proxy.send_event(()); 37 } 38 39 fn new_frame_ready(&self, 40 _: DocumentId, 41 _: FramePublishId, 42 params: &FrameReadyParams) { 43 self.wake_up(params.render); 44 } 45 } 46 47 #[allow(dead_code)] 48 pub trait HandyDandyRectBuilder { 49 fn to(&self, x2: i32, y2: i32) -> LayoutRect; 50 fn by(&self, w: i32, h: i32) -> LayoutRect; 51 } 52 // Allows doing `(x, y).to(x2, y2)` or `(x, y).by(width, height)` with i32 53 // values to build a f32 LayoutRect 54 impl HandyDandyRectBuilder for (i32, i32) { 55 fn to(&self, x2: i32, y2: i32) -> LayoutRect { 56 LayoutRect::from_origin_and_size( 57 LayoutPoint::new(self.0 as f32, self.1 as f32), 58 LayoutSize::new((x2 - self.0) as f32, (y2 - self.1) as f32), 59 ) 60 } 61 62 fn by(&self, w: i32, h: i32) -> LayoutRect { 63 LayoutRect::from_origin_and_size( 64 LayoutPoint::new(self.0 as f32, self.1 as f32), 65 LayoutSize::new(w as f32, h as f32), 66 ) 67 } 68 } 69 70 pub trait Example { 71 const TITLE: &'static str = "WebRender Sample App"; 72 const PRECACHE_SHADER_FLAGS: ShaderPrecacheFlags = ShaderPrecacheFlags::EMPTY; 73 const WIDTH: u32 = 1920; 74 const HEIGHT: u32 = 1080; 75 76 fn render( 77 &mut self, 78 api: &mut RenderApi, 79 builder: &mut DisplayListBuilder, 80 txn: &mut Transaction, 81 device_size: DeviceIntSize, 82 pipeline_id: PipelineId, 83 document_id: DocumentId, 84 ); 85 fn on_event( 86 &mut self, 87 _: winit::event::WindowEvent, 88 _: &winit::window::Window, 89 _: &mut RenderApi, 90 _: DocumentId, 91 ) -> bool { 92 false 93 } 94 fn get_image_handler( 95 &mut self, 96 _gl: &dyn gl::Gl, 97 ) -> Option<Box<dyn ExternalImageHandler>> { 98 None 99 } 100 fn draw_custom(&mut self, _gl: &dyn gl::Gl) { 101 } 102 } 103 104 pub fn main_wrapper<E: Example>( 105 example: &mut E, 106 options: Option<webrender::WebRenderOptions>, 107 ) { 108 env_logger::init(); 109 110 #[cfg(target_os = "macos")] 111 { 112 use core_foundation::{self as cf, base::TCFType}; 113 let i = cf::bundle::CFBundle::main_bundle().info_dictionary(); 114 let mut i = unsafe { i.to_mutable() }; 115 i.set( 116 cf::string::CFString::new("NSSupportsAutomaticGraphicsSwitching"), 117 cf::boolean::CFBoolean::true_value().into_CFType(), 118 ); 119 } 120 121 let args: Vec<String> = env::args().collect(); 122 let res_path = if args.len() > 1 { 123 Some(PathBuf::from(&args[1])) 124 } else { 125 None 126 }; 127 128 let mut events_loop = winit::event_loop::EventLoop::new(); 129 let window_builder = winit::window::WindowBuilder::new() 130 .with_title(E::TITLE) 131 .with_inner_size(winit::dpi::LogicalSize::new(E::WIDTH as f64, E::HEIGHT as f64)); 132 let windowed_context = glutin::ContextBuilder::new() 133 .with_gl(glutin::GlRequest::GlThenGles { 134 opengl_version: (3, 2), 135 opengles_version: (3, 0), 136 }) 137 .build_windowed(window_builder, &events_loop) 138 .unwrap(); 139 140 let windowed_context = unsafe { windowed_context.make_current().unwrap() }; 141 142 let gl = match windowed_context.get_api() { 143 glutin::Api::OpenGl => unsafe { 144 gl::GlFns::load_with( 145 |symbol| windowed_context.get_proc_address(symbol) as *const _ 146 ) 147 }, 148 glutin::Api::OpenGlEs => unsafe { 149 gl::GlesFns::load_with( 150 |symbol| windowed_context.get_proc_address(symbol) as *const _ 151 ) 152 }, 153 glutin::Api::WebGl => unimplemented!(), 154 }; 155 156 println!("OpenGL version {}", gl.get_string(gl::VERSION)); 157 println!("Shader resource path: {:?}", res_path); 158 let device_pixel_ratio = windowed_context.window().scale_factor() as f32; 159 println!("Device pixel ratio: {}", device_pixel_ratio); 160 161 println!("Loading shaders..."); 162 let mut debug_flags = DebugFlags::ECHO_DRIVER_MESSAGES | DebugFlags::TEXTURE_CACHE_DBG; 163 let opts = webrender::WebRenderOptions { 164 resource_override_path: res_path, 165 precache_flags: E::PRECACHE_SHADER_FLAGS, 166 clear_color: ColorF::new(0.3, 0.0, 0.0, 1.0), 167 debug_flags, 168 //allow_texture_swizzling: false, 169 ..options.unwrap_or(webrender::WebRenderOptions::default()) 170 }; 171 172 let device_size = { 173 let size = windowed_context 174 .window() 175 .inner_size(); 176 DeviceIntSize::new(size.width as i32, size.height as i32) 177 }; 178 let notifier = Box::new(Notifier::new(events_loop.create_proxy())); 179 let (mut renderer, sender) = webrender::create_webrender_instance( 180 gl.clone(), 181 notifier, 182 opts, 183 None, 184 ).unwrap(); 185 let mut api = sender.create_api(); 186 let document_id = api.add_document(device_size); 187 188 let external = example.get_image_handler(&*gl); 189 190 if let Some(external_image_handler) = external { 191 renderer.set_external_image_handler(external_image_handler); 192 } 193 194 let epoch = Epoch(0); 195 let pipeline_id = PipelineId(0, 0); 196 let mut builder = DisplayListBuilder::new(pipeline_id); 197 let mut txn = Transaction::new(); 198 builder.begin(); 199 200 example.render( 201 &mut api, 202 &mut builder, 203 &mut txn, 204 device_size, 205 pipeline_id, 206 document_id, 207 ); 208 txn.set_display_list( 209 epoch, 210 builder.end(), 211 ); 212 txn.set_root_pipeline(pipeline_id); 213 txn.generate_frame(0, true, false, RenderReasons::empty()); 214 api.send_transaction(document_id, txn); 215 216 println!("Entering event loop"); 217 events_loop.run_return(|global_event, _elwt, control_flow| { 218 let mut txn = Transaction::new(); 219 let mut custom_event = true; 220 221 let old_flags = debug_flags; 222 let win_event = match global_event { 223 winit::event::Event::WindowEvent { event, .. } => event, 224 _ => return, 225 }; 226 match win_event { 227 winit::event::WindowEvent::CloseRequested => { 228 *control_flow = winit::event_loop::ControlFlow::Exit; 229 return; 230 } 231 winit::event::WindowEvent::AxisMotion { .. } | 232 winit::event::WindowEvent::CursorMoved { .. } => { 233 custom_event = example.on_event( 234 win_event, 235 windowed_context.window(), 236 &mut api, 237 document_id, 238 ); 239 // skip high-frequency events from triggering a frame draw. 240 if !custom_event { 241 return; 242 } 243 }, 244 winit::event::WindowEvent::KeyboardInput { 245 input: winit::event::KeyboardInput { 246 state: winit::event::ElementState::Pressed, 247 virtual_keycode: Some(key), 248 .. 249 }, 250 .. 251 } => match key { 252 winit::event::VirtualKeyCode::Escape => { 253 *control_flow = winit::event_loop::ControlFlow::Exit; 254 return; 255 } 256 winit::event::VirtualKeyCode::P => debug_flags.toggle(DebugFlags::PROFILER_DBG), 257 winit::event::VirtualKeyCode::O => debug_flags.toggle(DebugFlags::RENDER_TARGET_DBG), 258 winit::event::VirtualKeyCode::I => debug_flags.toggle(DebugFlags::TEXTURE_CACHE_DBG), 259 winit::event::VirtualKeyCode::T => debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG), 260 winit::event::VirtualKeyCode::Q => debug_flags.toggle( 261 DebugFlags::GPU_TIME_QUERIES | DebugFlags::GPU_SAMPLE_QUERIES 262 ), 263 winit::event::VirtualKeyCode::G => debug_flags.toggle(DebugFlags::GPU_CACHE_DBG), 264 winit::event::VirtualKeyCode::M => api.notify_memory_pressure(), 265 winit::event::VirtualKeyCode::C => { 266 let path: PathBuf = "../captures/example".into(); 267 //TODO: switch between SCENE/FRAME capture types 268 // based on "shift" modifier, when `glutin` is updated. 269 let bits = CaptureBits::all(); 270 api.save_capture(path, bits); 271 }, 272 _ => { 273 custom_event = example.on_event( 274 win_event, 275 windowed_context.window(), 276 &mut api, 277 document_id, 278 ) 279 }, 280 }, 281 other => custom_event = example.on_event( 282 other, 283 windowed_context.window(), 284 &mut api, 285 document_id, 286 ), 287 }; 288 289 if debug_flags != old_flags { 290 api.send_debug_cmd(DebugCommand::SetFlags(debug_flags)); 291 } 292 293 if custom_event { 294 let mut builder = DisplayListBuilder::new(pipeline_id); 295 builder.begin(); 296 297 example.render( 298 &mut api, 299 &mut builder, 300 &mut txn, 301 device_size, 302 pipeline_id, 303 document_id, 304 ); 305 txn.set_display_list( 306 epoch, 307 builder.end(), 308 ); 309 txn.generate_frame(0, true, false, RenderReasons::empty()); 310 } 311 api.send_transaction(document_id, txn); 312 313 renderer.update(); 314 renderer.render(device_size, 0).unwrap(); 315 let _ = renderer.flush_pipeline_info(); 316 example.draw_custom(&*gl); 317 windowed_context.swap_buffers().ok(); 318 319 *control_flow = winit::event_loop::ControlFlow::Wait; 320 }); 321 322 renderer.deinit(); 323 }