diff --git a/src/frame.rs b/src/frame.rs index 18e6e23cbb..197875a236 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -5,11 +5,11 @@ use euclid::{Rect, Point2D, Point3D, Point4D, Size2D, Matrix4}; use fnv::FnvHasher; use geometry::ray_intersects_rect; use internal_types::{AxisDirection, LowLevelFilterOp, CompositionOp, DrawListItemIndex}; -use internal_types::{BatchUpdateList, RenderTargetIndex, DrawListId}; +use internal_types::{BatchUpdateList, ChildLayerIndex, DrawListId}; use internal_types::{CompositeBatchInfo, CompositeBatchJob}; use internal_types::{RendererFrame, StackingContextInfo, BatchInfo, DrawCall, StackingContextIndex}; use internal_types::{ANGLE_FLOAT_TO_FIXED, MAX_RECT, BatchUpdate, BatchUpdateOp, DrawLayer}; -use internal_types::{DrawCommand, ClearInfo, DrawTargetInfo, RenderTargetId, DrawListGroupId}; +use internal_types::{DrawCommand, ClearInfo, RenderTargetId, DrawListGroupId}; use layer::Layer; use node_compiler::NodeCompiler; use renderer::CompositionOpHelpers; @@ -75,6 +75,7 @@ struct FlattenContext<'a> { scene: &'a Scene, pipeline_sizes: &'a mut HashMap>, current_draw_list_group: Option, + device_pixel_ratio: f32, } struct FlattenInfo { @@ -97,29 +98,79 @@ pub enum FrameRenderItem { pub struct RenderTarget { id: RenderTargetId, - - // Child render targets - children: Vec, - - // Outputs + size: Size2D, + origin: Point2D, items: Vec, + texture_id: Option, + children: Vec, - // Texture id for any child render targets to use - child_texture_id: Option, - - size: Size2D, + page_allocator: Option, + texture_id_list: Vec, } impl RenderTarget { fn new(id: RenderTargetId, - size: Size2D) -> RenderTarget { + origin: Point2D, + size: Size2D, + texture_id: Option) -> RenderTarget { RenderTarget { id: id, - children: Vec::new(), - items: Vec::new(), - child_texture_id: None, size: size, + origin: origin, + items: Vec::new(), + texture_id: texture_id, + children: Vec::new(), + texture_id_list: Vec::new(), + page_allocator: None, + } + } + + fn allocate_target_rect(&mut self, + width: f32, + height: f32, + device_pixel_ratio: f32, + resource_cache: &mut ResourceCache) -> (Point2D, TextureId) { + // If the target is more than 512x512 (an arbitrary choice), assign it + // to an exact sized render target - assuming that there probably aren't + // many of them. This minimises GPU memory wastage if there are just a small + // number of large targets. Otherwise, attempt to allocate inside a shared render + // target texture - this allows composite batching to take place when + // there are a lot of small targets (which is more efficient). + if width < 512.0 && height < 512.0 { + if self.page_allocator.is_none() { + let texture_size = 2048; + let device_pixel_size = texture_size * device_pixel_ratio as u32; + + let texture_id = resource_cache.allocate_render_target(device_pixel_size, + device_pixel_size, + ImageFormat::RGBA8); + self.texture_id_list.push(texture_id); + self.page_allocator = Some(TexturePage::new(texture_id, texture_size)); + } + + // TODO(gw): This has accuracy issues if the size of a rendertarget is + // not scene pixel aligned! + let size = Size2D::new(width as u32, height as u32); + let allocated_origin = self.page_allocator + .as_mut() + .unwrap() + .allocate(&size, TextureFilter::Linear); + if let Some(allocated_origin) = allocated_origin { + let origin = Point2D::new(allocated_origin.x as f32, + allocated_origin.y as f32); + return (origin, self.page_allocator.as_ref().unwrap().texture_id()) + } } + + let device_pixel_width = width as u32 * device_pixel_ratio as u32; + let device_pixel_height = height as u32 * device_pixel_ratio as u32; + + let texture_id = resource_cache.allocate_render_target(device_pixel_width, + device_pixel_height, + ImageFormat::RGBA8); + self.texture_id_list.push(texture_id); + + (Point2D::zero(), texture_id) } fn collect_and_sort_visible_batches(&mut self, @@ -213,63 +264,28 @@ impl RenderTarget { let mut child_layers = Vec::new(); - let draw_target_info = if self.children.is_empty() { - None - } else { - let texture_size = 2048; - let device_pixel_size = texture_size * device_pixel_ratio as u32; - - // TODO(gw): This doesn't handle not having enough space to store - // draw all child render targets. However, this will soon - // be changing to do the RT allocation in a smarter way - // that greatly reduces the # of active RT allocations. - // When that happens, ensure it handles this case! - if let Some(child_texture_id) = self.child_texture_id.take() { - resource_cache.free_render_target(child_texture_id); - } - - self.child_texture_id = Some(resource_cache.allocate_render_target(device_pixel_size, - device_pixel_size, - ImageFormat::RGBA8)); - - // TODO(gw): Move this texture page allocator based on the suggested changes above. - let mut page = TexturePage::new(self.child_texture_id.unwrap(), texture_size); - - for child in &mut self.children { - let mut child_layer = child.collect_and_sort_visible_batches(resource_cache, - draw_list_groups, - layers, - stacking_context_info, - device_pixel_ratio); - - // TODO(gw): This has accuracy issues if the size of a rendertarget is - // not scene pixel aligned! - let layer_size = Size2D::new(child_layer.layer_size.width as u32, - child_layer.layer_size.height as u32); - let allocated_origin = page.allocate(&layer_size, TextureFilter::Linear).unwrap(); - child_layer.layer_origin = Point2D::new(allocated_origin.x as f32, - allocated_origin.y as f32); - - child_layers.push(child_layer); - } + for child in &mut self.children { + let child_layer = child.collect_and_sort_visible_batches(resource_cache, + draw_list_groups, + layers, + stacking_context_info, + device_pixel_ratio); - Some(DrawTargetInfo { - size: Size2D::new(texture_size, texture_size), - texture_id: self.child_texture_id.unwrap(), - }) - }; + child_layers.push(child_layer); + } - DrawLayer::new(draw_target_info, - child_layers, + DrawLayer::new(self.origin, + self.size, + self.texture_id, commands, - self.size) + child_layers) } fn reset(&mut self, pending_updates: &mut BatchUpdateList, resource_cache: &mut ResourceCache) { - if let Some(child_texture_id) = self.child_texture_id.take() { - resource_cache.free_render_target(child_texture_id); + for texture_id in self.texture_id_list.drain(..) { + resource_cache.free_render_target(texture_id); } for mut child in &mut self.children.drain(..) { @@ -286,14 +302,15 @@ impl RenderTarget { fn push_composite(&mut self, op: CompositionOp, + texture_id: TextureId, target: Rect, transform: &Matrix4, - render_target_index: RenderTargetIndex) { + child_layer_index: ChildLayerIndex) { // TODO(gw): Relax the restriction on batch breaks for FB reads // once the proper render target allocation code is done! let need_new_batch = op.needs_framebuffer() || match self.items.last() { Some(&FrameRenderItem::CompositeBatch(ref info)) => { - info.operation != op + info.operation != op || info.texture_id != texture_id } Some(&FrameRenderItem::Clear(..)) | Some(&FrameRenderItem::DrawListBatch(..)) | @@ -305,6 +322,7 @@ impl RenderTarget { if need_new_batch { self.items.push(FrameRenderItem::CompositeBatch(CompositeBatchInfo { operation: op, + texture_id: texture_id, jobs: Vec::new(), })); } @@ -315,7 +333,7 @@ impl RenderTarget { let job = CompositeBatchJob { rect: target, transform: *transform, - render_target_index: render_target_index + child_layer_index: child_layer_index, }; batch.jobs.push(job); } @@ -669,7 +687,8 @@ impl Frame { pub fn create(&mut self, scene: &Scene, resource_cache: &mut ResourceCache, - pipeline_sizes: &mut HashMap>) { + pipeline_sizes: &mut HashMap>, + device_pixel_ratio: f32) { if let Some(root_pipeline_id) = scene.root_pipeline_id { if let Some(root_pipeline) = scene.pipeline_map.get(&root_pipeline_id) { let old_layer_offsets = self.reset(resource_cache); @@ -686,7 +705,9 @@ impl Frame { let root_target_id = self.next_render_target_id(); let mut root_target = RenderTarget::new(root_target_id, - root_pipeline.viewport_size); + Point2D::zero(), + root_pipeline.viewport_size, + None); // Insert global position: fixed elements layer debug_assert!(self.layers.is_empty()); @@ -703,6 +724,7 @@ impl Frame { scene: scene, pipeline_sizes: pipeline_sizes, current_draw_list_group: None, + device_pixel_ratio: device_pixel_ratio, }; let parent_info = FlattenInfo { @@ -967,13 +989,21 @@ impl Frame { target_rect = composition_operation.target_rect(&target_rect); } - let render_target_index = RenderTargetIndex(target.children.len() as u32); + let child_layer_index = ChildLayerIndex(target.children.len() as u32); let render_target_size = Size2D::new(target_rect.size.width as f32, target_rect.size.height as f32); let render_target_id = self.next_render_target_id(); + + let (origin, texture_id) = target.allocate_target_rect(render_target_size.width, + render_target_size.height, + context.device_pixel_ratio, + context.resource_cache); + let mut new_target = RenderTarget::new(render_target_id, - render_target_size); + origin, + render_target_size, + Some(texture_id)); let local_transform = Matrix4::identity().translate(origin.x, origin.y, 0.0) @@ -981,9 +1011,10 @@ impl Frame { .translate(-origin.x, -origin.y, 0.0); for composition_operation in composition_operations { target.push_composite(composition_operation, + texture_id, target_rect, &local_transform, - render_target_index); + child_layer_index); } info.offset_from_current_layer = Point2D::zero(); @@ -1173,10 +1204,11 @@ impl Frame { device_pixel_ratio) } None => { - DrawLayer::new(None, - Vec::new(), + DrawLayer::new(Point2D::zero(), + Size2D::zero(), + None, Vec::new(), - Size2D::zero()) + Vec::new()) } }; diff --git a/src/internal_types.rs b/src/internal_types.rs index 59009142a1..0e4991587e 100644 --- a/src/internal_types.rs +++ b/src/internal_types.rs @@ -488,12 +488,13 @@ impl BatchInfo { pub struct CompositeBatchJob { pub rect: Rect, pub transform: Matrix4, - pub render_target_index: RenderTargetIndex, + pub child_layer_index: ChildLayerIndex, } #[derive(Debug, Clone)] pub struct CompositeBatchInfo { pub operation: CompositionOp, + pub texture_id: TextureId, pub jobs: Vec, } @@ -505,38 +506,33 @@ pub enum DrawCommand { } #[derive(Clone, Copy, Debug, Ord, PartialOrd, PartialEq, Eq, Hash)] -pub struct RenderTargetIndex(pub u32); - -#[derive(Debug)] -pub struct DrawTargetInfo { - pub texture_id: TextureId, - pub size: Size2D, -} +pub struct ChildLayerIndex(pub u32); #[derive(Debug)] pub struct DrawLayer { // This layer pub commands: Vec, - pub layer_origin: Point2D, - pub layer_size: Size2D, + pub texture_id: Option, + pub origin: Point2D, + pub size: Size2D, // Children - pub child_target: Option, pub child_layers: Vec, } impl DrawLayer { - pub fn new(child_target: Option, - child_layers: Vec, + pub fn new(origin: Point2D, + size: Size2D, + texture_id: Option, commands: Vec, - size: Size2D) + child_layers: Vec) -> DrawLayer { DrawLayer { - child_target: child_target, + origin: origin, + size: size, + texture_id: texture_id, commands: commands, child_layers: child_layers, - layer_origin: Point2D::zero(), - layer_size: size, } } } diff --git a/src/render_backend.rs b/src/render_backend.rs index df5448975b..9a9136396d 100644 --- a/src/render_backend.rs +++ b/src/render_backend.rs @@ -236,7 +236,8 @@ impl RenderBackend { self.frame.create(&self.scene, &mut self.resource_cache, - &mut new_pipeline_sizes); + &mut new_pipeline_sizes, + self.device_pixel_ratio); let mut updated_pipeline_sizes = HashMap::new(); diff --git a/src/renderer.rs b/src/renderer.rs index c26854bf06..4fa7c9d511 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -8,7 +8,7 @@ use gleam::gl; use internal_types::{RendererFrame, ResultMsg, TextureUpdateOp, BatchUpdateOp, BatchUpdateList}; use internal_types::{TextureUpdateDetails, TextureUpdateList, PackedVertex, RenderTargetMode}; use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE}; -use internal_types::{PackedVertexForTextureCacheUpdate, CompositionOp, RenderTargetIndex}; +use internal_types::{PackedVertexForTextureCacheUpdate, CompositionOp, ChildLayerIndex}; use internal_types::{AxisDirection, LowLevelFilterOp, DrawCommand, DrawLayer, ANGLE_FLOAT_TO_FIXED}; use ipc_channel::ipc; use profiler::{Profiler, BackendProfileCounters}; @@ -946,27 +946,25 @@ impl Renderer { } fn draw_layer(&mut self, - layer_target: Option, layer: &DrawLayer, render_context: &RenderContext) { // Draw child layers first, to ensure that dependent render targets // have been built before they are read as a texture. for child in &layer.child_layers { - self.draw_layer(Some(layer.child_target.as_ref().unwrap().texture_id), - child, + self.draw_layer(child, render_context); } - self.device.bind_render_target(layer_target); + self.device.bind_render_target(layer.texture_id); // TODO(gw): This may not be needed in all cases... - let layer_origin = Point2D::new((layer.layer_origin.x * self.device_pixel_ratio).round() as u32, - (layer.layer_origin.y * self.device_pixel_ratio).round() as u32); + let layer_origin = Point2D::new((layer.origin.x * self.device_pixel_ratio).round() as u32, + (layer.origin.y * self.device_pixel_ratio).round() as u32); - let layer_size = Size2D::new((layer.layer_size.width * self.device_pixel_ratio).round() as u32, - (layer.layer_size.height * self.device_pixel_ratio).round() as u32); + let layer_size = Size2D::new((layer.size.width * self.device_pixel_ratio).round() as u32, + (layer.size.height * self.device_pixel_ratio).round() as u32); - let layer_origin = match layer_target { + let layer_origin = match layer.texture_id { Some(..) => { layer_origin } @@ -988,7 +986,7 @@ impl Renderer { layer_origin.y as gl::GLint, layer_size.width as gl::GLint, layer_size.height as gl::GLint); - let clear_color = if layer_target.is_some() { + let clear_color = if layer.texture_id.is_some() { ColorF::new(0.0, 0.0, 0.0, 0.0) } else { ColorF::new(1.0, 1.0, 1.0, 1.0) @@ -997,8 +995,8 @@ impl Renderer { gl::clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT); let projection = Matrix4::ortho(0.0, - layer.layer_size.width, - layer.layer_size.height, + layer.size.width, + layer.size.height, 0.0, ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE); @@ -1027,7 +1025,7 @@ impl Renderer { gl::enable(gl::MULTISAMPLE); } - if layer_target.is_some() { + if layer.texture_id.is_some() { gl::blend_func_separate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA, gl::ONE, gl::ONE); } else { @@ -1258,7 +1256,7 @@ impl Renderer { let fb_rect_size = Size2D::new(job.rect.size.width as f32 * render_context.device_pixel_ratio, job.rect.size.height as f32 * render_context.device_pixel_ratio); - let inverted_y0 = layer.layer_size.height - + let inverted_y0 = layer.size.height - job.rect.size.height as f32 - p0.y; let fb_rect_origin = Point2D::new( @@ -1290,18 +1288,19 @@ impl Renderer { let color = ColorF::new(1.0, 1.0, 1.0, alpha); - let RenderTargetIndex(render_target_index) = job.render_target_index; - let src_target = &layer.child_layers[render_target_index as usize]; + let ChildLayerIndex(child_layer_index) = job.child_layer_index; + let src_target = &layer.child_layers[child_layer_index as usize]; + debug_assert!(src_target.texture_id.unwrap() == info.texture_id); let pixel_uv = Rect::new( - Point2D::new(src_target.layer_origin.x as u32, - src_target.layer_origin.y as u32), - Size2D::new(src_target.layer_size.width as u32, - src_target.layer_size.height as u32)); - let texture_width = - layer.child_target.as_ref().unwrap().size.width as f32; - let texture_height = - layer.child_target.as_ref().unwrap().size.height as f32; + Point2D::new(src_target.origin.x as u32, + src_target.origin.y as u32), + Size2D::new(src_target.size.width as u32, + src_target.size.height as u32)); + + let (texture_width, texture_height) = self.device.get_texture_dimensions(info.texture_id); + let texture_width = texture_width as f32; + let texture_height = texture_height as f32; let texture_uv = Rect::new( Point2D::new( pixel_uv.origin.x as f32 / texture_width, @@ -1367,7 +1366,7 @@ impl Renderer { &mut self.profile_counters, &indices[..], &vertices[..], - layer.child_target.as_ref().unwrap().texture_id); + info.texture_id); } } } @@ -1395,8 +1394,7 @@ impl Renderer { gl::depth_func(gl::LEQUAL); gl::enable(gl::SCISSOR_TEST); - self.draw_layer(None, - &frame.root_layer, + self.draw_layer(&frame.root_layer, &render_context); // Restore frame - avoid borrow checker! diff --git a/src/texture_cache.rs b/src/texture_cache.rs index bd693b9352..4ad349af3b 100644 --- a/src/texture_cache.rs +++ b/src/texture_cache.rs @@ -85,6 +85,10 @@ impl TexturePage { page } + pub fn texture_id(&self) -> TextureId { + self.texture_id + } + fn find_index_of_best_rect_in_bin(&self, bin: FreeListBin, requested_dimensions: &Size2D) -> Option { let mut smallest_index_and_area = None;