@@ -334,18 +334,19 @@ pub async fn render_output_cache<'a: 'n>(
334334 #[ data] tile_cache : TileCache ,
335335) -> RenderOutput {
336336 let footprint = ctx. footprint ( ) ;
337- let render_params = ctx
338- . vararg ( 0 )
339- . expect ( "Did not find var args" )
340- . downcast_ref :: < RenderParams > ( )
341- . expect ( "Downcasting render params yielded invalid type" ) ;
337+ let Some ( render_params) = ctx. vararg ( 0 ) . ok ( ) . and_then ( |v| v . downcast_ref :: < RenderParams > ( ) ) else {
338+ log :: warn! ( "render_output_cache: missing or invalid render params, falling back to direct render" ) ;
339+ let context = OwnedContextImpl :: empty ( ) . with_footprint ( * footprint ) ;
340+ return data . eval ( context . into_context ( ) ) . await ;
341+ } ;
342342
343- if !matches ! ( render_params. render_output_type, RenderOutputTypeRequest :: Vello ) {
343+ // Fall back to direct render for non-Vello or zero-size viewports
344+ let physical_resolution = footprint. resolution ;
345+ if !matches ! ( render_params. render_output_type, RenderOutputTypeRequest :: Vello ) || physical_resolution. x == 0 || physical_resolution. y == 0 {
344346 let context = OwnedContextImpl :: empty ( ) . with_footprint ( * footprint) . with_vararg ( Box :: new ( render_params. clone ( ) ) ) ;
345347 return data. eval ( context. into_context ( ) ) . await ;
346348 }
347349
348- let physical_resolution = footprint. resolution ;
349350 let logical_scale = footprint. decompose_scale ( ) . x ;
350351 let device_scale = render_params. scale ;
351352 let physical_scale = logical_scale * device_scale;
@@ -368,13 +369,23 @@ pub async fn render_output_cache<'a: 'n>(
368369
369370 let mut new_regions = Vec :: new ( ) ;
370371 for missing_region in & cache_query. missing_regions {
372+ if missing_region. tiles . is_empty ( ) {
373+ continue ;
374+ }
371375 let region = render_missing_region ( missing_region, |ctx| data. eval ( ctx) , ctx. clone ( ) , render_params, logical_scale, device_scale) . await ;
372376 new_regions. push ( region) ;
373377 }
374378
375379 tile_cache. store_regions ( new_regions. clone ( ) ) ;
376380
377381 let all_regions: Vec < _ > = cache_query. cached_regions . into_iter ( ) . chain ( new_regions. into_iter ( ) ) . collect ( ) ;
382+
383+ // If no regions, fall back to direct render
384+ if all_regions. is_empty ( ) {
385+ let context = OwnedContextImpl :: empty ( ) . with_footprint ( * footprint) . with_vararg ( Box :: new ( render_params. clone ( ) ) ) ;
386+ return data. eval ( context. into_context ( ) ) . await ;
387+ }
388+
378389 let exec = editor_api. application_io . as_ref ( ) . unwrap ( ) . gpu_executor ( ) . unwrap ( ) ;
379390 let ( output_texture, combined_metadata) = composite_cached_regions ( & all_regions, & viewport_bounds, physical_resolution, logical_scale, physical_scale, exec) ;
380391
@@ -406,7 +417,7 @@ where
406417 // Use round() on boundaries to ensure adjacent tiles share the same edge
407418 let pixel_start = ( min_tile. as_dvec2 ( ) * TILE_SIZE as f64 * device_scale) . round ( ) . as_ivec2 ( ) ;
408419 let pixel_end = ( ( max_tile + IVec2 :: ONE ) . as_dvec2 ( ) * TILE_SIZE as f64 * device_scale) . round ( ) . as_ivec2 ( ) ;
409- let region_pixel_size = ( pixel_end - pixel_start) . as_uvec2 ( ) ;
420+ let region_pixel_size = ( pixel_end - pixel_start) . max ( IVec2 :: ONE ) . as_uvec2 ( ) ;
410421
411422 let region_transform = glam:: DAffine2 :: from_scale ( DVec2 :: splat ( logical_scale) ) * glam:: DAffine2 :: from_translation ( -region_world_start) ;
412423 let region_footprint = Footprint {
@@ -421,7 +432,7 @@ where
421432 let mut result = render_fn ( region_ctx) . await ;
422433
423434 let RenderOutputType :: Texture ( rendered_texture) = result. data else {
424- panic ! ( "Expected texture output from render" ) ;
435+ unreachable ! ( "render_missing_region: expected texture output from Vello render" ) ;
425436 } ;
426437
427438 // Transform metadata from region pixel space to document space
0 commit comments