Bug 1374730 - Update webrender to cset 519e51986308fc11d6ba6771f1c11ea6a3133921. r=jrmuizel

MozReview-Commit-ID: 81YYW87APLn

--HG--
rename : gfx/webrender_traits/src/channel_ipc.rs => gfx/webrender_api/src/channel_ipc.rs
rename : gfx/webrender_traits/src/channel_mpsc.rs => gfx/webrender_api/src/channel_mpsc.rs
rename : gfx/webrender_traits/src/lib.rs => gfx/webrender_api/src/lib.rs
extra : rebase_source : 4f3a7bbda59241979fdafbbfb4a5e30c0375f8e6
This commit is contained in:
Kartikaya Gupta 2017-07-10 07:19:51 -04:00
parent 95251ef8fc
commit d56daf3647
60 changed files with 2271 additions and 2351 deletions

View File

@ -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: 1d6348023a4a4fdd89dce038640c5da906005acc
Latest Commit: 519e51986308fc11d6ba6771f1c11ea6a3133921

View File

@ -1,6 +1,6 @@
[package]
name = "webrender"
version = "0.43.0"
version = "0.47.0"
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
license = "MPL-2.0"
repository = "https://github.com/servo/webrender"
@ -10,24 +10,24 @@ build = "build.rs"
default = ["freetype-lib", "webgl"]
freetype-lib = ["freetype/servo-freetype-sys"]
profiler = ["thread_profiler/thread_profiler"]
webgl = ["offscreen_gl_context", "webrender_traits/webgl"]
webgl = ["offscreen_gl_context", "webrender_api/webgl"]
[dependencies]
app_units = "0.5"
bincode = "0.8"
bit-set = "0.4"
byteorder = "1.0"
euclid = "0.15"
euclid = "0.15.1"
fnv = "1.0"
gleam = "0.4.3"
gleam = "0.4.7"
lazy_static = "0.2"
log = "0.3"
num-traits = "0.1.32"
offscreen_gl_context = {version = "0.11", features = ["serde", "osmesa"], optional = true}
time = "0.1"
rayon = "0.8"
webrender_traits = {path = "../webrender_traits"}
bitflags = "0.7"
webrender_api = {path = "../webrender_api"}
bitflags = "0.9"
gamma-lut = "0.2"
thread_profiler = "0.1.1"
plane-split = "0.6"
@ -45,4 +45,4 @@ dwrote = "0.4"
[target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.8.0"
core-text = { version = "5.0.1", features = ["lion"] }
core-text = { version = "6.1", default-features = false }

View File

@ -3,12 +3,11 @@
extern crate rand;
extern crate test;
extern crate webrender;
extern crate webrender_traits;
use rand::Rng;
use test::Bencher;
use webrender::TexturePage;
use webrender_traits::{DeviceUintSize as Size};
use webrender::api::{DeviceUintSize as Size};
#[bench]
fn bench_coalesce(b: &mut Bencher) {

View File

@ -5,7 +5,6 @@
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate webrender_traits;
#[macro_use]
extern crate lazy_static;
@ -15,7 +14,7 @@ mod boilerplate;
use boilerplate::HandyDandyRectBuilder;
use std::sync::Mutex;
use webrender_traits::*;
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.
@ -23,25 +22,21 @@ use webrender_traits::*;
fn body(_api: &RenderApi,
builder: &mut DisplayListBuilder,
_pipeline_id: &PipelineId,
_layout_size: &LayoutSize)
{
_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(webrender_traits::ScrollPolicy::Scrollable,
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
Some(PropertyBinding::Binding(PropertyBindingKey::new(42))),
TransformStyle::Flat,
None,
webrender_traits::MixBlendMode::Normal,
MixBlendMode::Normal,
Vec::new());
// Fill it with a white rect
let clip = builder.push_clip_region(&bounds, vec![], None);
builder.push_rect(bounds,
clip,
ColorF::new(1.0, 1.0, 1.0, 1.0));
builder.push_rect(bounds, None, ColorF::new(1.0, 1.0, 1.0, 1.0));
builder.pop_stacking_context();
}

View File

@ -7,21 +7,22 @@ extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate webrender_traits;
#[macro_use]
extern crate lazy_static;
#[path="common/boilerplate.rs"]
mod boilerplate;
use app_units::Au;
use gleam::gl;
use boilerplate::HandyDandyRectBuilder;
use euclid::vec2;
use glutin::TouchPhase;
use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use webrender_traits::{ClipRegionToken, ColorF, DisplayListBuilder, Epoch, GlyphInstance};
use webrender_traits::{DeviceIntPoint, DeviceUintSize, LayoutPoint, LayoutRect, LayoutSize};
use webrender_traits::{ImageData, ImageDescriptor, ImageFormat};
use webrender_traits::{PipelineId, RenderApi, TransformStyle, BoxShadowClipMode};
use euclid::vec2;
use std::sync::Mutex;
use webrender::api::*;
#[derive(Debug)]
enum Gesture {
@ -168,145 +169,62 @@ fn load_file(name: &str) -> Vec<u8> {
buffer
}
struct Notifier {
window_proxy: glutin::WindowProxy,
}
impl Notifier {
fn new(window_proxy: glutin::WindowProxy) -> Notifier {
Notifier {
window_proxy: window_proxy,
}
}
}
impl webrender_traits::RenderNotifier for Notifier {
fn new_frame_ready(&mut self) {
#[cfg(not(target_os = "android"))]
self.window_proxy.wakeup_event_loop();
}
fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
#[cfg(not(target_os = "android"))]
self.window_proxy.wakeup_event_loop();
}
}
fn push_sub_clip(api: &RenderApi, builder: &mut DisplayListBuilder, bounds: &LayoutRect)
-> ClipRegionToken {
let mask_image = api.generate_image_key();
api.add_image(mask_image,
ImageDescriptor::new(2, 2, ImageFormat::A8, true),
ImageData::new(vec![0, 80, 180, 255]),
None);
let mask = webrender_traits::ImageMask {
image: mask_image,
rect: LayoutRect::new(LayoutPoint::new(75.0, 75.0), LayoutSize::new(100.0, 100.0)),
repeat: false,
};
let complex = webrender_traits::ComplexClipRegion::new(
LayoutRect::new(LayoutPoint::new(50.0, 50.0), LayoutSize::new(100.0, 100.0)),
webrender_traits::BorderRadius::uniform(20.0));
builder.push_clip_region(bounds, vec![complex], Some(mask))
}
fn main() {
let args: Vec<String> = env::args().collect();
let res_path = if args.len() > 1 {
Some(PathBuf::from(&args[1]))
} else {
None
};
boilerplate::main_wrapper(body, event_handler, None);
}
let window = glutin::WindowBuilder::new()
.with_title("WebRender Sample")
.with_multitouch()
.with_gl(glutin::GlRequest::GlThenGles {
opengl_version: (3, 2),
opengles_version: (3, 0)
})
.build()
.unwrap();
unsafe {
window.make_current().ok();
}
let gl = match gl::GlType::default() {
gl::GlType::Gl => unsafe { gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
gl::GlType::Gles => unsafe { gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
};
println!("OpenGL version {}", gl.get_string(gl::VERSION));
println!("Shader resource path: {:?}", res_path);
let (width, height) = window.get_inner_size_pixels().unwrap();
let opts = webrender::RendererOptions {
resource_override_path: res_path,
debug: true,
precache_shaders: true,
device_pixel_ratio: window.hidpi_factor(),
.. Default::default()
};
let size = DeviceUintSize::new(width, height);
let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
let api = sender.create_api();
let notifier = Box::new(Notifier::new(window.create_window_proxy()));
renderer.set_render_notifier(notifier);
let epoch = Epoch(0);
let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
let pipeline_id = PipelineId(0, 0);
let layout_size = LayoutSize::new(width as f32, height as f32);
let mut builder = webrender_traits::DisplayListBuilder::new(pipeline_id, layout_size);
let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
fn body(api: &RenderApi,
builder: &mut DisplayListBuilder,
_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,
webrender_traits::MixBlendMode::Normal,
MixBlendMode::Normal,
Vec::new());
let clip = push_sub_clip(&api, &mut builder, &bounds);
builder.push_rect(LayoutRect::new(LayoutPoint::new(100.0, 100.0), LayoutSize::new(100.0, 100.0)),
clip,
ColorF::new(0.0, 1.0, 0.0, 1.0));
let clip = push_sub_clip(&api, &mut builder, &bounds);
builder.push_rect(LayoutRect::new(LayoutPoint::new(250.0, 100.0), LayoutSize::new(100.0, 100.0)),
clip,
ColorF::new(0.0, 1.0, 0.0, 1.0));
let border_side = webrender_traits::BorderSide {
color: ColorF::new(0.0, 0.0, 1.0, 1.0),
style: webrender_traits::BorderStyle::Groove,
let image_mask_key = api.generate_image_key();
api.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 border_widths = webrender_traits::BorderWidths {
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 = webrender_traits::BorderDetails::Normal(webrender_traits::NormalBorder {
let border_details = BorderDetails::Normal(NormalBorder {
top: border_side,
right: border_side,
bottom: border_side,
left: border_side,
radius: webrender_traits::BorderRadius::uniform(20.0),
radius: BorderRadius::uniform(20.0),
});
let clip = push_sub_clip(&api, &mut builder, &bounds);
builder.push_border(LayoutRect::new(LayoutPoint::new(100.0, 100.0), LayoutSize::new(100.0, 100.0)),
clip,
border_widths,
border_details);
let bounds = (100, 100).to(200, 200);
builder.push_border(bounds, None, border_widths, border_details);
if false { // draw text?
@ -314,8 +232,7 @@ fn main() {
let font_bytes = load_file("res/FreeSans.ttf");
api.add_raw_font(font_key, font_bytes, 0);
let text_bounds = LayoutRect::new(LayoutPoint::new(100.0, 200.0), LayoutSize::new(700.0, 300.0));
let text_bounds = (100, 200).by(700, 300);
let glyphs = vec![
GlyphInstance {
index: 48,
@ -367,9 +284,8 @@ fn main() {
},
];
let clip = builder.push_clip_region(&bounds, Vec::new(), None);
builder.push_text(text_bounds,
clip,
None,
&glyphs,
font_key,
ColorF::new(1.0, 1.0, 0.0, 1.0),
@ -379,19 +295,17 @@ fn main() {
}
if false { // draw box shadow?
let rect = LayoutRect::new(LayoutPoint::new(0.0, 0.0), LayoutSize::new(0.0, 0.0));
let simple_box_bounds = LayoutRect::new(LayoutPoint::new(20.0, 200.0),
LayoutSize::new(50.0, 50.0));
let rect = LayoutRect::zero();
let simple_box_bounds = (20, 200).by(50, 50);
let offset = vec2(10.0, 10.0);
let color = ColorF::new(1.0, 1.0, 1.0, 1.0);
let blur_radius = 0.0;
let spread_radius = 0.0;
let simple_border_radius = 8.0;
let box_shadow_type = BoxShadowClipMode::Inset;
let full_screen_clip = builder.push_clip_region(&bounds, Vec::new(), None);
builder.push_box_shadow(rect,
full_screen_clip,
Some(LocalClip::from(bounds)),
simple_box_bounds,
offset,
color,
@ -401,57 +315,29 @@ fn main() {
box_shadow_type);
}
builder.pop_clip_id();
builder.pop_stacking_context();
}
api.set_display_list(
Some(root_background_color),
epoch,
LayoutSize::new(width as f32, height as f32),
builder.finalize(),
true);
api.set_root_pipeline(pipeline_id);
api.generate_frame(None);
lazy_static! {
static ref TOUCH_STATE: Mutex<TouchState> = Mutex::new(TouchState::new());
}
let mut touch_state = TouchState::new();
'outer: for event in window.wait_events() {
let mut events = Vec::new();
events.push(event);
for event in window.poll_events() {
events.push(event);
}
for event in events {
match event {
glutin::Event::Closed |
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
_, Some(glutin::VirtualKeyCode::P)) => {
let enable_profiler = !renderer.get_profiler_enabled();
renderer.set_profiler_enabled(enable_profiler);
fn event_handler(event: &glutin::Event, api: &RenderApi) {
match *event {
glutin::Event::Touch(touch) => {
match TOUCH_STATE.lock().unwrap().handle_event(touch) {
TouchResult::Pan(pan) => {
api.set_pan(pan);
api.generate_frame(None);
}
glutin::Event::Touch(touch) => {
match touch_state.handle_event(touch) {
TouchResult::Pan(pan) => {
api.set_pan(pan);
api.generate_frame(None);
}
TouchResult::Zoom(zoom) => {
api.set_pinch_zoom(webrender_traits::ZoomFactor::new(zoom));
api.generate_frame(None);
}
TouchResult::None => {}
}
TouchResult::Zoom(zoom) => {
api.set_pinch_zoom(ZoomFactor::new(zoom));
api.generate_frame(None);
}
_ => ()
TouchResult::None => {}
}
}
renderer.update();
renderer.render(DeviceUintSize::new(width, height));
window.swap_buffers().ok();
_ => ()
}
}

View File

@ -7,7 +7,6 @@ extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate webrender_traits;
extern crate rayon;
#[path="common/boilerplate.rs"]
@ -20,26 +19,26 @@ use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use webrender_traits as wt;
use webrender::api;
// This example shows how to implement a very basic BlobImageRenderer that can only render
// a checkerboard pattern.
// The deserialized command list internally used by this example is just a color.
type ImageRenderingCommands = wt::ColorU;
type ImageRenderingCommands = api::ColorU;
// Serialize/deserialze the blob.
// Ror real usecases you should probably use serde rather than doing it by hand.
fn serialize_blob(color: wt::ColorU) -> Vec<u8> {
fn serialize_blob(color: api::ColorU) -> Vec<u8> {
vec![color.r, color.g, color.b, color.a]
}
fn deserialize_blob(blob: &[u8]) -> Result<ImageRenderingCommands, ()> {
let mut iter = blob.iter();
return match (iter.next(), iter.next(), iter.next(), iter.next()) {
(Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(wt::ColorU::new(r, g, b, a)),
(Some(&a), None, None, None) => Ok(wt::ColorU::new(a, a, a, a)),
(Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(api::ColorU::new(r, g, b, a)),
(Some(&a), None, None, None) => Ok(api::ColorU::new(a, a, a, a)),
_ => Err(()),
}
}
@ -48,9 +47,9 @@ fn deserialize_blob(blob: &[u8]) -> Result<ImageRenderingCommands, ()> {
// actual image data.
fn render_blob(
commands: Arc<ImageRenderingCommands>,
descriptor: &wt::BlobImageDescriptor,
tile: Option<wt::TileOffset>,
) -> wt::BlobImageResult {
descriptor: &api::BlobImageDescriptor,
tile: Option<api::TileOffset>
) -> api::BlobImageResult {
let color = *commands;
// Allocate storage for the result. Right now the resource cache expects the
@ -77,17 +76,17 @@ fn render_blob(
let tc = if tile_checker { 0 } else { (1 - checker) * 40 };
match descriptor.format {
wt::ImageFormat::BGRA8 => {
api::ImageFormat::BGRA8 => {
texels.push(color.b * checker + tc);
texels.push(color.g * checker + tc);
texels.push(color.r * checker + tc);
texels.push(color.a * checker + tc);
}
wt::ImageFormat::A8 => {
api::ImageFormat::A8 => {
texels.push(color.a * checker + tc);
}
_ => {
return Err(wt::BlobImageError::Other(format!(
return Err(api::BlobImageError::Other(format!(
"Usupported image format {:?}",
descriptor.format
)));
@ -96,7 +95,7 @@ fn render_blob(
}
}
Ok(wt::RasterizedBlobImage {
Ok(api::RasterizedBlobImage {
data: texels,
width: descriptor.width,
height: descriptor.height,
@ -111,18 +110,18 @@ struct CheckerboardRenderer {
workers: Arc<ThreadPool>,
// the workers will use an mpsc channel to communicate the result.
tx: Sender<(wt::BlobImageRequest, wt::BlobImageResult)>,
rx: Receiver<(wt::BlobImageRequest, wt::BlobImageResult)>,
tx: Sender<(api::BlobImageRequest, api::BlobImageResult)>,
rx: Receiver<(api::BlobImageRequest, api::BlobImageResult)>,
// The deserialized drawing commands.
// In this example we store them in Arcs. This isn't necessary since in this simplified
// case the command list is a simple 32 bits value and would be cheap to clone before sending
// to the workers. But in a more realistic scenario the commands would typically be bigger
// and more expensive to clone, so let's pretend it is also the case here.
image_cmds: HashMap<wt::ImageKey, Arc<ImageRenderingCommands>>,
image_cmds: HashMap<api::ImageKey, Arc<ImageRenderingCommands>>,
// The images rendered in the current frame (not kept here between frames).
rendered_images: HashMap<wt::BlobImageRequest, Option<wt::BlobImageResult>>,
rendered_images: HashMap<api::BlobImageRequest, Option<api::BlobImageResult>>,
}
impl CheckerboardRenderer {
@ -131,33 +130,33 @@ impl CheckerboardRenderer {
CheckerboardRenderer {
image_cmds: HashMap::new(),
rendered_images: HashMap::new(),
workers: workers,
tx: tx,
rx: rx,
workers,
tx,
rx,
}
}
}
impl wt::BlobImageRenderer for CheckerboardRenderer {
fn add(&mut self, key: wt::ImageKey, cmds: wt::BlobImageData, _: Option<wt::TileSize>) {
impl api::BlobImageRenderer for CheckerboardRenderer {
fn add(&mut self, key: api::ImageKey, cmds: api::BlobImageData, _: Option<api::TileSize>) {
self.image_cmds.insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
}
fn update(&mut self, key: wt::ImageKey, cmds: wt::BlobImageData) {
fn update(&mut self, key: api::ImageKey, cmds: api::BlobImageData) {
// Here, updating is just replacing the current version of the commands with
// the new one (no incremental updates).
self.image_cmds.insert(key, Arc::new(deserialize_blob(&cmds[..]).unwrap()));
}
fn delete(&mut self, key: wt::ImageKey) {
fn delete(&mut self, key: api::ImageKey) {
self.image_cmds.remove(&key);
}
fn request(&mut self,
resources: &wt::BlobImageResources,
request: wt::BlobImageRequest,
descriptor: &wt::BlobImageDescriptor,
_dirty_rect: Option<wt::DeviceUintRect>) {
_resources: &api::BlobImageResources,
request: api::BlobImageRequest,
descriptor: &api::BlobImageDescriptor,
_dirty_rect: Option<api::DeviceUintRect>) {
// This method is where we kick off our rendering jobs.
// It should avoid doing work on the calling thread as much as possible.
// In this example we will use the thread pool to render individual tiles.
@ -179,7 +178,7 @@ impl wt::BlobImageRenderer for CheckerboardRenderer {
self.rendered_images.insert(request, None);
}
fn resolve(&mut self, request: wt::BlobImageRequest) -> wt::BlobImageResult {
fn resolve(&mut self, request: api::BlobImageRequest) -> api::BlobImageResult {
// In this method we wait until the work is complete on the worker threads and
// gather the results.
@ -187,7 +186,7 @@ impl wt::BlobImageRenderer for CheckerboardRenderer {
// that we are looking for.
match self.rendered_images.entry(request) {
Entry::Vacant(_) => {
return Err(wt::BlobImageError::InvalidKey);
return Err(api::BlobImageError::InvalidKey);
}
Entry::Occupied(entry) => {
// None means we haven't yet received the result.
@ -208,58 +207,55 @@ impl wt::BlobImageRenderer for CheckerboardRenderer {
}
// If we break out of the loop above it means the channel closed unexpectedly.
Err(wt::BlobImageError::Other("Channel closed".into()))
Err(api::BlobImageError::Other("Channel closed".into()))
}
fn delete_font(&mut self, font: wt::FontKey) {}
fn delete_font(&mut self, _font: api::FontKey) { }
}
fn body(api: &wt::RenderApi,
builder: &mut wt::DisplayListBuilder,
_pipeline_id: &wt::PipelineId,
layout_size: &wt::LayoutSize)
{
fn body(api: &api::RenderApi,
builder: &mut api::DisplayListBuilder,
_pipeline_id: &api::PipelineId,
layout_size: &api::LayoutSize) {
let blob_img1 = api.generate_image_key();
api.add_image(
blob_img1,
wt::ImageDescriptor::new(500, 500, wt::ImageFormat::BGRA8, true),
wt::ImageData::new_blob_image(serialize_blob(wt::ColorU::new(50, 50, 150, 255))),
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();
api.add_image(
blob_img2,
wt::ImageDescriptor::new(200, 200, wt::ImageFormat::BGRA8, true),
wt::ImageData::new_blob_image(serialize_blob(wt::ColorU::new(50, 150, 50, 255))),
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 = wt::LayoutRect::new(wt::LayoutPoint::zero(), *layout_size);
builder.push_stacking_context(wt::ScrollPolicy::Scrollable,
let bounds = api::LayoutRect::new(api::LayoutPoint::zero(), *layout_size);
builder.push_stacking_context(api::ScrollPolicy::Scrollable,
bounds,
None,
wt::TransformStyle::Flat,
api::TransformStyle::Flat,
None,
wt::MixBlendMode::Normal,
api::MixBlendMode::Normal,
Vec::new());
let clip = builder.push_clip_region(&bounds, vec![], None);
builder.push_image(
(30, 30).by(500, 500),
clip,
wt::LayoutSize::new(500.0, 500.0),
wt::LayoutSize::new(0.0, 0.0),
wt::ImageRendering::Auto,
Some(api::LocalClip::from(bounds)),
api::LayoutSize::new(500.0, 500.0),
api::LayoutSize::new(0.0, 0.0),
api::ImageRendering::Auto,
blob_img1,
);
let clip = builder.push_clip_region(&bounds, vec![], None);
builder.push_image(
(600, 600).by(200, 200),
clip,
wt::LayoutSize::new(200.0, 200.0),
wt::LayoutSize::new(0.0, 0.0),
wt::ImageRendering::Auto,
Some(api::LocalClip::from(bounds)),
api::LayoutSize::new(200.0, 200.0),
api::LayoutSize::new(0.0, 0.0),
api::ImageRendering::Auto,
blob_img2,
);
@ -267,7 +263,7 @@ fn body(api: &wt::RenderApi,
}
fn event_handler(_event: &glutin::Event,
_api: &wt::RenderApi)
_api: &api::RenderApi)
{
}

View File

@ -7,7 +7,7 @@ use glutin;
use std::env;
use std::path::PathBuf;
use webrender;
use webrender_traits::*;
use webrender::api::*;
struct Notifier {
window_proxy: glutin::WindowProxy,
@ -16,7 +16,7 @@ struct Notifier {
impl Notifier {
fn new(window_proxy: glutin::WindowProxy) -> Notifier {
Notifier {
window_proxy: window_proxy,
window_proxy,
}
}
}

View File

@ -5,7 +5,6 @@
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate webrender_traits;
#[macro_use]
extern crate lazy_static;
@ -15,63 +14,63 @@ mod boilerplate;
use boilerplate::HandyDandyRectBuilder;
use std::sync::Mutex;
use webrender_traits::*;
use webrender::api::*;
fn body(_api: &RenderApi,
builder: &mut DisplayListBuilder,
pipeline_id: &PipelineId,
layout_size: &LayoutSize)
{
layout_size: &LayoutSize) {
let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
None,
TransformStyle::Flat,
None,
webrender_traits::MixBlendMode::Normal,
MixBlendMode::Normal,
Vec::new());
let outer_scroll_frame_rect = (100, 100).to(600, 400);
let token = builder.push_clip_region(&outer_scroll_frame_rect, vec![], None);
builder.push_rect(outer_scroll_frame_rect,
token, ColorF::new(1.0, 1.0, 1.0, 1.0));
let token = builder.push_clip_region(&outer_scroll_frame_rect, vec![], None);
let nested_clip_id = builder.define_clip((100, 100).to(1000, 1000), token, None);
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);
builder.push_clip_id(nested_clip_id);
let mut builder2 = webrender_traits::DisplayListBuilder::new(*pipeline_id, *layout_size);
let mut builder3 = webrender_traits::DisplayListBuilder::new(*pipeline_id, *layout_size);
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);
let token = builder3.push_clip_region(&rect, vec![], None);
builder3.push_rect(rect, token, ColorF::new(0.0, 1.0, 0.0, 1.0));
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(webrender_traits::ScrollPolicy::Fixed,
builder3.push_stacking_context(ScrollPolicy::Fixed,
(220, 110).to(320, 210),
None,
TransformStyle::Flat,
None,
webrender_traits::MixBlendMode::Normal,
MixBlendMode::Normal,
Vec::new());
let rect = (0, 0).to(100, 100);
let token = builder3.push_clip_region(&rect, vec![], None);
builder3.push_rect(rect, token, ColorF::new(0.0, 1.0, 0.0, 1.0));
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);
let token = builder3.push_clip_region(&inner_scroll_frame_rect, vec![], None);
builder3.push_rect(inner_scroll_frame_rect, token, ColorF::new(1.0, 0.0, 1.0, 0.5));
let token = builder3.push_clip_region(&inner_scroll_frame_rect, vec![], None);
let inner_nested_clip_id = builder3.define_clip((330, 110).to(2000, 2000), token, None);
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);
builder3.push_clip_id(inner_nested_clip_id);
let rect = (340, 120).to(440, 220);
let token = builder3.push_clip_region(&rect, vec![], None);
builder3.push_rect(rect, token, ColorF::new(0.0, 1.0, 0.0, 1.0));
builder3.push_rect(rect, None, ColorF::new(0.0, 1.0, 0.0, 1.0));
builder3.pop_clip_id();
let (_, _, built_list) = builder3.finalize();

View File

@ -5,7 +5,6 @@
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate webrender_traits;
#[macro_use]
extern crate lazy_static;
@ -15,84 +14,76 @@ mod boilerplate;
use boilerplate::HandyDandyRectBuilder;
use std::sync::Mutex;
use webrender_traits::*;
use webrender::api::*;
fn body(_api: &RenderApi,
builder: &mut DisplayListBuilder,
pipeline_id: &PipelineId,
layout_size: &LayoutSize)
{
_pipeline_id: &PipelineId,
layout_size: &LayoutSize) {
let bounds = LayoutRect::new(LayoutPoint::zero(), *layout_size);
builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
builder.push_stacking_context(ScrollPolicy::Scrollable,
bounds,
None,
TransformStyle::Flat,
None,
webrender_traits::MixBlendMode::Normal,
MixBlendMode::Normal,
Vec::new());
if true { // scrolling and clips stuff
// let's make a scrollbox
let scrollbox = (0, 0).to(300, 400);
builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
builder.push_stacking_context(ScrollPolicy::Scrollable,
LayoutRect::new(LayoutPoint::new(10.0, 10.0),
LayoutSize::zero()),
None,
TransformStyle::Flat,
None,
webrender_traits::MixBlendMode::Normal,
MixBlendMode::Normal,
Vec::new());
// set the scrolling clip
let clip = builder.push_clip_region(&scrollbox, vec![], None);
let clip_id = builder.define_clip((0, 0).to(1000, 1000),
clip,
Some(ClipId::new(42, *pipeline_id)));
let clip_id = builder.define_scroll_frame(None,
(0, 0).by(1000, 1000),
scrollbox,
vec![],
None);
builder.push_clip_id(clip_id);
// now put some content into it.
// start with a white background
let clip = builder.push_clip_region(&(0, 0).to(1000, 1000), vec![], None);
builder.push_rect((0, 0).to(500, 500),
clip,
ColorF::new(1.0, 1.0, 1.0, 1.0));
builder.push_rect((0, 0).to(1000, 1000), None, ColorF::new(1.0, 1.0, 1.0, 1.0));
// let's make a 50x50 blue square as a visual reference
let clip = builder.push_clip_region(&(0, 0).to(50, 50), vec![], None);
builder.push_rect((0, 0).to(50, 50),
clip,
ColorF::new(0.0, 0.0, 1.0, 1.0));
builder.push_rect((0, 0).to(50, 50), None, ColorF::new(0.0, 0.0, 1.0, 1.0));
// and a 50x50 green square next to it with an offset clip
// to see what that looks like
let clip = builder.push_clip_region(&(60, 10).to(110, 60), vec![], None);
builder.push_rect((50, 0).to(100, 50),
clip,
Some(LocalClip::from((60, 10).to(110, 60))),
ColorF::new(0.0, 1.0, 0.0, 1.0));
// Below the above rectangles, set up a nested scrollbox. It's still in
// the same stacking context, so note that the rects passed in need to
// be relative to the stacking context.
let clip = builder.push_clip_region(&(0, 100).to(200, 300), vec![], None);
let nested_clip_id = builder.define_clip((0, 100).to(300, 400),
clip,
Some(ClipId::new(43, *pipeline_id)));
let nested_clip_id = builder.define_scroll_frame(None,
(0, 100).to(300, 400),
(0, 100).to(200, 300),
vec![],
None);
builder.push_clip_id(nested_clip_id);
// give it a giant gray background just to distinguish it and to easily
// visually identify the nested scrollbox
let clip = builder.push_clip_region(&(-1000, -1000).to(5000, 5000), vec![], None);
builder.push_rect((-1000, -1000).to(5000, 5000),
clip,
ColorF::new(0.5, 0.5, 0.5, 1.0));
builder.push_rect((-1000, -1000).to(5000, 5000), None, ColorF::new(0.5, 0.5, 0.5, 1.0));
// add a teal square to visualize the scrolling/clipping behaviour
// as you scroll the nested scrollbox with WASD keys
let clip = builder.push_clip_region(&(0, 100).to(50, 150), vec![], None);
builder.push_rect((0, 100).to(50, 150),
clip,
ColorF::new(0.0, 1.0, 1.0, 1.0));
builder.push_rect((0, 100).to(50, 150), None, ColorF::new(0.0, 1.0, 1.0, 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
let clip = builder.push_clip_region(&(250, 350).to(300, 400), vec![], None);
builder.push_rect((250, 350).to(300, 400),
clip,
ColorF::new(0.0, 1.0, 1.0, 1.0));
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
@ -106,9 +97,7 @@ lazy_static! {
static ref CURSOR_POSITION: Mutex<WorldPoint> = Mutex::new(WorldPoint::zero());
}
fn event_handler(event: &glutin::Event,
api: &RenderApi)
{
fn event_handler(event: &glutin::Event, api: &RenderApi) {
match *event {
glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
let offset = match key {

View File

@ -7,18 +7,17 @@ extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
extern crate webrender_traits;
use gleam::gl;
#[macro_use]
extern crate lazy_static;
#[path="common/boilerplate.rs"]
mod boilerplate;
use glutin::TouchPhase;
use std::collections::HashMap;
use std::env;
use std::path::PathBuf;
use webrender_traits::{ColorF, Epoch};
use webrender_traits::{DeviceIntPoint, DeviceUintSize, LayoutPoint, LayoutRect, LayoutSize};
use webrender_traits::{ImageData, ImageDescriptor, ImageFormat, ImageRendering};
use webrender_traits::{PipelineId, TransformStyle};
use webrender_traits::{YuvColorSpace, YuvData};
use std::sync::Mutex;
use webrender::api::*;
#[derive(Debug)]
enum Gesture {
@ -158,92 +157,21 @@ impl TouchState {
}
}
struct Notifier {
window_proxy: glutin::WindowProxy,
}
impl Notifier {
fn new(window_proxy: glutin::WindowProxy) -> Notifier {
Notifier {
window_proxy: window_proxy,
}
}
}
impl webrender_traits::RenderNotifier for Notifier {
fn new_frame_ready(&mut self) {
#[cfg(not(target_os = "android"))]
self.window_proxy.wakeup_event_loop();
}
fn new_scroll_frame_ready(&mut self, _composite_needed: bool) {
#[cfg(not(target_os = "android"))]
self.window_proxy.wakeup_event_loop();
}
}
fn main() {
let args: Vec<String> = env::args().collect();
let res_path = if args.len() > 1 {
Some(PathBuf::from(&args[1]))
} else {
None
};
boilerplate::main_wrapper(body, event_handler, None);
}
let window = glutin::WindowBuilder::new()
.with_title("WebRender Sample")
.with_multitouch()
.with_gl(glutin::GlRequest::GlThenGles {
opengl_version: (3, 2),
opengles_version: (3, 0)
})
.build()
.unwrap();
unsafe {
window.make_current().ok();
}
let gl = match gl::GlType::default() {
gl::GlType::Gl => unsafe { gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
gl::GlType::Gles => unsafe { gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _) },
};
println!("OpenGL version {}", gl.get_string(gl::VERSION));
println!("Shader resource path: {:?}", res_path);
let (width, height) = window.get_inner_size_pixels().unwrap();
let opts = webrender::RendererOptions {
resource_override_path: res_path,
debug: true,
precache_shaders: true,
blob_image_renderer: None,
device_pixel_ratio: window.hidpi_factor(),
.. Default::default()
};
let size = DeviceUintSize::new(width, height);
let (mut renderer, sender) = webrender::renderer::Renderer::new(gl, opts, size).unwrap();
let api = sender.create_api();
let notifier = Box::new(Notifier::new(window.create_window_proxy()));
renderer.set_render_notifier(notifier);
let epoch = Epoch(0);
let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
let pipeline_id = PipelineId(0, 0);
let layout_size = LayoutSize::new(width as f32, height as f32);
let mut builder = webrender_traits::DisplayListBuilder::new(pipeline_id, layout_size);
let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size);
builder.push_stacking_context(webrender_traits::ScrollPolicy::Scrollable,
fn body(api: &RenderApi,
builder: &mut DisplayListBuilder,
_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,
webrender_traits::MixBlendMode::Normal,
MixBlendMode::Normal,
Vec::new());
@ -276,76 +204,45 @@ fn main() {
None,
);
let clip = builder.push_clip_region(&bounds, vec![], None);
builder.push_yuv_image(
LayoutRect::new(LayoutPoint::new(100.0, 0.0), LayoutSize::new(100.0, 100.0)),
clip,
Some(LocalClip::from(bounds)),
YuvData::NV12(yuv_chanel1, yuv_chanel2),
YuvColorSpace::Rec601,
ImageRendering::Auto,
);
let clip = builder.push_clip_region(&bounds, vec![], None);
builder.push_yuv_image(
LayoutRect::new(LayoutPoint::new(300.0, 0.0), LayoutSize::new(100.0, 100.0)),
clip,
Some(LocalClip::from(bounds)),
YuvData::PlanarYCbCr(yuv_chanel1, yuv_chanel2_1, yuv_chanel3),
YuvColorSpace::Rec601,
ImageRendering::Auto,
);
builder.pop_stacking_context();
api.set_display_list(
Some(root_background_color),
epoch,
LayoutSize::new(width as f32, height as f32),
builder.finalize(),
true);
api.set_root_pipeline(pipeline_id);
api.generate_frame(None);
let mut touch_state = TouchState::new();
'outer: for event in window.wait_events() {
let mut events = Vec::new();
events.push(event);
for event in window.poll_events() {
events.push(event);
}
for event in events {
match event {
glutin::Event::Closed |
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Q)) => break 'outer,
glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
_, Some(glutin::VirtualKeyCode::P)) => {
let enable_profiler = !renderer.get_profiler_enabled();
renderer.set_profiler_enabled(enable_profiler);
api.generate_frame(None);
}
glutin::Event::Touch(touch) => {
match touch_state.handle_event(touch) {
TouchResult::Pan(pan) => {
api.set_pan(pan);
api.generate_frame(None);
}
TouchResult::Zoom(zoom) => {
api.set_pinch_zoom(webrender_traits::ZoomFactor::new(zoom));
api.generate_frame(None);
}
TouchResult::None => {}
}
}
_ => ()
}
}
renderer.update();
renderer.render(DeviceUintSize::new(width, height));
window.swap_buffers().ok();
}
}
lazy_static! {
static ref TOUCH_STATE: Mutex<TouchState> = Mutex::new(TouchState::new());
}
fn event_handler(event: &glutin::Event, api: &RenderApi) {
match *event {
glutin::Event::Touch(touch) => {
match TOUCH_STATE.lock().unwrap().handle_event(touch) {
TouchResult::Pan(pan) => {
api.set_pan(pan);
api.generate_frame(None);
}
TouchResult::Zoom(zoom) => {
api.set_pinch_zoom(ZoomFactor::new(zoom));
api.generate_frame(None);
}
TouchResult::None => {}
}
}
_ => ()
}
}

View File

@ -22,7 +22,7 @@ struct BorderCorner {
};
BorderCorner fetch_border_corner(int index) {
vec4 data[2] = fetch_data_2(index);
vec4 data[2] = fetch_from_resource_cache_2(index);
return BorderCorner(RectWithSize(data[0].xy, data[0].zw),
data[1].xy,
int(data[1].z),
@ -36,7 +36,7 @@ struct BorderClipDash {
};
BorderClipDash fetch_border_clip_dash(int index) {
vec4 data[2] = fetch_data_2(index);
vec4 data[2] = fetch_from_resource_cache_2(index);
return BorderClipDash(data[0], data[1]);
}
@ -46,8 +46,8 @@ struct BorderClipDot {
};
BorderClipDot fetch_border_clip_dot(int index) {
vec4 data[2] = fetch_data_2(index);
return BorderClipDot(data[0].xyz);
vec4 data = fetch_from_resource_cache_1(index);
return BorderClipDot(data.xyz);
}
void main(void) {
@ -85,7 +85,7 @@ void main(void) {
switch (corner.clip_mode) {
case CLIP_MODE_DASH: {
// Fetch the information about this particular dash.
BorderClipDash dash = fetch_border_clip_dash(cci.data_index + cci.segment_index);
BorderClipDash dash = fetch_border_clip_dash(cci.data_index + 2 + 2 * (cci.segment_index - 1));
vPoint_Tangent0 = dash.point_tangent_0 * sign_modifier.xyxy;
vPoint_Tangent1 = dash.point_tangent_1 * sign_modifier.xyxy;
vDotParams = vec3(0.0);
@ -93,7 +93,7 @@ void main(void) {
break;
}
case CLIP_MODE_DOT: {
BorderClipDot cdot = fetch_border_clip_dot(cci.data_index + cci.segment_index);
BorderClipDot cdot = fetch_border_clip_dot(cci.data_index + 2 + (cci.segment_index - 1));
vPoint_Tangent0 = vec4(1.0);
vPoint_Tangent1 = vec4(1.0);
vDotParams = vec3(cdot.center_radius.xy * sign_modifier, cdot.center_radius.z);

View File

@ -8,8 +8,8 @@ struct ImageMaskData {
};
ImageMaskData fetch_mask_data(int index) {
vec4 data[2] = fetch_data_2(index);
return ImageMaskData(RectWithSize(data[0].xy, data[0].zw));
vec4 data = fetch_from_resource_cache_1(index);
return ImageMaskData(RectWithSize(data.xy, data.zw));
}
void main(void) {

View File

@ -9,7 +9,7 @@ struct ClipRect {
};
ClipRect fetch_clip_rect(int index) {
vec4 data[2] = fetch_data_2(index);
vec4 data[2] = fetch_from_resource_cache_2(index);
return ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]);
}
@ -19,7 +19,7 @@ struct ClipCorner {
};
ClipCorner fetch_clip_corner(int index) {
vec4 data[2] = fetch_data_2(index);
vec4 data[2] = fetch_from_resource_cache_2(index);
return ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]);
}
@ -35,10 +35,10 @@ ClipData fetch_clip(int index) {
ClipData clip;
clip.rect = fetch_clip_rect(index + 0);
clip.top_left = fetch_clip_corner(index + 1);
clip.top_right = fetch_clip_corner(index + 2);
clip.bottom_left = fetch_clip_corner(index + 3);
clip.bottom_right = fetch_clip_corner(index + 4);
clip.top_left = fetch_clip_corner(index + 2);
clip.top_right = fetch_clip_corner(index + 4);
clip.bottom_left = fetch_clip_corner(index + 6);
clip.bottom_right = fetch_clip_corner(index + 8);
return clip;
}

View File

@ -31,8 +31,9 @@ void main(void) {
vec2 pos = mix(local_rect.xy,
local_rect.xy + local_rect.zw,
aPosition.xy);
vUv = mix(st0, st1, aPosition.xy);
vColor = text.color;
vUv = mix(st0, st1, aPosition.xy);
vColor = text.color;
gl_Position = uTransform * vec4(pos, 0.0, 1.0);
}

View File

@ -36,7 +36,7 @@
#define BORDER_RIGHT 2
#define BORDER_BOTTOM 3
// Border styles as defined in webrender_traits/types.rs
// Border styles as defined in webrender_api/types.rs
#define BORDER_STYLE_NONE 0
#define BORDER_STYLE_SOLID 1
#define BORDER_STYLE_DOUBLE 2
@ -140,8 +140,6 @@ vec4[2] fetch_from_resource_cache_2(int address) {
uniform HIGHP_SAMPLER_FLOAT sampler2D sLayers;
uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks;
uniform HIGHP_SAMPLER_FLOAT sampler2D sData32;
// Instanced attributes
in ivec4 aData0;
in ivec4 aData1;
@ -152,13 +150,6 @@ in ivec4 aData1;
// https://github.com/servo/servo/issues/13953
#define get_fetch_uv(i, vpi) ivec2(vpi * (i % (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi)), i / (WR_MAX_VERTEX_TEXTURE_WIDTH/vpi))
vec4[2] fetch_data_2(int index) {
ivec2 uv = get_fetch_uv(index, 2);
return vec4[2](
texelFetchOffset(sData32, uv, 0, ivec2(0, 0)),
texelFetchOffset(sData32, uv, 0, ivec2(1, 0))
);
}
vec4[8] fetch_from_resource_cache_8(int address) {
ivec2 uv = get_resource_cache_uv(address);

View File

@ -2,14 +2,15 @@
* 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::{BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ColorF, LayerPoint, LayerRect};
use api::{LayerSize, LocalClip, NormalBorder};
use ellipse::Ellipse;
use gpu_cache::GpuDataRequest;
use frame_builder::FrameBuilder;
use mask_cache::{ClipSource};
use prim_store::{BorderPrimitiveCpu, GpuBlock32, PrimitiveContainer};
use mask_cache::ClipSource;
use prim_store::{BorderPrimitiveCpu, PrimitiveContainer};
use tiling::PrimitiveFlags;
use util::{lerp, pack_as_float};
use webrender_traits::{BorderSide, BorderStyle, BorderWidths, ClipAndScrollInfo, ClipRegion};
use webrender_traits::{ColorF, LayerPoint, LayerRect, LayerSize, NormalBorder};
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq)]
@ -79,7 +80,7 @@ impl BorderCornerKind {
};
let clip_data = BorderCornerClipData {
corner_rect: LayerRect::new(origin, size),
clip_center: clip_center,
clip_center,
corner: pack_as_float(corner as u32),
kind: pack_as_float(kind as u32),
};
@ -225,7 +226,7 @@ impl FrameBuilder {
border: &NormalBorder,
widths: &BorderWidths,
clip_and_scroll: ClipAndScrollInfo,
clip_region: &ClipRegion,
local_clip: &LocalClip,
corner_instances: [BorderCornerInstance; 4],
extra_clips: &[ClipSource]) {
let radius = &border.radius;
@ -241,7 +242,7 @@ impl FrameBuilder {
let bottom_color = bottom.border_color(2.0/3.0, 1.0, 0.7, 0.3);
let prim_cpu = BorderPrimitiveCpu {
corner_instances: corner_instances,
corner_instances,
// TODO(gw): In the future, we will build these on demand
// from the deserialized display list, rather
@ -272,7 +273,7 @@ impl FrameBuilder {
self.add_primitive(clip_and_scroll,
&rect,
clip_region,
local_clip,
extra_clips,
PrimitiveContainer::Border(prim_cpu));
}
@ -286,7 +287,7 @@ impl FrameBuilder {
border: &NormalBorder,
widths: &BorderWidths,
clip_and_scroll: ClipAndScrollInfo,
clip_region: &ClipRegion) {
local_clip: &LocalClip) {
// The border shader is quite expensive. For simple borders, we can just draw
// the border with a few rectangles. This generally gives better batching, and
// a GPU win in fragment shader time.
@ -366,7 +367,7 @@ impl FrameBuilder {
if top_edge == BorderEdgeKind::Solid {
self.add_solid_rectangle(clip_and_scroll,
&LayerRect::new(p0, LayerSize::new(rect_width, top_len)),
clip_region,
local_clip,
&border.top.color,
PrimitiveFlags::None);
}
@ -375,7 +376,7 @@ impl FrameBuilder {
&LayerRect::new(LayerPoint::new(p0.x, p0.y + top_len),
LayerSize::new(left_len,
rect_height - top_len - bottom_len)),
clip_region,
local_clip,
&border.left.color,
PrimitiveFlags::None);
}
@ -385,7 +386,7 @@ impl FrameBuilder {
p0.y + top_len),
LayerSize::new(right_len,
rect_height - top_len - bottom_len)),
clip_region,
local_clip,
&border.right.color,
PrimitiveFlags::None);
}
@ -393,7 +394,7 @@ impl FrameBuilder {
self.add_solid_rectangle(clip_and_scroll,
&LayerRect::new(LayerPoint::new(p0.x, p1.y - bottom_len),
LayerSize::new(rect_width, bottom_len)),
clip_region,
local_clip,
&border.bottom.color,
PrimitiveFlags::None);
}
@ -422,7 +423,7 @@ impl FrameBuilder {
border,
widths,
clip_and_scroll,
clip_region,
local_clip,
corner_instances,
&extra_clips);
}
@ -535,18 +536,17 @@ impl BorderCornerClipSource {
};
BorderCornerClipSource {
kind: kind,
corner_data: corner_data,
max_clip_count: max_clip_count,
kind,
corner_data,
max_clip_count,
actual_clip_count: 0,
ellipse: ellipse,
widths: widths,
ellipse,
widths,
}
}
pub fn populate_gpu_data(&mut self, slice: &mut [GpuBlock32]) {
let (header, clips) = slice.split_first_mut().unwrap();
*header = self.corner_data.into();
pub fn write(&mut self, mut request: GpuDataRequest) {
self.corner_data.write(&mut request);
match self.kind {
BorderCornerClipKind::Dash => {
@ -554,7 +554,7 @@ impl BorderCornerClipSource {
self.actual_clip_count = self.max_clip_count;
let dash_arc_length = 0.5 * self.ellipse.total_arc_length / (self.actual_clip_count - 1) as f32;
let mut current_arc_length = -0.5 * dash_arc_length;
for dash_index in 0..self.actual_clip_count {
for _ in 0..self.actual_clip_count {
let arc_length0 = current_arc_length;
current_arc_length += dash_arc_length;
@ -564,9 +564,10 @@ impl BorderCornerClipSource {
let dash_data = BorderCornerDashClipData::new(arc_length0,
arc_length1,
&self.ellipse);
clips[dash_index] = dash_data.into();
dash_data.write(&mut request);
}
assert_eq!(request.close(), 2 + 2 * self.actual_clip_count);
}
BorderCornerClipKind::Dot => {
let mut forward_dots = Vec::new();
@ -623,17 +624,15 @@ impl BorderCornerClipSource {
// leftover space on the arc between them evenly. Once
// the final arc position is determined, generate the correct
// arc positions and angles that get passed to the clip shader.
self.actual_clip_count = 0;
let dot_count = forward_dots.len() + back_dots.len();
let extra_space_per_dot = leftover_arc_length / (dot_count - 1) as f32;
self.actual_clip_count = forward_dots.len() + back_dots.len();
let extra_space_per_dot = leftover_arc_length / (self.actual_clip_count - 1) as f32;
for (i, dot) in forward_dots.iter().enumerate() {
let extra_dist = i as f32 * extra_space_per_dot;
let dot = BorderCornerDotClipData::new(dot.arc_pos + extra_dist,
0.5 * dot.diameter,
&self.ellipse);
clips[self.actual_clip_count] = dot.into();
self.actual_clip_count += 1;
dot.write(&mut request);
}
for (i, dot) in back_dots.iter().enumerate() {
@ -641,9 +640,10 @@ impl BorderCornerClipSource {
let dot = BorderCornerDotClipData::new(dot.arc_pos - extra_dist,
0.5 * dot.diameter,
&self.ellipse);
clips[self.actual_clip_count] = dot.into();
self.actual_clip_count += 1;
dot.write(&mut request);
}
assert_eq!(request.close(), 2 + self.actual_clip_count);
}
}
}
@ -666,6 +666,13 @@ pub struct BorderCornerClipData {
kind: f32, // Of type BorderCornerClipKind enum
}
impl BorderCornerClipData {
fn write(&self, request: &mut GpuDataRequest) {
request.push(self.corner_rect);
request.push([self.clip_center.x, self.clip_center.y, self.corner, self.kind]);
}
}
/// Represents the GPU data for drawing a single dash
/// to a clip mask. A dash clip is defined by two lines.
/// We store a point on the ellipse curve, and a tangent
@ -697,6 +704,13 @@ impl BorderCornerDashClipData {
tangent1: t1,
}
}
fn write(&self, request: &mut GpuDataRequest) {
request.push([self.point0.x, self.point0.y,
self.tangent0.x, self.tangent0.y]);
request.push([self.point1.x, self.point1.y,
self.tangent1.x, self.tangent1.y]);
}
}
/// Represents the GPU data for drawing a single dot
@ -706,7 +720,6 @@ impl BorderCornerDashClipData {
pub struct BorderCornerDotClipData {
pub center: LayerPoint,
pub radius: f32,
pub padding: [f32; 5],
}
impl BorderCornerDotClipData {
@ -717,11 +730,14 @@ impl BorderCornerDotClipData {
let (center, _) = ellipse.get_point_and_tangent(theta);
BorderCornerDotClipData {
center: center,
radius: radius,
padding: [0.0; 5],
center,
radius,
}
}
fn write(&self, request: &mut GpuDataRequest) {
request.push([self.center.x, self.center.y, self.radius, 0.0]);
}
}
#[derive(Copy, Clone, Debug)]
@ -733,8 +749,8 @@ struct DotInfo {
impl DotInfo {
fn new(arc_pos: f32, diameter: f32) -> DotInfo {
DotInfo {
arc_pos: arc_pos,
diameter: diameter,
arc_pos,
diameter,
}
}
}

View File

@ -2,18 +2,14 @@
* 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::{ClipId, DeviceIntRect, LayerPixel, LayerPoint, LayerRect, LayerSize};
use api::{LayerToScrollTransform, LayerToWorldTransform, LayerVector2D, PipelineId};
use api::{ScrollClamping, ScrollEventPhase, ScrollLocation, WorldPoint};
use geometry::ray_intersects_rect;
use mask_cache::{ClipSource, MaskCacheInfo, RegionMode};
use prim_store::GpuBlock32;
use renderer::VertexDataStore;
use mask_cache::{ClipRegion, ClipSource, MaskCacheInfo};
use spring::{DAMPING, STIFFNESS, Spring};
use tiling::PackedLayerIndex;
use util::TransformedRectKind;
use webrender_traits::{ClipId, ClipRegion, DeviceIntRect, LayerPixel, LayerPoint, LayerRect};
use webrender_traits::{LayerSize, LayerToScrollTransform, LayerToWorldTransform, PipelineId};
use webrender_traits::{ScrollClamping, ScrollEventPhase, ScrollLayerRect, ScrollLocation};
use webrender_traits::{WorldPoint, LayerVector2D};
use webrender_traits::{as_scroll_parent_vector};
use util::{MatrixHelpers, TransformedRectKind};
#[cfg(target_os = "macos")]
const CAN_OVERSCROLL: bool = true;
@ -28,7 +24,7 @@ pub struct ClipInfo {
/// The MaskCacheInfo for this node, which is produced by processing the
/// provided ClipSource.
pub mask_cache_info: Option<MaskCacheInfo>,
pub mask_cache_info: MaskCacheInfo,
/// The packed layer index for this node, which is used to render a clip mask
/// for it, if necessary.
@ -38,32 +34,31 @@ pub struct ClipInfo {
/// which depends on the screen rectangle and the transformation of all of
/// the parents.
pub screen_bounding_rect: Option<(TransformedRectKind, DeviceIntRect)>,
/// The biggest final transformed rectangle that is completely inside the
/// clipping region for this node.
pub screen_inner_rect: DeviceIntRect,
/// A rectangle which defines the rough boundaries of this clip in reference
/// frame relative coordinates (with no scroll offsets).
pub clip_rect: LayerRect,
}
impl ClipInfo {
pub fn new(clip_region: &ClipRegion,
clip_store: &mut VertexDataStore<GpuBlock32>,
packed_layer_index: PackedLayerIndex,)
-> ClipInfo {
// We pass true here for the MaskCacheInfo because this type of
// mask needs an extra clip for the clip rectangle.
let clip_sources = vec![ClipSource::Region(clip_region.clone(), RegionMode::IncludeRect)];
pub fn new(clip_region: ClipRegion, packed_layer_index: PackedLayerIndex) -> ClipInfo {
let clip_rect = LayerRect::new(clip_region.origin, clip_region.main.size);
let clip_sources = vec![ClipSource::Region(clip_region)];
ClipInfo {
mask_cache_info: MaskCacheInfo::new(&clip_sources, clip_store),
clip_sources: clip_sources,
packed_layer_index: packed_layer_index,
mask_cache_info: MaskCacheInfo::new(&clip_sources),
clip_sources,
packed_layer_index,
screen_bounding_rect: None,
screen_inner_rect: DeviceIntRect::zero(),
clip_rect: clip_rect,
}
}
pub fn is_masking(&self) -> bool {
match self.mask_cache_info {
Some(ref info) => info.is_masking(),
_ => false,
}
}
}
#[derive(Clone, Debug)]
pub enum NodeType {
/// Transform for this layer, relative to parent reference frame. A reference
@ -91,7 +86,10 @@ pub struct ClipScrollNode {
pub local_clip_rect: LayerRect,
/// Viewport rectangle clipped against parent layer(s) viewport rectangles.
/// This is in the coordinate system which starts at our origin.
/// This is in the coordinate system of the node origin.
/// Precisely, it combines the local clipping rectangles of all the parent
/// nodes on the way to the root, including those of `ClipRegion` rectangles.
/// The combined clip is lossy/concervative on `ReferenceFrame` nodes.
pub combined_local_viewport_rect: LayerRect,
/// World transform for the viewport rect itself. This is the parent
@ -123,11 +121,11 @@ pub struct ClipScrollNode {
impl ClipScrollNode {
pub fn new_scroll_frame(pipeline_id: PipelineId,
parent_id: ClipId,
content_rect: &LayerRect,
frame_rect: &LayerRect)
frame_rect: &LayerRect,
content_size: &LayerSize)
-> ClipScrollNode {
ClipScrollNode {
content_size: content_rect.size,
content_size: *content_size,
local_viewport_rect: *frame_rect,
local_clip_rect: *frame_rect,
combined_local_viewport_rect: LayerRect::zero(),
@ -136,31 +134,23 @@ impl ClipScrollNode {
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
parent: Some(parent_id),
children: Vec::new(),
pipeline_id: pipeline_id,
pipeline_id,
node_type: NodeType::ScrollFrame(ScrollingState::new()),
}
}
pub fn new(pipeline_id: PipelineId,
parent_id: ClipId,
content_rect: &LayerRect,
clip_rect: &LayerRect,
clip_info: ClipInfo)
-> ClipScrollNode {
// FIXME(mrobinson): We don't yet handle clipping rectangles that don't start at the origin
// of the node.
let local_viewport_rect = LayerRect::new(content_rect.origin, clip_rect.size);
pub fn new(pipeline_id: PipelineId, parent_id: ClipId, clip_info: ClipInfo) -> ClipScrollNode {
ClipScrollNode {
content_size: content_rect.size,
local_viewport_rect: local_viewport_rect,
local_clip_rect: local_viewport_rect,
content_size: clip_info.clip_rect.size,
local_viewport_rect: clip_info.clip_rect,
local_clip_rect: clip_info.clip_rect,
combined_local_viewport_rect: LayerRect::zero(),
world_viewport_transform: LayerToWorldTransform::identity(),
world_content_transform: LayerToWorldTransform::identity(),
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
parent: Some(parent_id),
children: Vec::new(),
pipeline_id: pipeline_id,
pipeline_id,
node_type: NodeType::Clip(clip_info),
}
}
@ -172,7 +162,7 @@ impl ClipScrollNode {
pipeline_id: PipelineId)
-> ClipScrollNode {
ClipScrollNode {
content_size: content_size,
content_size,
local_viewport_rect: *local_viewport_rect,
local_clip_rect: *local_viewport_rect,
combined_local_viewport_rect: LayerRect::zero(),
@ -181,7 +171,7 @@ impl ClipScrollNode {
reference_frame_relative_scroll_offset: LayerVector2D::zero(),
parent: parent_id,
children: Vec::new(),
pipeline_id: pipeline_id,
pipeline_id,
node_type: NodeType::ReferenceFrame(*local_transform),
}
}
@ -192,7 +182,11 @@ impl ClipScrollNode {
pub fn finalize(&mut self, new_scrolling: &ScrollingState) {
match self.node_type {
NodeType::ReferenceFrame(_) | NodeType::Clip(_) => (),
NodeType::ReferenceFrame(_) | NodeType::Clip(_) => {
if new_scrolling.offset != LayerVector2D::zero() {
warn!("Tried to scroll a non-scroll node.");
}
}
NodeType::ScrollFrame(ref mut scrolling) => *scrolling = *new_scrolling,
}
}
@ -234,53 +228,30 @@ impl ClipScrollNode {
pub fn update_transform(&mut self,
parent_reference_frame_transform: &LayerToWorldTransform,
parent_combined_viewport_rect: &ScrollLayerRect,
parent_combined_viewport_rect: &LayerRect,
parent_scroll_offset: LayerVector2D,
parent_accumulated_scroll_offset: LayerVector2D) {
self.reference_frame_relative_scroll_offset = match self.node_type {
NodeType::ReferenceFrame(_) => LayerVector2D::zero(),
NodeType::Clip(_) | NodeType::ScrollFrame(..) => parent_accumulated_scroll_offset,
};
let local_transform = match self.node_type {
NodeType::ReferenceFrame(transform) => transform,
NodeType::Clip(_) | NodeType::ScrollFrame(..) => LayerToScrollTransform::identity(),
};
let scrolled_parent_combined_clip = parent_combined_viewport_rect
.translate(&-parent_scroll_offset);
let inv_transform = match local_transform.inverse() {
Some(transform) => transform,
None => {
// If a transform function causes the current transformation matrix of an object
// to be non-invertible, the object and its content do not get displayed.
self.combined_local_viewport_rect = LayerRect::zero();
return;
let (local_transform, combined_clip, reference_frame_scroll_offset) = match self.node_type {
NodeType::ReferenceFrame(transform) => {
let combined_clip = transform.with_destination::<LayerPixel>()
.inverse_rect_footprint(&scrolled_parent_combined_clip);
(transform, combined_clip, LayerVector2D::zero())
}
NodeType::Clip(_) | NodeType::ScrollFrame(_) => {
// Move the parent's viewport into the local space (of the node origin)
// and intersect with the local clip rectangle to get the local viewport.
let combined_clip = scrolled_parent_combined_clip.intersection(&self.local_clip_rect)
.unwrap_or(LayerRect::zero());
(LayerToScrollTransform::identity(), combined_clip, parent_accumulated_scroll_offset)
}
};
// We are trying to move the combined viewport rectangle of our parent nodes into the
// coordinate system of this node, so we must invert our transformation (only for
// reference frames) and then apply the scroll offset the parent layer. The combined
// local viewport rect doesn't include scrolling offsets so the only one that matters
// is the relative offset between us and the parent.
let parent_combined_viewport_in_local_space =
inv_transform.pre_translate(-as_scroll_parent_vector(&parent_scroll_offset).to_3d())
.transform_rect(parent_combined_viewport_rect);
// Now that we have the combined viewport rectangle of the parent nodes in local space,
// we do the intersection and get our combined viewport rect in the coordinate system
// starting from our origin.
self.combined_local_viewport_rect = match self.node_type {
NodeType::Clip(_) | NodeType::ScrollFrame(..) => {
parent_combined_viewport_in_local_space.intersection(&self.local_clip_rect)
.unwrap_or(LayerRect::zero())
}
NodeType::ReferenceFrame(_) => parent_combined_viewport_in_local_space,
};
// HACK: prevent the code above for non-AA transforms, it's incorrect.
if (local_transform.m13, local_transform.m23) != (0.0, 0.0) {
self.combined_local_viewport_rect = self.local_clip_rect;
}
self.combined_local_viewport_rect = combined_clip;
self.reference_frame_relative_scroll_offset = reference_frame_scroll_offset;
// The transformation for this viewport in world coordinates is the transformation for
// our parent reference frame, plus any accumulated scrolling offsets from nodes

View File

@ -7,10 +7,9 @@ use fnv::FnvHasher;
use print_tree::PrintTree;
use std::collections::{HashMap, HashSet};
use std::hash::BuildHasherDefault;
use webrender_traits::{ClipId, LayerPoint, LayerRect, LayerToScrollTransform};
use webrender_traits::{LayerToWorldTransform, PipelineId, ScrollClamping, ScrollEventPhase};
use webrender_traits::{ScrollLayerRect, ScrollLayerState, ScrollLocation, WorldPoint};
use webrender_traits::{as_scroll_parent_rect, LayerVector2D};
use api::{ClipId, LayerPoint, LayerRect, LayerToScrollTransform};
use api::{LayerToWorldTransform, PipelineId, ScrollClamping, ScrollEventPhase};
use api::{LayerVector2D, ScrollLayerState, ScrollLocation, WorldPoint};
pub type ScrollStates = HashMap<ClipId, ScrollingState, BuildHasherDefault<FnvHasher>>;
@ -231,7 +230,7 @@ impl ClipScrollTree {
let root_viewport = self.nodes[&root_reference_frame_id].local_clip_rect;
self.update_node_transform(root_reference_frame_id,
&LayerToWorldTransform::create_translation(pan.x, pan.y, 0.0),
&as_scroll_parent_rect(&root_viewport),
&root_viewport,
LayerVector2D::zero(),
LayerVector2D::zero());
}
@ -239,54 +238,56 @@ impl ClipScrollTree {
fn update_node_transform(&mut self,
layer_id: ClipId,
parent_reference_frame_transform: &LayerToWorldTransform,
parent_viewport_rect: &ScrollLayerRect,
parent_viewport_rect: &LayerRect,
parent_scroll_offset: LayerVector2D,
parent_accumulated_scroll_offset: LayerVector2D) {
// TODO(gw): This is an ugly borrow check workaround to clone these.
// Restructure this to avoid the clones!
let (reference_frame_transform,
viewport_rect,
combined_local_viewport_rect,
scroll_offset,
accumulated_scroll_offset,
node_children) = {
match self.nodes.get_mut(&layer_id) {
Some(node) => {
node.update_transform(parent_reference_frame_transform,
parent_viewport_rect,
parent_scroll_offset,
parent_accumulated_scroll_offset);
// The transformation we are passing is the transformation of the parent
// reference frame and the offset is the accumulated offset of all the nodes
// between us and the parent reference frame. If we are a reference frame,
// we need to reset both these values.
let (transform, offset, accumulated_scroll_offset) = match node.node_type {
NodeType::ReferenceFrame(..) =>
(node.world_viewport_transform,
LayerVector2D::zero(),
LayerVector2D::zero()),
_ => {
let scroll_offset = node.scroll_offset();
(*parent_reference_frame_transform,
scroll_offset,
scroll_offset + parent_accumulated_scroll_offset)
}
};
(transform,
as_scroll_parent_rect(&node.combined_local_viewport_rect),
offset,
accumulated_scroll_offset,
node.children.clone())
}
let mut node = match self.nodes.get_mut(&layer_id) {
Some(node) => node,
None => return,
}
};
node.update_transform(parent_reference_frame_transform,
parent_viewport_rect,
parent_scroll_offset,
parent_accumulated_scroll_offset);
// The transformation we are passing is the transformation of the parent
// reference frame and the offset is the accumulated offset of all the nodes
// between us and the parent reference frame. If we are a reference frame,
// we need to reset both these values.
let (reference_frame_transform, scroll_offset, accumulated_scroll_offset) = match node.node_type {
NodeType::ReferenceFrame(..) =>
(node.world_viewport_transform,
LayerVector2D::zero(),
LayerVector2D::zero()),
NodeType::Clip(..) =>
(*parent_reference_frame_transform,
LayerVector2D::zero(),
parent_accumulated_scroll_offset),
NodeType::ScrollFrame(ref scrolling) =>
(*parent_reference_frame_transform,
scrolling.offset,
scrolling.offset + parent_accumulated_scroll_offset),
};
(reference_frame_transform,
node.combined_local_viewport_rect,
scroll_offset,
accumulated_scroll_offset,
node.children.clone())
};
for child_layer_id in node_children {
self.update_node_transform(child_layer_id,
&reference_frame_transform,
&viewport_rect,
&combined_local_viewport_rect,
scroll_offset,
accumulated_scroll_offset);
}
@ -313,7 +314,6 @@ impl ClipScrollTree {
node.set_scroll_origin(&pending_offset, clamping);
}
}
}
pub fn generate_new_clip_id(&mut self, pipeline_id: PipelineId) -> ClipId {
@ -365,6 +365,7 @@ impl ClipScrollTree {
NodeType::Clip(ref info) => {
pt.new_level("Clip".to_owned());
pt.add_item(format!("screen_bounding_rect: {:?}", info.screen_bounding_rect));
pt.add_item(format!("screen_inner_rect: {:?}", info.screen_inner_rect));
pt.new_level(format!("Clip Sources [{}]", info.clip_sources.len()));
for source in &info.clip_sources {
@ -382,9 +383,9 @@ impl ClipScrollTree {
}
pt.add_item(format!("content_size: {:?}", node.content_size));
pt.add_item(format!("combined_local_viewport_rect: {:?}", node.combined_local_viewport_rect));
pt.add_item(format!("local_viewport_rect: {:?}", node.local_viewport_rect));
pt.add_item(format!("local_clip_rect: {:?}", node.local_clip_rect));
pt.add_item(format!("combined_local_viewport_rect: {:?}", node.combined_local_viewport_rect));
pt.add_item(format!("world_viewport_transform: {:?}", node.world_viewport_transform));
pt.add_item(format!("world_content_transform: {:?}", node.world_content_transform));

View File

@ -4,7 +4,7 @@
#![allow(dead_code)]
use webrender_traits::ColorF;
use api::ColorF;
// A subset of the standard CSS colors, useful for defining GPU tag colors etc.

View File

@ -9,7 +9,7 @@ use euclid::{Transform3D, Point2D, Size2D, Rect};
use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, TextureSampler};
use internal_types::{DebugFontVertex, DebugColorVertex, RenderTargetMode, PackedColor};
use std::f32;
use webrender_traits::{ColorF, ImageFormat, DeviceUintSize};
use api::{ColorF, ImageFormat, DeviceUintSize};
pub struct DebugRenderer {
font_vertices: Vec<DebugFontVertex>,
@ -48,14 +48,14 @@ impl DebugRenderer {
font_vertices: Vec::new(),
font_indices: Vec::new(),
line_vertices: Vec::new(),
tri_vao: tri_vao,
tri_vao,
tri_vertices: Vec::new(),
tri_indices: Vec::new(),
font_program_id: font_program_id,
color_program_id: color_program_id,
font_vao: font_vao,
line_vao: line_vao,
font_texture_id: font_texture_id,
font_program_id,
color_program_id,
font_vao,
line_vao,
font_texture_id,
}
}

View File

@ -18,11 +18,12 @@ use std::iter::repeat;
use std::mem;
use std::ops::Add;
use std::path::PathBuf;
use std::ptr;
use std::rc::Rc;
//use std::sync::mpsc::{channel, Sender};
//use std::thread;
use webrender_traits::{ColorF, ImageFormat};
use webrender_traits::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintSize};
use api::{ColorF, ImageFormat};
use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, DeviceUintSize};
#[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)]
pub struct FrameId(usize);
@ -51,11 +52,11 @@ const GL_FORMAT_BGRA_GL: gl::GLuint = gl::BGRA;
const GL_FORMAT_BGRA_GLES: gl::GLuint = gl::BGRA_EXT;
const SHADER_VERSION_GL: &'static str = "#version 150\n";
const SHADER_VERSION_GL: &str = "#version 150\n";
const SHADER_VERSION_GLES: &'static str = "#version 300 es\n";
const SHADER_VERSION_GLES: &str = "#version 300 es\n";
static SHADER_PREAMBLE: &'static str = "shared";
static SHADER_PREAMBLE: &str = "shared";
#[repr(u32)]
pub enum DepthFunction {
@ -298,7 +299,7 @@ impl TextureId {
pub fn new(name: gl::GLuint, texture_target: TextureTarget) -> TextureId {
TextureId {
name: name,
name,
target: texture_target.to_gl_target(),
}
}
@ -488,6 +489,9 @@ struct IBOId(gl::GLuint);
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
pub struct UBOId(gl::GLuint);
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
pub struct PBOId(gl::GLuint);
const MAX_EVENTS_PER_FRAME: usize = 256;
const MAX_PROFILE_FRAMES: usize = 4;
@ -517,8 +521,8 @@ impl<T> GpuFrameProfile<T> {
gl::GlType::Gl => {
let queries = gl.gen_queries(MAX_EVENTS_PER_FRAME as gl::GLint);
GpuFrameProfile {
gl: gl,
queries: queries,
gl,
queries,
samples: Vec::new(),
next_query: 0,
pending_query: 0,
@ -528,7 +532,7 @@ impl<T> GpuFrameProfile<T> {
}
gl::GlType::Gles => {
GpuFrameProfile {
gl: gl,
gl,
queries: Vec::new(),
samples: Vec::new(),
next_query: 0,
@ -585,7 +589,7 @@ impl<T> GpuFrameProfile<T> {
self.pending_query = self.queries[self.next_query];
self.gl.begin_query(gl::TIME_ELAPSED, self.pending_query);
self.samples.push(GpuSample {
tag: tag,
tag,
time_ns: 0,
});
} else {
@ -600,7 +604,7 @@ impl<T> GpuFrameProfile<T> {
where T: NamedTag {
let marker = GpuMarker::new(&self.gl, tag.get_label());
self.samples.push(GpuSample {
tag: tag,
tag,
time_ns: 0,
});
marker
@ -815,7 +819,7 @@ impl FileWatcherThread {
});
FileWatcherThread {
api_tx: api_tx,
api_tx,
}
}
@ -846,6 +850,7 @@ pub struct Device {
bound_textures: [TextureId; 16],
bound_program: ProgramId,
bound_vao: VAOId,
bound_pbo: PBOId,
bound_read_fbo: FBOId,
bound_draw_fbo: FBOId,
default_read_fbo: gl::GLuint,
@ -892,21 +897,22 @@ impl Device {
let max_texture_size = gl.get_integer_v(gl::MAX_TEXTURE_SIZE) as u32;
Device {
gl: gl,
resource_override_path: resource_override_path,
gl,
resource_override_path,
// This is initialized to 1 by default, but it is set
// every frame by the call to begin_frame().
device_pixel_ratio: 1.0,
inside_frame: false,
capabilities: Capabilities {
max_ubo_size: max_ubo_size,
max_ubo_size,
supports_multisampling: false, //TODO
},
bound_textures: [ TextureId::invalid(); 16 ],
bound_program: ProgramId(0),
bound_vao: VAOId(0),
bound_pbo: PBOId(0),
bound_read_fbo: FBOId(0),
bound_draw_fbo: FBOId(0),
default_read_fbo: 0,
@ -916,12 +922,12 @@ impl Device {
programs: HashMap::default(),
vaos: HashMap::default(),
shader_preamble: shader_preamble,
shader_preamble,
next_vao_id: 1,
//file_watcher: file_watcher,
max_texture_size: max_texture_size,
max_texture_size,
frame_id: FrameId(0),
}
}
@ -1006,6 +1012,8 @@ impl Device {
// Pixel op state
self.gl.pixel_store_i(gl::UNPACK_ALIGNMENT, 1);
self.bound_pbo = PBOId(0);
self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0);
// Default is sampler 0, always
self.gl.active_texture(gl::TEXTURE0);
@ -1089,7 +1097,7 @@ impl Device {
let texture = Texture {
gl: Rc::clone(&self.gl),
id: id,
id,
width: 0,
height: 0,
format: ImageFormat::Invalid,
@ -1438,7 +1446,7 @@ impl Device {
u_device_pixel_ratio: -1,
vs_source: get_shader_source(&vs_name, &self.resource_override_path),
fs_source: get_shader_source(&fs_name, &self.resource_override_path),
prefix: prefix,
prefix,
vs_id: None,
fs_id: None,
};
@ -1556,11 +1564,6 @@ impl Device {
self.gl.uniform_1i(u_tasks, TextureSampler::RenderTasks as i32);
}
let u_data32 = self.gl.get_uniform_location(program.id, "sData32");
if u_data32 != -1 {
self.gl.uniform_1i(u_data32, TextureSampler::Data32 as i32);
}
let u_resource_cache = self.gl.get_uniform_location(program.id, "sResourceCache");
if u_resource_cache != -1 {
self.gl.uniform_1i(u_resource_cache, TextureSampler::ResourceCache as i32);
@ -1628,6 +1631,69 @@ impl Device {
self.gl.uniform_1f(program.u_device_pixel_ratio, device_pixel_ratio);
}
pub fn create_pbo(&mut self) -> PBOId {
let id = self.gl.gen_buffers(1)[0];
PBOId(id)
}
pub fn destroy_pbo(&mut self, id: PBOId) {
self.gl.delete_buffers(&[id.0]);
}
pub fn bind_pbo(&mut self, pbo_id: Option<PBOId>) {
debug_assert!(self.inside_frame);
let pbo_id = pbo_id.unwrap_or(PBOId(0));
if self.bound_pbo != pbo_id {
self.bound_pbo = pbo_id;
self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, pbo_id.0);
}
}
pub fn update_pbo_data<T>(&mut self, data: &[T]) {
debug_assert!(self.inside_frame);
debug_assert!(self.bound_pbo.0 != 0);
gl::buffer_data(&*self.gl,
gl::PIXEL_UNPACK_BUFFER,
data,
gl::STREAM_DRAW);
}
pub fn orphan_pbo(&mut self, new_size: usize) {
debug_assert!(self.inside_frame);
debug_assert!(self.bound_pbo.0 != 0);
self.gl.buffer_data_untyped(gl::PIXEL_UNPACK_BUFFER,
new_size as isize,
ptr::null(),
gl::STREAM_DRAW);
}
pub fn update_texture_from_pbo(&mut self,
texture_id: TextureId,
x0: u32,
y0: u32,
width: u32,
height: u32,
offset: usize) {
debug_assert!(self.inside_frame);
debug_assert_eq!(self.textures.get(&texture_id).unwrap().format, ImageFormat::RGBAF32);
self.bind_texture(DEFAULT_TEXTURE, texture_id);
self.gl.tex_sub_image_2d_pbo(texture_id.target,
0,
x0 as gl::GLint,
y0 as gl::GLint,
width as gl::GLint,
height as gl::GLint,
gl::RGBA,
gl::FLOAT,
offset);
}
pub fn update_texture(&mut self,
texture_id: TextureId,
x0: u32,
@ -1728,13 +1794,13 @@ impl Device {
let vao = VAO {
gl: Rc::clone(&self.gl),
id: vao_id,
ibo_id: ibo_id,
main_vbo_id: main_vbo_id,
instance_vbo_id: instance_vbo_id,
instance_stride: instance_stride,
owns_indices: owns_indices,
owns_vertices: owns_vertices,
owns_instances: owns_instances,
ibo_id,
main_vbo_id,
instance_vbo_id,
instance_stride,
owns_indices,
owns_vertices,
owns_instances,
};
self.gl.bind_vertex_array(0);

View File

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use webrender_traits::{LayerPoint, LayerSize};
use api::{LayerPoint, LayerSize};
use std::f32::consts::FRAC_PI_2;
/// Number of steps to integrate arc length over.
@ -23,8 +23,8 @@ impl Ellipse {
radius.height);
Ellipse {
radius: radius,
total_arc_length: total_arc_length,
radius,
total_arc_length,
}
}

View File

@ -2,7 +2,14 @@
* 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::{BuiltDisplayList, BuiltDisplayListIter, ClipAndScrollInfo, ClipId, ColorF};
use api::{ComplexClipRegion, DeviceUintRect, DeviceUintSize, DisplayItemRef, Epoch, FilterOp};
use api::{ImageDisplayItem, ItemRange, LayerPoint, LayerRect, LayerSize, LayerToScrollTransform};
use api::{LayerVector2D, LayoutSize, LayoutTransform, LocalClip, MixBlendMode, PipelineId};
use api::{ScrollClamping, ScrollEventPhase, ScrollLayerState, ScrollLocation, ScrollPolicy};
use api::{SpecificDisplayItem, StackingContext, TileOffset, TransformStyle, WorldPoint};
use app_units::Au;
use clip_scroll_tree::{ClipScrollTree, ScrollStates};
use euclid::rect;
use fnv::FnvHasher;
use gpu_cache::GpuCache;
@ -10,7 +17,7 @@ use internal_types::{ANGLE_FLOAT_TO_FIXED, AxisDirection};
use internal_types::{LowLevelFilterOp};
use internal_types::{RendererFrame};
use frame_builder::{FrameBuilder, FrameBuilderConfig};
use clip_scroll_tree::{ClipScrollTree, ScrollStates};
use mask_cache::ClipRegion;
use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
use resource_cache::ResourceCache;
use scene::{Scene, SceneProperties};
@ -19,13 +26,6 @@ use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use tiling::{CompositeOps, DisplayListMap, PrimitiveFlags};
use util::{ComplexClipRegionHelpers, subtract_rect};
use webrender_traits::{BuiltDisplayList, BuiltDisplayListIter, ClipAndScrollInfo, ClipDisplayItem};
use webrender_traits::{ClipId, ClipRegion, ColorF, DeviceUintRect, DeviceUintSize, DisplayItemRef};
use webrender_traits::{Epoch, FilterOp, ImageDisplayItem, ItemRange, LayerPoint, LayerRect};
use webrender_traits::{LayerSize, LayerToScrollTransform, LayoutSize, LayoutTransform, LayerVector2D};
use webrender_traits::{MixBlendMode, PipelineId, ScrollClamping, ScrollEventPhase};
use webrender_traits::{ScrollLayerState, ScrollLocation, ScrollPolicy, SpecificDisplayItem};
use webrender_traits::{StackingContext, TileOffset, TransformStyle, WorldPoint};
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
pub struct FrameId(pub u32);
@ -97,9 +97,9 @@ impl<'a> FlattenContext<'a> {
resource_cache: &'a mut ResourceCache)
-> FlattenContext<'a> {
FlattenContext {
scene: scene,
builder: builder,
resource_cache: resource_cache,
scene,
builder,
resource_cache,
replacements: Vec::new(),
nested_display_list_info: Vec::new(),
current_nested_display_list_index: 0,
@ -152,6 +152,20 @@ impl<'a> FlattenContext<'a> {
_ => id,
}
}
fn get_complex_clips(&self,
pipeline_id: PipelineId,
complex_clips: ItemRange<ComplexClipRegion>)
-> Vec<ComplexClipRegion> {
if complex_clips.is_empty() {
return vec![];
}
self.scene.display_lists.get(&pipeline_id)
.expect("No display list?")
.get(complex_clips)
.collect()
}
}
// TODO: doc
@ -227,22 +241,6 @@ impl StackingContextHelpers for StackingContext {
}
}
fn clip_intersection(original_rect: &LayerRect,
region: &ClipRegion,
display_list: &BuiltDisplayList)
-> Option<LayerRect> {
if region.image_mask.is_some() {
return None;
}
let clips = display_list.get(region.complex_clips);
let base_rect = region.main.intersection(original_rect);
clips.fold(base_rect, |inner_combined, ccr| {
inner_combined.and_then(|combined| {
ccr.get_inner_rect_full().and_then(|ir| ir.intersection(&combined))
})
})
}
impl Frame {
pub fn new(config: FrameBuilderConfig) -> Frame {
Frame {
@ -357,26 +355,39 @@ impl Frame {
fn flatten_clip<'a>(&mut self,
context: &mut FlattenContext,
pipeline_id: PipelineId,
parent_id: ClipId,
item: &ClipDisplayItem,
content_rect: &LayerRect,
clip: &ClipRegion) {
let clip_viewport = LayerRect::new(content_rect.origin, clip.main.size);
let new_clip_id = self.clip_scroll_tree.generate_new_clip_id(pipeline_id);
context.builder.add_clip_scroll_node(new_clip_id,
parent_id,
pipeline_id,
&clip_viewport,
clip,
&mut self.clip_scroll_tree);
let new_id = context.convert_new_id_to_neested(&item.id);
context.builder.add_scroll_frame(new_id,
new_clip_id,
pipeline_id,
&content_rect,
&clip_viewport,
&mut self.clip_scroll_tree);
parent_id: &ClipId,
new_clip_id: &ClipId,
clip_region: ClipRegion) {
let new_clip_id = context.convert_new_id_to_neested(new_clip_id);
context.builder.add_clip_node(new_clip_id,
*parent_id,
pipeline_id,
clip_region,
&mut self.clip_scroll_tree);
}
fn flatten_scroll_frame<'a>(&mut self,
context: &mut FlattenContext,
pipeline_id: PipelineId,
parent_id: &ClipId,
new_scroll_frame_id: &ClipId,
frame_rect: &LayerRect,
content_rect: &LayerRect,
clip_region: ClipRegion) {
let clip_id = self.clip_scroll_tree.generate_new_clip_id(pipeline_id);
context.builder.add_clip_node(clip_id,
*parent_id,
pipeline_id,
clip_region,
&mut self.clip_scroll_tree);
let new_scroll_frame_id = context.convert_new_id_to_neested(new_scroll_frame_id);
context.builder.add_scroll_frame(new_scroll_frame_id,
clip_id,
pipeline_id,
&frame_rect,
&content_rect.size,
&mut self.clip_scroll_tree);
}
fn flatten_stacking_context<'a>(&mut self,
@ -449,7 +460,6 @@ impl Frame {
context.builder.push_stacking_context(&reference_frame_relative_offset,
pipeline_id,
composition_operations,
*bounds,
stacking_context.transform_style);
self.flatten_items(traversal,
@ -473,7 +483,6 @@ impl Frame {
pipeline_id: PipelineId,
parent_id: ClipId,
bounds: &LayerRect,
clip_region: &ClipRegion,
context: &mut FlattenContext,
reference_frame_relative_offset: LayerVector2D) {
let pipeline = match context.scene.pipeline_map.get(&pipeline_id) {
@ -494,16 +503,8 @@ impl Frame {
reference_frame_relative_offset.y + bounds.origin.y,
0.0);
let new_clip_id = self.clip_scroll_tree.generate_new_clip_id(pipeline_id);
context.builder.add_clip_scroll_node(new_clip_id,
parent_id,
parent_id.pipeline_id(),
bounds,
clip_region,
&mut self.clip_scroll_tree);
let iframe_reference_frame_id =
context.builder.push_reference_frame(Some(new_clip_id),
context.builder.push_reference_frame(Some(parent_id),
pipeline_id,
&iframe_rect,
&transform,
@ -513,8 +514,8 @@ impl Frame {
ClipId::root_scroll_node(pipeline_id),
iframe_reference_frame_id,
pipeline_id,
&LayerRect::new(LayerPoint::zero(), pipeline.content_size),
&iframe_rect,
&pipeline.content_size,
&mut self.clip_scroll_tree);
self.flatten_root(&mut display_list.iter(), pipeline_id, context, &pipeline.content_size);
@ -539,7 +540,7 @@ impl Frame {
SpecificDisplayItem::WebGL(ref info) => {
context.builder.add_webgl_rectangle(clip_and_scroll,
item.rect(),
item.clip_region(),
item.local_clip(),
info.context_id);
}
SpecificDisplayItem::Image(ref info) => {
@ -552,14 +553,14 @@ impl Frame {
self.decompose_image(clip_and_scroll,
context,
&item.rect(),
item.clip_region(),
item.local_clip(),
info,
image_size,
tile_size as u32);
} else {
context.builder.add_image(clip_and_scroll,
item.rect(),
item.clip_region(),
item.local_clip(),
&info.stretch_size,
&info.tile_spacing,
None,
@ -571,7 +572,7 @@ impl Frame {
SpecificDisplayItem::YuvImage(ref info) => {
context.builder.add_yuv_image(clip_and_scroll,
item.rect(),
item.clip_region(),
item.local_clip(),
info.yuv_data,
info.color_space,
info.image_rendering);
@ -579,7 +580,7 @@ impl Frame {
SpecificDisplayItem::Text(ref text_info) => {
context.builder.add_text(clip_and_scroll,
item.rect(),
item.clip_region(),
item.local_clip(),
text_info.font_key,
text_info.size,
text_info.blur_radius,
@ -589,38 +590,23 @@ impl Frame {
text_info.glyph_options);
}
SpecificDisplayItem::Rectangle(ref info) => {
// Try to extract the opaque inner rectangle out of the clipped primitive.
if let Some(opaque_rect) = clip_intersection(&item.rect(),
item.clip_region(),
item.display_list()) {
let mut results = Vec::new();
subtract_rect(&item.rect(), &opaque_rect, &mut results);
// The inner rectangle is considered opaque within this layer.
// It may still inherit some masking from the clip stack.
context.builder.add_solid_rectangle(clip_and_scroll,
&opaque_rect,
&ClipRegion::simple(&item.clip_region().main),
&info.color,
PrimitiveFlags::None);
for transparent_rect in &results {
context.builder.add_solid_rectangle(clip_and_scroll,
transparent_rect,
item.clip_region(),
&info.color,
PrimitiveFlags::None);
}
} else {
if !self.try_to_add_rectangle_splitting_on_clip(context,
&item.rect(),
item.local_clip(),
&info.color,
&clip_and_scroll) {
context.builder.add_solid_rectangle(clip_and_scroll,
&item.rect(),
item.clip_region(),
item.local_clip(),
&info.color,
PrimitiveFlags::None);
}
}
SpecificDisplayItem::Gradient(ref info) => {
context.builder.add_gradient(clip_and_scroll,
item.rect(),
item.clip_region(),
item.local_clip(),
info.gradient.start_point,
info.gradient.end_point,
item.gradient_stops(),
@ -633,7 +619,7 @@ impl Frame {
SpecificDisplayItem::RadialGradient(ref info) => {
context.builder.add_radial_gradient(clip_and_scroll,
item.rect(),
item.clip_region(),
item.local_clip(),
info.gradient.start_center,
info.gradient.start_radius,
info.gradient.end_center,
@ -647,7 +633,7 @@ impl Frame {
SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
context.builder.add_box_shadow(clip_and_scroll,
&box_shadow_info.box_bounds,
item.clip_region(),
item.local_clip(),
&box_shadow_info.offset,
&box_shadow_info.color,
box_shadow_info.blur_radius,
@ -658,7 +644,7 @@ impl Frame {
SpecificDisplayItem::Border(ref info) => {
context.builder.add_border(clip_and_scroll,
item.rect(),
item.clip_region(),
item.local_clip(),
info,
item.gradient_stops(),
item.display_list()
@ -680,18 +666,43 @@ impl Frame {
self.flatten_iframe(info.pipeline_id,
clip_and_scroll.scroll_node_id,
&item.rect(),
&item.clip_region(),
context,
reference_frame_relative_offset);
}
SpecificDisplayItem::Clip(ref info) => {
let content_rect = &item.rect().translate(&reference_frame_relative_offset);
let complex_clips = context.get_complex_clips(pipeline_id, item.complex_clip().0);
let mut clip_region = ClipRegion::for_clip_node(*item.local_clip().clip_rect(),
complex_clips,
info.image_mask);
clip_region.origin += reference_frame_relative_offset;
self.flatten_clip(context,
pipeline_id,
clip_and_scroll.scroll_node_id,
&info,
&content_rect,
item.clip_region());
&clip_and_scroll.scroll_node_id,
&info.id,
clip_region);
}
SpecificDisplayItem::ScrollFrame(ref info) => {
let complex_clips = context.get_complex_clips(pipeline_id, item.complex_clip().0);
let mut clip_region = ClipRegion::for_clip_node(*item.local_clip().clip_rect(),
complex_clips,
info.image_mask);
clip_region.origin += reference_frame_relative_offset;
// Just use clip rectangle as the frame rect for this scroll frame.
// This is only interesting when calculating scroll extents for the
// ClipScrollNode::scroll(..) API
let frame_rect = item.local_clip()
.clip_rect()
.translate(&reference_frame_relative_offset);
let content_rect = item.rect().translate(&reference_frame_relative_offset);
self.flatten_scroll_frame(context,
pipeline_id,
&clip_and_scroll.scroll_node_id,
&info.id,
&frame_rect,
&content_rect,
clip_region);
}
SpecificDisplayItem::PushNestedDisplayList => {
// Using the clip and scroll already processed for nesting here
@ -703,7 +714,7 @@ impl Frame {
SpecificDisplayItem::PopNestedDisplayList => context.pop_nested_display_list_ids(),
// Do nothing; these are dummy items for the display list parser
SpecificDisplayItem::SetGradientStops | SpecificDisplayItem::SetClipRegion(_) => { }
SpecificDisplayItem::SetGradientStops => { }
SpecificDisplayItem::PopStackingContext =>
unreachable!("Should have returned in parent method."),
@ -711,16 +722,63 @@ impl Frame {
None
}
/// Try to optimize the rendering of a solid rectangle that is clipped by a single
/// rounded rectangle, by only masking the parts of the rectangle that intersect
/// the rounded parts of the clip. This is pretty simple now, so has a lot of
/// potential for further optimizations.
fn try_to_add_rectangle_splitting_on_clip(&mut self,
context: &mut FlattenContext,
rect: &LayerRect,
local_clip: &LocalClip,
color: &ColorF,
clip_and_scroll: &ClipAndScrollInfo)
-> bool {
// If this rectangle is not opaque, splitting the rectangle up
// into an inner opaque region just ends up hurting batching and
// doing more work than necessary.
if color.a != 1.0 {
return false;
}
let inner_unclipped_rect = match local_clip {
&LocalClip::Rect(_) => return false,
&LocalClip::RoundedRect(_, ref region) => region.get_inner_rect_full(),
};
let inner_unclipped_rect = match inner_unclipped_rect {
Some(rect) => rect,
None => return false,
};
// The inner rectangle is not clipped by its assigned clipping node, so we can
// let it be clipped by the parent of the clipping node, which may result in
// less masking some cases.
let mut clipped_rects = Vec::new();
subtract_rect(rect, &inner_unclipped_rect, &mut clipped_rects);
context.builder.add_solid_rectangle(*clip_and_scroll,
&inner_unclipped_rect,
&LocalClip::from(*local_clip.clip_rect()),
color,
PrimitiveFlags::None);
for clipped_rect in &clipped_rects {
context.builder.add_solid_rectangle(*clip_and_scroll,
clipped_rect,
local_clip,
color,
PrimitiveFlags::None);
}
true
}
fn flatten_root<'a>(&mut self,
traversal: &mut BuiltDisplayListIter<'a>,
pipeline_id: PipelineId,
context: &mut FlattenContext,
content_size: &LayoutSize) {
let root_bounds = LayerRect::new(LayerPoint::zero(), *content_size);
context.builder.push_stacking_context(&LayerVector2D::zero(),
pipeline_id,
CompositeOps::default(),
root_bounds,
TransformStyle::Flat);
// We do this here, rather than above because we want any of the top-level
@ -735,9 +793,10 @@ impl Frame {
if context.scene.root_pipeline_id != Some(pipeline_id) {
if let Some(pipeline) = context.scene.pipeline_map.get(&pipeline_id) {
if let Some(bg_color) = pipeline.background_color {
let root_bounds = LayerRect::new(LayerPoint::zero(), *content_size);
context.builder.add_solid_rectangle(ClipAndScrollInfo::simple(clip_id),
&root_bounds,
&ClipRegion::simple(&root_bounds),
&LocalClip::from(root_bounds),
&bg_color,
PrimitiveFlags::None);
}
@ -752,7 +811,7 @@ impl Frame {
context.builder.add_solid_rectangle(
ClipAndScrollInfo::simple(clip_id),
&scrollbar_rect,
&ClipRegion::simple(&scrollbar_rect),
&LocalClip::from(scrollbar_rect),
&DEFAULT_SCROLLBAR_COLOR,
PrimitiveFlags::Scrollbar(self.clip_scroll_tree.topmost_scrolling_node_id(), 4.0));
}
@ -802,7 +861,7 @@ impl Frame {
clip_and_scroll: ClipAndScrollInfo,
context: &mut FlattenContext,
item_rect: &LayerRect,
item_clip: &ClipRegion,
item_local_clip: &LocalClip,
info: &ImageDisplayItem,
image_size: DeviceUintSize,
tile_size: u32) {
@ -812,7 +871,7 @@ impl Frame {
self.decompose_image_row(clip_and_scroll,
context,
item_rect,
item_clip,
item_local_clip,
info,
image_size,
tile_size);
@ -832,7 +891,7 @@ impl Frame {
self.decompose_image_row(clip_and_scroll,
context,
&row_rect,
item_clip,
item_local_clip,
info,
image_size,
tile_size);
@ -844,7 +903,7 @@ impl Frame {
clip_and_scroll: ClipAndScrollInfo,
context: &mut FlattenContext,
item_rect: &LayerRect,
item_clip: &ClipRegion,
item_local_clip: &LocalClip,
info: &ImageDisplayItem,
image_size: DeviceUintSize,
tile_size: u32) {
@ -854,7 +913,7 @@ impl Frame {
self.decompose_tiled_image(clip_and_scroll,
context,
item_rect,
item_clip,
item_local_clip,
info,
image_size,
tile_size);
@ -874,7 +933,7 @@ impl Frame {
self.decompose_tiled_image(clip_and_scroll,
context,
&decomposed_rect,
item_clip,
item_local_clip,
info,
image_size,
tile_size);
@ -886,7 +945,7 @@ impl Frame {
clip_and_scroll: ClipAndScrollInfo,
context: &mut FlattenContext,
item_rect: &LayerRect,
item_clip: &ClipRegion,
item_local_clip: &LocalClip,
info: &ImageDisplayItem,
image_size: DeviceUintSize,
tile_size: u32) {
@ -965,7 +1024,7 @@ impl Frame {
self.add_tile_primitive(clip_and_scroll,
context,
item_rect,
item_clip,
item_local_clip,
info,
TileOffset::new(tx, ty),
stretched_tile_size,
@ -977,7 +1036,7 @@ impl Frame {
self.add_tile_primitive(clip_and_scroll,
context,
item_rect,
item_clip,
item_local_clip,
info,
TileOffset::new(num_tiles_x, ty),
stretched_tile_size,
@ -993,7 +1052,7 @@ impl Frame {
self.add_tile_primitive(clip_and_scroll,
context,
item_rect,
item_clip,
item_local_clip,
info,
TileOffset::new(tx, num_tiles_y),
stretched_tile_size,
@ -1008,7 +1067,7 @@ impl Frame {
self.add_tile_primitive(clip_and_scroll,
context,
item_rect,
item_clip,
item_local_clip,
info,
TileOffset::new(num_tiles_x, num_tiles_y),
stretched_tile_size,
@ -1024,7 +1083,7 @@ impl Frame {
clip_and_scroll: ClipAndScrollInfo,
context: &mut FlattenContext,
item_rect: &LayerRect,
item_clip: &ClipRegion,
item_local_clip: &LocalClip,
info: &ImageDisplayItem,
tile_offset: TileOffset,
stretched_tile_size: LayerSize,
@ -1068,7 +1127,7 @@ impl Frame {
if let Some(prim_rect) = prim_rect.intersection(item_rect) {
context.builder.add_image(clip_and_scroll,
prim_rect,
item_clip,
item_local_clip,
&stretched_size,
&info.tile_spacing,
None,

File diff suppressed because it is too large Load Diff

View File

@ -18,10 +18,10 @@ use std::collections::hash_map::Entry;
use std::collections::HashSet;
use std::mem;
use texture_cache::{TextureCacheItemId, TextureCache};
use webrender_traits::FontTemplate;
use webrender_traits::{FontKey, FontRenderMode, ImageData, ImageFormat};
use webrender_traits::{ImageDescriptor, ColorF, LayoutPoint};
use webrender_traits::{GlyphKey, GlyphOptions, GlyphInstance, GlyphDimensions};
use api::FontTemplate;
use api::{FontKey, FontRenderMode, ImageData, ImageFormat};
use api::{ImageDescriptor, ColorF, LayoutPoint};
use api::{GlyphKey, GlyphOptions, GlyphInstance, GlyphDimensions};
pub type GlyphCache = ResourceClassCache<GlyphRequest, Option<TextureCacheItemId>>;
@ -114,10 +114,10 @@ impl GlyphRasterizer {
workers: Arc::clone(&workers),
}
),
glyph_rx: glyph_rx,
glyph_tx: glyph_tx,
glyph_rx,
glyph_tx,
pending_glyphs: HashSet::new(),
workers: workers,
workers,
fonts_to_remove: Vec::new(),
}
}
@ -337,8 +337,8 @@ impl GlyphRequest {
) -> GlyphRequest {
GlyphRequest {
key: GlyphKey::new(font_key, size, color, index, point, render_mode),
render_mode: render_mode,
glyph_options: glyph_options,
render_mode,
glyph_options,
}
}
}

View File

@ -29,7 +29,7 @@ use internal_types::UvRect;
use profiler::GpuCacheProfileCounters;
use renderer::MAX_VERTEX_TEXTURE_WIDTH;
use std::{mem, u32};
use webrender_traits::{ColorF, LayerRect};
use api::{ColorF, LayerRect};
pub const GPU_CACHE_INITIAL_HEIGHT: u32 = 512;
const FRAMES_BEFORE_EVICTION: usize = 10;
@ -56,6 +56,14 @@ pub struct GpuBlockData {
pub data: [f32; 4],
}
impl GpuBlockData {
pub fn empty() -> GpuBlockData {
GpuBlockData {
data: [0.0; 4],
}
}
}
/// Conversion helpers for GpuBlockData
impl Into<GpuBlockData> for ColorF {
fn into(self) -> GpuBlockData {
@ -154,8 +162,8 @@ impl Block {
next: Option<BlockIndex>,
frame_id: FrameId) -> Block {
Block {
address: address,
next: next,
address,
next,
last_access_time: frame_id,
epoch: Epoch(0),
}
@ -177,7 +185,7 @@ struct Row {
impl Row {
fn new(block_count_per_item: usize) -> Row {
Row {
block_count_per_item: block_count_per_item,
block_count_per_item,
}
}
}
@ -348,7 +356,7 @@ impl Texture {
// to be updated on the GPU.
self.updates.push(GpuCacheUpdate::Copy {
block_index: pending_block_index,
block_count: block_count,
block_count,
address: block.address,
});
}
@ -431,13 +439,20 @@ pub struct GpuDataRequest<'a> {
}
impl<'a> GpuDataRequest<'a> {
pub fn push(&mut self, block: GpuBlockData) {
self.texture.pending_blocks.push(block);
pub fn push<B>(&mut self, block: B)
where B: Into<GpuBlockData>
{
self.texture.pending_blocks.push(block.into());
}
pub fn extend_from_slice(&mut self, blocks: &[GpuBlockData]) {
self.texture.pending_blocks.extend_from_slice(blocks);
}
/// Consume the request and return the number of blocks written
pub fn close(self) -> usize {
self.texture.pending_blocks.len() - self.start_index
}
}
impl<'a> Drop for GpuDataRequest<'a> {
@ -499,7 +514,7 @@ impl GpuCache {
}
Some(GpuDataRequest {
handle: handle,
handle,
frame_id: self.frame_id,
start_index: self.texture.pending_blocks.len(),
texture: &mut self.texture,

View File

@ -1,158 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use device::TextureFilter;
use std::marker::PhantomData;
use std::mem;
use std::ops::Add;
use util::recycle_vec;
use webrender_traits::ImageFormat;
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
pub struct GpuStoreAddress(pub i32);
impl Add<i32> for GpuStoreAddress {
type Output = GpuStoreAddress;
fn add(self, other: i32) -> GpuStoreAddress {
GpuStoreAddress(self.0 + other)
}
}
impl Add<usize> for GpuStoreAddress {
type Output = GpuStoreAddress;
fn add(self, other: usize) -> GpuStoreAddress {
GpuStoreAddress(self.0 + other as i32)
}
}
pub trait GpuStoreLayout {
fn image_format() -> ImageFormat;
fn texture_width<T>() -> usize;
fn texture_filter() -> TextureFilter;
fn texel_size() -> usize {
match Self::image_format() {
ImageFormat::BGRA8 => 4,
ImageFormat::RGBAF32 => 16,
_ => unreachable!(),
}
}
fn texels_per_item<T>() -> usize {
let item_size = mem::size_of::<T>();
let texel_size = Self::texel_size();
debug_assert!(item_size % texel_size == 0);
item_size / texel_size
}
fn items_per_row<T>() -> usize {
Self::texture_width::<T>() / Self::texels_per_item::<T>()
}
fn rows_per_item<T>() -> usize {
Self::texels_per_item::<T>() / Self::texture_width::<T>()
}
}
/// A CPU-side buffer storing content to be uploaded to the GPU.
pub struct GpuStore<T, L> {
data: Vec<T>,
layout: PhantomData<L>,
// TODO(gw): Could store this intrusively inside
// the data array free slots.
//free_list: Vec<GpuStoreAddress>,
}
impl<T: Clone + Default, L: GpuStoreLayout> GpuStore<T, L> {
pub fn new() -> GpuStore<T, L> {
GpuStore {
data: Vec::new(),
layout: PhantomData,
//free_list: Vec::new(),
}
}
pub fn recycle(self) -> Self {
GpuStore {
data: recycle_vec(self.data),
layout: PhantomData,
}
}
pub fn push<E>(&mut self, data: E) -> GpuStoreAddress where T: From<E> {
let address = GpuStoreAddress(self.data.len() as i32);
self.data.push(T::from(data));
address
}
// TODO(gw): Change this to do incremental updates, which means
// there is no need to copy all this data during every scroll!
pub fn build(&self) -> Vec<T> {
let items_per_row = L::items_per_row::<T>();
let mut items = self.data.clone();
// Extend the data array to be a multiple of the row size.
// This ensures memory safety when the array is passed to
// OpenGL to upload to the GPU.
if items_per_row != 0 {
while items_per_row != 0 && items.len() % items_per_row != 0 {
items.push(T::default());
}
}
items
}
pub fn alloc(&mut self, count: usize) -> GpuStoreAddress {
let address = self.get_next_address();
for _ in 0..count {
self.data.push(T::default());
}
address
}
pub fn get_next_address(&self) -> GpuStoreAddress {
GpuStoreAddress(self.data.len() as i32)
}
pub fn get(&mut self, address: GpuStoreAddress) -> &T {
&self.data[address.0 as usize]
}
pub fn get_mut(&mut self, address: GpuStoreAddress) -> &mut T {
&mut self.data[address.0 as usize]
}
pub fn get_slice_mut(&mut self,
address: GpuStoreAddress,
count: usize) -> &mut [T] {
let offset = address.0 as usize;
&mut self.data[offset..offset + count]
}
pub fn clear(&mut self) {
self.data.clear()
}
// TODO(gw): Implement incremental updates of
// GPU backed data, and support freelist for removing
// dynamic items.
/*
pub fn free(&mut self, address: GpuStoreAddress) {
}
pub fn update(&mut self, address: GpuStoreAddress, data: T) {
}*/
}

View File

@ -14,8 +14,8 @@ use std::path::PathBuf;
use std::sync::Arc;
use tiling;
use renderer::BlendMode;
use webrender_traits::{ClipId, ColorF, DeviceUintRect, Epoch, ExternalImageData, ExternalImageId};
use webrender_traits::{DevicePoint, ImageData, ImageFormat, PipelineId};
use api::{ClipId, ColorF, DeviceUintRect, Epoch, ExternalImageData, ExternalImageId};
use api::{DevicePoint, ImageData, ImageFormat, PipelineId};
// An ID for a texture that is owned by the
// texture cache module. This can include atlases
@ -59,7 +59,6 @@ pub enum TextureSampler {
Color2,
CacheA8,
CacheRGBA8,
Data32,
ResourceCache,
Layers,
RenderTasks,
@ -171,11 +170,11 @@ pub struct DebugFontVertex {
impl DebugFontVertex {
pub fn new(x: f32, y: f32, u: f32, v: f32, color: PackedColor) -> DebugFontVertex {
DebugFontVertex {
x: x,
y: y,
color: color,
u: u,
v: v,
x,
y,
color,
u,
v,
}
}
}
@ -190,9 +189,9 @@ pub struct DebugColorVertex {
impl DebugColorVertex {
pub fn new(x: f32, y: f32, color: PackedColor) -> DebugColorVertex {
DebugColorVertex {
x: x,
y: y,
color: color,
x,
y,
color,
}
}
}
@ -279,9 +278,9 @@ impl RendererFrame {
frame: Option<tiling::Frame>)
-> RendererFrame {
RendererFrame {
pipeline_epoch_map: pipeline_epoch_map,
layers_bouncing_back: layers_bouncing_back,
frame: frame,
pipeline_epoch_map,
layers_bouncing_back,
frame,
}
}
}

View File

@ -33,7 +33,7 @@
//! they're nestable.
//!
//! [stacking_contexts]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
//! [newframe]: ../webrender_traits/struct.RenderApi.html#method.set_display_list
//! [newframe]: ../webrender_api/struct.RenderApi.html#method.set_display_list
//! [notifier]: renderer/struct.Renderer.html#method.set_render_notifier
#[macro_use]
@ -59,7 +59,6 @@ mod freelist;
mod geometry;
mod glyph_rasterizer;
mod gpu_cache;
mod gpu_store;
mod internal_types;
mod mask_cache;
mod prim_store;
@ -134,7 +133,7 @@ extern crate gleam;
extern crate num_traits;
//extern crate notify;
extern crate time;
extern crate webrender_traits;
pub extern crate webrender_api;
#[cfg(feature = "webgl")]
extern crate offscreen_gl_context;
extern crate byteorder;
@ -146,3 +145,5 @@ extern crate gamma_lut;
pub use renderer::{ExternalImage, ExternalImageSource, ExternalImageHandler};
pub use renderer::{GraphicsApi, GraphicsApiInfo, ReadPixelsFormat, Renderer, RendererOptions};
pub use webrender_api as api;

View File

@ -2,19 +2,63 @@
* 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::{BorderRadius, ComplexClipRegion, DeviceIntRect, ImageMask, LayerPoint, LayerRect};
use api::{LayerSize, LayerToWorldTransform, LocalClip};
use border::BorderCornerClipSource;
use gpu_store::GpuStoreAddress;
use prim_store::{ClipData, GpuBlock32, ImageMaskData, PrimitiveStore};
use prim_store::{CLIP_DATA_GPU_SIZE, MASK_DATA_GPU_SIZE};
use renderer::VertexDataStore;
use util::{ComplexClipRegionHelpers, MatrixHelpers, TransformedRect};
use webrender_traits::{BorderRadius, BuiltDisplayList, ClipRegion, ComplexClipRegion, ImageMask};
use webrender_traits::{DeviceIntRect, LayerToWorldTransform};
use webrender_traits::{DeviceRect, LayerRect, LayerPoint, LayerSize};
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
use prim_store::{CLIP_DATA_GPU_BLOCKS, ClipData, ImageMaskData};
use util::{ComplexClipRegionHelpers, TransformedRect};
use std::ops::Not;
const MAX_CLIP: f32 = 1000000.0;
#[derive(Clone, Debug)]
pub struct ClipRegion {
pub origin: LayerPoint,
pub main: LayerRect,
pub image_mask: Option<ImageMask>,
pub complex_clips: Vec<ComplexClipRegion>,
}
impl ClipRegion {
pub fn for_clip_node(rect: LayerRect,
mut complex_clips: Vec<ComplexClipRegion>,
mut image_mask: Option<ImageMask>)
-> ClipRegion {
// All the coordinates we receive are relative to the stacking context, but we want
// to convert them to something relative to the origin of the clip.
let negative_origin = -rect.origin.to_vector();
if let Some(ref mut image_mask) = image_mask {
image_mask.rect = image_mask.rect.translate(&negative_origin);
}
for complex_clip in complex_clips.iter_mut() {
complex_clip.rect = complex_clip.rect.translate(&negative_origin);
}
ClipRegion {
origin: rect.origin,
main: LayerRect::new(LayerPoint::zero(), rect.size),
image_mask,
complex_clips,
}
}
pub fn for_local_clip(local_clip: &LocalClip) -> ClipRegion {
let complex_clips = match local_clip {
&LocalClip::Rect(_) => Vec::new(),
&LocalClip::RoundedRect(_, ref region) => vec![region.clone()],
};
ClipRegion {
origin: LayerPoint::zero(),
main: *local_clip.clip_rect(),
image_mask: None,
complex_clips,
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ClipMode {
@ -33,26 +77,14 @@ impl Not for ClipMode {
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum RegionMode {
IncludeRect,
ExcludeRect,
}
#[derive(Clone, Debug)]
pub enum ClipSource {
Complex(LayerRect, f32, ClipMode),
// The RegionMode here specifies whether to consider the rect
// from the clip region as part of the mask. This is true
// for clip/scroll nodes, but false for primitives, where
// the clip rect is handled in local space.
Region(ClipRegion, RegionMode),
// TODO(gw): This currently only handles dashed style
// clips, where the border style is dashed for both
// adjacent border edges. Expand to handle dotted style
// and different styles per edge.
Region(ClipRegion),
/// TODO(gw): This currently only handles dashed style
/// clips, where the border style is dashed for both
/// adjacent border edges. Expand to handle dotted style
/// and different styles per edge.
BorderCorner(BorderCornerClipSource),
}
@ -60,42 +92,53 @@ impl ClipSource {
pub fn image_mask(&self) -> Option<ImageMask> {
match *self {
ClipSource::Complex(..) |
ClipSource::BorderCorner{..} => None,
ClipSource::Region(ref region, _) => region.image_mask,
ClipSource::BorderCorner(..) => None,
ClipSource::Region(ref region) => region.image_mask,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone)]
pub struct ClipAddressRange {
pub start: GpuStoreAddress,
pub location: GpuCacheHandle,
item_count: usize,
}
/// Represents a local rect and a device space
/// bounding rect that can be updated when the
/// transform changes.
#[derive(Clone, Debug, PartialEq)]
pub struct Geometry {
pub local_rect: LayerRect,
pub bounding_rect: DeviceIntRect,
}
impl Geometry {
fn new(local_rect: LayerRect) -> Geometry {
Geometry {
local_rect: local_rect,
bounding_rect: DeviceIntRect::zero(),
impl ClipAddressRange {
fn new(count: usize) -> Self {
ClipAddressRange {
location: GpuCacheHandle::new(),
item_count: count,
}
}
fn update(&mut self,
transform: &LayerToWorldTransform,
device_pixel_ratio: f32) {
let transformed = TransformedRect::new(&self.local_rect,
transform,
device_pixel_ratio);
self.bounding_rect = transformed.bounding_rect;
pub fn get_count(&self) -> usize {
self.item_count
}
fn get_block_count(&self) -> Option<usize> {
if self.item_count != 0 {
Some(self.item_count * CLIP_DATA_GPU_BLOCKS)
} else {
None
}
}
}
/// Represents a local rect and a device space
/// rectangles that are either outside or inside bounds.
#[derive(Clone, Debug, PartialEq)]
pub struct Geometry {
pub local_rect: LayerRect,
pub device_rect: DeviceIntRect,
}
impl From<LayerRect> for Geometry {
fn from(local_rect: LayerRect) -> Self {
Geometry {
local_rect,
device_rect: DeviceIntRect::zero(),
}
}
}
@ -106,41 +149,48 @@ impl Geometry {
/// correctness. In the future we can make this a lot
/// more clever with some proper region handling.
#[derive(Clone, Debug, PartialEq)]
pub enum MaskBounds {
/// We know both the outer and inner rect. This is the
/// fast path for, e.g. a simple rounded rect.
OuterInner(Geometry, Geometry),
/// We know the outer rect only.
Outer(Geometry),
/// We can't determine the bounds - draw mask over entire rect.
/// This is currently used for clip-out operations on
/// box shadows.
None,
pub struct MaskBounds {
pub outer: Option<Geometry>,
pub inner: Option<Geometry>,
}
impl MaskBounds {
pub fn update(&mut self, transform: &LayerToWorldTransform, device_pixel_ratio: f32) {
if let Some(ref mut outer) = self.outer {
let transformed = TransformedRect::new(&outer.local_rect,
transform,
device_pixel_ratio);
outer.device_rect = transformed.bounding_rect;
}
if let Some(ref mut inner) = self.inner {
let transformed = TransformedRect::new(&inner.local_rect,
transform,
device_pixel_ratio);
inner.device_rect = transformed.inner_rect;
}
}
}
#[derive(Clone, Debug)]
pub struct MaskCacheInfo {
/// Clip items that are always applied
pub complex_clip_range: ClipAddressRange,
pub effective_complex_clip_count: usize,
pub image: Option<(ImageMask, GpuStoreAddress)>,
pub border_corners: Vec<(BorderCornerClipSource, GpuStoreAddress)>,
pub bounds: Option<MaskBounds>,
pub is_aligned: bool,
/// Clip items that are only applied if the clip space is transformed from
/// the local space of target primitive/layer.
pub layer_clip_range: ClipAddressRange,
pub image: Option<(ImageMask, GpuCacheHandle)>,
pub border_corners: Vec<(BorderCornerClipSource, GpuCacheHandle)>,
pub bounds: MaskBounds,
}
impl MaskCacheInfo {
/// Create a new mask cache info. It allocates the GPU store data but leaves
/// it unitialized for the following `update()` call to deal with.
pub fn new(clips: &[ClipSource],
clip_store: &mut VertexDataStore<GpuBlock32>)
-> Option<MaskCacheInfo> {
if clips.is_empty() {
return None;
}
/// it uninitialized for the following `update()` call to deal with.
pub fn new(clips: &[ClipSource]) -> MaskCacheInfo {
let mut image = None;
let mut border_corners = Vec::new();
let mut complex_clip_count = 0;
let mut layer_clip_count = 0;
// Work out how much clip data space we need to allocate
// and if we have an image mask.
@ -149,63 +199,47 @@ impl MaskCacheInfo {
ClipSource::Complex(..) => {
complex_clip_count += 1;
}
ClipSource::Region(ref region, region_mode) => {
ClipSource::Region(ref region) => {
if let Some(info) = region.image_mask {
debug_assert!(image.is_none()); // TODO(gw): Support >1 image mask!
image = Some((info, clip_store.alloc(MASK_DATA_GPU_SIZE)));
}
complex_clip_count += region.complex_clip_count;
if region_mode == RegionMode::IncludeRect {
complex_clip_count += 1;
image = Some((info, GpuCacheHandle::new()));
}
complex_clip_count += region.complex_clips.len();
layer_clip_count += 1;
}
ClipSource::BorderCorner(ref source) => {
// One block for the corner header, plus one
// block per dash to clip out.
let gpu_address = clip_store.alloc(1 + source.max_clip_count);
border_corners.push((source.clone(), gpu_address));
border_corners.push((source.clone(), GpuCacheHandle::new()));
}
}
}
let complex_clip_range = ClipAddressRange {
start: if complex_clip_count > 0 {
clip_store.alloc(CLIP_DATA_GPU_SIZE * complex_clip_count)
} else {
GpuStoreAddress(0)
MaskCacheInfo {
complex_clip_range: ClipAddressRange::new(complex_clip_count),
layer_clip_range: ClipAddressRange::new(layer_clip_count),
image,
border_corners,
bounds: MaskBounds {
inner: None,
outer: None,
},
item_count: complex_clip_count,
};
Some(MaskCacheInfo {
complex_clip_range: complex_clip_range,
effective_complex_clip_count: complex_clip_range.item_count,
image: image,
border_corners: border_corners,
bounds: None,
is_aligned: true,
})
}
}
pub fn update(&mut self,
sources: &[ClipSource],
transform: &LayerToWorldTransform,
clip_store: &mut VertexDataStore<GpuBlock32>,
device_pixel_ratio: f32,
display_list: &BuiltDisplayList) {
let is_aligned = transform.preserves_2d_axis_alignment();
gpu_cache: &mut GpuCache,
device_pixel_ratio: f32)
-> &MaskBounds {
// If we haven't cached this info, or if the transform type has changed
// we need to re-calculate the number of clips.
if self.bounds.is_none() || self.is_aligned != is_aligned {
// Step[1] - compute the local bounds
//TODO: move to initialization stage?
if self.bounds.inner.is_none() {
let mut local_rect = Some(LayerRect::new(LayerPoint::new(-MAX_CLIP, -MAX_CLIP),
LayerSize::new(2.0 * MAX_CLIP, 2.0 * MAX_CLIP)));
let mut local_inner: Option<LayerRect> = None;
let mut has_clip_out = false;
let mut has_border_clip = false;
self.effective_complex_clip_count = 0;
self.is_aligned = is_aligned;
let has_border_clip = !self.border_corners.is_empty();
for source in sources {
match *source {
@ -214,48 +248,25 @@ impl MaskCacheInfo {
// case clip mask size, for now.
if mode == ClipMode::ClipOut {
has_clip_out = true;
break;
}
debug_assert!(self.effective_complex_clip_count < self.complex_clip_range.item_count);
let address = self.complex_clip_range.start + self.effective_complex_clip_count * CLIP_DATA_GPU_SIZE;
self.effective_complex_clip_count += 1;
let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
let data = ClipData::uniform(rect, radius, mode);
PrimitiveStore::populate_clip_data(slice, data);
local_rect = local_rect.and_then(|r| r.intersection(&rect));
local_inner = ComplexClipRegion::new(rect, BorderRadius::uniform(radius))
.get_inner_rect_safe();
}
ClipSource::Region(ref region, region_mode) => {
ClipSource::Region(ref region) => {
local_rect = local_rect.and_then(|r| r.intersection(&region.main));
local_inner = match region.image_mask {
Some(ref mask) if !mask.repeat => {
local_rect = local_rect.and_then(|r| r.intersection(&mask.rect));
Some(ref mask) => {
if !mask.repeat {
local_rect = local_rect.and_then(|r| r.intersection(&mask.rect));
}
None
},
Some(_) => None,
None => local_rect,
};
let clips = display_list.get(region.complex_clips);
if !self.is_aligned && region_mode == RegionMode::IncludeRect {
// we have an extra clip rect coming from the transformed layer
debug_assert!(self.effective_complex_clip_count < self.complex_clip_range.item_count);
let address = self.complex_clip_range.start + self.effective_complex_clip_count * CLIP_DATA_GPU_SIZE;
self.effective_complex_clip_count += 1;
let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE);
PrimitiveStore::populate_clip_data(slice, ClipData::uniform(region.main, 0.0, ClipMode::Clip));
}
debug_assert!(self.effective_complex_clip_count + clips.len() <= self.complex_clip_range.item_count);
let address = self.complex_clip_range.start + self.effective_complex_clip_count * CLIP_DATA_GPU_SIZE;
self.effective_complex_clip_count += clips.len();
let slice = clip_store.get_slice_mut(address, CLIP_DATA_GPU_SIZE * clips.len());
for (clip, chunk) in clips.zip(slice.chunks_mut(CLIP_DATA_GPU_SIZE)) {
let data = ClipData::from_clip_region(&clip);
PrimitiveStore::populate_clip_data(chunk, data);
for clip in &region.complex_clips {
local_rect = local_rect.and_then(|r| r.intersection(&clip.rect));
local_inner = local_inner.and_then(|r| clip.get_inner_rect_safe()
.and_then(|ref inner| r.intersection(inner)));
@ -265,26 +276,14 @@ impl MaskCacheInfo {
}
}
for &mut (ref mut source, gpu_address) in &mut self.border_corners {
has_border_clip = true;
let slice = clip_store.get_slice_mut(gpu_address,
1 + source.max_clip_count);
source.populate_gpu_data(slice);
}
if let Some((ref mask, gpu_address)) = self.image {
let mask_data = clip_store.get_slice_mut(gpu_address, MASK_DATA_GPU_SIZE);
mask_data[0] = GpuBlock32::from(ImageMaskData {
padding: DeviceRect::zero(),
local_rect: mask.rect,
});
}
// Work out the type of mask geometry we have, based on the
// list of clip sources above.
if has_clip_out || has_border_clip {
self.bounds = if has_clip_out || has_border_clip {
// For clip-out, the mask rect is not known.
self.bounds = Some(MaskBounds::None);
MaskBounds {
outer: None,
inner: Some(LayerRect::zero().into()),
}
} else {
// TODO(gw): local inner is only valid if there's a single clip (for now).
// This can be improved in the future, with some proper
@ -293,40 +292,81 @@ impl MaskCacheInfo {
local_inner = None;
}
let local_rect = local_rect.unwrap_or(LayerRect::zero());
MaskBounds {
outer: Some(local_rect.unwrap_or(LayerRect::zero()).into()),
inner: Some(local_inner.unwrap_or(LayerRect::zero()).into()),
}
};
}
self.bounds = match local_inner {
Some(local_inner) => {
Some(MaskBounds::OuterInner(Geometry::new(local_rect),
Geometry::new(local_inner)))
// Step[2] - update GPU cache data
if let Some(block_count) = self.complex_clip_range.get_block_count() {
if let Some(mut request) = gpu_cache.request(&mut self.complex_clip_range.location) {
for source in sources {
match *source {
ClipSource::Complex(rect, radius, mode) => {
let data = ClipData::uniform(rect, radius, mode);
data.write(&mut request);
}
ClipSource::Region(ref region) => {
for clip in &region.complex_clips {
let data = ClipData::from_clip_region(&clip);
data.write(&mut request);
}
}
ClipSource::BorderCorner{..} => {}
}
None => {
Some(MaskBounds::Outer(Geometry::new(local_rect)))
}
assert_eq!(request.close(), block_count);
}
}
if let Some(block_count) = self.layer_clip_range.get_block_count() {
if let Some(mut request) = gpu_cache.request(&mut self.layer_clip_range.location) {
for source in sources {
if let ClipSource::Region(ref region) = *source {
let data = ClipData::uniform(region.main, 0.0, ClipMode::Clip);
data.write(&mut request);
}
}
assert_eq!(request.close(), block_count);
}
}
for &mut (ref mut border_source, ref mut gpu_location) in &mut self.border_corners {
if let Some(request) = gpu_cache.request(gpu_location) {
border_source.write(request);
}
}
if let Some((ref mask, ref mut gpu_location)) = self.image {
if let Some(request) = gpu_cache.request(gpu_location) {
let data = ImageMaskData {
local_rect: mask.rect,
};
data.write_gpu_blocks(request);
}
}
// Update the device space bounding rects of the mask
// geometry.
match self.bounds.as_mut().unwrap() {
&mut MaskBounds::None => {}
&mut MaskBounds::Outer(ref mut outer) => {
outer.update(transform, device_pixel_ratio);
}
&mut MaskBounds::OuterInner(ref mut outer, ref mut inner) => {
outer.update(transform, device_pixel_ratio);
inner.update(transform, device_pixel_ratio);
}
}
// Step[3] - update the screen bounds
self.bounds.update(transform, device_pixel_ratio);
&self.bounds
}
/// Check if this `MaskCacheInfo` actually carries any masks. `effective_complex_clip_count`
/// can change during the `update` call depending on the transformation, so the mask may
/// appear to be empty.
/// Check if this `MaskCacheInfo` actually carries any masks.
pub fn is_masking(&self) -> bool {
self.image.is_some() ||
self.effective_complex_clip_count != 0 ||
self.complex_clip_range.item_count != 0 ||
self.layer_clip_range.item_count != 0 ||
!self.border_corners.is_empty()
}
/// Return a clone of this object without any layer-aligned clip items
pub fn strip_aligned(&self) -> Self {
MaskCacheInfo {
layer_clip_range: ClipAddressRange::new(0),
.. self.clone()
}
}
}

View File

@ -15,9 +15,9 @@ use core_text::font_descriptor::kCTFontDefaultOrientation;
use core_text;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use webrender_traits::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
use webrender_traits::{GlyphKey, GlyphOptions, SubpixelPoint};
use webrender_traits::NativeFontHandle;
use api::{ColorU, FontKey, FontRenderMode, GlyphDimensions};
use api::{GlyphKey, GlyphOptions, SubpixelPoint};
use api::NativeFontHandle;
use gamma_lut::{GammaLut, Color as ColorLut};
pub struct FontContext {

View File

@ -3,9 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use app_units::Au;
use webrender_traits::{FontKey, FontRenderMode, GlyphDimensions};
use webrender_traits::{NativeFontHandle, GlyphOptions};
use webrender_traits::{GlyphKey};
use api::{FontKey, FontRenderMode, GlyphDimensions};
use api::{NativeFontHandle, GlyphOptions};
use api::{GlyphKey};
use freetype::freetype::{FT_Render_Mode, FT_Pixel_Mode};
use freetype::freetype::{FT_Done_FreeType, FT_Library_SetLcdFilter};
@ -65,7 +65,7 @@ impl FontContext {
}
FontContext {
lib: lib,
lib,
faces: HashMap::new(),
}
}
@ -86,7 +86,7 @@ impl FontContext {
};
if result.succeeded() && !face.is_null() {
self.faces.insert(*font_key, Face {
face: face,
face,
//_bytes: bytes
});
} else {

View File

@ -3,8 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
use webrender_traits::{FontKey, FontRenderMode, GlyphDimensions};
use webrender_traits::{GlyphKey, GlyphOptions};
use api::{FontKey, FontRenderMode, GlyphDimensions};
use api::{GlyphKey, GlyphOptions};
use gamma_lut::{GammaLut, Color as ColorLut};
use dwrote;
@ -103,8 +103,8 @@ fn get_glyph_dimensions_with_analysis(analysis: dwrote::GlyphRunAnalysis,
Some(GlyphDimensions {
left: bounds.left,
top: -bounds.top,
width: width,
height: height,
width,
height,
})
}

View File

@ -2,30 +2,53 @@
* 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::{BuiltDisplayList, ColorF, ComplexClipRegion, DeviceIntRect, DeviceIntSize, DevicePoint};
use api::{ExtendMode, FontKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop};
use api::{ImageKey, ImageRendering, ItemRange, LayerPoint, LayerRect, LayerSize};
use api::{LayerToWorldTransform, TileOffset, WebGLContextId, YuvColorSpace, YuvFormat};
use api::device_length;
use app_units::Au;
use border::{BorderCornerClipData, BorderCornerDashClipData, BorderCornerDotClipData};
use border::BorderCornerInstance;
use euclid::{Size2D};
use gpu_cache::{GpuCacheAddress, GpuBlockData, GpuCache, GpuCacheHandle, GpuDataRequest, ToGpuBlocks};
use gpu_store::GpuStoreAddress;
use mask_cache::{ClipMode, ClipSource, MaskCacheInfo};
use renderer::{VertexDataStore, MAX_VERTEX_TEXTURE_WIDTH};
use mask_cache::{ClipMode, ClipRegion, ClipSource, MaskCacheInfo};
use renderer::MAX_VERTEX_TEXTURE_WIDTH;
use render_task::{RenderTask, RenderTaskLocation};
use resource_cache::{ImageProperties, ResourceCache};
use std::mem;
use std::usize;
use std::{mem, usize};
use util::{TransformedRect, recycle_vec};
use webrender_traits::{BuiltDisplayList, ColorF, ImageKey, ImageRendering, YuvColorSpace};
use webrender_traits::{YuvFormat, ClipRegion, ComplexClipRegion, ItemRange};
use webrender_traits::{FontKey, FontRenderMode, WebGLContextId};
use webrender_traits::{device_length, DeviceIntRect, DeviceIntSize};
use webrender_traits::{DeviceRect, DevicePoint};
use webrender_traits::{LayerRect, LayerSize, LayerPoint};
use webrender_traits::{LayerToWorldTransform, GlyphInstance, GlyphOptions};
use webrender_traits::{ExtendMode, GradientStop, TileOffset};
pub const CLIP_DATA_GPU_SIZE: usize = 5;
pub const MASK_DATA_GPU_SIZE: usize = 1;
pub const CLIP_DATA_GPU_BLOCKS: usize = 10;
#[derive(Debug, Copy, Clone)]
pub struct PrimitiveOpacity {
pub is_opaque: bool,
}
impl PrimitiveOpacity {
pub fn opaque() -> PrimitiveOpacity {
PrimitiveOpacity {
is_opaque: true,
}
}
pub fn translucent() -> PrimitiveOpacity {
PrimitiveOpacity {
is_opaque: false,
}
}
pub fn from_alpha(alpha: f32) -> PrimitiveOpacity {
PrimitiveOpacity {
is_opaque: alpha == 1.0,
}
}
pub fn accumulate(&mut self, alpha: f32) {
self.is_opaque = self.is_opaque && alpha == 1.0;
}
}
/// Stores two coordinates in texel space. The coordinates
/// are stored in texel coordinates because the texture atlas
@ -114,7 +137,7 @@ impl GpuCacheHandle {
// TODO(gw): Pack the fields here better!
#[derive(Debug)]
pub struct PrimitiveMetadata {
pub is_opaque: bool,
pub opacity: PrimitiveOpacity,
pub clips: Vec<ClipSource>,
pub clip_cache_info: Option<MaskCacheInfo>,
pub prim_kind: PrimitiveKind,
@ -152,7 +175,7 @@ pub struct RectanglePrimitive {
impl ToGpuBlocks for RectanglePrimitive {
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
request.push(self.color.into());
request.push(self.color);
}
}
@ -229,15 +252,15 @@ pub struct BoxShadowPrimitiveCpu {
impl ToGpuBlocks for BoxShadowPrimitiveCpu {
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
request.push(self.src_rect.into());
request.push(self.bs_rect.into());
request.push(self.color.into());
request.push(self.src_rect);
request.push(self.bs_rect);
request.push(self.color);
request.push([self.border_radius,
self.edge_size,
self.blur_radius,
self.inverted].into());
self.inverted]);
for &rect in &self.rects {
request.push(rect.into());
request.push(rect);
}
}
}
@ -254,14 +277,18 @@ pub struct GradientPrimitiveCpu {
impl GradientPrimitiveCpu {
fn build_gpu_blocks_for_aligned(&self,
display_list: &BuiltDisplayList,
mut request: GpuDataRequest) {
mut request: GpuDataRequest) -> PrimitiveOpacity {
let mut opacity = PrimitiveOpacity::opaque();
request.extend_from_slice(&self.gpu_blocks);
let src_stops = display_list.get(self.stops_range);
for src in src_stops {
request.push(src.color.premultiplied().into());
request.push([src.offset, 0.0, 0.0, 0.0].into());
request.push(src.color.premultiplied());
request.push([src.offset, 0.0, 0.0, 0.0]);
opacity.accumulate(src.color.a);
}
opacity
}
fn build_gpu_blocks_for_angle_radial(&self,
@ -307,8 +334,8 @@ impl<'a> GradientGpuBlockBuilder<'a> {
fn new(stops_range: ItemRange<GradientStop>,
display_list: &'a BuiltDisplayList) -> GradientGpuBlockBuilder<'a> {
GradientGpuBlockBuilder {
stops_range: stops_range,
display_list: display_list,
stops_range,
display_list,
}
}
@ -426,8 +453,8 @@ impl<'a> GradientGpuBlockBuilder<'a> {
}
for entry in entries.iter() {
request.push(entry.start_color.into());
request.push(entry.end_color.into());
request.push(entry.start_color);
request.push(entry.end_color);
}
}
}
@ -436,7 +463,6 @@ impl<'a> GradientGpuBlockBuilder<'a> {
pub struct RadialGradientPrimitiveCpu {
pub stops_range: ItemRange<GradientStop>,
pub extend_mode: ExtendMode,
pub gpu_data_address: GpuStoreAddress,
pub gpu_data_count: i32,
pub gpu_blocks: [GpuBlockData; 3],
}
@ -469,7 +495,7 @@ pub struct TextRunPrimitiveCpu {
impl ToGpuBlocks for TextRunPrimitiveCpu {
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
request.push(self.color.into());
request.push(self.color);
// Two glyphs are packed per GPU block.
for glyph_chunk in self.glyph_instances.chunks(2) {
@ -481,7 +507,7 @@ impl ToGpuBlocks for TextRunPrimitiveCpu {
request.push([first_glyph.point.x,
first_glyph.point.y,
second_glyph.point.x,
second_glyph.point.y].into());
second_glyph.point.y]);
}
}
}
@ -498,7 +524,6 @@ struct GlyphPrimitive {
struct ClipRect {
rect: LayerRect,
mode: f32,
padding: [f32; 3],
}
#[derive(Debug, Clone)]
@ -511,10 +536,23 @@ struct ClipCorner {
inner_radius_y: f32,
}
impl ToGpuBlocks for ClipCorner {
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
self.write(&mut request)
}
}
impl ClipCorner {
fn write(&self, request: &mut GpuDataRequest) {
request.push(self.rect);
request.push([self.outer_radius_x, self.outer_radius_y,
self.inner_radius_x, self.inner_radius_y,
]);
}
fn uniform(rect: LayerRect, outer_radius: f32, inner_radius: f32) -> ClipCorner {
ClipCorner {
rect: rect,
rect,
outer_radius_x: outer_radius,
outer_radius_y: outer_radius,
inner_radius_x: inner_radius,
@ -527,7 +565,12 @@ impl ClipCorner {
#[repr(C)]
pub struct ImageMaskData {
pub local_rect: LayerRect,
pub padding: DeviceRect,
}
impl ToGpuBlocks for ImageMaskData {
fn write_gpu_blocks(&self, mut request: GpuDataRequest) {
request.push(self.local_rect);
}
}
#[derive(Debug, Clone)]
@ -544,7 +587,6 @@ impl ClipData {
ClipData {
rect: ClipRect {
rect: clip.rect,
padding: [0.0; 3],
// TODO(gw): Support other clip modes for regions?
mode: ClipMode::Clip as u32 as f32,
},
@ -591,8 +633,7 @@ impl ClipData {
pub fn uniform(rect: LayerRect, radius: f32, mode: ClipMode) -> ClipData {
ClipData {
rect: ClipRect {
rect: rect,
padding: [0.0; 3],
rect,
mode: mode as u32 as f32,
},
top_left: ClipCorner::uniform(
@ -617,6 +658,14 @@ impl ClipData {
radius, 0.0),
}
}
pub fn write(&self, request: &mut GpuDataRequest) {
request.push(self.rect.rect);
request.push([self.rect.mode, 0.0, 0.0, 0.0]);
for corner in &[&self.top_left, &self.top_right, &self.bottom_left, &self.bottom_right] {
corner.write(request);
}
}
}
#[derive(Debug)]
@ -644,9 +693,6 @@ pub struct PrimitiveStore {
pub cpu_metadata: Vec<PrimitiveMetadata>,
pub cpu_borders: Vec<BorderPrimitiveCpu>,
pub cpu_box_shadows: Vec<BoxShadowPrimitiveCpu>,
/// Gets uploaded directly to GPU via vertex texture.
pub gpu_data32: VertexDataStore<GpuBlock32>,
}
impl PrimitiveStore {
@ -662,7 +708,6 @@ impl PrimitiveStore {
cpu_radial_gradients: Vec::new(),
cpu_borders: Vec::new(),
cpu_box_shadows: Vec::new(),
gpu_data32: VertexDataStore::new(),
}
}
@ -678,18 +723,9 @@ impl PrimitiveStore {
cpu_radial_gradients: recycle_vec(self.cpu_radial_gradients),
cpu_borders: recycle_vec(self.cpu_borders),
cpu_box_shadows: recycle_vec(self.cpu_box_shadows),
gpu_data32: self.gpu_data32.recycle(),
}
}
pub fn populate_clip_data(data: &mut [GpuBlock32], clip: ClipData) {
data[0] = GpuBlock32::from(clip.rect);
data[1] = GpuBlock32::from(clip.top_left);
data[2] = GpuBlock32::from(clip.top_right);
data[3] = GpuBlock32::from(clip.bottom_left);
data[4] = GpuBlock32::from(clip.bottom_right);
}
pub fn add_primitive(&mut self,
local_rect: &LayerRect,
local_clip_rect: &LayerRect,
@ -701,11 +737,9 @@ impl PrimitiveStore {
let metadata = match container {
PrimitiveContainer::Rectangle(rect) => {
let is_opaque = rect.color.a == 1.0;
let metadata = PrimitiveMetadata {
is_opaque: is_opaque,
clips: clips,
opacity: PrimitiveOpacity::from_alpha(rect.color.a),
clips,
clip_cache_info: clip_info,
prim_kind: PrimitiveKind::Rectangle,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_rectangles.len()),
@ -722,8 +756,8 @@ impl PrimitiveStore {
}
PrimitiveContainer::TextRun(text_cpu) => {
let metadata = PrimitiveMetadata {
is_opaque: false,
clips: clips,
opacity: PrimitiveOpacity::translucent(),
clips,
clip_cache_info: clip_info,
prim_kind: PrimitiveKind::TextRun,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_text_runs.len()),
@ -739,8 +773,8 @@ impl PrimitiveStore {
}
PrimitiveContainer::Image(image_cpu) => {
let metadata = PrimitiveMetadata {
is_opaque: false,
clips: clips,
opacity: PrimitiveOpacity::translucent(),
clips,
clip_cache_info: clip_info,
prim_kind: PrimitiveKind::Image,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_images.len()),
@ -756,8 +790,8 @@ impl PrimitiveStore {
}
PrimitiveContainer::YuvImage(image_cpu) => {
let metadata = PrimitiveMetadata {
is_opaque: true,
clips: clips,
opacity: PrimitiveOpacity::opaque(),
clips,
clip_cache_info: clip_info,
prim_kind: PrimitiveKind::YuvImage,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_yuv_images.len()),
@ -773,8 +807,8 @@ impl PrimitiveStore {
}
PrimitiveContainer::Border(border_cpu) => {
let metadata = PrimitiveMetadata {
is_opaque: false,
clips: clips,
opacity: PrimitiveOpacity::translucent(),
clips,
clip_cache_info: clip_info,
prim_kind: PrimitiveKind::Border,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_borders.len()),
@ -790,9 +824,8 @@ impl PrimitiveStore {
}
PrimitiveContainer::AlignedGradient(gradient_cpu) => {
let metadata = PrimitiveMetadata {
// TODO: calculate if the gradient is actually opaque
is_opaque: false,
clips: clips,
opacity: PrimitiveOpacity::translucent(),
clips,
clip_cache_info: clip_info,
prim_kind: PrimitiveKind::AlignedGradient,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()),
@ -809,8 +842,8 @@ impl PrimitiveStore {
PrimitiveContainer::AngleGradient(gradient_cpu) => {
let metadata = PrimitiveMetadata {
// TODO: calculate if the gradient is actually opaque
is_opaque: false,
clips: clips,
opacity: PrimitiveOpacity::translucent(),
clips,
clip_cache_info: clip_info,
prim_kind: PrimitiveKind::AngleGradient,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_gradients.len()),
@ -827,8 +860,8 @@ impl PrimitiveStore {
PrimitiveContainer::RadialGradient(radial_gradient_cpu) => {
let metadata = PrimitiveMetadata {
// TODO: calculate if the gradient is actually opaque
is_opaque: false,
clips: clips,
opacity: PrimitiveOpacity::translucent(),
clips,
clip_cache_info: clip_info,
prim_kind: PrimitiveKind::RadialGradient,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_radial_gradients.len()),
@ -869,8 +902,8 @@ impl PrimitiveStore {
PrimitiveIndex(prim_index));
let metadata = PrimitiveMetadata {
is_opaque: false,
clips: clips,
opacity: PrimitiveOpacity::translucent(),
clips,
clip_cache_info: clip_info,
prim_kind: PrimitiveKind::BoxShadow,
cpu_prim_index: SpecificPrimitiveIndex(self.cpu_box_shadows.len()),
@ -904,21 +937,21 @@ impl PrimitiveStore {
screen_rect: &DeviceIntRect,
layer_transform: &LayerToWorldTransform,
layer_combined_local_clip_rect: &LayerRect,
device_pixel_ratio: f32) -> bool {
device_pixel_ratio: f32) -> Option<(LayerRect, DeviceIntRect)> {
let metadata = &self.cpu_metadata[prim_index.0];
let local_rect = metadata.local_rect
.intersection(&metadata.local_clip_rect)
.and_then(|rect| rect.intersection(layer_combined_local_clip_rect));
let bounding_rect = metadata.local_rect
.intersection(&metadata.local_clip_rect)
.and_then(|rect| rect.intersection(layer_combined_local_clip_rect))
.and_then(|ref local_rect| {
let xf_rect = TransformedRect::new(local_rect,
let bounding_rect = local_rect.and_then(|local_rect| {
let xf_rect = TransformedRect::new(&local_rect,
layer_transform,
device_pixel_ratio);
xf_rect.bounding_rect.intersection(screen_rect)
});
self.cpu_bounding_rects[prim_index.0] = bounding_rect;
bounding_rect.is_some()
bounding_rect.map(|screen_bound| (local_rect.unwrap(), screen_bound))
}
/// Returns true if the bounding box needs to be updated.
@ -928,16 +961,17 @@ impl PrimitiveStore {
gpu_cache: &mut GpuCache,
layer_transform: &LayerToWorldTransform,
device_pixel_ratio: f32,
display_list: &BuiltDisplayList) {
display_list: &BuiltDisplayList)
-> &mut PrimitiveMetadata {
let metadata = &mut self.cpu_metadata[prim_index.0];
if let Some(ref mut clip_info) = metadata.clip_cache_info {
clip_info.update(&metadata.clips,
layer_transform,
&mut self.gpu_data32,
device_pixel_ratio,
display_list);
clip_info.update(&metadata.clips, layer_transform, gpu_cache, device_pixel_ratio);
//TODO-LCCR: we could tighten up the `local_clip_rect` here
// but that would require invalidating the whole GPU block
for clip in &metadata.clips {
if let ClipSource::Region(ClipRegion{ image_mask: Some(ref mask), .. }, ..) = *clip {
resource_cache.request_image(mask.image, ImageRendering::Auto, None);
@ -1020,9 +1054,9 @@ impl PrimitiveStore {
// right now, but if we introduce a cache for images for some other
// reason then we might as well cache this with it.
let image_properties = resource_cache.get_image_properties(image_key);
metadata.is_opaque = image_properties.descriptor.is_opaque &&
tile_spacing.width == 0.0 &&
tile_spacing.height == 0.0;
metadata.opacity.is_opaque = image_properties.descriptor.is_opaque &&
tile_spacing.width == 0.0 &&
tile_spacing.height == 0.0;
}
ImagePrimitiveKind::WebGL(..) => {}
}
@ -1035,9 +1069,6 @@ impl PrimitiveStore {
for channel in 0..channel_num {
resource_cache.request_image(image_cpu.yuv_key[channel], image_cpu.image_rendering, None);
}
// TODO(nical): Currently assuming no tile_spacing for yuv images.
metadata.is_opaque = true;
}
PrimitiveKind::AlignedGradient |
PrimitiveKind::AngleGradient |
@ -1046,8 +1077,8 @@ impl PrimitiveStore {
// Mark this GPU resource as required for this frame.
if let Some(mut request) = gpu_cache.request(&mut metadata.gpu_location) {
request.push(metadata.local_rect.into());
request.push(metadata.local_clip_rect.into());
request.push(metadata.local_rect);
request.push(metadata.local_clip_rect);
match metadata.prim_kind {
PrimitiveKind::Rectangle => {
@ -1072,8 +1103,8 @@ impl PrimitiveStore {
}
PrimitiveKind::AlignedGradient => {
let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
gradient.build_gpu_blocks_for_aligned(display_list,
request);
metadata.opacity = gradient.build_gpu_blocks_for_aligned(display_list,
request);
}
PrimitiveKind::AngleGradient => {
let gradient = &self.cpu_gradients[metadata.cpu_prim_index.0];
@ -1091,41 +1122,12 @@ impl PrimitiveStore {
}
}
}
metadata
}
}
macro_rules! define_gpu_block {
($name:ident: $ty:ty = $($derive:ident),* ) => (
#[derive(Clone)]
#[repr(C)]
pub struct $name {
data: $ty,
}
impl Default for $name {
fn default() -> $name {
$name {
data: unsafe { mem::uninitialized() }
}
}
}
$(
impl From<$derive> for $name {
fn from(data: $derive) -> $name {
unsafe { mem::transmute(data) }
}
}
)*
)
}
define_gpu_block!(GpuBlock32: [f32; 8] =
ClipCorner, ClipRect, ImageMaskData,
BorderCornerClipData, BorderCornerDashClipData, BorderCornerDotClipData
);
//Test for one clip region contains another
trait InsideTest<T> {
fn might_contain(&self, clip: &T) -> bool;

View File

@ -8,7 +8,7 @@ use euclid::{Point2D, Size2D, Rect, vec2};
use std::collections::vec_deque::VecDeque;
use std::f32;
use std::mem;
use webrender_traits::ColorF;
use api::ColorF;
use time::precise_time_ns;
const GRAPH_WIDTH: f32 = 1024.0;
@ -45,7 +45,7 @@ pub struct IntProfileCounter {
impl IntProfileCounter {
fn new(description: &'static str) -> IntProfileCounter {
IntProfileCounter {
description: description,
description,
value: 0,
}
}
@ -94,7 +94,7 @@ pub struct ResourceProfileCounter {
impl ResourceProfileCounter {
fn new(description: &'static str) -> ResourceProfileCounter {
ResourceProfileCounter {
description: description,
description,
value: 0,
size: 0,
}
@ -134,9 +134,9 @@ pub struct TimeProfileCounter {
impl TimeProfileCounter {
pub fn new(description: &'static str, invert: bool) -> TimeProfileCounter {
TimeProfileCounter {
description: description,
description,
nanoseconds: 0,
invert: invert,
invert,
}
}
@ -195,13 +195,13 @@ pub struct AverageTimeProfileCounter {
impl AverageTimeProfileCounter {
pub fn new(description: &'static str, invert: bool, average_over_ns: u64) -> AverageTimeProfileCounter {
AverageTimeProfileCounter {
description: description,
average_over_ns: average_over_ns,
description,
average_over_ns,
start_ns: precise_time_ns(),
sum_ns: 0,
num_samples: 0,
nanoseconds: 0,
invert: invert,
invert,
}
}
@ -425,7 +425,7 @@ struct ProfileGraph {
impl ProfileGraph {
fn new(max_samples: usize) -> ProfileGraph {
ProfileGraph {
max_samples: max_samples,
max_samples,
values: VecDeque::new(),
}
}
@ -556,8 +556,8 @@ impl GpuFrameCollection {
self.frames.pop_back();
}
self.frames.push_front(GpuFrame {
total_time: total_time,
samples: samples,
total_time,
samples,
});
}
}

View File

@ -9,7 +9,7 @@ use std::any::TypeId;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use webrender_traits::ApiMsg;
use api::ApiMsg;
use byteorder::{LittleEndian, WriteBytesExt};
pub static WEBRENDER_RECORDING_HEADER: u64 = 0xbeefbeefbeefbe01u64;
@ -37,7 +37,7 @@ impl BinaryRecorder {
file.write_u64::<LittleEndian>(apimsg_type_id).ok();
BinaryRecorder {
file: file,
file,
}
}

View File

@ -10,7 +10,7 @@ use profiler::{BackendProfileCounters, GpuCacheProfileCounters, TextureCacheProf
use record::ApiRecordingReceiver;
use resource_cache::ResourceCache;
use scene::Scene;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use texture_cache::TextureCache;
@ -18,13 +18,13 @@ use time::precise_time_ns;
use thread_profiler::register_thread_with_profiler;
use rayon::ThreadPool;
use webgl_types::{GLContextHandleWrapper, GLContextWrapper};
use webrender_traits::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods};
use webrender_traits::channel::{PayloadSender, PayloadSenderHelperMethods};
use webrender_traits::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DeviceIntPoint};
use webrender_traits::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, IdNamespace, ImageData};
use webrender_traits::{LayerPoint, PipelineId, RenderDispatcher, RenderNotifier};
use webrender_traits::{VRCompositorCommand, VRCompositorHandler, WebGLCommand, WebGLContextId};
use webrender_traits::{FontTemplate};
use api::channel::{MsgReceiver, PayloadReceiver, PayloadReceiverHelperMethods};
use api::channel::{PayloadSender, PayloadSenderHelperMethods};
use api::{ApiMsg, BlobImageRenderer, BuiltDisplayList, DeviceIntPoint};
use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, IdNamespace, ImageData};
use api::{LayerPoint, PipelineId, RenderDispatcher, RenderNotifier};
use api::{VRCompositorCommand, VRCompositorHandler, WebGLCommand, WebGLContextId};
use api::{FontTemplate};
#[cfg(feature = "webgl")]
use offscreen_gl_context::GLContextDispatcher;
@ -60,13 +60,21 @@ pub struct RenderBackend {
notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
webrender_context_handle: Option<GLContextHandleWrapper>,
webgl_contexts: HashMap<WebGLContextId, GLContextWrapper>,
dirty_webgl_contexts: HashSet<WebGLContextId>,
current_bound_webgl_context_id: Option<WebGLContextId>,
recorder: Option<Box<ApiRecordingReceiver>>,
main_thread_dispatcher: Arc<Mutex<Option<Box<RenderDispatcher>>>>,
next_webgl_id: usize,
vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>
vr_compositor_handler: Arc<Mutex<Option<Box<VRCompositorHandler>>>>,
// A helper switch to prevent any frames rendering triggered by scrolling
// messages between `SetDisplayList` and `GenerateFrame`.
// If we allow them, then a reftest that scrolls a few layers before generating
// the first frame would produce inconsistent rendering results, because
// scroll events are not necessarily received in deterministic order.
render_on_scroll: bool,
}
impl RenderBackend {
@ -91,29 +99,42 @@ impl RenderBackend {
register_thread_with_profiler("Backend".to_string());
RenderBackend {
api_rx: api_rx,
payload_rx: payload_rx,
payload_tx: payload_tx,
result_tx: result_tx,
hidpi_factor: hidpi_factor,
api_rx,
payload_rx,
payload_tx,
result_tx,
hidpi_factor,
page_zoom_factor: 1.0,
pinch_zoom_factor: 1.0,
pan: DeviceIntPoint::zero(),
resource_cache: resource_cache,
resource_cache,
gpu_cache: GpuCache::new(),
scene: Scene::new(),
frame: Frame::new(config),
next_namespace_id: IdNamespace(1),
notifier: notifier,
webrender_context_handle: webrender_context_handle,
notifier,
webrender_context_handle,
webgl_contexts: HashMap::new(),
dirty_webgl_contexts: HashSet::new(),
current_bound_webgl_context_id: None,
recorder: recorder,
main_thread_dispatcher: main_thread_dispatcher,
recorder,
main_thread_dispatcher,
next_webgl_id: 0,
vr_compositor_handler: vr_compositor_handler,
vr_compositor_handler,
window_size: initial_window_size,
inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), initial_window_size),
render_on_scroll: false,
}
}
fn scroll_frame(&mut self, frame_maybe: Option<RendererFrame>,
profile_counters: &mut BackendProfileCounters) {
match frame_maybe {
Some(frame) => {
self.publish_frame(frame, profile_counters);
self.notify_compositor_of_new_scroll_frame(true)
}
None => self.notify_compositor_of_new_scroll_frame(false),
}
}
@ -232,6 +253,8 @@ impl RenderBackend {
self.build_scene();
});
self.render_on_scroll = false; //wait for `GenerateFrame`
// Note: this isn't quite right as auxiliary values will be
// pulled out somewhere in the prim_store, but aux values are
// really simple and cheap to access, so it's not a big deal.
@ -259,7 +282,7 @@ impl RenderBackend {
let counters = &mut profile_counters.resources.texture_cache;
let gpu_cache_counters = &mut profile_counters.resources.gpu_cache;
profile_counters.total_time.profile(|| {
if self.frame.scroll(delta, cursor, move_phase) {
if self.frame.scroll(delta, cursor, move_phase) && self.render_on_scroll {
Some(self.render(counters, gpu_cache_counters))
} else {
None
@ -267,13 +290,7 @@ impl RenderBackend {
})
};
match frame {
Some(frame) => {
self.publish_frame(frame, &mut profile_counters);
self.notify_compositor_of_new_scroll_frame(true)
}
None => self.notify_compositor_of_new_scroll_frame(false),
}
self.scroll_frame(frame, &mut profile_counters);
}
ApiMsg::ScrollNodeWithId(origin, id, clamp) => {
profile_scope!("ScrollNodeWithScrollId");
@ -281,7 +298,7 @@ impl RenderBackend {
let counters = &mut profile_counters.resources.texture_cache;
let gpu_cache_counters = &mut profile_counters.resources.gpu_cache;
profile_counters.total_time.profile(|| {
if self.frame.scroll_node(origin, id, clamp) {
if self.frame.scroll_node(origin, id, clamp) && self.render_on_scroll {
Some(self.render(counters, gpu_cache_counters))
} else {
None
@ -289,14 +306,7 @@ impl RenderBackend {
})
};
match frame {
Some(frame) => {
self.publish_frame(frame, &mut profile_counters);
self.notify_compositor_of_new_scroll_frame(true)
}
None => self.notify_compositor_of_new_scroll_frame(false),
}
self.scroll_frame(frame, &mut profile_counters);
}
ApiMsg::TickScrollingBounce => {
profile_scope!("TickScrollingBounce");
@ -305,14 +315,18 @@ impl RenderBackend {
let gpu_cache_counters = &mut profile_counters.resources.gpu_cache;
profile_counters.total_time.profile(|| {
self.frame.tick_scrolling_bounce_animations();
self.render(counters, gpu_cache_counters)
if self.render_on_scroll {
Some(self.render(counters, gpu_cache_counters))
} else {
None
}
})
};
self.publish_frame_and_notify_compositor(frame, &mut profile_counters);
self.scroll_frame(frame, &mut profile_counters);
}
ApiMsg::TranslatePointToLayerSpace(..) => {
panic!("unused api - remove from webrender_traits");
panic!("unused api - remove from webrender_api");
}
ApiMsg::GetScrollNodeState(tx) => {
profile_scope!("GetScrollNodeState");
@ -345,6 +359,7 @@ impl RenderBackend {
self.resource_cache
.add_webgl_texture(id, SourceTexture::WebGL(texture_id),
real_size);
self.dirty_webgl_contexts.insert(id);
tx.send(Ok((id, limits))).unwrap();
},
@ -369,6 +384,7 @@ impl RenderBackend {
self.resource_cache
.update_webgl_texture(context_id, SourceTexture::WebGL(texture_id),
real_size);
self.dirty_webgl_contexts.insert(context_id);
},
Err(msg) => {
error!("Error resizing WebGLContext: {}", msg);
@ -384,6 +400,7 @@ impl RenderBackend {
self.current_bound_webgl_context_id = Some(context_id);
}
ctx.apply_command(command);
self.dirty_webgl_contexts.insert(context_id);
},
ApiMsg::VRCompositorCommand(context_id, command) => {
@ -392,6 +409,7 @@ impl RenderBackend {
self.current_bound_webgl_context_id = Some(context_id);
}
self.handle_vr_compositor_command(context_id, command);
self.dirty_webgl_contexts.insert(context_id);
}
ApiMsg::GenerateFrame(property_bindings) => {
profile_scope!("GenerateFrame");
@ -413,6 +431,8 @@ impl RenderBackend {
});
}
self.render_on_scroll = true;
let frame = {
let counters = &mut profile_counters.resources.texture_cache;
let gpu_cache_counters = &mut profile_counters.resources.gpu_cache;
@ -476,10 +496,18 @@ impl RenderBackend {
// implementations - a single flush for each webgl
// context at the start of a render frame should
// incur minimal cost.
for (_, webgl_context) in &self.webgl_contexts {
webgl_context.make_current();
webgl_context.apply_command(WebGLCommand::Flush);
webgl_context.unbind();
// glFlush is not enough in some GPUs.
// glFlush doesn't guarantee the completion of the GL commands when the shared texture is sampled.
// This leads to some graphic glitches on some demos or even nothing being rendered at all (GPU Mali-T880).
// glFinish guarantees the completion of the commands but it may hurt performance a lot.
// Sync Objects are the recommended way to ensure that textures are ready in OpenGL 3.0+.
// They are more performant than glFinish and guarantee the completion of the GL commands.
for (id, webgl_context) in &self.webgl_contexts {
if self.dirty_webgl_contexts.remove(&id) {
webgl_context.make_current();
webgl_context.apply_command(WebGLCommand::FenceAndWaitSync);
webgl_context.unbind();
}
}
let accumulated_scale_factor = self.accumulated_scale_factor();

View File

@ -4,13 +4,13 @@
use gpu_cache::GpuCacheHandle;
use internal_types::{HardwareCompositeOp, LowLevelFilterOp};
use mask_cache::{MaskBounds, MaskCacheInfo};
use mask_cache::MaskCacheInfo;
use prim_store::{PrimitiveCacheKey, PrimitiveIndex};
use std::{cmp, f32, i32, mem, usize};
use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex};
use tiling::{RenderTargetKind, StackingContextIndex};
use webrender_traits::{ClipId, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use webrender_traits::{MixBlendMode};
use api::{ClipId, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{MixBlendMode};
const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
@ -84,22 +84,16 @@ pub enum MaskGeometryKind {
// TODO(gw): Add more types here (e.g. 4 rectangles outside the inner rect)
}
pub type ClipWorkItem = (PackedLayerIndex, MaskCacheInfo);
#[derive(Debug, Clone)]
pub struct CacheMaskTask {
actual_rect: DeviceIntRect,
inner_rect: DeviceIntRect,
pub clips: Vec<(PackedLayerIndex, MaskCacheInfo)>,
pub clips: Vec<ClipWorkItem>,
pub geometry_kind: MaskGeometryKind,
}
#[derive(Debug)]
pub enum MaskResult {
/// The mask is completely outside the region
Outside,
/// The mask is inside and needs to be processed
Inside(RenderTask),
}
#[derive(Debug, Clone)]
pub struct RenderTaskData {
pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
@ -149,9 +143,9 @@ impl RenderTask {
RenderTask {
id: RenderTaskId::Static(task_index),
children: Vec::new(),
location: location,
location,
kind: RenderTaskKind::Alpha(AlphaRenderTask {
screen_origin: screen_origin,
screen_origin,
items: Vec::new(),
}),
}
@ -184,57 +178,33 @@ impl RenderTask {
}
}
pub fn new_mask(actual_rect: DeviceIntRect,
pub fn new_mask(task_rect: DeviceIntRect,
mask_key: MaskCacheKey,
clips: &[(PackedLayerIndex, MaskCacheInfo)])
-> MaskResult {
if clips.is_empty() {
return MaskResult::Outside;
}
// We scan through the clip stack and detect if our actual rectangle
// is in the intersection of all of all the outer bounds,
// and if it's completely inside the intersection of all of the inner bounds.
// TODO(gw): If we encounter a clip with unknown bounds, we'll just use
// the original rect. This is overly conservative, but can
// be optimized later.
let mut result = Some(actual_rect);
for &(_, ref clip) in clips {
match *clip.bounds.as_ref().unwrap() {
MaskBounds::OuterInner(ref outer, _) |
MaskBounds::Outer(ref outer) => {
result = result.and_then(|rect| {
rect.intersection(&outer.bounding_rect)
});
raw_clips: &[ClipWorkItem],
extra_clip: Option<ClipWorkItem>)
-> Option<RenderTask> {
/// Filter out all the clip instances that don't contribute to the result
let mut inner_rect = Some(task_rect);
let clips: Vec<_> = raw_clips.iter()
.chain(extra_clip.iter())
.filter(|&&(_, ref clip_info)| {
match clip_info.bounds.inner {
Some(ref inner) if !inner.device_rect.is_empty() => {
inner_rect = inner_rect.and_then(|r| r.intersection(&inner.device_rect));
!inner.device_rect.contains_rect(&task_rect)
}
MaskBounds::None => {
result = Some(actual_rect);
break;
_ => {
inner_rect = None;
true
}
}
}).cloned().collect();
// Nothing to do, all clips are irrelevant for this case
if clips.is_empty() {
return None
}
let task_rect = match result {
None => return MaskResult::Outside,
Some(rect) => rect,
};
// Accumulate inner rects. As soon as we encounter
// a clip mask where we don't have or don't know
// the inner rect, this will become None.
let inner_rect = clips.iter()
.fold(Some(task_rect), |current, clip| {
current.and_then(|rect| {
let inner_rect = match *clip.1.bounds.as_ref().unwrap() {
MaskBounds::Outer(..) |
MaskBounds::None => DeviceIntRect::zero(),
MaskBounds::OuterInner(_, ref inner) => inner.bounding_rect
};
rect.intersection(&inner_rect)
})
});
// TODO(gw): This optimization is very conservative for now.
// For now, only draw optimized geometry if it is
// a single aligned rect mask with rounded corners.
@ -242,25 +212,24 @@ impl RenderTask {
// more complex types of clip mask geometry.
let mut geometry_kind = MaskGeometryKind::Default;
if inner_rect.is_some() && clips.len() == 1 {
let (_, ref clip_info) = clips[0];
if clip_info.image.is_none() &&
clip_info.effective_complex_clip_count == 1 &&
clip_info.is_aligned {
let (_, ref info) = clips[0];
if info.border_corners.is_empty() &&
info.image.is_none() &&
info.complex_clip_range.get_count() == 1 &&
info.layer_clip_range.get_count() == 0 {
geometry_kind = MaskGeometryKind::CornersOnly;
}
}
let inner_rect = inner_rect.unwrap_or(DeviceIntRect::zero());
MaskResult::Inside(RenderTask {
Some(RenderTask {
id: RenderTaskId::Dynamic(RenderTaskKey::CacheMask(mask_key)),
children: Vec::new(),
location: RenderTaskLocation::Dynamic(None, task_rect.size),
kind: RenderTaskKind::CacheMask(CacheMaskTask {
actual_rect: task_rect,
inner_rect: inner_rect,
clips: clips.to_vec(),
geometry_kind: geometry_kind,
inner_rect: inner_rect.unwrap_or(DeviceIntRect::zero()),
clips,
geometry_kind,
}),
})
}

View File

@ -11,7 +11,7 @@
use debug_colors;
use debug_render::DebugRenderer;
use device::{DepthFunction, Device, FrameId, ProgramId, TextureId, VertexFormat, GpuMarker, GpuProfiler};
use device::{DepthFunction, Device, FrameId, ProgramId, TextureId, VertexFormat, GpuMarker, GpuProfiler, PBOId};
use device::{GpuSample, TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError};
use device::get_gl_format_bgra;
use euclid::Transform3D;
@ -19,7 +19,6 @@ use fnv::FnvHasher;
use frame_builder::FrameBuilderConfig;
use gleam::gl;
use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
use gpu_store::{GpuStore, GpuStoreLayout};
use internal_types::{CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp};
use internal_types::{TextureUpdateList, PackedVertex, RenderTargetMode};
use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, SourceTexture};
@ -38,7 +37,6 @@ use std::marker::PhantomData;
use std::mem;
use std::path::PathBuf;
use std::rc::Rc;
use std::slice;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
@ -51,13 +49,13 @@ use time::precise_time_ns;
use thread_profiler::{register_thread_with_profiler, write_profile};
use util::TransformedRectKind;
use webgl_types::GLContextHandleWrapper;
use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher};
use webrender_traits::{ExternalImageId, ExternalImageType, ImageData, ImageFormat, RenderApiSender};
use webrender_traits::{DeviceIntRect, DeviceUintRect, DeviceIntPoint, DeviceIntSize, DeviceUintSize};
use webrender_traits::{BlobImageRenderer, channel, FontRenderMode};
use webrender_traits::VRCompositorHandler;
use webrender_traits::{YuvColorSpace, YuvFormat};
use webrender_traits::{YUV_COLOR_SPACES, YUV_FORMATS};
use api::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher};
use api::{ExternalImageId, ExternalImageType, ImageData, ImageFormat, RenderApiSender};
use api::{DeviceIntRect, DeviceUintRect, DeviceIntPoint, DeviceIntSize, DeviceUintSize};
use api::{BlobImageRenderer, channel, FontRenderMode};
use api::VRCompositorHandler;
use api::{YuvColorSpace, YuvFormat};
use api::{YUV_COLOR_SPACES, YUV_FORMATS};
pub const GPU_DATA_TEXTURE_POOL: usize = 5;
pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
@ -156,8 +154,8 @@ impl GpuProfile {
paint_time_ns += sample.time_ns;
}
GpuProfile {
frame_id: frame_id,
paint_time_ns: paint_time_ns,
frame_id,
paint_time_ns,
}
}
}
@ -174,9 +172,9 @@ impl CpuProfile {
composite_time_ns: u64,
draw_calls: usize) -> CpuProfile {
CpuProfile {
frame_id: frame_id,
composite_time_ns: composite_time_ns,
draw_calls: draw_calls,
frame_id,
composite_time_ns,
draw_calls,
}
}
}
@ -191,90 +189,165 @@ pub enum BlendMode {
Subpixel(ColorF),
}
// Tracks the state of each row in the GPU cache texture.
struct CacheRow {
is_dirty: bool,
}
impl CacheRow {
fn new() -> CacheRow {
CacheRow {
is_dirty: false,
}
}
}
/// The device-specific representation of the cache texture in gpu_cache.rs
struct CacheTexture {
current_id: TextureId,
next_id: TextureId,
texture_id: TextureId,
pbo_id: PBOId,
rows: Vec<CacheRow>,
cpu_blocks: Vec<GpuBlockData>,
}
impl CacheTexture {
fn new(device: &mut Device) -> CacheTexture {
let ids = device.create_texture_ids(2, TextureTarget::Default);
let texture_id = device.create_texture_ids(1, TextureTarget::Default)[0];
let pbo_id = device.create_pbo();
CacheTexture {
current_id: ids[0],
next_id: ids[1],
texture_id,
pbo_id,
rows: Vec::new(),
cpu_blocks: Vec::new(),
}
}
fn apply_patch(&mut self,
device: &mut Device,
update: &GpuCacheUpdate,
blocks: &[GpuBlockData]) {
match update {
&GpuCacheUpdate::Copy { block_index, block_count, address } => {
// Apply an incremental update to the cache texture.
// TODO(gw): For the initial implementation, we will just
// use update_texture() since it's simple. If / when
// we profile this and find it to be slow on some / all
// devices - we can look into other options, such as
// using glMapBuffer() with the unsynchronized bit,
// and managing the synchronization ourselves with fences.
let data: &[u8] = unsafe {
let ptr = blocks.as_ptr()
.offset(block_index as isize);
slice::from_raw_parts(ptr as *const _, block_count * 16)
};
device.update_texture(self.current_id,
address.u as u32,
address.v as u32,
block_count as u32,
1,
None,
data);
let row = address.v as usize;
// Ensure that the CPU-side shadow copy of the GPU cache data has enough
// rows to apply this patch.
while self.rows.len() <= row {
// Add a new row.
self.rows.push(CacheRow::new());
// Add enough GPU blocks for this row.
self.cpu_blocks.extend_from_slice(&[GpuBlockData::empty(); MAX_VERTEX_TEXTURE_WIDTH]);
}
// This row is dirty (needs to be updated in GPU texture).
self.rows[row].is_dirty = true;
// Copy the blocks from the patch array in the shadow CPU copy.
let block_offset = row * MAX_VERTEX_TEXTURE_WIDTH + address.u as usize;
let data = &mut self.cpu_blocks[block_offset..(block_offset + block_count)];
for i in 0..block_count {
data[i] = blocks[block_index + i];
}
}
}
}
fn update(&mut self, device: &mut Device, updates: &GpuCacheUpdateList) {
// See if we need to create or resize the texture.
let current_dimensions = device.get_texture_dimensions(self.current_id);
let current_dimensions = device.get_texture_dimensions(self.texture_id);
if updates.height > current_dimensions.height {
// Create a f32 texture that can be used for the vertex shader
// to fetch data from.
device.init_texture(self.next_id,
device.init_texture(self.texture_id,
MAX_VERTEX_TEXTURE_WIDTH as u32,
updates.height as u32,
ImageFormat::RGBAF32,
TextureFilter::Nearest,
RenderTargetMode::SimpleRenderTarget,
RenderTargetMode::None,
None);
// Copy the current texture into the newly resized texture.
if current_dimensions.height > 0 {
device.bind_draw_target(Some((self.next_id, 0)), None);
let blit_rect = DeviceIntRect::new(DeviceIntPoint::zero(),
DeviceIntSize::new(MAX_VERTEX_TEXTURE_WIDTH as i32,
current_dimensions.height as i32));
// TODO(gw): Should probably switch this to glCopyTexSubImage2D, since we
// don't do any stretching here.
device.blit_render_target(Some((self.current_id, 0)),
Some(blit_rect),
blit_rect);
// Free the GPU memory for that texture until we need to resize again.
device.deinit_texture(self.current_id);
// If we had to resize the texture, just mark all rows
// as dirty so they will be uploaded to the texture
// during the next flush.
for row in &mut self.rows {
row.is_dirty = true;
}
}
mem::swap(&mut self.current_id, &mut self.next_id);
}
for update in &updates.updates {
self.apply_patch(device, update, &updates.blocks);
self.apply_patch(update, &updates.blocks);
}
}
fn flush(&mut self, device: &mut Device) {
// Bind a PBO to do the texture upload.
// Updating the texture via PBO avoids CPU-side driver stalls.
device.bind_pbo(Some(self.pbo_id));
for (row_index, row) in self.rows.iter_mut().enumerate() {
if row.is_dirty {
// Get the data for this row and push to the PBO.
let block_index = row_index * MAX_VERTEX_TEXTURE_WIDTH;
let cpu_blocks = &self.cpu_blocks[block_index..(block_index + MAX_VERTEX_TEXTURE_WIDTH)];
device.update_pbo_data(cpu_blocks);
// Insert a command to copy the PBO data to the right place in
// the GPU-side cache texture.
device.update_texture_from_pbo(self.texture_id,
0,
row_index as u32,
MAX_VERTEX_TEXTURE_WIDTH as u32,
1,
0);
// Orphan the PBO. This is the recommended way to hint to the
// driver to detach the underlying storage from this PBO id.
// Keeping the size the same gives the driver a hint for future
// use of this PBO.
device.orphan_pbo(mem::size_of::<GpuBlockData>() * MAX_VERTEX_TEXTURE_WIDTH);
row.is_dirty = false;
}
}
// Ensure that other texture updates won't read from this PBO.
device.bind_pbo(None);
}
}
trait GpuStoreLayout {
fn image_format() -> ImageFormat;
fn texture_width<T>() -> usize;
fn texture_filter() -> TextureFilter;
fn texel_size() -> usize {
match Self::image_format() {
ImageFormat::BGRA8 => 4,
ImageFormat::RGBAF32 => 16,
_ => unreachable!(),
}
}
fn texels_per_item<T>() -> usize {
let item_size = mem::size_of::<T>();
let texel_size = Self::texel_size();
debug_assert!(item_size % texel_size == 0);
item_size / texel_size
}
fn items_per_row<T>() -> usize {
Self::texture_width::<T>() / Self::texels_per_item::<T>()
}
fn rows_per_item<T>() -> usize {
Self::texels_per_item::<T>() / Self::texture_width::<T>()
}
}
struct GpuDataTexture<L> {
@ -287,7 +360,7 @@ impl<L: GpuStoreLayout> GpuDataTexture<L> {
let id = device.create_texture_ids(1, TextureTarget::Default)[0];
GpuDataTexture {
id: id,
id,
layout: PhantomData,
}
}
@ -344,11 +417,10 @@ impl GpuStoreLayout for VertexDataTextureLayout {
}
type VertexDataTexture = GpuDataTexture<VertexDataTextureLayout>;
pub type VertexDataStore<T> = GpuStore<T, VertexDataTextureLayout>;
const TRANSFORM_FEATURE: &'static str = "TRANSFORM";
const SUBPIXEL_AA_FEATURE: &'static str = "SUBPIXEL_AA";
const CLIP_FEATURE: &'static str = "CLIP";
const TRANSFORM_FEATURE: &str = "TRANSFORM";
const SUBPIXEL_AA_FEATURE: &str = "SUBPIXEL_AA";
const CLIP_FEATURE: &str = "CLIP";
enum ShaderKind {
Primitive,
@ -371,8 +443,8 @@ impl LazilyCompiledShader {
precache: bool) -> Result<LazilyCompiledShader, ShaderError> {
let mut shader = LazilyCompiledShader {
id: None,
name: name,
kind: kind,
name,
kind,
features: features.to_vec(),
};
@ -465,8 +537,8 @@ impl PrimitiveShader {
};
Ok(PrimitiveShader {
simple: simple,
transform: transform,
simple,
transform,
})
}
@ -511,7 +583,6 @@ fn create_clip_shader(name: &'static str, device: &mut Device) -> Result<Program
struct GpuDataTextures {
layer_texture: VertexDataTexture,
render_task_texture: VertexDataTexture,
data32_texture: VertexDataTexture,
}
impl GpuDataTextures {
@ -519,18 +590,15 @@ impl GpuDataTextures {
GpuDataTextures {
layer_texture: VertexDataTexture::new(device),
render_task_texture: VertexDataTexture::new(device),
data32_texture: VertexDataTexture::new(device),
}
}
fn init_frame(&mut self, device: &mut Device, frame: &mut Frame) {
self.data32_texture.init(device, &mut frame.gpu_data32);
self.layer_texture.init(device, &mut frame.layer_texture_data);
self.render_task_texture.init(device, &mut frame.render_task_data);
device.bind_texture(TextureSampler::Layers, self.layer_texture.id);
device.bind_texture(TextureSampler::RenderTasks, self.render_task_texture.id);
device.bind_texture(TextureSampler::Data32, self.data32_texture.id);
}
}
@ -1054,7 +1122,7 @@ impl Renderer {
let config = FrameBuilderConfig {
enable_scrollbars: options.enable_scrollbars,
default_font_render_mode: default_font_render_mode,
default_font_render_mode,
debug: options.debug,
cache_expiry_frames: options.cache_expiry_frames,
};
@ -1095,38 +1163,38 @@ impl Renderer {
let gpu_profile = GpuProfiler::new(device.rc_gl());
let renderer = Renderer {
result_rx: result_rx,
device: device,
result_rx,
device,
current_frame: None,
pending_texture_updates: Vec::new(),
pending_gpu_cache_updates: Vec::new(),
pending_shader_updates: Vec::new(),
cs_box_shadow: cs_box_shadow,
cs_text_run: cs_text_run,
cs_blur: cs_blur,
cs_clip_rectangle: cs_clip_rectangle,
cs_clip_border: cs_clip_border,
cs_clip_image: cs_clip_image,
ps_rectangle: ps_rectangle,
ps_rectangle_clip: ps_rectangle_clip,
ps_text_run: ps_text_run,
ps_text_run_subpixel: ps_text_run_subpixel,
ps_image: ps_image,
ps_yuv_image: ps_yuv_image,
ps_border_corner: ps_border_corner,
ps_border_edge: ps_border_edge,
ps_box_shadow: ps_box_shadow,
ps_gradient: ps_gradient,
ps_angle_gradient: ps_angle_gradient,
ps_radial_gradient: ps_radial_gradient,
ps_cache_image: ps_cache_image,
ps_blend: ps_blend,
ps_hw_composite: ps_hw_composite,
ps_split_composite: ps_split_composite,
ps_composite: ps_composite,
notifier: notifier,
cs_box_shadow,
cs_text_run,
cs_blur,
cs_clip_rectangle,
cs_clip_border,
cs_clip_image,
ps_rectangle,
ps_rectangle_clip,
ps_text_run,
ps_text_run_subpixel,
ps_image,
ps_yuv_image,
ps_border_corner,
ps_border_edge,
ps_box_shadow,
ps_gradient,
ps_angle_gradient,
ps_radial_gradient,
ps_cache_image,
ps_blend,
ps_hw_composite,
ps_split_composite,
ps_composite,
notifier,
debug: debug_renderer,
render_target_debug: render_target_debug,
render_target_debug,
enable_batcher: options.enable_batcher,
backend_profile_counters: BackendProfileCounters::new(),
profile_counters: RendererProfileCounters::new(),
@ -1139,23 +1207,23 @@ impl Renderer {
last_time: 0,
color_render_targets: Vec::new(),
alpha_render_targets: Vec::new(),
gpu_profile: gpu_profile,
prim_vao_id: prim_vao_id,
blur_vao_id: blur_vao_id,
clip_vao_id: clip_vao_id,
gpu_profile,
prim_vao_id,
blur_vao_id,
clip_vao_id,
gdt_index: 0,
gpu_data_textures: gpu_data_textures,
gpu_data_textures,
pipeline_epoch_map: HashMap::default(),
main_thread_dispatcher: main_thread_dispatcher,
main_thread_dispatcher,
cache_texture_id_map: Vec::new(),
dummy_cache_texture_id: dummy_cache_texture_id,
dither_matrix_texture_id: dither_matrix_texture_id,
dummy_cache_texture_id,
dither_matrix_texture_id,
external_image_handler: None,
external_images: HashMap::default(),
vr_compositor_handler: vr_compositor,
cpu_profiles: VecDeque::new(),
gpu_profiles: VecDeque::new(),
gpu_cache_texture: gpu_cache_texture,
gpu_cache_texture,
};
let sender = RenderApiSender::new(api_tx, payload_tx);
@ -1282,7 +1350,7 @@ impl Renderer {
/// Renders the current frame.
///
/// A Frame is supplied by calling [`set_display_list()`][newframe].
/// [newframe]: ../../webrender_traits/struct.RenderApi.html#method.set_display_list
/// [newframe]: ../../webrender_api/struct.RenderApi.html#method.set_display_list
pub fn render(&mut self, framebuffer_size: DeviceUintSize) {
profile_scope!("render");
@ -1319,10 +1387,9 @@ impl Renderer {
self.update_texture_cache();
self.update_gpu_cache();
self.update_deferred_resolves(frame);
self.update_gpu_cache(frame);
self.device.bind_texture(TextureSampler::ResourceCache, self.gpu_cache_texture.current_id);
self.device.bind_texture(TextureSampler::ResourceCache, self.gpu_cache_texture.texture_id);
frame_id
};
@ -1396,11 +1463,13 @@ impl Renderer {
}
*/
fn update_gpu_cache(&mut self) {
fn update_gpu_cache(&mut self, frame: &mut Frame) {
let _gm = GpuMarker::new(self.device.rc_gl(), "gpu cache update");
for update_list in self.pending_gpu_cache_updates.drain(..) {
self.gpu_cache_texture.update(&mut self.device, &update_list);
}
self.update_deferred_resolves(frame);
self.gpu_cache_texture.flush(&mut self.device);
}
fn update_texture_cache(&mut self) {
@ -1759,16 +1828,21 @@ impl Renderer {
self.device.set_blend(false);
let shader = self.cs_blur.get(&mut self.device).unwrap();
self.draw_instanced_batch(&target.vertical_blurs,
vao,
shader,
&BatchTextures::no_texture(),
&projection);
self.draw_instanced_batch(&target.horizontal_blurs,
vao,
shader,
&BatchTextures::no_texture(),
&projection);
if !target.vertical_blurs.is_empty() {
self.draw_instanced_batch(&target.vertical_blurs,
vao,
shader,
&BatchTextures::no_texture(),
&projection);
}
if !target.horizontal_blurs.is_empty() {
self.draw_instanced_batch(&target.horizontal_blurs,
vao,
shader,
&BatchTextures::no_texture(),
&projection);
}
}
// Draw any box-shadow caches for this target.
@ -1805,58 +1879,66 @@ impl Renderer {
&projection);
}
let _gm2 = GpuMarker::new(self.device.rc_gl(), "alpha batches");
self.device.set_blend(false);
let mut prev_blend_mode = BlendMode::None;
if !target.alpha_batcher.is_empty() {
let _gm2 = GpuMarker::new(self.device.rc_gl(), "alpha batches");
self.device.set_blend(false);
let mut prev_blend_mode = BlendMode::None;
//Note: depth equality is needed for split planes
self.device.set_depth_func(DepthFunction::LessEqual);
self.device.enable_depth();
self.device.enable_depth_write();
//Note: depth equality is needed for split planes
self.device.set_depth_func(DepthFunction::LessEqual);
self.device.enable_depth();
self.device.enable_depth_write();
for batch in &target.alpha_batcher.batch_list.opaque_batches {
self.submit_batch(batch,
&projection,
render_task_data,
color_cache_texture,
render_target,
target_size);
}
self.device.disable_depth_write();
for batch in &target.alpha_batcher.batch_list.alpha_batches {
if batch.key.blend_mode != prev_blend_mode {
match batch.key.blend_mode {
BlendMode::None => {
self.device.set_blend(false);
}
BlendMode::Alpha => {
self.device.set_blend(true);
self.device.set_blend_mode_alpha();
}
BlendMode::PremultipliedAlpha => {
self.device.set_blend(true);
self.device.set_blend_mode_premultiplied_alpha();
}
BlendMode::Subpixel(color) => {
self.device.set_blend(true);
self.device.set_blend_mode_subpixel(color);
}
}
prev_blend_mode = batch.key.blend_mode;
// Draw opaque batches front-to-back for maximum
// z-buffer efficiency!
for batch in target.alpha_batcher
.batch_list
.opaque_batches
.iter()
.rev() {
self.submit_batch(batch,
&projection,
render_task_data,
color_cache_texture,
render_target,
target_size);
}
self.submit_batch(batch,
&projection,
render_task_data,
color_cache_texture,
render_target,
target_size);
}
self.device.disable_depth_write();
self.device.disable_depth();
self.device.set_blend(false);
for batch in &target.alpha_batcher.batch_list.alpha_batches {
if batch.key.blend_mode != prev_blend_mode {
match batch.key.blend_mode {
BlendMode::None => {
self.device.set_blend(false);
}
BlendMode::Alpha => {
self.device.set_blend(true);
self.device.set_blend_mode_alpha();
}
BlendMode::PremultipliedAlpha => {
self.device.set_blend(true);
self.device.set_blend_mode_premultiplied_alpha();
}
BlendMode::Subpixel(color) => {
self.device.set_blend(true);
self.device.set_blend_mode_subpixel(color);
}
}
prev_blend_mode = batch.key.blend_mode;
}
self.submit_batch(batch,
&projection,
render_task_data,
color_cache_texture,
render_target,
target_size);
}
self.device.disable_depth();
self.device.set_blend(false);
}
}
fn draw_alpha_target(&mut self,
@ -1990,7 +2072,7 @@ impl Renderer {
address: deferred_resolve.address,
};
let blocks = [ [image.u0, image.v0, image.u1, image.v1].into() ];
self.gpu_cache_texture.apply_patch(&mut self.device, &update, &blocks);
self.gpu_cache_texture.apply_patch(&update, &blocks);
}
}
}

View File

@ -17,13 +17,13 @@ use std::hash::Hash;
use std::mem;
use std::sync::Arc;
use texture_cache::{TextureCache, TextureCacheItemId};
use webrender_traits::{Epoch, FontKey, FontTemplate, GlyphKey, ImageKey, ImageRendering};
use webrender_traits::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId};
use webrender_traits::{DevicePoint, DeviceIntSize, DeviceUintRect, ImageDescriptor, ColorF};
use webrender_traits::{GlyphOptions, GlyphInstance, TileOffset, TileSize};
use webrender_traits::{BlobImageRenderer, BlobImageDescriptor, BlobImageError, BlobImageRequest, BlobImageData};
use webrender_traits::BlobImageResources;
use webrender_traits::{ExternalImageData, ExternalImageType, LayoutPoint};
use api::{Epoch, FontKey, FontTemplate, GlyphKey, ImageKey, ImageRendering};
use api::{FontRenderMode, ImageData, GlyphDimensions, WebGLContextId};
use api::{DevicePoint, DeviceIntSize, DeviceUintRect, ImageDescriptor, ColorF};
use api::{GlyphOptions, GlyphInstance, TileOffset, TileSize};
use api::{BlobImageRenderer, BlobImageDescriptor, BlobImageError, BlobImageRequest, BlobImageData};
use api::BlobImageResources;
use api::{ExternalImageData, ExternalImageType, LayoutPoint};
use rayon::ThreadPool;
use glyph_rasterizer::{GlyphRasterizer, GlyphCache, GlyphRequest};
@ -228,13 +228,13 @@ impl ResourceCache {
image_templates: ImageTemplates::new(),
},
cached_glyph_dimensions: HashMap::default(),
texture_cache: texture_cache,
texture_cache,
state: State::Idle,
current_frame_id: FrameId(0),
pending_image_requests: Vec::new(),
glyph_rasterizer: GlyphRasterizer::new(workers),
blob_image_renderer: blob_image_renderer,
blob_image_renderer,
blob_image_requests: HashSet::new(),
requested_glyphs: HashSet::default(),
@ -294,10 +294,10 @@ impl ResourceCache {
}
let resource = ImageResource {
descriptor: descriptor,
data: data,
descriptor,
data,
epoch: Epoch(0),
tiling: tiling,
tiling,
dirty_rect: None,
};
@ -329,10 +329,10 @@ impl ResourceCache {
}
ImageResource {
descriptor: descriptor,
data: data,
descriptor,
data,
epoch: next_epoch,
tiling: tiling,
tiling,
dirty_rect: match (dirty_rect, image.dirty_rect) {
(Some(rect), Some(prev_rect)) => Some(rect.union(&prev_rect)),
(Some(rect), None) => Some(rect),
@ -364,7 +364,7 @@ impl ResourceCache {
pub fn add_webgl_texture(&mut self, id: WebGLContextId, texture_id: SourceTexture, size: DeviceIntSize) {
self.webgl_textures.insert(id, WebGLTexture {
id: texture_id,
size: size,
size,
});
}
@ -383,9 +383,9 @@ impl ResourceCache {
debug_assert_eq!(self.state, State::AddResources);
let request = ImageRequest {
key: key,
rendering: rendering,
tile: tile,
key,
rendering,
tile,
};
let template = self.resources.image_templates.get(key).unwrap();
@ -433,7 +433,7 @@ impl ResourceCache {
&BlobImageDescriptor {
width: w,
height: h,
offset: offset,
offset,
format: template.descriptor.format,
},
template.dirty_rect,
@ -525,7 +525,7 @@ impl ResourceCache {
let key = ImageRequest {
key: image_key,
rendering: image_rendering,
tile: tile,
tile,
};
let image_info = &self.cached_images.get(&key, self.current_frame_id);
let item = self.texture_cache.get(image_info.texture_cache_id);
@ -556,7 +556,7 @@ impl ResourceCache {
ImageProperties {
descriptor: image_template.descriptor,
external_image: external_image,
external_image,
tiling: image_template.tiling,
}
}
@ -635,15 +635,15 @@ impl ResourceCache {
for texture_cache_item_id in self.requested_images.drain() {
let item = self.texture_cache.get_mut(texture_cache_item_id);
if let Some(mut request) = gpu_cache.request(&mut item.uv_rect_handle) {
request.push(item.uv_rect.into());
request.push(item.uv_rect);
}
}
for texture_cache_item_id in self.requested_glyphs.drain() {
let item = self.texture_cache.get_mut(texture_cache_item_id);
if let Some(mut request) = gpu_cache.request(&mut item.uv_rect_handle) {
request.push(item.uv_rect.into());
request.push([item.user_data[0], item.user_data[1], 0.0, 0.0].into());
request.push(item.uv_rect);
request.push([item.user_data[0], item.user_data[1], 0.0, 0.0]);
}
}
}
@ -681,8 +681,8 @@ impl ResourceCache {
ImageDescriptor {
width: actual_width,
height: actual_height,
stride: stride,
offset: offset,
stride,
offset,
format: image_descriptor.format,
is_opaque: image_descriptor.is_opaque,
}

View File

@ -5,8 +5,8 @@
use fnv::FnvHasher;
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use webrender_traits::{BuiltDisplayList, ColorF, DynamicProperties, Epoch, LayerSize, LayoutSize};
use webrender_traits::{LayoutTransform, PipelineId, PropertyBinding, PropertyBindingId};
use api::{BuiltDisplayList, ColorF, DynamicProperties, Epoch, LayerSize, LayoutSize};
use api::{LayoutTransform, PipelineId, PropertyBinding, PropertyBindingId};
/// Stores a map of the animated property bindings for the current display list. These
/// can be used to animate the transform and/or opacity of a display list without
@ -120,11 +120,11 @@ impl Scene {
self.display_lists.insert(pipeline_id, built_display_list);
let new_pipeline = ScenePipeline {
pipeline_id: pipeline_id,
epoch: epoch,
viewport_size: viewport_size,
content_size: content_size,
background_color: background_color,
pipeline_id,
epoch,
viewport_size,
content_size,
background_color,
};
self.pipeline_map.insert(pipeline_id, new_pipeline);

View File

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use webrender_traits::LayerPoint;
use api::LayerPoint;
/// Some arbitrarily small positive number used as threshold value.
pub const EPSILON: f32 = 0.1;
@ -34,8 +34,8 @@ impl Spring {
cur: pos,
prev: pos,
dest: pos,
stiffness: stiffness,
damping: damping,
stiffness,
damping,
}
}

View File

@ -17,9 +17,9 @@ use std::mem;
use std::slice::Iter;
use time;
use util;
use webrender_traits::{ExternalImageType, ImageData, ImageFormat};
use webrender_traits::{DeviceUintRect, DeviceUintSize, DeviceUintPoint};
use webrender_traits::{DevicePoint, ImageDescriptor};
use api::{ExternalImageType, ImageData, ImageFormat};
use api::{DeviceUintRect, DeviceUintSize, DeviceUintPoint};
use api::{DevicePoint, ImageDescriptor};
/// The number of bytes we're allowed to use for a texture.
const MAX_BYTES_PER_TEXTURE: u32 = 1024 * 1024 * 256; // 256MB
@ -77,8 +77,8 @@ pub struct TexturePage {
impl TexturePage {
pub fn new(texture_id: CacheTextureId, texture_size: DeviceUintSize) -> TexturePage {
let mut page = TexturePage {
texture_id: texture_id,
texture_size: texture_size,
texture_id,
texture_size,
free_list: FreeRectList::new(),
coalesce_vec: Vec::new(),
allocations: 0,
@ -496,7 +496,7 @@ impl TextureCacheItem {
user_data: [f32; 2])
-> TextureCacheItem {
TextureCacheItem {
texture_id: texture_id,
texture_id,
uv_rect: UvRect {
uv0: DevicePoint::new(rect.origin.x as f32,
rect.origin.y as f32),
@ -505,7 +505,7 @@ impl TextureCacheItem {
},
allocated_rect: rect,
uv_rect_handle: GpuCacheHandle::new(),
user_data: user_data,
user_data,
}
}
}
@ -603,7 +603,7 @@ impl TextureCache {
items: FreeList::new(),
pending_updates: TextureUpdateList::new(),
arena: TextureCacheArena::new(),
max_texture_size: max_texture_size,
max_texture_size,
}
}
@ -642,7 +642,7 @@ impl TextureCache {
return AllocationResult {
item: self.items.get(image_id).clone(),
kind: AllocationKind::Standalone,
image_id: image_id,
image_id,
}
}
@ -717,7 +717,7 @@ impl TextureCache {
self.pending_updates.push(update_op);
free_texture_levels.push(FreeTextureLevel {
texture_id: texture_id,
texture_id,
});
}
let free_texture_level = free_texture_levels.pop().unwrap();
@ -739,7 +739,7 @@ impl TextureCache {
AllocationResult {
item: cache_item,
kind: AllocationKind::TexturePage,
image_id: image_id,
image_id,
}
}
@ -773,7 +773,7 @@ impl TextureCache {
height: dirty.size.height,
data: bytes,
stride: Some(stride),
offset: offset,
offset,
}
}
None => {
@ -793,7 +793,7 @@ impl TextureCache {
let update_op = TextureUpdate {
id: existing_item.texture_id,
op: op,
op,
};
self.pending_updates.push(update_op);
@ -845,7 +845,7 @@ impl TextureCache {
rect: result.item.allocated_rect,
id: ext_image.id,
channel_index: ext_image.channel_index,
stride: stride,
stride,
offset: descriptor.offset,
},
};
@ -866,7 +866,7 @@ impl TextureCache {
width: result.item.allocated_rect.size.width,
height: result.item.allocated_rect.size.height,
data: bytes,
stride: stride,
stride,
offset: descriptor.offset,
},
};
@ -888,10 +888,10 @@ impl TextureCache {
let update_op = TextureUpdate {
id: result.item.texture_id,
op: TextureUpdateOp::Create {
width: width,
height: height,
format: format,
filter: filter,
width,
height,
format,
filter,
mode: RenderTargetMode::None,
data: Some(data),
},
@ -905,10 +905,10 @@ impl TextureCache {
let update_op = TextureUpdate {
id: result.item.texture_id,
op: TextureUpdateOp::Create {
width: width,
height: height,
format: format,
filter: filter,
width,
height,
format,
filter,
mode: RenderTargetMode::None,
data: Some(data),
},
@ -953,9 +953,9 @@ fn texture_create_op(texture_size: DeviceUintSize, format: ImageFormat, mode: Re
TextureUpdateOp::Create {
width: texture_size.width,
height: texture_size.height,
format: format,
format,
filter: TextureFilter::Linear,
mode: mode,
mode,
data: None,
}
}
@ -967,9 +967,9 @@ fn texture_grow_op(texture_size: DeviceUintSize,
TextureUpdateOp::Grow {
width: texture_size.width,
height: texture_size.height,
format: format,
format,
filter: TextureFilter::Linear,
mode: mode,
mode,
}
}

View File

@ -7,12 +7,10 @@ use border::{BorderCornerInstance, BorderCornerSide};
use device::TextureId;
use fnv::FnvHasher;
use gpu_cache::{GpuCache, GpuCacheHandle, GpuCacheUpdateList};
use gpu_store::GpuStoreAddress;
use internal_types::{ANGLE_FLOAT_TO_FIXED, BatchTextures, CacheTextureId, LowLevelFilterOp};
use internal_types::SourceTexture;
use mask_cache::MaskCacheInfo;
use prim_store::{CLIP_DATA_GPU_SIZE, DeferredResolve, GpuBlock32};
use prim_store::{ImagePrimitiveKind, PrimitiveCacheKey};
use prim_store::{CLIP_DATA_GPU_BLOCKS, DeferredResolve, ImagePrimitiveKind, PrimitiveCacheKey};
use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
use profiler::FrameProfileCounters;
use render_task::{AlphaRenderItem, MaskGeometryKind, MaskSegment, RenderTask, RenderTaskData};
@ -26,11 +24,11 @@ use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use texture_cache::TexturePage;
use util::{TransformedRect, TransformedRectKind};
use webrender_traits::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey};
use webrender_traits::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize};
use webrender_traits::{ExternalImageType, FontRenderMode, ImageRendering, LayerRect};
use webrender_traits::{LayerToWorldTransform, MixBlendMode, PipelineId, TransformStyle};
use webrender_traits::{TileOffset, WorldToLayerTransform, YuvColorSpace, YuvFormat, LayerVector2D};
use api::{BuiltDisplayList, ClipAndScrollInfo, ClipId, ColorF, DeviceIntPoint, ImageKey};
use api::{DeviceIntRect, DeviceIntSize, DeviceUintPoint, DeviceUintSize};
use api::{ExternalImageType, FontRenderMode, ImageRendering, LayerRect};
use api::{LayerToWorldTransform, MixBlendMode, PipelineId, TransformStyle};
use api::{TileOffset, WorldToLayerTransform, YuvColorSpace, YuvFormat, LayerVector2D};
// Special sentinel value recognized by the shader. It is considered to be
// a dummy task that doesn't mask out anything.
@ -109,11 +107,13 @@ pub struct RenderTargetIndex(pub usize);
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct RenderPassIndex(isize);
#[derive(Debug)]
struct DynamicTaskInfo {
index: RenderTaskIndex,
rect: DeviceIntRect,
}
#[derive(Debug)]
pub struct RenderTaskCollection {
pub render_task_data: Vec<RenderTaskData>,
dynamic_tasks: HashMap<(RenderTaskKey, RenderPassIndex), DynamicTaskInfo, BuildHasherDefault<FnvHasher>>,
@ -136,9 +136,9 @@ impl RenderTaskCollection {
RenderTaskId::Dynamic(key) => {
let index = RenderTaskIndex(self.render_task_data.len());
let key = (key, pass);
debug_assert!(self.dynamic_tasks.contains_key(&key) == false);
debug_assert!(!self.dynamic_tasks.contains_key(&key));
self.dynamic_tasks.insert(key, DynamicTaskInfo {
index: index,
index,
rect: match task.location {
RenderTaskLocation::Fixed => panic!("Dynamic tasks should not have fixed locations!"),
RenderTaskLocation::Dynamic(Some((origin, _)), size) => DeviceIntRect::new(origin, size),
@ -250,6 +250,18 @@ impl BatchList {
batch
}
fn finalize(&mut self) {
// Reverse the instance arrays in the opaque batches
// to get maximum z-buffer efficiency by drawing
// front-to-back.
// TODO(gw): Maybe we can change the batch code to
// build these in reverse and avoid having
// to reverse the instance array here.
for batch in &mut self.opaque_batches {
batch.instances.reverse();
}
}
}
/// Encapsulates the logic of building batches for items that are blended.
@ -368,7 +380,7 @@ impl AlphaRenderItem {
OPAQUE_TASK_INDEX
}
};
let needs_blending = !prim_metadata.is_opaque ||
let needs_blending = !prim_metadata.opacity.is_opaque ||
needs_clipping ||
transform_kind == TransformedRectKind::Complex;
let blend_mode = ctx.prim_store.get_blend_mode(needs_blending, prim_metadata);
@ -660,6 +672,13 @@ impl AlphaBatcher {
deferred_resolves);
}
}
self.batch_list.finalize();
}
pub fn is_empty(&self) -> bool {
self.batch_list.opaque_batches.is_empty() &&
self.batch_list.alpha_batches.is_empty()
}
}
@ -695,40 +714,41 @@ impl ClipBatcher {
let instance = CacheClipInstance {
task_id: task_index.0 as i32,
layer_index: packed_layer_index.0 as i32,
address: GpuStoreAddress(0),
address: 0,
segment: 0,
resource_address: 0,
};
for clip_index in 0..info.effective_complex_clip_count as usize {
let offset = info.complex_clip_range.start.0 + ((CLIP_DATA_GPU_SIZE * clip_index) as i32);
for clip_index in 0 .. info.complex_clip_range.get_count() {
let gpu_address = info.complex_clip_range.location.as_int(gpu_cache) +
(CLIP_DATA_GPU_BLOCKS * clip_index) as i32;
match geometry_kind {
MaskGeometryKind::Default => {
self.rectangles.push(CacheClipInstance {
address: GpuStoreAddress(offset),
address: gpu_address,
segment: MaskSegment::All as i32,
..instance
});
}
MaskGeometryKind::CornersOnly => {
self.rectangles.extend(&[
self.rectangles.extend_from_slice(&[
CacheClipInstance {
address: GpuStoreAddress(offset),
address: gpu_address,
segment: MaskSegment::TopLeftCorner as i32,
..instance
},
CacheClipInstance {
address: GpuStoreAddress(offset),
address: gpu_address,
segment: MaskSegment::TopRightCorner as i32,
..instance
},
CacheClipInstance {
address: GpuStoreAddress(offset),
address: gpu_address,
segment: MaskSegment::BottomLeftCorner as i32,
..instance
},
CacheClipInstance {
address: GpuStoreAddress(offset),
address: gpu_address,
segment: MaskSegment::BottomRightCorner as i32,
..instance
},
@ -737,24 +757,34 @@ impl ClipBatcher {
}
}
if let Some((ref mask, address)) = info.image {
for clip_index in 0 .. info.layer_clip_range.get_count() {
let gpu_address = info.layer_clip_range.location.as_int(gpu_cache) +
(CLIP_DATA_GPU_BLOCKS * clip_index) as i32;
self.rectangles.push(CacheClipInstance {
address: gpu_address,
segment: MaskSegment::All as i32,
..instance
});
}
if let Some((ref mask, gpu_location)) = info.image {
let cache_item = resource_cache.get_cached_image(mask.image, ImageRendering::Auto, None);
self.images.entry(cache_item.texture_id)
.or_insert(Vec::new())
.push(CacheClipInstance {
address: address,
address: gpu_location.as_int(gpu_cache),
resource_address: cache_item.uv_rect_handle.as_int(gpu_cache),
..instance
})
}
for &(ref source, gpu_address) in &info.border_corners {
for &(ref source, gpu_location) in &info.border_corners {
let gpu_address = gpu_location.as_int(gpu_cache);
self.border_clears.push(CacheClipInstance {
address: gpu_address,
segment: 0,
..instance
});
for clip_index in 0..source.actual_clip_count {
self.borders.push(CacheClipInstance {
address: gpu_address,
@ -852,8 +882,8 @@ impl<T: RenderTarget> RenderTargetList<T> {
}
RenderTargetList {
targets: targets,
target_size: target_size,
targets,
target_size,
}
}
@ -1134,7 +1164,7 @@ impl RenderPass {
pub fn new(pass_index: isize, is_framebuffer: bool, size: DeviceUintSize) -> RenderPass {
RenderPass {
pass_index: RenderPassIndex(pass_index),
is_framebuffer: is_framebuffer,
is_framebuffer,
color_targets: RenderTargetList::new(size, is_framebuffer),
alpha_targets: RenderTargetList::new(size, false),
tasks: vec![],
@ -1254,9 +1284,9 @@ pub enum AlphaBatchKind {
}
bitflags! {
pub flags AlphaBatchKeyFlags: u8 {
const NEEDS_CLIPPING = 0b00000001,
const AXIS_ALIGNED = 0b00000010,
pub struct AlphaBatchKeyFlags: u8 {
const NEEDS_CLIPPING = 0b00000001;
const AXIS_ALIGNED = 0b00000010;
}
}
@ -1288,10 +1318,10 @@ impl AlphaBatchKey {
blend_mode: BlendMode,
textures: BatchTextures) -> AlphaBatchKey {
AlphaBatchKey {
kind: kind,
flags: flags,
blend_mode: blend_mode,
textures: textures,
kind,
flags,
blend_mode,
textures,
}
}
@ -1333,7 +1363,7 @@ pub struct BlurCommand {
pub struct CacheClipInstance {
task_id: i32,
layer_index: i32,
address: GpuStoreAddress,
address: i32,
segment: i32,
resource_address: i32,
}
@ -1365,11 +1395,11 @@ impl SimplePrimitiveInstance {
layer_index: PackedLayerIndex,
z_sort_index: i32) -> SimplePrimitiveInstance {
SimplePrimitiveInstance {
specific_prim_address: specific_prim_address,
specific_prim_address,
task_index: task_index.0 as i32,
clip_task_index: clip_task_index.0 as i32,
layer_index: layer_index.0 as i32,
z_sort_index: z_sort_index,
z_sort_index,
}
}
@ -1406,12 +1436,12 @@ impl CompositePrimitiveInstance {
data1: i32,
z: i32) -> CompositePrimitiveInstance {
CompositePrimitiveInstance {
task_index: task_index,
src_task_index: src_task_index,
backdrop_task_index: backdrop_task_index,
data0: data0,
data1: data1,
z: z,
task_index,
src_task_index,
backdrop_task_index,
data0,
data1,
z,
}
}
}
@ -1456,7 +1486,7 @@ pub struct PrimitiveBatch {
impl PrimitiveBatch {
fn new(key: AlphaBatchKey) -> PrimitiveBatch {
PrimitiveBatch {
key: key,
key,
instances: Vec::new(),
item_rects: Vec::new(),
}
@ -1499,13 +1529,15 @@ pub struct StackingContext {
/// The `ClipId` of the owning reference frame.
pub reference_frame_id: ClipId,
/// Local bounding rectangle for this stacking context.
pub local_bounds: LayerRect,
/// Screen space bounding rectangle for this stacking context,
/// calculated based on the size and position of all its children.
pub screen_bounds: DeviceIntRect,
/// Local bounding rectangle of this stacking context,
/// computed as the union of all contained items that are not
/// `ContextIsolation::Items` on their own
pub isolated_items_bounds: LayerRect,
pub composite_ops: CompositeOps,
pub clip_scroll_groups: Vec<ClipScrollGroupIndex>,
@ -1526,7 +1558,6 @@ impl StackingContext {
reference_frame_offset: LayerVector2D,
is_page_root: bool,
reference_frame_id: ClipId,
local_bounds: LayerRect,
transform_style: TransformStyle,
composite_ops: CompositeOps)
-> StackingContext {
@ -1535,15 +1566,15 @@ impl StackingContext {
TransformStyle::Preserve3D => ContextIsolation::Items,
};
StackingContext {
pipeline_id: pipeline_id,
reference_frame_offset: reference_frame_offset,
reference_frame_id: reference_frame_id,
local_bounds: local_bounds,
pipeline_id,
reference_frame_offset,
reference_frame_id,
screen_bounds: DeviceIntRect::zero(),
composite_ops: composite_ops,
isolated_items_bounds: LayerRect::zero(),
composite_ops,
clip_scroll_groups: Vec::new(),
isolation: isolation,
is_page_root: is_page_root,
isolation,
is_page_root,
is_visible: false,
}
}
@ -1610,9 +1641,15 @@ impl PackedLayer {
Default::default()
}
pub fn set_transform(&mut self, transform: LayerToWorldTransform) {
pub fn set_transform(&mut self, transform: LayerToWorldTransform) -> bool {
self.transform = transform;
self.inv_transform = self.transform.inverse().unwrap();
match self.transform.inverse() {
Some(inv) => {
self.inv_transform = inv;
true
}
None => false,
}
}
pub fn set_rect(&mut self,
@ -1620,11 +1657,10 @@ impl PackedLayer {
screen_rect: &DeviceIntRect,
device_pixel_ratio: f32)
-> Option<(TransformedRectKind, DeviceIntRect)> {
let xf_rect = TransformedRect::new(&local_rect, &self.transform, device_pixel_ratio);
xf_rect.bounding_rect.intersection(screen_rect).map(|rect| {
self.local_clip_rect = *local_rect;
(xf_rect.kind, rect)
})
self.local_clip_rect = *local_rect;
let xf_rect = TransformedRect::new(local_rect, &self.transform, device_pixel_ratio);
xf_rect.bounding_rect.intersection(screen_rect)
.map(|rect| (xf_rect.kind, rect))
}
}
@ -1640,7 +1676,7 @@ pub struct CompositeOps {
impl CompositeOps {
pub fn new(filters: Vec<LowLevelFilterOp>, mix_blend_mode: Option<MixBlendMode>) -> CompositeOps {
CompositeOps {
filters: filters,
filters,
mix_blend_mode: mix_blend_mode
}
}
@ -1680,7 +1716,6 @@ pub struct Frame {
pub layer_texture_data: Vec<PackedLayer>,
pub render_task_data: Vec<RenderTaskData>,
pub gpu_data32: Vec<GpuBlock32>,
// List of updates that need to be pushed to the
// gpu resource cache.
@ -1710,7 +1745,7 @@ fn resolve_image(image_key: ImageKey,
// the render thread...
let cache_handle = gpu_cache.push_deferred_per_frame_blocks(1);
deferred_resolves.push(DeferredResolve {
image_properties: image_properties,
image_properties,
address: gpu_cache.get_address(&cache_handle),
});

View File

@ -4,10 +4,10 @@
use std::f32::consts::{FRAC_1_SQRT_2};
use euclid::{Point2D, Rect, Size2D};
use euclid::{TypedRect, TypedPoint2D, TypedSize2D, TypedTransform3D};
use webrender_traits::{DeviceIntRect, DeviceIntPoint, DeviceIntSize};
use webrender_traits::{LayerRect, WorldPoint3D, LayerToWorldTransform};
use webrender_traits::{BorderRadius, ComplexClipRegion, LayoutRect};
use euclid::{TypedRect, TypedPoint2D, TypedSize2D, TypedTransform2D, TypedTransform3D};
use api::{DeviceIntRect, DevicePoint, DeviceRect, DeviceSize};
use api::{LayerRect, WorldPoint3D, LayerToWorldTransform};
use api::{BorderRadius, ComplexClipRegion, LayoutRect};
use num_traits::Zero;
// Matches the definition of SK_ScalarNearlyZero in Skia.
@ -18,6 +18,8 @@ pub trait MatrixHelpers<Src, Dst> {
fn transform_rect(&self, rect: &TypedRect<f32, Src>) -> TypedRect<f32, Dst>;
fn is_identity(&self) -> bool;
fn preserves_2d_axis_alignment(&self) -> bool;
fn inverse_project(&self, target: &TypedPoint2D<f32, Dst>) -> Option<TypedPoint2D<f32, Src>>;
fn inverse_rect_footprint(&self, rect: &TypedRect<f32, Dst>) -> TypedRect<f32, Src>;
}
impl<Src, Dst> MatrixHelpers<Src, Dst> for TypedTransform3D<f32, Src, Dst> {
@ -64,6 +66,31 @@ impl<Src, Dst> MatrixHelpers<Src, Dst> for TypedTransform3D<f32, Src, Dst> {
col0 < 2 && col1 < 2 && row0 < 2 && row1 < 2
}
fn inverse_project(&self, target: &TypedPoint2D<f32, Dst>) -> Option<TypedPoint2D<f32, Src>> {
let m: TypedTransform2D<f32, Src, Dst>;
m = TypedTransform2D::column_major(self.m11 - target.x * self.m14,
self.m21 - target.x * self.m24,
self.m41 - target.x * self.m44,
self.m12 - target.y * self.m14,
self.m22 - target.y * self.m24,
self.m42 - target.y * self.m44);
m.inverse()
.map(|inv| TypedPoint2D::new(inv.m31, inv.m32))
}
fn inverse_rect_footprint(&self, rect: &TypedRect<f32, Dst>) -> TypedRect<f32, Src> {
TypedRect::from_points(&[
self.inverse_project(&rect.origin)
.unwrap_or(TypedPoint2D::zero()),
self.inverse_project(&rect.top_right())
.unwrap_or(TypedPoint2D::zero()),
self.inverse_project(&rect.bottom_left())
.unwrap_or(TypedPoint2D::zero()),
self.inverse_project(&rect.bottom_right())
.unwrap_or(TypedPoint2D::zero()),
])
}
}
pub trait RectHelpers<U> where Self: Sized {
@ -175,6 +202,13 @@ pub struct TransformedRect {
pub kind: TransformedRectKind,
}
// Having an unlimited bounding box is fine up until we try
// to cast it to `i32`, where we get `-2147483648` for any
// values larger than or equal to 2^31.
//Note: clamping to i32::MIN and i32::MAX is not a solution,
// with explanation left as an exercise for the reader.
const MAX_COORD: f32 = 1.0e9;
impl TransformedRect {
pub fn new(rect: &LayerRect,
transform: &LayerToWorldTransform,
@ -186,80 +220,43 @@ impl TransformedRect {
TransformedRectKind::Complex
};
// FIXME(gw): This code is meant to be a fast path for simple transforms.
// However, it fails on transforms that translate Z but result in an
// axis aligned rect.
/*
match kind {
TransformedRectKind::AxisAligned => {
let v0 = transform.transform_point(&rect.origin);
let v1 = transform.transform_point(&rect.top_right());
let v2 = transform.transform_point(&rect.bottom_left());
let v3 = transform.transform_point(&rect.bottom_right());
let vertices = [
transform.transform_point3d(&rect.origin.to_3d()),
transform.transform_point3d(&rect.bottom_left().to_3d()),
transform.transform_point3d(&rect.bottom_right().to_3d()),
transform.transform_point3d(&rect.top_right().to_3d()),
];
let screen_min_dp = Point2D::new(DevicePixel((v0.x * device_pixel_ratio).floor() as i32),
DevicePixel((v0.y * device_pixel_ratio).floor() as i32));
let screen_max_dp = Point2D::new(DevicePixel((v3.x * device_pixel_ratio).ceil() as i32),
DevicePixel((v3.y * device_pixel_ratio).ceil() as i32));
let (mut xs, mut ys) = ([0.0; 4], [0.0; 4]);
let screen_rect_dp = Rect::new(screen_min_dp, Size2D::new(screen_max_dp.x - screen_min_dp.x,
screen_max_dp.y - screen_min_dp.y));
for (vertex, (x, y)) in vertices.iter().zip(xs.iter_mut().zip(ys.iter_mut())) {
*x = get_normal(vertex.x).unwrap_or(0.0);
*y = get_normal(vertex.y).unwrap_or(0.0);
}
TransformedRect {
local_rect: *rect,
vertices: [
Point4D::new(v0.x, v0.y, 0.0, 1.0),
Point4D::new(v1.x, v1.y, 0.0, 1.0),
Point4D::new(v2.x, v2.y, 0.0, 1.0),
Point4D::new(v3.x, v3.y, 0.0, 1.0),
],
bounding_rect: screen_rect_dp,
kind: kind,
}
}
TransformedRectKind::Complex => {
*/
let vertices = [
transform.transform_point3d(&rect.origin.to_3d()),
transform.transform_point3d(&rect.bottom_left().to_3d()),
transform.transform_point3d(&rect.bottom_right().to_3d()),
transform.transform_point3d(&rect.top_right().to_3d()),
];
xs.sort_by(|a, b| a.partial_cmp(b).unwrap());
ys.sort_by(|a, b| a.partial_cmp(b).unwrap());
let (mut xs, mut ys) = ([0.0; 4], [0.0; 4]);
let outer_min_dp = (DevicePoint::new(xs[0], ys[0]) * device_pixel_ratio).floor();
let outer_max_dp = (DevicePoint::new(xs[3], ys[3]) * device_pixel_ratio).ceil();
let inner_min_dp = (DevicePoint::new(xs[1], ys[1]) * device_pixel_ratio).ceil();
let inner_max_dp = (DevicePoint::new(xs[2], ys[2]) * device_pixel_ratio).floor();
for (vertex, (x, y)) in vertices.iter().zip(xs.iter_mut().zip(ys.iter_mut())) {
*x = get_normal(vertex.x).unwrap_or(0.0);
*y = get_normal(vertex.y).unwrap_or(0.0);
}
let max_rect = DeviceRect::new(DevicePoint::new(-MAX_COORD, -MAX_COORD),
DeviceSize::new(2.0*MAX_COORD, 2.0*MAX_COORD));
let bounding_rect = DeviceRect::new(outer_min_dp, (outer_max_dp - outer_min_dp).to_size())
.intersection(&max_rect).unwrap_or(max_rect).to_i32();
let inner_rect = DeviceRect::new(inner_min_dp, (inner_max_dp - inner_min_dp).to_size())
.intersection(&max_rect).unwrap_or(max_rect).to_i32();
xs.sort_by(|a, b| a.partial_cmp(b).unwrap());
ys.sort_by(|a, b| a.partial_cmp(b).unwrap());
let outer_min_dp = DeviceIntPoint::new((xs[0] * device_pixel_ratio).floor() as i32,
(ys[0] * device_pixel_ratio).floor() as i32);
let outer_max_dp = DeviceIntPoint::new((xs[3] * device_pixel_ratio).ceil() as i32,
(ys[3] * device_pixel_ratio).ceil() as i32);
let inner_min_dp = DeviceIntPoint::new((xs[1] * device_pixel_ratio).ceil() as i32,
(ys[1] * device_pixel_ratio).ceil() as i32);
let inner_max_dp = DeviceIntPoint::new((xs[2] * device_pixel_ratio).floor() as i32,
(ys[2] * device_pixel_ratio).floor() as i32);
TransformedRect {
local_rect: *rect,
vertices: vertices,
bounding_rect: DeviceIntRect::new(outer_min_dp,
DeviceIntSize::new(outer_max_dp.x.saturating_sub(outer_min_dp.x),
outer_max_dp.y.saturating_sub(outer_min_dp.y))),
inner_rect: DeviceIntRect::new(inner_min_dp,
DeviceIntSize::new(inner_max_dp.x.saturating_sub(inner_min_dp.x),
inner_max_dp.y.saturating_sub(inner_min_dp.y))),
kind: kind,
}
/*
}
}*/
TransformedRect {
local_rect: *rect,
vertices,
bounding_rect,
inner_rect,
kind,
}
}
}
@ -327,3 +324,22 @@ pub fn recycle_vec<T>(mut old_vec: Vec<T>) -> Vec<T> {
return old_vec;
}
#[cfg(test)]
pub mod test {
use super::*;
use euclid::{Point2D, Radians, Transform3D};
use std::f32::consts::PI;
#[test]
fn inverse_project() {
let m0 = Transform3D::identity();
let p0 = Point2D::new(1.0, 2.0);
// an identical transform doesn't need any inverse projection
assert_eq!(m0.inverse_project(&p0), Some(p0));
let m1 = Transform3D::create_rotation(0.0, 1.0, 0.0, Radians::new(PI / 3.0));
// rotation by 60 degrees would imply scaling of X component by a factor of 2
assert_eq!(m1.inverse_project(&p0), Some(Point2D::new(2.0, 2.0)));
}
}

View File

@ -7,9 +7,9 @@
//! The API surface provided here should be roughly the same to the one provided
//! in webgl_types, modulo completely compiled-out stuff.
use webrender_traits::DeviceIntSize;
use webrender_traits::{GLContextAttributes, GLLimits};
use webrender_traits::WebGLCommand;
use api::DeviceIntSize;
use api::{GLContextAttributes, GLLimits};
use api::WebGLCommand;
pub struct GLContextHandleWrapper;

View File

@ -10,7 +10,7 @@ use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle};
use offscreen_gl_context::{GLContext, NativeGLContextMethods, GLContextDispatcher};
use offscreen_gl_context::{OSMesaContext, OSMesaContextHandle};
use offscreen_gl_context::{ColorAttachmentType, GLContextAttributes, GLLimits};
use webrender_traits::{WebGLCommand, DeviceIntSize};
use api::{WebGLCommand, DeviceIntSize};
pub enum GLContextHandleWrapper {
Native(NativeGLContextHandle),

View File

@ -1,6 +1,6 @@
[package]
name = "webrender_traits"
version = "0.43.0"
name = "webrender_api"
version = "0.47.0"
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
license = "MPL-2.0"
repository = "https://github.com/servo/webrender"

View File

@ -117,7 +117,7 @@ pub struct GLLimits([u8; 0]);
#[cfg(not(feature = "webgl"))]
#[derive(Clone, Deserialize, Serialize)]
pub enum WebGLCommand {
Flush,
FenceAndWaitSync,
}
#[repr(C)]
@ -164,8 +164,8 @@ impl RenderApiSender {
payload_sender: PayloadSender)
-> RenderApiSender {
RenderApiSender {
api_sender: api_sender,
payload_sender: payload_sender,
api_sender,
payload_sender,
}
}
@ -271,7 +271,7 @@ impl RenderApi {
/// # Examples
///
/// ```
/// # use webrender_traits::{PipelineId, RenderApiSender};
/// # use webrender_api::{PipelineId, RenderApiSender};
/// # fn example(sender: RenderApiSender) {
/// let api = sender.create_api();
/// // ...
@ -322,9 +322,9 @@ impl RenderApi {
self.api_sender.send(msg).unwrap();
self.payload_sender.send_payload(Payload {
epoch: epoch,
pipeline_id: pipeline_id,
display_list_data: display_list_data,
epoch,
pipeline_id,
display_list_data,
}).unwrap();
}
@ -507,7 +507,6 @@ impl PropertyBindingId {
/// A unique key that is used for connecting animated property
/// values to bindings in the display list.
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct PropertyBindingKey<T> {
pub id: PropertyBindingId,
@ -519,7 +518,7 @@ impl<T: Copy> PropertyBindingKey<T> {
pub fn with(&self, value: T) -> PropertyValue<T> {
PropertyValue {
key: *self,
value: value,
value,
}
}
}

View File

@ -53,8 +53,8 @@ impl Payload {
assert_eq!(payload_reader.position(), data.len() as u64);
Payload {
epoch: epoch,
pipeline_id: pipeline_id,
epoch,
pipeline_id,
display_list_data: built_display_list_data,
}
}

View File

@ -65,11 +65,10 @@ impl ColorU {
pub fn new(r: u8, g: u8, b: u8, a: u8) -> ColorU {
ColorU {
r: r,
g: g,
b: b,
a: a,
r,
g,
b,
a,
}
}
}

View File

@ -4,9 +4,8 @@
use app_units::Au;
use euclid::SideOffsets2D;
use {ColorF, FontKey, ImageKey, ItemRange, PipelineId, WebGLContextId};
use {LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D};
use {PropertyBinding};
use {ColorF, FontKey, ImageKey, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform};
use {LayoutVector2D, PipelineId, PropertyBinding, WebGLContextId};
// NOTE: some of these structs have an "IMPLICIT" comment.
// This indicates that the BuiltDisplayList will have serialized
@ -29,7 +28,7 @@ impl ClipAndScrollInfo {
pub fn new(scroll_node_id: ClipId, clip_node_id: ClipId) -> ClipAndScrollInfo {
ClipAndScrollInfo {
scroll_node_id: scroll_node_id,
scroll_node_id,
clip_node_id: Some(clip_node_id),
}
}
@ -43,12 +42,14 @@ impl ClipAndScrollInfo {
pub struct DisplayItem {
pub item: SpecificDisplayItem,
pub rect: LayoutRect,
pub local_clip: LocalClip,
pub clip_and_scroll: ClipAndScrollInfo,
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum SpecificDisplayItem {
Clip(ClipDisplayItem),
ScrollFrame(ClipDisplayItem),
Rectangle(RectangleDisplayItem),
Text(TextDisplayItem),
Image(ImageDisplayItem),
@ -62,7 +63,6 @@ pub enum SpecificDisplayItem {
PushStackingContext(PushStackingContextDisplayItem),
PopStackingContext,
SetGradientStops,
SetClipRegion(ClipRegion),
PushNestedDisplayList,
PopNestedDisplayList,
}
@ -71,6 +71,7 @@ pub enum SpecificDisplayItem {
pub struct ClipDisplayItem {
pub id: ClipId,
pub parent_id: ClipId,
pub image_mask: Option<ImageMask>,
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
@ -118,6 +119,7 @@ pub enum RepeatMode {
Space,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct NinePatchDescriptor {
pub width: u32,
@ -129,6 +131,9 @@ pub struct NinePatchDescriptor {
pub struct ImageBorder {
pub image_key: ImageKey,
pub patch: NinePatchDescriptor,
/// Controls whether the center of the 9 patch image is
/// rendered or ignored.
pub fill: bool,
pub outset: SideOffsets2D<f32>,
pub repeat_horizontal: RepeatMode,
pub repeat_vertical: RepeatMode,
@ -160,6 +165,7 @@ pub struct BorderDisplayItem {
pub details: BorderDetails,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct BorderRadius {
pub top_left: LayoutSize,
@ -168,6 +174,7 @@ pub struct BorderRadius {
pub bottom_right: LayoutSize,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct BorderWidths {
pub left: f32,
@ -432,6 +439,7 @@ impl YuvFormat {
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct ImageMask {
pub image: ImageKey,
@ -440,13 +448,24 @@ pub struct ImageMask {
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub struct ClipRegion {
pub main: LayoutRect,
pub image_mask: Option<ImageMask>,
#[serde(default, skip_serializing, skip_deserializing)]
pub complex_clips: ItemRange<ComplexClipRegion>,
#[serde(default, skip_serializing, skip_deserializing)]
pub complex_clip_count: usize,
pub enum LocalClip {
Rect(LayoutRect),
RoundedRect(LayoutRect, ComplexClipRegion),
}
impl From<LayoutRect> for LocalClip {
fn from(rect: LayoutRect) -> Self {
LocalClip::Rect(rect)
}
}
impl LocalClip {
pub fn clip_rect(&self) -> &LayoutRect {
match *self {
LocalClip::Rect(ref rect) => rect,
LocalClip::RoundedRect(ref rect, _) => &rect,
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
@ -512,48 +531,13 @@ impl BorderRadius {
}
}
impl ClipRegion {
pub fn new(rect: &LayoutRect,
image_mask: Option<ImageMask>)
-> ClipRegion {
ClipRegion {
main: *rect,
image_mask: image_mask,
complex_clips: ItemRange::default(),
complex_clip_count: 0,
}
}
pub fn simple(rect: &LayoutRect) -> ClipRegion {
ClipRegion {
main: *rect,
image_mask: None,
complex_clips: ItemRange::default(),
complex_clip_count: 0,
}
}
pub fn empty() -> ClipRegion {
ClipRegion {
main: LayoutRect::zero(),
image_mask: None,
complex_clips: ItemRange::default(),
complex_clip_count: 0,
}
}
pub fn is_complex(&self) -> bool {
self.complex_clip_count != 0 || self.image_mask.is_some()
}
}
impl ColorF {
pub fn new(r: f32, g: f32, b: f32, a: f32) -> ColorF {
ColorF {
r: r,
g: g,
b: b,
a: a,
r,
g,
b,
a,
}
}
@ -575,8 +559,8 @@ impl ComplexClipRegion {
/// Create a new complex clip region.
pub fn new(rect: LayoutRect, radii: BorderRadius) -> ComplexClipRegion {
ComplexClipRegion {
rect: rect,
radii: radii,
rect,
radii,
}
}
}
@ -652,3 +636,4 @@ define_empty_heap_size_of!(RepeatMode);
define_empty_heap_size_of!(ImageKey);
define_empty_heap_size_of!(MixBlendMode);
define_empty_heap_size_of!(TransformStyle);
define_empty_heap_size_of!(LocalClip);

View File

@ -8,14 +8,14 @@ use serde::{Deserialize, Serialize, Serializer};
use serde::ser::{SerializeSeq, SerializeMap};
use time::precise_time_ns;
use {BorderDetails, BorderDisplayItem, BorderWidths, BoxShadowClipMode, BoxShadowDisplayItem};
use {ClipAndScrollInfo, ClipDisplayItem, ClipId, ClipRegion, ColorF, ComplexClipRegion};
use {DisplayItem, ExtendMode, FilterOp, FontKey, GlyphInstance, GlyphOptions, Gradient};
use {GradientDisplayItem, GradientStop, IframeDisplayItem, ImageDisplayItem, ImageKey, ImageMask};
use {ImageRendering, LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, MixBlendMode};
use {PipelineId, PropertyBinding, PushStackingContextDisplayItem, RadialGradient};
use {ClipAndScrollInfo, ClipDisplayItem, ClipId, ColorF, ComplexClipRegion, DisplayItem};
use {ExtendMode, FilterOp, FontKey, GlyphInstance, GlyphOptions, Gradient, GradientDisplayItem};
use {GradientStop, IframeDisplayItem, ImageDisplayItem, ImageKey, ImageMask, ImageRendering};
use {LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D, LocalClip};
use {MixBlendMode, PipelineId, PropertyBinding, PushStackingContextDisplayItem, RadialGradient};
use {RadialGradientDisplayItem, RectangleDisplayItem, ScrollPolicy, SpecificDisplayItem};
use {StackingContext, TextDisplayItem, TransformStyle, WebGLContextId, WebGLDisplayItem};
use {YuvColorSpace, YuvData, YuvImageDisplayItem, LayoutVector2D};
use {YuvColorSpace, YuvData, YuvImageDisplayItem};
use std::marker::PhantomData;
#[repr(C)]
@ -67,7 +67,7 @@ pub struct BuiltDisplayListIter<'a> {
cur_stops: ItemRange<GradientStop>,
cur_glyphs: ItemRange<GlyphInstance>,
cur_filters: ItemRange<FilterOp>,
cur_clip: ClipRegion,
cur_complex_clip: (ItemRange<ComplexClipRegion>, usize),
peeking: Peek,
}
@ -95,8 +95,8 @@ impl BuiltDisplayListDescriptor {
impl BuiltDisplayList {
pub fn from_data(data: Vec<u8>, descriptor: BuiltDisplayListDescriptor) -> BuiltDisplayList {
BuiltDisplayList {
data: data,
descriptor: descriptor,
data,
descriptor,
}
}
@ -127,19 +127,23 @@ impl BuiltDisplayList {
impl<'a> BuiltDisplayListIter<'a> {
pub fn new(list: &'a BuiltDisplayList) -> Self {
Self::new_with_list_and_data(list, &list.data)
}
pub fn new_with_list_and_data(list: &'a BuiltDisplayList, data: &'a [u8]) -> Self {
BuiltDisplayListIter {
list: list,
data: &list.data,
// Dummy data, will be overwritten by `next`
cur_item: DisplayItem {
list,
data: &data,
cur_item: DisplayItem { // Dummy data, will be overwritten by `next`
item: SpecificDisplayItem::PopStackingContext,
rect: LayoutRect::zero(),
local_clip: LocalClip::from(LayoutRect::zero()),
clip_and_scroll: ClipAndScrollInfo::simple(ClipId::new(0, PipelineId(0, 0))),
},
cur_stops: ItemRange::default(),
cur_glyphs: ItemRange::default(),
cur_filters: ItemRange::default(),
cur_clip: ClipRegion::empty(),
cur_complex_clip: (ItemRange::default(), 0),
peeking: Peek::NotPeeking,
}
}
@ -164,7 +168,7 @@ impl<'a> BuiltDisplayListIter<'a> {
// Don't let these bleed into another item
self.cur_stops = ItemRange::default();
self.cur_clip = ClipRegion::empty();
self.cur_complex_clip = (ItemRange::default(), 0);
loop {
if self.data.len() == 0 {
@ -175,27 +179,16 @@ impl<'a> BuiltDisplayListIter<'a> {
.expect("MEH: malicious process?");
match self.cur_item.item {
SetClipRegion(clip) => {
self.cur_clip = clip;
let (clip_range, clip_count) = self.skip_slice::<ComplexClipRegion>();
self.cur_clip.complex_clip_count = clip_count;
self.cur_clip.complex_clips = clip_range;
// This is a dummy item, skip over it
continue;
}
SetGradientStops => {
self.cur_stops = self.skip_slice::<GradientStop>().0;
// This is a dummy item, skip over it
continue;
}
Text(_) => {
self.cur_glyphs = self.skip_slice::<GlyphInstance>().0;
}
PushStackingContext(_) => {
self.cur_filters = self.skip_slice::<FilterOp>().0;
}
Clip(_) | ScrollFrame(_) =>
self.cur_complex_clip = self.skip_slice::<ComplexClipRegion>(),
Text(_) => self.cur_glyphs = self.skip_slice::<GlyphInstance>().0,
PushStackingContext(_) => self.cur_filters = self.skip_slice::<FilterOp>().0,
_ => { /* do nothing */ }
}
@ -279,6 +272,10 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
self.iter.cur_item.rect
}
pub fn local_clip(&self) -> &LocalClip {
&self.iter.cur_item.local_clip
}
pub fn clip_and_scroll(&self) -> ClipAndScrollInfo {
self.iter.cur_item.clip_and_scroll
}
@ -287,8 +284,8 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
&self.iter.cur_item.item
}
pub fn clip_region(&self) -> &ClipRegion {
&self.iter.cur_clip
pub fn complex_clip(&self) -> &(ItemRange<ComplexClipRegion>, usize) {
&self.iter.cur_complex_clip
}
pub fn gradient_stops(&self) -> ItemRange<GradientStop> {
@ -307,24 +304,9 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> {
self.iter.display_list()
}
// Creates a new iterator where this element's iterator
// is, to hack around borrowck.
// Creates a new iterator where this element's iterator is, to hack around borrowck.
pub fn sub_iter(&self) -> BuiltDisplayListIter<'a> {
BuiltDisplayListIter {
list: self.iter.list,
data: self.iter.data,
// Dummy data, will be overwritten by `next`
cur_item: DisplayItem {
item: SpecificDisplayItem::PopStackingContext,
rect: LayoutRect::zero(),
clip_and_scroll: ClipAndScrollInfo::simple(ClipId::new(0, PipelineId(0, 0))),
},
cur_stops: ItemRange::default(),
cur_glyphs: ItemRange::default(),
cur_filters: ItemRange::default(),
cur_clip: ClipRegion::empty(),
peeking: Peek::NotPeeking,
}
BuiltDisplayListIter::new_with_list_and_data(self.iter.list, self.iter.data)
}
}
@ -339,8 +321,8 @@ impl<'de, 'a, T: Deserialize<'de>> AuxIter<'a, T> {
};
AuxIter {
data: data,
size: size,
data,
size,
_boo: PhantomData,
}
}
@ -397,13 +379,12 @@ impl<'a, 'b> Serialize for DisplayItemRef<'a, 'b> {
_ => { }
}
let clip_region = self.clip_region();
let &(complex_clips, number_of_complex_clips) = self.complex_clip();
let gradient_stops = self.gradient_stops();
map.serialize_entry("clip_region", &clip_region)?;
if !clip_region.complex_clips.is_empty() {
if number_of_complex_clips > 0 {
map.serialize_entry("complex_clips",
&self.iter.list.get(clip_region.complex_clips).collect::<Vec<_>>())?;
&self.iter.list.get(complex_clips).collect::<Vec<_>>())?;
}
if !gradient_stops.is_empty() {
@ -443,11 +424,11 @@ impl DisplayListBuilder {
DisplayListBuilder {
data: Vec::with_capacity(capacity),
pipeline_id: pipeline_id,
pipeline_id,
clip_stack: vec![ClipAndScrollInfo::simple(ClipId::root_scroll_node(pipeline_id))],
next_clip_id: FIRST_CLIP_ID,
builder_start_time: start_time,
content_size: content_size,
content_size,
}
}
@ -465,18 +446,24 @@ impl DisplayListBuilder {
self.data = temp.data;
}
fn push_item(&mut self, item: SpecificDisplayItem, rect: LayoutRect) {
fn push_item(&mut self,
item: SpecificDisplayItem,
rect: LayoutRect,
local_clip: Option<LocalClip>) {
let local_clip = local_clip.unwrap_or_else(|| LocalClip::from(rect));
bincode::serialize_into(&mut self.data, &DisplayItem {
item: item,
rect: rect,
item,
rect,
local_clip: local_clip,
clip_and_scroll: *self.clip_stack.last().unwrap(),
}, bincode::Infinite).unwrap();
}
fn push_new_empty_item(&mut self, item: SpecificDisplayItem) {
bincode::serialize_into(&mut self.data, &DisplayItem {
item: item,
item,
rect: LayoutRect::zero(),
local_clip: LocalClip::from(LayoutRect::zero()),
clip_and_scroll: *self.clip_stack.last().unwrap(),
}, bincode::Infinite).unwrap();
}
@ -499,62 +486,59 @@ impl DisplayListBuilder {
debug_assert_eq!(len, count);
}
pub fn push_rect(&mut self,
rect: LayoutRect,
_token: ClipRegionToken,
color: ColorF) {
pub fn push_rect(&mut self, rect: LayoutRect, local_clip: Option<LocalClip>, color: ColorF) {
let item = SpecificDisplayItem::Rectangle(RectangleDisplayItem {
color: color,
color,
});
self.push_item(item, rect);
self.push_item(item, rect, local_clip);
}
pub fn push_image(&mut self,
rect: LayoutRect,
_token: ClipRegionToken,
local_clip: Option<LocalClip>,
stretch_size: LayoutSize,
tile_spacing: LayoutSize,
image_rendering: ImageRendering,
key: ImageKey) {
let item = SpecificDisplayItem::Image(ImageDisplayItem {
image_key: key,
stretch_size: stretch_size,
tile_spacing: tile_spacing,
image_rendering: image_rendering,
stretch_size,
tile_spacing,
image_rendering,
});
self.push_item(item, rect);
self.push_item(item, rect, local_clip);
}
/// Push a yuv image. All planar data in yuv image should use the same buffer type.
pub fn push_yuv_image(&mut self,
rect: LayoutRect,
_token: ClipRegionToken,
local_clip: Option<LocalClip>,
yuv_data: YuvData,
color_space: YuvColorSpace,
image_rendering: ImageRendering) {
let item = SpecificDisplayItem::YuvImage(YuvImageDisplayItem {
yuv_data: yuv_data,
color_space: color_space,
image_rendering: image_rendering,
yuv_data,
color_space,
image_rendering,
});
self.push_item(item, rect);
self.push_item(item, rect, local_clip);
}
pub fn push_webgl_canvas(&mut self,
rect: LayoutRect,
_token: ClipRegionToken,
local_clip: Option<LocalClip>,
context_id: WebGLContextId) {
let item = SpecificDisplayItem::WebGL(WebGLDisplayItem {
context_id: context_id,
context_id,
});
self.push_item(item, rect);
self.push_item(item, rect, local_clip);
}
pub fn push_text(&mut self,
rect: LayoutRect,
_token: ClipRegionToken,
local_clip: Option<LocalClip>,
glyphs: &[GlyphInstance],
font_key: FontKey,
color: ColorF,
@ -569,14 +553,14 @@ impl DisplayListBuilder {
// by the azure renderer.
if size < Au::from_px(4096) {
let item = SpecificDisplayItem::Text(TextDisplayItem {
color: color,
font_key: font_key,
size: size,
blur_radius: blur_radius,
glyph_options: glyph_options,
color,
font_key,
size,
blur_radius,
glyph_options,
});
self.push_item(item, rect);
self.push_item(item, rect, local_clip);
self.push_iter(glyphs);
}
}
@ -588,8 +572,7 @@ impl DisplayListBuilder {
// of the gradient line to where stop[0] and the end of the gradient line
// to stop[n-1]. this function adjusts the stops in place, and returns
// the amount to adjust the gradient line start and stop
fn normalize_stops(stops: &mut Vec<GradientStop>,
extend_mode: ExtendMode) -> (f32, f32) {
fn normalize_stops(stops: &mut Vec<GradientStop>, extend_mode: ExtendMode) -> (f32, f32) {
assert!(stops.len() >= 2);
let first = *stops.first().unwrap();
@ -675,7 +658,7 @@ impl DisplayListBuilder {
Gradient {
start_point: start_point + start_to_end * start_offset,
end_point: start_point + start_to_end * end_offset,
extend_mode: extend_mode,
extend_mode,
}
}
@ -711,7 +694,7 @@ impl DisplayListBuilder {
end_center: center,
end_radius: 1.0,
ratio_xy: 1.0,
extend_mode: extend_mode,
extend_mode,
};
}
@ -726,7 +709,7 @@ impl DisplayListBuilder {
end_center: center,
end_radius: radius.width * end_offset,
ratio_xy: radius.width / radius.height,
extend_mode: extend_mode,
extend_mode,
}
}
@ -744,31 +727,31 @@ impl DisplayListBuilder {
self.push_stops(&stops);
RadialGradient {
start_center: start_center,
start_radius: start_radius,
end_center: end_center,
end_radius: end_radius,
ratio_xy: ratio_xy,
extend_mode: extend_mode,
start_center,
start_radius,
end_center,
end_radius,
ratio_xy,
extend_mode,
}
}
pub fn push_border(&mut self,
rect: LayoutRect,
_token: ClipRegionToken,
local_clip: Option<LocalClip>,
widths: BorderWidths,
details: BorderDetails) {
let item = SpecificDisplayItem::Border(BorderDisplayItem {
details: details,
widths: widths,
details,
widths,
});
self.push_item(item, rect);
self.push_item(item, rect, local_clip);
}
pub fn push_box_shadow(&mut self,
rect: LayoutRect,
_token: ClipRegionToken,
local_clip: Option<LocalClip>,
box_bounds: LayoutRect,
offset: LayoutVector2D,
color: ColorF,
@ -777,46 +760,46 @@ impl DisplayListBuilder {
border_radius: f32,
clip_mode: BoxShadowClipMode) {
let item = SpecificDisplayItem::BoxShadow(BoxShadowDisplayItem {
box_bounds: box_bounds,
offset: offset,
color: color,
blur_radius: blur_radius,
spread_radius: spread_radius,
border_radius: border_radius,
clip_mode: clip_mode,
box_bounds,
offset,
color,
blur_radius,
spread_radius,
border_radius,
clip_mode,
});
self.push_item(item, rect);
self.push_item(item, rect, local_clip);
}
pub fn push_gradient(&mut self,
rect: LayoutRect,
_token: ClipRegionToken,
local_clip: Option<LocalClip>,
gradient: Gradient,
tile_size: LayoutSize,
tile_spacing: LayoutSize) {
let item = SpecificDisplayItem::Gradient(GradientDisplayItem {
gradient: gradient,
tile_size: tile_size,
tile_spacing: tile_spacing,
gradient,
tile_size,
tile_spacing,
});
self.push_item(item, rect);
self.push_item(item, rect, local_clip);
}
pub fn push_radial_gradient(&mut self,
rect: LayoutRect,
_token: ClipRegionToken,
local_clip: Option<LocalClip>,
gradient: RadialGradient,
tile_size: LayoutSize,
tile_spacing: LayoutSize) {
let item = SpecificDisplayItem::RadialGradient(RadialGradientDisplayItem {
gradient: gradient,
tile_size: tile_size,
tile_spacing: tile_spacing,
gradient,
tile_size,
tile_spacing,
});
self.push_item(item, rect);
self.push_item(item, rect, local_clip);
}
pub fn push_stacking_context(&mut self,
@ -829,15 +812,15 @@ impl DisplayListBuilder {
filters: Vec<FilterOp>) {
let item = SpecificDisplayItem::PushStackingContext(PushStackingContextDisplayItem {
stacking_context: StackingContext {
scroll_policy: scroll_policy,
transform: transform,
transform_style: transform_style,
perspective: perspective,
mix_blend_mode: mix_blend_mode,
scroll_policy,
transform,
transform_style,
perspective,
mix_blend_mode,
}
});
self.push_item(item, bounds);
self.push_item(item, bounds, None);
self.push_iter(&filters);
}
@ -853,34 +836,52 @@ impl DisplayListBuilder {
self.push_iter(stops);
}
pub fn define_clip(&mut self,
content_rect: LayoutRect,
_token: ClipRegionToken,
id: Option<ClipId>)
-> ClipId {
let id = match id {
Some(id) => id,
None => {
self.next_clip_id += 1;
ClipId::Clip(self.next_clip_id - 1, 0, self.pipeline_id)
}
};
fn generate_clip_id(&mut self, id: Option<ClipId>) -> ClipId {
id.unwrap_or_else(|| {
self.next_clip_id += 1;
ClipId::Clip(self.next_clip_id - 1, 0, self.pipeline_id)
})
}
let item = SpecificDisplayItem::Clip(ClipDisplayItem {
pub fn define_scroll_frame<I>(&mut self,
id: Option<ClipId>,
content_rect: LayoutRect,
clip_rect: LayoutRect,
complex_clips: I,
image_mask: Option<ImageMask>)
-> ClipId
where I: IntoIterator<Item = ComplexClipRegion>,
I::IntoIter: ExactSizeIterator {
let id = self.generate_clip_id(id);
let item = SpecificDisplayItem::ScrollFrame(ClipDisplayItem {
id: id,
parent_id: self.clip_stack.last().unwrap().scroll_node_id,
image_mask: image_mask,
});
self.push_item(item, content_rect);
self.push_item(item, content_rect, Some(LocalClip::from(clip_rect)));
self.push_iter(complex_clips);
id
}
pub fn push_clip_node(&mut self,
content_rect: LayoutRect,
token: ClipRegionToken,
id: Option<ClipId>){
let id = self.define_clip(content_rect, token, id);
self.clip_stack.push(ClipAndScrollInfo::simple(id));
pub fn define_clip<I>(&mut self,
id: Option<ClipId>,
clip_rect: LayoutRect,
complex_clips: I,
image_mask: Option<ImageMask>)
-> ClipId
where I: IntoIterator<Item = ComplexClipRegion>,
I::IntoIter: ExactSizeIterator {
let id = self.generate_clip_id(id);
let item = SpecificDisplayItem::Clip(ClipDisplayItem {
id,
parent_id: self.clip_stack.last().unwrap().scroll_node_id,
image_mask: image_mask,
});
self.push_item(item, clip_rect, Some(LocalClip::from(clip_rect)));
self.push_iter(complex_clips);
id
}
pub fn push_clip_id(&mut self, id: ClipId) {
@ -896,13 +897,9 @@ impl DisplayListBuilder {
assert!(self.clip_stack.len() > 0);
}
pub fn pop_clip_node(&mut self) {
self.pop_clip_id();
}
pub fn push_iframe(&mut self, rect: LayoutRect, _token: ClipRegionToken, pipeline_id: PipelineId) {
pub fn push_iframe(&mut self, rect: LayoutRect, pipeline_id: PipelineId) {
let item = SpecificDisplayItem::Iframe(IframeDisplayItem { pipeline_id: pipeline_id });
self.push_item(item, rect);
self.push_item(item, rect, None);
}
// Don't use this function. It will go away.
@ -912,31 +909,11 @@ impl DisplayListBuilder {
// will replace references to the root scroll frame id with the current scroll frame
// id.
pub fn push_nested_display_list(&mut self, built_display_list: &BuiltDisplayList) {
self.push_clip_region(&LayoutRect::zero(), vec![], None);
self.push_new_empty_item(SpecificDisplayItem::PushNestedDisplayList);
self.data.extend_from_slice(&built_display_list.data);
self.push_clip_region(&LayoutRect::zero(), vec![], None);
self.push_new_empty_item(SpecificDisplayItem::PopNestedDisplayList);
}
pub fn push_clip_region<I>(&mut self,
rect: &LayoutRect,
complex: I,
image_mask: Option<ImageMask>)
-> ClipRegionToken
where I: IntoIterator<Item = ComplexClipRegion>,
I::IntoIter: ExactSizeIterator,
{
self.push_new_empty_item(SpecificDisplayItem::SetClipRegion(
ClipRegion::new(rect, image_mask),
));
self.push_iter(complex);
ClipRegionToken { _unforgeable: () }
}
pub fn finalize(self) -> (PipelineId, LayoutSize, BuiltDisplayList) {
let end_time = precise_time_ns();
@ -951,7 +928,3 @@ impl DisplayListBuilder {
})
}
}
/// Verification that push_clip_region was called before
/// pushing an item that requires it.
pub struct ClipRegionToken { _unforgeable: () }

View File

@ -178,10 +178,10 @@ impl GlyphKey {
point: LayoutPoint,
render_mode: FontRenderMode) -> GlyphKey {
GlyphKey {
font_key: font_key,
size: size,
font_key,
size,
color: ColorU::from(color),
index: index,
index,
subpixel_point: SubpixelPoint::new(point, render_mode),
}
}

View File

@ -33,6 +33,7 @@ pub enum ExternalImageType {
ExternalBuffer,
}
#[repr(C)]
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct ExternalImageData {
pub id: ExternalImageId,
@ -77,12 +78,12 @@ pub struct ImageDescriptor {
impl ImageDescriptor {
pub fn new(width: u32, height: u32, format: ImageFormat, is_opaque: bool) -> Self {
ImageDescriptor {
width: width,
height: height,
format: format,
width,
height,
format,
stride: None,
offset: 0,
is_opaque: is_opaque,
is_opaque,
}
}

View File

@ -107,4 +107,3 @@ pub fn as_scroll_parent_rect(rect: &LayerRect) -> ScrollLayerRect {
pub fn as_scroll_parent_vector(vector: &LayerVector2D) -> ScrollLayerVector2D {
ScrollLayerVector2D::from_untyped(&vector.to_untyped())
}

View File

@ -129,6 +129,7 @@ pub enum WebGLCommand {
CreateVertexArray(MsgSender<Option<WebGLVertexArrayId>>),
DeleteVertexArray(WebGLVertexArrayId),
BindVertexArray(Option<WebGLVertexArrayId>),
FenceAndWaitSync,
}
#[cfg(feature = "nightly")]
@ -388,7 +389,8 @@ impl fmt::Debug for WebGLCommand {
GenerateMipmap(..) => "GenerateMipmap",
CreateVertexArray(..) => "CreateVertexArray",
DeleteVertexArray(..) => "DeleteVertexArray",
BindVertexArray(..) => "BindVertexArray"
BindVertexArray(..) => "BindVertexArray",
FenceAndWaitSync => "FenceAndWaitSync",
};
write!(f, "CanvasWebGLMsg::{}(..)", name)
@ -631,6 +633,8 @@ impl WebGLCommand {
ctx.gl().delete_vertex_arrays(&[id.get()]),
WebGLCommand::BindVertexArray(id) =>
ctx.gl().bind_vertex_array(id.map_or(0, WebGLVertexArrayId::get)),
WebGLCommand::FenceAndWaitSync =>
Self::fence_and_wait_sync(ctx.gl()),
}
// FIXME: Use debug_assertions once tests are run with them
@ -901,7 +905,7 @@ impl WebGLCommand {
shader_type: u32,
precision_type: u32,
chan: MsgSender<WebGLResult<(i32, i32, i32)>>) {
let result = match precision_type {
gl::LOW_FLOAT |
gl::MEDIUM_FLOAT |
@ -1040,4 +1044,13 @@ impl WebGLCommand {
gl.shader_source(shader_id.get(), &[source.as_bytes()]);
gl.compile_shader(shader_id.get());
}
fn fence_and_wait_sync(gl: &gl::Gl) {
// Call FenceSync and ClientWaitSync to ensure that textures are ready.
let sync = gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0);
// SYNC_FLUSH_COMMANDS_BIT is used to automatically generate a glFlush before blocking on the sync object.
gl.wait_sync(sync, gl::SYNC_FLUSH_COMMANDS_BIT, gl::TIMEOUT_IGNORED);
// Release GLsync object
gl.delete_sync(sync);
}
}