зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1388719 - Update webrender to commit 101c69db1a989fe89c308dabd53cf50aedfe4a96. r=jrmuizel
MozReview-Commit-ID: 10VsggYNyo3 --HG-- extra : rebase_source : b123e2ee5f51d958ec806cb943d042c4ee0b23cf
This commit is contained in:
Родитель
41dbd09508
Коммит
ea6719b16d
|
@ -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(¬ifier),
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче