mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
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:
parent
ece8791c26
commit
a9e1354118
5 changed files with 75 additions and 189 deletions
|
@ -42,7 +42,6 @@ pub mod color;
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
pub mod display_list;
|
pub mod display_list;
|
||||||
pub mod geometry;
|
pub mod geometry;
|
||||||
pub mod render_layers;
|
|
||||||
pub mod render_task;
|
pub mod render_task;
|
||||||
pub mod surface;
|
pub mod surface;
|
||||||
|
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,23 +5,28 @@
|
||||||
// The task that handles all rendering/painting.
|
// The task that handles all rendering/painting.
|
||||||
|
|
||||||
use azure::{AzFloat, AzGLContext};
|
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 font_context::FontContext;
|
||||||
use geom::matrix2d::Matrix2D;
|
use geom::matrix2d::Matrix2D;
|
||||||
|
use geom::point::Point2D;
|
||||||
|
use geom::size::Size2D;
|
||||||
|
use geom::rect::Rect;
|
||||||
use opts::Opts;
|
use opts::Opts;
|
||||||
use render_context::RenderContext;
|
use render_context::RenderContext;
|
||||||
use render_layers::{RenderLayer, render_layers};
|
|
||||||
|
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use core::comm::{Chan, Port, SharedChan};
|
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::{ProfilerChan, profile};
|
||||||
use servo_util::time;
|
use servo_util::time;
|
||||||
|
|
||||||
|
pub struct RenderLayer {
|
||||||
|
display_list: DisplayList<()>,
|
||||||
|
size: Size2D<uint>
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Msg<C> {
|
pub enum Msg<C> {
|
||||||
AttachCompositorMsg(C),
|
AttachCompositorMsg(C),
|
||||||
RenderMsg(RenderLayer),
|
RenderMsg(RenderLayer),
|
||||||
|
@ -62,38 +67,17 @@ pub fn create_render_task<C: RenderListener + Owned>(port: Port<Msg<C>>,
|
||||||
do spawn {
|
do spawn {
|
||||||
let compositor = compositor_cell.take();
|
let compositor = compositor_cell.take();
|
||||||
let share_gl_context = compositor.get_gl_context();
|
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 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 = profiler_chan.clone();
|
||||||
let profiler_chan_copy = 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
|
// FIXME: rust/#5967
|
||||||
let mut renderer = Renderer {
|
let mut renderer = Renderer {
|
||||||
port: port.take(),
|
port: port.take(),
|
||||||
compositor: compositor,
|
compositor: compositor,
|
||||||
thread_pool: thread_pool,
|
font_ctx: @mut FontContext::new(opts.render_backend,
|
||||||
|
false,
|
||||||
|
profiler_chan),
|
||||||
opts: opts_cell.take(),
|
opts: opts_cell.take(),
|
||||||
profiler_chan: profiler_chan_copy,
|
profiler_chan: profiler_chan_copy,
|
||||||
share_gl_context: share_gl_context,
|
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> {
|
priv struct Renderer<C> {
|
||||||
port: Port<Msg<C>>,
|
port: Port<Msg<C>>,
|
||||||
compositor: C,
|
compositor: C,
|
||||||
thread_pool: TaskPool<ThreadRenderContext>,
|
font_ctx: @mut FontContext,
|
||||||
opts: Opts,
|
opts: Opts,
|
||||||
|
|
||||||
/// A channel to the profiler.
|
/// A channel to the profiler.
|
||||||
|
@ -142,48 +119,74 @@ impl<C: RenderListener + Owned> Renderer<C> {
|
||||||
debug!("renderer: rendering");
|
debug!("renderer: rendering");
|
||||||
self.compositor.set_render_state(RenderingRenderState);
|
self.compositor.set_render_state(RenderingRenderState);
|
||||||
do profile(time::RenderingCategory, self.profiler_chan.clone()) {
|
do profile(time::RenderingCategory, self.profiler_chan.clone()) {
|
||||||
let layer_buffer_set = do render_layers(&render_layer,
|
let tile_size = self.opts.tile_size;
|
||||||
&self.opts,
|
let scale = self.opts.zoom;
|
||||||
self.profiler_chan.clone(),
|
|
||||||
self.share_gl_context) |render_layer_ref,
|
// FIXME: Try not to create a new array here.
|
||||||
layer_buffer,
|
let mut new_buffers = ~[];
|
||||||
buffer_chan| {
|
|
||||||
let layer_buffer_cell = Cell(layer_buffer);
|
// Divide up the layer into tiles.
|
||||||
do self.thread_pool.execute |thread_render_context| {
|
do time::profile(time::RenderingPrepBuffCategory, self.profiler_chan.clone()) {
|
||||||
do layer_buffer_cell.with_ref |layer_buffer| {
|
let mut y = 0;
|
||||||
// Build the render context.
|
while y < render_layer.size.height * scale {
|
||||||
let ctx = RenderContext {
|
let mut x = 0;
|
||||||
canvas: layer_buffer,
|
while x < render_layer.size.width * scale {
|
||||||
font_ctx: thread_render_context.font_ctx,
|
// Figure out the dimension of this tile.
|
||||||
opts: &thread_render_context.opts
|
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
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply the translation to render the tile we want.
|
{
|
||||||
let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
|
// Build the render context.
|
||||||
let scale = thread_render_context.opts.zoom as f32;
|
let ctx = RenderContext {
|
||||||
|
canvas: &buffer,
|
||||||
|
font_ctx: self.font_ctx,
|
||||||
|
opts: &self.opts
|
||||||
|
};
|
||||||
|
|
||||||
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
|
// Apply the translation to render the tile we want.
|
||||||
let matrix = matrix.translate(-(layer_buffer.rect.origin.x as f32) as AzFloat,
|
let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
|
||||||
-(layer_buffer.rect.origin.y as f32) as AzFloat);
|
let matrix = matrix.scale(scale as AzFloat, scale 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.
|
// Clear the buffer.
|
||||||
ctx.clear();
|
ctx.clear();
|
||||||
|
|
||||||
|
|
||||||
// Draw the display list.
|
// Draw the display list.
|
||||||
let render_layer: &RenderLayer = unsafe {
|
do profile(time::RenderingDrawingCategory, self.profiler_chan.clone()) {
|
||||||
cast::transmute(render_layer_ref)
|
render_layer.display_list.draw_into_context(&ctx);
|
||||||
};
|
ctx.canvas.draw_target.flush();
|
||||||
|
}
|
||||||
render_layer.display_list.draw_into_context(&ctx);
|
}
|
||||||
ctx.canvas.draw_target.flush();
|
|
||||||
|
new_buffers.push(buffer);
|
||||||
|
|
||||||
|
x += tile_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send back the buffer.
|
y += tile_size;
|
||||||
buffer_chan.send(layer_buffer_cell.take());
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let layer_buffer_set = LayerBufferSet {
|
||||||
|
buffers: new_buffers,
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("renderer: returning surface");
|
debug!("renderer: returning surface");
|
||||||
|
|
|
@ -25,8 +25,7 @@ use gfx::display_list::DisplayList;
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::FontContext;
|
||||||
use gfx::geometry::Au;
|
use gfx::geometry::Au;
|
||||||
use gfx::opts::Opts;
|
use gfx::opts::Opts;
|
||||||
use gfx::render_layers::RenderLayer;
|
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
|
||||||
use gfx::render_task::{RenderMsg, RenderChan};
|
|
||||||
use newcss::select::SelectCtx;
|
use newcss::select::SelectCtx;
|
||||||
use newcss::stylesheet::Stylesheet;
|
use newcss::stylesheet::Stylesheet;
|
||||||
use newcss::types::OriginAuthor;
|
use newcss::types::OriginAuthor;
|
||||||
|
|
|
@ -39,7 +39,6 @@ pub enum ProfilerCategory {
|
||||||
GfxRegenAvailableFontsCategory,
|
GfxRegenAvailableFontsCategory,
|
||||||
RenderingDrawingCategory,
|
RenderingDrawingCategory,
|
||||||
RenderingPrepBuffCategory,
|
RenderingPrepBuffCategory,
|
||||||
RenderingWaitSubtasksCategory,
|
|
||||||
RenderingCategory,
|
RenderingCategory,
|
||||||
// hackish but helps prevent errors when adding new categories
|
// hackish but helps prevent errors when adding new categories
|
||||||
NUM_BUCKETS,
|
NUM_BUCKETS,
|
||||||
|
@ -84,7 +83,6 @@ impl ProfilerCategory {
|
||||||
vec.push((GfxRegenAvailableFontsCategory, ~[]));
|
vec.push((GfxRegenAvailableFontsCategory, ~[]));
|
||||||
vec.push((RenderingDrawingCategory, ~[]));
|
vec.push((RenderingDrawingCategory, ~[]));
|
||||||
vec.push((RenderingPrepBuffCategory, ~[]));
|
vec.push((RenderingPrepBuffCategory, ~[]));
|
||||||
vec.push((RenderingWaitSubtasksCategory, ~[]));
|
|
||||||
vec.push((RenderingCategory, ~[]));
|
vec.push((RenderingCategory, ~[]));
|
||||||
|
|
||||||
ProfilerCategory::check_order(vec);
|
ProfilerCategory::check_order(vec);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue