Skip to content

Commit 09cfa10

Browse files
Remove surface and window from ApplicationIo
1 parent 36366b3 commit 09cfa10

File tree

19 files changed

+356
-499
lines changed

19 files changed

+356
-499
lines changed

Cargo.lock

Lines changed: 18 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ interpreted-executor = { path = "node-graph/interpreted-executor" }
8989
node-macro = { path = "node-graph/node-macro" }
9090
wgpu-executor = { path = "node-graph/libraries/wgpu-executor" }
9191
graphite-proc-macros = { path = "proc-macros" }
92+
graphene-canvas-utils = { path = "node-graph/libraries/canvas-utils" }
9293

9394
# Workspace dependencies
9495
rustc-hash = "2.0"

desktop/wrapper/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl DesktopWrapper {
5151
pub async fn execute_node_graph() -> NodeGraphExecutionResult {
5252
let result = graphite_editor::node_graph_executor::run_node_graph().await;
5353
match result {
54-
(true, texture) => NodeGraphExecutionResult::HasRun(texture.map(|t| t.texture)),
54+
(true, texture) => NodeGraphExecutionResult::HasRun(texture.map(Into::into)),
5555
(false, _) => NodeGraphExecutionResult::NotRun,
5656
}
5757
}

editor/src/messages/portfolio/document/node_graph/document_node_definitions.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,8 +1067,8 @@ fn document_node_definitions() -> HashMap<DefinitionIdentifier, DocumentNodeDefi
10671067
exports: vec![NodeInput::node(NodeId(2), 0)],
10681068
nodes: [
10691069
DocumentNode {
1070-
inputs: vec![NodeInput::scope("editor-api")],
1071-
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::create_surface::IDENTIFIER),
1070+
inputs: vec![],
1071+
implementation: DocumentNodeImplementation::ProtoNode(wasm_application_io::create_canvas::IDENTIFIER),
10721072
skip_deduplication: true,
10731073
..Default::default()
10741074
},

editor/src/node_graph_executor.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use graph_craft::proto::GraphErrors;
77
use graph_craft::wasm_application_io::EditorPreferences;
88
use graphene_std::application_io::{NodeGraphUpdateMessage, RenderConfig, TimingInformation};
99
use graphene_std::raster::{CPU, Raster};
10-
use graphene_std::renderer::{RenderMetadata, format_transform_matrix};
10+
use graphene_std::renderer::RenderMetadata;
1111
use graphene_std::text::FontCache;
1212
use graphene_std::transform::Footprint;
1313
use graphene_std::vector::Vector;
@@ -397,12 +397,11 @@ impl NodeGraphExecutor {
397397
responses.add(FrontendMessage::UpdateImageData { image_data });
398398
responses.add(FrontendMessage::UpdateDocumentArtwork { svg });
399399
}
400-
RenderOutputType::CanvasFrame(frame) => {
401-
let matrix = format_transform_matrix(frame.transform);
402-
let transform = if matrix.is_empty() { String::new() } else { format!(" transform=\"{matrix}\"") };
400+
#[cfg(target_family = "wasm")]
401+
RenderOutputType::CanvasFrame { canvas_id, resolution } => {
403402
let svg = format!(
404-
r#"<svg><foreignObject width="{}" height="{}"{transform}><div data-canvas-placeholder="{}" data-is-viewport="true"></div></foreignObject></svg>"#,
405-
frame.resolution.x, frame.resolution.y, frame.surface_id.0,
403+
r#"<svg><foreignObject width="{}" height="{}"><div data-canvas-placeholder="{}" data-is-viewport="true"></div></foreignObject></svg>"#,
404+
resolution.x, resolution.y, canvas_id,
406405
);
407406
responses.add(FrontendMessage::UpdateDocumentArtwork { svg });
408407
}

editor/src/node_graph_executor/runtime.rs

Lines changed: 14 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ use graphene_std::text::FontCache;
1919
use graphene_std::transform::RenderQuality;
2020
use graphene_std::vector::Vector;
2121
use graphene_std::vector::style::RenderMode;
22+
#[cfg(target_family = "wasm")]
23+
use graphene_std::wasm_application_io::canvas_utils::{Canvas, CanvasSurface, CanvasSurfaceHandle};
2224
use graphene_std::wasm_application_io::{RenderOutputType, WasmApplicationIo, WasmEditorApi};
2325
use graphene_std::{Artboard, Context, Graphic};
2426
use interpreted_executor::dynamic_executor::{DynamicExecutor, IntrospectError, ResolvedDocumentNodeTypesDelta};
@@ -58,7 +60,7 @@ pub struct NodeRuntime {
5860

5961
/// Cached surface for Wasm viewport rendering (reused across frames)
6062
#[cfg(all(target_family = "wasm", feature = "gpu"))]
61-
wasm_viewport_surface: Option<wgpu_executor::WgpuSurface>,
63+
wasm_canvas_cache: CanvasSurfaceHandle,
6264
/// Currently displayed texture, the runtime keeps a reference to it to avoid the texture getting destroyed while it is still in use.
6365
#[cfg(all(target_family = "wasm", feature = "gpu"))]
6466
current_viewport_texture: Option<ImageTexture>,
@@ -146,7 +148,7 @@ impl NodeRuntime {
146148
vector_modify: Default::default(),
147149
inspect_state: None,
148150
#[cfg(all(target_family = "wasm", feature = "gpu"))]
149-
wasm_viewport_surface: None,
151+
wasm_canvas_cache: CanvasSurfaceHandle::new(),
150152
#[cfg(all(target_family = "wasm", feature = "gpu"))]
151153
current_viewport_texture: None,
152154
}
@@ -280,7 +282,7 @@ impl NodeRuntime {
280282
.gpu_executor()
281283
.expect("GPU executor should be available when we receive a texture");
282284

283-
let raster_cpu = Raster::new_gpu(image_texture.texture.as_ref().clone()).convert(Footprint::BOUNDLESS, executor).await;
285+
let raster_cpu = Raster::new_gpu(image_texture.as_ref().clone()).convert(Footprint::BOUNDLESS, executor).await;
284286

285287
let (data, width, height) = raster_cpu.to_flat_u8();
286288

@@ -304,7 +306,7 @@ impl NodeRuntime {
304306
.gpu_executor()
305307
.expect("GPU executor should be available when we receive a texture");
306308

307-
let raster_cpu = Raster::new_gpu(image_texture.texture.as_ref().clone()).convert(Footprint::BOUNDLESS, executor).await;
309+
let raster_cpu = Raster::new_gpu(image_texture.as_ref().clone()).convert(Footprint::BOUNDLESS, executor).await;
308310

309311
self.sender.send_eyedropper_preview(raster_cpu);
310312
continue;
@@ -318,83 +320,20 @@ impl NodeRuntime {
318320
data: RenderOutputType::Texture(image_texture),
319321
metadata,
320322
})) if !render_config.for_export => {
321-
// On Wasm, for viewport rendering, blit the texture to a surface and return a CanvasFrame
323+
self.current_viewport_texture = Some(image_texture.clone());
324+
322325
let app_io = self.editor_api.application_io.as_ref().unwrap();
323326
let executor = app_io.gpu_executor().expect("GPU executor should be available when we receive a texture");
324327

325-
// Get or create the cached surface
326-
if self.wasm_viewport_surface.is_none() {
327-
let surface_handle = app_io.create_window();
328-
let wasm_surface = executor
329-
.create_surface(graphene_std::wasm_application_io::WasmSurfaceHandle {
330-
surface: surface_handle.surface.clone(),
331-
window_id: surface_handle.window_id,
332-
})
333-
.expect("Failed to create surface");
334-
self.wasm_viewport_surface = Some(Arc::new(wasm_surface));
335-
}
336-
337-
let surface = self.wasm_viewport_surface.as_ref().unwrap();
338-
339-
// Use logical resolution for CSS sizing, physical resolution for the actual surface/texture
340-
let physical_resolution = render_config.viewport.resolution;
341-
let logical_resolution = physical_resolution.as_dvec2() / render_config.scale;
342-
343-
// Blit the texture to the surface
344-
let mut encoder = executor.context.device.create_command_encoder(&vello::wgpu::CommandEncoderDescriptor {
345-
label: Some("Texture to Surface Blit"),
346-
});
347-
348-
// Configure the surface at physical resolution (for HiDPI displays)
349-
let surface_inner = &surface.surface.inner;
350-
let surface_caps = surface_inner.get_capabilities(&executor.context.adapter);
351-
surface_inner.configure(
352-
&executor.context.device,
353-
&vello::wgpu::SurfaceConfiguration {
354-
usage: vello::wgpu::TextureUsages::RENDER_ATTACHMENT | vello::wgpu::TextureUsages::COPY_DST,
355-
format: vello::wgpu::TextureFormat::Rgba8Unorm,
356-
width: physical_resolution.x,
357-
height: physical_resolution.y,
358-
present_mode: surface_caps.present_modes[0],
359-
alpha_mode: vello::wgpu::CompositeAlphaMode::PreMultiplied,
360-
view_formats: vec![],
361-
desired_maximum_frame_latency: 2,
362-
},
363-
);
364-
365-
let surface_texture = surface_inner.get_current_texture().expect("Failed to get surface texture");
366-
self.current_viewport_texture = Some(image_texture.clone());
367-
368-
encoder.copy_texture_to_texture(
369-
vello::wgpu::TexelCopyTextureInfoBase {
370-
texture: image_texture.texture.as_ref(),
371-
mip_level: 0,
372-
origin: Default::default(),
373-
aspect: Default::default(),
374-
},
375-
vello::wgpu::TexelCopyTextureInfoBase {
376-
texture: &surface_texture.texture,
377-
mip_level: 0,
378-
origin: Default::default(),
379-
aspect: Default::default(),
380-
},
381-
image_texture.texture.size(),
382-
);
383-
384-
executor.context.queue.submit([encoder.finish()]);
385-
surface_texture.present();
386-
387-
// TODO: Figure out if we can explicityl destroy the wgpu texture here to reduce the allocation pressure. We might also be able to use a texture allocation pool
388-
389-
let frame = graphene_std::application_io::SurfaceFrame {
390-
surface_id: surface.window_id,
391-
resolution: logical_resolution,
392-
transform: glam::DAffine2::IDENTITY,
393-
};
328+
self.wasm_canvas_cache.present(&image_texture, executor);
394329

330+
let logical_resolution = render_config.viewport.resolution.as_dvec2() / render_config.scale;
395331
(
396332
Ok(TaggedValue::RenderOutput(RenderOutput {
397-
data: RenderOutputType::CanvasFrame(frame),
333+
data: RenderOutputType::CanvasFrame {
334+
canvas_id: self.wasm_canvas_cache.id(),
335+
resolution: logical_resolution,
336+
},
398337
metadata,
399338
})),
400339
None,

node-graph/graph-craft/src/document/value.rs

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use dyn_any::DynAny;
1010
pub use dyn_any::StaticType;
1111
use glam::{Affine2, Vec2};
1212
pub use glam::{DAffine2, DVec2, IVec2, UVec2};
13-
use graphene_application_io::{ImageTexture, SurfaceFrame};
1413
use graphic_types::Artboard;
1514
use graphic_types::Graphic;
1615
use graphic_types::Vector;
@@ -40,7 +39,6 @@ macro_rules! tagged_value {
4039
None,
4140
$( $(#[$meta] ) *$identifier( $ty ), )*
4241
RenderOutput(RenderOutput),
43-
SurfaceFrame(SurfaceFrame),
4442
#[serde(skip)]
4543
EditorApi(Arc<WasmEditorApi>)
4644
}
@@ -54,7 +52,6 @@ macro_rules! tagged_value {
5452
Self::None => {}
5553
$( Self::$identifier(x) => {x.hash(state)}),*
5654
Self::RenderOutput(x) => x.hash(state),
57-
Self::SurfaceFrame(x) => x.hash(state),
5855
Self::EditorApi(x) => x.hash(state),
5956
}
6057
}
@@ -66,7 +63,6 @@ macro_rules! tagged_value {
6663
Self::None => Box::new(()),
6764
$( Self::$identifier(x) => Box::new(x), )*
6865
Self::RenderOutput(x) => Box::new(x),
69-
Self::SurfaceFrame(x) => Box::new(x),
7066
Self::EditorApi(x) => Box::new(x),
7167
}
7268
}
@@ -76,7 +72,6 @@ macro_rules! tagged_value {
7672
Self::None => Arc::new(()),
7773
$( Self::$identifier(x) => Arc::new(x), )*
7874
Self::RenderOutput(x) => Arc::new(x),
79-
Self::SurfaceFrame(x) => Arc::new(x),
8075
Self::EditorApi(x) => Arc::new(x),
8176
}
8277
}
@@ -86,7 +81,6 @@ macro_rules! tagged_value {
8681
Self::None => concrete!(()),
8782
$( Self::$identifier(_) => concrete!($ty), )*
8883
Self::RenderOutput(_) => concrete!(RenderOutput),
89-
Self::SurfaceFrame(_) => concrete!(SurfaceFrame),
9084
Self::EditorApi(_) => concrete!(&WasmEditorApi)
9185
}
9286
}
@@ -99,8 +93,6 @@ macro_rules! tagged_value {
9993
x if x == TypeId::of::<()>() => Ok(TaggedValue::None),
10094
$( x if x == TypeId::of::<$ty>() => Ok(TaggedValue::$identifier(*downcast(input).unwrap())), )*
10195
x if x == TypeId::of::<RenderOutput>() => Ok(TaggedValue::RenderOutput(*downcast(input).unwrap())),
102-
x if x == TypeId::of::<SurfaceFrame>() => Ok(TaggedValue::SurfaceFrame(*downcast(input).unwrap())),
103-
10496

10597
_ => Err(format!("Cannot convert {:?} to TaggedValue", DynAny::type_name(input.as_ref()))),
10698
}
@@ -113,8 +105,7 @@ macro_rules! tagged_value {
113105
x if x == TypeId::of::<()>() => Ok(TaggedValue::None),
114106
$( x if x == TypeId::of::<$ty>() => Ok(TaggedValue::$identifier(<$ty as Clone>::clone(input.downcast_ref().unwrap()))), )*
115107
x if x == TypeId::of::<RenderOutput>() => Ok(TaggedValue::RenderOutput(RenderOutput::clone(input.downcast_ref().unwrap()))),
116-
x if x == TypeId::of::<SurfaceFrame>() => Ok(TaggedValue::SurfaceFrame(SurfaceFrame::clone(input.downcast_ref().unwrap()))),
117-
_ => Err(format!("Cannot convert {:?} to TaggedValue",std::any::type_name_of_val(input))),
108+
_ => Err(format!("Cannot convert {:?} to TaggedValue", std::any::type_name_of_val(input))),
118109
}
119110
}
120111
/// Returns a TaggedValue from the type, where that value is its type's `Default::default()`
@@ -145,7 +136,6 @@ macro_rules! tagged_value {
145136
Self::None => "()".to_string(),
146137
$( Self::$identifier(x) => format!("{:?}", x), )*
147138
Self::RenderOutput(_) => "RenderOutput".to_string(),
148-
Self::SurfaceFrame(_) => "SurfaceFrame".to_string(),
149139
Self::EditorApi(_) => "WasmEditorApi".to_string(),
150140
}
151141
}
@@ -477,11 +467,10 @@ pub struct RenderOutput {
477467
pub metadata: RenderMetadata,
478468
}
479469

480-
#[derive(Debug, Clone, Hash, PartialEq, dyn_any::DynAny, serde::Serialize, serde::Deserialize)]
470+
#[derive(Debug, Clone, PartialEq, dyn_any::DynAny, serde::Serialize, serde::Deserialize)]
481471
pub enum RenderOutputType {
482-
CanvasFrame(SurfaceFrame),
483472
#[serde(skip)]
484-
Texture(ImageTexture),
473+
Texture(graphene_application_io::ImageTexture),
485474
#[serde(skip)]
486475
Buffer {
487476
data: Vec<u8>,
@@ -492,8 +481,35 @@ pub enum RenderOutputType {
492481
svg: String,
493482
image_data: Vec<(u64, Image<Color>)>,
494483
},
484+
#[cfg(target_family = "wasm")]
485+
CanvasFrame {
486+
canvas_id: u64,
487+
resolution: DVec2,
488+
},
495489
}
496490

491+
impl Hash for RenderOutputType {
492+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
493+
match self {
494+
Self::Texture(texture) => {
495+
texture.hash(state);
496+
}
497+
Self::Buffer { data, width, height } => {
498+
data.hash(state);
499+
width.hash(state);
500+
height.hash(state);
501+
}
502+
Self::Svg { svg, image_data } => {
503+
svg.hash(state);
504+
image_data.hash(state);
505+
}
506+
#[cfg(target_family = "wasm")]
507+
Self::CanvasFrame { canvas_id, .. } => {
508+
canvas_id.hash(state);
509+
}
510+
}
511+
}
512+
}
497513
impl Hash for RenderOutput {
498514
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
499515
self.data.hash(state)

0 commit comments

Comments
 (0)