VoxelServer no longer inherits Object.

The Godot-facing singleton is now a different class in gd:: namespace.
Moved the updater to its own file.
master
Marc Gilleron 2022-01-09 04:53:33 +00:00
parent d690ef0963
commit 774adfca3c
19 changed files with 225 additions and 146 deletions

View File

@ -10,6 +10,7 @@
#include <scene/gui/menu_button.h>
using namespace zylann;
using namespace voxel;
class VoxelTerrainEditorTaskIndicator : public HBoxContainer {
GDCLASS(VoxelTerrainEditorTaskIndicator, HBoxContainer)

View File

@ -18,6 +18,7 @@
#include "meshers/cubes/voxel_mesher_cubes.h"
#include "meshers/dmc/voxel_mesher_dmc.h"
#include "meshers/transvoxel/voxel_mesher_transvoxel.h"
#include "server/voxel_server_gd.h"
#include "storage/voxel_buffer.h"
#include "storage/voxel_memory_pool.h"
#include "streams/region/voxel_stream_region_files.h"
@ -69,11 +70,12 @@ void register_voxel_types() {
VoxelStringNames::create_singleton();
VoxelGraphNodeDB::create_singleton();
VoxelServer::create_singleton();
gd::VoxelServer::create_singleton();
Engine::get_singleton()->add_singleton(Engine::Singleton("VoxelServer", VoxelServer::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("VoxelServer", gd::VoxelServer::get_singleton()));
// TODO Can I prevent users from instancing it? is "register_virtual_class" correct for a class that's not abstract?
ClassDB::register_class<VoxelServer>();
ClassDB::register_class<gd::VoxelServer>();
// Misc
ClassDB::register_class<Voxel>();
@ -181,6 +183,7 @@ void unregister_voxel_types() {
VoxelStringNames::destroy_singleton();
VoxelGraphNodeDB::destroy_singleton();
gd::VoxelServer::destroy_singleton();
VoxelServer::destroy_singleton();
// Do this last as VoxelServer might still be holding some refs to voxel blocks

View File

@ -7,20 +7,17 @@
#include "../util/profiling.h"
#include "../util/tasks/async_dependency_tracker.h"
#include <core/config/project_settings.h>
#include <core/os/memory.h>
#include <scene/main/window.h> // Needed for doing `Node *root = SceneTree::get_root()`, Window* is forward-declared
#include <thread>
using namespace zylann;
using namespace voxel;
namespace zylann::voxel {
namespace {
VoxelServer *g_voxel_server = nullptr;
// Could be atomics, but it's for debugging so I don't bother for now
int g_debug_generate_tasks_count = 0;
int g_debug_stream_tasks_count = 0;
int g_debug_mesh_tasks_count = 0;
} // namespace
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@ -43,12 +40,6 @@ void VoxelServer::destroy_singleton() {
VoxelServer::VoxelServer() {
CRASH_COND(ProjectSettings::get_singleton() == nullptr);
#ifdef VOXEL_PROFILER_ENABLED
CRASH_COND(RenderingServer::get_singleton() == nullptr);
RenderingServer::get_singleton()->connect(
SNAME("frame_post_draw"), callable_mp(this, &VoxelServer::_on_rendering_server_frame_post_draw));
#endif
const int hw_threads_hint = std::thread::hardware_concurrency();
PRINT_VERBOSE(String("Voxel: HW threads hint: {0}").format(varray(hw_threads_hint)));
@ -573,12 +564,6 @@ void VoxelServer::push_async_tasks(Span<zylann::IThreadedTask *> tasks) {
_general_thread_pool.enqueue(tasks);
}
void VoxelServer::_on_rendering_server_frame_post_draw() {
#ifdef VOXEL_PROFILER_ENABLED
VOXEL_PROFILE_MARK_FRAME();
#endif
}
void VoxelServer::process() {
VOXEL_PROFILE_SCOPE();
VOXEL_PROFILE_PLOT("Static memory usage", int64_t(OS::get_singleton()->get_static_memory_usage()));
@ -681,14 +666,6 @@ VoxelServer::Stats VoxelServer::get_stats() const {
return s;
}
Dictionary VoxelServer::_b_get_stats() {
return get_stats().to_dict();
}
void VoxelServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_stats"), &VoxelServer::_b_get_stats);
}
//----------------------------------------------------------------------------------------------------------------------
VoxelServer::BlockDataRequest::BlockDataRequest() {
@ -1199,53 +1176,4 @@ void VoxelServer::BlockMeshRequest::apply_result() {
}
}
//----------------------------------------------------------------------------------------------------------------------
namespace {
bool g_updater_created = false;
}
VoxelServerUpdater::VoxelServerUpdater() {
PRINT_VERBOSE("Creating VoxelServerUpdater");
set_process(true);
g_updater_created = true;
}
VoxelServerUpdater::~VoxelServerUpdater() {
g_updater_created = false;
}
void VoxelServerUpdater::ensure_existence(SceneTree *st) {
if (st == nullptr) {
return;
}
if (g_updater_created) {
return;
}
Node *root = st->get_root();
for (int i = 0; i < root->get_child_count(); ++i) {
VoxelServerUpdater *u = Object::cast_to<VoxelServerUpdater>(root->get_child(i));
if (u != nullptr) {
return;
}
}
VoxelServerUpdater *u = memnew(VoxelServerUpdater);
u->set_name("VoxelServerUpdater_dont_touch_this");
root->add_child(u);
}
void VoxelServerUpdater::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_PROCESS:
// To workaround the absence of API to have a custom server processing in the main loop
VoxelServer::get_singleton()->process();
break;
case NOTIFICATION_PREDELETE:
PRINT_VERBOSE("Deleting VoxelServerUpdater");
break;
default:
break;
}
}
} // namespace zylann::voxel

View File

@ -10,8 +10,6 @@
#include "../util/tasks/time_spread_task_runner.h"
#include "struct_db.h"
#include <scene/main/node.h>
#include <memory>
class VoxelNode;
@ -20,12 +18,11 @@ namespace zylann {
class AsyncDependencyTracker;
}
// TODO Don't inherit Object. Instead have a Godot wrapper, there is very little use for Object stuff
namespace zylann::voxel {
// Access point for asynchronous voxel processing APIs.
// Functions must be used from the main thread.
class VoxelServer : public Object {
GDCLASS(VoxelServer, Object)
class VoxelServer {
public:
struct BlockMeshOutput {
enum Type {
@ -40,10 +37,13 @@ public:
};
struct BlockDataOutput {
enum Type { TYPE_LOAD, TYPE_SAVE };
enum Type { //
TYPE_LOAD,
TYPE_SAVE
};
Type type;
std::shared_ptr<zylann::voxel::VoxelBufferInternal> voxels;
std::shared_ptr<VoxelBufferInternal> voxels;
std::unique_ptr<VoxelInstanceBlockData> instances;
Vector3i position;
uint8_t lod;
@ -55,9 +55,7 @@ public:
struct BlockMeshInput {
// Moore area ordered by forward XYZ iteration
zylann::FixedArray<std::shared_ptr<zylann::voxel::VoxelBufferInternal>,
zylann::voxel::constants::MAX_BLOCK_COUNT_PER_REQUEST>
data_blocks;
FixedArray<std::shared_ptr<VoxelBufferInternal>, constants::MAX_BLOCK_COUNT_PER_REQUEST> data_blocks;
unsigned int data_blocks_count = 0;
Vector3i render_block_position;
uint8_t lod = 0;
@ -115,10 +113,10 @@ public:
// TODO Add parameter to skip stream loading
void request_block_load(uint32_t volume_id, Vector3i block_pos, int lod, bool request_instances);
void request_block_generate(
uint32_t volume_id, Vector3i block_pos, int lod, std::shared_ptr<zylann::AsyncDependencyTracker> tracker);
uint32_t volume_id, Vector3i block_pos, int lod, std::shared_ptr<AsyncDependencyTracker> tracker);
void request_all_stream_blocks(uint32_t volume_id);
void request_voxel_block_save(uint32_t volume_id, std::shared_ptr<zylann::voxel::VoxelBufferInternal> voxels,
Vector3i block_pos, int lod);
void request_voxel_block_save(
uint32_t volume_id, std::shared_ptr<VoxelBufferInternal> voxels, Vector3i block_pos, int lod);
void request_instance_block_save(
uint32_t volume_id, std::unique_ptr<VoxelInstanceBlockData> instances, Vector3i block_pos, int lod);
void remove_volume(uint32_t volume_id);
@ -141,13 +139,13 @@ public:
_world.viewers.for_each_with_id(f);
}
void push_time_spread_task(zylann::ITimeSpreadTask *task);
void push_time_spread_task(ITimeSpreadTask *task);
int get_main_thread_time_budget_usec() const;
void push_progressive_task(zylann::IProgressiveTask *task);
void push_progressive_task(IProgressiveTask *task);
void push_async_task(zylann::IThreadedTask *task);
void push_async_tasks(Span<zylann::IThreadedTask *> tasks);
void push_async_task(IThreadedTask *task);
void push_async_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(
@ -157,7 +155,7 @@ public:
void process();
void wait_and_clear_all_tasks(bool warn);
inline zylann::FileLocker &get_file_locker() {
inline FileLocker &get_file_locker() {
return _file_locker;
}
@ -201,11 +199,6 @@ private:
void request_block_generate_from_data_request(BlockDataRequest &src);
void request_block_save_from_generate_request(BlockGenerateRequest &src);
void _on_rendering_server_frame_post_draw();
Dictionary _b_get_stats();
static void _bind_methods();
// Since we are going to send data to tasks running in multiple threads, a few strategies are in place:
//
@ -255,8 +248,8 @@ private:
};
struct World {
zylann::StructDB<Volume> volumes;
zylann::StructDB<Viewer> viewers;
StructDB<Volume> volumes;
StructDB<Viewer> viewers;
// Must be overwritten with a new instance if count changes.
std::shared_ptr<PriorityDependencyShared> shared_priority_dependency;
@ -273,7 +266,7 @@ private:
PriorityDependency &dep, Vector3i block_position, uint8_t lod, const Volume &volume, int block_size);
static int get_priority(const PriorityDependency &dep, uint8_t lod_index, float *out_closest_distance_sq);
class BlockDataRequest : public zylann::IThreadedTask {
class BlockDataRequest : public IThreadedTask {
public:
enum Type { //
TYPE_LOAD = 0,
@ -284,12 +277,12 @@ private:
BlockDataRequest();
~BlockDataRequest();
void run(zylann::ThreadedTaskContext ctx) override;
void run(ThreadedTaskContext ctx) override;
int get_priority() override;
bool is_cancelled() override;
void apply_result() override;
std::shared_ptr<zylann::voxel::VoxelBufferInternal> voxels;
std::shared_ptr<VoxelBufferInternal> voxels;
std::unique_ptr<VoxelInstanceBlockData> instances;
Vector3i position; // In data blocks of the specified lod
uint32_t volume_id;
@ -306,12 +299,12 @@ private:
// TODO Find a way to separate save, it doesnt need sorting
};
class AllBlocksDataRequest : public zylann::IThreadedTask {
class AllBlocksDataRequest : public IThreadedTask {
public:
AllBlocksDataRequest();
~AllBlocksDataRequest();
void run(zylann::ThreadedTaskContext ctx) override;
void run(ThreadedTaskContext ctx) override;
int get_priority() override;
bool is_cancelled() override;
void apply_result() override;
@ -321,17 +314,17 @@ private:
std::shared_ptr<StreamingDependency> stream_dependency;
};
class BlockGenerateRequest : public zylann::IThreadedTask {
class BlockGenerateRequest : public IThreadedTask {
public:
BlockGenerateRequest();
~BlockGenerateRequest();
void run(zylann::ThreadedTaskContext ctx) override;
void run(ThreadedTaskContext ctx) override;
int get_priority() override;
bool is_cancelled() override;
void apply_result() override;
std::shared_ptr<zylann::voxel::VoxelBufferInternal> voxels;
std::shared_ptr<VoxelBufferInternal> voxels;
Vector3i position;
uint32_t volume_id;
uint8_t lod;
@ -342,22 +335,20 @@ private:
bool drop_beyond_max_distance = true;
PriorityDependency priority_dependency;
std::shared_ptr<StreamingDependency> stream_dependency;
std::shared_ptr<zylann::AsyncDependencyTracker> tracker;
std::shared_ptr<AsyncDependencyTracker> tracker;
};
class BlockMeshRequest : public zylann::IThreadedTask {
class BlockMeshRequest : public IThreadedTask {
public:
BlockMeshRequest();
~BlockMeshRequest();
void run(zylann::ThreadedTaskContext ctx) override;
void run(ThreadedTaskContext ctx) override;
int get_priority() override;
bool is_cancelled() override;
void apply_result() override;
zylann::FixedArray<std::shared_ptr<zylann::voxel::VoxelBufferInternal>,
zylann::voxel::constants::MAX_BLOCK_COUNT_PER_REQUEST>
blocks;
FixedArray<std::shared_ptr<VoxelBufferInternal>, constants::MAX_BLOCK_COUNT_PER_REQUEST> blocks;
// TODO Need to provide format
//FixedArray<uint8_t, VoxelBufferInternal::MAX_CHANNELS> channel_depths;
Vector3i position; // In mesh blocks of the specified lod
@ -376,29 +367,15 @@ private:
World _world;
// Pool specialized in file I/O
zylann::ThreadedTaskRunner _streaming_thread_pool;
ThreadedTaskRunner _streaming_thread_pool;
// Pool for every other task
zylann::ThreadedTaskRunner _general_thread_pool;
ThreadedTaskRunner _general_thread_pool;
// For tasks that can only run on the main thread and be spread out over frames
zylann::TimeSpreadTaskRunner _time_spread_task_runner;
TimeSpreadTaskRunner _time_spread_task_runner;
int _main_thread_time_budget_usec = 8000;
zylann::ProgressiveTaskRunner _progressive_task_runner;
ProgressiveTaskRunner _progressive_task_runner;
zylann::FileLocker _file_locker;
};
// TODO Hack to make VoxelServer update... need ways to integrate callbacks from main loop!
class VoxelServerUpdater : public Node {
GDCLASS(VoxelServerUpdater, Node)
public:
~VoxelServerUpdater();
static void ensure_existence(SceneTree *st);
protected:
void _notification(int p_what);
private:
VoxelServerUpdater();
FileLocker _file_locker;
};
struct VoxelFileLockerRead {
@ -425,4 +402,6 @@ struct VoxelFileLockerWrite {
String _path;
};
} // namespace zylann::voxel
#endif // VOXEL_SERVER_H

View File

@ -0,0 +1,47 @@
#include "voxel_server_gd.h"
#include "../util/macros.h"
#include "../util/profiling.h"
#include "voxel_server.h"
namespace zylann::voxel::gd {
VoxelServer *g_voxel_server = nullptr;
VoxelServer *VoxelServer::get_singleton() {
CRASH_COND_MSG(g_voxel_server == nullptr, "Accessing singleton while it's null");
return g_voxel_server;
}
void VoxelServer::create_singleton() {
CRASH_COND_MSG(g_voxel_server != nullptr, "Creating singleton twice");
g_voxel_server = memnew(VoxelServer);
}
void VoxelServer::destroy_singleton() {
CRASH_COND_MSG(g_voxel_server == nullptr, "Destroying singleton twice");
memdelete(g_voxel_server);
g_voxel_server = nullptr;
}
VoxelServer::VoxelServer() {
#ifdef VOXEL_PROFILER_ENABLED
CRASH_COND(RenderingServer::get_singleton() == nullptr);
RenderingServer::get_singleton()->connect(
SNAME("frame_post_draw"), callable_mp(this, &VoxelServer::_on_rendering_server_frame_post_draw));
#endif
}
Dictionary VoxelServer::get_stats() const {
return zylann::voxel::VoxelServer::get_singleton()->get_stats().to_dict();
}
void VoxelServer::_on_rendering_server_frame_post_draw() {
#ifdef VOXEL_PROFILER_ENABLED
VOXEL_PROFILE_MARK_FRAME();
#endif
}
void VoxelServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_stats"), &VoxelServer::get_stats);
}
} // namespace zylann::voxel::gd

30
server/voxel_server_gd.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef VOXEL_SERVER_GD_H
#define VOXEL_SERVER_GD_H
#include "core/object/class_db.h"
#include <core/object/object.h>
namespace zylann::voxel::gd {
// Godot-facing singleton class.
// the real class is internal and does not need anything from Object.
class VoxelServer : public Object {
GDCLASS(VoxelServer, Object)
public:
Dictionary get_stats() const;
VoxelServer();
static VoxelServer *get_singleton();
static void create_singleton();
static void destroy_singleton();
private:
void _on_rendering_server_frame_post_draw();
static void _bind_methods();
};
} // namespace zylann::voxel::gd
#endif // VOXEL_SERVER_GD_H

View File

@ -0,0 +1,55 @@
#include "voxel_server_updater.h"
#include "../util/macros.h"
#include "voxel_server.h"
// Needed for doing `Node *root = SceneTree::get_root()`, Window* is forward-declared
#include <scene/main/window.h>
namespace {
bool g_updater_created = false;
}
VoxelServerUpdater::VoxelServerUpdater() {
PRINT_VERBOSE("Creating VoxelServerUpdater");
set_process(true);
g_updater_created = true;
}
VoxelServerUpdater::~VoxelServerUpdater() {
g_updater_created = false;
}
void VoxelServerUpdater::ensure_existence(SceneTree *st) {
if (st == nullptr) {
return;
}
if (g_updater_created) {
return;
}
Node *root = st->get_root();
for (int i = 0; i < root->get_child_count(); ++i) {
VoxelServerUpdater *u = Object::cast_to<VoxelServerUpdater>(root->get_child(i));
if (u != nullptr) {
return;
}
}
VoxelServerUpdater *u = memnew(VoxelServerUpdater);
u->set_name("VoxelServerUpdater_dont_touch_this");
root->add_child(u);
}
void VoxelServerUpdater::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_PROCESS:
// To workaround the absence of API to have a custom server processing in the main loop
zylann::voxel::VoxelServer::get_singleton()->process();
break;
case NOTIFICATION_PREDELETE:
PRINT_VERBOSE("Deleting VoxelServerUpdater");
break;
default:
break;
}
}

View File

@ -0,0 +1,20 @@
#ifndef VOXEL_SERVER_UPDATER_H
#define VOXEL_SERVER_UPDATER_H
#include <scene/main/node.h>
// TODO Hack to make VoxelServer update... need ways to integrate callbacks from main loop!
class VoxelServerUpdater : public Node {
GDCLASS(VoxelServerUpdater, Node)
public:
~VoxelServerUpdater();
static void ensure_existence(SceneTree *st);
protected:
void _notification(int p_what);
private:
VoxelServerUpdater();
};
#endif // VOXEL_SERVER_UPDATER_H

View File

@ -47,7 +47,6 @@ FileResult check_magic_and_version(
}
Error check_directory_created(const String &directory_path) {
VoxelFileLockerWrite file_wlock(directory_path);
DirAccess *d = DirAccess::create_for_path(directory_path);
if (d == nullptr) {
@ -69,6 +68,13 @@ Error check_directory_created(const String &directory_path) {
return OK;
}
namespace voxel {
Error check_directory_created_using_file_locker(const String &directory_path) {
VoxelFileLockerWrite file_wlock(directory_path);
return check_directory_created(directory_path);
}
} // namespace voxel
// TODO Write tests
// Makes the file bigger to move the half from the current position further,

View File

@ -48,7 +48,11 @@ enum FileResult {
const char *to_string(FileResult res);
FileResult check_magic_and_version(
FileAccess *f, uint8_t expected_version, const char *expected_magic, uint8_t &out_version);
Error check_directory_created(const String &directory_path);
namespace voxel {
// Specific to voxel because it uses a global lock found only in VoxelServer
Error check_directory_created_using_file_locker(const String &directory_path);
} // namespace voxel
void insert_bytes(FileAccess *f, size_t count, size_t temp_chunk_size = 512);

View File

@ -195,7 +195,7 @@ Error RegionFile::open(const String &fpath, bool create_if_not_found) {
CRASH_COND(f != nullptr);
// Checking folders, needed for region "forests"
const Error dir_err = check_directory_created(fpath.get_base_dir());
const Error dir_err = check_directory_created_using_file_locker(fpath.get_base_dir());
if (dir_err != OK) {
return ERR_CANT_CREATE;
}

View File

@ -288,7 +288,7 @@ FileResult VoxelStreamRegionFiles::save_meta() {
// Make sure the directory exists
{
Error err = check_directory_created(_directory_path);
Error err = check_directory_created_using_file_locker(_directory_path);
if (err != OK) {
ERR_PRINT("Could not save meta");
return FILE_CANT_OPEN;

View File

@ -127,7 +127,7 @@ void VoxelStreamBlockFiles::immerge_block(VoxelBufferInternal &buffer, Vector3i
String file_path = get_block_file_path(block_pos, lod);
{
const Error err = check_directory_created(file_path.get_base_dir());
const Error err = check_directory_created_using_file_locker(file_path.get_base_dir());
ERR_FAIL_COND(err != OK);
}
@ -183,7 +183,7 @@ FileResult VoxelStreamBlockFiles::save_meta() {
// Make sure the directory exists
{
Error err = check_directory_created(_directory_path);
Error err = check_directory_created_using_file_locker(_directory_path);
if (err != OK) {
ERR_PRINT("Could not save meta");
return FILE_CANT_OPEN;

View File

@ -18,6 +18,7 @@
#include <algorithm>
using namespace zylann;
using namespace voxel;
VoxelInstancer::VoxelInstancer() {
set_notify_transform(true);

View File

@ -2,7 +2,8 @@
#include "../constants/voxel_string_names.h"
#include "../edition/voxel_tool_lod_terrain.h"
#include "../meshers/transvoxel/voxel_mesher_transvoxel.h"
#include "../server/voxel_server.h"
#include "../server/voxel_server_gd.h"
#include "../server/voxel_server_updater.h"
#include "../util/funcs.h"
#include "../util/godot/funcs.h"
#include "../util/macros.h"

View File

@ -247,8 +247,8 @@ private:
//void process_block_loading_responses();
void send_mesh_requests();
void apply_mesh_update(const VoxelServer::BlockMeshOutput &ob);
void apply_data_block_response(VoxelServer::BlockDataOutput &ob);
void apply_mesh_update(const zylann::voxel::VoxelServer::BlockMeshOutput &ob);
void apply_data_block_response(zylann::voxel::VoxelServer::BlockDataOutput &ob);
void unload_data_block_no_lock(Vector3i block_pos, uint8_t lod_index, std::vector<BlockToSave> &blocks_to_save);
void unload_mesh_block(Vector3i block_pos, uint8_t lod_index);

View File

@ -3,6 +3,7 @@
#include "../constants/voxel_string_names.h"
#include "../edition/voxel_tool_terrain.h"
#include "../server/voxel_server.h"
#include "../server/voxel_server_updater.h"
#include "../util/funcs.h"
#include "../util/godot/funcs.h"
#include "../util/macros.h"

View File

@ -121,8 +121,8 @@ private:
void process_viewers();
//void process_received_data_blocks();
void process_meshing();
void apply_mesh_update(const VoxelServer::BlockMeshOutput &ob);
void apply_data_block_response(VoxelServer::BlockDataOutput &ob);
void apply_mesh_update(const zylann::voxel::VoxelServer::BlockMeshOutput &ob);
void apply_data_block_response(zylann::voxel::VoxelServer::BlockDataOutput &ob);
void _on_stream_params_changed();
void _set_block_size_po2(int p_block_size_po2);

View File

@ -2,6 +2,9 @@
#include "../server/voxel_server.h"
#include <core/config/engine.h>
using namespace zylann;
using namespace voxel;
VoxelViewer::VoxelViewer() {
set_notify_transform(!Engine::get_singleton()->is_editor_hint());
}