Skip to content

Commit 97fcf76

Browse files
committed
Add texture pool to render cache node
1 parent 50ef6e1 commit 97fcf76

6 files changed

Lines changed: 65 additions & 39 deletions

File tree

desktop/src/render/state.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ pub(crate) struct RenderState {
1111
executor: WgpuExecutor,
1212
config: wgpu::SurfaceConfiguration,
1313
render_pipeline: wgpu::RenderPipeline,
14-
transparent_texture: wgpu::Texture,
14+
transparent_texture: std::sync::Arc<wgpu::Texture>,
1515
sampler: wgpu::Sampler,
1616
desired_width: u32,
1717
desired_height: u32,
1818
viewport_scale: [f32; 2],
1919
viewport_offset: [f32; 2],
20-
viewport_texture: Option<wgpu::Texture>,
20+
viewport_texture: Option<std::sync::Arc<wgpu::Texture>>,
2121
overlays_texture: Option<TargetTexture>,
2222
ui_texture: Option<wgpu::Texture>,
2323
bind_group: Option<wgpu::BindGroup>,
@@ -50,7 +50,7 @@ impl RenderState {
5050

5151
surface.configure(&context.device, &config);
5252

53-
let transparent_texture = context.device.create_texture(&wgpu::TextureDescriptor {
53+
let transparent_texture = std::sync::Arc::new(context.device.create_texture(&wgpu::TextureDescriptor {
5454
label: Some("Transparent Texture"),
5555
size: wgpu::Extent3d {
5656
width: 1,
@@ -63,7 +63,7 @@ impl RenderState {
6363
format: wgpu::TextureFormat::Bgra8UnormSrgb,
6464
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
6565
view_formats: &[],
66-
});
66+
}));
6767

6868
// Create shader module
6969
let shader = context.device.create_shader_module(wgpu::include_wgsl!("composite_shader.wgsl"));
@@ -207,7 +207,7 @@ impl RenderState {
207207
}
208208
}
209209

210-
pub(crate) fn bind_viewport_texture(&mut self, viewport_texture: wgpu::Texture) {
210+
pub(crate) fn bind_viewport_texture(&mut self, viewport_texture: std::sync::Arc<wgpu::Texture>) {
211211
self.viewport_texture = Some(viewport_texture);
212212
self.update_bindgroup();
213213
}

desktop/wrapper/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ impl DesktopWrapper {
5858
}
5959

6060
pub enum NodeGraphExecutionResult {
61-
HasRun(Option<wgpu::Texture>),
61+
HasRun(Option<std::sync::Arc<wgpu::Texture>>),
6262
NotRun,
6363
}
6464

editor/src/node_graph_executor/runtime.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pub struct NodeRuntime {
5959
/// Cached surface for WASM viewport rendering (reused across frames)
6060
#[cfg(all(target_family = "wasm", feature = "gpu"))]
6161
wasm_viewport_surface: Option<wgpu_executor::WgpuSurface>,
62+
current_viewport_texture: Option<ImageTexture>,
6263
}
6364

6465
/// Messages passed from the editor thread to the node runtime thread.
@@ -144,6 +145,7 @@ impl NodeRuntime {
144145
inspect_state: None,
145146
#[cfg(all(target_family = "wasm", feature = "gpu"))]
146147
wasm_viewport_surface: None,
148+
current_viewport_texture: None,
147149
}
148150
}
149151

@@ -275,7 +277,7 @@ impl NodeRuntime {
275277
.gpu_executor()
276278
.expect("GPU executor should be available when we receive a texture");
277279

278-
let raster_cpu = Raster::new_gpu(image_texture.texture).convert(Footprint::BOUNDLESS, executor).await;
280+
let raster_cpu = Raster::new_gpu(image_texture.texture.as_ref().clone()).convert(Footprint::BOUNDLESS, executor).await;
279281

280282
let (data, width, height) = raster_cpu.to_flat_u8();
281283

@@ -299,7 +301,7 @@ impl NodeRuntime {
299301
.gpu_executor()
300302
.expect("GPU executor should be available when we receive a texture");
301303

302-
let raster_cpu = Raster::new_gpu(image_texture.texture).convert(Footprint::BOUNDLESS, executor).await;
304+
let raster_cpu = Raster::new_gpu(image_texture.texture.as_ref().clone()).convert(Footprint::BOUNDLESS, executor).await;
303305

304306
self.sender.send_eyedropper_preview(raster_cpu);
305307
continue;
@@ -354,6 +356,7 @@ impl NodeRuntime {
354356
);
355357

356358
let surface_texture = surface_inner.get_current_texture().expect("Failed to get surface texture");
359+
self.current_viewport_texture = Some(image_texture.clone());
357360

358361
// Blit the rendered texture to the surface
359362
surface.surface.blitter.copy(

node-graph/libraries/application-io/src/lib.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ impl Size for web_sys::HtmlCanvasElement {
5050
}
5151
}
5252

53-
#[derive(Debug, Clone)]
53+
#[derive(Debug, Clone, DynAny)]
5454
pub struct ImageTexture {
5555
#[cfg(feature = "wgpu")]
56-
pub texture: wgpu::Texture,
56+
pub texture: Arc<wgpu::Texture>,
5757
#[cfg(not(feature = "wgpu"))]
5858
pub texture: (),
5959
}
@@ -89,10 +89,6 @@ impl PartialEq for ImageTexture {
8989
}
9090
}
9191

92-
unsafe impl StaticType for ImageTexture {
93-
type Static = ImageTexture;
94-
}
95-
9692
#[cfg(feature = "wgpu")]
9793
impl Size for ImageTexture {
9894
fn size(&self) -> UVec2 {

node-graph/nodes/gstd/src/render_cache.rs

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ struct TileCacheImpl {
9696
total_memory: usize,
9797
cache_key: CacheKey,
9898
current_scale: f64,
99+
texture_cache: (UVec2, Vec<Arc<wgpu::Texture>>),
99100
}
100101

101102
impl Default for TileCacheImpl {
@@ -106,6 +107,7 @@ impl Default for TileCacheImpl {
106107
total_memory: 0,
107108
cache_key: CacheKey::default(),
108109
current_scale: 0.0,
110+
texture_cache: Default::default(),
109111
}
110112
}
111113
}
@@ -224,6 +226,36 @@ impl TileCacheImpl {
224226
self.regions.clear();
225227
self.total_memory = 0;
226228
}
229+
230+
pub fn request_texture(&mut self, size: UVec2, device: &wgpu::Device) -> Arc<wgpu::Texture> {
231+
if self.texture_cache.0 != size {
232+
self.texture_cache.0 = size;
233+
self.texture_cache.1.clear();
234+
}
235+
self.texture_cache.1.truncate(5);
236+
for texture in &self.texture_cache.1 {
237+
if Arc::strong_count(&texture) == 1 {
238+
return Arc::clone(texture);
239+
}
240+
}
241+
let texture = Arc::new(device.create_texture(&wgpu::TextureDescriptor {
242+
label: Some("viewport_output"),
243+
size: wgpu::Extent3d {
244+
width: size.x,
245+
height: size.y,
246+
depth_or_array_layers: 1,
247+
},
248+
mip_level_count: 1,
249+
sample_count: 1,
250+
dimension: wgpu::TextureDimension::D2,
251+
format: wgpu::TextureFormat::Rgba8Unorm,
252+
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::TEXTURE_BINDING,
253+
view_formats: &[],
254+
}));
255+
self.texture_cache.1.push(texture.clone());
256+
257+
texture
258+
}
227259
}
228260

229261
impl TileCache {
@@ -234,6 +266,10 @@ impl TileCache {
234266
pub fn store_regions(&self, regions: Vec<CachedRegion>) {
235267
self.0.lock().unwrap().store_regions(regions);
236268
}
269+
270+
pub fn request_texture(&self, size: UVec2, device: &wgpu::Device) -> Arc<wgpu::Texture> {
271+
self.0.lock().unwrap().request_texture(size, device)
272+
}
237273
}
238274

239275
fn group_into_regions(tiles: &[TileCoord], scale: f64, max_region_area: u32) -> Vec<RenderRegion> {
@@ -421,7 +457,12 @@ pub async fn render_output_cache<'a: 'n>(
421457
}
422458

423459
let exec = editor_api.application_io.as_ref().unwrap().gpu_executor().unwrap();
424-
let (output_texture, combined_metadata) = composite_cached_regions(&all_regions, &viewport_bounds, physical_resolution, logical_scale, physical_scale, exec);
460+
461+
// TODO: Use texture pool to reuse existing unused textures instead of allocating fresh ones every time
462+
let device = &exec.context.device;
463+
let output_texture = tile_cache.request_texture(physical_resolution, device);
464+
465+
let combined_metadata = composite_cached_regions(&all_regions, &viewport_bounds, output_texture.as_ref(), logical_scale, physical_scale, exec);
425466

426467
RenderOutput {
427468
data: RenderOutputType::Texture(ImageTexture { texture: output_texture }),
@@ -475,7 +516,7 @@ where
475516
let memory_size = (region_pixel_size.x * region_pixel_size.y) as usize * BYTES_PER_PIXEL;
476517

477518
CachedRegion {
478-
texture: rendered_texture.texture,
519+
texture: rendered_texture.texture.as_ref().clone(),
479520
texture_size: region_pixel_size,
480521
scene_bounds: region.scene_bounds.clone(),
481522
tiles: region.tiles.clone(),
@@ -488,29 +529,14 @@ where
488529
fn composite_cached_regions(
489530
regions: &[CachedRegion],
490531
viewport_bounds: &AxisAlignedBbox,
491-
output_resolution: UVec2,
532+
output_texture: &wgpu::Texture,
492533
logical_scale: f64,
493534
physical_scale: f64,
494535
exec: &wgpu_executor::WgpuExecutor,
495-
) -> (wgpu::Texture, rendering::RenderMetadata) {
536+
) -> rendering::RenderMetadata {
496537
let device = &exec.context.device;
497538
let queue = &exec.context.queue;
498-
499-
// TODO: Use texture pool to reuse existing unused textures instead of allocating fresh ones every time
500-
let output_texture = device.create_texture(&wgpu::TextureDescriptor {
501-
label: Some("viewport_output"),
502-
size: wgpu::Extent3d {
503-
width: output_resolution.x,
504-
height: output_resolution.y,
505-
depth_or_array_layers: 1,
506-
},
507-
mip_level_count: 1,
508-
sample_count: 1,
509-
dimension: wgpu::TextureDimension::D2,
510-
format: wgpu::TextureFormat::Rgba8Unorm,
511-
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::TEXTURE_BINDING,
512-
view_formats: &[],
513-
});
539+
let output_resolution = UVec2::new(output_texture.width(), output_texture.height());
514540

515541
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("composite") });
516542
let mut combined_metadata = rendering::RenderMetadata::default();
@@ -570,5 +596,5 @@ fn composite_cached_regions(
570596
}
571597

572598
queue.submit([encoder.finish()]);
573-
(output_texture, combined_metadata)
599+
combined_metadata
574600
}

node-graph/nodes/gstd/src/render_node.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -197,10 +197,11 @@ async fn render<'a: 'n>(ctx: impl Ctx + ExtractFootprint + ExtractVarArgs, edito
197197
None
198198
};
199199

200-
let texture = exec
201-
.render_vello_scene_to_texture(&scene, physical_resolution, context, background)
202-
.await
203-
.expect("Failed to render Vello scene");
200+
let texture = Arc::new(
201+
exec.render_vello_scene_to_texture(&scene, physical_resolution, context, background)
202+
.await
203+
.expect("Failed to render Vello scene"),
204+
);
204205

205206
RenderOutputType::Texture(ImageTexture { texture })
206207
}

0 commit comments

Comments
 (0)