Files
Renderive/Core/flow/low_latency/Triple_Buffer.h
T
2026-08-02 14:20:27 +08:00

315 lines
12 KiB
C++

#pragma once
#include <array>
#include <atomic>
#include <cstdint>
namespace renderive {
static constexpr std::uint8_t triple_buffer_invalid_index = 255;
enum class Triple_Buffer_Result : std::uint8_t {
Success,
Busy,
State_Changed,
Condition_Failed,
Cancelled
};
struct Triple_Buffer_View {
std::array<std::uint8_t, 3> index{};
std::uint8_t operator[](std::uint8_t role) const {
return index[role];
}
};
struct Triple_Buffer_Lease {
std::uint8_t index = triple_buffer_invalid_index;
explicit operator bool() const {
return index != triple_buffer_invalid_index;
}
};
struct Triple_Buffer_Mark_Record {
Triple_Buffer_Result result = Triple_Buffer_Result::Condition_Failed;
Triple_Buffer_Lease lease{};
std::uint8_t busy_index = triple_buffer_invalid_index;
};
struct Triple_Buffer_Swap_Record {
Triple_Buffer_Result result = Triple_Buffer_Result::Condition_Failed;
Triple_Buffer_View old_view{};
std::uint8_t busy_index = triple_buffer_invalid_index;
};
struct alignas(64) Triple_Buffer_Busy {
std::atomic_bool busy{false};
};
class Triple_Role_Buffer_Control {
public:
void reset() {
closed.store(false, std::memory_order_release);
slot_state.store(0, std::memory_order_release);
for (auto& item : buffer_busy) {
item.busy.store(false, std::memory_order_release);
item.busy.notify_all();
}
wake_generation.fetch_add(1, std::memory_order_acq_rel);
wake_generation.notify_all();
slot_state.notify_all();
}
void close() {
closed.store(true, std::memory_order_release);
wake_generation.fetch_add(1, std::memory_order_acq_rel);
wake_generation.notify_all();
slot_state.notify_all();
}
void wait_until_idle() {
for (;;) {
bool any_busy = false;
for (auto& item : buffer_busy) {
if (!item.busy.load(std::memory_order_acquire))
continue;
any_busy = true;
item.busy.wait(true, std::memory_order_acquire);
break;
}
if (!any_busy)
return;
}
}
void close_and_wait() {
close();
wait_until_idle();
}
Triple_Buffer_View read_view() const {
return decode_order(slot_state.load(std::memory_order_acquire));
}
Triple_Buffer_Mark_Record try_acquire_role(std::uint8_t role) {
if (closed.load(std::memory_order_acquire))
return {Triple_Buffer_Result::Cancelled, {}, triple_buffer_invalid_index};
auto order = slot_state.load(std::memory_order_acquire);
auto view = decode_order(order);
auto index = view[role];
if (!try_mark_index(index))
return {Triple_Buffer_Result::Busy, {}, index};
auto cur_order = slot_state.load(std::memory_order_acquire);
if (cur_order != order) {
unmark_index(index);
return {Triple_Buffer_Result::State_Changed, {}, triple_buffer_invalid_index};
}
return {Triple_Buffer_Result::Success, {index}, triple_buffer_invalid_index};
}
Triple_Buffer_Mark_Record acquire_role(std::uint8_t role) {
while (!closed.load(std::memory_order_acquire)) {
auto order = slot_state.load(std::memory_order_acquire);
auto view = decode_order(order);
auto index = view[role];
if (!try_mark_index(index)) {
auto wake = wake_generation.load(std::memory_order_acquire);
if (buffer_busy[index].busy.load(std::memory_order_acquire))
wake_generation.wait(wake, std::memory_order_acquire);
continue;
}
auto cur_order = slot_state.load(std::memory_order_acquire);
if (cur_order != order) {
unmark_index(index);
continue;
}
if (closed.load(std::memory_order_acquire)) {
unmark_index(index);
break;
}
return {Triple_Buffer_Result::Success, {index}, triple_buffer_invalid_index};
}
return {Triple_Buffer_Result::Cancelled, {}, triple_buffer_invalid_index};
}
void unmark_use(Triple_Buffer_Lease lease) {
if (lease)
unmark_index(lease.index);
}
template <typename Can_Swap>
Triple_Buffer_Swap_Record try_swap_role(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap) {
std::uint8_t busy_index = triple_buffer_invalid_index;
return try_swap_role_once(left_role, right_role, can_swap, [](const Triple_Buffer_View&, const Triple_Buffer_View&) {}, busy_index);
}
template <typename Can_Swap, typename On_Success>
Triple_Buffer_Swap_Record try_swap_role(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap, On_Success on_success) {
std::uint8_t busy_index = triple_buffer_invalid_index;
return try_swap_role_once(left_role, right_role, can_swap, on_success, busy_index);
}
template <typename Can_Swap, typename On_Success>
Triple_Buffer_Swap_Record try_swap_role_with_left_lease(std::uint8_t left_role, std::uint8_t right_role, Triple_Buffer_Lease left_lease, Can_Swap can_swap, On_Success on_success) {
return try_swap_role_with_left_lease_once(left_role, right_role, left_lease, can_swap, on_success);
}
bool role_busy(std::uint8_t role) const {
auto view = read_view();
return buffer_busy[view[role]].busy.load(std::memory_order_acquire);
}
private:
std::atomic<std::uint8_t> slot_state{0};
std::atomic_bool closed{false};
std::atomic<std::uint64_t> wake_generation{0};
std::array<Triple_Buffer_Busy, 3> buffer_busy{};
static Triple_Buffer_View decode_order(std::uint8_t order) {
switch (order) {
case 0:
return {{0, 1, 2}};
case 1:
return {{0, 2, 1}};
case 2:
return {{1, 0, 2}};
case 3:
return {{1, 2, 0}};
case 4:
return {{2, 0, 1}};
case 5:
return {{2, 1, 0}};
}
return {{0, 1, 2}};
}
static std::uint8_t encode_order(const Triple_Buffer_View& view) {
if (view.index[0] == 0)
return view.index[1] == 1 ? 0 : 1;
if (view.index[0] == 1)
return view.index[1] == 0 ? 2 : 3;
return view.index[1] == 0 ? 4 : 5;
}
static std::uint8_t swap_order(std::uint8_t order, std::uint8_t left_role, std::uint8_t right_role) {
auto view = decode_order(order);
auto temp = view.index[left_role];
view.index[left_role] = view.index[right_role];
view.index[right_role] = temp;
return encode_order(view);
}
bool try_mark_index(std::uint8_t index) {
bool expected = false;
return buffer_busy[index].busy.compare_exchange_strong(expected, true, std::memory_order_acq_rel, std::memory_order_acquire);
}
void unmark_index(std::uint8_t index) {
buffer_busy[index].busy.store(false, std::memory_order_release);
buffer_busy[index].busy.notify_all();
wake_generation.fetch_add(1, std::memory_order_acq_rel);
wake_generation.notify_all();
}
bool try_mark_two_indices(std::uint8_t left_index, std::uint8_t right_index, std::uint8_t& busy_index) {
if (left_index == right_index) {
if (!try_mark_index(left_index)) {
busy_index = left_index;
return false;
}
return true;
}
auto first = left_index < right_index ? left_index : right_index;
auto second = left_index < right_index ? right_index : left_index;
if (!try_mark_index(first)) {
busy_index = first;
return false;
}
if (!try_mark_index(second)) {
unmark_index(first);
busy_index = second;
return false;
}
return true;
}
void unmark_two_indices(std::uint8_t left_index, std::uint8_t right_index) {
if (left_index == right_index) {
unmark_index(left_index);
return;
}
unmark_index(left_index);
unmark_index(right_index);
}
template <typename Can_Swap, typename On_Success>
Triple_Buffer_Swap_Record try_swap_role_once(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap, On_Success on_success, std::uint8_t& busy_index) {
if (closed.load(std::memory_order_acquire))
return {Triple_Buffer_Result::Cancelled, {}, triple_buffer_invalid_index};
auto old_order = slot_state.load(std::memory_order_acquire);
auto old_view = decode_order(old_order);
auto left_index = old_view[left_role];
auto right_index = old_view[right_role];
if (!try_mark_two_indices(left_index, right_index, busy_index))
return {Triple_Buffer_Result::Busy, old_view, busy_index};
auto cur_order = slot_state.load(std::memory_order_acquire);
if (cur_order != old_order) {
unmark_two_indices(left_index, right_index);
return {Triple_Buffer_Result::State_Changed, old_view, triple_buffer_invalid_index};
}
if (!can_swap(old_view)) {
unmark_two_indices(left_index, right_index);
return {Triple_Buffer_Result::Condition_Failed, old_view, triple_buffer_invalid_index};
}
auto new_order = swap_order(old_order, left_role, right_role);
auto expected = old_order;
if (!slot_state.compare_exchange_strong(expected, new_order, std::memory_order_acq_rel, std::memory_order_acquire)) {
unmark_two_indices(left_index, right_index);
return {Triple_Buffer_Result::State_Changed, old_view, triple_buffer_invalid_index};
}
on_success(old_view, decode_order(new_order));
unmark_two_indices(left_index, right_index);
slot_state.notify_all();
wake_generation.fetch_add(1, std::memory_order_acq_rel);
wake_generation.notify_all();
return {Triple_Buffer_Result::Success, old_view, triple_buffer_invalid_index};
}
template <typename Can_Swap, typename On_Success>
Triple_Buffer_Swap_Record try_swap_role_with_left_lease_once(std::uint8_t left_role, std::uint8_t right_role, Triple_Buffer_Lease left_lease, Can_Swap can_swap, On_Success on_success) {
if (!left_lease)
return {Triple_Buffer_Result::Condition_Failed, {}, triple_buffer_invalid_index};
if (closed.load(std::memory_order_acquire))
return {Triple_Buffer_Result::Cancelled, {}, triple_buffer_invalid_index};
auto old_order = slot_state.load(std::memory_order_acquire);
auto old_view = decode_order(old_order);
if (old_view[left_role] != left_lease.index)
return {Triple_Buffer_Result::State_Changed, old_view, triple_buffer_invalid_index};
auto right_index = old_view[right_role];
if (!try_mark_index(right_index))
return {Triple_Buffer_Result::Busy, old_view, right_index};
auto cur_order = slot_state.load(std::memory_order_acquire);
if (cur_order != old_order) {
unmark_index(right_index);
return {Triple_Buffer_Result::State_Changed, old_view, triple_buffer_invalid_index};
}
if (!can_swap(old_view)) {
unmark_index(right_index);
return {Triple_Buffer_Result::Condition_Failed, old_view, triple_buffer_invalid_index};
}
auto new_order = swap_order(old_order, left_role, right_role);
auto expected = old_order;
if (!slot_state.compare_exchange_strong(expected, new_order, std::memory_order_acq_rel, std::memory_order_acquire)) {
unmark_index(right_index);
return {Triple_Buffer_Result::State_Changed, old_view, triple_buffer_invalid_index};
}
on_success(old_view, decode_order(new_order));
unmark_index(right_index);
slot_state.notify_all();
wake_generation.fetch_add(1, std::memory_order_acq_rel);
wake_generation.notify_all();
return {Triple_Buffer_Result::Success, old_view, triple_buffer_invalid_index};
}
};
} // namespace renderive