--- a/gfx/webrender/Cargo.toml
+++ b/gfx/webrender/Cargo.toml
@@ -17,17 +17,17 @@ replay = ["webrender_api/deserialize", "
[dependencies]
app_units = "0.6"
bincode = "0.9"
byteorder = "1.0"
euclid = "0.16"
fxhash = "0.2.1"
gleam = "0.4.20"
lazy_static = "1"
-log = "0.3"
+log = "0.4"
num-traits = "0.1.32"
time = "0.1"
rayon = "0.8"
webrender_api = {path = "../webrender_api"}
bitflags = "1.0"
thread_profiler = "0.1.1"
plane-split = "0.7"
png = { optional = true, version = "0.11" }
@@ -36,22 +36,22 @@ ws = { optional = true, version = "0.7.3
serde_json = { optional = true, version = "1.0" }
serde = { optional = true, version = "1.0", features = ["serde_derive"] }
image = { optional = true, version = "0.17" }
base64 = { optional = true, version = "0.3.0" }
ron = { optional = true, version = "0.1.7" }
[dev-dependencies]
angle = {git = "https://github.com/servo/angle", branch = "servo"}
-env_logger = "0.4"
+env_logger = "0.5"
rand = "0.3" # for the benchmarks
-servo-glutin = "0.14" # for the example apps
+glutin = "0.12" # for the example apps
[target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
freetype = { version = "0.3", default-features = false }
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.4.1"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.5"
core-graphics = "0.13"
-core-text = { version = "9.0", default-features = false }
+core-text = { version = "9.2.0", default-features = false }
--- a/gfx/webrender/examples/alpha_perf.rs
+++ b/gfx/webrender/examples/alpha_perf.rs
@@ -48,22 +48,29 @@ impl Example for App {
builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 0.05));
}
builder.pop_stacking_context();
}
fn on_event(
&mut self,
- event: glutin::Event,
+ event: glutin::WindowEvent,
_api: &RenderApi,
_document_id: DocumentId
) -> bool {
match event {
- glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
+ glutin::WindowEvent::KeyboardInput {
+ input: glutin::KeyboardInput {
+ state: glutin::ElementState::Pressed,
+ virtual_keycode: Some(key),
+ ..
+ },
+ ..
+ } => {
match key {
glutin::VirtualKeyCode::Right => {
self.rect_count += 1;
println!("rects = {}", self.rect_count);
}
glutin::VirtualKeyCode::Left => {
self.rect_count = cmp::max(self.rect_count, 1) - 1;
println!("rects = {}", self.rect_count);
--- a/gfx/webrender/examples/animation.rs
+++ b/gfx/webrender/examples/animation.rs
@@ -66,19 +66,26 @@ impl Example for App {
);
// Fill it with a white rect
builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
builder.pop_stacking_context();
}
- fn on_event(&mut self, event: glutin::Event, api: &RenderApi, document_id: DocumentId) -> bool {
- match event {
- glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
+ fn on_event(&mut self, win_event: glutin::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
+ match win_event {
+ glutin::WindowEvent::KeyboardInput {
+ input: glutin::KeyboardInput {
+ state: glutin::ElementState::Pressed,
+ virtual_keycode: Some(key),
+ ..
+ },
+ ..
+ } => {
let (offset_x, offset_y, angle, delta_opacity) = match key {
glutin::VirtualKeyCode::Down => (0.0, 10.0, 0.0, 0.0),
glutin::VirtualKeyCode::Up => (0.0, -10.0, 0.0, 0.0),
glutin::VirtualKeyCode::Right => (10.0, 0.0, 0.0, 0.0),
glutin::VirtualKeyCode::Left => (-10.0, 0.0, 0.0, 0.0),
glutin::VirtualKeyCode::Comma => (0.0, 0.0, 0.1, 0.0),
glutin::VirtualKeyCode::Period => (0.0, 0.0, -0.1, 0.0),
glutin::VirtualKeyCode::Z => (0.0, 0.0, 0.0, -0.1),
--- a/gfx/webrender/examples/basic.rs
+++ b/gfx/webrender/examples/basic.rs
@@ -268,20 +268,20 @@ impl Example for App {
BorderRadius::uniform(simple_border_radius),
box_shadow_type,
);
}
builder.pop_stacking_context();
}
- fn on_event(&mut self, event: glutin::Event, api: &RenderApi, document_id: DocumentId) -> bool {
+ fn on_event(&mut self, event: glutin::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
let mut txn = Transaction::new();
match event {
- glutin::Event::Touch(touch) => match self.touch_state.handle_event(touch) {
+ glutin::WindowEvent::Touch(touch) => match self.touch_state.handle_event(touch) {
TouchResult::Pan(pan) => {
txn.set_pan(pan);
}
TouchResult::Zoom(zoom) => {
txn.set_pinch_zoom(ZoomFactor::new(zoom));
}
TouchResult::None => {}
},
--- a/gfx/webrender/examples/common/boilerplate.rs
+++ b/gfx/webrender/examples/common/boilerplate.rs
@@ -1,42 +1,42 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate env_logger;
extern crate euclid;
use gleam::gl;
-use glutin;
+use glutin::{self, GlContext};
use std::env;
use std::path::PathBuf;
use webrender;
use webrender::api::*;
struct Notifier {
- window_proxy: glutin::WindowProxy,
+ events_proxy: glutin::EventsLoopProxy,
}
impl Notifier {
- fn new(window_proxy: glutin::WindowProxy) -> Notifier {
- Notifier { window_proxy }
+ fn new(events_proxy: glutin::EventsLoopProxy) -> Notifier {
+ Notifier { events_proxy }
}
}
impl RenderNotifier for Notifier {
fn clone(&self) -> Box<RenderNotifier> {
Box::new(Notifier {
- window_proxy: self.window_proxy.clone(),
+ events_proxy: self.events_proxy.clone(),
})
}
fn wake_up(&self) {
#[cfg(not(target_os = "android"))]
- self.window_proxy.wakeup_event_loop();
+ let _ = self.events_proxy.wakeup();
}
fn new_document_ready(&self, _: DocumentId, _scrolled: bool, _composite_needed: bool) {
self.wake_up();
}
}
pub trait HandyDandyRectBuilder {
@@ -71,17 +71,17 @@ pub trait Example {
&mut self,
api: &RenderApi,
builder: &mut DisplayListBuilder,
resources: &mut ResourceUpdates,
framebuffer_size: DeviceUintSize,
pipeline_id: PipelineId,
document_id: DocumentId,
);
- fn on_event(&mut self, glutin::Event, &RenderApi, DocumentId) -> bool {
+ fn on_event(&mut self, glutin::WindowEvent, &RenderApi, DocumentId) -> bool {
false
}
fn get_image_handlers(
&mut self,
_gl: &gl::Gl,
) -> (Option<Box<webrender::ExternalImageHandler>>,
Option<Box<webrender::OutputImageHandler>>) {
(None, None)
@@ -89,34 +89,36 @@ pub trait Example {
fn draw_custom(&self, _gl: &gl::Gl) {
}
}
pub fn main_wrapper<E: Example>(
example: &mut E,
options: Option<webrender::RendererOptions>,
) {
- env_logger::init().unwrap();
+ env_logger::init();
let args: Vec<String> = env::args().collect();
let res_path = if args.len() > 1 {
Some(PathBuf::from(&args[1]))
} else {
None
};
- let window = glutin::WindowBuilder::new()
- .with_title(E::TITLE)
- .with_multitouch()
- .with_dimensions(E::WIDTH, E::HEIGHT)
+ let mut events_loop = glutin::EventsLoop::new();
+ let context_builder = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::GlThenGles {
opengl_version: (3, 2),
opengles_version: (3, 0),
- })
- .build()
+ });
+ let window_builder = glutin::WindowBuilder::new()
+ .with_title(E::TITLE)
+ .with_multitouch()
+ .with_dimensions(E::WIDTH, E::HEIGHT);
+ let window = glutin::GlWindow::new(window_builder, context_builder, &events_loop)
.unwrap();
unsafe {
window.make_current().ok();
}
let gl = match window.get_api() {
glutin::Api::OpenGl => unsafe {
@@ -140,20 +142,20 @@ pub fn main_wrapper<E: Example>(
precache_shaders: E::PRECACHE_SHADERS,
device_pixel_ratio,
clear_color: Some(ColorF::new(0.3, 0.0, 0.0, 1.0)),
//scatter_gpu_cache_updates: false,
..options.unwrap_or(webrender::RendererOptions::default())
};
let framebuffer_size = {
- let (width, height) = window.get_inner_size_pixels().unwrap();
+ let (width, height) = window.get_inner_size().unwrap();
DeviceUintSize::new(width, height)
};
- let notifier = Box::new(Notifier::new(window.create_window_proxy()));
+ let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
let (mut renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts).unwrap();
let api = sender.create_api();
let document_id = api.add_document(framebuffer_size, 0);
let (external, output) = example.get_image_handlers(&*gl);
if let Some(output_image_handler) = output {
renderer.set_output_image_handler(output_image_handler);
@@ -186,137 +188,97 @@ pub fn main_wrapper<E: Example>(
true,
);
txn.update_resources(resources);
txn.set_root_pipeline(pipeline_id);
txn.generate_frame();
api.send_transaction(document_id, txn);
println!("Entering event loop");
- 'outer: for event in window.wait_events() {
- let mut events = Vec::new();
- events.push(event);
- events.extend(window.poll_events());
-
+ events_loop.run_forever(|global_event| {
let mut txn = Transaction::new();
- for event in events {
- match event {
- glutin::Event::Closed |
- glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) => break 'outer,
+ let mut custom_event = true;
- glutin::Event::KeyboardInput(
- glutin::ElementState::Pressed,
- _,
- Some(glutin::VirtualKeyCode::P),
- ) => {
- renderer.toggle_debug_flags(webrender::DebugFlags::PROFILER_DBG);
- }
- glutin::Event::KeyboardInput(
- glutin::ElementState::Pressed,
- _,
- Some(glutin::VirtualKeyCode::O),
- ) => {
- renderer.toggle_debug_flags(webrender::DebugFlags::RENDER_TARGET_DBG);
- }
- glutin::Event::KeyboardInput(
- glutin::ElementState::Pressed,
- _,
- Some(glutin::VirtualKeyCode::I),
- ) => {
- renderer.toggle_debug_flags(webrender::DebugFlags::TEXTURE_CACHE_DBG);
- }
- glutin::Event::KeyboardInput(
- glutin::ElementState::Pressed,
- _,
- Some(glutin::VirtualKeyCode::B),
- ) => {
- renderer.toggle_debug_flags(webrender::DebugFlags::ALPHA_PRIM_DBG);
- }
- glutin::Event::KeyboardInput(
- glutin::ElementState::Pressed,
- _,
- Some(glutin::VirtualKeyCode::S),
- ) => {
- renderer.toggle_debug_flags(webrender::DebugFlags::COMPACT_PROFILER);
- }
- glutin::Event::KeyboardInput(
- glutin::ElementState::Pressed,
- _,
- Some(glutin::VirtualKeyCode::Q),
- ) => {
- renderer.toggle_debug_flags(webrender::DebugFlags::GPU_TIME_QUERIES
- | webrender::DebugFlags::GPU_SAMPLE_QUERIES);
- }
- glutin::Event::KeyboardInput(
- glutin::ElementState::Pressed,
- _,
- Some(glutin::VirtualKeyCode::Key1),
- ) => {
- txn.set_window_parameters(
- framebuffer_size,
- DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size),
- 1.0
- );
- }
- glutin::Event::KeyboardInput(
- glutin::ElementState::Pressed,
- _,
- Some(glutin::VirtualKeyCode::Key2),
- ) => {
- txn.set_window_parameters(
- framebuffer_size,
- DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size),
- 2.0
- );
- }
- glutin::Event::KeyboardInput(
- glutin::ElementState::Pressed,
- _,
- Some(glutin::VirtualKeyCode::M),
- ) => {
- api.notify_memory_pressure();
- }
+ match global_event {
+ glutin::Event::WindowEvent { event: glutin::WindowEvent::Closed, .. } => return glutin::ControlFlow::Break,
+ glutin::Event::WindowEvent {
+ event: glutin::WindowEvent::KeyboardInput {
+ input: glutin::KeyboardInput {
+ state: glutin::ElementState::Pressed,
+ virtual_keycode: Some(key),
+ ..
+ },
+ ..
+ },
+ ..
+ } => match key {
+ glutin::VirtualKeyCode::Escape => return glutin::ControlFlow::Break,
+ glutin::VirtualKeyCode::P => renderer.toggle_debug_flags(webrender::DebugFlags::PROFILER_DBG),
+ glutin::VirtualKeyCode::O => renderer.toggle_debug_flags(webrender::DebugFlags::RENDER_TARGET_DBG),
+ glutin::VirtualKeyCode::I => renderer.toggle_debug_flags(webrender::DebugFlags::TEXTURE_CACHE_DBG),
+ glutin::VirtualKeyCode::S => renderer.toggle_debug_flags(webrender::DebugFlags::COMPACT_PROFILER),
+ glutin::VirtualKeyCode::Q => renderer.toggle_debug_flags(webrender::DebugFlags::GPU_TIME_QUERIES | webrender::DebugFlags::GPU_SAMPLE_QUERIES),
+ glutin::VirtualKeyCode::Key1 => txn.set_window_parameters(
+ framebuffer_size,
+ DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size),
+ 1.0
+ ),
+ glutin::VirtualKeyCode::Key2 => txn.set_window_parameters(
+ framebuffer_size,
+ DeviceUintRect::new(DeviceUintPoint::zero(), framebuffer_size),
+ 2.0
+ ),
+ glutin::VirtualKeyCode::M => api.notify_memory_pressure(),
#[cfg(feature = "capture")]
- glutin::Event::KeyboardInput(
- glutin::ElementState::Pressed,
- _,
- Some(glutin::VirtualKeyCode::C),
- ) => {
+ glutin::VirtualKeyCode::C => {
let path: PathBuf = "../captures/example".into();
//TODO: switch between SCENE/FRAME capture types
// based on "shift" modifier, when `glutin` is updated.
let bits = CaptureBits::all();
api.save_capture(path, bits);
- }
- _ => if example.on_event(event, &api, document_id) {
- let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
- let mut resources = ResourceUpdates::new();
+ },
+ _ => {
+ let win_event = match global_event {
+ glutin::Event::WindowEvent { event, .. } => event,
+ _ => unreachable!()
+ };
+ custom_event = example.on_event(win_event, &api, document_id)
+ },
+ },
+ glutin::Event::WindowEvent { event, .. } => custom_event = example.on_event(event, &api, document_id),
+ _ => return glutin::ControlFlow::Continue,
+ };
+
+ if custom_event {
+ let mut builder = DisplayListBuilder::new(pipeline_id, layout_size);
+ let mut resources = ResourceUpdates::new();
- example.render(
- &api,
- &mut builder,
- &mut resources,
- framebuffer_size,
- pipeline_id,
- document_id,
- );
- txn.set_display_list(
- epoch,
- None,
- layout_size,
- builder.finalize(),
- true,
- );
- txn.update_resources(resources);
- txn.generate_frame();
- }
- }
+ example.render(
+ &api,
+ &mut builder,
+ &mut resources,
+ framebuffer_size,
+ pipeline_id,
+ document_id,
+ );
+ txn.set_display_list(
+ epoch,
+ None,
+ layout_size,
+ builder.finalize(),
+ true,
+ );
+ txn.update_resources(resources);
+ txn.generate_frame();
}
api.send_transaction(document_id, txn);
renderer.update();
renderer.render(framebuffer_size).unwrap();
+ let _ = renderer.flush_pipeline_info();
example.draw_custom(&*gl);
window.swap_buffers().ok();
- }
+
+ glutin::ControlFlow::Continue
+ });
renderer.deinit();
}
--- a/gfx/webrender/examples/image_resize.rs
+++ b/gfx/webrender/examples/image_resize.rs
@@ -74,44 +74,46 @@ impl Example for App {
ImageRendering::Pixelated,
AlphaType::PremultipliedAlpha,
self.image_key,
);
builder.pop_stacking_context();
}
- fn on_event(&mut self, event: glutin::Event, api: &RenderApi, document_id: DocumentId) -> bool {
+ fn on_event(&mut self, event: glutin::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
match event {
- glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
- match key {
- glutin::VirtualKeyCode::Space => {
- let mut image_data = Vec::new();
- for y in 0 .. 64 {
- for x in 0 .. 64 {
- let r = 255 * ((y & 32) == 0) as u8;
- let g = 255 * ((x & 32) == 0) as u8;
- image_data.extend_from_slice(&[0, g, r, 0xff]);
- }
- }
+ glutin::WindowEvent::KeyboardInput {
+ input: glutin::KeyboardInput {
+ state: glutin::ElementState::Pressed,
+ virtual_keycode: Some(glutin::VirtualKeyCode::Space),
+ ..
+ },
+ ..
+ } => {
+ let mut image_data = Vec::new();
+ for y in 0 .. 64 {
+ for x in 0 .. 64 {
+ let r = 255 * ((y & 32) == 0) as u8;
+ let g = 255 * ((x & 32) == 0) as u8;
+ image_data.extend_from_slice(&[0, g, r, 0xff]);
+ }
+ }
- let mut updates = ResourceUpdates::new();
- updates.update_image(
- self.image_key,
- ImageDescriptor::new(64, 64, ImageFormat::BGRA8, true),
- ImageData::new(image_data),
- None,
- );
- let mut txn = Transaction::new();
- txn.update_resources(updates);
- txn.generate_frame();
- api.send_transaction(document_id, txn);
- }
- _ => {}
- }
+ let mut updates = ResourceUpdates::new();
+ updates.update_image(
+ self.image_key,
+ ImageDescriptor::new(64, 64, ImageFormat::BGRA8, true),
+ ImageData::new(image_data),
+ None,
+ );
+ let mut txn = Transaction::new();
+ txn.update_resources(updates);
+ txn.generate_frame();
+ api.send_transaction(document_id, txn);
}
_ => {}
}
false
}
}
--- a/gfx/webrender/examples/multiwindow.rs
+++ b/gfx/webrender/examples/multiwindow.rs
@@ -3,71 +3,75 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate app_units;
extern crate euclid;
extern crate gleam;
extern crate glutin;
extern crate webrender;
-use app_units::Au;
use std::fs::File;
use std::io::Read;
use webrender::api::*;
+use app_units::Au;
use gleam::gl;
+use glutin::GlContext;
struct Notifier {
- window_proxy: glutin::WindowProxy,
+ events_proxy: glutin::EventsLoopProxy,
}
impl Notifier {
- fn new(window_proxy: glutin::WindowProxy) -> Notifier {
- Notifier { window_proxy }
+ fn new(events_proxy: glutin::EventsLoopProxy) -> Notifier {
+ Notifier { events_proxy }
}
}
impl RenderNotifier for Notifier {
fn clone(&self) -> Box<RenderNotifier> {
Box::new(Notifier {
- window_proxy: self.window_proxy.clone(),
+ events_proxy: self.events_proxy.clone(),
})
}
fn wake_up(&self) {
#[cfg(not(target_os = "android"))]
- self.window_proxy.wakeup_event_loop();
+ let _ = self.events_proxy.wakeup();
}
fn new_document_ready(&self, _: DocumentId, _scrolled: bool, _composite_needed: bool) {
self.wake_up();
}
}
struct Window {
- window: glutin::Window,
+ events_loop: glutin::EventsLoop, //TODO: share events loop?
+ window: glutin::GlWindow,
renderer: webrender::Renderer,
name: &'static str,
pipeline_id: PipelineId,
document_id: DocumentId,
epoch: Epoch,
api: RenderApi,
font_instance_key: FontInstanceKey,
}
impl Window {
- fn new(name: &'static str, clear_color: ColorF) -> Window {
- let window = glutin::WindowBuilder::new()
- .with_title(name)
- .with_multitouch()
- .with_dimensions(800, 600)
+ fn new(name: &'static str, clear_color: ColorF) -> Self {
+ let events_loop = glutin::EventsLoop::new();
+ let context_builder = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::GlThenGles {
opengl_version: (3, 2),
opengles_version: (3, 0),
- })
- .build()
+ });
+ let window_builder = glutin::WindowBuilder::new()
+ .with_title(name)
+ .with_multitouch()
+ .with_dimensions(800, 600);
+ let window = glutin::GlWindow::new(window_builder, context_builder, &events_loop)
.unwrap();
unsafe {
window.make_current().ok();
}
let gl = match window.get_api() {
glutin::Api::OpenGl => unsafe {
@@ -84,20 +88,20 @@ impl Window {
let opts = webrender::RendererOptions {
debug: true,
device_pixel_ratio,
clear_color: Some(clear_color),
..webrender::RendererOptions::default()
};
let framebuffer_size = {
- let (width, height) = window.get_inner_size_pixels().unwrap();
+ let (width, height) = window.get_inner_size().unwrap();
DeviceUintSize::new(width, height)
};
- let notifier = Box::new(Notifier::new(window.create_window_proxy()));
+ let notifier = Box::new(Notifier::new(events_loop.create_proxy()));
let (renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts).unwrap();
let api = sender.create_api();
let document_id = api.add_document(framebuffer_size, 0);
let epoch = Epoch(0);
let pipeline_id = PipelineId(0, 0);
let mut resources = ResourceUpdates::new();
@@ -108,52 +112,69 @@ impl Window {
let font_instance_key = api.generate_font_instance_key();
resources.add_font_instance(font_instance_key, font_key, Au::from_px(32), None, None, Vec::new());
let mut txn = Transaction::new();
txn.update_resources(resources);
api.send_transaction(document_id, txn);
Window {
+ events_loop,
window,
renderer,
name,
epoch,
pipeline_id,
document_id,
api,
font_instance_key,
}
}
fn tick(&mut self) -> bool {
unsafe {
self.window.make_current().ok();
}
-
- for event in self.window.poll_events() {
- match event {
- glutin::Event::Closed |
- glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) => return true,
+ let mut do_exit = false;
+ let my_name = &self.name;
+ let renderer = &mut self.renderer;
- glutin::Event::KeyboardInput(
- glutin::ElementState::Pressed,
- _,
- Some(glutin::VirtualKeyCode::P),
- ) => {
- println!("toggle flags {}", self.name);
- self.renderer.toggle_debug_flags(webrender::DebugFlags::PROFILER_DBG);
+ self.events_loop.poll_events(|global_event| match global_event {
+ glutin::Event::WindowEvent { event, .. } => match event {
+ glutin::WindowEvent::Closed |
+ glutin::WindowEvent::KeyboardInput {
+ input: glutin::KeyboardInput {
+ virtual_keycode: Some(glutin::VirtualKeyCode::Escape),
+ ..
+ },
+ ..
+ } => {
+ do_exit = true
}
-
+ glutin::WindowEvent::KeyboardInput {
+ input: glutin::KeyboardInput {
+ state: glutin::ElementState::Pressed,
+ virtual_keycode: Some(glutin::VirtualKeyCode::P),
+ ..
+ },
+ ..
+ } => {
+ println!("toggle flags {}", my_name);
+ renderer.toggle_debug_flags(webrender::DebugFlags::PROFILER_DBG);
+ }
_ => {}
}
+ _ => {}
+ });
+ if do_exit {
+ return true
}
let framebuffer_size = {
- let (width, height) = self.window.get_inner_size_pixels().unwrap();
+ let (width, height) = self.window.get_inner_size().unwrap();
DeviceUintSize::new(width, height)
};
let device_pixel_ratio = self.window.hidpi_factor();
let layout_size = framebuffer_size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
let mut txn = Transaction::new();
let mut builder = DisplayListBuilder::new(self.pipeline_id, layout_size);
let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size());
@@ -240,18 +261,18 @@ impl Window {
layout_size,
builder.finalize(),
true,
);
txn.set_root_pipeline(self.pipeline_id);
txn.generate_frame();
self.api.send_transaction(self.document_id, txn);
- self.renderer.update();
- self.renderer.render(framebuffer_size).unwrap();
+ renderer.update();
+ renderer.render(framebuffer_size).unwrap();
self.window.swap_buffers().ok();
false
}
fn deinit(self) {
self.renderer.deinit();
}
--- a/gfx/webrender/examples/scrolling.rs
+++ b/gfx/webrender/examples/scrolling.rs
@@ -40,19 +40,18 @@ impl Example for App {
MixBlendMode::Normal,
Vec::new(),
);
if true {
// scrolling and clips stuff
// let's make a scrollbox
let scrollbox = (0, 0).to(300, 400);
- let info = LayoutPrimitiveInfo::new((10, 10).by(0, 0));
builder.push_stacking_context(
- &info,
+ &LayoutPrimitiveInfo::new((10, 10).by(0, 0)),
ScrollPolicy::Scrollable,
None,
TransformStyle::Flat,
None,
MixBlendMode::Normal,
Vec::new(),
);
// set the scrolling clip
@@ -63,27 +62,30 @@ impl Example for App {
vec![],
None,
ScrollSensitivity::ScriptAndInputEvents,
);
builder.push_clip_id(clip_id);
// now put some content into it.
// start with a white background
- let info = LayoutPrimitiveInfo::new((0, 0).to(1000, 1000));
+ let mut info = LayoutPrimitiveInfo::new((0, 0).to(1000, 1000));
+ info.tag = Some((0, 1));
builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
// let's make a 50x50 blue square as a visual reference
- let info = LayoutPrimitiveInfo::new((0, 0).to(50, 50));
+ let mut info = LayoutPrimitiveInfo::new((0, 0).to(50, 50));
+ info.tag = Some((0, 2));
builder.push_rect(&info, ColorF::new(0.0, 0.0, 1.0, 1.0));
// and a 50x50 green square next to it with an offset clip
// to see what that looks like
- let info =
+ let mut info =
LayoutPrimitiveInfo::with_clip_rect((50, 0).to(100, 50), (60, 10).to(110, 60));
+ info.tag = Some((0, 3));
builder.push_rect(&info, ColorF::new(0.0, 1.0, 0.0, 1.0));
// Below the above rectangles, set up a nested scrollbox. It's still in
// the same stacking context, so note that the rects passed in need to
// be relative to the stacking context.
let nested_clip_id = builder.define_scroll_frame(
None,
(0, 100).to(300, 1000),
@@ -91,93 +93,114 @@ impl Example for App {
vec![],
None,
ScrollSensitivity::ScriptAndInputEvents,
);
builder.push_clip_id(nested_clip_id);
// give it a giant gray background just to distinguish it and to easily
// visually identify the nested scrollbox
- let info = LayoutPrimitiveInfo::new((-1000, -1000).to(5000, 5000));
+ let mut info = LayoutPrimitiveInfo::new((-1000, -1000).to(5000, 5000));
+ info.tag = Some((0, 4));
builder.push_rect(&info, ColorF::new(0.5, 0.5, 0.5, 1.0));
// add a teal square to visualize the scrolling/clipping behaviour
// as you scroll the nested scrollbox
- let info = LayoutPrimitiveInfo::new((0, 200).to(50, 250));
+ let mut info = LayoutPrimitiveInfo::new((0, 200).to(50, 250));
+ info.tag = Some((0, 5));
builder.push_rect(&info, ColorF::new(0.0, 1.0, 1.0, 1.0));
// Add a sticky frame. It will "stick" twice while scrolling, once
// at a margin of 10px from the bottom, for 40 pixels of scrolling,
// and once at a margin of 10px from the top, for 60 pixels of
// scrolling.
let sticky_id = builder.define_sticky_frame(
(50, 350).by(50, 50),
SideOffsets2D::new(Some(10.0), None, Some(10.0), None),
StickyOffsetBounds::new(-40.0, 60.0),
StickyOffsetBounds::new(0.0, 0.0),
LayoutVector2D::new(0.0, 0.0)
);
builder.push_clip_id(sticky_id);
- let info = LayoutPrimitiveInfo::new((50, 350).by(50, 50));
+ let mut info = LayoutPrimitiveInfo::new((50, 350).by(50, 50));
+ info.tag = Some((0, 6));
builder.push_rect(&info, ColorF::new(0.5, 0.5, 1.0, 1.0));
builder.pop_clip_id(); // sticky_id
// just for good measure add another teal square further down and to
// the right, which can be scrolled into view by the user
- let info = LayoutPrimitiveInfo::new((250, 350).to(300, 400));
+ let mut info = LayoutPrimitiveInfo::new((250, 350).to(300, 400));
+ info.tag = Some((0, 7));
builder.push_rect(&info, ColorF::new(0.0, 1.0, 1.0, 1.0));
builder.pop_clip_id(); // nested_clip_id
builder.pop_clip_id(); // clip_id
builder.pop_stacking_context();
}
builder.pop_stacking_context();
}
- fn on_event(&mut self, event: glutin::Event, api: &RenderApi, document_id: DocumentId) -> bool {
+ fn on_event(&mut self, event: glutin::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
let mut txn = Transaction::new();
match event {
- glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
+ glutin::WindowEvent::KeyboardInput {
+ input: glutin::KeyboardInput {
+ state: glutin::ElementState::Pressed,
+ virtual_keycode: Some(key),
+ ..
+ },
+ ..
+ } => {
let offset = match key {
glutin::VirtualKeyCode::Down => (0.0, -10.0),
glutin::VirtualKeyCode::Up => (0.0, 10.0),
glutin::VirtualKeyCode::Right => (-10.0, 0.0),
glutin::VirtualKeyCode::Left => (10.0, 0.0),
_ => return false,
};
txn.scroll(
ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)),
self.cursor_position,
ScrollEventPhase::Start,
);
}
- glutin::Event::MouseMoved(x, y) => {
+ glutin::WindowEvent::CursorMoved { position: (x, y), .. } => {
self.cursor_position = WorldPoint::new(x as f32, y as f32);
}
- glutin::Event::MouseWheel(delta, _, event_cursor_position) => {
- if let Some((x, y)) = event_cursor_position {
- self.cursor_position = WorldPoint::new(x as f32, y as f32);
- }
-
+ glutin::WindowEvent::MouseWheel { delta, .. } => {
const LINE_HEIGHT: f32 = 38.0;
let (dx, dy) = match delta {
glutin::MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
glutin::MouseScrollDelta::PixelDelta(dx, dy) => (dx, dy),
};
txn.scroll(
ScrollLocation::Delta(LayoutVector2D::new(dx, dy)),
self.cursor_position,
ScrollEventPhase::Start,
);
}
+ glutin::WindowEvent::MouseInput { .. } => {
+ let results = api.hit_test(
+ document_id,
+ None,
+ self.cursor_position,
+ HitTestFlags::FIND_ALL
+ );
+
+ println!("Hit test results:");
+ for item in &results.items {
+ println!(" • {:?}", item);
+ }
+ println!("");
+ }
_ => (),
}
api.send_transaction(document_id, txn);
false
}
}
--- a/gfx/webrender/examples/texture_cache_stress.rs
+++ b/gfx/webrender/examples/texture_cache_stress.rs
@@ -181,22 +181,29 @@ impl Example for App {
);
self.swap_index = 1 - self.swap_index;
builder.pop_stacking_context();
}
fn on_event(
&mut self,
- event: glutin::Event,
+ event: glutin::WindowEvent,
api: &RenderApi,
_document_id: DocumentId,
) -> bool {
match event {
- glutin::Event::KeyboardInput(glutin::ElementState::Pressed, _, Some(key)) => {
+ glutin::WindowEvent::KeyboardInput {
+ input: glutin::KeyboardInput {
+ state: glutin::ElementState::Pressed,
+ virtual_keycode: Some(key),
+ ..
+ },
+ ..
+ } => {
let mut updates = ResourceUpdates::new();
match key {
glutin::VirtualKeyCode::S => {
self.stress_keys.clear();
for _ in 0 .. 16 {
for _ in 0 .. 16 {
--- a/gfx/webrender/examples/yuv.rs
+++ b/gfx/webrender/examples/yuv.rs
@@ -235,20 +235,20 @@ impl Example for App {
YuvData::PlanarYCbCr(yuv_chanel1, yuv_chanel2_1, yuv_chanel3),
YuvColorSpace::Rec601,
ImageRendering::Auto,
);
builder.pop_stacking_context();
}
- fn on_event(&mut self, event: glutin::Event, api: &RenderApi, document_id: DocumentId) -> bool {
+ fn on_event(&mut self, event: glutin::WindowEvent, api: &RenderApi, document_id: DocumentId) -> bool {
let mut txn = Transaction::new();
match event {
- glutin::Event::Touch(touch) => match self.touch_state.handle_event(touch) {
+ glutin::WindowEvent::Touch(touch) => match self.touch_state.handle_event(touch) {
TouchResult::Pan(pan) => {
txn.set_pan(pan);
}
TouchResult::Zoom(zoom) => {
txn.set_pinch_zoom(ZoomFactor::new(zoom));
}
TouchResult::None => {}
},
--- a/gfx/webrender/res/brush_line.glsl
+++ b/gfx/webrender/res/brush_line.glsl
@@ -35,44 +35,45 @@ void brush_vs(
int prim_address,
vec2 local_pos,
RectWithSize local_rect,
ivec2 user_data,
PictureTask pic_task
) {
vLocalPos = local_pos;
- Line line = fetch_line(prim_address);
+ // Note: `line` name is reserved in HLSL
+ Line line_prim = fetch_line(prim_address);
switch (int(abs(pic_task.pic_kind_and_raster_mode))) {
case PIC_TYPE_TEXT_SHADOW:
vColor = pic_task.color;
break;
default:
- vColor = line.color;
+ vColor = line_prim.color;
break;
}
vec2 pos, size;
- switch (int(line.orientation)) {
+ switch (int(line_prim.orientation)) {
case LINE_ORIENTATION_HORIZONTAL:
vAxisSelect = 0.0;
pos = local_rect.p0;
size = local_rect.size;
break;
case LINE_ORIENTATION_VERTICAL:
vAxisSelect = 1.0;
pos = local_rect.p0.yx;
size = local_rect.size.yx;
break;
}
vLocalOrigin = pos;
- vStyle = int(line.style);
+ vStyle = int(line_prim.style);
switch (vStyle) {
case LINE_STYLE_SOLID: {
break;
}
case LINE_STYLE_DASHED: {
float dash_length = size.y * 3.0;
vParams = vec4(2.0 * dash_length, // period
@@ -89,17 +90,17 @@ void brush_vs(
vParams = vec4(period,
diameter / 2.0, // radius
center_line,
max_x);
break;
}
case LINE_STYLE_WAVY: {
// This logic copied from gecko to get the same results
- float line_thickness = max(line.wavyLineThickness, 1.0);
+ float line_thickness = max(line_prim.wavyLineThickness, 1.0);
// Difference in height between peaks and troughs
// (and since slopes are 45 degrees, the length of each slope)
float slope_length = size.y - line_thickness;
// Length of flat runs
float flat_length = max((line_thickness - 1.0) * 2.0, 1.0);
vParams = vec4(line_thickness / 2.0,
slope_length,
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -19,18 +19,18 @@
uniform sampler2DArray sCacheA8;
uniform sampler2DArray sCacheRGBA8;
// An A8 target for standalone tasks that is available to all passes.
uniform sampler2DArray sSharedCacheA8;
uniform sampler2D sGradients;
-vec2 clamp_rect(vec2 point, RectWithSize rect) {
- return clamp(point, rect.p0, rect.p0 + rect.size);
+vec2 clamp_rect(vec2 pt, RectWithSize rect) {
+ return clamp(pt, rect.p0, rect.p0 + rect.size);
}
float distance_to_line(vec2 p0, vec2 perp_dir, vec2 p) {
vec2 dir_to_p0 = p0 - p;
return dot(normalize(perp_dir), dir_to_p0);
}
// TODO: convert back to RectWithEndPoint if driver issues are resolved, if ever.
@@ -476,21 +476,21 @@ Primitive load_primitive() {
prim.z = float(pi.z);
return prim;
}
// Return the intersection of the plane (set up by "normal" and "point")
// with the ray (set up by "ray_origin" and "ray_dir"),
// writing the resulting scaler into "t".
-bool ray_plane(vec3 normal, vec3 point, vec3 ray_origin, vec3 ray_dir, out float t)
+bool ray_plane(vec3 normal, vec3 pt, vec3 ray_origin, vec3 ray_dir, out float t)
{
float denom = dot(normal, ray_dir);
if (abs(denom) > 1e-6) {
- vec3 d = point - ray_origin;
+ vec3 d = pt - ray_origin;
t = dot(d, normal) / denom;
return t >= 0.0;
}
return false;
}
// Apply the inverse transform "inv_transform"
--- a/gfx/webrender/src/batch.rs
+++ b/gfx/webrender/src/batch.rs
@@ -1,36 +1,36 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use api::{AlphaType, DeviceIntRect, DeviceIntSize, ImageKey, LayerToWorldScale};
+use api::{AlphaType, DeviceIntRect, DeviceIntSize, LayerToWorldScale};
use api::{DeviceUintRect, DeviceUintPoint, DeviceUintSize, ExternalImageType, FilterOp, ImageRendering, LayerRect};
-use api::{SubpixelDirection, TileOffset, YuvColorSpace, YuvFormat};
+use api::{DeviceIntPoint, LayerPoint, SubpixelDirection, YuvColorSpace, YuvFormat};
use api::{LayerToWorldTransform, WorldPixel};
use border::{BorderCornerInstance, BorderCornerSide, BorderEdgeKind};
use clip::{ClipSource, ClipStore};
use clip_scroll_tree::{CoordinateSystemId};
use euclid::{TypedTransform3D, vec3};
use glyph_rasterizer::GlyphFormat;
use gpu_cache::{GpuCache, GpuCacheAddress};
use gpu_types::{BrushImageKind, BrushInstance, ClipChainRectIndex};
-use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex, PictureType};
+use gpu_types::{ClipMaskInstance, ClipScrollNodeIndex};
use gpu_types::{CompositePrimitiveInstance, PrimitiveInstance, SimplePrimitiveInstance};
use internal_types::{FastHashMap, SourceTexture};
-use picture::{PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface};
+use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface};
use plane_split::{BspSplitter, Polygon, Splitter};
use prim_store::{ImageSource, PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
use prim_store::{BrushPrimitive, BrushKind, DeferredResolve, EdgeAaSegmentMask, PrimitiveRun};
use render_task::{ClipWorkItem};
-use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKind};
-use render_task::{RenderTaskTree};
+use render_task::{RenderTaskAddress, RenderTaskId};
+use render_task::{RenderTaskKind, RenderTaskTree};
use renderer::{BlendMode, ImageBufferKind};
use renderer::BLOCKS_PER_UV_RECT;
-use resource_cache::{CacheItem, GlyphFetchResult, ResourceCache};
+use resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache};
use std::{usize, f32, i32};
use tiling::{RenderTargetContext, RenderTargetKind};
use util::{MatrixHelpers, TransformedRectKind};
// Special sentinel value recognized by the shader. It is considered to be
// a dummy task that doesn't mask out anything.
const OPAQUE_TASK_ADDRESS: RenderTaskAddress = RenderTaskAddress(i32::MAX as u32);
@@ -119,52 +119,16 @@ impl BatchTextures {
pub fn color(texture: SourceTexture) -> Self {
BatchTextures {
colors: [texture, texture, SourceTexture::Invalid],
}
}
}
-#[derive(Debug)]
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct AlphaPrimitiveBatch {
- pub key: BatchKey,
- pub instances: Vec<PrimitiveInstance>,
- pub item_rects: Vec<DeviceIntRect>,
-}
-
-impl AlphaPrimitiveBatch {
- pub fn new(key: BatchKey) -> AlphaPrimitiveBatch {
- AlphaPrimitiveBatch {
- key,
- instances: Vec::new(),
- item_rects: Vec::new(),
- }
- }
-}
-
-#[derive(Debug)]
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct OpaquePrimitiveBatch {
- pub key: BatchKey,
- pub instances: Vec<PrimitiveInstance>,
-}
-
-impl OpaquePrimitiveBatch {
- pub fn new(key: BatchKey) -> OpaquePrimitiveBatch {
- OpaquePrimitiveBatch {
- key,
- instances: Vec::new(),
- }
- }
-}
-
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BatchKey {
pub kind: BatchKind,
pub blend_mode: BlendMode,
pub textures: BatchTextures,
}
@@ -186,51 +150,51 @@ impl BatchKey {
}
}
#[inline]
fn textures_compatible(t1: SourceTexture, t2: SourceTexture) -> bool {
t1 == SourceTexture::Invalid || t2 == SourceTexture::Invalid || t1 == t2
}
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct AlphaBatchList {
- pub batches: Vec<AlphaPrimitiveBatch>,
+ pub batches: Vec<PrimitiveBatch>,
+ pub item_rects: Vec<Vec<DeviceIntRect>>,
}
impl AlphaBatchList {
fn new() -> Self {
AlphaBatchList {
batches: Vec::new(),
+ item_rects: Vec::new(),
}
}
pub fn get_suitable_batch(
&mut self,
key: BatchKey,
- item_bounding_rect: &DeviceIntRect,
+ task_relative_bounding_rect: &DeviceIntRect,
) -> &mut Vec<PrimitiveInstance> {
let mut selected_batch_index = None;
match (key.kind, key.blend_mode) {
(BatchKind::Composite { .. }, _) => {
// Composites always get added to their own batch.
// This is because the result of a composite can affect
// the input to the next composite. Perhaps we can
// optimize this in the future.
}
(BatchKind::Transformable(_, TransformBatchKind::TextRun(_)), BlendMode::SubpixelWithBgColor) |
(BatchKind::Transformable(_, TransformBatchKind::TextRun(_)), BlendMode::SubpixelVariableTextColor) => {
'outer_text: for (batch_index, batch) in self.batches.iter().enumerate().rev().take(10) {
// Subpixel text is drawn in two passes. Because of this, we need
// to check for overlaps with every batch (which is a bit different
// than the normal batching below).
- for item_rect in &batch.item_rects {
- if item_rect.intersects(item_bounding_rect) {
+ for item_rect in &self.item_rects[batch_index] {
+ if item_rect.intersects(task_relative_bounding_rect) {
break 'outer_text;
}
}
if batch.key.is_compatible_with(&key) {
selected_batch_index = Some(batch_index);
break;
}
@@ -243,60 +207,58 @@ impl AlphaBatchList {
// is compatible, then we know there isn't any potential overlap
// issues to worry about.
if batch.key.is_compatible_with(&key) {
selected_batch_index = Some(batch_index);
break;
}
// check for intersections
- for item_rect in &batch.item_rects {
- if item_rect.intersects(item_bounding_rect) {
+ for item_rect in &self.item_rects[batch_index] {
+ if item_rect.intersects(task_relative_bounding_rect) {
break 'outer_default;
}
}
}
}
}
if selected_batch_index.is_none() {
- let new_batch = AlphaPrimitiveBatch::new(key);
+ let new_batch = PrimitiveBatch::new(key);
selected_batch_index = Some(self.batches.len());
self.batches.push(new_batch);
+ self.item_rects.push(Vec::new());
}
- let batch = &mut self.batches[selected_batch_index.unwrap()];
- batch.item_rects.push(*item_bounding_rect);
-
- &mut batch.instances
+ let selected_batch_index = selected_batch_index.unwrap();
+ self.item_rects[selected_batch_index].push(*task_relative_bounding_rect);
+ &mut self.batches[selected_batch_index].instances
}
}
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct OpaqueBatchList {
- pub pixel_area_threshold_for_new_batch: i32,
- pub batches: Vec<OpaquePrimitiveBatch>,
+ pub pixel_area_threshold_for_new_batch: f32,
+ pub batches: Vec<PrimitiveBatch>,
}
impl OpaqueBatchList {
- fn new(pixel_area_threshold_for_new_batch: i32) -> Self {
+ fn new(pixel_area_threshold_for_new_batch: f32) -> Self {
OpaqueBatchList {
batches: Vec::new(),
pixel_area_threshold_for_new_batch,
}
}
pub fn get_suitable_batch(
&mut self,
key: BatchKey,
- item_bounding_rect: &DeviceIntRect
+ task_relative_bounding_rect: &DeviceIntRect
) -> &mut Vec<PrimitiveInstance> {
let mut selected_batch_index = None;
- let item_area = item_bounding_rect.size.area();
+ let item_area = task_relative_bounding_rect.size.to_f32().area();
// If the area of this primitive is larger than the given threshold,
// then it is large enough to warrant breaking a batch for. In this
// case we just see if it can be added to the existing batch or
// create a new one.
if item_area > self.pixel_area_threshold_for_new_batch {
if let Some(ref batch) = self.batches.last() {
if batch.key.is_compatible_with(&key) {
@@ -309,17 +271,17 @@ impl OpaqueBatchList {
if batch.key.is_compatible_with(&key) {
selected_batch_index = Some(batch_index);
break;
}
}
}
if selected_batch_index.is_none() {
- let new_batch = OpaquePrimitiveBatch::new(key);
+ let new_batch = PrimitiveBatch::new(key);
selected_batch_index = Some(self.batches.len());
self.batches.push(new_batch);
}
let batch = &mut self.batches[selected_batch_index.unwrap()];
&mut batch.instances
}
@@ -332,128 +294,205 @@ impl OpaqueBatchList {
// build these in reverse and avoid having
// to reverse the instance array here.
for batch in &mut self.batches {
batch.instances.reverse();
}
}
}
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct BatchList {
pub alpha_batch_list: AlphaBatchList,
pub opaque_batch_list: OpaqueBatchList,
+ pub combined_bounding_rect: DeviceIntRect,
}
impl BatchList {
pub fn new(screen_size: DeviceIntSize) -> Self {
// The threshold for creating a new batch is
// one quarter the screen size.
- let batch_area_threshold = screen_size.width * screen_size.height / 4;
+ let batch_area_threshold = (screen_size.width * screen_size.height) as f32 / 4.0;
BatchList {
alpha_batch_list: AlphaBatchList::new(),
opaque_batch_list: OpaqueBatchList::new(batch_area_threshold),
+ combined_bounding_rect: DeviceIntRect::zero(),
}
}
pub fn get_suitable_batch(
&mut self,
key: BatchKey,
- item_bounding_rect: &DeviceIntRect,
+ task_relative_bounding_rect: &DeviceIntRect,
) -> &mut Vec<PrimitiveInstance> {
+ self.combined_bounding_rect = self.combined_bounding_rect.union(task_relative_bounding_rect);
+
match key.blend_mode {
BlendMode::None => {
self.opaque_batch_list
- .get_suitable_batch(key, item_bounding_rect)
+ .get_suitable_batch(key, task_relative_bounding_rect)
}
BlendMode::Alpha |
BlendMode::PremultipliedAlpha |
BlendMode::PremultipliedDestOut |
BlendMode::SubpixelConstantTextColor(..) |
BlendMode::SubpixelVariableTextColor |
BlendMode::SubpixelWithBgColor |
BlendMode::SubpixelDualSource => {
self.alpha_batch_list
- .get_suitable_batch(key, item_bounding_rect)
+ .get_suitable_batch(key, task_relative_bounding_rect)
}
}
}
fn finalize(&mut self) {
self.opaque_batch_list.finalize()
}
}
-/// Encapsulates the logic of building batches for items that are blended.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct AlphaBatcher {
- pub batch_list: BatchList,
- pub text_run_cache_prims: FastHashMap<SourceTexture, Vec<PrimitiveInstance>>,
- glyph_fetch_buffer: Vec<GlyphFetchResult>,
+pub struct PrimitiveBatch {
+ pub key: BatchKey,
+ pub instances: Vec<PrimitiveInstance>,
+}
+
+impl PrimitiveBatch {
+ fn new(key: BatchKey) -> PrimitiveBatch {
+ PrimitiveBatch {
+ key,
+ instances: Vec::new(),
+ }
+ }
}
-impl AlphaBatcher {
- pub fn new(screen_size: DeviceIntSize) -> Self {
- AlphaBatcher {
- batch_list: BatchList::new(screen_size),
- glyph_fetch_buffer: Vec::new(),
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct AlphaBatchContainer {
+ pub text_run_cache_prims: FastHashMap<SourceTexture, Vec<PrimitiveInstance>>,
+ pub opaque_batches: Vec<PrimitiveBatch>,
+ pub alpha_batches: Vec<PrimitiveBatch>,
+ pub target_rect: Option<DeviceIntRect>,
+}
+
+impl AlphaBatchContainer {
+ pub fn new(target_rect: Option<DeviceIntRect>) -> AlphaBatchContainer {
+ AlphaBatchContainer {
text_run_cache_prims: FastHashMap::default(),
+ opaque_batches: Vec::new(),
+ alpha_batches: Vec::new(),
+ target_rect,
}
}
- pub fn build(
- &mut self,
- tasks: &[RenderTaskId],
- ctx: &RenderTargetContext,
- gpu_cache: &mut GpuCache,
- render_tasks: &RenderTaskTree,
- deferred_resolves: &mut Vec<DeferredResolve>,
- ) {
- for &task_id in tasks {
- match render_tasks[task_id].kind {
- RenderTaskKind::Picture(ref pic_task) => {
- let pic_index = ctx.prim_store.cpu_metadata[pic_task.prim_index.0].cpu_prim_index;
- let pic = &ctx.prim_store.cpu_pictures[pic_index.0];
- self.add_pic_to_batch(
- pic,
- task_id,
- ctx,
- gpu_cache,
- render_tasks,
- deferred_resolves,
- );
+ fn merge(&mut self, builder: AlphaBatchBuilder) {
+ self.text_run_cache_prims.extend(builder.text_run_cache_prims);
+
+ for other_batch in builder.batch_list.opaque_batch_list.batches {
+ let batch_index = self.opaque_batches.iter().position(|batch| {
+ batch.key.is_compatible_with(&other_batch.key)
+ });
+
+ match batch_index {
+ Some(batch_index) => {
+ self.opaque_batches[batch_index].instances.extend(other_batch.instances);
}
- _ => {
- unreachable!();
+ None => {
+ self.opaque_batches.push(other_batch);
}
}
}
- self.batch_list.finalize();
+ let mut min_batch_index = 0;
+
+ for other_batch in builder.batch_list.alpha_batch_list.batches {
+ let batch_index = self.alpha_batches.iter().skip(min_batch_index).position(|batch| {
+ batch.key.is_compatible_with(&other_batch.key)
+ });
+
+ match batch_index {
+ Some(batch_index) => {
+ let batch_index = batch_index + min_batch_index;
+ self.alpha_batches[batch_index].instances.extend(other_batch.instances);
+ min_batch_index = batch_index;
+ }
+ None => {
+ self.alpha_batches.push(other_batch);
+ min_batch_index = self.alpha_batches.len();
+ }
+ }
+ }
+ }
+}
+
+/// Encapsulates the logic of building batches for items that are blended.
+pub struct AlphaBatchBuilder {
+ pub batch_list: BatchList,
+ pub text_run_cache_prims: FastHashMap<SourceTexture, Vec<PrimitiveInstance>>,
+ glyph_fetch_buffer: Vec<GlyphFetchResult>,
+ target_rect: DeviceIntRect,
+}
+
+impl AlphaBatchBuilder {
+ pub fn new(
+ screen_size: DeviceIntSize,
+ target_rect: DeviceIntRect,
+ ) -> Self {
+ AlphaBatchBuilder {
+ batch_list: BatchList::new(screen_size),
+ glyph_fetch_buffer: Vec::new(),
+ text_run_cache_prims: FastHashMap::default(),
+ target_rect,
+ }
}
- pub fn is_empty(&self) -> bool {
- self.batch_list.opaque_batch_list.batches.is_empty() &&
- self.batch_list.alpha_batch_list.batches.is_empty()
+ pub fn build(mut self, merged_batches: &mut AlphaBatchContainer) -> Option<AlphaBatchContainer> {
+ self.batch_list.finalize();
+
+ let task_relative_target_rect = DeviceIntRect::new(
+ DeviceIntPoint::zero(),
+ self.target_rect.size,
+ );
+
+ let can_merge = task_relative_target_rect.contains_rect(&self.batch_list.combined_bounding_rect);
+
+ if can_merge {
+ merged_batches.merge(self);
+ None
+ } else {
+ Some(AlphaBatchContainer {
+ alpha_batches: self.batch_list.alpha_batch_list.batches,
+ opaque_batches: self.batch_list.opaque_batch_list.batches,
+ target_rect: Some(self.target_rect),
+ text_run_cache_prims: self.text_run_cache_prims,
+ })
+ }
}
- fn add_pic_to_batch(
+ pub fn add_pic_to_batch(
&mut self,
pic: &PicturePrimitive,
task_id: RenderTaskId,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
) {
let task_address = render_tasks.get_task_address(task_id);
+ let task = &render_tasks[task_id];
+ let content_origin = match task.kind {
+ RenderTaskKind::Picture(ref pic_task) => {
+ pic_task.content_origin
+ }
+ _ => {
+ panic!("todo: tidy this up");
+ }
+ };
+
// Even though most of the time a splitter isn't used or needed,
// they are cheap to construct so we will always pass one down.
let mut splitter = BspSplitter::new();
// Add each run in this picture to the batch.
for run in &pic.runs {
let scroll_node = &ctx.clip_scroll_tree.nodes[&run.clip_and_scroll.scroll_node_id];
let scroll_id = scroll_node.node_data_index;
@@ -462,17 +501,18 @@ impl AlphaBatcher {
scroll_id,
ctx,
gpu_cache,
render_tasks,
task_id,
task_address,
deferred_resolves,
&mut splitter,
- pic.picture_type(),
+ pic,
+ content_origin,
);
}
// Flush the accumulated plane splits onto the task tree.
// Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order.
for poly in splitter.sort(vec3(0.0, 0.0, 1.0)) {
let prim_index = PrimitiveIndex(poly.anchor);
debug!("process sorted poly {:?} {:?}", prim_index, poly.points);
@@ -485,17 +525,17 @@ impl AlphaBatcher {
let gpu_handle = gpu_cache.push_per_frame_blocks(&gpu_blocks);
let key = BatchKey::new(
BatchKind::SplitComposite,
BlendMode::PremultipliedAlpha,
BatchTextures::no_texture(),
);
let pic_metadata = &ctx.prim_store.cpu_metadata[prim_index.0];
let pic = &ctx.prim_store.cpu_pictures[pic_metadata.cpu_prim_index.0];
- let batch = self.batch_list.get_suitable_batch(key, pic_metadata.screen_rect.as_ref().expect("bug"));
+ let batch = self.batch_list.get_suitable_batch(key, &pic_metadata.screen_rect.as_ref().expect("bug").clipped);
let render_task_id = match pic.surface {
Some(PictureSurface::RenderTask(render_task_id)) => render_task_id,
Some(PictureSurface::TextureCache(..)) | None => panic!("BUG: unexpected surface in splitting"),
};
let source_task_address = render_tasks.get_task_address(render_task_id);
let gpu_address = gpu_handle.as_int(gpu_cache);
@@ -523,42 +563,49 @@ impl AlphaBatcher {
scroll_id: ClipScrollNodeIndex,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &RenderTaskTree,
task_id: RenderTaskId,
task_address: RenderTaskAddress,
deferred_resolves: &mut Vec<DeferredResolve>,
splitter: &mut BspSplitter<f64, WorldPixel>,
- pic_type: PictureType,
+ pic: &PicturePrimitive,
+ content_origin: ContentOrigin,
) {
for i in 0 .. run.count {
let prim_index = PrimitiveIndex(run.base_prim_index.0 + i);
let metadata = &ctx.prim_store.cpu_metadata[prim_index.0];
// Now that we walk the primitive runs in order to add
// items to batches, we need to check if they are
// visible here.
// We currently only support culling on normal (Image)
// picture types.
// TODO(gw): Support culling on shadow image types.
- if pic_type != PictureType::Image || metadata.screen_rect.is_some() {
+ let is_image = match pic.kind {
+ PictureKind::Image { .. } => true,
+ PictureKind::BoxShadow { .. } | PictureKind::TextShadow { .. } => false,
+ };
+
+ if !is_image || metadata.screen_rect.is_some() {
self.add_prim_to_batch(
metadata.clip_chain_rect_index,
scroll_id,
prim_index,
ctx,
gpu_cache,
render_tasks,
task_id,
task_address,
deferred_resolves,
splitter,
- pic_type,
+ content_origin,
+ pic,
);
}
}
}
fn get_buffer_kind(texture: SourceTexture) -> ImageBufferKind {
match texture {
SourceTexture::External(ext_image) => {
@@ -588,32 +635,52 @@ impl AlphaBatcher {
prim_index: PrimitiveIndex,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &RenderTaskTree,
task_id: RenderTaskId,
task_address: RenderTaskAddress,
deferred_resolves: &mut Vec<DeferredResolve>,
splitter: &mut BspSplitter<f64, WorldPixel>,
- pic_type: PictureType,
+ content_origin: ContentOrigin,
+ pic: &PicturePrimitive,
) {
let z = prim_index.0 as i32;
let prim_metadata = ctx.prim_store.get_metadata(prim_index);
let scroll_node = &ctx.node_data[scroll_id.0 as usize];
// TODO(gw): Calculating this for every primitive is a bit
// wasteful. We should probably cache this in
// the scroll node...
let transform_kind = scroll_node.transform.transform_kind();
- let item_bounding_rect = &match prim_metadata.screen_rect {
- Some(screen_rect) => screen_rect,
- None => {
- debug_assert_ne!(pic_type, PictureType::Image);
- DeviceIntRect::zero()
+
+ let task_relative_bounding_rect = match content_origin {
+ ContentOrigin::Screen(point) => {
+ // translate by content-origin
+ let screen_rect = prim_metadata.screen_rect.expect("bug");
+ DeviceIntRect::new(
+ DeviceIntPoint::new(
+ screen_rect.unclipped.origin.x - point.x,
+ screen_rect.unclipped.origin.y - point.y,
+ ),
+ screen_rect.unclipped.size,
+ )
+ }
+ ContentOrigin::Local(point) => {
+ // scale local rect by device pixel ratio
+ let content_rect = LayerRect::new(
+ LayerPoint::new(
+ prim_metadata.local_rect.origin.x - point.x,
+ prim_metadata.local_rect.origin.y - point.y,
+ ),
+ prim_metadata.local_rect.size,
+ );
+ (content_rect * LayerToWorldScale::new(1.0) * ctx.device_pixel_scale).round().to_i32()
}
};
+
let prim_cache_address = gpu_cache.get_address(&prim_metadata.gpu_location);
let no_textures = BatchTextures::no_texture();
let clip_task_address = prim_metadata
.clip_task_id
.map_or(OPAQUE_TASK_ADDRESS, |id| render_tasks.get_task_address(id));
let base_instance = SimplePrimitiveInstance::new(
prim_cache_address,
task_address,
@@ -631,17 +698,17 @@ impl AlphaBatcher {
let batch_key = brush.get_batch_key(blend_mode);
self.add_brush_to_batch(
brush,
prim_metadata,
batch_key,
clip_chain_rect_index,
clip_task_address,
- item_bounding_rect,
+ &task_relative_bounding_rect,
prim_cache_address,
scroll_id,
task_address,
transform_kind,
z,
render_tasks,
0,
0,
@@ -660,17 +727,17 @@ impl AlphaBatcher {
transform_kind,
TransformBatchKind::BorderEdge,
);
let edge_key = BatchKey::new(edge_kind, blend_mode, no_textures);
// Work around borrow ck on borrowing batch_list twice.
{
let batch =
- self.batch_list.get_suitable_batch(corner_key, item_bounding_rect);
+ self.batch_list.get_suitable_batch(corner_key, &task_relative_bounding_rect);
for (i, instance_kind) in border_cpu.corner_instances.iter().enumerate()
{
let sub_index = i as i32;
match *instance_kind {
BorderCornerInstance::None => {}
BorderCornerInstance::Single => {
batch.push(base_instance.build(
sub_index,
@@ -689,70 +756,71 @@ impl AlphaBatcher {
BorderCornerSide::Second as i32,
0,
));
}
}
}
}
- let batch = self.batch_list.get_suitable_batch(edge_key, item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(edge_key, &task_relative_bounding_rect);
for (border_segment, instance_kind) in border_cpu.edges.iter().enumerate() {
match *instance_kind {
BorderEdgeKind::None => {},
_ => {
batch.push(base_instance.build(border_segment as i32, 0, 0));
}
}
}
}
PrimitiveKind::Image => {
let image_cpu = &ctx.prim_store.cpu_images[prim_metadata.cpu_prim_index.0];
let cache_item = match image_cpu.source {
ImageSource::Default => {
resolve_image(
- image_cpu.key.image_key,
- image_cpu.key.image_rendering,
- image_cpu.key.tile_offset,
+ image_cpu.key.request,
ctx.resource_cache,
gpu_cache,
deferred_resolves,
)
}
ImageSource::Cache { ref item, .. } => {
item.clone()
}
};
if cache_item.texture_id == SourceTexture::Invalid {
warn!("Warnings: skip a PrimitiveKind::Image");
- debug!("at {:?}.", item_bounding_rect);
+ debug!("at {:?}.", task_relative_bounding_rect);
return;
}
let batch_kind = TransformBatchKind::Image(Self::get_buffer_kind(cache_item.texture_id));
let key = BatchKey::new(
BatchKind::Transformable(transform_kind, batch_kind),
blend_mode,
BatchTextures {
colors: [
cache_item.texture_id,
SourceTexture::Invalid,
SourceTexture::Invalid,
],
},
);
- let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
batch.push(base_instance.build(cache_item.uv_rect_handle.as_int(gpu_cache), 0, 0));
}
PrimitiveKind::TextRun => {
let text_cpu =
&ctx.prim_store.cpu_text_runs[prim_metadata.cpu_prim_index.0];
- let is_shadow = pic_type == PictureType::TextShadow;
+ let is_shadow = match pic.kind {
+ PictureKind::TextShadow { .. } => true,
+ PictureKind::BoxShadow { .. } | PictureKind::Image { .. } => false,
+ };
// TODO(gw): It probably makes sense to base this decision on the content
// origin field in the future (once that's configurable).
let font_transform = if is_shadow {
None
} else {
Some(&scroll_node.transform)
};
@@ -770,17 +838,17 @@ impl AlphaBatcher {
font,
&text_cpu.glyph_keys,
glyph_fetch_buffer,
gpu_cache,
|texture_id, mut glyph_format, glyphs| {
debug_assert_ne!(texture_id, SourceTexture::Invalid);
// Ignore color and only sample alpha when shadowing.
- if text_cpu.is_shadow() {
+ if text_cpu.shadow {
glyph_format = glyph_format.ignore_color();
}
let subpx_dir = match glyph_format {
GlyphFormat::Bitmap |
GlyphFormat::ColorBitmap => SubpixelDirection::None,
_ => text_cpu.font.subpx_dir.limit_by(text_cpu.font.render_mode),
};
@@ -806,27 +874,27 @@ impl AlphaBatcher {
let blend_mode = match glyph_format {
GlyphFormat::Subpixel |
GlyphFormat::TransformedSubpixel => {
if text_cpu.font.bg_color.a != 0 {
BlendMode::SubpixelWithBgColor
} else if ctx.use_dual_source_blending {
BlendMode::SubpixelDualSource
} else {
- BlendMode::SubpixelConstantTextColor(text_cpu.get_color())
+ BlendMode::SubpixelConstantTextColor(text_cpu.font.color.into())
}
}
GlyphFormat::Alpha |
GlyphFormat::TransformedAlpha |
GlyphFormat::Bitmap |
GlyphFormat::ColorBitmap => BlendMode::PremultipliedAlpha,
};
let key = BatchKey::new(kind, blend_mode, textures);
- batch_list.get_suitable_batch(key, item_bounding_rect)
+ batch_list.get_suitable_batch(key, &task_relative_bounding_rect)
};
for glyph in glyphs {
batch.push(base_instance.build(
glyph.index_in_text_run,
glyph.uv_rect_address.as_int(),
subpx_dir as u32 as i32,
));
@@ -858,17 +926,17 @@ impl AlphaBatcher {
);
self.add_brush_to_batch(
&picture.brush,
prim_metadata,
alpha_batch_key,
clip_chain_rect_index,
clip_task_address,
- item_bounding_rect,
+ &task_relative_bounding_rect,
prim_cache_address,
scroll_id,
task_address,
transform_kind,
z,
render_tasks,
cache_item.uv_rect_handle.as_int(gpu_cache),
image_kind as i32,
@@ -882,17 +950,17 @@ impl AlphaBatcher {
match picture.kind {
PictureKind::TextShadow { .. } => {
let kind = BatchKind::Brush(
BrushBatchKind::Picture(
BrushImageSourceKind::from_render_target_kind(picture.target_kind())),
);
let key = BatchKey::new(kind, blend_mode, textures);
- let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
let instance = BrushInstance {
picture_address: task_address,
prim_address: prim_cache_address,
clip_chain_rect_index,
scroll_id,
clip_task_address,
z,
@@ -946,17 +1014,18 @@ impl AlphaBatcher {
match filter {
FilterOp::Blur(..) => {
let src_task_address = render_tasks.get_task_address(source_id);
let key = BatchKey::new(
BatchKind::HardwareComposite,
BlendMode::PremultipliedAlpha,
BatchTextures::render_target_cache(),
);
- let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
+ let item_bounding_rect = prim_metadata.screen_rect.expect("bug!!").clipped;
let instance = CompositePrimitiveInstance::new(
task_address,
src_task_address,
RenderTaskAddress(0),
item_bounding_rect.origin.x,
item_bounding_rect.origin.y,
z,
item_bounding_rect.size.width,
@@ -980,17 +1049,17 @@ impl AlphaBatcher {
z,
segment_index: 0,
edge_flags: EdgeAaSegmentMask::empty(),
user_data0: cache_task_address.0 as i32,
user_data1: BrushImageKind::Simple as i32,
};
{
- let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
batch.push(PrimitiveInstance::from(instance));
}
let secondary_id = secondary_render_task_id.expect("no secondary!?");
let render_task = &render_tasks[secondary_id];
let secondary_task_address = render_tasks.get_task_address(secondary_id);
let render_pass_index = render_task.pass_index.expect("no render_pass_index!?");
let secondary_textures = BatchTextures {
@@ -1000,17 +1069,17 @@ impl AlphaBatcher {
SourceTexture::Invalid,
],
};
let key = BatchKey::new(
BatchKind::HardwareComposite,
BlendMode::PremultipliedAlpha,
secondary_textures,
);
- let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
let content_rect = prim_metadata.local_rect.translate(&-offset);
let rect =
(content_rect * LayerToWorldScale::new(1.0) * ctx.device_pixel_scale).round()
.to_i32();
let instance = CompositePrimitiveInstance::new(
task_address,
secondary_task_address,
@@ -1042,17 +1111,17 @@ impl AlphaBatcher {
FilterOp::Sepia(amount) => (6, amount),
FilterOp::Brightness(amount) => (7, amount),
FilterOp::Opacity(_, amount) => (8, amount),
FilterOp::DropShadow(..) => unreachable!(),
FilterOp::ColorMatrix(_) => (10, 0.0),
};
let amount = (amount * 65535.0).round() as i32;
- let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
let instance = CompositePrimitiveInstance::new(
task_address,
src_task_address,
RenderTaskAddress(0),
filter_mode,
amount,
z,
@@ -1071,17 +1140,17 @@ impl AlphaBatcher {
BatchKind::Composite {
task_id,
source_id,
backdrop_id,
},
BlendMode::PremultipliedAlpha,
BatchTextures::no_texture(),
);
- let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
let backdrop_task_address = render_tasks.get_task_address(backdrop_id);
let source_task_address = render_tasks.get_task_address(source_id);
let instance = CompositePrimitiveInstance::new(
task_address,
source_task_address,
backdrop_task_address,
mode as u32 as i32,
@@ -1095,17 +1164,18 @@ impl AlphaBatcher {
}
PictureCompositeMode::Blit => {
let src_task_address = render_tasks.get_task_address(source_id);
let key = BatchKey::new(
BatchKind::HardwareComposite,
BlendMode::PremultipliedAlpha,
BatchTextures::render_target_cache(),
);
- let batch = self.batch_list.get_suitable_batch(key, &item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
+ let item_bounding_rect = prim_metadata.screen_rect.expect("bug!!").clipped;
let instance = CompositePrimitiveInstance::new(
task_address,
src_task_address,
RenderTaskAddress(0),
item_bounding_rect.origin.x,
item_bounding_rect.origin.y,
z,
item_bounding_rect.size.width,
@@ -1135,63 +1205,65 @@ impl AlphaBatcher {
PrimitiveKind::AlignedGradient => {
let gradient_cpu =
&ctx.prim_store.cpu_gradients[prim_metadata.cpu_prim_index.0];
let kind = BatchKind::Transformable(
transform_kind,
TransformBatchKind::AlignedGradient,
);
let key = BatchKey::new(kind, blend_mode, no_textures);
- let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
for part_index in 0 .. (gradient_cpu.stops_count - 1) {
batch.push(base_instance.build(part_index as i32, 0, 0));
}
}
PrimitiveKind::AngleGradient => {
let kind = BatchKind::Transformable(
transform_kind,
TransformBatchKind::AngleGradient,
);
let key = BatchKey::new(kind, blend_mode, no_textures);
- let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
batch.push(base_instance.build(0, 0, 0));
}
PrimitiveKind::RadialGradient => {
let kind = BatchKind::Transformable(
transform_kind,
TransformBatchKind::RadialGradient,
);
let key = BatchKey::new(kind, blend_mode, no_textures);
- let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
batch.push(base_instance.build(0, 0, 0));
}
PrimitiveKind::YuvImage => {
let mut textures = BatchTextures::no_texture();
let mut uv_rect_addresses = [0; 3];
let image_yuv_cpu =
&ctx.prim_store.cpu_yuv_images[prim_metadata.cpu_prim_index.0];
//yuv channel
let channel_count = image_yuv_cpu.format.get_plane_num();
debug_assert!(channel_count <= 3);
for channel in 0 .. channel_count {
let image_key = image_yuv_cpu.yuv_key[channel];
let cache_item = resolve_image(
- image_key,
- image_yuv_cpu.image_rendering,
- None,
+ ImageRequest {
+ key: image_key,
+ rendering: image_yuv_cpu.image_rendering,
+ tile: None,
+ },
ctx.resource_cache,
gpu_cache,
deferred_resolves,
);
if cache_item.texture_id == SourceTexture::Invalid {
warn!("Warnings: skip a PrimitiveKind::YuvImage");
- debug!("at {:?}.", item_bounding_rect);
+ debug!("at {:?}.", task_relative_bounding_rect);
return;
}
textures.colors[channel] = cache_item.texture_id;
uv_rect_addresses[channel] = cache_item.uv_rect_handle.as_int(gpu_cache);
}
// All yuv textures should be the same type.
@@ -1206,17 +1278,17 @@ impl AlphaBatcher {
transform_kind,
TransformBatchKind::YuvImage(
buffer_kind,
image_yuv_cpu.format,
image_yuv_cpu.color_space,
),
);
let key = BatchKey::new(kind, blend_mode, textures);
- let batch = self.batch_list.get_suitable_batch(key, item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(key, &task_relative_bounding_rect);
batch.push(base_instance.build(
uv_rect_addresses[0],
uv_rect_addresses[1],
uv_rect_addresses[2],
));
}
}
@@ -1224,17 +1296,17 @@ impl AlphaBatcher {
fn add_brush_to_batch(
&mut self,
brush: &BrushPrimitive,
prim_metadata: &PrimitiveMetadata,
batch_key: BatchKey,
clip_chain_rect_index: ClipChainRectIndex,
clip_task_address: RenderTaskAddress,
- item_bounding_rect: &DeviceIntRect,
+ task_relative_bounding_rect: &DeviceIntRect,
prim_cache_address: GpuCacheAddress,
scroll_id: ClipScrollNodeIndex,
task_address: RenderTaskAddress,
transform_kind: TransformedRectKind,
z: i32,
render_tasks: &RenderTaskTree,
user_data0: i32,
user_data1: i32,
@@ -1256,27 +1328,27 @@ impl AlphaBatcher {
Some(ref segment_desc) => {
let alpha_batch_key = BatchKey {
blend_mode: BlendMode::PremultipliedAlpha,
..batch_key
};
let alpha_batch = self.batch_list.alpha_batch_list.get_suitable_batch(
alpha_batch_key,
- item_bounding_rect
+ task_relative_bounding_rect
);
let opaque_batch_key = BatchKey {
blend_mode: BlendMode::None,
..batch_key
};
let opaque_batch = self.batch_list.opaque_batch_list.get_suitable_batch(
opaque_batch_key,
- item_bounding_rect
+ task_relative_bounding_rect
);
for (i, segment) in segment_desc.segments.iter().enumerate() {
let is_inner = segment.edge_flags.is_empty();
let needs_blending = !prim_metadata.opacity.is_opaque ||
segment.clip_task_id.is_some() ||
(!is_inner && transform_kind == TransformedRectKind::Complex);
@@ -1294,17 +1366,17 @@ impl AlphaBatcher {
if needs_blending {
alpha_batch.push(instance);
} else {
opaque_batch.push(instance);
}
}
}
None => {
- let batch = self.batch_list.get_suitable_batch(batch_key, item_bounding_rect);
+ let batch = self.batch_list.get_suitable_batch(batch_key, task_relative_bounding_rect);
batch.push(PrimitiveInstance::from(base_instance));
}
}
}
}
impl BrushPrimitive {
fn get_batch_key(&self, blend_mode: BlendMode) -> BatchKey {
@@ -1380,24 +1452,22 @@ impl AlphaBatchHelpers for PrimitiveStor
} else {
BlendMode::None
},
}
}
}
pub fn resolve_image(
- image_key: ImageKey,
- image_rendering: ImageRendering,
- tile_offset: Option<TileOffset>,
+ request: ImageRequest,
resource_cache: &ResourceCache,
gpu_cache: &mut GpuCache,
deferred_resolves: &mut Vec<DeferredResolve>,
) -> CacheItem {
- match resource_cache.get_image_properties(image_key) {
+ match resource_cache.get_image_properties(request.key) {
Some(image_properties) => {
// Check if an external image that needs to be resolved
// by the render thread.
match image_properties.external_image {
Some(external_image) => {
// This is an external texture - we will add it to
// the deferred resolves list to be patched by
// the render thread...
@@ -1418,17 +1488,17 @@ pub fn resolve_image(
deferred_resolves.push(DeferredResolve {
image_properties,
address: gpu_cache.get_address(&cache_handle),
});
cache_item
}
None => {
- if let Ok(cache_item) = resource_cache.get_cached_image(image_key, image_rendering, tile_offset) {
+ if let Ok(cache_item) = resource_cache.get_cached_image(request) {
cache_item
} else {
// There is no usable texture entry for the image key. Just return an invalid texture here.
CacheItem::invalid()
}
}
}
}
@@ -1511,17 +1581,23 @@ impl ClipBatcher {
.get_opt(&work_item.clip_sources)
.expect("bug: clip handle should be valid");
for &(ref source, ref handle) in &info.clips {
let gpu_address = gpu_cache.get_address(handle);
match *source {
ClipSource::Image(ref mask) => {
- if let Ok(cache_item) = resource_cache.get_cached_image(mask.image, ImageRendering::Auto, None) {
+ if let Ok(cache_item) = resource_cache.get_cached_image(
+ ImageRequest {
+ key: mask.image,
+ rendering: ImageRendering::Auto,
+ tile: None,
+ }
+ ) {
self.images
.entry(cache_item.texture_id)
.or_insert(Vec::new())
.push(ClipMaskInstance {
clip_data_address: gpu_address,
resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
..instance
});
--- a/gfx/webrender/src/clip.rs
+++ b/gfx/webrender/src/clip.rs
@@ -5,17 +5,17 @@
use api::{BorderRadius, ClipMode, ComplexClipRegion, DeviceIntRect, DevicePixelScale, ImageMask};
use api::{ImageRendering, LayerRect, LayerToWorldTransform, LayoutPoint, LayoutVector2D};
use api::LocalClip;
use border::{BorderCornerClipSource, ensure_no_corner_overlap};
use ellipse::Ellipse;
use freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
use gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks};
use prim_store::{ClipData, ImageMaskData};
-use resource_cache::ResourceCache;
+use resource_cache::{ImageRequest, ResourceCache};
use util::{MaxRect, MatrixHelpers, calculate_screen_bounding_rect, extract_inner_rect_safe};
pub type ClipStore = FreeList<ClipSources>;
pub type ClipSourcesHandle = FreeListHandle<ClipSources>;
pub type ClipSourcesWeakHandle = WeakFreeListHandle<ClipSources>;
#[derive(Clone, Debug)]
pub struct ClipRegion {
@@ -225,17 +225,24 @@ impl ClipSources {
}
ClipSource::BorderCorner(ref mut source) => {
source.write(request);
}
}
}
if let ClipSource::Image(ref mask) = *source {
- resource_cache.request_image(mask.image, ImageRendering::Auto, None, gpu_cache);
+ resource_cache.request_image(
+ ImageRequest {
+ key: mask.image,
+ rendering: ImageRendering::Auto,
+ tile: None,
+ },
+ gpu_cache,
+ );
}
}
}
/// Whether or not this ClipSources has any clips (does any clipping).
pub fn has_clips(&self) -> bool {
!self.clips.is_empty()
}
--- a/gfx/webrender/src/clip_scroll_tree.rs
+++ b/gfx/webrender/src/clip_scroll_tree.rs
@@ -38,29 +38,29 @@ impl CoordinateSystemId {
CoordinateSystemId(id + 1)
}
pub fn advance(&mut self) {
self.0 += 1;
}
}
-struct ClipChainDescriptor {
- id: ClipChainId,
- parent: Option<ClipChainId>,
- clips: Vec<ClipId>,
+pub struct ClipChainDescriptor {
+ pub id: ClipChainId,
+ pub parent: Option<ClipChainId>,
+ pub clips: Vec<ClipId>,
}
pub struct ClipScrollTree {
pub nodes: FastHashMap<ClipId, ClipScrollNode>,
/// A Vec of all descriptors that describe ClipChains in the order in which they are
/// encountered during display list flattening. ClipChains are expected to never be
/// the children of ClipChains later in the list.
- clip_chains_descriptors: Vec<ClipChainDescriptor>,
+ pub clip_chains_descriptors: Vec<ClipChainDescriptor>,
/// A HashMap of built ClipChains that are described by `clip_chains_descriptors`.
pub clip_chains: FastHashMap<ClipChainId, ClipChain>,
pub pending_scroll_offsets: FastHashMap<ScrollNodeIdType, (LayerPoint, ScrollClamping)>,
/// The ClipId of the currently scrolling node. Used to allow the same
/// node to scroll even if a touch operation leaves the boundaries of that node.
--- a/gfx/webrender/src/debug_server.rs
+++ b/gfx/webrender/src/debug_server.rs
@@ -48,18 +48,16 @@ impl ws::Handler for Server {
ws::Message::Text(string) => {
let cmd = match string.as_str() {
"enable_profiler" => DebugCommand::EnableProfiler(true),
"disable_profiler" => DebugCommand::EnableProfiler(false),
"enable_texture_cache_debug" => DebugCommand::EnableTextureCacheDebug(true),
"disable_texture_cache_debug" => DebugCommand::EnableTextureCacheDebug(false),
"enable_render_target_debug" => DebugCommand::EnableRenderTargetDebug(true),
"disable_render_target_debug" => DebugCommand::EnableRenderTargetDebug(false),
- "enable_alpha_rects_debug" => DebugCommand::EnableAlphaRectsDebug(true),
- "disable_alpha_rects_debug" => DebugCommand::EnableAlphaRectsDebug(false),
"enable_gpu_time_queries" => DebugCommand::EnableGpuTimeQueries(true),
"disable_gpu_time_queries" => DebugCommand::EnableGpuTimeQueries(false),
"enable_gpu_sample_queries" => DebugCommand::EnableGpuSampleQueries(true),
"disable_gpu_sample_queries" => DebugCommand::EnableGpuSampleQueries(false),
"fetch_passes" => DebugCommand::FetchPasses,
"fetch_screenshot" => DebugCommand::FetchScreenshot,
"fetch_documents" => DebugCommand::FetchDocuments,
"fetch_clip_scroll_tree" => DebugCommand::FetchClipScrollTree,
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -1938,16 +1938,29 @@ impl Device {
pub fn disable_depth_write(&self) {
self.gl.depth_mask(false);
}
pub fn disable_stencil(&self) {
self.gl.disable(gl::STENCIL_TEST);
}
+ pub fn set_scissor_rect(&self, rect: DeviceIntRect) {
+ self.gl.scissor(
+ rect.origin.x,
+ rect.origin.y,
+ rect.size.width,
+ rect.size.height,
+ );
+ }
+
+ pub fn enable_scissor(&self) {
+ self.gl.enable(gl::SCISSOR_TEST);
+ }
+
pub fn disable_scissor(&self) {
self.gl.disable(gl::SCISSOR_TEST);
}
pub fn set_blend(&self, enable: bool) {
if enable {
self.gl.enable(gl::BLEND);
} else {
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -17,16 +17,17 @@ use euclid::rect;
use frame_builder::{FrameBuilder, FrameBuilderConfig, ScrollbarInfo};
use gpu_cache::GpuCache;
use hit_test::HitTester;
use internal_types::{FastHashMap, FastHashSet, RenderedDocument};
use profiler::{GpuCacheProfileCounters, TextureCacheProfileCounters};
use resource_cache::{FontInstanceMap,ResourceCache, TiledImageMap};
use scene::{Scene, StackingContextHelpers, ScenePipeline, SceneProperties};
use tiling::{CompositeOps, Frame};
+use renderer::PipelineInfo;
#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Ord)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct FrameId(pub u32);
static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
r: 0.3,
@@ -1096,42 +1097,51 @@ impl FrameContext {
debug_assert!(roller.builder.picture_stack.is_empty());
self.pipeline_epoch_map.extend(roller.pipeline_epochs.drain(..));
roller.builder
};
self.clip_scroll_tree
.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
+
frame_builder
}
pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) {
self.pipeline_epoch_map.insert(pipeline_id, epoch);
}
- pub fn make_rendered_document(&self, frame: Frame) -> RenderedDocument {
+ pub fn make_rendered_document(&mut self, frame: Frame, removed_pipelines: Vec<PipelineId>) -> RenderedDocument {
let nodes_bouncing_back = self.clip_scroll_tree.collect_nodes_bouncing_back();
- RenderedDocument::new(self.pipeline_epoch_map.clone(), nodes_bouncing_back, frame)
+ RenderedDocument::new(
+ PipelineInfo {
+ epochs: self.pipeline_epoch_map.clone(),
+ removed_pipelines,
+ },
+ nodes_bouncing_back,
+ frame
+ )
}
//TODO: this can probably be simplified if `build()` is called directly by RB.
// The only things it needs from the frame context is the CST and frame ID.
pub fn build_rendered_document(
&mut self,
frame_builder: &mut FrameBuilder,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
pipelines: &FastHashMap<PipelineId, ScenePipeline>,
device_pixel_scale: DevicePixelScale,
layer: DocumentLayer,
pan: WorldPoint,
texture_cache_profile: &mut TextureCacheProfileCounters,
gpu_cache_profile: &mut GpuCacheProfileCounters,
- scene_properties: &SceneProperties,
+ scene_properties: &SceneProperties,
+ removed_pipelines: Vec<PipelineId>,
) -> (HitTester, RenderedDocument) {
let frame = frame_builder.build(
resource_cache,
gpu_cache,
self.id,
&mut self.clip_scroll_tree,
pipelines,
self.window_size,
@@ -1140,11 +1150,11 @@ impl FrameContext {
pan,
texture_cache_profile,
gpu_cache_profile,
scene_properties,
);
let hit_tester = frame_builder.create_hit_tester(&self.clip_scroll_tree);
- (hit_tester, self.make_rendered_document(frame))
+ (hit_tester, self.make_rendered_document(frame, removed_pipelines))
}
}
--- a/gfx/webrender/src/frame_builder.rs
+++ b/gfx/webrender/src/frame_builder.rs
@@ -1,14 +1,14 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayList, ClipAndScrollInfo};
-use api::{ClipId, ColorF, ColorU, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale};
+use api::{ClipId, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DevicePixelScale};
use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentLayer, Epoch, ExtendMode};
use api::{ExternalScrollId, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop, ImageKey};
use api::{ImageRendering, ItemRange, LayerPoint, LayerPrimitiveInfo, LayerRect, LayerSize};
use api::{LayerTransform, LayerVector2D, LayoutTransform, LayoutVector2D, LineOrientation};
use api::{LineStyle, LocalClip, PipelineId, PremultipliedColorF, PropertyBinding, RepeatMode};
use api::{ScrollSensitivity, Shadow, TexelRect, TileOffset, TransformStyle, WorldPoint};
use api::{WorldToLayerTransform, YuvColorSpace, YuvData};
use app_units::Au;
@@ -25,18 +25,18 @@ use hit_test::{HitTester, HitTestingItem
use internal_types::{FastHashMap, FastHashSet, RenderPassIndex};
use picture::{ContentOrigin, PictureCompositeMode, PictureKind, PicturePrimitive, PictureSurface};
use prim_store::{BrushKind, BrushPrimitive, ImageCacheKey, YuvImagePrimitiveCpu};
use prim_store::{GradientPrimitiveCpu, ImagePrimitiveCpu, ImageSource, PrimitiveKind};
use prim_store::{PrimitiveContainer, PrimitiveIndex};
use prim_store::{PrimitiveStore, RadialGradientPrimitiveCpu};
use prim_store::{BrushSegmentDescriptor, PrimitiveRun, TextRunPrimitiveCpu};
use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
-use render_task::{ClearMode, ClipChain, RenderTask, RenderTaskId, RenderTaskTree};
-use resource_cache::ResourceCache;
+use render_task::{ClearMode, ClipChain, RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
+use resource_cache::{ImageRequest, ResourceCache};
use scene::{ScenePipeline, SceneProperties};
use std::{mem, usize, f32};
use tiling::{CompositeOps, Frame, RenderPass, RenderTargetKind};
use tiling::{RenderPassKind, RenderTargetContext, ScrollbarPrimitive};
use util::{self, MaxRect, pack_as_float, RectHelpers, recycle_vec};
#[derive(Debug)]
pub struct ScrollbarInfo(pub ClipId, pub LayerRect);
@@ -1402,17 +1402,17 @@ impl FrameBuilder {
);
let prim = TextRunPrimitiveCpu {
font: prim_font,
glyph_range,
glyph_count,
glyph_gpu_blocks: Vec::new(),
glyph_keys: Vec::new(),
offset: run_offset,
- shadow_color: ColorU::new(0, 0, 0, 0),
+ shadow: false,
};
// Text shadows that have a blur radius of 0 need to be rendered as normal
// text elements to get pixel perfect results for reftests. It's also a big
// performance win to avoid blurs and render target allocations where
// possible. For any text shadows that have zero blur, create a normal text
// primitive with the shadow's color and offset. These need to be added
// *before* the visual text primitive in order to get the correct paint
@@ -1420,17 +1420,18 @@ impl FrameBuilder {
// TODO(gw): Refactor to avoid having to store them in a Vec first.
let mut fast_shadow_prims = Vec::new();
for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
let picture_prim = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
match picture_prim.kind {
PictureKind::TextShadow { offset, color, blur_radius, .. } if blur_radius == 0.0 => {
let mut text_prim = prim.clone();
- text_prim.shadow_color = color.into();
+ text_prim.font.color = color.into();
+ text_prim.shadow = true;
text_prim.offset += offset;
fast_shadow_prims.push((idx, text_prim));
}
_ => {}
}
}
for (idx, text_prim) in fast_shadow_prims {
@@ -1513,19 +1514,21 @@ impl FrameBuilder {
let prim_cpu = ImagePrimitiveCpu {
tile_spacing,
alpha_type,
stretch_size,
current_epoch: Epoch::invalid(),
source: ImageSource::Default,
key: ImageCacheKey {
- image_key,
- image_rendering,
- tile_offset,
+ request: ImageRequest {
+ key: image_key,
+ rendering: image_rendering,
+ tile: tile_offset,
+ },
texel_rect: sub_rect.map(|texel_rect| {
DeviceIntRect::new(
DeviceIntPoint::new(
texel_rect.uv0.x as i32,
texel_rect.uv0.y as i32,
),
DeviceIntSize::new(
(texel_rect.uv1.x - texel_rect.uv0.x) as i32,
@@ -1641,17 +1644,17 @@ impl FrameBuilder {
&frame_context,
&mut frame_state,
);
let pic = &mut self.prim_store.cpu_pictures[0];
pic.runs = pic_context.prim_runs;
let root_render_task = RenderTask::new_picture(
- None,
+ RenderTaskLocation::Fixed(frame_context.screen_rect),
PrimitiveIndex(0),
RenderTargetKind::Color,
ContentOrigin::Screen(DeviceIntPoint::zero()),
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
pic_state.tasks,
PictureType::Image,
);
--- a/gfx/webrender/src/hit_test.rs
+++ b/gfx/webrender/src/hit_test.rs
@@ -9,34 +9,40 @@ use clip::{ClipSource, ClipStore, Contai
use clip_scroll_node::{ClipScrollNode, NodeType};
use clip_scroll_tree::ClipScrollTree;
use internal_types::FastHashMap;
/// A copy of important clip scroll node data to use during hit testing. This a copy of
/// data from the ClipScrollTree that will persist as a new frame is under construction,
/// allowing hit tests consistent with the currently rendered frame.
pub struct HitTestClipScrollNode {
- /// This node's parent in the ClipScrollTree. This is used to ensure that we can
- /// travel up the tree of nodes.
- parent: Option<ClipId>,
-
/// A particular point must be inside all of these regions to be considered clipped in
/// for the purposes of a hit test.
regions: Vec<HitTestRegion>,
/// World transform for content transformed by this node.
world_content_transform: LayerToWorldTransform,
/// World viewport transform for content transformed by this node.
world_viewport_transform: LayerToWorldTransform,
/// Origin of the viewport of the node, used to calculate node-relative positions.
node_origin: LayerPoint,
}
+/// A description of a clip chain in the HitTester. This is used to describe
+/// hierarchical clip scroll nodes as well as ClipChains, so that they can be
+/// handled the same way during hit testing. Once we represent all ClipChains
+/// using ClipChainDescriptors, we can get rid of this and just use the
+/// ClipChainDescriptor here.
+struct HitTestClipChainDescriptor {
+ parent: Option<ClipId>,
+ clips: Vec<ClipId>,
+}
+
#[derive(Clone)]
pub struct HitTestingItem {
rect: LayerRect,
clip: LocalClip,
tag: ItemTag,
}
impl HitTestingItem {
@@ -67,122 +73,137 @@ impl HitTestRegion {
!rounded_rectangle_contains_point(point, &rect, &radii),
}
}
}
pub struct HitTester {
runs: Vec<HitTestingRun>,
nodes: FastHashMap<ClipId, HitTestClipScrollNode>,
+ clip_chains: FastHashMap<ClipId, HitTestClipChainDescriptor>,
}
impl HitTester {
pub fn new(
runs: &Vec<HitTestingRun>,
clip_scroll_tree: &ClipScrollTree,
clip_store: &ClipStore
) -> HitTester {
let mut hit_tester = HitTester {
runs: runs.clone(),
nodes: FastHashMap::default(),
+ clip_chains: FastHashMap::default(),
};
hit_tester.read_clip_scroll_tree(clip_scroll_tree, clip_store);
hit_tester
}
fn read_clip_scroll_tree(
&mut self,
clip_scroll_tree: &ClipScrollTree,
clip_store: &ClipStore
) {
self.nodes.clear();
for (id, node) in &clip_scroll_tree.nodes {
self.nodes.insert(*id, HitTestClipScrollNode {
- parent: node.parent,
regions: get_regions_for_clip_scroll_node(node, clip_store),
world_content_transform: node.world_content_transform,
world_viewport_transform: node.world_viewport_transform,
node_origin: node.local_viewport_rect.origin,
});
+
+ self.clip_chains.insert(*id, HitTestClipChainDescriptor {
+ parent: node.parent,
+ clips: vec![*id],
+ });
+ }
+
+ for descriptor in &clip_scroll_tree.clip_chains_descriptors {
+ self.clip_chains.insert(
+ ClipId::ClipChain(descriptor.id),
+ HitTestClipChainDescriptor {
+ parent: descriptor.parent.map(|id| ClipId::ClipChain(id)),
+ clips: descriptor.clips.clone(),
+ }
+ );
}
}
- pub fn is_point_clipped_in_for_node(
+ fn is_point_clipped_in_for_clip_chain(
+ &self,
+ point: WorldPoint,
+ chain_id: &ClipId,
+ test: &mut HitTest
+ ) -> bool {
+ if let Some(result) = test.clip_chain_cache.get(&chain_id) {
+ return *result;
+ }
+
+ let descriptor = &self.clip_chains[&chain_id];
+ let parent_clipped_in = match descriptor.parent {
+ None => true,
+ Some(ref parent) => self.is_point_clipped_in_for_clip_chain(point, parent, test),
+ };
+
+ if !parent_clipped_in {
+ test.clip_chain_cache.insert(*chain_id, false);
+ return false;
+ }
+
+ for clip_node in &descriptor.clips {
+ if !self.is_point_clipped_in_for_node(point, clip_node, test) {
+ test.clip_chain_cache.insert(*chain_id, false);
+ return false;
+ }
+ }
+
+ test.clip_chain_cache.insert(*chain_id, true);
+ true
+ }
+
+ fn is_point_clipped_in_for_node(
&self,
point: WorldPoint,
node_id: &ClipId,
- cache: &mut FastHashMap<ClipId, Option<LayerPoint>>,
+ test: &mut HitTest
) -> bool {
- if let Some(point) = cache.get(node_id) {
+ if let Some(point) = test.node_cache.get(node_id) {
return point.is_some();
}
let node = self.nodes.get(node_id).unwrap();
- let parent_clipped_in = match node.parent {
- None => true, // This is the root node.
- Some(ref parent_id) => {
- self.is_point_clipped_in_for_node(point, parent_id, cache)
- }
- };
-
- if !parent_clipped_in {
- cache.insert(*node_id, None);
- return false;
- }
-
let transform = node.world_viewport_transform;
let transformed_point = match transform.inverse() {
Some(inverted) => inverted.transform_point2d(&point),
None => {
- cache.insert(*node_id, None);
+ test.node_cache.insert(*node_id, None);
return false;
}
};
let point_in_layer = transformed_point - node.node_origin.to_vector();
for region in &node.regions {
if !region.contains(&transformed_point) {
- cache.insert(*node_id, None);
+ test.node_cache.insert(*node_id, None);
return false;
}
}
- cache.insert(*node_id, Some(point_in_layer));
+ test.node_cache.insert(*node_id, Some(point_in_layer));
true
}
- pub fn make_node_relative_point_absolute(
- &self,
- pipeline_id: Option<PipelineId>,
- point: &LayerPoint
- ) -> WorldPoint {
- pipeline_id.and_then(|id| self.nodes.get(&ClipId::root_reference_frame(id)))
- .map(|node| node.world_viewport_transform.transform_point2d(point))
- .unwrap_or_else(|| WorldPoint::new(point.x, point.y))
-
- }
+ pub fn hit_test(&self, mut test: HitTest) -> HitTestResult {
+ let point = test.get_absolute_point(self);
- pub fn hit_test(
- &self,
- pipeline_id: Option<PipelineId>,
- point: WorldPoint,
- flags: HitTestFlags
- ) -> HitTestResult {
- let point = if flags.contains(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT) {
- self.make_node_relative_point_absolute(pipeline_id, &LayerPoint::new(point.x, point.y))
- } else {
- point
- };
-
- let mut node_cache = FastHashMap::default();
let mut result = HitTestResult::default();
for &HitTestingRun(ref items, ref clip_and_scroll) in self.runs.iter().rev() {
let scroll_node = &self.nodes[&clip_and_scroll.scroll_node_id];
- match (pipeline_id, clip_and_scroll.scroll_node_id.pipeline_id()) {
+ match (test.pipeline_id, clip_and_scroll.scroll_node_id.pipeline_id()) {
(Some(id), node_id) if node_id != id => continue,
_ => {},
}
let transform = scroll_node.world_content_transform;
let point_in_layer = match transform.inverse() {
Some(inverted) => inverted.transform_point2d(&point),
None => continue,
@@ -191,34 +212,43 @@ impl HitTester {
let mut clipped_in = false;
for item in items.iter().rev() {
if !item.rect.contains(&point_in_layer) || !item.clip.contains(&point_in_layer) {
continue;
}
let clip_id = &clip_and_scroll.clip_node_id();
if !clipped_in {
- clipped_in = self.is_point_clipped_in_for_node(point, clip_id, &mut node_cache);
+ clipped_in = self.is_point_clipped_in_for_clip_chain(point, clip_id, &mut test);
if !clipped_in {
break;
}
}
- let point_in_viewport = node_cache
- .get(&ClipId::root_reference_frame(clip_id.pipeline_id()))
- .expect("Hittest target's root reference frame not hit.")
- .expect("Hittest target's root reference frame not hit.");
+ // We need to trigger a lookup against the root reference frame here, because
+ // items that are clipped by clip chains won't test against that part of the
+ // hierarchy. If we don't have a valid point for this test, we are likely
+ // in a situation where the reference frame has an univertible transform, but the
+ // item's clip does not.
+ let root_reference_frame = ClipId::root_reference_frame(clip_id.pipeline_id());
+ if !self.is_point_clipped_in_for_node(point, &root_reference_frame, &mut test) {
+ continue;
+ }
+ let point_in_viewport = match test.node_cache[&root_reference_frame] {
+ Some(point) => point,
+ None => continue,
+ };
result.items.push(HitTestItem {
pipeline: clip_and_scroll.clip_node_id().pipeline_id(),
tag: item.tag,
point_in_viewport,
point_relative_to_item: point_in_layer - item.rect.origin.to_vector(),
});
- if !flags.contains(HitTestFlags::FIND_ALL) {
+ if !test.flags.contains(HitTestFlags::FIND_ALL) {
return result;
}
}
}
result.items.dedup();
result
}
@@ -239,8 +269,43 @@ fn get_regions_for_clip_scroll_node(
ClipSource::RoundedRectangle(ref rect, ref radii, ref mode) =>
HitTestRegion::RoundedRectangle(*rect, *radii, *mode),
ClipSource::Image(ref mask) => HitTestRegion::Rectangle(mask.rect),
ClipSource::BorderCorner(_) =>
unreachable!("Didn't expect to hit test against BorderCorner"),
}
}).collect()
}
+
+pub struct HitTest {
+ pipeline_id: Option<PipelineId>,
+ point: WorldPoint,
+ flags: HitTestFlags,
+ node_cache: FastHashMap<ClipId, Option<LayerPoint>>,
+ clip_chain_cache: FastHashMap<ClipId, bool>,
+}
+
+impl HitTest {
+ pub fn new(
+ pipeline_id: Option<PipelineId>,
+ point: WorldPoint,
+ flags: HitTestFlags,
+ ) -> HitTest {
+ HitTest {
+ pipeline_id,
+ point,
+ flags,
+ node_cache: FastHashMap::default(),
+ clip_chain_cache: FastHashMap::default(),
+ }
+ }
+
+ pub fn get_absolute_point(&self, hit_tester: &HitTester) -> WorldPoint {
+ if !self.flags.contains(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT) {
+ return self.point;
+ }
+
+ let point = &LayerPoint::new(self.point.x, self.point.y);
+ self.pipeline_id.and_then(|id| hit_tester.nodes.get(&ClipId::root_reference_frame(id)))
+ .map(|node| node.world_viewport_transform.transform_point2d(&point))
+ .unwrap_or_else(|| WorldPoint::new(self.point.x, self.point.y))
+ }
+}
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -1,17 +1,18 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use api::{ClipId, DeviceUintRect, DocumentId, Epoch};
+use api::{ClipId, DeviceUintRect, DocumentId};
use api::{ExternalImageData, ExternalImageId};
-use api::{ImageFormat, PipelineId};
+use api::ImageFormat;
use api::DebugCommand;
use device::TextureFilter;
+use renderer::PipelineInfo;
use gpu_cache::GpuCacheUpdateList;
use fxhash::FxHasher;
use profiler::BackendProfileCounters;
use std::{usize, i32};
use std::collections::{HashMap, HashSet};
use std::f32;
use std::hash::BuildHasherDefault;
use std::path::PathBuf;
@@ -126,34 +127,36 @@ impl TextureUpdateList {
#[inline]
pub fn push(&mut self, update: TextureUpdate) {
self.updates.push(update);
}
}
/// Mostly wraps a tiling::Frame, adding a bit of extra information.
pub struct RenderedDocument {
- /// The last rendered epoch for each pipeline present in the frame.
+ /// The pipeline info contains:
+ /// - The last rendered epoch for each pipeline present in the frame.
/// This information is used to know if a certain transformation on the layout has
/// been rendered, which is necessary for reftests.
- pub pipeline_epoch_map: FastHashMap<PipelineId, Epoch>,
+ /// - Pipelines that were removed from the scene.
+ pub pipeline_info: PipelineInfo,
/// The layers that are currently affected by the over-scrolling animation.
pub layers_bouncing_back: FastHashSet<ClipId>,
pub frame: tiling::Frame,
}
impl RenderedDocument {
pub fn new(
- pipeline_epoch_map: FastHashMap<PipelineId, Epoch>,
+ pipeline_info: PipelineInfo,
layers_bouncing_back: FastHashSet<ClipId>,
frame: tiling::Frame,
) -> Self {
RenderedDocument {
- pipeline_epoch_map,
+ pipeline_info,
layers_bouncing_back,
frame,
}
}
}
pub enum DebugOutput {
FetchDocuments(String),
--- a/gfx/webrender/src/picture.rs
+++ b/gfx/webrender/src/picture.rs
@@ -7,17 +7,17 @@ use api::{DeviceIntPoint, DeviceIntRect,
use api::{BoxShadowClipMode, LayerPoint, LayerRect, LayerVector2D, Shadow};
use api::{ClipId, PremultipliedColorF};
use box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowCacheKey};
use frame_builder::{FrameContext, FrameState, PictureState};
use gpu_cache::GpuDataRequest;
use gpu_types::{BrushImageKind, PictureType};
use prim_store::{BrushKind, BrushPrimitive, PrimitiveIndex, PrimitiveRun, PrimitiveRunLocalRect};
use render_task::{ClearMode, RenderTask, RenderTaskCacheKey};
-use render_task::{RenderTaskCacheKeyKind, RenderTaskId};
+use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
use resource_cache::CacheItem;
use scene::{FilterOpHelpers, SceneProperties};
use tiling::RenderTargetKind;
/*
A picture represents a dynamically rendered image. It consists of:
* A number of primitives that are drawn onto the picture.
@@ -313,24 +313,16 @@ impl PicturePrimitive {
}
};
prim_local_rect
}
}
}
- pub fn picture_type(&self) -> PictureType {
- match self.kind {
- PictureKind::Image { .. } => PictureType::Image,
- PictureKind::BoxShadow { .. } => PictureType::BoxShadow,
- PictureKind::TextShadow { .. } => PictureType::TextShadow,
- }
- }
-
pub fn prepare_for_render(
&mut self,
prim_index: PrimitiveIndex,
prim_screen_rect: &DeviceIntRect,
prim_local_rect: &LayerRect,
pic_state_for_children: PictureState,
pic_state: &mut PictureState,
frame_context: &FrameContext,
@@ -343,17 +335,17 @@ impl PicturePrimitive {
ref mut secondary_render_task_id,
composite_mode,
..
} => {
let content_origin = ContentOrigin::Screen(prim_screen_rect.origin);
match composite_mode {
Some(PictureCompositeMode::Filter(FilterOp::Blur(blur_radius))) => {
let picture_task = RenderTask::new_picture(
- Some(prim_screen_rect.size),
+ RenderTaskLocation::Dynamic(None, prim_screen_rect.size),
prim_index,
RenderTargetKind::Color,
content_origin,
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
pic_state_for_children.tasks,
PictureType::Image,
);
@@ -372,17 +364,17 @@ impl PicturePrimitive {
let render_task_id = frame_state.render_tasks.add(blur_render_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(PictureSurface::RenderTask(render_task_id));
}
Some(PictureCompositeMode::Filter(FilterOp::DropShadow(offset, blur_radius, color))) => {
let rect = (prim_local_rect.translate(&-offset) * content_scale).round().to_i32();
let picture_task = RenderTask::new_picture(
- Some(rect.size),
+ RenderTaskLocation::Dynamic(None, rect.size),
prim_index,
RenderTargetKind::Color,
ContentOrigin::Screen(rect.origin),
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
pic_state_for_children.tasks,
PictureType::Image,
);
@@ -402,17 +394,17 @@ impl PicturePrimitive {
*secondary_render_task_id = Some(picture_task_id);
let render_task_id = frame_state.render_tasks.add(blur_render_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(PictureSurface::RenderTask(render_task_id));
}
Some(PictureCompositeMode::MixBlend(..)) => {
let picture_task = RenderTask::new_picture(
- Some(prim_screen_rect.size),
+ RenderTaskLocation::Dynamic(None, prim_screen_rect.size),
prim_index,
RenderTargetKind::Color,
content_origin,
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
pic_state_for_children.tasks,
PictureType::Image,
);
@@ -432,34 +424,34 @@ impl PicturePrimitive {
// current render task. This most commonly occurs
// when opacity == 1.0, but can also occur on other
// filters and be a significant performance win.
if filter.is_noop() {
pic_state.tasks.extend(pic_state_for_children.tasks);
self.surface = None;
} else {
let picture_task = RenderTask::new_picture(
- Some(prim_screen_rect.size),
+ RenderTaskLocation::Dynamic(None, prim_screen_rect.size),
prim_index,
RenderTargetKind::Color,
content_origin,
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
pic_state_for_children.tasks,
PictureType::Image,
);
let render_task_id = frame_state.render_tasks.add(picture_task);
pic_state.tasks.push(render_task_id);
self.surface = Some(PictureSurface::RenderTask(render_task_id));
}
}
Some(PictureCompositeMode::Blit) => {
let picture_task = RenderTask::new_picture(
- Some(prim_screen_rect.size),
+ RenderTaskLocation::Dynamic(None, prim_screen_rect.size),
prim_index,
RenderTargetKind::Color,
content_origin,
PremultipliedColorF::TRANSPARENT,
ClearMode::Transparent,
pic_state_for_children.tasks,
PictureType::Image,
);
@@ -489,17 +481,17 @@ impl PicturePrimitive {
// Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur
// "the image that would be generated by applying to the shadow a
// Gaussian blur with a standard deviation equal to half the blur radius."
let device_radius = (blur_radius * frame_context.device_pixel_scale.0).round();
let blur_std_deviation = device_radius * 0.5;
let picture_task = RenderTask::new_picture(
- Some(cache_size),
+ RenderTaskLocation::Dynamic(None, cache_size),
prim_index,
RenderTargetKind::Color,
ContentOrigin::Local(content_rect.origin),
color.premultiplied(),
ClearMode::Transparent,
Vec::new(),
PictureType::TextShadow,
);
@@ -549,17 +541,17 @@ impl PicturePrimitive {
ClearMode::One
}
BoxShadowClipMode::Inset => {
ClearMode::Zero
}
};
let picture_task = RenderTask::new_picture(
- Some(cache_size),
+ RenderTaskLocation::Dynamic(None, cache_size),
prim_index,
RenderTargetKind::Alpha,
ContentOrigin::Local(content_rect.origin),
color.premultiplied(),
ClearMode::Zero,
Vec::new(),
PictureType::BoxShadow,
);
--- a/gfx/webrender/src/platform/macos/font.rs
+++ b/gfx/webrender/src/platform/macos/font.rs
@@ -248,17 +248,17 @@ fn new_ct_font_with_variations(cg_font:
vals.push((name, CFNumber::from(val)));
}
}
if vals.is_empty() {
return ct_font;
}
let vals_dict = CFDictionary::from_CFType_pairs(&vals);
let cg_var_font = cg_font.create_copy_from_variations(&vals_dict).unwrap();
- core_text::font::new_from_CGFont(&cg_var_font, size)
+ core_text::font::new_from_CGFont_with_variations(&cg_var_font, size, &vals_dict)
}
}
fn is_bitmap_font(ct_font: &CTFont) -> bool {
let traits = ct_font.symbolic_traits();
(traits & kCTFontColorGlyphsTrait) != 0
}
--- a/gfx/webrender/src/prim_store.rs
+++ b/gfx/webrender/src/prim_store.rs
@@ -1,33 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{AlphaType, BorderRadius, BuiltDisplayList, ClipAndScrollInfo, ClipMode};
-use api::{ColorF, ColorU, DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch};
+use api::{ColorF, DeviceIntRect, DeviceIntSize, DevicePixelScale, Epoch};
use api::{ComplexClipRegion, ExtendMode, FontRenderMode};
use api::{GlyphInstance, GlyphKey, GradientStop, ImageKey, ImageRendering, ItemRange, ItemTag};
use api::{LayerPoint, LayerRect, LayerSize, LayerToWorldTransform, LayerVector2D, LineOrientation};
-use api::{LineStyle, PremultipliedColorF, TileOffset};
+use api::{LineStyle, PremultipliedColorF};
use api::{WorldToLayerTransform, YuvColorSpace, YuvFormat};
use border::{BorderCornerInstance, BorderEdgeKind};
use clip_scroll_tree::{CoordinateSystemId};
use clip_scroll_node::ClipScrollNode;
use clip::{ClipSource, ClipSourcesHandle};
use frame_builder::{FrameContext, FrameState, PictureContext, PictureState, PrimitiveRunContext};
use glyph_rasterizer::{FontInstance, FontTransform};
use gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest,
ToGpuBlocks};
use gpu_types::{ClipChainRectIndex};
use picture::{PictureKind, PicturePrimitive};
use render_task::{BlitSource, ClipChain, ClipChainNode, ClipChainNodeIter, ClipChainNodeRef, ClipWorkItem};
use render_task::{RenderTask, RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskId};
use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
-use resource_cache::{CacheItem, ImageProperties, ResourceCache};
+use resource_cache::{CacheItem, ImageProperties, ImageRequest, ResourceCache};
use segment::SegmentBuilder;
use std::{mem, usize};
use std::rc::Rc;
use util::{MatrixHelpers, calculate_screen_bounding_rect, pack_as_float};
use util::recycle_vec;
const MIN_BRUSH_SPLIT_AREA: f32 = 128.0 * 128.0;
@@ -131,16 +131,22 @@ impl GpuCacheAddress {
pub fn as_int(&self) -> i32 {
// TODO(gw): Temporarily encode GPU Cache addresses as a single int.
// In the future, we can change the PrimitiveInstance struct
// to use 2x u16 for the vertex attribute instead of an i32.
self.v as i32 * MAX_VERTEX_TEXTURE_WIDTH as i32 + self.u as i32
}
}
+#[derive(Debug, Copy, Clone)]
+pub struct ScreenRect {
+ pub clipped: DeviceIntRect,
+ pub unclipped: DeviceIntRect,
+}
+
// TODO(gw): Pack the fields here better!
#[derive(Debug)]
pub struct PrimitiveMetadata {
pub opacity: PrimitiveOpacity,
pub clip_sources: ClipSourcesHandle,
pub prim_kind: PrimitiveKind,
pub cpu_prim_index: SpecificPrimitiveIndex,
pub gpu_location: GpuCacheHandle,
@@ -148,17 +154,17 @@ pub struct PrimitiveMetadata {
// TODO(gw): In the future, we should just pull these
// directly from the DL item, instead of
// storing them here.
pub local_rect: LayerRect,
pub local_clip_rect: LayerRect,
pub clip_chain_rect_index: ClipChainRectIndex,
pub is_backface_visible: bool,
- pub screen_rect: Option<DeviceIntRect>,
+ pub screen_rect: Option<ScreenRect>,
/// A tag used to identify this primitive outside of WebRender. This is
/// used for returning useful data during hit testing.
pub tag: Option<ItemTag>,
}
#[derive(Debug)]
pub enum BrushMaskKind {
@@ -320,22 +326,17 @@ impl BrushPrimitive {
}
// Key that identifies a unique (partial) image that is being
// stored in the render task cache.
#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ImageCacheKey {
- // TODO(gw): Consider introducing a struct that collectively
- // identifies an image in the resource cache
- // uniquely. We pass this around to a few places.
- pub image_key: ImageKey,
- pub image_rendering: ImageRendering,
- pub tile_offset: Option<TileOffset>,
+ pub request: ImageRequest,
pub texel_rect: Option<DeviceIntRect>,
}
// Where to find the texture data for an image primitive.
#[derive(Debug)]
pub enum ImageSource {
// A normal image - just reference the texture cache.
Default,
@@ -644,17 +645,17 @@ impl RadialGradientPrimitiveCpu {
#[derive(Debug, Clone)]
pub struct TextRunPrimitiveCpu {
pub font: FontInstance,
pub offset: LayerVector2D,
pub glyph_range: ItemRange<GlyphInstance>,
pub glyph_count: usize,
pub glyph_keys: Vec<GlyphKey>,
pub glyph_gpu_blocks: Vec<GpuBlockData>,
- pub shadow_color: ColorU,
+ pub shadow: bool,
}
impl TextRunPrimitiveCpu {
pub fn get_font(
&self,
device_pixel_scale: DevicePixelScale,
transform: Option<&LayerToWorldTransform>,
) -> FontInstance {
@@ -665,28 +666,16 @@ impl TextRunPrimitiveCpu {
font.render_mode = font.render_mode.limit_by(FontRenderMode::Alpha);
} else {
font.transform = FontTransform::from(transform).quantize();
}
}
font
}
- pub fn is_shadow(&self) -> bool {
- self.shadow_color.a != 0
- }
-
- pub fn get_color(&self) -> ColorF {
- ColorF::from(if self.is_shadow() {
- self.shadow_color
- } else {
- self.font.color
- })
- }
-
fn prepare_for_render(
&mut self,
resource_cache: &mut ResourceCache,
device_pixel_scale: DevicePixelScale,
transform: Option<&LayerToWorldTransform>,
display_list: &BuiltDisplayList,
gpu_cache: &mut GpuCache,
) {
@@ -726,17 +715,17 @@ impl TextRunPrimitiveCpu {
self.glyph_gpu_blocks.push(gpu_block.into());
}
}
resource_cache.request_glyphs(font, &self.glyph_keys, gpu_cache);
}
fn write_gpu_blocks(&self, request: &mut GpuDataRequest) {
- request.push(self.get_color().premultiplied());
+ request.push(ColorF::from(self.font.color).premultiplied());
// this is the only case where we need to provide plain color to GPU
let bg_color = ColorF::from(self.font.bg_color);
request.push([bg_color.r, bg_color.g, bg_color.b, 1.0]);
request.push([
self.offset.x,
self.offset.y,
0.0,
0.0,
@@ -1153,17 +1142,19 @@ impl PrimitiveStore {
) {
let metadata = &mut self.cpu_metadata[prim_index.0];
match metadata.prim_kind {
PrimitiveKind::Border => {}
PrimitiveKind::Picture => {
self.cpu_pictures[metadata.cpu_prim_index.0]
.prepare_for_render(
prim_index,
- metadata.screen_rect.as_ref().expect("bug: trying to draw an off-screen picture!?"),
+ &metadata.screen_rect
+ .expect("bug: trying to draw an off-screen picture!?")
+ .clipped,
&metadata.local_rect,
pic_state_for_children,
pic_state,
frame_context,
frame_state,
);
}
PrimitiveKind::TextRun => {
@@ -1181,17 +1172,17 @@ impl PrimitiveStore {
pic_context.display_list,
frame_state.gpu_cache,
);
}
PrimitiveKind::Image => {
let image_cpu = &mut self.cpu_images[metadata.cpu_prim_index.0];
let image_properties = frame_state
.resource_cache
- .get_image_properties(image_cpu.key.image_key);
+ .get_image_properties(image_cpu.key.request.key);
// TODO(gw): Add image.rs and move this code out to a separate
// source file as it gets more complicated, and we
// start pre-rendering images for other reasons.
if let Some(image_properties) = image_properties {
// See if this image has been updated since we last hit this code path.
// If so, we need to (at least) update the opacity, and also rebuild
@@ -1216,84 +1207,98 @@ impl PrimitiveStore {
}
None => {
// Simple image - just use a normal texture cache entry.
ImageSource::Default
}
};
}
- // TODO(gw): Don't actually need this in cached source mode if
- // the cache item is still valid...
- frame_state.resource_cache.request_image(
- image_cpu.key.image_key,
- image_cpu.key.image_rendering,
- image_cpu.key.tile_offset,
- frame_state.gpu_cache,
- );
+ // Set if we need to request the source image from the cache this frame.
+ let mut request_source_image = false;
// Every frame, for cached items, we need to request the render
// task cache item. The closure will be invoked on the first
// time through, and any time the render task output has been
// evicted from the texture cache.
- if let ImageSource::Cache { size, ref mut item } = image_cpu.source {
- let key = image_cpu.key;
+ match image_cpu.source {
+ ImageSource::Cache { size, ref mut item } => {
+ let key = image_cpu.key;
- // Request a pre-rendered image task.
- *item = frame_state.resource_cache.request_render_task(
- RenderTaskCacheKey {
- size,
- kind: RenderTaskCacheKeyKind::Image(key),
- },
- frame_state.gpu_cache,
- frame_state.render_tasks,
- |render_tasks| {
- // Create a task to blit from the texture cache to
- // a normal transient render task surface. This will
- // copy only the sub-rect, if specified.
- let cache_to_target_task = RenderTask::new_blit(
+ // Request a pre-rendered image task.
+ *item = frame_state.resource_cache.request_render_task(
+ RenderTaskCacheKey {
size,
- BlitSource::Image {
- key,
- },
- );
- let cache_to_target_task_id = render_tasks.add(cache_to_target_task);
+ kind: RenderTaskCacheKeyKind::Image(key),
+ },
+ frame_state.gpu_cache,
+ frame_state.render_tasks,
+ |render_tasks| {
+ // We need to render the image cache this frame,
+ // so will need access to the source texture.
+ request_source_image = true;
+
+ // Create a task to blit from the texture cache to
+ // a normal transient render task surface. This will
+ // copy only the sub-rect, if specified.
+ let cache_to_target_task = RenderTask::new_blit(
+ size,
+ BlitSource::Image {
+ key,
+ },
+ );
+ let cache_to_target_task_id = render_tasks.add(cache_to_target_task);
- // Create a task to blit the rect from the child render
- // task above back into the right spot in the persistent
- // render target cache.
- let target_to_cache_task = RenderTask::new_blit(
- size,
- BlitSource::RenderTask {
- task_id: cache_to_target_task_id,
- },
- );
- let target_to_cache_task_id = render_tasks.add(target_to_cache_task);
+ // Create a task to blit the rect from the child render
+ // task above back into the right spot in the persistent
+ // render target cache.
+ let target_to_cache_task = RenderTask::new_blit(
+ size,
+ BlitSource::RenderTask {
+ task_id: cache_to_target_task_id,
+ },
+ );
+ let target_to_cache_task_id = render_tasks.add(target_to_cache_task);
+
+ // Hook this into the render task tree at the right spot.
+ pic_state.tasks.push(target_to_cache_task_id);
- // Hook this into the render task tree at the right spot.
- pic_state.tasks.push(target_to_cache_task_id);
+ // Pass the image opacity, so that the cached render task
+ // item inherits the same opacity properties.
+ (target_to_cache_task_id, [0.0; 3], image_properties.descriptor.is_opaque)
+ }
+ );
+ }
+ ImageSource::Default => {
+ // Normal images just reference the source texture each frame.
+ request_source_image = true;
+ }
+ }
- // Pass the image opacity, so that the cached render task
- // item inherits the same opacity properties.
- (target_to_cache_task_id, [0.0; 3], image_properties.descriptor.is_opaque)
- }
+ // Request source image from the texture cache, if required.
+ if request_source_image {
+ frame_state.resource_cache.request_image(
+ image_cpu.key.request,
+ frame_state.gpu_cache,
);
}
}
}
PrimitiveKind::YuvImage => {
let image_cpu = &mut self.cpu_yuv_images[metadata.cpu_prim_index.0];
let channel_num = image_cpu.format.get_plane_num();
debug_assert!(channel_num <= 3);
for channel in 0 .. channel_num {
frame_state.resource_cache.request_image(
- image_cpu.yuv_key[channel],
- image_cpu.image_rendering,
- None,
+ ImageRequest {
+ key: image_cpu.yuv_key[channel],
+ rendering: image_cpu.image_rendering,
+ tile: None,
+ },
frame_state.gpu_cache,
);
}
}
PrimitiveKind::Brush |
PrimitiveKind::AlignedGradient |
PrimitiveKind::AngleGradient |
PrimitiveKind::RadialGradient => {}
@@ -1814,17 +1819,24 @@ impl PrimitiveStore {
&local_rect,
frame_context.device_pixel_scale,
);
let clip_bounds = match prim_run_context.clip_chain {
Some(ref node) => node.combined_outer_screen_rect,
None => frame_context.screen_rect,
};
- metadata.screen_rect = screen_bounding_rect.intersection(&clip_bounds);
+ metadata.screen_rect = screen_bounding_rect
+ .intersection(&clip_bounds)
+ .map(|clipped| {
+ ScreenRect {
+ clipped,
+ unclipped: screen_bounding_rect,
+ }
+ });
if metadata.screen_rect.is_none() && pic_context.perform_culling {
return None;
}
metadata.clip_chain_rect_index = prim_run_context.clip_chain_rect_index;
(local_rect, screen_bounding_rect)
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -14,17 +14,17 @@ use api::channel::{PayloadSender, Payloa
use api::CaptureBits;
#[cfg(feature = "replay")]
use api::CapturedDocument;
#[cfg(feature = "debugger")]
use debug_server;
use frame::FrameContext;
use frame_builder::{FrameBuilder, FrameBuilderConfig};
use gpu_cache::GpuCache;
-use hit_test::HitTester;
+use hit_test::{HitTest, HitTester};
use internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
use profiler::{BackendProfileCounters, IpcProfileCounters, ResourceProfileCounters};
use record::ApiRecordingReceiver;
use resource_cache::ResourceCache;
#[cfg(feature = "replay")]
use resource_cache::PlainCacheOwn;
#[cfg(any(feature = "capture", feature = "replay"))]
use resource_cache::PlainResources;
@@ -32,20 +32,20 @@ use scene::Scene;
#[cfg(feature = "serialize")]
use serde::{Serialize, Deserialize};
#[cfg(feature = "debugger")]
use serde_json;
#[cfg(any(feature = "capture", feature = "replay"))]
use std::path::PathBuf;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
use std::sync::mpsc::Sender;
+use std::mem::replace;
use std::u32;
use time::precise_time_ns;
-
#[cfg_attr(feature = "capture", derive(Clone, Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
struct DocumentView {
window_size: DeviceUintSize,
inner_rect: DeviceUintRect,
layer: DocumentLayer,
pan: DeviceIntPoint,
device_pixel_ratio: f32,
@@ -67,16 +67,22 @@ struct Document {
scene: Scene,
view: DocumentView,
frame_ctx: FrameContext,
// the `Option` here is only to deal with borrow checker
frame_builder: Option<FrameBuilder>,
// A set of pipelines that the caller has requested be
// made available as output textures.
output_pipelines: FastHashSet<PipelineId>,
+ // The pipeline removal notifications that will be sent in the next frame.
+ // Because of async scene building, removed pipelines should not land here
+ // as soon as the render backend receives a DocumentMsg::RemovePipeline.
+ // Instead, the notification should be added to this list when the first
+ // scene that does not contain the pipeline becomes current.
+ removed_pipelines: Vec<PipelineId>,
// 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: Option<bool>,
// A helper flag to prevent any hit-tests from happening between calls
// to build_scene and rendering the document. In between these two calls,
@@ -99,16 +105,17 @@ impl Document {
) -> Self {
let render_on_scroll = if enable_render_on_scroll {
Some(false)
} else {
None
};
Document {
scene: Scene::new(),
+ removed_pipelines: Vec::new(),
view: DocumentView {
window_size,
inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), window_size),
layer,
pan: DeviceIntPoint::zero(),
page_zoom_factor: 1.0,
pinch_zoom_factor: 1.0,
device_pixel_ratio: default_device_pixel_ratio,
@@ -128,16 +135,17 @@ impl Document {
self.frame_builder.take().unwrap(),
&self.scene,
resource_cache,
self.view.window_size,
self.view.inner_rect,
self.view.accumulated_scale_factor(),
&self.output_pipelines,
);
+ self.removed_pipelines.extend(self.scene.removed_pipelines.drain(..));
self.frame_builder = Some(frame_builder);
}
fn render(
&mut self,
resource_cache: &mut ResourceCache,
gpu_cache: &mut GpuCache,
resource_profile: &mut ResourceProfileCounters,
@@ -150,16 +158,17 @@ impl Document {
gpu_cache,
&self.scene.pipelines,
accumulated_scale_factor,
self.view.layer,
pan,
&mut resource_profile.texture_cache,
&mut resource_profile.gpu_cache,
&self.scene.properties,
+ replace(&mut self.removed_pipelines, Vec::new()),
);
self.hit_tester = Some(hit_tester);
rendered_document
}
}
@@ -414,17 +423,19 @@ impl RenderBackend {
render: should_render,
composite: should_render,
..DocumentOps::nop()
}
}
DocumentMsg::HitTest(pipeline_id, point, flags, tx) => {
let result = match doc.hit_tester {
- Some(ref hit_tester) => hit_tester.hit_test(pipeline_id, point, flags),
+ Some(ref hit_tester) => {
+ hit_tester.hit_test(HitTest::new(pipeline_id, point, flags))
+ }
None => HitTestResult { items: Vec::new() },
};
tx.send(result).unwrap();
DocumentOps::nop()
}
DocumentMsg::ScrollNodeWithId(origin, id, clamp) => {
profile_scope!("ScrollNodeWithScrollId");
@@ -971,24 +982,25 @@ impl RenderBackend {
let mut doc = Document {
scene,
view,
frame_ctx: FrameContext::new(self.frame_config.clone()),
frame_builder: Some(FrameBuilder::empty()),
output_pipelines: FastHashSet::default(),
render_on_scroll: None,
render_on_hittest: false,
+ removed_pipelines: Vec::new(),
hit_tester: None,
};
let frame_name = format!("frame-{}-{}", (id.0).0, id.1);
let render_doc = match CaptureConfig::deserialize::<Frame, _>(root, frame_name) {
Some(frame) => {
info!("\tloaded a built frame with {} passes", frame.passes.len());
- doc.frame_ctx.make_rendered_document(frame)
+ doc.frame_ctx.make_rendered_document(frame, Vec::new())
}
None => {
doc.build_scene(&mut self.resource_cache);
doc.render(
&mut self.resource_cache,
&mut self.gpu_cache,
&mut profile_counters.resources,
)
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -162,17 +162,17 @@ impl RenderTaskTree {
let task = &self.tasks[id.0 as usize];
for child in &task.children {
self.assign_to_passes(*child, pass_index - 1, passes);
}
// Sanity check - can be relaxed if needed
match task.location {
- RenderTaskLocation::Fixed => {
+ RenderTaskLocation::Fixed(..) => {
debug_assert!(pass_index == passes.len() - 1);
}
RenderTaskLocation::Dynamic(..) |
RenderTaskLocation::TextureCache(..) => {
debug_assert!(pass_index < passes.len() - 1);
}
}
@@ -213,17 +213,17 @@ impl ops::IndexMut<RenderTaskId> for Ren
&mut self.tasks[id.0 as usize]
}
}
#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum RenderTaskLocation {
- Fixed,
+ Fixed(DeviceIntRect),
Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize),
TextureCache(SourceTexture, i32, DeviceIntRect),
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ClipWorkItem {
@@ -331,30 +331,25 @@ pub struct RenderTask {
pub children: Vec<RenderTaskId>,
pub kind: RenderTaskKind,
pub clear_mode: ClearMode,
pub pass_index: Option<RenderPassIndex>,
}
impl RenderTask {
pub fn new_picture(
- size: Option<DeviceIntSize>,
+ location: RenderTaskLocation,
prim_index: PrimitiveIndex,
target_kind: RenderTargetKind,
content_origin: ContentOrigin,
color: PremultipliedColorF,
clear_mode: ClearMode,
children: Vec<RenderTaskId>,
pic_type: PictureType,
) -> Self {
- let location = match size {
- Some(size) => RenderTaskLocation::Dynamic(None, size),
- None => RenderTaskLocation::Fixed,
- };
-
RenderTask {
children,
location,
kind: RenderTaskKind::Picture(PictureTask {
prim_index,
target_kind,
content_origin,
color,
@@ -596,26 +591,26 @@ impl RenderTask {
data2[2],
data2[3],
]
}
}
pub fn get_dynamic_size(&self) -> DeviceIntSize {
match self.location {
- RenderTaskLocation::Fixed => DeviceIntSize::zero(),
+ RenderTaskLocation::Fixed(..) => DeviceIntSize::zero(),
RenderTaskLocation::Dynamic(_, size) => size,
RenderTaskLocation::TextureCache(_, _, rect) => rect.size,
}
}
pub fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) {
match self.location {
- RenderTaskLocation::Fixed => {
- (DeviceIntRect::zero(), RenderTargetIndex(0))
+ RenderTaskLocation::Fixed(rect) => {
+ (rect, RenderTargetIndex(0))
}
// Previously, we only added render tasks after the entire
// primitive chain was determined visible. This meant that
// we could assert any render task in the list was also
// allocated (assigned to passes). Now, we add render
// tasks earlier, and the picture they belong to may be
// culled out later, so we can't assert that the task
// has been allocated.
@@ -819,17 +814,17 @@ impl RenderTaskCache {
// Select the right texture page to allocate from.
let image_format = match render_task.target_kind() {
RenderTargetKind::Color => ImageFormat::BGRA8,
RenderTargetKind::Alpha => ImageFormat::R8,
};
// Find out what size to alloc in the texture cache.
let size = match render_task.location {
- RenderTaskLocation::Fixed |
+ RenderTaskLocation::Fixed(..) |
RenderTaskLocation::TextureCache(..) => {
panic!("BUG: dynamic task was expected");
}
RenderTaskLocation::Dynamic(_, size) => size,
};
// TODO(gw): Support color tasks in the texture cache,
// and perhaps consider if we can determine
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -257,22 +257,21 @@ impl BatchKind {
}
bitflags! {
#[derive(Default)]
pub struct DebugFlags: u32 {
const PROFILER_DBG = 1 << 0;
const RENDER_TARGET_DBG = 1 << 1;
const TEXTURE_CACHE_DBG = 1 << 2;
- const ALPHA_PRIM_DBG = 1 << 3;
- const GPU_TIME_QUERIES = 1 << 4;
- const GPU_SAMPLE_QUERIES= 1 << 5;
- const DISABLE_BATCHING = 1 << 6;
- const EPOCHS = 1 << 7;
- const COMPACT_PROFILER = 1 << 8;
+ const GPU_TIME_QUERIES = 1 << 3;
+ const GPU_SAMPLE_QUERIES= 1 << 4;
+ const DISABLE_BATCHING = 1 << 5;
+ const EPOCHS = 1 << 6;
+ const COMPACT_PROFILER = 1 << 7;
}
}
fn flag_changed(before: DebugFlags, after: DebugFlags, select: DebugFlags) -> Option<bool> {
if before & select != after & select {
Some(after.contains(select))
} else {
None
@@ -1646,17 +1645,17 @@ pub struct Renderer {
node_data_texture: VertexDataTexture,
local_clip_rects_texture: VertexDataTexture,
render_task_texture: VertexDataTexture,
gpu_cache_texture: CacheTexture,
gpu_cache_frame_id: FrameId,
- pipeline_epoch_map: FastHashMap<PipelineId, Epoch>,
+ pipeline_info: PipelineInfo,
// Manages and resolves source textures IDs to real texture IDs.
texture_resolver: SourceTextureResolver,
// A PBO used to do asynchronous texture cache uploads.
texture_cache_upload_pbo: PBO,
dither_matrix_texture: Option<Texture>,
@@ -2301,17 +2300,17 @@ impl Renderer {
last_time: 0,
gpu_profile,
prim_vao,
blur_vao,
clip_vao,
node_data_texture,
local_clip_rects_texture,
render_task_texture,
- pipeline_epoch_map: FastHashMap::default(),
+ pipeline_info: PipelineInfo::default(),
dither_matrix_texture,
external_image_handler: None,
output_image_handler: None,
output_targets: FastHashMap::default(),
cpu_profiles: VecDeque::new(),
gpu_profiles: VecDeque::new(),
gpu_cache_texture,
gpu_cache_frame_id: FrameId::new(0),
@@ -2348,23 +2347,21 @@ impl Renderer {
color_space: YuvColorSpace,
) -> usize {
((buffer_kind as usize) * YUV_FORMATS.len() + (format as usize)) * YUV_COLOR_SPACES.len() +
(color_space as usize)
}
/// Returns the Epoch of the current frame in a pipeline.
pub fn current_epoch(&self, pipeline_id: PipelineId) -> Option<Epoch> {
- self.pipeline_epoch_map.get(&pipeline_id).cloned()
+ self.pipeline_info.epochs.get(&pipeline_id).cloned()
}
- /// Returns a HashMap containing the pipeline ids that have been received by the renderer and
- /// their respective epochs since the last time the method was called.
- pub fn flush_rendered_epochs(&mut self) -> FastHashMap<PipelineId, Epoch> {
- mem::replace(&mut self.pipeline_epoch_map, FastHashMap::default())
+ pub fn flush_pipeline_info(&mut self) -> PipelineInfo {
+ mem::replace(&mut self.pipeline_info, PipelineInfo::default())
}
// update the program cache with new binaries, e.g. when some of the lazy loaded
// shader programs got activated in the mean time
pub fn update_program_cache(&mut self, cached_programs: Rc<ProgramCache>) {
self.device.update_program_cache(cached_programs);
}
@@ -2373,25 +2370,26 @@ impl Renderer {
/// Should be called before `render()`, as texture cache updates are done here.
pub fn update(&mut self) {
profile_scope!("update");
// Pull any pending results and return the most recent.
while let Ok(msg) = self.result_rx.try_recv() {
match msg {
ResultMsg::PublishDocument(
document_id,
- doc,
+ mut doc,
texture_update_list,
profile_counters,
) => {
// Update the list of available epochs for use during reftests.
// This is a workaround for https://github.com/servo/servo/issues/13149.
- for (pipeline_id, epoch) in &doc.pipeline_epoch_map {
- self.pipeline_epoch_map.insert(*pipeline_id, *epoch);
+ for (pipeline_id, epoch) in &doc.pipeline_info.epochs {
+ self.pipeline_info.epochs.insert(*pipeline_id, *epoch);
}
+ self.pipeline_info.removed_pipelines.extend(doc.pipeline_info.removed_pipelines.drain(..));
// Add a new document to the active set, expressed as a `Vec` in order
// to re-order based on `DocumentLayer` during rendering.
match self.active_documents.iter().position(|&(id, _)| id == document_id) {
Some(pos) => {
// If the document we are replacing must be drawn
// (in order to update the texture cache), issue
// a render just to off-screen targets.
@@ -2562,45 +2560,45 @@ impl Renderer {
"Vertical Blur",
target.vertical_blurs.len(),
);
debug_target.add(
debug_server::BatchKind::Cache,
"Horizontal Blur",
target.horizontal_blurs.len(),
);
- for (_, batch) in &target.alpha_batcher.text_run_cache_prims {
- debug_target.add(
- debug_server::BatchKind::Cache,
- "Text Shadow",
- batch.len(),
- );
- }
-
- for batch in target
- .alpha_batcher
- .batch_list
- .opaque_batch_list
- .batches
- .iter()
- .rev()
- {
- debug_target.add(
- debug_server::BatchKind::Opaque,
- batch.key.kind.debug_name(),
- batch.instances.len(),
- );
- }
-
- for batch in &target.alpha_batcher.batch_list.alpha_batch_list.batches {
- debug_target.add(
- debug_server::BatchKind::Alpha,
- batch.key.kind.debug_name(),
- batch.instances.len(),
- );
+
+ for alpha_batch_container in &target.alpha_batch_containers {
+ for (_, batch) in &alpha_batch_container.text_run_cache_prims {
+ debug_target.add(
+ debug_server::BatchKind::Cache,
+ "Text Shadow",
+ batch.len(),
+ );
+ }
+
+ for batch in alpha_batch_container
+ .opaque_batches
+ .iter()
+ .rev() {
+ debug_target.add(
+ debug_server::BatchKind::Opaque,
+ batch.key.kind.debug_name(),
+ batch.instances.len(),
+ );
+ }
+
+ for batch in &alpha_batch_container
+ .alpha_batches {
+ debug_target.add(
+ debug_server::BatchKind::Alpha,
+ batch.key.kind.debug_name(),
+ batch.instances.len(),
+ );
+ }
}
debug_target
}
#[cfg(feature = "debugger")]
fn debug_texture_cache_target(target: &TextureCacheRenderTarget) -> debug_server::Target {
let mut debug_target = debug_server::Target::new("Texture Cache");
@@ -2670,19 +2668,16 @@ impl Renderer {
self.set_debug_flag(DebugFlags::PROFILER_DBG, enable);
}
DebugCommand::EnableTextureCacheDebug(enable) => {
self.set_debug_flag(DebugFlags::TEXTURE_CACHE_DBG, enable);
}
DebugCommand::EnableRenderTargetDebug(enable) => {
self.set_debug_flag(DebugFlags::RENDER_TARGET_DBG, enable);
}
- DebugCommand::EnableAlphaRectsDebug(enable) => {
- self.set_debug_flag(DebugFlags::ALPHA_PRIM_DBG, enable);
- }
DebugCommand::EnableGpuTimeQueries(enable) => {
self.set_debug_flag(DebugFlags::GPU_TIME_QUERIES, enable);
}
DebugCommand::EnableGpuSampleQueries(enable) => {
self.set_debug_flag(DebugFlags::GPU_SAMPLE_QUERIES, enable);
}
DebugCommand::EnableDualSourceBlending(_) => {
panic!("Should be handled by render backend");
@@ -3162,16 +3157,17 @@ impl Renderer {
&mut self,
key: &BatchKey,
instances: &[PrimitiveInstance],
projection: &Transform3D<f32>,
render_tasks: &RenderTaskTree,
render_target: Option<(&Texture, i32)>,
framebuffer_size: DeviceUintSize,
stats: &mut RendererStats,
+ scissor_rect: Option<DeviceIntRect>,
) {
match key.kind {
BatchKind::Composite { .. } => {
self.ps_composite.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
}
BatchKind::HardwareComposite => {
self.ps_hw_composite
.bind(&mut self.device, projection, 0, &mut self.renderer_errors);
@@ -3298,16 +3294,20 @@ impl Renderer {
&mut self.renderer_errors,
);
}
},
};
// Handle special case readback for composites.
if let BatchKind::Composite { task_id, source_id, backdrop_id } = key.kind {
+ if scissor_rect.is_some() {
+ self.device.disable_scissor();
+ }
+
// composites can't be grouped together because
// they may overlap and affect each other.
debug_assert_eq!(instances.len(), 1);
let cache_texture = self.texture_resolver
.resolve(&SourceTexture::CacheRGBA8)
.unwrap();
// Before submitting the composite batch, do the
@@ -3356,16 +3356,20 @@ impl Renderer {
}
self.device.bind_read_target(render_target);
self.device.blit_render_target(src, dest);
// Restore draw target to current pass render target + layer.
// Note: leaving the viewport unchanged, it's not a part of FBO state
self.device.bind_draw_target(render_target, None);
+
+ if scissor_rect.is_some() {
+ self.device.enable_scissor();
+ }
}
let _timer = self.gpu_profile.start_timer(key.kind.gpu_sampler_tag());
self.draw_instanced_batch(
instances,
VertexArrayKind::Primitive,
&key.textures,
stats
@@ -3543,92 +3547,92 @@ impl Renderer {
self.handle_scaling(render_tasks, &target.scalings, SourceTexture::CacheRGBA8);
// Draw any textrun caches for this target. For now, this
// is only used to cache text runs that are to be blurred
// for shadow support. In the future it may be worth
// considering using this for (some) other text runs, since
// it removes the overhead of submitting many small glyphs
// to multiple tiles in the normal text run case.
- if !target.alpha_batcher.text_run_cache_prims.is_empty() {
- self.device.set_blend(true);
- self.device.set_blend_mode_premultiplied_alpha();
-
- let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_TEXT_RUN);
- self.cs_text_run
- .bind(&mut self.device, projection, 0, &mut self.renderer_errors);
- for (texture_id, instances) in &target.alpha_batcher.text_run_cache_prims {
- self.draw_instanced_batch(
- instances,
- VertexArrayKind::Primitive,
- &BatchTextures::color(*texture_id),
- stats,
- );
+ for alpha_batch_container in &target.alpha_batch_containers {
+ if !alpha_batch_container.text_run_cache_prims.is_empty() {
+ self.device.set_blend(true);
+ self.device.set_blend_mode_premultiplied_alpha();
+
+ let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_TEXT_RUN);
+ self.cs_text_run
+ .bind(&mut self.device, projection, 0, &mut self.renderer_errors);
+ for (texture_id, instances) in &alpha_batch_container.text_run_cache_prims {
+ self.draw_instanced_batch(
+ instances,
+ VertexArrayKind::Primitive,
+ &BatchTextures::color(*texture_id),
+ stats,
+ );
+ }
}
}
//TODO: record the pixel count for cached primitives
- if !target.alpha_batcher.is_empty() {
- let _gl = self.gpu_profile.start_marker("alpha batches");
+ if target.needs_depth() {
+ let _gl = self.gpu_profile.start_marker("opaque batches");
+ let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE);
self.device.set_blend(false);
- let mut prev_blend_mode = BlendMode::None;
-
- if target.needs_depth() {
- let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE);
-
- //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 alpha_batch_container in &target.alpha_batch_containers {
+ if let Some(target_rect) = alpha_batch_container.target_rect {
+ self.device.enable_scissor();
+ self.device.set_scissor_rect(target_rect);
+ }
// Draw opaque batches front-to-back for maximum
// z-buffer efficiency!
- for batch in target
- .alpha_batcher
- .batch_list
- .opaque_batch_list
- .batches
+ for batch in alpha_batch_container
+ .opaque_batches
.iter()
.rev()
{
self.submit_batch(
&batch.key,
&batch.instances,
&projection,
render_tasks,
render_target,
target_size,
stats,
+ alpha_batch_container.target_rect,
);
}
- self.device.disable_depth_write();
- self.gpu_profile.finish_sampler(opaque_sampler);
+ if alpha_batch_container.target_rect.is_some() {
+ self.device.disable_scissor();
+ }
}
- let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
-
- for batch in &target.alpha_batcher.batch_list.alpha_batch_list.batches {
- if self.debug_flags.contains(DebugFlags::ALPHA_PRIM_DBG) {
- let color = match batch.key.blend_mode {
- BlendMode::None => debug_colors::BLACK,
- BlendMode::Alpha |
- BlendMode::PremultipliedAlpha => debug_colors::GREY,
- BlendMode::PremultipliedDestOut => debug_colors::SALMON,
- BlendMode::SubpixelConstantTextColor(..) => debug_colors::GREEN,
- BlendMode::SubpixelVariableTextColor => debug_colors::RED,
- BlendMode::SubpixelWithBgColor => debug_colors::BLUE,
- BlendMode::SubpixelDualSource => debug_colors::YELLOW,
- }.into();
- for item_rect in &batch.item_rects {
- self.debug.add_rect(item_rect, color);
- }
- }
-
+ self.device.disable_depth_write();
+ self.gpu_profile.finish_sampler(opaque_sampler);
+ }
+
+ let _gl = self.gpu_profile.start_marker("alpha batches");
+ let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT);
+ self.device.set_blend(false);
+ let mut prev_blend_mode = BlendMode::None;
+
+ for alpha_batch_container in &target.alpha_batch_containers {
+ if let Some(target_rect) = alpha_batch_container.target_rect {
+ self.device.enable_scissor();
+ self.device.set_scissor_rect(target_rect);
+ }
+
+ for batch in &alpha_batch_container.alpha_batches {
match batch.key.kind {
BatchKind::Transformable(transform_kind, TransformBatchKind::TextRun(glyph_format)) => {
// Text run batches are handled by this special case branch.
// In the case of subpixel text, we draw it as a two pass
// effect, to ensure we can apply clip masks correctly.
// In the future, there are several optimizations available:
// 1) Use dual source blending where available (almost all recent hardware).
// 2) Use frame buffer fetch where available (most modern hardware).
@@ -3832,26 +3836,31 @@ impl Renderer {
self.submit_batch(
&batch.key,
&batch.instances,
&projection,
render_tasks,
render_target,
target_size,
stats,
+ alpha_batch_container.target_rect,
);
}
}
}
- self.device.disable_depth();
- self.device.set_blend(false);
- self.gpu_profile.finish_sampler(transparent_sampler);
+ if alpha_batch_container.target_rect.is_some() {
+ self.device.disable_scissor();
+ }
}
+ self.device.disable_depth();
+ self.device.set_blend(false);
+ self.gpu_profile.finish_sampler(transparent_sampler);
+
// For any registered image outputs on this render target,
// get the texture from caller and blit it.
for output in &target.outputs {
let handler = self.output_image_handler
.as_mut()
.expect("Found output image, but no handler set!");
if let Some((texture_id, output_size)) = handler.lock(output.pipeline_id) {
let fbo_id = match self.output_targets.entry(texture_id) {
@@ -4620,17 +4629,17 @@ impl Renderer {
return;
}
let dy = self.debug.line_height();
let x0: f32 = 30.0;
let y0: f32 = 30.0;
let mut y = y0;
let mut text_width = 0.0;
- for (pipeline, epoch) in &self.pipeline_epoch_map {
+ for (pipeline, epoch) in &self.pipeline_info.epochs {
y += dy;
let w = self.debug.add_text(
x0, y,
&format!("{:?}: {:?}", pipeline, epoch),
ColorU::new(255, 255, 0, 255),
).size.width;
text_width = f32::max(text_width, w);
}
@@ -4935,16 +4944,22 @@ impl OutputImageHandler for () {
fn lock(&mut self, _: PipelineId) -> Option<(u32, DeviceIntSize)> {
None
}
fn unlock(&mut self, _: PipelineId) {
unreachable!()
}
}
+#[derive(Default)]
+pub struct PipelineInfo {
+ pub epochs: FastHashMap<PipelineId, Epoch>,
+ pub removed_pipelines: Vec<PipelineId>,
+}
+
impl Renderer {
#[cfg(feature = "capture")]
fn save_texture(
texture: &Texture, name: &str, root: &PathBuf, device: &mut Device
) -> PlainTexture {
use std::fs;
use std::io::Write;
--- a/gfx/webrender/src/resource_cache.rs
+++ b/gfx/webrender/src/resource_cache.rs
@@ -204,20 +204,20 @@ where
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
-struct ImageRequest {
- key: ImageKey,
- rendering: ImageRendering,
- tile: Option<TileOffset>,
+pub struct ImageRequest {
+ pub key: ImageKey,
+ pub rendering: ImageRendering,
+ pub tile: Option<TileOffset>,
}
impl Into<BlobImageRequest> for ImageRequest {
fn into(self) -> BlobImageRequest {
BlobImageRequest {
key: self.key,
tile: self.tile,
}
@@ -533,33 +533,26 @@ impl ResourceCache {
warn!("Delete the non-exist key");
debug!("key={:?}", image_key);
}
}
}
pub fn request_image(
&mut self,
- key: ImageKey,
- rendering: ImageRendering,
- tile: Option<TileOffset>,
+ request: ImageRequest,
gpu_cache: &mut GpuCache,
) {
debug_assert_eq!(self.state, State::AddResources);
- let request = ImageRequest {
- key,
- rendering,
- tile,
- };
- let template = match self.resources.image_templates.get(key) {
+ let template = match self.resources.image_templates.get(request.key) {
Some(template) => template,
None => {
warn!("ERROR: Trying to render deleted / non-existent key");
- debug!("key={:?}", key);
+ debug!("key={:?}", request.key);
return
}
};
// Images that don't use the texture cache can early out.
if !template.data.uses_texture_cache() {
return;
}
@@ -729,36 +722,29 @@ impl ResourceCache {
pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
self.glyph_rasterizer.get_glyph_index(font_key, ch)
}
#[inline]
pub fn get_cached_image(
&self,
- image_key: ImageKey,
- image_rendering: ImageRendering,
- tile: Option<TileOffset>,
+ request: ImageRequest,
) -> Result<CacheItem, ()> {
debug_assert_eq!(self.state, State::QueryResources);
- let key = ImageRequest {
- key: image_key,
- rendering: image_rendering,
- tile,
- };
// TODO(Jerry): add a debug option to visualize the corresponding area for
// the Err() case of CacheItem.
- match *self.cached_images.get(&key) {
- Ok(ref image_info) => {
- Ok(self.texture_cache.get(&image_info.texture_cache_handle))
- }
- Err(_) => {
- Err(())
- }
+ match *self.cached_images.get(&request) {
+ Ok(ref image_info) => {
+ Ok(self.texture_cache.get(&image_info.texture_cache_handle))
+ }
+ Err(_) => {
+ Err(())
+ }
}
}
pub fn get_image_properties(&self, image_key: ImageKey) -> Option<ImageProperties> {
let image_template = &self.resources.image_templates.get(image_key);
image_template.map(|image_template| {
let external_image = match image_template.data {
--- a/gfx/webrender/src/scene.rs
+++ b/gfx/webrender/src/scene.rs
@@ -96,24 +96,26 @@ pub struct ScenePipeline {
}
/// A complete representation of the layout bundling visible pipelines together.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct Scene {
pub root_pipeline_id: Option<PipelineId>,
pub pipelines: FastHashMap<PipelineId, ScenePipeline>,
+ pub removed_pipelines: Vec<PipelineId>,
pub properties: SceneProperties,
}
impl Scene {
pub fn new() -> Self {
Scene {
root_pipeline_id: None,
pipelines: FastHashMap::default(),
+ removed_pipelines: Vec::new(),
properties: SceneProperties::new(),
}
}
pub fn set_root_pipeline_id(&mut self, pipeline_id: PipelineId) {
self.root_pipeline_id = Some(pipeline_id);
}
@@ -138,16 +140,17 @@ impl Scene {
self.pipelines.insert(pipeline_id, new_pipeline);
}
pub fn remove_pipeline(&mut self, pipeline_id: PipelineId) {
if self.root_pipeline_id == Some(pipeline_id) {
self.root_pipeline_id = None;
}
self.pipelines.remove(&pipeline_id);
+ self.removed_pipelines.push(pipeline_id);
}
pub fn update_epoch(&mut self, pipeline_id: PipelineId, epoch: Epoch) {
if let Some(pipeline) = self.pipelines.get_mut(&pipeline_id) {
pipeline.epoch = epoch;
}
}
}
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -1,32 +1,32 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use api::{ClipId, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
use api::{DevicePixelScale, DeviceUintPoint, DeviceUintRect, DeviceUintSize};
use api::{DocumentLayer, FilterOp, ImageFormat};
use api::{LayerRect, MixBlendMode, PipelineId};
-use batch::{AlphaBatcher, ClipBatcher, resolve_image};
+use batch::{AlphaBatchBuilder, AlphaBatchContainer, ClipBatcher, resolve_image};
use clip::{ClipStore};
use clip_scroll_tree::{ClipScrollTree};
use device::{FrameId, Texture};
use gpu_cache::{GpuCache};
use gpu_types::{BlurDirection, BlurInstance, BrushInstance, ClipChainRectIndex};
use gpu_types::{ClipScrollNodeData, ClipScrollNodeIndex};
use gpu_types::{PrimitiveInstance};
use internal_types::{FastHashMap, RenderPassIndex, SourceTexture};
use picture::{PictureKind};
use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveStore};
use prim_store::{BrushMaskKind, BrushKind, DeferredResolve, EdgeAaSegmentMask};
use profiler::FrameProfileCounters;
use render_task::{BlitSource, RenderTaskAddress, RenderTaskId, RenderTaskKind};
use render_task::{BlurTask, ClearMode, RenderTaskLocation, RenderTaskTree};
-use resource_cache::{ResourceCache};
+use resource_cache::ResourceCache;
use std::{cmp, usize, f32, i32};
use texture_allocator::GuillotineAllocator;
const MIN_TARGET_SIZE: u32 = 2048;
#[derive(Debug)]
pub struct ScrollbarPrimitive {
pub clip_id: ClipId,
@@ -264,68 +264,96 @@ pub struct BlitJob {
pub source: BlitJobSource,
pub target_rect: DeviceIntRect,
}
/// A render target represents a number of rendering operations on a surface.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ColorRenderTarget {
- pub alpha_batcher: AlphaBatcher,
+ pub alpha_batch_containers: Vec<AlphaBatchContainer>,
// List of blur operations to apply for this render target.
pub vertical_blurs: Vec<BlurInstance>,
pub horizontal_blurs: Vec<BlurInstance>,
pub readbacks: Vec<DeviceIntRect>,
pub scalings: Vec<ScalingInfo>,
pub blits: Vec<BlitJob>,
// List of frame buffer outputs for this render target.
pub outputs: Vec<FrameOutput>,
allocator: Option<TextureAllocator>,
alpha_tasks: Vec<RenderTaskId>,
+ screen_size: DeviceIntSize,
}
impl RenderTarget for ColorRenderTarget {
fn allocate(&mut self, size: DeviceUintSize) -> Option<DeviceUintPoint> {
self.allocator
.as_mut()
.expect("bug: calling allocate on framebuffer")
.allocate(&size)
}
fn new(
size: Option<DeviceUintSize>,
screen_size: DeviceIntSize,
) -> Self {
ColorRenderTarget {
- alpha_batcher: AlphaBatcher::new(screen_size),
+ alpha_batch_containers: Vec::new(),
vertical_blurs: Vec::new(),
horizontal_blurs: Vec::new(),
readbacks: Vec::new(),
scalings: Vec::new(),
blits: Vec::new(),
allocator: size.map(TextureAllocator::new),
outputs: Vec::new(),
alpha_tasks: Vec::new(),
+ screen_size,
}
}
fn build(
&mut self,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &mut RenderTaskTree,
deferred_resolves: &mut Vec<DeferredResolve>,
) {
- self.alpha_batcher.build(
- &self.alpha_tasks,
- ctx,
- gpu_cache,
- render_tasks,
- deferred_resolves,
- );
+ let mut merged_batches = AlphaBatchContainer::new(None);
+
+ for task_id in &self.alpha_tasks {
+ let task = &render_tasks[*task_id];
+
+ match task.kind {
+ RenderTaskKind::Picture(ref pic_task) => {
+ let pic_index = ctx.prim_store.cpu_metadata[pic_task.prim_index.0].cpu_prim_index;
+ let pic = &ctx.prim_store.cpu_pictures[pic_index.0];
+ let (target_rect, _) = task.get_target_rect();
+
+ let mut batch_builder = AlphaBatchBuilder::new(self.screen_size, target_rect);
+
+ batch_builder.add_pic_to_batch(
+ pic,
+ *task_id,
+ ctx,
+ gpu_cache,
+ render_tasks,
+ deferred_resolves,
+ );
+
+ if let Some(batch_container) = batch_builder.build(&mut merged_batches) {
+ self.alpha_batch_containers.push(batch_container);
+ }
+ }
+ _ => {
+ unreachable!();
+ }
+ }
+ }
+
+ self.alpha_batch_containers.push(merged_batches);
}
fn add_task(
&mut self,
task_id: RenderTaskId,
ctx: &RenderTargetContext,
gpu_cache: &mut GpuCache,
render_tasks: &RenderTaskTree,
@@ -390,19 +418,17 @@ impl RenderTarget for ColorRenderTarget
dest_task_id: task_id,
});
}
RenderTaskKind::Blit(ref task_info) => {
match task_info.source {
BlitSource::Image { key } => {
// Get the cache item for the source texture.
let cache_item = resolve_image(
- key.image_key,
- key.image_rendering,
- key.tile_offset,
+ key.request,
ctx.resource_cache,
gpu_cache,
deferred_resolves,
);
// Work out a source rect to copy from the texture, depending on whether
// a sub-rect is present or not.
// TODO(gw): We have much type confusion below - f32, i32 and u32 for
@@ -441,17 +467,19 @@ impl RenderTarget for ColorRenderTarget
fn used_rect(&self) -> DeviceIntRect {
self.allocator
.as_ref()
.expect("bug: used_rect called on framebuffer")
.used_rect
}
fn needs_depth(&self) -> bool {
- !self.alpha_batcher.batch_list.opaque_batch_list.batches.is_empty()
+ self.alpha_batch_containers.iter().any(|ab| {
+ !ab.opaque_batches.is_empty()
+ })
}
}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct AlphaRenderTarget {
pub clip_batcher: ClipBatcher,
pub brush_mask_corners: Vec<PrimitiveInstance>,
@@ -788,17 +816,17 @@ impl RenderPass {
// one if required.
match task.location {
RenderTaskLocation::TextureCache(texture_id, layer, _) => {
// TODO(gw): When we support caching color items, we will
// need to calculate that here to get the
// correct target kind.
(RenderTargetKind::Alpha, Some((texture_id, layer)))
}
- RenderTaskLocation::Fixed => {
+ RenderTaskLocation::Fixed(..) => {
(RenderTargetKind::Color, None)
}
RenderTaskLocation::Dynamic(ref mut origin, size) => {
let alloc_size = DeviceUintSize::new(size.width as u32, size.height as u32);
let (alloc_origin, target_index) = match target_kind {
RenderTargetKind::Color => color.allocate(alloc_size),
RenderTargetKind::Alpha => alpha.allocate(alloc_size),
};
--- a/gfx/webrender/src/util.rs
+++ b/gfx/webrender/src/util.rs
@@ -106,27 +106,21 @@ impl<Src, Dst> MatrixHelpers<Src, Dst> f
self.m34.abs() < NEARLY_ZERO
}
}
pub trait RectHelpers<U>
where
Self: Sized,
{
- fn contains_rect(&self, other: &Self) -> bool;
fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self;
fn is_well_formed_and_nonempty(&self) -> bool;
}
impl<U> RectHelpers<U> for TypedRect<f32, U> {
- fn contains_rect(&self, other: &Self) -> bool {
- self.origin.x <= other.origin.x && self.origin.y <= other.origin.y &&
- self.max_x() >= other.max_x() && self.max_y() >= other.max_y()
- }
-
fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {
TypedRect::new(
TypedPoint2D::new(x0, y0),
TypedSize2D::new(x1 - x0, y1 - y0),
)
}
fn is_well_formed_and_nonempty(&self) -> bool {
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -472,18 +472,16 @@ pub struct CapturedDocument {
#[derive(Clone, Deserialize, Serialize)]
pub enum DebugCommand {
/// Display the frame profiler on screen.
EnableProfiler(bool),
/// Display all texture cache pages on screen.
EnableTextureCacheDebug(bool),
/// Display intermediate render targets on screen.
EnableRenderTargetDebug(bool),
- /// Display alpha primitive rects.
- EnableAlphaRectsDebug(bool),
/// Display GPU timing results.
EnableGpuTimeQueries(bool),
/// Display GPU overdraw results
EnableGpuSampleQueries(bool),
/// Configure if dual-source blending is used, if available.
EnableDualSourceBlending(bool),
/// Fetch current documents and display lists.
FetchDocuments,
--- a/gfx/webrender_bindings/Cargo.toml
+++ b/gfx/webrender_bindings/Cargo.toml
@@ -5,17 +5,17 @@ authors = ["The Mozilla Project Develope
license = "MPL-2.0"
[dependencies]
rayon = "0.8"
thread_profiler = "0.1.1"
euclid = "0.16"
app_units = "0.6"
gleam = "0.4.20"
-log = "0.3"
+log = "0.4"
[dependencies.webrender]
path = "../webrender"
version = "0.57.0"
default-features = false
features = ["capture"]
[target.'cfg(target_os = "windows")'.dependencies]
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-08e49649f1fc9cacff4e10ebc390babcea752236
+342bc314db94aa439b2001249c5f24ccfcbccc22
--- a/gfx/wrench/Cargo.toml
+++ b/gfx/wrench/Cargo.toml
@@ -4,25 +4,25 @@ version = "0.3.0"
authors = ["Vladimir Vukicevic <vladimir@pobox.com>"]
build = "build.rs"
license = "MPL-2.0"
[dependencies]
base64 = "0.3"
bincode = "0.9"
byteorder = "1.0"
-env_logger = { version = "0.4", optional = true }
+env_logger = { version = "0.5", optional = true }
euclid = "0.16"
gleam = "0.4"
-servo-glutin = "0.14"
+glutin = "0.12"
app_units = "0.6"
image = "0.17"
clap = { version = "2", features = ["yaml"] }
lazy_static = "1"
-log = "0.3"
+log = "0.4"
yaml-rust = { git = "https://github.com/vvuk/yaml-rust", features = ["preserve_order"] }
serde_json = "1.0"
ron = "0.1.5"
time = "0.1"
crossbeam = "0.2"
osmesa-sys = { version = "0.1.2", optional = true }
osmesa-src = { git = "https://github.com/servo/osmesa-src", optional = true }
webrender = {path = "../webrender", features=["capture","replay","debugger","png","profiler"]}
@@ -30,15 +30,14 @@ webrender_api = {path = "../webrender_ap
serde = {version = "1.0", features = ["derive"] }
[target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.13"
core-foundation = "0.5"
[features]
headless = [ "osmesa-sys", "osmesa-src" ]
-logging = [ "env_logger" ]
[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.4.1"
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
font-loader = "0.6"
--- a/gfx/wrench/README.md
+++ b/gfx/wrench/README.md
@@ -24,12 +24,12 @@ If you are working on gecko integration
static ENABLE_RECORDING: bool = true;
```
`wrench replay --save yaml` will convert the recording into frames described in yaml. Frames can then be replayed with `wrench show`.
## `reftest`
Wrench also has a reftest system for catching regressions.
-* To run all reftests, run `./headless.py reftest`
-* To run specific reftests, run `./headless.py reftest path/to/test/or/dir`
+* To run all reftests, run `script/headless.py reftest`
+* To run specific reftests, run `script/headless.py reftest path/to/test/or/dir`
* To examine test failures, use the [reftest analyzer](https://hg.mozilla.org/mozilla-central/raw-file/tip/layout/tools/reftest/reftest-analyzer.xhtml)
* To add a new reftest, create an example frame and a reference frame in `reftests/` and then add an entry to `reftests/reftest.list`
--- a/gfx/wrench/src/main.rs
+++ b/gfx/wrench/src/main.rs
@@ -52,17 +52,17 @@ mod wrench;
mod yaml_frame_reader;
mod yaml_frame_writer;
mod yaml_helper;
#[cfg(target_os = "macos")]
mod cgfont_to_data;
use binary_frame_reader::BinaryFrameReader;
use gleam::gl;
-use glutin::{ElementState, VirtualKeyCode, WindowProxy};
+use glutin::{GlContext, VirtualKeyCode};
use perf::PerfHarness;
use png::save_flipped;
use rawtest::RawtestHarness;
use reftest::{ReftestHarness, ReftestOptions};
#[cfg(feature = "headless")]
use std::ffi::CString;
#[cfg(feature = "headless")]
use std::mem;
@@ -95,17 +95,17 @@ pub struct HeadlessContext {
#[cfg(not(feature = "headless"))]
pub struct HeadlessContext {
width: u32,
height: u32,
}
impl HeadlessContext {
#[cfg(feature = "headless")]
- fn new(width: u32, height: u32) -> HeadlessContext {
+ fn new(width: u32, height: u32) -> Self {
let mut attribs = Vec::new();
attribs.push(osmesa_sys::OSMESA_PROFILE);
attribs.push(osmesa_sys::OSMESA_CORE_PROFILE);
attribs.push(osmesa_sys::OSMESA_CONTEXT_MAJOR_VERSION);
attribs.push(3);
attribs.push(osmesa_sys::OSMESA_CONTEXT_MINOR_VERSION);
attribs.push(3);
@@ -135,80 +135,81 @@ impl HeadlessContext {
width,
height,
_context: context,
_buffer: buffer,
}
}
#[cfg(not(feature = "headless"))]
- fn new(width: u32, height: u32) -> HeadlessContext {
+ fn new(width: u32, height: u32) -> Self {
HeadlessContext { width, height }
}
#[cfg(feature = "headless")]
fn get_proc_address(s: &str) -> *const c_void {
let c_str = CString::new(s).expect("Unable to create CString");
unsafe { mem::transmute(osmesa_sys::OSMesaGetProcAddress(c_str.as_ptr())) }
}
#[cfg(not(feature = "headless"))]
fn get_proc_address(_: &str) -> *const c_void {
ptr::null() as *const _
}
}
pub enum WindowWrapper {
- Window(glutin::Window, Rc<gl::Gl>),
+ Window(glutin::GlWindow, Rc<gl::Gl>),
Headless(HeadlessContext, Rc<gl::Gl>),
}
pub struct HeadlessEventIterater;
impl WindowWrapper {
fn swap_buffers(&self) {
match *self {
WindowWrapper::Window(ref window, _) => window.swap_buffers().unwrap(),
- WindowWrapper::Headless(..) => {}
+ WindowWrapper::Headless(_, _) => {}
}
}
fn get_inner_size(&self) -> DeviceUintSize {
let (w, h) = match *self {
- WindowWrapper::Window(ref window, _) => window.get_inner_size_pixels().unwrap(),
+ //HACK: `winit` needs to figure out its hidpi story...
+ #[cfg(target_os = "macos")]
+ WindowWrapper::Window(ref window, _) => {
+ let (w, h) = window.get_inner_size().unwrap();
+ let factor = window.hidpi_factor();
+ ((w as f32 * factor) as _, (h as f32 * factor) as _)
+ },
+ #[cfg(not(target_os = "macos"))]
+ WindowWrapper::Window(ref window, _) => window.get_inner_size().unwrap(),
WindowWrapper::Headless(ref context, _) => (context.width, context.height),
};
DeviceUintSize::new(w, h)
}
fn hidpi_factor(&self) -> f32 {
match *self {
WindowWrapper::Window(ref window, _) => window.hidpi_factor(),
- WindowWrapper::Headless(..) => 1.0,
+ WindowWrapper::Headless(_, _) => 1.0,
}
}
fn resize(&mut self, size: DeviceUintSize) {
match *self {
WindowWrapper::Window(ref mut window, _) => window.set_inner_size(size.width, size.height),
WindowWrapper::Headless(_, _) => unimplemented!(), // requites Glutin update
}
}
- fn create_window_proxy(&mut self) -> Option<WindowProxy> {
- match *self {
- WindowWrapper::Window(ref window, _) => Some(window.create_window_proxy()),
- WindowWrapper::Headless(..) => None,
- }
- }
-
fn set_title(&mut self, title: &str) {
match *self {
WindowWrapper::Window(ref window, _) => window.set_title(title),
- WindowWrapper::Headless(..) => (),
+ WindowWrapper::Headless(_, _) => (),
}
}
pub fn gl(&self) -> &gl::Gl {
match *self {
WindowWrapper::Window(_, ref gl) | WindowWrapper::Headless(_, ref gl) => &**gl,
}
}
@@ -219,57 +220,65 @@ impl WindowWrapper {
}
}
}
fn make_window(
size: DeviceUintSize,
dp_ratio: Option<f32>,
vsync: bool,
- headless: bool,
+ events_loop: &Option<glutin::EventsLoop>,
) -> WindowWrapper {
- let wrapper = if headless {
- let gl = match gl::GlType::default() {
- gl::GlType::Gl => unsafe {
- gl::GlFns::load_with(|symbol| {
- HeadlessContext::get_proc_address(symbol) as *const _
- })
- },
- gl::GlType::Gles => unsafe {
- gl::GlesFns::load_with(|symbol| {
- HeadlessContext::get_proc_address(symbol) as *const _
+ let wrapper = match *events_loop {
+ Some(ref events_loop) => {
+ let context_builder = glutin::ContextBuilder::new()
+ .with_gl(glutin::GlRequest::GlThenGles {
+ opengl_version: (3, 2),
+ opengles_version: (3, 0),
})
- },
- };
- WindowWrapper::Headless(HeadlessContext::new(size.width, size.height), gl)
- } else {
- let mut builder = glutin::WindowBuilder::new()
- .with_gl(glutin::GlRequest::GlThenGles {
- opengl_version: (3, 2),
- opengles_version: (3, 0),
- })
- .with_dimensions(size.width, size.height);
- builder.opengl.vsync = vsync;
- let window = builder.build().unwrap();
- unsafe {
- window
- .make_current()
- .expect("unable to make context current!");
+ .with_vsync(vsync);
+ let window_builder = glutin::WindowBuilder::new()
+ .with_title("WRech")
+ .with_multitouch()
+ .with_dimensions(size.width, size.height);
+ let window = glutin::GlWindow::new(window_builder, context_builder, events_loop)
+ .unwrap();
+
+ unsafe {
+ window
+ .make_current()
+ .expect("unable to make context current!");
+ }
+
+ let gl = match window.get_api() {
+ glutin::Api::OpenGl => unsafe {
+ gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
+ },
+ glutin::Api::OpenGlEs => unsafe {
+ gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
+ },
+ glutin::Api::WebGl => unimplemented!(),
+ };
+ WindowWrapper::Window(window, gl)
}
-
- let gl = match window.get_api() {
- glutin::Api::OpenGl => unsafe {
- gl::GlFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
- },
- glutin::Api::OpenGlEs => unsafe {
- gl::GlesFns::load_with(|symbol| window.get_proc_address(symbol) as *const _)
- },
- glutin::Api::WebGl => unimplemented!(),
- };
- WindowWrapper::Window(window, gl)
+ None => {
+ let gl = match gl::GlType::default() {
+ gl::GlType::Gl => unsafe {
+ gl::GlFns::load_with(|symbol| {
+ HeadlessContext::get_proc_address(symbol) as *const _
+ })
+ },
+ gl::GlType::Gles => unsafe {
+ gl::GlesFns::load_with(|symbol| {
+ HeadlessContext::get_proc_address(symbol) as *const _
+ })
+ },
+ };
+ WindowWrapper::Headless(HeadlessContext::new(size.width, size.height), gl)
+ }
};
wrapper.gl().clear_color(0.3, 0.0, 0.0, 1.0);
let gl_version = wrapper.gl().get_string(gl::VERSION);
let gl_renderer = wrapper.gl().get_string(gl::RENDERER);
let dp_ratio = dp_ratio.unwrap_or(wrapper.hidpi_factor());
@@ -308,17 +317,17 @@ impl RenderNotifier for Notifier {
fn create_notifier() -> (Box<RenderNotifier>, Receiver<()>) {
let (tx, rx) = channel();
(Box::new(Notifier { tx: tx }), rx)
}
fn main() {
#[cfg(feature = "logging")]
- env_logger::init().unwrap();
+ env_logger::init();
let args_yaml = load_yaml!("args.yaml");
let args = clap::App::from_yaml(args_yaml)
.setting(clap::AppSettings::ArgRequiredElseHelp)
.get_matches();
// handle some global arguments
let res_path = args.value_of("shaders").map(|s| PathBuf::from(s));
@@ -341,34 +350,41 @@ fn main() {
let x = s.find('x').expect(
"Size must be specified exactly as 720p, 1080p, 4k, or width x height",
);
let w = s[0 .. x].parse::<u32>().expect("Invalid size width");
let h = s[x + 1 ..].parse::<u32>().expect("Invalid size height");
DeviceUintSize::new(w, h)
})
.unwrap_or(DeviceUintSize::new(1920, 1080));
- let is_headless = args.is_present("headless");
let zoom_factor = args.value_of("zoom").map(|z| z.parse::<f32>().unwrap());
- let mut window = make_window(size, dp_ratio, args.is_present("vsync"), is_headless);
+
+ let mut events_loop = if args.is_present("headless") {
+ None
+ } else {
+ Some(glutin::EventsLoop::new())
+ };
+
+ let mut window = make_window(size, dp_ratio, args.is_present("vsync"), &events_loop);
let dp_ratio = dp_ratio.unwrap_or(window.hidpi_factor());
let dim = window.get_inner_size();
let needs_frame_notifier = ["perf", "reftest", "png", "rawtest"]
.iter()
.any(|s| args.subcommand_matches(s).is_some());
let (notifier, rx) = if needs_frame_notifier {
let (notifier, rx) = create_notifier();
(Some(notifier), Some(rx))
} else {
(None, None)
};
let mut wrench = Wrench::new(
&mut window,
+ events_loop.as_mut().map(|el| el.create_proxy()),
res_path,
dp_ratio,
save_type,
dim,
args.is_present("rebuild"),
args.is_present("no_subpixel_aa"),
args.is_present("debug"),
args.is_present("verbose"),
@@ -442,69 +458,62 @@ fn main() {
let mut show_help = false;
let mut do_loop = false;
let mut cpu_profile_index = 0;
let dim = window.get_inner_size();
wrench.update(dim);
thing.do_frame(&mut wrench);
- 'outer: loop {
+ let mut body = |wrench: &mut Wrench, global_event: glutin::Event| {
if let Some(window_title) = wrench.take_title() {
window.set_title(&window_title);
}
- let mut events = Vec::new();
-
- match window {
- WindowWrapper::Headless(..) => {
- events.push(glutin::Event::Awakened);
- }
- WindowWrapper::Window(ref window, _) => {
- events.push(window.wait_events().next().unwrap());
- events.extend(window.poll_events());
- }
- }
-
let mut do_frame = false;
let mut do_render = false;
- for event in events {
- match event {
- glutin::Event::Closed => {
- break 'outer;
+ match global_event {
+ glutin::Event::Awakened => {
+ do_render = true;
+ }
+ glutin::Event::WindowEvent { event, .. } => match event {
+ glutin::WindowEvent::Closed => {
+ return glutin::ControlFlow::Break;
}
- glutin::Event::Refresh |
- glutin::Event::Awakened |
- glutin::Event::Focused(..) |
- glutin::Event::MouseMoved(..) => {
+ glutin::WindowEvent::Refresh |
+ glutin::WindowEvent::Focused(..) |
+ glutin::WindowEvent::CursorMoved { .. } => {
do_render = true;
}
- glutin::Event::KeyboardInput(ElementState::Pressed, _scan_code, Some(vk)) => match vk {
+ glutin::WindowEvent::KeyboardInput {
+ input: glutin::KeyboardInput {
+ state: glutin::ElementState::Pressed,
+ virtual_keycode: Some(vk),
+ ..
+ },
+ ..
+ } => match vk {
VirtualKeyCode::Escape => {
- break 'outer;
+ return glutin::ControlFlow::Break;
}
VirtualKeyCode::P => {
wrench.renderer.toggle_debug_flags(DebugFlags::PROFILER_DBG);
do_render = true;
}
VirtualKeyCode::O => {
wrench.renderer.toggle_debug_flags(DebugFlags::RENDER_TARGET_DBG);
do_render = true;
}
VirtualKeyCode::I => {
wrench.renderer.toggle_debug_flags(DebugFlags::TEXTURE_CACHE_DBG);
do_render = true;
}
- VirtualKeyCode::B => {
- wrench.renderer.toggle_debug_flags(DebugFlags::ALPHA_PRIM_DBG);
- do_render = true;
- }
VirtualKeyCode::S => {
wrench.renderer.toggle_debug_flags(DebugFlags::COMPACT_PROFILER);
do_render = true;
}
VirtualKeyCode::Q => {
wrench.renderer.toggle_debug_flags(
DebugFlags::GPU_TIME_QUERIES | DebugFlags::GPU_SAMPLE_QUERIES
);
@@ -555,24 +564,25 @@ fn main() {
let new_zoom_factor = ZoomFactor::new((current_zoom.get() - 0.1).max(0.1));
wrench.set_page_zoom(new_zoom_factor);
do_frame = true;
}
_ => {}
}
_ => {}
- }
- }
+ },
+ _ => return glutin::ControlFlow::Continue,
+ };
let dim = window.get_inner_size();
wrench.update(dim);
if do_frame {
- let frame_num = thing.do_frame(&mut wrench);
+ let frame_num = thing.do_frame(wrench);
unsafe {
CURRENT_FRAME_NUMBER = frame_num;
}
}
if do_render {
if show_help {
wrench.show_onscreen_help();
@@ -580,18 +590,26 @@ fn main() {
wrench.render();
window.swap_buffers();
if do_loop {
thing.next_frame();
}
}
- }
+
+ glutin::ControlFlow::Continue
+ };
- if is_headless {
- let rect = DeviceUintRect::new(DeviceUintPoint::zero(), size);
- let pixels = wrench.renderer.read_pixels_rgba8(rect);
- save_flipped("screenshot.png", pixels, size);
+ match events_loop {
+ None => {
+ while body(&mut wrench, glutin::Event::Awakened) == glutin::ControlFlow::Continue {}
+ let rect = DeviceUintRect::new(DeviceUintPoint::zero(), size);
+ let pixels = wrench.renderer.read_pixels_rgba8(rect);
+ save_flipped("screenshot.png", pixels, size);
+ }
+ Some(ref mut events_loop) => {
+ events_loop.run_forever(|event| body(&mut wrench, event));
+ }
}
wrench.renderer.deinit();
}
--- a/gfx/wrench/src/rawtest.rs
+++ b/gfx/wrench/src/rawtest.rs
@@ -34,16 +34,17 @@ impl<'a> RawtestHarness<'a> {
RawtestHarness {
wrench,
rx,
window,
}
}
pub fn run(mut self) {
+ self.test_hit_testing();
self.test_retained_blob_images_test();
self.test_blob_update_test();
self.test_blob_update_epoch_test();
self.test_tile_decomposition();
self.test_save_restore();
self.test_capture();
}
@@ -566,9 +567,129 @@ impl<'a> RawtestHarness<'a> {
// 6. rebuild the scene and compare again
let mut txn = Transaction::new();
txn.set_root_pipeline(captured.root_pipeline_id.unwrap());
txn.generate_frame();
self.wrench.api.send_transaction(captured.document_id, txn);
let pixels2 = self.render_and_get_pixels(window_rect);
assert!(pixels0 == pixels2);
}
+
+
+ fn test_hit_testing(&mut self) {
+ println!("\thit testing test...");
+
+ let layout_size = LayoutSize::new(400., 400.);
+ let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size);
+
+ // Add a rectangle that covers the entire scene.
+ let mut info = LayoutPrimitiveInfo::new(LayoutRect::new(LayoutPoint::zero(), layout_size));
+ info.tag = Some((0, 1));
+ builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
+
+
+ // Add a simple 100x100 rectangle at 100,0.
+ let mut info = LayoutPrimitiveInfo::new(LayoutRect::new(
+ LayoutPoint::new(100., 0.),
+ LayoutSize::new(100., 100.)
+ ));
+ info.tag = Some((0, 2));
+ builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
+
+ let make_rounded_complex_clip = | rect: &LayoutRect, radius: f32 | -> ComplexClipRegion {
+ ComplexClipRegion::new(
+ *rect,
+ BorderRadius::uniform_size(LayoutSize::new(radius, radius)),
+ ClipMode::Clip
+ )
+ };
+
+ // Add a rounded 100x100 rectangle at 200,0.
+ let rect = LayoutRect::new(LayoutPoint::new(200., 0.), LayoutSize::new(100., 100.));
+ let mut info = LayoutPrimitiveInfo::with_clip(
+ rect, LocalClip::RoundedRect(rect, make_rounded_complex_clip(&rect, 20.)));
+ info.tag = Some((0, 3));
+ builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
+
+
+ // Add a rectangle that is clipped by a rounded rect clip item.
+ let rect = LayoutRect::new(LayoutPoint::new(100., 100.), LayoutSize::new(100., 100.));
+ let clip_id = builder.define_clip(rect, vec![make_rounded_complex_clip(&rect, 20.)], None);
+ builder.push_clip_id(clip_id);
+ let mut info = LayoutPrimitiveInfo::new(rect);
+ info.tag = Some((0, 4));
+ builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
+ builder.pop_clip_id();
+
+
+ // Add a rectangle that is clipped by a ClipChain containing a rounded rect.
+ let rect = LayoutRect::new(LayoutPoint::new(200., 100.), LayoutSize::new(100., 100.));
+ let clip_id = builder.define_clip(rect, vec![make_rounded_complex_clip(&rect, 20.)], None);
+ let clip_chain_id = builder.define_clip_chain(None, vec![clip_id]);
+ builder.push_clip_and_scroll_info(ClipAndScrollInfo::new(
+ ClipId::root_scroll_node(self.wrench.root_pipeline_id),
+ ClipId::ClipChain(clip_chain_id),
+ ));
+ let mut info = LayoutPrimitiveInfo::new(rect);
+ info.tag = Some((0, 5));
+ builder.push_rect(&info, ColorF::new(1.0, 1.0, 1.0, 1.0));
+ builder.pop_clip_id();
+
+
+ let mut epoch = Epoch(0);
+ self.submit_dl(&mut epoch, layout_size, builder, None);
+
+ // We render to ensure that the hit tester is up to date with the current scene.
+ self.rx.recv().unwrap();
+ self.wrench.render();
+
+ let hit_test = | point: WorldPoint | -> HitTestResult {
+ self.wrench.api.hit_test(
+ self.wrench.document_id,
+ None,
+ point,
+ HitTestFlags::FIND_ALL,
+ )
+ };
+
+ let assert_hit_test = | point: WorldPoint, tags: Vec<ItemTag> | {
+ let result = hit_test(point);
+ assert_eq!(result.items.len(), tags.len());
+
+ for (hit_test_item, item_b) in result.items.iter().zip(tags.iter()) {
+ assert_eq!(hit_test_item.tag, *item_b);
+ }
+ };
+
+ // We should not have any hits outside the boundaries of the scene.
+ assert_hit_test(WorldPoint::new(-10., -10.), Vec::new());
+ assert_hit_test(WorldPoint::new(-10., 10.), Vec::new());
+ assert_hit_test(WorldPoint::new(450., 450.), Vec::new());
+ assert_hit_test(WorldPoint::new(100., 450.), Vec::new());
+
+ // The top left corner of the scene should only contain the background.
+ assert_hit_test(WorldPoint::new(50., 50.), vec![(0, 1)]);
+
+ // The middle of the normal rectangle should be hit.
+ assert_hit_test(WorldPoint::new(150., 50.), vec![(0, 2), (0, 1)]);
+
+ let test_rounded_rectangle = | point: WorldPoint, size: WorldSize, tag: ItemTag | {
+ // The cut out corners of the rounded rectangle should not be hit.
+ let top_left = point + WorldVector2D::new(5., 5.);
+ let bottom_right = point + size.to_vector() - WorldVector2D::new(5., 5.);
+
+ assert_hit_test(
+ WorldPoint::new(point.x + (size.width / 2.), point.y + (size.height / 2.)),
+ vec![tag, (0, 1)]
+ );
+
+ assert_hit_test(top_left, vec![(0, 1)]);
+ assert_hit_test(WorldPoint::new(bottom_right.x, top_left.y), vec![(0, 1)]);
+ assert_hit_test(WorldPoint::new(top_left.x, bottom_right.y), vec![(0, 1)]);
+ assert_hit_test(bottom_right, vec![(0, 1)]);
+ };
+
+ test_rounded_rectangle(WorldPoint::new(200., 0.), WorldSize::new(100., 100.), (0, 3));
+ test_rounded_rectangle(WorldPoint::new(100., 100.), WorldSize::new(100., 100.), (0, 4));
+ test_rounded_rectangle(WorldPoint::new(200., 100.), WorldSize::new(100., 100.), (0, 5));
+ }
+
}
--- a/gfx/wrench/src/wrench.rs
+++ b/gfx/wrench/src/wrench.rs
@@ -5,17 +5,17 @@
use app_units::Au;
use blob;
use crossbeam::sync::chase_lev;
#[cfg(windows)]
use dwrote;
#[cfg(any(target_os = "linux", target_os = "macos"))]
use font_loader::system_fonts;
-use glutin::WindowProxy;
+use glutin::EventsLoopProxy;
use json_frame_writer::JsonFrameWriter;
use ron_frame_writer::RonFrameWriter;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use time;
use webrender;
use webrender::api::*;
@@ -41,30 +41,30 @@ pub enum FontDescriptor {
pub enum SaveType {
Yaml,
Json,
Ron,
Binary,
}
struct NotifierData {
- window_proxy: Option<WindowProxy>,
+ events_loop_proxy: Option<EventsLoopProxy>,
frames_notified: u32,
timing_receiver: chase_lev::Stealer<time::SteadyTime>,
verbose: bool,
}
impl NotifierData {
fn new(
- window_proxy: Option<WindowProxy>,
+ events_loop_proxy: Option<EventsLoopProxy>,
timing_receiver: chase_lev::Stealer<time::SteadyTime>,
verbose: bool,
) -> Self {
NotifierData {
- window_proxy,
+ events_loop_proxy,
frames_notified: 0,
timing_receiver,
verbose,
}
}
}
struct Notifier(Arc<Mutex<NotifierData>>);
@@ -86,19 +86,20 @@ impl Notifier {
data.frames_notified = 0;
}
}
_ => {
println!("Notified of frame, but no frame was ready?");
}
}
}
- if let Some(ref window_proxy) = data.window_proxy {
+
+ if let Some(ref elp) = data.events_loop_proxy {
#[cfg(not(target_os = "android"))]
- window_proxy.wakeup_event_loop();
+ let _ = elp.wakeup();
}
}
}
impl RenderNotifier for Notifier {
fn clone(&self) -> Box<RenderNotifier> {
Box::new(Notifier(self.0.clone()))
}
@@ -160,16 +161,17 @@ pub struct Wrench {
pub frame_start_sender: chase_lev::Worker<time::SteadyTime>,
pub callbacks: Arc<Mutex<blob::BlobCallbacks>>,
}
impl Wrench {
pub fn new(
window: &mut WindowWrapper,
+ proxy: Option<EventsLoopProxy>,
shader_override_path: Option<PathBuf>,
dp_ratio: f32,
save_type: Option<SaveType>,
size: DeviceUintSize,
do_rebuild: bool,
no_subpixel_aa: bool,
debug: bool,
verbose: bool,
@@ -209,21 +211,20 @@ impl Wrench {
enable_clear_scissor: !no_scissor,
max_recorded_profiles: 16,
precache_shaders,
blob_image_renderer: Some(Box::new(blob::CheckerboardRenderer::new(callbacks.clone()))),
disable_dual_source_blending,
..Default::default()
};
- let proxy = window.create_window_proxy();
// put an Awakened event into the queue to kick off the first frame
- if let Some(ref wp) = proxy {
+ if let Some(ref elp) = proxy {
#[cfg(not(target_os = "android"))]
- wp.wakeup_event_loop();
+ let _ = elp.wakeup();
}
let (timing_sender, timing_receiver) = chase_lev::deque();
let notifier = notifier.unwrap_or_else(|| {
let data = Arc::new(Mutex::new(NotifierData::new(proxy, timing_receiver, verbose)));
Box::new(Notifier(data))
});
@@ -543,16 +544,17 @@ impl Wrench {
pub fn get_frame_profiles(
&mut self,
) -> (Vec<webrender::CpuProfile>, Vec<webrender::GpuProfile>) {
self.renderer.get_frame_profiles()
}
pub fn render(&mut self) -> RendererStats {
self.renderer.update();
+ let _ = self.renderer.flush_pipeline_info();
self.renderer
.render(self.window_size)
.expect("errors encountered during render!")
}
pub fn refresh(&mut self) {
self.begin_frame();
let mut txn = Transaction::new();
--- a/gfx/wrench/src/yaml_frame_reader.rs
+++ b/gfx/wrench/src/yaml_frame_reader.rs
@@ -69,44 +69,47 @@ fn broadcast<T: Clone>(base_vals: &[T],
}
vals.extend_from_slice(base_vals);
}
vals
}
fn generate_checkerboard_image(
border: u32,
- tile_size: u32,
- tile_count: u32
+ tile_x_size: u32,
+ tile_y_size: u32,
+ tile_x_count: u32,
+ tile_y_count: u32,
) -> (ImageDescriptor, ImageData) {
- let size = 2 * border + tile_size * tile_count;
+ let width = 2 * border + tile_x_size * tile_x_count;
+ let height = 2 * border + tile_y_size * tile_y_count;
let mut pixels = Vec::new();
- for y in 0 .. size {
- for x in 0 .. size {
- if y < border || y >= (size-border) ||
- x < border || x >= (size-border) {
+ for y in 0 .. height {
+ for x in 0 .. width {
+ if y < border || y >= (height - border) ||
+ x < border || x >= (width - border) {
pixels.push(0);
pixels.push(0);
pixels.push(0xff);
pixels.push(0xff);
} else {
- let xon = ((x - border) % (2 * tile_size)) < tile_size;
- let yon = ((y - border) % (2 * tile_size)) < tile_size;
+ let xon = ((x - border) % (2 * tile_x_size)) < tile_x_size;
+ let yon = ((y - border) % (2 * tile_y_size)) < tile_y_size;
let value = if xon ^ yon { 0xff } else { 0x7f };
pixels.push(value);
pixels.push(value);
pixels.push(value);
pixels.push(0xff);
}
}
}
(
- ImageDescriptor::new(size, size, ImageFormat::BGRA8, true),
+ ImageDescriptor::new(width, height, ImageFormat::BGRA8, true),
ImageData::new(pixels),
)
}
fn generate_xy_gradient_image(w: u32, h: u32) -> (ImageDescriptor, ImageData) {
let mut pixels = Vec::with_capacity((w * h * 4) as usize);
for y in 0 .. h {
for x in 0 .. w {
@@ -434,21 +437,45 @@ impl YamlFrameReader {
("solid-color", args, _) => generate_solid_color_image(
args.get(0).unwrap_or(&"255").parse::<u8>().unwrap(),
args.get(1).unwrap_or(&"255").parse::<u8>().unwrap(),
args.get(2).unwrap_or(&"255").parse::<u8>().unwrap(),
args.get(3).unwrap_or(&"255").parse::<u8>().unwrap(),
args.get(4).unwrap_or(&"1000").parse::<u32>().unwrap(),
args.get(5).unwrap_or(&"1000").parse::<u32>().unwrap(),
),
- ("checkerboard", args, _) => generate_checkerboard_image(
- args.get(0).unwrap_or(&"4").parse::<u32>().unwrap(),
- args.get(1).unwrap_or(&"32").parse::<u32>().unwrap(),
- args.get(2).unwrap_or(&"8").parse::<u32>().unwrap(),
- ),
+ ("checkerboard", args, _) => {
+ let border = args.get(0).unwrap_or(&"4").parse::<u32>().unwrap();
+
+ let (x_size, y_size, x_count, y_count) = match args.len() {
+ 3 => {
+ let size = args.get(1).unwrap_or(&"32").parse::<u32>().unwrap();
+ let count = args.get(2).unwrap_or(&"8").parse::<u32>().unwrap();
+ (size, size, count, count)
+ }
+ 5 => {
+ let x_size = args.get(1).unwrap_or(&"32").parse::<u32>().unwrap();
+ let y_size = args.get(2).unwrap_or(&"32").parse::<u32>().unwrap();
+ let x_count = args.get(3).unwrap_or(&"8").parse::<u32>().unwrap();
+ let y_count = args.get(4).unwrap_or(&"8").parse::<u32>().unwrap();
+ (x_size, y_size, x_count, y_count)
+ }
+ _ => {
+ panic!("invalid checkerboard function");
+ }
+ };
+
+ generate_checkerboard_image(
+ border,
+ x_size,
+ y_size,
+ x_count,
+ y_count,
+ )
+ }
_ => {
panic!("Failed to load image {:?}", file.to_str());
}
}
}
};
let tiling = tiling.map(|tile_size| tile_size as u16);
let image_key = wrench.api.generate_image_key();
--- a/gfx/wrench/src/yaml_frame_writer.rs
+++ b/gfx/wrench/src/yaml_frame_writer.rs
@@ -1013,17 +1013,17 @@ impl YamlFrameWriter {
if let Some(mask_yaml) = self.make_clip_mask_image_node(&item.image_mask) {
yaml_node(&mut v, "image-mask", mask_yaml);
}
}
ClipChain(item) => {
str_node(&mut v, "type", "clip-chain");
let id = ClipId::ClipChain(item.id);
- u32_node(&mut v, "id", clip_id_mapper.map_id(&id) as u32);
+ u32_node(&mut v, "id", clip_id_mapper.add_id(id) as u32);
let clip_ids: Vec<u32> = display_list.get(base.clip_chain_items()).map(|clip_id| {
clip_id_mapper.map_id(&clip_id) as u32
}).collect();
u32_vec_node(&mut v, "clips", &clip_ids);
if let Some(parent) = item.parent {
let parent = ClipId::ClipChain(parent);