OverviewFilesCommitsIssuesDocsReleasesPeers

dc-terrain commits / f8103c4

atlas: emit HBLK_ATLAS_W + HBLK_USE_ATLAS so binding 14 can actually be read
Binding 14 has been allocated, uploaded and bound since it was added, and never READ — HBLK_USE_ATLAS appeared in two comments and was defined nowhere. Worse, dc_shader_source.h:200 claims "the generator emits HBLK_ATLAS_W beside the block constants" and it did not: that identifier existed only inside that sentence. - dc_compute.h gains `static constexpr int ATLAS_W = 4096` as the ONE definition. _upload_atlas lays the texture out with it and the generator emits it into the shader. Two copies of this number that disagree would be a SHEAR, not a crash — every sample lands on the wrong row and the terrain is silently wrong. - the generator emits HBLK_ATLAS_W and HBLK_USE_ATLAS beside HBLK_N, at BOTH emit sites (the heightfield_only path and the live node path). - the switch is an ENV VAR, not a property, deliberately: the point is to run the same unchanged scene BOTH ways, and a property would have to be authored into the map to be flipped. Built debug AND release for dc-terrain and proc-nodes — the .so files are symlinks into each repo's bin/ and are not built by CI, so a half-built pair is how a stale extension segfaults. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
f8103c41b768f4edf297a7523472b5deeea3b441
by plankatron <fleeky@gmail.com> · 2026-09-03T16:40:41.000Z

Changed files (3) +29 -1

src/dc_compute.cpp +1 -1
……@@ -397,7 +397,7 @@ void DCCompute::_upload_atlas(const float* data, int n) {
397397 if (atlas_tex_.is_valid()) { rd_->free_rid(atlas_tex_); atlas_tex_ = RID(); }
398398 if (atlas_staging_.is_valid()) { rd_->free_rid(atlas_staging_); atlas_staging_ = RID(); }
399399
400 const int W = 4096;
400 const int W = ATLAS_W; // see dc_compute.h — the generator emits this same number
401401 int count = (data == nullptr || n <= 0) ? 1 : n;
402402 int H = (count + W - 1) / W;
403403 atlas_w_ = W; atlas_h_ = H;
src/dc_compute.h +7 -0
……@@ -138,6 +138,13 @@ private:
138138 // shader chooses with HBLK_USE_ATLAS, so the two can be compared against
139139 // each other rather than the swap being taken on faith. A half-migration
140140 // here is exactly what shipped #55's count/size SIGSEGV.
141 // THE atlas width. ONE definition: _upload_atlas lays the texture out with
142 // it and the generator emits it into the shader as HBLK_ATLAS_W. Two copies
143 // of this number that disagree is a shear, not a crash — every sample lands
144 // on the wrong row and the terrain is silently wrong.
145public:
146 static constexpr int ATLAS_W = 4096;
147private:
141148 RID atlas_tex_; // R32F, linear index -> (x, y); binding 14
142149 RID atlas_staging_; // one row-block, for texture_update + copy
143150 int atlas_w_ = 0;
src/dc_terrain_generator_procnodes.cpp +21 -0
……@@ -16,7 +16,9 @@
1616// comment still stands. It is a shared math header and nothing more.
1717#include "erosion_cpp_shim.h"
1818#include "erosion_wrapper_glsl.h"
19#include "dc_compute.h"
1920#include <godot_cpp/classes/file_access.hpp>
21#include <godot_cpp/classes/os.hpp>
2022
2123namespace godot {
2224
……@@ -526,6 +528,21 @@ void DCTerrainGeneratorProcNodes::set_graph(const Ref<Resource> &p_graph) {
526528//
527529// One producer for now, which matches one bound buffer. More than one wants an
528530// offset table emitted alongside, which is the same change a grid of tiles needs.
531// Read the SAME floats through binding 14 (the atlas image) instead of binding
532// 13 (the SSBO). Both hold identical values at identical indices, so this is a
533// pure TRANSPORT swap and gate_dc_vs_lookdev must agree to 0.001 m either way.
534//
535// An ENV VAR rather than a property, deliberately: the point is to run the gate
536// BOTH ways over one unchanged scene, and a property would have to be authored
537// into the map to be flipped.
538//
539// HBLK_USE_ATLAS=1 godot4 --path . --script res://scenes/test/...
540static bool _use_atlas() {
541 String v = OS::get_singleton()->get_environment("HBLK_USE_ATLAS");
542 return v == "1" || v.to_lower() == "true";
543}
544
545
529546void DCTerrainGeneratorProcNodes::_collect_node_data() {
530547 if (graph_.is_null()) return;
531548 Variant nc_var = graph_->call("get_node_count");
……@@ -733,6 +750,8 @@ void DCTerrainGeneratorProcNodes::_recompile_glsl() {
733750 if (heightfield_only_ && hblk_n_ > 0) {
734751 String hb =
735752 String("const int HBLK_N = ") + itos(hblk_n_) + ";\n"
753 "const int HBLK_ATLAS_W = " + itos(DCCompute::ATLAS_W) + ";\n"
754 "const bool HBLK_USE_ATLAS = " + String(_use_atlas() ? "true" : "false") + ";\n"
736755 "const float HBLK_OX = " + String::num(hblk_ox_, 4) + ";\n"
737756 "const float HBLK_OZ = " + String::num(hblk_oz_, 4) + ";\n"
738757 "const float HBLK_MPP = " + String::num(hblk_mpp_, 6) + ";\n"
……@@ -861,6 +880,8 @@ void DCTerrainGeneratorProcNodes::_recompile_glsl() {
861880 // that never compiled and a world with no terrain.
862881 hblk_glsl_sampler =
863882 String("const int HBLK_N = ") + itos(hblk_n_) + ";\n"
883 "const int HBLK_ATLAS_W = " + itos(DCCompute::ATLAS_W) + ";\n"
884 "const bool HBLK_USE_ATLAS = " + String(_use_atlas() ? "true" : "false") + ";\n"
864885 "const float HBLK_OX = " + String::num(hblk_ox_, 4) + ";\n"
865886 "const float HBLK_OZ = " + String::num(hblk_oz_, 4) + ";\n"
866887 "const float HBLK_MPP = " + String::num(hblk_mpp_, 6) + ";\n"