servo: Merge #6530 - Move LayerBuffer cache to the compositor (from mrobinson:move-cache); r=pcwalton

Now that NativeDisplay can be shared between the compositor and the
paint task, we can move the LayerBuffer cache to the compositor. This
allows surfaces to be potentially reused between different paint tasks
and will eventually allow OpenGL contexts to be preserved between
instances of GL rasterization.

Source-Repo: https://github.com/servo/servo
Source-Revision: 10b0d8c537c226400a617d28e8a060f9ca53d242

--HG--
rename : servo/components/gfx/buffer_map.rs => servo/components/compositing/buffer_map.rs
This commit is contained in:
Martin Robinson 2015-07-08 03:14:21 -06:00
Родитель 2a3ec77664
Коммит 17cdbf9af6
12 изменённых файлов: 174 добавлений и 226 удалений

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

@ -8,7 +8,6 @@ use euclid::size::Size2D;
use layers::platform::surface::NativeDisplay;
use layers::layers::LayerBuffer;
use std::hash::{Hash, Hasher};
use std::mem;
/// This is a struct used to store buffers when they are not in use.
/// The paint task can quickly query for a particular size of buffer when it
@ -70,6 +69,13 @@ impl BufferMap {
}
}
pub fn insert_buffers(&mut self, display: &NativeDisplay, buffers: Vec<Box<LayerBuffer>>) {
for mut buffer in buffers.into_iter() {
buffer.mark_wont_leak();
self.insert(display, buffer)
}
}
/// Insert a new buffer into the map.
pub fn insert(&mut self, display: &NativeDisplay, new_buffer: Box<LayerBuffer>) {
let new_key = BufferKey::get(new_buffer.get_size_2d());
@ -150,17 +156,6 @@ impl BufferMap {
ret
}
/// Destroys all buffers.
pub fn clear(&mut self, display: &NativeDisplay) {
let map = mem::replace(&mut self.map, HashMap::new());
for (_, value) in map.into_iter() {
for tile in value.buffers.into_iter() {
tile.destroy(display)
}
}
self.mem = 0
}
pub fn mem(&self) -> usize {
self.mem
}

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

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use buffer_map::BufferMap;
use compositor_layer::{CompositorData, CompositorLayer, WantsScrollEventsFlag};
use compositor_task::{CompositorEventListener, CompositorProxy, CompositorReceiver};
use compositor_task::Msg;
@ -51,6 +52,8 @@ use url::Url;
use util::geometry::{Au, PagePx, ScreenPx, ViewportPx};
use util::opts;
const BUFFER_MAP_SIZE : usize = 10000000;
/// Holds the state when running reftests that determines when it is
/// safe to save the output image.
#[derive(Copy, Clone, PartialEq)]
@ -154,6 +157,9 @@ pub struct IOCompositor<Window: WindowMethods> {
/// Used by the logic that determines when it is safe to output an
/// image for the reftest framework.
ready_to_save_state: ReadyState,
/// A data structure to store unused LayerBuffers.
buffer_map: BufferMap,
}
pub struct ScrollEvent {
@ -290,6 +296,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
last_composite_time: 0,
has_seen_quit_event: false,
ready_to_save_state: ReadyState::Unknown,
buffer_map: BufferMap::new(BUFFER_MAP_SIZE),
}
}
@ -387,6 +394,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
}
(Msg::ReturnUnusedLayerBuffers(layer_buffers),
ShutdownState::NotShuttingDown) => {
self.cache_unused_buffers(layer_buffers);
}
(Msg::ScrollFragmentPoint(pipeline_id, layer_id, point),
ShutdownState::NotShuttingDown) => {
self.scroll_fragment_to_point(pipeline_id, layer_id, point);
@ -547,10 +559,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.root_pipeline = Some(frame_tree.pipeline.clone());
// If we have an old root layer, release all old tiles before replacing it.
match self.scene.root {
Some(ref layer) => layer.clear_all_tiles(self),
None => { }
let old_root_layer = self.scene.root.take();
if let Some(ref old_root_layer) = old_root_layer {
old_root_layer.clear_all_tiles(self)
}
self.scene.root = Some(self.create_frame_tree_root_layers(frame_tree, None));
self.scene.set_root_layer_size(self.window_size.as_f32());
@ -616,13 +629,15 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
fn remove_pipeline_root_layer(&mut self, pipeline_id: PipelineId) {
if let Some(ref root_layer) = self.scene.root {
// Remove all the compositor layers for this pipeline
// and send any owned buffers back to the paint task.
root_layer.remove_root_layer_with_pipeline_id(self, pipeline_id);
let root_layer = match self.scene.root {
Some(ref root_layer) => root_layer.clone(),
None => return,
};
self.pipeline_details.remove(&pipeline_id);
}
// Remove all the compositor layers for this pipeline and recache
// any buffers that they owned.
root_layer.remove_root_layer_with_pipeline_id(self, pipeline_id);
self.pipeline_details.remove(&pipeline_id);
}
fn update_layer_if_exists(&mut self, pipeline_id: PipelineId, properties: LayerProperties) -> bool {
@ -787,9 +802,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
}
let pipeline = self.get_pipeline(pipeline_id);
let message = PaintMsg::UnusedBuffer(new_layer_buffer_set.buffers);
let _ = pipeline.paint_chan.send(message);
self.cache_unused_buffers(new_layer_buffer_set.buffers);
}
fn assign_painted_buffers_to_layer(&mut self,
@ -1141,7 +1154,29 @@ impl<Window: WindowMethods> IOCompositor<Window> {
chan.send(ConstellationMsg::KeyEvent(key, state, modifiers)).unwrap()
}
fn convert_buffer_requests_to_pipeline_requests_map(&self,
fn fill_paint_request_with_cached_layer_buffers(&mut self, paint_request: &mut PaintRequest) {
if opts::get().gpu_painting {
return;
}
for buffer_request in paint_request.buffer_requests.iter_mut() {
if self.buffer_map.mem() == 0 {
return;
}
if let Some(mut buffer) = self.buffer_map.find(buffer_request.screen_rect.size) {
buffer.rect = buffer_request.page_rect;
buffer.screen_pos = buffer_request.screen_rect;
buffer.resolution = paint_request.scale;
buffer.native_surface.mark_wont_leak();
buffer.painted_with_cpu = true;
buffer.content_age = buffer_request.content_age;
buffer_request.layer_buffer = Some(buffer);
}
}
}
fn convert_buffer_requests_to_pipeline_requests_map(&mut self,
requests: Vec<(Rc<Layer<CompositorData>>,
Vec<BufferRequest>)>)
-> HashMap<PipelineId, Vec<PaintRequest>> {
@ -1173,29 +1208,20 @@ impl<Window: WindowMethods> IOCompositor<Window> {
LayerKind::Layer2D
};
vec.push(PaintRequest {
let mut paint_request = PaintRequest {
buffer_requests: layer_requests,
scale: scale.get(),
layer_id: layer.extra_data.borrow().id,
epoch: layer.extra_data.borrow().requested_epoch,
layer_kind: layer_kind,
});
};
self.fill_paint_request_with_cached_layer_buffers(&mut paint_request);
vec.push(paint_request);
}
results
}
fn send_back_unused_buffers(&mut self,
unused_buffers: Vec<(Rc<Layer<CompositorData>>,
Vec<Box<LayerBuffer>>)>) {
for (layer, buffers) in unused_buffers.into_iter() {
if !buffers.is_empty() {
let pipeline = self.get_pipeline(layer.pipeline_id());
let _ = pipeline.paint_chan.send_opt(PaintMsg::UnusedBuffer(buffers));
}
}
}
fn send_viewport_rect_for_layer(&self, layer: Rc<Layer<CompositorData>>) {
if layer.extra_data.borrow().id == LayerId::null() {
let layer_rect = Rect::new(-layer.extra_data.borrow().scroll_offset.to_untyped(),
@ -1230,7 +1256,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.scene.get_buffer_requests(&mut layers_and_requests, &mut unused_buffers);
// Return unused tiles first, so that they can be reused by any new BufferRequests.
self.send_back_unused_buffers(unused_buffers);
self.cache_unused_buffers(unused_buffers);
if layers_and_requests.len() == 0 {
return false;
@ -1528,6 +1554,12 @@ impl<Window: WindowMethods> IOCompositor<Window> {
None => None,
}
}
pub fn cache_unused_buffers(&mut self, buffers: Vec<Box<LayerBuffer>>) {
if !buffers.is_empty() {
self.buffer_map.insert_buffers(&self.native_display, buffers);
}
}
}
fn find_layer_with_pipeline_and_layer_id_for_layer(layer: Rc<Layer<CompositorData>>,

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

@ -10,7 +10,6 @@ use euclid::length::Length;
use euclid::point::{Point2D, TypedPoint2D};
use euclid::size::TypedSize2D;
use euclid::rect::Rect;
use gfx::paint_task::Msg as PaintMsg;
use layers::color::Color;
use layers::geometry::LayerPixel;
use layers::layers::{Layer, LayerBufferSet};
@ -76,25 +75,25 @@ pub trait CompositorLayer {
fn update_layer(&self, layer_properties: LayerProperties);
fn add_buffers<Window>(&self,
compositor: &IOCompositor<Window>,
compositor: &mut IOCompositor<Window>,
new_buffers: Box<LayerBufferSet>,
epoch: Epoch)
where Window: WindowMethods;
/// Destroys all layer tiles, sending the buffers back to the painter to be destroyed or
/// reused.
fn clear<Window>(&self, compositor: &IOCompositor<Window>) where Window: WindowMethods;
fn clear<Window>(&self, compositor: &mut IOCompositor<Window>) where Window: WindowMethods;
/// Destroys tiles for this layer and all descendent layers, sending the buffers back to the
/// painter to be destroyed or reused.
fn clear_all_tiles<Window>(&self, compositor: &IOCompositor<Window>)
fn clear_all_tiles<Window>(&self, compositor: &mut IOCompositor<Window>)
where Window: WindowMethods;
/// Removes the root layer (and any children) for a given pipeline from the
/// compositor. Buffers that the compositor is holding are returned to the
/// owning paint task.
fn remove_root_layer_with_pipeline_id<Window>(&self,
compositor: &IOCompositor<Window>,
compositor: &mut IOCompositor<Window>,
pipeline_id: PipelineId)
where Window: WindowMethods;
@ -214,7 +213,7 @@ impl CompositorLayer for Layer<CompositorData> {
// If the epoch of the message does not match the layer's epoch, the message is ignored, the
// layer buffer set is consumed, and None is returned.
fn add_buffers<Window>(&self,
compositor: &IOCompositor<Window>,
compositor: &mut IOCompositor<Window>,
new_buffers: Box<LayerBufferSet>,
epoch: Epoch)
where Window: WindowMethods {
@ -225,33 +224,21 @@ impl CompositorLayer for Layer<CompositorData> {
self.add_buffer(buffer);
}
let unused_buffers = self.collect_unused_buffers();
if !unused_buffers.is_empty() { // send back unused buffers
let pipeline = compositor.get_pipeline(self.pipeline_id());
let _ = pipeline.paint_chan.send(PaintMsg::UnusedBuffer(unused_buffers));
}
compositor.cache_unused_buffers(self.collect_unused_buffers())
}
fn clear<Window>(&self, compositor: &IOCompositor<Window>) where Window: WindowMethods {
let mut buffers = self.collect_buffers();
fn clear<Window>(&self, compositor: &mut IOCompositor<Window>) where Window: WindowMethods {
let buffers = self.collect_buffers();
if !buffers.is_empty() {
// We have no way of knowing without a race whether the paint task is even up and
// running, but mark the buffers as not leaking. If the paint task died, then the
// buffers are going to be cleaned up.
for buffer in buffers.iter_mut() {
buffer.mark_wont_leak()
}
let pipeline = compositor.get_pipeline(self.pipeline_id());
let _ = pipeline.paint_chan.send(PaintMsg::UnusedBuffer(buffers));
compositor.cache_unused_buffers(buffers);
}
}
/// Destroys tiles for this layer and all descendent layers, sending the buffers back to the
/// painter to be destroyed or reused.
fn clear_all_tiles<Window>(&self,
compositor: &IOCompositor<Window>)
compositor: &mut IOCompositor<Window>)
where Window: WindowMethods {
self.clear(compositor);
for kid in self.children().iter() {
@ -260,7 +247,7 @@ impl CompositorLayer for Layer<CompositorData> {
}
fn remove_root_layer_with_pipeline_id<Window>(&self,
compositor: &IOCompositor<Window>,
compositor: &mut IOCompositor<Window>,
pipeline_id: PipelineId)
where Window: WindowMethods {
// Find the child that is the root layer for this pipeline.

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

@ -14,7 +14,7 @@ use windowing::{WindowEvent, WindowMethods};
use euclid::point::Point2D;
use euclid::rect::Rect;
use layers::platform::surface::NativeDisplay;
use layers::layers::LayerBufferSet;
use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet};
use msg::compositor_msg::{Epoch, LayerId, LayerProperties, FrameTreeId};
use msg::compositor_msg::{PaintListener, ScriptListener};
use msg::constellation_msg::{AnimationState, ConstellationChan, PipelineId};
@ -111,6 +111,18 @@ impl PaintListener for Box<CompositorProxy+'static+Send> {
self.send(Msg::AssignPaintedBuffers(pipeline_id, epoch, replies, frame_tree_id));
}
fn ignore_buffer_requests(&mut self, buffer_requests: Vec<BufferRequest>) {
let mut layer_buffers = Vec::new();
for request in buffer_requests.into_iter() {
if let Some(layer_buffer) = request.layer_buffer {
layer_buffers.push(layer_buffer);
}
}
if !layer_buffers.is_empty() {
self.send(Msg::ReturnUnusedLayerBuffers(layer_buffers));
}
}
fn initialize_layers_for_pipeline(&mut self,
pipeline_id: PipelineId,
properties: Vec<LayerProperties>,
@ -184,6 +196,9 @@ pub enum Msg {
NewFavicon(Url),
/// <head> tag finished parsing
HeadParsed,
/// Signal that the paint task ignored the paint requests that carried
/// these layer buffers, so that they can be re-added to the surface cache.
ReturnUnusedLayerBuffers(Vec<Box<LayerBuffer>>),
}
impl Debug for Msg {
@ -212,6 +227,7 @@ impl Debug for Msg {
Msg::IsReadyToSaveImageReply(..) => write!(f, "IsReadyToSaveImageReply"),
Msg::NewFavicon(..) => write!(f, "NewFavicon"),
Msg::HeadParsed => write!(f, "HeadParsed"),
Msg::ReturnUnusedLayerBuffers(..) => write!(f, "ReturnUnusedLayerBuffers"),
}
}
}

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

@ -111,6 +111,7 @@ impl CompositorEventListener for NullCompositor {
Msg::IsReadyToSaveImageReply(..) => {}
Msg::NewFavicon(..) => {}
Msg::HeadParsed => {}
Msg::ReturnUnusedLayerBuffers(..) => {}
}
true
}

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

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![feature(box_syntax)]
#![feature(iter_cmp)]
#![feature(slice_bytes)]
#![feature(vec_push_all)]
@ -43,6 +44,7 @@ pub use constellation::Constellation;
pub mod compositor_task;
mod buffer_map;
mod compositor_layer;
mod compositor;
mod headless;

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

@ -9,7 +9,6 @@
#![feature(custom_derive)]
#![feature(hashmap_hasher)]
#![cfg_attr(any(target_os="linux", target_os = "android"), feature(heap_api))]
#![feature(iter_cmp)]
#![feature(plugin)]
#![feature(str_char)]
#![feature(vec_push_all)]
@ -76,7 +75,6 @@ pub mod font_cache_task;
pub mod font_template;
// Misc.
mod buffer_map;
mod filters;
// Platform-specific implementations.

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

@ -4,7 +4,6 @@
//! The task that handles all painting.
use buffer_map::BufferMap;
use display_list::{self, StackingContext};
use font_cache_task::FontCacheTask;
use font_context::FontContext;
@ -25,7 +24,7 @@ use msg::compositor_msg::{LayerProperties, PaintListener, ScrollPolicy};
use msg::constellation_msg::Msg as ConstellationMsg;
use msg::constellation_msg::{ConstellationChan, Failure, PipelineId};
use msg::constellation_msg::PipelineExitType;
use profile_traits::mem::{self, Report, Reporter, ReportsChan};
use profile_traits::mem::{self, Reporter, ReportsChan};
use profile_traits::time::{self, profile};
use rand::{self, Rng};
use skia::SkiaGrGLNativeContextRef;
@ -75,7 +74,6 @@ pub enum Msg {
PaintInit(Epoch, Arc<StackingContext>),
CanvasLayer(LayerId, Arc<Mutex<Sender<CanvasMsg>>>),
Paint(Vec<PaintRequest>, FrameTreeId),
UnusedBuffer(Vec<Box<LayerBuffer>>),
PaintPermissionGranted,
PaintPermissionRevoked,
CollectReports(ReportsChan),
@ -111,7 +109,7 @@ impl Reporter for PaintChan {
pub struct PaintTask<C> {
id: PipelineId,
url: Url,
_url: Url,
port: Receiver<Msg>,
compositor: C,
constellation_chan: ConstellationChan,
@ -125,9 +123,6 @@ pub struct PaintTask<C> {
/// The name used for the task's memory reporter.
pub reporter_name: String,
/// The native graphics context.
native_display: Option<NativeDisplay>,
/// The root stacking context sent to us by the layout thread.
root_stacking_context: Option<Arc<StackingContext>>,
@ -137,16 +132,9 @@ pub struct PaintTask<C> {
/// The current epoch counter is passed by the layout task
current_epoch: Option<Epoch>,
/// A data structure to store unused LayerBuffers
buffer_map: BufferMap,
/// Communication handles to each of the worker threads.
worker_threads: Vec<WorkerThreadProxy>,
/// Tracks the number of buffers that the compositor currently owns. The
/// PaintTask waits to exit until all buffers are returned.
used_buffer_count: usize,
/// A map to track the canvas specific layers
canvas_map: HashMap<LayerId, Arc<Mutex<Sender<CanvasMsg>>>>,
}
@ -192,31 +180,22 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
// FIXME: rust/#5967
let mut paint_task = PaintTask {
id: id,
url: url,
_url: url,
port: port,
compositor: compositor,
constellation_chan: constellation_chan,
time_profiler_chan: time_profiler_chan,
mem_profiler_chan: mem_profiler_chan,
reporter_name: reporter_name,
native_display: native_display,
root_stacking_context: None,
paint_permission: false,
current_epoch: None,
buffer_map: BufferMap::new(10000000),
worker_threads: worker_threads,
used_buffer_count: 0,
canvas_map: HashMap::new()
};
paint_task.start();
// Destroy all the buffers.
match paint_task.native_display.as_ref() {
Some(ctx) => paint_task.buffer_map.clear(ctx),
None => (),
}
// Tell all the worker threads to shut down.
for worker_thread in paint_task.worker_threads.iter_mut() {
worker_thread.exit()
@ -231,8 +210,6 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
fn start(&mut self) {
debug!("PaintTask: beginning painting loop");
let mut exit_response_channel : Option<Sender<()>> = None;
let mut waiting_for_compositor_buffers_to_exit = false;
loop {
match self.port.recv().unwrap() {
Msg::PaintInit(epoch, stacking_context) => {
@ -246,11 +223,6 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
continue;
}
// If waiting to exit, ignore any more paint commands
if waiting_for_compositor_buffers_to_exit {
continue;
}
self.initialize_layers();
}
// Inserts a new canvas renderer to the layer map
@ -266,46 +238,25 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
continue;
}
// If waiting to exit, ignore any more paint commands
if waiting_for_compositor_buffers_to_exit {
continue;
}
let mut replies = Vec::new();
for PaintRequest { buffer_requests, scale, layer_id, epoch, layer_kind }
in requests.into_iter() {
if self.current_epoch == Some(epoch) {
self.paint(&mut replies, buffer_requests, scale, layer_id, layer_kind);
} else {
debug!("painter epoch mismatch: {:?} != {:?}", self.current_epoch, epoch);
debug!("PaintTask: Ignoring requests with epoch mismatch: {:?} != {:?}",
self.current_epoch,
epoch);
self.compositor.ignore_buffer_requests(buffer_requests);
}
}
for reply in replies.iter() {
let &(_, ref buffer_set) = reply;
self.used_buffer_count += (*buffer_set).buffers.len();
}
debug!("PaintTask: returning surfaces");
self.compositor.assign_painted_buffers(self.id,
self.current_epoch.unwrap(),
replies,
frame_tree_id);
}
Msg::UnusedBuffer(unused_buffers) => {
debug!("PaintTask {:?}: Received {} unused buffers", self.id, unused_buffers.len());
self.used_buffer_count -= unused_buffers.len();
for buffer in unused_buffers.into_iter().rev() {
self.buffer_map.insert(native_display!(self), buffer);
}
if waiting_for_compositor_buffers_to_exit && self.used_buffer_count == 0 {
debug!("PaintTask: Received all loaned buffers, exiting.");
exit_response_channel.map(|channel| channel.send(()));
break;
}
}
Msg::PaintPermissionGranted => {
self.paint_permission = true;
@ -316,88 +267,25 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
Msg::PaintPermissionRevoked => {
self.paint_permission = false;
}
Msg::CollectReports(reports_chan) => {
// FIXME(njn): should eventually measure other parts of the paint task.
let mut reports = vec![];
reports.push(Report {
path: path!["pages", format!("url({})", self.url), "paint-task", "buffer-map"],
size: self.buffer_map.mem(),
});
reports_chan.send(reports);
Msg::CollectReports(_) => {
// FIXME(njn): should eventually measure the paint task.
}
Msg::Exit(response_channel, exit_type) => {
Msg::Exit(response_channel, _) => {
let msg = mem::ProfilerMsg::UnregisterReporter(self.reporter_name.clone());
self.mem_profiler_chan.send(msg);
// Ask the compositor to return any used buffers it
// is holding for this paint task. This previously was
// sent from the constellation. However, it needs to be sent
// from here to avoid a race condition with the paint
// messages above.
// Ask the compositor to remove any layers it is holding for this paint task.
// FIXME(mrobinson): This can probably move back to the constellation now.
self.compositor.notify_paint_task_exiting(self.id);
let should_wait_for_compositor_buffers = match exit_type {
PipelineExitType::Complete => false,
PipelineExitType::PipelineOnly => self.used_buffer_count != 0
};
if !should_wait_for_compositor_buffers {
debug!("PaintTask: Exiting without waiting for compositor buffers.");
response_channel.map(|channel| channel.send(()));
break;
}
// If we own buffers in the compositor and we are not exiting completely, wait
// for the compositor to return buffers, so that we can release them properly.
// When doing a complete exit, the compositor lets all buffers leak.
debug!("PaintTask {:?}: Saw ExitMsg, {} buffers in use", self.id, self.used_buffer_count);
waiting_for_compositor_buffers_to_exit = true;
exit_response_channel = response_channel;
debug!("PaintTask: Exiting.");
response_channel.map(|channel| channel.send(()));
break;
}
}
}
}
/// Retrieves an appropriately-sized layer buffer from the cache to match the requirements of
/// the given tile, or creates one if a suitable one cannot be found.
fn find_or_create_layer_buffer_for_tile(&mut self, tile: &BufferRequest, scale: f32)
-> Option<Box<LayerBuffer>> {
let width = tile.screen_rect.size.width;
let height = tile.screen_rect.size.height;
if opts::get().gpu_painting {
return None
}
match self.buffer_map.find(tile.screen_rect.size) {
Some(mut buffer) => {
buffer.rect = tile.page_rect;
buffer.screen_pos = tile.screen_rect;
buffer.resolution = scale;
buffer.native_surface.mark_wont_leak();
buffer.painted_with_cpu = true;
buffer.content_age = tile.content_age;
return Some(buffer)
}
None => {}
}
// Create an empty native surface. We mark it as not leaking
// in case it dies in transit to the compositor task.
let mut native_surface: NativeSurface =
layers::platform::surface::NativeSurface::new(native_display!(self),
Size2D::new(width as i32, height as i32));
native_surface.mark_wont_leak();
Some(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,
})
}
/// Paints one layer and places the painted tiles in `replies`.
fn paint(&mut self,
replies: &mut Vec<(LayerId, Box<LayerBufferSet>)>,
@ -423,10 +311,8 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
let tile_count = tiles.len();
for (i, tile) in tiles.into_iter().enumerate() {
let thread_id = i % self.worker_threads.len();
let layer_buffer = self.find_or_create_layer_buffer_for_tile(&tile, scale);
self.worker_threads[thread_id].paint_tile(thread_id,
tile,
layer_buffer,
stacking_context.clone(),
scale,
layer_kind);
@ -559,13 +445,11 @@ impl WorkerThreadProxy {
fn paint_tile(&mut self,
thread_id: usize,
tile: BufferRequest,
layer_buffer: Option<Box<LayerBuffer>>,
stacking_context: Arc<StackingContext>,
scale: f32,
layer_kind: LayerKind) {
let msg = MsgToWorkerThread::PaintTile(thread_id,
tile,
layer_buffer,
stacking_context,
scale,
layer_kind);
@ -613,14 +497,13 @@ impl WorkerThread {
loop {
match self.receiver.recv().unwrap() {
MsgToWorkerThread::Exit => break,
MsgToWorkerThread::PaintTile(thread_id, tile, layer_buffer, stacking_context, scale, layer_kind) => {
MsgToWorkerThread::PaintTile(thread_id, tile, stacking_context, scale, layer_kind) => {
let draw_target = self.optimize_and_paint_tile(thread_id,
&tile,
stacking_context,
scale,
layer_kind);
let buffer = self.create_layer_buffer_for_painted_tile(&tile,
layer_buffer,
let buffer = self.create_layer_buffer_for_painted_tile(tile,
draw_target,
scale);
self.sender.send(MsgFromWorkerThread::PaintedTile(buffer)).unwrap()
@ -715,32 +598,18 @@ impl WorkerThread {
draw_target
}
fn create_layer_buffer_for_painted_tile(&mut self,
tile: &BufferRequest,
layer_buffer: Option<Box<LayerBuffer>>,
draw_target: DrawTarget,
scale: f32)
-> Box<LayerBuffer> {
// Extract the texture from the draw target and place it into its slot in the buffer. If
// using CPU painting, upload it first.
//
// FIXME(pcwalton): We should supply the texture and native surface *to* the draw target in
// GPU painting mode, so that it doesn't have to recreate it.
if !opts::get().gpu_painting {
let mut buffer = layer_buffer.unwrap();
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());
});
return buffer
}
fn create_layer_buffer_for_gpu_painted_tile(&mut self,
tile: BufferRequest,
draw_target: DrawTarget,
scale: f32)
-> Box<LayerBuffer> {
// GPU painting path:
draw_target.make_current();
// We mark the native surface as not leaking in case the surfaces
// die on their way to the compositor task.
// FIXME(pcwalton): We should supply the texture and native surface *to* the draw target in
// GPU painting mode, so that it doesn't have to recreate it.
let mut native_surface: NativeSurface =
NativeSurface::from_draw_target_backing(draw_target.steal_draw_target_backing());
native_surface.mark_wont_leak();
@ -754,11 +623,56 @@ impl WorkerThread {
content_age: tile.content_age,
}
}
fn create_layer_buffer_for_cpu_painted_tile(&mut self,
mut tile: BufferRequest,
draw_target: DrawTarget,
scale: f32)
-> Box<LayerBuffer> {
let mut layer_buffer = tile.layer_buffer.take().unwrap_or_else(|| {
// Create an empty native surface. We mark it as not leaking
// in case it dies in transit to the compositor task.
let width = tile.screen_rect.size.width;
let height = tile.screen_rect.size.height;
let mut native_surface: NativeSurface =
layers::platform::surface::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,
}
});
draw_target.snapshot().get_data_surface().with_data(|data| {
layer_buffer.native_surface.upload(native_display!(self), data);
debug!("painting worker thread uploading to native surface {}",
layer_buffer.native_surface.get_id());
});
layer_buffer
}
fn create_layer_buffer_for_painted_tile(&mut self,
tile: BufferRequest,
draw_target: DrawTarget,
scale: f32)
-> Box<LayerBuffer> {
if opts::get().gpu_painting {
self.create_layer_buffer_for_gpu_painted_tile(tile, draw_target, scale)
} else {
self.create_layer_buffer_for_cpu_painted_tile(tile, draw_target, scale)
}
}
}
enum MsgToWorkerThread {
Exit,
PaintTile(usize, BufferRequest, Option<Box<LayerBuffer>>, Arc<StackingContext>, f32, LayerKind),
PaintTile(usize, BufferRequest, Arc<StackingContext>, f32, LayerKind),
}
enum MsgFromWorkerThread {

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

@ -8,7 +8,7 @@ use euclid::point::Point2D;
use euclid::rect::Rect;
use euclid::Matrix4;
use layers::platform::surface::NativeDisplay;
use layers::layers::LayerBufferSet;
use layers::layers::{BufferRequest, LayerBufferSet};
use std::fmt::{Formatter, Debug};
use std::fmt;
@ -108,6 +108,9 @@ pub trait PaintListener {
replies: Vec<(LayerId, Box<LayerBufferSet>)>,
frame_tree_id: FrameTreeId);
/// Inform the compositor that these buffer requests will be ignored.
fn ignore_buffer_requests(&mut self, buffer_requests: Vec<BufferRequest>);
// Notification that the paint task wants to exit.
fn notify_paint_task_exiting(&mut self, pipeline_id: PipelineId);
}

2
servo/components/servo/Cargo.lock сгенерированный
Просмотреть файл

@ -640,7 +640,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "layers"
version = "0.1.0"
source = "git+https://github.com/servo/rust-layers#01bfc9fbb0748af62d89720f67a84e9542fad1d1"
source = "git+https://github.com/servo/rust-layers#fff80972a75b5c4bce731de2ee2452bb8ef96430"
dependencies = [
"azure 0.1.0 (git+https://github.com/servo/rust-azure)",
"cgl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",

2
servo/ports/cef/Cargo.lock сгенерированный
Просмотреть файл

@ -632,7 +632,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "layers"
version = "0.1.0"
source = "git+https://github.com/servo/rust-layers#01bfc9fbb0748af62d89720f67a84e9542fad1d1"
source = "git+https://github.com/servo/rust-layers#fff80972a75b5c4bce731de2ee2452bb8ef96430"
dependencies = [
"azure 0.1.0 (git+https://github.com/servo/rust-azure)",
"cgl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",

2
servo/ports/gonk/Cargo.lock сгенерированный
Просмотреть файл

@ -566,7 +566,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "layers"
version = "0.1.0"
source = "git+https://github.com/servo/rust-layers#01bfc9fbb0748af62d89720f67a84e9542fad1d1"
source = "git+https://github.com/servo/rust-layers#fff80972a75b5c4bce731de2ee2452bb8ef96430"
dependencies = [
"azure 0.1.0 (git+https://github.com/servo/rust-azure)",
"cgl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",