#include "render_3D/Point_Visual.h" #include "render_3D/detail/Point_Core.h" #include #include #include #include #include #include namespace renderive::render_3d { namespace { static_assert(std::derived_from); static_assert(std::derived_from); TEST(PointState, UsesKernelDoubleStateAtFrameBoundary) { auto visual = Point_Visual::Builder{}.build( std::vector{{{1.0F, 2.0F, 3.0F}, {1, 2, 3, 255}, 7.0F}}); ASSERT_TRUE(visual); const auto initial_observation = visual->observation(); EXPECT_EQ(initial_observation.event, Renderable_Observer_Event::Data_Updated); EXPECT_EQ(initial_observation.data_revision, 1U); EXPECT_EQ(initial_observation.item_count, 1U); Point_State configured; configured.visible = false; configured.style.stroke_width_px = 3.0F; visual->set<&Point_State::visible>(configured.visible); visual->set<&Point_State::style>(configured.style); EXPECT_EQ(visual->observation().event, Renderable_Observer_Event::Cache_Updated); const auto published = detail::publish_point(*visual); EXPECT_EQ(visual->observation().event, Renderable_Observer_Event::Published); EXPECT_EQ(published.state, configured); ASSERT_EQ(published.data->size(), 1U); EXPECT_EQ(published.data->front().position, (Vec3{1.0F, 2.0F, 3.0F})); EXPECT_EQ(published.state_revision, 1U); EXPECT_EQ(published.data_revision, 1U); } TEST(PointState, PublishesImmutableBulkPayloadsThroughDoubleBuffer) { auto visual = Point_Visual::Builder{}.build(); ASSERT_TRUE(visual); constexpr int update_count = 500; constexpr std::uint64_t initial_data_revision = 1; std::atomic done{}; std::thread writer([&] { for (int value = 1; value <= update_count; ++value) { std::vector points(static_cast(value % 31 + 1)); for (auto& point : points) { point.position.x = static_cast(value); point.diameter_px = static_cast(value % 12 + 1); } visual->update_points(std::move(points)); } done.store(true, std::memory_order_release); }); do { const auto published = detail::publish_point(*visual); if (!published.data->empty()) { const float expected = published.data->front().position.x; for (const auto& point : *published.data) EXPECT_EQ(point.position.x, expected); } } while (!done.load(std::memory_order_acquire)); writer.join(); const auto published = detail::publish_point(*visual); EXPECT_GT(published.data_revision, 0U); EXPECT_LE(published.data_revision, initial_data_revision + update_count); ASSERT_FALSE(published.data->empty()); EXPECT_EQ(published.data->front().position.x, static_cast(update_count)); } TEST(PointState, RejectsInvalidStateAndPayloadAtTheOwningBoundary) { auto visual = Point_Visual::Builder{}.build(); ASSERT_TRUE(visual); Point_State invalid; invalid.style.stroke_width_px = -1.0F; EXPECT_THROW(visual->set<&Point_State::style>(invalid.style), std::invalid_argument); EXPECT_THROW(visual->update_points(std::vector{{{}, {}, 0.0F}}), std::invalid_argument); } TEST(PointFrameControl, UsesDedicatedThreeDimensionalFrameStrategy) { auto visual = Point_Visual::Builder{}.build( std::vector{{{}, {}, 8.0F}}); ASSERT_TRUE(visual); detail::Scene_State_Buffer scene({{320, 180}, {}}); detail::Input_Collector input; const auto prepare = [&](detail::Point_Frame_Strategy& strategy) { auto lease = strategy.acquire_painter(); if (!lease) return false; auto published_scene = scene.publish_state(); lease->scene = std::move(published_scene.state); lease->scene_revision = published_scene.revision; lease->point = detail::publish_point(*visual); lease->input = input.drain(); return true; }; detail::Point_Frame_Strategy manual(Frame_Mode::Manual, 60.0); EXPECT_TRUE(prepare(manual)); auto manual_status = manual.status(); EXPECT_EQ(manual_status.mode, Frame_Mode::Manual); EXPECT_EQ(manual_status.produced_frame_count, 1U); EXPECT_EQ(manual_status.pending_frame_count, 1U); EXPECT_FALSE(static_cast(manual.acquire_renderer())); EXPECT_TRUE(manual.refresh()); { auto rendered = manual.acquire_renderer(); ASSERT_TRUE(rendered); EXPECT_EQ(rendered->point.data->size(), 1U); } manual_status = manual.status(); EXPECT_EQ(manual_status.consumed_frame_count, 1U); EXPECT_EQ(manual_status.pending_frame_count, 0U); detail::Point_Frame_Strategy playback(Frame_Mode::Playback, 60.0); EXPECT_TRUE(prepare(playback)); visual->update_points(std::vector(2, Point{{}, {}, 8.0F})); EXPECT_TRUE(prepare(playback)); auto playback_status = playback.status(); EXPECT_EQ(playback_status.produced_frame_count, 2U); EXPECT_EQ(playback_status.pending_frame_count, 2U); { auto first = playback.acquire_renderer(); ASSERT_TRUE(first); EXPECT_EQ(first->point.data->size(), 1U); } { auto second = playback.acquire_renderer(); ASSERT_TRUE(second); EXPECT_EQ(second->point.data->size(), 2U); } playback_status = playback.status(); EXPECT_EQ(playback_status.consumed_frame_count, 2U); EXPECT_EQ(playback_status.pending_frame_count, 0U); } } // namespace } // namespace renderive::render_3d