内核优化
This commit is contained in:
@@ -17,7 +17,7 @@ public:
|
||||
Observer_State(Observer observer, Time_Source time_source) : observer_(std::move(observer)), time_source_(std::move(time_source)) {}
|
||||
Observer_State(const Observer_State&) = delete;
|
||||
Observer_State& operator=(const Observer_State&) = delete;
|
||||
Observer_State(Observer_State&& other) noexcept requires std::move_constructible<Observer> && std::move_constructible<Time_Source>
|
||||
Observer_State(Observer_State&& other) noexcept(std::is_nothrow_move_constructible_v<Observer> && std::is_nothrow_move_constructible_v<Time_Source> && std::is_nothrow_default_constructible_v<Mutex>) requires std::move_constructible<Observer> && std::move_constructible<Time_Source>
|
||||
: 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 {
|
||||
|
||||
@@ -18,8 +18,8 @@ public:
|
||||
virtual double frequency_hz() const noexcept = 0;
|
||||
virtual std::uint64_t next_refresh_interval_ns() const noexcept = 0;
|
||||
virtual State frame_control_state() const = 0;
|
||||
virtual void on_real_time_data_update(const Real_Time_Data_Observation& observation) = 0;
|
||||
virtual bool discard_stale_latest_data_frame() {
|
||||
virtual void on_real_time_data_update(const Real_Time_Data_Observation& observation) noexcept = 0;
|
||||
virtual bool discard_stale_latest_data_frame() noexcept {
|
||||
return false;
|
||||
}
|
||||
static constexpr double invalid_frequency_hz() noexcept {
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
#include "renderive/real_time_data/Observation.hpp"
|
||||
template <class That>
|
||||
concept Real_Time_Data_Aware_Frame_Strategy = Frame_Control_Strategy<That> && requires(That& strategy, const Real_Time_Data_Observation& observation) {
|
||||
{ strategy.on_real_time_data_update(observation) } -> std::same_as<void>;
|
||||
{ strategy.on_real_time_data_update(observation) } noexcept -> std::same_as<void>;
|
||||
};
|
||||
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
Frame_Control_Strategy_Base::State frame_control_state() const override;
|
||||
std::size_t pending_frame_count() const;
|
||||
State state() const;
|
||||
void on_real_time_data_update(const Real_Time_Data_Observation& observation) override;
|
||||
void on_real_time_data_update(const Real_Time_Data_Observation& observation) noexcept override;
|
||||
private:
|
||||
std::uint64_t now_ns() const noexcept;
|
||||
void observe(const Observation& observation) noexcept;
|
||||
|
||||
@@ -190,7 +190,7 @@ auto Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::state() const -> State
|
||||
return result;
|
||||
}
|
||||
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
|
||||
void Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::on_real_time_data_update(const Real_Time_Data_Observation& observation) {
|
||||
void Flow_Refresh_Strategy<Scene_Frame, Mutex, Observer>::on_real_time_data_update(const Real_Time_Data_Observation& observation) noexcept {
|
||||
Observation strategy_observation;
|
||||
{
|
||||
std::lock_guard<Mutex> lock(state_mutex_);
|
||||
|
||||
@@ -148,10 +148,10 @@ public:
|
||||
Frame_Control_Strategy_Base::State frame_control_state() const override;
|
||||
State state() const;
|
||||
Counter_Statistics counter_statistics() const;
|
||||
void on_real_time_data_update(const Real_Time_Data_Observation& observation) override;
|
||||
void on_real_time_data_update(const Real_Time_Data_Observation& observation) noexcept override;
|
||||
bool discard_pending_frame();
|
||||
bool discard_pending_frame_before(std::uint64_t real_time_data_update_sequence);
|
||||
bool discard_stale_latest_data_frame() override;
|
||||
bool discard_stale_latest_data_frame() noexcept override;
|
||||
private:
|
||||
static double checked_frequency_hz(double frequency_hz);
|
||||
std::uint64_t now_ns() const noexcept;
|
||||
|
||||
@@ -250,7 +250,7 @@ auto Low_Latency_Strategy<Scene_Frame, Mutex, Observer>::counter_statistics() co
|
||||
return counters_;
|
||||
}
|
||||
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
|
||||
void Low_Latency_Strategy<Scene_Frame, Mutex, Observer>::on_real_time_data_update(const Real_Time_Data_Observation& observation) {
|
||||
void Low_Latency_Strategy<Scene_Frame, Mutex, Observer>::on_real_time_data_update(const Real_Time_Data_Observation& observation) noexcept {
|
||||
Observation strategy_observation;
|
||||
{
|
||||
std::lock_guard<Mutex> lock(state_mutex_);
|
||||
@@ -290,7 +290,7 @@ bool Low_Latency_Strategy<Scene_Frame, Mutex, Observer>::discard_pending_frame_b
|
||||
return discarded;
|
||||
}
|
||||
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
|
||||
bool Low_Latency_Strategy<Scene_Frame, Mutex, Observer>::discard_stale_latest_data_frame() {
|
||||
bool Low_Latency_Strategy<Scene_Frame, Mutex, Observer>::discard_stale_latest_data_frame() noexcept {
|
||||
Observation observation;
|
||||
bool discarded{};
|
||||
{
|
||||
|
||||
@@ -100,7 +100,7 @@ public:
|
||||
bool refresh();
|
||||
bool discard_pending_frame();
|
||||
State state() const;
|
||||
void on_real_time_data_update(const Real_Time_Data_Observation& observation) override;
|
||||
void on_real_time_data_update(const Real_Time_Data_Observation& observation) noexcept override;
|
||||
private:
|
||||
std::uint64_t now_ns() const noexcept;
|
||||
void observe(const Observation& observation) noexcept;
|
||||
|
||||
@@ -195,7 +195,7 @@ auto Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::state() const -> Sta
|
||||
return state_;
|
||||
}
|
||||
template <class Scene_Frame, Mutex_Type Mutex, class Observer>
|
||||
void Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::on_real_time_data_update(const Real_Time_Data_Observation& observation) {
|
||||
void Manual_Refresh_Strategy<Scene_Frame, Mutex, Observer>::on_real_time_data_update(const Real_Time_Data_Observation& observation) noexcept {
|
||||
Observation strategy_observation;
|
||||
{
|
||||
std::lock_guard<Mutex> lock(state_mutex_);
|
||||
|
||||
@@ -13,7 +13,14 @@
|
||||
#include "renderive/renderable/concept/Renderable.hpp"
|
||||
#include "renderive/scene/base/Scene_Base.hpp"
|
||||
#include "renderive/scene/base/Scene_Render_Context.hpp"
|
||||
template <class... Data>
|
||||
inline constexpr bool Unique_Real_Time_Data_Types_Value = true;
|
||||
template <class Data, class... Rest>
|
||||
inline constexpr bool Unique_Real_Time_Data_Types_Value<Data, Rest...> = (!std::same_as<Data, Rest> && ...) && Unique_Real_Time_Data_Types_Value<Rest...>;
|
||||
template <class... Data>
|
||||
concept Unique_Real_Time_Data_Types = Unique_Real_Time_Data_Types_Value<Data...>;
|
||||
template <Real_Time_Data... Data>
|
||||
requires Unique_Real_Time_Data_Types<Data...>
|
||||
struct With_Real_Time_Data {
|
||||
explicit With_Real_Time_Data(std::shared_ptr<Data>... data) : data(std::move(data)...) {
|
||||
if (!std::apply([](const auto&... source) {
|
||||
@@ -36,6 +43,7 @@ enum class Real_Time_Data_Discard_Mode {
|
||||
retain_frame_interval
|
||||
};
|
||||
template <Renderable Renderable_Type, Real_Time_Data... Data>
|
||||
requires Unique_Real_Time_Data_Types<Data...>
|
||||
class Attach_Real_Time_Data : public Renderable_Type {
|
||||
public:
|
||||
template <class... Args>
|
||||
|
||||
@@ -24,11 +24,18 @@ void History_Real_Time_Data<Value_Type, Container, Mutex, Observer>::update(Valu
|
||||
Real_Time_Data_Observation observation;
|
||||
{
|
||||
std::lock_guard<Mutex> lock(mutex_);
|
||||
values_.push_back(std::move(value));
|
||||
update_times_.push_back(0);
|
||||
try {
|
||||
values_.push_back(std::move(value));
|
||||
} catch (...) {
|
||||
update_times_.pop_back();
|
||||
throw;
|
||||
}
|
||||
const std::uint64_t update_time_ns = observer_.now_ns();
|
||||
update_times_.back() = update_time_ns;
|
||||
++revision_;
|
||||
++total_update_count_;
|
||||
last_update_time_ns_ = observer_.now_ns();
|
||||
update_times_.push_back(last_update_time_ns_);
|
||||
last_update_time_ns_ = update_time_ns;
|
||||
observation = {Real_Time_Data_Observation_Event::updated, {this, Real_Time_Data_Retention::history, revision_, last_update_time_ns_, total_update_count_, values_.size()}};
|
||||
}
|
||||
observer_.observe(observation);
|
||||
|
||||
@@ -21,7 +21,7 @@ template <class Frame_Control, class... Args>
|
||||
concept Scene2D_Frame_Control_Constructible = std::constructible_from<Frame_Control, Args...> || std::constructible_from<Frame_Control, std::pmr::memory_resource&, Args...>;
|
||||
template <class Strategy = Low_Latency_Strategy<Scene2D_Frame_Data>, Color_Cache_Type Cache = Recording_Color_Cache, class State = Scene2D_State, class State_Observer = Observer_State<>, class Scene_Observer = Observer_State<>>
|
||||
requires Frame_Control_Strategy_For<Strategy, Scene2D_Frame_Data> && State_Value<State>
|
||||
class Scene2D_Context : public Triple_State_Strategy<Scene_2D_Base, State, Atomic_Spin_Mutex, State_Observer> {
|
||||
class Scene2D_Context final : public Triple_State_Strategy<Scene_2D_Base, State, Atomic_Spin_Mutex, State_Observer> {
|
||||
public:
|
||||
using Scene_State_Strategy = Triple_State_Strategy<Scene_2D_Base, State, Atomic_Spin_Mutex, State_Observer>;
|
||||
using Render_Task = Scene_Base::Render_Task;
|
||||
@@ -73,17 +73,29 @@ protected:
|
||||
return &dependency_root_;
|
||||
}
|
||||
void on_renderable_attached(Renderable_Base& renderable) override {
|
||||
Cache_Pointer cache = make_cache_pointer(this->memory_resource());
|
||||
auto& display_node = Scene_Base::layer_node(renderable);
|
||||
auto& dependency_node = Scene_Base::dependency_node(renderable);
|
||||
if (!display_node.parent()) {
|
||||
display_root_.append_child(display_node);
|
||||
}
|
||||
if (!dependency_node.parent()) {
|
||||
dependency_root_.append_child(dependency_node);
|
||||
}
|
||||
if (color_caches_.try_emplace(&renderable, make_cache_pointer(this->memory_resource())).second) {
|
||||
renderable.invalidate_cache();
|
||||
const bool attach_display = !display_node.parent();
|
||||
const bool attach_dependency = !dependency_node.parent();
|
||||
try {
|
||||
if (attach_display) {
|
||||
display_root_.append_child(display_node);
|
||||
}
|
||||
if (attach_dependency) {
|
||||
dependency_root_.append_child(dependency_node);
|
||||
}
|
||||
color_caches_.try_emplace(&renderable, std::move(cache));
|
||||
} catch (...) {
|
||||
if (attach_dependency) {
|
||||
dependency_node.detach();
|
||||
}
|
||||
if (attach_display) {
|
||||
display_node.detach();
|
||||
}
|
||||
throw;
|
||||
}
|
||||
renderable.invalidate_cache();
|
||||
}
|
||||
void on_renderable_detached(Renderable_Base& renderable) override {
|
||||
promote_children(Scene_Base::layer_node(renderable));
|
||||
|
||||
@@ -14,7 +14,7 @@ template <class Frame_Control, class... Args>
|
||||
concept Scene3D_Frame_Control_Constructible = std::constructible_from<Frame_Control, Args...> || std::constructible_from<Frame_Control, std::pmr::memory_resource&, Args...>;
|
||||
template <class Strategy = Low_Latency_Strategy<Scene3D_Frame_Data>, class State = Scene3D_State, class State_Observer = Observer_State<>, class Scene_Observer = Observer_State<>>
|
||||
requires Frame_Control_Strategy_For<Strategy, Scene3D_Frame_Data> && State_Value<State>
|
||||
class Scene3D_Context : public Triple_State_Strategy<Scene_3D_Base, State, Atomic_Spin_Mutex, State_Observer> {
|
||||
class Scene3D_Context final : public Triple_State_Strategy<Scene_3D_Base, State, Atomic_Spin_Mutex, State_Observer> {
|
||||
public:
|
||||
using Scene_State_Strategy = Triple_State_Strategy<Scene_3D_Base, State, Atomic_Spin_Mutex, State_Observer>;
|
||||
using Frame_Control = Strategy;
|
||||
|
||||
@@ -117,8 +117,9 @@ void Scene_Base::attach_renderable(Renderable renderable) {
|
||||
if (is_renderable_attached_locked(*renderable)) {
|
||||
return;
|
||||
}
|
||||
cache_renderables_->push_back(renderable);
|
||||
cache_renderables_->reserve(cache_renderables_->size() + 1);
|
||||
on_renderable_attached(*renderable);
|
||||
cache_renderables_->push_back(std::move(renderable));
|
||||
}
|
||||
void Scene_Base::detach_renderable(Renderable_Base& renderable) {
|
||||
auto task_lock = lock_render_idle();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <concepts>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "renderive/base/Atomic_Mutex.hpp"
|
||||
@@ -25,17 +26,17 @@ struct Double_State_Strategy : That, State_Strategy_Base {
|
||||
State state;
|
||||
};
|
||||
static_assert(Timed_Struct_Observer<Observer, Observation>);
|
||||
Double_State_Strategy() requires std::default_initializable<That> && std::default_initializable<State> : That(), states{}, render_state(&states[0]), cache_state(&states[1]) {}
|
||||
Double_State_Strategy() requires std::default_initializable<That> && std::default_initializable<State> : That(), states{}, render_state(&states[0]), cache_state(&states[1]), scratch_state(&states[2]) {}
|
||||
explicit Double_State_Strategy(With_Observer<Observer> option) requires std::default_initializable<That> && std::default_initializable<State>
|
||||
: That(), observer(std::move(option.observer)), states{}, render_state(&states[0]), cache_state(&states[1]) {}
|
||||
: That(), observer(std::move(option.observer)), states{}, render_state(&states[0]), cache_state(&states[1]), scratch_state(&states[2]) {}
|
||||
template <class... Args>
|
||||
requires std::constructible_from<That, Args&&...>
|
||||
explicit Double_State_Strategy(const State& state, Args&&... args)
|
||||
: That(std::forward<Args>(args)...), states{state, state}, render_state(&states[0]), cache_state(&states[1]) {}
|
||||
: That(std::forward<Args>(args)...), states{state, state, state}, render_state(&states[0]), cache_state(&states[1]), scratch_state(&states[2]) {}
|
||||
template <class... Args>
|
||||
requires std::constructible_from<That, Args&&...>
|
||||
Double_State_Strategy(const State& state, With_Observer<Observer> option, Args&&... args)
|
||||
: That(std::forward<Args>(args)...), observer(std::move(option.observer)), states{state, state}, render_state(&states[0]), cache_state(&states[1]) {}
|
||||
: That(std::forward<Args>(args)...), observer(std::move(option.observer)), states{state, state, state}, render_state(&states[0]), cache_state(&states[1]), scratch_state(&states[2]) {}
|
||||
template <auto Member, Property_Member_Assignable<State, Member> Value>
|
||||
Self& set(Value&& value) {
|
||||
Observation observation;
|
||||
@@ -59,15 +60,15 @@ struct Double_State_Strategy : That, State_Strategy_Base {
|
||||
}
|
||||
}
|
||||
void publish() override {
|
||||
Observation observation;
|
||||
std::optional<Observation> observation;
|
||||
{
|
||||
std::lock_guard lock(mtx);
|
||||
std::swap(render_state, cache_state);
|
||||
*cache_state = *render_state;
|
||||
*scratch_state = *cache_state;
|
||||
observation.emplace(Observation_Event::published, observer.now_ns(), cache_update_count, publish_count + 1, *cache_state);
|
||||
std::swap(render_state, scratch_state);
|
||||
++publish_count;
|
||||
observation = {Observation_Event::published, observer.now_ns(), cache_update_count, publish_count, *render_state};
|
||||
}
|
||||
observer.observe(observation);
|
||||
observer.observe(*observation);
|
||||
}
|
||||
std::uint64_t state_revision() const override {
|
||||
std::lock_guard lock(mtx);
|
||||
@@ -79,9 +80,10 @@ struct Double_State_Strategy : That, State_Strategy_Base {
|
||||
}
|
||||
private:
|
||||
Observer observer;
|
||||
State states[2];
|
||||
State states[3];
|
||||
State* render_state;
|
||||
State* cache_state;
|
||||
State* scratch_state;
|
||||
std::uint64_t cache_update_count{};
|
||||
std::uint64_t publish_count{};
|
||||
mutable Mutex mtx;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <concepts>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "renderive/base/Atomic_Mutex.hpp"
|
||||
@@ -28,17 +29,17 @@ struct Triple_State_Strategy : That, State_Strategy_Base {
|
||||
};
|
||||
static_assert(Timed_Struct_Observer<Observer, Observation>);
|
||||
Triple_State_Strategy() requires std::default_initializable<That> && std::default_initializable<State>
|
||||
: That(), states{}, render_state(&states[0]), published_state(&states[1]), cache_state(&states[2]) {}
|
||||
: That(), states{}, render_state(&states[0]), published_state(&states[1]), cache_state(&states[2]), scratch_state(&states[3]) {}
|
||||
explicit Triple_State_Strategy(With_Observer<Observer> option) requires std::default_initializable<That> && std::default_initializable<State>
|
||||
: That(), observer(std::move(option.observer)), states{}, render_state(&states[0]), published_state(&states[1]), cache_state(&states[2]) {}
|
||||
: That(), observer(std::move(option.observer)), states{}, render_state(&states[0]), published_state(&states[1]), cache_state(&states[2]), scratch_state(&states[3]) {}
|
||||
template <class... Args>
|
||||
requires std::constructible_from<That, Args&&...>
|
||||
explicit Triple_State_Strategy(const State& state, Args&&... args)
|
||||
: That(std::forward<Args>(args)...), states{state, state, state}, render_state(&states[0]), published_state(&states[1]), cache_state(&states[2]) {}
|
||||
: That(std::forward<Args>(args)...), states{state, state, state, state}, render_state(&states[0]), published_state(&states[1]), cache_state(&states[2]), scratch_state(&states[3]) {}
|
||||
template <class... Args>
|
||||
requires std::constructible_from<That, Args&&...>
|
||||
Triple_State_Strategy(const State& state, With_Observer<Observer> option, Args&&... args)
|
||||
: That(std::forward<Args>(args)...), observer(std::move(option.observer)), states{state, state, state}, render_state(&states[0]), published_state(&states[1]), cache_state(&states[2]) {}
|
||||
: That(std::forward<Args>(args)...), observer(std::move(option.observer)), states{state, state, state, state}, render_state(&states[0]), published_state(&states[1]), cache_state(&states[2]), scratch_state(&states[3]) {}
|
||||
template <auto Member, Property_Member_Assignable<State, Member> Value>
|
||||
Self& set(Value&& value) {
|
||||
Observation observation;
|
||||
@@ -62,33 +63,31 @@ struct Triple_State_Strategy : That, State_Strategy_Base {
|
||||
}
|
||||
}
|
||||
void publish() override {
|
||||
Observation observation;
|
||||
std::optional<Observation> observation;
|
||||
{
|
||||
std::lock_guard lock(mtx);
|
||||
std::swap(published_state, cache_state);
|
||||
*cache_state = *published_state;
|
||||
*scratch_state = *cache_state;
|
||||
observation.emplace(Observation_Event::published, observer.now_ns(), cache_update_count, publish_count + 1, render_revision, *cache_state);
|
||||
std::swap(published_state, scratch_state);
|
||||
++publish_count;
|
||||
published_revision = publish_count;
|
||||
observation = {Observation_Event::published, observer.now_ns(), cache_update_count, publish_count, render_revision, *published_state};
|
||||
}
|
||||
observer.observe(observation);
|
||||
observer.observe(*observation);
|
||||
}
|
||||
std::uint64_t acquire_render_state() {
|
||||
Observation observation;
|
||||
bool acquired{};
|
||||
std::optional<Observation> observation;
|
||||
std::uint64_t revision{};
|
||||
{
|
||||
std::lock_guard lock(mtx);
|
||||
if (render_revision != published_revision) {
|
||||
observation.emplace(Observation_Event::render_acquired, observer.now_ns(), cache_update_count, publish_count, published_revision, *published_state);
|
||||
std::swap(render_state, published_state);
|
||||
render_revision = published_revision;
|
||||
acquired = true;
|
||||
observation = {Observation_Event::render_acquired, observer.now_ns(), cache_update_count, publish_count, render_revision, *render_state};
|
||||
}
|
||||
revision = render_revision;
|
||||
}
|
||||
if (acquired) {
|
||||
observer.observe(observation);
|
||||
if (observation) {
|
||||
observer.observe(*observation);
|
||||
}
|
||||
return revision;
|
||||
}
|
||||
@@ -102,10 +101,11 @@ struct Triple_State_Strategy : That, State_Strategy_Base {
|
||||
}
|
||||
private:
|
||||
Observer observer;
|
||||
State states[3];
|
||||
State states[4];
|
||||
State* render_state;
|
||||
State* published_state;
|
||||
State* cache_state;
|
||||
State* scratch_state;
|
||||
std::uint64_t cache_update_count{};
|
||||
std::uint64_t publish_count{};
|
||||
std::uint64_t published_revision{};
|
||||
|
||||
@@ -81,3 +81,20 @@ TEST(observer_state_test, serializes_time_source_access) {
|
||||
EXPECT_FALSE(data->overlap.load(std::memory_order_acquire));
|
||||
EXPECT_EQ(data->value, 8);
|
||||
}
|
||||
struct Observer_Test_Throwing_Move_Time_Source {
|
||||
Observer_Test_Throwing_Move_Time_Source() = default;
|
||||
Observer_Test_Throwing_Move_Time_Source(Observer_Test_Throwing_Move_Time_Source&&) {
|
||||
throw std::runtime_error("time source move failed");
|
||||
}
|
||||
Observer_Test_Throwing_Move_Time_Source& operator=(Observer_Test_Throwing_Move_Time_Source&&) = delete;
|
||||
std::uint64_t now_ns() const noexcept {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
TEST(observer_state_test, move_constructor_propagates_throwing_time_source_move) {
|
||||
using State = Observer_State<Disabled_Observer, Observer_Test_Throwing_Move_Time_Source>;
|
||||
static_assert(std::move_constructible<Observer_Test_Throwing_Move_Time_Source>);
|
||||
static_assert(!std::is_nothrow_move_constructible_v<State>);
|
||||
State source;
|
||||
EXPECT_THROW(State(std::move(source)), std::runtime_error);
|
||||
}
|
||||
|
||||
@@ -18,3 +18,5 @@ static_assert(Flow_Frame_Refresh_Strategy<Frame_Control_Concept_Flow>);
|
||||
TEST(frame_control_concepts_test, concepts_compile) {
|
||||
SUCCEED();
|
||||
}
|
||||
static_assert(noexcept(std::declval<Low_Latency_Test_Strategy&>().on_real_time_data_update(std::declval<const Real_Time_Data_Observation&>())));
|
||||
static_assert(noexcept(std::declval<Low_Latency_Test_Strategy&>().discard_stale_latest_data_frame()));
|
||||
|
||||
@@ -224,3 +224,26 @@ TEST(real_time_data_attachment_test, frame_strategy_observer_can_reacquire_scene
|
||||
latest->update(1);
|
||||
EXPECT_TRUE(reacquired.load(std::memory_order_acquire));
|
||||
}
|
||||
class Real_Time_Data_Failing_Memory_Resource : public std::pmr::memory_resource {
|
||||
private:
|
||||
void* do_allocate(std::size_t, std::size_t) override {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
void do_deallocate(void*, std::size_t, std::size_t) override {}
|
||||
bool do_is_equal(const std::pmr::memory_resource& other) const noexcept override {
|
||||
return this == &other;
|
||||
}
|
||||
};
|
||||
TEST(history_real_time_data_test, failed_timestamp_allocation_rolls_back_value_and_revision) {
|
||||
Real_Time_Data_Failing_Memory_Resource memory_resource;
|
||||
History_Real_Time_Data<int> data(memory_resource);
|
||||
EXPECT_THROW(data.update(7), std::bad_alloc);
|
||||
EXPECT_TRUE(data.snapshot().empty());
|
||||
EXPECT_EQ(data.revision(), 0);
|
||||
const auto state = data.update_state();
|
||||
EXPECT_EQ(state.total_update_count, 0);
|
||||
EXPECT_EQ(state.retained_value_count, 0);
|
||||
EXPECT_EQ(state.update_time_ns, 0);
|
||||
}
|
||||
static_assert(Unique_Real_Time_Data_Types<Real_Time_Data_Test_Latest, Real_Time_Data_Test_History>);
|
||||
static_assert(!Unique_Real_Time_Data_Types<Real_Time_Data_Test_Latest, Real_Time_Data_Test_Latest>);
|
||||
|
||||
@@ -157,3 +157,26 @@ TEST(scene2d_context_test, detach_dependency_parent_invalidates_promoted_cached_
|
||||
}
|
||||
}
|
||||
}
|
||||
struct Scene2D_Attach_Throwing_Cache : Color_Cache {
|
||||
explicit Scene2D_Attach_Throwing_Cache(std::pmr::memory_resource&) {
|
||||
if (++construction_count == throw_on_construction) {
|
||||
throw std::runtime_error("cache construction failed");
|
||||
}
|
||||
}
|
||||
void clear() override {}
|
||||
void composite(const Color_Cache&) override {}
|
||||
inline static int construction_count{};
|
||||
inline static int throw_on_construction{};
|
||||
};
|
||||
TEST(scene2d_context_test, failed_attach_leaves_renderable_fully_detached) {
|
||||
Scene2D_Attach_Throwing_Cache::construction_count = 0;
|
||||
Scene2D_Attach_Throwing_Cache::throw_on_construction = 2;
|
||||
Scene2D_Context<Low_Latency_Strategy<Scene2D_Frame_Data>, Scene2D_Attach_Throwing_Cache> scene;
|
||||
auto renderable = std::make_shared<Scene2D_Context_Test_Renderable>(scene);
|
||||
EXPECT_THROW(scene.attach_renderable(renderable), std::runtime_error);
|
||||
EXPECT_EQ(scene.renderable_count(), 0);
|
||||
EXPECT_TRUE(scene.topology_snapshot().renderables.empty());
|
||||
Scene2D_Attach_Throwing_Cache::throw_on_construction = 0;
|
||||
EXPECT_NO_THROW(scene.attach_renderable(renderable));
|
||||
EXPECT_EQ(scene.renderable_count(), 1);
|
||||
}
|
||||
|
||||
@@ -8,3 +8,5 @@ static_assert(!Scene_3D<Scene2D_Context<>>);
|
||||
TEST(scene_concept_test, accepts_2d_and_3d_contexts) {
|
||||
SUCCEED();
|
||||
}
|
||||
static_assert(std::is_final_v<Scene2D_Context<>>);
|
||||
static_assert(std::is_final_v<Scene3D_Context<>>);
|
||||
|
||||
@@ -76,3 +76,38 @@ TEST(double_state_strategy_test, notifies_observer_for_cache_update_and_publish)
|
||||
EXPECT_EQ(recorder.data->publish_count, 1);
|
||||
EXPECT_EQ(recorder.data->value, 8);
|
||||
}
|
||||
struct Double_State_Throwing_Assignment_State {
|
||||
int first{};
|
||||
int second{};
|
||||
Double_State_Throwing_Assignment_State() = default;
|
||||
Double_State_Throwing_Assignment_State(int first, int second) : first(first), second(second) {}
|
||||
Double_State_Throwing_Assignment_State(const Double_State_Throwing_Assignment_State&) = default;
|
||||
Double_State_Throwing_Assignment_State(Double_State_Throwing_Assignment_State&&) noexcept = default;
|
||||
Double_State_Throwing_Assignment_State& operator=(const Double_State_Throwing_Assignment_State& other) {
|
||||
first = other.first;
|
||||
if (throw_on_copy_assignment) {
|
||||
throw std::runtime_error("state assignment failed");
|
||||
}
|
||||
second = other.second;
|
||||
return *this;
|
||||
}
|
||||
Double_State_Throwing_Assignment_State& operator=(Double_State_Throwing_Assignment_State&&) noexcept = default;
|
||||
inline static bool throw_on_copy_assignment{};
|
||||
};
|
||||
TEST(double_state_strategy_test, failed_publish_keeps_render_state_and_revision_unchanged) {
|
||||
using Strategy = Double_State_Strategy<State_Plain_Base, Double_State_Throwing_Assignment_State>;
|
||||
Double_State_Throwing_Assignment_State::throw_on_copy_assignment = false;
|
||||
Strategy strategy(Double_State_Throwing_Assignment_State(1, 2));
|
||||
strategy.set<&Double_State_Throwing_Assignment_State::first>(10);
|
||||
strategy.set<&Double_State_Throwing_Assignment_State::second>(20);
|
||||
Double_State_Throwing_Assignment_State::throw_on_copy_assignment = true;
|
||||
EXPECT_THROW(strategy.publish(), std::runtime_error);
|
||||
Double_State_Throwing_Assignment_State::throw_on_copy_assignment = false;
|
||||
EXPECT_EQ(strategy.state_revision(), 0);
|
||||
EXPECT_EQ(strategy.render_use_state().first, 1);
|
||||
EXPECT_EQ(strategy.render_use_state().second, 2);
|
||||
strategy.publish();
|
||||
EXPECT_EQ(strategy.state_revision(), 1);
|
||||
EXPECT_EQ(strategy.render_use_state().first, 10);
|
||||
EXPECT_EQ(strategy.render_use_state().second, 20);
|
||||
}
|
||||
|
||||
@@ -66,3 +66,40 @@ TEST(triple_state_strategy_test, concurrently_acquires_render_revision_without_d
|
||||
EXPECT_EQ(strategy.acquire_render_state(), 1000);
|
||||
EXPECT_EQ(strategy.render_use_state().value, 1000);
|
||||
}
|
||||
struct Triple_State_Throwing_Assignment_State {
|
||||
int first{};
|
||||
int second{};
|
||||
Triple_State_Throwing_Assignment_State() = default;
|
||||
Triple_State_Throwing_Assignment_State(int first, int second) : first(first), second(second) {}
|
||||
Triple_State_Throwing_Assignment_State(const Triple_State_Throwing_Assignment_State&) = default;
|
||||
Triple_State_Throwing_Assignment_State(Triple_State_Throwing_Assignment_State&&) noexcept = default;
|
||||
Triple_State_Throwing_Assignment_State& operator=(const Triple_State_Throwing_Assignment_State& other) {
|
||||
first = other.first;
|
||||
if (throw_on_copy_assignment) {
|
||||
throw std::runtime_error("state assignment failed");
|
||||
}
|
||||
second = other.second;
|
||||
return *this;
|
||||
}
|
||||
Triple_State_Throwing_Assignment_State& operator=(Triple_State_Throwing_Assignment_State&&) noexcept = default;
|
||||
inline static bool throw_on_copy_assignment{};
|
||||
};
|
||||
TEST(triple_state_strategy_test, failed_publish_keeps_published_revision_and_render_state_unchanged) {
|
||||
using Strategy = Triple_State_Strategy<Triple_State_Test_Base, Triple_State_Throwing_Assignment_State>;
|
||||
Triple_State_Throwing_Assignment_State::throw_on_copy_assignment = false;
|
||||
Strategy strategy(Triple_State_Throwing_Assignment_State(1, 2));
|
||||
strategy.set<&Triple_State_Throwing_Assignment_State::first>(10);
|
||||
strategy.set<&Triple_State_Throwing_Assignment_State::second>(20);
|
||||
Triple_State_Throwing_Assignment_State::throw_on_copy_assignment = true;
|
||||
EXPECT_THROW(strategy.publish(), std::runtime_error);
|
||||
Triple_State_Throwing_Assignment_State::throw_on_copy_assignment = false;
|
||||
EXPECT_EQ(strategy.state_revision(), 0);
|
||||
EXPECT_EQ(strategy.acquire_render_state(), 0);
|
||||
EXPECT_EQ(strategy.render_use_state().first, 1);
|
||||
EXPECT_EQ(strategy.render_use_state().second, 2);
|
||||
strategy.publish();
|
||||
EXPECT_EQ(strategy.state_revision(), 1);
|
||||
EXPECT_EQ(strategy.acquire_render_state(), 1);
|
||||
EXPECT_EQ(strategy.render_use_state().first, 10);
|
||||
EXPECT_EQ(strategy.render_use_state().second, 20);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user