Split data request
parent
b7e56c6bec
commit
c6199563c8
|
@ -1,231 +0,0 @@
|
|||
#include "block_data_request.h"
|
||||
#include "../util/godot/funcs.h"
|
||||
#include "../util/macros.h"
|
||||
#include "../util/profiling.h"
|
||||
#include "block_generate_request.h"
|
||||
#include "voxel_server.h"
|
||||
|
||||
namespace zylann::voxel {
|
||||
|
||||
namespace {
|
||||
std::atomic_int g_debug_stream_tasks_count;
|
||||
}
|
||||
|
||||
BlockDataRequest::BlockDataRequest(uint32_t p_volume_id, Vector3i p_block_pos, uint8_t p_lod, uint8_t p_block_size,
|
||||
bool p_request_instances, std::shared_ptr<StreamingDependency> p_stream_dependency,
|
||||
PriorityDependency p_priority_dependency) :
|
||||
_volume_id(p_volume_id),
|
||||
_position(p_block_pos),
|
||||
_lod(p_lod),
|
||||
_block_size(p_block_size),
|
||||
_type(TYPE_LOAD),
|
||||
_request_voxels(true),
|
||||
_request_instances(p_request_instances),
|
||||
_stream_dependency(p_stream_dependency),
|
||||
_priority_dependency(p_priority_dependency) {
|
||||
//
|
||||
++g_debug_stream_tasks_count;
|
||||
}
|
||||
|
||||
BlockDataRequest::BlockDataRequest(uint32_t p_volume_id, Vector3i p_block_pos, uint8_t p_lod, uint8_t p_block_size,
|
||||
std::shared_ptr<VoxelBufferInternal> p_voxels, std::shared_ptr<StreamingDependency> p_stream_dependency) :
|
||||
_volume_id(p_volume_id),
|
||||
_position(p_block_pos),
|
||||
_lod(p_lod),
|
||||
_block_size(p_block_size),
|
||||
_type(TYPE_SAVE),
|
||||
_request_voxels(true),
|
||||
_request_instances(false),
|
||||
_stream_dependency(p_stream_dependency),
|
||||
_voxels(p_voxels) {
|
||||
//
|
||||
++g_debug_stream_tasks_count;
|
||||
}
|
||||
|
||||
// For saving instances
|
||||
BlockDataRequest::BlockDataRequest(uint32_t p_volume_id, Vector3i p_block_pos, uint8_t p_lod, uint8_t p_block_size,
|
||||
std::unique_ptr<InstanceBlockData> p_instances, std::shared_ptr<StreamingDependency> p_stream_dependency) :
|
||||
_volume_id(p_volume_id),
|
||||
_position(p_block_pos),
|
||||
_lod(p_lod),
|
||||
_block_size(p_block_size),
|
||||
_type(TYPE_SAVE),
|
||||
_request_voxels(false),
|
||||
_request_instances(true),
|
||||
_stream_dependency(p_stream_dependency),
|
||||
_instances(std::move(p_instances)) {
|
||||
//
|
||||
++g_debug_stream_tasks_count;
|
||||
}
|
||||
|
||||
BlockDataRequest::~BlockDataRequest() {
|
||||
--g_debug_stream_tasks_count;
|
||||
}
|
||||
|
||||
int BlockDataRequest::debug_get_running_count() {
|
||||
return g_debug_stream_tasks_count;
|
||||
}
|
||||
|
||||
void BlockDataRequest::run(zylann::ThreadedTaskContext ctx) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
CRASH_COND(_stream_dependency == nullptr);
|
||||
Ref<VoxelStream> stream = _stream_dependency->stream;
|
||||
CRASH_COND(stream.is_null());
|
||||
|
||||
const Vector3i origin_in_voxels = (_position << _lod) * _block_size;
|
||||
|
||||
switch (_type) {
|
||||
case TYPE_LOAD: {
|
||||
ERR_FAIL_COND(_voxels != nullptr);
|
||||
_voxels = gd_make_shared<VoxelBufferInternal>();
|
||||
_voxels->create(_block_size, _block_size, _block_size);
|
||||
|
||||
// TODO We should consider batching this again, but it needs to be done carefully.
|
||||
// Each task is one block, and priority depends on distance to closest viewer.
|
||||
// If we batch blocks, we have to do it by distance too.
|
||||
|
||||
// TODO Assign max_lod_hint when available
|
||||
|
||||
const VoxelStream::Result voxel_result = stream->load_voxel_block(*_voxels, origin_in_voxels, _lod);
|
||||
|
||||
if (voxel_result == VoxelStream::RESULT_ERROR) {
|
||||
ERR_PRINT("Error loading voxel block");
|
||||
|
||||
} else if (voxel_result == VoxelStream::RESULT_BLOCK_NOT_FOUND) {
|
||||
Ref<VoxelGenerator> generator = _stream_dependency->generator;
|
||||
|
||||
if (generator.is_valid()) {
|
||||
BlockGenerateRequest *r = memnew(BlockGenerateRequest);
|
||||
r->voxels = _voxels;
|
||||
r->volume_id = _volume_id;
|
||||
r->position = _position;
|
||||
r->lod = _lod;
|
||||
r->block_size = _block_size;
|
||||
r->stream_dependency = _stream_dependency;
|
||||
r->priority_dependency = _priority_dependency;
|
||||
|
||||
VoxelServer::get_singleton()->push_async_task(r);
|
||||
_type = TYPE_FALLBACK_ON_GENERATOR;
|
||||
|
||||
} else {
|
||||
// If there is no generator... what do we do? What defines the format of that empty block?
|
||||
// If the user leaves the defaults it's fine, but otherwise blocks of inconsistent format can
|
||||
// end up in the volume and that can cause errors.
|
||||
// TODO Define format on volume?
|
||||
}
|
||||
}
|
||||
|
||||
if (_request_instances && stream->supports_instance_blocks()) {
|
||||
ERR_FAIL_COND(_instances != nullptr);
|
||||
|
||||
VoxelStreamInstanceDataRequest instance_data_request;
|
||||
instance_data_request.lod = _lod;
|
||||
instance_data_request.position = _position;
|
||||
VoxelStream::Result instances_result;
|
||||
stream->load_instance_blocks(Span<VoxelStreamInstanceDataRequest>(&instance_data_request, 1),
|
||||
Span<VoxelStream::Result>(&instances_result, 1));
|
||||
|
||||
if (instances_result == VoxelStream::RESULT_ERROR) {
|
||||
ERR_PRINT("Error loading instance block");
|
||||
|
||||
} else if (voxel_result == VoxelStream::RESULT_BLOCK_FOUND) {
|
||||
_instances = std::move(instance_data_request.data);
|
||||
}
|
||||
// If not found, instances will return null,
|
||||
// which means it can be generated by the instancer after the meshing process
|
||||
}
|
||||
} break;
|
||||
|
||||
case TYPE_SAVE: {
|
||||
if (_request_voxels) {
|
||||
ERR_FAIL_COND(_voxels == nullptr);
|
||||
VoxelBufferInternal voxels_copy;
|
||||
{
|
||||
RWLockRead lock(_voxels->get_lock());
|
||||
// TODO Optimization: is that copy necessary? It's possible it was already done while issuing the
|
||||
// request
|
||||
_voxels->duplicate_to(voxels_copy, true);
|
||||
}
|
||||
_voxels = nullptr;
|
||||
stream->save_voxel_block(voxels_copy, origin_in_voxels, _lod);
|
||||
}
|
||||
|
||||
if (_request_instances && stream->supports_instance_blocks()) {
|
||||
// If the provided data is null, it means this instance block was never modified.
|
||||
// Since we are in a save request, the saved data will revert to unmodified.
|
||||
// On the other hand, if we want to represent the fact that "everything was deleted here",
|
||||
// this should not be null.
|
||||
|
||||
PRINT_VERBOSE(String("Saving instance block {0} lod {1} with data {2}")
|
||||
.format(varray(_position, _lod, ptr2s(_instances.get()))));
|
||||
|
||||
VoxelStreamInstanceDataRequest instance_data_request;
|
||||
instance_data_request.lod = _lod;
|
||||
instance_data_request.position = _position;
|
||||
instance_data_request.data = std::move(_instances);
|
||||
stream->save_instance_blocks(Span<VoxelStreamInstanceDataRequest>(&instance_data_request, 1));
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
CRASH_NOW_MSG("Invalid type");
|
||||
}
|
||||
|
||||
_has_run = true;
|
||||
}
|
||||
|
||||
int BlockDataRequest::get_priority() {
|
||||
if (_type == TYPE_SAVE) {
|
||||
return 0;
|
||||
}
|
||||
float closest_viewer_distance_sq;
|
||||
const int p = _priority_dependency.evaluate(_lod, &closest_viewer_distance_sq);
|
||||
_too_far = closest_viewer_distance_sq > _priority_dependency.drop_distance_squared;
|
||||
return p;
|
||||
}
|
||||
|
||||
bool BlockDataRequest::is_cancelled() {
|
||||
return _type == TYPE_LOAD && (!_stream_dependency->valid || _too_far);
|
||||
}
|
||||
|
||||
void BlockDataRequest::apply_result() {
|
||||
if (VoxelServer::get_singleton()->is_volume_valid(_volume_id)) {
|
||||
// TODO Comparing pointer may not be guaranteed
|
||||
// The request response must match the dependency it would have been requested with.
|
||||
// If it doesn't match, we are no longer interested in the result.
|
||||
if (_stream_dependency->valid && _type != BlockDataRequest::TYPE_FALLBACK_ON_GENERATOR) {
|
||||
VoxelServer::BlockDataOutput o;
|
||||
o.voxels = _voxels;
|
||||
o.instances = std::move(_instances);
|
||||
o.position = _position;
|
||||
o.lod = _lod;
|
||||
o.dropped = !_has_run;
|
||||
o.max_lod_hint = _max_lod_hint;
|
||||
o.initial_load = false;
|
||||
|
||||
switch (_type) {
|
||||
case BlockDataRequest::TYPE_SAVE:
|
||||
o.type = VoxelServer::BlockDataOutput::TYPE_SAVED;
|
||||
break;
|
||||
|
||||
case BlockDataRequest::TYPE_LOAD:
|
||||
o.type = VoxelServer::BlockDataOutput::TYPE_LOADED;
|
||||
break;
|
||||
|
||||
default:
|
||||
CRASH_NOW_MSG("Unexpected data request response type");
|
||||
}
|
||||
|
||||
VoxelServer::VolumeCallbacks callbacks = VoxelServer::get_singleton()->get_volume_callbacks(_volume_id);
|
||||
CRASH_COND(callbacks.data_output_callback == nullptr);
|
||||
callbacks.data_output_callback(callbacks.data, o);
|
||||
}
|
||||
|
||||
} else {
|
||||
// This can happen if the user removes the volume while requests are still about to return
|
||||
PRINT_VERBOSE("Stream data request response came back but volume wasn't found");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace zylann::voxel
|
|
@ -1,61 +0,0 @@
|
|||
#ifndef BLOCK_DATA_REQUEST_H
|
||||
#define BLOCK_DATA_REQUEST_H
|
||||
|
||||
#include "../util/tasks/threaded_task.h"
|
||||
#include "priority_dependency.h"
|
||||
#include "streaming_dependency.h"
|
||||
|
||||
namespace zylann::voxel {
|
||||
|
||||
class BlockDataRequest : public IThreadedTask {
|
||||
public:
|
||||
enum Type { //
|
||||
TYPE_LOAD = 0,
|
||||
TYPE_SAVE,
|
||||
TYPE_FALLBACK_ON_GENERATOR
|
||||
};
|
||||
|
||||
// For loading.
|
||||
// Only this one needs priority, since we want loading to have low latency.
|
||||
BlockDataRequest(uint32_t p_volume_id, Vector3i p_block_pos, uint8_t p_lod, uint8_t p_block_size,
|
||||
bool p_request_instances, std::shared_ptr<StreamingDependency> p_stream_dependency,
|
||||
PriorityDependency p_priority_dependency);
|
||||
|
||||
// For saving voxels
|
||||
BlockDataRequest(uint32_t p_volume_id, Vector3i p_block_pos, uint8_t p_lod, uint8_t p_block_size,
|
||||
std::shared_ptr<VoxelBufferInternal> p_voxels, std::shared_ptr<StreamingDependency> p_stream_dependency);
|
||||
|
||||
// For saving instances
|
||||
BlockDataRequest(uint32_t p_volume_id, Vector3i p_block_pos, uint8_t p_lod, uint8_t p_block_size,
|
||||
std::unique_ptr<InstanceBlockData> p_instances, std::shared_ptr<StreamingDependency> p_stream_dependency);
|
||||
|
||||
~BlockDataRequest();
|
||||
|
||||
void run(ThreadedTaskContext ctx) override;
|
||||
int get_priority() override;
|
||||
bool is_cancelled() override;
|
||||
void apply_result() override;
|
||||
|
||||
static int debug_get_running_count();
|
||||
|
||||
private:
|
||||
PriorityDependency _priority_dependency;
|
||||
std::shared_ptr<VoxelBufferInternal> _voxels;
|
||||
std::unique_ptr<InstanceBlockData> _instances;
|
||||
Vector3i _position; // In data blocks of the specified lod
|
||||
uint32_t _volume_id;
|
||||
uint8_t _lod;
|
||||
uint8_t _block_size;
|
||||
uint8_t _type;
|
||||
bool _has_run = false;
|
||||
bool _too_far = false;
|
||||
bool _request_instances = false;
|
||||
bool _request_voxels = false;
|
||||
bool _max_lod_hint = false;
|
||||
std::shared_ptr<StreamingDependency> _stream_dependency;
|
||||
// TODO Find a way to separate save, it doesnt need sorting
|
||||
};
|
||||
|
||||
} // namespace zylann::voxel
|
||||
|
||||
#endif // BLOCK_DATA_REQUEST_H
|
|
@ -2,7 +2,7 @@
|
|||
#include "../util/godot/funcs.h"
|
||||
#include "../util/macros.h"
|
||||
#include "../util/profiling.h"
|
||||
#include "block_data_request.h"
|
||||
#include "save_block_data_request.h"
|
||||
#include "voxel_server.h"
|
||||
|
||||
namespace zylann::voxel {
|
||||
|
@ -57,8 +57,8 @@ void BlockGenerateRequest::run(zylann::ThreadedTaskContext ctx) {
|
|||
// No instances, generators are not designed to produce them at this stage yet.
|
||||
// No priority data, saving doesnt need sorting
|
||||
|
||||
BlockDataRequest *r =
|
||||
memnew(BlockDataRequest(volume_id, position, lod, block_size, voxels_copy, stream_dependency));
|
||||
SaveBlockDataRequest *r =
|
||||
memnew(SaveBlockDataRequest(volume_id, position, lod, block_size, voxels_copy, stream_dependency));
|
||||
|
||||
VoxelServer::get_singleton()->push_async_task(r);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
#include "load_block_data_request.h"
|
||||
#include "../util/godot/funcs.h"
|
||||
#include "../util/macros.h"
|
||||
#include "../util/profiling.h"
|
||||
#include "block_generate_request.h"
|
||||
#include "voxel_server.h"
|
||||
|
||||
namespace zylann::voxel {
|
||||
|
||||
namespace {
|
||||
std::atomic_int g_debug_load_block_tasks_count;
|
||||
}
|
||||
|
||||
LoadBlockDataRequest::LoadBlockDataRequest(uint32_t p_volume_id, Vector3i p_block_pos, uint8_t p_lod,
|
||||
uint8_t p_block_size, bool p_request_instances, std::shared_ptr<StreamingDependency> p_stream_dependency,
|
||||
PriorityDependency p_priority_dependency) :
|
||||
_volume_id(p_volume_id),
|
||||
_position(p_block_pos),
|
||||
_lod(p_lod),
|
||||
_block_size(p_block_size),
|
||||
_request_voxels(true),
|
||||
_request_instances(p_request_instances),
|
||||
_stream_dependency(p_stream_dependency),
|
||||
_priority_dependency(p_priority_dependency) {
|
||||
//
|
||||
++g_debug_load_block_tasks_count;
|
||||
}
|
||||
|
||||
LoadBlockDataRequest::~LoadBlockDataRequest() {
|
||||
--g_debug_load_block_tasks_count;
|
||||
}
|
||||
|
||||
int LoadBlockDataRequest::debug_get_running_count() {
|
||||
return g_debug_load_block_tasks_count;
|
||||
}
|
||||
|
||||
void LoadBlockDataRequest::run(zylann::ThreadedTaskContext ctx) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
CRASH_COND(_stream_dependency == nullptr);
|
||||
Ref<VoxelStream> stream = _stream_dependency->stream;
|
||||
CRASH_COND(stream.is_null());
|
||||
|
||||
const Vector3i origin_in_voxels = (_position << _lod) * _block_size;
|
||||
|
||||
ERR_FAIL_COND(_voxels != nullptr);
|
||||
_voxels = gd_make_shared<VoxelBufferInternal>();
|
||||
_voxels->create(_block_size, _block_size, _block_size);
|
||||
|
||||
// TODO We should consider batching this again, but it needs to be done carefully.
|
||||
// Each task is one block, and priority depends on distance to closest viewer.
|
||||
// If we batch blocks, we have to do it by distance too.
|
||||
|
||||
// TODO Assign max_lod_hint when available
|
||||
|
||||
const VoxelStream::Result voxel_result = stream->load_voxel_block(*_voxels, origin_in_voxels, _lod);
|
||||
|
||||
if (voxel_result == VoxelStream::RESULT_ERROR) {
|
||||
ERR_PRINT("Error loading voxel block");
|
||||
|
||||
} else if (voxel_result == VoxelStream::RESULT_BLOCK_NOT_FOUND) {
|
||||
Ref<VoxelGenerator> generator = _stream_dependency->generator;
|
||||
|
||||
if (generator.is_valid()) {
|
||||
BlockGenerateRequest *r = memnew(BlockGenerateRequest);
|
||||
r->voxels = _voxels;
|
||||
r->volume_id = _volume_id;
|
||||
r->position = _position;
|
||||
r->lod = _lod;
|
||||
r->block_size = _block_size;
|
||||
r->stream_dependency = _stream_dependency;
|
||||
r->priority_dependency = _priority_dependency;
|
||||
|
||||
VoxelServer::get_singleton()->push_async_task(r);
|
||||
_fallback_on_generator = true;
|
||||
|
||||
} else {
|
||||
// If there is no generator... what do we do? What defines the format of that empty block?
|
||||
// If the user leaves the defaults it's fine, but otherwise blocks of inconsistent format can
|
||||
// end up in the volume and that can cause errors.
|
||||
// TODO Define format on volume?
|
||||
}
|
||||
}
|
||||
|
||||
if (_request_instances && stream->supports_instance_blocks()) {
|
||||
ERR_FAIL_COND(_instances != nullptr);
|
||||
|
||||
VoxelStreamInstanceDataRequest instance_data_request;
|
||||
instance_data_request.lod = _lod;
|
||||
instance_data_request.position = _position;
|
||||
VoxelStream::Result instances_result;
|
||||
stream->load_instance_blocks(Span<VoxelStreamInstanceDataRequest>(&instance_data_request, 1),
|
||||
Span<VoxelStream::Result>(&instances_result, 1));
|
||||
|
||||
if (instances_result == VoxelStream::RESULT_ERROR) {
|
||||
ERR_PRINT("Error loading instance block");
|
||||
|
||||
} else if (voxel_result == VoxelStream::RESULT_BLOCK_FOUND) {
|
||||
_instances = std::move(instance_data_request.data);
|
||||
}
|
||||
// If not found, instances will return null,
|
||||
// which means it can be generated by the instancer after the meshing process
|
||||
}
|
||||
|
||||
_has_run = true;
|
||||
}
|
||||
|
||||
int LoadBlockDataRequest::get_priority() {
|
||||
float closest_viewer_distance_sq;
|
||||
const int p = _priority_dependency.evaluate(_lod, &closest_viewer_distance_sq);
|
||||
_too_far = closest_viewer_distance_sq > _priority_dependency.drop_distance_squared;
|
||||
return p;
|
||||
}
|
||||
|
||||
bool LoadBlockDataRequest::is_cancelled() {
|
||||
return !_stream_dependency->valid || _too_far;
|
||||
}
|
||||
|
||||
void LoadBlockDataRequest::apply_result() {
|
||||
if (VoxelServer::get_singleton()->is_volume_valid(_volume_id)) {
|
||||
// TODO Comparing pointer may not be guaranteed
|
||||
// The request response must match the dependency it would have been requested with.
|
||||
// If it doesn't match, we are no longer interested in the result.
|
||||
if (_stream_dependency->valid && !_fallback_on_generator) {
|
||||
VoxelServer::BlockDataOutput o;
|
||||
o.voxels = _voxels;
|
||||
o.instances = std::move(_instances);
|
||||
o.position = _position;
|
||||
o.lod = _lod;
|
||||
o.dropped = !_has_run;
|
||||
o.max_lod_hint = _max_lod_hint;
|
||||
o.initial_load = false;
|
||||
o.type = VoxelServer::BlockDataOutput::TYPE_LOADED;
|
||||
|
||||
VoxelServer::VolumeCallbacks callbacks = VoxelServer::get_singleton()->get_volume_callbacks(_volume_id);
|
||||
CRASH_COND(callbacks.data_output_callback == nullptr);
|
||||
callbacks.data_output_callback(callbacks.data, o);
|
||||
}
|
||||
|
||||
} else {
|
||||
// This can happen if the user removes the volume while requests are still about to return
|
||||
PRINT_VERBOSE("Stream data request response came back but volume wasn't found");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace zylann::voxel
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef LOAD_BLOCK_DATA_REQUEST_H
|
||||
#define LOAD_BLOCK_DATA_REQUEST_H
|
||||
|
||||
#include "../util/tasks/threaded_task.h"
|
||||
#include "priority_dependency.h"
|
||||
#include "streaming_dependency.h"
|
||||
|
||||
namespace zylann::voxel {
|
||||
|
||||
class LoadBlockDataRequest : public IThreadedTask {
|
||||
public:
|
||||
LoadBlockDataRequest(uint32_t p_volume_id, Vector3i p_block_pos, uint8_t p_lod, uint8_t p_block_size,
|
||||
bool p_request_instances, std::shared_ptr<StreamingDependency> p_stream_dependency,
|
||||
PriorityDependency p_priority_dependency);
|
||||
|
||||
~LoadBlockDataRequest();
|
||||
|
||||
void run(ThreadedTaskContext ctx) override;
|
||||
int get_priority() override;
|
||||
bool is_cancelled() override;
|
||||
void apply_result() override;
|
||||
|
||||
static int debug_get_running_count();
|
||||
|
||||
private:
|
||||
PriorityDependency _priority_dependency;
|
||||
std::shared_ptr<VoxelBufferInternal> _voxels;
|
||||
std::unique_ptr<InstanceBlockData> _instances;
|
||||
Vector3i _position; // In data blocks of the specified lod
|
||||
uint32_t _volume_id;
|
||||
uint8_t _lod;
|
||||
uint8_t _block_size;
|
||||
bool _has_run = false;
|
||||
bool _too_far = false;
|
||||
bool _request_instances = false;
|
||||
bool _request_voxels = false;
|
||||
bool _max_lod_hint = false;
|
||||
bool _fallback_on_generator = false;
|
||||
std::shared_ptr<StreamingDependency> _stream_dependency;
|
||||
};
|
||||
|
||||
} // namespace zylann::voxel
|
||||
|
||||
#endif // LOAD_BLOCK_DATA_REQUEST_H
|
|
@ -0,0 +1,125 @@
|
|||
#include "save_block_data_request.h"
|
||||
#include "../util/godot/funcs.h"
|
||||
#include "../util/macros.h"
|
||||
#include "../util/profiling.h"
|
||||
#include "block_generate_request.h"
|
||||
#include "voxel_server.h"
|
||||
|
||||
namespace zylann::voxel {
|
||||
|
||||
namespace {
|
||||
std::atomic_int g_debug_save_block_tasks_count;
|
||||
}
|
||||
|
||||
SaveBlockDataRequest::SaveBlockDataRequest(uint32_t p_volume_id, Vector3i p_block_pos, uint8_t p_lod,
|
||||
uint8_t p_block_size, std::shared_ptr<VoxelBufferInternal> p_voxels,
|
||||
std::shared_ptr<StreamingDependency> p_stream_dependency) :
|
||||
_volume_id(p_volume_id),
|
||||
_position(p_block_pos),
|
||||
_lod(p_lod),
|
||||
_block_size(p_block_size),
|
||||
_save_voxels(true),
|
||||
_save_instances(false),
|
||||
_stream_dependency(p_stream_dependency),
|
||||
_voxels(p_voxels) {
|
||||
//
|
||||
++g_debug_save_block_tasks_count;
|
||||
}
|
||||
|
||||
SaveBlockDataRequest::SaveBlockDataRequest(uint32_t p_volume_id, Vector3i p_block_pos, uint8_t p_lod,
|
||||
uint8_t p_block_size, std::unique_ptr<InstanceBlockData> p_instances,
|
||||
std::shared_ptr<StreamingDependency> p_stream_dependency) :
|
||||
_volume_id(p_volume_id),
|
||||
_position(p_block_pos),
|
||||
_lod(p_lod),
|
||||
_block_size(p_block_size),
|
||||
_save_voxels(false),
|
||||
_save_instances(true),
|
||||
_stream_dependency(p_stream_dependency),
|
||||
_instances(std::move(p_instances)) {
|
||||
//
|
||||
++g_debug_save_block_tasks_count;
|
||||
}
|
||||
|
||||
SaveBlockDataRequest::~SaveBlockDataRequest() {
|
||||
--g_debug_save_block_tasks_count;
|
||||
}
|
||||
|
||||
int SaveBlockDataRequest::debug_get_running_count() {
|
||||
return g_debug_save_block_tasks_count;
|
||||
}
|
||||
|
||||
void SaveBlockDataRequest::run(zylann::ThreadedTaskContext ctx) {
|
||||
VOXEL_PROFILE_SCOPE();
|
||||
|
||||
CRASH_COND(_stream_dependency == nullptr);
|
||||
Ref<VoxelStream> stream = _stream_dependency->stream;
|
||||
CRASH_COND(stream.is_null());
|
||||
|
||||
const Vector3i origin_in_voxels = (_position << _lod) * _block_size;
|
||||
|
||||
if (_save_voxels) {
|
||||
ERR_FAIL_COND(_voxels == nullptr);
|
||||
VoxelBufferInternal voxels_copy;
|
||||
{
|
||||
RWLockRead lock(_voxels->get_lock());
|
||||
// TODO Optimization: is that copy necessary? It's possible it was already done while issuing the
|
||||
// request
|
||||
_voxels->duplicate_to(voxels_copy, true);
|
||||
}
|
||||
_voxels = nullptr;
|
||||
stream->save_voxel_block(voxels_copy, origin_in_voxels, _lod);
|
||||
}
|
||||
|
||||
if (_save_instances && stream->supports_instance_blocks()) {
|
||||
// If the provided data is null, it means this instance block was never modified.
|
||||
// Since we are in a save request, the saved data will revert to unmodified.
|
||||
// On the other hand, if we want to represent the fact that "everything was deleted here",
|
||||
// this should not be null.
|
||||
|
||||
PRINT_VERBOSE(String("Saving instance block {0} lod {1} with data {2}")
|
||||
.format(varray(_position, _lod, ptr2s(_instances.get()))));
|
||||
|
||||
VoxelStreamInstanceDataRequest instance_data_request;
|
||||
instance_data_request.lod = _lod;
|
||||
instance_data_request.position = _position;
|
||||
instance_data_request.data = std::move(_instances);
|
||||
stream->save_instance_blocks(Span<VoxelStreamInstanceDataRequest>(&instance_data_request, 1));
|
||||
}
|
||||
|
||||
_has_run = true;
|
||||
}
|
||||
|
||||
int SaveBlockDataRequest::get_priority() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SaveBlockDataRequest::is_cancelled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void SaveBlockDataRequest::apply_result() {
|
||||
if (VoxelServer::get_singleton()->is_volume_valid(_volume_id)) {
|
||||
if (_stream_dependency->valid) {
|
||||
VoxelServer::BlockDataOutput o;
|
||||
o.voxels = _voxels;
|
||||
o.instances = std::move(_instances);
|
||||
o.position = _position;
|
||||
o.lod = _lod;
|
||||
o.dropped = !_has_run;
|
||||
o.max_lod_hint = false; // Unused
|
||||
o.initial_load = false; // Unused
|
||||
o.type = VoxelServer::BlockDataOutput::TYPE_SAVED;
|
||||
|
||||
VoxelServer::VolumeCallbacks callbacks = VoxelServer::get_singleton()->get_volume_callbacks(_volume_id);
|
||||
CRASH_COND(callbacks.data_output_callback == nullptr);
|
||||
callbacks.data_output_callback(callbacks.data, o);
|
||||
}
|
||||
|
||||
} else {
|
||||
// This can happen if the user removes the volume while requests are still about to return
|
||||
PRINT_VERBOSE("Stream data request response came back but volume wasn't found");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace zylann::voxel
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef SAVE_BLOCK_DATA_REQUEST_H
|
||||
#define SAVE_BLOCK_DATA_REQUEST_H
|
||||
|
||||
#include "../util/tasks/threaded_task.h"
|
||||
#include "streaming_dependency.h"
|
||||
|
||||
namespace zylann::voxel {
|
||||
|
||||
class SaveBlockDataRequest : public IThreadedTask {
|
||||
public:
|
||||
// For saving voxels only
|
||||
SaveBlockDataRequest(uint32_t p_volume_id, Vector3i p_block_pos, uint8_t p_lod, uint8_t p_block_size,
|
||||
std::shared_ptr<VoxelBufferInternal> p_voxels, std::shared_ptr<StreamingDependency> p_stream_dependency);
|
||||
|
||||
// For saving instances only
|
||||
SaveBlockDataRequest(uint32_t p_volume_id, Vector3i p_block_pos, uint8_t p_lod, uint8_t p_block_size,
|
||||
std::unique_ptr<InstanceBlockData> p_instances, std::shared_ptr<StreamingDependency> p_stream_dependency);
|
||||
|
||||
~SaveBlockDataRequest();
|
||||
|
||||
void run(ThreadedTaskContext ctx) override;
|
||||
int get_priority() override;
|
||||
bool is_cancelled() override;
|
||||
void apply_result() override;
|
||||
|
||||
static int debug_get_running_count();
|
||||
|
||||
private:
|
||||
std::shared_ptr<VoxelBufferInternal> _voxels;
|
||||
std::unique_ptr<InstanceBlockData> _instances;
|
||||
Vector3i _position; // In data blocks of the specified lod
|
||||
uint32_t _volume_id;
|
||||
uint8_t _lod;
|
||||
uint8_t _block_size;
|
||||
bool _has_run = false;
|
||||
bool _save_instances = false;
|
||||
bool _save_voxels = false;
|
||||
std::shared_ptr<StreamingDependency> _stream_dependency;
|
||||
};
|
||||
|
||||
} // namespace zylann::voxel
|
||||
|
||||
#endif // SAVE_BLOCK_DATA_REQUEST_H
|
|
@ -6,8 +6,9 @@
|
|||
#include "../util/macros.h"
|
||||
#include "../util/profiling.h"
|
||||
#include "all_blocks_data_request.h"
|
||||
#include "block_data_request.h"
|
||||
#include "block_generate_request.h"
|
||||
#include "load_block_data_request.h"
|
||||
#include "save_block_data_request.h"
|
||||
|
||||
#include <core/config/project_settings.h>
|
||||
#include <core/os/memory.h>
|
||||
|
@ -100,7 +101,8 @@ VoxelServer::VoxelServer() {
|
|||
// Init world
|
||||
_world.shared_priority_dependency = gd_make_shared<PriorityDependency::ViewersData>();
|
||||
|
||||
PRINT_VERBOSE(String("Size of BlockDataRequest: {0}").format(varray((int)sizeof(BlockDataRequest))));
|
||||
PRINT_VERBOSE(String("Size of LoadBlockDataRequest: {0}").format(varray((int)sizeof(LoadBlockDataRequest))));
|
||||
PRINT_VERBOSE(String("Size of SaveBlockDataRequest: {0}").format(varray((int)sizeof(SaveBlockDataRequest))));
|
||||
PRINT_VERBOSE(String("Size of BlockMeshRequest: {0}").format(varray((int)sizeof(BlockMeshRequest))));
|
||||
}
|
||||
|
||||
|
@ -294,7 +296,7 @@ void VoxelServer::request_block_load(uint32_t volume_id, Vector3i block_pos, int
|
|||
PriorityDependency priority_dependency;
|
||||
init_priority_dependency(priority_dependency, block_pos, lod, volume, volume.data_block_size);
|
||||
|
||||
BlockDataRequest *r = memnew(BlockDataRequest(volume_id, block_pos, lod, volume.data_block_size,
|
||||
LoadBlockDataRequest *r = memnew(LoadBlockDataRequest(volume_id, block_pos, lod, volume.data_block_size,
|
||||
request_instances, volume.stream_dependency, priority_dependency));
|
||||
|
||||
_streaming_thread_pool.enqueue(r);
|
||||
|
@ -356,8 +358,8 @@ void VoxelServer::request_voxel_block_save(
|
|||
ERR_FAIL_COND(volume.stream.is_null());
|
||||
CRASH_COND(volume.stream_dependency == nullptr);
|
||||
|
||||
BlockDataRequest *r = memnew(
|
||||
BlockDataRequest(volume_id, block_pos, lod, volume.data_block_size, voxels, volume.stream_dependency));
|
||||
SaveBlockDataRequest *r = memnew(
|
||||
SaveBlockDataRequest(volume_id, block_pos, lod, volume.data_block_size, voxels, volume.stream_dependency));
|
||||
|
||||
// No priority data, saving doesnt need sorting
|
||||
|
||||
|
@ -370,7 +372,7 @@ void VoxelServer::request_instance_block_save(
|
|||
ERR_FAIL_COND(volume.stream.is_null());
|
||||
CRASH_COND(volume.stream_dependency == nullptr);
|
||||
|
||||
BlockDataRequest *r = memnew(BlockDataRequest(
|
||||
SaveBlockDataRequest *r = memnew(SaveBlockDataRequest(
|
||||
volume_id, block_pos, lod, volume.data_block_size, std::move(instances), volume.stream_dependency));
|
||||
|
||||
// No priority data, saving doesnt need sorting
|
||||
|
@ -587,7 +589,8 @@ VoxelServer::Stats VoxelServer::get_stats() const {
|
|||
s.general = debug_get_pool_stats(_general_thread_pool);
|
||||
s.generation_tasks = BlockGenerateRequest::debug_get_running_count();
|
||||
s.meshing_tasks = BlockMeshRequest::debug_get_running_count();
|
||||
s.streaming_tasks = BlockDataRequest::debug_get_running_count();
|
||||
s.streaming_tasks =
|
||||
LoadBlockDataRequest::debug_get_running_count() + SaveBlockDataRequest::debug_get_running_count();
|
||||
s.main_thread_tasks = _time_spread_task_runner.get_pending_count() + _progressive_task_runner.get_pending_count();
|
||||
return s;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue