3D 第一个控件成功显示

This commit is contained in:
2026-08-14 11:07:38 +08:00
parent 02c60679e4
commit 38231dbc1c
28 changed files with 2758 additions and 28 deletions
+20
View File
@@ -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;
};