Bug 1388719 - Update webrender to commit 101c69db1a989fe89c308dabd53cf50aedfe4a96. r=jrmuizel

MozReview-Commit-ID: 10VsggYNyo3

--HG--
extra : rebase_source : b123e2ee5f51d958ec806cb943d042c4ee0b23cf
This commit is contained in:
Kartikaya Gupta 2017-08-11 09:21:41 -04:00
Родитель 41dbd09508
Коммит ea6719b16d
26 изменённых файлов: 1298 добавлений и 806 удалений

Просмотреть файл

@ -79,4 +79,4 @@ to make sure that mozjs_sys also has its Cargo.lock file updated if needed, henc
the need to run the cargo update command in js/src as well. Hopefully this will
be resolved soon.
Latest Commit: e68c8acb021656440d26ac46e705e7ceb31891e6
Latest Commit: 101c69db1a989fe89c308dabd53cf50aedfe4a96

Просмотреть файл

@ -6,76 +6,83 @@ extern crate gleam;
extern crate glutin;
extern crate webrender;
#[macro_use]
extern crate lazy_static;
#[path="common/boilerplate.rs"]
mod boilerplate;
use boilerplate::HandyDandyRectBuilder;
use std::sync::Mutex;
use boilerplate::{Example, HandyDandyRectBuilder};
use webrender::api::*;
// This example creates a 100x100 white rect and allows the user to move it
// around by using the arrow keys. It does this by using the animation API.
fn body(_api: &RenderApi,
_document_id: &DocumentId,
builder: &mut DisplayListBuilder,
_resouces: &mut ResourceUpdates,
_pipeline_id: &PipelineId,
_layout_size: &LayoutSize) {
// Create a 100x100 stacking context with an animatable transform property.
// Note the magic "42" we use as the animation key. That is used to update
// the transform in the keyboard event handler code.
let bounds = (0,0).to(100, 100);
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
Some(PropertyBinding::Binding(PropertyBindingKey::new(42))),
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
// Fill it with a white rect
builder.push_rect(bounds, None, ColorF::new(1.0, 1.0, 1.0, 1.0));
builder.pop_stacking_context();
struct App {
transform: LayoutTransform,
}
lazy_static! {
static ref TRANSFORM: Mutex<LayoutTransform> = Mutex::new(LayoutTransform::identity());
}
impl Example for App {
fn render(&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
_resources: &mut ResourceUpdates,
_layout_size: LayoutSize,
_pipeline_id: PipelineId,
_document_id: DocumentId) {
// Create a 100x100 stacking context with an animatable transform property.
// Note the magic "42" we use as the animation key. That is used to update
// the transform in the keyboard event handler code.
let bounds = (0,0).to(100, 100);
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
Some(PropertyBinding::Binding(PropertyBindingKey::new(42))),
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
fn event_handler(event: &glutin::Event, document_id: DocumentId, api: &RenderApi) {
match *event {
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
let offset = match key {
glutin::VirtualKeyCode::Down => (0.0, 10.0),
glutin::VirtualKeyCode::Up => (0.0, -10.0),
glutin::VirtualKeyCode::Right => (10.0, 0.0),
glutin::VirtualKeyCode::Left => (-10.0, 0.0),
_ => return,
};
// 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.
let new_transform = TRANSFORM.lock().unwrap().post_translate(LayoutVector3D::new(offset.0, offset.1, 0.0));
api.generate_frame(document_id, Some(DynamicProperties {
transforms: vec![
PropertyValue {
key: PropertyBindingKey::new(42),
value: new_transform,
},
],
floats: vec![],
}));
*TRANSFORM.lock().unwrap() = new_transform;
// Fill it with a white rect
builder.push_rect(bounds, None, ColorF::new(1.0, 1.0, 1.0, 1.0));
builder.pop_stacking_context();
}
fn on_event(&mut self,
event: glutin::Event,
api: &RenderApi,
document_id: DocumentId) -> bool {
match event {
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
let offset = match key {
glutin::VirtualKeyCode::Down => (0.0, 10.0),
glutin::VirtualKeyCode::Up => (0.0, -10.0),
glutin::VirtualKeyCode::Right => (10.0, 0.0),
glutin::VirtualKeyCode::Left => (-10.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.
let new_transform = self.transform.post_translate(LayoutVector3D::new(offset.0, offset.1, 0.0));
api.generate_frame(document_id, Some(DynamicProperties {
transforms: vec![
PropertyValue {
key: PropertyBindingKey::new(42),
value: new_transform,
},
],
floats: vec![],
}));
self.transform = new_transform;
}
_ => ()
}
_ => ()
false
}
}
fn main() {
boilerplate::main_wrapper(body, event_handler, None);
let mut app = App {
transform: LayoutTransform::identity(),
};
boilerplate::main_wrapper(&mut app, None);
}

Просмотреть файл

@ -8,20 +8,16 @@ extern crate gleam;
extern crate glutin;
extern crate webrender;
#[macro_use]
extern crate lazy_static;
#[path="common/boilerplate.rs"]
mod boilerplate;
use app_units::Au;
use boilerplate::HandyDandyRectBuilder;
use boilerplate::{Example, HandyDandyRectBuilder};
use euclid::vec2;
use glutin::TouchPhase;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::sync::Mutex;
use webrender::api::*;
#[derive(Debug)]
@ -170,177 +166,188 @@ fn load_file(name: &str) -> Vec<u8> {
}
fn main() {
boilerplate::main_wrapper(body, event_handler, None);
let mut app = App {
touch_state: TouchState::new(),
};
boilerplate::main_wrapper(&mut app, None);
}
fn body(api: &RenderApi,
_document_id: &DocumentId,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
_pipeline_id: &PipelineId,
layout_size: &LayoutSize) {
let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
let image_mask_key = api.generate_image_key();
resources.add_image(
image_mask_key,
ImageDescriptor::new(2, 2, ImageFormat::A8, true),
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));
let id = builder.define_clip(None, bounds, vec![complex], Some(mask));
builder.push_clip_id(id);
let bounds = (100, 100).to(200, 200);
builder.push_rect(bounds, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
let bounds = (250, 100).to(350, 200);
builder.push_rect(bounds, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
let border_side = BorderSide {
color: ColorF::new(0.0, 0.0, 1.0, 1.0),
style: BorderStyle::Groove,
};
let border_widths = BorderWidths {
top: 10.0,
left: 10.0,
bottom: 10.0,
right: 10.0,
};
let border_details = BorderDetails::Normal(NormalBorder {
top: border_side,
right: border_side,
bottom: border_side,
left: border_side,
radius: BorderRadius::uniform(20.0),
});
let bounds = (100, 100).to(200, 200);
builder.push_border(bounds, None, border_widths, border_details);
if false { // draw text?
let font_key = api.generate_font_key();
let font_bytes = load_file("res/FreeSans.ttf");
resources.add_raw_font(font_key, font_bytes, 0);
let text_bounds = (100, 200).by(700, 300);
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(text_bounds,
None,
&glyphs,
font_key,
ColorF::new(1.0, 1.0, 0.0, 1.0),
Au::from_px(32),
None);
}
if false { // draw box shadow?
let rect = LayoutRect::zero();
let simple_box_bounds = (20, 200).by(50, 50);
let offset = vec2(10.0, 10.0);
let color = ColorF::new(1.0, 1.0, 1.0, 1.0);
let blur_radius = 0.0;
let spread_radius = 0.0;
let simple_border_radius = 8.0;
let box_shadow_type = BoxShadowClipMode::Inset;
builder.push_box_shadow(rect,
Some(LocalClip::from(bounds)),
simple_box_bounds,
offset,
color,
blur_radius,
spread_radius,
simple_border_radius,
box_shadow_type);
}
builder.pop_clip_id();
builder.pop_stacking_context();
struct App {
touch_state: TouchState,
}
lazy_static! {
static ref TOUCH_STATE: Mutex<TouchState> = Mutex::new(TouchState::new());
}
impl Example for App {
fn render(&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
layout_size: LayoutSize,
_pipeline_id: PipelineId,
_document_id: DocumentId) {
let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
fn event_handler(event: &glutin::Event, document_id: DocumentId, api: &RenderApi) {
match *event {
glutin::Event::Touch(touch) => {
match TOUCH_STATE.lock().unwrap().handle_event(touch) {
TouchResult::Pan(pan) => {
api.set_pan(document_id, pan);
api.generate_frame(document_id, None);
}
TouchResult::Zoom(zoom) => {
api.set_pinch_zoom(document_id, ZoomFactor::new(zoom));
api.generate_frame(document_id, None);
}
TouchResult::None => {}
}
let image_mask_key = api.generate_image_key();
resources.add_image(
image_mask_key,
ImageDescriptor::new(2, 2, ImageFormat::A8, true),
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));
let id = builder.define_clip(None, bounds, vec![complex], Some(mask));
builder.push_clip_id(id);
let bounds = (100, 100).to(200, 200);
builder.push_rect(bounds, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
let bounds = (250, 100).to(350, 200);
builder.push_rect(bounds, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
let border_side = BorderSide {
color: ColorF::new(0.0, 0.0, 1.0, 1.0),
style: BorderStyle::Groove,
};
let border_widths = BorderWidths {
top: 10.0,
left: 10.0,
bottom: 10.0,
right: 10.0,
};
let border_details = BorderDetails::Normal(NormalBorder {
top: border_side,
right: border_side,
bottom: border_side,
left: border_side,
radius: BorderRadius::uniform(20.0),
});
let bounds = (100, 100).to(200, 200);
builder.push_border(bounds, None, border_widths, border_details);
if false { // draw text?
let font_key = api.generate_font_key();
let font_bytes = load_file("res/FreeSans.ttf");
resources.add_raw_font(font_key, font_bytes, 0);
let text_bounds = (100, 200).by(700, 300);
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(text_bounds,
None,
&glyphs,
font_key,
ColorF::new(1.0, 1.0, 0.0, 1.0),
Au::from_px(32),
None);
}
_ => ()
if false { // draw box shadow?
let rect = LayoutRect::zero();
let simple_box_bounds = (20, 200).by(50, 50);
let offset = vec2(10.0, 10.0);
let color = ColorF::new(1.0, 1.0, 1.0, 1.0);
let blur_radius = 0.0;
let spread_radius = 0.0;
let simple_border_radius = 8.0;
let box_shadow_type = BoxShadowClipMode::Inset;
builder.push_box_shadow(rect,
Some(LocalClip::from(bounds)),
simple_box_bounds,
offset,
color,
blur_radius,
spread_radius,
simple_border_radius,
box_shadow_type);
}
builder.pop_clip_id();
builder.pop_stacking_context();
}
fn on_event(&mut self,
event: glutin::Event,
api: &RenderApi,
document_id: DocumentId) -> bool {
match event {
glutin::Event::Touch(touch) => {
match self.touch_state.handle_event(touch) {
TouchResult::Pan(pan) => {
api.set_pan(document_id, pan);
api.generate_frame(document_id, None);
}
TouchResult::Zoom(zoom) => {
api.set_pinch_zoom(document_id, ZoomFactor::new(zoom));
api.generate_frame(document_id, None);
}
TouchResult::None => {}
}
}
_ => ()
}
false
}
}

Просмотреть файл

@ -12,14 +12,14 @@ extern crate rayon;
#[path="common/boilerplate.rs"]
mod boilerplate;
use boilerplate::HandyDandyRectBuilder;
use boilerplate::{Example, HandyDandyRectBuilder};
use rayon::ThreadPool;
use rayon::Configuration as ThreadPoolConfig;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use webrender::api;
use webrender::api::{self, RenderApi, DisplayListBuilder, ResourceUpdates, LayoutSize, PipelineId, DocumentId};
// This example shows how to implement a very basic BlobImageRenderer that can only render
// a checkerboard pattern.
@ -212,59 +212,70 @@ impl api::BlobImageRenderer for CheckerboardRenderer {
fn delete_font(&mut self, _font: api::FontKey) { }
}
fn body(api: &api::RenderApi,
_document_id: &api::DocumentId,
builder: &mut api::DisplayListBuilder,
resources: &mut api::ResourceUpdates,
_pipeline_id: &api::PipelineId,
layout_size: &api::LayoutSize) {
let blob_img1 = api.generate_image_key();
resources.add_image(
blob_img1,
api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true),
api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))),
Some(128),
);
struct App {
let blob_img2 = api.generate_image_key();
resources.add_image(
blob_img2,
api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true),
api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))),
None,
);
let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), *layout_size);
builder.push_stacking_context(api::ScrollPolicy::Scrollable,
bounds,
None,
api::TransformStyle::Flat,
None,
api::MixBlendMode::Normal,
Vec::new());
builder.push_image(
(30, 30).by(500, 500),
Some(api::LocalClip::from(bounds)),
api::LayoutSize::new(500.0, 500.0),
api::LayoutSize::new(0.0, 0.0),
api::ImageRendering::Auto,
blob_img1,
);
builder.push_image(
(600, 600).by(200, 200),
Some(api::LocalClip::from(bounds)),
api::LayoutSize::new(200.0, 200.0),
api::LayoutSize::new(0.0, 0.0),
api::ImageRendering::Auto,
blob_img2,
);
builder.pop_stacking_context();
}
fn event_handler(_event: &glutin::Event, _document_id: api::DocumentId, _api: &api::RenderApi) {
impl Example for App {
fn render(&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
layout_size: LayoutSize,
_pipeline_id: PipelineId,
_document_id: DocumentId) {
let blob_img1 = api.generate_image_key();
resources.add_image(
blob_img1,
api::ImageDescriptor::new(500, 500, api::ImageFormat::BGRA8, true),
api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 50, 150, 255))),
Some(128),
);
let blob_img2 = api.generate_image_key();
resources.add_image(
blob_img2,
api::ImageDescriptor::new(200, 200, api::ImageFormat::BGRA8, true),
api::ImageData::new_blob_image(serialize_blob(api::ColorU::new(50, 150, 50, 255))),
None,
);
let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), layout_size);
builder.push_stacking_context(api::ScrollPolicy::Scrollable,
bounds,
None,
api::TransformStyle::Flat,
None,
api::MixBlendMode::Normal,
Vec::new());
builder.push_image(
(30, 30).by(500, 500),
Some(api::LocalClip::from(bounds)),
api::LayoutSize::new(500.0, 500.0),
api::LayoutSize::new(0.0, 0.0),
api::ImageRendering::Auto,
blob_img1,
);
builder.push_image(
(600, 600).by(200, 200),
Some(api::LocalClip::from(bounds)),
api::LayoutSize::new(200.0, 200.0),
api::LayoutSize::new(0.0, 0.0),
api::ImageRendering::Auto,
blob_img2,
);
builder.pop_stacking_context();
}
fn on_event(&mut self,
_event: glutin::Event,
_api: &RenderApi,
_document_id: DocumentId) -> bool {
false
}
}
fn main() {
@ -282,5 +293,7 @@ fn main() {
.. Default::default()
};
boilerplate::main_wrapper(body, event_handler, Some(opts));
let mut app = App {};
boilerplate::main_wrapper(&mut app, Some(opts));
}

