mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Implement CPU rendering. Replace texture sharing with native OS surface sharing.
This commit is contained in:
parent
d5bd4bfdb7
commit
3d0bfa5040
25 changed files with 675 additions and 277 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue