内核优化

This commit is contained in:
2026-08-10 16:17:59 +08:00
parent 3f37c8e6d5
commit c677c65f0e
8 changed files with 136 additions and 2 deletions
@@ -21,6 +21,7 @@ public:
: observer_(std::move(other.observer_)), time_source_(std::move(other.time_source_)) {}
Observer_State& operator=(Observer_State&&) = delete;
std::uint64_t now_ns() const noexcept {
std::lock_guard<Mutex> lock(time_source_mutex_);
return static_cast<std::uint64_t>(time_source_.now_ns());
}
template <class Target>
@@ -43,6 +44,7 @@ private:
struct Disabled_Mutex {};
[[no_unique_address]] Observer observer_;
[[no_unique_address]] Time_Source time_source_;
mutable Mutex time_source_mutex_;
[[no_unique_address]] std::conditional_t<Observer::enabled, Mutex, Disabled_Mutex> mutex_;
};
template <class Observer>
@@ -5,6 +5,7 @@
#include <cstddef>
#include <cstdint>
#include <memory>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include <utility>
@@ -14,7 +15,13 @@
#include "renderive/scene/base/Scene_Render_Context.hpp"
template <Real_Time_Data... Data>
struct With_Real_Time_Data {
explicit With_Real_Time_Data(std::shared_ptr<Data>... data) : data(std::move(data)...) {}
explicit With_Real_Time_Data(std::shared_ptr<Data>... data) : data(std::move(data)...) {
if (!std::apply([](const auto&... source) {
return (static_cast<bool>(source) && ...);
}, this->data)) {
throw std::invalid_argument("real-time data source is null");
}
}
std::tuple<std::shared_ptr<Data>...> data;
};
template <class... Data>
@@ -1,8 +1,9 @@
#pragma once
#include <concepts>
#include <memory_resource>
#include "Color_Cache.hpp"
template <class Cache>
concept Color_Cache_Type = std::derived_from<Cache, Color_Cache> && std::default_initializable<Cache> && requires(Cache& cache, const Cache& source) {
concept Color_Cache_Type = std::derived_from<Cache, Color_Cache> && (std::default_initializable<Cache> || std::constructible_from<Cache, std::pmr::memory_resource&>) && requires(Cache& cache, const Cache& source) {
{ cache.clear() } -> std::same_as<void>;
{ cache.composite(source) } -> std::same_as<void>;
};
@@ -47,6 +47,14 @@ protected:
const Frame_Control_Strategy_Base& frame_control_strategy_impl() const noexcept override {
return frame_control;
}
void on_renderable_detached(Renderable_Base& renderable) override {
promote_children(Scene_Base::layer_node(renderable));
auto& dependency = Scene_Base::dependency_node(renderable);
for (std::size_t index = 0; index < dependency.child_count(); ++index) {
dependency.child(index).owner()->invalidate_cache();
}
promote_children(dependency);
}
std::uint64_t acquire_scene_state() override {
return this->Scene_State_Strategy::acquire_render_state();
}
@@ -57,6 +65,24 @@ protected:
return scene_observer_.now_ns();
}
private:
template <class Node>
static void promote_children(Node& node) {
Node* parent = node.parent();
if (!parent) {
while (!node.children_empty()) {
node.child(0).detach();
}
return;
}
std::size_t index{};
while (&parent->child(index) != &node) {
++index;
}
node.detach();
while (!node.children_empty()) {
parent->insert_child(index++, node.child(0));
}
}
template <class... Args>
static Frame_Control make_frame_control(std::pmr::memory_resource& memory_resource, Args&&... args) {
if constexpr (std::constructible_from<Frame_Control, std::pmr::memory_resource&, Args&&...>) {
@@ -1,7 +1,10 @@
#include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <cstdint>
#include <memory>
#include <thread>
#include <vector>
#include "renderive/base/observer/Observer.hpp"
struct Observer_Test_Observation {
int value{};
@@ -40,3 +43,41 @@ TEST(disabled_observer_test, accepts_any_observation_structure) {
EXPECT_NO_THROW(observer.observe(Observer_Test_Observation{7}));
EXPECT_GT(observer.now_ns(), 0);
}
struct Observer_Test_Concurrent_Time_Source_Data {
std::atomic<int> active{};
std::atomic<bool> overlap{};
std::uint64_t value{};
};
struct Observer_Test_Concurrent_Time_Source {
std::shared_ptr<Observer_Test_Concurrent_Time_Source_Data> data{std::make_shared<Observer_Test_Concurrent_Time_Source_Data>()};
std::uint64_t now_ns() const noexcept {
if (data->active.fetch_add(1, std::memory_order_acq_rel) != 0) {
data->overlap.store(true, std::memory_order_release);
}
std::this_thread::sleep_for(std::chrono::milliseconds(2));
const auto value = data->value++;
data->active.fetch_sub(1, std::memory_order_acq_rel);
return value;
}
};
TEST(observer_state_test, serializes_time_source_access) {
Observer_Test_Concurrent_Time_Source time_source;
const auto data = time_source.data;
Observer_State<Disabled_Observer, Observer_Test_Concurrent_Time_Source> observer(Disabled_Observer{}, std::move(time_source));
std::atomic<bool> start{};
std::vector<std::thread> threads;
for (int index = 0; index < 8; ++index) {
threads.emplace_back([&] {
while (!start.load(std::memory_order_acquire)) {
std::this_thread::yield();
}
observer.now_ns();
});
}
start.store(true, std::memory_order_release);
for (auto& thread : threads) {
thread.join();
}
EXPECT_FALSE(data->overlap.load(std::memory_order_acquire));
EXPECT_EQ(data->value, 8);
}
@@ -76,6 +76,10 @@ TEST(real_time_data_attachment_test, discards_history_older_than_one_frame_inter
EXPECT_EQ(history->snapshot(), (std::vector<int>{2, 3}));
EXPECT_EQ(renderable->render_count.load(), 1);
}
TEST(real_time_data_attachment_test, rejects_null_real_time_data_source) {
std::shared_ptr<Real_Time_Data_Test_Latest> latest;
EXPECT_THROW((With_Real_Time_Data(latest)), std::invalid_argument);
}
using Real_Time_Data_Test_Latest_Attachment = Attach_Real_Time_Data<Real_Time_Data_Test_Renderable, Real_Time_Data_Test_Latest>;
TEST(real_time_data_attachment_test, supports_multiple_renderables_and_unbinds_destroyed_renderable) {
Scene2D_Context<> first_scene;
@@ -1,6 +1,14 @@
#include <gtest/gtest.h>
#include "renderive/renderable/Renderable.hpp"
#include "renderive/scene/Scene.hpp"
static_assert(Color_Cache_Type<Recording_Color_Cache>);
struct Resource_Only_Color_Cache : Color_Cache {
explicit Resource_Only_Color_Cache(std::pmr::memory_resource& memory_resource) : memory_resource(&memory_resource) {}
void clear() override {}
void composite(const Color_Cache&) override {}
std::pmr::memory_resource* memory_resource;
};
static_assert(Color_Cache_Type<Resource_Only_Color_Cache>);
TEST(color_cache_test, composites_source_values_in_order) {
Recording_Color_Cache first;
Recording_Color_Cache second;
@@ -13,3 +21,10 @@ TEST(color_cache_test, composites_source_values_in_order) {
EXPECT_EQ(result.values.at(0), 1);
EXPECT_EQ(result.values.at(1), 2);
}
TEST(color_cache_test, scene_accepts_resource_only_cache) {
using Strategy = Low_Latency_Strategy<Scene2D_Frame_Data>;
Scene2D_Context<Strategy, Resource_Only_Color_Cache> scene;
scene.with_final_color_cache([&](const Resource_Only_Color_Cache& cache) {
EXPECT_EQ(cache.memory_resource, &scene.memory_resource());
});
}
@@ -50,3 +50,41 @@ TEST(scene3d_context_test, propagates_cache_invalidation_through_unsorted_depend
EXPECT_EQ(middle->render_count, 2);
EXPECT_EQ(leaf->render_count, 2);
}
TEST(scene3d_context_test, detach_parent_promotes_children_and_invalidates_dependency_cache) {
Scene3D_Context<> scene;
auto grandparent = std::make_shared<Scene3D_Dependency_Cache_Test_Renderable>(scene);
auto parent = std::make_shared<Scene3D_Dependency_Cache_Test_Renderable>(scene);
auto child = std::make_shared<Scene3D_Dependency_Cache_Test_Renderable>(scene);
scene.attach_renderable(grandparent);
scene.attach_renderable(parent);
scene.attach_renderable(child);
scene.set_display_parent(*parent, grandparent.get());
scene.set_display_parent(*child, parent.get());
scene.set_dependency_parent(*parent, grandparent.get());
scene.set_dependency_parent(*child, parent.get());
scene.render();
scene.wait_for_render();
EXPECT_EQ(child->render_count, 1);
scene.detach_renderable(*parent);
const auto topology = scene.topology_snapshot();
bool display_checked{};
bool dependency_checked{};
for (const auto& relation : topology.display) {
if (relation.child.get() == child.get()) {
EXPECT_EQ(relation.parent.get(), grandparent.get());
display_checked = true;
}
}
for (const auto& relation : topology.dependency) {
if (relation.child.get() == child.get()) {
EXPECT_EQ(relation.parent.get(), grandparent.get());
dependency_checked = true;
}
}
EXPECT_TRUE(display_checked);
EXPECT_TRUE(dependency_checked);
scene.render();
scene.wait_for_render();
EXPECT_EQ(parent->render_count, 1);
EXPECT_EQ(child->render_count, 2);
}