Skip to content

Commit 369e469

Browse files
committed
Update threaded graphics resource creation auto-detection
1 parent fb6c9c0 commit 369e469

7 files changed

+219
-21
lines changed

engine/gpu/gpu_task_runner.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ GPUTaskRunner::~GPUTaskRunner() {
2626

2727
void GPUTaskRunner::start() {
2828
ZN_ASSERT(!_running);
29+
ZN_PRINT_VERBOSE("Starting GPUTaskRunner");
2930
_running = true;
3031
_thread.start(
3132
[](void *p_userdata) {

engine/voxel_engine.cpp

+42-16
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include "../streams/load_all_blocks_data_task.h"
66
#include "../streams/load_block_data_task.h"
77
#include "../streams/save_block_data_task.h"
8+
#include "../util/godot/classes/os.h"
9+
#include "../util/godot/classes/project_settings.h"
810
#include "../util/godot/classes/rd_sampler_state.h"
911
#include "../util/godot/classes/rendering_device.h"
1012
#include "../util/godot/classes/rendering_server.h"
@@ -82,23 +84,43 @@ VoxelEngine::~VoxelEngine() {
8284
_gpu_task_runner.stop();
8385
}
8486

85-
void VoxelEngine::try_initialize_gpu_features() {
86-
#ifdef ZN_GODOT
87-
// RenderingServer can be null with `tests=yes`.
88-
// TODO There is no hook to integrate modules to Godot's test framework, update this when it gets improved
89-
RenderingServer *rs = RenderingServer::get_singleton();
90-
if (rs != nullptr) {
91-
// TODO Enhancement: threaded graphics resource building should be initialized better.
92-
// Pick this from the current renderer + user option (at time of writing, Godot 4 has only one
93-
// renderer and has not figured out how such option would be exposed). Could use
94-
// `can_create_resources_async` but this is internal. AFAIK `is_low_end` will be `true` only for OpenGL
95-
// backends, which are the only ones not supporting async resource creation.
96-
_threaded_graphics_resource_building_enabled = (rs->is_low_end() == false);
87+
static bool auto_detect_threaded_graphics_resource_building_support() {
88+
const ProjectSettings *project = ProjectSettings::get_singleton();
89+
ZN_ASSERT_RETURN_V(project != nullptr, false);
90+
91+
const zylann::godot::RenderThreadModel rendering_thread_model = zylann::godot::get_render_thread_model(*project);
92+
const zylann::godot::RenderMethod rendering_method = zylann::godot::get_current_rendering_method();
93+
const zylann::godot::RenderDriverName driver = zylann ::godot::get_current_rendering_driver();
94+
95+
if (!zylann::godot::is_render_thread_model_safe(rendering_thread_model)) {
96+
return false;
9797
}
98-
#elif defined(ZN_GODOT_EXTENSION)
99-
// TODO GDX: `RenderingServer::is_low_end` is not defined, so we can't determine if threaded resource creation is
100-
// available!
101-
#endif
98+
99+
switch (rendering_method) {
100+
case zylann::godot::RENDER_METHOD_GL_COMPATIBILITY:
101+
return false;
102+
case zylann::godot::RENDER_METHOD_UNKNOWN:
103+
return false;
104+
default:
105+
break;
106+
}
107+
108+
switch (driver) {
109+
case zylann::godot::RENDER_DRIVER_OPENGL3:
110+
case zylann::godot::RENDER_DRIVER_OPENGL3_ANGLE:
111+
case zylann::godot::RENDER_DRIVER_OPENGL3_ES:
112+
case zylann::godot::RENDER_DRIVER_UNKNOWN:
113+
return false;
114+
default:
115+
return true;
116+
}
117+
}
118+
119+
void VoxelEngine::try_initialize_gpu_features() {
120+
_threaded_graphics_resource_building_enabled = auto_detect_threaded_graphics_resource_building_support();
121+
ZN_PRINT_VERBOSE(format(
122+
"Auto-detected threaded graphics resource building: {}", _threaded_graphics_resource_building_enabled
123+
));
102124

103125
if (_gpu_task_runner.is_running() == false) {
104126
_gpu_task_runner.start();
@@ -234,6 +256,10 @@ bool VoxelEngine::is_threaded_graphics_resource_building_enabled() const {
234256
return _threaded_graphics_resource_building_enabled;
235257
}
236258

259+
// void VoxelEngine::set_threaded_graphics_resource_building_enabled(bool enabled) {
260+
// _threaded_graphics_resource_building_enabled = enabled;
261+
// }
262+
237263
void VoxelEngine::push_async_task(zylann::IThreadedTask *task) {
238264
_general_thread_pool.enqueue(task, false);
239265
}

engine/voxel_engine.h

+1
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ class VoxelEngine {
190190

191191
// This should be fast and safe to access from multiple threads.
192192
bool is_threaded_graphics_resource_building_enabled() const;
193+
// void set_threaded_graphics_resource_building_enabled(bool enabled);
193194

194195
void push_main_thread_progressive_task(IProgressiveTask *task);
195196

engine/voxel_engine_gd.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,32 @@ Vector3 VoxelEngine::get_editor_camera_direction() const {
182182

183183
#endif
184184

185+
bool VoxelEngine::_b_get_threaded_graphics_resource_building_enabled() const {
186+
const zylann::voxel::VoxelEngine &ve = zylann::voxel::VoxelEngine::get_singleton();
187+
return ve.is_threaded_graphics_resource_building_enabled();
188+
}
189+
190+
// This is normally automatic. This method is mainly to allow overriding it just in case.
191+
// void VoxelEngine::_b_set_threaded_graphics_resource_building_enabled(bool enabled) {
192+
// zylann::voxel::VoxelEngine &ve = zylann::voxel::VoxelEngine::get_singleton();
193+
// ve.set_threaded_graphics_resource_building_enabled(enabled);
194+
// }
195+
185196
void VoxelEngine::_bind_methods() {
186197
ClassDB::bind_method(D_METHOD("get_version_major"), &VoxelEngine::get_version_major);
187198
ClassDB::bind_method(D_METHOD("get_version_minor"), &VoxelEngine::get_version_minor);
188199
ClassDB::bind_method(D_METHOD("get_version_patch"), &VoxelEngine::get_version_patch);
189200
ClassDB::bind_method(D_METHOD("get_stats"), &VoxelEngine::get_stats);
201+
202+
ClassDB::bind_method(
203+
D_METHOD("get_threaded_graphics_resource_building_enabled"),
204+
&VoxelEngine::_b_get_threaded_graphics_resource_building_enabled
205+
);
206+
207+
// ClassDB::bind_method(
208+
// D_METHOD("set_threaded_graphics_resource_building_enabled", "enabled"),
209+
// &VoxelEngine::_b_set_threaded_graphics_resource_building_enabled
210+
// );
190211
}
191212

192213
} // namespace zylann::voxel::godot

engine/voxel_engine_gd.h

+3
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class VoxelEngine : public Object {
4343
private:
4444
void _on_rendering_server_frame_post_draw();
4545

46+
bool _b_get_threaded_graphics_resource_building_enabled() const;
47+
// void _b_set_threaded_graphics_resource_building_enabled(bool enabled);
48+
4649
static void _bind_methods();
4750

4851
#ifdef TOOLS_ENABLED

util/godot/classes/rendering_server.cpp

+110-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
#include "rendering_server.h"
2+
#include "../../errors.h"
3+
#include "../../string/format.h"
4+
#include "../core/string.h"
5+
#include "../core/version.h"
6+
#include "project_settings.h"
27

38
namespace zylann::godot {
49

@@ -29,14 +34,115 @@ void get_shader_parameter_list(const RID &shader_rid, StdVector<ShaderParameterI
2934
#endif
3035
}
3136

32-
String get_current_rendering_method() {
33-
// TODO This is not well exposed at the moment.
37+
String get_current_rendering_method_name() {
38+
#if GODOT_VERSION_MAJOR == 4 && GODOT_VERSION_MINOR >= 4
39+
RenderingServer *rs = RenderingServer::get_singleton();
40+
// RenderingServer can be null with `tests=yes`.
41+
ZN_ASSERT_RETURN_V(rs != nullptr, "");
42+
43+
const String method_name = rs->get_current_rendering_method();
44+
return method_name;
45+
46+
#else
47+
// See https://github.com/godotengine/godot/pull/85430
48+
49+
#if defined(ZN_GODOT)
50+
OS *os = OS::get_singleton();
51+
ZN_ASSERT_RETURN(os != nullptr, "");
52+
return os->get_current_rendering_method();
53+
54+
#elif defined(ZN_GODOT_EXTENSION)
55+
ZN_PRINT_WARNING("Unable to get current rendering method, Godot doesn't expose it.");
56+
return "";
57+
#endif
58+
59+
#endif
60+
}
61+
62+
RenderMethod get_current_rendering_method() {
63+
const String name = get_current_rendering_method_name();
64+
65+
if (name == "forward_plus") {
66+
return RENDER_METHOD_FORWARD_PLUS;
67+
}
68+
if (name == "mobile") {
69+
return RENDER_METHOD_MOBILE;
70+
}
71+
if (name == "gl_compatibility") {
72+
return RENDER_METHOD_GL_COMPATIBILITY;
73+
}
74+
75+
ZN_PRINT_WARNING(format("Rendering method {} is unknown", name));
76+
return RENDER_METHOD_UNKNOWN;
77+
}
78+
79+
String get_current_rendering_driver_name() {
80+
#if GODOT_VERSION_MAJOR == 4 && GODOT_VERSION_MINOR >= 4
81+
RenderingServer *rs = RenderingServer::get_singleton();
82+
// RenderingServer can be null with `tests=yes`.
83+
ZN_ASSERT_RETURN_V(rs != nullptr, "");
84+
85+
const String driver_name = rs->get_current_rendering_driver_name();
86+
return driver_name;
87+
88+
#else
3489
// See https://github.com/godotengine/godot/pull/85430
90+
3591
#if defined(ZN_GODOT)
36-
return OS::get_singleton()->get_current_rendering_method();
92+
OS *os = OS::get_singleton();
93+
ZN_ASSERT_RETURN(os != nullptr, "");
94+
return os->get_current_rendering_driver_name();
95+
3796
#elif defined(ZN_GODOT_EXTENSION)
38-
return "<unable to get current rendering method, Godot doesn't expose it>";
97+
ZN_PRINT_WARNING("Unable to get current rendering driver name, Godot doesn't expose it.");
98+
return "";
99+
#endif
100+
39101
#endif
40102
}
41103

104+
RenderDriverName get_current_rendering_driver() {
105+
const String name = get_current_rendering_driver_name();
106+
107+
if (name == "vulkan") {
108+
return RENDER_DRIVER_VULKAN;
109+
}
110+
if (name == "d3d12") {
111+
return RENDER_DRIVER_D3D12;
112+
}
113+
if (name == "metal") {
114+
return RENDER_DRIVER_METAL;
115+
}
116+
if (name == "opengl3") {
117+
return RENDER_DRIVER_OPENGL3;
118+
}
119+
if (name == "opengl3_es") {
120+
return RENDER_DRIVER_OPENGL3_ES;
121+
}
122+
if (name == "opengl3_angle") {
123+
return RENDER_DRIVER_OPENGL3_ANGLE;
124+
}
125+
126+
ZN_PRINT_WARNING(format("Rendering driver {} is unknown", name));
127+
return RENDER_DRIVER_UNKNOWN;
128+
}
129+
130+
RenderThreadModel get_render_thread_model(const ProjectSettings &settings) {
131+
const int rendering_thread_model = settings.get("rendering/driver/threads/thread_model");
132+
return static_cast<RenderThreadModel>(rendering_thread_model);
133+
}
134+
135+
bool is_render_thread_model_safe(const RenderThreadModel mode) {
136+
switch (mode) {
137+
case RENDER_SEPARATE_THREAD:
138+
case RENDER_THREAD_SAFE:
139+
return true;
140+
case RENDER_THREAD_UNSAFE:
141+
return false;
142+
default:
143+
ZN_PRINT_ERROR("Unhandled enum value");
144+
return false;
145+
}
146+
}
147+
42148
} // namespace zylann::godot

util/godot/classes/rendering_server.h

+41-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ using namespace godot;
99
#endif
1010

1111
#include "../../containers/std_vector.h"
12+
#include "../macros.h"
13+
14+
ZN_GODOT_FORWARD_DECLARE(class ProjectSettings);
1215

1316
namespace zylann::godot {
1417

@@ -27,7 +30,44 @@ struct ShaderParameterInfo {
2730

2831
void get_shader_parameter_list(const RID &shader_rid, StdVector<ShaderParameterInfo> &out_parameters);
2932

30-
String get_current_rendering_method();
33+
String get_current_rendering_method_name();
34+
35+
// Enum equivalent to strings used in ProjectSettings and RenderingServer.
36+
enum RenderMethod {
37+
RENDER_METHOD_FORWARD_PLUS,
38+
RENDER_METHOD_MOBILE,
39+
RENDER_METHOD_GL_COMPATIBILITY,
40+
RENDER_METHOD_UNKNOWN,
41+
};
42+
43+
RenderMethod get_current_rendering_method();
44+
45+
String get_current_rendering_driver_name();
46+
47+
enum RenderDriverName {
48+
RENDER_DRIVER_VULKAN,
49+
RENDER_DRIVER_D3D12,
50+
RENDER_DRIVER_METAL,
51+
RENDER_DRIVER_OPENGL3,
52+
RENDER_DRIVER_OPENGL3_ES,
53+
RENDER_DRIVER_OPENGL3_ANGLE,
54+
RENDER_DRIVER_UNKNOWN,
55+
};
56+
57+
RenderDriverName get_current_rendering_driver();
58+
59+
// Enum equivalent of `ProjectSettings.rendering/driver/threads/thread_model`.
60+
// This is unfortunately not exposed.
61+
enum RenderThreadModel {
62+
RENDER_THREAD_UNSAFE,
63+
RENDER_THREAD_SAFE,
64+
RENDER_SEPARATE_THREAD,
65+
};
66+
67+
RenderThreadModel get_render_thread_model(const ProjectSettings &settings);
68+
69+
// Tells if it is safe to call functions of the RenderingServer from a thread other than the main one.
70+
bool is_render_thread_model_safe(const RenderThreadModel mode);
3171

3272
} // namespace zylann::godot
3373

0 commit comments

Comments
 (0)