{"record":{"id":"adef77191e529bce","repo":"GraphiteEditor/Graphite","slug":"failed-to-render-vello-scene","errorCode":null,"errorMessage":"Failed to render Vello scene","messagePattern":"Failed to render Vello scene","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"node-graph/nodes/gstd/src/render_node.rs","lineNumber":140,"sourceCode":"\t\t\t//\n\t\t\t// `!is_finite()` rather than `== f32::INFINITY`: `scene.append` composes the child's `Affine::scale(INFINITY)` with\n\t\t\t// the viewport rotation, leaving `matrix[0] = cos(θ) * INFINITY`. In the (90°, 270°) tilt range cos is negative so\n\t\t\t// the result is `-INFINITY`, which the old equality check missed; Vello then rasterized a unit rect with non-finite\n\t\t\t// vertices, dropping the gradient and tanking performance. `!is_finite()` also covers NaN as a guard against future\n\t\t\t// code paths where `matrix[0]` could land on `0 * INFINITY`.\n\t\t\tlet scaled_infinite_transform = vello::kurbo::Affine::scale_non_uniform(footprint.resolution.x as f64, footprint.resolution.y as f64);\n\t\t\tfor transform in transformed_scene.encoding_mut().transforms.iter_mut() {\n\t\t\t\tif !transform.matrix[0].is_finite() {\n\t\t\t\t\t*transform = vello_encoding::Transform::from_kurbo(&scaled_infinite_transform);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet texture = executor\n\t\t\t\t.into_element()\n\t\t\t\t.expect(\"GPU executor not available\")\n\t\t\t\t.render_vello_scene(&transformed_scene, footprint.resolution, context, None)\n\t\t\t\t.await\n\t\t\t\t.expect(\"Failed to render Vello scene\");\n\t\t\tRenderOutputType::Texture(texture)\n\t\t}\n\t\t_ => unreachable!(\"Render node did not receive its requested data type\"),\n\t};\n\n\tItem::new_from_element(RenderOutput { data, metadata })\n}\n\n#[node_macro::node(category(\"\"))]\nasync fn create_context<'a: 'n>(\n\t// The executor boundary supplies the render config as the sole vararg (see `wrap_network_in_scope()`)\n\tctx: impl Ctx + ExtractAll + CloneVarArgs + Sync,\n\tdata: impl Node<Context<'static>, Output = Item<RenderOutput>>,\n) -> Item<RenderOutput> {\n\tlet render_config = ctx.vararg(0).ok().and_then(|config| config.downcast_ref::<RenderConfig>()).copied().unwrap_or_else(|| {\n\t\tlog::error!(\"The boundary context is missing its render config vararg\");\n\t\tRenderConfig::default()\n\t});","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/node-graph/nodes/gstd/src/render_node.rs#L122-L158","documentation":"After the render node sanitizes non-finite transforms in the Vello scene encoding, it calls executor.render_vello_scene(...) and .expect(\"Failed to render Vello scene\") panics when the awaited render returns Err. Failures originate inside the wgpu/Vello pipeline: device lost mid-render, out-of-memory allocating the render target texture at the requested resolution, compute shaders/pipelines failing on old or software drivers, or invalid scene data surviving the transform guard.","triggerScenarios":"Rendering a Vello scene at a resolution whose render target exceeds GPU memory; device/driver loss during the render; a driver or browser without the features Vello's compute pipelines need; degenerate scene content that the !is_finite() transform patch above did not neutralize.","commonSituations":"Large exports (poster-size artboards at high scale) exhausting VRAM; GPU driver crashes/resets; VMs and remote desktops without proper GPU support; WebGPU implementations with incomplete compute-shader support; long editor sessions after a device loss.","solutions":["Reduce the render resolution or render in smaller tiles/regions (the render cache already tiles) so texture allocation fits GPU memory","Enable wgpu logging / set an uncaptured-error handler to capture the underlying GPU error before the panic","Update GPU drivers or switch to a software adapter (lavapipe/LLVMpipe) where Vello shaders fail to compile or run","Propagate the error instead of .expect so the render node can surface a user-visible failure and optionally retry at reduced resolution"],"exampleFix":"// before\nlet texture = executor.into_element().expect(\"GPU executor not available\")\n    .render_vello_scene(&transformed_scene, footprint.resolution, context, None).await\n    .expect(\"Failed to render Vello scene\");\n\n// after\nlet texture = executor.render_vello_scene(&transformed_scene, footprint.resolution, context, None).await\n    .map_err(|e| {\n        log::error!(\"Vello render failed at resolution {:?}: {e}\", footprint.resolution);\n        RenderError::VelloRenderFailed(e)\n    })?; // caller may retry at reduced resolution on OOM","handlingStrategy":"retry","validationCode":"// Pre-check the requested render target against device limits before rendering\nlet max_dim = executor.context().device.limits().max_texture_dimension_2d;\nif footprint.resolution.x > max_dim || footprint.resolution.y > max_dim {\n    // clamp or tile the resolution before calling render_vello_scene\n}","typeGuard":null,"tryCatchPattern":"let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    executor.render_vello_scene(&scene, resolution, context, None)\n}));\nmatch outcome.and_then(|f| futures::executor::block_on(f).map_err(std::panic::panic_any)) {\n    _ => { /* on failure: halve the resolution and retry, or re-create the executor if the device was lost */ }\n}","preventionTips":["Cap export resolution and render large outputs in tiles (the render cache already does this)","Register device.on_uncaptured_error and log wgpu validation/OOM errors so the real cause is captured before the panic","Keep GPU drivers updated; test on the software adapter to distinguish driver issues from scene-data issues","Sanitize scene data (finite transforms, valid geometry) before handing it to Vello"],"tags":["rust","vello","wgpu","gpu-render","oom","panic"],"backgroundTag":"gpu-scene-render-failed","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}