接入3D secene taskflow
This commit is contained in:
@@ -12,7 +12,8 @@ using Render_Plan_Version = std::uint64_t;
|
||||
enum class Render_Node_Kind : std::uint8_t {
|
||||
prepare,
|
||||
paint,
|
||||
composite
|
||||
composite,
|
||||
render
|
||||
};
|
||||
|
||||
struct Render_Node {
|
||||
|
||||
@@ -176,6 +176,8 @@ void Renderable_Base::Impl::build_paint_graph(
|
||||
|
||||
void Renderable_Base::Impl::prepare(const Prepare_Render_Context&) {}
|
||||
|
||||
void Renderable_Base::Impl::capture_frame_data(Frame_Render_Snapshot&) const {}
|
||||
|
||||
void Renderable_Base::Impl::add_prepare_action(Prepare_Action action) {
|
||||
if (!action)
|
||||
throw std::invalid_argument("renderable prepare action is empty");
|
||||
@@ -254,6 +256,18 @@ bool Renderable_Base::is_visible() const noexcept {
|
||||
return d_func().visible.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
std::string Renderable_Base::object_name() const {
|
||||
const auto& implementation = d_func();
|
||||
std::lock_guard lock(implementation.metadata_mutex);
|
||||
return implementation.object_name;
|
||||
}
|
||||
|
||||
void Renderable_Base::set_object_name(std::string name) {
|
||||
auto& implementation = d_func();
|
||||
std::lock_guard lock(implementation.metadata_mutex);
|
||||
implementation.object_name = std::move(name);
|
||||
}
|
||||
|
||||
void Renderable_Base::set_visible(bool visible) {
|
||||
d_func().set_visible(visible);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <string>
|
||||
|
||||
#include "renderive/renderable/Renderable_Configuration.hpp"
|
||||
#include "renderive/renderable/Renderable_Id.hpp"
|
||||
@@ -17,6 +18,8 @@ public:
|
||||
|
||||
[[nodiscard]] Renderable_Id renderable_id() const noexcept;
|
||||
[[nodiscard]] bool is_visible() const noexcept;
|
||||
[[nodiscard]] std::string object_name() const;
|
||||
void set_object_name(std::string name);
|
||||
void set_visible(bool visible);
|
||||
void discard_stale_frame_on_latest_data_update(bool enabled) noexcept;
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ public:
|
||||
void reset_render_graph() noexcept;
|
||||
void add_prepare_action(Prepare_Action action);
|
||||
void execute_prepare(const Prepare_Render_Context& context);
|
||||
virtual void capture_frame_data(Frame_Render_Snapshot& snapshot) const;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] Renderable_Base& owner() noexcept;
|
||||
@@ -80,6 +81,8 @@ public:
|
||||
const Render_Node_Id composite_node_id;
|
||||
std::atomic<Renderable_Configuration> configuration;
|
||||
std::atomic<bool> visible{true};
|
||||
mutable std::mutex metadata_mutex;
|
||||
std::string object_name;
|
||||
std::mutex render_graph_mutex;
|
||||
std::shared_ptr<const Renderable_Graph> render_graph;
|
||||
std::unordered_map<std::string, Render_Node_Id> node_identities;
|
||||
|
||||
@@ -85,6 +85,10 @@ protected:
|
||||
std::uint64_t acquire_scene_state() override {
|
||||
return this->Scene_State_Strategy::state_revision();
|
||||
}
|
||||
void capture_scene_state(Frame_Render_Snapshot& snapshot) const override {
|
||||
snapshot.set_scene_state(
|
||||
this->Scene_State_Strategy::render_state_value());
|
||||
}
|
||||
void observe_scene(const Scene_Base::Observation& observation) noexcept override {
|
||||
scene_observer_.observe(observation);
|
||||
}
|
||||
|
||||
@@ -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 final : public Scene_3D_Base,
|
||||
class Scene3D_Context : public Scene_3D_Base,
|
||||
public Triple_State_Storage<
|
||||
State, Atomic_Spin_Mutex, State_Observer> {
|
||||
public:
|
||||
@@ -59,6 +59,10 @@ protected:
|
||||
std::uint64_t acquire_scene_state() override {
|
||||
return this->Scene_State_Strategy::acquire_render_state();
|
||||
}
|
||||
void capture_scene_state(Frame_Render_Snapshot& snapshot) const override {
|
||||
snapshot.set_scene_state(
|
||||
this->Scene_State_Strategy::render_state_value());
|
||||
}
|
||||
void observe_scene(const Scene_Base::Observation& observation) noexcept override {
|
||||
scene_observer_.observe(observation);
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ struct Frame_Snapshot {
|
||||
};
|
||||
|
||||
class Scene_Base;
|
||||
class Frame_Render_Snapshot;
|
||||
|
||||
class Abstract_Frame {
|
||||
public:
|
||||
@@ -74,6 +75,7 @@ private:
|
||||
std::shared_ptr<const Frame_Snapshot> completed_snapshot_;
|
||||
bool capture_{};
|
||||
bool rendering_{};
|
||||
std::shared_ptr<Frame_Render_Snapshot> published_snapshot_;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::uint64_t render_clock_now_ns() noexcept;
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <any>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "renderive/capture/Capture_Types.hpp"
|
||||
#include "renderive/renderable/Renderable_Configuration.hpp"
|
||||
@@ -44,9 +49,83 @@ public:
|
||||
std::vector<Renderable_Frame_State> renderables;
|
||||
std::vector<Renderable_Id> display_order;
|
||||
|
||||
[[nodiscard]] std::uint64_t scene_state_revision() const noexcept {
|
||||
return scene_state_revision_;
|
||||
}
|
||||
|
||||
template <class State>
|
||||
[[nodiscard]] const State& scene_state() const {
|
||||
const auto* value = std::any_cast<State>(&scene_state_);
|
||||
if (!value)
|
||||
throw std::logic_error("frame scene state type mismatch");
|
||||
return *value;
|
||||
}
|
||||
|
||||
template <class State>
|
||||
void set_scene_state(State state) {
|
||||
scene_state_ = std::move(state);
|
||||
}
|
||||
|
||||
template <class Data>
|
||||
void capture(Renderable_Id owner, std::shared_ptr<const Data> data) {
|
||||
if (!data)
|
||||
throw std::invalid_argument("captured render data is null");
|
||||
const auto [entry, inserted] = captured_.try_emplace(
|
||||
owner, Typed_Data{typeid(Data), std::move(data)});
|
||||
if (!inserted)
|
||||
throw std::logic_error(
|
||||
"renderable captured frame data more than once");
|
||||
}
|
||||
|
||||
template <class Data>
|
||||
[[nodiscard]] std::shared_ptr<const Data> captured(
|
||||
Renderable_Id owner) const {
|
||||
const auto entry = captured_.find(owner);
|
||||
if (entry == captured_.end())
|
||||
return {};
|
||||
if (entry->second.type != std::type_index(typeid(Data)))
|
||||
throw std::logic_error("captured render data type mismatch");
|
||||
return std::static_pointer_cast<const Data>(entry->second.value);
|
||||
}
|
||||
|
||||
template <class Data>
|
||||
void publish_prepared(Renderable_Id owner,
|
||||
std::shared_ptr<const Data> data) const {
|
||||
if (!data)
|
||||
throw std::invalid_argument("prepared render data is null");
|
||||
std::lock_guard lock(prepared_mutex_);
|
||||
const auto [entry, inserted] = prepared_.try_emplace(
|
||||
owner, Typed_Data{typeid(Data), std::move(data)});
|
||||
if (!inserted)
|
||||
throw std::logic_error(
|
||||
"renderable published prepared data more than once");
|
||||
}
|
||||
|
||||
template <class Data>
|
||||
[[nodiscard]] std::shared_ptr<const Data> prepared(
|
||||
Renderable_Id owner) const {
|
||||
std::lock_guard lock(prepared_mutex_);
|
||||
const auto entry = prepared_.find(owner);
|
||||
if (entry == prepared_.end())
|
||||
return {};
|
||||
if (entry->second.type != std::type_index(typeid(Data)))
|
||||
throw std::logic_error("prepared render data type mismatch");
|
||||
return std::static_pointer_cast<const Data>(entry->second.value);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Scene_Base;
|
||||
|
||||
struct Typed_Data {
|
||||
std::type_index type{typeid(void)};
|
||||
std::shared_ptr<const void> value;
|
||||
};
|
||||
|
||||
std::uint64_t scene_state_revision_{};
|
||||
std::uint64_t next_refresh_interval_ns_{};
|
||||
Capture_Frame_Ticket capture_ticket_;
|
||||
std::any scene_state_;
|
||||
std::unordered_map<Renderable_Id, Typed_Data> captured_;
|
||||
mutable std::mutex prepared_mutex_;
|
||||
mutable std::unordered_map<Renderable_Id, Typed_Data> prepared_;
|
||||
};
|
||||
|
||||
@@ -10,6 +10,16 @@ struct Prepare_Render_Context {
|
||||
const Frame_Render_Snapshot& frame;
|
||||
const Renderable_Frame_State& renderable;
|
||||
Node_Execution_Metrics* metrics{};
|
||||
|
||||
template <class Data>
|
||||
void publish(std::shared_ptr<const Data> data) const {
|
||||
frame.publish_prepared(renderable.renderable_id, std::move(data));
|
||||
}
|
||||
|
||||
template <class Data>
|
||||
[[nodiscard]] std::shared_ptr<const Data> captured() const {
|
||||
return frame.captured<Data>(renderable.renderable_id);
|
||||
}
|
||||
};
|
||||
|
||||
struct Paint_Render_Context {
|
||||
@@ -25,3 +35,14 @@ struct Composite_Render_Context {
|
||||
const Color_Cache* color_cache{};
|
||||
Node_Execution_Metrics* metrics{};
|
||||
};
|
||||
|
||||
struct Scene_Render_Context {
|
||||
const Frame_Render_Snapshot& frame;
|
||||
Node_Execution_Metrics* metrics{};
|
||||
|
||||
template <class Data>
|
||||
[[nodiscard]] std::shared_ptr<const Data> prepared(
|
||||
Renderable_Id owner) const {
|
||||
return frame.prepared<Data>(owner);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -179,7 +179,8 @@ Scene_Base::Scene_Base(std::pmr::memory_resource& upstream_memory_resource)
|
||||
color_caches_(&memory_domain_->resource()),
|
||||
task_(memory_domain_->resource()),
|
||||
renderable_edit_queue_(&memory_domain_->resource()),
|
||||
composite_begin_node_id_(allocate_renderive_node_id()) {
|
||||
composite_begin_node_id_(allocate_renderive_node_id()),
|
||||
scene_render_node_id_(allocate_renderive_node_id()) {
|
||||
worker_ = std::thread([this] { render_loop(); });
|
||||
}
|
||||
Scene_Base::~Scene_Base() {
|
||||
@@ -366,14 +367,15 @@ void Scene_Base::submit_render(Abstract_Frame* frame) {
|
||||
|
||||
auto& strategy = frame_control_strategy();
|
||||
strategy.swap();
|
||||
auto snapshot = snapshot_live_model();
|
||||
auto snapshot = frame ? std::exchange(frame->published_snapshot_, {})
|
||||
: nullptr;
|
||||
if (!snapshot)
|
||||
snapshot = capture_live_frame();
|
||||
snapshot->next_refresh_interval_ns_ =
|
||||
strategy.frame_control_state().next_refresh_interval_ns;
|
||||
snapshot->scene_state_revision_ = acquire_scene_state();
|
||||
if (render_sequence_ == std::numeric_limits<std::uint64_t>::max())
|
||||
throw std::overflow_error("render sequence exhausted");
|
||||
snapshot->render_sequence = ++render_sequence_;
|
||||
snapshot->viewport = frame_viewport();
|
||||
|
||||
Render_Task task(memory_resource());
|
||||
task.frame = frame
|
||||
@@ -527,6 +529,16 @@ void Scene_Base::request_render_graph_rebuild(Renderable_Base& renderable) {
|
||||
}
|
||||
void Scene_Base::publish_frame_state() {
|
||||
auto task_lock = lock_render_idle();
|
||||
publish_frame_state_locked();
|
||||
}
|
||||
|
||||
void Scene_Base::publish_frame_state(Abstract_Frame& frame) {
|
||||
auto task_lock = lock_render_idle();
|
||||
publish_frame_state_locked();
|
||||
frame.published_snapshot_ = capture_live_frame();
|
||||
}
|
||||
|
||||
void Scene_Base::publish_frame_state_locked() {
|
||||
if (auto* state = dynamic_cast<State_Strategy_Base*>(this))
|
||||
state->publish();
|
||||
std::vector<Renderable> renderables;
|
||||
@@ -640,6 +652,7 @@ std::shared_ptr<Frame_Render_Snapshot> Scene_Base::snapshot_live_model() {
|
||||
if (const auto cache = color_caches_.find(id);
|
||||
cache != color_caches_.end())
|
||||
state.paint_buffer_ = cache->second;
|
||||
renderable_data.capture_frame_data(*snapshot);
|
||||
snapshot->renderables.push_back(std::move(state));
|
||||
}
|
||||
|
||||
@@ -675,6 +688,14 @@ std::shared_ptr<Frame_Render_Snapshot> Scene_Base::snapshot_live_model() {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
std::shared_ptr<Frame_Render_Snapshot> Scene_Base::capture_live_frame() {
|
||||
auto snapshot = snapshot_live_model();
|
||||
snapshot->scene_state_revision_ = acquire_scene_state();
|
||||
capture_scene_state(*snapshot);
|
||||
snapshot->viewport = frame_viewport();
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
std::shared_ptr<const Render_Plan> Scene_Base::render_plan_snapshot() const {
|
||||
return render_plan_history_.current();
|
||||
}
|
||||
@@ -772,6 +793,8 @@ std::uint64_t Scene_Base::acquire_scene_state() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Scene_Base::capture_scene_state(Frame_Render_Snapshot&) const {}
|
||||
|
||||
void Scene_Base::observe_scene(const Observation&) noexcept {}
|
||||
|
||||
std::uint64_t Scene_Base::observer_now_ns() const noexcept {
|
||||
@@ -1043,6 +1066,31 @@ std::shared_ptr<const Render_Plan> Scene_Base::compile_render_plan(
|
||||
}
|
||||
}
|
||||
|
||||
if (auto* renderer = dynamic_cast<Scene_Renderer*>(this)) {
|
||||
std::vector<Render_Node_Id> terminals;
|
||||
terminals.reserve(graph.nodes.size());
|
||||
for (const auto& node : graph.nodes) {
|
||||
const bool has_successor = std::any_of(
|
||||
graph.edges.begin(), graph.edges.end(),
|
||||
[&node](const Render_Edge& edge) {
|
||||
return edge.from == node.node_id;
|
||||
});
|
||||
if (!has_successor)
|
||||
terminals.push_back(node.node_id);
|
||||
}
|
||||
graph.nodes.push_back({scene_render_node_id_, 0, "Render Scene",
|
||||
Render_Node_Kind::render, 0});
|
||||
functions.emplace(
|
||||
scene_render_node_id_,
|
||||
Execution_Binding{{}, std::nullopt,
|
||||
Scene_Render_Node_Function{
|
||||
[renderer](const Scene_Render_Context& context) {
|
||||
renderer->render_scene(context);
|
||||
}}});
|
||||
for (const Render_Node_Id terminal : terminals)
|
||||
append_edge(terminal, scene_render_node_id_);
|
||||
}
|
||||
|
||||
auto plan = render_plan_history_.publish(std::move(graph));
|
||||
task.execution_bindings.clear();
|
||||
task.execution_bindings.resize(plan->graph.nodes.size());
|
||||
@@ -1129,6 +1177,10 @@ void Scene_Base::execute_taskflow(Render_Task& task) {
|
||||
metrics});
|
||||
break;
|
||||
}
|
||||
case Render_Node_Kind::render:
|
||||
std::get<Scene_Render_Node_Function>(binding.function)(
|
||||
Scene_Render_Context{snapshot, metrics});
|
||||
break;
|
||||
}
|
||||
};
|
||||
if (!execution) {
|
||||
|
||||
@@ -39,6 +39,12 @@ public:
|
||||
virtual void composite(const Composite_Render_Context& context) = 0;
|
||||
};
|
||||
|
||||
class Scene_Renderer {
|
||||
public:
|
||||
virtual ~Scene_Renderer() = default;
|
||||
virtual void render_scene(const Scene_Render_Context& context) = 0;
|
||||
};
|
||||
|
||||
class Scene_Base {
|
||||
private:
|
||||
struct Render_Completion;
|
||||
@@ -128,6 +134,7 @@ public:
|
||||
void render(Abstract_Frame& frame);
|
||||
void wait_for_render();
|
||||
void publish_frame_state();
|
||||
void publish_frame_state(Abstract_Frame& frame);
|
||||
void notify_model_dirty() noexcept;
|
||||
|
||||
Attach_Builder attach_builder();
|
||||
@@ -164,6 +171,7 @@ protected:
|
||||
virtual std::shared_ptr<Color_Cache> make_renderable_color_cache();
|
||||
virtual Frame_Viewport frame_viewport() const;
|
||||
virtual std::uint64_t acquire_scene_state();
|
||||
virtual void capture_scene_state(Frame_Render_Snapshot& snapshot) const;
|
||||
virtual void observe_scene(const Observation& observation) noexcept;
|
||||
virtual std::uint64_t observer_now_ns() const noexcept;
|
||||
|
||||
@@ -209,9 +217,12 @@ private:
|
||||
|
||||
using Composite_Render_Node_Function =
|
||||
std::function<void(const Composite_Render_Context&)>;
|
||||
using Scene_Render_Node_Function =
|
||||
std::function<void(const Scene_Render_Context&)>;
|
||||
using Execution_Function =
|
||||
std::variant<Prepare_Render_Node_Function, Paint_Render_Node_Function,
|
||||
Composite_Render_Node_Function>;
|
||||
Composite_Render_Node_Function,
|
||||
Scene_Render_Node_Function>;
|
||||
|
||||
struct Execution_Binding {
|
||||
Renderable owner;
|
||||
@@ -251,6 +262,9 @@ private:
|
||||
void enqueue_renderable_edit_locked(Renderable_Edit edit);
|
||||
void request_render_graph_rebuild(Renderable_Base& renderable);
|
||||
[[nodiscard]] std::shared_ptr<Frame_Render_Snapshot> snapshot_live_model();
|
||||
[[nodiscard]] std::shared_ptr<Frame_Render_Snapshot>
|
||||
capture_live_frame();
|
||||
void publish_frame_state_locked();
|
||||
std::shared_ptr<const Render_Plan> compile_render_plan(
|
||||
const Frame_Render_Snapshot& snapshot, Render_Task& task);
|
||||
void render_loop();
|
||||
@@ -293,6 +307,7 @@ private:
|
||||
bool stop_{};
|
||||
|
||||
const Render_Node_Id composite_begin_node_id_;
|
||||
const Render_Node_Id scene_render_node_id_;
|
||||
Render_Plan_History render_plan_history_;
|
||||
Capture_Controller capture_controller_;
|
||||
Capture_Repository capture_repository_;
|
||||
|
||||
@@ -125,13 +125,15 @@ struct Triple_State_Storage : State_Strategy_Base {
|
||||
return publish_count;
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
friend class Render_State_View;
|
||||
|
||||
[[nodiscard]] const State& render_state_value() const noexcept {
|
||||
return *render_state;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Observer observer;
|
||||
State states[4];
|
||||
State* render_state;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#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"
|
||||
@@ -23,6 +25,95 @@ TEST(scene3d_context_test, uses_scene_3d_base_contract) {
|
||||
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}) {}
|
||||
|
||||
@@ -9,4 +9,4 @@ 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<>>);
|
||||
static_assert(!std::is_final_v<Scene3D_Context<>>);
|
||||
|
||||
Reference in New Issue
Block a user