mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
Implement enough of 3d transforms spec to run the CSS FPS demo.
This commit is contained in:
parent
d86c587925
commit
39ddbbb0e1
19 changed files with 894 additions and 145 deletions
|
@ -32,7 +32,7 @@ use euclid::approxeq::ApproxEq;
|
|||
use euclid::num::Zero;
|
||||
use libc::uintptr_t;
|
||||
use paint_task::PaintLayer;
|
||||
use msg::compositor_msg::LayerId;
|
||||
use msg::compositor_msg::{LayerId, LayerKind};
|
||||
use net_traits::image::base::Image;
|
||||
use util::opts;
|
||||
use util::cursor::Cursor;
|
||||
|
@ -246,6 +246,12 @@ pub struct StackingContext {
|
|||
|
||||
/// A transform to be applied to this stacking context.
|
||||
pub transform: Matrix4,
|
||||
|
||||
/// The perspective matrix to be applied to children.
|
||||
pub perspective: Matrix4,
|
||||
|
||||
/// Whether this stacking context creates a new 3d rendering context.
|
||||
pub establishes_3d_context: bool,
|
||||
}
|
||||
|
||||
impl StackingContext {
|
||||
|
@ -255,30 +261,40 @@ impl StackingContext {
|
|||
bounds: &Rect<Au>,
|
||||
overflow: &Rect<Au>,
|
||||
z_index: i32,
|
||||
transform: &Matrix4,
|
||||
filters: filter::T,
|
||||
blend_mode: mix_blend_mode::T,
|
||||
layer: Option<Arc<PaintLayer>>)
|
||||
layer: Option<Arc<PaintLayer>>,
|
||||
transform: Matrix4,
|
||||
perspective: Matrix4,
|
||||
establishes_3d_context: bool)
|
||||
-> StackingContext {
|
||||
StackingContext {
|
||||
display_list: display_list,
|
||||
layer: layer,
|
||||
bounds: *bounds,
|
||||
overflow: *overflow,
|
||||
z_index: z_index,
|
||||
transform: *transform,
|
||||
filters: filters,
|
||||
blend_mode: blend_mode,
|
||||
layer: layer,
|
||||
transform: transform,
|
||||
perspective: perspective,
|
||||
establishes_3d_context: establishes_3d_context,
|
||||
}
|
||||
}
|
||||
|
||||
/// Draws the stacking context in the proper order according to the steps in CSS 2.1 § E.2.
|
||||
pub fn optimize_and_draw_into_context(&self,
|
||||
paint_context: &mut PaintContext,
|
||||
tile_bounds: &Rect<AzFloat>,
|
||||
transform: &Matrix4,
|
||||
clip_rect: Option<&Rect<Au>>) {
|
||||
let transform = transform.mul(&self.transform);
|
||||
pub fn draw_into_context(&self,
|
||||
display_list: &DisplayList,
|
||||
paint_context: &mut PaintContext,
|
||||
tile_bounds: &Rect<AzFloat>,
|
||||
transform: &Matrix4,
|
||||
clip_rect: Option<&Rect<Au>>) {
|
||||
// If a layer is being used, the transform for this layer
|
||||
// will be handled by the compositor.
|
||||
let transform = match self.layer {
|
||||
Some(..) => *transform,
|
||||
None => transform.mul(&self.transform),
|
||||
};
|
||||
let temporary_draw_target =
|
||||
paint_context.get_or_create_temporary_draw_target(&self.filters, self.blend_mode);
|
||||
{
|
||||
|
@ -289,12 +305,9 @@ impl StackingContext {
|
|||
screen_rect: paint_context.screen_rect,
|
||||
clip_rect: clip_rect.map(|clip_rect| *clip_rect),
|
||||
transient_clip: None,
|
||||
layer_kind: paint_context.layer_kind,
|
||||
};
|
||||
|
||||
// Optimize the display list to throw out out-of-bounds display items and so forth.
|
||||
let display_list =
|
||||
DisplayListOptimizer::new(tile_bounds).optimize(&*self.display_list);
|
||||
|
||||
if opts::get().dump_display_list_optimized {
|
||||
println!("**** optimized display list. Tile bounds: {:?}", tile_bounds);
|
||||
display_list.print_items("*".to_owned());
|
||||
|
@ -409,6 +422,35 @@ impl StackingContext {
|
|||
paint_context.draw_temporary_draw_target_if_necessary(&temporary_draw_target,
|
||||
&self.filters,
|
||||
self.blend_mode)
|
||||
|
||||
}
|
||||
|
||||
/// Optionally optimize and then draws the stacking context.
|
||||
pub fn optimize_and_draw_into_context(&self,
|
||||
paint_context: &mut PaintContext,
|
||||
tile_bounds: &Rect<AzFloat>,
|
||||
transform: &Matrix4,
|
||||
clip_rect: Option<&Rect<Au>>) {
|
||||
// TODO(gw): This is a hack to avoid running the DL optimizer
|
||||
// on 3d transformed tiles. We should have a better solution
|
||||
// than just disabling the opts here.
|
||||
if paint_context.layer_kind == LayerKind::Layer3D {
|
||||
self.draw_into_context(&self.display_list,
|
||||
paint_context,
|
||||
tile_bounds,
|
||||
transform,
|
||||
clip_rect);
|
||||
|
||||
} else {
|
||||
// Optimize the display list to throw out out-of-bounds display items and so forth.
|
||||
let display_list = DisplayListOptimizer::new(tile_bounds).optimize(&*self.display_list);
|
||||
|
||||
self.draw_into_context(&display_list,
|
||||
paint_context,
|
||||
tile_bounds,
|
||||
transform,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate the given tile rect into the coordinate system of a child stacking context.
|
||||
|
@ -1005,7 +1047,7 @@ impl<'a> Iterator for DisplayItemIterator<'a> {
|
|||
impl DisplayItem {
|
||||
/// Paints this display item into the given painting context.
|
||||
fn draw_into_context(&self, paint_context: &mut PaintContext) {
|
||||
{
|
||||
if paint_context.layer_kind == LayerKind::Layer2D {
|
||||
let this_clip = &self.base().clip;
|
||||
match paint_context.transient_clip {
|
||||
Some(ref transient_clip) if transient_clip == this_clip => {}
|
||||
|
|
|
@ -29,6 +29,7 @@ use euclid::rect::Rect;
|
|||
use euclid::side_offsets::SideOffsets2D;
|
||||
use euclid::size::Size2D;
|
||||
use libc::types::common::c99::uint32_t;
|
||||
use msg::compositor_msg::LayerKind;
|
||||
use net_traits::image::base::Image;
|
||||
use png::PixelsByColorType;
|
||||
use std::default::Default;
|
||||
|
@ -54,6 +55,8 @@ pub struct PaintContext<'a> {
|
|||
/// clipping region used by the last display item. We cache the last value so that we avoid
|
||||
/// pushing and popping clipping regions unnecessarily.
|
||||
pub transient_clip: Option<ClippingRegion>,
|
||||
/// A temporary hack to disable clipping optimizations on 3d layers.
|
||||
pub layer_kind: LayerKind,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
|
@ -21,7 +21,7 @@ use layers::platform::surface::NativeSurface;
|
|||
use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet};
|
||||
use layers;
|
||||
use canvas_traits::CanvasMsg;
|
||||
use msg::compositor_msg::{Epoch, FrameTreeId, LayerId};
|
||||
use msg::compositor_msg::{Epoch, FrameTreeId, LayerId, LayerKind};
|
||||
use msg::compositor_msg::{LayerProperties, PaintListener, ScrollPolicy};
|
||||
use msg::constellation_msg::Msg as ConstellationMsg;
|
||||
use msg::constellation_msg::{ConstellationChan, Failure, PipelineId};
|
||||
|
@ -69,6 +69,7 @@ pub struct PaintRequest {
|
|||
pub scale: f32,
|
||||
pub layer_id: LayerId,
|
||||
pub epoch: Epoch,
|
||||
pub layer_kind: LayerKind,
|
||||
}
|
||||
|
||||
pub enum Msg {
|
||||
|
@ -272,10 +273,10 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
|
|||
}
|
||||
|
||||
let mut replies = Vec::new();
|
||||
for PaintRequest { buffer_requests, scale, layer_id, epoch }
|
||||
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);
|
||||
self.paint(&mut replies, buffer_requests, scale, layer_id, layer_kind);
|
||||
} else {
|
||||
debug!("painter epoch mismatch: {:?} != {:?}", self.current_epoch, epoch);
|
||||
}
|
||||
|
@ -405,7 +406,8 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
|
|||
replies: &mut Vec<(LayerId, Box<LayerBufferSet>)>,
|
||||
mut tiles: Vec<BufferRequest>,
|
||||
scale: f32,
|
||||
layer_id: LayerId) {
|
||||
layer_id: LayerId,
|
||||
layer_kind: LayerKind) {
|
||||
time::profile(time::ProfilerCategory::Painting, None, self.time_profiler_chan.clone(), || {
|
||||
// Bail out if there is no appropriate stacking context.
|
||||
let stacking_context = if let Some(ref stacking_context) = self.root_stacking_context {
|
||||
|
@ -429,7 +431,8 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
|
|||
tile,
|
||||
layer_buffer,
|
||||
stacking_context.clone(),
|
||||
scale);
|
||||
scale,
|
||||
layer_kind);
|
||||
}
|
||||
let new_buffers = (0..tile_count).map(|i| {
|
||||
let thread_id = i % self.worker_threads.len();
|
||||
|
@ -450,32 +453,72 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
|
|||
};
|
||||
|
||||
let mut properties = Vec::new();
|
||||
build(&mut properties, &**root_stacking_context, &ZERO_POINT);
|
||||
build(&mut properties,
|
||||
&**root_stacking_context,
|
||||
&ZERO_POINT,
|
||||
&Matrix4::identity(),
|
||||
&Matrix4::identity(),
|
||||
None);
|
||||
self.compositor.initialize_layers_for_pipeline(self.id, properties, self.current_epoch.unwrap());
|
||||
|
||||
fn build(properties: &mut Vec<LayerProperties>,
|
||||
stacking_context: &StackingContext,
|
||||
page_position: &Point2D<Au>) {
|
||||
let page_position = stacking_context.bounds.origin + *page_position;
|
||||
if let Some(ref paint_layer) = stacking_context.layer {
|
||||
// Layers start at the top left of their overflow rect, as far as the info we give to
|
||||
// the compositor is concerned.
|
||||
let overflow_relative_page_position = page_position + stacking_context.overflow.origin;
|
||||
let layer_position =
|
||||
Rect::new(Point2D::new(overflow_relative_page_position.x.to_nearest_px() as f32,
|
||||
overflow_relative_page_position.y.to_nearest_px() as f32),
|
||||
Size2D::new(stacking_context.overflow.size.width.to_nearest_px() as f32,
|
||||
stacking_context.overflow.size.height.to_nearest_px() as f32));
|
||||
properties.push(LayerProperties {
|
||||
id: paint_layer.id,
|
||||
rect: layer_position,
|
||||
background_color: paint_layer.background_color,
|
||||
scroll_policy: paint_layer.scroll_policy,
|
||||
})
|
||||
}
|
||||
page_position: &Point2D<Au>,
|
||||
transform: &Matrix4,
|
||||
perspective: &Matrix4,
|
||||
parent_id: Option<LayerId>) {
|
||||
|
||||
let transform = transform.mul(&stacking_context.transform);
|
||||
let perspective = perspective.mul(&stacking_context.perspective);
|
||||
|
||||
let (next_parent_id, page_position, transform, perspective) = match stacking_context.layer {
|
||||
Some(ref paint_layer) => {
|
||||
// Layers start at the top left of their overflow rect, as far as the info we give to
|
||||
// the compositor is concerned.
|
||||
let overflow_relative_page_position = *page_position +
|
||||
stacking_context.bounds.origin +
|
||||
stacking_context.overflow.origin;
|
||||
let layer_position =
|
||||
Rect::new(Point2D::new(overflow_relative_page_position.x.to_nearest_px() as f32,
|
||||
overflow_relative_page_position.y.to_nearest_px() as f32),
|
||||
Size2D::new(stacking_context.overflow.size.width.to_nearest_px() as f32,
|
||||
stacking_context.overflow.size.height.to_nearest_px() as f32));
|
||||
|
||||
let establishes_3d_context = stacking_context.establishes_3d_context;
|
||||
|
||||
properties.push(LayerProperties {
|
||||
id: paint_layer.id,
|
||||
parent_id: parent_id,
|
||||
rect: layer_position,
|
||||
background_color: paint_layer.background_color,
|
||||
scroll_policy: paint_layer.scroll_policy,
|
||||
transform: transform,
|
||||
perspective: perspective,
|
||||
establishes_3d_context: establishes_3d_context,
|
||||
});
|
||||
|
||||
// When there is a new layer, the transforms and origin
|
||||
// are handled by the compositor.
|
||||
(Some(paint_layer.id),
|
||||
Point2D::zero(),
|
||||
Matrix4::identity(),
|
||||
Matrix4::identity())
|
||||
}
|
||||
None => {
|
||||
(parent_id,
|
||||
stacking_context.bounds.origin + *page_position,
|
||||
transform,
|
||||
perspective)
|
||||
}
|
||||
};
|
||||
|
||||
for kid in stacking_context.display_list.children.iter() {
|
||||
build(properties, &**kid, &page_position)
|
||||
build(properties,
|
||||
&**kid,
|
||||
&page_position,
|
||||
&transform,
|
||||
&perspective,
|
||||
next_parent_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -522,8 +565,14 @@ impl WorkerThreadProxy {
|
|||
tile: BufferRequest,
|
||||
layer_buffer: Option<Box<LayerBuffer>>,
|
||||
stacking_context: Arc<StackingContext>,
|
||||
scale: f32) {
|
||||
let msg = MsgToWorkerThread::PaintTile(thread_id, tile, layer_buffer, stacking_context, scale);
|
||||
scale: f32,
|
||||
layer_kind: LayerKind) {
|
||||
let msg = MsgToWorkerThread::PaintTile(thread_id,
|
||||
tile,
|
||||
layer_buffer,
|
||||
stacking_context,
|
||||
scale,
|
||||
layer_kind);
|
||||
self.sender.send(msg).unwrap()
|
||||
}
|
||||
|
||||
|
@ -568,8 +617,12 @@ impl WorkerThread {
|
|||
loop {
|
||||
match self.receiver.recv().unwrap() {
|
||||
MsgToWorkerThread::Exit => break,
|
||||
MsgToWorkerThread::PaintTile(thread_id, tile, layer_buffer, stacking_context, scale) => {
|
||||
let draw_target = self.optimize_and_paint_tile(thread_id, &tile, stacking_context, scale);
|
||||
MsgToWorkerThread::PaintTile(thread_id, tile, layer_buffer, 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,
|
||||
draw_target,
|
||||
|
@ -584,7 +637,8 @@ impl WorkerThread {
|
|||
thread_id: usize,
|
||||
tile: &BufferRequest,
|
||||
stacking_context: Arc<StackingContext>,
|
||||
scale: f32)
|
||||
scale: f32,
|
||||
layer_kind: LayerKind)
|
||||
-> DrawTarget {
|
||||
let size = Size2D::new(tile.screen_rect.size.width as i32, tile.screen_rect.size.height as i32);
|
||||
let draw_target = if !opts::get().gpu_painting {
|
||||
|
@ -612,6 +666,7 @@ impl WorkerThread {
|
|||
screen_rect: tile.screen_rect,
|
||||
clip_rect: None,
|
||||
transient_clip: None,
|
||||
layer_kind: layer_kind,
|
||||
};
|
||||
|
||||
// Apply a translation to start at the boundaries of the stacking context, since the
|
||||
|
@ -708,7 +763,7 @@ impl WorkerThread {
|
|||
|
||||
enum MsgToWorkerThread {
|
||||
Exit,
|
||||
PaintTile(usize, BufferRequest, Option<Box<LayerBuffer>>, Arc<StackingContext>, f32),
|
||||
PaintTile(usize, BufferRequest, Option<Box<LayerBuffer>>, Arc<StackingContext>, f32, LayerKind),
|
||||
}
|
||||
|
||||
enum MsgFromWorkerThread {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue