Added class to schedule custom tasks from scripts

master
Marc Gilleron 2022-04-07 22:34:07 +01:00
parent 22f17f6294
commit f58199e7de
7 changed files with 127 additions and 0 deletions

1
SCsub
View File

@ -39,6 +39,7 @@ voxel_files = [
"util/noise/gd_noise_range.cpp",
"util/tasks/*.cpp",
"util/tasks/godot/*.cpp",
"terrain/*.cpp",
"terrain/instancing/*.cpp",

View File

@ -18,6 +18,7 @@ Godot 4 is required from this version.
- Added `FastNoise2` for faster SIMD noise
- Added experimental support functions to help setting up basic multiplayer with `VoxelTerrain` (might change in the future)
- Improved support for 64-bit floats
- Added `ZN_ThreadedTask` to allow running custom tasks using the thread pool system
- `VoxelGeneratorGraph`: added support for outputting to the TYPE channel, allowing use with `VoxelMesherBlocky`
- `VoxelGeneratorGraph`: editor: unconnected inputs show their default value directly on the node
- `VoxelGeneratorGraph`: editor: allow to change the axes on preview nodes 3D slices

View File

@ -38,6 +38,7 @@
#include "util/macros.h"
#include "util/noise/fast_noise_lite/fast_noise_lite.h"
#include "util/noise/fast_noise_lite/fast_noise_lite_gradient.h"
#include "util/tasks/godot/threaded_task_gd.h"
#ifdef VOXEL_ENABLE_FAST_NOISE_2
#include "util/noise/fast_noise_2.h"
@ -131,6 +132,7 @@ void register_voxel_types() {
ClassDB::register_class<VoxelVoxLoader>();
ClassDB::register_class<ZN_FastNoiseLite>();
ClassDB::register_class<ZN_FastNoiseLiteGradient>();
ClassDB::register_class<ZN_ThreadedTask>();
// See SCsub
#ifdef VOXEL_ENABLE_FAST_NOISE_2
ClassDB::register_class<FastNoise2>();

View File

@ -1,6 +1,7 @@
#include "voxel_server_gd.h"
#include "../util/macros.h"
#include "../util/profiling.h"
#include "../util/tasks/godot/threaded_task_gd.h"
#include "voxel_server.h"
namespace zylann::voxel::gd {
@ -34,6 +35,12 @@ Dictionary VoxelServer::get_stats() const {
return zylann::voxel::VoxelServer::get_singleton()->get_stats().to_dict();
}
void VoxelServer::schedule_task(Ref<ZN_ThreadedTask> task) {
ERR_FAIL_COND(task.is_null());
ERR_FAIL_COND_MSG(task->is_scheduled(), "Cannot schedule again a task that is already scheduled");
zylann::voxel::VoxelServer::get_singleton()->push_async_task(task->create_task());
}
void VoxelServer::_on_rendering_server_frame_post_draw() {
#ifdef VOXEL_PROFILER_ENABLED
VOXEL_PROFILE_MARK_FRAME();

View File

@ -4,6 +4,10 @@
#include "core/object/class_db.h"
#include <core/object/object.h>
namespace zylann {
class ZN_ThreadedTask;
} // namespace zylann
namespace zylann::voxel::gd {
// Godot-facing singleton class.
@ -12,6 +16,7 @@ class VoxelServer : public Object {
GDCLASS(VoxelServer, Object)
public:
Dictionary get_stats() const;
void schedule_task(Ref<ZN_ThreadedTask> task);
VoxelServer();

View File

@ -0,0 +1,72 @@
#include "threaded_task_gd.h"
namespace zylann {
// Using a decoupled pattern so we can do a few more safety checks for scripters
class ZN_ThreadedTaskInternal : public IThreadedTask {
public:
Ref<ZN_ThreadedTask> ref;
void run(ThreadedTaskContext ctx) override {
ref->run(ctx.thread_index);
}
void apply_result() override {
// Not exposed. Scripters may prefer to use a `completed` signal instead.
ref->mark_completed();
}
int get_priority() override {
return ref->get_priority();
}
bool is_cancelled() override {
return ref->is_cancelled();
}
};
void ZN_ThreadedTask::run(int thread_index) {
GDVIRTUAL_CALL(_run, thread_index);
}
int ZN_ThreadedTask::get_priority() {
int priority = 0;
if (GDVIRTUAL_CALL(_get_priority, priority)) {
return priority;
}
return 0;
}
bool ZN_ThreadedTask::is_cancelled() {
bool cancelled = false;
if (GDVIRTUAL_CALL(_is_cancelled, cancelled)) {
return cancelled;
}
return false;
}
bool ZN_ThreadedTask::is_scheduled() const {
return _scheduled_task != nullptr;
}
void ZN_ThreadedTask::mark_completed() {
_scheduled_task = nullptr;
emit_signal(SNAME("completed"));
}
IThreadedTask *ZN_ThreadedTask::create_task() {
CRASH_COND(_scheduled_task != nullptr);
_scheduled_task = memnew(ZN_ThreadedTaskInternal);
_scheduled_task->ref.reference_ptr(this);
return _scheduled_task;
}
void ZN_ThreadedTask::_bind_methods() {
ADD_SIGNAL(MethodInfo("completed"));
GDVIRTUAL_BIND(_run, "thread_index");
GDVIRTUAL_BIND(_get_priority);
GDVIRTUAL_BIND(_is_cancelled);
}
} // namespace zylann

View File

@ -0,0 +1,39 @@
#ifndef ZN_THREADED_TASK_GD
#define ZN_THREADED_TASK_GD
#include "../threaded_task.h"
#include <core/object/ref_counted.h>
#include <core/object/script_language.h> // needed for GDVIRTUAL macro
#include <core/object/gdvirtual.gen.inc> // Also needed for GDVIRTUAL macro...
namespace zylann {
class ZN_ThreadedTaskInternal;
class ZN_ThreadedTask : public RefCounted {
GDCLASS(ZN_ThreadedTask, RefCounted)
public:
void run(int thread_index);
int get_priority();
bool is_cancelled();
// Internal
bool is_scheduled() const;
void mark_completed();
IThreadedTask *create_task();
private:
GDVIRTUAL1(_run, int);
GDVIRTUAL0R(int, _get_priority);
GDVIRTUAL0R(bool, _is_cancelled);
static void _bind_methods();
// Created upon scheduling, owned by the task runner
ZN_ThreadedTaskInternal *_scheduled_task = nullptr;
bool _completed = false;
};
} // namespace zylann
#endif // ZN_THREADED_TASK_GD