Update VoxelLodTerrain stats

This commit is contained in:
Marc Gilleron 2022-03-20 18:30:18 +00:00
parent f25a019c62
commit 669e5230ed
4 changed files with 97 additions and 44 deletions

View File

@ -1086,6 +1086,9 @@ inline bool check_block_sizes(int data_block_size, int mesh_block_size) {
void VoxelLodTerrain::_process(float delta) {
VOXEL_PROFILE_SCOPE();
_stats.dropped_block_loads = 0;
_stats.dropped_block_meshs = 0;
if (get_lod_count() == 0) {
// If there isn't a LOD 0, there is nothing to load
return;
@ -1232,6 +1235,12 @@ void VoxelLodTerrain::apply_main_thread_update_tasks() {
return false;
});
_stats.blocked_lods = state.stats.blocked_lods;
_stats.time_detect_required_blocks = state.stats.time_detect_required_blocks;
_stats.time_io_requests = state.stats.time_io_requests;
_stats.time_mesh_requests = state.stats.time_mesh_requests;
_stats.time_update_task = state.stats.time_total;
}
template <typename T>
@ -1650,16 +1659,18 @@ Dictionary VoxelLodTerrain::_b_get_statistics() const {
deferred_collision_updates += _deferred_collision_updates_per_lod[lod_index].size();
}
// Breakdown of time spent in _process
d["time_detect_required_blocks"] = _stats.time_detect_required_blocks;
d["time_request_blocks_to_load"] = _stats.time_request_blocks_to_load;
d["time_process_load_responses"] = _stats.time_process_load_responses;
d["time_request_blocks_to_update"] = _stats.time_request_blocks_to_update;
// Breakdown of information and time spent in _process and the update task.
// Update task
d["time_detect_required_blocks"] = _stats.time_detect_required_blocks;
d["time_io_requests"] = _stats.time_io_requests;
d["time_mesh_requests"] = _stats.time_mesh_requests;
d["time_update_task"] = _stats.time_update_task;
d["blocked_lods"] = _stats.blocked_lods;
// Process
d["dropped_block_loads"] = _stats.dropped_block_loads;
d["dropped_block_meshs"] = _stats.dropped_block_meshs;
d["updated_blocks"] = _stats.updated_blocks;
d["blocked_lods"] = _stats.blocked_lods;
return d;
}

View File

@ -178,14 +178,21 @@ public:
Ref<VoxelTool> get_voxel_tool();
struct Stats {
int blocked_lods = 0;
int updated_blocks = 0;
int dropped_block_loads = 0;
int dropped_block_meshs = 0;
// Amount of octree nodes waiting for data. It should reach zero when everything is loaded.
uint32_t blocked_lods = 0;
// How many data blocks were rejected this frame (due to loading too late for example).
uint32_t dropped_block_loads = 0;
// How many mesh blocks were rejected this frame (due to loading too late for example).
uint32_t dropped_block_meshs = 0;
// Time spent in the last update unloading unused blocks and detecting required ones, in microseconds
uint32_t time_detect_required_blocks = 0;
uint32_t time_request_blocks_to_load = 0;
uint32_t time_process_load_responses = 0;
uint32_t time_request_blocks_to_update = 0;
// Time spent in the last update requesting data blocks, in microseconds
uint32_t time_io_requests = 0;
// Time spent in the last update requesting meshes, in microseconds
uint32_t time_mesh_requests = 0;
// Total time spent in the last update task, in microseconds.
// This only includes the threadable part, not the whole `process` function.
uint32_t time_update_task = 0;
};
const Stats &get_stats() const;

View File

