内核优化
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user