Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions desktop/src/render/state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::window::Window;

use crate::wrapper::{Color, WgpuContext, WgpuExecutor};
use wgpu_executor::TargetTexture;

#[derive(derivative::Derivative)]
#[derivative(Debug)]
Expand All @@ -17,7 +18,7 @@ pub(crate) struct RenderState {
viewport_scale: [f32; 2],
viewport_offset: [f32; 2],
viewport_texture: Option<wgpu::Texture>,
overlays_texture: Option<wgpu::Texture>,
overlays_target_texture: Option<TargetTexture>,
ui_texture: Option<wgpu::Texture>,
bind_group: Option<wgpu::BindGroup>,
#[derivative(Debug = "ignore")]
Expand Down Expand Up @@ -181,7 +182,7 @@ impl RenderState {
viewport_scale: [1.0, 1.0],
viewport_offset: [0.0, 0.0],
viewport_texture: None,
overlays_texture: None,
overlays_target_texture: None,
ui_texture: None,
bind_group: None,
overlays_scene: None,
Expand All @@ -208,11 +209,6 @@ impl RenderState {
self.update_bindgroup();
}

pub(crate) fn bind_overlays_texture(&mut self, overlays_texture: wgpu::Texture) {
self.overlays_texture = Some(overlays_texture);
self.update_bindgroup();
}

pub(crate) fn bind_ui_texture(&mut self, bind_ui_texture: wgpu::Texture) {
self.ui_texture = Some(bind_ui_texture);
self.update_bindgroup();
Expand All @@ -236,12 +232,15 @@ impl RenderState {
return;
};
let size = glam::UVec2::new(viewport_texture.width(), viewport_texture.height());
let texture = futures::executor::block_on(self.executor.render_vello_scene_to_texture(&scene, size, &Default::default(), Color::TRANSPARENT));
let Ok(texture) = texture else {
tracing::error!("Error rendering overlays");
let result = futures::executor::block_on(
self.executor
.render_vello_scene_to_target_texture(&scene, size, &Default::default(), Color::TRANSPARENT, &mut self.overlays_target_texture),
);
if let Err(e) = result {
tracing::error!("Error rendering overlays: {:?}", e);
return;
};
self.bind_overlays_texture(texture);
}
self.update_bindgroup();
}

pub(crate) fn render(&mut self, window: &Window) -> Result<(), RenderError> {
Expand Down Expand Up @@ -312,7 +311,11 @@ impl RenderState {

fn update_bindgroup(&mut self) {
let viewport_texture_view = self.viewport_texture.as_ref().unwrap_or(&self.transparent_texture).create_view(&wgpu::TextureViewDescriptor::default());
let overlays_texture_view = self.overlays_texture.as_ref().unwrap_or(&self.transparent_texture).create_view(&wgpu::TextureViewDescriptor::default());
let overlays_texture_view = self
.overlays_target_texture
.as_ref()
.map(|target| target.view())
.unwrap_or_else(|| &self.transparent_texture.create_view(&wgpu::TextureViewDescriptor::default()));
let ui_texture_view = self.ui_texture.as_ref().unwrap_or(&self.transparent_texture).create_view(&wgpu::TextureViewDescriptor::default());

let bind_group = self.context.device.create_bind_group(&wgpu::BindGroupDescriptor {
Expand Down
66 changes: 52 additions & 14 deletions node-graph/libraries/wgpu-executor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,47 @@ pub struct TargetTexture {
size: UVec2,
}

impl TargetTexture {
/// Ensures the texture has the specified size, creating a new one if needed.
/// This allows reusing the same texture across frames when the size hasn't changed.
pub fn ensure_size(&mut self, device: &wgpu::Device, size: UVec2) {
let size = size.max(UVec2::ONE);
if self.size == size {
return;
}

let texture = device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: size.x,
height: size.y,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_SRC,
format: VELLO_SURFACE_FORMAT,
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());

self.texture = texture;
self.view = view;
self.size = size;
}

/// Returns a reference to the texture view for rendering.
pub fn view(&self) -> &wgpu::TextureView {
&self.view
}

/// Returns a reference to the underlying texture.
pub fn texture(&self) -> &wgpu::Texture {
&self.texture
}
}

#[cfg(target_family = "wasm")]
pub type Window = web_sys::HtmlCanvasElement;
#[cfg(not(target_family = "wasm"))]
Expand All @@ -71,19 +112,14 @@ impl WgpuExecutor {
self.render_vello_scene_to_target_texture(scene, size, context, background, &mut output).await?;
Ok(output.unwrap().texture)
}

async fn render_vello_scene_to_target_texture(&self, scene: &Scene, size: UVec2, context: &RenderContext, background: Color, output: &mut Option<TargetTexture>) -> Result<()> {
let size = size.max(UVec2::ONE);
let target_texture = if let Some(target_texture) = output
&& target_texture.size == size
{
target_texture
} else {
pub async fn render_vello_scene_to_target_texture(&self, scene: &Scene, size: UVec2, context: &RenderContext, background: Color, output: &mut Option<TargetTexture>) -> Result<()> {
// Initialize with a minimal texture if this is the first call
if output.is_none() {
let texture = self.context.device.create_texture(&wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: size.x,
height: size.y,
width: 1,
height: 1,
depth_or_array_layers: 1,
},
mip_level_count: 1,
Expand All @@ -94,9 +130,11 @@ impl WgpuExecutor {
view_formats: &[],
});
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
*output = Some(TargetTexture { texture, view, size });
output.as_mut().unwrap()
};
*output = Some(TargetTexture { texture, view, size: UVec2::ONE });
}

let target_texture = output.as_mut().unwrap();
target_texture.ensure_size(&self.context.device, size);

let [r, g, b, a] = background.to_rgba8_srgb();
let render_params = RenderParams {
Expand All @@ -117,7 +155,7 @@ impl WgpuExecutor {
};
renderer.override_image(&image_brush.image, Some(texture_view));
}
renderer.render_to_texture(&self.context.device, &self.context.queue, scene, &target_texture.view, &render_params)?;
renderer.render_to_texture(&self.context.device, &self.context.queue, scene, target_texture.view(), &render_params)?;
for (image_brush, _) in context.resource_overrides.iter() {
renderer.override_image(&image_brush.image, None);
}
Expand Down