149 lines
5.0 KiB
C++
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();
|
|
}
|
|
}
|