升级
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "Scene_Base.hpp"
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <memory_resource>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@@ -11,6 +12,12 @@
|
||||
#include "renderive/state/base/State_Strategy_Base.hpp"
|
||||
static_assert(TF_VERSION == 400100, "Renderive requires Taskflow 4.1.0");
|
||||
namespace {
|
||||
std::uint64_t steady_now_ns() noexcept {
|
||||
return static_cast<std::uint64_t>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||
}
|
||||
|
||||
template <class Node_Accessor>
|
||||
Scene_Base::Renderable_List topological_order(
|
||||
const Scene_Base::Renderable_List& renderables,
|
||||
@@ -331,6 +338,50 @@ std::vector<Scene_Base::Const_Renderable> Scene_Base::paint_order_snapshot() con
|
||||
const auto ordered = display_order(*cache_renderables_);
|
||||
return {ordered.begin(), ordered.end()};
|
||||
}
|
||||
Task_Execution_Snapshot Scene_Base::task_execution_snapshot() const noexcept {
|
||||
Task_Execution_Snapshot snapshot;
|
||||
snapshot.worker_count = task_executor_worker_count();
|
||||
snapshot.render_sequence =
|
||||
task_execution_render_sequence_.load(std::memory_order_acquire);
|
||||
snapshot.graph_node_count =
|
||||
task_execution_node_count_.load(std::memory_order_acquire);
|
||||
snapshot.completed_count =
|
||||
task_execution_completed_count_.load(std::memory_order_acquire);
|
||||
snapshot.running_count =
|
||||
task_execution_running_count_.load(std::memory_order_acquire);
|
||||
snapshot.peak_parallelism =
|
||||
task_execution_peak_parallelism_.load(std::memory_order_acquire);
|
||||
snapshot.failed_count =
|
||||
task_execution_failed_count_.load(std::memory_order_acquire);
|
||||
const std::size_t accounted = snapshot.completed_count + snapshot.running_count;
|
||||
snapshot.pending_count = accounted < snapshot.graph_node_count
|
||||
? snapshot.graph_node_count - accounted
|
||||
: 0;
|
||||
{
|
||||
std::lock_guard lock(task_mutex_);
|
||||
if (task_pending_)
|
||||
snapshot.state = Task_Execution_State::Queued;
|
||||
else if (rendering_)
|
||||
snapshot.state = Task_Execution_State::Running;
|
||||
else if (current_completion_ && current_completion_->completed)
|
||||
snapshot.state = current_completion_->exception
|
||||
? Task_Execution_State::Failed
|
||||
: Task_Execution_State::Completed;
|
||||
}
|
||||
snapshot.duration_ns =
|
||||
task_execution_duration_ns_.load(std::memory_order_acquire);
|
||||
if (snapshot.state == Task_Execution_State::Running) {
|
||||
const auto started =
|
||||
task_execution_started_ns_.load(std::memory_order_acquire);
|
||||
const auto now = steady_now_ns();
|
||||
if (started != 0 && now >= started)
|
||||
snapshot.duration_ns = now - started;
|
||||
}
|
||||
return snapshot;
|
||||
}
|
||||
std::size_t Scene_Base::task_executor_worker_count() noexcept {
|
||||
return Execution_Context::executor().num_workers();
|
||||
}
|
||||
std::pmr::memory_resource& Scene_Base::memory_resource() const noexcept {
|
||||
return memory_domain_->resource();
|
||||
}
|
||||
@@ -459,6 +510,20 @@ void Scene_Base::execute_taskflow(Render_Task& task) {
|
||||
};
|
||||
std::pmr::monotonic_buffer_resource scratch_resource(&memory_resource());
|
||||
tf::Taskflow scene_graph;
|
||||
std::size_t planned_task_count{};
|
||||
const auto tracked = [this, &planned_task_count](auto function) {
|
||||
++planned_task_count;
|
||||
return [this, function = std::move(function)]() mutable {
|
||||
task_execution_started();
|
||||
try {
|
||||
function();
|
||||
task_execution_finished(false);
|
||||
} catch (...) {
|
||||
task_execution_finished(true);
|
||||
throw;
|
||||
}
|
||||
};
|
||||
};
|
||||
std::pmr::unordered_map<Renderable_Base*, std::size_t> indices(&scratch_resource);
|
||||
indices.reserve(task.render_order.size());
|
||||
std::pmr::vector<bool> render_flags(task.render_order.size(), false, &scratch_resource);
|
||||
@@ -500,21 +565,21 @@ void Scene_Base::execute_taskflow(Render_Task& task) {
|
||||
std::pmr::vector<tf::Task> internal_tasks(&scratch_resource);
|
||||
internal_tasks.reserve(nodes.size());
|
||||
for (const auto& node : nodes) {
|
||||
internal_tasks.push_back(module.graph.emplace([this, function = node.function, context] {
|
||||
internal_tasks.push_back(module.graph.emplace(tracked([this, function = node.function, context] {
|
||||
Render_Execution_Scope scope(*this);
|
||||
function(context);
|
||||
}).name(std::string(node.name)));
|
||||
})).name(std::string(node.name)));
|
||||
}
|
||||
for (std::size_t node_index = 0; node_index < nodes.size(); ++node_index) {
|
||||
for (std::size_t successor : nodes[node_index].successors) {
|
||||
internal_tasks[node_index].precede(internal_tasks.at(successor));
|
||||
}
|
||||
}
|
||||
tf::Task mark_rendered = module.graph.emplace([&renderable, render_revision] {
|
||||
tf::Task mark_rendered = module.graph.emplace(tracked([&renderable, render_revision] {
|
||||
renderable.mark_rendered(render_revision);
|
||||
}).name("cache_publish");
|
||||
})).name("cache_publish");
|
||||
if (internal_tasks.empty()) {
|
||||
module.graph.emplace([] {}).precede(mark_rendered);
|
||||
module.graph.emplace(tracked([] {})).precede(mark_rendered);
|
||||
} else {
|
||||
for (std::size_t node_index = 0; node_index < nodes.size(); ++node_index) {
|
||||
if (nodes[node_index].successors.empty()) {
|
||||
@@ -523,7 +588,7 @@ void Scene_Base::execute_taskflow(Render_Task& task) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
module.graph.emplace([] {}).name("cache_hit");
|
||||
module.graph.emplace(tracked([] {})).name("cache_hit");
|
||||
}
|
||||
module.module_task = scene_graph.composed_of(module.graph).name("renderable");
|
||||
}
|
||||
@@ -538,18 +603,62 @@ void Scene_Base::execute_taskflow(Render_Task& task) {
|
||||
}
|
||||
}
|
||||
const Scene_Render_Context final_context{this, task.frame_control_state, task.scene_state_revision, task.render_sequence, nullptr, nullptr};
|
||||
tf::Task final_task = scene_graph.emplace([this, final_context, &task] {
|
||||
tf::Task final_task = scene_graph.emplace(tracked([this, final_context, &task] {
|
||||
Render_Execution_Scope scope(*this);
|
||||
generate_final_color_cache(final_context, task.display_order);
|
||||
}).name("final_color_cache");
|
||||
})).name("final_color_cache");
|
||||
if (modules.empty()) {
|
||||
scene_graph.emplace([] {}).precede(final_task);
|
||||
scene_graph.emplace(tracked([] {})).precede(final_task);
|
||||
} else {
|
||||
for (Module& module : modules) {
|
||||
module.module_task.precede(final_task);
|
||||
}
|
||||
}
|
||||
Execution_Context::executor().run(scene_graph).get();
|
||||
begin_task_execution(task.render_sequence);
|
||||
set_task_execution_node_count(planned_task_count);
|
||||
try {
|
||||
Execution_Context::executor().run(scene_graph).get();
|
||||
} catch (...) {
|
||||
finish_task_execution();
|
||||
throw;
|
||||
}
|
||||
finish_task_execution();
|
||||
}
|
||||
void Scene_Base::begin_task_execution(std::uint64_t render_sequence) noexcept {
|
||||
task_execution_render_sequence_.store(render_sequence, std::memory_order_release);
|
||||
task_execution_node_count_.store(0, std::memory_order_release);
|
||||
task_execution_completed_count_.store(0, std::memory_order_release);
|
||||
task_execution_running_count_.store(0, std::memory_order_release);
|
||||
task_execution_peak_parallelism_.store(0, std::memory_order_release);
|
||||
task_execution_failed_count_.store(0, std::memory_order_release);
|
||||
task_execution_duration_ns_.store(0, std::memory_order_release);
|
||||
task_execution_started_ns_.store(steady_now_ns(), std::memory_order_release);
|
||||
}
|
||||
void Scene_Base::set_task_execution_node_count(std::size_t count) noexcept {
|
||||
task_execution_node_count_.store(count, std::memory_order_release);
|
||||
}
|
||||
void Scene_Base::task_execution_started() noexcept {
|
||||
const std::size_t active =
|
||||
task_execution_running_count_.fetch_add(1, std::memory_order_acq_rel) + 1;
|
||||
std::size_t peak =
|
||||
task_execution_peak_parallelism_.load(std::memory_order_acquire);
|
||||
while (peak < active &&
|
||||
!task_execution_peak_parallelism_.compare_exchange_weak(
|
||||
peak, active, std::memory_order_acq_rel, std::memory_order_acquire)) {}
|
||||
}
|
||||
void Scene_Base::task_execution_finished(bool failed) noexcept {
|
||||
if (failed)
|
||||
task_execution_failed_count_.fetch_add(1, std::memory_order_acq_rel);
|
||||
task_execution_completed_count_.fetch_add(1, std::memory_order_acq_rel);
|
||||
task_execution_running_count_.fetch_sub(1, std::memory_order_acq_rel);
|
||||
}
|
||||
void Scene_Base::finish_task_execution() noexcept {
|
||||
const auto started =
|
||||
task_execution_started_ns_.load(std::memory_order_acquire);
|
||||
const auto now = steady_now_ns();
|
||||
task_execution_duration_ns_.store(
|
||||
started != 0 && now >= started ? now - started : 0,
|
||||
std::memory_order_release);
|
||||
}
|
||||
void Scene_Base::validate_renderable_scene(const Renderable_Base& renderable) const {
|
||||
if (renderable.real_time_data_state_->scene_lifetime.get() != scene_lifetime_.get()) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
@@ -15,6 +16,7 @@
|
||||
#include "renderive/renderable/base/Renderable_Base.hpp"
|
||||
#include "Scene_Lifetime.hpp"
|
||||
#include "Scene_Render_Context.hpp"
|
||||
#include "Task_Execution_Snapshot.hpp"
|
||||
class Color_Cache;
|
||||
class Scene_Base {
|
||||
private:
|
||||
@@ -65,6 +67,8 @@ public:
|
||||
std::size_t renderable_count() const;
|
||||
Topology_Snapshot topology_snapshot() const;
|
||||
std::vector<Const_Renderable> paint_order_snapshot() const;
|
||||
[[nodiscard]] Task_Execution_Snapshot task_execution_snapshot() const noexcept;
|
||||
[[nodiscard]] static std::size_t task_executor_worker_count() noexcept;
|
||||
std::pmr::memory_resource& memory_resource() const noexcept;
|
||||
std::pmr::memory_resource& upstream_memory_resource() const noexcept;
|
||||
Frame_Control_Strategy_Base& frame_control_strategy();
|
||||
@@ -114,6 +118,11 @@ private:
|
||||
};
|
||||
void render_loop();
|
||||
void execute_taskflow(Render_Task& task);
|
||||
void begin_task_execution(std::uint64_t render_sequence) noexcept;
|
||||
void set_task_execution_node_count(std::size_t count) noexcept;
|
||||
void task_execution_started() noexcept;
|
||||
void task_execution_finished(bool failed) noexcept;
|
||||
void finish_task_execution() noexcept;
|
||||
void validate_renderable_scene(const Renderable_Base& renderable) const;
|
||||
bool is_renderable_attached_locked(const Renderable_Base& renderable) const;
|
||||
void validate_renderable_attached_locked(const Renderable_Base& renderable) const;
|
||||
@@ -125,7 +134,7 @@ private:
|
||||
Renderable_List* render_renderables_;
|
||||
Renderable_List* cache_renderables_;
|
||||
mutable std::mutex renderable_mutex_;
|
||||
std::recursive_mutex task_mutex_;
|
||||
mutable std::recursive_mutex task_mutex_;
|
||||
std::condition_variable_any task_ready_;
|
||||
std::condition_variable_any render_completed_;
|
||||
std::thread worker_;
|
||||
@@ -138,6 +147,14 @@ private:
|
||||
bool task_pending_{};
|
||||
bool rendering_{};
|
||||
bool stop_{};
|
||||
std::atomic<std::uint64_t> task_execution_render_sequence_{};
|
||||
std::atomic<std::size_t> task_execution_node_count_{};
|
||||
std::atomic<std::size_t> task_execution_completed_count_{};
|
||||
std::atomic<std::size_t> task_execution_running_count_{};
|
||||
std::atomic<std::size_t> task_execution_peak_parallelism_{};
|
||||
std::atomic<std::size_t> task_execution_failed_count_{};
|
||||
std::atomic<std::uint64_t> task_execution_started_ns_{};
|
||||
std::atomic<std::uint64_t> task_execution_duration_ns_{};
|
||||
};
|
||||
class Scene_2D_Base : public Scene_Base {
|
||||
public:
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
enum class Task_Execution_State : std::uint8_t {
|
||||
Idle,
|
||||
Queued,
|
||||
Running,
|
||||
Completed,
|
||||
Failed
|
||||
};
|
||||
|
||||
struct Task_Execution_Snapshot {
|
||||
Task_Execution_State state{Task_Execution_State::Idle};
|
||||
std::size_t worker_count{};
|
||||
std::uint64_t render_sequence{};
|
||||
std::size_t graph_node_count{};
|
||||
std::size_t completed_count{};
|
||||
std::size_t running_count{};
|
||||
std::size_t pending_count{};
|
||||
std::size_t peak_parallelism{};
|
||||
std::size_t failed_count{};
|
||||
std::uint64_t duration_ns{};
|
||||
};
|
||||
@@ -129,8 +129,25 @@ TEST(renderable_task_graph_concurrency_test, runs_independent_internal_tasks_con
|
||||
auto renderable = std::make_shared<Renderable_Task_Graph_Concurrency_Test_Renderable>(scene, active, maximum);
|
||||
scene.attach_renderable(renderable);
|
||||
scene.render();
|
||||
while (active.load(std::memory_order_acquire) == 0)
|
||||
std::this_thread::yield();
|
||||
const auto running = scene.task_execution_snapshot();
|
||||
EXPECT_EQ(running.state, Task_Execution_State::Running);
|
||||
EXPECT_EQ(running.graph_node_count, 4u);
|
||||
EXPECT_GT(running.running_count, 0u);
|
||||
EXPECT_LT(running.completed_count, running.graph_node_count);
|
||||
scene.wait_for_render();
|
||||
EXPECT_GE(maximum.load(std::memory_order_acquire), 2);
|
||||
const auto execution = scene.task_execution_snapshot();
|
||||
EXPECT_EQ(execution.state, Task_Execution_State::Completed);
|
||||
EXPECT_EQ(execution.worker_count, Scene_Base::task_executor_worker_count());
|
||||
EXPECT_EQ(execution.graph_node_count, 4u);
|
||||
EXPECT_EQ(execution.completed_count, execution.graph_node_count);
|
||||
EXPECT_EQ(execution.running_count, 0u);
|
||||
EXPECT_EQ(execution.pending_count, 0u);
|
||||
EXPECT_GE(execution.peak_parallelism, 2u);
|
||||
EXPECT_EQ(execution.failed_count, 0u);
|
||||
EXPECT_GT(execution.duration_ns, 0u);
|
||||
}
|
||||
#endif
|
||||
TEST(renderable_task_graph_test, rejects_tasks_from_different_graphs) {
|
||||
|
||||
@@ -38,6 +38,12 @@ TEST(scene_base_test, wait_for_render_propagates_background_render_failure) {
|
||||
scene.attach_renderable(renderable);
|
||||
scene.render();
|
||||
EXPECT_THROW(scene.wait_for_render(), std::runtime_error);
|
||||
const auto execution = scene.task_execution_snapshot();
|
||||
EXPECT_EQ(execution.state, Task_Execution_State::Failed);
|
||||
EXPECT_EQ(execution.graph_node_count, 3u);
|
||||
EXPECT_EQ(execution.failed_count, 1u);
|
||||
EXPECT_EQ(execution.running_count, 0u);
|
||||
EXPECT_GT(execution.duration_ns, 0u);
|
||||
}
|
||||
TEST(scene_base_test, scene_destruction_does_not_throw_after_background_render_failure) {
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user