From 89281818900caea5e81d2885d59e41f6372b307a Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Wed, 26 Oct 2016 06:38:13 +1000 Subject: [PATCH] Use texture layers for render target allocations, remove render phases. Instead of splitting the scene into multiple phases, use texture layers to allocate slices as needed for each pass of the render frame. These are pooled and only allocated when the frame render target configuration changes. Previously, the code asserted if it was unable to fit the render tasks into a single render target for a single tile. Now, this (pathological) case is handled correctly. This makes the upcoming dynamic primitive cache support a lot simpler to add. It also means that we don't use a ping-pong allocation style, which typically performs badly on mobile / tiled rendering architectures. --- webrender/res/prim_shared.glsl | 12 +- webrender/res/ps_blend.fs.glsl | 2 - webrender/res/ps_blend.glsl | 2 +- webrender/res/ps_blend.vs.glsl | 9 +- webrender/res/ps_composite.fs.glsl | 2 - webrender/res/ps_composite.glsl | 4 +- webrender/res/ps_composite.vs.glsl | 19 ++- webrender/src/debug_render.rs | 4 +- webrender/src/device.rs | 226 ++++++++++++++++-------- webrender/src/internal_types.rs | 3 +- webrender/src/prim_store.rs | 18 +- webrender/src/profiler.rs | 8 +- webrender/src/render_backend.rs | 2 +- webrender/src/renderer.rs | 102 +++++------ webrender/src/texture_cache.rs | 10 +- webrender/src/tiling.rs | 266 +++++++++++++---------------- 16 files changed, 371 insertions(+), 318 deletions(-) diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index beb9abca5c..d25b715254 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -34,6 +34,8 @@ #define MAX_STOPS_PER_ANGLE_GRADIENT 8 +uniform sampler2DArray sCache; + #ifdef WR_VERTEX_SHADER #define VECS_PER_LAYER 13 @@ -121,7 +123,7 @@ Layer fetch_layer(int index) { struct Tile { vec4 screen_origin_task_origin; - vec4 size; + vec4 size_target_index; }; Tile fetch_tile(int index) { @@ -130,7 +132,7 @@ Tile fetch_tile(int index) { ivec2 uv = get_fetch_uv(index, VECS_PER_TILE); tile.screen_origin_task_origin = texelFetchOffset(sRenderTasks, uv, 0, ivec2(0, 0)); - tile.size = texelFetchOffset(sRenderTasks, uv, 0, ivec2(1, 0)); + tile.size_target_index = texelFetchOffset(sRenderTasks, uv, 0, ivec2(1, 0)); return tile; } @@ -425,7 +427,7 @@ VertexInfo write_vertex(vec4 instance_rect, vec2 clamped_pos = clamp(device_pos, vec2(tile.screen_origin_task_origin.xy), - vec2(tile.screen_origin_task_origin.xy + tile.size.xy)); + vec2(tile.screen_origin_task_origin.xy + tile.size_target_index.xy)); vec4 local_clamped_pos = layer.inv_transform * vec4(clamped_pos / uDevicePixelRatio, world_pos.z, 1); local_clamped_pos.xyz /= local_clamped_pos.w; @@ -479,11 +481,11 @@ TransformVertexInfo write_transform_vertex(vec4 instance_rect, vec2 min_pos_clamped = clamp(min_pos * uDevicePixelRatio, vec2(tile.screen_origin_task_origin.xy), - vec2(tile.screen_origin_task_origin.xy + tile.size.xy)); + vec2(tile.screen_origin_task_origin.xy + tile.size_target_index.xy)); vec2 max_pos_clamped = clamp(max_pos * uDevicePixelRatio, vec2(tile.screen_origin_task_origin.xy), - vec2(tile.screen_origin_task_origin.xy + tile.size.xy)); + vec2(tile.screen_origin_task_origin.xy + tile.size_target_index.xy)); vec2 clamped_pos = mix(min_pos_clamped, max_pos_clamped, diff --git a/webrender/res/ps_blend.fs.glsl b/webrender/res/ps_blend.fs.glsl index 30a8712cc4..7ce203fbbf 100644 --- a/webrender/res/ps_blend.fs.glsl +++ b/webrender/res/ps_blend.fs.glsl @@ -2,8 +2,6 @@ * 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/. */ -uniform sampler2D sCache; - vec3 rgbToHsv(vec3 c) { float value = max(max(c.r, c.g), c.b); diff --git a/webrender/res/ps_blend.glsl b/webrender/res/ps_blend.glsl index 0531cf06f5..09e6d9262a 100644 --- a/webrender/res/ps_blend.glsl +++ b/webrender/res/ps_blend.glsl @@ -2,6 +2,6 @@ * 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/. */ -varying vec2 vUv; +varying vec3 vUv; flat varying float vAmount; flat varying int vOp; diff --git a/webrender/res/ps_blend.vs.glsl b/webrender/res/ps_blend.vs.glsl index 93f928aa1c..97a9c6838e 100644 --- a/webrender/res/ps_blend.vs.glsl +++ b/webrender/res/ps_blend.vs.glsl @@ -13,12 +13,13 @@ void main(void) { src.screen_origin_task_origin.xy; vec2 local_pos = mix(dest_origin, - dest_origin + src.size.xy, + dest_origin + src.size_target_index.xy, aPosition.xy); - vec2 st0 = vec2(src.screen_origin_task_origin.zw) / 2048.0; - vec2 st1 = vec2(src.screen_origin_task_origin.zw + src.size.xy) / 2048.0; - vUv = mix(st0, st1, aPosition.xy); + vec2 texture_size = vec2(textureSize(sCache, 0)); + vec2 st0 = src.screen_origin_task_origin.zw / texture_size; + vec2 st1 = (src.screen_origin_task_origin.zw + src.size_target_index.xy) / texture_size; + vUv = vec3(mix(st0, st1, aPosition.xy), src.size_target_index.z); vOp = blend.src_id_target_id_op_amount.z; vAmount = blend.src_id_target_id_op_amount.w / 65535.0; diff --git a/webrender/res/ps_composite.fs.glsl b/webrender/res/ps_composite.fs.glsl index 8d97b91ae4..3b6606c880 100644 --- a/webrender/res/ps_composite.fs.glsl +++ b/webrender/res/ps_composite.fs.glsl @@ -4,8 +4,6 @@ * 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/. */ -uniform sampler2D sCache; - float gauss(float x, float sigma) { if (sigma == 0.0) return 1.0; diff --git a/webrender/res/ps_composite.glsl b/webrender/res/ps_composite.glsl index cf9fecde52..d2a1310d59 100644 --- a/webrender/res/ps_composite.glsl +++ b/webrender/res/ps_composite.glsl @@ -2,7 +2,7 @@ * 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/. */ -varying vec2 vUv0; -varying vec2 vUv1; +varying vec3 vUv0; +varying vec3 vUv1; flat varying vec4 vUv1Rect; flat varying int vOp; diff --git a/webrender/res/ps_composite.vs.glsl b/webrender/res/ps_composite.vs.glsl index 1789d79e0f..2af03286cc 100644 --- a/webrender/res/ps_composite.vs.glsl +++ b/webrender/res/ps_composite.vs.glsl @@ -10,20 +10,21 @@ void main(void) { Tile dest = fetch_tile(composite.src0_src1_target_id_op.z); vec2 local_pos = mix(dest.screen_origin_task_origin.zw, - dest.screen_origin_task_origin.zw + dest.size.xy, + dest.screen_origin_task_origin.zw + dest.size_target_index.xy, aPosition.xy); - vec2 st0 = vec2(src0.screen_origin_task_origin.zw) / 2048.0; - vec2 st1 = vec2(src0.screen_origin_task_origin.zw + src0.size.xy) / 2048.0; - vUv0 = mix(st0, st1, aPosition.xy); + vec2 texture_size = vec2(textureSize(sCache, 0)); + vec2 st0 = src0.screen_origin_task_origin.zw / texture_size; + vec2 st1 = (src0.screen_origin_task_origin.zw + src0.size_target_index.xy) / texture_size; + vUv0 = vec3(mix(st0, st1, aPosition.xy), src0.size_target_index.z); - st0 = vec2(src1.screen_origin_task_origin.zw) / 2048.0; - st1 = vec2(src1.screen_origin_task_origin.zw + src1.size.xy) / 2048.0; + st0 = vec2(src1.screen_origin_task_origin.zw) / texture_size; + st1 = vec2(src1.screen_origin_task_origin.zw + src1.size_target_index.xy) / texture_size; vec2 local_virtual_pos = mix(dest.screen_origin_task_origin.xy, - dest.screen_origin_task_origin.xy + dest.size.xy, + dest.screen_origin_task_origin.xy + dest.size_target_index.xy, aPosition.xy); - vec2 f = (local_virtual_pos - src1.screen_origin_task_origin.xy) / src1.size.xy; - vUv1 = mix(st0, st1, f); + vec2 f = (local_virtual_pos - src1.screen_origin_task_origin.xy) / src1.size_target_index.xy; + vUv1 = vec3(mix(st0, st1, f), src1.size_target_index.z); vUv1Rect = vec4(st0, st1); vOp = composite.src0_src1_target_id_op.w; diff --git a/webrender/src/debug_render.rs b/webrender/src/debug_render.rs index 6d1d678e7b..1bcce86a17 100644 --- a/webrender/src/debug_render.rs +++ b/webrender/src/debug_render.rs @@ -4,7 +4,7 @@ use debug_font_data; use device::{Device, ProgramId, VAOId, TextureId, VertexFormat}; -use device::{TextureFilter, VertexUsageHint}; +use device::{TextureFilter, VertexUsageHint, TextureTarget}; use euclid::{Matrix4D, Point2D, Size2D, Rect}; use gleam::gl; use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, TextureSampler}; @@ -36,7 +36,7 @@ impl DebugRenderer { let line_vao = device.create_vao(VertexFormat::DebugColor, None); let tri_vao = device.create_vao(VertexFormat::DebugColor, None); - let font_texture_id = device.create_texture_ids(1)[0]; + let font_texture_id = device.create_texture_ids(1, TextureTarget::Default)[0]; device.init_texture(font_texture_id, debug_font_data::BMP_WIDTH, debug_font_data::BMP_HEIGHT, diff --git a/webrender/src/device.rs b/webrender/src/device.rs index c857e01e2c..e54cfc9453 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -45,6 +45,12 @@ lazy_static! { }; } +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum TextureTarget { + Default, + Array, +} + #[derive(Copy, Clone, Debug, PartialEq)] pub enum TextureFilter { Nearest, @@ -317,12 +323,21 @@ impl VertexFormat { impl TextureId { pub fn bind(&self) { - let TextureId(id) = *self; - gl::bind_texture(gl::TEXTURE_2D, id); + gl::bind_texture(self.target, self.name); + } + + pub fn new(name: gl::GLuint) -> TextureId { + TextureId { + name: name, + target: gl::TEXTURE_2D, + } } pub fn invalid() -> TextureId { - TextureId(0) + TextureId { + name: 0, + target: gl::TEXTURE_2D, + } } } @@ -490,7 +505,10 @@ impl Drop for VAO { } #[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Copy, Clone)] -pub struct TextureId(pub gl::GLuint); // TODO: HACK: Should not be public! +pub struct TextureId { + name: gl::GLuint, + target: gl::GLuint, +} #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] pub struct ProgramId(pub gl::GLuint); @@ -804,7 +822,7 @@ impl Device { device_pixel_ratio: device_pixel_ratio, inside_frame: false, - bound_textures: [ TextureId(0); 16 ], + bound_textures: [ TextureId::invalid(); 16 ], bound_program: ProgramId(0), bound_vao: VAOId(0), bound_fbo: FBOId(0), @@ -866,7 +884,7 @@ impl Device { // Texture state for i in 0..self.bound_textures.len() { - self.bound_textures[i] = TextureId(0); + self.bound_textures[i] = TextureId::invalid(); gl::active_texture(gl::TEXTURE0 + i as gl::GLuint); gl::bind_texture(gl::TEXTURE_2D, 0); } @@ -889,7 +907,9 @@ impl Device { gl::active_texture(gl::TEXTURE0); } - pub fn bind_texture(&mut self, sampler: TextureSampler, texture_id: TextureId) { + pub fn bind_texture(&mut self, + sampler: TextureSampler, + texture_id: TextureId) { debug_assert!(self.inside_frame); let sampler_index = sampler as usize; @@ -901,11 +921,11 @@ impl Device { } } - pub fn bind_render_target(&mut self, texture_id: Option) { + pub fn bind_render_target(&mut self, texture_id: Option<(TextureId, i32)>) { debug_assert!(self.inside_frame); let fbo_id = texture_id.map_or(FBOId(self.default_fbo), |texture_id| { - self.textures.get(&texture_id).unwrap().fbo_ids[0] + self.textures.get(&texture_id.0).unwrap().fbo_ids[texture_id.1 as usize] }); if self.bound_fbo != fbo_id { @@ -928,12 +948,22 @@ impl Device { self.set_uniforms(program, projection); } - pub fn create_texture_ids(&mut self, count: i32) -> Vec { + pub fn create_texture_ids(&mut self, + count: i32, + target: TextureTarget) -> Vec { let id_list = gl::gen_textures(count); let mut texture_ids = Vec::new(); + let target = match target { + TextureTarget::Default => gl::TEXTURE_2D, + TextureTarget::Array => gl::TEXTURE_2D_ARRAY, + }; + for id in id_list { - let texture_id = TextureId(id); + let texture_id = TextureId { + name: id, + target: target, + }; let texture = Texture { id: id, @@ -980,7 +1010,7 @@ impl Device { self.raw_textures.insert(texture_id, (x0, y0, width, height)); } - fn set_texture_parameters(&mut self, filter: TextureFilter) { + fn set_texture_parameters(&mut self, target: gl::GLuint, filter: TextureFilter) { let filter = match filter { TextureFilter::Nearest => { gl::NEAREST @@ -990,21 +1020,22 @@ impl Device { } }; - gl::tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, filter as gl::GLint); - gl::tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, filter as gl::GLint); + gl::tex_parameter_i(target, gl::TEXTURE_MAG_FILTER, filter as gl::GLint); + gl::tex_parameter_i(target, gl::TEXTURE_MIN_FILTER, filter as gl::GLint); - gl::tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as gl::GLint); - gl::tex_parameter_i(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint); + gl::tex_parameter_i(target, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as gl::GLint); + gl::tex_parameter_i(target, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint); } fn upload_texture_image(&mut self, - width: u32, - height: u32, - internal_format: u32, - format: u32, - type_: u32, - pixels: Option<&[u8]>) { - gl::tex_image_2d(gl::TEXTURE_2D, + target: gl::GLuint, + width: u32, + height: u32, + internal_format: u32, + format: u32, + type_: u32, + pixels: Option<&[u8]>) { + gl::tex_image_2d(target, 0, internal_format as gl::GLint, width as gl::GLint, height as gl::GLint, @@ -1014,20 +1045,6 @@ impl Device { pixels); } - fn deinit_texture_image(&mut self, format: ImageFormat) { - let (internal_format, gl_format) = gl_texture_formats_for_image_format(format); - let type_ = gl_type_for_texture_format(format); - gl::tex_image_2d(gl::TEXTURE_2D, - 0, - internal_format, - 0, - 0, - 0, - gl_format, - type_, - None); - } - pub fn init_texture(&mut self, texture_id: TextureId, width: u32, @@ -1044,29 +1061,36 @@ impl Device { texture.width = width; texture.height = height; texture.filter = filter; - texture.mode = mode + texture.mode = mode; } let (internal_format, gl_format) = gl_texture_formats_for_image_format(format); let type_ = gl_type_for_texture_format(format); match mode { - RenderTargetMode::RenderTarget => { + RenderTargetMode::SimpleRenderTarget => { self.bind_texture(TextureSampler::Color, texture_id); - self.set_texture_parameters(filter); - self.upload_texture_image(width, + self.set_texture_parameters(texture_id.target, filter); + self.upload_texture_image(texture_id.target, + width, height, internal_format as u32, gl_format, type_, None); - self.create_fbo_for_texture_if_necessary(texture_id); + self.create_fbo_for_texture_if_necessary(texture_id, None); + } + RenderTargetMode::LayerRenderTarget(layer_count) => { + self.bind_texture(TextureSampler::Color, texture_id); + self.set_texture_parameters(texture_id.target, filter); + self.create_fbo_for_texture_if_necessary(texture_id, Some(layer_count)); } RenderTargetMode::None => { - texture_id.bind(); - self.set_texture_parameters(filter); - - self.upload_texture_image(width, height, + self.bind_texture(TextureSampler::Color, texture_id); + self.set_texture_parameters(texture_id.target, filter); + self.upload_texture_image(texture_id.target, + width, + height, internal_format as u32, gl_format, type_, @@ -1075,26 +1099,69 @@ impl Device { } } - pub fn create_fbo_for_texture_if_necessary(&mut self, texture_id: TextureId) { - if !self.textures.get(&texture_id).unwrap().fbo_ids.is_empty() { - return - } + pub fn create_fbo_for_texture_if_necessary(&mut self, + texture_id: TextureId, + layer_count: Option) { + let texture = self.textures.get_mut(&texture_id).unwrap(); + + match layer_count { + Some(layer_count) => { + debug_assert!(layer_count > 0); - let fbo_ids: Vec<_> = - gl::gen_framebuffers(1).into_iter().map(|fbo_id| FBOId(fbo_id)).collect(); - for fbo_id in &fbo_ids[..] { - gl::bind_framebuffer(gl::FRAMEBUFFER, fbo_id.0); + // If we have enough layers allocated already, just use them. + // TODO(gw): Probably worth removing some after a while if + // there is a surplus? + let current_layer_count = texture.fbo_ids.len() as i32; + if current_layer_count >= layer_count { + return; + } - gl::framebuffer_texture_2d(gl::FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::TEXTURE_2D, - texture_id.0, - 0); + let (internal_format, gl_format) = gl_texture_formats_for_image_format(texture.format); + let type_ = gl_type_for_texture_format(texture.format); + + gl::tex_image_3d(texture_id.target, + 0, + internal_format as gl::GLint, + texture.width as gl::GLint, + texture.height as gl::GLint, + layer_count, + 0, + gl_format, + type_, + None); + + let needed_layer_count = layer_count - current_layer_count; + let new_fbos = gl::gen_framebuffers(needed_layer_count); + texture.fbo_ids.extend(new_fbos.into_iter().map(|id| FBOId(id))); + + for (fbo_index, fbo_id) in texture.fbo_ids.iter().enumerate() { + gl::bind_framebuffer(gl::FRAMEBUFFER, fbo_id.0); + gl::framebuffer_texture_layer(gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + texture_id.name, + 0, + fbo_index as gl::GLint); + } + } + None => { + debug_assert!(texture.fbo_ids.len() == 0 || texture.fbo_ids.len() == 1); + if texture.fbo_ids.is_empty() { + let new_fbo = gl::gen_framebuffers(1)[0]; + + gl::bind_framebuffer(gl::FRAMEBUFFER, new_fbo); + + gl::framebuffer_texture_2d(gl::FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + texture_id.target, + texture_id.name, + 0); + + texture.fbo_ids.push(FBOId(new_fbo)); + } + } } gl::bind_framebuffer(gl::FRAMEBUFFER, self.default_fbo); - - self.textures.get_mut(&texture_id).unwrap().fbo_ids = fbo_ids; } pub fn resize_texture(&mut self, @@ -1108,14 +1175,14 @@ impl Device { let (old_width, old_height) = self.get_texture_dimensions(texture_id); - let temp_texture_id = self.create_texture_ids(1)[0]; + let temp_texture_id = self.create_texture_ids(1, TextureTarget::Default)[0]; self.init_texture(temp_texture_id, old_width, old_height, format, filter, mode, None); - self.create_fbo_for_texture_if_necessary(temp_texture_id); + self.create_fbo_for_texture_if_necessary(temp_texture_id, None); - self.bind_render_target(Some(texture_id)); + self.bind_render_target(Some((texture_id, 0))); self.bind_texture(TextureSampler::Color, temp_texture_id); - gl::copy_tex_sub_image_2d(gl::TEXTURE_2D, + gl::copy_tex_sub_image_2d(temp_texture_id.target, 0, 0, 0, @@ -1126,11 +1193,11 @@ impl Device { self.deinit_texture(texture_id); self.init_texture(texture_id, new_width, new_height, format, filter, mode, None); - self.create_fbo_for_texture_if_necessary(texture_id); - self.bind_render_target(Some(temp_texture_id)); + self.create_fbo_for_texture_if_necessary(texture_id, None); + self.bind_render_target(Some((temp_texture_id, 0))); self.bind_texture(TextureSampler::Color, texture_id); - gl::copy_tex_sub_image_2d(gl::TEXTURE_2D, + gl::copy_tex_sub_image_2d(texture_id.target, 0, 0, 0, @@ -1146,11 +1213,22 @@ impl Device { pub fn deinit_texture(&mut self, texture_id: TextureId) { debug_assert!(self.inside_frame); - let texture_format = self.textures[&texture_id].format; self.bind_texture(TextureSampler::Color, texture_id); - self.deinit_texture_image(texture_format); let texture = self.textures.get_mut(&texture_id).unwrap(); + let (internal_format, gl_format) = gl_texture_formats_for_image_format(texture.format); + let type_ = gl_type_for_texture_format(texture.format); + + gl::tex_image_2d(texture_id.target, + 0, + internal_format, + 0, + 0, + 0, + gl_format, + type_, + None); + if !texture.fbo_ids.is_empty() { let fbo_ids: Vec<_> = texture.fbo_ids.iter().map(|&FBOId(fbo_id)| fbo_id).collect(); gl::delete_framebuffers(&fbo_ids[..]); @@ -1402,13 +1480,14 @@ impl Device { } fn update_image_for_2d_texture(&mut self, + target: gl::GLuint, x0: gl::GLint, y0: gl::GLint, width: gl::GLint, height: gl::GLint, format: gl::GLuint, data: &[u8]) { - gl::tex_sub_image_2d(gl::TEXTURE_2D, + gl::tex_sub_image_2d(target, 0, x0, y0, width, height, @@ -1450,7 +1529,8 @@ impl Device { assert!(data.len() as u32 == bpp * width * height); self.bind_texture(TextureSampler::Color, texture_id); - self.update_image_for_2d_texture(x0 as gl::GLint, + self.update_image_for_2d_texture(texture_id.target, + x0 as gl::GLint, y0 as gl::GLint, width as gl::GLint, height as gl::GLint, @@ -1467,7 +1547,7 @@ impl Device { width: i32, height: i32) { self.bind_texture(TextureSampler::Color, texture_id); - gl::copy_tex_sub_image_2d(gl::TEXTURE_2D, + gl::copy_tex_sub_image_2d(texture_id.target, 0, dest_x, dest_y, diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index c3236dc702..a20b7aa60f 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -308,7 +308,8 @@ impl DebugColorVertex { #[derive(Copy, Clone, Debug, PartialEq)] pub enum RenderTargetMode { None, - RenderTarget, + SimpleRenderTarget, + LayerRenderTarget(i32), // Number of texture layers } #[derive(Debug)] diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index d8a607c387..148878c243 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -383,12 +383,12 @@ impl PrimitiveStore { let gpu_address = self.gpu_data32.alloc(6); self.populate_clip_data(gpu_address, clip); let mask = match masked.mask { - MaskImageSource::User(image_key) => (Some(image_key), TextureId(0)), + MaskImageSource::User(image_key) => (Some(image_key), TextureId::invalid()), MaskImageSource::Renderer(texture_id) => (None, texture_id), }; (Some(gpu_address), mask) } else { - (None, (None, TextureId(0))) + (None, (None, TextureId::invalid())) }; let metadata = match container { @@ -399,7 +399,7 @@ impl PrimitiveStore { let metadata = PrimitiveMetadata { is_opaque: is_opaque, need_to_build_cache: mask_image.is_some(), - color_texture_id: TextureId(0), + color_texture_id: TextureId::invalid(), mask_texture_id: mask_texture_id, mask_image: mask_image, clip_index: clip_index, @@ -419,7 +419,7 @@ impl PrimitiveStore { let metadata = PrimitiveMetadata { is_opaque: false, need_to_build_cache: true, - color_texture_id: TextureId(0), + color_texture_id: TextureId::invalid(), mask_texture_id: mask_texture_id, mask_image: mask_image, clip_index: clip_index, @@ -439,7 +439,7 @@ impl PrimitiveStore { let metadata = PrimitiveMetadata { is_opaque: false, need_to_build_cache: true, - color_texture_id: TextureId(0), + color_texture_id: TextureId::invalid(), mask_texture_id: mask_texture_id, mask_image: mask_image, clip_index: clip_index, @@ -459,7 +459,7 @@ impl PrimitiveStore { let metadata = PrimitiveMetadata { is_opaque: false, need_to_build_cache: mask_image.is_some(), - color_texture_id: TextureId(0), + color_texture_id: TextureId::invalid(), mask_texture_id: mask_texture_id, mask_image: mask_image, clip_index: clip_index, @@ -480,7 +480,7 @@ impl PrimitiveStore { let metadata = PrimitiveMetadata { is_opaque: false, need_to_build_cache: true, - color_texture_id: TextureId(0), + color_texture_id: TextureId::invalid(), mask_texture_id: mask_texture_id, mask_image: mask_image, clip_index: clip_index, @@ -501,7 +501,7 @@ impl PrimitiveStore { let metadata = PrimitiveMetadata { is_opaque: false, need_to_build_cache: mask_image.is_some(), - color_texture_id: TextureId(0), + color_texture_id: TextureId::invalid(), mask_texture_id: mask_texture_id, mask_image: mask_image, clip_index: clip_index, @@ -678,7 +678,7 @@ impl PrimitiveStore { Some(image_info) => image_info, }; - debug_assert!(metadata.color_texture_id == TextureId(0) || + debug_assert!(metadata.color_texture_id == TextureId::invalid() || metadata.color_texture_id == image_info.texture_id); metadata.color_texture_id = image_info.texture_id; diff --git a/webrender/src/profiler.rs b/webrender/src/profiler.rs index ff6309d0b9..4d4042ede4 100644 --- a/webrender/src/profiler.rs +++ b/webrender/src/profiler.rs @@ -231,7 +231,7 @@ impl ProfileCounter for AverageTimeProfileCounter { pub struct FrameProfileCounters { pub total_primitives: IntProfileCounter, pub visible_primitives: IntProfileCounter, - pub phases: IntProfileCounter, + pub passes: IntProfileCounter, pub targets: IntProfileCounter, } @@ -240,8 +240,8 @@ impl FrameProfileCounters { FrameProfileCounters { total_primitives: IntProfileCounter::new("Total Primitives"), visible_primitives: IntProfileCounter::new("Visible Primitives"), - phases: IntProfileCounter::new("Phases"), - targets: IntProfileCounter::new("Target switches"), + passes: IntProfileCounter::new("Passes"), + targets: IntProfileCounter::new("Render Targets"), } } } @@ -620,7 +620,7 @@ impl Profiler { self.draw_counters(&[ &frame_profile.total_primitives, &frame_profile.visible_primitives, - &frame_profile.phases, + &frame_profile.passes, &frame_profile.targets, ], debug_renderer, true); diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs index 2123fe5a51..51c01a2d7c 100644 --- a/webrender/src/render_backend.rs +++ b/webrender/src/render_backend.rs @@ -288,7 +288,7 @@ impl RenderBackend { self.webgl_contexts.insert(id, ctx); self.resource_cache - .add_webgl_texture(id, TextureId(texture_id), real_size); + .add_webgl_texture(id, TextureId::new(texture_id), real_size); tx.send(Ok((id, limits))).unwrap(); }, diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 1083490417..96dee632ba 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -12,7 +12,7 @@ use batch::RasterBatch; use debug_render::DebugRenderer; use device::{Device, ProgramId, TextureId, UniformLocation, VertexFormat, GpuProfiler}; -use device::{TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler}; +use device::{TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler, TextureTarget}; use euclid::{Matrix4D, Point2D, Rect, Size2D}; use fnv::FnvHasher; use gleam::gl; @@ -92,7 +92,7 @@ struct VertexDataTexture { impl VertexDataTexture { fn new(device: &mut Device) -> VertexDataTexture { - let id = device.create_texture_ids(1)[0]; + let id = device.create_texture_ids(1, TextureTarget::Default)[0]; VertexDataTexture { id: id, @@ -358,7 +358,7 @@ pub struct Renderer { max_raster_op_size: u32, raster_op_target_a8: TextureId, raster_op_target_rgba8: TextureId, - render_targets: [TextureId; 2], + render_targets: Vec, gpu_profile: GpuProfiler, quad_vao_id: VAOId, @@ -497,7 +497,7 @@ impl Renderer { &mut device, options.precache_shaders); - let texture_ids = device.create_texture_ids(1024); + let texture_ids = device.create_texture_ids(1024, TextureTarget::Default); let mut texture_cache = TextureCache::new(texture_ids); let white_pixels: Vec = vec![ @@ -540,22 +540,22 @@ impl Renderer { let debug_renderer = DebugRenderer::new(&mut device); - let raster_op_target_a8 = device.create_texture_ids(1)[0]; + let raster_op_target_a8 = device.create_texture_ids(1, TextureTarget::Default)[0]; device.init_texture(raster_op_target_a8, max_raster_op_size, max_raster_op_size, ImageFormat::A8, TextureFilter::Nearest, - RenderTargetMode::RenderTarget, + RenderTargetMode::SimpleRenderTarget, None); - let raster_op_target_rgba8 = device.create_texture_ids(1)[0]; + let raster_op_target_rgba8 = device.create_texture_ids(1, TextureTarget::Default)[0]; device.init_texture(raster_op_target_rgba8, max_raster_op_size, max_raster_op_size, ImageFormat::RGBA8, TextureFilter::Nearest, - RenderTargetMode::RenderTarget, + RenderTargetMode::SimpleRenderTarget, None); let layer_texture = VertexDataTexture::new(&mut device); @@ -668,7 +668,7 @@ impl Renderer { last_time: 0, raster_op_target_a8: raster_op_target_a8, raster_op_target_rgba8: raster_op_target_rgba8, - render_targets: [TextureId(0), TextureId(0)], + render_targets: Vec::new(), max_raster_op_size: max_raster_op_size, gpu_profile: GpuProfiler::new(), quad_vao_id: quad_vao_id, @@ -1148,13 +1148,13 @@ impl Renderer { gl::disable(gl::BLEND); } - self.device.bind_render_target(Some(target_texture_id)); + self.device.bind_render_target(Some((target_texture_id, 0))); gl::viewport(0, 0, self.max_raster_op_size as gl::GLint, self.max_raster_op_size as gl::GLint); self.device.bind_program(program_id, &projection); self.device.bind_texture(TextureSampler::Color, color_texture_id); - self.device.bind_texture(TextureSampler::Mask, TextureId(0)); + self.device.bind_texture(TextureSampler::Mask, TextureId::invalid()); match blur_direction { Some(AxisDirection::Horizontal) => { @@ -1346,10 +1346,10 @@ impl Renderer { } fn draw_target(&mut self, - render_target: Option, + render_target: Option<(TextureId, i32)>, target: &RenderTarget, target_size: &Size2D, - cache_texture: TextureId, + cache_texture: Option, should_clear: bool) { self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET); @@ -1363,7 +1363,9 @@ impl Renderer { gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); gl::blend_equation(gl::FUNC_ADD); - self.device.bind_texture(TextureSampler::Cache, cache_texture); + if let Some(cache_texture) = cache_texture { + self.device.bind_texture(TextureSampler::Cache, cache_texture); + } let projection = match render_target { Some(..) => { @@ -1572,28 +1574,30 @@ impl Renderer { ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE); - if frame.phases.is_empty() { + if frame.passes.is_empty() { gl::clear_color(1.0, 1.0, 1.0, 1.0); gl::clear(gl::COLOR_BUFFER_BIT); } else { - if self.render_targets[0] == TextureId(0) { - self.render_targets[0] = self.device.create_texture_ids(1)[0]; - self.render_targets[1] = self.device.create_texture_ids(1)[0]; - self.device.init_texture(self.render_targets[0], - frame.cache_size.width as u32, - frame.cache_size.height as u32, - ImageFormat::RGBA8, - TextureFilter::Linear, - RenderTargetMode::RenderTarget, - None); + // Add new render targets to the pool if required. + let needed_targets = frame.passes.len() - 1; // framebuffer doesn't need a target! + let current_target_count = self.render_targets.len(); + if needed_targets > current_target_count { + let new_target_count = needed_targets - current_target_count; + let new_targets = self.device.create_texture_ids(new_target_count as i32, + TextureTarget::Array); + self.render_targets.extend_from_slice(&new_targets); + } - self.device.init_texture(self.render_targets[1], + // Init textures and render targets to match this scene. This shouldn't + // block in drivers, but it might be worth checking... + for (pass, texture_id) in frame.passes.iter().zip(self.render_targets.iter()) { + self.device.init_texture(*texture_id, frame.cache_size.width as u32, frame.cache_size.height as u32, ImageFormat::RGBA8, TextureFilter::Linear, - RenderTargetMode::RenderTarget, + RenderTargetMode::LayerRenderTarget(pass.targets.len() as i32), None); } @@ -1613,28 +1617,30 @@ impl Renderer { self.device.bind_texture(TextureSampler::Data64, self.data64_texture.id); self.device.bind_texture(TextureSampler::Data128, self.data128_texture.id); - for (phase_index, phase) in frame.phases.iter().enumerate() { - let mut render_target_index = 0; - - for target in &phase.targets { - if target.is_framebuffer { - let ct_index = self.render_targets[1 - render_target_index]; - self.draw_target(None, - target, - &Size2D::new(framebuffer_size.width as f32, framebuffer_size.height as f32), - ct_index, - needs_clear && phase_index == 0); - } else { - let rt_index = self.render_targets[render_target_index]; - let ct_index = self.render_targets[1 - render_target_index]; - self.draw_target(Some(rt_index), - target, - &frame.cache_size, - ct_index, - true); - render_target_index = 1 - render_target_index; - } + let mut src_id = None; + + for (pass_index, pass) in frame.passes.iter().enumerate() { + let (do_clear, size, target_id) = if pass.is_framebuffer { + (needs_clear, + Size2D::new(framebuffer_size.width as f32, framebuffer_size.height as f32), + None) + } else { + (true, frame.cache_size, Some(self.render_targets[pass_index])) + }; + + for (target_index, target) in pass.targets.iter().enumerate() { + let render_target = target_id.map(|texture_id| { + (texture_id, target_index as i32) + }); + self.draw_target(render_target, + target, + &size, + src_id, + do_clear); + } + + src_id = target_id; } } diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index 1a451ef3f3..c6a673a3de 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -691,19 +691,19 @@ impl TextureCache { let (page_list, mode) = match (format, kind) { (ImageFormat::A8, TextureCacheItemKind::Standard) => { - (&mut self.arena.pages_a8, RenderTargetMode::RenderTarget) + (&mut self.arena.pages_a8, RenderTargetMode::SimpleRenderTarget) } (ImageFormat::A8, TextureCacheItemKind::Alternate) => { - (&mut self.arena.alternate_pages_a8, RenderTargetMode::RenderTarget) + (&mut self.arena.alternate_pages_a8, RenderTargetMode::SimpleRenderTarget) } (ImageFormat::RGBA8, TextureCacheItemKind::Standard) => { - (&mut self.arena.pages_rgba8, RenderTargetMode::RenderTarget) + (&mut self.arena.pages_rgba8, RenderTargetMode::SimpleRenderTarget) } (ImageFormat::RGBA8, TextureCacheItemKind::Alternate) => { - (&mut self.arena.alternate_pages_rgba8, RenderTargetMode::RenderTarget) + (&mut self.arena.alternate_pages_rgba8, RenderTargetMode::SimpleRenderTarget) } (ImageFormat::RGB8, TextureCacheItemKind::Standard) => { - (&mut self.arena.pages_rgb8, RenderTargetMode::RenderTarget) + (&mut self.arena.pages_rgb8, RenderTargetMode::SimpleRenderTarget) } (ImageFormat::Invalid, TextureCacheItemKind::Standard) | (ImageFormat::RGBAF32, TextureCacheItemKind::Standard) | diff --git a/webrender/src/tiling.rs b/webrender/src/tiling.rs index 7539071ca9..de2c90082b 100644 --- a/webrender/src/tiling.rs +++ b/webrender/src/tiling.rs @@ -233,6 +233,9 @@ pub enum PrimitiveFlags { Scrollbar(ScrollLayerId, f32) } +#[derive(Debug, Copy, Clone)] +struct RenderTargetIndex(usize); + #[derive(Debug, Copy, Clone)] struct RenderTaskIndex(usize); @@ -427,83 +430,95 @@ struct RenderTargetContext<'a> { } pub struct RenderTarget { - pub is_framebuffer: bool, - page_allocator: TexturePage, - tasks: Vec, pub alpha_batcher: AlphaBatcher, + page_allocator: TexturePage, } impl RenderTarget { - fn new(is_framebuffer: bool) -> RenderTarget { + fn new() -> RenderTarget { RenderTarget { - is_framebuffer: is_framebuffer, - page_allocator: TexturePage::new(TextureId(0), RENDERABLE_CACHE_SIZE as u32), - tasks: Vec::new(), alpha_batcher: AlphaBatcher::new(), + page_allocator: TexturePage::new(TextureId::invalid(), RENDERABLE_CACHE_SIZE as u32), } } - fn add_render_task(&mut self, task: RenderTask) { - self.tasks.push(task); - } - fn build(&mut self, ctx: &RenderTargetContext, render_tasks: &mut RenderTaskCollection) { - // Step through each task, adding to batches as appropriate. - for task in self.tasks.drain(..) { - match task.kind { - RenderTaskKind::Alpha(info) => { - self.alpha_batcher.add_task(AlphaBatchTask { - task_id: task.id, - items: info.items, - }); - } + self.alpha_batcher.build(ctx, render_tasks); + } + + fn add_task(&mut self, task: RenderTask) { + match task.kind { + RenderTaskKind::Alpha(info) => { + self.alpha_batcher.add_task(AlphaBatchTask { + task_id: task.id, + items: info.items, + }); } } - - self.alpha_batcher.build(ctx, render_tasks); } } -pub struct RenderPhase { +pub struct RenderPass { + pub is_framebuffer: bool, + tasks: Vec, pub targets: Vec, } -impl RenderPhase { - fn new(max_target_count: usize) -> RenderPhase { - //println!("+ start render phase: targets={}", max_target_count); - let mut targets = Vec::with_capacity(max_target_count); - for index in 0..max_target_count { - targets.push(RenderTarget::new(index == max_target_count-1)); - } - - RenderPhase { - targets: targets, +impl RenderPass { + fn new(is_framebuffer: bool) -> RenderPass { + RenderPass { + is_framebuffer: is_framebuffer, + targets: vec![ RenderTarget::new() ], + tasks: Vec::new(), } } - fn add_compiled_screen_tile(&mut self, - mut tile: CompiledScreenTile, - render_tasks: &mut RenderTaskCollection) -> Option { - debug_assert!(tile.required_target_count <= self.targets.len()); - - let ok = tile.main_render_task.alloc_if_required(self.targets.len() - 1, - &mut self.targets); - - if ok { - tile.main_render_task.assign_to_targets(self.targets.len() - 1, - &mut self.targets, - render_tasks); - None - } else { - Some(tile) - } + fn add_render_task(&mut self, task: RenderTask) { + self.tasks.push(task); } fn build(&mut self, ctx: &RenderTargetContext, render_tasks: &mut RenderTaskCollection) { + // Step through each task, adding to batches as appropriate. + for mut task in self.tasks.drain(..) { + // Find a target to assign this task to, or create a new + // one if required. + match task.location { + RenderTaskLocation::Fixed(..) => {} + RenderTaskLocation::Dynamic(ref mut origin, ref size) => { + let alloc_size = Size2D::new(size.width as u32, + size.height as u32); + + let alloc_origin = self.targets + .last_mut() + .unwrap() + .page_allocator.allocate(&alloc_size); + + let alloc_origin = match alloc_origin { + Some(alloc_origin) => alloc_origin, + None => { + let mut new_target = RenderTarget::new(); + let origin = new_target.page_allocator + .allocate(&alloc_size) + .expect("Each render task must allocate <= size of one target!"); + self.targets.push(new_target); + origin + } + }; + + let alloc_origin = DevicePoint::new(alloc_origin.x as i32, + alloc_origin.y as i32); + *origin = Some((alloc_origin, RenderTargetIndex(self.targets.len() - 1))); + } + } + + render_tasks.add(&task); + self.targets.last_mut().unwrap().add_task(task); + } + for target in &mut self.targets { target.build(ctx, render_tasks); } @@ -513,7 +528,7 @@ impl RenderPhase { #[derive(Debug)] enum RenderTaskLocation { Fixed(DeviceRect), - Dynamic(Option, DeviceSize), + Dynamic(Option<(DevicePoint, RenderTargetIndex)>, DeviceSize), } #[derive(Debug)] @@ -566,7 +581,7 @@ impl RenderTask { fn write_task_data(&self) -> RenderTaskData { match self.kind { RenderTaskKind::Alpha(ref task) => { - let target_rect = self.get_target_rect(); + let (target_rect, target_index) = self.get_target_rect(); debug_assert!(target_rect.size.width == task.actual_rect.size.width); debug_assert!(target_rect.size.height == task.actual_rect.size.height); @@ -578,7 +593,7 @@ impl RenderTask { target_rect.origin.y as f32, task.actual_rect.size.width as f32, task.actual_rect.size.height as f32, - 0.0, + target_index.0 as f32, 0.0, ], } @@ -597,75 +612,36 @@ impl RenderTask { } } - fn get_target_rect(&self) -> DeviceRect { + fn get_target_rect(&self) -> (DeviceRect, RenderTargetIndex) { match self.location { - RenderTaskLocation::Fixed(rect) => rect, - RenderTaskLocation::Dynamic(origin, size) => { - DeviceRect::new(origin.expect("Should have been allocated by now!"), - size) + RenderTaskLocation::Fixed(rect) => (rect, RenderTargetIndex(0)), + RenderTaskLocation::Dynamic(origin_and_target_index, size) => { + let (origin, target_index) = origin_and_target_index.expect("Should have been allocated by now!"); + (DeviceRect::new(origin, size), target_index) } } } - fn assign_to_targets(mut self, - target_index: usize, - targets: &mut Vec, - render_tasks: &mut RenderTaskCollection) { + fn assign_to_passes(mut self, + pass_index: usize, + passes: &mut Vec) { for child in self.children.drain(..) { - child.assign_to_targets(target_index - 1, - targets, - render_tasks); + child.assign_to_passes(pass_index - 1, + passes); } - render_tasks.add(&self); - // Sanity check - can be relaxed if needed match self.location { RenderTaskLocation::Fixed(..) => { - debug_assert!(target_index == targets.len() - 1); + debug_assert!(pass_index == passes.len() - 1); } RenderTaskLocation::Dynamic(..) => { - debug_assert!(target_index < targets.len() - 1); - } - } - - let target = &mut targets[target_index]; - target.add_render_task(self); - } - - fn alloc_if_required(&mut self, - target_index: usize, - targets: &mut Vec) -> bool { - match self.location { - RenderTaskLocation::Fixed(..) => {} - RenderTaskLocation::Dynamic(ref mut origin, ref size) => { - let target = &mut targets[target_index]; - - let alloc_size = Size2D::new(size.width as u32, - size.height as u32); - - let alloc_origin = target.page_allocator.allocate(&alloc_size); - - match alloc_origin { - Some(alloc_origin) => { - *origin = Some(DevicePoint::new(alloc_origin.x as i32, - alloc_origin.y as i32)); - } - None => { - return false; - } - } + debug_assert!(pass_index < passes.len() - 1); } } - for child in &mut self.children { - if !child.alloc_if_required(target_index - 1, - targets) { - return false; - } - } - - true + let pass = &mut passes[pass_index]; + pass.add_render_task(self); } fn max_depth(&self, @@ -739,8 +715,8 @@ impl AlphaBatchKey { AlphaBatchKey { kind: AlphaBatchKind::Blend, flags: AlphaBatchKeyFlags(0), - color_texture_id: TextureId(0), - mask_texture_id: TextureId(0), + color_texture_id: TextureId::invalid(), + mask_texture_id: TextureId::invalid(), } } @@ -748,8 +724,8 @@ impl AlphaBatchKey { AlphaBatchKey { kind: AlphaBatchKind::Composite, flags: AlphaBatchKeyFlags(0), - color_texture_id: TextureId(0), - mask_texture_id: TextureId(0), + color_texture_id: TextureId::invalid(), + mask_texture_id: TextureId::invalid(), } } @@ -769,9 +745,9 @@ impl AlphaBatchKey { fn is_compatible_with(&self, other: &AlphaBatchKey) -> bool { self.kind == other.kind && self.flags == other.flags && - (self.color_texture_id == TextureId(0) || other.color_texture_id == TextureId(0) || + (self.color_texture_id == TextureId::invalid() || other.color_texture_id == TextureId::invalid() || self.color_texture_id == other.color_texture_id) && - (self.mask_texture_id == TextureId(0) || other.mask_texture_id == TextureId(0) || + (self.mask_texture_id == TextureId::invalid() || other.mask_texture_id == TextureId::invalid() || self.mask_texture_id == other.mask_texture_id) } } @@ -860,8 +836,8 @@ pub struct PrimitiveBatch { impl PrimitiveBatch { fn blend() -> PrimitiveBatch { PrimitiveBatch { - color_texture_id: TextureId(0), - mask_texture_id: TextureId(0), + color_texture_id: TextureId::invalid(), + mask_texture_id: TextureId::invalid(), transform_kind: TransformedRectKind::AxisAligned, has_complex_clip: false, blending_enabled: true, @@ -871,8 +847,8 @@ impl PrimitiveBatch { fn composite() -> PrimitiveBatch { PrimitiveBatch { - color_texture_id: TextureId(0), - mask_texture_id: TextureId(0), + color_texture_id: TextureId::invalid(), + mask_texture_id: TextureId::invalid(), transform_kind: TransformedRectKind::AxisAligned, has_complex_clip: false, blending_enabled: true, @@ -1089,7 +1065,7 @@ pub struct Frame { pub viewport_size: Size2D, pub debug_rects: Vec, pub cache_size: Size2D, - pub phases: Vec, + pub passes: Vec, pub clear_tiles: Vec, pub profile_counters: FrameProfileCounters, @@ -1114,22 +1090,27 @@ enum CompiledScreenTileInfo { #[derive(Debug)] struct CompiledScreenTile { main_render_task: RenderTask, - required_target_count: usize, + required_pass_count: usize, info: CompiledScreenTileInfo, } impl CompiledScreenTile { fn new(main_render_task: RenderTask, info: CompiledScreenTileInfo) -> CompiledScreenTile { - let mut required_target_count = 0; - main_render_task.max_depth(0, &mut required_target_count); + let mut required_pass_count = 0; + main_render_task.max_depth(0, &mut required_pass_count); CompiledScreenTile { main_render_task: main_render_task, - required_target_count: required_target_count, + required_pass_count: required_pass_count, info: info, } } + + fn build(self, passes: &mut Vec) { + self.main_render_task.assign_to_passes(passes.len() - 1, + passes); + } } #[derive(Debug, Eq, PartialEq, Copy, Clone)] @@ -2045,10 +2026,13 @@ impl FrameBuilder { // Build list of passes, target allocs that each tile needs. let mut compiled_screen_tiles = Vec::new(); + let mut max_passes_needed = 0; for screen_tile in screen_tiles { let rect = screen_tile.rect; // TODO(gw): Remove clone here match screen_tile.compile(&ctx) { Some(compiled_screen_tile) => { + max_passes_needed = cmp::max(max_passes_needed, + compiled_screen_tile.required_pass_count); if self.debug { let (label, color) = match &compiled_screen_tile.info { &CompiledScreenTileInfo::SimpleAlpha(prim_count) => { @@ -2074,44 +2058,26 @@ impl FrameBuilder { } } - let mut phases = Vec::new(); + let mut passes = Vec::new(); let static_render_task_count = ctx.render_task_id_counter.load(Ordering::SeqCst); let mut render_tasks = RenderTaskCollection::new(static_render_task_count); if !compiled_screen_tiles.is_empty() { - // Sort by pass count to minimize render target switches. - compiled_screen_tiles.sort_by(|a, b| { - let a_passes = a.required_target_count; - let b_passes = b.required_target_count; - b_passes.cmp(&a_passes) - }); - - // Do the allocations now, assigning each tile to a render - // phase as required. - - let mut current_phase = RenderPhase::new(compiled_screen_tiles[0].required_target_count); + // Do the allocations now, assigning each tile's tasks to a render + // pass and target as required. + for index in 0..max_passes_needed { + passes.push(RenderPass::new(index == max_passes_needed-1)); + } for compiled_screen_tile in compiled_screen_tiles { - if let Some(failed_tile) = current_phase.add_compiled_screen_tile(compiled_screen_tile, - &mut render_tasks) { - let full_phase = mem::replace(&mut current_phase, - RenderPhase::new(failed_tile.required_target_count)); - phases.push(full_phase); - - let result = current_phase.add_compiled_screen_tile(failed_tile, - &mut render_tasks); - assert!(result.is_none(), "TODO: Handle single tile not fitting in render phase."); - } + compiled_screen_tile.build(&mut passes); } - phases.push(current_phase); - - //println!("rendering: phase count={}", phases.len()); - for phase in &mut phases { - phase.build(&ctx, &mut render_tasks); + for pass in passes.iter_mut().rev() { + pass.build(&ctx, &mut render_tasks); - profile_counters.phases.inc(); - profile_counters.targets.add(phase.targets.len()); + profile_counters.passes.inc(); + profile_counters.targets.add(pass.targets.len()); } } @@ -2119,7 +2085,7 @@ impl FrameBuilder { viewport_size: self.screen_rect.size, debug_rects: debug_rects, profile_counters: profile_counters, - phases: phases, + passes: passes, clear_tiles: clear_tiles, cache_size: Size2D::new(RENDERABLE_CACHE_SIZE as f32, RENDERABLE_CACHE_SIZE as f32),