Reduce locking of VoxelGeneratorGraph.

Generation hold its own shared pointer. If the user recompiles the graph,
the runtime is replaced with a new one, while the generator can finish
its current block with the old runtime without locking.
master
Marc Gilleron 2021-01-17 20:01:56 +00:00
parent 67b1a2b86f
commit f54b152e2d
2 changed files with 43 additions and 34 deletions

View File

@ -22,10 +22,7 @@ void VoxelGeneratorGraph::clear() {
_graph.clear(); _graph.clear();
{ {
RWLockWrite wlock(_runtime_lock); RWLockWrite wlock(_runtime_lock);
if (_runtime != nullptr) { _runtime.reset();
memdelete(_runtime);
_runtime = nullptr;
}
} }
} }
@ -202,9 +199,11 @@ void VoxelGeneratorGraph::generate_block(VoxelBlockRequest &input) {
// If the user tries to edit (and recompile) the graph while it's still generating stuff in the editor, // If the user tries to edit (and recompile) the graph while it's still generating stuff in the editor,
// the editor will freeze until generation has completed. // the editor will freeze until generation has completed.
// Use std::shared_ptr? // Use std::shared_ptr?
RWLockRead rlock(_runtime_lock); std::shared_ptr<VoxelGraphRuntime> runtime;
{
const VoxelGraphRuntime *runtime = _runtime; RWLockRead rlock(_runtime_lock);
runtime = _runtime;
}
if (runtime == nullptr || !runtime->has_output()) { if (runtime == nullptr || !runtime->has_output()) {
return; return;
@ -314,19 +313,12 @@ void VoxelGeneratorGraph::generate_block(VoxelBlockRequest &input) {
} }
VoxelGraphRuntime::CompilationResult VoxelGeneratorGraph::compile() { VoxelGraphRuntime::CompilationResult VoxelGeneratorGraph::compile() {
VoxelGraphRuntime::CompilationResult result; std::shared_ptr<VoxelGraphRuntime> r = std::make_shared<VoxelGraphRuntime>();
VoxelGraphRuntime *r = memnew(VoxelGraphRuntime); const VoxelGraphRuntime::CompilationResult result = r->compile(_graph, Engine::get_singleton()->is_editor_hint());
result = r->compile(_graph, Engine::get_singleton()->is_editor_hint());
if (result.success) { if (result.success) {
RWLockWrite wlock(_runtime_lock); RWLockWrite wlock(_runtime_lock);
if (_runtime != nullptr) {
memdelete(_runtime);
}
_runtime = r; _runtime = r;
} else {
memdelete(r);
} }
return result; return result;
@ -335,19 +327,17 @@ VoxelGraphRuntime::CompilationResult VoxelGeneratorGraph::compile() {
// This is an external API which involves locking so better not use this internally // This is an external API which involves locking so better not use this internally
bool VoxelGeneratorGraph::is_good() const { bool VoxelGeneratorGraph::is_good() const {
RWLockRead rlock(_runtime_lock); RWLockRead rlock(_runtime_lock);
const VoxelGraphRuntime *runtime = _runtime; return _runtime != nullptr && _runtime->has_output();
return runtime != nullptr && runtime->has_output();
} }
void VoxelGeneratorGraph::generate_set(ArraySlice<float> in_x, ArraySlice<float> in_y, ArraySlice<float> in_z, void VoxelGeneratorGraph::generate_set(ArraySlice<float> in_x, ArraySlice<float> in_y, ArraySlice<float> in_z,
ArraySlice<float> out_sdf) { ArraySlice<float> out_sdf) {
RWLockRead rlock(_runtime_lock); RWLockRead rlock(_runtime_lock);
const VoxelGraphRuntime *runtime = _runtime; ERR_FAIL_COND(_runtime == nullptr || !_runtime->has_output());
ERR_FAIL_COND(runtime == nullptr || !runtime->has_output());
Cache &cache = _cache; Cache &cache = _cache;
runtime->prepare_state(cache.state, in_x.size()); _runtime->prepare_state(cache.state, in_x.size());
runtime->generate_set(cache.state, in_x, in_y, in_z, out_sdf, false); _runtime->generate_set(cache.state, in_x, in_y, in_z, out_sdf, false);
} }
const VoxelGraphRuntime::State &VoxelGeneratorGraph::get_last_state_from_current_thread() { const VoxelGraphRuntime::State &VoxelGeneratorGraph::get_last_state_from_current_thread() {
@ -356,9 +346,8 @@ const VoxelGraphRuntime::State &VoxelGeneratorGraph::get_last_state_from_current
uint32_t VoxelGeneratorGraph::get_output_port_address(ProgramGraph::PortLocation port) const { uint32_t VoxelGeneratorGraph::get_output_port_address(ProgramGraph::PortLocation port) const {
RWLockRead rlock(_runtime_lock); RWLockRead rlock(_runtime_lock);
const VoxelGraphRuntime *runtime = _runtime; ERR_FAIL_COND_V(_runtime == nullptr || !_runtime->has_output(), 0);
ERR_FAIL_COND_V(runtime == nullptr || !runtime->has_output(), 0); return _runtime->get_output_port_address(port);
return runtime->get_output_port_address(port);
} }
inline Vector3 get_3d_pos_from_panorama_uv(Vector2 uv) { inline Vector3 get_3d_pos_from_panorama_uv(Vector2 uv) {
@ -374,8 +363,12 @@ inline Vector3 get_3d_pos_from_panorama_uv(Vector2 uv) {
void VoxelGeneratorGraph::bake_sphere_bumpmap(Ref<Image> im, float ref_radius, float sdf_min, float sdf_max) { void VoxelGeneratorGraph::bake_sphere_bumpmap(Ref<Image> im, float ref_radius, float sdf_min, float sdf_max) {
ERR_FAIL_COND(im.is_null()); ERR_FAIL_COND(im.is_null());
RWLockRead rlock(_runtime_lock); std::shared_ptr<const VoxelGraphRuntime> runtime;
const VoxelGraphRuntime *runtime = _runtime; {
RWLockRead rlock(_runtime_lock);
runtime = _runtime;
}
ERR_FAIL_COND(runtime == nullptr || !runtime->has_output()); ERR_FAIL_COND(runtime == nullptr || !runtime->has_output());
Cache &cache = _cache; Cache &cache = _cache;
@ -409,8 +402,12 @@ void VoxelGeneratorGraph::bake_sphere_bumpmap(Ref<Image> im, float ref_radius, f
void VoxelGeneratorGraph::bake_sphere_normalmap(Ref<Image> im, float ref_radius, float strength) { void VoxelGeneratorGraph::bake_sphere_normalmap(Ref<Image> im, float ref_radius, float strength) {
ERR_FAIL_COND(im.is_null()); ERR_FAIL_COND(im.is_null());
RWLockRead rlock(_runtime_lock); std::shared_ptr<const VoxelGraphRuntime> runtime;
const VoxelGraphRuntime *runtime = _runtime; {
RWLockRead rlock(_runtime_lock);
runtime = _runtime;
}
ERR_FAIL_COND(runtime == nullptr || !runtime->has_output()); ERR_FAIL_COND(runtime == nullptr || !runtime->has_output());
Cache &cache = _cache; Cache &cache = _cache;
@ -469,8 +466,11 @@ void VoxelGeneratorGraph::bake_sphere_normalmap(Ref<Image> im, float ref_radius,
// TODO This function isn't used yet, but whatever uses it should probably put locking and cache outside // TODO This function isn't used yet, but whatever uses it should probably put locking and cache outside
float VoxelGeneratorGraph::generate_single(const Vector3i &position) { float VoxelGeneratorGraph::generate_single(const Vector3i &position) {
RWLockRead rlock(_runtime_lock); std::shared_ptr<const VoxelGraphRuntime> runtime;
const VoxelGraphRuntime *runtime = _runtime; {
RWLockRead rlock(_runtime_lock);
runtime = _runtime;
}
ERR_FAIL_COND_V(runtime == nullptr || !runtime->has_output(), 0.f); ERR_FAIL_COND_V(runtime == nullptr || !runtime->has_output(), 0.f);
Cache &cache = _cache; Cache &cache = _cache;
runtime->prepare_state(cache.state, 1); runtime->prepare_state(cache.state, 1);
@ -479,7 +479,11 @@ float VoxelGeneratorGraph::generate_single(const Vector3i &position) {
Interval VoxelGeneratorGraph::analyze_range(Vector3i min_pos, Vector3i max_pos) { Interval VoxelGeneratorGraph::analyze_range(Vector3i min_pos, Vector3i max_pos) {
RWLockRead rlock(_runtime_lock); RWLockRead rlock(_runtime_lock);
const VoxelGraphRuntime *runtime = _runtime; std::shared_ptr<const VoxelGraphRuntime> runtime;
{
RWLockRead rlock(_runtime_lock);
runtime = _runtime;
}
ERR_FAIL_COND_V(runtime == nullptr || !runtime->has_output(), Interval::from_single_value(0.f)); ERR_FAIL_COND_V(runtime == nullptr || !runtime->has_output(), Interval::from_single_value(0.f));
Cache &cache = _cache; Cache &cache = _cache;
// Note, buffer size is irrelevant here // Note, buffer size is irrelevant here
@ -644,7 +648,11 @@ void VoxelGeneratorGraph::load_graph_from_variant_data(Dictionary data) {
float VoxelGeneratorGraph::debug_measure_microseconds_per_voxel(bool singular) { float VoxelGeneratorGraph::debug_measure_microseconds_per_voxel(bool singular) {
RWLockRead rlock(_runtime_lock); RWLockRead rlock(_runtime_lock);
const VoxelGraphRuntime *runtime = _runtime; std::shared_ptr<const VoxelGraphRuntime> runtime;
{
RWLockRead rlock(_runtime_lock);
runtime = _runtime;
}
ERR_FAIL_COND_V(runtime == nullptr || !runtime->has_output(), 0.f); ERR_FAIL_COND_V(runtime == nullptr || !runtime->has_output(), 0.f);
const uint32_t cube_size = 16; const uint32_t cube_size = 16;

View File

@ -4,6 +4,7 @@
#include "../voxel_generator.h" #include "../voxel_generator.h"
#include "program_graph.h" #include "program_graph.h"
#include "voxel_graph_runtime.h" #include "voxel_graph_runtime.h"
#include <memory>
class VoxelGeneratorGraph : public VoxelGenerator { class VoxelGeneratorGraph : public VoxelGenerator {
GDCLASS(VoxelGeneratorGraph, VoxelGenerator) GDCLASS(VoxelGeneratorGraph, VoxelGenerator)
@ -150,7 +151,7 @@ private:
// Only compiling and generation methods are thread-safe. // Only compiling and generation methods are thread-safe.
VoxelGraphRuntime *_runtime = nullptr; std::shared_ptr<VoxelGraphRuntime> _runtime = nullptr;
RWLock *_runtime_lock = nullptr; RWLock *_runtime_lock = nullptr;
struct Cache { struct Cache {