This commit is contained in:
2026-08-16 00:07:05 +08:00
parent 7a94264c13
commit 893be8d2c6
21 changed files with 363 additions and 211 deletions
+1 -1
View File
@@ -49,7 +49,7 @@ install(TARGETS Renderive_Kernel
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
install(DIRECTORY "${Renderive_Kernel_source_dir}/renderive"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp")
FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" PATTERN "*.inl")
if (RENDERIVE_BUILD_TESTS)
set(Renderive_Kernel_test_dir "${CMAKE_CURRENT_LIST_DIR}/tests")
append_glob_source(Renderive_Kernel_test_sources "${Renderive_Kernel_test_dir}")
@@ -224,7 +224,10 @@ Frame_Analysis analyze_frame(const Render_Plan& plan, const Frame_Snapshot& fram
}
auto& analysis = result.nodes[index];
const auto& execution = frame.node_executions[index];
analysis.dependency_ready_time_ns = execution.ready_time_ns;
analysis.dependency_ready_offset_ns =
execution.ready_time_ns >= frame.render_start_ns
? execution.ready_time_ns - frame.render_start_ns
: 0;
analysis.scheduler_wait_ns = execution.start_time_ns >= execution.ready_time_ns
? execution.start_time_ns - execution.ready_time_ns
: 0;
@@ -23,7 +23,7 @@ struct Node_Frame_Analysis {
std::uint64_t gpu_execution_duration_ns{};
std::uint64_t start_offset_ns{};
std::uint64_t end_offset_ns{};
std::uint64_t dependency_ready_time_ns{};
std::uint64_t dependency_ready_offset_ns{};
std::uint64_t scheduler_wait_ns{};
double work_contribution{};
double critical_path_contribution{};
@@ -64,8 +64,17 @@ struct Render_Graph_Runtime::State
}
void execute(std::span<Node_Execution* const> execution_slots,
Execute_Node execute) {
if (running.exchange(true, std::memory_order_acq_rel))
throw std::logic_error("render graph runtime is already executing");
{
std::lock_guard lock(lifecycle_mutex);
if (running.load(std::memory_order_acquire))
throw std::logic_error("render graph runtime is already executing");
cancellation_requested.store(false, std::memory_order_release);
{
std::lock_guard cancellation_lock(cancellation_mutex);
cancellation_exception = nullptr;
}
running.store(true, std::memory_order_release);
}
std::exception_ptr execution_error;
try {
@@ -119,23 +128,31 @@ struct Render_Graph_Runtime::State
std::fill(external_operations.begin(),
external_operations.end(), External_Operation{});
}
cancellation_requested.store(false, std::memory_order_release);
{
std::lock_guard lock(cancellation_mutex);
cancellation_exception = nullptr;
std::lock_guard lock(lifecycle_mutex);
cancellation_requested.store(false, std::memory_order_release);
{
std::lock_guard cancellation_lock(cancellation_mutex);
cancellation_exception = nullptr;
}
running.store(false, std::memory_order_release);
}
running.store(false, std::memory_order_release);
}
void cancel_pending(std::exception_ptr reason) noexcept {
auto error = make_cancellation_error(std::move(reason));
{
std::lock_guard lock(cancellation_mutex);
if (!cancellation_exception)
cancellation_exception = error;
else
error = cancellation_exception;
std::lock_guard lifecycle_lock(lifecycle_mutex);
if (!running.load(std::memory_order_acquire))
return;
{
std::lock_guard cancellation_lock(cancellation_mutex);
if (!cancellation_exception)
cancellation_exception = error;
else
error = cancellation_exception;
}
cancellation_requested.store(true, std::memory_order_release);
}
cancellation_requested.store(true, std::memory_order_release);
cancel_external_operations(error);
}
std::exception_ptr cancellation_error() noexcept {
@@ -291,6 +308,7 @@ struct Render_Graph_Runtime::State
std::vector<External_Operation> external_operations;
std::mutex cancellation_mutex;
std::exception_ptr cancellation_exception;
std::mutex lifecycle_mutex;
std::atomic_bool running{};
std::atomic_bool failed{};
std::atomic_bool cancellation_requested{};
+25 -56
View File
@@ -196,9 +196,11 @@ void Scene_Base::Renderable_Editor::attach(Renderable renderable) {
}
if (!scene_.dependency_resolver_.contains(id))
scene_.dependency_resolver_.attach(id);
if (auto* composition = scene_.composition_relationships())
composition->attach(id);
auto cache = scene_.make_renderable_color_cache();
if (scene_.raster_capabilities_)
scene_.raster_capabilities_->attach(id);
auto cache = scene_.raster_capabilities_
? scene_.raster_capabilities_->make_renderable_color_cache()
: std::shared_ptr<Color_Cache>{};
if (!scene_.renderables_.try_emplace(id, renderable).second)
throw std::logic_error("renderable id is already attached");
try {
@@ -275,7 +277,9 @@ Scene_2D_Base::Scene_2D_Base(std::pmr::memory_resource& memory_resource)
Scene_2D_Base::Scene_2D_Base(std::pmr::memory_resource& memory_resource,
std::unique_ptr<Scene_Base::Impl> impl)
: Scene_Base(memory_resource, std::move(impl)),
display_resolver_(this->memory_resource()) {}
display_resolver_(this->memory_resource()) {
bind_raster_capabilities(*this);
}
Scene_2D_Base::Attach_Builder Scene_2D_Base::attach_builder() {
return Attach_Builder(*this);
@@ -512,8 +516,8 @@ void Scene_Base::cleanup_detached_topology_locked() {
std::to_string(id));
dependency_resolver_.erase(id);
}
if (auto* composition = composition_relationships())
composition->cleanup();
if (raster_capabilities_)
raster_capabilities_->cleanup();
}
void Scene_Base::validate_structure_locked() {
for (const auto& [id, renderable] : renderables_) {
@@ -536,8 +540,8 @@ void Scene_Base::validate_structure_locked() {
"dependency graph references detached renderable " +
std::to_string(id));
}
if (const auto* composition = composition_relationships())
composition->validate();
if (raster_capabilities_)
raster_capabilities_->validate();
}
[[noreturn]] void Scene_Base::structure_fail_fast(const char* stage, std::exception_ptr exception) noexcept {
std::fprintf(stderr, "Renderive scene structure failure during %s", stage ? stage : "unknown stage");
@@ -641,9 +645,8 @@ Scene_Base::Topology_Snapshot Scene_Base::topology_snapshot() const {
std::lock_guard lock(model_mutex_);
Topology_Snapshot snapshot;
const auto dependency = dependency_resolver_.resolve();
const auto* composition = composition_relationships();
const auto display = composition
? composition->resolve()
const auto display = raster_capabilities_
? raster_capabilities_->resolve()
: renderive::scene::dependency::Resolution<
Renderable_Id>{};
const auto& ids = dependency.order;
@@ -675,9 +678,8 @@ Scene_Base::Topology_Snapshot Scene_Base::topology_snapshot() const {
std::vector<Scene_Base::Const_Renderable> Scene_Base::paint_order_snapshot() const {
std::lock_guard lock(model_mutex_);
std::vector<Const_Renderable> result;
const auto* composition = composition_relationships();
auto display = composition
? composition->resolve()
auto display = raster_capabilities_
? raster_capabilities_->resolve()
: renderive::scene::dependency::Resolution<
Renderable_Id>{};
if (display.order.empty())
@@ -691,9 +693,8 @@ std::shared_ptr<Frame_Render_Snapshot> Scene_Base::snapshot_live_model() {
auto snapshot = std::make_shared<Frame_Render_Snapshot>();
std::lock_guard lock(model_mutex_);
const auto dependency = dependency_resolver_.resolve();
const auto* composition = composition_relationships();
const auto display = composition
? composition->resolve()
const auto display = raster_capabilities_
? raster_capabilities_->resolve()
: renderive::scene::dependency::Resolution<
Renderable_Id>{};
const auto& dependency_order = dependency.order;
@@ -774,7 +775,8 @@ std::shared_ptr<Frame_Render_Snapshot> Scene_Base::capture_live_frame() {
auto snapshot = snapshot_live_model();
snapshot->scene_state_revision_ = acquire_scene_state();
capture_scene_state(*snapshot);
snapshot->viewport = frame_viewport();
if (raster_capabilities_)
snapshot->viewport = raster_capabilities_->frame_viewport();
return snapshot;
}
@@ -855,44 +857,11 @@ const Frame_Control_Strategy_Base& Scene_Base::frame_control_strategy() const {
return frame_control_strategy_impl();
}
Frame_Control_Strategy_Base& Scene_Base::frame_control_strategy_impl() {
throw std::logic_error("frame control strategy is not available");
}
const Frame_Control_Strategy_Base& Scene_Base::frame_control_strategy_impl() const {
throw std::logic_error("frame control strategy is not available");
}
std::shared_ptr<Color_Cache> Scene_Base::make_renderable_color_cache() {
return {};
}
Frame_Viewport Scene_Base::frame_viewport() const {
return {};
}
std::uint64_t Scene_Base::acquire_scene_state() {
return 0;
}
void Scene_Base::capture_scene_state(Frame_Render_Snapshot&) const {}
renderive::scene::detail::Composition_Relationships*
Scene_Base::composition_relationships() noexcept {
auto* scene_2d = dynamic_cast<Scene_2D_Base*>(this);
return scene_2d
? static_cast<
renderive::scene::detail::Composition_Relationships*>(
scene_2d)
: nullptr;
}
const renderive::scene::detail::Composition_Relationships*
Scene_Base::composition_relationships() const noexcept {
const auto* scene_2d = dynamic_cast<const Scene_2D_Base*>(this);
return scene_2d
? static_cast<const renderive::scene::detail::
Composition_Relationships*>(scene_2d)
: nullptr;
void Scene_Base::bind_raster_capabilities(
renderive::scene::detail::Raster_Capabilities& capabilities) noexcept {
if (raster_capabilities_ != nullptr)
std::terminate();
raster_capabilities_ = &capabilities;
}
void Scene_2D_Base::attach(Renderable_Id id) {
+15 -11
View File
@@ -44,6 +44,11 @@ struct Composition_Relationships {
virtual dependency::Resolution<Renderable_Id> resolve() const = 0;
};
struct Raster_Capabilities : Composition_Relationships {
virtual std::shared_ptr<Color_Cache> make_renderable_color_cache() = 0;
virtual Frame_Viewport frame_viewport() const = 0;
};
} // namespace renderive::scene::detail
class Scene_Compositor {
@@ -204,12 +209,10 @@ protected:
Impl& d_func() noexcept { return *impl_; }
const Impl& d_func() const noexcept { return *impl_; }
virtual Frame_Control_Strategy_Base& frame_control_strategy_impl();
virtual const Frame_Control_Strategy_Base& frame_control_strategy_impl() const;
virtual std::shared_ptr<Color_Cache> make_renderable_color_cache();
virtual Frame_Viewport frame_viewport() const;
virtual std::uint64_t acquire_scene_state();
virtual void capture_scene_state(Frame_Render_Snapshot& snapshot) const;
virtual Frame_Control_Strategy_Base& frame_control_strategy_impl() = 0;
virtual const Frame_Control_Strategy_Base& frame_control_strategy_impl() const = 0;
virtual std::uint64_t acquire_scene_state() = 0;
virtual void capture_scene_state(Frame_Render_Snapshot& snapshot) const = 0;
std::unique_lock<std::recursive_mutex> lock_render_idle();
[[nodiscard]] bool is_render_execution_context() const noexcept;
bool consume_model_dirty() noexcept;
@@ -271,10 +274,8 @@ private:
const Renderable& renderable) const;
void validate_structure_locked();
void cleanup_detached_topology_locked();
renderive::scene::detail::Composition_Relationships*
composition_relationships() noexcept;
const renderive::scene::detail::Composition_Relationships*
composition_relationships() const noexcept;
void bind_raster_capabilities(
renderive::scene::detail::Raster_Capabilities& capabilities) noexcept;
[[noreturn]] static void structure_fail_fast(const char* stage, std::exception_ptr exception = {}) noexcept;
inline static thread_local Scene_Base* active_execution_scene_{};
@@ -289,6 +290,7 @@ private:
std::pmr::unordered_map<Renderable_Id, Renderable> renderables_;
renderive::scene::dependency::Dependency_Resolver<Renderable_Id>
dependency_resolver_;
renderive::scene::detail::Raster_Capabilities* raster_capabilities_{};
std::pmr::unordered_map<Renderable_Id, std::shared_ptr<Color_Cache>> color_caches_;
mutable std::mutex model_mutex_;
@@ -313,7 +315,7 @@ private:
class Scene_2D_Base
: public Scene_Base,
private renderive::scene::detail::Composition_Relationships {
private renderive::scene::detail::Raster_Capabilities {
public:
class Renderable_Editor
: public renderive::scene_inheritance::Editor_Node<
@@ -366,6 +368,8 @@ protected:
};
Scene_2D_Base(std::pmr::memory_resource& memory_resource,
std::unique_ptr<Scene_Base::Impl> impl);
virtual std::shared_ptr<Color_Cache> make_renderable_color_cache() override = 0;
virtual Frame_Viewport frame_viewport() const override = 0;
private:
friend class Scene_Base;
@@ -89,7 +89,7 @@ TEST(render_dag_test, analysis_derives_wait_critical_path_and_parallel_overlap_f
const auto analysis = analyze_frame(*plan, snapshot);
ASSERT_EQ(analysis.nodes.size(), 3u);
EXPECT_EQ(analysis.nodes[2].dependency_ready_time_ns, 150u);
EXPECT_EQ(analysis.nodes[2].dependency_ready_offset_ns, 50u);
EXPECT_EQ(analysis.nodes[2].scheduler_wait_ns, 10u);
EXPECT_EQ(analysis.total_render_duration_ns, 100u);
EXPECT_EQ(analysis.total_work_duration_ns, 100u);
@@ -280,6 +280,25 @@ TEST(render_graph_runtime_test,
EXPECT_EQ(source.operation().status(), External_Operation_Status::cancelled);
}
TEST(render_graph_runtime_test, idle_cancellation_does_not_arm_next_execution) {
const auto plan = two_node_plan();
std::vector<Node_Execution> execution_storage;
auto slots = execution_slots(*plan, execution_storage);
renderive::render_graph::detail::Render_Graph_Runtime runtime(*plan);
runtime.cancel_pending();
std::atomic<int> executed{};
EXPECT_NO_THROW(runtime.execute(
slots, [&](std::size_t, Node_Execution_Metrics*) {
executed.fetch_add(1, std::memory_order_relaxed);
return Node_Execution_Result::completed();
}));
EXPECT_EQ(executed.load(std::memory_order_relaxed), 2);
EXPECT_EQ(execution_storage[0].status, Node_Execution_Status::complete);
EXPECT_EQ(execution_storage[1].status, Node_Execution_Status::complete);
}
TEST(external_operation_test, completion_commit_is_exclusive_with_cancellation) {
External_Operation_Source source;
const auto operation = source.operation();