@ -126,6 +126,14 @@ struct VoxelLodTerrainUpdateData {
Box3i box;
};
struct Stats {
uint32_t blocked_lods;
uint32_t time_detect_required_blocks;
uint32_t time_io_requests;
uint32_t time_mesh_requests;
uint32_t time_total;
};
// Data modified by the update task
struct State {
// This terrain type is a sparse grid of octrees.
@ -146,6 +154,8 @@ struct VoxelLodTerrainUpdateData {
std::vector<AsyncEdit> pending_async_edits;
BinaryMutex pending_async_edits_mutex;
std::vector<RunningAsyncEdit> running_async_edits;
Stats stats;
};
// Set to true when the update task is finished

View File

@ -6,6 +6,7 @@
#include "../server/voxel_server.h"
#include "../util/godot/funcs.h"
#include "../util/profiling.h"
#include "../util/profiling_clock.h"
namespace zylann::voxel {
@ -968,7 +969,7 @@ static void process_octrees_fitting(VoxelLodTerrainUpdateData::State &state,
// Ideally, this stat should stabilize to zero.
// If not, something in block management prevents LODs from properly show up and should be fixed.
//_stats.blocked_lods += octree_actions.blocked_count;
state.stats.blocked_lods += octree_actions.blocked_count;
}
{
@ -1327,6 +1328,14 @@ static void process_async_edits(VoxelLodTerrainUpdateData::State &state,
void VoxelLodTerrainUpdateTask::run(ThreadedTaskContext ctx) {
VOXEL_PROFILE_SCOPE();
struct SetCompleteOnScopeExit {
std::atomic_bool &_complete;
SetCompleteOnScopeExit(std::atomic_bool &b) : _complete(b) {}
~SetCompleteOnScopeExit() {
_complete = true;
}
};
CRASH_COND(_update_data == nullptr);
CRASH_COND(_data == nullptr);
CRASH_COND(_streaming_dependency == nullptr);
@ -1338,6 +1347,11 @@ void VoxelLodTerrainUpdateTask::run(ThreadedTaskContext ctx) {
VoxelDataLodMap &data = *_data;
Ref<VoxelGenerator> generator = _streaming_dependency->generator;
Ref<VoxelStream> stream = _streaming_dependency->stream;
ProfilingClock profiling_clock;
ProfilingClock profiling_clock_total;
const bool stream_enabled = (stream.is_valid() || generator.is_valid()) &&
(Engine::get_singleton()->is_editor_hint() == false || settings.run_stream_in_editor);
CRASH_COND(data.lod_count != update_data.settings.lod_count);
@ -1349,6 +1363,8 @@ void VoxelLodTerrainUpdateTask::run(ThreadedTaskContext ctx) {
CRASH_COND(lod.mesh_blocks_to_deactivate.size() != 0);
}
SetCompleteOnScopeExit scoped_complete(update_data.task_is_complete);
CRASH_COND_MSG(update_data.task_is_complete, "Expected only one update task to run on a given volume");
MutexLock mutex_lock(update_data.completion_mutex);
@ -1358,47 +1374,56 @@ void VoxelLodTerrainUpdateTask::run(ThreadedTaskContext ctx) {
flush_pending_lod_edits(state, data, generator, settings.full_load_mode, 1 << settings.mesh_block_size_po2);
static thread_local std::vector<VoxelLodTerrainUpdateData::BlockToSave> data_blocks_to_save;
// Unload data blocks falling out of block region extent
if (update_data.settings.full_load_mode == false) {
process_unload_data_blocks_sliding_box(
state, data, _viewer_pos, data_blocks_to_save, stream.is_valid(), settings);
}
// Unload mesh blocks falling out of block region extent
process_unload_mesh_blocks_sliding_box(state, _viewer_pos, settings);
// Create and remove octrees in a grid around the viewer.
// Mesh blocks drive the loading of voxel data and visuals.
process_octrees_sliding_box(state, _viewer_pos, settings);
const bool stream_enabled = (stream.is_valid() || generator.is_valid()) &&
(Engine::get_singleton()->is_editor_hint() == false || settings.run_stream_in_editor);
static thread_local std::vector<VoxelLodTerrainUpdateData::BlockLocation> data_blocks_to_load;
data_blocks_to_load.clear();
// Find which blocks we need to load and see, within each octree
if (stream_enabled) {
process_octrees_fitting(state, settings, data, _viewer_pos, data_blocks_to_load);
profiling_clock.restart();
{
// Unload data blocks falling out of block region extent
if (update_data.settings.full_load_mode == false) {
process_unload_data_blocks_sliding_box(
state, data, _viewer_pos, data_blocks_to_save, stream.is_valid(), settings);
}
// Unload mesh blocks falling out of block region extent
process_unload_mesh_blocks_sliding_box(state, _viewer_pos, settings);
// Create and remove octrees in a grid around the viewer.
// Mesh blocks drive the loading of voxel data and visuals.
process_octrees_sliding_box(state, _viewer_pos, settings);
state.stats.blocked_lods = 0;
// Find which blocks we need to load and see, within each octree
if (stream_enabled) {
process_octrees_fitting(state, settings, data, _viewer_pos, data_blocks_to_load);
}
}
state.stats.time_detect_required_blocks = profiling_clock.restart();
process_async_edits(
state, settings, data, _volume_id, _streaming_dependency, _shared_viewers_data, _volume_transform);
// It's possible the user didn't set a stream yet, or it is turned off
if (stream_enabled) {
const unsigned int data_block_size = data.lods[0].map.get_block_size();
send_block_data_requests(_volume_id, to_span_const(data_blocks_to_load), _streaming_dependency,
_shared_viewers_data, data_block_size, _request_instances, _volume_transform, settings.lod_distance);
send_block_save_requests(_volume_id, to_span(data_blocks_to_save), _streaming_dependency, data_block_size);
profiling_clock.restart();
{
// It's possible the user didn't set a stream yet, or it is turned off
if (stream_enabled) {
const unsigned int data_block_size = data.lods[0].map.get_block_size();
send_block_data_requests(_volume_id, to_span_const(data_blocks_to_load), _streaming_dependency,
_shared_viewers_data, data_block_size, _request_instances, _volume_transform,
settings.lod_distance);
send_block_save_requests(_volume_id, to_span(data_blocks_to_save), _streaming_dependency, data_block_size);
}
data_blocks_to_load.clear();
data_blocks_to_save.clear();
}
data_blocks_to_load.clear();
data_blocks_to_save.clear();
state.stats.time_io_requests = profiling_clock.restart();
// TODO Don't request meshes if there is no mesher
send_mesh_requests(_volume_id, state, settings, data, _meshing_dependency, _shared_viewers_data, _volume_transform);
state.stats.time_mesh_requests = profiling_clock.restart();
update_data.task_is_complete = true;
state.stats.time_total = profiling_clock.restart();
}
} // namespace zylann::voxel