#pragma once #include #include #include #include #include #include #include #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 }; struct Triple_Buffer_View { std::array 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}; }; class Triple_Role_Buffer_Control { public: 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)) { return {Triple_Buffer_Result::Busy, {}, index}; } 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)) { return {index}; } 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)) { 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_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) { wait_index_free(record.busy_index); } } } void unmark_use(Triple_Buffer_Lease lease) { unmark_index(lease.index); } template 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 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) { wait_index_free(busy_index); } else { slot_state.wait(encode_order(record.old_view), std::memory_order_acquire); } } } private: std::atomic slot_state{0}; std::array 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(); } 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 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)) { 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); 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); 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); 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(); 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(a - b) > 0; } #define INIT_GET(className) className##Private* privateData = d(); \ className##RenderState* renderState = reinterpret_cast(d()->state(src)); 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 RenderData { RenderAble* qPtr{}; bool mRenderStart = false; Triple_Role_Buffer_Control mStateControl; std::array mStateBuffers{}; Triple_Role_Buffer_Control mInputControl; std::array mInputBuffers{}; std::uint64_t mInputVersion = 0; std::atomic mBufferAcquireMode{Triple_Buffer_Mode::Wait}; RenderState* state(std::uint8_t role) const { return stateByIndex(mStateControl.read_view()[role]); } RenderState* state(SRC src) const { return state(static_cast(src)); } RenderState* stateByIndex(std::uint8_t index) const { return mStateBuffers[index]; } Triple_Buffer_Mode bufferAcquireMode() const { return mBufferAcquireMode.load(std::memory_order_acquire); } void setBufferAcquireMode(Triple_Buffer_Mode mode) { mBufferAcquireMode.store(mode, std::memory_order_release); } Triple_Buffer_Lease markStateRole(std::uint8_t role) { if(bufferAcquireMode() == Triple_Buffer_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]; } void markInputDirty(Triple_Buffer_Lease lease); 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(); } 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 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; std::array colorBuffers; std::atomic 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); } Triple_Buffer_Mode bufferAcquireMode() const; void setBufferAcquireMode(Triple_Buffer_Mode mode); 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 mBeRelyList; bool dectCycleRely(); private: bool hasCycleRely(RenderAble* node, QSet& visited, QSet& recursionStack, QVector& currentPath); }; #define Q_Ptr2(ClassName) \ ClassName##Private* d() {return reinterpret_cast(dPtr);} \ RenderData* createRenderData() override; \ RenderState* createRenderState() override; \ InputData* createInputData() override; \ protected: \ ClassName(); \ friend struct ClassName##Private; \ friend struct ClassName##RenderState; \ friend struct ClassName##InputData; \ public: #define Q_Ptr(ClassName) \ ClassName(); \ ClassName##Private* d() {return reinterpret_cast(dPtr);} \ RenderData* createRenderData() override; \ RenderState* createRenderState() override; \ InputData* createInputData() override; #define Q_Ptr_cpp(ClassName) \ ClassName::ClassName() { \ dPtr = new ClassName##Private; \ mObjectName = #ClassName; \ } \ RenderData* ClassName::createRenderData() {return new ClassName##Private;} \ RenderState* ClassName::createRenderState() {return new ClassName##RenderState;} \ InputData* ClassName::createInputData() {return new ClassName##InputData;} #define D_Ptr_3(ClassName) \ ClassName##RenderState* renderState() override {return reinterpret_cast(state(State_Render));} \ ClassName##RenderState* readyState() override {return reinterpret_cast(state(State_Ready));} \ ClassName##RenderState* editState() override {return reinterpret_cast(state(State_Edit));} \ ClassName##InputData* renderInputData() override {return reinterpret_cast(inputData(Input_Render));} \ ClassName##InputData* editInputData() override {return reinterpret_cast(inputData(Input_Edit));} \ friend class ClassName; \ void syncStatePipeline() override { \ Triple_Buffer_Lease editLease = mStateControl.wait_mark_use_role(State_Edit); \ Triple_Buffer_Lease readyLease = mStateControl.wait_mark_use_role(State_Ready); \ auto ready = reinterpret_cast(stateByIndex(readyLease.index)); \ auto edit = reinterpret_cast(stateByIndex(editLease.index)); \ if(buffer_version_newer(edit->version, ready->version)) *ready = *edit; \ mStateControl.unmark_use(readyLease); \ mStateControl.unmark_use(editLease); \ mStateControl.wait_swap_role(State_Ready, State_Render, [this](const Triple_Buffer_View& view) { \ auto ready = reinterpret_cast(stateByIndex(view[State_Ready])); \ auto render = reinterpret_cast(stateByIndex(view[State_Render])); \ return buffer_version_newer(ready->version, render->version); \ }); \ mInputControl.wait_swap_role(Input_Edit, Input_Ready, [this](const Triple_Buffer_View& view) { \ const InputBufferSlot& edit = inputSlotByIndex(view[Input_Edit]); \ const InputBufferSlot& ready = inputSlotByIndex(view[Input_Ready]); \ return edit.pending && buffer_version_newer(edit.version, ready.version); \ }); \ mInputControl.wait_swap_role(Input_Ready, Input_Render, [this](const Triple_Buffer_View& view) { \ const InputBufferSlot& ready = inputSlotByIndex(view[Input_Ready]); \ const InputBufferSlot& render = inputSlotByIndex(view[Input_Render]); \ return ready.pending && buffer_version_newer(ready.version, render.version); \ }); \ if(!mRenderStart) mRenderStart = true; \ } #define D_Ptr(ClassName) \ ClassName* q() {return reinterpret_cast(qPtr);} \ ClassName##RenderState* renderState() override {return reinterpret_cast(state(State_Render));} \ ClassName##RenderState* readyState() override {return reinterpret_cast(state(State_Ready));} \ ClassName##RenderState* editState() override {return reinterpret_cast(state(State_Edit));} \ ClassName##InputData* renderInputData() override {return reinterpret_cast(inputData(Input_Render));} \ ClassName##InputData* editInputData() override {return reinterpret_cast(inputData(Input_Edit));} \ friend class ClassName; \ void syncStatePipeline() override { \ Triple_Buffer_Lease editLease = mStateControl.wait_mark_use_role(State_Edit); \ Triple_Buffer_Lease readyLease = mStateControl.wait_mark_use_role(State_Ready); \ auto ready = reinterpret_cast(stateByIndex(readyLease.index)); \ auto edit = reinterpret_cast(stateByIndex(editLease.index)); \ if(buffer_version_newer(edit->version, ready->version)) *ready = *edit; \ mStateControl.unmark_use(readyLease); \ mStateControl.unmark_use(editLease); \ mStateControl.wait_swap_role(State_Ready, State_Render, [this](const Triple_Buffer_View& view) { \ auto ready = reinterpret_cast(stateByIndex(view[State_Ready])); \ auto render = reinterpret_cast(stateByIndex(view[State_Render])); \ return buffer_version_newer(ready->version, render->version); \ }); \ mInputControl.wait_swap_role(Input_Edit, Input_Ready, [this](const Triple_Buffer_View& view) { \ const InputBufferSlot& edit = inputSlotByIndex(view[Input_Edit]); \ const InputBufferSlot& ready = inputSlotByIndex(view[Input_Ready]); \ return edit.pending && buffer_version_newer(edit.version, ready.version); \ }); \ mInputControl.wait_swap_role(Input_Ready, Input_Render, [this](const Triple_Buffer_View& view) { \ const InputBufferSlot& ready = inputSlotByIndex(view[Input_Ready]); \ const InputBufferSlot& render = inputSlotByIndex(view[Input_Render]); \ return ready.pending && buffer_version_newer(ready.version, render.version); \ }); \ if(!mRenderStart) mRenderStart = true; \ } #define D_Ptr2(ClassName) \ ClassName* q() {return reinterpret_cast(qPtr);} \ ClassName##RenderState* renderState() override {return reinterpret_cast(state(State_Render));} \ ClassName##RenderState* readyState() override {return reinterpret_cast(state(State_Ready));} \ ClassName##RenderState* editState() override {return reinterpret_cast(state(State_Edit));} \ ClassName##InputData* renderInputData() override {return reinterpret_cast(inputData(Input_Render));} \ ClassName##InputData* editInputData() override {return reinterpret_cast(inputData(Input_Edit));} \ friend class ClassName; \ void syncStatePipeline() override { \ Triple_Buffer_Lease editLease = mStateControl.wait_mark_use_role(State_Edit); \ Triple_Buffer_Lease readyLease = mStateControl.wait_mark_use_role(State_Ready); \ auto ready = reinterpret_cast(stateByIndex(readyLease.index)); \ auto edit = reinterpret_cast(stateByIndex(editLease.index)); \ if(buffer_version_newer(edit->version, ready->version)) *ready = *edit; \ mStateControl.unmark_use(readyLease); \ mStateControl.unmark_use(editLease); \ mStateControl.wait_swap_role(State_Ready, State_Render, [this](const Triple_Buffer_View& view) { \ auto ready = reinterpret_cast(stateByIndex(view[State_Ready])); \ auto render = reinterpret_cast(stateByIndex(view[State_Render])); \ return buffer_version_newer(ready->version, render->version); \ }); \ mInputControl.wait_swap_role(Input_Edit, Input_Ready, [this](const Triple_Buffer_View& view) { \ const InputBufferSlot& edit = inputSlotByIndex(view[Input_Edit]); \ const InputBufferSlot& ready = inputSlotByIndex(view[Input_Ready]); \ return edit.pending && buffer_version_newer(edit.version, ready.version); \ }); \ mInputControl.wait_swap_role(Input_Ready, Input_Render, [this](const Triple_Buffer_View& view) { \ const InputBufferSlot& ready = inputSlotByIndex(view[Input_Ready]); \ const InputBufferSlot& render = inputSlotByIndex(view[Input_Render]); \ return ready.pending && buffer_version_newer(ready.version, render.version); \ }); \ if(!mRenderStart) mRenderStart = true; \ } #define INIT_SET(className) className##Private* pd = d(); \ Triple_Buffer_Lease _stateLease = pd->mStateControl.wait_mark_use_role(State_Edit); \ className##RenderState* sc = reinterpret_cast(pd->stateByIndex(_stateLease.index));\ RenderEditGuard _editGuard(pd, _stateLease); #define INIT_INPUT(className) className##Private* pd = d(); \ Triple_Buffer_Lease _inputLease = pd->mInputControl.wait_mark_use_role(Input_Edit); \ className##InputData* input = reinterpret_cast(pd->inputDataByIndex(_inputLease.index)); \ RenderInputGuard _inputGuard(pd, _inputLease); }