summaryrefslogtreecommitdiffstats
path: root/gfx/wr/examples/common/boilerplate.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/wr/examples/common/boilerplate.rs323
1 files changed, 323 insertions, 0 deletions
diff --git a/gfx/wr/examples/common/boilerplate.rs b/gfx/wr/examples/common/boilerplate.rs
new file mode 100644
index 0000000000..11f153f6e8
--- /dev/null
+++ b/gfx/wr/examples/common/boilerplate.rs
@@ -0,0 +1,323 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use gleam::gl;
+use glutin;
+use std::env;
+use std::path::PathBuf;
+use webrender;
+use winit;
+use winit::platform::run_return::EventLoopExtRunReturn;
+use webrender::{DebugFlags, ShaderPrecacheFlags};
+use webrender::api::*;
+use webrender::render_api::*;
+use webrender::api::units::*;
+
+struct Notifier {
+ events_proxy: winit::event_loop::EventLoopProxy<()>,
+}
+
+impl Notifier {
+ fn new(events_proxy: winit::event_loop::EventLoopProxy<()>) -> Notifier {
+ Notifier { events_proxy }
+ }
+}
+
+impl RenderNotifier for Notifier {
+ fn clone(&self) -> Box<dyn RenderNotifier> {
+ Box::new(Notifier {
+ events_proxy: self.events_proxy.clone(),
+ })
+ }
+
+ fn wake_up(&self, _composite_needed: bool) {
+ #[cfg(not(target_os = "android"))]
+ let _ = self.events_proxy.send_event(());
+ }
+
+ fn new_frame_ready(&self,
+ _: DocumentId,
+ _scrolled: bool,
+ composite_needed: bool,
+ _: FramePublishId) {
+ self.wake_up(composite_needed);
+ }
+}
+
+pub trait HandyDandyRectBuilder {
+ fn to(&self, x2: i32, y2: i32) -> LayoutRect;
+ fn by(&self, w: i32, h: i32) -> LayoutRect;
+}
+// Allows doing `(x, y).to(x2, y2)` or `(x, y).by(width, height)` with i32
+// values to build a f32 LayoutRect
+impl HandyDandyRectBuilder for (i32, i32) {
+ fn to(&self, x2: i32, y2: i32) -> LayoutRect {
+ LayoutRect::from_origin_and_size(
+ LayoutPoint::new(self.0 as f32, self.1 as f32),
+ LayoutSize::new((x2 - self.0) as f32, (y2 - self.1) as f32),
+ )
+ }
+
+ fn by(&self, w: i32, h: i32) -> LayoutRect {
+ LayoutRect::from_origin_and_size(
+ LayoutPoint::new(self.0 as f32, self.1 as f32),
+ LayoutSize::new(w as f32, h as f32),
+ )
+ }
+}
+
+pub trait Example {
+ const TITLE: &'static str = "WebRender Sample App";
+ const PRECACHE_SHADER_FLAGS: ShaderPrecacheFlags = ShaderPrecacheFlags::EMPTY;
+ const WIDTH: u32 = 1920;
+ const HEIGHT: u32 = 1080;
+
+ fn render(
+ &mut self,
+ api: &mut RenderApi,
+ builder: &mut DisplayListBuilder,
+ txn: &mut Transaction,
+ device_size: DeviceIntSize,
+ pipeline_id: PipelineId,
+ document_id: DocumentId,
+ );
+ fn on_event(
+ &mut self,
+ _: winit::event::WindowEvent,
+ _: &winit::window::Window,
+ _: &mut RenderApi,
+ _: DocumentId,
+ ) -> bool {
+ false
+ }
+ fn get_image_handler(
+ &mut self,
+ _gl: &dyn gl::Gl,
+ ) -> Option<Box<dyn ExternalImageHandler>> {
+ None
+ }
+ fn draw_custom(&mut self, _gl: &dyn gl::Gl) {
+ }
+}
+
+pub fn main_wrapper<E: Example>(
+ example: &mut E,
+ options: Option<webrender::WebRenderOptions>,
+) {
+ env_logger::init();
+
+ #[cfg(target_os = "macos")]
+ {
+ use core_foundation::{self as cf, base::TCFType};
+ let i = cf::bundle::CFBundle::main_bundle().info_dictionary();
+ let mut i = unsafe { i.to_mutable() };
+ i.set(
+ cf::string::CFString::new("NSSupportsAutomaticGraphicsSwitching"),
+ cf::boolean::CFBoolean::true_value().into_CFType(),
+ );
+ }
+
+ let args: Vec<String> = env::args().collect();
+ let res_path = if args.len() > 1 {
+ Some(PathBuf::from(&args[1]))
+ } else {
+ None
+ };
+
+ let mut events_loop = winit::event_loop::EventLoop::new();
+ let window_builder = winit::window::WindowBuilder::new()
+ .with_title(E::TITLE)
+ .with_inner_size(winit::dpi::LogicalSize::new(E::WIDTH as f64, E::HEIGHT as f64));
+ let windowed_context = glutin::ContextBuilder::new()
+ .with_gl(glutin::GlRequest::GlThenGles {
+ opengl_version: (3, 2),
+ opengles_version: (3, 0),
+ })
+ .build_windowed(window_builder, &events_loop)
+ .unwrap();
+
+ let windowed_context = unsafe { windowed_context.make_current().unwrap() };
+
+ let gl = match windowed_context.get_api() {
+ glutin::Api::OpenGl => unsafe {
+ gl::GlFns::load_with(
+ |symbol| windowed_context.get_proc_address(symbol) as *const _
+ )
+ },
+ glutin::Api::OpenGlEs => unsafe {
+ gl::GlesFns::load_with(
+ |symbol| windowed_context.get_proc_address(symbol) as *const _
+ )
+ },
+ glutin::Api::WebGl => unimplemented!(),
+ };
+
+ println!("OpenGL version {}", gl.get_string(gl::VERSION));
+ println!("Shader resource path: {:?}", res_path);
+ let device_pixel_ratio = windowed_context.window().scale_factor() as f32;
+ println!("Device pixel ratio: {}", device_pixel_ratio);
+
+ println!("Loading shaders...");
+ let mut debug_flags = DebugFlags::ECHO_DRIVER_MESSAGES | DebugFlags::TEXTURE_CACHE_DBG;
+ let opts = webrender::WebRenderOptions {
+ resource_override_path: res_path,
+ precache_flags: E::PRECACHE_SHADER_FLAGS,
+ clear_color: ColorF::new(0.3, 0.0, 0.0, 1.0),
+ debug_flags,
+ //allow_texture_swizzling: false,
+ ..options.unwrap_or(webrender::WebRenderOptions::default())
+ };
+
+ let device_size = {
+ let size = windowed_context
+ .window()
+ .inner_size();
+ DeviceIntSize::new(size.width as i32, size.height as i32)
+ };
+ let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
+ let (mut renderer, sender) = webrender::create_webrender_instance(
+ gl.clone(),
+ notifier,
+ opts,
+ None,
+ ).unwrap();
+ let mut api = sender.create_api();
+ let document_id = api.add_document(device_size);
+
+ let external = example.get_image_handler(&*gl);
+
+ if let Some(external_image_handler) = external {
+ renderer.set_external_image_handler(external_image_handler);
+ }
+
+ let epoch = Epoch(0);
+ let pipeline_id = PipelineId(0, 0);
+ let mut builder = DisplayListBuilder::new(pipeline_id);
+ let mut txn = Transaction::new();
+ builder.begin();
+
+ example.render(
+ &mut api,
+ &mut builder,
+ &mut txn,
+ device_size,
+ pipeline_id,
+ document_id,
+ );
+ txn.set_display_list(
+ epoch,
+ builder.end(),
+ );
+ txn.set_root_pipeline(pipeline_id);
+ txn.generate_frame(0, RenderReasons::empty());
+ api.send_transaction(document_id, txn);
+
+ println!("Entering event loop");
+ events_loop.run_return(|global_event, _elwt, control_flow| {
+ let mut txn = Transaction::new();
+ let mut custom_event = true;
+
+ let old_flags = debug_flags;
+ let win_event = match global_event {
+ winit::event::Event::WindowEvent { event, .. } => event,
+ _ => return,
+ };
+ match win_event {
+ winit::event::WindowEvent::CloseRequested => {
+ *control_flow = winit::event_loop::ControlFlow::Exit;
+ return;
+ }
+ winit::event::WindowEvent::AxisMotion { .. } |
+ winit::event::WindowEvent::CursorMoved { .. } => {
+ custom_event = example.on_event(
+ win_event,
+ windowed_context.window(),
+ &mut api,
+ document_id,
+ );
+ // skip high-frequency events from triggering a frame draw.
+ if !custom_event {
+ return;
+ }
+ },
+ winit::event::WindowEvent::KeyboardInput {
+ input: winit::event::KeyboardInput {
+ state: winit::event::ElementState::Pressed,
+ virtual_keycode: Some(key),
+ ..
+ },
+ ..
+ } => match key {
+ winit::event::VirtualKeyCode::Escape => {
+ *control_flow = winit::event_loop::ControlFlow::Exit;
+ return;
+ }
+ winit::event::VirtualKeyCode::P => debug_flags.toggle(DebugFlags::PROFILER_DBG),
+ winit::event::VirtualKeyCode::O => debug_flags.toggle(DebugFlags::RENDER_TARGET_DBG),
+ winit::event::VirtualKeyCode::I => debug_flags.toggle(DebugFlags::TEXTURE_CACHE_DBG),
+ winit::event::VirtualKeyCode::T => debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG),
+ winit::event::VirtualKeyCode::Q => debug_flags.toggle(
+ DebugFlags::GPU_TIME_QUERIES | DebugFlags::GPU_SAMPLE_QUERIES
+ ),
+ winit::event::VirtualKeyCode::G => debug_flags.toggle(DebugFlags::GPU_CACHE_DBG),
+ winit::event::VirtualKeyCode::M => api.notify_memory_pressure(),
+ winit::event::VirtualKeyCode::C => {
+ let path: PathBuf = "../captures/example".into();
+ //TODO: switch between SCENE/FRAME capture types
+ // based on "shift" modifier, when `glutin` is updated.
+ let bits = CaptureBits::all();
+ api.save_capture(path, bits);
+ },
+ _ => {
+ custom_event = example.on_event(
+ win_event,
+ windowed_context.window(),
+ &mut api,
+ document_id,
+ )
+ },
+ },
+ other => custom_event = example.on_event(
+ other,
+ windowed_context.window(),
+ &mut api,
+ document_id,
+ ),
+ };
+
+ if debug_flags != old_flags {
+ api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
+ }
+
+ if custom_event {
+ let mut builder = DisplayListBuilder::new(pipeline_id);
+ builder.begin();
+
+ example.render(
+ &mut api,
+ &mut builder,
+ &mut txn,
+ device_size,
+ pipeline_id,
+ document_id,
+ );
+ txn.set_display_list(
+ epoch,
+ builder.end(),
+ );
+ txn.generate_frame(0, RenderReasons::empty());
+ }
+ api.send_transaction(document_id, txn);
+
+ renderer.update();
+ renderer.render(device_size, 0).unwrap();
+ let _ = renderer.flush_pipeline_info();
+ example.draw_custom(&*gl);
+ windowed_context.swap_buffers().ok();
+
+ *control_flow = winit::event_loop::ControlFlow::Wait;
+ });
+
+ renderer.deinit();
+}