Files
Renderive/Kernel/src/renderive/real_time_data/Frame_Strategy_Observer.cpp
T
2026-08-16 17:53:46 +08:00

149 lines
5.0 KiB
C++

#include "Frame_Strategy_Observer.hpp"
#include <algorithm>
#include <atomic>
#include <cmath>
#include <cstddef>
#include <mutex>
#include <utility>
#include <vector>
#include "renderive/renderable/base/Renderable_Base_p.hpp"
#include "renderive/scene/base/Scene_Base.hpp"
struct Frame_Strategy_Real_Time_Data_Observer::State {
using Target = Renderable_Base::Impl::Real_Time_Data_State;
struct Entry {
explicit Entry(std::shared_ptr<Target> target)
: target(std::move(target)) {}
std::shared_ptr<Target> target;
std::size_t binding_count{1};
std::atomic<bool> active{true};
};
using Entry_List = std::vector<std::shared_ptr<Entry>>;
class Binding;
std::mutex mutex;
std::shared_ptr<const Entry_List> entries{
std::make_shared<Entry_List>()};
};
class Frame_Strategy_Real_Time_Data_Observer::State::Binding {
public:
Binding(std::shared_ptr<State> state, std::shared_ptr<Entry> entry)
: state_(std::move(state)), entry_(std::move(entry)) {}
~Binding() {
std::lock_guard lock(state_->mutex);
if (entry_->binding_count > 1) {
--entry_->binding_count;
return;
}
entry_->binding_count = 0;
entry_->active.store(false, std::memory_order_release);
}
private:
std::shared_ptr<State> state_;
std::shared_ptr<Entry> entry_;
};
Frame_Strategy_Real_Time_Data_Observer::
Frame_Strategy_Real_Time_Data_Observer()
: state_(std::make_shared<State>()) {}
Frame_Strategy_Real_Time_Data_Observer::
Frame_Strategy_Real_Time_Data_Observer(Renderable_Base& renderable)
: Frame_Strategy_Real_Time_Data_Observer() {
direct_binding_ = bind(renderable);
}
std::shared_ptr<void> Frame_Strategy_Real_Time_Data_Observer::bind(
Renderable_Base& renderable) {
const auto target = renderable.d_func().real_time_data_state;
std::shared_ptr<State::Entry> entry;
{
std::lock_guard lock(state_->mutex);
for (const auto& value : *state_->entries) {
if (value->active.load(std::memory_order_acquire) &&
value->target == target) {
++value->binding_count;
entry = value;
break;
}
}
if (!entry) {
entry = std::make_shared<State::Entry>(target);
auto entries = std::make_shared<State::Entry_List>();
entries->reserve(state_->entries->size() + 1);
for (const auto& value : *state_->entries) {
if (value->active.load(std::memory_order_acquire))
entries->push_back(value);
}
entries->push_back(entry);
state_->entries = std::move(entries);
}
}
return std::make_shared<State::Binding>(state_, std::move(entry));
}
void Frame_Strategy_Real_Time_Data_Observer::observe(
const Real_Time_Data_Observation& observation) {
std::shared_ptr<const State::Entry_List> entries;
{
std::lock_guard lock(state_->mutex);
entries = state_->entries;
}
const auto is_attached = [](const auto& entry) {
return entry->active.load(std::memory_order_acquire) &&
entry->target->renderable_alive.load(
std::memory_order_acquire) &&
entry->target->attached.load(std::memory_order_acquire);
};
for (std::size_t index = 0; index < entries->size(); ++index) {
const auto& entry = (*entries)[index];
if (!is_attached(entry))
continue;
const auto& lifetime = entry->target->scene_lifetime;
bool first_for_scene = true;
for (std::size_t previous = 0; previous < index; ++previous) {
const auto& previous_entry = (*entries)[previous];
if (is_attached(previous_entry) &&
previous_entry->target->scene_lifetime.get() ==
lifetime.get()) {
first_for_scene = false;
break;
}
}
if (!first_for_scene)
continue;
bool discard_stale_frame{};
if (observation.state.retention ==
Real_Time_Data_Retention::latest) {
for (const auto& value : *entries) {
if (is_attached(value) &&
value->target->scene_lifetime.get() == lifetime.get() &&
value->target
->discard_stale_frame_on_latest_data_update.load(
std::memory_order_acquire)) {
discard_stale_frame = true;
break;
}
}
}
auto lease = lifetime->acquire();
if (!lease)
continue;
auto& strategy = lease.scene().frame_control_strategy();
strategy.on_real_time_data_update(observation);
const double frequency_hz = strategy.frequency_hz();
if (discard_stale_frame && std::isfinite(frequency_hz) &&
frequency_hz > 0.0)
strategy.discard_stale_latest_data_frame();
}
}