接口优化
This commit is contained in:
@@ -336,44 +336,40 @@ bool Scene_Base::Mutation::ready() const {
|
||||
std::lock_guard lock(state_->mutex);
|
||||
return state_->completed;
|
||||
}
|
||||
void Scene_Base::Mutation::then(Mutation::Callback callback) const {
|
||||
if (!callback)
|
||||
return;
|
||||
if (!state_) {
|
||||
try {
|
||||
callback({});
|
||||
} catch (...) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
std::exception_ptr exception;
|
||||
{
|
||||
std::lock_guard lock(state_->mutex);
|
||||
if (!state_->completed) {
|
||||
state_->callbacks.push_back(std::move(callback));
|
||||
return;
|
||||
}
|
||||
exception = state_->exception;
|
||||
}
|
||||
try {
|
||||
callback(exception);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
Scene_Base::Attach_Builder::Attach_Builder(Scene_Base& scene)
|
||||
: scene_(&scene), task_lock_(scene.task_mutex_) {
|
||||
if (scene.runtime_started_)
|
||||
throw std::logic_error("scene attach builder is only available before runtime starts");
|
||||
}
|
||||
void Scene_Base::Attach_Builder::attach_renderable(Renderable renderable,
|
||||
Attach_Relationships relationships) {
|
||||
void Scene_Base::Attach_Builder::attach(Renderable renderable,
|
||||
Attach_Relationships relationships) {
|
||||
std::lock_guard lock(scene_->model_mutex_);
|
||||
scene_->apply_attach_renderable_locked(std::move(renderable), std::move(relationships));
|
||||
}
|
||||
void Scene_Base::Attach_Builder::set_display_parent(const Renderable& child,
|
||||
const Renderable& parent) {
|
||||
std::lock_guard lock(scene_->model_mutex_);
|
||||
scene_->apply_set_display_parent_locked(child, parent);
|
||||
}
|
||||
void Scene_Base::Attach_Builder::add_display_parent(const Renderable& child,
|
||||
const Renderable& parent) {
|
||||
std::lock_guard lock(scene_->model_mutex_);
|
||||
scene_->apply_add_display_parent_locked(child, parent);
|
||||
}
|
||||
void Scene_Base::Attach_Builder::clear_display_parent(const Renderable& child) {
|
||||
std::lock_guard lock(scene_->model_mutex_);
|
||||
scene_->apply_clear_display_parent_locked(child);
|
||||
}
|
||||
void Scene_Base::Attach_Builder::set_dependency_parent(const Renderable& child,
|
||||
const Renderable& parent) {
|
||||
std::lock_guard lock(scene_->model_mutex_);
|
||||
scene_->apply_set_dependency_parent_locked(child, parent);
|
||||
}
|
||||
void Scene_Base::Attach_Builder::add_dependency_parent(const Renderable& child,
|
||||
const Renderable& parent) {
|
||||
std::lock_guard lock(scene_->model_mutex_);
|
||||
scene_->apply_add_dependency_parent_locked(child, parent);
|
||||
}
|
||||
void Scene_Base::Attach_Builder::clear_dependency_parent(const Renderable& child) {
|
||||
std::lock_guard lock(scene_->model_mutex_);
|
||||
scene_->apply_clear_dependency_parent_locked(child);
|
||||
}
|
||||
Scene_Base::Attach_Builder Scene_Base::attach_builder() {
|
||||
return Attach_Builder(*this);
|
||||
}
|
||||
@@ -468,15 +464,6 @@ void Scene_Base::wait_for_render() {
|
||||
if (exception)
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
void Scene_Base::wait_for_mutations() {
|
||||
if (is_render_worker_thread())
|
||||
throw std::logic_error("scene mutation wait is not allowed from render execution");
|
||||
std::unique_lock<std::recursive_mutex> lock(task_mutex_);
|
||||
render_completed_.wait(lock, [this] {
|
||||
return mutation_queue_.empty() && !mutating_;
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<Renderable_Id> Scene_Base::relationship_ids_locked(
|
||||
const std::vector<Renderable>& renderables) const {
|
||||
std::vector<Renderable_Id> ids;
|
||||
@@ -490,28 +477,28 @@ std::vector<Renderable_Id> Scene_Base::relationship_ids_locked(
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
Scene_Base::Mutation Scene_Base::enqueue_mutation(std::function<void()> operation,
|
||||
Mutation_Callback callback) {
|
||||
Scene_Base::Mutation Scene_Base::enqueue_mutation(std::function<void()> operation) {
|
||||
std::lock_guard<std::recursive_mutex> lock(task_mutex_);
|
||||
return enqueue_mutation_locked(std::move(operation), std::move(callback));
|
||||
return enqueue_mutation_locked(std::move(operation));
|
||||
}
|
||||
Scene_Base::Mutation Scene_Base::enqueue_mutation_locked(std::function<void()> operation,
|
||||
Mutation_Callback callback) {
|
||||
Scene_Base::Mutation Scene_Base::enqueue_mutation_locked(std::function<void()> operation) {
|
||||
if (stop_)
|
||||
throw std::logic_error("scene is shutting down");
|
||||
runtime_started_ = true;
|
||||
auto completion = std::make_shared<Mutation::State>(*this);
|
||||
mutation_queue_.push_back({std::move(operation), std::move(callback), completion});
|
||||
mutation_queue_.push_back({std::move(operation), completion});
|
||||
task_ready_.notify_one();
|
||||
return Mutation(std::move(completion));
|
||||
}
|
||||
void Scene_Base::complete_mutation(Mutation_Command& command,
|
||||
std::exception_ptr exception) noexcept {
|
||||
std::exception_ptr exception,
|
||||
std::vector<Mutation::Callback>& callbacks) noexcept {
|
||||
{
|
||||
std::lock_guard lock(command.completion->mutex);
|
||||
command.completion->exception = exception;
|
||||
command.completion->completed = true;
|
||||
command.completion->scene = nullptr;
|
||||
callbacks = std::move(command.completion->callbacks);
|
||||
}
|
||||
command.completion->condition.notify_all();
|
||||
}
|
||||
@@ -639,80 +626,60 @@ void Scene_Base::apply_clear_dependency_parent_locked(const Renderable& child) {
|
||||
notify_model_dirty();
|
||||
}
|
||||
Scene_Base::Mutation Scene_Base::attach_renderable(Renderable renderable,
|
||||
Attach_Relationships relationships,
|
||||
Mutation_Callback callback) {
|
||||
Attach_Relationships relationships) {
|
||||
if (!renderable)
|
||||
throw std::invalid_argument("renderable is null");
|
||||
validate_renderable_scene(*renderable);
|
||||
return enqueue_mutation(
|
||||
[this, renderable = std::move(renderable), relationships = std::move(relationships)]() mutable {
|
||||
apply_attach_renderable_locked(std::move(renderable), std::move(relationships));
|
||||
},
|
||||
std::move(callback));
|
||||
});
|
||||
}
|
||||
Scene_Base::Mutation Scene_Base::detach_renderable(const Renderable& renderable,
|
||||
Mutation_Callback callback) {
|
||||
Scene_Base::Mutation Scene_Base::detach_renderable(const Renderable& renderable) {
|
||||
if (!renderable)
|
||||
throw std::invalid_argument("renderable is null");
|
||||
validate_renderable_scene(*renderable);
|
||||
return enqueue_mutation([this, renderable] { apply_detach_renderable_locked(renderable); },
|
||||
std::move(callback));
|
||||
return enqueue_mutation([this, renderable] { apply_detach_renderable_locked(renderable); });
|
||||
}
|
||||
Scene_Base::Mutation Scene_Base::set_display_parent(const Renderable& child,
|
||||
const Renderable& parent,
|
||||
Mutation_Callback callback) {
|
||||
Scene_Base::Mutation Scene_Base::set_parent(Relationship relationship,
|
||||
const Renderable& child,
|
||||
const Renderable& parent) {
|
||||
if (!child || !parent)
|
||||
throw std::invalid_argument("display relationship endpoint is null");
|
||||
throw std::invalid_argument("topology relationship endpoint is null");
|
||||
validate_renderable_scene(*child);
|
||||
validate_renderable_scene(*parent);
|
||||
return enqueue_mutation([this, child, parent] { apply_set_display_parent_locked(child, parent); },
|
||||
std::move(callback));
|
||||
return enqueue_mutation([this, relationship, child, parent] {
|
||||
if (relationship == Relationship::display)
|
||||
apply_set_display_parent_locked(child, parent);
|
||||
else
|
||||
apply_set_dependency_parent_locked(child, parent);
|
||||
});
|
||||
}
|
||||
Scene_Base::Mutation Scene_Base::add_display_parent(const Renderable& child,
|
||||
const Renderable& parent,
|
||||
Mutation_Callback callback) {
|
||||
Scene_Base::Mutation Scene_Base::add_parent(Relationship relationship,
|
||||
const Renderable& child,
|
||||
const Renderable& parent) {
|
||||
if (!child || !parent)
|
||||
throw std::invalid_argument("display relationship endpoint is null");
|
||||
throw std::invalid_argument("topology relationship endpoint is null");
|
||||
validate_renderable_scene(*child);
|
||||
validate_renderable_scene(*parent);
|
||||
return enqueue_mutation([this, child, parent] { apply_add_display_parent_locked(child, parent); },
|
||||
std::move(callback));
|
||||
return enqueue_mutation([this, relationship, child, parent] {
|
||||
if (relationship == Relationship::display)
|
||||
apply_add_display_parent_locked(child, parent);
|
||||
else
|
||||
apply_add_dependency_parent_locked(child, parent);
|
||||
});
|
||||
}
|
||||
Scene_Base::Mutation Scene_Base::clear_display_parent(const Renderable& child,
|
||||
Mutation_Callback callback) {
|
||||
Scene_Base::Mutation Scene_Base::clear_parents(Relationship relationship,
|
||||
const Renderable& child) {
|
||||
if (!child)
|
||||
throw std::invalid_argument("display child is null");
|
||||
throw std::invalid_argument("topology child is null");
|
||||
validate_renderable_scene(*child);
|
||||
return enqueue_mutation([this, child] { apply_clear_display_parent_locked(child); },
|
||||
std::move(callback));
|
||||
}
|
||||
Scene_Base::Mutation Scene_Base::set_dependency_parent(const Renderable& child,
|
||||
const Renderable& parent,
|
||||
Mutation_Callback callback) {
|
||||
if (!child || !parent)
|
||||
throw std::invalid_argument("dependency relationship endpoint is null");
|
||||
validate_renderable_scene(*child);
|
||||
validate_renderable_scene(*parent);
|
||||
return enqueue_mutation([this, child, parent] { apply_set_dependency_parent_locked(child, parent); },
|
||||
std::move(callback));
|
||||
}
|
||||
Scene_Base::Mutation Scene_Base::add_dependency_parent(const Renderable& child,
|
||||
const Renderable& parent,
|
||||
Mutation_Callback callback) {
|
||||
if (!child || !parent)
|
||||
throw std::invalid_argument("dependency relationship endpoint is null");
|
||||
validate_renderable_scene(*child);
|
||||
validate_renderable_scene(*parent);
|
||||
return enqueue_mutation([this, child, parent] { apply_add_dependency_parent_locked(child, parent); },
|
||||
std::move(callback));
|
||||
}
|
||||
Scene_Base::Mutation Scene_Base::clear_dependency_parent(const Renderable& child,
|
||||
Mutation_Callback callback) {
|
||||
if (!child)
|
||||
throw std::invalid_argument("dependency child is null");
|
||||
validate_renderable_scene(*child);
|
||||
return enqueue_mutation([this, child] { apply_clear_dependency_parent_locked(child); },
|
||||
std::move(callback));
|
||||
return enqueue_mutation([this, relationship, child] {
|
||||
if (relationship == Relationship::display)
|
||||
apply_clear_display_parent_locked(child);
|
||||
else
|
||||
apply_clear_dependency_parent_locked(child);
|
||||
});
|
||||
}
|
||||
void Scene_Base::request_render_graph_rebuild(Renderable_Base& renderable) {
|
||||
validate_renderable_scene(renderable);
|
||||
@@ -726,7 +693,7 @@ void Scene_Base::request_render_graph_rebuild(Renderable_Base& renderable) {
|
||||
static_cast<void>(enqueue_mutation_locked([this, owner = std::move(owner)] {
|
||||
owner->reset_render_graph();
|
||||
notify_model_dirty();
|
||||
}, {}));
|
||||
}));
|
||||
}
|
||||
void Scene_Base::publish_frame_state() {
|
||||
auto task_lock = lock_render_idle();
|
||||
@@ -1055,6 +1022,7 @@ void Scene_Base::render_loop() {
|
||||
}
|
||||
if (!mutations.empty()) {
|
||||
std::vector<std::exception_ptr> exceptions(mutations.size());
|
||||
std::vector<std::vector<Mutation::Callback>> callbacks(mutations.size());
|
||||
{
|
||||
std::lock_guard lock(model_mutex_);
|
||||
for (std::size_t index = 0; index < mutations.size(); ++index) {
|
||||
@@ -1066,16 +1034,16 @@ void Scene_Base::render_loop() {
|
||||
}
|
||||
}
|
||||
for (std::size_t index = 0; index < mutations.size(); ++index)
|
||||
complete_mutation(mutations[index], exceptions[index]);
|
||||
complete_mutation(mutations[index], exceptions[index], callbacks[index]);
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(task_mutex_);
|
||||
mutating_ = false;
|
||||
}
|
||||
render_completed_.notify_all();
|
||||
for (std::size_t index = 0; index < mutations.size(); ++index) {
|
||||
if (mutations[index].callback) {
|
||||
for (std::size_t index = 0; index < callbacks.size(); ++index) {
|
||||
for (auto& callback : callbacks[index]) {
|
||||
try {
|
||||
mutations[index].callback(exceptions[index]);
|
||||
callback(exceptions[index]);
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,11 @@ private:
|
||||
public:
|
||||
using Renderable = std::shared_ptr<Renderable_Base>;
|
||||
using Const_Renderable = std::shared_ptr<const Renderable_Base>;
|
||||
using Mutation_Callback = std::function<void(std::exception_ptr)>;
|
||||
|
||||
enum class Relationship {
|
||||
display,
|
||||
dependency
|
||||
};
|
||||
|
||||
struct Attach_Relationships {
|
||||
std::vector<Renderable> display_parents;
|
||||
@@ -54,15 +58,18 @@ public:
|
||||
|
||||
class Mutation {
|
||||
public:
|
||||
using Callback = std::function<void(std::exception_ptr)>;
|
||||
Mutation() = default;
|
||||
void wait() const;
|
||||
[[nodiscard]] bool ready() const;
|
||||
void then(Callback callback) const;
|
||||
private:
|
||||
struct State {
|
||||
explicit State(Scene_Base& scene) : scene(&scene) {}
|
||||
Scene_Base* scene{};
|
||||
mutable std::mutex mutex;
|
||||
std::condition_variable condition;
|
||||
std::vector<Callback> callbacks;
|
||||
std::exception_ptr exception;
|
||||
bool completed{};
|
||||
};
|
||||
@@ -75,16 +82,9 @@ public:
|
||||
public:
|
||||
Attach_Builder(const Attach_Builder&) = delete;
|
||||
Attach_Builder& operator=(const Attach_Builder&) = delete;
|
||||
Attach_Builder(Attach_Builder&&) noexcept = default;
|
||||
Attach_Builder& operator=(Attach_Builder&&) noexcept = default;
|
||||
void attach_renderable(Renderable renderable,
|
||||
Attach_Relationships relationships = {});
|
||||
void set_display_parent(const Renderable& child, const Renderable& parent);
|
||||
void add_display_parent(const Renderable& child, const Renderable& parent);
|
||||
void clear_display_parent(const Renderable& child);
|
||||
void set_dependency_parent(const Renderable& child, const Renderable& parent);
|
||||
void add_dependency_parent(const Renderable& child, const Renderable& parent);
|
||||
void clear_dependency_parent(const Renderable& child);
|
||||
Attach_Builder(Attach_Builder&&) = delete;
|
||||
Attach_Builder& operator=(Attach_Builder&&) = delete;
|
||||
void attach(Renderable renderable, Attach_Relationships relationships = {});
|
||||
private:
|
||||
explicit Attach_Builder(Scene_Base& scene);
|
||||
Scene_Base* scene_{};
|
||||
@@ -129,28 +129,18 @@ public:
|
||||
void render();
|
||||
void render(Abstract_Frame& frame);
|
||||
void wait_for_render();
|
||||
void wait_for_mutations();
|
||||
void publish_frame_state();
|
||||
void notify_model_dirty() noexcept;
|
||||
|
||||
Attach_Builder attach_builder();
|
||||
Mutation attach_renderable(Renderable renderable,
|
||||
Attach_Relationships relationships = {},
|
||||
Mutation_Callback callback = {});
|
||||
Mutation detach_renderable(const Renderable& renderable,
|
||||
Mutation_Callback callback = {});
|
||||
Mutation set_display_parent(const Renderable& child, const Renderable& parent,
|
||||
Mutation_Callback callback = {});
|
||||
Mutation add_display_parent(const Renderable& child, const Renderable& parent,
|
||||
Mutation_Callback callback = {});
|
||||
Mutation clear_display_parent(const Renderable& child,
|
||||
Mutation_Callback callback = {});
|
||||
Mutation set_dependency_parent(const Renderable& child, const Renderable& parent,
|
||||
Mutation_Callback callback = {});
|
||||
Mutation add_dependency_parent(const Renderable& child, const Renderable& parent,
|
||||
Mutation_Callback callback = {});
|
||||
Mutation clear_dependency_parent(const Renderable& child,
|
||||
Mutation_Callback callback = {});
|
||||
Attach_Relationships relationships = {});
|
||||
Mutation detach_renderable(const Renderable& renderable);
|
||||
Mutation set_parent(Relationship relationship, const Renderable& child,
|
||||
const Renderable& parent);
|
||||
Mutation add_parent(Relationship relationship, const Renderable& child,
|
||||
const Renderable& parent);
|
||||
Mutation clear_parents(Relationship relationship, const Renderable& child);
|
||||
void set_renderable_configuration(const Renderable& renderable,
|
||||
Renderable_Configuration configuration);
|
||||
|
||||
@@ -265,7 +255,6 @@ private:
|
||||
|
||||
struct Mutation_Command {
|
||||
std::function<void()> operation;
|
||||
Mutation_Callback callback;
|
||||
std::shared_ptr<Mutation::State> completion;
|
||||
};
|
||||
|
||||
@@ -279,12 +268,10 @@ private:
|
||||
};
|
||||
|
||||
void submit_render(Abstract_Frame* frame);
|
||||
[[nodiscard]] Mutation enqueue_mutation(std::function<void()> operation,
|
||||
Mutation_Callback callback);
|
||||
[[nodiscard]] Mutation enqueue_mutation_locked(std::function<void()> operation,
|
||||
Mutation_Callback callback);
|
||||
void complete_mutation(Mutation_Command& command,
|
||||
std::exception_ptr exception) noexcept;
|
||||
[[nodiscard]] Mutation enqueue_mutation(std::function<void()> operation);
|
||||
[[nodiscard]] Mutation enqueue_mutation_locked(std::function<void()> operation);
|
||||
void complete_mutation(Mutation_Command& command, std::exception_ptr exception,
|
||||
std::vector<Mutation::Callback>& callbacks) noexcept;
|
||||
void apply_attach_renderable_locked(Renderable renderable,
|
||||
Attach_Relationships relationships);
|
||||
void apply_detach_renderable_locked(const Renderable& renderable);
|
||||
|
||||
@@ -104,7 +104,8 @@ TEST(dynamic_renderable_lifecycle_test,
|
||||
scene.render();
|
||||
gate->wait_until_arrived();
|
||||
std::atomic<bool> callback_called{};
|
||||
auto mutation = scene.attach_renderable(attached, {}, [&](std::exception_ptr exception) {
|
||||
auto mutation = scene.attach_renderable(attached);
|
||||
mutation.then([&](std::exception_ptr exception) {
|
||||
EXPECT_FALSE(exception);
|
||||
callback_called.store(true, std::memory_order_release);
|
||||
});
|
||||
@@ -154,13 +155,13 @@ TEST(dynamic_renderable_lifecycle_test,
|
||||
auto child = std::make_shared<Counting_Renderable>(scene);
|
||||
scene.attach_renderable(first_parent);
|
||||
scene.attach_renderable(second_parent);
|
||||
scene.attach_renderable(child, {
|
||||
auto attach = scene.attach_renderable(child, {
|
||||
.dependency_parents = {first_parent}
|
||||
});
|
||||
scene.wait_for_mutations();
|
||||
attach.wait();
|
||||
scene.render();
|
||||
first_gate->wait_until_arrived();
|
||||
auto reparent = scene.set_dependency_parent(child, second_parent);
|
||||
auto reparent = scene.set_parent(Scene_Base::Relationship::dependency, child, second_parent);
|
||||
EXPECT_FALSE(reparent.ready());
|
||||
EXPECT_EQ(child->prepare_count.load(std::memory_order_acquire), 0);
|
||||
first_gate->open();
|
||||
@@ -252,7 +253,7 @@ TEST(dynamic_renderable_lifecycle_test,
|
||||
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
scene.wait_for_mutations();
|
||||
EXPECT_TRUE(wait_until([&] { return scene.renderable_count() == 0; }));
|
||||
EXPECT_EQ(renderable->prepare_count.load(std::memory_order_acquire), 1);
|
||||
EXPECT_EQ(scene.renderable_count(), 0u);
|
||||
scene.render();
|
||||
@@ -428,11 +429,11 @@ TEST(dynamic_renderable_lifecycle_test,
|
||||
.display_parents = {parent},
|
||||
.dependency_parents = {parent}
|
||||
});
|
||||
scene.set_display_parent(child, alternate_parent);
|
||||
scene.set_dependency_parent(child, alternate_parent);
|
||||
scene.set_parent(Scene_Base::Relationship::display, child, alternate_parent);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, child, alternate_parent);
|
||||
child->invalidate_graph();
|
||||
scene.set_display_parent(child, parent);
|
||||
scene.set_dependency_parent(child, parent);
|
||||
scene.set_parent(Scene_Base::Relationship::display, child, parent);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, child, parent);
|
||||
scene.detach_renderable(child);
|
||||
} catch (...) {
|
||||
failures.fetch_add(1, std::memory_order_relaxed);
|
||||
@@ -444,7 +445,8 @@ TEST(dynamic_renderable_lifecycle_test,
|
||||
controller.join();
|
||||
controls_done.store(true, std::memory_order_release);
|
||||
renderer.join();
|
||||
scene.wait_for_mutations();
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(failures.load(std::memory_order_acquire), 0);
|
||||
EXPECT_EQ(violations.load(std::memory_order_acquire), 0);
|
||||
EXPECT_EQ(scene.renderable_count(), 2u);
|
||||
|
||||
@@ -95,8 +95,8 @@ TEST(render_plan_execution_test, dependency_edges_connect_prepare_only_and_layer
|
||||
auto child = std::make_shared<Pipeline_Renderable>(scene);
|
||||
scene.attach_renderable(parent);
|
||||
scene.attach_renderable(child);
|
||||
scene.set_dependency_parent(child, parent);
|
||||
scene.set_display_parent(child, parent);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, child, parent);
|
||||
scene.set_parent(Scene_Base::Relationship::display, child, parent);
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
|
||||
@@ -166,7 +166,7 @@ TEST(render_plan_execution_test, dependent_prepare_does_not_wait_for_dependency_
|
||||
child->prepare_action = [&] { child_prepared.store(true, std::memory_order_release); };
|
||||
scene.attach_renderable(parent);
|
||||
scene.attach_renderable(child);
|
||||
scene.set_dependency_parent(child, parent);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, child, parent);
|
||||
scene.render();
|
||||
{
|
||||
std::unique_lock lock(mutex);
|
||||
@@ -248,21 +248,24 @@ TEST(render_plan_execution_test, logical_node_ids_are_stable_and_retired_ids_are
|
||||
const Render_Node_Id retired_chunk = first->graph.nodes[2].node_id;
|
||||
|
||||
renderable->set_chunk_count(2);
|
||||
scene.wait_for_mutations();
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
const auto unchanged = renderable->graph_snapshot();
|
||||
EXPECT_EQ(unchanged->graph.nodes[0].node_id, root);
|
||||
EXPECT_EQ(unchanged->graph.nodes[1].node_id, first_chunk);
|
||||
EXPECT_EQ(unchanged->graph.nodes[2].node_id, retired_chunk);
|
||||
|
||||
renderable->set_chunk_count(1);
|
||||
scene.wait_for_mutations();
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
const auto reduced = renderable->graph_snapshot();
|
||||
ASSERT_EQ(reduced->graph.nodes.size(), 2u);
|
||||
EXPECT_EQ(reduced->graph.nodes[0].node_id, root);
|
||||
EXPECT_EQ(reduced->graph.nodes[1].node_id, first_chunk);
|
||||
|
||||
renderable->set_chunk_count(2);
|
||||
scene.wait_for_mutations();
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
const auto expanded = renderable->graph_snapshot();
|
||||
ASSERT_EQ(expanded->graph.nodes.size(), 3u);
|
||||
EXPECT_EQ(expanded->graph.nodes[0].node_id, root);
|
||||
|
||||
@@ -110,13 +110,13 @@ TEST(scene2d_context_test, dependency_reparent_invalidates_cached_child) {
|
||||
scene.attach_renderable(first_parent);
|
||||
scene.attach_renderable(second_parent);
|
||||
scene.attach_renderable(child);
|
||||
scene.set_dependency_parent(child, first_parent);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, child, first_parent);
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(first_parent->render_count, 1);
|
||||
EXPECT_EQ(second_parent->render_count, 1);
|
||||
EXPECT_EQ(child->render_count, 1);
|
||||
scene.set_dependency_parent(child, second_parent);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, child, second_parent);
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(first_parent->render_count, 1);
|
||||
@@ -131,8 +131,8 @@ TEST(scene2d_context_test, multiple_dependency_parents_invalidate_cached_child)
|
||||
scene.attach_renderable(first_parent);
|
||||
scene.attach_renderable(second_parent);
|
||||
scene.attach_renderable(child);
|
||||
scene.set_dependency_parent(child, first_parent);
|
||||
scene.add_dependency_parent(child, second_parent);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, child, first_parent);
|
||||
scene.add_parent(Scene_Base::Relationship::dependency, child, second_parent);
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(child->render_count, 1);
|
||||
@@ -171,8 +171,8 @@ TEST(scene2d_context_test, detach_dependency_parent_invalidates_promoted_cached_
|
||||
scene.attach_renderable(grandparent);
|
||||
scene.attach_renderable(parent);
|
||||
scene.attach_renderable(child);
|
||||
scene.set_dependency_parent(parent, grandparent);
|
||||
scene.set_dependency_parent(child, parent);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, parent, grandparent);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, child, parent);
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(grandparent->render_count, 1);
|
||||
|
||||
@@ -39,8 +39,8 @@ TEST(scene2d_render_order_test, separates_dependency_order_from_display_order) {
|
||||
auto axis = std::make_shared<Scene2D_Render_Order_Renderable>(scene, state, 1);
|
||||
scene.attach_renderable(spectrum);
|
||||
scene.attach_renderable(axis);
|
||||
scene.set_dependency_parent(spectrum, axis);
|
||||
scene.set_display_parent(axis, spectrum);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, spectrum, axis);
|
||||
scene.set_parent(Scene_Base::Relationship::display, axis, spectrum);
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(state.snapshot(), (std::vector<std::uint64_t>{1, 2}));
|
||||
@@ -61,10 +61,10 @@ TEST(scene2d_render_order_test, shared_overlay_waits_for_every_display_parent) {
|
||||
scene.attach_renderable(first_plot);
|
||||
scene.attach_renderable(axis);
|
||||
scene.attach_renderable(second_plot);
|
||||
scene.add_dependency_parent(first_plot, axis);
|
||||
scene.add_dependency_parent(second_plot, axis);
|
||||
scene.add_display_parent(axis, first_plot);
|
||||
scene.add_display_parent(axis, second_plot);
|
||||
scene.add_parent(Scene_Base::Relationship::dependency, first_plot, axis);
|
||||
scene.add_parent(Scene_Base::Relationship::dependency, second_plot, axis);
|
||||
scene.add_parent(Scene_Base::Relationship::display, axis, first_plot);
|
||||
scene.add_parent(Scene_Base::Relationship::display, axis, second_plot);
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
const auto render_order = state.snapshot();
|
||||
|
||||
@@ -36,8 +36,8 @@ TEST(scene3d_context_test, propagates_cache_invalidation_through_unsorted_depend
|
||||
scene.attach_renderable(leaf);
|
||||
scene.attach_renderable(middle);
|
||||
scene.attach_renderable(root);
|
||||
scene.set_dependency_parent(leaf, middle);
|
||||
scene.set_dependency_parent(middle, root);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, leaf, middle);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, middle, root);
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(root->render_count, 1);
|
||||
@@ -58,10 +58,10 @@ TEST(scene3d_context_test, detach_parent_promotes_children_and_invalidates_depen
|
||||
scene.attach_renderable(grandparent);
|
||||
scene.attach_renderable(parent);
|
||||
scene.attach_renderable(child);
|
||||
scene.set_display_parent(parent, grandparent);
|
||||
scene.set_display_parent(child, parent);
|
||||
scene.set_dependency_parent(parent, grandparent);
|
||||
scene.set_dependency_parent(child, parent);
|
||||
scene.set_parent(Scene_Base::Relationship::display, parent, grandparent);
|
||||
scene.set_parent(Scene_Base::Relationship::display, child, parent);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, parent, grandparent);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, child, parent);
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(child->render_count, 1);
|
||||
|
||||
@@ -124,11 +124,11 @@ TEST(scene_base_test, concurrent_render_and_topology_updates_remain_serialized)
|
||||
std::thread mutator([&] {
|
||||
for (int index = 0; index < 100; ++index) {
|
||||
if (index % 2 == 0) {
|
||||
scene.set_dependency_parent(child, parent);
|
||||
scene.set_display_parent(child, parent);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, child, parent);
|
||||
scene.set_parent(Scene_Base::Relationship::display, child, parent);
|
||||
} else {
|
||||
scene.clear_dependency_parent(child);
|
||||
scene.clear_display_parent(child);
|
||||
scene.clear_parents(Scene_Base::Relationship::dependency, child);
|
||||
scene.clear_parents(Scene_Base::Relationship::display, child);
|
||||
}
|
||||
scene.set_renderable_configuration(child, {.cache_enabled = index % 2 == 0});
|
||||
}
|
||||
@@ -158,11 +158,11 @@ TEST(scene_base_test, topology_rejects_unattached_renderables) {
|
||||
auto parent = std::make_shared<Scene_Base_Test_Renderable>(scene);
|
||||
auto child = std::make_shared<Scene_Base_Test_Renderable>(scene);
|
||||
scene.attach_renderable(child);
|
||||
EXPECT_THROW(scene.set_dependency_parent(child, parent).wait(), std::invalid_argument);
|
||||
EXPECT_THROW(scene.set_display_parent(child, parent).wait(), std::invalid_argument);
|
||||
EXPECT_THROW(scene.set_parent(Scene_Base::Relationship::dependency, child, parent).wait(), std::invalid_argument);
|
||||
EXPECT_THROW(scene.set_parent(Scene_Base::Relationship::display, child, parent).wait(), std::invalid_argument);
|
||||
scene.attach_renderable(parent);
|
||||
auto detached_child = std::make_shared<Scene_Base_Test_Renderable>(scene);
|
||||
EXPECT_THROW(scene.set_dependency_parent(detached_child, parent).wait(), std::invalid_argument);
|
||||
EXPECT_THROW(scene.set_parent(Scene_Base::Relationship::dependency, detached_child, parent).wait(), std::invalid_argument);
|
||||
}
|
||||
TEST(scene_base_test, setting_same_topology_parent_is_a_noop) {
|
||||
Scene2D_Context<> scene;
|
||||
@@ -170,9 +170,9 @@ TEST(scene_base_test, setting_same_topology_parent_is_a_noop) {
|
||||
auto child = std::make_shared<Scene_Base_Test_Renderable>(scene);
|
||||
scene.attach_renderable(parent);
|
||||
scene.attach_renderable(child);
|
||||
scene.set_dependency_parent(child, parent).wait();
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, child, parent).wait();
|
||||
const auto revision = child->prepare_revision();
|
||||
EXPECT_NO_THROW(scene.set_dependency_parent(child, parent).wait());
|
||||
EXPECT_NO_THROW(scene.set_parent(Scene_Base::Relationship::dependency, child, parent).wait());
|
||||
EXPECT_EQ(child->prepare_revision(), revision);
|
||||
const auto topology = scene.topology_snapshot();
|
||||
EXPECT_EQ(topology.dependency.size(), 2);
|
||||
@@ -193,9 +193,9 @@ TEST(scene_base_test, display_graph_rejects_cycles) {
|
||||
scene.attach_renderable(first);
|
||||
scene.attach_renderable(second);
|
||||
scene.attach_renderable(third);
|
||||
scene.set_display_parent(second, first);
|
||||
scene.add_display_parent(third, second);
|
||||
EXPECT_THROW(scene.add_display_parent(first, third).wait(), std::invalid_argument);
|
||||
scene.set_parent(Scene_Base::Relationship::display, second, first);
|
||||
scene.add_parent(Scene_Base::Relationship::display, third, second);
|
||||
EXPECT_THROW(scene.add_parent(Scene_Base::Relationship::display, first, third).wait(), std::invalid_argument);
|
||||
}
|
||||
TEST(scene_base_test, mutation_callback_receives_apply_failure) {
|
||||
Scene2D_Context<> scene;
|
||||
@@ -203,10 +203,11 @@ TEST(scene_base_test, mutation_callback_receives_apply_failure) {
|
||||
auto second = std::make_shared<Scene_Base_Test_Renderable>(scene);
|
||||
scene.attach_renderable(first).wait();
|
||||
scene.attach_renderable(second).wait();
|
||||
scene.set_display_parent(second, first).wait();
|
||||
scene.set_parent(Scene_Base::Relationship::display, second, first).wait();
|
||||
std::atomic<bool> callback_called{};
|
||||
std::atomic<bool> callback_failed{};
|
||||
auto mutation = scene.set_display_parent(first, second, [&](std::exception_ptr exception) {
|
||||
auto mutation = scene.set_parent(Scene_Base::Relationship::display, first, second);
|
||||
mutation.then([&](std::exception_ptr exception) {
|
||||
callback_failed.store(static_cast<bool>(exception), std::memory_order_release);
|
||||
callback_called.store(true, std::memory_order_release);
|
||||
});
|
||||
@@ -221,12 +222,12 @@ TEST(scene_base_test, atomic_attach_can_place_existing_display_children) {
|
||||
auto renderable = std::make_shared<Scene_Base_Test_Renderable>(scene);
|
||||
scene.attach_renderable(parent);
|
||||
scene.attach_renderable(axis);
|
||||
scene.attach_renderable(renderable, {
|
||||
auto mutation = scene.attach_renderable(renderable, {
|
||||
.display_parents = {parent},
|
||||
.display_children = {axis},
|
||||
.dependency_parents = {parent, axis}
|
||||
});
|
||||
scene.wait_for_mutations();
|
||||
mutation.wait();
|
||||
const auto topology = scene.topology_snapshot();
|
||||
const auto contains = [](const auto& relationships, const auto& child,
|
||||
const auto& parent_value) {
|
||||
@@ -275,16 +276,15 @@ TEST(scene_base_test, topology_snapshot_is_safe_during_topology_updates) {
|
||||
auto parent = std::make_shared<Scene_Base_Test_Renderable>(scene);
|
||||
auto child = std::make_shared<Scene_Base_Test_Renderable>(scene);
|
||||
scene.attach_renderable(parent);
|
||||
scene.attach_renderable(child);
|
||||
scene.wait_for_mutations();
|
||||
scene.attach_renderable(child).wait();
|
||||
std::thread writer([&] {
|
||||
for (int index = 0; index < 500; ++index) {
|
||||
if (index % 2 == 0) {
|
||||
scene.set_dependency_parent(child, parent);
|
||||
scene.set_display_parent(child, parent);
|
||||
scene.set_parent(Scene_Base::Relationship::dependency, child, parent);
|
||||
scene.set_parent(Scene_Base::Relationship::display, child, parent);
|
||||
} else {
|
||||
scene.clear_dependency_parent(child);
|
||||
scene.clear_display_parent(child);
|
||||
scene.clear_parents(Scene_Base::Relationship::dependency, child);
|
||||
scene.clear_parents(Scene_Base::Relationship::display, child);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -363,7 +363,7 @@ TEST(scene_base_test, attach_builder_is_synchronous_before_runtime) {
|
||||
auto renderable = std::make_shared<Scene_Base_Test_Renderable>(scene);
|
||||
{
|
||||
auto builder = scene.attach_builder();
|
||||
builder.attach_renderable(renderable);
|
||||
builder.attach(renderable);
|
||||
EXPECT_EQ(scene.renderable_count(), 1u);
|
||||
}
|
||||
scene.render();
|
||||
|
||||
@@ -556,8 +556,9 @@ TEST(threading_contract_test, scene_render_and_render_graph_rebuild_are_serializ
|
||||
start.store(true, std::memory_order_release);
|
||||
renderer.join();
|
||||
rebuilder.join();
|
||||
scene.wait_for_mutations();
|
||||
EXPECT_EQ(executed.load(std::memory_order_acquire), render_count);
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(executed.load(std::memory_order_acquire), render_count + 1);
|
||||
}
|
||||
TEST(threading_contract_test, real_time_data_updates_can_race_with_scene_destruction) {
|
||||
using Data = Latest_Real_Time_Data<int, std::mutex, Observer_State<Frame_Strategy_Real_Time_Data_Observer>>;
|
||||
|
||||
@@ -210,7 +210,7 @@ public:
|
||||
auto root = detail::make_renderable_group(*this, true);
|
||||
root->set_object_name("root");
|
||||
auto builder = this->attach_builder();
|
||||
builder.attach_renderable(root);
|
||||
builder.attach(root);
|
||||
}
|
||||
[[nodiscard]] std::shared_ptr<Renderable> root_renderable() const {
|
||||
const auto topology = this->topology_snapshot();
|
||||
@@ -236,12 +236,10 @@ public:
|
||||
attach_renderable_node(renderable, parent);
|
||||
return renderable;
|
||||
}
|
||||
::Scene_Base::Mutation remove_renderable(
|
||||
const std::shared_ptr<Renderable>& renderable,
|
||||
::Scene_Base::Mutation_Callback callback = {}) {
|
||||
::Scene_Base::Mutation remove_renderable(const std::shared_ptr<Renderable>& renderable) {
|
||||
if (!renderable || renderable == root_renderable())
|
||||
return {};
|
||||
return this->detach_renderable(renderable, std::move(callback));
|
||||
return this->detach_renderable(renderable);
|
||||
}
|
||||
void set_background_color(Color color) {
|
||||
if (background_color() == color)
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
#include "Web_Server.h"
|
||||
|
||||
#include "Gallery_WebSocket_Controller.h"
|
||||
#include "Renderive_WebSocket_Controller.h"
|
||||
#include "Web_Performance_Log.h"
|
||||
|
||||
#include <drogon/drogon.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
namespace renderive::web {
|
||||
|
||||
int run_web_server(std::uint16_t port, const std::filesystem::path& asset_root) {
|
||||
const std::filesystem::path gallery_root = asset_root / "webapp_gallery";
|
||||
const std::filesystem::path gallery_root = asset_root / "webapp_gallery" / "dist";
|
||||
const std::filesystem::path gallery_index = gallery_root / "index.html";
|
||||
if (!std::filesystem::is_regular_file(gallery_index)) {
|
||||
std::cerr << "Renderive Gallery assets not found: " << gallery_index << '\n';
|
||||
@@ -27,12 +22,12 @@ int run_web_server(std::uint16_t port, const std::filesystem::path& asset_root)
|
||||
const auto gallery_controller = std::make_shared<Gallery_WebSocket_Controller>();
|
||||
const auto hardware_threads = std::max(2U, std::thread::hardware_concurrency());
|
||||
std::cout << "Renderive WebSocket backend: ws://127.0.0.1:" << port
|
||||
<< "/renderive\n"
|
||||
<< "Renderive control gallery: ws://127.0.0.1:" << port
|
||||
<< "/renderive/gallery\n"
|
||||
<< "Renderive hosted gallery: http://127.0.0.1:" << port << "/\n"
|
||||
<< "Renderive performance log: " << web_performance_log_path().string() << "\n"
|
||||
<< "HTTP only serves static HTML/CSS/JS; events and pixels stay on WebSocket.\n";
|
||||
<< "/renderive\n"
|
||||
<< "Renderive control gallery: ws://127.0.0.1:" << port
|
||||
<< "/renderive/gallery\n"
|
||||
<< "Renderive hosted gallery: http://127.0.0.1:" << port << "/\n"
|
||||
<< "Renderive performance log: " << web_performance_log_path().string() << "\n"
|
||||
<< "HTTP only serves static HTML/CSS/JS; events and pixels stay on WebSocket.\n";
|
||||
auto& app = drogon::app();
|
||||
const auto redirect_to_gallery = [](
|
||||
const drogon::HttpRequestPtr&,
|
||||
@@ -42,16 +37,15 @@ int run_web_server(std::uint16_t port, const std::filesystem::path& asset_root)
|
||||
app.registerHandler("/gallery", redirect_to_gallery, {drogon::Get});
|
||||
app.registerHandler("/gallery/", redirect_to_gallery, {drogon::Get});
|
||||
app
|
||||
.registerController(controller)
|
||||
.registerController(gallery_controller)
|
||||
.setDocumentRoot(gallery_root.string())
|
||||
.setHomePage("index.html")
|
||||
.setStaticFileHeaders({{"Cache-Control", "no-store"}})
|
||||
.addListener("127.0.0.1", port)
|
||||
.setThreadNum(std::min(8U, hardware_threads))
|
||||
.setIdleConnectionTimeout(90)
|
||||
.run();
|
||||
.registerController(controller)
|
||||
.registerController(gallery_controller)
|
||||
.setDocumentRoot(gallery_root.string())
|
||||
.setHomePage("index.html")
|
||||
.setStaticFileHeaders({{"Cache-Control", "no-store"}})
|
||||
.addListener("127.0.0.1", port)
|
||||
.setThreadNum(std::min(8U, hardware_threads))
|
||||
.setIdleConnectionTimeout(90)
|
||||
.run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace renderive::web
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
namespace {
|
||||
std::uint16_t parse_port(int argc, char** argv) {
|
||||
constexpr std::uint16_t default_port = 8848;
|
||||
if(argc != 3 || std::string_view(argv[1]) != "--port")
|
||||
if (argc != 3 || std::string_view(argv[1]) != "--port")
|
||||
return default_port;
|
||||
unsigned value{};
|
||||
const std::string_view text(argv[2]);
|
||||
const auto [end, error] = std::from_chars(text.data(), text.data() + text.size(), value);
|
||||
if(error != std::errc{} || end != text.data() + text.size() || value == 0 || value > 65535) {
|
||||
if (error != std::errc{} || end != text.data() + text.size() || value == 0 || value > 65535) {
|
||||
std::cerr << "Invalid port: " << text << '\n';
|
||||
return 0;
|
||||
}
|
||||
@@ -21,9 +21,9 @@ std::uint16_t parse_port(int argc, char** argv) {
|
||||
}
|
||||
int main(int argc, char** argv) {
|
||||
const std::uint16_t port = parse_port(argc, argv);
|
||||
if(port == 0)
|
||||
if (port == 0)
|
||||
return 2;
|
||||
std::cout << "http://127.0.0.1:8848" << std::endl;
|
||||
const std::filesystem::path executable = std::filesystem::absolute(argv[0]);
|
||||
return renderive::web::run_web_server(port, executable.parent_path());
|
||||
return renderive::web::run_web_server(port, executable.parent_path().parent_path().parent_path());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user