Moved Godot config access to module initialization
parent
acbc52e717
commit
6c47c177ab
|
@ -48,6 +48,7 @@
|
|||
#endif
|
||||
|
||||
#include <core/config/engine.h>
|
||||
#include <core/config/project_settings.h>
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/editor_plugin.h"
|
||||
|
@ -68,6 +69,52 @@
|
|||
#include "tests/tests.h"
|
||||
#endif
|
||||
|
||||
namespace zylann::voxel {
|
||||
|
||||
static VoxelServer::ThreadsConfig get_config_from_godot(unsigned int &out_main_thread_time_budget_usec) {
|
||||
CRASH_COND(ProjectSettings::get_singleton() == nullptr);
|
||||
|
||||
VoxelServer::ThreadsConfig config;
|
||||
|
||||
// Compute thread count for general pool.
|
||||
// Note that the I/O thread counts as one used thread and will always be present.
|
||||
|
||||
// "RST" means changing the property requires an editor restart (or game restart)
|
||||
GLOBAL_DEF_RST("voxel/threads/count/minimum", 1);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("voxel/threads/count/minimum",
|
||||
PropertyInfo(Variant::INT, "voxel/threads/count/minimum", PROPERTY_HINT_RANGE, "1,64"));
|
||||
|
||||
GLOBAL_DEF_RST("voxel/threads/count/margin_below_max", 1);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("voxel/threads/count/margin_below_max",
|
||||
PropertyInfo(Variant::INT, "voxel/threads/count/margin_below_max", PROPERTY_HINT_RANGE, "1,64"));
|
||||
|
||||
GLOBAL_DEF_RST("voxel/threads/count/ratio_over_max", 0.5f);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("voxel/threads/count/ratio_over_max",
|
||||
PropertyInfo(Variant::FLOAT, "voxel/threads/count/ratio_over_max", PROPERTY_HINT_RANGE, "0,1,0.1"));
|
||||
|
||||
GLOBAL_DEF_RST("voxel/threads/main/time_budget_ms", 8);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("voxel/threads/main/time_budget_ms",
|
||||
PropertyInfo(Variant::INT, "voxel/threads/main/time_budget_ms", PROPERTY_HINT_RANGE, "0,1000"));
|
||||
|
||||
out_main_thread_time_budget_usec =
|
||||
1000 * int(ProjectSettings::get_singleton()->get("voxel/threads/main/time_budget_ms"));
|
||||
|
||||
config.thread_count_minimum =
|
||||
math::max(1, int(ProjectSettings::get_singleton()->get("voxel/threads/count/minimum")));
|
||||
|
||||
// How many threads below available count on the CPU should we set as limit
|
||||
config.thread_count_margin_below_max =
|
||||
math::max(1, int(ProjectSettings::get_singleton()->get("voxel/threads/count/margin_below_max")));
|
||||
|
||||
// Portion of available CPU threads to attempt using
|
||||
config.thread_count_ratio_over_max = zylann::math::clamp(
|
||||
float(ProjectSettings::get_singleton()->get("voxel/threads/count/ratio_over_max")), 0.f, 1.f);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
} // namespace zylann::voxel
|
||||
|
||||
void initialize_voxel_module(ModuleInitializationLevel p_level) {
|
||||
using namespace zylann;
|
||||
using namespace voxel;
|
||||
|
@ -76,9 +123,13 @@ void initialize_voxel_module(ModuleInitializationLevel p_level) {
|
|||
VoxelMemoryPool::create_singleton();
|
||||
VoxelStringNames::create_singleton();
|
||||
VoxelGraphNodeDB::create_singleton();
|
||||
VoxelServer::create_singleton();
|
||||
gd::VoxelServer::create_singleton();
|
||||
|
||||
unsigned int main_thread_budget_usec;
|
||||
const VoxelServer::ThreadsConfig threads_config = get_config_from_godot(main_thread_budget_usec);
|
||||
VoxelServer::create_singleton(threads_config);
|
||||
VoxelServer::get_singleton().set_main_thread_time_budget_usec(main_thread_budget_usec);
|
||||
|
||||
gd::VoxelServer::create_singleton();
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("VoxelServer", gd::VoxelServer::get_singleton()));
|
||||
|
||||
VoxelMetadataFactory::get_singleton().add_constructor_by_type<gd::VoxelMetadataVariant>(
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "voxel_server.h"
|
||||
#include "../constants/voxel_constants.h"
|
||||
#include "../storage/voxel_memory_pool.h"
|
||||
#include "../util/log.h"
|
||||
#include "../util/macros.h"
|
||||
#include "../util/profiling.h"
|
||||
|
@ -11,8 +10,6 @@
|
|||
#include "mesh_block_task.h"
|
||||
#include "save_block_data_task.h"
|
||||
|
||||
#include <core/config/project_settings.h>
|
||||
|
||||
namespace zylann::voxel {
|
||||
|
||||
VoxelServer *g_voxel_server = nullptr;
|
||||
|
@ -22,65 +19,39 @@ VoxelServer &VoxelServer::get_singleton() {
|
|||
return *g_voxel_server;
|
||||
}
|
||||
|
||||
void VoxelServer::create_singleton() {
|
||||
void VoxelServer::create_singleton(ThreadsConfig threads_config) {
|
||||
ZN_ASSERT_MSG(g_voxel_server == nullptr, "Creating singleton twice");
|
||||
g_voxel_server = memnew(VoxelServer);
|
||||
g_voxel_server = ZN_NEW(VoxelServer(threads_config));
|
||||
}
|
||||
|
||||
void VoxelServer::destroy_singleton() {
|
||||
ZN_ASSERT_MSG(g_voxel_server != nullptr, "Destroying singleton twice");
|
||||
memdelete(g_voxel_server);
|
||||
ZN_DELETE(g_voxel_server);
|
||||
g_voxel_server = nullptr;
|
||||
}
|
||||
|
||||
VoxelServer::VoxelServer() {
|
||||
CRASH_COND(ProjectSettings::get_singleton() == nullptr);
|
||||
|
||||
VoxelServer::VoxelServer(ThreadsConfig threads_config) {
|
||||
const int hw_threads_hint = Thread::get_hardware_concurrency();
|
||||
ZN_PRINT_VERBOSE(format("Voxel: HW threads hint: {}", hw_threads_hint));
|
||||
|
||||
ZN_ASSERT(threads_config.thread_count_margin_below_max >= 0);
|
||||
ZN_ASSERT(threads_config.thread_count_minimum >= 1);
|
||||
ZN_ASSERT(threads_config.thread_count_ratio_over_max >= 0.f);
|
||||
|
||||
// Compute thread count for general pool.
|
||||
// Note that the I/O thread counts as one used thread and will always be present.
|
||||
|
||||
// "RST" means changing the property requires an editor restart (or game restart)
|
||||
GLOBAL_DEF_RST("voxel/threads/count/minimum", 1);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("voxel/threads/count/minimum",
|
||||
PropertyInfo(Variant::INT, "voxel/threads/count/minimum", PROPERTY_HINT_RANGE, "1,64"));
|
||||
|
||||
GLOBAL_DEF_RST("voxel/threads/count/margin_below_max", 1);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("voxel/threads/count/margin_below_max",
|
||||
PropertyInfo(Variant::INT, "voxel/threads/count/margin_below_max", PROPERTY_HINT_RANGE, "1,64"));
|
||||
|
||||
GLOBAL_DEF_RST("voxel/threads/count/ratio_over_max", 0.5f);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("voxel/threads/count/ratio_over_max",
|
||||
PropertyInfo(Variant::FLOAT, "voxel/threads/count/ratio_over_max", PROPERTY_HINT_RANGE, "0,1,0.1"));
|
||||
|
||||
GLOBAL_DEF_RST("voxel/threads/main/time_budget_ms", 8);
|
||||
ProjectSettings::get_singleton()->set_custom_property_info("voxel/threads/main/time_budget_ms",
|
||||
PropertyInfo(Variant::INT, "voxel/threads/main/time_budget_ms", PROPERTY_HINT_RANGE, "0,1000"));
|
||||
|
||||
_main_thread_time_budget_usec =
|
||||
1000 * int(ProjectSettings::get_singleton()->get("voxel/threads/main/time_budget_ms"));
|
||||
|
||||
const int minimum_thread_count =
|
||||
math::max(1, int(ProjectSettings::get_singleton()->get("voxel/threads/count/minimum")));
|
||||
|
||||
// How many threads below available count on the CPU should we set as limit
|
||||
const int thread_count_margin =
|
||||
math::max(1, int(ProjectSettings::get_singleton()->get("voxel/threads/count/margin_below_max")));
|
||||
|
||||
// Portion of available CPU threads to attempt using
|
||||
const float threads_ratio =
|
||||
math::clamp(float(ProjectSettings::get_singleton()->get("voxel/threads/count/ratio_over_max")), 0.f, 1.f);
|
||||
|
||||
const int maximum_thread_count = math::max(hw_threads_hint - thread_count_margin, minimum_thread_count);
|
||||
const int maximum_thread_count = math::max(
|
||||
hw_threads_hint - threads_config.thread_count_margin_below_max, threads_config.thread_count_minimum);
|
||||
// `-1` is for the stream thread
|
||||
const int thread_count_by_ratio = int(Math::round(float(threads_ratio) * hw_threads_hint)) - 1;
|
||||
const int thread_count = math::clamp(thread_count_by_ratio, minimum_thread_count, maximum_thread_count);
|
||||
const int thread_count_by_ratio =
|
||||
int(Math::round(float(threads_config.thread_count_ratio_over_max) * hw_threads_hint)) - 1;
|
||||
const int thread_count =
|
||||
math::clamp(thread_count_by_ratio, threads_config.thread_count_minimum, maximum_thread_count);
|
||||
ZN_PRINT_VERBOSE(format("Voxel: automatic thread count set to {}", thread_count));
|
||||
|
||||
if (thread_count > hw_threads_hint) {
|
||||
WARN_PRINT("Configured thread count exceeds hardware thread count. Performance may not be optimal");
|
||||
ZN_PRINT_WARNING("Configured thread count exceeds hardware thread count. Performance may not be optimal");
|
||||
}
|
||||
|
||||
// I/O can't be more than 1 thread. File access with more threads isn't worth it.
|
||||
|
@ -244,6 +215,10 @@ int VoxelServer::get_main_thread_time_budget_usec() const {
|
|||
return _main_thread_time_budget_usec;
|
||||
}
|
||||
|
||||
void VoxelServer::set_main_thread_time_budget_usec(unsigned int usec) {
|
||||
_main_thread_time_budget_usec = usec;
|
||||
}
|
||||
|
||||
void VoxelServer::push_async_task(zylann::IThreadedTask *task) {
|
||||
_general_thread_pool.enqueue(task);
|
||||
}
|
||||
|
@ -327,30 +302,6 @@ static VoxelServer::Stats::ThreadPoolStats debug_get_pool_stats(const zylann::Th
|
|||
return d;
|
||||
}
|
||||
|
||||
Dictionary VoxelServer::Stats::to_dict() {
|
||||
Dictionary pools;
|
||||
pools["streaming"] = streaming.to_dict();
|
||||
pools["general"] = general.to_dict();
|
||||
|
||||
Dictionary tasks;
|
||||
tasks["streaming"] = streaming_tasks;
|
||||
tasks["generation"] = generation_tasks;
|
||||
tasks["meshing"] = meshing_tasks;
|
||||
tasks["main_thread"] = main_thread_tasks;
|
||||
|
||||
// This part is additional for scripts because VoxelMemoryPool is not exposed
|
||||
Dictionary mem;
|
||||
mem["voxel_total"] = ZN_SIZE_T_TO_VARIANT(VoxelMemoryPool::get_singleton().debug_get_total_memory());
|
||||
mem["voxel_used"] = ZN_SIZE_T_TO_VARIANT(VoxelMemoryPool::get_singleton().debug_get_used_memory());
|
||||
mem["block_count"] = VoxelMemoryPool::get_singleton().debug_get_used_blocks();
|
||||
|
||||
Dictionary d;
|
||||
d["thread_pools"] = pools;
|
||||
d["tasks"] = tasks;
|
||||
d["memory_pools"] = mem;
|
||||
return d;
|
||||
}
|
||||
|
||||
VoxelServer::Stats VoxelServer::get_stats() const {
|
||||
Stats s;
|
||||
s.streaming = debug_get_pool_stats(_streaming_thread_pool);
|
||||
|
|
|
@ -78,12 +78,17 @@ public:
|
|||
int network_peer_id = -1;
|
||||
};
|
||||
|
||||
static VoxelServer &get_singleton();
|
||||
static void create_singleton();
|
||||
static void destroy_singleton();
|
||||
struct ThreadsConfig {
|
||||
int thread_count_minimum = 1;
|
||||
// How many threads below available count on the CPU should we set as limit
|
||||
int thread_count_margin_below_max = 1;
|
||||
// Portion of available CPU threads to attempt using
|
||||
float thread_count_ratio_over_max = 0.5;
|
||||
};
|
||||
|
||||
VoxelServer();
|
||||
~VoxelServer();
|
||||
static VoxelServer &get_singleton();
|
||||
static void create_singleton(ThreadsConfig threads_config);
|
||||
static void destroy_singleton();
|
||||
|
||||
uint32_t add_volume(VolumeCallbacks callbacks);
|
||||
VolumeCallbacks get_volume_callbacks(uint32_t volume_id) const;
|
||||
|
@ -117,6 +122,7 @@ public:
|
|||
|
||||
void push_main_thread_time_spread_task(ITimeSpreadTask *task);
|
||||
int get_main_thread_time_budget_usec() const;
|
||||
void set_main_thread_time_budget_usec(unsigned int usec);
|
||||
|
||||
void push_main_thread_progressive_task(IProgressiveTask *task);
|
||||
|
||||
|
@ -129,11 +135,6 @@ public:
|
|||
// Thread-safe.
|
||||
void push_async_io_tasks(Span<IThreadedTask *> tasks);
|
||||
|
||||
// Gets by how much voxels must be padded with neighbors in order to be polygonized properly
|
||||
// void get_min_max_block_padding(
|
||||
// bool blocky_enabled, bool smooth_enabled,
|
||||
// unsigned int &out_min_padding, unsigned int &out_max_padding) const;
|
||||
|
||||
void process();
|
||||
void wait_and_clear_all_tasks(bool warn);
|
||||
|
||||
|
@ -153,14 +154,6 @@ public:
|
|||
unsigned int thread_count;
|
||||
unsigned int active_threads;
|
||||
unsigned int tasks;
|
||||
|
||||
Dictionary to_dict() {
|
||||
Dictionary d;
|
||||
d["tasks"] = tasks;
|
||||
d["active_threads"] = active_threads;
|
||||
d["thread_count"] = thread_count;
|
||||
return d;
|
||||
}
|
||||
};
|
||||
|
||||
ThreadPoolStats streaming;
|
||||
|
@ -169,13 +162,16 @@ public:
|
|||
int streaming_tasks;
|
||||
int meshing_tasks;
|
||||
int main_thread_tasks;
|
||||
|
||||
Dictionary to_dict();
|
||||
};
|
||||
|
||||
Stats get_stats() const;
|
||||
|
||||
// TODO Should be private, but can't because `memdelete<T>` would be unable to call it otherwise...
|
||||
~VoxelServer();
|
||||
|
||||
private:
|
||||
VoxelServer(ThreadsConfig threads_config);
|
||||
|
||||
// Since we are going to send data to tasks running in multiple threads, a few strategies are in place:
|
||||
//
|
||||
// - Copy the data for each task. This is suitable for simple information that doesn't change after scheduling.
|
||||
|
@ -209,7 +205,7 @@ private:
|
|||
ThreadedTaskRunner _general_thread_pool;
|
||||
// For tasks that can only run on the main thread and be spread out over frames
|
||||
TimeSpreadTaskRunner _time_spread_task_runner;
|
||||
int _main_thread_time_budget_usec = 8000;
|
||||
unsigned int _main_thread_time_budget_usec = 8000;
|
||||
ProgressiveTaskRunner _progressive_task_runner;
|
||||
|
||||
FileLocker _file_locker;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "voxel_server_gd.h"
|
||||
#include "../storage/voxel_memory_pool.h"
|
||||
#include "../util/macros.h"
|
||||
#include "../util/profiling.h"
|
||||
#include "../util/tasks/godot/threaded_task_gd.h"
|
||||
|
@ -31,8 +32,40 @@ VoxelServer::VoxelServer() {
|
|||
#endif
|
||||
}
|
||||
|
||||
Dictionary to_dict(const zylann::voxel::VoxelServer::Stats::ThreadPoolStats &stats) {
|
||||
Dictionary d;
|
||||
d["tasks"] = stats.tasks;
|
||||
d["active_threads"] = stats.active_threads;
|
||||
d["thread_count"] = stats.thread_count;
|
||||
return d;
|
||||
}
|
||||
|
||||
Dictionary to_dict(const zylann::voxel::VoxelServer::Stats &stats) {
|
||||
Dictionary pools;
|
||||
pools["streaming"] = to_dict(stats.streaming);
|
||||
pools["general"] = to_dict(stats.general);
|
||||
|
||||
Dictionary tasks;
|
||||
tasks["streaming"] = stats.streaming_tasks;
|
||||
tasks["generation"] = stats.generation_tasks;
|
||||
tasks["meshing"] = stats.meshing_tasks;
|
||||
tasks["main_thread"] = stats.main_thread_tasks;
|
||||
|
||||
// This part is additional for scripts because VoxelMemoryPool is not exposed
|
||||
Dictionary mem;
|
||||
mem["voxel_total"] = ZN_SIZE_T_TO_VARIANT(VoxelMemoryPool::get_singleton().debug_get_total_memory());
|
||||
mem["voxel_used"] = ZN_SIZE_T_TO_VARIANT(VoxelMemoryPool::get_singleton().debug_get_used_memory());
|
||||
mem["block_count"] = VoxelMemoryPool::get_singleton().debug_get_used_blocks();
|
||||
|
||||
Dictionary d;
|
||||
d["thread_pools"] = pools;
|
||||
d["tasks"] = tasks;
|
||||
d["memory_pools"] = mem;
|
||||
return d;
|
||||
}
|
||||
|
||||
Dictionary VoxelServer::get_stats() const {
|
||||
return zylann::voxel::VoxelServer::get_singleton().get_stats().to_dict();
|
||||
return to_dict(zylann::voxel::VoxelServer::get_singleton().get_stats());
|
||||
}
|
||||
|
||||
void VoxelServer::schedule_task(Ref<ZN_ThreadedTask> task) {
|
||||
|
|
Loading…
Reference in New Issue