Moved Godot config access to module initialization

master
Marc Gilleron 2022-05-23 00:13:37 +01:00
parent acbc52e717
commit 6c47c177ab
4 changed files with 123 additions and 92 deletions

View File

@ -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>(

View File

@ -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);

View File

@ -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;

View File

@ -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) {