OverviewFilesCommitsIssuesDocsReleasesPeers

proc-nodes commits / e9c167b

gpu: in() must check emission too, and a failed shader now prints itself
`n30_s0` survived the last fix because the guard was only in param(). Nodes that read their inputs directly go through in(), which resolved a connected socket to `nX_sY` unconditionally. The bridged mountain's batch held nodes 31 and 32 only, and Combine XYZ still emitted vec3 n31_s0 = vec3(0.0, 0.0, n30_s0); ERROR: 0:46: 'n30_s0' : undeclared identifier for a producer that was never written into that shader. in() now falls back to the socket's literal when the producer is not emitted here -- which is exactly what it did before the fusion existed. ⚠ SCOPED TO THE BATCH PATH by push_constants. compile_to_function emits the whole graph and sets no emitted_nodes at all, so an unscoped check would reduce every input to its default and silently gut the density shader. AND THE REASON THIS TOOK TWO GOES: a compile error names a line in a shader nobody can see. create_pipeline now prints the numbered source when compilation fails, which is how the two-node batch and the dangling reference became visible at all. "'n30_s0' : undeclared identifier" is unanswerable on its own. gpu_batch_gate 6/6 glsl_golden_gate 32/32 sdf_grid_gate 3/3 volume_mesh_gate 4/4 bridged mountain, GPU path: no compile error, 48151 verts, 13.8 s -> 3.99 s Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
e9c167bfc1bb5e1c3538b7ae58aae571fc9a7f08
by plankatron <fleeky@gmail.com> · 2026-09-06T20:18:22.000Z

Changed files (3) +69 -0

demo/import_json_gpu_probe.gd +43 -0
……@@ -0,0 +1,43 @@
1## Import a Blender-bridged graph and try to evaluate it.
2extends SceneTree
3func _init() -> void:
4 var a := OS.get_cmdline_user_args()
5 var js := FileAccess.get_file_as_string(a[0])
6 var g: ProcNodeGraph = GraphImporter.import_from_json(js)
7 if g == null:
8 push_error("import returned null"); quit(1); return
9 print("imported: %d nodes, %d connections" % [g.get_node_count(), g.get_connection_count()])
10 var missing := 0
11 for i in range(g.get_node_count()):
12 var n: ProcNode = g.get_node(i)
13 if n == null:
14 missing += 1
15 else:
16 print(" %2d %s" % [i, n.get_node_type()])
17 print("null nodes: %d" % missing)
18 var host := ProcGeometryNode.new()
19 host.use_gpu = true
20 # THE CAGE. A Blender geometry tree acts on the object's own mesh, which its
21 # Group Input carries; proc-nodes now takes the same thing as input_mesh.
22 # A box, subdivided, matching the cage the Blender side displaces.
23 if a.size() > 1:
24 var bm := BoxMesh.new()
25 bm.size = Vector3(40.0, 4.0, 40.0)
26 bm.subdivide_width = int(a[1])
27 bm.subdivide_depth = int(a[1])
28 bm.subdivide_height = 2
29 host.set_input_mesh(bm)
30 print("input mesh: BoxMesh %d subdivisions" % int(a[1]))
31 host.set_graph(g)
32 get_root().add_child(host)
33 var m: ArrayMesh = host.bake_mesh()
34 if m == null or m.get_surface_count() == 0:
35 print("BAKE: no mesh")
36 else:
37 var v: PackedVector3Array = m.surface_get_arrays(0)[Mesh.ARRAY_VERTEX]
38 var lo := INF
39 var hi := -INF
40 for p in v:
41 lo = minf(lo, p.y); hi = maxf(hi, p.y)
42 print("BAKE: %d verts, y %.2f .. %.2f" % [v.size(), lo, hi])
43 quit()
src/gpu/glsl_emit_context.cpp +16 -0
……@@ -142,6 +142,22 @@ String GlslEmitContext::in(int p_node_idx, int p_socket) const {
142142 if (src_node < 0) {
143143 return literal(p_node_idx, p_socket);
144144 }
145 // ⛔ A LOCAL ONLY EXISTS IF ITS PRODUCER IS IN THIS SHADER.
146 //
147 // param() learned this; in() did not, and nodes that read their inputs
148 // directly -- Combine XYZ among them -- go straight through here. The batch
149 // for the bridged mountain held nodes 31 and 32 only, and Combine XYZ still
150 // emitted `vec3 n31_s0 = vec3(0.0, 0.0, n30_s0);` for a producer that was
151 // never written:
152 //
153 // ERROR: 0:46: 'n30_s0' : undeclared identifier
154 //
155 // Scoped to the BATCH path by push_constants. compile_to_function emits the
156 // whole graph and sets no emitted_nodes at all, so checking there would
157 // reduce every input to its default and silently gut the density shader.
158 if (push_constants && !is_emitted(src_node)) {
159 return literal(p_node_idx, p_socket);
160 }
145161 const int src_socket = graph->get_input_source_socket(p_node_idx, p_socket);
146162 const String expr = out(src_node, src_socket);
147163
src/gpu/gpu_compiler.cpp +10 -0
……@@ -455,6 +455,16 @@ bool GpuCompiler::create_pipeline(
455455 String err = spirv->get_stage_compile_error(RenderingDevice::SHADER_STAGE_COMPUTE);
456456 if (!err.is_empty()) {
457457 UtilityFunctions::push_error("GpuCompiler: Shader compile error: ", err);
458 // ⛔ PRINT THE TEXT THAT FAILED. A compile error names a line in a
459 // shader nobody can see, and "'n30_s0' : undeclared identifier" is
460 // unanswerable without the body -- which node emitted, which did not,
461 // and what it named. The emitter is the only thing that knows.
462 const PackedStringArray lines = p_glsl_source.split("\n");
463 String dump;
464 for (int i = 0; i < lines.size(); i++) {
465 dump += String::num_int64(i + 1) + ": " + lines[i] + "\n";
466 }
467 UtilityFunctions::push_error("GpuCompiler: the shader that failed:\n", dump);
458468 return false;
459469 }
460470