Remove old rendering backend.

This removes paint threads, rust-layers dependency, and changes
optional webrender types to be required.

The use_webrender option has been removed, however I've left
the "-w" command line option in place so that wpt
runner can continue to pass that. Once it's removed from there
we can also remove the -w option.

Once this stage is complete, it should be fine to change the
display list building code to generate webrender display
lists directly and avoid the conversion step.
This commit is contained in:
Glenn Watson 2016-10-12 10:13:27 +10:00
parent 4af21e3ae1
commit acfdfd2fa9
55 changed files with 422 additions and 3611 deletions

View file

@ -22,7 +22,6 @@ harfbuzz-sys = "0.1"
heapsize = "0.3.0"
heapsize_plugin = "0.1.2"
ipc-channel = "0.5"
layers = {git = "https://github.com/servo/rust-layers", features = ["plugins"]}
lazy_static = "0.2"
libc = "0.2"
log = "0.3.5"
@ -31,8 +30,6 @@ msg = {path = "../msg"}
net_traits = {path = "../net_traits"}
ordered-float = "0.2.2"
plugins = {path = "../plugins"}
profile_traits = {path = "../profile_traits"}
rand = "0.3"
range = {path = "../range"}
rustc-serialize = "0.3"
serde = "0.8"

View file

@ -112,7 +112,7 @@ pub struct Font {
shaper: Option<Shaper>,
shape_cache: RefCell<HashMap<ShapeCacheEntry, Arc<GlyphStore>>>,
glyph_advance_cache: RefCell<HashMap<u32, FractionalPixel>>,
pub font_key: Option<webrender_traits::FontKey>,
pub font_key: webrender_traits::FontKey,
}
impl Font {
@ -121,7 +121,7 @@ impl Font {
descriptor: FontTemplateDescriptor,
requested_pt_size: Au,
actual_pt_size: Au,
font_key: Option<webrender_traits::FontKey>) -> Font {
font_key: webrender_traits::FontKey) -> Font {
let metrics = handle.metrics();
Font {
handle: handle,

View file

@ -349,16 +349,18 @@ impl FontCache {
}
fn get_font_template_info(&mut self, template: Arc<FontTemplateData>) -> FontTemplateInfo {
let webrender_fonts = &mut self.webrender_fonts;
let font_key = self.webrender_api.as_ref().map(|webrender_api| {
*webrender_fonts.entry(template.identifier.clone()).or_insert_with(|| {
let mut font_key = None;
if let Some(ref webrender_api) = self.webrender_api {
let webrender_fonts = &mut self.webrender_fonts;
font_key = Some(*webrender_fonts.entry(template.identifier.clone()).or_insert_with(|| {
match (template.bytes_if_in_memory(), template.native_font()) {
(Some(bytes), _) => webrender_api.add_raw_font(bytes),
(None, Some(native_font)) => webrender_api.add_native_font(native_font),
(None, None) => webrender_api.add_raw_font(template.bytes().clone()),
}
})
});
}));
}
FontTemplateInfo {
font_template: template,

View file

@ -109,7 +109,7 @@ impl FontContext {
descriptor: FontTemplateDescriptor,
pt_size: Au,
variant: font_variant::T,
font_key: Option<webrender_traits::FontKey>) -> Result<Font, ()> {
font_key: webrender_traits::FontKey) -> Result<Font, ()> {
// TODO: (Bug #3463): Currently we only support fake small-caps
// painting. We should also support true small-caps (where the
// font supports it) in the future.
@ -197,7 +197,8 @@ impl FontContext {
desc.clone(),
style.font_size,
style.font_variant,
template_info.font_key);
template_info.font_key
.expect("No font key present!"));
let font = match layout_font {
Ok(layout_font) => {
let layout_font = Rc::new(RefCell::new(layout_font));
@ -242,7 +243,7 @@ impl FontContext {
desc.clone(),
style.font_size,
style.font_variant,
template_info.font_key);
template_info.font_key.expect("No font key present!"));
match layout_font {
Ok(layout_font) => {
let layout_font = Rc::new(RefCell::new(layout_font));

View file

@ -9,7 +9,6 @@
#![feature(box_syntax)]
#![feature(custom_attribute)]
#![feature(custom_derive)]
#![feature(mpsc_select)]
#![feature(plugin)]
#![feature(proc_macro)]
#![feature(range_contains)]
@ -57,7 +56,6 @@ extern crate harfbuzz_sys as harfbuzz;
extern crate heapsize;
extern crate ipc_channel;
extern crate layers;
#[allow(unused_extern_crates)]
#[macro_use]
extern crate lazy_static;
@ -69,9 +67,6 @@ extern crate msg;
extern crate net_traits;
extern crate ordered_float;
#[macro_use]
extern crate profile_traits;
extern crate rand;
#[macro_use]
extern crate range;
extern crate rustc_serialize;
extern crate serde;
@ -110,8 +105,6 @@ pub mod font_cache_thread;
pub mod font_context;
pub mod font_template;
pub mod paint_thread;
// Platform-specific implementations.
#[allow(unsafe_code)]
mod platform;

View file

@ -1,779 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! The thread that handles all painting.
use app_units::Au;
use azure::AzFloat;
use azure::azure_hl::{BackendType, Color, DrawTarget, SurfaceFormat};
use display_list::{DisplayItem, DisplayList, DisplayListTraversal};
use display_list::{LayerInfo, StackingContext, StackingContextType};
use euclid::Matrix4D;
use euclid::point::Point2D;
use euclid::rect::{Rect, TypedRect};
use euclid::size::Size2D;
use font_cache_thread::FontCacheThread;
use font_context::FontContext;
use gfx_traits::{ChromeToPaintMsg, Epoch, LayerId, LayerKind, LayerProperties};
use gfx_traits::{PaintListener, PaintRequest, StackingContextId};
use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet};
use layers::platform::surface::{NativeDisplay, NativeSurface};
use msg::constellation_msg::PipelineId;
use paint_context::PaintContext;
use profile_traits::mem;
use profile_traits::time;
use rand::{self, Rng};
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::mem as std_mem;
use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use style::thread_state;
use url::Url;
use util::geometry::ExpandToPixelBoundaries;
use util::opts;
use util::thread;
#[derive(Clone, HeapSizeOf)]
struct PaintLayer {
/// The LayerProperties, which describe the layer in a way that the Compositor
/// can consume.
pub layer_properties: LayerProperties,
/// The StackingContextId of the StackingContext that is the immediate
/// parent of this layer. This is used to ensure applying the proper transform
/// when painting.
pub starting_stacking_context_id: StackingContextId,
/// The indices (in the DisplayList) to the first and last display item
/// that are the contents of this layer.
pub display_list_indices: Option<(usize, usize)>,
/// When painting, whether to draw the start by entering the surrounding StackingContext
/// or simply to draw the single item this PaintLayer contains.
pub single_item: bool,
/// The layer's bounds start at the overflow origin, but display items are
/// positioned relative to the stacking context bounds, so we need to
/// offset by the overflow rect (which will be in the coordinate system of
/// the stacking context bounds).
pub display_list_origin: Point2D<f32>
}
impl PaintLayer {
fn new_from_stacking_context(layer_info: &LayerInfo,
stacking_context: &StackingContext,
parent_origin: &Point2D<Au>,
transform: &Matrix4D<f32>,
perspective: &Matrix4D<f32>,
parent_id: Option<LayerId>)
-> PaintLayer {
let bounds = Rect::new(stacking_context.bounds.origin + stacking_context.overflow.origin,
stacking_context.overflow.size);
let layer_boundaries = Rect::new(
Point2D::new((parent_origin.x + bounds.min_x()).to_nearest_px() as f32,
(parent_origin.y + bounds.min_y()).to_nearest_px() as f32),
Size2D::new(bounds.size.width.to_nearest_px() as f32,
bounds.size.height.to_nearest_px() as f32));
let transform = transform.pre_mul(&stacking_context.transform);
let perspective = perspective.pre_mul(&stacking_context.perspective);
let establishes_3d_context = stacking_context.establishes_3d_context;
let scrolls_overflow_area = stacking_context.scrolls_overflow_area;
PaintLayer {
layer_properties: LayerProperties {
id: layer_info.layer_id,
parent_id: parent_id,
rect: layer_boundaries,
background_color: layer_info.background_color,
scroll_policy: layer_info.scroll_policy,
transform: transform,
perspective: perspective,
establishes_3d_context: establishes_3d_context,
scrolls_overflow_area: scrolls_overflow_area,
subpage_pipeline_id: layer_info.subpage_pipeline_id,
},
starting_stacking_context_id: stacking_context.id,
display_list_indices: None,
single_item: false,
display_list_origin: Point2D::new(stacking_context.overflow.origin.x.to_f32_px(),
stacking_context.overflow.origin.y.to_f32_px()),
}
}
fn new_for_display_item(layer_info: &LayerInfo,
item_bounds: &Rect<Au>,
parent_origin: &Point2D<Au>,
transform: &Matrix4D<f32>,
perspective: &Matrix4D<f32>,
parent_id: Option<LayerId>,
stacking_context_id: StackingContextId,
item_index: usize)
-> PaintLayer {
let bounds = item_bounds.expand_to_px_boundaries();
let layer_boundaries = Rect::new(
Point2D::new((parent_origin.x + bounds.min_x()).to_nearest_px() as f32,
(parent_origin.y + bounds.min_y()).to_nearest_px() as f32),
Size2D::new(bounds.size.width.to_nearest_px() as f32,
bounds.size.height.to_nearest_px() as f32));
PaintLayer {
layer_properties: LayerProperties {
id: layer_info.layer_id,
parent_id: parent_id,
rect: layer_boundaries,
background_color: layer_info.background_color,
scroll_policy: layer_info.scroll_policy,
transform: *transform,
perspective: *perspective,
establishes_3d_context: false,
scrolls_overflow_area: false,
subpage_pipeline_id: layer_info.subpage_pipeline_id,
},
starting_stacking_context_id: stacking_context_id,
display_list_indices: Some((item_index, item_index)),
single_item: true,
display_list_origin: Point2D::new(bounds.origin.x.to_f32_px(),
bounds.origin.y.to_f32_px()),
}
}
fn add_item(&mut self, index: usize) {
let indices = match self.display_list_indices {
Some((first, _)) => (first, index),
None => (index, index),
};
self.display_list_indices = Some(indices);
}
fn make_companion_layer(&mut self) {
self.layer_properties.id = self.layer_properties.id.companion_layer_id();
self.display_list_indices = None;
}
}
struct LayerCreator {
layers: Vec<PaintLayer>,
layer_details_stack: Vec<PaintLayer>,
current_layer: Option<PaintLayer>,
}
impl LayerCreator {
fn create_layers_with_display_list<'a>(display_list: &'a DisplayList) -> Vec<PaintLayer> {
let mut layer_creator = LayerCreator {
layers: Vec::new(),
layer_details_stack: Vec::new(),
current_layer: None,
};
let mut traversal = DisplayListTraversal::new(display_list);
layer_creator.process_stacking_context_items(&mut traversal,
&Point2D::zero(),
&Matrix4D::identity(),
&Matrix4D::identity());
layer_creator.layers
}
fn finalize_current_layer(&mut self) {
if let Some(current_layer) = self.current_layer.take() {
self.layers.push(current_layer);
}
}
fn current_parent_layer_id(&self) -> Option<LayerId> {
self.layer_details_stack.last().as_ref().map(|layer|
layer.layer_properties.id
)
}
fn current_parent_stacking_context_id(&self) -> StackingContextId {
self.layer_details_stack.last().unwrap().starting_stacking_context_id
}
fn create_layers_for_stacking_context<'a>(&mut self,
stacking_context: &StackingContext,
traversal: &mut DisplayListTraversal<'a>,
parent_origin: &Point2D<Au>,
transform: &Matrix4D<f32>,
perspective: &Matrix4D<f32>) {
if let Some(ref layer_info) = stacking_context.layer_info {
self.finalize_current_layer();
let new_layer = PaintLayer::new_from_stacking_context(
layer_info,
stacking_context,
parent_origin,
transform,
perspective,
self.current_parent_layer_id());
self.layer_details_stack.push(new_layer.clone());
self.current_layer = Some(new_layer);
// When there is a new layer, the transforms and origin are handled by
// the compositor, so the new transform and perspective matrices are
// just the identity.
//
// The origin for child layers which might be somewhere other than the
// layer origin, since layer boundaries are expanded to include overflow.
self.process_stacking_context_items(traversal,
&-stacking_context.overflow.origin,
&Matrix4D::identity(),
&Matrix4D::identity());
self.finalize_current_layer();
self.layer_details_stack.pop();
return;
}
debug_assert!(stacking_context.context_type == StackingContextType::Real);
self.process_stacking_context_items(traversal,
&(stacking_context.bounds.origin + *parent_origin),
&transform.pre_mul(&stacking_context.transform),
&perspective.pre_mul(&stacking_context.perspective));
}
fn process_stacking_context_items<'a>(&mut self,
traversal: &mut DisplayListTraversal<'a>,
parent_origin: &Point2D<Au>,
transform: &Matrix4D<f32>,
perspective: &Matrix4D<f32>) {
while let Some(item) = traversal.next() {
match item {
&DisplayItem::PushStackingContextClass(ref stacking_context_item) => {
self.create_layers_for_stacking_context(&stacking_context_item.stacking_context,
traversal,
parent_origin,
transform,
perspective);
}
&DisplayItem::PopStackingContextClass(_) => return,
_ => {
self.create_layers_for_item(traversal.previous_item_id(),
item,
parent_origin,
transform,
perspective);
}
}
}
}
fn create_layers_for_item<'a>(&mut self,
current_item_index: usize,
item: &DisplayItem,
parent_origin: &Point2D<Au>,
transform: &Matrix4D<f32>,
perspective: &Matrix4D<f32>) {
if let &DisplayItem::LayeredItemClass(ref layered_item) = item {
// We need to finalize the last layer here before incrementing the item
// index, otherwise this item will be placed into the parent layer.
self.finalize_current_layer();
let layer = PaintLayer::new_for_display_item(
&layered_item.layer_info,
&layered_item.item.bounds(),
parent_origin,
transform,
perspective,
self.current_parent_layer_id(),
self.current_parent_stacking_context_id(),
current_item_index);
self.layers.push(layer);
return;
}
// If we don't have a current layer, we are an item that belonged to a
// previous layer that was finalized by a child layer. We need to
// resurrect a copy of the original ancestor layer to ensure that this
// item is ordered on top of the child layers when painted.
if self.current_layer.is_none() {
let mut new_layer = self.layer_details_stack.pop().unwrap();
new_layer.make_companion_layer();
if new_layer.layer_properties.parent_id == None {
new_layer.layer_properties.parent_id =
Some(new_layer.layer_properties.id.original());
}
self.layer_details_stack.push(new_layer.clone());
self.current_layer = Some(new_layer);
}
if let Some(ref mut current_layer) = self.current_layer {
current_layer.add_item(current_item_index);
}
}
}
pub enum Msg {
FromLayout(LayoutToPaintMsg),
FromChrome(ChromeToPaintMsg),
}
#[derive(Deserialize, Serialize)]
pub enum LayoutToPaintMsg {
PaintInit(Epoch, Arc<DisplayList>),
Exit,
}
pub struct PaintThread<C> {
id: PipelineId,
_url: Url,
layout_to_paint_port: Receiver<LayoutToPaintMsg>,
chrome_to_paint_port: Receiver<ChromeToPaintMsg>,
compositor: C,
/// A channel to the time profiler.
time_profiler_chan: time::ProfilerChan,
/// The root paint layer sent to us by the layout thread.
root_display_list: Option<Arc<DisplayList>>,
/// A map that associates LayerIds with their corresponding layers.
layer_map: HashMap<LayerId, Arc<PaintLayer>>,
/// Permission to send paint messages to the compositor
paint_permission: bool,
/// The current epoch counter is passed by the layout thread
current_epoch: Option<Epoch>,
/// Communication handles to each of the worker threads.
worker_threads: Vec<WorkerThreadProxy>,
}
// If we implement this as a function, we get borrowck errors from borrowing
// the whole PaintThread struct.
macro_rules! native_display(
($thread:expr) => (
$thread.native_display.as_ref().expect("Need a graphics context to do painting")
)
);
impl<C> PaintThread<C> where C: PaintListener + Send + 'static {
pub fn create(id: PipelineId,
url: Url,
chrome_to_paint_chan: Sender<ChromeToPaintMsg>,
layout_to_paint_port: Receiver<LayoutToPaintMsg>,
chrome_to_paint_port: Receiver<ChromeToPaintMsg>,
mut compositor: C,
font_cache_thread: FontCacheThread,
time_profiler_chan: time::ProfilerChan,
mem_profiler_chan: mem::ProfilerChan) {
thread::spawn_named(format!("PaintThread {:?}", id), move || {
thread_state::initialize(thread_state::PAINT);
PipelineId::install(id);
let native_display = compositor.native_display();
let worker_threads = WorkerThreadProxy::spawn(native_display,
font_cache_thread,
time_profiler_chan.clone());
let mut paint_thread = PaintThread {
id: id,
_url: url,
layout_to_paint_port: layout_to_paint_port,
chrome_to_paint_port: chrome_to_paint_port,
compositor: compositor,
time_profiler_chan: time_profiler_chan,
root_display_list: None,
layer_map: HashMap::new(),
paint_permission: false,
current_epoch: None,
worker_threads: worker_threads,
};
let reporter_name = format!("paint-reporter-{}", id);
mem_profiler_chan.run_with_memory_reporting(|| {
paint_thread.start();
}, reporter_name, chrome_to_paint_chan, ChromeToPaintMsg::CollectReports);
// Tell all the worker threads to shut down.
for worker_thread in &mut paint_thread.worker_threads {
worker_thread.exit()
}
});
}
#[allow(unsafe_code)]
fn start(&mut self) {
debug!("PaintThread: beginning painting loop");
loop {
let message = {
let layout_to_paint = &self.layout_to_paint_port;
let chrome_to_paint = &self.chrome_to_paint_port;
select! {
msg = layout_to_paint.recv() =>
Msg::FromLayout(msg.expect("expected message from layout")),
msg = chrome_to_paint.recv() =>
Msg::FromChrome(msg.expect("expected message from chrome"))
}
};
match message {
Msg::FromLayout(LayoutToPaintMsg::PaintInit(epoch, display_list)) => {
self.current_epoch = Some(epoch);
self.root_display_list = Some(display_list);
if self.paint_permission {
self.initialize_layers();
}
}
Msg::FromChrome(ChromeToPaintMsg::Paint(requests, frame_tree_id)) => {
if self.paint_permission && self.root_display_list.is_some() {
let mut replies = Vec::new();
for PaintRequest { buffer_requests, scale, layer_id, epoch, layer_kind }
in requests {
if self.current_epoch == Some(epoch) {
self.paint(&mut replies, buffer_requests, scale, layer_id, layer_kind);
} else {
debug!("PaintThread: Ignoring requests with epoch mismatch: {:?} != {:?}",
self.current_epoch,
epoch);
self.compositor.ignore_buffer_requests(buffer_requests);
}
}
debug!("PaintThread: returning surfaces");
self.compositor.assign_painted_buffers(self.id,
self.current_epoch.unwrap(),
replies,
frame_tree_id);
}
}
Msg::FromChrome(ChromeToPaintMsg::PaintPermissionGranted) => {
self.paint_permission = true;
if self.root_display_list.is_some() {
self.initialize_layers();
}
}
Msg::FromChrome(ChromeToPaintMsg::PaintPermissionRevoked) => {
self.paint_permission = false;
}
Msg::FromChrome(ChromeToPaintMsg::CollectReports(ref channel)) => {
// FIXME(njn): should eventually measure the paint thread.
channel.send(Vec::new())
}
Msg::FromLayout(LayoutToPaintMsg::Exit) => {
// Ask the compositor to remove any layers it is holding for this paint thread.
// FIXME(mrobinson): This can probably move back to the constellation now.
debug!("PaintThread: Exiting.");
self.compositor.notify_paint_thread_exiting(self.id);
break;
}
Msg::FromChrome(ChromeToPaintMsg::Exit) => {
// Ask the compositor to remove any layers it is holding for this paint thread.
// FIXME(mrobinson): This can probably move back to the constellation now.
debug!("PaintThread: Exiting.");
self.compositor.notify_paint_thread_exiting(self.id);
break;
}
}
}
}
/// Paints one layer and places the painted tiles in `replies`.
fn paint(&mut self,
replies: &mut Vec<(LayerId, Box<LayerBufferSet>)>,
mut tiles: Vec<BufferRequest>,
scale: f32,
layer_id: LayerId,
layer_kind: LayerKind) {
time::profile(time::ProfilerCategory::Painting, None, self.time_profiler_chan.clone(), || {
let display_list = match self.root_display_list {
Some(ref display_list) => display_list.clone(),
None => return,
};
// Bail out if there is no appropriate layer.
let layer = match self.layer_map.get(&layer_id) {
Some(layer) => layer.clone(),
None => return,
};
// Divide up the layer into tiles and distribute them to workers via a simple round-
// robin strategy.
let tiles = std_mem::replace(&mut tiles, Vec::new());
let tile_count = tiles.len();
for (i, tile) in tiles.into_iter().enumerate() {
let thread_id = i % self.worker_threads.len();
self.worker_threads[thread_id].paint_tile(thread_id,
tile,
display_list.clone(),
layer.clone(),
scale,
layer_kind);
}
let new_buffers = (0..tile_count).map(|i| {
let thread_id = i % self.worker_threads.len();
self.worker_threads[thread_id].painted_tile_buffer()
}).collect();
let layer_buffer_set = box LayerBufferSet {
buffers: new_buffers,
};
replies.push((layer_id, layer_buffer_set));
})
}
fn initialize_layers(&mut self) {
let root_display_list = match self.root_display_list {
None => return,
Some(ref root_display_list) => root_display_list,
};
let layers = LayerCreator::create_layers_with_display_list(&root_display_list);
let properties = layers.iter().map(|layer| layer.layer_properties.clone()).collect();
self.compositor.initialize_layers_for_pipeline(self.id,
properties,
self.current_epoch.unwrap());
self.layer_map.clear();
for layer in layers.into_iter() {
self.layer_map.insert(layer.layer_properties.id, Arc::new(layer));
}
}
}
struct WorkerThreadProxy {
sender: Sender<MsgToWorkerThread>,
receiver: Receiver<MsgFromWorkerThread>,
}
impl WorkerThreadProxy {
fn spawn(native_display: Option<NativeDisplay>,
font_cache_thread: FontCacheThread,
time_profiler_chan: time::ProfilerChan)
-> Vec<WorkerThreadProxy> {
// Don't make any paint threads if we're using WebRender. They're just a waste of
// resources.
if opts::get().use_webrender {
return vec![]
}
let thread_count = opts::get().paint_threads;
(0..thread_count).map(|_| {
let (from_worker_sender, from_worker_receiver) = channel();
let (to_worker_sender, to_worker_receiver) = channel();
let font_cache_thread = font_cache_thread.clone();
let time_profiler_chan = time_profiler_chan.clone();
thread::spawn_named("PaintWorker".to_owned(), move || {
let mut worker_thread = WorkerThread::new(from_worker_sender,
to_worker_receiver,
native_display,
font_cache_thread,
time_profiler_chan);
worker_thread.main();
});
WorkerThreadProxy {
receiver: from_worker_receiver,
sender: to_worker_sender,
}
}).collect()
}
fn paint_tile(&mut self,
thread_id: usize,
tile: BufferRequest,
display_list: Arc<DisplayList>,
paint_layer: Arc<PaintLayer>,
scale: f32,
layer_kind: LayerKind) {
let msg = MsgToWorkerThread::PaintTile(thread_id,
tile,
display_list,
paint_layer,
scale,
layer_kind);
self.sender.send(msg).unwrap()
}
fn painted_tile_buffer(&mut self) -> Box<LayerBuffer> {
match self.receiver.recv().unwrap() {
MsgFromWorkerThread::PaintedTile(layer_buffer) => layer_buffer,
}
}
fn exit(&mut self) {
self.sender.send(MsgToWorkerThread::Exit).unwrap()
}
}
struct WorkerThread {
sender: Sender<MsgFromWorkerThread>,
receiver: Receiver<MsgToWorkerThread>,
native_display: Option<NativeDisplay>,
font_context: Box<FontContext>,
time_profiler_sender: time::ProfilerChan,
}
impl WorkerThread {
fn new(sender: Sender<MsgFromWorkerThread>,
receiver: Receiver<MsgToWorkerThread>,
native_display: Option<NativeDisplay>,
font_cache_thread: FontCacheThread,
time_profiler_sender: time::ProfilerChan)
-> WorkerThread {
WorkerThread {
sender: sender,
receiver: receiver,
native_display: native_display,
font_context: box FontContext::new(font_cache_thread.clone()),
time_profiler_sender: time_profiler_sender,
}
}
fn main(&mut self) {
loop {
match self.receiver.recv().unwrap() {
MsgToWorkerThread::Exit => break,
MsgToWorkerThread::PaintTile(thread_id,
tile,
display_list,
paint_layer,
scale,
layer_kind) => {
let buffer = self.optimize_and_paint_tile(thread_id,
tile,
display_list,
paint_layer,
scale,
layer_kind);
self.sender.send(MsgFromWorkerThread::PaintedTile(buffer)).unwrap()
}
}
}
}
fn optimize_and_paint_tile(&mut self,
thread_id: usize,
mut tile: BufferRequest,
display_list: Arc<DisplayList>,
paint_layer: Arc<PaintLayer>,
scale: f32,
layer_kind: LayerKind)
-> Box<LayerBuffer> {
let size = Size2D::new(tile.screen_rect.size.width as i32,
tile.screen_rect.size.height as i32);
let mut buffer = self.create_layer_buffer(&mut tile, scale);
let draw_target = DrawTarget::new(BackendType::Skia, size, SurfaceFormat::B8G8R8A8);
{
// Build the paint context.
let mut paint_context = PaintContext {
draw_target: draw_target.clone(),
font_context: &mut self.font_context,
page_rect: TypedRect::from_untyped(&tile.page_rect.translate(&paint_layer.display_list_origin)),
screen_rect: TypedRect::from_untyped(&tile.screen_rect),
clip_rect: None,
transient_clip: None,
layer_kind: layer_kind,
subpixel_offset: Point2D::zero(),
};
// Apply the translation to paint the tile we want.
let matrix = Matrix4D::identity();
let matrix = matrix.pre_scaled(scale as AzFloat, scale as AzFloat, 1.0);
let tile_bounds = tile.page_rect.translate(&paint_layer.display_list_origin);
let matrix = matrix.pre_translated(-tile_bounds.origin.x as AzFloat,
-tile_bounds.origin.y as AzFloat,
0.0);
// Clear the buffer.
paint_context.clear();
// Draw the display list.
time::profile(time::ProfilerCategory::PaintingPerTile,
None,
self.time_profiler_sender.clone(), || {
if let Some((start, end)) = paint_layer.display_list_indices {
if paint_layer.single_item {
display_list.draw_item_at_index_into_context(
&mut paint_context, &matrix, start);
} else {
display_list.draw_into_context(
&mut paint_context,
&matrix,
paint_layer.starting_stacking_context_id,
start,
end);
}
}
paint_context.draw_target.flush();
});
if opts::get().show_debug_parallel_paint {
// Overlay a transparent solid color to identify the thread that
// painted this tile.
let color = THREAD_TINT_COLORS[thread_id % THREAD_TINT_COLORS.len()];
paint_context.draw_solid_color(&Rect::new(Point2D::new(Au(0), Au(0)),
Size2D::new(Au::from_px(size.width),
Au::from_px(size.height))),
color);
}
if opts::get().paint_flashing {
// Overlay a random transparent color.
let color = *rand::thread_rng().choose(&THREAD_TINT_COLORS[..]).unwrap();
paint_context.draw_solid_color(&Rect::new(Point2D::new(Au(0), Au(0)),
Size2D::new(Au::from_px(size.width),
Au::from_px(size.height))),
color);
}
}
// Extract the texture from the draw target and place it into its slot in the buffer.
// Upload it first.
draw_target.snapshot().get_data_surface().with_data(|data| {
buffer.native_surface.upload(native_display!(self), data);
debug!("painting worker thread uploading to native surface {}",
buffer.native_surface.get_id());
});
draw_target.finish();
buffer
}
fn create_layer_buffer(&mut self,
tile: &mut BufferRequest,
scale: f32)
-> Box<LayerBuffer> {
// Create an empty native surface. We mark it as not leaking
// in case it dies in transit to the compositor thread.
let width = tile.screen_rect.size.width;
let height = tile.screen_rect.size.height;
let mut native_surface = tile.native_surface.take().unwrap_or_else(|| {
NativeSurface::new(native_display!(self), Size2D::new(width as i32, height as i32))
});
native_surface.mark_wont_leak();
box LayerBuffer {
native_surface: native_surface,
rect: tile.page_rect,
screen_pos: tile.screen_rect,
resolution: scale,
painted_with_cpu: true,
content_age: tile.content_age,
}
}
}
enum MsgToWorkerThread {
Exit,
PaintTile(usize, BufferRequest, Arc<DisplayList>, Arc<PaintLayer>, f32, LayerKind),
}
enum MsgFromWorkerThread {
PaintedTile(Box<LayerBuffer>),
}
pub static THREAD_TINT_COLORS: [Color; 8] = [
Color { r: 6.0 / 255.0, g: 153.0 / 255.0, b: 198.0 / 255.0, a: 0.7 },
Color { r: 255.0 / 255.0, g: 212.0 / 255.0, b: 83.0 / 255.0, a: 0.7 },
Color { r: 116.0 / 255.0, g: 29.0 / 255.0, b: 109.0 / 255.0, a: 0.7 },
Color { r: 204.0 / 255.0, g: 158.0 / 255.0, b: 199.0 / 255.0, a: 0.7 },
Color { r: 242.0 / 255.0, g: 46.0 / 255.0, b: 121.0 / 255.0, a: 0.7 },
Color { r: 116.0 / 255.0, g: 203.0 / 255.0, b: 196.0 / 255.0, a: 0.7 },
Color { r: 255.0 / 255.0, g: 249.0 / 255.0, b: 201.0 / 255.0, a: 0.7 },
Color { r: 137.0 / 255.0, g: 196.0 / 255.0, b: 78.0 / 255.0, a: 0.7 },
];

View file

@ -29,7 +29,7 @@ pub struct TextRun {
pub font_template: Arc<FontTemplateData>,
pub actual_pt_size: Au,
pub font_metrics: FontMetrics,
pub font_key: Option<webrender_traits::FontKey>,
pub font_key: webrender_traits::FontKey,
/// The glyph runs that make up this text run.
pub glyphs: Arc<Vec<GlyphRun>>,
pub bidi_level: u8,