Просмотреть файл

@ -52,15 +52,21 @@ impl HandyDandyRectBuilder for (i32, i32) {
}
}
pub fn main_wrapper(builder_callback: fn(&RenderApi,
&DocumentId,
&mut DisplayListBuilder,
&mut ResourceUpdates,
&PipelineId,
&LayoutSize) -> (),
event_handler: fn(&glutin::Event,
DocumentId,
&RenderApi) -> (),
pub trait Example {
fn render(&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
layout_size: LayoutSize,
pipeline_id: PipelineId,
document_id: DocumentId);
fn on_event(&mut self,
event: glutin::Event,
api: &RenderApi,
document_id: DocumentId) -> bool;
}
pub fn main_wrapper(example: &mut Example,
options: Option<webrender::RendererOptions>)
{
let args: Vec<String> = env::args().collect();
@ -118,8 +124,7 @@ pub fn main_wrapper(builder_callback: fn(&RenderApi,
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
let mut resources = ResourceUpdates::new();
builder_callback(&api, &document_id, &mut builder, &mut resources, &pipeline_id, &layout_size);
example.render(&api, &mut builder, &mut resources, layout_size, pipeline_id, document_id);
api.set_display_list(
document_id,
epoch,
@ -151,21 +156,41 @@ pub fn main_wrapper(builder_callback: fn(&RenderApi,
let mut flags = renderer.get_debug_flags();
flags.toggle(PROFILER_DBG);
renderer.set_debug_flags(flags);
},
}
glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
_, Some(glutin::VirtualKeyCode::O)) => {
let mut flags = renderer.get_debug_flags();
flags.toggle(RENDER_TARGET_DBG);
renderer.set_debug_flags(flags);
},
}
glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
_, Some(glutin::VirtualKeyCode::I)) => {
let mut flags = renderer.get_debug_flags();
flags.toggle(TEXTURE_CACHE_DBG);
renderer.set_debug_flags(flags);
},
}
glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
_, Some(glutin::VirtualKeyCode::M)) => {
api.notify_memory_pressure();
}
_ => {
if example.on_event(event, &api, document_id) {
let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
let mut resources = ResourceUpdates::new();
_ => event_handler(&event, document_id, &api),
example.render(&api, &mut builder, &mut resources, layout_size, pipeline_id, document_id);
api.set_display_list(
document_id,
epoch,
Some(root_background_color),
LayoutSize::new(width as f32, height as f32),
builder.finalize(),
true,
resources
);
api.generate_frame(document_id, None);
}
}
}
}

Просмотреть файл

