Refactor renderer into single function.

This removes the task per tile rendering and instead renders tiles
serially. This also unwraps the rendering into a single function so that it's
much clearer.
This commit is contained in:
Jack Moffitt 2013-06-17 14:17:06 -06:00
parent ece8791c26
commit a9e1354118
5 changed files with 75 additions and 189 deletions

View file

@ -42,7 +42,6 @@ pub mod color;
pub mod compositor;
pub mod display_list;
pub mod geometry;
pub mod render_layers;
pub mod render_task;
pub mod surface;

View file

@ -1,113 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 compositor::{LayerBuffer, LayerBufferSet};
use display_list::DisplayList;
use opts::Opts;
use servo_util::time;
use servo_util::time::ProfilerChan;
use azure::azure_hl::{B8G8R8A8, DrawTarget};
use azure::azure::{AzGLContext};
use core::comm::Chan;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
/// The type representing the lack of extra display list data. This is used when sending display
/// list data off to be rendered.
pub type Nothing = ();
pub struct RenderLayer {
display_list: DisplayList<Nothing>,
size: Size2D<uint>
}
type RenderFn<'self> = &'self fn(layer: *RenderLayer,
buffer: LayerBuffer,
return_buffer: Chan<LayerBuffer>);
/// Given a layer and a buffer, either reuses the buffer (if it's of the right size and format)
/// or creates a new buffer (if it's not of the appropriate size and format) and invokes the
/// given callback with the render layer and the buffer. Returns the resulting layer buffer (which
/// might be the old layer buffer if it had the appropriate size and format).
pub fn render_layers(layer_ref: *RenderLayer,
opts: &Opts,
prof_chan: ProfilerChan,
share_gl_context: AzGLContext,
f: RenderFn)
-> LayerBufferSet {
let tile_size = opts.tile_size;
let scale = opts.zoom;
// FIXME: Try not to create a new array here.
let mut new_buffer_ports = ~[];
// Divide up the layer into tiles.
do time::profile(time::RenderingPrepBuffCategory, prof_chan.clone()) {
let layer: &RenderLayer = unsafe { cast::transmute(layer_ref) };
let mut y = 0;
while y < layer.size.height * scale {
let mut x = 0;
while x < layer.size.width * scale {
// Figure out the dimension of this tile.
let right = uint::min(x + tile_size, layer.size.width * scale);
let bottom = uint::min(y + tile_size, layer.size.height * scale);
let width = right - x;
let height = bottom - y;
let tile_rect = Rect(Point2D(x / scale, y / scale), Size2D(width, height)); //change this
let screen_rect = Rect(Point2D(x, y), Size2D(width, height)); //change this
let buffer;
// FIXME: Try harder to search for a matching tile.
// FIXME: Don't use shift; it's bad for perf. Maybe reverse and pop.
/*if buffers.len() != 0 && buffers[0].rect == tile_rect {
debug!("reusing tile, (%u, %u)", x, y);
buffer = buffers.shift();
} else {*/
// Create a new buffer.
debug!("creating tile, (%u, %u)", x, y);
// FIXME: This may not be always true.
let stride = width * 4;
buffer = LayerBuffer {
draw_target: DrawTarget::new_with_fbo(opts.render_backend,
share_gl_context,
Size2D(width as i32, height as i32),
B8G8R8A8),
rect: tile_rect,
screen_pos: screen_rect,
stride: stride as uint
};
//}
// Create a port and channel pair to receive the new buffer.
let (new_buffer_port, new_buffer_chan) = comm::stream();
// Send the buffer to the child.
f(layer_ref, buffer, new_buffer_chan);
// Enqueue the port.
new_buffer_ports.push(new_buffer_port);
x += tile_size;
}
y += tile_size;
}
}
let mut new_buffers = ~[];
do time::profile(time::RenderingWaitSubtasksCategory, prof_chan.clone()) {
for new_buffer_ports.each |new_buffer_port| {
new_buffers.push(new_buffer_port.recv());
}
}
LayerBufferSet {
buffers: new_buffers,
}
}

View file

