3D 第一个控件成功显示
This commit is contained in:
@@ -10,3 +10,23 @@ concept Mutex_Type = std::default_initializable<That> && requires(That& mutex) {
|
||||
{ mutex.lock() } -> std::same_as<void>;
|
||||
{ mutex.unlock() } -> std::same_as<void>;
|
||||
};
|
||||
class Non_Copyable {
|
||||
public:
|
||||
Non_Copyable(const Non_Copyable&) = delete;
|
||||
Non_Copyable& operator=(const Non_Copyable&) = delete;
|
||||
Non_Copyable(Non_Copyable&&) = default;
|
||||
Non_Copyable& operator=(Non_Copyable&&) = default;
|
||||
protected:
|
||||
Non_Copyable() = default;
|
||||
~Non_Copyable() = default;
|
||||
};
|
||||
class Non_Movable {
|
||||
public:
|
||||
Non_Movable(const Non_Movable&) = default;
|
||||
Non_Movable& operator=(const Non_Movable&) = default;
|
||||
Non_Movable(Non_Movable&&) = delete;
|
||||
Non_Movable& operator=(Non_Movable&&) = delete;
|
||||
protected:
|
||||
Non_Movable() = default;
|
||||
~Non_Movable() = default;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "renderive/base/Atomic_Mutex.hpp"
|
||||
#include "renderive/base/Concepts.hpp"
|
||||
#include "renderive/base/observer/Observer.hpp"
|
||||
#include "base/State_Strategy_Base.hpp"
|
||||
template <class Tag_Type, class Data_Type>
|
||||
struct Buffered_Data {
|
||||
using Tag = Tag_Type;
|
||||
using Data = Data_Type;
|
||||
};
|
||||
template <class Entry>
|
||||
concept Buffered_Data_Entry = requires {
|
||||
typename Entry::Tag;
|
||||
typename Entry::Data;
|
||||
};
|
||||
template <Buffered_Data_Entry... Entries>
|
||||
struct Double_Buffer_Layout {};
|
||||
namespace double_buffer_detail {
|
||||
template <class Data>
|
||||
struct Slot {
|
||||
Data buffers[2]{};
|
||||
std::uint8_t render_index{};
|
||||
std::uint8_t cache_index{1};
|
||||
std::uint64_t cache_update_count{};
|
||||
std::uint64_t publish_count{};
|
||||
bool dirty{};
|
||||
};
|
||||
template <class... Entries>
|
||||
struct Unique_Tags;
|
||||
template <>
|
||||
struct Unique_Tags<> : std::true_type {};
|
||||
template <class First, class... Rest>
|
||||
struct Unique_Tags<First, Rest...> : std::bool_constant<((!std::same_as<typename First::Tag, typename Rest::Tag>) && ...) && Unique_Tags<Rest...>::value> {};
|
||||
template <class Tag, class... Entries>
|
||||
struct Entry_Index {
|
||||
static constexpr std::size_t match_count = (static_cast<std::size_t>(std::same_as<Tag, typename Entries::Tag>) + ...);
|
||||
static_assert(match_count == 1);
|
||||
static constexpr std::size_t value = []() consteval {
|
||||
constexpr bool matches[] = {std::same_as<Tag, typename Entries::Tag>...};
|
||||
for(std::size_t i = 0; i < sizeof...(Entries); ++i) {
|
||||
if(matches[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return sizeof...(Entries);
|
||||
}();
|
||||
};
|
||||
template <class Tag, class... Entries>
|
||||
using Entry_Of = std::tuple_element_t<Entry_Index<Tag, Entries...>::value, std::tuple<Entries...>>;
|
||||
}
|
||||
template <Derivable_Type That, class Data_Type, Mutex_Type Mutex = Atomic_Spin_Mutex, class Observer = Observer_State<>>
|
||||
struct Double_Buffer_Strategy : That, State_Strategy_Base {
|
||||
using Self = Double_Buffer_Strategy;
|
||||
using Data = Data_Type;
|
||||
enum class Observation_Event {
|
||||
cache_updated,
|
||||
published
|
||||
};
|
||||
struct Observation {
|
||||
Observation_Event event{};
|
||||
std::uint64_t time_ns{};
|
||||
std::uint64_t cache_update_count{};
|
||||
std::uint64_t publish_count{};
|
||||
};
|
||||
static_assert(Timed_Struct_Observer<Observer, Observation>);
|
||||
Double_Buffer_Strategy() requires std::default_initializable<That> && std::default_initializable<Data> : That() {}
|
||||
explicit Double_Buffer_Strategy(With_Observer<Observer> option) requires std::default_initializable<That> && std::default_initializable<Data> : That(), observer(std::move(option.observer)) {}
|
||||
template <class... Args>
|
||||
explicit Double_Buffer_Strategy(std::in_place_t, Args&&... args) requires std::default_initializable<Data> : That(std::forward<Args>(args)...) {}
|
||||
template <class... Args>
|
||||
Double_Buffer_Strategy(std::in_place_t, With_Observer<Observer> option, Args&&... args) requires std::default_initializable<Data> : That(std::forward<Args>(args)...), observer(std::move(option.observer)) {}
|
||||
Self& write(Data value) {
|
||||
std::optional<Observation> observation;
|
||||
{
|
||||
std::lock_guard lock(mtx);
|
||||
slot.buffers[slot.cache_index] = std::move(value);
|
||||
slot.dirty = true;
|
||||
++slot.cache_update_count;
|
||||
observation.emplace(Observation_Event::cache_updated, observer.now_ns(), slot.cache_update_count, slot.publish_count);
|
||||
}
|
||||
observer.observe(*observation);
|
||||
return *this;
|
||||
}
|
||||
void publish() override {
|
||||
std::optional<Observation> observation;
|
||||
{
|
||||
std::lock_guard lock(mtx);
|
||||
if(!slot.dirty) {
|
||||
return;
|
||||
}
|
||||
std::swap(slot.render_index, slot.cache_index);
|
||||
slot.dirty = false;
|
||||
++slot.publish_count;
|
||||
observation.emplace(Observation_Event::published, observer.now_ns(), slot.cache_update_count, slot.publish_count);
|
||||
}
|
||||
observer.observe(*observation);
|
||||
}
|
||||
std::uint64_t state_revision() const override {
|
||||
std::lock_guard lock(mtx);
|
||||
return slot.publish_count;
|
||||
}
|
||||
protected:
|
||||
const Data& render_buffer_value() const noexcept {
|
||||
return slot.buffers[slot.render_index];
|
||||
}
|
||||
private:
|
||||
Observer observer;
|
||||
double_buffer_detail::Slot<Data> slot;
|
||||
mutable Mutex mtx;
|
||||
};
|
||||
template <Derivable_Type That, class Layout, Mutex_Type Mutex = Atomic_Spin_Mutex, class Observer = Observer_State<>>
|
||||
struct Multi_Double_Buffer_Strategy;
|
||||
template <Derivable_Type That, Mutex_Type Mutex, class Observer, Buffered_Data_Entry... Entries>
|
||||
struct Multi_Double_Buffer_Strategy<That, Double_Buffer_Layout<Entries...>, Mutex, Observer> : That, State_Strategy_Base {
|
||||
using Self = Multi_Double_Buffer_Strategy;
|
||||
enum class Observation_Event {
|
||||
cache_updated,
|
||||
published
|
||||
};
|
||||
struct Observation {
|
||||
Observation_Event event{};
|
||||
std::size_t buffer_index{};
|
||||
std::uint64_t time_ns{};
|
||||
std::uint64_t cache_update_count{};
|
||||
std::uint64_t publish_count{};
|
||||
};
|
||||
static_assert(sizeof...(Entries) > 0);
|
||||
static_assert(double_buffer_detail::Unique_Tags<Entries...>::value);
|
||||
static_assert((std::default_initializable<typename Entries::Data> && ...));
|
||||
static_assert(Timed_Struct_Observer<Observer, Observation>);
|
||||
Multi_Double_Buffer_Strategy() requires std::default_initializable<That> : That() {}
|
||||
explicit Multi_Double_Buffer_Strategy(With_Observer<Observer> option) requires std::default_initializable<That> : That(), observer(std::move(option.observer)) {}
|
||||
template <class... Args>
|
||||
explicit Multi_Double_Buffer_Strategy(std::in_place_t, Args&&... args) : That(std::forward<Args>(args)...) {}
|
||||
template <class... Args>
|
||||
Multi_Double_Buffer_Strategy(std::in_place_t, With_Observer<Observer> option, Args&&... args) : That(std::forward<Args>(args)...), observer(std::move(option.observer)) {}
|
||||
template <class Tag>
|
||||
using Data = typename double_buffer_detail::Entry_Of<Tag, Entries...>::Data;
|
||||
template <class Tag>
|
||||
static consteval std::size_t buffer_index() {
|
||||
return double_buffer_detail::Entry_Index<Tag, Entries...>::value;
|
||||
}
|
||||
template <class Tag>
|
||||
Self& write(Data<Tag> value) {
|
||||
std::optional<Observation> observation;
|
||||
{
|
||||
std::lock_guard lock(mtx);
|
||||
auto& current = slot<Tag>();
|
||||
current.buffers[current.cache_index] = std::move(value);
|
||||
current.dirty = true;
|
||||
++current.cache_update_count;
|
||||
observation.emplace(Observation_Event::cache_updated, buffer_index<Tag>(), observer.now_ns(), current.cache_update_count, current.publish_count);
|
||||
}
|
||||
observer.observe(*observation);
|
||||
return *this;
|
||||
}
|
||||
void publish() override {
|
||||
std::array<Observation, sizeof...(Entries)> observations{};
|
||||
std::size_t observation_count{};
|
||||
{
|
||||
std::lock_guard lock(mtx);
|
||||
bool published{};
|
||||
(publish_entry<Entries>(observations, observation_count, published), ...);
|
||||
if(published) {
|
||||
++revision;
|
||||
}
|
||||
}
|
||||
for(std::size_t i = 0; i < observation_count; ++i) {
|
||||
observer.observe(observations[i]);
|
||||
}
|
||||
}
|
||||
std::uint64_t state_revision() const override {
|
||||
std::lock_guard lock(mtx);
|
||||
return revision;
|
||||
}
|
||||
template <class Tag>
|
||||
std::uint64_t buffer_revision() const {
|
||||
std::lock_guard lock(mtx);
|
||||
return slot<Tag>().publish_count;
|
||||
}
|
||||
protected:
|
||||
template <class Tag>
|
||||
const Data<Tag>& render_buffer_value() const noexcept {
|
||||
const auto& current = slot<Tag>();
|
||||
return current.buffers[current.render_index];
|
||||
}
|
||||
private:
|
||||
template <class Tag>
|
||||
auto& slot() noexcept {
|
||||
return std::get<buffer_index<Tag>()>(slots);
|
||||
}
|
||||
template <class Tag>
|
||||
const auto& slot() const noexcept {
|
||||
return std::get<buffer_index<Tag>()>(slots);
|
||||
}
|
||||
template <class Entry>
|
||||
void publish_entry(std::array<Observation, sizeof...(Entries)>& observations, std::size_t& observation_count, bool& published) {
|
||||
using Tag = typename Entry::Tag;
|
||||
auto& current = slot<Tag>();
|
||||
if(!current.dirty) {
|
||||
return;
|
||||
}
|
||||
std::swap(current.render_index, current.cache_index);
|
||||
current.dirty = false;
|
||||
++current.publish_count;
|
||||
observations[observation_count++] = Observation{Observation_Event::published, buffer_index<Tag>(), observer.now_ns(), current.cache_update_count, current.publish_count};
|
||||
published = true;
|
||||
}
|
||||
Observer observer;
|
||||
std::tuple<double_buffer_detail::Slot<typename Entries::Data>...> slots;
|
||||
std::uint64_t revision{};
|
||||
mutable Mutex mtx;
|
||||
};
|
||||
Reference in New Issue
Block a user