tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 07d49b9e042c62d7d5273b31b7decbf4a08e1733
parent 3910e404dc48a9ca593e3f1e098afed4c3c87260
Author: Nicolas Silva <nical@fastmail.com>
Date:   Wed, 19 Nov 2025 09:40:34 +0000

Bug 1996818 - Recycle the gpu buffer textures. r=gw

Differential Revision: https://phabricator.services.mozilla.com/D272344

Diffstat:
Mgfx/wr/webrender/src/frame_builder.rs | 4++--
Mgfx/wr/webrender/src/render_task_graph.rs | 4++--
Mgfx/wr/webrender/src/renderer/gpu_buffer.rs | 12++++--------
Mgfx/wr/webrender/src/renderer/init.rs | 4++++
Mgfx/wr/webrender/src/renderer/mod.rs | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mgfx/wr/webrender/src/texture_cache.rs | 2+-
6 files changed, 141 insertions(+), 62 deletions(-)

diff --git a/gfx/wr/webrender/src/frame_builder.rs b/gfx/wr/webrender/src/frame_builder.rs @@ -658,8 +658,8 @@ impl FrameBuilder { let mut frame_memory = FrameMemory::new(chunk_pool, stamp.frame_id()); // TODO(gw): Recycle backing vec buffers for gpu buffer builder between frames let mut gpu_buffer_builder = GpuBufferBuilder { - f32: GpuBufferBuilderF::new(&frame_memory), - i32: GpuBufferBuilderI::new(&frame_memory), + f32: GpuBufferBuilderF::new(&frame_memory, 8 * 1024), + i32: GpuBufferBuilderI::new(&frame_memory, 2 * 1024), }; profile.set(profiler::PRIMITIVES, scene.prim_instances.len()); diff --git a/gfx/wr/webrender/src/render_task_graph.rs b/gfx/wr/webrender/src/render_task_graph.rs @@ -1104,8 +1104,8 @@ impl RenderTaskGraphBuilder { let frame_memory = FrameMemory::fallback(); let mut gpu_buffers = GpuBufferBuilder { - f32: GpuBufferBuilderF::new(&frame_memory), - i32: GpuBufferBuilderI::new(&frame_memory), + f32: GpuBufferBuilderF::new(&frame_memory, 0), + i32: GpuBufferBuilderI::new(&frame_memory, 0), }; let g = self.end_frame(&mut rc, &mut gpu_buffers, &mut frame_memory.new_vec(), 2048, &frame_memory); g.print(); diff --git a/gfx/wr/webrender/src/renderer/gpu_buffer.rs b/gfx/wr/webrender/src/renderer/gpu_buffer.rs @@ -3,12 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* - TODO: - Recycle GpuBuffers in a pool (support return from render thread) Efficiently allow writing to buffer (better push interface) - Support other texel types (e.g. i32) - */ use std::i32; @@ -302,9 +298,9 @@ pub struct GpuBufferBuilderImpl<T> { } impl<T> GpuBufferBuilderImpl<T> where T: Texel + std::convert::From<DeviceIntRect> { - pub fn new(memory: &FrameMemory) -> Self { + pub fn new(memory: &FrameMemory, capacity: usize) -> Self { GpuBufferBuilderImpl { - data: memory.new_vec(), + data: memory.new_vec_with_capacity(capacity), deferred: Vec::new(), } } @@ -435,7 +431,7 @@ impl<T> GpuBuffer<T> { fn test_gpu_buffer_sizing_push() { let frame_memory = FrameMemory::fallback(); let render_task_graph = RenderTaskGraph::new_for_testing(); - let mut builder = GpuBufferBuilderF::new(&frame_memory); + let mut builder = GpuBufferBuilderF::new(&frame_memory, 0); let row = vec![GpuBufferBlockF::EMPTY; MAX_VERTEX_TEXTURE_WIDTH]; builder.push(&row); @@ -451,7 +447,7 @@ fn test_gpu_buffer_sizing_push() { fn test_gpu_buffer_sizing_writer() { let frame_memory = FrameMemory::fallback(); let render_task_graph = RenderTaskGraph::new_for_testing(); - let mut builder = GpuBufferBuilderF::new(&frame_memory); + let mut builder = GpuBufferBuilderF::new(&frame_memory, 0); let mut writer = builder.write_blocks(MAX_VERTEX_TEXTURE_WIDTH); for _ in 0 .. MAX_VERTEX_TEXTURE_WIDTH { diff --git a/gfx/wr/webrender/src/renderer/init.rs b/gfx/wr/webrender/src/renderer/init.rs @@ -780,6 +780,10 @@ pub fn create_webrender_instance( last_time: 0, gpu_profiler, vaos, + gpu_buffer_texture_f: None, + gpu_buffer_texture_f_too_large: 0, + gpu_buffer_texture_i: None, + gpu_buffer_texture_i_too_large: 0, vertex_data_textures, current_vertex_data_textures: 0, pipeline_info: PipelineInfo::default(), diff --git a/gfx/wr/webrender/src/renderer/mod.rs b/gfx/wr/webrender/src/renderer/mod.rs @@ -847,6 +847,10 @@ pub struct Renderer { pub gpu_profiler: GpuProfiler, vaos: vertex::RendererVAOs, + gpu_buffer_texture_f: Option<Texture>, + gpu_buffer_texture_f_too_large: i32, + gpu_buffer_texture_i: Option<Texture>, + gpu_buffer_texture_i_too_large: i32, vertex_data_textures: Vec<vertex::VertexDataTextures>, current_vertex_data_textures: usize, @@ -1151,6 +1155,12 @@ impl Renderer { if memory_pressure { self.texture_upload_pbo_pool.on_memory_pressure(&mut self.device); self.staging_texture_pool.delete_textures(&mut self.device); + if let Some(texture) = self.gpu_buffer_texture_f.take() { + self.device.delete_texture(texture); + } + if let Some(texture) = self.gpu_buffer_texture_i.take() { + self.device.delete_texture(texture); + } } self.device.end_frame(); @@ -5110,6 +5120,22 @@ impl Renderer { ); self.current_vertex_data_textures = (self.current_vertex_data_textures + 1) % VERTEX_DATA_TEXTURE_COUNT; + + if let Some(texture) = &self.gpu_buffer_texture_f { + self.device.bind_texture( + TextureSampler::GpuBufferF, + &texture, + Swizzle::default(), + ); + } + + if let Some(texture) = &self.gpu_buffer_texture_i { + self.device.bind_texture( + TextureSampler::GpuBufferI, + &texture, + Swizzle::default(), + ); + } } fn update_native_surfaces(&mut self) { @@ -5173,35 +5199,75 @@ impl Renderer { } } - fn create_gpu_buffer_texture<T: Texel>( - &mut self, + fn update_gpu_buffer_texture<T: Texel>( + device: &mut Device, buffer: &GpuBuffer<T>, - sampler: TextureSampler, - ) -> Option<Texture> { + dst_texture: &mut Option<Texture>, + pbo_pool: &mut UploadPBOPool, + ) { if buffer.is_empty() { - None - } else { - let gpu_buffer_texture = self.device.create_texture( - ImageBufferKind::Texture2D, - buffer.format, - buffer.size.width, - buffer.size.height, - TextureFilter::Nearest, - None, - ); + return; + } - self.device.bind_texture( - sampler, - &gpu_buffer_texture, - Swizzle::default(), - ); + if let Some(texture) = dst_texture { + assert!(texture.get_dimensions().width == buffer.size.width); + if texture.get_dimensions().height < buffer.size.height { + device.delete_texture(dst_texture.take().unwrap()); + } + } - self.device.upload_texture_immediate( - &gpu_buffer_texture, - &buffer.data, + if dst_texture.is_none() { + let height = ((buffer.size.height + 7) & !7).max(8); + assert!(height >= buffer.size.height); + *dst_texture = Some( + device.create_texture( + ImageBufferKind::Texture2D, + buffer.format, + buffer.size.width, + height, + TextureFilter::Nearest, + None, + ) ); + } + + let mut uploader = device.upload_texture(pbo_pool); + + uploader.upload( + device, + dst_texture.as_mut().unwrap(), + DeviceIntRect { + min: DeviceIntPoint::zero(), + max: DeviceIntPoint::new(buffer.size.width, buffer.size.height), + }, + None, + None, + buffer.data.as_ptr(), + buffer.data.len(), + ); - Some(gpu_buffer_texture) + uploader.flush(device); + } + + fn maybe_evict_gpu_buffer_texture( + device: &mut Device, + gpu_buffer_height: i32, + texture: &mut Option<Texture>, + texture_too_large: &mut i32, + ) { + if let Some(tex) = texture { + if tex.get_dimensions().height > gpu_buffer_height * 2 { + *texture_too_large += 1; + } else { + *texture_too_large = 0; + } + } + + // Delete the texture if it has been too large for 10 frames + // or more. + if *texture_too_large > 10 { + device.delete_texture(texture.take().unwrap()); + *texture_too_large = 0; } } @@ -5223,32 +5289,32 @@ impl Renderer { return; } + { + let _gm = self.gpu_profiler.start_marker("gpu buffer update"); + + Self::update_gpu_buffer_texture( + &mut self.device, + &frame.gpu_buffer_f, + &mut self.gpu_buffer_texture_f, + &mut self.texture_upload_pbo_pool, + ); + Self::update_gpu_buffer_texture( + &mut self.device, + &frame.gpu_buffer_i, + &mut self.gpu_buffer_texture_i, + &mut self.texture_upload_pbo_pool, + ); + } + self.device.disable_depth_write(); self.set_blend(false, FramebufferKind::Other); self.device.disable_stencil(); self.bind_frame_data(frame); - // Upload experimental GPU buffer texture if there is any data present - // TODO: Recycle these textures, upload via PBO or best approach for platform - let gpu_buffer_texture_f = self.create_gpu_buffer_texture( - &frame.gpu_buffer_f, - TextureSampler::GpuBufferF, - ); - let gpu_buffer_texture_i = self.create_gpu_buffer_texture( - &frame.gpu_buffer_i, - TextureSampler::GpuBufferI, - ); - let bytes_to_mb = 1.0 / 1000000.0; - let gpu_buffer_bytes_f = gpu_buffer_texture_f - .as_ref() - .map(|tex| tex.size_in_bytes()) - .unwrap_or(0); - let gpu_buffer_bytes_i = gpu_buffer_texture_i - .as_ref() - .map(|tex| tex.size_in_bytes()) - .unwrap_or(0); + let gpu_buffer_bytes_f = frame.gpu_buffer_f.size.to_f32().area() * 16.0; + let gpu_buffer_bytes_i = frame.gpu_buffer_i.size.to_f32().area() * 16.0; let gpu_buffer_mb = (gpu_buffer_bytes_f + gpu_buffer_bytes_i) as f32 * bytes_to_mb; self.profile.set(profiler::GPU_BUFFER_MEM, gpu_buffer_mb); @@ -5451,14 +5517,21 @@ impl Renderer { present_mode, ); - if let Some(gpu_buffer_texture_f) = gpu_buffer_texture_f { - self.device.delete_texture(gpu_buffer_texture_f); - } - if let Some(gpu_buffer_texture_i) = gpu_buffer_texture_i { - self.device.delete_texture(gpu_buffer_texture_i); - } - frame.has_been_rendered = true; + + Self::maybe_evict_gpu_buffer_texture( + &mut self.device, + frame.gpu_buffer_f.size.height, + &mut self.gpu_buffer_texture_f, + &mut self.gpu_buffer_texture_f_too_large, + ); + + Self::maybe_evict_gpu_buffer_texture( + &mut self.device, + frame.gpu_buffer_i.size.height, + &mut self.gpu_buffer_texture_i, + &mut self.gpu_buffer_texture_i_too_large, + ); } fn composite_frame( @@ -5992,6 +6065,12 @@ impl Renderer { if let Some(zoom_debug_texture) = self.zoom_debug_texture { self.device.delete_texture(zoom_debug_texture); } + if let Some(texture) = self.gpu_buffer_texture_f { + self.device.delete_texture(texture); + } + if let Some(texture) = self.gpu_buffer_texture_i { + self.device.delete_texture(texture); + } for textures in self.vertex_data_textures.drain(..) { textures.deinit(&mut self.device); } diff --git a/gfx/wr/webrender/src/texture_cache.rs b/gfx/wr/webrender/src/texture_cache.rs @@ -1664,7 +1664,7 @@ mod test_texture_cache { use euclid::size2; let mut texture_cache = TextureCache::new_for_testing(2048, ImageFormat::BGRA8); let memory = FrameMemory::fallback(); - let mut gpu_buffer = GpuBufferBuilderF::new(&memory); + let mut gpu_buffer = GpuBufferBuilderF::new(&memory, 0); let sizes: &[DeviceIntSize] = &[ size2(23, 27),