aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/gfx/render_task.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/gfx/render_task.rs')
-rw-r--r--src/components/gfx/render_task.rs372
1 files changed, 206 insertions, 166 deletions
diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs
index bb9ed1f665a..24e3580eb33 100644
--- a/src/components/gfx/render_task.rs
+++ b/src/components/gfx/render_task.rs
@@ -2,7 +2,12 @@
* 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 task that handles all rendering/painting.
+//! The task that handles all rendering/painting.
+
+use buffer_map::BufferMap;
+use display_list::DisplayList;
+use font_context::{FontContext, FontContextInfo};
+use render_context::RenderContext;
use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
use azure::AzFloat;
@@ -12,12 +17,14 @@ use geom::size::Size2D;
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
use layers::platform::surface::{NativeSurfaceMethods};
use layers;
-use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer, LayerBufferSet};
-use servo_msg::compositor_msg::{RenderListener, RenderingRenderState};
+use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer};
+use servo_msg::compositor_msg::{LayerBufferSet, LayerId, LayerMetadata, RenderListener};
+use servo_msg::compositor_msg::{RenderingRenderState, ScrollPolicy};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg};
use servo_msg::constellation_msg::{Failure, FailureMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::opts::Opts;
+use servo_util::smallvec::{SmallVec, SmallVec1};
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use servo_util::task::send_on_failure;
@@ -26,20 +33,23 @@ use std::comm::{Chan, Port};
use std::task;
use sync::Arc;
-use buffer_map::BufferMap;
-use display_list::DisplayListCollection;
-use font_context::{FontContext, FontContextInfo};
-use render_context::RenderContext;
-
-pub struct RenderLayer<T> {
- display_list_collection: Arc<DisplayListCollection<T>>,
- size: Size2D<uint>,
- color: Color
+/// Information about a layer that layout sends to the painting task.
+pub struct RenderLayer {
+ /// A per-pipeline ID describing this layer that should be stable across reflows.
+ id: LayerId,
+ /// The display list describing the contents of this layer.
+ display_list: Arc<DisplayList>,
+ /// The position of the layer in pixels.
+ position: Rect<uint>,
+ /// The color of the background in this layer. Used for unrendered content.
+ background_color: Color,
+ /// The scrolling policy of this layer.
+ scroll_policy: ScrollPolicy,
}
-pub enum Msg<T> {
- RenderMsg(RenderLayer<T>),
- ReRenderMsg(~[BufferRequest], f32, Epoch),
+pub enum Msg {
+ RenderMsg(SmallVec1<RenderLayer>),
+ ReRenderMsg(~[BufferRequest], f32, LayerId, Epoch),
UnusedBufferMsg(~[~LayerBuffer]),
PaintPermissionGranted,
PaintPermissionRevoked,
@@ -63,22 +73,21 @@ pub fn BufferRequest(screen_rect: Rect<uint>, page_rect: Rect<f32>) -> BufferReq
}
}
-// FIXME(rust#9155): this should be a newtype struct, but
-// generic newtypes ICE when compiled cross-crate
-pub struct RenderChan<T> {
- chan: Chan<Msg<T>>,
+// FIXME(#2005, pcwalton): This should be a newtype struct.
+pub struct RenderChan {
+ chan: Chan<Msg>,
}
-impl<T: Send> Clone for RenderChan<T> {
- fn clone(&self) -> RenderChan<T> {
+impl Clone for RenderChan {
+ fn clone(&self) -> RenderChan {
RenderChan {
chan: self.chan.clone(),
}
}
}
-impl<T: Send> RenderChan<T> {
- pub fn new() -> (Port<Msg<T>>, RenderChan<T>) {
+impl RenderChan {
+ pub fn new() -> (Port<Msg>, RenderChan) {
let (port, chan) = Chan::new();
let render_chan = RenderChan {
chan: chan,
@@ -86,11 +95,11 @@ impl<T: Send> RenderChan<T> {
(port, render_chan)
}
- pub fn send(&self, msg: Msg<T>) {
+ pub fn send(&self, msg: Msg) {
assert!(self.try_send(msg), "RenderChan.send: render port closed")
}
- pub fn try_send(&self, msg: Msg<T>) -> bool {
+ pub fn try_send(&self, msg: Msg) -> bool {
self.chan.try_send(msg)
}
}
@@ -102,9 +111,9 @@ enum GraphicsContext {
GpuGraphicsContext,
}
-pub struct RenderTask<C,T> {
+pub struct RenderTask<C> {
id: PipelineId,
- port: Port<Msg<T>>,
+ port: Port<Msg>,
compositor: C,
constellation_chan: ConstellationChan,
font_ctx: ~FontContext,
@@ -119,8 +128,8 @@ pub struct RenderTask<C,T> {
/// The native graphics context.
native_graphics_context: Option<NativePaintingGraphicsContext>,
- /// The layer to be rendered
- render_layer: Option<RenderLayer<T>>,
+ /// The layers to be rendered.
+ render_layers: SmallVec1<RenderLayer>,
/// Permission to send paint messages to the compositor
paint_permission: bool,
@@ -140,9 +149,25 @@ macro_rules! native_graphics_context(
)
)
-impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
+fn initialize_layers<C:RenderListener>(
+ compositor: &mut C,
+ pipeline_id: PipelineId,
+ epoch: Epoch,
+ render_layers: &[RenderLayer]) {
+ let metadata = render_layers.iter().map(|render_layer| {
+ LayerMetadata {
+ id: render_layer.id,
+ position: render_layer.position,
+ background_color: render_layer.background_color,
+ scroll_policy: render_layer.scroll_policy,
+ }
+ }).collect();
+ compositor.initialize_layers_for_pipeline(pipeline_id, metadata, epoch);
+}
+
+impl<C: RenderListener + Send> RenderTask<C> {
pub fn create(id: PipelineId,
- port: Port<Msg<T>>,
+ port: Port<Msg>,
compositor: C,
constellation_chan: ConstellationChan,
failure_msg: Failure,
@@ -181,7 +206,7 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
native_graphics_context: native_graphics_context,
- render_layer: None,
+ render_layers: SmallVec1::new(),
paint_permission: false,
epoch: Epoch(0),
@@ -207,20 +232,25 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
loop {
match self.port.recv() {
- RenderMsg(render_layer) => {
- if self.paint_permission {
- self.epoch.next();
- self.compositor.set_layer_page_size_and_color(self.id, render_layer.size, self.epoch, render_layer.color);
- } else {
+ RenderMsg(render_layers) => {
+ self.epoch.next();
+ self.render_layers = render_layers;
+
+ if !self.paint_permission {
debug!("render_task: render ready msg");
let ConstellationChan(ref mut c) = self.constellation_chan;
c.send(RendererReadyMsg(self.id));
+ continue;
}
- self.render_layer = Some(render_layer);
+
+ initialize_layers(&mut self.compositor,
+ self.id,
+ self.epoch,
+ self.render_layers.as_slice());
}
- ReRenderMsg(tiles, scale, epoch) => {
+ ReRenderMsg(tiles, scale, layer_id, epoch) => {
if self.epoch == epoch {
- self.render(tiles, scale);
+ self.render(tiles, scale, layer_id);
} else {
debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
}
@@ -233,12 +263,16 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
}
PaintPermissionGranted => {
self.paint_permission = true;
- match self.render_layer {
- Some(ref render_layer) => {
- self.epoch.next();
- self.compositor.set_layer_page_size_and_color(self.id, render_layer.size, self.epoch, render_layer.color);
- }
- None => {}
+
+ // Here we assume that the main layer—the layer responsible for the page size—
+ // is the first layer. This is a pretty fragile assumption. It will be fixed
+ // once we use the layers-based scrolling infrastructure for all scrolling.
+ if self.render_layers.len() > 1 {
+ self.epoch.next();
+ initialize_layers(&mut self.compositor,
+ self.id,
+ self.epoch,
+ self.render_layers.as_slice());
}
}
PaintPermissionRevoked => {
@@ -253,138 +287,144 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
}
}
- fn render(&mut self, tiles: ~[BufferRequest], scale: f32) {
- if self.render_layer.is_none() {
- return
- }
-
- self.compositor.set_render_state(RenderingRenderState);
+ /// Renders one layer and sends the tiles back to the layer.
+ ///
+ /// FIXME(pcwalton): We will probably want to eventually send all layers belonging to a page in
+ /// one transaction, to avoid the user seeing inconsistent states.
+ fn render(&mut self, tiles: ~[BufferRequest], scale: f32, layer_id: LayerId) {
time::profile(time::RenderingCategory, self.profiler_chan.clone(), || {
// FIXME: Try not to create a new array here.
let mut new_buffers = ~[];
+ // Find the appropriate render layer.
+ let render_layer = match self.render_layers.iter().find(|layer| layer.id == layer_id) {
+ Some(render_layer) => render_layer,
+ None => return,
+ };
+
+ self.compositor.set_render_state(RenderingRenderState);
+
// Divide up the layer into tiles.
- time::profile(time::RenderingPrepBuffCategory, self.profiler_chan.clone(), || {
- for tile in tiles.iter() {
- let width = tile.screen_rect.size.width;
- let height = tile.screen_rect.size.height;
-
- let size = Size2D(width as i32, height as i32);
- let draw_target = match self.graphics_context {
- CpuGraphicsContext => {
- DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
- }
- GpuGraphicsContext => {
- // FIXME(pcwalton): Cache the components of draw targets
- // (texture color buffer, renderbuffers) instead of recreating them.
- let draw_target =
- DrawTarget::new_with_fbo(self.opts.render_backend,
- native_graphics_context!(self),
- size,
- B8G8R8A8);
- draw_target.make_current();
- draw_target
- }
+ for tile in tiles.iter() {
+ let width = tile.screen_rect.size.width;
+ let height = tile.screen_rect.size.height;
+
+ let size = Size2D(width as i32, height as i32);
+ let draw_target = match self.graphics_context {
+ CpuGraphicsContext => {
+ DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
+ }
+ GpuGraphicsContext => {
+ // FIXME(pcwalton): Cache the components of draw targets
+ // (texture color buffer, renderbuffers) instead of recreating them.
+ let draw_target =
+ DrawTarget::new_with_fbo(self.opts.render_backend,
+ native_graphics_context!(self),
+ size,
+ B8G8R8A8);
+ draw_target.make_current();
+ draw_target
+ }
+ };
+
+ {
+ // Build the render context.
+ let mut ctx = RenderContext {
+ draw_target: &draw_target,
+ font_ctx: &mut self.font_ctx,
+ opts: &self.opts,
+ page_rect: tile.page_rect,
+ screen_rect: tile.screen_rect,
};
- {
- // Build the render context.
- let mut ctx = RenderContext {
- draw_target: &draw_target,
- font_ctx: &mut self.font_ctx,
- opts: &self.opts,
- page_rect: tile.page_rect,
- screen_rect: tile.screen_rect,
- };
+ // Apply the translation to render the tile we want.
+ let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
+ let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
+ let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
+ -(tile.page_rect.origin.y) as AzFloat);
+ let matrix = matrix.translate(-(render_layer.position.origin.x as AzFloat),
+ -(render_layer.position.origin.y as AzFloat));
- // Apply the translation to render the tile we want.
- let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
- let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
- let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
- -(tile.page_rect.origin.y) as AzFloat);
-
- ctx.draw_target.set_transform(&matrix);
-
- // Clear the buffer.
- ctx.clear();
-
- // Draw the display list.
- profile(time::RenderingDrawingCategory, self.profiler_chan.clone(), || {
- let render_layer = self.render_layer.as_ref().unwrap();
- render_layer.display_list_collection.get().draw_lists_into_context(&mut ctx);
- ctx.draw_target.flush();
- });
- }
+ ctx.draw_target.set_transform(&matrix);
- // Extract the texture from the draw target and place it into its slot in the
- // buffer. If using CPU rendering, upload it first.
- //
- // FIXME(pcwalton): We should supply the texture and native surface *to* the
- // draw target in GPU rendering mode, so that it doesn't have to recreate it.
- let buffer = match self.graphics_context {
- CpuGraphicsContext => {
- let buffer = match self.buffer_map.find(tile.screen_rect.size) {
- Some(buffer) => {
- let mut buffer = buffer;
- buffer.rect = tile.page_rect;
- buffer.screen_pos = tile.screen_rect;
- buffer.resolution = scale;
- buffer.native_surface.mark_wont_leak();
- 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::NativeSurfaceMethods::new(
- native_graphics_context!(self),
- Size2D(width as i32, height as i32),
- width as i32 * 4);
- native_surface.mark_wont_leak();
-
- ~LayerBuffer {
- native_surface: native_surface,
- rect: tile.page_rect,
- screen_pos: tile.screen_rect,
- resolution: scale,
- stride: (width * 4) as uint
- }
- }
- };
+ // Clear the buffer.
+ ctx.clear();
- draw_target.snapshot().get_data_surface().with_data(|data| {
- buffer.native_surface.upload(native_graphics_context!(self), data);
- debug!("RENDERER uploading to native surface {:d}",
- buffer.native_surface.get_id() as int);
- });
+ // Draw the display list.
+ profile(time::RenderingDrawingCategory, self.profiler_chan.clone(), || {
+ render_layer.display_list.get().draw_into_context(&mut ctx);
+ ctx.draw_target.flush();
+ });
+ }
- buffer
- }
- GpuGraphicsContext => {
- draw_target.make_current();
- let StolenGLResources {
- surface: native_surface
- } = draw_target.steal_gl_resources().unwrap();
-
- // We mark the native surface as not leaking in case the surfaces
- // die on their way to the compositor task.
- let mut native_surface: NativeSurface =
- NativeSurfaceAzureMethods::from_azure_surface(native_surface);
- native_surface.mark_wont_leak();
-
- ~LayerBuffer {
- native_surface: native_surface,
- rect: tile.page_rect,
- screen_pos: tile.screen_rect,
- resolution: scale,
- stride: (width * 4) as uint
+ // Extract the texture from the draw target and place it into its slot in the
+ // buffer. If using CPU rendering, upload it first.
+ //
+ // FIXME(pcwalton): We should supply the texture and native surface *to* the
+ // draw target in GPU rendering mode, so that it doesn't have to recreate it.
+ let buffer = match self.graphics_context {
+ CpuGraphicsContext => {
+ let buffer = match self.buffer_map.find(tile.screen_rect.size) {
+ Some(buffer) => {
+ let mut buffer = buffer;
+ buffer.rect = tile.page_rect;
+ buffer.screen_pos = tile.screen_rect;
+ buffer.resolution = scale;
+ buffer.native_surface.mark_wont_leak();
+ 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::NativeSurfaceMethods::new(
+ native_graphics_context!(self),
+ Size2D(width as i32, height as i32),
+ width as i32 * 4);
+ native_surface.mark_wont_leak();
+
+ ~LayerBuffer {
+ native_surface: native_surface,
+ rect: tile.page_rect,
+ screen_pos: tile.screen_rect,
+ resolution: scale,
+ stride: (width * 4) as uint
+ }
}
+ };
+
+ draw_target.snapshot().get_data_surface().with_data(|data| {
+ buffer.native_surface.upload(native_graphics_context!(self), data);
+ debug!("RENDERER uploading to native surface {:d}",
+ buffer.native_surface.get_id() as int);
+ });
+
+ buffer
+ }
+ GpuGraphicsContext => {
+ draw_target.make_current();
+ let StolenGLResources {
+ surface: native_surface
+ } = draw_target.steal_gl_resources().unwrap();
+
+ // We mark the native surface as not leaking in case the surfaces
+ // die on their way to the compositor task.
+ let mut native_surface: NativeSurface =
+ NativeSurfaceAzureMethods::from_azure_surface(native_surface);
+ native_surface.mark_wont_leak();
+
+ ~LayerBuffer {
+ native_surface: native_surface,
+ rect: tile.page_rect,
+ screen_pos: tile.screen_rect,
+ resolution: scale,
+ stride: (width * 4) as uint
}
- };
-
- new_buffers.push(buffer);
- }
- });
+ }
+ };
+
+ new_buffers.push(buffer);
+ }
let layer_buffer_set = ~LayerBufferSet {
buffers: new_buffers,
@@ -392,7 +432,7 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
debug!("render_task: returning surface");
if self.paint_permission {
- self.compositor.paint(self.id, layer_buffer_set, self.epoch);
+ self.compositor.paint(self.id, render_layer.id, layer_buffer_set, self.epoch);
} else {
debug!("render_task: RendererReadyMsg send");
let ConstellationChan(ref mut c) = self.constellation_chan;