统一 2D/3D Renderable 继承链与实时数据策略
This commit is contained in:
@@ -64,13 +64,12 @@ struct Real_Time_Data_Test_Renderable : Renderable_Test_Harness {
|
||||
std::atomic<int> render_count{};
|
||||
};
|
||||
using Real_Time_Data_Test_Bridge = Observer_State<Frame_Strategy_Real_Time_Data_Observer, Real_Time_Data_Test_Time_Source>;
|
||||
using Real_Time_Data_Test_Latest = Latest_Real_Time_Data<int, std::mutex, Real_Time_Data_Test_Bridge>;
|
||||
using Real_Time_Data_Test_History = History_Real_Time_Data<int, std::vector<int>, std::mutex, Real_Time_Data_Test_Bridge>;
|
||||
using Real_Time_Data_Test_Attachment = Attach_Real_Time_Data<Real_Time_Data_Test_Renderable, Real_Time_Data_Test_Latest, Real_Time_Data_Test_History>;
|
||||
using Real_Time_Data_Test_Attachment = Attach_Real_Time_Data<Real_Time_Data_Test_Renderable, Real_Time_Data_Test_History>;
|
||||
class Real_Time_Data_Render_Probe : public Renderable_Test_Harness {
|
||||
class Bound_Latest : public Latest_Real_Time_Data<int> {
|
||||
class Bound_History : public History_Real_Time_Data<int> {
|
||||
public:
|
||||
explicit Bound_Latest(Renderable_Base& owner)
|
||||
explicit Bound_History(Renderable_Base& owner)
|
||||
: binding_(bind_renderable(owner)) {}
|
||||
private:
|
||||
Real_Time_Data_Binding binding_;
|
||||
@@ -79,9 +78,9 @@ public:
|
||||
explicit Real_Time_Data_Render_Probe() : data(*this) {}
|
||||
void prepare_for_test(const Prepare_Render_Context& context) override {
|
||||
const auto& value = context.frame.render_state.get(data);
|
||||
rendered_value = value.value_or(-1);
|
||||
rendered_value = value.empty() ? -1 : value.back();
|
||||
}
|
||||
Bound_Latest data;
|
||||
Bound_History data;
|
||||
int rendered_value{-1};
|
||||
};
|
||||
TEST(real_time_data_render_state_test, publishes_once_at_frame_boundary_and_reads_without_snapshot) {
|
||||
@@ -102,15 +101,6 @@ TEST(real_time_data_render_state_test, publishes_once_at_frame_boundary_and_read
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(renderable->rendered_value, 2);
|
||||
}
|
||||
TEST(latest_real_time_data_test, retains_only_latest_value) {
|
||||
Latest_Real_Time_Data<int> data;
|
||||
data.update(1);
|
||||
data.update(2);
|
||||
ASSERT_TRUE(data.snapshot());
|
||||
EXPECT_EQ(*data.snapshot(), 2);
|
||||
EXPECT_EQ(data.update_state().retained_value_count, 1);
|
||||
EXPECT_EQ(data.revision(), 2);
|
||||
}
|
||||
TEST(history_real_time_data_test, retains_all_values_until_explicit_discard) {
|
||||
using Observer = Observer_State<Disabled_Observer, Real_Time_Data_Test_Time_Source>;
|
||||
History_Real_Time_Data<int, std::vector<int>, std::mutex, Observer> data;
|
||||
@@ -135,10 +125,8 @@ TEST(history_real_time_data_test, retains_latest_requested_value_count) {
|
||||
}
|
||||
TEST(real_time_data_attachment_test, binds_updates_to_renderable_frame_strategy) {
|
||||
Scene2D_Context<> scene;
|
||||
auto latest = std::make_shared<Real_Time_Data_Test_Latest>();
|
||||
auto history = std::make_shared<Real_Time_Data_Test_History>();
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_Attachment>::make(With_Real_Time_Data(latest, history));
|
||||
renderable->discard_stale_frame_on_latest_data_update(true);
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_Attachment>::make(With_Real_Time_Data(history));
|
||||
{
|
||||
auto attach = scene.attach_builder();
|
||||
attach.attach(renderable);
|
||||
@@ -147,17 +135,15 @@ TEST(real_time_data_attachment_test, binds_updates_to_renderable_frame_strategy)
|
||||
auto frame = scene.frame_control.acquire_painter();
|
||||
ASSERT_TRUE(frame);
|
||||
}
|
||||
latest->update(1);
|
||||
EXPECT_EQ(scene.frame_control.counter_statistics().manually_discarded_frame_count, 1);
|
||||
auto frame = scene.frame_control.acquire_renderer();
|
||||
EXPECT_FALSE(frame);
|
||||
history->update(1);
|
||||
EXPECT_EQ(renderable->real_time_data_attachment_state().total_update_count,
|
||||
1U);
|
||||
}
|
||||
TEST(real_time_data_attachment_test, discards_history_older_than_one_frame_interval) {
|
||||
using Strategy = Low_Latency_Strategy<Scene2D_Frame_Data>;
|
||||
Scene2D_Context<Strategy> scene(Observer_State<>{}, Strategy::Configuration{100'000'000.0});
|
||||
auto latest = std::make_shared<Real_Time_Data_Test_Latest>();
|
||||
auto history = std::make_shared<Real_Time_Data_Test_History>();
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_Attachment>::make(With_Real_Time_Data(latest, history));
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_Attachment>::make(With_Real_Time_Data(history));
|
||||
renderable->real_time_data_discard_mode = Real_Time_Data_Discard_Mode::retain_frame_interval;
|
||||
history->update(1);
|
||||
history->update(2);
|
||||
@@ -172,22 +158,21 @@ TEST(real_time_data_attachment_test, discards_history_older_than_one_frame_inter
|
||||
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);
|
||||
std::shared_ptr<Real_Time_Data_Test_History> history;
|
||||
EXPECT_THROW((With_Real_Time_Data(history)), std::invalid_argument);
|
||||
}
|
||||
using Real_Time_Data_Test_Latest_Attachment = Attach_Real_Time_Data<Real_Time_Data_Test_Renderable, Real_Time_Data_Test_Latest>;
|
||||
using Real_Time_Data_Test_History_Attachment = Attach_Real_Time_Data<Real_Time_Data_Test_Renderable, Real_Time_Data_Test_History>;
|
||||
TEST(real_time_data_attachment_test, rejects_binding_one_source_to_multiple_renderables) {
|
||||
auto latest = std::make_shared<Real_Time_Data_Test_Latest>();
|
||||
auto first = renderive_Owner<Real_Time_Data_Test_Latest_Attachment>::make(With_Real_Time_Data(latest));
|
||||
EXPECT_THROW((renderive_Owner<Real_Time_Data_Test_Latest_Attachment>::make(With_Real_Time_Data(latest))), std::logic_error);
|
||||
auto history = std::make_shared<Real_Time_Data_Test_History>();
|
||||
auto first = renderive_Owner<Real_Time_Data_Test_History_Attachment>::make(With_Real_Time_Data(history));
|
||||
EXPECT_THROW((renderive_Owner<Real_Time_Data_Test_History_Attachment>::make(With_Real_Time_Data(history))), std::logic_error);
|
||||
first.reset();
|
||||
EXPECT_NO_THROW((renderive_Owner<Real_Time_Data_Test_Latest_Attachment>::make(With_Real_Time_Data(latest))));
|
||||
EXPECT_NO_THROW((renderive_Owner<Real_Time_Data_Test_History_Attachment>::make(With_Real_Time_Data(history))));
|
||||
}
|
||||
TEST(real_time_data_attachment_test, detached_renderable_no_longer_drives_frame_strategy) {
|
||||
Scene2D_Context<> scene;
|
||||
auto latest = std::make_shared<Real_Time_Data_Test_Latest>();
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_Latest_Attachment>::make(With_Real_Time_Data(latest));
|
||||
renderable->discard_stale_frame_on_latest_data_update(true);
|
||||
auto history = std::make_shared<Real_Time_Data_Test_History>();
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_History_Attachment>::make(With_Real_Time_Data(history));
|
||||
{
|
||||
auto attach = scene.attach_builder();
|
||||
attach.attach(renderable);
|
||||
@@ -196,8 +181,8 @@ TEST(real_time_data_attachment_test, detached_renderable_no_longer_drives_frame_
|
||||
auto frame = scene.frame_control.acquire_painter();
|
||||
ASSERT_TRUE(frame);
|
||||
}
|
||||
latest->update(1);
|
||||
EXPECT_EQ(scene.frame_control.counter_statistics().manually_discarded_frame_count, 1);
|
||||
history->update(1);
|
||||
const auto before_detach = scene.frame_control.state().real_time_data_update_sequence;
|
||||
wait_renderable_edit(scene, [renderable](auto& editor) {
|
||||
editor.detach(renderable);
|
||||
});
|
||||
@@ -205,19 +190,20 @@ TEST(real_time_data_attachment_test, detached_renderable_no_longer_drives_frame_
|
||||
auto frame = scene.frame_control.acquire_painter();
|
||||
ASSERT_TRUE(frame);
|
||||
}
|
||||
latest->update(2);
|
||||
EXPECT_EQ(scene.frame_control.counter_statistics().manually_discarded_frame_count, 1);
|
||||
history->update(2);
|
||||
EXPECT_EQ(scene.frame_control.state().real_time_data_update_sequence,
|
||||
before_detach);
|
||||
}
|
||||
TEST(real_time_data_attachment_test, unbind_is_synchronized_with_concurrent_updates) {
|
||||
Scene2D_Context<> scene;
|
||||
auto latest = std::make_shared<Real_Time_Data_Test_Latest>();
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_Latest_Attachment>::make(With_Real_Time_Data(latest));
|
||||
auto history = std::make_shared<Real_Time_Data_Test_History>();
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_History_Attachment>::make(With_Real_Time_Data(history));
|
||||
std::atomic<bool> running{true};
|
||||
std::atomic<bool> updated{};
|
||||
std::thread updater([&] {
|
||||
int value{};
|
||||
while (running.load(std::memory_order_acquire)) {
|
||||
latest->update(++value);
|
||||
history->update(++value);
|
||||
updated.store(true, std::memory_order_release);
|
||||
}
|
||||
});
|
||||
@@ -227,20 +213,19 @@ TEST(real_time_data_attachment_test, unbind_is_synchronized_with_concurrent_upda
|
||||
renderable.reset();
|
||||
running.store(false, std::memory_order_release);
|
||||
updater.join();
|
||||
EXPECT_TRUE(latest->revision() > 0);
|
||||
EXPECT_TRUE(history->revision() > 0);
|
||||
}
|
||||
TEST(real_time_data_attachment_test, runtime_discard_configuration_is_safe_during_updates_and_rendering) {
|
||||
Scene2D_Context<> scene;
|
||||
auto latest = std::make_shared<Real_Time_Data_Test_Latest>();
|
||||
auto history = std::make_shared<Real_Time_Data_Test_History>();
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_Attachment>::make(With_Real_Time_Data(latest, history));
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_Attachment>::make(With_Real_Time_Data(history));
|
||||
{
|
||||
auto attach = scene.attach_builder();
|
||||
attach.attach(renderable);
|
||||
}
|
||||
std::thread updater([&] {
|
||||
for (int value = 0; value < 1000; ++value) {
|
||||
latest->update(value);
|
||||
history->update(value, 1);
|
||||
}
|
||||
});
|
||||
std::thread configurator([&] {
|
||||
@@ -256,40 +241,40 @@ TEST(real_time_data_attachment_test, runtime_discard_configuration_is_safe_durin
|
||||
}
|
||||
updater.join();
|
||||
configurator.join();
|
||||
EXPECT_EQ(latest->revision(), 1000);
|
||||
EXPECT_EQ(history->revision(), 1000);
|
||||
}
|
||||
TEST(real_time_data_attachment_test, ignores_updates_after_bound_scene_is_destroyed) {
|
||||
auto latest = std::make_shared<Real_Time_Data_Test_Latest>();
|
||||
renderive_Owner<Real_Time_Data_Test_Latest_Attachment> renderable;
|
||||
auto history = std::make_shared<Real_Time_Data_Test_History>();
|
||||
renderive_Owner<Real_Time_Data_Test_History_Attachment> renderable;
|
||||
{
|
||||
Scene2D_Context<> scene;
|
||||
renderable = renderive_Owner<Real_Time_Data_Test_Latest_Attachment>::make(With_Real_Time_Data(latest));
|
||||
renderable = renderive_Owner<Real_Time_Data_Test_History_Attachment>::make(With_Real_Time_Data(history));
|
||||
{
|
||||
auto attach = scene.attach_builder();
|
||||
attach.attach(renderable);
|
||||
}
|
||||
latest->update(1);
|
||||
history->update(1);
|
||||
EXPECT_EQ(scene.frame_control.state().real_time_data_update_sequence, 1);
|
||||
}
|
||||
EXPECT_NO_THROW(latest->update(2));
|
||||
EXPECT_NO_THROW(history->update(2));
|
||||
EXPECT_THROW(renderable->scene_for_test(), std::logic_error);
|
||||
renderable.reset();
|
||||
}
|
||||
TEST(real_time_data_attachment_test, source_binding_is_owned_by_renderable_lifetime) {
|
||||
auto latest = std::make_shared<Real_Time_Data_Test_Latest>();
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_Latest_Attachment>::make(With_Real_Time_Data(latest));
|
||||
EXPECT_THROW((renderive_Owner<Real_Time_Data_Test_Latest_Attachment>::make(With_Real_Time_Data(latest))), std::logic_error);
|
||||
auto history = std::make_shared<Real_Time_Data_Test_History>();
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_History_Attachment>::make(With_Real_Time_Data(history));
|
||||
EXPECT_THROW((renderive_Owner<Real_Time_Data_Test_History_Attachment>::make(With_Real_Time_Data(history))), std::logic_error);
|
||||
renderable.reset();
|
||||
EXPECT_NO_THROW((renderive_Owner<Real_Time_Data_Test_Latest_Attachment>::make(With_Real_Time_Data(latest))));
|
||||
EXPECT_NO_THROW((renderive_Owner<Real_Time_Data_Test_History_Attachment>::make(With_Real_Time_Data(history))));
|
||||
}
|
||||
TEST(real_time_data_attachment_test, attachment_owns_real_time_data_sources) {
|
||||
Scene2D_Context<> scene;
|
||||
auto latest = std::make_shared<Real_Time_Data_Test_Latest>();
|
||||
std::weak_ptr<Real_Time_Data_Test_Latest> weak = latest;
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_Latest_Attachment>::make(With_Real_Time_Data(latest));
|
||||
latest.reset();
|
||||
auto history = std::make_shared<Real_Time_Data_Test_History>();
|
||||
std::weak_ptr<Real_Time_Data_Test_History> weak = history;
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_History_Attachment>::make(With_Real_Time_Data(history));
|
||||
history.reset();
|
||||
EXPECT_FALSE(weak.expired());
|
||||
renderable->real_time_data<Real_Time_Data_Test_Latest>().update(1);
|
||||
renderable->real_time_data<Real_Time_Data_Test_History>().update(1);
|
||||
renderable.reset();
|
||||
EXPECT_TRUE(weak.expired());
|
||||
}
|
||||
@@ -314,8 +299,8 @@ TEST(real_time_data_attachment_test, frame_strategy_observer_can_reacquire_scene
|
||||
Real_Time_Data_Reentrant_Frame_Observer_State observer_state(observer, Real_Time_Data_Test_Time_Source{});
|
||||
Real_Time_Data_Reentrant_Strategy::Configuration configuration{60.0};
|
||||
Scene2D_Context<Real_Time_Data_Reentrant_Strategy> scene(std::move(observer_state), configuration);
|
||||
auto latest = std::make_shared<Real_Time_Data_Test_Latest>();
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_Latest_Attachment>::make(With_Real_Time_Data(latest));
|
||||
auto history = std::make_shared<Real_Time_Data_Test_History>();
|
||||
auto renderable = renderive_Owner<Real_Time_Data_Test_History_Attachment>::make(With_Real_Time_Data(history));
|
||||
{
|
||||
auto attach = scene.attach_builder();
|
||||
attach.attach(renderable);
|
||||
@@ -325,7 +310,7 @@ TEST(real_time_data_attachment_test, frame_strategy_observer_can_reacquire_scene
|
||||
reacquired.store(&renderable->scene_for_test() == &scene,
|
||||
std::memory_order_release);
|
||||
};
|
||||
latest->update(1);
|
||||
history->update(1);
|
||||
EXPECT_TRUE(reacquired.load(std::memory_order_acquire));
|
||||
}
|
||||
class Real_Time_Data_Failing_Memory_Resource : public std::pmr::memory_resource {
|
||||
@@ -351,5 +336,8 @@ TEST(history_real_time_data_test, failed_timestamp_allocation_rolls_back_value_a
|
||||
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>);
|
||||
using Real_Time_Data_Test_Other_History = History_Real_Time_Data<long>;
|
||||
static_assert(Unique_Real_Time_Data_Types<Real_Time_Data_Test_History,
|
||||
Real_Time_Data_Test_Other_History>);
|
||||
static_assert(!Unique_Real_Time_Data_Types<Real_Time_Data_Test_History,
|
||||
Real_Time_Data_Test_History>);
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "renderive/real_time_data/Real_Time_Data.hpp"
|
||||
static_assert(std::derived_from<Latest_Real_Time_Data<int>, Real_Time_Data_Base>);
|
||||
static_assert(std::derived_from<History_Real_Time_Data<int>, Real_Time_Data_Base>);
|
||||
TEST(real_time_data_base_test, exposes_runtime_retention) {
|
||||
Latest_Real_Time_Data<int> latest;
|
||||
History_Real_Time_Data<int> history;
|
||||
Real_Time_Data_Base* latest_base = &latest;
|
||||
Real_Time_Data_Base* history_base = &history;
|
||||
EXPECT_EQ(latest_base->retention(), Real_Time_Data_Retention::latest);
|
||||
EXPECT_EQ(history_base->retention(), Real_Time_Data_Retention::history);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user