commit 10851eae0acbbfff35050b18d12bafe429a18a67
parent 0af394da8576a773f9232cb6f2ac87bf2d6cafb1
Author: Nicolas Silva <nical@fastmail.com>
Date: Tue, 9 Dec 2025 08:19:03 +0000
Bug 1996818 - Recycle the gpu buffer textures. r=gw
Differential Revision: https://phabricator.services.mozilla.com/D272344
Diffstat:
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(),
}
}
@@ -438,7 +434,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);
@@ -454,7 +450,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),