184 lines
6.4 KiB
C++
184 lines
6.4 KiB
C++
#include <gtest/gtest.h>
|
|
#include <algorithm>
|
|
#include <atomic>
|
|
#include <memory>
|
|
#include <vector>
|
|
#include "renderive/renderable/Renderable.hpp"
|
|
#include "renderive/renderable/Renderable_Test_Harness.hpp"
|
|
#include "renderive/scene/Scene.hpp"
|
|
#include "Scene_Test_Helpers.hpp"
|
|
struct Scene3D_Context_Test_Renderable : Renderable_Test_Harness {
|
|
explicit Scene3D_Context_Test_Renderable() = default;
|
|
void prepare_for_test(const Prepare_Render_Context&) override {
|
|
scene_is_3d = dynamic_cast<Scene_3D_Base*>(&scene_for_test()) != nullptr;
|
|
++render_count;
|
|
}
|
|
std::atomic<int> render_count{};
|
|
bool scene_is_3d{};
|
|
};
|
|
TEST(scene3d_context_test, uses_scene_3d_base_contract) {
|
|
Scene3D_Context<> scene;
|
|
auto renderable = renderive_Owner<Scene3D_Context_Test_Renderable>::make();
|
|
attach_initial(scene, renderable);
|
|
scene.render();
|
|
scene.wait_for_render();
|
|
EXPECT_EQ(renderable->render_count.load(), 1);
|
|
EXPECT_TRUE(renderable->scene_is_3d);
|
|
}
|
|
|
|
struct Prepared_Mesh_Count {
|
|
std::size_t vertex_count{};
|
|
};
|
|
|
|
struct Scene3D_Prepared_Data_Renderable : Renderable_Test_Harness {
|
|
void prepare_for_test(const Prepare_Render_Context& context) override {
|
|
context.publish<Prepared_Mesh_Count>(
|
|
std::make_shared<const Prepared_Mesh_Count>(
|
|
Prepared_Mesh_Count{42}));
|
|
}
|
|
};
|
|
|
|
struct Scene3D_Render_Node_Test_Scene final
|
|
: Scene3D_Context<>, Scene_Renderer {
|
|
void render_scene(const Scene_Render_Context& context) override {
|
|
prepared = context.prepared<Prepared_Mesh_Count>(owner);
|
|
}
|
|
|
|
Renderable_Id owner{};
|
|
std::shared_ptr<const Prepared_Mesh_Count> prepared;
|
|
};
|
|
|
|
TEST(scene3d_context_test,
|
|
transfers_frame_local_prepared_data_to_single_scene_render_node) {
|
|
Scene3D_Render_Node_Test_Scene scene;
|
|
auto renderable = renderive_Owner<Scene3D_Prepared_Data_Renderable>::make();
|
|
scene.owner = renderable->renderable_id();
|
|
attach_initial(scene, renderable);
|
|
scene.render();
|
|
scene.wait_for_render();
|
|
|
|
ASSERT_TRUE(scene.prepared);
|
|
EXPECT_EQ(scene.prepared->vertex_count, 42U);
|
|
const auto plan = scene.render_plan_snapshot();
|
|
ASSERT_TRUE(plan);
|
|
const auto render_node = std::find_if(
|
|
plan->graph.nodes.begin(), plan->graph.nodes.end(),
|
|
[](const Render_Node& node) {
|
|
return node.kind == Render_Node_Kind::render;
|
|
});
|
|
ASSERT_NE(render_node, plan->graph.nodes.end());
|
|
EXPECT_TRUE(std::any_of(
|
|
plan->graph.edges.begin(), plan->graph.edges.end(),
|
|
[&](const Render_Edge& edge) {
|
|
return edge.to == render_node->node_id;
|
|
}));
|
|
}
|
|
|
|
struct Scene3D_Historical_State {
|
|
int value{};
|
|
};
|
|
|
|
struct Scene3D_Playback_Snapshot_Test_Scene final
|
|
: Scene3D_Context<Flow_Refresh_Strategy<Scene3D_Frame_Data>,
|
|
Scene3D_Historical_State>,
|
|
Scene_Renderer {
|
|
void enqueue(int value) {
|
|
Scene_State_Strategy::set<&Scene3D_Historical_State::value>(value);
|
|
auto frame = frame_control.acquire_painter();
|
|
ASSERT_TRUE(frame);
|
|
publish_frame_state(*frame);
|
|
}
|
|
|
|
void render_next() {
|
|
auto frame = frame_control.acquire_renderer();
|
|
ASSERT_TRUE(frame);
|
|
render(*frame);
|
|
wait_for_render();
|
|
}
|
|
|
|
void render_scene(const Scene_Render_Context& context) override {
|
|
rendered.push_back(
|
|
context.frame.scene_state<Scene3D_Historical_State>().value);
|
|
}
|
|
|
|
std::vector<int> rendered;
|
|
};
|
|
|
|
TEST(scene3d_context_test,
|
|
playback_uses_the_scene_snapshot_captured_at_frame_publication) {
|
|
Scene3D_Playback_Snapshot_Test_Scene scene;
|
|
scene.enqueue(11);
|
|
scene.enqueue(23);
|
|
scene.render_next();
|
|
scene.render_next();
|
|
EXPECT_EQ(scene.rendered, (std::vector<int>{11, 23}));
|
|
}
|
|
|
|
struct Scene3D_Dependency_Cache_Test_Renderable : Renderable_Test_Harness {
|
|
explicit Scene3D_Dependency_Cache_Test_Renderable()
|
|
: Renderable_Test_Harness({.cache_enabled = true}) {}
|
|
void prepare_for_test(const Prepare_Render_Context&) override {
|
|
++render_count;
|
|
}
|
|
int render_count{};
|
|
};
|
|
TEST(scene3d_context_test, propagates_cache_invalidation_through_unsorted_dependency_chain) {
|
|
Scene3D_Context<> scene;
|
|
auto leaf = renderive_Owner<Scene3D_Dependency_Cache_Test_Renderable>::make();
|
|
auto middle = renderive_Owner<Scene3D_Dependency_Cache_Test_Renderable>::make();
|
|
auto root = renderive_Owner<Scene3D_Dependency_Cache_Test_Renderable>::make();
|
|
with_attach_builder(scene, [&](auto& attach) {
|
|
attach.attach(leaf);
|
|
attach.attach(middle);
|
|
attach.attach(root);
|
|
attach.set_dependency_parent(leaf, middle);
|
|
attach.set_dependency_parent(middle, root);
|
|
});
|
|
scene.render();
|
|
scene.wait_for_render();
|
|
EXPECT_EQ(root->render_count, 1);
|
|
EXPECT_EQ(middle->render_count, 1);
|
|
EXPECT_EQ(leaf->render_count, 1);
|
|
root->invalidate_prepare_for_test();
|
|
scene.render();
|
|
scene.wait_for_render();
|
|
EXPECT_EQ(root->render_count, 2);
|
|
EXPECT_EQ(middle->render_count, 2);
|
|
EXPECT_EQ(leaf->render_count, 2);
|
|
}
|
|
TEST(scene3d_context_test, runtime_edit_explicitly_reparents_children_before_detach) {
|
|
Scene3D_Context<> scene;
|
|
auto grandparent = renderive_Owner<Scene3D_Dependency_Cache_Test_Renderable>::make();
|
|
auto parent = renderive_Owner<Scene3D_Dependency_Cache_Test_Renderable>::make();
|
|
auto child = renderive_Owner<Scene3D_Dependency_Cache_Test_Renderable>::make();
|
|
with_attach_builder(scene, [&](auto& attach) {
|
|
attach.attach(grandparent);
|
|
attach.attach(parent);
|
|
attach.attach(child);
|
|
attach.set_dependency_parent(parent, grandparent);
|
|
attach.set_dependency_parent(child, parent);
|
|
});
|
|
scene.render();
|
|
scene.wait_for_render();
|
|
EXPECT_EQ(child->render_count, 1);
|
|
wait_renderable_edit(scene, [grandparent, parent, child](auto& editor) {
|
|
editor.set_dependency_parent(child, grandparent);
|
|
editor.clear_dependency_parent(parent);
|
|
editor.detach(parent);
|
|
});
|
|
const auto topology = scene.topology_snapshot();
|
|
bool dependency_checked{};
|
|
for (const auto& relation : topology.dependency) {
|
|
if (relation.child.get() == child.get()) {
|
|
EXPECT_EQ(relation.parent.get(), grandparent.get());
|
|
dependency_checked = true;
|
|
}
|
|
}
|
|
EXPECT_TRUE(topology.display.empty());
|
|
EXPECT_TRUE(dependency_checked);
|
|
scene.render();
|
|
scene.wait_for_render();
|
|
EXPECT_EQ(parent->render_count, 1);
|
|
EXPECT_EQ(child->render_count, 2);
|
|
}
|