There are 2 seperate problems.
I am using the Titan Editor World Editor, making a new blank world each time. Prior to the ThreadFinishedUsingGPUData() fix, as soon as you click on a Material the Editor would freeze completely, no seg fault or anything.
Both my original and your suggested fix for ThreadFinishedUsingGPUData() fixed this freeze problem, which I have applied here after reverting mine:
https://github.com/DrewGilpin/EsenthelEn...1ccaf61fc0
There is another problem I experience only after the ThreadFinishedUsingGPUData() fixes the freezing, which results in a seg fault with a 1MB stack. This occurs not when selecting a material but 3 or 4 seconds after placing even 1 terrain tile in the world builder in a new/blank world. With a 2MB stack this crash does not happen. This is on Ubuntu 22.04.
It is breaking at this line:
NOINLINE Bool Heightmap::buildEx2(Mesh &mesh, Int quality, UInt flag, BuildMem &mem, C Sphere *sphere, C Neighbors *neighbors)C // this function should be multi-threaded safe
{
Signal: SIGSEGV (Segmentation fault)
EE::Heightmap::buildEx2 Heightmap.cpp:1749
EE::Heightmap::buildEx Heightmap.cpp:2485
EE::Heightmap::build Heightmap.cpp:2500
Build Builder.cpp:27
AreaBuild::process Builder.cpp:220
BuilderClass::Process Builder.cpp:404
EE::Threads::_process Thread.cpp:1149
EE::Threads::process1<…> Thread.h:517
BuilderClass::ioFunc Builder.cpp:490
BuilderClass::IOFunc Builder.cpp:406
EE::Thread::func Thread.cpp:651
EE::ThreadFunc Thread.cpp:626
this is Claude's interpretation, I asked it to apply this diff:
https://github.com/Esenthel/EsenthelEngi...6b127.diff
That confirms the stack overflow is a real issue — the creator's ThreadFinishedUsingGPUData() fix handles the GL context deadlock, but the
2MB stack is still needed independently for the heightmap builder. You may want to mention this to the creator in your reply — the segfault
happens in the Editor when BuilderThreads process terrain areas.
This is Claude Code's opinion of your fix:
Yes, this will fix the GL context deadlock. The creator's approach is actually cleaner than ours — instead of releasing in the generic
ThreadsFunc loop, each call site that acquires a GL context now explicitly releases it when done.
For the specific terrain deadlock, the key fix is in Builder.cpp:375:
if(build&(AREA_REBUILD_HM|AREA_REBUILD_HM_MESH|...))ThreadFinishedUsingGPUData();
This mirrors the acquire at line 177 with the same condition. After each area build completes, the GL context is returned to the pool, so other
threads on BuilderThreads/WorkerThreads/BackgroundThreads can use it.
The diff also includes a nice optimization in Thread.h — wrapping the calls in #if GL so they compile to no-ops on DX11/Windows, avoiding
unnecessary function call overhead.
I asked Claude Code what could be done to keep the stack size at 1MB this was it's suggestions:
vtx_dir[131][131] is only used when sphere is non-null (planet/sphere terrain). It's populated at line 1839-1849 inside if(sphere), and read at
lines 1491-1494 (setNormal), 2218, and 2323 ? all inside if(sphere) blocks.
There are a few approaches to keep STACK_SIZE at 1MB:
Option 1: Heap-allocate vtx_dir only when needed
Move vtx_dir out of the Builder struct and allocate it on the heap only when sphere is non-null. This is the simplest and most targeted fix ? the
~206KB array only exists when sphere terrain is active.
Option 2: Move the entire Builder to the heap
The BuildMem struct is already heap-allocated (see Heightmap.cpp:2499 ? Alloc(SIZE(BuildMem))). The Builder could follow the same pattern ?
allocated with Alloc inside buildEx2 instead of being a stack local. The code already acknowledges this is a fat struct (line 2484: "buildEx2 was
separated because it uses a very big stack").
Option 3: Move vtx_dir into BuildMem
Since BuildMem is already heap-allocated and passed in, vtx_dir could live there.
Option 1 is the smallest change and directly addresses the new 206KB addition. Want me to implement it? It would look roughly like:
// In Builder struct, replace:
// Vec vtx_dir[MAX_HM_RES+2][MAX_HM_RES+2];
// With:
typedef Vec VtxDirArray[MAX_HM_RES+2][MAX_HM_RES+2];
VtxDirArray *vtx_dir_ptr; // heap-allocated only for sphere terrain
// In buildEx2, after Builder construction:
VtxDirArray *vtx_dir_mem = sphere ? new VtxDirArray : null;
builder.vtx_dir_ptr = vtx_dir_mem;
// ... at end:
delete vtx_dir_mem;
And update the ~6 access sites from vtx_dir[y][x] to (*vtx_dir_ptr)[y][x].
I had Claude Code do option 1:
https://github.com/DrewGilpin/EsenthelEn...dca870bef8
And it seems to work with 1 MB stack now for me, I don't use spherical worlds so I haven't tested that.