Improve texture caching by allowing weak refs and more formats (#4447)

* Destroy GPU textures when the last reference drops

* Request pooled textures synchronously

* Pool cached textures by format

* Add the owned GPU buffer type

* Route the remaining GPU allocations through the executor

* Add weak texture parking to the texture cache

* Raise the texture cache budget to 1GB for native and 512MB for wasm
This commit is contained in:
Timon
2026-08-27 15:19:00 +00:00
committed by GitHub
parent 8c48d5acce
commit 96cc520c4b
13 changed files with 213 additions and 169 deletions

View File

@@ -9,8 +9,7 @@ use graph_craft::document::value::{RenderOutput, RenderOutputType};
use graphic_types::raster_types::Texture;
use rendering::{RenderParams, SvgRender, SvgRenderOutput};
use std::fmt::Write;
use wgpu::util::DeviceExt;
use wgpu_executor::{AsyncWgpuPipeline, WgpuExecutor, WgpuPipelineCache};
use wgpu_executor::{AsyncWgpuPipeline, Buffer, WgpuExecutor, WgpuPipelineCache};
#[node_macro::node(category(""))]
async fn render_background<'a: 'n>(
@@ -342,7 +341,7 @@ impl AsyncWgpuPipeline for CompositeBackground {
} = args;
let foreground_size = foreground.size();
let output = executor.request_texture(UVec2::new(foreground_size.width, foreground_size.height)).await;
let output = executor.request_texture(UVec2::new(foreground_size.width, foreground_size.height));
if zoom <= 0. {
return output;
@@ -360,10 +359,8 @@ impl AsyncWgpuPipeline for CompositeBackground {
let foreground_view = foreground.create_view(&wgpu::TextureViewDescriptor::default());
let checker_draws = if backgrounds.is_empty() {
vec![(
3,
self.create_checker_bind_group(device, CompositeUniforms::fullscreen(viewport_size, screen_to_document, checker_size_doc)),
)]
let uniforms = CompositeUniforms::fullscreen(viewport_size, screen_to_document, checker_size_doc).create_buffer(executor);
vec![(3, self.create_checker_bind_group(device, &uniforms), uniforms)]
} else {
backgrounds
.iter()
@@ -378,8 +375,8 @@ impl AsyncWgpuPipeline for CompositeBackground {
return None;
}
let uniforms = CompositeUniforms::rect(min, max, document_to_screen, viewport_size, checker_size_doc);
Some((6, self.create_checker_bind_group(device, uniforms)))
let uniforms = CompositeUniforms::rect(min, max, document_to_screen, viewport_size, checker_size_doc).create_buffer(executor);
Some((6, self.create_checker_bind_group(device, &uniforms), uniforms))
})
.collect()
};
@@ -421,13 +418,13 @@ impl AsyncWgpuPipeline for CompositeBackground {
if backgrounds.is_empty() {
pass.set_pipeline(&self.checker_viewport_pipeline);
for (vertex_count, bind_group) in &checker_draws {
for (vertex_count, bind_group, _uniforms) in &checker_draws {
pass.set_bind_group(0, bind_group, &[]);
pass.draw(0..*vertex_count, 0..1);
}
} else {
pass.set_pipeline(&self.checker_rect_pipeline);
for (vertex_count, bind_group) in &checker_draws {
for (vertex_count, bind_group, _uniforms) in &checker_draws {
pass.set_bind_group(0, bind_group, &[]);
pass.draw(0..*vertex_count, 0..1);
}
@@ -445,19 +442,13 @@ impl AsyncWgpuPipeline for CompositeBackground {
}
impl CompositeBackground {
fn create_checker_bind_group(&self, device: &wgpu::Device, uniforms: CompositeUniforms) -> wgpu::BindGroup {
let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("background_checker_uniforms"),
contents: bytemuck::bytes_of(&uniforms),
usage: wgpu::BufferUsages::UNIFORM,
});
fn create_checker_bind_group(&self, device: &wgpu::Device, uniforms: &Buffer) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("background_checker_bind_group"),
layout: &self.checker_bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: buffer.as_entire_binding(),
resource: uniforms.as_entire_binding(),
}],
})
}
@@ -499,4 +490,12 @@ impl CompositeUniforms {
_pad: 0.,
}
}
fn create_buffer(&self, executor: &WgpuExecutor) -> Buffer {
executor.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("background_checker_uniforms"),
contents: bytemuck::bytes_of(self),
usage: wgpu::BufferUsages::UNIFORM,
})
}
}

View File

@@ -391,7 +391,7 @@ pub async fn render_output_cache<'a: 'n>(
}
let executor = executor.into_element().expect("GPU executor not available");
let output_texture = executor.request_texture(physical_resolution).await;
let output_texture = executor.request_texture(physical_resolution);
let combined_metadata = composite_cached_regions(&all_regions, &output_texture, &device_origin_offset, &footprint.transform, executor);

View File

@@ -177,7 +177,7 @@ impl AsyncWgpuPipeline for PixelPreview {
let context = &executor.context();
let &PixelPreviewArgs { source, transform, size } = args;
let output = executor.request_texture(size).await;
let output = executor.request_texture(size);
let source_view = source.create_view(&wgpu::TextureViewDescriptor::default());
let output_view = output.create_view(&wgpu::TextureViewDescriptor::default());