@@ -19,6 +19,8 @@ use graphene_std::text::FontCache;
1919use graphene_std:: transform:: RenderQuality ;
2020use graphene_std:: vector:: Vector ;
2121use graphene_std:: vector:: style:: RenderMode ;
22+ #[ cfg( target_family = "wasm" ) ]
23+ use graphene_std:: wasm_application_io:: canvas_utils:: { Canvas , CanvasSurface , CanvasSurfaceHandle } ;
2224use graphene_std:: wasm_application_io:: { RenderOutputType , WasmApplicationIo , WasmEditorApi } ;
2325use graphene_std:: { Artboard , Context , Graphic } ;
2426use 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 ,
0 commit comments