Files
Renderive/YSGraphic_Core/RenderAble.h
T
2026-07-23 16:35:55 +08:00

879 lines
33 KiB
C++

#pragma once
#include <array>
#include <atomic>
#include <concepts>
#include <cstdint>
#include <type_traits>
#include <utility>
#include <qlinkedlist.h>
#include <QImage>
#include <QRegion>
#include <qscreen.h>
#include "GlobalTypes.h"
namespace YSG {
static constexpr std::uint8_t triple_buffer_invalid_index = 255;
enum class Triple_Buffer_Result : std::uint8_t {
Success,
Busy,
State_Changed,
Condition_Failed
};
enum class Triple_Buffer_Mode : std::uint8_t {
Try,
Wait
};
enum class Plot_Buffer_Acquire_Mode : std::uint8_t {
Try,
Wait
};
enum class Plot_Render_Schedule_Mode : std::uint8_t {
Timer_Check,
Chase_Latest,
Max_Fps,
Manual
};
enum class RenderAble_Render_Schedule_Mode : std::uint8_t {
Inherit,
Chase_Latest,
Max_Fps,
On_Dirty,
Manual
};
enum class Render_Request_Source : std::uint8_t {
Timer,
Dirty,
Finish,
Manual
};
inline Triple_Buffer_Mode to_triple_buffer_mode(Plot_Buffer_Acquire_Mode mode) {
return mode == Plot_Buffer_Acquire_Mode::Wait ? Triple_Buffer_Mode::Wait : Triple_Buffer_Mode::Try;
}
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{};
Triple_Buffer_View new_view{};
std::uint8_t busy_index = triple_buffer_invalid_index;
};
struct alignas(64) Triple_Buffer_Busy {
std::atomic_bool busy{false};
};
struct Triple_Buffer_Stats {
std::atomic<std::uint64_t> mark_success{0};
std::atomic<std::uint64_t> mark_busy{0};
std::atomic<std::uint64_t> mark_state_changed{0};
std::atomic<std::uint64_t> swap_success{0};
std::atomic<std::uint64_t> swap_busy{0};
std::atomic<std::uint64_t> swap_state_changed{0};
std::atomic<std::uint64_t> swap_condition_failed{0};
std::atomic<std::uint64_t> wait_count{0};
};
class Triple_Role_Buffer_Control {
public:
void set_stats(Triple_Buffer_Stats* value) {
stats = value;
}
void reset() {
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();
}
slot_state.notify_all();
}
Triple_Buffer_View read_view() const {
return decode_order(slot_state.load(std::memory_order_acquire));
}
Triple_Buffer_Mark_Record try_mark_use_index(std::uint8_t index) {
if(!try_mark_index(index)) {
record_mark(Triple_Buffer_Result::Busy);
return {Triple_Buffer_Result::Busy, {}, index};
}
record_mark(Triple_Buffer_Result::Success);
return {Triple_Buffer_Result::Success, {index}, triple_buffer_invalid_index};
}
Triple_Buffer_Lease wait_mark_use_index(std::uint8_t index) {
for(;;) {
if(try_mark_index(index)) {
record_mark(Triple_Buffer_Result::Success);
return {index};
}
record_mark(Triple_Buffer_Result::Busy);
record_wait();
wait_index_free(index);
}
}
Triple_Buffer_Mark_Record try_mark_use_role(std::uint8_t role) {
auto order = slot_state.load(std::memory_order_acquire);
auto view = decode_order(order);
auto index = view[role];
if(!try_mark_index(index)) {
record_mark(Triple_Buffer_Result::Busy);
return {Triple_Buffer_Result::Busy, {}, index};
}
auto cur_order = slot_state.load(std::memory_order_acquire);
if(cur_order != order) {
unmark_index(index);
record_mark(Triple_Buffer_Result::State_Changed);
return {Triple_Buffer_Result::State_Changed, {}, triple_buffer_invalid_index};
}
record_mark(Triple_Buffer_Result::Success);
return {Triple_Buffer_Result::Success, {index}, triple_buffer_invalid_index};
}
Triple_Buffer_Lease wait_mark_use_role(std::uint8_t role) {
for(;;) {
auto record = try_mark_use_role(role);
if(record.result == Triple_Buffer_Result::Success) {
return record.lease;
}
if(record.result == Triple_Buffer_Result::Busy) {
record_wait();
wait_index_free(record.busy_index);
}
}
}
void unmark_use(Triple_Buffer_Lease 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, busy_index);
}
template <typename Can_Swap>
Triple_Buffer_Swap_Record wait_swap_role(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap) {
for(;;) {
std::uint8_t busy_index = triple_buffer_invalid_index;
auto record = try_swap_role_once(left_role, right_role, can_swap, busy_index);
if(record.result == Triple_Buffer_Result::Success || record.result == Triple_Buffer_Result::Condition_Failed) {
return record;
}
if(record.result == Triple_Buffer_Result::Busy) {
record_wait();
wait_index_free(busy_index);
} else {
record_wait();
slot_state.wait(encode_order(record.old_view), std::memory_order_acquire);
}
}
}
private:
std::atomic<std::uint8_t> slot_state{0};
std::array<Triple_Buffer_Busy, 3> buffer_busy{};
Triple_Buffer_Stats* stats{};
void record_mark(Triple_Buffer_Result result) {
if(!stats) return;
switch(result) {
case Triple_Buffer_Result::Success:
stats->mark_success.fetch_add(1, std::memory_order_relaxed);
break;
case Triple_Buffer_Result::Busy:
stats->mark_busy.fetch_add(1, std::memory_order_relaxed);
break;
case Triple_Buffer_Result::State_Changed:
stats->mark_state_changed.fetch_add(1, std::memory_order_relaxed);
break;
case Triple_Buffer_Result::Condition_Failed:
break;
}
}
void record_swap(Triple_Buffer_Result result) {
if(!stats) return;
switch(result) {
case Triple_Buffer_Result::Success:
stats->swap_success.fetch_add(1, std::memory_order_relaxed);
break;
case Triple_Buffer_Result::Busy:
stats->swap_busy.fetch_add(1, std::memory_order_relaxed);
break;
case Triple_Buffer_Result::State_Changed:
stats->swap_state_changed.fetch_add(1, std::memory_order_relaxed);
break;
case Triple_Buffer_Result::Condition_Failed:
stats->swap_condition_failed.fetch_add(1, std::memory_order_relaxed);
break;
}
}
void record_wait() {
if(stats) stats->wait_count.fetch_add(1, std::memory_order_relaxed);
}
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();
}
void wait_index_free(std::uint8_t index) {
while(buffer_busy[index].busy.load(std::memory_order_acquire)) {
buffer_busy[index].busy.wait(true, std::memory_order_acquire);
}
}
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>
Triple_Buffer_Swap_Record try_swap_role_once(std::uint8_t left_role, std::uint8_t right_role, Can_Swap can_swap, std::uint8_t& busy_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)) {
record_swap(Triple_Buffer_Result::Busy);
return {Triple_Buffer_Result::Busy, old_view, old_view, busy_index};
}
auto cur_order = slot_state.load(std::memory_order_acquire);
if(cur_order != old_order) {
auto cur_view = decode_order(cur_order);
unmark_two_indices(left_index, right_index);
record_swap(Triple_Buffer_Result::State_Changed);
return {Triple_Buffer_Result::State_Changed, old_view, cur_view, triple_buffer_invalid_index};
}
if(!can_swap(old_view)) {
unmark_two_indices(left_index, right_index);
record_swap(Triple_Buffer_Result::Condition_Failed);
return {Triple_Buffer_Result::Condition_Failed, old_view, old_view, triple_buffer_invalid_index};
}
auto new_order = swap_order(old_order, left_role, right_role);
auto new_view = decode_order(new_order);
auto expected = old_order;
if(!slot_state.compare_exchange_strong(expected, new_order, std::memory_order_acq_rel, std::memory_order_acquire)) {
auto cur_view = decode_order(expected);
unmark_two_indices(left_index, right_index);
record_swap(Triple_Buffer_Result::State_Changed);
return {Triple_Buffer_Result::State_Changed, old_view, cur_view, triple_buffer_invalid_index};
}
unmark_two_indices(left_index, right_index);
slot_state.notify_all();
record_swap(Triple_Buffer_Result::Success);
return {Triple_Buffer_Result::Success, old_view, new_view, triple_buffer_invalid_index};
}
};
enum State_Role : std::uint8_t {
State_Edit,
State_Ready,
State_Render
};
enum Input_Role : std::uint8_t {
Input_Edit,
Input_Ready,
Input_Render
};
enum Color_Role : std::uint8_t {
Color_Front,
Color_Ready,
Color_Render
};
inline bool buffer_version_newer(std::uint64_t a, std::uint64_t b) {
return static_cast<std::int64_t>(a - b) > 0;
}
struct InputData {
virtual ~InputData() = default;
virtual void clear() {}
};
struct RenderState {
virtual ~RenderState() = default;
std::uint64_t version = 0;
RenderData* dPtr{};
SRC src();
};
struct InputBufferSlot {
InputData* data{};
std::uint64_t version = 0;
bool pending = false;
};
struct PixelBufferSlot {
std::atomic<std::uint64_t> version{0};
QImage image;
QRegion dirtyRegion;
};
struct PixelBufferLease {
PixelBufferSlot* buffer{};
Triple_Buffer_Lease lease{};
explicit operator bool() const {
return buffer && lease;
}
};
struct RenderData {
RenderData() {
mStateControl.set_stats(&mStateBufferStats);
mInputControl.set_stats(&mInputBufferStats);
mPixelControl.set_stats(&mPixelBufferStats);
}
RenderAble* qPtr{};
bool mRenderStart = false;
Triple_Role_Buffer_Control mStateControl;
Triple_Buffer_Stats mStateBufferStats;
std::array<RenderState*, 3> mStateBuffers{};
Triple_Role_Buffer_Control mInputControl;
Triple_Buffer_Stats mInputBufferStats;
std::array<InputBufferSlot, 3> mInputBuffers{};
std::uint64_t mInputVersion = 0;
std::atomic<Plot_Buffer_Acquire_Mode> mBufferAcquireMode{Plot_Buffer_Acquire_Mode::Wait};
std::atomic<RenderAble_Render_Schedule_Mode> mRenderScheduleMode{RenderAble_Render_Schedule_Mode::Inherit};
std::atomic<double> mRenderScheduleMaxFps{0.0};
std::atomic<std::uint64_t> mNextRenderSubmitNs{0};
Triple_Role_Buffer_Control mPixelControl;
Triple_Buffer_Stats mPixelBufferStats;
std::array<PixelBufferSlot, 3> mPixelBuffers{};
std::atomic_bool mIndependentPixelCache{false};
std::atomic_bool mIndependentRenderQueued{false};
std::atomic<std::uint64_t> mPixelEditVersion{1};
std::uint64_t mPixelRenderVersion = 0;
RenderState* state(std::uint8_t role) const {
return stateByIndex(mStateControl.read_view()[role]);
}
RenderState* state(SRC src) const {
return state(static_cast<std::uint8_t>(src));
}
RenderState* stateByIndex(std::uint8_t index) const {
return mStateBuffers[index];
}
Plot_Buffer_Acquire_Mode bufferAcquireMode() const {
return mBufferAcquireMode.load(std::memory_order_acquire);
}
void setBufferAcquireMode(Plot_Buffer_Acquire_Mode mode) {
mBufferAcquireMode.store(mode, std::memory_order_release);
}
RenderAble_Render_Schedule_Mode renderScheduleMode() const {
return mRenderScheduleMode.load(std::memory_order_acquire);
}
void setRenderScheduleMode(RenderAble_Render_Schedule_Mode mode) {
mRenderScheduleMode.store(mode, std::memory_order_release);
}
double renderScheduleMaxFps() const {
return mRenderScheduleMaxFps.load(std::memory_order_acquire);
}
void setRenderScheduleMaxFps(double fps) {
mRenderScheduleMaxFps.store(fps, std::memory_order_release);
}
Triple_Buffer_Lease markStateRole(std::uint8_t role) {
if(bufferAcquireMode() == Plot_Buffer_Acquire_Mode::Wait) {
return mStateControl.wait_mark_use_role(role);
}
auto record = mStateControl.try_mark_use_role(role);
if(record.result != Triple_Buffer_Result::Success) {
return {};
}
return record.lease;
}
void unmarkState(Triple_Buffer_Lease lease) {
mStateControl.unmark_use(lease);
}
void clearStateBuffers() {
for(RenderState*& state : mStateBuffers) {
delete state;
state = nullptr;
}
mStateControl.reset();
}
InputData* inputData(std::uint8_t role) const {
return inputSlot(role).data;
}
InputData* inputDataByIndex(std::uint8_t index) const {
return mInputBuffers[index].data;
}
InputBufferSlot& inputSlot(std::uint8_t role) {
return inputSlotByIndex(mInputControl.read_view()[role]);
}
const InputBufferSlot& inputSlot(std::uint8_t role) const {
return inputSlotByIndex(mInputControl.read_view()[role]);
}
InputBufferSlot& inputSlotByIndex(std::uint8_t index) {
return mInputBuffers[index];
}
const InputBufferSlot& inputSlotByIndex(std::uint8_t index) const {
return mInputBuffers[index];
}
bool hasPendingState() const {
auto view = mStateControl.read_view();
return buffer_version_newer(mStateBuffers[view[State_Edit]]->version, mStateBuffers[view[State_Render]]->version);
}
bool hasPendingInput() const {
auto view = mInputControl.read_view();
const InputBufferSlot& edit = mInputBuffers[view[Input_Edit]];
const InputBufferSlot& ready = mInputBuffers[view[Input_Ready]];
const InputBufferSlot& render = mInputBuffers[view[Input_Render]];
return (edit.pending && buffer_version_newer(edit.version, render.version)) || (ready.pending && buffer_version_newer(ready.version, render.version));
}
bool hasPendingRenderData() const {
return hasPendingState() || hasPendingInput();
}
void markInputDirty(Triple_Buffer_Lease lease, bool notifyRender = true);
void clearInputRole(std::uint8_t role) {
InputBufferSlot& slot = inputSlot(role);
slot.data->clear();
slot.pending = false;
}
void clearRenderInput() {
clearInputRole(Input_Render);
}
void clearInputBuffers() {
for(InputBufferSlot& slot : mInputBuffers) {
delete slot.data;
slot.data = nullptr;
slot.version = 0;
slot.pending = false;
}
mInputVersion = 0;
mInputControl.reset();
}
bool independentPixelCache() const {
return mIndependentPixelCache.load(std::memory_order_acquire);
}
void setIndependentPixelCache(bool enabled) {
mIndependentPixelCache.store(enabled, std::memory_order_release);
}
PixelBufferSlot* pixelByIndex(std::uint8_t index) {
return &mPixelBuffers[index];
}
PixelBufferLease waitRenderPixel() {
auto lease = mPixelControl.wait_mark_use_role(Color_Render);
return {pixelByIndex(lease.index), lease};
}
PixelBufferLease tryFrontPixel() {
auto record = mPixelControl.try_mark_use_role(Color_Front);
if(record.result != Triple_Buffer_Result::Success) return {};
return {pixelByIndex(record.lease.index), record.lease};
}
void unmarkPixel(PixelBufferLease lease) {
mPixelControl.unmark_use(lease.lease);
}
bool hasNewPixelState() const {
return buffer_version_newer(mPixelEditVersion.load(std::memory_order_acquire), mPixelRenderVersion);
}
void markPixelDirty() {
mPixelEditVersion.fetch_add(1, std::memory_order_release);
}
QRegion publishRenderPixel() {
mPixelControl.wait_swap_role(Color_Ready, Color_Render, [this](const Triple_Buffer_View& view) {
auto render = pixelByIndex(view[Color_Render]);
auto ready = pixelByIndex(view[Color_Ready]);
return buffer_version_newer(render->version.load(std::memory_order_acquire), ready->version.load(std::memory_order_acquire));
});
auto view = mPixelControl.read_view();
auto ready = pixelByIndex(view[Color_Ready]);
auto front = pixelByIndex(view[Color_Front]);
if(buffer_version_newer(ready->version.load(std::memory_order_acquire), front->version.load(std::memory_order_acquire))) return ready->dirtyRegion;
return {};
}
PixelBufferLease consumeReadyPixel(Triple_Buffer_Mode mode) {
auto canSwap = [this](const Triple_Buffer_View& view) {
auto ready = pixelByIndex(view[Color_Ready]);
auto front = pixelByIndex(view[Color_Front]);
return buffer_version_newer(ready->version.load(std::memory_order_acquire), front->version.load(std::memory_order_acquire));
};
if(mode == Triple_Buffer_Mode::Wait) {
mPixelControl.wait_swap_role(Color_Front, Color_Ready, canSwap);
} else {
mPixelControl.try_swap_role(Color_Front, Color_Ready, canSwap);
}
return tryFrontPixel();
}
protected:
virtual void syncStatePipeline() = 0;
virtual RenderState* renderState() = 0;
virtual RenderState* readyState() = 0;
virtual RenderState* editState() = 0;
virtual InputData* renderInputData() = 0;
virtual InputData* editInputData() = 0;
virtual ~RenderData() {
clearStateBuffers();
clearInputBuffers();
}
virtual void draw(QPainter* painter) {}
virtual void prepareData() {
syncStatePipeline();
}
virtual bool selectTest(const QPointF& pos) {
return false;
}
virtual void plotEvent(QEvent* event) {}
virtual void mousePressEvent(QMouseEvent* event) {}
virtual void mouseMoveEvent(QMouseEvent* event) {}
virtual void mouseReleaseEvent(QMouseEvent* event) {}
virtual void setHoverState(const QPoint& pos, bool active) {}
virtual void keyPressEvent(QKeyEvent* event) {}
virtual void keyReleaseEvent(QKeyEvent* event) {}
virtual void firstResizeEvent(QResizeEvent* event) {}
virtual void resizeEvent(QResizeEvent* event) {}
virtual void wheelEvent(QWheelEvent* event) {}
friend struct RenderAble;
};
struct BaseRenderData : RenderData {
RenderState* renderState() override {
return nullptr;
}
RenderState* readyState() override {
return nullptr;
}
RenderState* editState() override {
return nullptr;
}
InputData* renderInputData() override {
return nullptr;
}
InputData* editInputData() override {
return nullptr;
}
void syncStatePipeline() override {}
};
class Plot;
struct LIB_DECL RenderEditGuard {
RenderEditGuard(RenderData* data, Triple_Buffer_Lease lease);
~RenderEditGuard();
RenderData* mData{};
Triple_Buffer_Lease mLease{};
};
struct LIB_DECL RenderInputGuard {
RenderInputGuard(RenderData* data, Triple_Buffer_Lease lease);
~RenderInputGuard();
RenderData* mData{};
Triple_Buffer_Lease mLease{};
};
struct RenderAble {
enum class JobState {
Idle,
Queued,
Running
};
struct ColorBuffer {
std::atomic<std::uint64_t> version{0};
QImage image;
QRegion dirtyRegion;
};
struct ColorBufferLease {
ColorBuffer* buffer{};
Triple_Buffer_Lease lease{};
explicit operator bool() const {
return buffer && lease;
}
};
struct RenderPipeline {
Triple_Role_Buffer_Control colorControl;
Triple_Buffer_Stats colorBufferStats;
std::array<ColorBuffer, 3> colorBuffers;
std::atomic<std::uint64_t> editStateVersion{1};
std::uint64_t renderStateVersion = 0;
JobState jobState = JobState::Idle;
std::atomic_bool paintRequestPending{false};
RenderPipeline();
void markEditState();
bool hasNewState() const;
ColorBuffer* colorByIndex(std::uint8_t index);
ColorBuffer* colorByRole(std::uint8_t role);
ColorBufferLease waitRenderColor();
ColorBufferLease waitFrontColor();
ColorBufferLease tryFrontColor();
void unmarkColor(ColorBufferLease lease);
QRegion publishRenderColor();
ColorBufferLease consumeReadyColor(Triple_Buffer_Mode mode);
};
virtual RenderData* createRenderData() {
return new BaseRenderData;
}
virtual RenderState* createRenderState() {
return new RenderState;
}
virtual InputData* createInputData() {
return new InputData;
}
virtual void init(Plot* plot, QString layerName = "");
virtual void draw(QPainter* painter) {
dPtr->draw(painter);
}
virtual void prepareData() {
dPtr->prepareData();
}
virtual bool selectTest(const QPointF& pos) {
return dPtr->selectTest(pos);
}
virtual void plotEvent(QEvent* event) {
dPtr->plotEvent(event);
}
virtual void mousePressEvent(QMouseEvent* event) {
dPtr->mousePressEvent(event);
}
virtual void mouseMoveEvent(QMouseEvent* event) {
dPtr->mouseMoveEvent(event);
}
virtual void mouseReleaseEvent(QMouseEvent* event) {
dPtr->mouseReleaseEvent(event);
}
virtual void setHoverState(const QPoint& pos, bool active) {
dPtr->setHoverState(pos, active);
}
virtual void keyPressEvent(QKeyEvent* event) {
dPtr->keyPressEvent(event);
}
virtual void keyReleaseEvent(QKeyEvent* event) {
dPtr->keyReleaseEvent(event);
}
virtual void firstResizeEvent(QResizeEvent* event) {
dPtr->firstResizeEvent(event);
}
virtual void resizeEvent(QResizeEvent* event) {
dPtr->resizeEvent(event);
}
virtual void wheelEvent(QWheelEvent* event) {
dPtr->wheelEvent(event);
};
static bool version_newer(std::uint64_t a, std::uint64_t b) {
return buffer_version_newer(a, b);
}
Plot_Buffer_Acquire_Mode bufferAcquireMode() const;
void setBufferAcquireMode(Plot_Buffer_Acquire_Mode mode);
RenderAble_Render_Schedule_Mode renderScheduleMode() const;
void setRenderScheduleMode(RenderAble_Render_Schedule_Mode mode);
double renderScheduleMaxFps() const;
void setRenderScheduleMaxFps(double fps);
bool independentPixelCache() const;
void setIndependentPixelCache(bool enabled);
void markStateDirty();
void markRenderDirty();
QString mObjectName;
RenderData* dPtr{};
Plot* mPlot{};
[[nodiscard]] bool ok() const;
bool mPrepared = false;
// 这里面可以预先准备数据用来同步
virtual ~RenderAble();
virtual bool select(QPointF pos) {
return false;
}
bool mVisable = true;
RenderAble* mParent{};
QVector<RenderAble*> mBeRelyList;
bool dectCycleRely();
private:
bool hasCycleRely(RenderAble* node, QSet<RenderAble*>& visited, QSet<RenderAble*>& recursionStack, QVector<RenderAble*>& currentPath);
};
template <typename T>
concept Render_State_Type = std::derived_from<T, RenderState> && std::is_copy_assignable_v<T>;
template <typename T>
concept Input_Data_Type = std::derived_from<T, InputData>;
template <typename T>
concept Render_Data_Type = std::derived_from<T, RenderData>;
template <Render_Data_Type Base, typename Owner, Render_State_Type State, Input_Data_Type Input>
struct Typed_Render_Data_Base : Base {
Owner* q() {
return reinterpret_cast<Owner*>(this->qPtr);
}
State* renderState() override {
return reinterpret_cast<State*>(this->state(State_Render));
}
State* readyState() override {
return reinterpret_cast<State*>(this->state(State_Ready));
}
State* editState() override {
return reinterpret_cast<State*>(this->state(State_Edit));
}
Input* renderInputData() override {
return reinterpret_cast<Input*>(this->inputData(Input_Render));
}
Input* editInputData() override {
return reinterpret_cast<Input*>(this->inputData(Input_Edit));
}
State* state_data(SRC src) {
return reinterpret_cast<State*>(this->state(src));
}
State* state_data_by_index(std::uint8_t index) {
return reinterpret_cast<State*>(this->stateByIndex(index));
}
Input* input_data_by_index(std::uint8_t index) {
return reinterpret_cast<Input*>(this->inputDataByIndex(index));
}
template <typename Field>
Field state_value(Field State::*field, SRC src) {
return state_data(src)->*field;
}
template <typename Field>
void set_state_value(Field State::*field, Field value) {
auto lease = this->mStateControl.wait_mark_use_role(State_Edit);
auto state = state_data_by_index(lease.index);
RenderEditGuard guard(this, lease);
state->*field = std::move(value);
}
void syncStatePipeline() override {
Triple_Buffer_Lease editLease = this->mStateControl.wait_mark_use_role(State_Edit);
Triple_Buffer_Lease readyLease = this->mStateControl.wait_mark_use_role(State_Ready);
auto ready = reinterpret_cast<State*>(this->stateByIndex(readyLease.index));
auto edit = reinterpret_cast<State*>(this->stateByIndex(editLease.index));
if(buffer_version_newer(edit->version, ready->version)) *ready = *edit;
this->mStateControl.unmark_use(readyLease);
this->mStateControl.unmark_use(editLease);
this->mStateControl.wait_swap_role(State_Ready, State_Render, [this](const Triple_Buffer_View& view) {
auto ready = reinterpret_cast<State*>(this->stateByIndex(view[State_Ready]));
auto render = reinterpret_cast<State*>(this->stateByIndex(view[State_Render]));
return buffer_version_newer(ready->version, render->version);
});
this->mInputControl.wait_swap_role(Input_Edit, Input_Ready, [this](const Triple_Buffer_View& view) {
const InputBufferSlot& edit = this->inputSlotByIndex(view[Input_Edit]);
const InputBufferSlot& ready = this->inputSlotByIndex(view[Input_Ready]);
return edit.pending && buffer_version_newer(edit.version, ready.version);
});
this->mInputControl.wait_swap_role(Input_Ready, Input_Render, [this](const Triple_Buffer_View& view) {
const InputBufferSlot& ready = this->inputSlotByIndex(view[Input_Ready]);
const InputBufferSlot& render = this->inputSlotByIndex(view[Input_Render]);
return ready.pending && buffer_version_newer(ready.version, render.version);
});
if(!this->mRenderStart) this->mRenderStart = true;
}
};
template <typename Owner, Render_State_Type State, Input_Data_Type Input>
using Typed_Render_Data = Typed_Render_Data_Base<RenderData, Owner, State, Input>;
template <typename Owner, Render_Data_Type Data, Render_State_Type State, Input_Data_Type Input>
struct RenderAble_Binding {
static void init(Owner* owner, const char* name) {
owner->dPtr = new Data;
owner->mObjectName = name;
}
static Data* d(Owner* owner) {
return reinterpret_cast<Data*>(owner->dPtr);
}
static RenderData* create_render_data() {
return new Data;
}
static RenderState* create_render_state() {
return new State;
}
static InputData* create_input_data() {
return new Input;
}
};
template <Render_Data_Type Data, Render_State_Type State>
struct Render_State_Lease {
explicit Render_State_Lease(Data* data) : data(data), lease(data->mStateControl.wait_mark_use_role(State_Edit)), state(reinterpret_cast<State*>(data->stateByIndex(lease.index))) {}
Render_State_Lease(const Render_State_Lease&) = delete;
Render_State_Lease& operator=(const Render_State_Lease&) = delete;
~Render_State_Lease() {
data->unmarkState(lease);
}
State* operator->() {
return state;
}
State& operator*() {
return *state;
}
Data* data{};
Triple_Buffer_Lease lease{};
State* state{};
};
template <Render_Data_Type Data, Render_State_Type State>
struct Render_Edit_Lease {
explicit Render_Edit_Lease(Data* data) : data(data), lease(data->mStateControl.wait_mark_use_role(State_Edit)), state(reinterpret_cast<State*>(data->stateByIndex(lease.index))) {}
Render_Edit_Lease(const Render_Edit_Lease&) = delete;
Render_Edit_Lease& operator=(const Render_Edit_Lease&) = delete;
~Render_Edit_Lease() {
++data->stateByIndex(lease.index)->version;
if(data->qPtr) data->qPtr->markRenderDirty();
data->unmarkState(lease);
}
State* operator->() {
return state;
}
State& operator*() {
return *state;
}
Data* data{};
Triple_Buffer_Lease lease{};
State* state{};
};
template <Render_Data_Type Data, Input_Data_Type Input>
struct Render_Input_Lease {
explicit Render_Input_Lease(Data* data, bool notify_render = true) : data(data), notify_render(notify_render), lease(data->mInputControl.wait_mark_use_role(Input_Edit)), input(reinterpret_cast<Input*>(data->inputDataByIndex(lease.index))) {}
Render_Input_Lease(const Render_Input_Lease&) = delete;
Render_Input_Lease& operator=(const Render_Input_Lease&) = delete;
~Render_Input_Lease() {
data->markInputDirty(lease, notify_render);
data->mInputControl.unmark_use(lease);
}
Input* operator->() {
return input;
}
Input& operator*() {
return *input;
}
Data* data{};
bool notify_render{};
Triple_Buffer_Lease lease{};
Input* input{};
};
}