Add some stats
parent
d8a8936f34
commit
bcfc8ce04e
|
@ -33,6 +33,11 @@ void register_voxel_types() {
|
|||
VoxelGraphNodeDB::create_singleton();
|
||||
VoxelServer::create_singleton();
|
||||
|
||||
Engine::get_singleton()->add_singleton(Engine::Singleton("VoxelServer", 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>();
|
||||
|
||||
// Storage
|
||||
ClassDB::register_class<VoxelBuffer>();
|
||||
ClassDB::register_class<VoxelMap>();
|
||||
|
@ -98,11 +103,12 @@ void unregister_voxel_types() {
|
|||
.format(varray(used_blocks)));
|
||||
}
|
||||
VoxelMemoryPool::destroy_singleton();
|
||||
// TODO No remove?
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
VoxelDebug::free_debug_box_mesh();
|
||||
|
||||
// TODO No remove?
|
||||
// TODO Seriously, no remove?
|
||||
//EditorPlugins::remove_by_type<VoxelGraphEditorPlugin>();
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -385,6 +385,36 @@ void VoxelServer::get_min_max_block_padding(
|
|||
}
|
||||
}
|
||||
|
||||
static unsigned int debug_get_active_thread_count(const VoxelThreadPool &pool) {
|
||||
unsigned int active_count = 0;
|
||||
for (unsigned int i = 0; i < pool.get_thread_count(); ++i) {
|
||||
VoxelThreadPool::State s = pool.get_thread_debug_state(i);
|
||||
if (s == VoxelThreadPool::STATE_RUNNING) {
|
||||
++active_count;
|
||||
}
|
||||
}
|
||||
return active_count;
|
||||
}
|
||||
|
||||
static Dictionary debug_get_pool_stats(const VoxelThreadPool &pool) {
|
||||
Dictionary d;
|
||||
d["tasks"] = pool.get_debug_remaining_tasks();
|
||||
d["active_threads"] = debug_get_active_thread_count(pool);
|
||||
d["thread_count"] = pool.get_thread_count();
|
||||
return d;
|
||||
}
|
||||
|
||||
Dictionary VoxelServer::_b_get_stats() {
|
||||
Dictionary d;
|
||||
d["streaming"] = debug_get_pool_stats(_streaming_thread_pool);
|
||||
d["meshing"] = debug_get_pool_stats(_meshing_thread_pool);
|
||||
return d;
|
||||
}
|
||||
|
||||
void VoxelServer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_stats"), &VoxelServer::_b_get_stats);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
void VoxelServer::BlockDataRequest::run(VoxelTaskContext ctx) {
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
|
||||
// Access point for asynchronous voxel processing APIs.
|
||||
// Functions must be used from the main thread.
|
||||
class VoxelServer {
|
||||
class VoxelServer : public Object {
|
||||
GDCLASS(VoxelServer, Object)
|
||||
public:
|
||||
struct BlockMeshOutput {
|
||||
enum Type {
|
||||
|
@ -74,6 +75,10 @@ public:
|
|||
void process();
|
||||
|
||||
private:
|
||||
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:
|
||||
//
|
||||
// - Copy the data for each thread. This is suitable for simple information that doesn't change after scheduling.
|
||||
|
|
|
@ -72,6 +72,7 @@ void VoxelThreadPool::enqueue(IVoxelTask *task) {
|
|||
TaskItem t;
|
||||
t.task = task;
|
||||
_tasks.push_back(t);
|
||||
++_debug_received_tasks;
|
||||
}
|
||||
// TODO Do I need to post a certain amount of times?
|
||||
_tasks_semaphore->post();
|
||||
|
@ -84,6 +85,7 @@ void VoxelThreadPool::enqueue(ArraySlice<IVoxelTask *> tasks) {
|
|||
TaskItem t;
|
||||
t.task = tasks[i];
|
||||
_tasks.push_back(t);
|
||||
++_debug_received_tasks;
|
||||
}
|
||||
}
|
||||
// TODO Do I need to post a certain amount of times?
|
||||
|
@ -148,6 +150,7 @@ void VoxelThreadPool::thread_func(ThreadData &data) {
|
|||
MutexLock lock(_completed_tasks_mutex);
|
||||
for (size_t i = 0; i < cancelled_tasks.size(); ++i) {
|
||||
_completed_tasks.push_back(cancelled_tasks[i]);
|
||||
++_debug_completed_tasks;
|
||||
}
|
||||
cancelled_tasks.clear();
|
||||
}
|
||||
|
@ -155,7 +158,7 @@ void VoxelThreadPool::thread_func(ThreadData &data) {
|
|||
//print_line(String("Processing {0} tasks").format(varray(tasks.size())));
|
||||
|
||||
if (tasks.empty()) {
|
||||
data.debug_state = STATE_WAITNG;
|
||||
data.debug_state = STATE_WAITING;
|
||||
|
||||
// Wait for more tasks
|
||||
data.waiting = true;
|
||||
|
@ -178,6 +181,7 @@ void VoxelThreadPool::thread_func(ThreadData &data) {
|
|||
for (size_t i = 0; i < tasks.size(); ++i) {
|
||||
TaskItem &item = tasks[i];
|
||||
_completed_tasks.push_back(item.task);
|
||||
++_debug_completed_tasks;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,6 +239,14 @@ void VoxelThreadPool::wait_for_all_tasks() {
|
|||
}
|
||||
}
|
||||
|
||||
// Debug information can be wrong, on some rare occasions.
|
||||
// The variables should be safely updated, but computing or reading from them is not thread safe.
|
||||
// Thought it wasnt worth locking for debugging.
|
||||
|
||||
VoxelThreadPool::State VoxelThreadPool::get_thread_debug_state(uint32_t i) const {
|
||||
return _threads[i].debug_state;
|
||||
}
|
||||
|
||||
unsigned int VoxelThreadPool::get_debug_remaining_tasks() const {
|
||||
return _debug_received_tasks - _debug_completed_tasks;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ public:
|
|||
enum State {
|
||||
STATE_RUNNING = 0,
|
||||
STATE_PICKING,
|
||||
STATE_WAITNG,
|
||||
STATE_WAITING,
|
||||
STATE_STOPPED
|
||||
};
|
||||
|
||||
|
@ -75,6 +75,7 @@ public:
|
|||
void wait_for_all_tasks();
|
||||
|
||||
State get_thread_debug_state(uint32_t i) const;
|
||||
unsigned int get_debug_remaining_tasks() const;
|
||||
|
||||
private:
|
||||
struct TaskItem {
|
||||
|
@ -110,6 +111,9 @@ private:
|
|||
|
||||
uint32_t _batch_count = 1;
|
||||
uint32_t _priority_update_period = 32;
|
||||
|
||||
unsigned int _debug_received_tasks = 0;
|
||||
unsigned int _debug_completed_tasks = 0;
|
||||
};
|
||||
|
||||
#endif // VOXEL_THREAD_TASK_MANAGER_H
|
||||
|
|
Loading…
Reference in New Issue