aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/gfx/render_layers.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/gfx/render_layers.rs')
-rw-r--r--src/components/gfx/render_layers.rs141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/components/gfx/render_layers.rs b/src/components/gfx/render_layers.rs
new file mode 100644
index 00000000000..49ee76130a5
--- /dev/null
+++ b/src/components/gfx/render_layers.rs
@@ -0,0 +1,141 @@
+/* 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 core::comm::Chan;
+use geom::point::Point2D;
+use geom::rect::Rect;
+use geom::size::Size2D;
+
+pub struct RenderLayer {
+ display_list: DisplayList,
+ 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,
+ f: RenderFn)
+ -> LayerBufferSet {
+ let tile_size = opts.tile_size;
+
+ // 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 {
+ let mut x = 0;
+ while x < layer.size.width {
+ // Figure out the dimension of this tile.
+ let right = uint::min(x + tile_size, layer.size.width);
+ let bottom = uint::min(y + tile_size, layer.size.height);
+ let width = right - x;
+ let height = bottom - y;
+
+ let tile_rect = Rect(Point2D(x, y), Size2D(width, height));
+
+ // Round the width up the nearest 32 pixels for DMA on the Mac.
+ let aligned_width = if width % 32 == 0 {
+ width
+ } else {
+ (width & !(32 - 1)) + 32
+ };
+ assert!(aligned_width % 32 == 0);
+ assert!(aligned_width >= width);
+
+ debug!("tile aligned_width %u", aligned_width);
+
+ 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);
+
+ let size = Size2D(aligned_width as i32, height as i32);
+ // FIXME: This may not be always true.
+ let stride = size.width * 4;
+
+ let mut data: ~[u8] = ~[0];
+ let offset;
+ unsafe {
+ // FIXME: Evil black magic to ensure that we don't perform a slow memzero
+ // of this buffer. This should be made safe.
+
+ let align = 256;
+
+ let len = ((stride * size.height) as uint) + align;
+ vec::reserve(&mut data, len);
+ vec::raw::set_len(&mut data, len);
+
+ // Round up to the nearest 32-byte-aligned address for DMA on the Mac.
+ let addr: uint = cast::transmute(ptr::to_unsafe_ptr(&data[0]));
+ if addr % align == 0 {
+ offset = 0;
+ } else {
+ offset = align - addr % align;
+ }
+
+ debug!("tile offset is %u, expected addr is %x", offset, addr + offset);
+ }
+
+ buffer = LayerBuffer {
+ draw_target: DrawTarget::new_with_data(opts.render_backend,
+ data,
+ offset,
+ size,
+ stride,
+ B8G8R8A8),
+ rect: tile_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,
+ }
+}
+