{"record":{"id":"1ceb343b8b382249","repo":"GraphiteEditor/Graphite","slug":"gpu-executor-not-available-1ceb34","errorCode":null,"errorMessage":"GPU executor not available","messagePattern":"GPU executor not available","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"node-graph/nodes/gstd/src/render_node.rs","lineNumber":137,"sourceCode":"\n\t\t\t// We now replace all transforms which are supposed to be infinite with a transform which covers the entire viewport.\n\t\t\t// See <https://xi.zulipchat.com/#narrow/channel/197075-vello/topic/Full.20screen.20color.2Fgradients/near/538435044> for more detail.\n\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(|| {","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/node-graph/nodes/gstd/src/render_node.rs#L119-L155","documentation":"In the render node's Vello branch, the executor parameter is declared as Item<Option<&WgpuExecutor>> injected from the try_wgpu_executor scope slot (render_node.rs:81). Into the texture-output path, .expect(\"GPU executor not available\") panics when that slot delivered None, i.e. this environment cannot supply a WgpuExecutor (no adapter, GPU init failed, headless host) even though the node is asked to rasterize a Vello scene into a GPU texture.","triggerScenarios":"A render node evaluated with RenderOutputTypeRequest::Vello in an environment where the try_wgpu_executor scope dependency produced None: no GPU adapter/driver, WebGPU disabled, or a host that never attached a GPU executor to ApplicationIo.","commonSituations":"Documents with render nodes opened on GPU-less machines; exporting renders from headless CLI tools; CI screenshot pipelines; browsers/OSes with WebGPU unavailable; after a device loss that reset the executor slot.","solutions":["Run the evaluation in an environment that supplies a GPU executor: working adapter/driver, WebGPU enabled, or a software adapter fallback","Check startup logs for why the try_wgpu_executor slot is None (adapter request failure, ApplicationIo without GPU executor)","Replace the .expect with a graceful node error or a CPU rasterization fallback instead of a panic","For hosts, detect the missing executor up front (try_wgpu_executor returns Option) and skip/warn before evaluating render-to-texture graphs"],"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 Some(executor) = executor.into_element() else {\n    // surface a node-level error instead of panicking\n    return node_error(\"render requires a GPU executor, none available in this environment\");\n};\nlet texture = executor.render_vello_scene(&transformed_scene, footprint.resolution, context, None).await?;","handlingStrategy":"fallback","validationCode":"// The render node's executor comes from the try_wgpu_executor scope slot; check it before evaluating\nlet executor: Option<&WgpuExecutor> = try_wgpu_executor_result.into_element();\nif executor.is_none() {\n    // skip render-to-texture evaluation or substitute a CPU rasterization path\n}","typeGuard":"fn can_gpu_render(executor_slot: Option<&WgpuExecutor>) -> bool {\n    executor_slot.is_some()\n}","tryCatchPattern":"let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| render_node.evaluate(ctx).await));\nif outcome.is_err() {\n    // no GPU executor (or render failed): report a node error and fall back to CPU/SVG rendering\n}","preventionTips":["Check GPU availability once at startup and gate render-to-texture graphs on it","Keep an SVG/CPU output path available for documents that must render without a GPU","Sandbox graph evaluation so environment-dependent panics become node-level errors"],"tags":["rust","graphite","node-graph","wgpu","vello","render","panic"],"backgroundTag":"gpu-adapter-unavailable","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}