From cfe69c19424319fe000c31f53d05ce2fcc5ccf0e Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 4 Oct 2018 10:34:15 -0700 Subject: [PATCH 1/7] Stop reinitializing render targets with different dimensions. This is a prerequisite to switching to immutable storage. Differential Revision: https://phabricator.services.mozilla.com/D8161 --- webrender/src/device/gl.rs | 15 ++++++++ webrender/src/renderer.rs | 77 ++++++++++++++++---------------------- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index a5a987a15f..520dae486f 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -1299,6 +1299,21 @@ impl Device { } } + /// Notifies the device that a render target is about to be reused. + /// + /// This method adds or removes a depth target as necessary. + pub fn reuse_render_target( + &mut self, + texture: &mut Texture, + rt_info: RenderTargetInfo, + ) { + texture.last_frame_used = self.frame_id; + texture.render_target = Some(rt_info); + if rt_info.has_depth != texture.has_depth() { + self.update_target_storage::(texture, &rt_info, /* is_resized = */ false, None); + } + } + /// Updates the render target storage for the texture, creating FBOs as required. fn update_target_storage( &mut self, diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 6cbcd08b58..d3510dd5f6 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -3681,20 +3681,20 @@ impl Renderer { /// Allocates a texture to be used as the output for a rendering pass. /// /// We make an effort to reuse render targe textures across passes and - /// across frames. Reusing a texture with the same dimensions (width, - /// height, and layer-count) and format is obviously ideal. Reusing a - /// texture with different dimensions but the same format can be faster - /// than allocating a new texture, since it basically boils down to - /// a realloc in GPU memory, which can be very cheap if the existing - /// region can be resized. However, some drivers/GPUs require textures - /// with different formats to be allocated in different arenas, - /// reinitializing with a different format can force a large copy. As - /// such, we just allocate a new texture in that case. + /// across frames when the format and dimensions match. Because we use + /// immutable storage, we can't resize textures. + /// + /// We could consider approaches to re-use part of a larger target, if + /// available. However, we'd need to be careful about eviction. Currently, + /// render targets are freed if they haven't been used in 30 frames. If we + /// used partial targets, we'd need to track how _much_ of the target has + /// been used in the last 30 frames, since we could otherwise end up + /// keeping an enormous target alive indefinitely by constantly using it + /// in situations where a much smaller target would suffice. fn allocate_target_texture( &mut self, list: &mut RenderTargetList, counters: &mut FrameProfileCounters, - frame_id: FrameId, ) -> Option { debug_assert_ne!(list.max_size, DeviceUintSize::zero()); if list.targets.is_empty() { @@ -3703,16 +3703,16 @@ impl Renderer { counters.targets_used.inc(); - // First, try finding a perfect match + // Try finding a match in the existing pool. If there's no match, we'll + // create a new texture. let selector = TargetSelector { size: list.max_size, num_layers: list.targets.len() as _, format: list.format, }; - let mut index = self.texture_resolver.render_target_pool + let index = self.texture_resolver.render_target_pool .iter() .position(|texture| { - //TODO: re-use a part of a larger target, if available selector == TargetSelector { size: texture.get_dimensions(), num_layers: texture.get_render_target_layer_count(), @@ -3720,37 +3720,26 @@ impl Renderer { } }); - // Next, try at least finding a matching format - if index.is_none() { - counters.targets_changed.inc(); - index = self.texture_resolver.render_target_pool - .iter() - .position(|texture| texture.get_format() == list.format && !texture.used_in_frame(frame_id)); - } - - let mut texture = match index { - Some(pos) => { - self.texture_resolver.render_target_pool.swap_remove(pos) - } - None => { - counters.targets_created.inc(); - // finally, give up and create a new one - self.device.create_texture(TextureTarget::Array, list.format) - } + let rt_info = RenderTargetInfo { has_depth: list.needs_depth() }; + let texture = if let Some(idx) = index { + let mut t = self.texture_resolver.render_target_pool.swap_remove(idx); + self.device.reuse_render_target::(&mut t, rt_info); + t + } else { + counters.targets_created.inc(); + let mut t = self.device.create_texture(TextureTarget::Array, list.format); + self.device.init_texture::( + &mut t, + list.max_size.width, + list.max_size.height, + TextureFilter::Linear, + Some(rt_info), + list.targets.len() as _, + None, + ); + t }; - self.device.init_texture::( - &mut texture, - list.max_size.width, - list.max_size.height, - TextureFilter::Linear, - Some(RenderTargetInfo { - has_depth: list.needs_depth(), - }), - list.targets.len() as _, - None, - ); - list.check_ready(&texture); Some(ActiveTexture { texture, @@ -3868,8 +3857,8 @@ impl Renderer { (None, None) } RenderPassKind::OffScreen { ref mut alpha, ref mut color, ref mut texture_cache } => { - let alpha_tex = self.allocate_target_texture(alpha, &mut frame.profile_counters, frame_id); - let color_tex = self.allocate_target_texture(color, &mut frame.profile_counters, frame_id); + let alpha_tex = self.allocate_target_texture(alpha, &mut frame.profile_counters); + let color_tex = self.allocate_target_texture(color, &mut frame.profile_counters); // If this frame has already been drawn, then any texture // cache targets have already been updated and can be From e37d135ac1f9e0c209ffd2195070fa8745b4ad93 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 8 Oct 2018 16:21:09 -0700 Subject: [PATCH 2/7] Stop reinitializing the GPU cache texture with different dimensions. Differential Revision: https://phabricator.services.mozilla.com/D8162 --- webrender/src/device/gl.rs | 35 +++------ webrender/src/renderer.rs | 147 ++++++++++++++++++++++++------------- 2 files changed, 104 insertions(+), 78 deletions(-) diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index 520dae486f..e3ff001121 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -1225,37 +1225,22 @@ impl Device { .tex_parameter_i(target, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint); } - /// Resizes a texture with enabled render target views, - /// preserves the data by blitting the old texture contents over. - pub fn resize_renderable_texture( + /// Copies the contents from one renderable texture to another. + pub fn blit_renderable_texture( &mut self, - texture: &mut Texture, - new_size: DeviceUintSize, + dst: &mut Texture, + src: &Texture, ) { debug_assert!(self.inside_frame); + debug_assert!(dst.width >= src.width); + debug_assert!(dst.height >= src.height); - let old_size = texture.get_dimensions(); - let old_fbos = mem::replace(&mut texture.fbo_ids, Vec::new()); - let old_texture_id = mem::replace(&mut texture.id, self.gl.gen_textures(1)[0]); - - texture.width = new_size.width; - texture.height = new_size.height; - let rt_info = texture.render_target - .clone() - .expect("Only renderable textures are expected for resize here"); - - self.bind_texture(DEFAULT_TEXTURE, texture); - self.set_texture_parameters(texture.target, texture.filter); - self.update_target_storage::(texture, &rt_info, true, None); - - let rect = DeviceIntRect::new(DeviceIntPoint::zero(), old_size.to_i32()); - for (read_fbo, &draw_fbo) in old_fbos.into_iter().zip(&texture.fbo_ids) { - self.bind_read_target_impl(read_fbo); - self.bind_draw_target_impl(draw_fbo); + let rect = DeviceIntRect::new(DeviceIntPoint::zero(), src.get_dimensions().to_i32()); + for (read_fbo, draw_fbo) in src.fbo_ids.iter().zip(&dst.fbo_ids) { + self.bind_read_target_impl(*read_fbo); + self.bind_draw_target_impl(*draw_fbo); self.blit_render_target(rect, rect); - self.delete_fbo(read_fbo); } - self.gl.delete_textures(&[old_texture_id]); self.bind_read_target(None); } diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index d3510dd5f6..ee9faa9cf8 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -1014,16 +1014,80 @@ enum GpuCacheBus { }, } +impl GpuCacheBus { + /// Returns true if this bus uses a render target for a texture. + fn uses_render_target(&self) -> bool { + match *self { + GpuCacheBus::Scatter { .. } => true, + GpuCacheBus::PixelBuffer { .. } => false, + } + } +} + /// The device-specific representation of the cache texture in gpu_cache.rs struct GpuCacheTexture { - texture: Texture, + texture: Option, bus: GpuCacheBus, } impl GpuCacheTexture { - fn new(device: &mut Device, use_scatter: bool) -> Result { - let texture = device.create_texture(TextureTarget::Default, ImageFormat::RGBAF32); + /// Ensures that we have an appropriately-sized texture. Returns true if a + /// new texture was created. + fn ensure_texture(&mut self, device: &mut Device, height: u32) -> bool { + // If we already have a texture that works, we're done. + if self.texture.as_ref().map_or(false, |t| t.get_dimensions().height >= height) { + if GPU_CACHE_RESIZE_TEST && self.bus.uses_render_target() { + // Special debug mode - resize the texture even though it's fine. + } else { + return false; + } + } + + // Compute a few parameters for the new texture. We round the height up to + // a multiple of 256 to avoid many small resizes. + let new_height = (height + 255) & !255; + let new_size = DeviceUintSize::new(MAX_VERTEX_TEXTURE_WIDTH as _, new_height); + let rt_info = if self.bus.uses_render_target() { + Some(RenderTargetInfo { has_depth: false }) + } else { + None + }; + + // Take the old texture, if any, and deinitialize it unless we're going + // to blit it's contents to the new one. + let mut blit_source = None; + if let Some(t) = self.texture.take() { + if rt_info.is_some() { + blit_source = Some(t); + } else { + device.delete_texture(t); + } + } + + // Create the new texture. + let mut texture = device.create_texture(TextureTarget::Default, ImageFormat::RGBAF32); + device.init_texture::( + &mut texture, + new_size.width, + new_size.height, + TextureFilter::Nearest, + rt_info, + 1, + None, + ); + + // Blit the contents of the previous texture, if applicable. + if let Some(blit_source) = blit_source { + device.blit_renderable_texture(&mut texture, &blit_source); + device.delete_texture(blit_source); + } + + self.texture = Some(texture); + true + } + + fn new(device: &mut Device, use_scatter: bool) -> Result { let bus = if use_scatter { let program = device.create_program_linked( "gpu_cache_update", @@ -1055,13 +1119,15 @@ impl GpuCacheTexture { }; Ok(GpuCacheTexture { - texture, + texture: None, bus, }) } - fn deinit(self, device: &mut Device) { - device.delete_texture(self.texture); + fn deinit(mut self, device: &mut Device) { + if let Some(t) = self.texture.take() { + device.delete_texture(t); + } match self.bus { GpuCacheBus::PixelBuffer { buffer, ..} => { device.delete_pbo(buffer); @@ -1076,7 +1142,7 @@ impl GpuCacheTexture { } fn get_height(&self) -> u32 { - self.texture.get_dimensions().height + self.texture.as_ref().map_or(0, |t| t.get_dimensions().height) } fn prepare_for_updates( @@ -1085,25 +1151,10 @@ impl GpuCacheTexture { total_block_count: usize, max_height: u32, ) { - // See if we need to create or resize the texture. - let old_size = self.texture.get_dimensions(); - let new_size = DeviceUintSize::new(MAX_VERTEX_TEXTURE_WIDTH as _, max_height); - + let allocated_new_texture = self.ensure_texture(device, max_height); match self.bus { GpuCacheBus::PixelBuffer { ref mut rows, .. } => { - if max_height > old_size.height { - // Create a f32 texture that can be used for the vertex shader - // to fetch data from. - device.init_texture::( - &mut self.texture, - new_size.width, - new_size.height, - TextureFilter::Nearest, - None, - 1, - None, - ); - + if allocated_new_texture { // If we had to resize the texture, just mark all rows // as dirty so they will be uploaded to the texture // during the next flush. @@ -1123,24 +1174,6 @@ impl GpuCacheTexture { device.allocate_vbo(buf_position, total_block_count, VertexUsageHint::Stream); device.allocate_vbo(buf_value, total_block_count, VertexUsageHint::Stream); } - - if new_size.height > old_size.height || GPU_CACHE_RESIZE_TEST { - if old_size.height > 0 { - device.resize_renderable_texture(&mut self.texture, new_size); - } else { - device.init_texture::( - &mut self.texture, - new_size.width, - new_size.height, - TextureFilter::Nearest, - Some(RenderTargetInfo { - has_depth: false, - }), - 1, - None, - ); - } - } } } } @@ -1190,7 +1223,7 @@ impl GpuCacheTexture { // Unused positions will be left as 0xFFFF, which translates to // (1.0, 1.0) in the vertex output position and gets culled out let mut position_data = vec![[!0u16; 2]; updates.blocks.len()]; - let size = self.texture.get_dimensions().to_usize(); + let size = self.texture.as_ref().unwrap().get_dimensions().to_usize(); for update in &updates.updates { match *update { @@ -1217,6 +1250,7 @@ impl GpuCacheTexture { } fn flush(&mut self, device: &mut Device) -> usize { + let texture = self.texture.as_ref().unwrap(); match self.bus { GpuCacheBus::PixelBuffer { ref buffer, ref mut rows, ref cpu_blocks } => { let rows_dirty = rows @@ -1228,7 +1262,7 @@ impl GpuCacheTexture { } let mut uploader = device.upload_texture( - &self.texture, + texture, buffer, rows_dirty * MAX_VERTEX_TEXTURE_WIDTH, ); @@ -1259,8 +1293,8 @@ impl GpuCacheTexture { device.bind_program(program); device.bind_custom_vao(vao); device.bind_draw_target( - Some((&self.texture, 0)), - Some(self.texture.get_dimensions()), + Some((texture, 0)), + Some(texture.get_dimensions()), ); device.draw_nonindexed_points(0, count as _); 0 @@ -2666,7 +2700,7 @@ impl Renderer { // so we need to bind it here. self.device.bind_texture( TextureSampler::GpuCache, - &self.gpu_cache_texture.texture, + self.gpu_cache_texture.texture.as_ref().unwrap(), ); } @@ -4180,10 +4214,11 @@ impl Renderer { } pub fn read_gpu_cache(&mut self) -> (DeviceUintSize, Vec) { - let size = self.gpu_cache_texture.texture.get_dimensions(); + let texture = self.gpu_cache_texture.texture.as_ref().unwrap(); + let size = texture.get_dimensions(); let mut texels = vec![0; (size.width * size.height * 16) as usize]; self.device.begin_frame(); - self.device.bind_read_target(Some((&self.gpu_cache_texture.texture, 0))); + self.device.bind_read_target(Some((texture, 0))); self.device.read_pixels_into( DeviceUintRect::new(DeviceUintPoint::zero(), size), ReadPixelsFormat::Standard(ImageFormat::RGBAF32), @@ -4249,7 +4284,8 @@ impl Renderer { } // GPU cache GPU memory. - report.gpu_cache_textures += self.gpu_cache_texture.texture.size_in_bytes(); + report.gpu_cache_textures += + self.gpu_cache_texture.texture.as_ref().map_or(0, |t| t.size_in_bytes()); // Render task CPU memory. for (_id, doc) in &self.active_documents { @@ -4760,7 +4796,7 @@ impl Renderer { self.update_gpu_cache(); // flush pending updates let mut plain_self = PlainRenderer { gpu_cache: Self::save_texture( - &self.gpu_cache_texture.texture, + &self.gpu_cache_texture.texture.as_ref().unwrap(), "gpu", &config.root, &mut self.device, ), gpu_cache_frame_id: self.gpu_cache_frame_id, @@ -4834,15 +4870,20 @@ impl Renderer { } info!("loading gpu cache"); + if let Some(t) = self.gpu_cache_texture.texture.take() { + self.device.delete_texture(t); + } + self.gpu_cache_texture.texture = + Some(self.device.create_texture(TextureTarget::Default, ImageFormat::RGBAF32)); let gpu_cache_data = Self::load_texture( - &mut self.gpu_cache_texture.texture, + self.gpu_cache_texture.texture.as_mut().unwrap(), &renderer.gpu_cache, &root, &mut self.device, ); match self.gpu_cache_texture.bus { GpuCacheBus::PixelBuffer { ref mut rows, ref mut cpu_blocks, .. } => { - let dim = self.gpu_cache_texture.texture.get_dimensions(); + let dim = self.gpu_cache_texture.texture.as_ref().unwrap().get_dimensions(); let blocks = unsafe { slice::from_raw_parts( gpu_cache_data.as_ptr() as *const GpuBlockData, From a31337ad392688b06efed416b7bc3dc1efccffc9 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 8 Oct 2018 16:54:28 -0700 Subject: [PATCH 3/7] Stop reinitializing VertexDataTextures with different dimensions. Differential Revision: https://phabricator.services.mozilla.com/D8163 --- webrender/src/renderer.rs | 57 ++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index ee9faa9cf8..717b04afc9 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -1304,7 +1304,8 @@ impl GpuCacheTexture { } struct VertexDataTexture { - texture: Texture, + texture: Option, + format: ImageFormat, pbo: PBO, } @@ -1313,13 +1314,18 @@ impl VertexDataTexture { device: &mut Device, format: ImageFormat, ) -> VertexDataTexture { - let texture = device.create_texture( - TextureTarget::Default, - format, - ); let pbo = device.create_pbo(); + VertexDataTexture { texture: None, format, pbo } + } + + /// Returns a borrow of the GPU texture. Panics if it hasn't been initialized. + fn texture(&self) -> &Texture { + self.texture.as_ref().unwrap() + } - VertexDataTexture { texture, pbo } + /// Returns an estimate of the GPU memory consumed by this VertexDataTexture. + fn size_in_bytes(&self) -> usize { + self.texture.as_ref().map_or(0, |t| t.size_in_bytes()) } fn update(&mut self, device: &mut Device, data: &mut Vec) { @@ -1343,15 +1349,19 @@ impl VertexDataTexture { let width = (MAX_VERTEX_TEXTURE_WIDTH - (MAX_VERTEX_TEXTURE_WIDTH % texels_per_item)) as u32; let needed_height = (data.len() / items_per_row) as u32; + let existing_height = self.texture.as_ref().map_or(0, |t| t.get_dimensions().height); - // Determine if the texture needs to be resized. - let texture_size = self.texture.get_dimensions(); - - if needed_height > texture_size.height { + // Create a new texture if needed. + if needed_height > existing_height { + // Drop the existing texture, if any. + if let Some(t) = self.texture.take() { + device.delete_texture(t); + } let new_height = (needed_height + 127) & !127; + let mut texture = device.create_texture(TextureTarget::Default, self.format); device.init_texture::( - &mut self.texture, + &mut texture, width, new_height, TextureFilter::Nearest, @@ -1359,6 +1369,7 @@ impl VertexDataTexture { 1, None, ); + self.texture = Some(texture); } let rect = DeviceUintRect::new( @@ -1366,13 +1377,15 @@ impl VertexDataTexture { DeviceUintSize::new(width, needed_height), ); device - .upload_texture(&self.texture, &self.pbo, 0) + .upload_texture(self.texture(), &self.pbo, 0) .upload(rect, 0, None, data); } - fn deinit(self, device: &mut Device) { + fn deinit(mut self, device: &mut Device) { device.delete_pbo(self.pbo); - device.delete_texture(self.texture); + if let Some(t) = self.texture.take() { + device.delete_texture(t); + } } } @@ -3791,7 +3804,7 @@ impl Renderer { ); self.device.bind_texture( TextureSampler::PrimitiveHeadersF, - &self.prim_header_f_texture.texture, + &self.prim_header_f_texture.texture(), ); self.prim_header_i_texture.update( @@ -3800,7 +3813,7 @@ impl Renderer { ); self.device.bind_texture( TextureSampler::PrimitiveHeadersI, - &self.prim_header_i_texture.texture, + &self.prim_header_i_texture.texture(), ); self.transforms_texture.update( @@ -3809,14 +3822,14 @@ impl Renderer { ); self.device.bind_texture( TextureSampler::TransformPalette, - &self.transforms_texture.texture, + &self.transforms_texture.texture(), ); self.render_task_texture .update(&mut self.device, &mut frame.render_tasks.task_data); self.device.bind_texture( TextureSampler::RenderTasks, - &self.render_task_texture.texture, + &self.render_task_texture.texture(), ); debug_assert!(self.texture_resolver.prev_pass_alpha.is_none()); @@ -4294,10 +4307,10 @@ impl Renderer { } // Vertex data GPU memory. - report.vertex_data_textures += self.prim_header_f_texture.texture.size_in_bytes(); - report.vertex_data_textures += self.prim_header_i_texture.texture.size_in_bytes(); - report.vertex_data_textures += self.transforms_texture.texture.size_in_bytes(); - report.vertex_data_textures += self.render_task_texture.texture.size_in_bytes(); + report.vertex_data_textures += self.prim_header_f_texture.size_in_bytes(); + report.vertex_data_textures += self.prim_header_i_texture.size_in_bytes(); + report.vertex_data_textures += self.transforms_texture.size_in_bytes(); + report.vertex_data_textures += self.render_task_texture.size_in_bytes(); // Texture cache and render target GPU memory. report += self.texture_resolver.report_memory(); From ad53a1b18c5c41ed6eff6dba22fb8593d1e3ce1e Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Tue, 9 Oct 2018 09:47:34 -0700 Subject: [PATCH 4/7] Stop reinitializing freed texture cache textures. We also drop the vec scheme and just use a hash-map. None of the operations here are hot enough that the difference between a HashMap and a Vec should matter, and using a HashMap simplifies things. Glenn says that hashmap lookups were once hot in profiles, but that was before FastHashMap and not specifically these lookups. Differential Revision: https://phabricator.services.mozilla.com/D8164 --- webrender/src/device/gl.rs | 2 +- webrender/src/internal_types.rs | 4 ++- webrender/src/renderer.rs | 59 ++++++++++++++------------------- webrender/src/texture_cache.rs | 58 +++++--------------------------- 4 files changed, 36 insertions(+), 87 deletions(-) diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index e3ff001121..7bd9c6f5ae 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -1506,7 +1506,7 @@ impl Device { } } - pub fn free_texture_storage(&mut self, texture: &mut Texture) { + fn free_texture_storage(&mut self, texture: &mut Texture) { debug_assert!(self.inside_frame); if texture.width + texture.height == 0 { diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index 03224f015f..176332cff4 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -31,10 +31,12 @@ pub type FastHashSet = HashSet>; /// cache (e.g. if an image is too large to be added to an atlas). The texture /// cache manages the allocation and freeing of these IDs, and the rendering /// thread maintains a map from cache texture ID to native texture. +/// +/// We never reuse IDs, so we use a u64 here to be safe. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct CacheTextureId(pub usize); +pub struct CacheTextureId(pub u64); /// Identifies a render pass target that is persisted until the end of the frame. /// diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 717b04afc9..5ce5e4e4d0 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -730,13 +730,8 @@ struct ActiveTexture { /// `RenderBackend` (which does not directly interface with the GPU) and actual /// device texture handles. struct TextureResolver { - /// A vector for fast resolves of texture cache IDs to native texture IDs. - /// This maps to a free-list managed by the backend thread / texture cache. - /// We free the texture memory associated with a TextureId when its texture - /// cache ID is freed by the texture cache, but reuse the TextureId when the - /// texture caches's free list reuses the texture cache ID. This saves - /// having to use a hashmap, and allows a flat vector for performance. - texture_cache_map: Vec, + /// A map to resolve texture cache IDs to native textures. + texture_cache_map: FastHashMap, /// Map of external image IDs to native textures. external_images: FastHashMap<(ExternalImageId, u8), ExternalTexture>, @@ -788,7 +783,7 @@ impl TextureResolver { ); TextureResolver { - texture_cache_map: Vec::new(), + texture_cache_map: FastHashMap::default(), external_images: FastHashMap::default(), dummy_cache_texture, prev_pass_alpha: None, @@ -801,7 +796,7 @@ impl TextureResolver { fn deinit(self, device: &mut Device) { device.delete_texture(self.dummy_cache_texture); - for texture in self.texture_cache_map { + for (_id, texture) in self.texture_cache_map { device.delete_texture(texture); } @@ -905,7 +900,7 @@ impl TextureResolver { device.bind_external_texture(sampler, texture); } TextureSource::TextureCache(index) => { - let texture = &self.texture_cache_map[index.0]; + let texture = &self.texture_cache_map[&index]; device.bind_texture(sampler, texture); } TextureSource::RenderTaskCache(saved_index) => { @@ -937,7 +932,7 @@ impl TextureResolver { panic!("BUG: External textures cannot be resolved, they can only be bound."); } TextureSource::TextureCache(index) => { - Some(&self.texture_cache_map[index.0]) + Some(&self.texture_cache_map[&index]) } TextureSource::RenderTaskCache(saved_index) => { Some(&self.saved_targets[saved_index.0]) @@ -950,7 +945,7 @@ impl TextureResolver { // We're reporting GPU memory rather than heap-allocations, so we don't // use size_of_op. - for t in self.texture_cache_map.iter() { + for t in self.texture_cache_map.values() { report.texture_cache_textures += t.size_in_bytes(); } for t in self.render_target_pool.iter() { @@ -2732,20 +2727,13 @@ impl Renderer { filter, render_target, } => { - let CacheTextureId(cache_texture_index) = update.id; - if self.texture_resolver.texture_cache_map.len() == cache_texture_index { - // Create a new native texture, as requested by the texture cache. - let texture = self.device.create_texture(TextureTarget::Array, format); - self.texture_resolver.texture_cache_map.push(texture); - } - let texture = - &mut self.texture_resolver.texture_cache_map[cache_texture_index]; - assert_eq!(texture.get_format(), format); - + // Create a new native texture, as requested by the texture cache. + // // Ensure no PBO is bound when creating the texture storage, // or GL will attempt to read data from there. + let mut texture = self.device.create_texture(TextureTarget::Array, format); self.device.init_texture::( - texture, + &mut texture, width, height, filter, @@ -2753,6 +2741,7 @@ impl Renderer { layer_count, None, ); + self.texture_resolver.texture_cache_map.insert(update.id, texture); } TextureUpdateOp::Update { rect, @@ -2761,7 +2750,7 @@ impl Renderer { layer_index, offset, } => { - let texture = &self.texture_resolver.texture_cache_map[update.id.0]; + let texture = &self.texture_resolver.texture_cache_map[&update.id]; let mut uploader = self.device.upload_texture( texture, &self.texture_cache_upload_pbo, @@ -2809,8 +2798,8 @@ impl Renderer { self.profile_counters.texture_data_uploaded.add(bytes_uploaded >> 10); } TextureUpdateOp::Free => { - let texture = &mut self.texture_resolver.texture_cache_map[update.id.0]; - self.device.free_texture_storage(texture); + let texture = self.texture_resolver.texture_cache_map.remove(&update.id).unwrap(); + self.device.delete_texture(texture); } } } @@ -4099,7 +4088,7 @@ impl Renderer { let fb_width = framebuffer_size.width as i32; let num_layers: i32 = self.texture_resolver .texture_cache_map - .iter() + .values() .map(|texture| texture.get_layer_count()) .sum(); @@ -4110,7 +4099,7 @@ impl Renderer { } let mut i = 0; - for texture in &self.texture_resolver.texture_cache_map { + for texture in self.texture_resolver.texture_cache_map.values() { let y = spacing + if self.debug_flags.contains(DebugFlags::RENDER_TARGET_DBG) { 528 } else { @@ -4592,7 +4581,7 @@ struct PlainTexture { struct PlainRenderer { gpu_cache: PlainTexture, gpu_cache_frame_id: FrameId, - textures: Vec, + textures: FastHashMap, external_images: Vec } @@ -4813,16 +4802,16 @@ impl Renderer { "gpu", &config.root, &mut self.device, ), gpu_cache_frame_id: self.gpu_cache_frame_id, - textures: Vec::new(), + textures: FastHashMap::default(), external_images: deferred_images, }; info!("saving cached textures"); - for texture in &self.texture_resolver.texture_cache_map { + for (id, texture) in &self.texture_resolver.texture_cache_map { let file_name = format!("cache-{}", plain_self.textures.len() + 1); info!("\t{}", file_name); let plain = Self::save_texture(texture, &file_name, &config.root, &mut self.device); - plain_self.textures.push(plain); + plain_self.textures.insert(*id, plain); } config.serialize(&plain_self, "renderer"); @@ -4872,14 +4861,14 @@ impl Renderer { info!("loading cached textures"); self.device.begin_frame(); - for texture in self.texture_resolver.texture_cache_map.drain(..) { + for (_id, texture) in self.texture_resolver.texture_cache_map.drain() { self.device.delete_texture(texture); } - for texture in renderer.textures { + for (id, texture) in renderer.textures { info!("\t{}", texture.data); let mut t = self.device.create_texture(TextureTarget::Array, texture.format); Self::load_texture(&mut t, &texture, &root, &mut self.device); - self.texture_resolver.texture_cache_map.push(t); + self.texture_resolver.texture_cache_map.insert(id, t); } info!("loading gpu cache"); diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index fb80c468c8..fae9e5e274 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -9,7 +9,7 @@ use device::TextureFilter; use freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle}; use gpu_cache::{GpuCache, GpuCacheHandle}; use gpu_types::{ImageSource, UvRectKind}; -use internal_types::{CacheTextureId, FastHashMap, TextureUpdateList, TextureUpdateSource}; +use internal_types::{CacheTextureId, TextureUpdateList, TextureUpdateSource}; use internal_types::{RenderTargetInfo, TextureSource, TextureUpdate, TextureUpdateOp}; use profiler::{ResourceProfileCounter, TextureCacheProfileCounters}; use render_backend::FrameId; @@ -30,42 +30,6 @@ const TEXTURE_LAYER_DIMENSIONS: u32 = 2048; // The size of each region (page) in a texture layer. const TEXTURE_REGION_DIMENSIONS: u32 = 512; -// Maintains a simple freelist of texture IDs that are mapped -// to real API-specific texture IDs in the renderer. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -struct CacheTextureIdList { - free_lists: FastHashMap>, - next_id: usize, -} - -impl CacheTextureIdList { - fn new() -> Self { - CacheTextureIdList { - next_id: 0, - free_lists: FastHashMap::default(), - } - } - - fn allocate(&mut self, format: ImageFormat) -> CacheTextureId { - // If nothing on the free list of texture IDs, - // allocate a new one. - self.free_lists.get_mut(&format) - .and_then(|fl| fl.pop()) - .unwrap_or_else(|| { - self.next_id += 1; - CacheTextureId(self.next_id - 1) - }) - } - - fn free(&mut self, id: CacheTextureId, format: ImageFormat) { - self.free_lists - .entry(format) - .or_insert(Vec::new()) - .push(id); - } -} - // Items in the texture cache can either be standalone textures, // or a sub-rect inside the shared cache. #[derive(Debug)] @@ -244,11 +208,8 @@ pub struct TextureCache { // Maximum texture size supported by hardware. max_texture_size: u32, - // A list of texture IDs that represent native - // texture handles. This indirection allows the texture - // cache to create / destroy / reuse texture handles - // without knowing anything about the device code. - cache_textures: CacheTextureIdList, + // The next unused virtual texture ID. Monotonically increasing. + next_id: CacheTextureId, // A list of updates that need to be applied to the // texture cache in the rendering thread this frame. @@ -297,7 +258,7 @@ impl TextureCache { TextureFilter::Nearest, TEXTURE_ARRAY_LAYERS_NEAREST, ), - cache_textures: CacheTextureIdList::new(), + next_id: CacheTextureId(1), pending_updates: TextureUpdateList::new(), frame_id: FrameId(0), entries: FreeList::new(), @@ -336,7 +297,6 @@ impl TextureCache { id: texture_id, op: TextureUpdateOp::Free, }); - self.cache_textures.free(texture_id, self.array_a8_linear.format); } if let Some(texture_id) = self.array_a16_linear.clear() { @@ -344,7 +304,6 @@ impl TextureCache { id: texture_id, op: TextureUpdateOp::Free, }); - self.cache_textures.free(texture_id, self.array_a16_linear.format); } if let Some(texture_id) = self.array_rgba8_linear.clear() { @@ -352,7 +311,6 @@ impl TextureCache { id: texture_id, op: TextureUpdateOp::Free, }); - self.cache_textures.free(texture_id, self.array_rgba8_linear.format); } if let Some(texture_id) = self.array_rgba8_nearest.clear() { @@ -360,7 +318,6 @@ impl TextureCache { id: texture_id, op: TextureUpdateOp::Free, }); - self.cache_textures.free(texture_id, self.array_rgba8_nearest.format); } } @@ -736,7 +693,6 @@ impl TextureCache { id: entry.texture_id, op: TextureUpdateOp::Free, }); - self.cache_textures.free(entry.texture_id, entry.format); None } EntryKind::Cache { @@ -782,7 +738,8 @@ impl TextureCache { // Lazy initialize this texture array if required. if texture_array.texture_id.is_none() { - let texture_id = self.cache_textures.allocate(descriptor.format); + let texture_id = self.next_id; + self.next_id.0 += 1; let update_op = TextureUpdate { id: texture_id, @@ -895,7 +852,8 @@ impl TextureCache { // will just have to be in a unique texture. This hurts batching but should // only occur on a small number of images (or pathological test cases!). if new_cache_entry.is_none() { - let texture_id = self.cache_textures.allocate(descriptor.format); + let texture_id = self.next_id; + self.next_id.0 += 1; // Create an update operation to allocate device storage // of the right size / format. From 7fa14aed08db63e15569b47a57a910f8a62222be Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 8 Oct 2018 17:19:47 -0700 Subject: [PATCH 5/7] Combine texture creation and initialization into the same API. Differential Revision: https://phabricator.services.mozilla.com/D8165 --- webrender/src/debug_render.rs | 6 +- webrender/src/device/gl.rs | 16 +++-- webrender/src/gpu_glyph_renderer.rs | 63 +++++++++-------- webrender/src/renderer.rs | 104 ++++++++++++++++------------ 4 files changed, 110 insertions(+), 79 deletions(-) diff --git a/webrender/src/debug_render.rs b/webrender/src/debug_render.rs index 86d2ff5648..b3bd6520d9 100644 --- a/webrender/src/debug_render.rs +++ b/webrender/src/debug_render.rs @@ -123,9 +123,9 @@ impl DebugRenderer { let line_vao = device.create_vao(&DESC_COLOR); let tri_vao = device.create_vao(&DESC_COLOR); - let mut font_texture = device.create_texture(TextureTarget::Array, ImageFormat::R8); - device.init_texture( - &mut font_texture, + let font_texture = device.create_texture::( + TextureTarget::Array, + ImageFormat::R8, debug_font_data::BMP_WIDTH, debug_font_data::BMP_HEIGHT, TextureFilter::Linear, diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index 7bd9c6f5ae..dd04a083f0 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -1182,12 +1182,18 @@ impl Device { } } - pub fn create_texture( + pub fn create_texture( &mut self, target: TextureTarget, format: ImageFormat, + width: u32, + height: u32, + filter: TextureFilter, + render_target: Option, + layer_count: i32, + pixels: Option<&[T]>, ) -> Texture { - Texture { + let mut t = Texture { id: self.gl.gen_textures(1)[0], target: get_gl_target(target), width: 0, @@ -1199,7 +1205,9 @@ impl Device { fbo_ids: vec![], depth_rb: None, last_frame_used: self.frame_id, - } + }; + self.init_texture(&mut t, width, height, filter, render_target, layer_count, pixels); + t } fn set_texture_parameters(&mut self, target: gl::GLuint, filter: TextureFilter) { @@ -1244,7 +1252,7 @@ impl Device { self.bind_read_target(None); } - pub fn init_texture( + fn init_texture( &mut self, texture: &mut Texture, mut width: u32, diff --git a/webrender/src/gpu_glyph_renderer.rs b/webrender/src/gpu_glyph_renderer.rs index dfd4c7e20e..bdfa6d719d 100644 --- a/webrender/src/gpu_glyph_renderer.rs +++ b/webrender/src/gpu_glyph_renderer.rs @@ -54,14 +54,16 @@ impl GpuGlyphRenderer { let area_lut_pixels = &AREA_LUT_TGA_BYTES[18..(18 + area_lut_width * area_lut_height) as usize]; - let mut area_lut_texture = device.create_texture(TextureTarget::Default, ImageFormat::R8); - device.init_texture(&mut area_lut_texture, - area_lut_width, - area_lut_height, - TextureFilter::Linear, - None, - 1, - Some(area_lut_pixels)); + let area_lut_texture = device.create_texture( + TextureTarget::Default, + ImageFormat::R8, + area_lut_width, + area_lut_height, + TextureFilter::Linear, + None, + 1, + Some(area_lut_pixels) + ); let vector_stencil_vao = device.create_vao_with_new_instances(&renderer::desc::VECTOR_STENCIL, prim_vao); @@ -108,22 +110,26 @@ impl Renderer { let _timer = self.gpu_profile.start_timer(GPU_TAG_GLYPH_STENCIL); + let texture = self.device.create_texture::( + TextureTarget::Default, + ImageFormat::RGBAF32, + target_size.width, + target_size.height, + TextureFilter::Nearest, + Some(RenderTargetInfo { + has_depth: false, + }), + 1, + None + ); + // Initialize temporary framebuffer. // FIXME(pcwalton): Cache this! // FIXME(pcwalton): Use RF32, not RGBAF32! let mut current_page = StenciledGlyphPage { - texture: self.device.create_texture(TextureTarget::Default, ImageFormat::RGBAF32), + texture, glyphs: vec![], }; - self.device.init_texture::(&mut current_page.texture, - target_size.width, - target_size.height, - TextureFilter::Nearest, - Some(RenderTargetInfo { - has_depth: false, - }), - 1, - None); // Allocate all target rects. let mut packer = ShelfBinPacker::new(&target_size.to_i32().to_untyped(), @@ -150,9 +156,6 @@ impl Renderer { } // Initialize path info. - // TODO(pcwalton): Cache this texture! - let mut path_info_texture = self.device.create_texture(TextureTarget::Default, - ImageFormat::RGBAF32); let mut path_info_texels = Vec::with_capacity(glyphs.len() * 12); for (stenciled_glyph_index, &glyph_index) in glyph_indices.iter().enumerate() { @@ -176,13 +179,17 @@ impl Renderer { ]); } - self.device.init_texture(&mut path_info_texture, - 3, - glyphs.len() as u32, - TextureFilter::Nearest, - None, - 1, - Some(&path_info_texels)); + // TODO(pcwalton): Cache this texture! + let path_info_texture = self.device.create_texture( + TextureTarget::Default, + ImageFormat::RGBAF32, + 3, + glyphs.len() as u32, + TextureFilter::Nearest, + None, + 1, + Some(&path_info_texels) + ); self.gpu_glyph_renderer.vector_stencil.bind(&mut self.device, projection, diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 5ce5e4e4d0..5e8cc6fa50 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -770,17 +770,17 @@ struct TextureResolver { impl TextureResolver { fn new(device: &mut Device) -> TextureResolver { - let mut dummy_cache_texture = device - .create_texture(TextureTarget::Array, ImageFormat::BGRA8); - device.init_texture::( - &mut dummy_cache_texture, - 1, - 1, - TextureFilter::Linear, - None, - 1, - None, - ); + let dummy_cache_texture = device + .create_texture::( + TextureTarget::Array, + ImageFormat::BGRA8, + 1, + 1, + TextureFilter::Linear, + None, + 1, + None, + ); TextureResolver { texture_cache_map: FastHashMap::default(), @@ -1061,9 +1061,9 @@ impl GpuCacheTexture { } // Create the new texture. - let mut texture = device.create_texture(TextureTarget::Default, ImageFormat::RGBAF32); - device.init_texture::( - &mut texture, + let mut texture = device.create_texture::( + TextureTarget::Default, + ImageFormat::RGBAF32, new_size.width, new_size.height, TextureFilter::Nearest, @@ -1354,9 +1354,9 @@ impl VertexDataTexture { } let new_height = (needed_height + 127) & !127; - let mut texture = device.create_texture(TextureTarget::Default, self.format); - device.init_texture::( - &mut texture, + let texture = device.create_texture::( + TextureTarget::Default, + self.format, width, new_height, TextureFilter::Nearest, @@ -1709,10 +1709,9 @@ impl Renderer { 21, ]; - let mut texture = device - .create_texture(TextureTarget::Default, ImageFormat::R8); - device.init_texture( - &mut texture, + let mut texture = device.create_texture::( + TextureTarget::Default, + ImageFormat::R8, 8, 8, TextureFilter::Nearest, @@ -2731,9 +2730,9 @@ impl Renderer { // // Ensure no PBO is bound when creating the texture storage, // or GL will attempt to read data from there. - let mut texture = self.device.create_texture(TextureTarget::Array, format); - self.device.init_texture::( - &mut texture, + let texture = self.device.create_texture::( + TextureTarget::Array, + format, width, height, filter, @@ -3763,9 +3762,9 @@ impl Renderer { t } else { counters.targets_created.inc(); - let mut t = self.device.create_texture(TextureTarget::Array, list.format); - self.device.init_texture::( - &mut t, + let mut t = self.device.create_texture::( + TextureTarget::Array, + list.format, list.max_size.width, list.max_size.height, TextureFilter::Linear, @@ -4686,24 +4685,34 @@ impl Renderer { } #[cfg(feature = "replay")] - fn load_texture(texture: &mut Texture, plain: &PlainTexture, root: &PathBuf, device: &mut Device) -> Vec { + fn load_texture( + target: TextureTarget, + plain: &PlainTexture, + root: &PathBuf, + device: &mut Device + ) -> (Texture, Vec) + { use std::fs::File; use std::io::Read; let mut texels = Vec::new(); - assert_eq!(plain.format, texture.get_format()); File::open(root.join(&plain.data)) .expect(&format!("Unable to open texture at {}", plain.data)) .read_to_end(&mut texels) .unwrap(); - device.init_texture( - texture, plain.size.0, plain.size.1, - plain.filter, plain.render_target, - plain.size.2, Some(texels.as_slice()), + let texture = device.create_texture( + target, + plain.format, + plain.size.0, + plain.size.1, + plain.filter, + plain.render_target, + plain.size.2, + Some(texels.as_slice()), ); - texels + (texture, texels) } #[cfg(feature = "capture")] @@ -4866,23 +4875,26 @@ impl Renderer { } for (id, texture) in renderer.textures { info!("\t{}", texture.data); - let mut t = self.device.create_texture(TextureTarget::Array, texture.format); - Self::load_texture(&mut t, &texture, &root, &mut self.device); - self.texture_resolver.texture_cache_map.insert(id, t); + let t = Self::load_texture( + TextureTarget::Array, + &texture, + &root, + &mut self.device + ); + self.texture_resolver.texture_cache_map.insert(id, t.0); } info!("loading gpu cache"); if let Some(t) = self.gpu_cache_texture.texture.take() { self.device.delete_texture(t); } - self.gpu_cache_texture.texture = - Some(self.device.create_texture(TextureTarget::Default, ImageFormat::RGBAF32)); - let gpu_cache_data = Self::load_texture( - self.gpu_cache_texture.texture.as_mut().unwrap(), + let (t, gpu_cache_data) = Self::load_texture( + TextureTarget::Default, &renderer.gpu_cache, &root, &mut self.device, ); + self.gpu_cache_texture.texture = Some(t); match self.gpu_cache_texture.bus { GpuCacheBus::PixelBuffer { ref mut rows, ref mut cpu_blocks, .. } => { let dim = self.gpu_cache_texture.texture.as_ref().unwrap().get_dimensions(); @@ -4925,9 +4937,13 @@ impl Renderer { filter, render_target: None, }; - let mut t = self.device.create_texture(target, plain_tex.format); - Self::load_texture(&mut t, &plain_tex, &root, &mut self.device); - let extex = t.into_external(); + let t = Self::load_texture( + target, + &plain_tex, + &root, + &mut self.device + ); + let extex = t.0.into_external(); self.owned_external_images.insert(key, extex.clone()); e.insert(extex.internal_id()).clone() } From 4e58b70df9468c3bfb24fae364da5def111311c5 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Tue, 9 Oct 2018 13:21:40 -0700 Subject: [PATCH 6/7] Simplify device texture management. Things get simpler when reinitialization goes away. Differential Revision: https://phabricator.services.mozilla.com/D8166 --- webrender/src/device/gl.rs | 312 ++++++++++++++----------------------- 1 file changed, 117 insertions(+), 195 deletions(-) diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index dd04a083f0..4c39d79460 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -1186,28 +1186,78 @@ impl Device { &mut self, target: TextureTarget, format: ImageFormat, - width: u32, - height: u32, + mut width: u32, + mut height: u32, filter: TextureFilter, render_target: Option, layer_count: i32, pixels: Option<&[T]>, ) -> Texture { - let mut t = Texture { + debug_assert!(self.inside_frame); + + if width > self.max_texture_size || height > self.max_texture_size { + error!("Attempting to allocate a texture of size {}x{} above the limit, trimming", width, height); + width = width.min(self.max_texture_size); + height = height.min(self.max_texture_size); + } + + // Set up the texture book-keeping. + let mut texture = Texture { id: self.gl.gen_textures(1)[0], target: get_gl_target(target), - width: 0, - height: 0, - layer_count: 0, + width, + height, + layer_count, format, - filter: TextureFilter::Nearest, - render_target: None, + filter, + render_target, fbo_ids: vec![], depth_rb: None, last_frame_used: self.frame_id, }; - self.init_texture(&mut t, width, height, filter, render_target, layer_count, pixels); - t + self.bind_texture(DEFAULT_TEXTURE, &texture); + self.set_texture_parameters(texture.target, filter); + + // Allocate storage. + let desc = self.gl_describe_format(texture.format); + match texture.target { + gl::TEXTURE_2D_ARRAY => { + self.gl.tex_image_3d( + gl::TEXTURE_2D_ARRAY, + 0, + desc.internal, + texture.width as _, + texture.height as _, + texture.layer_count, + 0, + desc.external, + desc.pixel_type, + pixels.map(texels_to_u8_slice), + ) + } + gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => { + assert_eq!(texture.layer_count, 1); + self.gl.tex_image_2d( + texture.target, + 0, + desc.internal, + texture.width as _, + texture.height as _, + 0, + desc.external, + desc.pixel_type, + pixels.map(texels_to_u8_slice), + ) + }, + _ => panic!("BUG: Unexpected texture target!"), + } + + // Set up FBOs, if required. + if let Some(rt_info) = render_target { + self.init_fbos(&mut texture, rt_info); + } + + texture } fn set_texture_parameters(&mut self, target: gl::GLuint, filter: TextureFilter) { @@ -1252,46 +1302,6 @@ impl Device { self.bind_read_target(None); } - fn init_texture( - &mut self, - texture: &mut Texture, - mut width: u32, - mut height: u32, - filter: TextureFilter, - render_target: Option, - layer_count: i32, - pixels: Option<&[T]>, - ) { - debug_assert!(self.inside_frame); - - if width > self.max_texture_size || height > self.max_texture_size { - error!("Attempting to allocate a texture of size {}x{} above the limit, trimming", width, height); - width = width.min(self.max_texture_size); - height = height.min(self.max_texture_size); - } - - let is_resized = texture.width != width || texture.height != height; - - texture.width = width; - texture.height = height; - texture.filter = filter; - texture.layer_count = layer_count; - texture.render_target = render_target; - texture.last_frame_used = self.frame_id; - - self.bind_texture(DEFAULT_TEXTURE, texture); - self.set_texture_parameters(texture.target, filter); - - match render_target { - Some(info) => { - self.update_target_storage(texture, &info, is_resized, pixels); - } - None => { - self.update_texture_storage(texture, pixels); - } - } - } - /// Notifies the device that a render target is about to be reused. /// /// This method adds or removes a depth target as necessary. @@ -1302,166 +1312,89 @@ impl Device { ) { texture.last_frame_used = self.frame_id; texture.render_target = Some(rt_info); + + // If the depth target requirements changed, just drop the FBOs and + // reinitialize. + // + // FIXME(bholley): I have a patch to do this better. if rt_info.has_depth != texture.has_depth() { - self.update_target_storage::(texture, &rt_info, /* is_resized = */ false, None); + self.deinit_fbos(texture); + self.init_fbos(texture, rt_info); } } - /// Updates the render target storage for the texture, creating FBOs as required. - fn update_target_storage( - &mut self, - texture: &mut Texture, - rt_info: &RenderTargetInfo, - is_resized: bool, - pixels: Option<&[T]>, - ) { - assert!(texture.layer_count > 0 || texture.width + texture.height == 0); + fn init_fbos(&mut self, texture: &mut Texture, rt_info: RenderTargetInfo) { + // Generate the FBOs. + assert!(texture.fbo_ids.is_empty()); + texture.fbo_ids.extend( + self.gl.gen_framebuffers(texture.layer_count).into_iter().map(FBOId) + ); - let needed_layer_count = texture.layer_count - texture.fbo_ids.len() as i32; - let allocate_color = needed_layer_count != 0 || is_resized || pixels.is_some(); + // Optionally generate a depth target. + if rt_info.has_depth { + let renderbuffer_ids = self.gl.gen_renderbuffers(1); + let depth_rb = renderbuffer_ids[0]; + texture.depth_rb = Some(RBOId(depth_rb)); + self.gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb); + self.gl.renderbuffer_storage( + gl::RENDERBUFFER, + gl::DEPTH_COMPONENT24, + texture.width as _, + texture.height as _, + ); + } - if allocate_color { - let desc = self.gl_describe_format(texture.format); + // Bind the FBOs. + let original_bound_fbo = self.bound_draw_fbo; + for (fbo_index, &fbo_id) in texture.fbo_ids.iter().enumerate() { + self.bind_external_draw_target(fbo_id); match texture.target { gl::TEXTURE_2D_ARRAY => { - self.gl.tex_image_3d( - texture.target, - 0, - desc.internal, - texture.width as _, - texture.height as _, - texture.layer_count, + self.gl.framebuffer_texture_layer( + gl::DRAW_FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + texture.id, 0, - desc.external, - desc.pixel_type, - pixels.map(texels_to_u8_slice), + fbo_index as _, ) } _ => { - assert_eq!(texture.layer_count, 1); - self.gl.tex_image_2d( + assert_eq!(fbo_index, 0); + self.gl.framebuffer_texture_2d( + gl::DRAW_FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, texture.target, + texture.id, 0, - desc.internal, - texture.width as _, - texture.height as _, - 0, - desc.external, - desc.pixel_type, - pixels.map(texels_to_u8_slice), ) } } - } - - if needed_layer_count > 0 { - // Create more framebuffers to fill the gap - let new_fbos = self.gl.gen_framebuffers(needed_layer_count); - texture - .fbo_ids - .extend(new_fbos.into_iter().map(FBOId)); - } else if needed_layer_count < 0 { - // Remove extra framebuffers - for old in texture.fbo_ids.drain(texture.layer_count as usize ..) { - self.gl.delete_framebuffers(&[old.0]); - } - } - - let (mut depth_rb, allocate_depth) = match texture.depth_rb { - Some(rbo) => (rbo.0, is_resized || !rt_info.has_depth), - None if rt_info.has_depth => { - let renderbuffer_ids = self.gl.gen_renderbuffers(1); - let depth_rb = renderbuffer_ids[0]; - texture.depth_rb = Some(RBOId(depth_rb)); - (depth_rb, true) - }, - None => (0, false), - }; - - if allocate_depth { - if rt_info.has_depth { - self.gl.bind_renderbuffer(gl::RENDERBUFFER, depth_rb); - self.gl.renderbuffer_storage( - gl::RENDERBUFFER, - gl::DEPTH_COMPONENT24, - texture.width as _, - texture.height as _, - ); - } else { - self.gl.delete_renderbuffers(&[depth_rb]); - depth_rb = 0; - texture.depth_rb = None; - } - } - - if allocate_color || allocate_depth { - let original_bound_fbo = self.bound_draw_fbo; - for (fbo_index, &fbo_id) in texture.fbo_ids.iter().enumerate() { - self.bind_external_draw_target(fbo_id); - match texture.target { - gl::TEXTURE_2D_ARRAY => { - self.gl.framebuffer_texture_layer( - gl::DRAW_FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - texture.id, - 0, - fbo_index as _, - ) - } - _ => { - assert_eq!(fbo_index, 0); - self.gl.framebuffer_texture_2d( - gl::DRAW_FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - texture.target, - texture.id, - 0, - ) - } - } + if let Some(depth_rb) = texture.depth_rb { self.gl.framebuffer_renderbuffer( gl::DRAW_FRAMEBUFFER, gl::DEPTH_ATTACHMENT, gl::RENDERBUFFER, - depth_rb, + depth_rb.0, ); } - self.bind_external_draw_target(original_bound_fbo); } + self.bind_external_draw_target(original_bound_fbo); } - fn update_texture_storage(&mut self, texture: &Texture, pixels: Option<&[T]>) { - let desc = self.gl_describe_format(texture.format); - match texture.target { - gl::TEXTURE_2D_ARRAY => { - self.gl.tex_image_3d( - gl::TEXTURE_2D_ARRAY, - 0, - desc.internal, - texture.width as _, - texture.height as _, - texture.layer_count, - 0, - desc.external, - desc.pixel_type, - pixels.map(texels_to_u8_slice), - ); - } - gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => { - self.gl.tex_image_2d( - texture.target, - 0, - desc.internal, - texture.width as _, - texture.height as _, - 0, - desc.external, - desc.pixel_type, - pixels.map(texels_to_u8_slice), - ); - } - _ => panic!("BUG: Unexpected texture target!"), + fn deinit_fbos(&mut self, texture: &mut Texture) { + if let Some(RBOId(depth_rb)) = texture.depth_rb.take() { + self.gl.delete_renderbuffers(&[depth_rb]); + texture.depth_rb = None; + } + + if !texture.fbo_ids.is_empty() { + let fbo_ids: Vec<_> = texture + .fbo_ids + .drain(..) + .map(|FBOId(fbo_id)| fbo_id) + .collect(); + self.gl.delete_framebuffers(&fbo_ids[..]); } } @@ -1526,18 +1459,7 @@ impl Device { self.free_texture_storage_impl(texture.target, desc); - if let Some(RBOId(depth_rb)) = texture.depth_rb.take() { - self.gl.delete_renderbuffers(&[depth_rb]); - } - - if !texture.fbo_ids.is_empty() { - let fbo_ids: Vec<_> = texture - .fbo_ids - .drain(..) - .map(|FBOId(fbo_id)| fbo_id) - .collect(); - self.gl.delete_framebuffers(&fbo_ids[..]); - } + self.deinit_fbos(texture); texture.width = 0; texture.height = 0; From 788d251d4c7f5878d2f6ab4221cfcb921da3d20d Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Wed, 10 Oct 2018 16:24:39 -0700 Subject: [PATCH 7/7] Eliminate free_texture_storage. --- webrender/src/device/gl.rs | 62 ++------------------------------------ 1 file changed, 3 insertions(+), 59 deletions(-) diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index 4c39d79460..ddec23feec 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -1415,59 +1415,9 @@ impl Device { ); } - fn free_texture_storage_impl(&mut self, target: gl::GLenum, desc: FormatDesc) { - match target { - gl::TEXTURE_2D_ARRAY => { - self.gl.tex_image_3d( - gl::TEXTURE_2D_ARRAY, - 0, - desc.internal, - 0, - 0, - 0, - 0, - desc.external, - desc.pixel_type, - None, - ); - } - _ => { - self.gl.tex_image_2d( - target, - 0, - desc.internal, - 0, - 0, - 0, - desc.external, - desc.pixel_type, - None, - ); - } - } - } - - fn free_texture_storage(&mut self, texture: &mut Texture) { - debug_assert!(self.inside_frame); - - if texture.width + texture.height == 0 { - return; - } - - self.bind_texture(DEFAULT_TEXTURE, texture); - let desc = self.gl_describe_format(texture.format); - - self.free_texture_storage_impl(texture.target, desc); - - self.deinit_fbos(texture); - - texture.width = 0; - texture.height = 0; - texture.layer_count = 0; - } - pub fn delete_texture(&mut self, mut texture: Texture) { - self.free_texture_storage(&mut texture); + debug_assert!(self.inside_frame); + self.deinit_fbos(&mut texture); self.gl.delete_textures(&[texture.id]); for bound_texture in &mut self.bound_textures { @@ -1476,18 +1426,12 @@ impl Device { } } + // Disarm the assert in Texture::drop(). texture.id = 0; } #[cfg(feature = "replay")] pub fn delete_external_texture(&mut self, mut external: ExternalTexture) { - self.bind_external_texture(DEFAULT_TEXTURE, &external); - //Note: the format descriptor here doesn't really matter - self.free_texture_storage_impl(external.target, FormatDesc { - internal: gl::R8 as _, - external: gl::RED, - pixel_type: gl::UNSIGNED_BYTE, - }); self.gl.delete_textures(&[external.id]); external.id = 0; }