--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -58,20 +58,16 @@ git = "https://github.com/pcwalton/pathf
optional = true
[dependencies.pathfinder_path_utils]
git = "https://github.com/pcwalton/pathfinder"
optional = true
[dev-dependencies]
mozangle = "0.1"
-env_logger = "0.5"
-rand = "0.3" # for the benchmarks
-glutin = "0.15" # for the example apps
-winit = "0.13" # for the example apps
[target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
freetype = { version = "0.4", default-features = false }
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.4.1"
[target.'cfg(target_os = "macos")'.dependencies]
deleted file mode 100644
--- a/gfx/webrender/examples/alpha_perf.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-/* 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/. */
-
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use std::cmp;
-use webrender::api::*;
-
-struct App {
- rect_count: usize,
-}
-
-impl Example for App {
- fn render(
- &mut self,
- _api: &RenderApi,
- builder: &mut DisplayListBuilder,
- _txn: &mut Transaction,
- _framebuffer_size: DeviceUintSize,
- _pipeline_id: PipelineId,
- _document_id: DocumentId,
- ) {
- let bounds = (0, 0).to(1920, 1080);
- let info = LayoutPrimitiveInfo::new(bounds);
-
- builder.push_stacking_context(
- &info,
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- Vec::new(),
- GlyphRasterSpace::Screen,
- );
-
- for _ in 0 .. self.rect_count {
- builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 0.05));
- }
-
- builder.pop_stacking_context();
- }
-
- fn on_event(
- &mut self,
- event: winit::WindowEvent,
- _api: &RenderApi,
- _document_id: DocumentId
- ) -> bool {
- match event {
- winit::WindowEvent::KeyboardInput {
- input: winit::KeyboardInput {
- state: winit::ElementState::Pressed,
- virtual_keycode: Some(key),
- ..
- },
- ..
- } => {
- match key {
- winit::VirtualKeyCode::Right => {
- self.rect_count += 1;
- println!("rects = {}", self.rect_count);
- }
- winit::VirtualKeyCode::Left => {
- self.rect_count = cmp::max(self.rect_count, 1) - 1;
- println!("rects = {}", self.rect_count);
- }
- _ => {}
- };
- }
- _ => (),
- }
-
- true
- }
-}
-
-fn main() {
- let mut app = App {
- rect_count: 1,
- };
- boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/animation.rs
+++ /dev/null
@@ -1,152 +0,0 @@
-/* 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/. */
-
-//! This example creates a 200x200 white rect and allows the user to move it
-//! around by using the arrow keys and rotate with '<'/'>'.
-//! It does this by using the animation API.
-
-//! The example also features seamless opaque/transparent split of a
-//! rounded cornered rectangle, which is done automatically during the
-//! scene building for render optimization.
-
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use euclid::Angle;
-use webrender::api::*;
-
-struct App {
- property_key: PropertyBindingKey<LayoutTransform>,
- opacity_key: PropertyBindingKey<f32>,
- transform: LayoutTransform,
- opacity: f32,
-}
-
-impl Example for App {
- fn render(
- &mut self,
- _api: &RenderApi,
- builder: &mut DisplayListBuilder,
- _txn: &mut Transaction,
- _framebuffer_size: DeviceUintSize,
- _pipeline_id: PipelineId,
- _document_id: DocumentId,
- ) {
- // Create a 200x200 stacking context with an animated transform property.
- let bounds = (0, 0).to(200, 200);
-
- let filters = vec![
- FilterOp::Opacity(PropertyBinding::Binding(self.opacity_key, self.opacity), self.opacity),
- ];
-
- let info = LayoutPrimitiveInfo::new(bounds);
- let reference_frame_id = builder.push_reference_frame(
- &info,
- Some(PropertyBinding::Binding(self.property_key, LayoutTransform::identity())),
- None,
- );
-
- builder.push_clip_id(reference_frame_id);
-
- let info = LayoutPrimitiveInfo::new(bounds);
- builder.push_stacking_context(
- &info,
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- filters,
- GlyphRasterSpace::Screen,
- );
-
- let complex_clip = ComplexClipRegion {
- rect: bounds,
- radii: BorderRadius::uniform(50.0),
- mode: ClipMode::Clip,
- };
- let clip_id = builder.define_clip(bounds, vec![complex_clip], None);
- builder.push_clip_id(clip_id);
-
- // Fill it with a white rect
- builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
-
- builder.pop_clip_id();
-
- builder.pop_stacking_context();
-
- builder.pop_clip_id();
- builder.pop_reference_frame();
- }
-
- fn on_event(&mut self, win_event: winit::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
- match win_event {
- winit::WindowEvent::KeyboardInput {
- input: winit::KeyboardInput {
- state: winit::ElementState::Pressed,
- virtual_keycode: Some(key),
- ..
- },
- ..
- } => {
- let (offset_x, offset_y, angle, delta_opacity) = match key {
- winit::VirtualKeyCode::Down => (0.0, 10.0, 0.0, 0.0),
- winit::VirtualKeyCode::Up => (0.0, -10.0, 0.0, 0.0),
- winit::VirtualKeyCode::Right => (10.0, 0.0, 0.0, 0.0),
- winit::VirtualKeyCode::Left => (-10.0, 0.0, 0.0, 0.0),
- winit::VirtualKeyCode::Comma => (0.0, 0.0, 0.1, 0.0),
- winit::VirtualKeyCode::Period => (0.0, 0.0, -0.1, 0.0),
- winit::VirtualKeyCode::Z => (0.0, 0.0, 0.0, -0.1),
- winit::VirtualKeyCode::X => (0.0, 0.0, 0.0, 0.1),
- _ => return false,
- };
- // Update the transform based on the keyboard input and push it to
- // webrender using the generate_frame API. This will recomposite with
- // the updated transform.
- self.opacity += delta_opacity;
- let new_transform = self.transform
- .pre_rotate(0.0, 0.0, 1.0, Angle::radians(angle))
- .post_translate(LayoutVector3D::new(offset_x, offset_y, 0.0));
- let mut txn = Transaction::new();
- txn.update_dynamic_properties(
- DynamicProperties {
- transforms: vec![
- PropertyValue {
- key: self.property_key,
- value: new_transform,
- },
- ],
- floats: vec![
- PropertyValue {
- key: self.opacity_key,
- value: self.opacity,
- }
- ],
- },
- );
- txn.generate_frame();
- api.send_transaction(document_id, txn);
- self.transform = new_transform;
- }
- _ => (),
- }
-
- false
- }
-}
-
-fn main() {
- let mut app = App {
- property_key: PropertyBindingKey::new(42), // arbitrary magic number
- opacity_key: PropertyBindingKey::new(43),
- transform: LayoutTransform::create_translation(0.0, 0.0, 0.0),
- opacity: 0.5,
- };
- boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/basic.rs
+++ /dev/null
@@ -1,298 +0,0 @@
-/* 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/. */
-
-extern crate app_units;
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use euclid::vec2;
-use winit::TouchPhase;
-use std::collections::HashMap;
-use webrender::api::*;
-
-#[derive(Debug)]
-enum Gesture {
- None,
- Pan,
- Zoom,
-}
-
-#[derive(Debug)]
-struct Touch {
- id: u64,
- start_x: f32,
- start_y: f32,
- current_x: f32,
- current_y: f32,
-}
-
-fn dist(x0: f32, y0: f32, x1: f32, y1: f32) -> f32 {
- let dx = x0 - x1;
- let dy = y0 - y1;
- ((dx * dx) + (dy * dy)).sqrt()
-}
-
-impl Touch {
- fn distance_from_start(&self) -> f32 {
- dist(self.start_x, self.start_y, self.current_x, self.current_y)
- }
-
- fn initial_distance_from_other(&self, other: &Touch) -> f32 {
- dist(self.start_x, self.start_y, other.start_x, other.start_y)
- }
-
- fn current_distance_from_other(&self, other: &Touch) -> f32 {
- dist(
- self.current_x,
- self.current_y,
- other.current_x,
- other.current_y,
- )
- }
-}
-
-struct TouchState {
- active_touches: HashMap<u64, Touch>,
- current_gesture: Gesture,
- start_zoom: f32,
- current_zoom: f32,
- start_pan: DeviceIntPoint,
- current_pan: DeviceIntPoint,
-}
-
-enum TouchResult {
- None,
- Pan(DeviceIntPoint),
- Zoom(f32),
-}
-
-impl TouchState {
- fn new() -> TouchState {
- TouchState {
- active_touches: HashMap::new(),
- current_gesture: Gesture::None,
- start_zoom: 1.0,
- current_zoom: 1.0,
- start_pan: DeviceIntPoint::zero(),
- current_pan: DeviceIntPoint::zero(),
- }
- }
-
- fn handle_event(&mut self, touch: winit::Touch) -> TouchResult {
- match touch.phase {
- TouchPhase::Started => {
- debug_assert!(!self.active_touches.contains_key(&touch.id));
- self.active_touches.insert(
- touch.id,
- Touch {
- id: touch.id,
- start_x: touch.location.0 as f32,
- start_y: touch.location.1 as f32,
- current_x: touch.location.0 as f32,
- current_y: touch.location.1 as f32,
- },
- );
- self.current_gesture = Gesture::None;
- }
- TouchPhase::Moved => {
- match self.active_touches.get_mut(&touch.id) {
- Some(active_touch) => {
- active_touch.current_x = touch.location.0 as f32;
- active_touch.current_y = touch.location.1 as f32;
- }
- None => panic!("move touch event with unknown touch id!"),
- }
-
- match self.current_gesture {
- Gesture::None => {
- let mut over_threshold_count = 0;
- let active_touch_count = self.active_touches.len();
-
- for (_, touch) in &self.active_touches {
- if touch.distance_from_start() > 8.0 {
- over_threshold_count += 1;
- }
- }
-
- if active_touch_count == over_threshold_count {
- if active_touch_count == 1 {
- self.start_pan = self.current_pan;
- self.current_gesture = Gesture::Pan;
- } else if active_touch_count == 2 {
- self.start_zoom = self.current_zoom;
- self.current_gesture = Gesture::Zoom;
- }
- }
- }
- Gesture::Pan => {
- let keys: Vec<u64> = self.active_touches.keys().cloned().collect();
- debug_assert!(keys.len() == 1);
- let active_touch = &self.active_touches[&keys[0]];
- let x = active_touch.current_x - active_touch.start_x;
- let y = active_touch.current_y - active_touch.start_y;
- self.current_pan.x = self.start_pan.x + x.round() as i32;
- self.current_pan.y = self.start_pan.y + y.round() as i32;
- return TouchResult::Pan(self.current_pan);
- }
- Gesture::Zoom => {
- let keys: Vec<u64> = self.active_touches.keys().cloned().collect();
- debug_assert!(keys.len() == 2);
- let touch0 = &self.active_touches[&keys[0]];
- let touch1 = &self.active_touches[&keys[1]];
- let initial_distance = touch0.initial_distance_from_other(touch1);
- let current_distance = touch0.current_distance_from_other(touch1);
- self.current_zoom = self.start_zoom * current_distance / initial_distance;
- return TouchResult::Zoom(self.current_zoom);
- }
- }
- }
- TouchPhase::Ended | TouchPhase::Cancelled => {
- self.active_touches.remove(&touch.id).unwrap();
- self.current_gesture = Gesture::None;
- }
- }
-
- TouchResult::None
- }
-}
-
-fn main() {
- let mut app = App {
- touch_state: TouchState::new(),
- };
- boilerplate::main_wrapper(&mut app, None);
-}
-
-struct App {
- touch_state: TouchState,
-}
-
-impl Example for App {
- // Make this the only example to test all shaders for compile errors.
- const PRECACHE_SHADERS: bool = true;
-
- fn render(
- &mut self,
- api: &RenderApi,
- builder: &mut DisplayListBuilder,
- txn: &mut Transaction,
- _: DeviceUintSize,
- _pipeline_id: PipelineId,
- _document_id: DocumentId,
- ) {
- let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
- let info = LayoutPrimitiveInfo::new(bounds);
- builder.push_stacking_context(
- &info,
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- Vec::new(),
- GlyphRasterSpace::Screen,
- );
-
- let image_mask_key = api.generate_image_key();
- txn.add_image(
- image_mask_key,
- ImageDescriptor::new(2, 2, ImageFormat::R8, true, false),
- ImageData::new(vec![0, 80, 180, 255]),
- None,
- );
- let mask = ImageMask {
- image: image_mask_key,
- rect: (75, 75).by(100, 100),
- repeat: false,
- };
- let complex = ComplexClipRegion::new(
- (50, 50).to(150, 150),
- BorderRadius::uniform(20.0),
- ClipMode::Clip
- );
- let id = builder.define_clip(bounds, vec![complex], Some(mask));
- builder.push_clip_id(id);
-
- let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
- builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
-
- let info = LayoutPrimitiveInfo::new((250, 100).to(350, 200));
- builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
- let border_side = BorderSide {
- color: ColorF::new(0.0, 0.0, 1.0, 1.0),
- style: BorderStyle::Groove,
- };
- let border_widths = BorderWidths {
- top: 10.0,
- left: 10.0,
- bottom: 10.0,
- right: 10.0,
- };
- let border_details = BorderDetails::Normal(NormalBorder {
- top: border_side,
- right: border_side,
- bottom: border_side,
- left: border_side,
- radius: BorderRadius::uniform(20.0),
- });
-
- let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
- builder.push_border(&info, border_widths, border_details);
- builder.pop_clip_id();
-
- if false {
- // draw box shadow?
- let rect = LayoutRect::zero();
- let simple_box_bounds = (20, 200).by(50, 50);
- let offset = vec2(10.0, 10.0);
- let color = ColorF::new(1.0, 1.0, 1.0, 1.0);
- let blur_radius = 0.0;
- let spread_radius = 0.0;
- let simple_border_radius = 8.0;
- let box_shadow_type = BoxShadowClipMode::Inset;
- let info = LayoutPrimitiveInfo::with_clip_rect(rect, bounds);
-
- builder.push_box_shadow(
- &info,
- simple_box_bounds,
- offset,
- color,
- blur_radius,
- spread_radius,
- BorderRadius::uniform(simple_border_radius),
- box_shadow_type,
- );
- }
-
- builder.pop_stacking_context();
- }
-
- fn on_event(&mut self, event: winit::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
- let mut txn = Transaction::new();
- match event {
- winit::WindowEvent::Touch(touch) => match self.touch_state.handle_event(touch) {
- TouchResult::Pan(pan) => {
- txn.set_pan(pan);
- }
- TouchResult::Zoom(zoom) => {
- txn.set_pinch_zoom(ZoomFactor::new(zoom));
- }
- TouchResult::None => {}
- },
- _ => (),
- }
-
- if !txn.is_empty() {
- txn.generate_frame();
- api.send_transaction(document_id, txn);
- }
-
- false
- }
-}
deleted file mode 100644
--- a/gfx/webrender/examples/blob.rs
+++ /dev/null
@@ -1,302 +0,0 @@
-/* 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/. */
-
-extern crate gleam;
-extern crate glutin;
-extern crate rayon;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use rayon::{ThreadPool, ThreadPoolBuilder};
-use std::collections::HashMap;
-use std::collections::hash_map::Entry;
-use std::sync::Arc;
-use std::sync::mpsc::{Receiver, Sender, channel};
-use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, RenderApi, Transaction};
-
-// This example shows how to implement a very basic BlobImageRenderer that can only render
-// a checkerboard pattern.
-
-// The deserialized command list internally used by this example is just a color.
-type ImageRenderingCommands = api::ColorU;
-
-// Serialize/deserialize the blob.
-// For real usecases you should probably use serde rather than doing it by hand.
-
-fn serialize_blob(color: api::ColorU) -> Vec<u8> {
- vec![color.r, color.g, color.b, color.a]
-}
-
-fn deserialize_blob(blob: &[u8]) -> Result<ImageRenderingCommands, ()> {
- let mut iter = blob.iter();
- return match (iter.next(), iter.next(), iter.next(), iter.next()) {
- (Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(api::ColorU::new(r, g, b, a)),
- (Some(&a), None, None, None) => Ok(api::ColorU::new(a, a, a, a)),
- _ => Err(()),
- };
-}
-
-// This is the function that applies the deserialized drawing commands and generates
-// actual image data.
-fn render_blob(
- commands: Arc<ImageRenderingCommands>,
- descriptor: &api::BlobImageDescriptor,
- tile: Option<api::TileOffset>,
-) -> api::BlobImageResult {
- let color = *commands;
-
- // Allocate storage for the result. Right now the resource cache expects the
- // tiles to have have no stride or offset.
- let mut texels = Vec::with_capacity((descriptor.size.width * descriptor.size.height * 4) as usize);
-
- // Generate a per-tile pattern to see it in the demo. For a real use case it would not
- // make sense for the rendered content to depend on its tile.
- let tile_checker = match tile {
- Some(tile) => (tile.x % 2 == 0) != (tile.y % 2 == 0),
- None => true,
- };
-
- for y in 0 .. descriptor.size.height {
- for x in 0 .. descriptor.size.width {
- // Apply the tile's offset. This is important: all drawing commands should be
- // translated by this offset to give correct results with tiled blob images.
- let x2 = x + descriptor.offset.x as u32;
- let y2 = y + descriptor.offset.y as u32;
-
- // Render a simple checkerboard pattern
- let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) {
- 1
- } else {
- 0
- };
- // ..nested in the per-tile checkerboard pattern
- let tc = if tile_checker { 0 } else { (1 - checker) * 40 };
-
- match descriptor.format {
- api::ImageFormat::BGRA8 => {
- texels.push(color.b * checker + tc);
- texels.push(color.g * checker + tc);
- texels.push(color.r * checker + tc);
- texels.push(color.a * checker + tc);
- }
- api::ImageFormat::R8 => {
- texels.push(color.a * checker + tc);
- }
- _ => {
- return Err(api::BlobImageError::Other(
- format!("Unsupported image format"),
- ));
- }
- }
- }
- }
-
- Ok(api::RasterizedBlobImage {
- data: texels,
- size: descriptor.size,
- })
-}
-
-struct CheckerboardRenderer {
- // We are going to defer the rendering work to worker threads.
- // Using a pre-built Arc<ThreadPool> rather than creating our own threads
- // makes it possible to share the same thread pool as the glyph renderer (if we
- // want to).
- workers: Arc<ThreadPool>,
-
- // the workers will use an mpsc channel to communicate the result.
- tx: Sender<(api::BlobImageRequest, api::BlobImageResult)>,
- rx: Receiver<(api::BlobImageRequest, api::BlobImageResult)>,
-
- // The deserialized drawing commands.
- // In this example we store them in Arcs. This isn't necessary since in this simplified
- // case the command list is a simple 32 bits value and would be cheap to clone before sending
- // to the workers. But in a more realistic scenario the commands would typically be bigger
- // and more expensive to clone, so let's pretend it is also the case here.
- image_cmds: HashMap<api::ImageKey, Arc<ImageRenderingCommands>>,
-
- // The images rendered in the current frame (not kept here between frames).
- rendered_images: HashMap<api::BlobImageRequest, Option<api::BlobImageResult>>,
-}
-
-impl CheckerboardRenderer {
- fn new(workers: Arc<ThreadPool>) -> Self {
- let (tx, rx) = channel();
- CheckerboardRenderer {
- image_cmds: HashMap::new(),
- rendered_images: HashMap::new(),
- workers,
- tx,
- rx,
- }
- }
-}
-
-impl api::BlobImageRenderer for CheckerboardRenderer {
- fn add(&mut self, key: api::ImageKey, cmds: Arc<api::BlobImageData>, _: Option<api::TileSize>) {
- self.image_cmds
- .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
- }
-
- fn update(&mut self, key: api::ImageKey, cmds: Arc<api::BlobImageData>, _dirty_rect: Option<api::DeviceUintRect>) {
- // Here, updating is just replacing the current version of the commands with
- // the new one (no incremental updates).
- self.image_cmds
- .insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
- }
-
- fn delete(&mut self, key: api::ImageKey) {
- self.image_cmds.remove(&key);
- }
-
- fn request(
- &mut self,
- _resources: &api::BlobImageResources,
- request: api::BlobImageRequest,
- descriptor: &api::BlobImageDescriptor,
- _dirty_rect: Option<api::DeviceUintRect>,
- ) {
- // This method is where we kick off our rendering jobs.
- // It should avoid doing work on the calling thread as much as possible.
- // In this example we will use the thread pool to render individual tiles.
-
- // Gather the input data to send to a worker thread.
- let cmds = Arc::clone(&self.image_cmds.get(&request.key).unwrap());
- let tx = self.tx.clone();
- let descriptor = descriptor.clone();
-
- self.workers.spawn(move || {
- let result = render_blob(cmds, &descriptor, request.tile);
- tx.send((request, result)).unwrap();
- });
-
- // Add None in the map of rendered images. This makes it possible to differentiate
- // between commands that aren't finished yet (entry in the map is equal to None) and
- // keys that have never been requested (entry not in the map), which would cause deadlocks
- // if we were to block upon receiving their result in resolve!
- self.rendered_images.insert(request, None);
- }
-
- fn resolve(&mut self, request: api::BlobImageRequest) -> api::BlobImageResult {
- // In this method we wait until the work is complete on the worker threads and
- // gather the results.
-
- // First look at whether we have already received the rendered image
- // that we are looking for.
- match self.rendered_images.entry(request) {
- Entry::Vacant(_) => {
- return Err(api::BlobImageError::InvalidKey);
- }
- Entry::Occupied(entry) => {
- // None means we haven't yet received the result.
- if entry.get().is_some() {
- let result = entry.remove();
- return result.unwrap();
- }
- }
- }
-
- // We haven't received it yet, pull from the channel until we receive it.
- while let Ok((req, result)) = self.rx.recv() {
- if req == request {
- // There it is!
- return result;
- }
- self.rendered_images.insert(req, Some(result));
- }
-
- // If we break out of the loop above it means the channel closed unexpectedly.
- Err(api::BlobImageError::Other("Channel closed".into()))
- }
- fn delete_font(&mut self, _font: api::FontKey) {}
- fn delete_font_instance(&mut self, _instance: api::FontInstanceKey) {}
- fn clear_namespace(&mut self, _namespace: api::IdNamespace) {}
-}
-
-struct App {}
-
-impl Example for App {
- fn render(
- &mut self,
- api: &RenderApi,
- builder: &mut DisplayListBuilder,
- txn: &mut Transaction,
- _framebuffer_size: api::DeviceUintSize,
- _pipeline_id: PipelineId,
- _document_id: DocumentId,
- ) {
- let blob_img1 = api.generate_image_key();
- txn.add_image(
- blob_img1,
- api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true, false),
- api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))),
- Some(128),
- );
-
- let blob_img2 = api.generate_image_key();
- txn.add_image(
- blob_img2,
- api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true, false),
- api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))),
- None,
- );
-
- let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), builder.content_size());
- let info = api::LayoutPrimitiveInfo::new(bounds);
- builder.push_stacking_context(
- &info,
- None,
- api::TransformStyle::Flat,
- api::MixBlendMode::Normal,
- Vec::new(),
- api::GlyphRasterSpace::Screen,
- );
-
- let info = api::LayoutPrimitiveInfo::new((30, 30).by(500, 500));
- builder.push_image(
- &info,
- api::LayoutSize::new(500.0, 500.0),
- api::LayoutSize::new(0.0, 0.0),
- api::ImageRendering::Auto,
- api::AlphaType::PremultipliedAlpha,
- blob_img1,
- );
-
- let info = api::LayoutPrimitiveInfo::new((600, 600).by(200, 200));
- builder.push_image(
- &info,
- api::LayoutSize::new(200.0, 200.0),
- api::LayoutSize::new(0.0, 0.0),
- api::ImageRendering::Auto,
- api::AlphaType::PremultipliedAlpha,
- blob_img2,
- );
-
- builder.pop_stacking_context();
- }
-}
-
-fn main() {
- let workers =
- ThreadPoolBuilder::new().thread_name(|idx| format!("WebRender:Worker#{}", idx))
- .build();
-
- let workers = Arc::new(workers.unwrap());
-
- let opts = webrender::RendererOptions {
- workers: Some(Arc::clone(&workers)),
- // Register our blob renderer, so that WebRender integrates it in the resource cache..
- // Share the same pool of worker threads between WebRender and our blob renderer.
- blob_image_renderer: Some(Box::new(CheckerboardRenderer::new(Arc::clone(&workers)))),
- ..Default::default()
- };
-
- let mut app = App {};
-
- boilerplate::main_wrapper(&mut app, Some(opts));
-}
deleted file mode 100644
--- a/gfx/webrender/examples/common/boilerplate.rs
+++ /dev/null
@@ -1,286 +0,0 @@
-/* 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/. */
-
-extern crate env_logger;
-extern crate euclid;
-
-use gleam::gl;
-use glutin::{self, GlContext};
-use std::env;
-use std::path::PathBuf;
-use webrender;
-use winit;
-use webrender::api::*;
-
-struct Notifier {
- events_proxy: winit::EventsLoopProxy,
-}
-
-impl Notifier {
- fn new(events_proxy: winit::EventsLoopProxy) -> Notifier {
- Notifier { events_proxy }
- }
-}
-
-impl RenderNotifier for Notifier {
- fn clone(&self) -> Box<RenderNotifier> {
- Box::new(Notifier {
- events_proxy: self.events_proxy.clone(),
- })
- }
-
- fn wake_up(&self) {
- #[cfg(not(target_os = "android"))]
- let _ = self.events_proxy.wakeup();
- }
-
- fn new_frame_ready(&self, _: DocumentId, _scrolled: bool, _composite_needed: bool) {
- self.wake_up();
- }
-}
-
-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::new(
- 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::new(
- 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_SHADERS: bool = false;
- const WIDTH: u32 = 1920;
- const HEIGHT: u32 = 1080;
-
- fn render(
- &mut self,
- api: &RenderApi,
- builder: &mut DisplayListBuilder,
- txn: &mut Transaction,
- framebuffer_size: DeviceUintSize,
- pipeline_id: PipelineId,
- document_id: DocumentId,
- );
- fn on_event(&mut self, winit::WindowEvent, &RenderApi, DocumentId) -> bool {
- false
- }
- fn get_image_handlers(
- &mut self,
- _gl: &gl::Gl,
- ) -> (Option<Box<webrender::ExternalImageHandler>>,
- Option<Box<webrender::OutputImageHandler>>) {
- (None, None)
- }
- fn draw_custom(&self, _gl: &gl::Gl) {
- }
-}
-
-pub fn main_wrapper<E: Example>(
- example: &mut E,
- options: Option<webrender::RendererOptions>,
-) {
- env_logger::init();
-
- 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::EventsLoop::new();
- let context_builder = glutin::ContextBuilder::new()
- .with_gl(glutin::GlRequest::GlThenGles {
- opengl_version: (3, 2),
- opengles_version: (3, 0),
- });
- let window_builder = winit::WindowBuilder::new()
- .with_title(E::TITLE)
- .with_multitouch()
- .with_dimensions(E::WIDTH, E::HEIGHT);
- let window = glutin::GlWindow::new(window_builder, context_builder, &events_loop)
- .unwrap();
-
- unsafe {
- window.make_current().ok();
- }
-
- let gl = match window.get_api() {
- glutin::Api::OpenGl => unsafe {
- gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
- },
- glutin::Api::OpenGlEs => unsafe {
- gl::GlesFns::load_with(|symbol| window.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 = window.hidpi_factor();
- println!("Device pixel ratio: {}", device_pixel_ratio);
-
- println!("Loading shaders...");
- let opts = webrender::RendererOptions {
- resource_override_path: res_path,
- precache_shaders: E::PRECACHE_SHADERS,
- device_pixel_ratio,
- clear_color: Some(ColorF::new(0.3, 0.0, 0.0, 1.0)),
- //scatter_gpu_cache_updates: false,
- debug_flags: webrender::DebugFlags::ECHO_DRIVER_MESSAGES,
- ..options.unwrap_or(webrender::RendererOptions::default())
- };
-
- let framebuffer_size = {
- let (width, height) = window.get_inner_size().unwrap();
- DeviceUintSize::new(width, height)
- };
- let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
- let (mut renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts).unwrap();
- let api = sender.create_api();
- let document_id = api.add_document(framebuffer_size, 0);
-
- let (external, output) = example.get_image_handlers(&*gl);
-
- if let Some(output_image_handler) = output {
- renderer.set_output_image_handler(output_image_handler);
- }
-
- 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 layout_size = framebuffer_size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
- let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
- let mut txn = Transaction::new();
-
- example.render(
- &api,
- &mut builder,
- &mut txn,
- framebuffer_size,
- pipeline_id,
- document_id,
- );
- txn.set_display_list(
- epoch,
- None,
- layout_size,
- builder.finalize(),
- true,
- );
- txn.set_root_pipeline(pipeline_id);
- txn.generate_frame();
- api.send_transaction(document_id, txn);
-
- println!("Entering event loop");
- events_loop.run_forever(|global_event| {
- let mut txn = Transaction::new();
- let mut custom_event = true;
-
- match global_event {
- winit::Event::WindowEvent {
- event: winit::WindowEvent::CloseRequested,
- ..
- } => return winit::ControlFlow::Break,
- winit::Event::WindowEvent {
- event: winit::WindowEvent::KeyboardInput {
- input: winit::KeyboardInput {
- state: winit::ElementState::Pressed,
- virtual_keycode: Some(key),
- ..
- },
- ..
- },
- ..
- } => match key {
- winit::VirtualKeyCode::Escape => return winit::ControlFlow::Break,
- winit::VirtualKeyCode::P => renderer.toggle_debug_flags(webrender::DebugFlags::PROFILER_DBG),
- winit::VirtualKeyCode::O => renderer.toggle_debug_flags(webrender::DebugFlags::RENDER_TARGET_DBG),
- winit::VirtualKeyCode::I => renderer.toggle_debug_flags(webrender::DebugFlags::TEXTURE_CACHE_DBG),
- winit::VirtualKeyCode::S => renderer.toggle_debug_flags(webrender::DebugFlags::COMPACT_PROFILER),
- winit::VirtualKeyCode::Q => renderer.toggle_debug_flags(
- webrender::DebugFlags::GPU_TIME_QUERIES | webrender::DebugFlags::GPU_SAMPLE_QUERIES
- ),
- winit::VirtualKeyCode::Key1 => txn.set_window_parameters(
- framebuffer_size,
- DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size),
- 1.0
- ),
- winit::VirtualKeyCode::Key2 => txn.set_window_parameters(
- framebuffer_size,
- DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size),
- 2.0
- ),
- winit::VirtualKeyCode::M => api.notify_memory_pressure(),
- #[cfg(feature = "capture")]
- winit::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);
- },
- _ => {
- let win_event = match global_event {
- winit::Event::WindowEvent { event, .. } => event,
- _ => unreachable!()
- };
- custom_event = example.on_event(win_event, &api, document_id)
- },
- },
- winit::Event::WindowEvent { event, .. } => custom_event = example.on_event(event, &api, document_id),
- _ => return winit::ControlFlow::Continue,
- };
-
- if custom_event {
- let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
-
- example.render(
- &api,
- &mut builder,
- &mut txn,
- framebuffer_size,
- pipeline_id,
- document_id,
- );
- txn.set_display_list(
- epoch,
- None,
- layout_size,
- builder.finalize(),
- true,
- );
- txn.generate_frame();
- }
- api.send_transaction(document_id, txn);
-
- renderer.update();
- renderer.render(framebuffer_size).unwrap();
- let _ = renderer.flush_pipeline_info();
- example.draw_custom(&*gl);
- window.swap_buffers().ok();
-
- winit::ControlFlow::Continue
- });
-
- renderer.deinit();
-}
deleted file mode 100644
--- a/gfx/webrender/examples/common/image_helper.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-/* 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 webrender::api::{ImageData, ImageDescriptor, ImageFormat};
-
-pub fn make_checkerboard(width: u32, height: u32) -> (ImageDescriptor, ImageData) {
- let mut image_data = Vec::new();
- for y in 0 .. height {
- for x in 0 .. width {
- let lum = 255 * (((x & 8) == 0) ^ ((y & 8) == 0)) as u8;
- image_data.extend_from_slice(&[lum, lum, lum, 0xff]);
- }
- }
- (ImageDescriptor::new(width, height, ImageFormat::BGRA8, true, false), ImageData::new(image_data))
-}
deleted file mode 100644
--- a/gfx/webrender/examples/document.rs
+++ /dev/null
@@ -1,147 +0,0 @@
-/* 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/. */
-
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::Example;
-use euclid::TypedScale;
-use webrender::api::*;
-
-// This example creates multiple documents overlapping each other with
-// specified layer indices.
-
-struct Document {
- id: DocumentId,
- pipeline_id: PipelineId,
- content_rect: LayoutRect,
- color: ColorF,
-}
-
-struct App {
- documents: Vec<Document>,
-}
-
-impl App {
- fn init(
- &mut self,
- api: &RenderApi,
- framebuffer_size: DeviceUintSize,
- device_pixel_ratio: f32,
- ) {
- let init_data = vec![
- (
- PipelineId(1, 0),
- -1,
- ColorF::new(0.0, 1.0, 0.0, 1.0),
- DeviceUintPoint::new(0, 0),
- ),
- (
- PipelineId(2, 0),
- -2,
- ColorF::new(1.0, 1.0, 0.0, 1.0),
- DeviceUintPoint::new(200, 0),
- ),
- (
- PipelineId(3, 0),
- -3,
- ColorF::new(1.0, 0.0, 0.0, 1.0),
- DeviceUintPoint::new(200, 200),
- ),
- (
- PipelineId(4, 0),
- -4,
- ColorF::new(1.0, 0.0, 1.0, 1.0),
- DeviceUintPoint::new(0, 200),
- ),
- ];
-
- for (pipeline_id, layer, color, offset) in init_data {
- let size = DeviceUintSize::new(250, 250);
- let bounds = DeviceUintRect::new(offset, size);
-
- let document_id = api.add_document(size, layer);
- let mut txn = Transaction::new();
- txn.set_window_parameters(framebuffer_size, bounds, device_pixel_ratio);
- txn.set_root_pipeline(pipeline_id);
- api.send_transaction(document_id, txn);
-
- self.documents.push(Document {
- id: document_id,
- pipeline_id,
- content_rect: bounds.to_f32() / TypedScale::new(device_pixel_ratio),
- color,
- });
- }
- }
-}
-
-impl Example for App {
- fn render(
- &mut self,
- api: &RenderApi,
- base_builder: &mut DisplayListBuilder,
- _txn: &mut Transaction,
- framebuffer_size: DeviceUintSize,
- _: PipelineId,
- _: DocumentId,
- ) {
- if self.documents.is_empty() {
- let device_pixel_ratio = framebuffer_size.width as f32 /
- base_builder.content_size().width;
- // this is the first run, hack around the boilerplate,
- // which assumes an example only needs one document
- self.init(api, framebuffer_size, device_pixel_ratio);
- }
-
- for doc in &self.documents {
- let mut builder = DisplayListBuilder::new(
- doc.pipeline_id,
- doc.content_rect.size,
- );
- let local_rect = LayoutRect::new(
- LayoutPoint::zero(),
- doc.content_rect.size,
- );
-
- builder.push_stacking_context(
- &LayoutPrimitiveInfo::new(doc.content_rect),
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- Vec::new(),
- GlyphRasterSpace::Screen,
- );
- builder.push_rect(
- &LayoutPrimitiveInfo::new(local_rect),
- doc.color,
- );
- builder.pop_stacking_context();
-
- let mut txn = Transaction::new();
- txn.set_display_list(
- Epoch(0),
- None,
- doc.content_rect.size,
- builder.finalize(),
- true,
- );
- txn.generate_frame();
- api.send_transaction(doc.id, txn);
- }
- }
-}
-
-fn main() {
- let mut app = App {
- documents: Vec::new(),
- };
- boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/frame_output.rs
+++ /dev/null
@@ -1,222 +0,0 @@
-/* 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/. */
-
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use euclid::TypedScale;
-use gleam::gl;
-use webrender::api::*;
-
-// This example demonstrates using the frame output feature to copy
-// the output of a WR framebuffer to a custom texture.
-
-#[derive(Debug)]
-struct Document {
- id: DocumentId,
- pipeline_id: PipelineId,
- content_rect: LayoutRect,
- color: ColorF,
-}
-
-
-struct App {
- external_image_key: Option<ImageKey>,
- output_document: Option<Document>
-}
-
-struct OutputHandler {
- texture_id: gl::GLuint
-}
-
-struct ExternalHandler {
- texture_id: gl::GLuint
-}
-
-impl webrender::OutputImageHandler for OutputHandler {
- fn lock(&mut self, _id: PipelineId) -> Option<(u32, DeviceIntSize)> {
- Some((self.texture_id, DeviceIntSize::new(500, 500)))
- }
-
- fn unlock(&mut self, _id: PipelineId) {}
-}
-
-impl webrender::ExternalImageHandler for ExternalHandler {
- fn lock(&mut self, _key: ExternalImageId, _channel_index: u8) -> webrender::ExternalImage {
- webrender::ExternalImage {
- uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
- source: webrender::ExternalImageSource::NativeTexture(self.texture_id),
- }
- }
- fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {}
-}
-
-impl App {
- fn init_output_document(
- &mut self,
- api: &RenderApi,
- framebuffer_size: DeviceUintSize,
- device_pixel_ratio: f32,
- ) {
- // Generate the external image key that will be used to render the output document to the root document.
- self.external_image_key = Some(api.generate_image_key());
- let mut txn = Transaction::new();
- txn.add_image(
- self.external_image_key.unwrap(),
- ImageDescriptor::new(100, 100, ImageFormat::BGRA8, true, false),
- ImageData::External(ExternalImageData {
- id: ExternalImageId(0),
- channel_index: 0,
- image_type: ExternalImageType::TextureHandle(TextureTarget::Default),
- }),
- None,
- );
-
- let pipeline_id = PipelineId(1, 0);
- let layer = 1;
- let color = ColorF::new(1., 1., 0., 1.);
- let bounds = DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size);
- let document_id = api.add_document(framebuffer_size, layer);
-
- let document = Document {
- id: document_id,
- pipeline_id,
- content_rect: bounds.to_f32() / TypedScale::new(device_pixel_ratio),
- color,
- };
-
- let info = LayoutPrimitiveInfo::new(document.content_rect);
- let mut builder = DisplayListBuilder::new(
- document.pipeline_id,
- document.content_rect.size,
- );
-
- builder.push_stacking_context(
- &info,
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- Vec::new(),
- GlyphRasterSpace::Screen,
- );
-
- builder.push_rect(&info, ColorF::new(1.0, 1.0, 0.0, 1.0));
- builder.pop_stacking_context();
-
- txn.set_root_pipeline(pipeline_id);
- txn.enable_frame_output(document.pipeline_id, true);
- txn.set_display_list(
- Epoch(0),
- Some(document.color),
- document.content_rect.size,
- builder.finalize(),
- true,
- );
- txn.generate_frame();
- api.send_transaction(document.id, txn);
- self.output_document = Some(document);
- }
-}
-
-impl Example for App {
- fn render(
- &mut self,
- api: &RenderApi,
- builder: &mut DisplayListBuilder,
- _txn: &mut Transaction,
- framebuffer_size: DeviceUintSize,
- _pipeline_id: PipelineId,
- _document_id: DocumentId,
- ) {
- if self.output_document.is_none() {
- let device_pixel_ratio = framebuffer_size.width as f32 /
- builder.content_size().width;
- self.init_output_document(api, DeviceUintSize::new(200, 200), device_pixel_ratio);
- }
-
- let info = LayoutPrimitiveInfo::new((100, 100).to(200, 200));
- builder.push_stacking_context(
- &info,
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- Vec::new(),
- GlyphRasterSpace::Screen,
- );
-
- builder.push_image(
- &info,
- info.rect.size,
- LayoutSize::zero(),
- ImageRendering::Auto,
- AlphaType::PremultipliedAlpha,
- self.external_image_key.unwrap()
- );
-
- builder.pop_stacking_context();
- }
-
- fn get_image_handlers(
- &mut self,
- gl: &gl::Gl,
- ) -> (Option<Box<webrender::ExternalImageHandler>>,
- Option<Box<webrender::OutputImageHandler>>) {
- let texture_id = gl.gen_textures(1)[0];
-
- gl.bind_texture(gl::TEXTURE_2D, texture_id);
- gl.tex_parameter_i(
- gl::TEXTURE_2D,
- gl::TEXTURE_MAG_FILTER,
- gl::LINEAR as gl::GLint,
- );
- gl.tex_parameter_i(
- gl::TEXTURE_2D,
- gl::TEXTURE_MIN_FILTER,
- gl::LINEAR as gl::GLint,
- );
- gl.tex_parameter_i(
- gl::TEXTURE_2D,
- gl::TEXTURE_WRAP_S,
- gl::CLAMP_TO_EDGE as gl::GLint,
- );
- gl.tex_parameter_i(
- gl::TEXTURE_2D,
- gl::TEXTURE_WRAP_T,
- gl::CLAMP_TO_EDGE as gl::GLint,
- );
- gl.tex_image_2d(
- gl::TEXTURE_2D,
- 0,
- gl::RGBA as gl::GLint,
- 100,
- 100,
- 0,
- gl::BGRA,
- gl::UNSIGNED_BYTE,
- None,
- );
- gl.bind_texture(gl::TEXTURE_2D, 0);
-
- (
- Some(Box::new(ExternalHandler { texture_id })),
- Some(Box::new(OutputHandler { texture_id }))
- )
- }
-}
-
-fn main() {
- let mut app = App {
- external_image_key: None,
- output_document: None
- };
-
- boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/iframe.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-/* 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/. */
-
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use webrender::api::*;
-
-// This example uses the push_iframe API to nest a second pipeline's displaylist
-// inside the root pipeline's display list. When it works, a green square is
-// shown. If it fails, a red square is shown.
-
-struct App {}
-
-impl Example for App {
- fn render(
- &mut self,
- api: &RenderApi,
- builder: &mut DisplayListBuilder,
- _txn: &mut Transaction,
- _framebuffer_size: DeviceUintSize,
- pipeline_id: PipelineId,
- document_id: DocumentId,
- ) {
- // All the sub_* things are for the nested pipeline
- let sub_size = DeviceUintSize::new(100, 100);
- let sub_bounds = (0, 0).to(sub_size.width as i32, sub_size.height as i32);
-
- let sub_pipeline_id = PipelineId(pipeline_id.0, 42);
- let mut sub_builder = DisplayListBuilder::new(sub_pipeline_id, sub_bounds.size);
-
- let info = LayoutPrimitiveInfo::new(sub_bounds);
- sub_builder.push_stacking_context(
- &info,
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- Vec::new(),
- GlyphRasterSpace::Screen,
- );
-
- // green rect visible == success
- sub_builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
- sub_builder.pop_stacking_context();
-
- let mut txn = Transaction::new();
- txn.set_display_list(
- Epoch(0),
- None,
- sub_bounds.size,
- sub_builder.finalize(),
- true,
- );
- api.send_transaction(document_id, txn);
-
- let info = LayoutPrimitiveInfo::new(sub_bounds);
- let reference_frame_id = builder.push_reference_frame(
- &info,
- Some(PropertyBinding::Binding(PropertyBindingKey::new(42), LayoutTransform::identity())),
- None,
- );
- builder.push_clip_id(reference_frame_id);
-
-
- // And this is for the root pipeline
- builder.push_stacking_context(
- &info,
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- Vec::new(),
- GlyphRasterSpace::Screen,
- );
- // red rect under the iframe: if this is visible, things have gone wrong
- builder.push_rect(&info, ColorF::new(1.0, 0.0, 0.0, 1.0));
- builder.push_iframe(&info, sub_pipeline_id, false);
- builder.pop_stacking_context();
-
- builder.pop_clip_id();
- builder.pop_reference_frame();
- }
-}
-
-fn main() {
- let mut app = App {};
- boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/image_resize.rs
+++ /dev/null
@@ -1,124 +0,0 @@
-/* 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/. */
-
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-#[path = "common/image_helper.rs"]
-mod image_helper;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use webrender::api::*;
-
-struct App {
- image_key: ImageKey,
-}
-
-impl Example for App {
- fn render(
- &mut self,
- _api: &RenderApi,
- builder: &mut DisplayListBuilder,
- txn: &mut Transaction,
- _framebuffer_size: DeviceUintSize,
- _pipeline_id: PipelineId,
- _document_id: DocumentId,
- ) {
- let (image_descriptor, image_data) = image_helper::make_checkerboard(32, 32);
- txn.add_image(
- self.image_key,
- image_descriptor,
- image_data,
- None,
- );
-
- let bounds = (0, 0).to(512, 512);
- let info = LayoutPrimitiveInfo::new(bounds);
- builder.push_stacking_context(
- &info,
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- Vec::new(),
- GlyphRasterSpace::Screen,
- );
-
- let image_size = LayoutSize::new(100.0, 100.0);
-
- let info = LayoutPrimitiveInfo::with_clip_rect(
- LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
- bounds,
- );
- builder.push_image(
- &info,
- image_size,
- LayoutSize::zero(),
- ImageRendering::Auto,
- AlphaType::PremultipliedAlpha,
- self.image_key,
- );
-
- let info = LayoutPrimitiveInfo::with_clip_rect(
- LayoutRect::new(LayoutPoint::new(250.0, 100.0), image_size),
- bounds,
- );
- builder.push_image(
- &info,
- image_size,
- LayoutSize::zero(),
- ImageRendering::Pixelated,
- AlphaType::PremultipliedAlpha,
- self.image_key,
- );
-
- builder.pop_stacking_context();
- }
-
- fn on_event(&mut self, event: winit::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
- match event {
- winit::WindowEvent::KeyboardInput {
- input: winit::KeyboardInput {
- state: winit::ElementState::Pressed,
- virtual_keycode: Some(winit::VirtualKeyCode::Space),
- ..
- },
- ..
- } => {
- let mut image_data = Vec::new();
- for y in 0 .. 64 {
- for x in 0 .. 64 {
- let r = 255 * ((y & 32) == 0) as u8;
- let g = 255 * ((x & 32) == 0) as u8;
- image_data.extend_from_slice(&[0, g, r, 0xff]);
- }
- }
-
- let mut txn = Transaction::new();
- txn.update_image(
- self.image_key,
- ImageDescriptor::new(64, 64, ImageFormat::BGRA8, true, false),
- ImageData::new(image_data),
- None,
- );
- let mut txn = Transaction::new();
- txn.generate_frame();
- api.send_transaction(document_id, txn);
- }
- _ => {}
- }
-
- false
- }
-}
-
-fn main() {
- let mut app = App {
- image_key: ImageKey(IdNamespace(0), 0),
- };
- boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/multiwindow.rs
+++ /dev/null
@@ -1,306 +0,0 @@
-/* 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/. */
-
-extern crate app_units;
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-use app_units::Au;
-use gleam::gl;
-use glutin::GlContext;
-use std::fs::File;
-use std::io::Read;
-use webrender::api::*;
-
-struct Notifier {
- events_proxy: winit::EventsLoopProxy,
-}
-
-impl Notifier {
- fn new(events_proxy: winit::EventsLoopProxy) -> Notifier {
- Notifier { events_proxy }
- }
-}
-
-impl RenderNotifier for Notifier {
- fn clone(&self) -> Box<RenderNotifier> {
- Box::new(Notifier {
- events_proxy: self.events_proxy.clone(),
- })
- }
-
- fn wake_up(&self) {
- #[cfg(not(target_os = "android"))]
- let _ = self.events_proxy.wakeup();
- }
-
- fn new_frame_ready(&self, _: DocumentId, _scrolled: bool, _composite_needed: bool) {
- self.wake_up();
- }
-}
-
-struct Window {
- events_loop: winit::EventsLoop, //TODO: share events loop?
- window: glutin::GlWindow,
- renderer: webrender::Renderer,
- name: &'static str,
- pipeline_id: PipelineId,
- document_id: DocumentId,
- epoch: Epoch,
- api: RenderApi,
- font_instance_key: FontInstanceKey,
-}
-
-impl Window {
- fn new(name: &'static str, clear_color: ColorF) -> Self {
- let events_loop = winit::EventsLoop::new();
- let context_builder = glutin::ContextBuilder::new()
- .with_gl(glutin::GlRequest::GlThenGles {
- opengl_version: (3, 2),
- opengles_version: (3, 0),
- });
- let window_builder = winit::WindowBuilder::new()
- .with_title(name)
- .with_multitouch()
- .with_dimensions(800, 600);
- let window = glutin::GlWindow::new(window_builder, context_builder, &events_loop)
- .unwrap();
-
- unsafe {
- window.make_current().ok();
- }
-
- let gl = match window.get_api() {
- glutin::Api::OpenGl => unsafe {
- gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
- },
- glutin::Api::OpenGlEs => unsafe {
- gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
- },
- glutin::Api::WebGl => unimplemented!(),
- };
-
- let device_pixel_ratio = window.hidpi_factor();
-
- let opts = webrender::RendererOptions {
- device_pixel_ratio,
- clear_color: Some(clear_color),
- ..webrender::RendererOptions::default()
- };
-
- let framebuffer_size = {
- let (width, height) = window.get_inner_size().unwrap();
- DeviceUintSize::new(width, height)
- };
- let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
- let (renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts).unwrap();
- let api = sender.create_api();
- let document_id = api.add_document(framebuffer_size, 0);
-
- let epoch = Epoch(0);
- let pipeline_id = PipelineId(0, 0);
- let mut txn = Transaction::new();
-
- let font_key = api.generate_font_key();
- let font_bytes = load_file("../wrench/reftests/text/FreeSans.ttf");
- txn.add_raw_font(font_key, font_bytes, 0);
-
- let font_instance_key = api.generate_font_instance_key();
- txn.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None, Vec::new());
-
- api.send_transaction(document_id, txn);
-
- Window {
- events_loop,
- window,
- renderer,
- name,
- epoch,
- pipeline_id,
- document_id,
- api,
- font_instance_key,
- }
- }
-
- fn tick(&mut self) -> bool {
- unsafe {
- self.window.make_current().ok();
- }
- let mut do_exit = false;
- let my_name = &self.name;
- let renderer = &mut self.renderer;
-
- self.events_loop.poll_events(|global_event| match global_event {
- winit::Event::WindowEvent { event, .. } => match event {
- winit::WindowEvent::CloseRequested |
- winit::WindowEvent::KeyboardInput {
- input: winit::KeyboardInput {
- virtual_keycode: Some(winit::VirtualKeyCode::Escape),
- ..
- },
- ..
- } => {
- do_exit = true
- }
- winit::WindowEvent::KeyboardInput {
- input: winit::KeyboardInput {
- state: winit::ElementState::Pressed,
- virtual_keycode: Some(winit::VirtualKeyCode::P),
- ..
- },
- ..
- } => {
- println!("toggle flags {}", my_name);
- renderer.toggle_debug_flags(webrender::DebugFlags::PROFILER_DBG);
- }
- _ => {}
- }
- _ => {}
- });
- if do_exit {
- return true
- }
-
- let framebuffer_size = {
- let (width, height) = self.window.get_inner_size().unwrap();
- DeviceUintSize::new(width, height)
- };
- let device_pixel_ratio = self.window.hidpi_factor();
- let layout_size = framebuffer_size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
- let mut txn = Transaction::new();
- let mut builder = DisplayListBuilder::new(self.pipeline_id, layout_size);
-
- let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
- let info = LayoutPrimitiveInfo::new(bounds);
- builder.push_stacking_context(
- &info,
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- Vec::new(),
- GlyphRasterSpace::Screen,
- );
-
- let info = LayoutPrimitiveInfo::new(LayoutRect::new(
- LayoutPoint::new(100.0, 100.0),
- LayoutSize::new(100.0, 200.0)
- ));
- builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
-
- let text_bounds = LayoutRect::new(
- LayoutPoint::new(100.0, 50.0),
- LayoutSize::new(700.0, 200.0)
- );
- let glyphs = vec![
- GlyphInstance {
- index: 48,
- point: LayoutPoint::new(100.0, 100.0),
- },
- GlyphInstance {
- index: 68,
- point: LayoutPoint::new(150.0, 100.0),
- },
- GlyphInstance {
- index: 80,
- point: LayoutPoint::new(200.0, 100.0),
- },
- GlyphInstance {
- index: 82,
- point: LayoutPoint::new(250.0, 100.0),
- },
- GlyphInstance {
- index: 81,
- point: LayoutPoint::new(300.0, 100.0),
- },
- GlyphInstance {
- index: 3,
- point: LayoutPoint::new(350.0, 100.0),
- },
- GlyphInstance {
- index: 86,
- point: LayoutPoint::new(400.0, 100.0),
- },
- GlyphInstance {
- index: 79,
- point: LayoutPoint::new(450.0, 100.0),
- },
- GlyphInstance {
- index: 72,
- point: LayoutPoint::new(500.0, 100.0),
- },
- GlyphInstance {
- index: 83,
- point: LayoutPoint::new(550.0, 100.0),
- },
- GlyphInstance {
- index: 87,
- point: LayoutPoint::new(600.0, 100.0),
- },
- GlyphInstance {
- index: 17,
- point: LayoutPoint::new(650.0, 100.0),
- },
- ];
-
- let info = LayoutPrimitiveInfo::new(text_bounds);
- builder.push_text(
- &info,
- &glyphs,
- self.font_instance_key,
- ColorF::new(1.0, 1.0, 0.0, 1.0),
- None,
- );
-
- builder.pop_stacking_context();
-
- txn.set_display_list(
- self.epoch,
- None,
- layout_size,
- builder.finalize(),
- true,
- );
- txn.set_root_pipeline(self.pipeline_id);
- txn.generate_frame();
- self.api.send_transaction(self.document_id, txn);
-
- renderer.update();
- renderer.render(framebuffer_size).unwrap();
- self.window.swap_buffers().ok();
-
- false
- }
-
- fn deinit(self) {
- self.renderer.deinit();
- }
-}
-
-fn main() {
- let mut win1 = Window::new("window1", ColorF::new(0.3, 0.0, 0.0, 1.0));
- let mut win2 = Window::new("window2", ColorF::new(0.0, 0.3, 0.0, 1.0));
-
- loop {
- if win1.tick() {
- break;
- }
- if win2.tick() {
- break;
- }
- }
-
- win1.deinit();
- win2.deinit();
-}
-
-fn load_file(name: &str) -> Vec<u8> {
- let mut file = File::open(name).unwrap();
- let mut buffer = vec![];
- file.read_to_end(&mut buffer).unwrap();
- buffer
-}
deleted file mode 100644
--- a/gfx/webrender/examples/scrolling.rs
+++ /dev/null
@@ -1,210 +0,0 @@
-/* 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/. */
-
-extern crate euclid;
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use euclid::SideOffsets2D;
-use webrender::api::*;
-
-struct App {
- cursor_position: WorldPoint,
-}
-
-impl Example for App {
- fn render(
- &mut self,
- _api: &RenderApi,
- builder: &mut DisplayListBuilder,
- _txn: &mut Transaction,
- _framebuffer_size: DeviceUintSize,
- _pipeline_id: PipelineId,
- _document_id: DocumentId,
- ) {
- let info = LayoutPrimitiveInfo::new(
- LayoutRect::new(LayoutPoint::zero(), builder.content_size())
- );
- builder.push_stacking_context(
- &info,
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- Vec::new(),
- GlyphRasterSpace::Screen,
- );
-
- if true {
- // scrolling and clips stuff
- // let's make a scrollbox
- let scrollbox = (0, 0).to(300, 400);
- builder.push_stacking_context(
- &LayoutPrimitiveInfo::new((10, 10).by(0, 0)),
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- Vec::new(),
- GlyphRasterSpace::Screen,
- );
- // set the scrolling clip
- let clip_id = builder.define_scroll_frame(
- None,
- (0, 0).by(1000, 1000),
- scrollbox,
- vec![],
- None,
- ScrollSensitivity::ScriptAndInputEvents,
- );
- builder.push_clip_id(clip_id);
-
- // now put some content into it.
- // start with a white background
- let mut info = LayoutPrimitiveInfo::new((0, 0).to(1000, 1000));
- info.tag = Some((0, 1));
- builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
-
- // let's make a 50x50 blue square as a visual reference
- let mut info = LayoutPrimitiveInfo::new((0, 0).to(50, 50));
- info.tag = Some((0, 2));
- builder.push_rect(&info, ColorF::new(0.0, 0.0, 1.0, 1.0));
-
- // and a 50x50 green square next to it with an offset clip
- // to see what that looks like
- let mut info =
- LayoutPrimitiveInfo::with_clip_rect((50, 0).to(100, 50), (60, 10).to(110, 60));
- info.tag = Some((0, 3));
- builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
-
- // Below the above rectangles, set up a nested scrollbox. It's still in
- // the same stacking context, so note that the rects passed in need to
- // be relative to the stacking context.
- let nested_clip_id = builder.define_scroll_frame(
- None,
- (0, 100).to(300, 1000),
- (0, 100).to(200, 300),
- vec![],
- None,
- ScrollSensitivity::ScriptAndInputEvents,
- );
- builder.push_clip_id(nested_clip_id);
-
- // give it a giant gray background just to distinguish it and to easily
- // visually identify the nested scrollbox
- let mut info = LayoutPrimitiveInfo::new((-1000, -1000).to(5000, 5000));
- info.tag = Some((0, 4));
- builder.push_rect(&info, ColorF::new(0.5, 0.5, 0.5, 1.0));
-
- // add a teal square to visualize the scrolling/clipping behaviour
- // as you scroll the nested scrollbox
- let mut info = LayoutPrimitiveInfo::new((0, 200).to(50, 250));
- info.tag = Some((0, 5));
- builder.push_rect(&info, ColorF::new(0.0, 1.0, 1.0, 1.0));
-
- // Add a sticky frame. It will "stick" twice while scrolling, once
- // at a margin of 10px from the bottom, for 40 pixels of scrolling,
- // and once at a margin of 10px from the top, for 60 pixels of
- // scrolling.
- let sticky_id = builder.define_sticky_frame(
- (50, 350).by(50, 50),
- SideOffsets2D::new(Some(10.0), None, Some(10.0), None),
- StickyOffsetBounds::new(-40.0, 60.0),
- StickyOffsetBounds::new(0.0, 0.0),
- LayoutVector2D::new(0.0, 0.0)
- );
-
- builder.push_clip_id(sticky_id);
- let mut info = LayoutPrimitiveInfo::new((50, 350).by(50, 50));
- info.tag = Some((0, 6));
- builder.push_rect(&info, ColorF::new(0.5, 0.5, 1.0, 1.0));
- builder.pop_clip_id(); // sticky_id
-
- // just for good measure add another teal square further down and to
- // the right, which can be scrolled into view by the user
- let mut info = LayoutPrimitiveInfo::new((250, 350).to(300, 400));
- info.tag = Some((0, 7));
- builder.push_rect(&info, ColorF::new(0.0, 1.0, 1.0, 1.0));
-
- builder.pop_clip_id(); // nested_clip_id
-
- builder.pop_clip_id(); // clip_id
- builder.pop_stacking_context();
- }
-
- builder.pop_stacking_context();
- }
-
- fn on_event(&mut self, event: winit::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
- let mut txn = Transaction::new();
- match event {
- winit::WindowEvent::KeyboardInput {
- input: winit::KeyboardInput {
- state: winit::ElementState::Pressed,
- virtual_keycode: Some(key),
- ..
- },
- ..
- } => {
- let offset = match key {
- winit::VirtualKeyCode::Down => (0.0, -10.0),
- winit::VirtualKeyCode::Up => (0.0, 10.0),
- winit::VirtualKeyCode::Right => (-10.0, 0.0),
- winit::VirtualKeyCode::Left => (10.0, 0.0),
- _ => return false,
- };
-
- txn.scroll(
- ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
- self.cursor_position,
- );
- }
- winit::WindowEvent::CursorMoved { position: (x, y), .. } => {
- self.cursor_position = WorldPoint::new(x as f32, y as f32);
- }
- winit::WindowEvent::MouseWheel { delta, .. } => {
- const LINE_HEIGHT: f32 = 38.0;
- let (dx, dy) = match delta {
- winit::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
- winit::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
- };
-
- txn.scroll(
- ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
- self.cursor_position,
- );
- }
- winit::WindowEvent::MouseInput { .. } => {
- let results = api.hit_test(
- document_id,
- None,
- self.cursor_position,
- HitTestFlags::FIND_ALL
- );
-
- println!("Hit test results:");
- for item in &results.items {
- println!(" • {:?}", item);
- }
- println!("");
- }
- _ => (),
- }
-
- api.send_transaction(document_id, txn);
-
- false
- }
-}
-
-fn main() {
- let mut app = App {
- cursor_position: WorldPoint::zero(),
- };
- boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/texture_cache_stress.rs
+++ /dev/null
@@ -1,312 +0,0 @@
-/* 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/. */
-
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::{Example, HandyDandyRectBuilder};
-use gleam::gl;
-use std::mem;
-use webrender::api::*;
-
-struct ImageGenerator {
- patterns: [[u8; 3]; 6],
- next_pattern: usize,
- current_image: Vec<u8>,
-}
-
-impl ImageGenerator {
- fn new() -> Self {
- ImageGenerator {
- next_pattern: 0,
- patterns: [
- [1, 0, 0],
- [0, 1, 0],
- [0, 0, 1],
- [1, 1, 0],
- [0, 1, 1],
- [1, 0, 1],
- ],
- current_image: Vec::new(),
- }
- }
-
- fn generate_image(&mut self, size: u32) {
- let pattern = &self.patterns[self.next_pattern];
- self.current_image.clear();
- for y in 0 .. size {
- for x in 0 .. size {
- let lum = 255 * (1 - (((x & 8) == 0) ^ ((y & 8) == 0)) as u8);
- self.current_image.extend_from_slice(&[
- lum * pattern[0],
- lum * pattern[1],
- lum * pattern[2],
- 0xff,
- ]);
- }
- }
-
- self.next_pattern = (self.next_pattern + 1) % self.patterns.len();
- }
-
- fn take(&mut self) -> Vec<u8> {
- mem::replace(&mut self.current_image, Vec::new())
- }
-}
-
-impl webrender::ExternalImageHandler for ImageGenerator {
- fn lock(&mut self, _key: ExternalImageId, channel_index: u8) -> webrender::ExternalImage {
- self.generate_image(channel_index as u32);
- webrender::ExternalImage {
- uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
- source: webrender::ExternalImageSource::RawData(&self.current_image),
- }
- }
- fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {}
-}
-
-struct App {
- stress_keys: Vec<ImageKey>,
- image_key: Option<ImageKey>,
- image_generator: ImageGenerator,
- swap_keys: Vec<ImageKey>,
- swap_index: usize,
-}
-
-impl Example for App {
- fn render(
- &mut self,
- api: &RenderApi,
- builder: &mut DisplayListBuilder,
- txn: &mut Transaction,
- _framebuffer_size: DeviceUintSize,
- _pipeline_id: PipelineId,
- _document_id: DocumentId,
- ) {
- let bounds = (0, 0).to(512, 512);
- let info = LayoutPrimitiveInfo::new(bounds);
- builder.push_stacking_context(
- &info,
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- Vec::new(),
- GlyphRasterSpace::Screen,
- );
-
- let x0 = 50.0;
- let y0 = 50.0;
- let image_size = LayoutSize::new(4.0, 4.0);
-
- if self.swap_keys.is_empty() {
- let key0 = api.generate_image_key();
- let key1 = api.generate_image_key();
-
- self.image_generator.generate_image(128);
- txn.add_image(
- key0,
- ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true, false),
- ImageData::new(self.image_generator.take()),
- None,
- );
-
- self.image_generator.generate_image(128);
- txn.add_image(
- key1,
- ImageDescriptor::new(128, 128, ImageFormat::BGRA8, true, false),
- ImageData::new(self.image_generator.take()),
- None,
- );
-
- self.swap_keys.push(key0);
- self.swap_keys.push(key1);
- }
-
- for (i, key) in self.stress_keys.iter().enumerate() {
- let x = (i % 128) as f32;
- let y = (i / 128) as f32;
- let info = LayoutPrimitiveInfo::with_clip_rect(
- LayoutRect::new(
- LayoutPoint::new(x0 + image_size.width * x, y0 + image_size.height * y),
- image_size,
- ),
- bounds,
- );
-
- builder.push_image(
- &info,
- image_size,
- LayoutSize::zero(),
- ImageRendering::Auto,
- AlphaType::PremultipliedAlpha,
- *key,
- );
- }
-
- if let Some(image_key) = self.image_key {
- let image_size = LayoutSize::new(100.0, 100.0);
- let info = LayoutPrimitiveInfo::with_clip_rect(
- LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
- bounds,
- );
- builder.push_image(
- &info,
- image_size,
- LayoutSize::zero(),
- ImageRendering::Auto,
- AlphaType::PremultipliedAlpha,
- image_key,
- );
- }
-
- let swap_key = self.swap_keys[self.swap_index];
- let image_size = LayoutSize::new(64.0, 64.0);
- let info = LayoutPrimitiveInfo::with_clip_rect(
- LayoutRect::new(LayoutPoint::new(100.0, 400.0), image_size),
- bounds,
- );
- builder.push_image(
- &info,
- image_size,
- LayoutSize::zero(),
- ImageRendering::Auto,
- AlphaType::PremultipliedAlpha,
- swap_key,
- );
- self.swap_index = 1 - self.swap_index;
-
- builder.pop_stacking_context();
- }
-
- fn on_event(
- &mut self,
- event: winit::WindowEvent,
- api: &RenderApi,
- _document_id: DocumentId,
- ) -> bool {
- match event {
- winit::WindowEvent::KeyboardInput {
- input: winit::KeyboardInput {
- state: winit::ElementState::Pressed,
- virtual_keycode: Some(key),
- ..
- },
- ..
- } => {
- let mut txn = Transaction::new();
-
- match key {
- winit::VirtualKeyCode::S => {
- self.stress_keys.clear();
-
- for _ in 0 .. 16 {
- for _ in 0 .. 16 {
- let size = 4;
-
- let image_key = api.generate_image_key();
-
- self.image_generator.generate_image(size);
-
- txn.add_image(
- image_key,
- ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
- ImageData::new(self.image_generator.take()),
- None,
- );
-
- self.stress_keys.push(image_key);
- }
- }
- }
- winit::VirtualKeyCode::D => if let Some(image_key) = self.image_key.take() {
- txn.delete_image(image_key);
- },
- winit::VirtualKeyCode::U => if let Some(image_key) = self.image_key {
- let size = 128;
- self.image_generator.generate_image(size);
-
- txn.update_image(
- image_key,
- ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
- ImageData::new(self.image_generator.take()),
- None,
- );
- },
- winit::VirtualKeyCode::E => {
- if let Some(image_key) = self.image_key.take() {
- txn.delete_image(image_key);
- }
-
- let size = 32;
- let image_key = api.generate_image_key();
-
- let image_data = ExternalImageData {
- id: ExternalImageId(0),
- channel_index: size as u8,
- image_type: ExternalImageType::Buffer,
- };
-
- txn.add_image(
- image_key,
- ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
- ImageData::External(image_data),
- None,
- );
-
- self.image_key = Some(image_key);
- }
- winit::VirtualKeyCode::R => {
- if let Some(image_key) = self.image_key.take() {
- txn.delete_image(image_key);
- }
-
- let image_key = api.generate_image_key();
- let size = 32;
- self.image_generator.generate_image(size);
-
- txn.add_image(
- image_key,
- ImageDescriptor::new(size, size, ImageFormat::BGRA8, true, false),
- ImageData::new(self.image_generator.take()),
- None,
- );
-
- self.image_key = Some(image_key);
- }
- _ => {}
- }
-
- api.update_resources(txn.resource_updates);
- return true;
- }
- _ => {}
- }
-
- false
- }
-
- fn get_image_handlers(
- &mut self,
- _gl: &gl::Gl,
- ) -> (Option<Box<webrender::ExternalImageHandler>>,
- Option<Box<webrender::OutputImageHandler>>) {
- (Some(Box::new(ImageGenerator::new())), None)
- }
-}
-
-fn main() {
- let mut app = App {
- image_key: None,
- stress_keys: Vec::new(),
- image_generator: ImageGenerator::new(),
- swap_keys: Vec::new(),
- swap_index: 0,
- };
- boilerplate::main_wrapper(&mut app, None);
-}
deleted file mode 100644
--- a/gfx/webrender/examples/yuv.rs
+++ /dev/null
@@ -1,198 +0,0 @@
-/* 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/. */
-
-extern crate gleam;
-extern crate glutin;
-extern crate webrender;
-extern crate winit;
-
-#[path = "common/boilerplate.rs"]
-mod boilerplate;
-
-use boilerplate::Example;
-use gleam::gl;
-use webrender::api::*;
-
-fn init_gl_texture(
- id: gl::GLuint,
- internal: gl::GLenum,
- external: gl::GLenum,
- bytes: &[u8],
- gl: &gl::Gl,
-) {
- gl.bind_texture(gl::TEXTURE_2D, id);
- gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as gl::GLint);
- gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as gl::GLint);
- gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as gl::GLint);
- gl.tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint);
- gl.tex_image_2d(
- gl::TEXTURE_2D,
- 0,
- internal as gl::GLint,
- 100,
- 100,
- 0,
- external,
- gl::UNSIGNED_BYTE,
- Some(bytes),
- );
- gl.bind_texture(gl::TEXTURE_2D, 0);
-}
-
-struct YuvImageProvider {
- texture_ids: Vec<gl::GLuint>,
-}
-
-impl YuvImageProvider {
- fn new(gl: &gl::Gl) -> Self {
- let texture_ids = gl.gen_textures(4);
-
- init_gl_texture(texture_ids[0], gl::RED, gl::RED, &[127; 100 * 100], gl);
- init_gl_texture(texture_ids[1], gl::RG8, gl::RG, &[0; 100 * 100 * 2], gl);
- init_gl_texture(texture_ids[2], gl::RED, gl::RED, &[127; 100 * 100], gl);
- init_gl_texture(texture_ids[3], gl::RED, gl::RED, &[127; 100 * 100], gl);
-
- YuvImageProvider {
- texture_ids
- }
- }
-}
-
-impl webrender::ExternalImageHandler for YuvImageProvider {
- fn lock(&mut self, key: ExternalImageId, _channel_index: u8) -> webrender::ExternalImage {
- let id = self.texture_ids[key.0 as usize];
- webrender::ExternalImage {
- uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
- source: webrender::ExternalImageSource::NativeTexture(id),
- }
- }
- fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {
- }
-}
-
-struct App {
-}
-
-impl Example for App {
- fn render(
- &mut self,
- api: &RenderApi,
- builder: &mut DisplayListBuilder,
- txn: &mut Transaction,
- _framebuffer_size: DeviceUintSize,
- _pipeline_id: PipelineId,
- _document_id: DocumentId,
- ) {
- let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
- let info = LayoutPrimitiveInfo::new(bounds);
- builder.push_stacking_context(
- &info,
- None,
- TransformStyle::Flat,
- MixBlendMode::Normal,
- Vec::new(),
- GlyphRasterSpace::Screen,
- );
-
- let yuv_chanel1 = api.generate_image_key();
- let yuv_chanel2 = api.generate_image_key();
- let yuv_chanel2_1 = api.generate_image_key();
- let yuv_chanel3 = api.generate_image_key();
- txn.add_image(
- yuv_chanel1,
- ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
- ImageData::External(ExternalImageData {
- id: ExternalImageId(0),
- channel_index: 0,
- image_type: ExternalImageType::TextureHandle(
- TextureTarget::Default,
- ),
- }),
- None,
- );
- txn.add_image(
- yuv_chanel2,
- ImageDescriptor::new(100, 100, ImageFormat::RG8, true, false),
- ImageData::External(ExternalImageData {
- id: ExternalImageId(1),
- channel_index: 0,
- image_type: ExternalImageType::TextureHandle(
- TextureTarget::Default,
- ),
- }),
- None,
- );
- txn.add_image(
- yuv_chanel2_1,
- ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
- ImageData::External(ExternalImageData {
- id: ExternalImageId(2),
- channel_index: 0,
- image_type: ExternalImageType::TextureHandle(
- TextureTarget::Default,
- ),
- }),
- None,
- );
- txn.add_image(
- yuv_chanel3,
- ImageDescriptor::new(100, 100, ImageFormat::R8, true, false),
- ImageData::External(ExternalImageData {
- id: ExternalImageId(3),
- channel_index: 0,
- image_type: ExternalImageType::TextureHandle(
- TextureTarget::Default,
- ),
- }),
- None,
- );
-
- let info = LayoutPrimitiveInfo::with_clip_rect(
- LayoutRect::new(LayoutPoint::new(100.0, 0.0), LayoutSize::new(100.0, 100.0)),
- bounds,
- );
- builder.push_yuv_image(
- &info,
- YuvData::NV12(yuv_chanel1, yuv_chanel2),
- YuvColorSpace::Rec601,
- ImageRendering::Auto,
- );
-
- let info = LayoutPrimitiveInfo::with_clip_rect(
- LayoutRect::new(LayoutPoint::new(300.0, 0.0), LayoutSize::new(100.0, 100.0)),
- bounds,
- );
- builder.push_yuv_image(
- &info,
- YuvData::PlanarYCbCr(yuv_chanel1, yuv_chanel2_1, yuv_chanel3),
- YuvColorSpace::Rec601,
- ImageRendering::Auto,
- );
-
- builder.pop_stacking_context();
- }
-
- fn on_event(
- &mut self,
- _event: winit::WindowEvent,
- _api: &RenderApi,
- _document_id: DocumentId,
- ) -> bool {
- false
- }
-
- fn get_image_handlers(
- &mut self,
- gl: &gl::Gl,
- ) -> (Option<Box<webrender::ExternalImageHandler>>,
- Option<Box<webrender::OutputImageHandler>>) {
- (Some(Box::new(YuvImageProvider::new(gl))), None)
- }
-}
-
-fn main() {
- let mut app = App {
- };
- boilerplate::main_wrapper(&mut app, None);
-}
--- a/gfx/webrender/res/ps_text_run.glsl
+++ b/gfx/webrender/res/ps_text_run.glsl
@@ -17,44 +17,52 @@ varying vec4 vUvClip;
VertexInfo write_text_vertex(vec2 clamped_local_pos,
RectWithSize local_clip_rect,
float z,
ClipScrollNode scroll_node,
PictureTask task,
RectWithSize snap_rect,
vec2 snap_bias) {
-#if defined(WR_FEATURE_GLYPH_TRANSFORM) || !defined(WR_FEATURE_TRANSFORM)
// Ensure the transform does not contain a subpixel translation to ensure
// that glyph snapping is stable for equivalent glyph subpixel positions.
- scroll_node.transform[3].xy = floor(scroll_node.transform[3].xy + 0.5);
+#if defined(WR_FEATURE_GLYPH_TRANSFORM)
+ bool remove_subpx_offset = true;
+#else
+ bool remove_subpx_offset = scroll_node.is_axis_aligned;
#endif
+ if (remove_subpx_offset) {
+ scroll_node.transform[3].xy = floor(scroll_node.transform[3].xy + 0.5);
+ }
+
// Transform the current vertex to world space.
vec4 world_pos = scroll_node.transform * vec4(clamped_local_pos, 0.0, 1.0);
// Convert the world positions to device pixel space.
vec2 device_pos = world_pos.xy / world_pos.w * uDevicePixelRatio;
// Apply offsets for the render task to get correct screen location.
vec2 final_pos = device_pos -
task.content_origin +
task.common_data.task_rect.p0;
#ifdef WR_FEATURE_GLYPH_TRANSFORM
// For transformed subpixels, we just need to align the glyph origin to a device pixel.
final_pos += floor(snap_rect.p0 + snap_bias) - snap_rect.p0;
-#elif !defined(WR_FEATURE_TRANSFORM)
+#else
// Compute the snapping offset only if the scroll node transform is axis-aligned.
- final_pos += compute_snap_offset(
- clamped_local_pos,
- scroll_node.transform,
- snap_rect,
- snap_bias
- );
+ if (scroll_node.is_axis_aligned) {
+ final_pos += compute_snap_offset(
+ clamped_local_pos,
+ scroll_node.transform,
+ snap_rect,
+ snap_bias
+ );
+ }
#endif
gl_Position = uTransform * vec4(final_pos, z, 1.0);
VertexInfo vi = VertexInfo(
clamped_local_pos,
device_pos,
world_pos.w,
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -33,23 +33,16 @@ use util::{MatrixHelpers, TransformedRec
// Special sentinel value recognized by the shader. It is considered to be
// a dummy task that doesn't mask out anything.
const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(0x7fff);
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub enum TransformBatchKind {
- TextRun(GlyphFormat),
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum BrushBatchKind {
Solid,
Image(ImageBufferKind),
Blend,
MixBlend {
task_id: RenderTaskId,
source_id: RenderTaskId,
backdrop_id: RenderTaskId,
@@ -59,17 +52,17 @@ pub enum BrushBatchKind {
LinearGradient,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum BatchKind {
SplitComposite,
- Transformable(TransformedRectKind, TransformBatchKind),
+ TextRun(GlyphFormat),
Brush(BrushBatchKind),
}
/// Optional textures that can be used as a source in the shaders.
/// Textures that are not used by the batch are equal to TextureId::invalid().
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
@@ -1096,17 +1089,17 @@ impl AlphaBatchBuilder {
}
}
PrimitiveKind::TextRun => {
let text_cpu =
&ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
let font = text_cpu.get_font(
ctx.device_pixel_scale,
- Some(scroll_node.transform),
+ scroll_node.transform,
);
let subpx_dir = font.get_subpx_dir();
let glyph_fetch_buffer = &mut self.glyph_fetch_buffer;
let batch_list = &mut self.batch_list;
ctx.resource_cache.fetch_glyphs(
font,
@@ -1126,20 +1119,17 @@ impl AlphaBatchBuilder {
let textures = BatchTextures {
colors: [
texture_id,
SourceTexture::Invalid,
SourceTexture::Invalid,
],
};
- let kind = BatchKind::Transformable(
- transform_kind,
- TransformBatchKind::TextRun(glyph_format),
- );
+ let kind = BatchKind::TextRun(glyph_format);
let (blend_mode, color_mode) = match glyph_format {
GlyphFormat::Subpixel |
GlyphFormat::TransformedSubpixel => {
if text_cpu.font.bg_color.a != 0 {
(
BlendMode::SubpixelWithBgColor,
ShaderColorMode::FromRenderPassMode,
--- a/gfx/webrender/src/display_list_flattener.rs
+++ b/gfx/webrender/src/display_list_flattener.rs
@@ -8,17 +8,16 @@ use api::{ClipId, ColorF, ComplexClipReg
use api::{DevicePixelScale, DeviceUintRect, DisplayItemRef, Epoch, ExtendMode, ExternalScrollId};
use api::{FilterOp, FontInstanceKey, GlyphInstance, GlyphOptions, GlyphRasterSpace, GradientStop};
use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, LayoutPoint};
use api::{LayoutPrimitiveInfo, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
use api::{LineOrientation, LineStyle, LocalClip, NinePatchBorderSource, PipelineId};
use api::{PropertyBinding, ReferenceFrame, RepeatMode, ScrollFrameDisplayItem, ScrollSensitivity};
use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
use api::{TransformStyle, YuvColorSpace, YuvData};
-use app_units::Au;
use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
use clip_scroll_node::{ClipScrollNode, NodeType, StickyFrameInfo};
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
use euclid::{SideOffsets2D, vec2};
use frame_builder::{FrameBuilder, FrameBuilderConfig};
use glyph_rasterizer::FontInstance;
use gpu_cache::GpuCacheHandle;
use gpu_types::BrushFlags;
@@ -1884,26 +1883,16 @@ impl<'a> DisplayListFlattener<'a> {
}
};
// Trivial early out checks
if font_instance.size.0 <= 0 {
return;
}
- // Sanity check - anything with glyphs bigger than this
- // is probably going to consume too much memory to render
- // efficiently anyway. This is specifically to work around
- // the font_advance.html reftest, which creates a very large
- // font as a crash test - the rendering is also ignored
- // by the azure renderer.
- if font_instance.size >= Au::from_px(4096) {
- return;
- }
-
// TODO(gw): Use a proper algorithm to select
// whether this item should be rendered with
// subpixel AA!
let mut render_mode = self.config
.default_font_render_mode
.limit_by(font_instance.render_mode);
let mut flags = font_instance.flags;
if let Some(options) = glyph_options {
--- a/gfx/webrender/src/glyph_rasterizer/mod.rs
+++ b/gfx/webrender/src/glyph_rasterizer/mod.rs
@@ -154,16 +154,18 @@ impl FontTransform {
}
impl<'a> From<&'a LayoutToWorldTransform> for FontTransform {
fn from(xform: &'a LayoutToWorldTransform) -> Self {
FontTransform::new(xform.m11, xform.m21, xform.m12, xform.m22)
}
}
+pub const FONT_SIZE_LIMIT: f64 = 1024.0;
+
#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct FontInstance {
pub font_key: FontKey,
// The font size is in *device* pixels, not logical pixels.
// It is stored as an Au since we need sub-pixel sizes, but
// can't store as a f32 due to use of this type as a hash key.
@@ -265,16 +267,33 @@ impl FontInstance {
if bold_offset < 1.0 {
bold_offset = 0.25 + 0.75 * bold_offset;
}
(bold_offset * x_scale).max(1.0).round() as usize
} else {
0
}
}
+
+ pub fn oversized_scale_factor(&self, x_scale: f64, y_scale: f64) -> f64 {
+ // If the scaled size is over the limit, then it will need to
+ // be scaled up from the size limit to the scaled size.
+ // However, this should only occur when the font isn't using any
+ // features that would tie it to device space, like transforms,
+ // subpixel AA, or subpixel positioning.
+ let max_size = self.size.to_f64_px() * x_scale.max(y_scale);
+ if max_size > FONT_SIZE_LIMIT &&
+ self.transform.is_identity() &&
+ self.render_mode != FontRenderMode::Subpixel &&
+ !self.use_subpixel_position() {
+ max_size / FONT_SIZE_LIMIT
+ } else {
+ 1.0
+ }
+ }
}
#[repr(u32)]
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)]
pub enum SubpixelDirection {
None = 0,
Horizontal,
Vertical,
--- a/gfx/webrender/src/glyph_rasterizer/pathfinder.rs
+++ b/gfx/webrender/src/glyph_rasterizer/pathfinder.rs
@@ -111,38 +111,40 @@ impl GlyphRasterizer {
&render_task_cache_key);
Some((cache_item, font.get_glyph_format()))
}
pub(in super) fn request_glyph_from_pathfinder_if_necessary(
&mut self,
glyph_key: &GlyphKey,
font: &FontInstance,
+ scale: f32,
cached_glyph_info: CachedGlyphInfo,
texture_cache: &mut TextureCache,
gpu_cache: &mut GpuCache,
render_task_cache: &mut RenderTaskCache,
render_task_tree: &mut RenderTaskTree,
render_passes: &mut SpecialRenderPasses)
-> Result<(RenderTaskCacheEntryHandle,GlyphFormat), ()>
{
let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();
let render_task_cache_key = cached_glyph_info.render_task_cache_key;
let (glyph_origin, glyph_size) = (cached_glyph_info.origin, render_task_cache_key.size);
- let user_data = [glyph_origin.x as f32, (glyph_origin.y - glyph_size.height) as f32, 1.0];
+ let user_data = [glyph_origin.x as f32, (glyph_origin.y - glyph_size.height) as f32, scale];
let handle = try!(render_task_cache.request_render_task(render_task_cache_key,
texture_cache,
gpu_cache,
render_task_tree,
Some(user_data),
false,
|render_tasks| {
// TODO(pcwalton): Non-subpixel font render mode.
request_render_task_from_pathfinder(glyph_key,
font,
+ scale,
&glyph_origin,
&glyph_size,
&mut *pathfinder_font_context,
font.render_mode,
render_tasks,
render_passes)
}));
Ok((handle, font.get_glyph_format()))
@@ -158,16 +160,19 @@ impl GlyphRasterizer {
render_task_cache: &mut RenderTaskCache,
render_task_tree: &mut RenderTaskTree,
render_passes: &mut SpecialRenderPasses,
) {
debug_assert!(self.font_contexts.lock_shared_context().has_font(&font.font_key));
let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font.clone());
+ let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
+ let scale = font.oversized_scale_factor(x_scale, y_scale) as f32;
+
// select glyphs that have not been requested yet.
for glyph_key in glyph_keys {
let mut cached_glyph_info = None;
match glyph_key_cache.entry(glyph_key.clone()) {
Entry::Occupied(mut entry) => {
let value = entry.into_mut();
match *value {
GlyphCacheEntry::Cached(ref glyph_info) => {
@@ -179,17 +184,17 @@ impl GlyphRasterizer {
Entry::Vacant(_) => {}
}
if cached_glyph_info.is_none() {
let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();
let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
font_key: font.font_key.clone(),
- size: font.size,
+ size: font.size.scale_by(scale.recip()),
};
// TODO: pathfinder will need to support 2D subpixel offset
let pathfinder_subpixel_offset =
pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
let pathfinder_glyph_key =
pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
pathfinder_subpixel_offset);
@@ -211,16 +216,17 @@ impl GlyphRasterizer {
self.next_gpu_glyph_cache_key.0 += 1;
}
}
let handle = match cached_glyph_info {
Some(glyph_info) => {
match self.request_glyph_from_pathfinder_if_necessary(glyph_key,
&font,
+ scale,
glyph_info.clone(),
texture_cache,
gpu_cache,
render_task_cache,
render_task_tree,
render_passes) {
Ok(_) => GlyphCacheEntry::Cached(glyph_info),
Err(_) => GlyphCacheEntry::Blank,
@@ -254,26 +260,28 @@ impl FontContexts {
fn compute_embolden_amount(ppem: f32) -> TypedVector2D<f32, DevicePixel> {
TypedVector2D::new(f32::min(ppem * STEM_DARKENING_FACTOR_X, MAX_STEM_DARKENING_AMOUNT),
f32::min(ppem * STEM_DARKENING_FACTOR_Y, MAX_STEM_DARKENING_AMOUNT))
}
fn request_render_task_from_pathfinder(glyph_key: &GlyphKey,
font: &FontInstance,
+ scale: f32,
glyph_origin: &DeviceIntPoint,
glyph_size: &DeviceIntSize,
font_context: &mut PathfinderFontContext,
render_mode: FontRenderMode,
render_tasks: &mut RenderTaskTree,
render_passes: &mut SpecialRenderPasses)
-> Result<RenderTaskId, ()> {
+ let size = font.size.scale_by(scale.recip());
let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
font_key: font.font_key.clone(),
- size: font.size,
+ size,
};
// TODO: pathfinder will need to support 2D subpixel offset
let pathfinder_subpixel_offset =
pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
let glyph_subpixel_offset: f64 = glyph_key.subpixel_offset.0.into();
let pathfinder_glyph_key = pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
pathfinder_subpixel_offset);
@@ -286,17 +294,17 @@ fn request_render_task_from_pathfinder(g
mesh.push_stencil_segments(CubicToQuadraticTransformer::new(outline.iter(), tolerance));
mesh.push_stencil_normals(CubicToQuadraticTransformer::new(outline.iter(), tolerance));
// FIXME(pcwalton): Support vertical subpixel offsets.
// FIXME(pcwalton): Embolden amount should be 0 on macOS if "Use LCD font
// smoothing" is unchecked in System Preferences.
let subpixel_offset = TypedPoint2D::new(glyph_subpixel_offset as f32, 0.0);
- let embolden_amount = compute_embolden_amount(font.size.to_f32_px());
+ let embolden_amount = compute_embolden_amount(size.to_f32_px());
let location = RenderTaskLocation::Dynamic(None, Some(*glyph_size));
let glyph_render_task = RenderTask::new_glyph(location,
mesh,
&glyph_origin,
&subpixel_offset,
font.render_mode,
&embolden_amount);
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -485,17 +485,18 @@ impl FontContext {
};
}
}
}
#[cfg(not(feature = "pathfinder"))]
pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
- let size = font.size.scale_by(y_scale as f32);
+ let scale = font.oversized_scale_factor(x_scale, y_scale);
+ let size = font.size.scale_by((y_scale / scale) as f32);
let ct_font = match self.get_ct_font(font.font_key, size, &font.variations) {
Some(font) => font,
None => return GlyphRasterResult::LoadFailed,
};
let bitmap = is_bitmap_font(&ct_font);
let (mut shape, (x_offset, y_offset)) = if bitmap {
(FontTransform::identity(), (0.0, 0.0))
@@ -524,17 +525,17 @@ impl FontContext {
ty: 0.0,
})
} else {
None
};
let glyph = key.index as CGGlyph;
let (strike_scale, pixel_step) = if bitmap { (y_scale, 1.0) } else { (x_scale, y_scale / x_scale) };
- let extra_strikes = font.get_extra_strikes(strike_scale);
+ let extra_strikes = font.get_extra_strikes(strike_scale / scale);
let metrics = get_glyph_metrics(
&ct_font,
transform.as_ref(),
glyph,
x_offset,
y_offset,
extra_strikes as f64 * pixel_step,
);
@@ -719,17 +720,17 @@ impl FontContext {
}
}
GlyphRasterResult::Bitmap(RasterizedGlyph {
left: metrics.rasterized_left as f32,
top: metrics.rasterized_ascent as f32,
width: metrics.rasterized_width,
height: metrics.rasterized_height,
- scale: if bitmap { y_scale.recip() as f32 } else { 1.0 },
+ scale: (if bitmap { scale / y_scale } else { scale }) as f32,
format: if bitmap { GlyphFormat::ColorBitmap } else { font.get_glyph_format() },
bytes: rasterized_pixels,
})
}
}
#[cfg(feature = "pathfinder")]
impl<'a> Into<CGFont> for NativeFontHandleWrapper<'a> {
--- a/gfx/webrender/src/platform/unix/font.rs
+++ b/gfx/webrender/src/platform/unix/font.rs
@@ -231,17 +231,17 @@ impl FontContext {
pub fn delete_font(&mut self, font_key: &FontKey) {
if let Some(face) = self.faces.remove(font_key) {
let result = unsafe { FT_Done_Face(face.face) };
assert!(succeeded(result));
}
}
- fn load_glyph(&self, font: &FontInstance, glyph: &GlyphKey) -> Option<FT_GlyphSlot> {
+ fn load_glyph(&self, font: &FontInstance, glyph: &GlyphKey) -> Option<(FT_GlyphSlot, f32)> {
debug_assert!(self.faces.contains_key(&font.font_key));
let face = self.faces.get(&font.font_key).unwrap();
let mut load_flags = FT_LOAD_DEFAULT;
let FontInstancePlatformOptions { mut hinting, .. } = font.platform_options.unwrap_or_default();
// Disable hinting if there is a non-axis-aligned transform.
if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) ||
((font.transform.scale_x != 0.0 || font.transform.scale_y != 0.0) &&
@@ -278,23 +278,24 @@ impl FontContext {
if font.flags.contains(FontInstanceFlags::VERTICAL_LAYOUT) {
load_flags |= FT_LOAD_VERTICAL_LAYOUT;
}
load_flags |= FT_LOAD_COLOR;
load_flags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
+ let scale = font.oversized_scale_factor(x_scale, y_scale);
let req_size = font.size.to_f64_px();
let face_flags = unsafe { (*face.face).face_flags };
let mut result = if (face_flags & (FT_FACE_FLAG_FIXED_SIZES as FT_Long)) != 0 &&
(face_flags & (FT_FACE_FLAG_SCALABLE as FT_Long)) == 0 &&
(load_flags & FT_LOAD_NO_BITMAP) == 0 {
unsafe { FT_Set_Transform(face.face, ptr::null_mut(), ptr::null_mut()) };
- self.choose_bitmap_size(face.face, req_size * y_scale)
+ self.choose_bitmap_size(face.face, req_size * y_scale / scale)
} else {
let mut shape = font.transform.invert_scale(x_scale, y_scale);
if font.flags.contains(FontInstanceFlags::FLIP_X) {
shape = shape.flip_x();
}
if font.flags.contains(FontInstanceFlags::FLIP_Y) {
shape = shape.flip_y();
}
@@ -309,18 +310,18 @@ impl FontContext {
xy: (shape.skew_x * -65536.0) as FT_Fixed,
yx: (shape.skew_y * -65536.0) as FT_Fixed,
yy: (shape.scale_y * 65536.0) as FT_Fixed,
};
unsafe {
FT_Set_Transform(face.face, &mut ft_shape, ptr::null_mut());
FT_Set_Char_Size(
face.face,
- (req_size * x_scale * 64.0 + 0.5) as FT_F26Dot6,
- (req_size * y_scale * 64.0 + 0.5) as FT_F26Dot6,
+ (req_size * x_scale / scale * 64.0 + 0.5) as FT_F26Dot6,
+ (req_size * y_scale / scale * 64.0 + 0.5) as FT_F26Dot6,
0,
0,
)
}
};
if succeeded(result) {
result = unsafe { FT_Load_Glyph(face.face, glyph.index as FT_UInt, load_flags as FT_Int32) };
@@ -331,18 +332,21 @@ impl FontContext {
assert!(slot != ptr::null_mut());
if font.flags.contains(FontInstanceFlags::SYNTHETIC_BOLD) {
unsafe { FT_GlyphSlot_Embolden(slot) };
}
let format = unsafe { (*slot).format };
match format {
- FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE |
- FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => Some(slot),
+ FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
+ let y_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem };
+ Some((slot, req_size as f32 / y_size as f32))
+ }
+ FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => Some((slot, scale as f32)),
_ => {
error!("Unsupported format");
debug!("format={:?}", format);
None
}
}
} else {
error!("Unable to load glyph");
@@ -409,77 +413,81 @@ impl FontContext {
cbox
}
fn get_glyph_dimensions_impl(
&self,
slot: FT_GlyphSlot,
font: &FontInstance,
glyph: &GlyphKey,
- transform_bitmaps: bool,
+ use_transform: Option<f32>,
) -> Option<GlyphDimensions> {
- let metrics = unsafe { &(*slot).metrics };
-
- let mut advance = metrics.horiAdvance as f32 / 64.0;
- match unsafe { (*slot).format } {
+ let format = unsafe { (*slot).format };
+ let (mut left, mut top, mut width, mut height) = match format {
FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
- let mut left = unsafe { (*slot).bitmap_left };
- let mut top = unsafe { (*slot).bitmap_top };
- let mut width = unsafe { (*slot).bitmap.width };
- let mut height = unsafe { (*slot).bitmap.rows };
- if transform_bitmaps {
- let y_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem };
- let scale = font.size.to_f32_px() / y_size as f32;
- let x0 = left as f32 * scale;
- let x1 = width as f32 * scale + x0;
- let y1 = top as f32 * scale;
- let y0 = y1 - height as f32 * scale;
- left = x0.round() as i32;
- top = y1.round() as i32;
- width = (x1.ceil() - x0.floor()) as u32;
- height = (y1.ceil() - y0.floor()) as u32;
- advance *= scale;
- if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
- let (skew_min, skew_max) = get_skew_bounds(top - height as i32, top);
- left += skew_min as i32;
- width += (skew_max - skew_min) as u32;
- }
- if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
- mem::swap(&mut width, &mut height);
- mem::swap(&mut left, &mut top);
- left -= width as i32;
- top += height as i32;
- }
- if font.flags.contains(FontInstanceFlags::FLIP_X) {
- left = -(left + width as i32);
- }
- if font.flags.contains(FontInstanceFlags::FLIP_Y) {
- top = -(top - height as i32);
- }
- }
- Some(GlyphDimensions {
- left,
- top,
- width,
- height,
- advance,
- })
+ unsafe { (
+ (*slot).bitmap_left,
+ (*slot).bitmap_top,
+ (*slot).bitmap.width,
+ (*slot).bitmap.rows,
+ ) }
}
FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => {
let cbox = self.get_bounding_box(slot, font, glyph);
- Some(GlyphDimensions {
- left: (cbox.xMin >> 6) as i32,
- top: (cbox.yMax >> 6) as i32,
- width: ((cbox.xMax - cbox.xMin) >> 6) as u32,
- height: ((cbox.yMax - cbox.yMin) >> 6) as u32,
- advance,
- })
+ (
+ (cbox.xMin >> 6) as i32,
+ (cbox.yMax >> 6) as i32,
+ ((cbox.xMax - cbox.xMin) >> 6) as u32,
+ ((cbox.yMax - cbox.yMin) >> 6) as u32,
+ )
+ }
+ _ => return None,
+ };
+ let mut advance = unsafe { (*slot).metrics.horiAdvance as f32 / 64.0 };
+ if let Some(scale) = use_transform {
+ if scale != 1.0 {
+ let x0 = left as f32 * scale;
+ let x1 = width as f32 * scale + x0;
+ let y1 = top as f32 * scale;
+ let y0 = y1 - height as f32 * scale;
+ left = x0.round() as i32;
+ top = y1.round() as i32;
+ width = (x1.ceil() - x0.floor()) as u32;
+ height = (y1.ceil() - y0.floor()) as u32;
+ advance *= scale;
}
- _ => None,
+ // An outline glyph's cbox would have already been transformed inside FT_Load_Glyph,
+ // so only handle bitmap glyphs which are not handled by FT_Load_Glyph.
+ if format == FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP {
+ if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
+ let (skew_min, skew_max) = get_skew_bounds(top - height as i32, top);
+ left += skew_min as i32;
+ width += (skew_max - skew_min) as u32;
+ }
+ if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
+ mem::swap(&mut width, &mut height);
+ mem::swap(&mut left, &mut top);
+ left -= width as i32;
+ top += height as i32;
+ }
+ if font.flags.contains(FontInstanceFlags::FLIP_X) {
+ left = -(left + width as i32);
+ }
+ if font.flags.contains(FontInstanceFlags::FLIP_Y) {
+ top = -(top - height as i32);
+ }
+ }
}
+ Some(GlyphDimensions {
+ left,
+ top,
+ width,
+ height,
+ advance,
+ })
}
pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
let face = self.faces.get(&font_key).expect("Unknown font key!");
unsafe {
let idx = FT_Get_Char_Index(face.face, ch as _);
if idx != 0 {
Some(idx)
@@ -490,17 +498,17 @@ impl FontContext {
}
pub fn get_glyph_dimensions(
&mut self,
font: &FontInstance,
key: &GlyphKey,
) -> Option<GlyphDimensions> {
let slot = self.load_glyph(font, key);
- slot.and_then(|slot| self.get_glyph_dimensions_impl(slot, font, key, true))
+ slot.and_then(|(slot, scale)| self.get_glyph_dimensions_impl(slot, font, key, Some(scale)))
}
fn choose_bitmap_size(&self, face: FT_Face, requested_size: f64) -> FT_Error {
let mut best_dist = unsafe { *(*face).available_sizes.offset(0) }.y_ppem as f64 / 64.0 - requested_size;
let mut best_size = 0;
let num_fixed_sizes = unsafe { (*face).num_fixed_sizes };
for i in 1 .. num_fixed_sizes {
// Distance is positive if strike is larger than desired size,
@@ -587,40 +595,38 @@ impl FontContext {
false
} else {
true
}
}
#[cfg(not(feature = "pathfinder"))]
pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
- let slot = match self.load_glyph(font, key) {
- Some(slot) => slot,
+ let (slot, scale) = match self.load_glyph(font, key) {
+ Some(val) => val,
None => return GlyphRasterResult::LoadFailed,
};
// Get dimensions of the glyph, to see if we need to rasterize it.
- let dimensions = match self.get_glyph_dimensions_impl(slot, font, key, false) {
+ // Don't apply scaling to the dimensions, as the glyph cache needs to know the actual
+ // footprint of the glyph.
+ let dimensions = match self.get_glyph_dimensions_impl(slot, font, key, None) {
Some(val) => val,
None => return GlyphRasterResult::LoadFailed,
};
let GlyphDimensions { mut left, mut top, width, height, .. } = dimensions;
// For spaces and other non-printable characters, early out.
if width == 0 || height == 0 {
return GlyphRasterResult::LoadFailed;
}
let format = unsafe { (*slot).format };
- let mut scale = 1.0;
match format {
- FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {
- let y_size = unsafe { (*(*(*slot).face).size).metrics.y_ppem };
- scale = font.size.to_f32_px() / y_size as f32;
- }
+ FT_Glyph_Format::FT_GLYPH_FORMAT_BITMAP => {}
FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => {
if !self.rasterize_glyph_outline(slot, font, key) {
return GlyphRasterResult::LoadFailed;
}
}
_ => {
error!("Unsupported format");
debug!("format={:?}", format);
--- a/gfx/webrender/src/platform/windows/font.rs
+++ b/gfx/webrender/src/platform/windows/font.rs
@@ -380,18 +380,19 @@ impl FontContext {
FontRenderMode::Subpixel => {
font.color = font.color.quantize();
}
}
}
#[cfg(not(feature = "pathfinder"))]
pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult {
- let (.., y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
- let size = (font.size.to_f64_px() * y_scale) as f32;
+ let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0));
+ let scale = font.oversized_scale_factor(x_scale, y_scale);
+ let size = (font.size.to_f64_px() * y_scale / scale) as f32;
let bitmaps = is_bitmap_font(font);
let (mut shape, (x_offset, y_offset)) = if bitmaps {
(FontTransform::identity(), (0.0, 0.0))
} else {
(font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
};
if font.flags.contains(FontInstanceFlags::FLIP_X) {
shape = shape.flip_x();
@@ -446,17 +447,17 @@ impl FontContext {
};
lut_correction.preblend(&mut bgra_pixels, font.color);
GlyphRasterResult::Bitmap(RasterizedGlyph {
left: bounds.left as f32,
top: -bounds.top as f32,
width,
height,
- scale: if bitmaps { y_scale.recip() as f32 } else { 1.0 },
+ scale: (if bitmaps { scale / y_scale } else { scale }) as f32,
format: if bitmaps { GlyphFormat::Bitmap } else { font.get_glyph_format() },
bytes: bgra_pixels,
})
}
}
#[cfg(feature = "pathfinder")]
impl<'a> From<NativeFontHandleWrapper<'a>> for PathfinderComPtr<IDWriteFontFace> {
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -12,17 +12,17 @@ use app_units::Au;
use border::{BorderCacheKey, BorderRenderTaskInfo};
use box_shadow::BLUR_SAMPLE_SCALE;
use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, CoordinateSystemId};
use clip_scroll_node::ClipScrollNode;
use clip::{ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipSource};
use clip::{ClipSourcesHandle, ClipWorkItem};
use frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
use frame_builder::PrimitiveRunContext;
-use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey};
+use glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks};
use gpu_types::{BrushFlags, ClipChainRectIndex};
use image::{for_each_tile, for_each_repetition};
use picture::{PictureCompositeMode, PictureId, PicturePrimitive};
#[cfg(debug_assertions)]
use render_backend::FrameId;
use render_task::{BlitSource, RenderTask, RenderTaskCacheKey};
@@ -768,37 +768,40 @@ impl TextRunPrimitiveCpu {
shadow,
glyph_raster_space,
}
}
pub fn get_font(
&self,
device_pixel_scale: DevicePixelScale,
- transform: Option<LayoutToWorldTransform>,
+ transform: LayoutToWorldTransform,
) -> FontInstance {
let mut font = self.font.clone();
font.size = font.size.scale_by(device_pixel_scale.0);
- if let Some(transform) = transform {
- if transform.has_perspective_component() ||
- !transform.has_2d_inverse() ||
- self.glyph_raster_space != GlyphRasterSpace::Screen {
- font.disable_subpixel_aa();
- font.disable_subpixel_position();
- } else {
- font.transform = FontTransform::from(&transform).quantize();
- }
+ // Only support transforms that can be coerced to simple 2D transforms.
+ if transform.has_perspective_component() ||
+ !transform.has_2d_inverse() ||
+ // Font sizes larger than the limit need to be scaled, thus can't use subpixels.
+ transform.exceeds_2d_scale(FONT_SIZE_LIMIT / font.size.to_f64_px()) ||
+ // Otherwise, ensure the font is rasterized in screen-space.
+ self.glyph_raster_space != GlyphRasterSpace::Screen {
+ font.disable_subpixel_aa();
+ font.disable_subpixel_position();
+ } else {
+ // Quantize the transform to minimize thrashing of the glyph cache.
+ font.transform = FontTransform::from(&transform).quantize();
}
font
}
fn prepare_for_render(
&mut self,
device_pixel_scale: DevicePixelScale,
- transform: Option<LayoutToWorldTransform>,
+ transform: LayoutToWorldTransform,
allow_subpixel_aa: bool,
display_list: &BuiltDisplayList,
frame_building_state: &mut FrameBuildingState,
) {
if !allow_subpixel_aa && self.font.bg_color.a == 0 {
self.font.disable_subpixel_aa();
}
@@ -1485,17 +1488,17 @@ impl PrimitiveStore {
{
metadata.prepared_frame_id = frame_state.render_tasks.frame_id();
}
match metadata.prim_kind {
PrimitiveKind::TextRun => {
let text = &mut self.cpu_text_runs[metadata.cpu_prim_index.0];
// The transform only makes sense for screen space rasterization
- let transform = Some(prim_run_context.scroll_node.world_content_transform.into());
+ let transform = prim_run_context.scroll_node.world_content_transform.into();
text.prepare_for_render(
frame_context.device_pixel_scale,
transform,
pic_context.allow_subpixel_aa,
pic_context.display_list,
frame_state,
);
}
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -714,19 +714,24 @@ impl RenderBackend {
SceneBuilderResult::Transaction {
document_id,
mut built_scene,
resource_updates,
frame_ops,
render,
result_tx,
} => {
+ let mut ops = DocumentOps::nop();
if let Some(doc) = self.documents.get_mut(&document_id) {
if let Some(mut built_scene) = built_scene.take() {
doc.new_async_scene_ready(built_scene);
+ // After applying the new scene we need to
+ // rebuild the hit-tester, so we trigger a render
+ // step.
+ ops = DocumentOps::render();
}
if let Some(tx) = result_tx {
let (resume_tx, resume_rx) = channel();
tx.send(SceneSwapResult::Complete(resume_tx)).unwrap();
// Block until the post-swap hook has completed on
// the scene builder thread. We need to do this before
// we can sample from the sampler hook which might happen
// in the update_document call below.
@@ -745,23 +750,23 @@ impl RenderBackend {
let transaction_msg = TransactionMsg {
scene_ops: Vec::new(),
frame_ops,
resource_updates,
generate_frame: render,
use_scene_builder_thread: false,
};
- if !transaction_msg.is_empty() {
+ if !transaction_msg.is_empty() || ops.render {
self.update_document(
document_id,
transaction_msg,
&mut frame_counter,
&mut profile_counters,
- DocumentOps::render(),
+ ops,
);
}
},
SceneBuilderResult::FlushComplete(tx) => {
tx.send(()).ok();
}
SceneBuilderResult::Stopped => {
panic!("We haven't sent a Stop yet, how did we get a Stopped back?");
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -11,17 +11,17 @@
use api::{BlobImageRenderer, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentId, Epoch, ExternalImageId};
use api::{ExternalImageType, FontRenderMode, FrameMsg, ImageFormat, PipelineId};
use api::{RenderApiSender, RenderNotifier, TexelRect, TextureTarget};
use api::{channel};
use api::DebugCommand;
use api::channel::PayloadReceiverHelperMethods;
-use batch::{BatchKind, BatchTextures, BrushBatchKind, TransformBatchKind};
+use batch::{BatchKind, BatchTextures, BrushBatchKind};
#[cfg(any(feature = "capture", feature = "replay"))]
use capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage};
use debug_colors;
use device::{DepthFunction, Device, FrameId, Program, UploadMethod, Texture, PBO};
use device::{ExternalTexture, FBOId, TextureSlot};
use device::{FileWatcherHandler, ShaderError, TextureFilter,
VertexUsageHint, VAO, VBO, CustomVAO};
use device::{ProgramCache, ReadPixelsFormat};
@@ -109,17 +109,17 @@ const GPU_TAG_BRUSH_YUV_IMAGE: GpuProfil
color: debug_colors::DARKGREEN,
};
const GPU_TAG_BRUSH_MIXBLEND: GpuProfileTag = GpuProfileTag {
label: "B_MixBlend",
color: debug_colors::MAGENTA,
};
const GPU_TAG_BRUSH_BLEND: GpuProfileTag = GpuProfileTag {
label: "B_Blend",
- color: debug_colors::LIGHTBLUE,
+ color: debug_colors::ORANGE,
};
const GPU_TAG_BRUSH_IMAGE: GpuProfileTag = GpuProfileTag {
label: "B_Image",
color: debug_colors::SPRINGGREEN,
};
const GPU_TAG_BRUSH_SOLID: GpuProfileTag = GpuProfileTag {
label: "B_Solid",
color: debug_colors::RED,
@@ -165,48 +165,33 @@ const GPU_SAMPLER_TAG_OPAQUE: GpuProfile
label: "Opaque Pass",
color: debug_colors::BLACK,
};
const GPU_SAMPLER_TAG_TRANSPARENT: GpuProfileTag = GpuProfileTag {
label: "Transparent Pass",
color: debug_colors::BLACK,
};
-impl TransformBatchKind {
- #[cfg(feature = "debugger")]
- fn debug_name(&self) -> &'static str {
- match *self {
- TransformBatchKind::TextRun(..) => "TextRun",
- }
- }
-
- fn sampler_tag(&self) -> GpuProfileTag {
- match *self {
- TransformBatchKind::TextRun(..) => GPU_TAG_PRIM_TEXT_RUN,
- }
- }
-}
-
impl BatchKind {
#[cfg(feature = "debugger")]
fn debug_name(&self) -> &'static str {
match *self {
BatchKind::SplitComposite => "SplitComposite",
BatchKind::Brush(kind) => {
match kind {
BrushBatchKind::Solid => "Brush (Solid)",
BrushBatchKind::Image(..) => "Brush (Image)",
BrushBatchKind::Blend => "Brush (Blend)",
BrushBatchKind::MixBlend { .. } => "Brush (Composite)",
BrushBatchKind::YuvImage(..) => "Brush (YuvImage)",
BrushBatchKind::RadialGradient => "Brush (RadialGradient)",
BrushBatchKind::LinearGradient => "Brush (LinearGradient)",
}
}
- BatchKind::Transformable(_, batch_kind) => batch_kind.debug_name(),
+ BatchKind::TextRun(_) => "TextRun",
}
}
fn sampler_tag(&self) -> GpuProfileTag {
match *self {
BatchKind::SplitComposite => GPU_TAG_PRIM_SPLIT_COMPOSITE,
BatchKind::Brush(kind) => {
match kind {
@@ -214,17 +199,17 @@ impl BatchKind {
BrushBatchKind::Image(..) => GPU_TAG_BRUSH_IMAGE,
BrushBatchKind::Blend => GPU_TAG_BRUSH_BLEND,
BrushBatchKind::MixBlend { .. } => GPU_TAG_BRUSH_MIXBLEND,
BrushBatchKind::YuvImage(..) => GPU_TAG_BRUSH_YUV_IMAGE,
BrushBatchKind::RadialGradient => GPU_TAG_BRUSH_RADIAL_GRADIENT,
BrushBatchKind::LinearGradient => GPU_TAG_BRUSH_LINEAR_GRADIENT,
}
}
- BatchKind::Transformable(_, batch_kind) => batch_kind.sampler_tag(),
+ BatchKind::TextRun(_) => GPU_TAG_PRIM_TEXT_RUN,
}
}
}
bitflags! {
#[derive(Default)]
pub struct DebugFlags: u32 {
const PROFILER_DBG = 1 << 0;
--- a/gfx/webrender/src/shade.rs
+++ b/gfx/webrender/src/shade.rs
@@ -1,27 +1,26 @@
/* 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 api::{
YUV_COLOR_SPACES, YUV_FORMATS,
YuvColorSpace, YuvFormat,
};
-use batch::{BatchKey, BatchKind, BrushBatchKind, TransformBatchKind};
+use batch::{BatchKey, BatchKind, BrushBatchKind};
use device::{Device, Program, ShaderError};
use euclid::{Transform3D};
use glyph_rasterizer::GlyphFormat;
use renderer::{
desc,
MAX_VERTEX_TEXTURE_WIDTH,
BlendMode, ImageBufferKind, RendererError, RendererOptions,
TextureSampler, VertexArrayKind,
};
-use util::TransformedRectKind;
use gleam::gl::GlType;
use time::precise_time_ns;
impl ImageBufferKind {
pub(crate) fn get_feature_string(&self) -> &'static str {
match *self {
@@ -257,17 +256,16 @@ impl BrushShader {
if let Some(dual_source) = self.dual_source {
dual_source.deinit(device);
}
}
}
pub struct TextShader {
simple: LazilyCompiledShader,
- transform: LazilyCompiledShader,
glyph_transform: LazilyCompiledShader,
}
impl TextShader {
fn new(
name: &'static str,
device: &mut Device,
features: &[&'static str],
@@ -276,62 +274,46 @@ impl TextShader {
let simple = LazilyCompiledShader::new(
ShaderKind::Text,
name,
features,
device,
precache,
)?;
- let mut transform_features = features.to_vec();
- transform_features.push("TRANSFORM");
-
- let transform = LazilyCompiledShader::new(
- ShaderKind::Text,
- name,
- &transform_features,
- device,
- precache,
- )?;
-
let mut glyph_transform_features = features.to_vec();
glyph_transform_features.push("GLYPH_TRANSFORM");
let glyph_transform = LazilyCompiledShader::new(
ShaderKind::Text,
name,
&glyph_transform_features,
device,
precache,
)?;
- Ok(TextShader { simple, transform, glyph_transform })
+ Ok(TextShader { simple, glyph_transform })
}
pub fn get(
&mut self,
glyph_format: GlyphFormat,
- transform_kind: TransformedRectKind,
) -> &mut LazilyCompiledShader {
match glyph_format {
GlyphFormat::Alpha |
GlyphFormat::Subpixel |
GlyphFormat::Bitmap |
- GlyphFormat::ColorBitmap => match transform_kind {
- TransformedRectKind::AxisAligned => &mut self.simple,
- TransformedRectKind::Complex => &mut self.transform,
- }
+ GlyphFormat::ColorBitmap => &mut self.simple,
GlyphFormat::TransformedAlpha |
GlyphFormat::TransformedSubpixel => &mut self.glyph_transform,
}
}
fn deinit(self, device: &mut Device) {
self.simple.deinit(device);
- self.transform.deinit(device);
self.glyph_transform.deinit(device);
}
}
fn create_prim_shader(
name: &'static str,
device: &mut Device,
features: &[&'static str],
@@ -721,30 +703,22 @@ impl Shaders {
Self::get_yuv_shader_index(image_buffer_kind, format, color_space);
self.brush_yuv_image[shader_index]
.as_mut()
.expect("Unsupported YUV shader kind")
}
};
brush_shader.get(key.blend_mode)
}
- BatchKind::Transformable(transform_kind, batch_kind) => {
- match batch_kind {
- TransformBatchKind::TextRun(glyph_format) => {
- let text_shader = match key.blend_mode {
- BlendMode::SubpixelDualSource => {
- &mut self.ps_text_run_dual_source
- }
- _ => {
- &mut self.ps_text_run
- }
- };
- return text_shader.get(glyph_format, transform_kind);
- }
- }
+ BatchKind::TextRun(glyph_format) => {
+ let text_shader = match key.blend_mode {
+ BlendMode::SubpixelDualSource => &mut self.ps_text_run_dual_source,
+ _ => &mut self.ps_text_run,
+ };
+ text_shader.get(glyph_format)
}
}
}
pub fn deinit(self, device: &mut Device) {
self.cs_blur_a8.deinit(device);
self.cs_blur_rgba8.deinit(device);
self.brush_solid.deinit(device);
--- a/gfx/webrender/src/util.rs
+++ b/gfx/webrender/src/util.rs
@@ -15,16 +15,17 @@ use std::{i32, f32};
// Matches the definition of SK_ScalarNearlyZero in Skia.
const NEARLY_ZERO: f32 = 1.0 / 4096.0;
// TODO: Implement these in euclid!
pub trait MatrixHelpers<Src, Dst> {
fn preserves_2d_axis_alignment(&self) -> bool;
fn has_perspective_component(&self) -> bool;
fn has_2d_inverse(&self) -> bool;
+ fn exceeds_2d_scale(&self, limit: f64) -> bool;
fn inverse_project(&self, target: &TypedPoint2D<f32, Dst>) -> Option<TypedPoint2D<f32, Src>>;
fn inverse_rect_footprint(&self, rect: &TypedRect<f32, Dst>) -> TypedRect<f32, Src>;
fn transform_kind(&self) -> TransformedRectKind;
fn is_simple_translation(&self) -> bool;
fn is_simple_2d_translation(&self) -> bool;
}
impl<Src, Dst> MatrixHelpers<Src, Dst> for TypedTransform3D<f32, Src, Dst> {
@@ -63,16 +64,24 @@ impl<Src, Dst> MatrixHelpers<Src, Dst> f
fn has_perspective_component(&self) -> bool {
self.m14 != 0.0 || self.m24 != 0.0 || self.m34 != 0.0 || self.m44 != 1.0
}
fn has_2d_inverse(&self) -> bool {
self.m11 * self.m22 - self.m12 * self.m21 != 0.0
}
+ // Check if the matrix post-scaling on either the X or Y axes could cause geometry
+ // transformed by this matrix to have scaling exceeding the supplied limit.
+ fn exceeds_2d_scale(&self, limit: f64) -> bool {
+ let limit2 = (limit * limit) as f32;
+ self.m11 * self.m11 + self.m12 * self.m12 > limit2 ||
+ self.m21 * self.m21 + self.m22 * self.m22 > limit2
+ }
+
fn inverse_project(&self, target: &TypedPoint2D<f32, Dst>) -> Option<TypedPoint2D<f32, Src>> {
let m: TypedTransform2D<f32, Src, Dst>;
m = TypedTransform2D::column_major(
self.m11 - target.x * self.m14,
self.m21 - target.x * self.m24,
self.m41 - target.x * self.m44,
self.m12 - target.y * self.m14,
self.m22 - target.y * self.m24,
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-aff9f409f3d6a3518c38c1f7755657f564c1083a
+dd30fbb21c876b252b805b607bd04f3bab1fd228
--- a/gfx/wrench/src/args.yaml
+++ b/gfx/wrench/src/args.yaml
@@ -77,51 +77,52 @@ subcommands:
long: surface
help: 'What rendered surface to save as PNG, one of: screen, gpu-cache'
takes_value: true
- INPUT:
help: The input YAML file
required: true
index: 1
- show:
- about: show frame(s) described by YAML
+ about: show frame(s) described by YAML, binary recording, or capture
+ aliases: ['load', 'replay']
args:
- queue:
short: q
long: queue
- help: How many frames to submit to WR ahead of time (default 1)
+ help: How many frames to submit to WR ahead of time (default 1) (YAML only)
takes_value: true
- include:
long: include
- help: Include the given element type. Can be specified multiple times. (rect/image/text/glyphs/border)
+ help: Include the given element type. Can be specified multiple times. (rect/image/text/glyphs/border) (YAML only)
multiple: true
takes_value: true
- list-resources:
long: list-resources
- help: List the resources used by this YAML file
+ help: List the resources used by this render (YAML only)
- watch:
short: w
long: watch
- help: Watch the given YAML file, reloading whenever it changes
+ help: Watch the given file, reloading whenever it changes (YAML only)
+ - api:
+ long: api
+ help: Reissue Api messages for each frame (binary recording only)
+ - skip-uploads:
+ long: skip-uploads
+ help: Skip re-uploads while reissuing Api messages (BROKEN)
+ - play:
+ long: play
+ help: Play entire recording through, then quit (useful with --save) (binary recording only)
- INPUT:
- help: The input YAML file
+ help: The input YAML, binary recording, or capture directory
required: true
index: 1
- replay:
about: replay binary recording
args:
- - api:
- long: api
- help: Reissue Api messages for each frame
- - skip-uploads:
- long: skip-uploads
- help: Skip re-uploads while reissuing Api messages (BROKEN)
- - play:
- long: play
- help: Play entire recording through, then quit (useful with --save)
- INPUT:
help: The input binary file or directory
required: true
index: 1
- reftest:
about: run reftests
args:
- fuzz_tolerance:
@@ -148,16 +149,8 @@ subcommands:
- first_filename:
help: first benchmark file to compare
required: true
index: 1
- second_filename:
help: second benchmark file to compare
required: true
index: 2
- - load:
- about: load a capture
- args:
- - path:
- help: directory containing the capture
- takes_value: true
- required: true
- index: 1
--- a/gfx/wrench/src/main.rs
+++ b/gfx/wrench/src/main.rs
@@ -358,16 +358,41 @@ impl RenderNotifier for Notifier {
}
}
fn create_notifier() -> (Box<RenderNotifier>, Receiver<NotifierEvent>) {
let (tx, rx) = channel();
(Box::new(Notifier { tx: tx }), rx)
}
+fn rawtest(mut wrench: Wrench, window: &mut WindowWrapper, rx: Receiver<NotifierEvent>) {
+ RawtestHarness::new(&mut wrench, window, &rx).run();
+ wrench.shut_down(rx);
+}
+
+fn reftest<'a>(
+ mut wrench: Wrench,
+ window: &mut WindowWrapper,
+ subargs: &clap::ArgMatches<'a>,
+ rx: Receiver<NotifierEvent>
+) -> usize {
+ let dim = window.get_inner_size();
+ let base_manifest = Path::new("reftests/reftest.list");
+ let specific_reftest = subargs.value_of("REFTEST").map(|x| Path::new(x));
+ let mut reftest_options = ReftestOptions::default();
+ if let Some(allow_max_diff) = subargs.value_of("fuzz_tolerance") {
+ reftest_options.allow_max_difference = allow_max_diff.parse().unwrap_or(1);
+ reftest_options.allow_num_differences = dim.width as usize * dim.height as usize;
+ }
+ let num_failures = ReftestHarness::new(&mut wrench, window, &rx)
+ .run(base_manifest, specific_reftest, &reftest_options);
+ wrench.shut_down(rx);
+ num_failures
+}
+
fn main() {
#[cfg(feature = "env_logger")]
env_logger::init();
let args_yaml = load_yaml!("args.yaml");
let args = clap::App::from_yaml(args_yaml)
.setting(clap::AppSettings::ArgRequiredElseHelp)
.get_matches();
@@ -435,87 +460,92 @@ fn main() {
args.is_present("no_scissor"),
args.is_present("no_batch"),
args.is_present("precache"),
args.is_present("slow_subpixel"),
zoom_factor.unwrap_or(1.0),
notifier,
);
- let mut thing = if let Some(subargs) = args.subcommand_matches("show") {
- Box::new(YamlFrameReader::new_from_args(subargs)) as Box<WrenchThing>
- } else if let Some(subargs) = args.subcommand_matches("replay") {
- Box::new(BinaryFrameReader::new_from_args(subargs)) as Box<WrenchThing>
+ if let Some(subargs) = args.subcommand_matches("show") {
+ render(&mut wrench, &mut window, size, &mut events_loop, subargs);
} else if let Some(subargs) = args.subcommand_matches("png") {
let surface = match subargs.value_of("surface") {
Some("screen") | None => png::ReadSurface::Screen,
Some("gpu-cache") => png::ReadSurface::GpuCache,
_ => panic!("Unknown surface argument value")
};
let reader = YamlFrameReader::new_from_args(subargs);
png::png(&mut wrench, surface, &mut window, reader, rx.unwrap());
- wrench.renderer.deinit();
- return;
} else if let Some(subargs) = args.subcommand_matches("reftest") {
- let dim = window.get_inner_size();
- let base_manifest = Path::new("reftests/reftest.list");
- let specific_reftest = subargs.value_of("REFTEST").map(|x| Path::new(x));
- let mut reftest_options = ReftestOptions::default();
- if let Some(allow_max_diff) = subargs.value_of("fuzz_tolerance") {
- reftest_options.allow_max_difference = allow_max_diff.parse().unwrap_or(1);
- reftest_options.allow_num_differences = dim.width as usize * dim.height as usize;
- }
- let rx = rx.unwrap();
- let num_failures = ReftestHarness::new(&mut wrench, &mut window, &rx)
- .run(base_manifest, specific_reftest, &reftest_options);
- wrench.shut_down(rx);
- // exit with an error code to fail on CI
- process::exit(num_failures as _);
+ // Exit with an error code in order to ensure the CI job fails.
+ process::exit(reftest(wrench, &mut window, subargs, rx.unwrap()) as _);
} else if let Some(_) = args.subcommand_matches("rawtest") {
- let rx = rx.unwrap();
- {
- let harness = RawtestHarness::new(&mut wrench, &mut window, &rx);
- harness.run();
- }
- wrench.shut_down(rx);
+ rawtest(wrench, &mut window, rx.unwrap());
return;
} else if let Some(subargs) = args.subcommand_matches("perf") {
// Perf mode wants to benchmark the total cost of drawing
// a new displaty list each frame.
wrench.rebuild_display_lists = true;
let harness = PerfHarness::new(&mut wrench, &mut window, rx.unwrap());
let base_manifest = Path::new("benchmarks/benchmarks.list");
let filename = subargs.value_of("filename").unwrap();
harness.run(base_manifest, filename);
return;
} else if let Some(subargs) = args.subcommand_matches("compare_perf") {
let first_filename = subargs.value_of("first_filename").unwrap();
let second_filename = subargs.value_of("second_filename").unwrap();
perf::compare(first_filename, second_filename);
return;
- } else if let Some(subargs) = args.subcommand_matches("load") {
- let path = PathBuf::from(subargs.value_of("path").unwrap());
- let mut documents = wrench.api.load_capture(path);
+ } else {
+ panic!("Should never have gotten here! {:?}", args);
+ };
+
+ wrench.renderer.deinit();
+}
+
+fn render<'a>(
+ wrench: &mut Wrench,
+ window: &mut WindowWrapper,
+ size: DeviceUintSize,
+ events_loop: &mut Option<winit::EventsLoop>,
+ subargs: &clap::ArgMatches<'a>,
+) {
+ let input_path = subargs.value_of("INPUT").map(PathBuf::from).unwrap();
+
+ // If the input is a directory, we are looking at a capture.
+ let mut thing = if input_path.as_path().is_dir() {
+ let mut documents = wrench.api.load_capture(input_path);
println!("loaded {:?}", documents.iter().map(|cd| cd.document_id).collect::<Vec<_>>());
let captured = documents.swap_remove(0);
window.resize(captured.window_size);
wrench.document_id = captured.document_id;
Box::new(captured) as Box<WrenchThing>
} else {
- panic!("Should never have gotten here! {:?}", args);
+ let extension = input_path
+ .extension()
+ .expect("Tried to render with an unknown file type.")
+ .to_str()
+ .expect("Tried to render with an unknown file type.");
+
+ match extension {
+ "yaml" => Box::new(YamlFrameReader::new_from_args(subargs)) as Box<WrenchThing>,
+ "bin" => Box::new(BinaryFrameReader::new_from_args(subargs)) as Box<WrenchThing>,
+ _ => panic!("Tried to render with an unknown file type."),
+ }
};
let mut show_help = false;
let mut do_loop = false;
let mut cpu_profile_index = 0;
let mut cursor_position = WorldPoint::zero();
let dim = window.get_inner_size();
wrench.update(dim);
- thing.do_frame(&mut wrench);
+ thing.do_frame(wrench);
let mut body = |wrench: &mut Wrench, global_event: winit::Event| {
if let Some(window_title) = wrench.take_title() {
if !cfg!(windows) { //TODO: calling `set_title` from inside the `run_forever` loop is illegal...
window.set_title(&window_title);
}
}
@@ -660,22 +690,18 @@ fn main() {
if do_loop {
thing.next_frame();
}
}
winit::ControlFlow::Continue
};
- match events_loop {
+ match *events_loop {
None => {
- while body(&mut wrench, winit::Event::Awakened) == winit::ControlFlow::Continue {}
+ while body(wrench, winit::Event::Awakened) == winit::ControlFlow::Continue {}
let rect = DeviceUintRect::new(DeviceUintPoint::zero(), size);
let pixels = wrench.renderer.read_pixels_rgba8(rect);
save_flipped("screenshot.png", pixels, size);
}
- Some(ref mut events_loop) => {
- events_loop.run_forever(|event| body(&mut wrench, event));
- }
+ Some(ref mut events_loop) => events_loop.run_forever(|event| body(wrench, event)),
}
-
- wrench.renderer.deinit();
}