@ -5,23 +5,28 @@
// The task that handles all rendering/painting.
use azure::{AzFloat, AzGLContext};
use compositor::{RenderListener, IdleRenderState, RenderingRenderState};
use azure::azure_hl::{B8G8R8A8, DrawTarget};
use compositor::{RenderListener, IdleRenderState, RenderingRenderState, LayerBuffer, LayerBufferSet};
use display_list::DisplayList;
use font_context::FontContext;
use geom::matrix2d::Matrix2D;
use geom::point::Point2D;
use geom::size::Size2D;
use geom::rect::Rect;
use opts::Opts;
use render_context::RenderContext;
use render_layers::{RenderLayer, render_layers};
use core::cell::Cell;
use core::comm::{Chan, Port, SharedChan};
use core::task::SingleThreaded;
use std::task_pool::TaskPool;
use servo_net::util::spawn_listener;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
pub struct RenderLayer {
display_list: DisplayList<()>,
size: Size2D<uint>
}
pub enum Msg<C> {
AttachCompositorMsg(C),
RenderMsg(RenderLayer),
@ -62,38 +67,17 @@ pub fn create_render_task<C: RenderListener + Owned>(port: Port<Msg<C>>,
do spawn {
let compositor = compositor_cell.take();
let share_gl_context = compositor.get_gl_context();
// FIXME: Annoying three-cell dance here. We need one-shot closures.
let opts = opts_cell.with_ref(|o| copy *o);
let n_threads = opts.n_render_threads;
let new_opts_cell = Cell(opts);
let profiler_chan = profiler_chan.clone();
let profiler_chan_copy = profiler_chan.clone();
let thread_pool = do TaskPool::new(n_threads, Some(SingleThreaded)) {
let opts_cell = Cell(new_opts_cell.with_ref(|o| copy *o));
let profiler_chan = Cell(profiler_chan.clone());
let f: ~fn(uint) -> ThreadRenderContext = |thread_index| {
let opts = opts_cell.with_ref(|opts| copy *opts);
ThreadRenderContext {
thread_index: thread_index,
font_ctx: @mut FontContext::new(opts.render_backend,
false,
profiler_chan.take()),
opts: opts,
}
};
f
};
// FIXME: rust/#5967
let mut renderer = Renderer {
port: port.take(),
compositor: compositor,
thread_pool: thread_pool,
font_ctx: @mut FontContext::new(opts.render_backend,
false,
profiler_chan),
opts: opts_cell.take(),
profiler_chan: profiler_chan_copy,
share_gl_context: share_gl_context,
@ -103,17 +87,10 @@ pub fn create_render_task<C: RenderListener + Owned>(port: Port<Msg<C>>,
}
}
/// Data that needs to be kept around for each render thread.
priv struct ThreadRenderContext {
thread_index: uint,
font_ctx: @mut FontContext,
opts: Opts,
}
priv struct Renderer<C> {
port: Port<Msg<C>>,
compositor: C,
thread_pool: TaskPool<ThreadRenderContext>,
font_ctx: @mut FontContext,
opts: Opts,
/// A channel to the profiler.
@ -142,48 +119,74 @@ impl<C: RenderListener + Owned> Renderer<C> {
debug!("renderer: rendering");
self.compositor.set_render_state(RenderingRenderState);
do profile(time::RenderingCategory, self.profiler_chan.clone()) {
let layer_buffer_set = do render_layers(&render_layer,
&self.opts,
self.profiler_chan.clone(),
self.share_gl_context) |render_layer_ref,
layer_buffer,
buffer_chan| {
let layer_buffer_cell = Cell(layer_buffer);
do self.thread_pool.execute |thread_render_context| {
do layer_buffer_cell.with_ref |layer_buffer| {
let tile_size = self.opts.tile_size;
let scale = self.opts.zoom;
// FIXME: Try not to create a new array here.
let mut new_buffers = ~[];
// Divide up the layer into tiles.
do time::profile(time::RenderingPrepBuffCategory, self.profiler_chan.clone()) {
let mut y = 0;
while y < render_layer.size.height * scale {
let mut x = 0;
while x < render_layer.size.width * scale {
// Figure out the dimension of this tile.
let right = uint::min(x + tile_size, render_layer.size.width * scale);
let bottom = uint::min(y + tile_size, render_layer.size.height * scale);
let width = right - x;
let height = bottom - y;
let tile_rect = Rect(Point2D(x / scale, y / scale), Size2D(width, height)); //change this
let screen_rect = Rect(Point2D(x, y), Size2D(width, height)); //change this
let buffer = 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_rect,
screen_pos: screen_rect,
stride: (width * 4) as uint
};
{
// Build the render context.
let ctx = RenderContext {
canvas: layer_buffer,
font_ctx: thread_render_context.font_ctx,
opts: &thread_render_context.opts
canvas: &buffer,
font_ctx: self.font_ctx,
opts: &self.opts
};
// Apply the translation to render the tile we want.
let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
let scale = thread_render_context.opts.zoom as f32;
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
let matrix = matrix.translate(-(layer_buffer.rect.origin.x as f32) as AzFloat,
-(layer_buffer.rect.origin.y as f32) as AzFloat);
let matrix = matrix.translate(-(buffer.rect.origin.x as f32) as AzFloat,
-(buffer.rect.origin.y as f32) as AzFloat);
layer_buffer.draw_target.set_transform(&matrix);
ctx.canvas.draw_target.set_transform(&matrix);
// Clear the buffer.
ctx.clear();
// Draw the display list.
let render_layer: &RenderLayer = unsafe {
cast::transmute(render_layer_ref)
};
do profile(time::RenderingDrawingCategory, self.profiler_chan.clone()) {
render_layer.display_list.draw_into_context(&ctx);
ctx.canvas.draw_target.flush();
}
// Send back the buffer.
buffer_chan.send(layer_buffer_cell.take());
}
new_buffers.push(buffer);
x += tile_size;
}
y += tile_size;
}
}
let layer_buffer_set = LayerBufferSet {
buffers: new_buffers,
};
debug!("renderer: returning surface");

View file

@ -25,8 +25,7 @@ use gfx::display_list::DisplayList;
use gfx::font_context::FontContext;
use gfx::geometry::Au;
use gfx::opts::Opts;
use gfx::render_layers::RenderLayer;
use gfx::render_task::{RenderMsg, RenderChan};
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
use newcss::select::SelectCtx;
use newcss::stylesheet::Stylesheet;
use newcss::types::OriginAuthor;

View file

@ -39,7 +39,6 @@ pub enum ProfilerCategory {
GfxRegenAvailableFontsCategory,
RenderingDrawingCategory,
RenderingPrepBuffCategory,
RenderingWaitSubtasksCategory,
RenderingCategory,
// hackish but helps prevent errors when adding new categories
NUM_BUCKETS,
@ -84,7 +83,6 @@ impl ProfilerCategory {
vec.push((GfxRegenAvailableFontsCategory, ~[]));
vec.push((RenderingDrawingCategory, ~[]));
vec.push((RenderingPrepBuffCategory, ~[]));
vec.push((RenderingWaitSubtasksCategory, ~[]));
vec.push((RenderingCategory, ~[]));
ProfilerCategory::check_order(vec);