mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Vendor the current version of WebRender
This is a step toward upgrading WebRender, which will be upgraded and patched in the `third_party` directory. This change vendors the current private branch of WebRender that we use and adds a `patches` directory which tracks the changes on top of the upstream WebRender commit described by third_party/webrender/patches/head.
This commit is contained in:
parent
c19eb800de
commit
49277f5c3f
1215 changed files with 185677 additions and 34 deletions
71
third_party/webrender/examples/Cargo.toml
vendored
Normal file
71
third_party/webrender/examples/Cargo.toml
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
[package]
|
||||
name = "webrender-examples"
|
||||
version = "0.1.0"
|
||||
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
|
||||
license = "MPL-2.0"
|
||||
repository = "https://github.com/servo/webrender"
|
||||
edition = "2018"
|
||||
|
||||
[[bin]]
|
||||
name = "alpha_perf"
|
||||
path = "alpha_perf.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "animation"
|
||||
path = "animation.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "basic"
|
||||
path = "basic.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "blob"
|
||||
path = "blob.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "document"
|
||||
path = "document.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "frame_output"
|
||||
path = "frame_output.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "iframe"
|
||||
path = "iframe.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "image_resize"
|
||||
path = "image_resize.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "multiwindow"
|
||||
path = "multiwindow.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "scrolling"
|
||||
path = "scrolling.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "texture_cache_stress"
|
||||
path = "texture_cache_stress.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "yuv"
|
||||
path = "yuv.rs"
|
||||
|
||||
[features]
|
||||
debug = ["webrender/capture", "webrender/debugger", "webrender/profiler"]
|
||||
|
||||
[dependencies]
|
||||
app_units = "0.7"
|
||||
env_logger = "0.5"
|
||||
euclid = "0.22"
|
||||
gleam = "0.12"
|
||||
glutin = "0.21"
|
||||
rayon = "1"
|
||||
webrender = { path = "../webrender" }
|
||||
winit = "0.19"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-foundation = "0.7"
|
8
third_party/webrender/examples/README.md
vendored
Normal file
8
third_party/webrender/examples/README.md
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Examples
|
||||
|
||||
This directory contains a collection of examples which uses the WebRender API.
|
||||
|
||||
To run an example e.g. `basic`, try:
|
||||
```
|
||||
cargo run --bin basic
|
||||
```
|
93
third_party/webrender/examples/alpha_perf.rs
vendored
Normal file
93
third_party/webrender/examples/alpha_perf.rs
vendored
Normal file
|
@ -0,0 +1,93 @@
|
|||
/* 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 crate::boilerplate::{Example, HandyDandyRectBuilder};
|
||||
use std::cmp;
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::DeviceIntSize;
|
||||
|
||||
|
||||
struct App {
|
||||
rect_count: usize,
|
||||
}
|
||||
|
||||
impl Example for App {
|
||||
fn render(
|
||||
&mut self,
|
||||
_api: &mut RenderApi,
|
||||
builder: &mut DisplayListBuilder,
|
||||
_txn: &mut Transaction,
|
||||
_device_size: DeviceIntSize,
|
||||
pipeline_id: PipelineId,
|
||||
_document_id: DocumentId,
|
||||
) {
|
||||
let bounds = (0, 0).to(1920, 1080);
|
||||
let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
|
||||
|
||||
builder.push_simple_stacking_context(
|
||||
bounds.origin,
|
||||
space_and_clip.spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
|
||||
for _ in 0 .. self.rect_count {
|
||||
builder.push_rect(
|
||||
&CommonItemProperties::new(bounds, space_and_clip),
|
||||
bounds,
|
||||
ColorF::new(1.0, 1.0, 1.0, 0.05)
|
||||
);
|
||||
}
|
||||
|
||||
builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: winit::WindowEvent,
|
||||
_api: &mut 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);
|
||||
}
|
219
third_party/webrender/examples/animation.rs
vendored
Normal file
219
third_party/webrender/examples/animation.rs
vendored
Normal file
|
@ -0,0 +1,219 @@
|
|||
/* 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 crate::boilerplate::{Example, HandyDandyRectBuilder};
|
||||
use euclid::Angle;
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::*;
|
||||
|
||||
|
||||
struct App {
|
||||
property_key0: PropertyBindingKey<LayoutTransform>,
|
||||
property_key1: PropertyBindingKey<LayoutTransform>,
|
||||
property_key2: PropertyBindingKey<LayoutTransform>,
|
||||
opacity_key: PropertyBindingKey<f32>,
|
||||
opacity: f32,
|
||||
angle0: f32,
|
||||
angle1: f32,
|
||||
angle2: f32,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn add_rounded_rect(
|
||||
&mut self,
|
||||
bounds: LayoutRect,
|
||||
color: ColorF,
|
||||
builder: &mut DisplayListBuilder,
|
||||
pipeline_id: PipelineId,
|
||||
property_key: PropertyBindingKey<LayoutTransform>,
|
||||
opacity_key: Option<PropertyBindingKey<f32>>,
|
||||
) {
|
||||
let filters = match opacity_key {
|
||||
Some(opacity_key) => {
|
||||
vec![
|
||||
FilterOp::Opacity(PropertyBinding::Binding(opacity_key, self.opacity), self.opacity),
|
||||
]
|
||||
}
|
||||
None => {
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
|
||||
let spatial_id = builder.push_reference_frame(
|
||||
bounds.origin,
|
||||
SpatialId::root_scroll_node(pipeline_id),
|
||||
TransformStyle::Flat,
|
||||
PropertyBinding::Binding(property_key, LayoutTransform::identity()),
|
||||
ReferenceFrameKind::Transform,
|
||||
);
|
||||
|
||||
builder.push_simple_stacking_context_with_filters(
|
||||
LayoutPoint::zero(),
|
||||
spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
&filters,
|
||||
&[],
|
||||
&[]
|
||||
);
|
||||
|
||||
let space_and_clip = SpaceAndClipInfo {
|
||||
spatial_id,
|
||||
clip_id: ClipId::root(pipeline_id),
|
||||
};
|
||||
let clip_bounds = LayoutRect::new(LayoutPoint::zero(), bounds.size);
|
||||
let complex_clip = ComplexClipRegion {
|
||||
rect: clip_bounds,
|
||||
radii: BorderRadius::uniform(30.0),
|
||||
mode: ClipMode::Clip,
|
||||
};
|
||||
let clip_id = builder.define_clip_rounded_rect(
|
||||
&space_and_clip,
|
||||
complex_clip,
|
||||
);
|
||||
|
||||
// Fill it with a white rect
|
||||
builder.push_rect(
|
||||
&CommonItemProperties::new(
|
||||
LayoutRect::new(LayoutPoint::zero(), bounds.size),
|
||||
SpaceAndClipInfo {
|
||||
spatial_id,
|
||||
clip_id,
|
||||
}
|
||||
),
|
||||
LayoutRect::new(LayoutPoint::zero(), bounds.size),
|
||||
color,
|
||||
);
|
||||
|
||||
builder.pop_stacking_context();
|
||||
builder.pop_reference_frame();
|
||||
}
|
||||
}
|
||||
|
||||
impl Example for App {
|
||||
const WIDTH: u32 = 2048;
|
||||
const HEIGHT: u32 = 1536;
|
||||
|
||||
fn render(
|
||||
&mut self,
|
||||
_api: &mut RenderApi,
|
||||
builder: &mut DisplayListBuilder,
|
||||
_txn: &mut Transaction,
|
||||
_device_size: DeviceIntSize,
|
||||
pipeline_id: PipelineId,
|
||||
_document_id: DocumentId,
|
||||
) {
|
||||
let opacity_key = self.opacity_key;
|
||||
|
||||
let bounds = (150, 150).to(250, 250);
|
||||
let key0 = self.property_key0;
|
||||
self.add_rounded_rect(bounds, ColorF::new(1.0, 0.0, 0.0, 0.5), builder, pipeline_id, key0, Some(opacity_key));
|
||||
|
||||
let bounds = (400, 400).to(600, 600);
|
||||
let key1 = self.property_key1;
|
||||
self.add_rounded_rect(bounds, ColorF::new(0.0, 1.0, 0.0, 0.5), builder, pipeline_id, key1, None);
|
||||
|
||||
let bounds = (200, 500).to(350, 580);
|
||||
let key2 = self.property_key2;
|
||||
self.add_rounded_rect(bounds, ColorF::new(0.0, 0.0, 1.0, 0.5), builder, pipeline_id, key2, None);
|
||||
}
|
||||
|
||||
fn on_event(&mut self, win_event: winit::WindowEvent, api: &mut RenderApi, document_id: DocumentId) -> bool {
|
||||
let mut rebuild_display_list = false;
|
||||
|
||||
match win_event {
|
||||
winit::WindowEvent::KeyboardInput {
|
||||
input: winit::KeyboardInput {
|
||||
state: winit::ElementState::Pressed,
|
||||
virtual_keycode: Some(key),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
let (delta_angle, delta_opacity) = match key {
|
||||
winit::VirtualKeyCode::Down => (0.0, -0.1),
|
||||
winit::VirtualKeyCode::Up => (0.0, 0.1),
|
||||
winit::VirtualKeyCode::Right => (1.0, 0.0),
|
||||
winit::VirtualKeyCode::Left => (-1.0, 0.0),
|
||||
winit::VirtualKeyCode::R => {
|
||||
rebuild_display_list = true;
|
||||
(0.0, 0.0)
|
||||
}
|
||||
_ => 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;
|
||||
self.angle0 += delta_angle * 0.1;
|
||||
self.angle1 += delta_angle * 0.2;
|
||||
self.angle2 -= delta_angle * 0.15;
|
||||
let xf0 = LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::radians(self.angle0));
|
||||
let xf1 = LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::radians(self.angle1));
|
||||
let xf2 = LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::radians(self.angle2));
|
||||
let mut txn = Transaction::new();
|
||||
txn.update_dynamic_properties(
|
||||
DynamicProperties {
|
||||
transforms: vec![
|
||||
PropertyValue {
|
||||
key: self.property_key0,
|
||||
value: xf0,
|
||||
},
|
||||
PropertyValue {
|
||||
key: self.property_key1,
|
||||
value: xf1,
|
||||
},
|
||||
PropertyValue {
|
||||
key: self.property_key2,
|
||||
value: xf2,
|
||||
},
|
||||
],
|
||||
floats: vec![
|
||||
PropertyValue {
|
||||
key: self.opacity_key,
|
||||
value: self.opacity,
|
||||
}
|
||||
],
|
||||
colors: vec![],
|
||||
},
|
||||
);
|
||||
txn.generate_frame();
|
||||
api.send_transaction(document_id, txn);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
rebuild_display_list
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut app = App {
|
||||
property_key0: PropertyBindingKey::new(42), // arbitrary magic number
|
||||
property_key1: PropertyBindingKey::new(44), // arbitrary magic number
|
||||
property_key2: PropertyBindingKey::new(45), // arbitrary magic number
|
||||
opacity_key: PropertyBindingKey::new(43),
|
||||
opacity: 0.5,
|
||||
angle0: 0.0,
|
||||
angle1: 0.0,
|
||||
angle2: 0.0,
|
||||
};
|
||||
boilerplate::main_wrapper(&mut app, None);
|
||||
}
|
321
third_party/webrender/examples/basic.rs
vendored
Normal file
321
third_party/webrender/examples/basic.rs
vendored
Normal file
|
@ -0,0 +1,321 @@
|
|||
/* 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 crate::boilerplate::{Example, HandyDandyRectBuilder};
|
||||
use euclid::vec2;
|
||||
use winit::TouchPhase;
|
||||
use std::collections::HashMap;
|
||||
use webrender::ShaderPrecacheFlags;
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::*;
|
||||
|
||||
|
||||
#[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.x as f32,
|
||||
start_y: touch.location.y as f32,
|
||||
current_x: touch.location.x as f32,
|
||||
current_y: touch.location.y 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.x as f32;
|
||||
active_touch.current_y = touch.location.y 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_SHADER_FLAGS: ShaderPrecacheFlags = ShaderPrecacheFlags::FULL_COMPILE;
|
||||
|
||||
fn render(
|
||||
&mut self,
|
||||
api: &mut RenderApi,
|
||||
builder: &mut DisplayListBuilder,
|
||||
txn: &mut Transaction,
|
||||
_: DeviceIntSize,
|
||||
pipeline_id: PipelineId,
|
||||
_document_id: DocumentId,
|
||||
) {
|
||||
let content_bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
|
||||
let root_space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
|
||||
let spatial_id = root_space_and_clip.spatial_id;
|
||||
|
||||
builder.push_simple_stacking_context(
|
||||
content_bounds.origin,
|
||||
spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
|
||||
let image_mask_key = api.generate_image_key();
|
||||
txn.add_image(
|
||||
image_mask_key,
|
||||
ImageDescriptor::new(2, 2, ImageFormat::R8, ImageDescriptorFlags::IS_OPAQUE),
|
||||
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 mask_clip_id = builder.define_clip_image_mask(
|
||||
&root_space_and_clip,
|
||||
mask,
|
||||
);
|
||||
let clip_id = builder.define_clip_rounded_rect(
|
||||
&SpaceAndClipInfo {
|
||||
spatial_id: root_space_and_clip.spatial_id,
|
||||
clip_id: mask_clip_id,
|
||||
},
|
||||
complex,
|
||||
);
|
||||
|
||||
builder.push_rect(
|
||||
&CommonItemProperties::new(
|
||||
(100, 100).to(200, 200),
|
||||
SpaceAndClipInfo { spatial_id, clip_id },
|
||||
),
|
||||
(100, 100).to(200, 200),
|
||||
ColorF::new(0.0, 1.0, 0.0, 1.0),
|
||||
);
|
||||
|
||||
builder.push_rect(
|
||||
&CommonItemProperties::new(
|
||||
(250, 100).to(350, 200),
|
||||
SpaceAndClipInfo { spatial_id, clip_id },
|
||||
),
|
||||
(250, 100).to(350, 200),
|
||||
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 = LayoutSideOffsets::new_all_same(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),
|
||||
do_aa: true,
|
||||
});
|
||||
|
||||
let bounds = (100, 100).to(200, 200);
|
||||
builder.push_border(
|
||||
&CommonItemProperties::new(
|
||||
bounds,
|
||||
SpaceAndClipInfo { spatial_id, clip_id },
|
||||
),
|
||||
bounds,
|
||||
border_widths,
|
||||
border_details,
|
||||
);
|
||||
|
||||
if false {
|
||||
// draw box shadow?
|
||||
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;
|
||||
|
||||
builder.push_box_shadow(
|
||||
&CommonItemProperties::new(content_bounds, root_space_and_clip),
|
||||
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: &mut 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
|
||||
}
|
||||
}
|
289
third_party/webrender/examples/blob.rs
vendored
Normal file
289
third_party/webrender/examples/blob.rs
vendored
Normal file
|
@ -0,0 +1,289 @@
|
|||
/* 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 crate::boilerplate::{Example, HandyDandyRectBuilder};
|
||||
use rayon::{ThreadPool, ThreadPoolBuilder};
|
||||
use rayon::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, PrimitiveFlags, RenderApi, Transaction};
|
||||
use webrender::api::{ColorF, CommonItemProperties, SpaceAndClipInfo, ImageDescriptorFlags};
|
||||
use webrender::api::units::*;
|
||||
use webrender::euclid::size2;
|
||||
|
||||
// This example shows how to implement a very basic BlobImageHandler 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) -> Arc<Vec<u8>> {
|
||||
Arc::new(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: TileOffset,
|
||||
) -> api::BlobImageResult {
|
||||
let color = *commands;
|
||||
|
||||
// Note: This implementation ignores the dirty rect which isn't incorrect
|
||||
// but is a missed optimization.
|
||||
|
||||
// Allocate storage for the result. Right now the resource cache expects the
|
||||
// tiles to have have no stride or offset.
|
||||
let bpp = 4;
|
||||
let mut texels = Vec::with_capacity((descriptor.rect.size.area() * bpp) 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 = (tile.x % 2 == 0) != (tile.y % 2 == 0);
|
||||
|
||||
let [w, h] = descriptor.rect.size.to_array();
|
||||
let offset = descriptor.rect.origin;
|
||||
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
// 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 + offset.x;
|
||||
let y2 = y + offset.y;
|
||||
|
||||
// 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: Arc::new(texels),
|
||||
rasterized_rect: size2(w, h).into(),
|
||||
})
|
||||
}
|
||||
|
||||
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 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::BlobImageKey, Arc<ImageRenderingCommands>>,
|
||||
}
|
||||
|
||||
impl CheckerboardRenderer {
|
||||
fn new(workers: Arc<ThreadPool>) -> Self {
|
||||
CheckerboardRenderer {
|
||||
image_cmds: HashMap::new(),
|
||||
workers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl api::BlobImageHandler for CheckerboardRenderer {
|
||||
fn create_similar(&self) -> Box<dyn api::BlobImageHandler> {
|
||||
Box::new(CheckerboardRenderer::new(Arc::clone(&self.workers)))
|
||||
}
|
||||
|
||||
fn add(&mut self, key: api::BlobImageKey, cmds: Arc<api::BlobImageData>,
|
||||
_visible_rect: &DeviceIntRect, _: api::TileSize) {
|
||||
self.image_cmds
|
||||
.insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
|
||||
}
|
||||
|
||||
fn update(&mut self, key: api::BlobImageKey, cmds: Arc<api::BlobImageData>,
|
||||
_visible_rect: &DeviceIntRect, _dirty_rect: &BlobDirtyRect) {
|
||||
// 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::BlobImageKey) {
|
||||
self.image_cmds.remove(&key);
|
||||
}
|
||||
|
||||
fn prepare_resources(
|
||||
&mut self,
|
||||
_services: &dyn api::BlobImageResources,
|
||||
_requests: &[api::BlobImageParams],
|
||||
) {}
|
||||
|
||||
fn enable_multithreading(&mut self, _: bool) {}
|
||||
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) {}
|
||||
fn create_blob_rasterizer(&mut self) -> Box<dyn api::AsyncBlobImageRasterizer> {
|
||||
Box::new(Rasterizer {
|
||||
workers: Arc::clone(&self.workers),
|
||||
image_cmds: self.image_cmds.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct Rasterizer {
|
||||
workers: Arc<ThreadPool>,
|
||||
image_cmds: HashMap<api::BlobImageKey, Arc<ImageRenderingCommands>>,
|
||||
}
|
||||
|
||||
impl api::AsyncBlobImageRasterizer for Rasterizer {
|
||||
fn rasterize(
|
||||
&mut self,
|
||||
requests: &[api::BlobImageParams],
|
||||
_low_priority: bool
|
||||
) -> Vec<(api::BlobImageRequest, api::BlobImageResult)> {
|
||||
let requests: Vec<(&api::BlobImageParams, Arc<ImageRenderingCommands>)> = requests.into_iter().map(|params| {
|
||||
(params, Arc::clone(&self.image_cmds[¶ms.request.key]))
|
||||
}).collect();
|
||||
|
||||
self.workers.install(|| {
|
||||
requests.into_par_iter().map(|(params, commands)| {
|
||||
(params.request, render_blob(commands, ¶ms.descriptor, params.request.tile))
|
||||
}).collect()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct App {}
|
||||
|
||||
impl Example for App {
|
||||
fn render(
|
||||
&mut self,
|
||||
api: &mut RenderApi,
|
||||
builder: &mut DisplayListBuilder,
|
||||
txn: &mut Transaction,
|
||||
_device_size: DeviceIntSize,
|
||||
pipeline_id: PipelineId,
|
||||
_document_id: DocumentId,
|
||||
) {
|
||||
let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
|
||||
|
||||
builder.push_simple_stacking_context(
|
||||
LayoutPoint::zero(),
|
||||
space_and_clip.spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
|
||||
let size1 = DeviceIntSize::new(500, 500);
|
||||
let blob_img1 = api.generate_blob_image_key();
|
||||
txn.add_blob_image(
|
||||
blob_img1,
|
||||
api::ImageDescriptor::new(
|
||||
size1.width,
|
||||
size1.height,
|
||||
api::ImageFormat::BGRA8,
|
||||
ImageDescriptorFlags::IS_OPAQUE,
|
||||
),
|
||||
serialize_blob(api::ColorU::new(50, 50, 150, 255)),
|
||||
size1.into(),
|
||||
Some(128),
|
||||
);
|
||||
let bounds = (30, 30).by(size1.width, size1.height);
|
||||
builder.push_image(
|
||||
&CommonItemProperties::new(bounds, space_and_clip),
|
||||
bounds,
|
||||
api::ImageRendering::Auto,
|
||||
api::AlphaType::PremultipliedAlpha,
|
||||
blob_img1.as_image(),
|
||||
ColorF::WHITE,
|
||||
);
|
||||
|
||||
let size2 = DeviceIntSize::new(256, 256);
|
||||
let blob_img2 = api.generate_blob_image_key();
|
||||
txn.add_blob_image(
|
||||
blob_img2,
|
||||
api::ImageDescriptor::new(
|
||||
size2.width,
|
||||
size2.height,
|
||||
api::ImageFormat::BGRA8,
|
||||
ImageDescriptorFlags::IS_OPAQUE,
|
||||
),
|
||||
serialize_blob(api::ColorU::new(50, 150, 50, 255)),
|
||||
size2.into(),
|
||||
None,
|
||||
);
|
||||
let bounds = (600, 600).by(size2.width, size2.height);
|
||||
builder.push_image(
|
||||
&CommonItemProperties::new(bounds, space_and_clip),
|
||||
bounds,
|
||||
api::ImageRendering::Auto,
|
||||
api::AlphaType::PremultipliedAlpha,
|
||||
blob_img2.as_image(),
|
||||
ColorF::WHITE,
|
||||
);
|
||||
|
||||
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_handler: Some(Box::new(CheckerboardRenderer::new(Arc::clone(&workers)))),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut app = App {};
|
||||
|
||||
boilerplate::main_wrapper(&mut app, Some(opts));
|
||||
}
|
338
third_party/webrender/examples/common/boilerplate.rs
vendored
Normal file
338
third_party/webrender/examples/common/boilerplate.rs
vendored
Normal file
|
@ -0,0 +1,338 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use gleam::gl;
|
||||
use glutin;
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use webrender;
|
||||
use winit;
|
||||
use webrender::{DebugFlags, ShaderPrecacheFlags};
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::*;
|
||||
|
||||
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<dyn 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,
|
||||
_render_time: Option<u64>) {
|
||||
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_SHADER_FLAGS: ShaderPrecacheFlags = ShaderPrecacheFlags::EMPTY;
|
||||
const WIDTH: u32 = 1920;
|
||||
const HEIGHT: u32 = 1080;
|
||||
|
||||
fn render(
|
||||
&mut self,
|
||||
api: &mut RenderApi,
|
||||
builder: &mut DisplayListBuilder,
|
||||
txn: &mut Transaction,
|
||||
device_size: DeviceIntSize,
|
||||
pipeline_id: PipelineId,
|
||||
document_id: DocumentId,
|
||||
);
|
||||
fn on_event(
|
||||
&mut self,
|
||||
_: winit::WindowEvent,
|
||||
_: &mut RenderApi,
|
||||
_: DocumentId,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
fn get_image_handlers(
|
||||
&mut self,
|
||||
_gl: &dyn gl::Gl,
|
||||
) -> (Option<Box<dyn ExternalImageHandler>>,
|
||||
Option<Box<dyn OutputImageHandler>>) {
|
||||
(None, None)
|
||||
}
|
||||
fn draw_custom(&mut self, _gl: &dyn gl::Gl) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main_wrapper<E: Example>(
|
||||
example: &mut E,
|
||||
options: Option<webrender::RendererOptions>,
|
||||
) {
|
||||
env_logger::init();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
use core_foundation::{self as cf, base::TCFType};
|
||||
let i = cf::bundle::CFBundle::main_bundle().info_dictionary();
|
||||
let mut i = unsafe { i.to_mutable() };
|
||||
i.set(
|
||||
cf::string::CFString::new("NSSupportsAutomaticGraphicsSwitching"),
|
||||
cf::boolean::CFBoolean::true_value().into_CFType(),
|
||||
);
|
||||
}
|
||||
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let res_path = if args.len() > 1 {
|
||||
Some(PathBuf::from(&args[1]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
let window_builder = winit::WindowBuilder::new()
|
||||
.with_title(E::TITLE)
|
||||
.with_multitouch()
|
||||
.with_dimensions(winit::dpi::LogicalSize::new(E::WIDTH as f64, E::HEIGHT as f64));
|
||||
let windowed_context = glutin::ContextBuilder::new()
|
||||
.with_gl(glutin::GlRequest::GlThenGles {
|
||||
opengl_version: (3, 2),
|
||||
opengles_version: (3, 0),
|
||||
})
|
||||
.build_windowed(window_builder, &events_loop)
|
||||
.unwrap();
|
||||
|
||||
let windowed_context = unsafe { windowed_context.make_current().unwrap() };
|
||||
|
||||
let gl = match windowed_context.get_api() {
|
||||
glutin::Api::OpenGl => unsafe {
|
||||
gl::GlFns::load_with(
|
||||
|symbol| windowed_context.get_proc_address(symbol) as *const _
|
||||
)
|
||||
},
|
||||
glutin::Api::OpenGlEs => unsafe {
|
||||
gl::GlesFns::load_with(
|
||||
|symbol| windowed_context.get_proc_address(symbol) as *const _
|
||||
)
|
||||
},
|
||||
glutin::Api::WebGl => unimplemented!(),
|
||||
};
|
||||
|
||||
println!("OpenGL version {}", gl.get_string(gl::VERSION));
|
||||
println!("Shader resource path: {:?}", res_path);
|
||||
let device_pixel_ratio = windowed_context.window().get_hidpi_factor() as f32;
|
||||
println!("Device pixel ratio: {}", device_pixel_ratio);
|
||||
|
||||
println!("Loading shaders...");
|
||||
let mut debug_flags = DebugFlags::ECHO_DRIVER_MESSAGES | DebugFlags::TEXTURE_CACHE_DBG;
|
||||
let opts = webrender::RendererOptions {
|
||||
resource_override_path: res_path,
|
||||
precache_flags: E::PRECACHE_SHADER_FLAGS,
|
||||
device_pixel_ratio,
|
||||
clear_color: Some(ColorF::new(0.3, 0.0, 0.0, 1.0)),
|
||||
debug_flags,
|
||||
//allow_texture_swizzling: false,
|
||||
..options.unwrap_or(webrender::RendererOptions::default())
|
||||
};
|
||||
|
||||
let device_size = {
|
||||
let size = windowed_context
|
||||
.window()
|
||||
.get_inner_size()
|
||||
.unwrap()
|
||||
.to_physical(device_pixel_ratio as f64);
|
||||
DeviceIntSize::new(size.width as i32, size.height as i32)
|
||||
};
|
||||
let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
|
||||
let (mut renderer, sender) = webrender::Renderer::new(
|
||||
gl.clone(),
|
||||
notifier,
|
||||
opts,
|
||||
None,
|
||||
device_size,
|
||||
).unwrap();
|
||||
let mut api = sender.create_api();
|
||||
let document_id = api.add_document(device_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 = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio);
|
||||
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
|
||||
let mut txn = Transaction::new();
|
||||
|
||||
example.render(
|
||||
&mut api,
|
||||
&mut builder,
|
||||
&mut txn,
|
||||
device_size,
|
||||
pipeline_id,
|
||||
document_id,
|
||||
);
|
||||
txn.set_display_list(
|
||||
epoch,
|
||||
Some(ColorF::new(0.3, 0.0, 0.0, 1.0)),
|
||||
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;
|
||||
|
||||
let old_flags = debug_flags;
|
||||
let win_event = match global_event {
|
||||
winit::Event::WindowEvent { event, .. } => event,
|
||||
_ => return winit::ControlFlow::Continue,
|
||||
};
|
||||
match win_event {
|
||||
winit::WindowEvent::CloseRequested => return winit::ControlFlow::Break,
|
||||
winit::WindowEvent::AxisMotion { .. } |
|
||||
winit::WindowEvent::CursorMoved { .. } => {
|
||||
custom_event = example.on_event(
|
||||
win_event,
|
||||
&mut api,
|
||||
document_id,
|
||||
);
|
||||
// skip high-frequency events from triggering a frame draw.
|
||||
if !custom_event {
|
||||
return winit::ControlFlow::Continue;
|
||||
}
|
||||
},
|
||||
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 => debug_flags.toggle(DebugFlags::PROFILER_DBG),
|
||||
winit::VirtualKeyCode::O => debug_flags.toggle(DebugFlags::RENDER_TARGET_DBG),
|
||||
winit::VirtualKeyCode::I => debug_flags.toggle(DebugFlags::TEXTURE_CACHE_DBG),
|
||||
winit::VirtualKeyCode::S => debug_flags.toggle(DebugFlags::COMPACT_PROFILER),
|
||||
winit::VirtualKeyCode::T => debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG),
|
||||
winit::VirtualKeyCode::Q => debug_flags.toggle(
|
||||
DebugFlags::GPU_TIME_QUERIES | DebugFlags::GPU_SAMPLE_QUERIES
|
||||
),
|
||||
winit::VirtualKeyCode::F => debug_flags.toggle(
|
||||
DebugFlags::NEW_FRAME_INDICATOR | DebugFlags::NEW_SCENE_INDICATOR
|
||||
),
|
||||
winit::VirtualKeyCode::G => debug_flags.toggle(DebugFlags::GPU_CACHE_DBG),
|
||||
winit::VirtualKeyCode::Key1 => txn.set_document_view(
|
||||
device_size.into(),
|
||||
1.0
|
||||
),
|
||||
winit::VirtualKeyCode::Key2 => txn.set_document_view(
|
||||
device_size.into(),
|
||||
2.0
|
||||
),
|
||||
winit::VirtualKeyCode::M => api.notify_memory_pressure(),
|
||||
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);
|
||||
},
|
||||
_ => {
|
||||
custom_event = example.on_event(
|
||||
win_event,
|
||||
&mut api,
|
||||
document_id,
|
||||
)
|
||||
},
|
||||
},
|
||||
other => custom_event = example.on_event(
|
||||
other,
|
||||
&mut api,
|
||||
document_id,
|
||||
),
|
||||
};
|
||||
|
||||
if debug_flags != old_flags {
|
||||
api.send_debug_cmd(DebugCommand::SetFlags(debug_flags));
|
||||
}
|
||||
|
||||
if custom_event {
|
||||
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
|
||||
|
||||
example.render(
|
||||
&mut api,
|
||||
&mut builder,
|
||||
&mut txn,
|
||||
device_size,
|
||||
pipeline_id,
|
||||
document_id,
|
||||
);
|
||||
txn.set_display_list(
|
||||
epoch,
|
||||
Some(ColorF::new(0.3, 0.0, 0.0, 1.0)),
|
||||
layout_size,
|
||||
builder.finalize(),
|
||||
true,
|
||||
);
|
||||
txn.generate_frame();
|
||||
}
|
||||
api.send_transaction(document_id, txn);
|
||||
|
||||
renderer.update();
|
||||
renderer.render(device_size).unwrap();
|
||||
let _ = renderer.flush_pipeline_info();
|
||||
example.draw_custom(&*gl);
|
||||
windowed_context.swap_buffers().ok();
|
||||
|
||||
winit::ControlFlow::Continue
|
||||
});
|
||||
|
||||
renderer.deinit();
|
||||
}
|
19
third_party/webrender/examples/common/image_helper.rs
vendored
Normal file
19
third_party/webrender/examples/common/image_helper.rs
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* 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, ImageDescriptorFlags};
|
||||
|
||||
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 as i32, height as i32, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
|
||||
ImageData::new(image_data)
|
||||
)
|
||||
}
|
149
third_party/webrender/examples/document.rs
vendored
Normal file
149
third_party/webrender/examples/document.rs
vendored
Normal file
|
@ -0,0 +1,149 @@
|
|||
/* 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 crate::boilerplate::Example;
|
||||
use euclid::Scale;
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::*;
|
||||
|
||||
// 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: &mut RenderApi,
|
||||
device_pixel_ratio: f32,
|
||||
) {
|
||||
let init_data = vec![
|
||||
(
|
||||
PipelineId(1, 0),
|
||||
-2,
|
||||
ColorF::new(0.0, 1.0, 0.0, 1.0),
|
||||
DeviceIntPoint::new(0, 0),
|
||||
),
|
||||
(
|
||||
PipelineId(2, 0),
|
||||
-1,
|
||||
ColorF::new(1.0, 1.0, 0.0, 1.0),
|
||||
DeviceIntPoint::new(200, 0),
|
||||
),
|
||||
(
|
||||
PipelineId(3, 0),
|
||||
0,
|
||||
ColorF::new(1.0, 0.0, 0.0, 1.0),
|
||||
DeviceIntPoint::new(200, 200),
|
||||
),
|
||||
(
|
||||
PipelineId(4, 0),
|
||||
1,
|
||||
ColorF::new(1.0, 0.0, 1.0, 1.0),
|
||||
DeviceIntPoint::new(0, 200),
|
||||
),
|
||||
];
|
||||
|
||||
for (pipeline_id, layer, color, offset) in init_data {
|
||||
let size = DeviceIntSize::new(250, 250);
|
||||
let bounds = DeviceIntRect::new(offset, size);
|
||||
|
||||
let document_id = api.add_document(size, layer);
|
||||
let mut txn = Transaction::new();
|
||||
txn.set_document_view(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: LayoutRect::new(
|
||||
LayoutPoint::origin(),
|
||||
bounds.size.to_f32() / Scale::new(device_pixel_ratio),
|
||||
),
|
||||
color,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Example for App {
|
||||
fn render(
|
||||
&mut self,
|
||||
api: &mut RenderApi,
|
||||
base_builder: &mut DisplayListBuilder,
|
||||
_txn: &mut Transaction,
|
||||
device_size: DeviceIntSize,
|
||||
_pipeline_id: PipelineId,
|
||||
_: DocumentId,
|
||||
) {
|
||||
if self.documents.is_empty() {
|
||||
let device_pixel_ratio = device_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, device_pixel_ratio);
|
||||
}
|
||||
|
||||
for doc in &self.documents {
|
||||
let space_and_clip = SpaceAndClipInfo::root_scroll(doc.pipeline_id);
|
||||
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_simple_stacking_context(
|
||||
doc.content_rect.origin,
|
||||
space_and_clip.spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
builder.push_rect(
|
||||
&CommonItemProperties::new(local_rect, space_and_clip),
|
||||
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);
|
||||
}
|
238
third_party/webrender/examples/frame_output.rs
vendored
Normal file
238
third_party/webrender/examples/frame_output.rs
vendored
Normal file
|
@ -0,0 +1,238 @@
|
|||
/* 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 crate::boilerplate::{Example, HandyDandyRectBuilder};
|
||||
use euclid::Scale;
|
||||
use gleam::gl;
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::*;
|
||||
|
||||
|
||||
// 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 OutputImageHandler for OutputHandler {
|
||||
fn lock(&mut self, _id: PipelineId) -> Option<(u32, FramebufferIntSize)> {
|
||||
Some((self.texture_id, FramebufferIntSize::new(500, 500)))
|
||||
}
|
||||
|
||||
fn unlock(&mut self, _id: PipelineId) {}
|
||||
}
|
||||
|
||||
impl ExternalImageHandler for ExternalHandler {
|
||||
fn lock(
|
||||
&mut self,
|
||||
_key: ExternalImageId,
|
||||
_channel_index: u8,
|
||||
_rendering: ImageRendering
|
||||
) -> ExternalImage {
|
||||
ExternalImage {
|
||||
uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
|
||||
source: ExternalImageSource::NativeTexture(self.texture_id),
|
||||
}
|
||||
}
|
||||
fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {}
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn init_output_document(
|
||||
&mut self,
|
||||
api: &mut RenderApi,
|
||||
device_size: DeviceIntSize,
|
||||
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 pipeline_id = PipelineId(1, 0);
|
||||
let layer = 1;
|
||||
let color = ColorF::new(1., 1., 0., 1.);
|
||||
let document_id = api.add_document(device_size, layer);
|
||||
api.enable_frame_output(document_id, pipeline_id, true);
|
||||
api.set_document_view(
|
||||
document_id,
|
||||
device_size.into(),
|
||||
device_pixel_ratio,
|
||||
);
|
||||
|
||||
let document = Document {
|
||||
id: document_id,
|
||||
pipeline_id,
|
||||
content_rect: LayoutRect::new(
|
||||
LayoutPoint::zero(),
|
||||
device_size.to_f32() / Scale::new(device_pixel_ratio),
|
||||
),
|
||||
color,
|
||||
};
|
||||
|
||||
let mut txn = Transaction::new();
|
||||
|
||||
txn.add_image(
|
||||
self.external_image_key.unwrap(),
|
||||
ImageDescriptor::new(100, 100, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
|
||||
ImageData::External(ExternalImageData {
|
||||
id: ExternalImageId(0),
|
||||
channel_index: 0,
|
||||
image_type: ExternalImageType::TextureHandle(TextureTarget::Default),
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
|
||||
let mut builder = DisplayListBuilder::new(
|
||||
document.pipeline_id,
|
||||
document.content_rect.size,
|
||||
);
|
||||
|
||||
builder.push_simple_stacking_context(
|
||||
document.content_rect.origin,
|
||||
space_and_clip.spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
|
||||
builder.push_rect(
|
||||
&CommonItemProperties::new(document.content_rect, space_and_clip),
|
||||
document.content_rect,
|
||||
ColorF::new(1.0, 1.0, 0.0, 1.0)
|
||||
);
|
||||
builder.pop_stacking_context();
|
||||
|
||||
txn.set_root_pipeline(pipeline_id);
|
||||
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: &mut RenderApi,
|
||||
builder: &mut DisplayListBuilder,
|
||||
_txn: &mut Transaction,
|
||||
device_size: DeviceIntSize,
|
||||
pipeline_id: PipelineId,
|
||||
_document_id: DocumentId,
|
||||
) {
|
||||
if self.output_document.is_none() {
|
||||
let device_pixel_ratio = device_size.width as f32 /
|
||||
builder.content_size().width;
|
||||
self.init_output_document(api, DeviceIntSize::new(200, 200), device_pixel_ratio);
|
||||
}
|
||||
|
||||
let bounds = (100, 100).to(200, 200);
|
||||
let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
|
||||
|
||||
builder.push_simple_stacking_context(
|
||||
bounds.origin,
|
||||
space_and_clip.spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
|
||||
builder.push_image(
|
||||
&CommonItemProperties::new(bounds, space_and_clip),
|
||||
bounds,
|
||||
ImageRendering::Auto,
|
||||
AlphaType::PremultipliedAlpha,
|
||||
self.external_image_key.unwrap(),
|
||||
ColorF::WHITE,
|
||||
);
|
||||
|
||||
builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
fn get_image_handlers(
|
||||
&mut self,
|
||||
gl: &dyn gl::Gl,
|
||||
) -> (Option<Box<dyn ExternalImageHandler>>,
|
||||
Option<Box<dyn 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);
|
||||
}
|
95
third_party/webrender/examples/iframe.rs
vendored
Normal file
95
third_party/webrender/examples/iframe.rs
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
/* 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 crate::boilerplate::{Example, HandyDandyRectBuilder};
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::*;
|
||||
|
||||
// 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: &mut RenderApi,
|
||||
builder: &mut DisplayListBuilder,
|
||||
_txn: &mut Transaction,
|
||||
_device_size: DeviceIntSize,
|
||||
pipeline_id: PipelineId,
|
||||
document_id: DocumentId,
|
||||
) {
|
||||
// All the sub_* things are for the nested pipeline
|
||||
let sub_size = DeviceIntSize::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 mut space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
|
||||
|
||||
sub_builder.push_simple_stacking_context(
|
||||
sub_bounds.origin,
|
||||
space_and_clip.spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
|
||||
// green rect visible == success
|
||||
sub_builder.push_rect(
|
||||
&CommonItemProperties::new(sub_bounds, space_and_clip),
|
||||
sub_bounds,
|
||||
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);
|
||||
|
||||
space_and_clip.spatial_id = builder.push_reference_frame(
|
||||
sub_bounds.origin,
|
||||
space_and_clip.spatial_id,
|
||||
TransformStyle::Flat,
|
||||
PropertyBinding::Binding(PropertyBindingKey::new(42), LayoutTransform::identity()),
|
||||
ReferenceFrameKind::Transform,
|
||||
);
|
||||
|
||||
// And this is for the root pipeline
|
||||
builder.push_simple_stacking_context(
|
||||
sub_bounds.origin,
|
||||
space_and_clip.spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
|
||||
// red rect under the iframe: if this is visible, things have gone wrong
|
||||
builder.push_rect(
|
||||
&CommonItemProperties::new(sub_bounds, space_and_clip),
|
||||
sub_bounds,
|
||||
ColorF::new(1.0, 0.0, 0.0, 1.0)
|
||||
);
|
||||
builder.push_iframe(sub_bounds, sub_bounds, &space_and_clip, sub_pipeline_id, false);
|
||||
builder.pop_stacking_context();
|
||||
builder.pop_reference_frame();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut app = App {};
|
||||
boilerplate::main_wrapper(&mut app, None);
|
||||
}
|
121
third_party/webrender/examples/image_resize.rs
vendored
Normal file
121
third_party/webrender/examples/image_resize.rs
vendored
Normal file
|
@ -0,0 +1,121 @@
|
|||
/* 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 crate::boilerplate::{Example, HandyDandyRectBuilder};
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::*;
|
||||
|
||||
struct App {
|
||||
image_key: ImageKey,
|
||||
}
|
||||
|
||||
impl Example for App {
|
||||
fn render(
|
||||
&mut self,
|
||||
_api: &mut RenderApi,
|
||||
builder: &mut DisplayListBuilder,
|
||||
txn: &mut Transaction,
|
||||
_device_size: DeviceIntSize,
|
||||
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 space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
|
||||
|
||||
builder.push_simple_stacking_context(
|
||||
bounds.origin,
|
||||
space_and_clip.spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
|
||||
let image_size = LayoutSize::new(100.0, 100.0);
|
||||
|
||||
builder.push_image(
|
||||
&CommonItemProperties::new(
|
||||
LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
|
||||
space_and_clip,
|
||||
),
|
||||
bounds,
|
||||
ImageRendering::Auto,
|
||||
AlphaType::PremultipliedAlpha,
|
||||
self.image_key,
|
||||
ColorF::WHITE,
|
||||
);
|
||||
|
||||
builder.push_image(
|
||||
&CommonItemProperties::new(
|
||||
LayoutRect::new(LayoutPoint::new(250.0, 100.0), image_size),
|
||||
space_and_clip,
|
||||
),
|
||||
bounds,
|
||||
ImageRendering::Pixelated,
|
||||
AlphaType::PremultipliedAlpha,
|
||||
self.image_key,
|
||||
ColorF::WHITE,
|
||||
);
|
||||
|
||||
builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
fn on_event(&mut self, event: winit::WindowEvent, api: &mut 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, ImageDescriptorFlags::IS_OPAQUE),
|
||||
ImageData::new(image_data),
|
||||
&DirtyRect::All,
|
||||
);
|
||||
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);
|
||||
}
|
326
third_party/webrender/examples/multiwindow.rs
vendored
Normal file
326
third_party/webrender/examples/multiwindow.rs
vendored
Normal file
|
@ -0,0 +1,326 @@
|
|||
/* 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;
|
||||
|
||||
use gleam::gl;
|
||||
use glutin::NotCurrent;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::*;
|
||||
use webrender::DebugFlags;
|
||||
use winit::dpi::LogicalSize;
|
||||
|
||||
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<dyn 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,
|
||||
_render_time: Option<u64>) {
|
||||
self.wake_up();
|
||||
}
|
||||
}
|
||||
|
||||
struct Window {
|
||||
events_loop: winit::EventsLoop, //TODO: share events loop?
|
||||
context: Option<glutin::WindowedContext<NotCurrent>>,
|
||||
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 window_builder = winit::WindowBuilder::new()
|
||||
.with_title(name)
|
||||
.with_multitouch()
|
||||
.with_dimensions(LogicalSize::new(800., 600.));
|
||||
let context = glutin::ContextBuilder::new()
|
||||
.with_gl(glutin::GlRequest::GlThenGles {
|
||||
opengl_version: (3, 2),
|
||||
opengles_version: (3, 0),
|
||||
})
|
||||
.build_windowed(window_builder, &events_loop)
|
||||
.unwrap();
|
||||
|
||||
let context = unsafe { context.make_current().unwrap() };
|
||||
|
||||
let gl = match context.get_api() {
|
||||
glutin::Api::OpenGl => unsafe {
|
||||
gl::GlFns::load_with(|symbol| context.get_proc_address(symbol) as *const _)
|
||||
},
|
||||
glutin::Api::OpenGlEs => unsafe {
|
||||
gl::GlesFns::load_with(|symbol| context.get_proc_address(symbol) as *const _)
|
||||
},
|
||||
glutin::Api::WebGl => unimplemented!(),
|
||||
};
|
||||
|
||||
let device_pixel_ratio = context.window().get_hidpi_factor() as f32;
|
||||
|
||||
let opts = webrender::RendererOptions {
|
||||
device_pixel_ratio,
|
||||
clear_color: Some(clear_color),
|
||||
..webrender::RendererOptions::default()
|
||||
};
|
||||
|
||||
let device_size = {
|
||||
let size = context
|
||||
.window()
|
||||
.get_inner_size()
|
||||
.unwrap()
|
||||
.to_physical(device_pixel_ratio as f64);
|
||||
DeviceIntSize::new(size.width as i32, size.height as i32)
|
||||
};
|
||||
let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
|
||||
let (renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts, None, device_size).unwrap();
|
||||
let mut api = sender.create_api();
|
||||
let document_id = api.add_document(device_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, 32.0, None, None, Vec::new());
|
||||
|
||||
api.send_transaction(document_id, txn);
|
||||
|
||||
Window {
|
||||
events_loop,
|
||||
context: Some(unsafe { context.make_not_current().unwrap() }),
|
||||
renderer,
|
||||
name,
|
||||
epoch,
|
||||
pipeline_id,
|
||||
document_id,
|
||||
api,
|
||||
font_instance_key,
|
||||
}
|
||||
}
|
||||
|
||||
fn tick(&mut self) -> bool {
|
||||
let mut do_exit = false;
|
||||
let my_name = &self.name;
|
||||
let renderer = &mut self.renderer;
|
||||
let api = &mut self.api;
|
||||
|
||||
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!("set flags {}", my_name);
|
||||
api.send_debug_cmd(DebugCommand::SetFlags(DebugFlags::PROFILER_DBG))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
if do_exit {
|
||||
return true
|
||||
}
|
||||
|
||||
let context = unsafe { self.context.take().unwrap().make_current().unwrap() };
|
||||
let device_pixel_ratio = context.window().get_hidpi_factor() as f32;
|
||||
let device_size = {
|
||||
let size = context
|
||||
.window()
|
||||
.get_inner_size()
|
||||
.unwrap()
|
||||
.to_physical(device_pixel_ratio as f64);
|
||||
DeviceIntSize::new(size.width as i32, size.height as i32)
|
||||
};
|
||||
let layout_size = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio);
|
||||
let mut txn = Transaction::new();
|
||||
let mut builder = DisplayListBuilder::new(self.pipeline_id, layout_size);
|
||||
let space_and_clip = SpaceAndClipInfo::root_scroll(self.pipeline_id);
|
||||
|
||||
let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
|
||||
builder.push_simple_stacking_context(
|
||||
bounds.origin,
|
||||
space_and_clip.spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
|
||||
builder.push_rect(
|
||||
&CommonItemProperties::new(
|
||||
LayoutRect::new(
|
||||
LayoutPoint::new(100.0, 200.0),
|
||||
LayoutSize::new(100.0, 200.0),
|
||||
),
|
||||
space_and_clip,
|
||||
),
|
||||
LayoutRect::new(
|
||||
LayoutPoint::new(100.0, 200.0),
|
||||
LayoutSize::new(100.0, 200.0),
|
||||
),
|
||||
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),
|
||||
},
|
||||
];
|
||||
|
||||
builder.push_text(
|
||||
&CommonItemProperties::new(
|
||||
text_bounds,
|
||||
space_and_clip,
|
||||
),
|
||||
text_bounds,
|
||||
&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();
|
||||
api.send_transaction(self.document_id, txn);
|
||||
|
||||
renderer.update();
|
||||
renderer.render(device_size).unwrap();
|
||||
context.swap_buffers().ok();
|
||||
|
||||
self.context = Some(unsafe { context.make_not_current().unwrap() });
|
||||
|
||||
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
|
||||
}
|
231
third_party/webrender/examples/scrolling.rs
vendored
Normal file
231
third_party/webrender/examples/scrolling.rs
vendored
Normal file
|
@ -0,0 +1,231 @@
|
|||
/* 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 crate::boilerplate::{Example, HandyDandyRectBuilder};
|
||||
use euclid::SideOffsets2D;
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::*;
|
||||
use winit::dpi::LogicalPosition;
|
||||
|
||||
|
||||
struct App {
|
||||
cursor_position: WorldPoint,
|
||||
}
|
||||
|
||||
impl Example for App {
|
||||
fn render(
|
||||
&mut self,
|
||||
_api: &mut RenderApi,
|
||||
builder: &mut DisplayListBuilder,
|
||||
_txn: &mut Transaction,
|
||||
_device_size: DeviceIntSize,
|
||||
pipeline_id: PipelineId,
|
||||
_document_id: DocumentId,
|
||||
) {
|
||||
let root_space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
|
||||
builder.push_simple_stacking_context(
|
||||
LayoutPoint::zero(),
|
||||
root_space_and_clip.spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
|
||||
if true {
|
||||
// scrolling and clips stuff
|
||||
// let's make a scrollbox
|
||||
let scrollbox = (0, 0).to(300, 400);
|
||||
builder.push_simple_stacking_context(
|
||||
LayoutPoint::new(10., 10.),
|
||||
root_space_and_clip.spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
// set the scrolling clip
|
||||
let space_and_clip1 = builder.define_scroll_frame(
|
||||
&root_space_and_clip,
|
||||
None,
|
||||
(0, 0).by(1000, 1000),
|
||||
scrollbox,
|
||||
ScrollSensitivity::ScriptAndInputEvents,
|
||||
LayoutVector2D::zero(),
|
||||
);
|
||||
|
||||
// now put some content into it.
|
||||
// start with a white background
|
||||
let mut info = CommonItemProperties::new((0, 0).to(1000, 1000), space_and_clip1);
|
||||
info.hit_info = Some((0, 1));
|
||||
builder.push_rect(&info, info.clip_rect, ColorF::new(1.0, 1.0, 1.0, 1.0));
|
||||
|
||||
// let's make a 50x50 blue square as a visual reference
|
||||
let mut info = CommonItemProperties::new((0, 0).to(50, 50), space_and_clip1);
|
||||
info.hit_info = Some((0, 2));
|
||||
builder.push_rect(&info, info.clip_rect, 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 = CommonItemProperties::new(
|
||||
(50, 0).to(100, 50).intersection(&(60, 10).to(110, 60)).unwrap(),
|
||||
space_and_clip1,
|
||||
);
|
||||
info.hit_info = Some((0, 3));
|
||||
builder.push_rect(&info, info.clip_rect, 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 space_and_clip2 = builder.define_scroll_frame(
|
||||
&space_and_clip1,
|
||||
None,
|
||||
(0, 100).to(300, 1000),
|
||||
(0, 100).to(200, 300),
|
||||
ScrollSensitivity::ScriptAndInputEvents,
|
||||
LayoutVector2D::zero(),
|
||||
);
|
||||
|
||||
// give it a giant gray background just to distinguish it and to easily
|
||||
// visually identify the nested scrollbox
|
||||
let mut info = CommonItemProperties::new(
|
||||
(-1000, -1000).to(5000, 5000),
|
||||
space_and_clip2,
|
||||
);
|
||||
info.hit_info = Some((0, 4));
|
||||
builder.push_rect(&info, info.clip_rect, 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 = CommonItemProperties::new((0, 200).to(50, 250), space_and_clip2);
|
||||
info.hit_info = Some((0, 5));
|
||||
builder.push_rect(&info, info.clip_rect, 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(
|
||||
space_and_clip2.spatial_id,
|
||||
(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)
|
||||
);
|
||||
|
||||
let mut info = CommonItemProperties::new(
|
||||
(50, 350).by(50, 50),
|
||||
SpaceAndClipInfo {
|
||||
spatial_id: sticky_id,
|
||||
clip_id: space_and_clip2.clip_id,
|
||||
},
|
||||
);
|
||||
info.hit_info = Some((0, 6));
|
||||
builder.push_rect(
|
||||
&info,
|
||||
info.clip_rect,
|
||||
ColorF::new(0.5, 0.5, 1.0, 1.0),
|
||||
);
|
||||
|
||||
// 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 = CommonItemProperties::new(
|
||||
(250, 350).to(300, 400),
|
||||
space_and_clip2,
|
||||
);
|
||||
info.hit_info = Some((0, 7));
|
||||
builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 1.0, 1.0));
|
||||
|
||||
builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
fn on_event(&mut self, event: winit::WindowEvent, api: &mut 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 => Some((0.0, -10.0)),
|
||||
winit::VirtualKeyCode::Up => Some((0.0, 10.0)),
|
||||
winit::VirtualKeyCode::Right => Some((-10.0, 0.0)),
|
||||
winit::VirtualKeyCode::Left => Some((10.0, 0.0)),
|
||||
_ => None,
|
||||
};
|
||||
let zoom = match key {
|
||||
winit::VirtualKeyCode::Key0 => Some(1.0),
|
||||
winit::VirtualKeyCode::Minus => Some(0.8),
|
||||
winit::VirtualKeyCode::Equals => Some(1.25),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(offset) = offset {
|
||||
txn.scroll(
|
||||
ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
|
||||
self.cursor_position,
|
||||
);
|
||||
txn.generate_frame();
|
||||
}
|
||||
if let Some(zoom) = zoom {
|
||||
txn.set_pinch_zoom(ZoomFactor::new(zoom));
|
||||
txn.generate_frame();
|
||||
}
|
||||
}
|
||||
winit::WindowEvent::CursorMoved { position: LogicalPosition { 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(pos) => (pos.x as f32, pos.y as f32),
|
||||
};
|
||||
|
||||
txn.scroll(
|
||||
ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
|
||||
self.cursor_position,
|
||||
);
|
||||
txn.generate_frame();
|
||||
}
|
||||
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);
|
||||
}
|
322
third_party/webrender/examples/texture_cache_stress.rs
vendored
Normal file
322
third_party/webrender/examples/texture_cache_stress.rs
vendored
Normal file
|
@ -0,0 +1,322 @@
|
|||
/* 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 crate::boilerplate::{Example, HandyDandyRectBuilder};
|
||||
use gleam::gl;
|
||||
use std::mem;
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::*;
|
||||
|
||||
|
||||
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: i32) {
|
||||
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 ExternalImageHandler for ImageGenerator {
|
||||
fn lock(
|
||||
&mut self,
|
||||
_key: ExternalImageId,
|
||||
channel_index: u8,
|
||||
_rendering: ImageRendering
|
||||
) -> ExternalImage {
|
||||
self.generate_image(channel_index as i32);
|
||||
ExternalImage {
|
||||
uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
|
||||
source: 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: &mut RenderApi,
|
||||
builder: &mut DisplayListBuilder,
|
||||
txn: &mut Transaction,
|
||||
_device_size: DeviceIntSize,
|
||||
pipeline_id: PipelineId,
|
||||
_document_id: DocumentId,
|
||||
) {
|
||||
let bounds = (0, 0).to(512, 512);
|
||||
let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
|
||||
|
||||
builder.push_simple_stacking_context(
|
||||
bounds.origin,
|
||||
space_and_clip.spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
|
||||
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, ImageDescriptorFlags::IS_OPAQUE),
|
||||
ImageData::new(self.image_generator.take()),
|
||||
None,
|
||||
);
|
||||
|
||||
self.image_generator.generate_image(128);
|
||||
txn.add_image(
|
||||
key1,
|
||||
ImageDescriptor::new(128, 128, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
|
||||
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 = CommonItemProperties::new(
|
||||
LayoutRect::new(
|
||||
LayoutPoint::new(x0 + image_size.width * x, y0 + image_size.height * y),
|
||||
image_size,
|
||||
),
|
||||
space_and_clip,
|
||||
);
|
||||
|
||||
builder.push_image(
|
||||
&info,
|
||||
bounds,
|
||||
ImageRendering::Auto,
|
||||
AlphaType::PremultipliedAlpha,
|
||||
*key,
|
||||
ColorF::WHITE,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(image_key) = self.image_key {
|
||||
let image_size = LayoutSize::new(100.0, 100.0);
|
||||
let info = CommonItemProperties::new(
|
||||
LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
|
||||
space_and_clip,
|
||||
);
|
||||
builder.push_image(
|
||||
&info,
|
||||
bounds,
|
||||
ImageRendering::Auto,
|
||||
AlphaType::PremultipliedAlpha,
|
||||
image_key,
|
||||
ColorF::WHITE,
|
||||
);
|
||||
}
|
||||
|
||||
let swap_key = self.swap_keys[self.swap_index];
|
||||
let image_size = LayoutSize::new(64.0, 64.0);
|
||||
let info = CommonItemProperties::new(
|
||||
LayoutRect::new(LayoutPoint::new(100.0, 400.0), image_size),
|
||||
space_and_clip,
|
||||
);
|
||||
builder.push_image(
|
||||
&info,
|
||||
bounds,
|
||||
ImageRendering::Auto,
|
||||
AlphaType::PremultipliedAlpha,
|
||||
swap_key,
|
||||
ColorF::WHITE,
|
||||
);
|
||||
self.swap_index = 1 - self.swap_index;
|
||||
|
||||
builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: winit::WindowEvent,
|
||||
api: &mut 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,
|
||||
ImageDescriptorFlags::IS_OPAQUE,
|
||||
),
|
||||
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, ImageDescriptorFlags::IS_OPAQUE),
|
||||
ImageData::new(self.image_generator.take()),
|
||||
&DirtyRect::All,
|
||||
);
|
||||
},
|
||||
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, ImageDescriptorFlags::IS_OPAQUE),
|
||||
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, ImageDescriptorFlags::IS_OPAQUE),
|
||||
ImageData::new(self.image_generator.take()),
|
||||
None,
|
||||
);
|
||||
|
||||
self.image_key = Some(image_key);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
api.send_transaction(document_id, txn);
|
||||
return true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn get_image_handlers(
|
||||
&mut self,
|
||||
_gl: &dyn gl::Gl,
|
||||
) -> (Option<Box<dyn ExternalImageHandler>>,
|
||||
Option<Box<dyn 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);
|
||||
}
|
226
third_party/webrender/examples/yuv.rs
vendored
Normal file
226
third_party/webrender/examples/yuv.rs
vendored
Normal file
|
@ -0,0 +1,226 @@
|
|||
/* 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 crate::boilerplate::Example;
|
||||
use gleam::gl;
|
||||
use webrender::api::*;
|
||||
use webrender::api::units::*;
|
||||
|
||||
|
||||
fn init_gl_texture(
|
||||
id: gl::GLuint,
|
||||
internal: gl::GLenum,
|
||||
external: gl::GLenum,
|
||||
bytes: &[u8],
|
||||
gl: &dyn 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: &dyn 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 ExternalImageHandler for YuvImageProvider {
|
||||
fn lock(
|
||||
&mut self,
|
||||
key: ExternalImageId,
|
||||
_channel_index: u8,
|
||||
_rendering: ImageRendering
|
||||
) -> ExternalImage {
|
||||
let id = self.texture_ids[key.0 as usize];
|
||||
ExternalImage {
|
||||
uv: TexelRect::new(0.0, 0.0, 1.0, 1.0),
|
||||
source: ExternalImageSource::NativeTexture(id),
|
||||
}
|
||||
}
|
||||
fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {
|
||||
}
|
||||
}
|
||||
|
||||
struct App {
|
||||
texture_id: gl::GLuint,
|
||||
current_value: u8,
|
||||
}
|
||||
|
||||
impl Example for App {
|
||||
fn render(
|
||||
&mut self,
|
||||
api: &mut RenderApi,
|
||||
builder: &mut DisplayListBuilder,
|
||||
txn: &mut Transaction,
|
||||
_device_size: DeviceIntSize,
|
||||
pipeline_id: PipelineId,
|
||||
_document_id: DocumentId,
|
||||
) {
|
||||
let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
|
||||
let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id);
|
||||
|
||||
builder.push_simple_stacking_context(
|
||||
bounds.origin,
|
||||
space_and_clip.spatial_id,
|
||||
PrimitiveFlags::IS_BACKFACE_VISIBLE,
|
||||
);
|
||||
|
||||
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, ImageDescriptorFlags::IS_OPAQUE),
|
||||
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, ImageDescriptorFlags::IS_OPAQUE),
|
||||
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, ImageDescriptorFlags::IS_OPAQUE),
|
||||
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, ImageDescriptorFlags::IS_OPAQUE),
|
||||
ImageData::External(ExternalImageData {
|
||||
id: ExternalImageId(3),
|
||||
channel_index: 0,
|
||||
image_type: ExternalImageType::TextureHandle(
|
||||
TextureTarget::Default,
|
||||
),
|
||||
}),
|
||||
None,
|
||||
);
|
||||
|
||||
let info = CommonItemProperties::new(
|
||||
LayoutRect::new(LayoutPoint::new(100.0, 0.0), LayoutSize::new(100.0, 100.0)),
|
||||
space_and_clip,
|
||||
);
|
||||
builder.push_yuv_image(
|
||||
&info,
|
||||
bounds,
|
||||
YuvData::NV12(yuv_chanel1, yuv_chanel2),
|
||||
ColorDepth::Color8,
|
||||
YuvColorSpace::Rec601,
|
||||
ColorRange::Limited,
|
||||
ImageRendering::Auto,
|
||||
);
|
||||
|
||||
let info = CommonItemProperties::new(
|
||||
LayoutRect::new(LayoutPoint::new(300.0, 0.0), LayoutSize::new(100.0, 100.0)),
|
||||
space_and_clip,
|
||||
);
|
||||
builder.push_yuv_image(
|
||||
&info,
|
||||
bounds,
|
||||
YuvData::PlanarYCbCr(yuv_chanel1, yuv_chanel2_1, yuv_chanel3),
|
||||
ColorDepth::Color8,
|
||||
YuvColorSpace::Rec601,
|
||||
ColorRange::Limited,
|
||||
ImageRendering::Auto,
|
||||
);
|
||||
|
||||
builder.pop_stacking_context();
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
_event: winit::WindowEvent,
|
||||
_api: &mut RenderApi,
|
||||
_document_id: DocumentId,
|
||||
) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_image_handlers(
|
||||
&mut self,
|
||||
gl: &dyn gl::Gl,
|
||||
) -> (Option<Box<dyn ExternalImageHandler>>,
|
||||
Option<Box<dyn OutputImageHandler>>) {
|
||||
let provider = YuvImageProvider::new(gl);
|
||||
self.texture_id = provider.texture_ids[0];
|
||||
(Some(Box::new(provider)), None)
|
||||
}
|
||||
|
||||
fn draw_custom(&mut self, gl: &dyn gl::Gl) {
|
||||
init_gl_texture(self.texture_id, gl::RED, gl::RED, &[self.current_value; 100 * 100], gl);
|
||||
self.current_value = self.current_value.wrapping_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut app = App {
|
||||
texture_id: 0,
|
||||
current_value: 0,
|
||||
};
|
||||
|
||||
let opts = webrender::RendererOptions {
|
||||
debug_flags: webrender::DebugFlags::NEW_FRAME_INDICATOR | webrender::DebugFlags::NEW_SCENE_INDICATOR,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
boilerplate::main_wrapper(&mut app, Some(opts));
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue