接入3D secene taskflow

This commit is contained in:
2026-08-14 23:38:18 +08:00
parent 9a0601073b
commit 12e46b4c21
31 changed files with 1210 additions and 6116 deletions
+88 -84
View File
@@ -1,10 +1,14 @@
#include "render_3D/Point_Visual.h"
#include "render_3D/detail/Point_Core.h"
#include <renderive/frame_control/Frame_Control.hpp>
#include <renderive/scene/Scene.hpp>
#include <gtest/gtest.h>
#include <algorithm>
#include <array>
#include <atomic>
#include <cmath>
#include <memory>
#include <thread>
#include <vector>
@@ -12,42 +16,73 @@
namespace renderive::render_3d {
namespace {
using Test_Frame_Control = Low_Latency_Strategy<Scene3D_Frame_Data>;
struct Test_Scene final
: Scene3D_Context<Test_Frame_Control, detail::Scene_State>,
Scene_Renderer {
explicit Test_Scene(const std::shared_ptr<Point_Visual>& visual) {
Scene_State_Strategy::set<&detail::Scene_State::viewport>(
Extent{320, 180});
point_id = visual->renderable_id();
auto builder = attach_builder();
builder.attach(renderive_Owner<Point_Visual>(visual));
}
~Test_Scene() override { shutdown(); }
void render_frame() {
{
auto painter = frame_control.acquire_painter();
ASSERT_TRUE(painter);
publish_frame_state();
}
auto renderer = frame_control.acquire_renderer();
ASSERT_TRUE(renderer);
render(*renderer);
wait_for_render();
}
void render_scene(const Scene_Render_Context& context) override {
prepared = context.prepared<detail::Prepared_Point>(point_id);
ASSERT_TRUE(prepared);
EXPECT_EQ(context.frame.scene_state<detail::Scene_State>().viewport,
(Extent{320, 180}));
}
Renderable_Id point_id{};
std::shared_ptr<const detail::Prepared_Point> prepared;
};
static_assert(std::derived_from<Point_Visual, Renderable>);
static_assert(std::derived_from<Point_Visual, ::Renderable_Base>);
TEST(PointState, UsesKernelDoubleStateAtFrameBoundary) {
TEST(PointState, PublishesStateAndBulkDataIntoFrameLocalPreparedOutput) {
auto visual = Point_Visual::Builder{}.build(
std::vector<Point>{{{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_Scene scene(visual);
scene.render_frame();
ASSERT_TRUE(scene.prepared);
EXPECT_EQ(scene.prepared->state, configured);
ASSERT_EQ(scene.prepared->positions.size(), 1U);
EXPECT_EQ(scene.prepared->positions.front(),
(std::array<float, 3>{1.0F, 2.0F, 3.0F}));
EXPECT_EQ(scene.prepared->data_revision, 1U);
}
TEST(PointState, PublishesImmutableBulkPayloadsThroughDoubleBuffer) {
TEST(PointState, PublishesImmutableBulkPayloadsAcrossConcurrentUpdates) {
auto visual = Point_Visual::Builder{}.build();
ASSERT_TRUE(visual);
constexpr int update_count = 500;
constexpr std::uint64_t initial_data_revision = 1;
Test_Scene scene(visual);
constexpr int update_count = 200;
std::atomic<bool> done{};
std::thread writer([&] {
@@ -63,21 +98,22 @@ TEST(PointState, PublishesImmutableBulkPayloadsThroughDoubleBuffer) {
});
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);
scene.render_frame();
const auto prepared = scene.prepared;
ASSERT_TRUE(prepared);
if (!prepared->positions.empty()) {
const float expected = prepared->positions.front()[0];
for (const auto& point : prepared->positions)
EXPECT_EQ(point[0], 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<float>(update_count));
scene.render_frame();
ASSERT_TRUE(scene.prepared);
ASSERT_FALSE(scene.prepared->positions.empty());
EXPECT_EQ(scene.prepared->positions.front()[0],
static_cast<float>(update_count));
}
TEST(PointState, RejectsInvalidStateAndPayloadAtTheOwningBoundary) {
@@ -87,66 +123,34 @@ TEST(PointState, RejectsInvalidStateAndPayloadAtTheOwningBoundary) {
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<Point>{{{}, {}, 0.0F}}),
std::invalid_argument);
}
TEST(PointFrameControl, UsesDedicatedThreeDimensionalFrameStrategy) {
TEST(PointRenderPlan, OrdersParallelPreparationBeforeSingleSceneRenderNode) {
auto visual = Point_Visual::Builder{}.build(
std::vector<Point>{{{}, {}, 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;
};
Test_Scene scene(visual);
scene.render_frame();
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<bool>(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<Point>(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);
const auto plan = scene.render_plan_snapshot();
ASSERT_TRUE(plan);
const auto prepare = std::find_if(
plan->graph.nodes.begin(), plan->graph.nodes.end(),
[&](const Render_Node& node) {
return node.owner_id == visual->renderable_id() &&
node.kind == Render_Node_Kind::prepare;
});
const auto render = std::find_if(
plan->graph.nodes.begin(), plan->graph.nodes.end(),
[](const Render_Node& node) {
return node.kind == Render_Node_Kind::render;
});
ASSERT_NE(prepare, plan->graph.nodes.end());
ASSERT_NE(render, plan->graph.nodes.end());
EXPECT_NE(std::find(plan->graph.edges.begin(), plan->graph.edges.end(),
Render_Edge{prepare->node_id, render->node_id}),
plan->graph.edges.end());
}
} // namespace