Skip to content

Commit 1554ae4

Browse files
committed
Workaround crash in release GDExtension builds.
Unlike module builds, RenderingServer::get_singleton() crashes when used in initialize_voxel_module() in release GDExtension builds. See #733 And godotengine/godot-cpp#1180
1 parent d984cfc commit 1554ae4

7 files changed

+40
-34
lines changed

engine/gpu/gpu_task_runner.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ void GPUTaskRunner::stop() {
4646
_thread.wait_to_finish();
4747
}
4848

49+
bool GPUTaskRunner::is_running() const {
50+
return _running;
51+
}
52+
4953
void GPUTaskRunner::push(IGPUTask *task) {
5054
ZN_ASSERT_RETURN(task != nullptr);
5155
MutexLock mlock(_mutex);

engine/gpu/gpu_task_runner.h

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ class GPUTaskRunner {
8080
void push(IGPUTask *task);
8181
unsigned int get_pending_task_count() const;
8282
bool has_rendering_device() const;
83+
bool is_running() const;
8384

8485
private:
8586
void thread_func();

engine/voxel_engine.cpp

+18-13
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,6 @@ VoxelEngine::VoxelEngine(Config config) {
6868
ZN_PRINT_VERBOSE(format("Size of SaveBlockDataTask: {}", sizeof(SaveBlockDataTask)));
6969
ZN_PRINT_VERBOSE(format("Size of MeshBlockTask: {}", sizeof(MeshBlockTask)));
7070

71-
if (RenderingServer::get_singleton() == nullptr) {
72-
// Sadly, that happens. This is a problem in GDExtension...
73-
ZN_PRINT_ERROR("RenderingServer singleton is null when creating VoxelEngine!");
74-
// RenderingServer can also be null with `tests=yes`.
75-
// TODO There is no hook to integrate modules to Godot's test framework, update this when it gets improved
76-
} else {
77-
_gpu_task_runner.start();
78-
}
79-
8071
set_main_thread_time_budget_usec(config.main_thread_budget_usec);
8172
}
8273

@@ -91,6 +82,24 @@ VoxelEngine::~VoxelEngine() {
9182
_gpu_task_runner.stop();
9283
}
9384

85+
void VoxelEngine::try_initialize_gpu_features() {
86+
// RenderingServer can be null with `tests=yes`.
87+
// TODO There is no hook to integrate modules to Godot's test framework, update this when it gets improved
88+
RenderingServer *rs = RenderingServer::get_singleton();
89+
if (rs != nullptr) {
90+
// TODO Enhancement: threaded graphics resource building should be initialized better.
91+
// Pick this from the current renderer + user option (at time of writing, Godot 4 has only one
92+
// renderer and has not figured out how such option would be exposed). Could use
93+
// `can_create_resources_async` but this is internal. AFAIK `is_low_end` will be `true` only for OpenGL
94+
// backends, which are the only ones not supporting async resource creation.
95+
_threaded_graphics_resource_building_enabled = (rs->is_low_end() == false);
96+
}
97+
98+
if (_gpu_task_runner.is_running() == false) {
99+
_gpu_task_runner.start();
100+
}
101+
}
102+
94103
void VoxelEngine::wait_and_clear_all_tasks(bool warn) {
95104
_general_thread_pool.wait_for_all_tasks();
96105

@@ -216,10 +225,6 @@ void VoxelEngine::set_main_thread_time_budget_usec(unsigned int usec) {
216225
_main_thread_time_budget_usec = usec;
217226
}
218227

219-
void VoxelEngine::set_threaded_graphics_resource_building_enabled(bool enable) {
220-
_threaded_graphics_resource_building_enabled = enable;
221-
}
222-
223228
bool VoxelEngine::is_threaded_graphics_resource_building_enabled() const {
224229
return _threaded_graphics_resource_building_enabled;
225230
}

engine/voxel_engine.h

+9-4
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@ class VoxelEngine {
144144
static void create_singleton(Config config);
145145
static void destroy_singleton();
146146

147+
// This is a separate initialization step due to GDExtension limitations.
148+
// It must be called when RenderingServer singleton is available (which is not the case with GDExtension during
149+
// class registrations, contrary to modules).
150+
// See https://github.com/godotengine/godot-cpp/issues/1180
151+
void try_initialize_gpu_features();
152+
147153
VolumeID add_volume(VolumeCallbacks callbacks);
148154
VolumeCallbacks get_volume_callbacks(VolumeID volume_id) const;
149155

@@ -182,10 +188,6 @@ class VoxelEngine {
182188
int get_main_thread_time_budget_usec() const;
183189
void set_main_thread_time_budget_usec(unsigned int usec);
184190

185-
// Allows/disallows building Mesh and Texture resources from inside threads.
186-
// Depends on Godot's efficiency at doing so, and which renderer is used.
187-
// For example, the OpenGL renderer does not support this well, but the Vulkan one should.
188-
void set_threaded_graphics_resource_building_enabled(bool enable);
189191
// This should be fast and safe to access from multiple threads.
190192
bool is_threaded_graphics_resource_building_enabled() const;
191193

@@ -319,6 +321,9 @@ class VoxelEngine {
319321

320322
FileLocker _file_locker;
321323

324+
// Caches whether building Mesh and Texture resources is allowed from inside threads.
325+
// Depends on Godot's efficiency at doing so, and which renderer is used.
326+
// For example, the OpenGL renderer does not support this well, but the Vulkan one should.
322327
bool _threaded_graphics_resource_building_enabled = false;
323328

324329
GPUTaskRunner _gpu_task_runner;

engine/voxel_engine_updater.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ void VoxelEngineUpdater::ensure_existence(SceneTree *st) {
4141
// TODO This can fail (for example if `Node::data.blocked > 0` while in `_ready()`) but Godot offers no API to check
4242
// anything. So if this fail, the node will leak.
4343
root->add_child(u);
44+
45+
VoxelEngine::get_singleton().try_initialize_gpu_features();
4446
}
4547

4648
void VoxelEngineUpdater::_notification(int p_what) {

register_types.cpp

-17
Original file line numberDiff line numberDiff line change
@@ -350,23 +350,6 @@ void initialize_voxel_module(ModuleInitializationLevel p_level) {
350350
CheckRefCountDoesNotChange::set_enabled(config.ownership_checks);
351351
#endif
352352
VoxelEngine::create_singleton(config.inner);
353-
#if defined(ZN_GODOT)
354-
// RenderingServer can be null with `tests=yes`.
355-
// TODO There is no hook to integrate modules to Godot's test framework, update this when it gets improved
356-
if (RenderingServer::get_singleton() != nullptr) {
357-
// TODO Enhancement: threaded graphics resource building should be initialized better.
358-
// Pick this from the current renderer + user option (at time of writing, Godot 4 has only one
359-
// renderer and has not figured out how such option would be exposed). Could use
360-
// `can_create_resources_async` but this is internal. AFAIK `is_low_end` will be `true` only for OpenGL
361-
// backends, which are the only ones not supporting async resource creation.
362-
VoxelEngine::get_singleton().set_threaded_graphics_resource_building_enabled(
363-
RenderingServer::get_singleton()->is_low_end() == false
364-
);
365-
}
366-
#else
367-
// TODO GDX: RenderingServer::is_low_end() is not exposed, can't tell if we can generate graphics resources in
368-
// different threads
369-
#endif
370353

371354
zylann::voxel::godot::VoxelEngine::create_singleton();
372355
zylann::godot::add_singleton("VoxelEngine", zylann::voxel::godot::VoxelEngine::get_singleton());

tests/voxel/test_detail_rendering_gpu.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
namespace zylann::voxel::tests {
1313

1414
void test_normalmap_render_gpu() {
15+
#ifdef ZN_GODOT_EXTENSION
16+
// https://github.com/godotengine/godot-cpp/issues/1180
17+
ZN_PRINT_ERROR("This test might not work in GDExtension builds at the moment.");
18+
#endif
19+
VoxelEngine::get_singleton().try_initialize_gpu_features();
20+
1521
Ref<VoxelGeneratorGraph> generator;
1622
generator.instantiate();
1723
{

0 commit comments

Comments
 (0)