@ -9,66 +9,77 @@ extern crate webrender;
#[path="common/boilerplate.rs"]
mod boilerplate;
use boilerplate::HandyDandyRectBuilder;
use boilerplate::{Example, HandyDandyRectBuilder};
use webrender::api::*;
// This example uses the push_iframe API to nest a second pipeline's displaylist
// inside the root pipeline's display list. When it works, a green square is
// shown. If it fails, a red square is shown.
fn body(api: &RenderApi,
document_id: &DocumentId,
builder: &mut DisplayListBuilder,
_resources: &mut ResourceUpdates,
pipeline_id: &PipelineId,
_layout_size: &LayoutSize) {
struct App {
// All the sub_* things are for the nested pipeline
let sub_size = DeviceUintSize::new(100, 100);
let sub_bounds = (0,0).to(sub_size.width as i32, sub_size.height as i32);
}
let sub_pipeline_id = PipelineId(pipeline_id.0, 42);
let mut sub_builder = DisplayListBuilder::new(sub_pipeline_id, sub_bounds.size);
impl Example for App {
fn render(&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
_resources: &mut ResourceUpdates,
_layout_size: LayoutSize,
pipeline_id: PipelineId,
document_id: DocumentId) {
// All the sub_* things are for the nested pipeline
let sub_size = DeviceUintSize::new(100, 100);
let sub_bounds = (0,0).to(sub_size.width as i32, sub_size.height as i32);
sub_builder.push_stacking_context(ScrollPolicy::Scrollable,
sub_bounds,
None,
let sub_pipeline_id = PipelineId(pipeline_id.0, 42);
let mut sub_builder = DisplayListBuilder::new(sub_pipeline_id, sub_bounds.size);
sub_builder.push_stacking_context(ScrollPolicy::Scrollable,
sub_bounds,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
// green rect visible == success
sub_builder.push_rect(sub_bounds, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
sub_builder.pop_stacking_context();
api.set_display_list(
document_id,
Epoch(0),
None,
sub_bounds.size,
sub_builder.finalize(),
true,
ResourceUpdates::new(),
);
let bounds = sub_bounds;
// And this is for the root pipeline
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
Some(PropertyBinding::Binding(PropertyBindingKey::new(42))),
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
// green rect visible == success
sub_builder.push_rect(sub_bounds, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
sub_builder.pop_stacking_context();
// red rect under the iframe: if this is visible, things have gone wrong
builder.push_rect(bounds, None, ColorF::new(1.0, 0.0, 0.0, 1.0));
builder.push_iframe(bounds, None, sub_pipeline_id);
builder.pop_stacking_context();
}
api.set_display_list(
*document_id,
Epoch(0),
None,
sub_bounds.size,
sub_builder.finalize(),
true,
ResourceUpdates::new(),
);
let bounds = sub_bounds;
// And this is for the root pipeline
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
Some(PropertyBinding::Binding(PropertyBindingKey::new(42))),
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
// red rect under the iframe: if this is visible, things have gone wrong
builder.push_rect(bounds, None, ColorF::new(1.0, 0.0, 0.0, 1.0));
builder.push_iframe(bounds, None, sub_pipeline_id);
builder.pop_stacking_context();
}
fn event_handler(_event: &glutin::Event, _document_id: DocumentId, _api: &RenderApi) {
fn on_event(&mut self,
_event: glutin::Event,
_api: &RenderApi,
_document_id: DocumentId) -> bool {
false
}
}
fn main() {
boilerplate::main_wrapper(body, event_handler, None);
let mut app = App {};
boilerplate::main_wrapper(&mut app, None);
}

Просмотреть файл

@ -0,0 +1,114 @@
/* 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;
#[path="common/boilerplate.rs"]
mod boilerplate;
use boilerplate::{Example, HandyDandyRectBuilder};
use webrender::api::*;
struct App {
image_key: ImageKey,
}
impl Example for App {
fn render(&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
_layout_size: LayoutSize,
_pipeline_id: PipelineId,
_document_id: DocumentId) {
let mut image_data = Vec::new();
for y in 0..32 {
for x in 0..32 {
let lum = 255 * (((x & 8) == 0) ^ ((y & 8) == 0)) as u8;
image_data.extend_from_slice(&[lum, lum, lum, 0xff]);
}
}
resources.add_image(
self.image_key,
ImageDescriptor::new(32, 32, ImageFormat::BGRA8, true),
ImageData::new(image_data),
None,
);
let bounds = (0,0).to(512, 512);
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
let image_size = LayoutSize::new(100.0, 100.0);
builder.push_image(
LayoutRect::new(LayoutPoint::new(100.0, 100.0), image_size),
Some(LocalClip::from(bounds)),
image_size,
LayoutSize::zero(),
ImageRendering::Auto,
self.image_key
);
builder.push_image(
LayoutRect::new(LayoutPoint::new(250.0, 100.0), image_size),
Some(LocalClip::from(bounds)),
image_size,
LayoutSize::zero(),
ImageRendering::Pixelated,
self.image_key
);
builder.pop_stacking_context();
}
fn on_event(&mut self,
event: glutin::Event,
api: &RenderApi,
document_id: DocumentId) -> bool {
match event {
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
match key {
glutin::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 updates = ResourceUpdates::new();
updates.update_image(self.image_key,
ImageDescriptor::new(64, 64, ImageFormat::BGRA8, true),
ImageData::new(image_data),
None);
api.update_resources(updates);
api.generate_frame(document_id, None);
}
_ => {}
}
}
_ => {}
}
false
}
}
fn main() {
let mut app = App {
image_key: ImageKey(IdNamespace(0), 0),
};
boilerplate::main_wrapper(&mut app, None);
}

Просмотреть файл

@ -6,131 +6,138 @@ extern crate gleam;
extern crate glutin;
extern crate webrender;
#[macro_use]
extern crate lazy_static;
#[path="common/boilerplate.rs"]
mod boilerplate;
use boilerplate::HandyDandyRectBuilder;
use std::sync::Mutex;
use boilerplate::{Example, HandyDandyRectBuilder};
use webrender::api::*;
fn body(_api: &RenderApi,
_document_id: &DocumentId,
builder: &mut DisplayListBuilder,
_resources: &mut ResourceUpdates,
pipeline_id: &PipelineId,
layout_size: &LayoutSize) {
let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
let outer_scroll_frame_rect = (100, 100).to(600, 400);
builder.push_rect(outer_scroll_frame_rect, None, ColorF::new(1.0, 1.0, 1.0, 1.0));
let nested_clip_id = builder.define_scroll_frame(None,
(100, 100).to(1000, 1000),
outer_scroll_frame_rect,
vec![],
None,
ScrollSensitivity::ScriptAndInputEvents);
builder.push_clip_id(nested_clip_id);
let mut builder2 = DisplayListBuilder::new(*pipeline_id, *layout_size);
let mut builder3 = DisplayListBuilder::new(*pipeline_id, *layout_size);
let rect = (110, 110).to(210, 210);
builder3.push_rect(rect, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
// A fixed position rectangle should be fixed to the reference frame that starts
// in the outer display list.
builder3.push_stacking_context(ScrollPolicy::Fixed,
(220, 110).to(320, 210),
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
let rect = (0, 0).to(100, 100);
builder3.push_rect(rect, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
builder3.pop_stacking_context();
// Now we push an inner scroll frame that should have the same id as the outer one,
// but the WebRender nested display list replacement code should convert it into
// a unique ClipId.
let inner_scroll_frame_rect = (330, 110).to(530, 360);
builder3.push_rect(inner_scroll_frame_rect, None, ColorF::new(1.0, 0.0, 1.0, 0.5));
let inner_nested_clip_id =
builder3.define_scroll_frame(None,
(330, 110).to(2000, 2000),
inner_scroll_frame_rect,
vec![],
None,
ScrollSensitivity::ScriptAndInputEvents);
builder3.push_clip_id(inner_nested_clip_id);
let rect = (340, 120).to(440, 220);
builder3.push_rect(rect, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
builder3.pop_clip_id();
let (_, _, built_list) = builder3.finalize();
builder2.push_nested_display_list(&built_list);
let (_, _, built_list) = builder2.finalize();
builder.push_nested_display_list(&built_list);
builder.pop_clip_id();
builder.pop_stacking_context();
struct App {
cursor_position: WorldPoint,
}
lazy_static! {
static ref CURSOR_POSITION: Mutex<WorldPoint> = Mutex::new(WorldPoint::zero());
}
impl Example for App {
fn render(&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
_resources: &mut ResourceUpdates,
layout_size: LayoutSize,
pipeline_id: PipelineId,
_document_id: DocumentId) {
let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
fn event_handler(event: &glutin::Event, document_id: DocumentId, api: &RenderApi) {
match *event {
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
let offset = match key {
glutin::VirtualKeyCode::Down => (0.0, -10.0),
glutin::VirtualKeyCode::Up => (0.0, 10.0),
glutin::VirtualKeyCode::Right => (-10.0, 0.0),
glutin::VirtualKeyCode::Left => (10.0, 0.0),
_ => return,
};
let outer_scroll_frame_rect = (100, 100).to(600, 400);
builder.push_rect(outer_scroll_frame_rect, None, ColorF::new(1.0, 1.0, 1.0, 1.0));
api.scroll(document_id,
ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
*CURSOR_POSITION.lock().unwrap(),
ScrollEventPhase::Start);
}
glutin::Event::MouseMoved(x, y) => {
*CURSOR_POSITION.lock().unwrap() = WorldPoint::new(x as f32, y as f32);
}
glutin::Event::MouseWheel(delta, _, event_cursor_position) => {
if let Some((x, y)) = event_cursor_position {
*CURSOR_POSITION.lock().unwrap() = WorldPoint::new(x as f32, y as f32);
let nested_clip_id = builder.define_scroll_frame(None,
(100, 100).to(1000, 1000),
outer_scroll_frame_rect,
vec![],
None,
ScrollSensitivity::ScriptAndInputEvents);
builder.push_clip_id(nested_clip_id);
let mut builder2 = DisplayListBuilder::new(pipeline_id, layout_size);
let mut builder3 = DisplayListBuilder::new(pipeline_id, layout_size);
let rect = (110, 110).to(210, 210);
builder3.push_rect(rect, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
// A fixed position rectangle should be fixed to the reference frame that starts
// in the outer display list.
builder3.push_stacking_context(ScrollPolicy::Fixed,
(220, 110).to(320, 210),
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
let rect = (0, 0).to(100, 100);
builder3.push_rect(rect, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
builder3.pop_stacking_context();
// Now we push an inner scroll frame that should have the same id as the outer one,
// but the WebRender nested display list replacement code should convert it into
// a unique ClipId.
let inner_scroll_frame_rect = (330, 110).to(530, 360);
builder3.push_rect(inner_scroll_frame_rect, None, ColorF::new(1.0, 0.0, 1.0, 0.5));
let inner_nested_clip_id =
builder3.define_scroll_frame(None,
(330, 110).to(2000, 2000),
inner_scroll_frame_rect,
vec![],
None,
ScrollSensitivity::ScriptAndInputEvents);
builder3.push_clip_id(inner_nested_clip_id);
let rect = (340, 120).to(440, 220);
builder3.push_rect(rect, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
builder3.pop_clip_id();
let (_, _, built_list) = builder3.finalize();
builder2.push_nested_display_list(&built_list);
let (_, _, built_list) = builder2.finalize();
builder.push_nested_display_list(&built_list);
builder.pop_clip_id();
builder.pop_stacking_context();
}
fn on_event(&mut self,
event: glutin::Event,
api: &RenderApi,
document_id: DocumentId) -> bool {
match event {
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
let offset = match key {
glutin::VirtualKeyCode::Down => (0.0, -10.0),
glutin::VirtualKeyCode::Up => (0.0, 10.0),
glutin::VirtualKeyCode::Right => (-10.0, 0.0),
glutin::VirtualKeyCode::Left => (10.0, 0.0),
_ => return false,
};
api.scroll(document_id,
ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
self.cursor_position,
ScrollEventPhase::Start);
}
glutin::Event::MouseMoved(x, y) => {
self.cursor_position = WorldPoint::new(x as f32, y as f32);
}
glutin::Event::MouseWheel(delta, _, event_cursor_position) => {
if let Some((x, y)) = event_cursor_position {
self.cursor_position = WorldPoint::new(x as f32, y as f32);
}
const LINE_HEIGHT: f32 = 38.0;
let (dx, dy) = match delta {
glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
};
const LINE_HEIGHT: f32 = 38.0;
let (dx, dy) = match delta {
glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
};
api.scroll(document_id,
ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
*CURSOR_POSITION.lock().unwrap(),
ScrollEventPhase::Start);
api.scroll(document_id,
ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
self.cursor_position,
ScrollEventPhase::Start);
}
_ => ()
}
_ => ()
false
}
}
fn main() {
boilerplate::main_wrapper(body, event_handler, None);
let mut app = App {
cursor_position: WorldPoint::zero(),
};
boilerplate::main_wrapper(&mut app, None);
}

Просмотреть файл

@ -6,140 +6,147 @@ extern crate gleam;
extern crate glutin;
extern crate webrender;
#[macro_use]
extern crate lazy_static;
#[path="common/boilerplate.rs"]
mod boilerplate;
use boilerplate::HandyDandyRectBuilder;
use std::sync::Mutex;
use boilerplate::{Example, HandyDandyRectBuilder};
use webrender::api::*;
fn body(_api: &RenderApi,
_document_id: &DocumentId,
builder: &mut DisplayListBuilder,
_resources: &mut ResourceUpdates,
_pipeline_id: &PipelineId,
layout_size: &LayoutSize) {
let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
struct App {
cursor_position: WorldPoint,
}
if true { // scrolling and clips stuff
// let's make a scrollbox
let scrollbox = (0, 0).to(300, 400);
impl Example for App {
fn render(&mut self,
_api: &RenderApi,
builder: &mut DisplayListBuilder,
_resources: &mut ResourceUpdates,
layout_size: LayoutSize,
_pipeline_id: PipelineId,
_document_id: DocumentId) {
let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
builder.push_stacking_context(ScrollPolicy::Scrollable,
LayoutRect::new(LayoutPoint::new(10.0, 10.0),
LayoutSize::zero()),
bounds,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
// set the scrolling clip
let clip_id = builder.define_scroll_frame(None,
(0, 0).by(1000, 1000),
scrollbox,
vec![],
None,
ScrollSensitivity::ScriptAndInputEvents);
builder.push_clip_id(clip_id);
// now put some content into it.
// start with a white background
builder.push_rect((0, 0).to(1000, 1000), None, ColorF::new(1.0, 1.0, 1.0, 1.0));
if true { // scrolling and clips stuff
// let's make a scrollbox
let scrollbox = (0, 0).to(300, 400);
builder.push_stacking_context(ScrollPolicy::Scrollable,
LayoutRect::new(LayoutPoint::new(10.0, 10.0),
LayoutSize::zero()),
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
// set the scrolling clip
let clip_id = builder.define_scroll_frame(None,
(0, 0).by(1000, 1000),
scrollbox,
vec![],
None,
ScrollSensitivity::ScriptAndInputEvents);
builder.push_clip_id(clip_id);
// let's make a 50x50 blue square as a visual reference
builder.push_rect((0, 0).to(50, 50), None, ColorF::new(0.0, 0.0, 1.0, 1.0));
// now put some content into it.
// start with a white background
builder.push_rect((0, 0).to(1000, 1000), None, ColorF::new(1.0, 1.0, 1.0, 1.0));
// and a 50x50 green square next to it with an offset clip
// to see what that looks like
builder.push_rect((50, 0).to(100, 50),
Some(LocalClip::from((60, 10).to(110, 60))),
ColorF::new(0.0, 1.0, 0.0, 1.0));
// let's make a 50x50 blue square as a visual reference
builder.push_rect((0, 0).to(50, 50), None, ColorF::new(0.0, 0.0, 1.0, 1.0));
// Below the above rectangles, set up a nested scrollbox. It's still in
// the same stacking context, so note that the rects passed in need to
// be relative to the stacking context.
let nested_clip_id = builder.define_scroll_frame(None,
(0, 100).to(300, 400),
(0, 100).to(200, 300),
vec![],
None,
ScrollSensitivity::ScriptAndInputEvents);
builder.push_clip_id(nested_clip_id);
// and a 50x50 green square next to it with an offset clip
// to see what that looks like
builder.push_rect((50, 0).to(100, 50),
Some(LocalClip::from((60, 10).to(110, 60))),
ColorF::new(0.0, 1.0, 0.0, 1.0));
// give it a giant gray background just to distinguish it and to easily
// visually identify the nested scrollbox
builder.push_rect((-1000, -1000).to(5000, 5000), None, ColorF::new(0.5, 0.5, 0.5, 1.0));
// Below the above rectangles, set up a nested scrollbox. It's still in
// the same stacking context, so note that the rects passed in need to
// be relative to the stacking context.
let nested_clip_id = builder.define_scroll_frame(None,
(0, 100).to(300, 400),
(0, 100).to(200, 300),
vec![],
None,
ScrollSensitivity::ScriptAndInputEvents);
builder.push_clip_id(nested_clip_id);
// add a teal square to visualize the scrolling/clipping behaviour
// as you scroll the nested scrollbox with WASD keys
builder.push_rect((0, 100).to(50, 150), None, ColorF::new(0.0, 1.0, 1.0, 1.0));
// give it a giant gray background just to distinguish it and to easily
// visually identify the nested scrollbox
builder.push_rect((-1000, -1000).to(5000, 5000), None, ColorF::new(0.5, 0.5, 0.5, 1.0));
// just for good measure add another teal square in the bottom-right
// corner of the nested scrollframe content, which can be scrolled into
// view by the user
builder.push_rect((250, 350).to(300, 400), None, ColorF::new(0.0, 1.0, 1.0, 1.0));
// add a teal square to visualize the scrolling/clipping behaviour
// as you scroll the nested scrollbox with WASD keys
builder.push_rect((0, 100).to(50, 150), None, ColorF::new(0.0, 1.0, 1.0, 1.0));
builder.pop_clip_id(); // nested_clip_id
// just for good measure add another teal square in the bottom-right
// corner of the nested scrollframe content, which can be scrolled into
// view by the user
builder.push_rect((250, 350).to(300, 400), None, ColorF::new(0.0, 1.0, 1.0, 1.0));
builder.pop_clip_id(); // nested_clip_id
builder.pop_clip_id(); // clip_id
builder.pop_stacking_context();
}
builder.pop_clip_id(); // clip_id
builder.pop_stacking_context();
}
builder.pop_stacking_context();
}
fn on_event(&mut self,
event: glutin::Event,
api: &RenderApi,
document_id: DocumentId) -> bool {
match event {
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
let offset = match key {
glutin::VirtualKeyCode::Down => (0.0, -10.0),
glutin::VirtualKeyCode::Up => (0.0, 10.0),
glutin::VirtualKeyCode::Right => (-10.0, 0.0),
glutin::VirtualKeyCode::Left => (10.0, 0.0),
_ => return false,
};
lazy_static! {
static ref CURSOR_POSITION: Mutex<WorldPoint> = Mutex::new(WorldPoint::zero());
}
fn event_handler(event: &glutin::Event, document_id: DocumentId, api: &RenderApi) {
match *event {
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
let offset = match key {
glutin::VirtualKeyCode::Down => (0.0, -10.0),
glutin::VirtualKeyCode::Up => (0.0, 10.0),
glutin::VirtualKeyCode::Right => (-10.0, 0.0),
glutin::VirtualKeyCode::Left => (10.0, 0.0),
_ => return,
};
api.scroll(document_id,
ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
*CURSOR_POSITION.lock().unwrap(),
ScrollEventPhase::Start);
}
glutin::Event::MouseMoved(x, y) => {
*CURSOR_POSITION.lock().unwrap() = WorldPoint::new(x as f32, y as f32);
}
glutin::Event::MouseWheel(delta, _, event_cursor_position) => {
if let Some((x, y)) = event_cursor_position {
*CURSOR_POSITION.lock().unwrap() = WorldPoint::new(x as f32, y as f32);
api.scroll(document_id,
ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
self.cursor_position,
ScrollEventPhase::Start);
}
glutin::Event::MouseMoved(x, y) => {
self.cursor_position = WorldPoint::new(x as f32, y as f32);
}
glutin::Event::MouseWheel(delta, _, event_cursor_position) => {
if let Some((x, y)) = event_cursor_position {
self.cursor_position = WorldPoint::new(x as f32, y as f32);
}
const LINE_HEIGHT: f32 = 38.0;
let (dx, dy) = match delta {
glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
};
const LINE_HEIGHT: f32 = 38.0;
let (dx, dy) = match delta {
glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
};
api.scroll(document_id,
ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
*CURSOR_POSITION.lock().unwrap(),
ScrollEventPhase::Start);
api.scroll(document_id,
ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
self.cursor_position,
ScrollEventPhase::Start);
}
_ => ()
}
_ => ()
false
}
}
fn main() {
boilerplate::main_wrapper(body, event_handler, None);
let mut app = App {
cursor_position: WorldPoint::zero(),
};
boilerplate::main_wrapper(&mut app, None);
}

Просмотреть файл

@ -8,15 +8,12 @@ extern crate gleam;
extern crate glutin;
extern crate webrender;
#[macro_use]
extern crate lazy_static;
#[path="common/boilerplate.rs"]
mod boilerplate;
use boilerplate::Example;
use glutin::TouchPhase;
use std::collections::HashMap;
use std::sync::Mutex;
use webrender::api::*;
#[derive(Debug)]
@ -157,93 +154,103 @@ impl TouchState {
}
}
fn main() {
boilerplate::main_wrapper(body, event_handler, None);
struct App {
touch_state: TouchState,
}
fn body(api: &RenderApi,
_document_id: &DocumentId,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
_pipeline_id: &PipelineId,
layout_size: &LayoutSize) {
let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
impl Example for App {
fn render(&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
layout_size: LayoutSize,
_pipeline_id: PipelineId,
_document_id: DocumentId) {
let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new());
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();
resources.add_image(
yuv_chanel1,
ImageDescriptor::new(100, 100, ImageFormat::A8, true),
ImageData::new(vec![127; 100 * 100]),
None,
);
resources.add_image(
yuv_chanel2,
ImageDescriptor::new(100, 100, ImageFormat::RG8, true),
ImageData::new(vec![0; 100 * 100 * 2]),
None,
);
resources.add_image(
yuv_chanel2_1,
ImageDescriptor::new(100, 100, ImageFormat::A8, true),
ImageData::new(vec![127; 100 * 100]),
None,
);
resources.add_image(
yuv_chanel3,
ImageDescriptor::new(100, 100, ImageFormat::A8, true),
ImageData::new(vec![127; 100 * 100]),
None,
);
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();
resources.add_image(
yuv_chanel1,
ImageDescriptor::new(100, 100, ImageFormat::A8, true),
ImageData::new(vec![127; 100 * 100]),
None,
);
resources.add_image(
yuv_chanel2,
ImageDescriptor::new(100, 100, ImageFormat::RG8, true),
ImageData::new(vec![0; 100 * 100 * 2]),
None,
);
resources.add_image(
yuv_chanel2_1,
ImageDescriptor::new(100, 100, ImageFormat::A8, true),
ImageData::new(vec![127; 100 * 100]),
None,
);
resources.add_image(
yuv_chanel3,
ImageDescriptor::new(100, 100, ImageFormat::A8, true),
ImageData::new(vec![127; 100 * 100]),
None,
);
builder.push_yuv_image(
LayoutRect::new(LayoutPoint::new(100.0, 0.0), LayoutSize::new(100.0, 100.0)),
Some(LocalClip::from(bounds)),
YuvData::NV12(yuv_chanel1, yuv_chanel2),
YuvColorSpace::Rec601,
ImageRendering::Auto,
);
builder.push_yuv_image(
LayoutRect::new(LayoutPoint::new(100.0, 0.0), LayoutSize::new(100.0, 100.0)),
Some(LocalClip::from(bounds)),
YuvData::NV12(yuv_chanel1, yuv_chanel2),
YuvColorSpace::Rec601,
ImageRendering::Auto,
);
builder.push_yuv_image(
LayoutRect::new(LayoutPoint::new(300.0, 0.0), LayoutSize::new(100.0, 100.0)),
Some(LocalClip::from(bounds)),
YuvData::PlanarYCbCr(yuv_chanel1, yuv_chanel2_1, yuv_chanel3),
YuvColorSpace::Rec601,
ImageRendering::Auto,
);
builder.push_yuv_image(
LayoutRect::new(LayoutPoint::new(300.0, 0.0), LayoutSize::new(100.0, 100.0)),
Some(LocalClip::from(bounds)),
YuvData::PlanarYCbCr(yuv_chanel1, yuv_chanel2_1, yuv_chanel3),
YuvColorSpace::Rec601,
ImageRendering::Auto,
);
builder.pop_stacking_context();
}
builder.pop_stacking_context();
}
lazy_static! {
static ref TOUCH_STATE: Mutex<TouchState> = Mutex::new(TouchState::new());
}
fn event_handler(event: &glutin::Event, document_id: DocumentId, api: &RenderApi) {
match *event {
glutin::Event::Touch(touch) => {
match TOUCH_STATE.lock().unwrap().handle_event(touch) {
TouchResult::Pan(pan) => {
api.set_pan(document_id, pan);
api.generate_frame(document_id, None);
fn on_event(&mut self,
event: glutin::Event,
api: &RenderApi,
document_id: DocumentId) -> bool {
match event {
glutin::Event::Touch(touch) => {
match self.touch_state.handle_event(touch) {
TouchResult::Pan(pan) => {
api.set_pan(document_id, pan);
api.generate_frame(document_id, None);
}
TouchResult::Zoom(zoom) => {
api.set_pinch_zoom(document_id, ZoomFactor::new(zoom));
api.generate_frame(document_id, None);
}
TouchResult::None => {}
}
TouchResult::Zoom(zoom) => {
api.set_pinch_zoom(document_id, ZoomFactor::new(zoom));
api.generate_frame(document_id, None);
}
TouchResult::None => {}
}
_ => ()
}
_ => ()
false
}
}
fn main() {
let mut app = App {
touch_state: TouchState::new(),
};
boilerplate::main_wrapper(&mut app, None);
}

Просмотреть файл

@ -63,11 +63,11 @@ void main(void) {
// Choose some arbitrary values to scale thickness,
// wave period etc.
// TODO(gw): Tune these to get closer to what Gecko uses.
float thickness = 0.2 * size.y;
float thickness = 0.15 * size.y;
vParams = vec4(thickness,
size.y * 0.5,
size.y * 0.75,
max(3.0, thickness * 2.0));
size.y * 0.5);
break;
}
}

Просмотреть файл

@ -2,104 +2,105 @@
* 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/. */
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct FreeListItemId(u32);
use std::marker::PhantomData;
use std::mem;
impl FreeListItemId {
#[inline]
pub fn new(value: u32) -> FreeListItemId {
FreeListItemId(value)
}
// TODO(gw): Add a weak free list handle. This is like a strong
// free list handle below, but will contain an epoch
// field. Weak handles will use a get_opt style API
// which returns an Option<T> instead of T.
#[inline]
pub fn value(&self) -> u32 {
self.0
// TODO(gw): Add an occupied list head, for fast
// iteration of the occupied list to implement
// retain() style functionality.
#[derive(Debug)]
pub struct FreeListHandle<T> {
index: u32,
_marker: PhantomData<T>,
}
enum SlotValue<T> {
Free,
Occupied(T),
}
impl<T> SlotValue<T> {
fn take(&mut self) -> T {
match mem::replace(self, SlotValue::Free) {
SlotValue::Free => unreachable!(),
SlotValue::Occupied(data) => data,
}
}
}
pub trait FreeListItem {
fn take(&mut self) -> Self;
fn next_free_id(&self) -> Option<FreeListItemId>;
fn set_next_free_id(&mut self, id: Option<FreeListItemId>);
}
struct FreeListIter<'a, T: 'a> {
items: &'a [T],
cur_index: Option<FreeListItemId>,
}
impl<'a, T: FreeListItem> Iterator for FreeListIter<'a, T> {
type Item = FreeListItemId;
fn next(&mut self) -> Option<Self::Item> {
self.cur_index.map(|free_id| {
self.cur_index = self.items[free_id.0 as usize].next_free_id();
free_id
})
}
struct Slot<T> {
next: Option<u32>,
value: SlotValue<T>,
}
pub struct FreeList<T> {
items: Vec<T>,
first_free_index: Option<FreeListItemId>,
alloc_count: usize,
slots: Vec<Slot<T>>,
free_list_head: Option<u32>,
}
impl<T: FreeListItem> FreeList<T> {
impl<T> FreeList<T> {
pub fn new() -> FreeList<T> {
FreeList {
items: Vec::new(),
first_free_index: None,
alloc_count: 0,
slots: Vec::new(),
free_list_head: None,
}
}
fn free_iter(&self) -> FreeListIter<T> {
FreeListIter {
items: &self.items,
cur_index: self.first_free_index,
pub fn get(&self, id: &FreeListHandle<T>) -> &T {
match self.slots[id.index as usize].value {
SlotValue::Free => unreachable!(),
SlotValue::Occupied(ref data) => data,
}
}
pub fn insert(&mut self, item: T) -> FreeListItemId {
self.alloc_count += 1;
match self.first_free_index {
pub fn get_mut(&mut self, id: &FreeListHandle<T>) -> &mut T {
match self.slots[id.index as usize].value {
SlotValue::Free => unreachable!(),
SlotValue::Occupied(ref mut data) => data,
}
}
pub fn insert(&mut self, item: T) -> FreeListHandle<T> {
match self.free_list_head {
Some(free_index) => {
let FreeListItemId(index) = free_index;
let free_item = &mut self.items[index as usize];
self.first_free_index = free_item.next_free_id();
*free_item = item;
free_index
let slot = &mut self.slots[free_index as usize];
// Remove from free list.
self.free_list_head = slot.next;
slot.next = None;
slot.value = SlotValue::Occupied(item);
FreeListHandle {
index: free_index,
_marker: PhantomData,
}
}
None => {
let item_id = FreeListItemId(self.items.len() as u32);
self.items.push(item);
item_id
let index = self.slots.len() as u32;
self.slots.push(Slot {
next: None,
value: SlotValue::Occupied(item),
});
FreeListHandle {
index,
_marker: PhantomData,
}
}
}
}
pub fn get(&self, id: FreeListItemId) -> &T {
debug_assert_eq!(self.free_iter().find(|&fid| fid==id), None);
&self.items[id.0 as usize]
}
pub fn get_mut(&mut self, id: FreeListItemId) -> &mut T {
debug_assert_eq!(self.free_iter().find(|&fid| fid==id), None);
&mut self.items[id.0 as usize]
}
#[allow(dead_code)]
pub fn len(&self) -> usize {
self.alloc_count
}
pub fn free(&mut self, id: FreeListItemId) -> T {
self.alloc_count -= 1;
let FreeListItemId(index) = id;
let item = &mut self.items[index as usize];
let data = item.take();
item.set_next_free_id(self.first_free_index);
self.first_free_index = Some(id);
data
pub fn free(&mut self, id: FreeListHandle<T>) -> T {
let slot = &mut self.slots[id.index as usize];
slot.next = self.free_list_head;
self.free_list_head = Some(id.index);
slot.value.take()
}
}

Просмотреть файл

@ -15,7 +15,7 @@ pub struct CachedGlyphInfo {
}
impl Resource for CachedGlyphInfo {
fn free(&self, texture_cache: &mut TextureCache) {
fn free(self, texture_cache: &mut TextureCache) {
if let Some(id) = self.texture_cache_id {
texture_cache.free(id);
}
@ -29,7 +29,7 @@ impl Resource for CachedGlyphInfo {
fn add_to_gpu_cache(&self,
texture_cache: &mut TextureCache,
gpu_cache: &mut GpuCache) {
if let Some(texture_cache_id) = self.texture_cache_id {
if let Some(texture_cache_id) = self.texture_cache_id.as_ref() {
let item = texture_cache.get_mut(texture_cache_id);
if let Some(mut request) = gpu_cache.request(&mut item.uv_rect_handle) {
request.push(item.uv_rect);
@ -89,6 +89,15 @@ impl GlyphCache {
}
}
pub fn clear(&mut self, texture_cache: &mut TextureCache) {
for (_, glyph_key_cache) in &mut self.glyph_key_caches {
glyph_key_cache.clear(texture_cache)
}
// We use this in on_memory_pressure where retaining memory allocations
// isn't desirable, so we completely remove the hash map instead of clearing it.
self.glyph_key_caches = FastHashMap::default();
}
pub fn clear_fonts<F>(&mut self, texture_cache: &mut TextureCache, key_fun: F)
where for<'r> F: Fn(&'r &FontInstanceKey) -> bool
{

Просмотреть файл

@ -296,7 +296,7 @@ impl FontContext {
fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate) {
match template {
&FontTemplate::Raw(ref bytes, index) => {
self.add_raw_font(&font_key, &**bytes, index);
self.add_raw_font(&font_key, bytes.clone(), index);
}
&FontTemplate::Native(ref native_font_handle) => {
self.add_native_font(&font_key, (*native_font_handle).clone());

Просмотреть файл

@ -190,6 +190,7 @@ impl RendererFrame {
pub enum ResultMsg {
RefreshShader(PathBuf),
NewFrame(DocumentId, RendererFrame, TextureUpdateList, BackendProfileCounters),
UpdateResources { updates: TextureUpdateList, cancel_rendering: bool },
}
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]

Просмотреть файл

@ -75,6 +75,7 @@ mod render_task;
mod resource_cache;
mod scene;
mod spring;
mod texture_allocator;
mod texture_cache;
mod tiling;
mod util;

Просмотреть файл

@ -16,10 +16,11 @@ use core_text;
use internal_types::FastHashMap;
use std::collections::hash_map::Entry;
use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
use api::{GlyphKey, SubpixelDirection};
use api::{GlyphKey};
use api::{FontInstanceKey, NativeFontHandle};
use gamma_lut::{GammaLut, Color as ColorLut};
use std::ptr;
use std::sync::Arc;
pub struct FontContext {
cg_fonts: FastHashMap<FontKey, CGFont>,
@ -163,13 +164,13 @@ impl FontContext {
self.cg_fonts.contains_key(font_key)
}
pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: &[u8], index: u32) {
pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: Arc<Vec<u8>>, index: u32) {
if self.cg_fonts.contains_key(font_key) {
return
}
assert_eq!(index, 0);
let data_provider = CGDataProvider::from_buffer(bytes);
let data_provider = CGDataProvider::from_buffer(&**bytes);
let cg_font = match CGFont::from_data_provider(data_provider) {
Err(_) => return,
Ok(cg_font) => cg_font,

Просмотреть файл

@ -16,6 +16,7 @@ use freetype::freetype::{FT_New_Memory_Face, FT_GlyphSlot, FT_LcdFilter};
use freetype::freetype::{FT_Done_Face, FT_Error, FT_Int32, FT_Get_Char_Index};
use std::{mem, ptr, slice};
use std::sync::Arc;
// This constant is not present in the freetype
// bindings due to bindgen not handling the way
@ -30,6 +31,9 @@ const GLYPH_LOAD_FLAGS: FT_Int32 = FT_LOAD_TARGET_LIGHT;
struct Face {
face: FT_Face,
// Raw byte data has to live until the font is deleted, according to
// https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_New_Memory_Face
_bytes: Arc<Vec<u8>>,
}
pub struct FontContext {
@ -84,7 +88,7 @@ impl FontContext {
self.faces.contains_key(font_key)
}
pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: &[u8], index: u32) {
pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: Arc<Vec<u8>>, index: u32) {
if !self.faces.contains_key(&font_key) {
let mut face: FT_Face = ptr::null_mut();
let result = unsafe {
@ -97,7 +101,7 @@ impl FontContext {
if result.succeeded() && !face.is_null() {
self.faces.insert(*font_key, Face {
face,
//_bytes: bytes
_bytes: bytes,
});
} else {
println!("WARN: webrender failed to load font {:?}", font_key);

Просмотреть файл

@ -8,6 +8,7 @@ use gamma_lut::{GammaLut, Color as ColorLut};
use internal_types::FastHashMap;
use dwrote;
use std::sync::Arc;
lazy_static! {
static ref DEFAULT_FONT_DESCRIPTOR: dwrote::FontDescriptor = dwrote::FontDescriptor {
@ -105,12 +106,12 @@ impl FontContext {
self.fonts.contains_key(font_key)
}
pub fn add_raw_font(&mut self, font_key: &FontKey, data: &[u8], index: u32) {
pub fn add_raw_font(&mut self, font_key: &FontKey, data: Arc<Vec<u8>>, index: u32) {
if self.fonts.contains_key(font_key) {
return
}
if let Some(font_file) = dwrote::FontFile::new_from_data(data) {
if let Some(font_file) = dwrote::FontFile::new_from_data(&**data) {
let face = font_file.create_face(index, dwrote::DWRITE_FONT_SIMULATIONS_NONE);
self.fonts.insert((*font_key).clone(), face);
} else {

Просмотреть файл

@ -12,6 +12,7 @@ use resource_cache::ResourceCache;
use scene::Scene;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use std::u32;
use texture_cache::TextureCache;
use time::precise_time_ns;
use thread_profiler::register_thread_with_profiler;
@ -584,6 +585,17 @@ impl RenderBackend {
self.documents.remove(&document);
}
}
ApiMsg::MemoryPressure => {
self.resource_cache.on_memory_pressure();
let pending_update = self.resource_cache.pending_updates();
let msg = ResultMsg::UpdateResources { updates: pending_update, cancel_rendering: true };
self.result_tx.send(msg).unwrap();
// We use new_frame_ready to wake up the renderer and get the
// resource updates processed, but the UpdateResources message
// will cancel rendering the frame.
self.notifier.lock().unwrap().as_mut().unwrap().new_frame_ready();
}
ApiMsg::ShutDown => {
let notifier = self.notifier.lock();
notifier.unwrap()

Просмотреть файл

@ -810,6 +810,7 @@ pub struct Renderer {
pub enum InitError {
Shader(ShaderError),
Thread(std::io::Error),
MaxTextureSize,
}
impl From<ShaderError> for InitError {
@ -839,13 +840,12 @@ impl Renderer {
/// ```
/// [rendereroptions]: struct.RendererOptions.html
pub fn new(gl: Rc<gl::Gl>, mut options: RendererOptions) -> Result<(Renderer, RenderApiSender), InitError> {
let (api_tx, api_rx) = try!{ channel::msg_channel() };
let (payload_tx, payload_rx) = try!{ channel::payload_channel() };
let (result_tx, result_rx) = channel();
let gl_type = gl.get_type();
register_thread_with_profiler("Compositor".to_owned());
let notifier = Arc::new(Mutex::new(None));
let file_watch_handler = FileWatcher {
@ -853,9 +853,28 @@ impl Renderer {
notifier: Arc::clone(&notifier),
};
let mut device = Device::new(gl,
options.resource_override_path.clone(),
Box::new(file_watch_handler));
let mut device = Device::new(
gl,
options.resource_override_path.clone(),
Box::new(file_watch_handler)
);
let device_max_size = device.max_texture_size();
// 512 is the minimum that the texture cache can work with.
// Broken GL contexts can return a max texture size of zero (See #1260). Better to
// gracefully fail now than panic as soon as a texture is allocated.
let min_texture_size = 512;
if device_max_size < min_texture_size {
println!("Device reporting insufficient max texture size ({})", device_max_size);
return Err(InitError::MaxTextureSize);
}
let max_device_size = cmp::max(
cmp::min(device_max_size, options.max_texture_size.unwrap_or(device_max_size)),
min_texture_size
);
register_thread_with_profiler("Compositor".to_owned());
// device-pixel ratio doesn't matter here - we are just creating resources.
device.begin_frame(1.0);
@ -1112,10 +1131,9 @@ impl Renderer {
options.precache_shaders)
};
let device_max_size = device.max_texture_size();
let max_texture_size = cmp::min(device_max_size, options.max_texture_size.unwrap_or(device_max_size));
let texture_cache = TextureCache::new(max_device_size);
let max_texture_size = texture_cache.max_texture_size();
let texture_cache = TextureCache::new(max_texture_size);
let backend_profile_counters = BackendProfileCounters::new();
let dummy_cache_texture_id = device.create_texture_ids(1, TextureTarget::Array)[0];
@ -1409,6 +1427,17 @@ impl Renderer {
self.current_frame = Some(frame);
}
ResultMsg::UpdateResources { updates, cancel_rendering } => {
self.pending_texture_updates.push(updates);
self.update_texture_cache();
// If we receive a NewFrame message followed by this one within
// the same update we need ot cancel the frame because we might
// have deleted the resources in use in the frame dut to a memory
// pressure event.
if cancel_rendering {
self.current_frame = None;
}
}
ResultMsg::RefreshShader(path) => {
self.pending_shader_updates.push(path);
}

Просмотреть файл

@ -553,7 +553,9 @@ impl ResourceCache {
for (loop_index, key) in glyph_keys.iter().enumerate() {
let glyph = glyph_key_cache.get(key, self.current_frame_id);
let cache_item = glyph.texture_cache_id.map(|image_id| self.texture_cache.get(image_id));
let cache_item = glyph.texture_cache_id
.as_ref()
.map(|image_id| self.texture_cache.get(image_id));
if let Some(cache_item) = cache_item {
f(loop_index, &cache_item.uv_rect_handle);
debug_assert!(texture_id == None ||
@ -594,7 +596,7 @@ impl ResourceCache {
tile,
};
let image_info = &self.cached_images.get(&key, self.current_frame_id);
let item = self.texture_cache.get(image_info.texture_cache_id);
let item = self.texture_cache.get(&image_info.texture_cache_id);
CacheItem {
texture_id: SourceTexture::TextureCache(item.texture_id),
uv_rect_handle: item.uv_rect_handle,
@ -756,25 +758,21 @@ impl ResourceCache {
};
match self.cached_images.entry(request, self.current_frame_id) {
Occupied(entry) => {
let image_id = entry.get().texture_cache_id;
Occupied(mut entry) => {
let entry = entry.get_mut();
// We should only get to this code path if the image
// definitely needs to be updated.
debug_assert!(entry.get().epoch != image_template.epoch);
self.texture_cache.update(image_id,
debug_assert!(entry.epoch != image_template.epoch);
self.texture_cache.update(&entry.texture_cache_id,
descriptor,
filter,
image_data,
image_template.dirty_rect);
// Update the cached epoch
debug_assert_eq!(self.current_frame_id, entry.get().last_access);
*entry.into_mut() = CachedImageInfo {
texture_cache_id: image_id,
epoch: image_template.epoch,
last_access: self.current_frame_id,
};
debug_assert_eq!(self.current_frame_id, entry.last_access);
entry.epoch = image_template.epoch;
image_template.dirty_rect = None;
}
Vacant(entry) => {
@ -799,6 +797,19 @@ impl ResourceCache {
self.state = State::Idle;
}
pub fn on_memory_pressure(&mut self) {
// This is drastic. It will basically flush everything out of the cache,
// and the next frame will have to rebuild all of its resources.
// We may want to look into something less extreme, but on the other hand this
// should only be used in situations where are running low enough on memory
// that we risk crashing if we don't do something about it.
// The advantage of clearing the cache completely is that it gets rid of any
// remaining fragmentation that could have persisted if we kept around the most
// recently used resources.
self.cached_images.clear(&mut self.texture_cache);
self.cached_glyphs.clear(&mut self.texture_cache);
}
pub fn clear_namespace(&mut self, namespace: IdNamespace) {
//TODO: use `retain` when we are on Rust-1.18
let image_keys: Vec<_> = self.resources.image_templates.images.keys()
@ -823,7 +834,7 @@ impl ResourceCache {
}
pub trait Resource {
fn free(&self, texture_cache: &mut TextureCache);
fn free(self, texture_cache: &mut TextureCache);
fn get_last_access_time(&self) -> FrameId;
fn set_last_access_time(&mut self, frame_id: FrameId);
fn add_to_gpu_cache(&self,
@ -832,7 +843,7 @@ pub trait Resource {
}
impl Resource for CachedImageInfo {
fn free(&self, texture_cache: &mut TextureCache) {
fn free(self, texture_cache: &mut TextureCache) {
texture_cache.free(self.texture_cache_id);
}
fn get_last_access_time(&self) -> FrameId {
@ -844,7 +855,7 @@ impl Resource for CachedImageInfo {
fn add_to_gpu_cache(&self,
texture_cache: &mut TextureCache,
gpu_cache: &mut GpuCache) {
let item = texture_cache.get_mut(self.texture_cache_id);
let item = texture_cache.get_mut(&self.texture_cache_id);
if let Some(mut request) = gpu_cache.request(&mut item.uv_rect_handle) {
request.push(item.uv_rect);
}

Просмотреть файл

@ -0,0 +1,220 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize};
use std::slice::Iter;
use util;
/// The minimum number of pixels on each side that we require for rects to be classified as
/// "medium" within the free list.
const MINIMUM_MEDIUM_RECT_SIZE: u32 = 16;
/// The minimum number of pixels on each side that we require for rects to be classified as
/// "large" within the free list.
const MINIMUM_LARGE_RECT_SIZE: u32 = 32;
/// A texture allocator using the guillotine algorithm with the rectangle merge improvement. See
/// sections 2.2 and 2.2.5 in "A Thousand Ways to Pack the Bin - A Practical Approach to Two-
/// Dimensional Rectangle Bin Packing":
///
/// http://clb.demon.fi/files/RectangleBinPack.pdf
///
/// This approach was chosen because of its simplicity, good performance, and easy support for
/// dynamic texture deallocation.
pub struct GuillotineAllocator {
texture_size: DeviceUintSize,
free_list: FreeRectList,
allocations: u32,
dirty: bool,
}
impl GuillotineAllocator {
pub fn new(texture_size: DeviceUintSize) -> GuillotineAllocator {
let mut page = GuillotineAllocator {
texture_size,
free_list: FreeRectList::new(),
allocations: 0,
dirty: false,
};
page.clear();
page
}
fn find_index_of_best_rect_in_bin(&self, bin: FreeListBin, requested_dimensions: &DeviceUintSize)
-> Option<FreeListIndex> {
let mut smallest_index_and_area = None;
for (candidate_index, candidate_rect) in self.free_list.iter(bin).enumerate() {
if !requested_dimensions.fits_inside(&candidate_rect.size) {
continue
}
let candidate_area = candidate_rect.size.width * candidate_rect.size.height;
smallest_index_and_area = Some((candidate_index, candidate_area));
break
}
smallest_index_and_area.map(|(index, _)| FreeListIndex(bin, index))
}
/// Find a suitable rect in the free list. We choose the smallest such rect
/// in terms of area (Best-Area-Fit, BAF).
fn find_index_of_best_rect(&self, requested_dimensions: &DeviceUintSize)
-> Option<FreeListIndex> {
let bin = FreeListBin::for_size(requested_dimensions);
for &target_bin in &[FreeListBin::Small, FreeListBin::Medium, FreeListBin::Large] {
if bin <= target_bin {
if let Some(index) = self.find_index_of_best_rect_in_bin(target_bin,
requested_dimensions) {
return Some(index);
}
}
}
None
}
pub fn allocate(&mut self, requested_dimensions: &DeviceUintSize) -> Option<DeviceUintPoint> {
if requested_dimensions.width == 0 || requested_dimensions.height == 0 {
return Some(DeviceUintPoint::new(0, 0))
}
let index = match self.find_index_of_best_rect(requested_dimensions) {
None => return None,
Some(index) => index,
};
// Remove the rect from the free list and decide how to guillotine it. We choose the split
// that results in the single largest area (Min Area Split Rule, MINAS).
let chosen_rect = self.free_list.remove(index);
let candidate_free_rect_to_right =
DeviceUintRect::new(
DeviceUintPoint::new(chosen_rect.origin.x + requested_dimensions.width, chosen_rect.origin.y),
DeviceUintSize::new(chosen_rect.size.width - requested_dimensions.width, requested_dimensions.height));
let candidate_free_rect_to_bottom =
DeviceUintRect::new(
DeviceUintPoint::new(chosen_rect.origin.x, chosen_rect.origin.y + requested_dimensions.height),
DeviceUintSize::new(requested_dimensions.width, chosen_rect.size.height - requested_dimensions.height));
let candidate_free_rect_to_right_area = candidate_free_rect_to_right.size.width *
candidate_free_rect_to_right.size.height;
let candidate_free_rect_to_bottom_area = candidate_free_rect_to_bottom.size.width *
candidate_free_rect_to_bottom.size.height;
// Guillotine the rectangle.
let new_free_rect_to_right;
let new_free_rect_to_bottom;
if candidate_free_rect_to_right_area > candidate_free_rect_to_bottom_area {
new_free_rect_to_right = DeviceUintRect::new(
candidate_free_rect_to_right.origin,
DeviceUintSize::new(candidate_free_rect_to_right.size.width,
chosen_rect.size.height));
new_free_rect_to_bottom = candidate_free_rect_to_bottom
} else {
new_free_rect_to_right = candidate_free_rect_to_right;
new_free_rect_to_bottom =
DeviceUintRect::new(candidate_free_rect_to_bottom.origin,
DeviceUintSize::new(chosen_rect.size.width,
candidate_free_rect_to_bottom.size.height))
}
// Add the guillotined rects back to the free list. If any changes were made, we're now
// dirty since coalescing might be able to defragment.
if !util::rect_is_empty(&new_free_rect_to_right) {
self.free_list.push(&new_free_rect_to_right);
self.dirty = true
}
if !util::rect_is_empty(&new_free_rect_to_bottom) {
self.free_list.push(&new_free_rect_to_bottom);
self.dirty = true
}
// Bump the allocation counter.
self.allocations += 1;
// Return the result.
Some(chosen_rect.origin)
}
fn clear(&mut self) {
self.free_list = FreeRectList::new();
self.free_list.push(&DeviceUintRect::new(
DeviceUintPoint::zero(),
self.texture_size));
self.allocations = 0;
self.dirty = false;
}
}
/// A binning free list. Binning is important to avoid sifting through lots of small strips when
/// allocating many texture items.
struct FreeRectList {
small: Vec<DeviceUintRect>,
medium: Vec<DeviceUintRect>,
large: Vec<DeviceUintRect>,
}
impl FreeRectList {
fn new() -> FreeRectList {
FreeRectList {
small: vec![],
medium: vec![],
large: vec![],
}
}
fn push(&mut self, rect: &DeviceUintRect) {
match FreeListBin::for_size(&rect.size) {
FreeListBin::Small => self.small.push(*rect),
FreeListBin::Medium => self.medium.push(*rect),
FreeListBin::Large => self.large.push(*rect),
}
}
fn remove(&mut self, index: FreeListIndex) -> DeviceUintRect {
match index.0 {
FreeListBin::Small => self.small.swap_remove(index.1),
FreeListBin::Medium => self.medium.swap_remove(index.1),
FreeListBin::Large => self.large.swap_remove(index.1),
}
}
fn iter(&self, bin: FreeListBin) -> Iter<DeviceUintRect> {
match bin {
FreeListBin::Small => self.small.iter(),
FreeListBin::Medium => self.medium.iter(),
FreeListBin::Large => self.large.iter(),
}
}
}
#[derive(Debug, Clone, Copy)]
struct FreeListIndex(FreeListBin, usize);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
enum FreeListBin {
Small,
Medium,
Large,
}
impl FreeListBin {
fn for_size(size: &DeviceUintSize) -> FreeListBin {
if size.width >= MINIMUM_LARGE_RECT_SIZE && size.height >= MINIMUM_LARGE_RECT_SIZE {
FreeListBin::Large
} else if size.width >= MINIMUM_MEDIUM_RECT_SIZE &&
size.height >= MINIMUM_MEDIUM_RECT_SIZE {
FreeListBin::Medium
} else {
debug_assert!(size.width > 0 && size.height > 0);
FreeListBin::Small
}
}
}
trait FitsInside {
fn fits_inside(&self, other: &Self) -> bool;
}
impl FitsInside for DeviceUintSize {
fn fits_inside(&self, other: &DeviceUintSize) -> bool {
self.width <= other.width && self.height <= other.height
}
}

Просмотреть файл

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use device::TextureFilter;
use freelist::{FreeList, FreeListItem, FreeListItemId};
use freelist::{FreeList, FreeListHandle};
use gpu_cache::GpuCacheHandle;
use internal_types::{FastHashMap, TextureUpdate, TextureUpdateOp, UvRect};
use internal_types::{CacheTextureId, RenderTargetMode, TextureUpdateList};
@ -46,7 +46,7 @@ const COALESCING_TIMEOUT: u64 = 100;
/// the timeout.
const COALESCING_TIMEOUT_CHECKING_INTERVAL: usize = 256;
pub type TextureCacheItemId = FreeListItemId;
pub type TextureCacheItemId = FreeListHandle<TextureCacheItem>;
enum CoalescingStatus {
Changed,
@ -72,7 +72,7 @@ pub struct TexturePage {
}
impl TexturePage {
pub fn new(texture_id: CacheTextureId, texture_size: DeviceUintSize) -> TexturePage {
fn new(texture_id: CacheTextureId, texture_size: DeviceUintSize) -> TexturePage {
let mut page = TexturePage {
texture_id,
texture_size,
@ -117,7 +117,7 @@ impl TexturePage {
None
}
pub fn can_allocate(&self, requested_dimensions: &DeviceUintSize) -> bool {
fn can_allocate(&self, requested_dimensions: &DeviceUintSize) -> bool {
self.find_index_of_best_rect(requested_dimensions).is_some()
}
@ -280,7 +280,7 @@ impl TexturePage {
changed
}
pub fn clear(&mut self) {
fn clear(&mut self) {
self.free_list = FreeRectList::new();
self.free_list.push(&DeviceUintRect::new(
DeviceUintPoint::zero(),
@ -457,38 +457,6 @@ pub struct TextureCacheItem {
pub user_data: [f32; 2],
}
// Structure squat the width/height fields to maintain the free list information :)
impl FreeListItem for TextureCacheItem {
fn take(&mut self) -> Self {
let data = self.clone();
self.texture_id = CacheTextureId(0);
data
}
fn next_free_id(&self) -> Option<FreeListItemId> {
if self.allocated_rect.size.width == 0 {
debug_assert_eq!(self.allocated_rect.size.height, 0);
None
} else {
debug_assert_eq!(self.allocated_rect.size.width, 1);
Some(FreeListItemId::new(self.allocated_rect.size.height))
}
}
fn set_next_free_id(&mut self, id: Option<FreeListItemId>) {
match id {
Some(id) => {
self.allocated_rect.size.width = 1;
self.allocated_rect.size.height = id.value();
}
None => {
self.allocated_rect.size.width = 0;
self.allocated_rect.size.height = 0;
}
}
}
}
impl TextureCacheItem {
fn new(texture_id: CacheTextureId,
rect: DeviceUintRect,
@ -587,7 +555,7 @@ pub enum AllocationKind {
#[derive(Debug)]
pub struct AllocationResult {
image_id: TextureCacheItemId,
new_image_id: Option<TextureCacheItemId>,
kind: AllocationKind,
item: TextureCacheItem,
}
@ -636,7 +604,7 @@ impl TextureCache {
)
}
// If item_id is None, create a new id, otherwise reuse it.
// If existing_item_id is None, create a new id, otherwise reuse it.
fn allocate_impl(
&mut self,
requested_width: u32,
@ -645,7 +613,7 @@ impl TextureCache {
filter: TextureFilter,
user_data: [f32; 2],
profile: &mut TextureCacheProfileCounters,
item_id: Option<TextureCacheItemId>
existing_item_id: Option<&TextureCacheItemId>
) -> AllocationResult {
let requested_size = DeviceUintSize::new(requested_width, requested_height);
@ -657,6 +625,17 @@ impl TextureCache {
if filter == TextureFilter::Nearest {
// Fall back to standalone texture allocation.
let texture_id = self.cache_id_list.allocate();
let update_op = TextureUpdate {
id: texture_id,
op: texture_create_op(DeviceUintSize::new(requested_width,
requested_height),
format,
RenderTargetMode::None,
filter),
};
self.pending_updates.push(update_op);
let cache_item = TextureCacheItem::new(
texture_id,
DeviceUintRect::new(DeviceUintPoint::zero(), requested_size),
@ -664,15 +643,21 @@ impl TextureCache {
user_data
);
let image_id = match item_id {
Some(id) => id,
None => self.items.insert(cache_item.clone()),
let new_image_id = match existing_item_id {
Some(id) => {
*self.items.get_mut(id) = cache_item.clone();
None
}
None => {
let id = self.items.insert(cache_item.clone());
Some(id)
}
};
return AllocationResult {
item: self.items.get(image_id).clone(),
item: cache_item,
kind: AllocationKind::Standalone,
image_id,
new_image_id,
}
}
@ -751,7 +736,10 @@ impl TextureCache {
let update_op = TextureUpdate {
id: texture_id,
op: texture_create_op(texture_size, format, mode),
op: texture_create_op(texture_size,
format,
mode,
filter),
};
self.pending_updates.push(update_op);
@ -777,21 +765,27 @@ impl TextureCache {
user_data
);
let image_id = match item_id {
Some(id) => id,
None => self.items.insert(cache_item.clone()),
let new_image_id = match existing_item_id {
Some(id) => {
*self.items.get_mut(id) = cache_item.clone();
None
}
None => {
let id = self.items.insert(cache_item.clone());
Some(id)
}
};
AllocationResult {
item: cache_item,
kind: AllocationKind::TexturePage,
image_id,
new_image_id,
}
}
pub fn update(
&mut self,
image_id: TextureCacheItemId,
image_id: &TextureCacheItemId,
descriptor: ImageDescriptor,
filter: TextureFilter,
data: ImageData,
@ -1002,14 +996,14 @@ impl TextureCache {
}
}
result.image_id
result.new_image_id.unwrap()
}
pub fn get(&self, id: TextureCacheItemId) -> &TextureCacheItem {
pub fn get(&self, id: &TextureCacheItemId) -> &TextureCacheItem {
self.items.get(id)
}
pub fn get_mut(&mut self, id: TextureCacheItemId) -> &mut TextureCacheItem {
pub fn get_mut(&mut self, id: &TextureCacheItemId) -> &mut TextureCacheItem {
self.items.get_mut(id)
}
@ -1034,13 +1028,16 @@ impl TextureCache {
}
}
fn texture_create_op(texture_size: DeviceUintSize, format: ImageFormat, mode: RenderTargetMode)
fn texture_create_op(texture_size: DeviceUintSize,
format: ImageFormat,
mode: RenderTargetMode,
filter: TextureFilter)
-> TextureUpdateOp {
TextureUpdateOp::Create {
width: texture_size.width,
height: texture_size.height,
format,
filter: TextureFilter::Linear,
filter,
mode,
data: None,
}

Просмотреть файл

@ -5,7 +5,7 @@
use border::{BorderCornerInstance, BorderCornerSide};
use device::TextureId;
use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheUpdateList};
use internal_types::{BatchTextures, CacheTextureId};
use internal_types::BatchTextures;
use internal_types::{FastHashMap, SourceTexture};
use mask_cache::MaskCacheInfo;
use prim_store::{CLIP_DATA_GPU_BLOCKS, DeferredResolve, ImagePrimitiveKind, PrimitiveCacheKey};
@ -18,7 +18,7 @@ use renderer::BlendMode;
use renderer::ImageBufferKind;
use resource_cache::ResourceCache;
use std::{f32, i32, mem, usize};
use texture_cache::TexturePage;
use texture_allocator::GuillotineAllocator;
use util::{TransformedRect, TransformedRectKind};
use api::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey};
use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize, FontInstanceKey};
@ -800,7 +800,7 @@ struct TextureAllocator {
// render target allocation - this use case doesn't need
// to deal with coalescing etc that the general texture
// cache allocator requires.
page_allocator: TexturePage,
allocator: GuillotineAllocator,
// Track the used rect of the render target, so that
// we can set a scissor rect and only clear to the
@ -811,13 +811,13 @@ struct TextureAllocator {
impl TextureAllocator {
fn new(size: DeviceUintSize) -> TextureAllocator {
TextureAllocator {
page_allocator: TexturePage::new(CacheTextureId(0), size),
allocator: GuillotineAllocator::new(size),
used_rect: DeviceIntRect::zero(),
}
}
fn allocate(&mut self, size: &DeviceUintSize) -> Option<DeviceUintPoint> {
let origin = self.page_allocator.allocate(size);
let origin = self.allocator.allocate(size);
if let Some(origin) = origin {
// TODO(gw): We need to make all the device rects

Просмотреть файл

@ -170,6 +170,8 @@ pub enum ApiMsg {
ExternalEvent(ExternalEvent),
/// Removes all resources associated with a namespace.
ClearNamespace(IdNamespace),
/// Flush from the caches anything that isn't necessary, to free some memory.
MemoryPressure,
ShutDown,
}
@ -189,6 +191,7 @@ impl fmt::Debug for ApiMsg {
ApiMsg::VRCompositorCommand(..) => "ApiMsg::VRCompositorCommand",
ApiMsg::ExternalEvent(..) => "ApiMsg::ExternalEvent",
ApiMsg::ClearNamespace(..) => "ApiMsg::ClearNamespace",
ApiMsg::MemoryPressure => "ApiMsg::MemoryPressure",
ApiMsg::ShutDown => "ApiMsg::ShutDown",
})
}
@ -399,6 +402,10 @@ impl RenderApi {
self.api_sender.send(msg).unwrap();
}
pub fn notify_memory_pressure(&self) {
self.api_sender.send(ApiMsg::MemoryPressure).unwrap();
}
pub fn shut_down(&self) {
self.api_sender.send(ApiMsg::ShutDown).unwrap();
}