Implement CPU rendering. Replace texture sharing with native OS surface sharing.

This commit is contained in:
Patrick Walton 2013-10-25 14:52:53 -07:00
parent d5bd4bfdb7
commit 3d0bfa5040
25 changed files with 675 additions and 277 deletions

View file

@ -4,28 +4,31 @@
// The task that handles all rendering/painting.
use azure::{AzFloat, AzGLContext};
use azure::azure_hl::{B8G8R8A8, DrawTarget};
use display_list::DisplayList;
use servo_msg::compositor_msg::{RenderListener, IdleRenderState, RenderingRenderState, LayerBuffer};
use servo_msg::compositor_msg::{LayerBufferSet, Epoch};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg};
use font_context::FontContext;
use azure::azure_hl::{B8G8R8A8, DrawTarget, StolenGLResources};
use azure::AzFloat;
use geom::matrix2d::Matrix2D;
use geom::size::Size2D;
use geom::rect::Rect;
use opts::Opts;
use render_context::RenderContext;
use std::comm::{Chan, Port, SharedChan};
use std::task::spawn_with;
use extra::arc::Arc;
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::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use buffer_map::BufferMap;
use std::comm::{Chan, Port, SharedChan};
use std::task::spawn_with;
use std::util;
use extra::arc::Arc;
use buffer_map::BufferMap;
use display_list::DisplayList;
use font_context::FontContext;
use opts::Opts;
use render_context::RenderContext;
pub struct RenderLayer<T> {
display_list: Arc<DisplayList<T>>,
@ -82,6 +85,13 @@ impl<T: Send> GenericSmartChan<Msg<T>> for RenderChan<T> {
}
}
/// If we're using GPU rendering, this provides the metadata needed to create a GL context that
/// is compatible with that of the main thread.
enum GraphicsContext {
CpuGraphicsContext,
GpuGraphicsContext,
}
pub struct RenderTask<C,T> {
id: PipelineId,
port: Port<Msg<T>>,
@ -93,16 +103,21 @@ pub struct RenderTask<C,T> {
/// A channel to the profiler.
profiler_chan: ProfilerChan,
share_gl_context: AzGLContext,
/// The graphics context to use.
graphics_context: GraphicsContext,
/// The native graphics context.
native_graphics_context: NativePaintingGraphicsContext,
/// The layer to be rendered
render_layer: Option<RenderLayer<T>>,
/// Permission to send paint messages to the compositor
paint_permission: bool,
/// Cached copy of last layers rendered
last_paint_msg: Option<~LayerBufferSet>,
/// A counter for epoch messages
epoch: Epoch,
/// A data structure to store unused LayerBuffers
buffer_map: BufferMap<~LayerBuffer>,
}
@ -114,11 +129,13 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
constellation_chan: ConstellationChan,
opts: Opts,
profiler_chan: ProfilerChan) {
do spawn_with((port, compositor, constellation_chan, opts, profiler_chan))
|(port, compositor, constellation_chan, opts, profiler_chan)| {
let share_gl_context = compositor.get_gl_context();
let graphics_metadata = compositor.get_graphics_metadata();
let cpu_painting = opts.cpu_painting;
let native_graphics_context =
NativePaintingGraphicsContext::from_metadata(&graphics_metadata);
// FIXME: rust/#5967
let mut render_task = RenderTask {
@ -131,16 +148,26 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
profiler_chan.clone()),
opts: opts,
profiler_chan: profiler_chan,
share_gl_context: share_gl_context,
graphics_context: if cpu_painting {
CpuGraphicsContext
} else {
GpuGraphicsContext
},
native_graphics_context: native_graphics_context,
render_layer: None,
paint_permission: false,
last_paint_msg: None,
epoch: Epoch(0),
buffer_map: BufferMap::new(10000000),
};
render_task.start();
// Destroy all the buffers.
render_task.buffer_map.clear(&render_task.native_graphics_context)
}
}
@ -157,7 +184,6 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
self.constellation_chan.send(RendererReadyMsg(self.id));
}
self.render_layer = Some(render_layer);
self.last_paint_msg = None;
}
ReRenderMsg(tiles, scale, epoch) => {
if self.epoch == epoch {
@ -169,7 +195,7 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
UnusedBufferMsg(unused_buffers) => {
// move_rev_iter is more efficient
for buffer in unused_buffers.move_rev_iter() {
self.buffer_map.insert(buffer);
self.buffer_map.insert(&self.native_graphics_context, buffer);
}
}
PaintPermissionGranted => {
@ -181,16 +207,6 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
}
None => {}
}
// FIXME: This sends the last paint request, anticipating what
// the compositor will ask for. However, even if it sends the right
// tiles, the compositor still asks for them, and they will be
// re-rendered redundantly.
match self.last_paint_msg {
Some(ref layer_buffer_set) => {
self.compositor.paint(self.id, layer_buffer_set.clone(), self.epoch);
}
None => {} // Nothing to do
}
}
PaintPermissionRevoked => {
self.paint_permission = false;
@ -221,7 +237,6 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
self.compositor.set_render_state(RenderingRenderState);
do time::profile(time::RenderingCategory, self.profiler_chan.clone()) {
// FIXME: Try not to create a new array here.
let mut new_buffers = ~[];
@ -230,43 +245,42 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
for tile in tiles.iter() {
let width = tile.screen_rect.size.width;
let height = tile.screen_rect.size.height;
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
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)
}
None => ~LayerBuffer {
draw_target: DrawTarget::new_with_fbo(self.opts.render_backend,
self.share_gl_context,
Size2D(width as i32, height as i32),
B8G8R8A8),
rect: tile.page_rect,
screen_pos: tile.screen_rect,
resolution: scale,
stride: (width * 4) as uint
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,
&self.native_graphics_context,
size,
B8G8R8A8);
draw_target.make_current();
draw_target
}
};
{
// Build the render context.
let ctx = RenderContext {
canvas: &buffer,
draw_target: &draw_target,
font_ctx: self.font_ctx,
opts: &self.opts
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(-(buffer.rect.origin.x) as AzFloat,
-(buffer.rect.origin.y) as AzFloat);
let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
-(tile.page_rect.origin.y) as AzFloat);
ctx.canvas.draw_target.set_transform(&matrix);
ctx.draw_target.set_transform(&matrix);
// Clear the buffer.
ctx.clear();
@ -274,14 +288,78 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
// Draw the display list.
do profile(time::RenderingDrawingCategory, self.profiler_chan.clone()) {
render_layer.display_list.get().draw_into_context(&ctx);
ctx.canvas.draw_target.flush();
ctx.draw_target.flush();
}
}
// 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(
&self.native_graphics_context,
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
}
}
};
do draw_target.snapshot().get_data_surface().with_data |data| {
buffer.native_surface.upload(&self.native_graphics_context, 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);
}
}
let layer_buffer_set = ~LayerBufferSet {
@ -290,12 +368,10 @@ 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.clone(), self.epoch);
self.compositor.paint(self.id, layer_buffer_set, self.epoch);
} else {
self.constellation_chan.send(RendererReadyMsg(self.id));
}
debug!("caching paint msg");
self.last_paint_msg = Some(layer_buffer_set);
self.compositor.set_render_state(IdleRenderState);
}
}