更新前
This commit is contained in:
@@ -17,6 +17,7 @@ namespace YSG {
|
||||
}
|
||||
|
||||
RenderAble::RenderPipeline::RenderPipeline() {
|
||||
colorControl.set_stats(&colorBufferStats);
|
||||
colorControl.reset();
|
||||
}
|
||||
void RenderAble::RenderPipeline::markEditState() {
|
||||
@@ -49,12 +50,18 @@ namespace YSG {
|
||||
void RenderAble::RenderPipeline::unmarkColor(ColorBufferLease lease) {
|
||||
colorControl.unmark_use(lease.lease);
|
||||
}
|
||||
Triple_Buffer_Mode RenderAble::bufferAcquireMode() const {
|
||||
Plot_Buffer_Acquire_Mode RenderAble::bufferAcquireMode() const {
|
||||
return dPtr->bufferAcquireMode();
|
||||
}
|
||||
void RenderAble::setBufferAcquireMode(Triple_Buffer_Mode mode) {
|
||||
void RenderAble::setBufferAcquireMode(Plot_Buffer_Acquire_Mode mode) {
|
||||
dPtr->setBufferAcquireMode(mode);
|
||||
}
|
||||
bool RenderAble::independentPixelCache() const {
|
||||
return dPtr->independentPixelCache();
|
||||
}
|
||||
void RenderAble::setIndependentPixelCache(bool enabled) {
|
||||
dPtr->setIndependentPixelCache(enabled);
|
||||
}
|
||||
QRegion RenderAble::RenderPipeline::publishRenderColor() {
|
||||
colorControl.wait_swap_role(Color_Ready, Color_Render, [this](const Triple_Buffer_View& view) {
|
||||
auto render = colorByIndex(view[Color_Render]);
|
||||
@@ -101,11 +108,11 @@ namespace YSG {
|
||||
mData->markInputDirty(mLease);
|
||||
mData->mInputControl.unmark_use(mLease);
|
||||
}
|
||||
void RenderData::markInputDirty(Triple_Buffer_Lease lease) {
|
||||
void RenderData::markInputDirty(Triple_Buffer_Lease lease, bool notifyRender) {
|
||||
InputBufferSlot& slot = inputSlotByIndex(lease.index);
|
||||
slot.version = ++mInputVersion;
|
||||
slot.pending = true;
|
||||
if(qPtr) qPtr->markRenderDirty();
|
||||
if(notifyRender && qPtr) qPtr->markRenderDirty();
|
||||
}
|
||||
void RenderAble::init(Plot* plot, QString layerName) {
|
||||
if(layerName.isEmpty()) {
|
||||
@@ -114,6 +121,7 @@ namespace YSG {
|
||||
plot->mLayerMap[layerName]->mRenderAbles.append(this);
|
||||
mPlot = plot;
|
||||
if(!dPtr) dPtr = createRenderData();
|
||||
dPtr->setBufferAcquireMode(plot->renderAbleBufferAcquireMode());
|
||||
dPtr->clearStateBuffers();
|
||||
dPtr->clearInputBuffers();
|
||||
dPtr->mStateBuffers[State_Edit] = createRenderState();
|
||||
@@ -138,7 +146,13 @@ namespace YSG {
|
||||
markRenderDirty();
|
||||
}
|
||||
void RenderAble::markRenderDirty() {
|
||||
if(mPlot) mPlot->markRenderStateDirty();
|
||||
if(!mPlot) return;
|
||||
if(dPtr && dPtr->independentPixelCache()) {
|
||||
dPtr->markPixelDirty();
|
||||
mPlot->markIndependentRenderDirty(this);
|
||||
return;
|
||||
}
|
||||
mPlot->markRenderStateDirty();
|
||||
}
|
||||
bool RenderAble::ok() const {
|
||||
return dPtr->mRenderStart && mPlot && !mPlot->mFirstResize && !mPlot->mFirstPrepareData;
|
||||
|
||||
+161
-7
@@ -19,6 +19,13 @@ enum class Triple_Buffer_Mode : std::uint8_t {
|
||||
Try,
|
||||
Wait
|
||||
};
|
||||
enum class Plot_Buffer_Acquire_Mode : std::uint8_t {
|
||||
Try,
|
||||
Wait
|
||||
};
|
||||
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 {
|
||||
@@ -45,8 +52,21 @@ struct Triple_Buffer_Swap_Record {
|
||||
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) {
|
||||
@@ -60,15 +80,20 @@ public:
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -77,13 +102,16 @@ public:
|
||||
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) {
|
||||
@@ -93,6 +121,7 @@ public:
|
||||
return record.lease;
|
||||
}
|
||||
if(record.result == Triple_Buffer_Result::Busy) {
|
||||
record_wait();
|
||||
wait_index_free(record.busy_index);
|
||||
}
|
||||
}
|
||||
@@ -114,8 +143,10 @@ public:
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -123,6 +154,43 @@ public:
|
||||
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:
|
||||
@@ -205,16 +273,19 @@ private:
|
||||
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);
|
||||
@@ -223,10 +294,12 @@ private:
|
||||
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};
|
||||
}
|
||||
};
|
||||
@@ -265,15 +338,41 @@ struct InputBufferSlot {
|
||||
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<Triple_Buffer_Mode> mBufferAcquireMode{Triple_Buffer_Mode::Wait};
|
||||
std::atomic<Plot_Buffer_Acquire_Mode> mBufferAcquireMode{Plot_Buffer_Acquire_Mode::Wait};
|
||||
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]);
|
||||
}
|
||||
@@ -283,14 +382,14 @@ struct RenderData {
|
||||
RenderState* stateByIndex(std::uint8_t index) const {
|
||||
return mStateBuffers[index];
|
||||
}
|
||||
Triple_Buffer_Mode bufferAcquireMode() const {
|
||||
Plot_Buffer_Acquire_Mode bufferAcquireMode() const {
|
||||
return mBufferAcquireMode.load(std::memory_order_acquire);
|
||||
}
|
||||
void setBufferAcquireMode(Triple_Buffer_Mode mode) {
|
||||
void setBufferAcquireMode(Plot_Buffer_Acquire_Mode mode) {
|
||||
mBufferAcquireMode.store(mode, std::memory_order_release);
|
||||
}
|
||||
Triple_Buffer_Lease markStateRole(std::uint8_t role) {
|
||||
if(bufferAcquireMode() == Triple_Buffer_Mode::Wait) {
|
||||
if(bufferAcquireMode() == Plot_Buffer_Acquire_Mode::Wait) {
|
||||
return mStateControl.wait_mark_use_role(role);
|
||||
}
|
||||
auto record = mStateControl.try_mark_use_role(role);
|
||||
@@ -327,7 +426,7 @@ struct RenderData {
|
||||
const InputBufferSlot& inputSlotByIndex(std::uint8_t index) const {
|
||||
return mInputBuffers[index];
|
||||
}
|
||||
void markInputDirty(Triple_Buffer_Lease lease);
|
||||
void markInputDirty(Triple_Buffer_Lease lease, bool notifyRender = true);
|
||||
void clearInputRole(std::uint8_t role) {
|
||||
InputBufferSlot& slot = inputSlot(role);
|
||||
slot.data->clear();
|
||||
@@ -346,6 +445,58 @@ struct RenderData {
|
||||
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;
|
||||
@@ -427,6 +578,7 @@ struct RenderAble {
|
||||
};
|
||||
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;
|
||||
@@ -496,8 +648,10 @@ struct RenderAble {
|
||||
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);
|
||||
Plot_Buffer_Acquire_Mode bufferAcquireMode() const;
|
||||
void setBufferAcquireMode(Plot_Buffer_Acquire_Mode mode);
|
||||
bool independentPixelCache() const;
|
||||
void setIndependentPixelCache(bool enabled);
|
||||
void markStateDirty();
|
||||
void markRenderDirty();
|
||||
QString mObjectName;
|
||||
|
||||
@@ -3,6 +3,7 @@ namespace YSG {
|
||||
PerformanceShower::PerformanceShower() {
|
||||
dPtr = new PerformanceShowerPrivate;
|
||||
mObjectName = "PerformanceShower";
|
||||
setIndependentPixelCache(true);
|
||||
}
|
||||
PerformanceShowerPrivate* PerformanceShower::d() {
|
||||
return reinterpret_cast<PerformanceShowerPrivate*>(dPtr);
|
||||
@@ -30,38 +31,88 @@ namespace YSG {
|
||||
appendEvent({PerformanceEventType::Clear});
|
||||
}
|
||||
void PerformanceShower::counterIncrease(const QString& counterName) {
|
||||
incrementCounter("General", counterName);
|
||||
}
|
||||
void PerformanceShower::incrementCounter(const QString& scope, const QString& name) {
|
||||
PerformanceEvent event;
|
||||
event.type = PerformanceEventType::Counter;
|
||||
event.key = counterName;
|
||||
event.scope = scope;
|
||||
event.key = name;
|
||||
appendEvent(event);
|
||||
}
|
||||
void PerformanceShower::setPerformanceLine(const QString& key, const PerformanceLine& line) {
|
||||
PerformanceEvent event;
|
||||
event.type = PerformanceEventType::Line;
|
||||
event.scope = "General";
|
||||
event.key = key;
|
||||
event.line = line;
|
||||
appendEvent(event);
|
||||
}
|
||||
void PerformanceShower::setInfo(const QString& key, const QString& value) {
|
||||
setState("General", key, value);
|
||||
}
|
||||
void PerformanceShower::setState(const QString& scope, const QString& name, const QString& value) {
|
||||
PerformanceEvent event;
|
||||
event.type = PerformanceEventType::Info;
|
||||
event.key = key;
|
||||
event.type = PerformanceEventType::State;
|
||||
event.scope = scope;
|
||||
event.key = name;
|
||||
event.value = value;
|
||||
appendEvent(event);
|
||||
}
|
||||
void PerformanceShower::recordCost(const QString& key, double value) {
|
||||
void PerformanceShower::setGauge(const QString& scope, const QString& name, double value) {
|
||||
PerformanceEvent event;
|
||||
event.type = PerformanceEventType::Cost;
|
||||
event.key = key;
|
||||
event.type = PerformanceEventType::Gauge;
|
||||
event.scope = scope;
|
||||
event.key = name;
|
||||
event.number = value;
|
||||
appendEvent(event);
|
||||
}
|
||||
void PerformanceShower::recordCost(const QString& key, double value) {
|
||||
recordDuration("General", key, value);
|
||||
}
|
||||
void PerformanceShower::recordDuration(const QString& scope, const QString& name, double value) {
|
||||
PerformanceEvent event;
|
||||
event.type = PerformanceEventType::Duration;
|
||||
event.scope = scope;
|
||||
event.key = name;
|
||||
event.number = value;
|
||||
appendEvent(event);
|
||||
}
|
||||
void PerformanceShower::setViewFilter(const QString& name) {
|
||||
setViewFilter(name, {});
|
||||
}
|
||||
void PerformanceShower::setViewFilter(const QString& group, const QString& name) {
|
||||
PerformanceEvent event;
|
||||
event.type = PerformanceEventType::SelectView;
|
||||
event.scope = group;
|
||||
event.key = name;
|
||||
appendEvent(event);
|
||||
}
|
||||
void PerformanceShower::scrollView(int lines) {
|
||||
PerformanceEvent event;
|
||||
event.type = PerformanceEventType::ScrollView;
|
||||
event.number = lines;
|
||||
appendEvent(event);
|
||||
}
|
||||
void PerformanceShower::appendEvent(const PerformanceEvent& event) {
|
||||
if(!enabled() && event.type != PerformanceEventType::Clear) return;
|
||||
auto pd = d();
|
||||
Triple_Buffer_Lease inputLease = pd->mInputControl.wait_mark_use_role(Input_Edit);
|
||||
auto input = reinterpret_cast<PerformanceShowerInputData*>(pd->inputDataByIndex(inputLease.index));
|
||||
input->events.append(event);
|
||||
RenderInputGuard inputGuard(pd, inputLease);
|
||||
pd->markInputDirty(inputLease, false);
|
||||
pd->mInputControl.unmark_use(inputLease);
|
||||
notifyObservedUpdate(event.type == PerformanceEventType::Clear || event.type == PerformanceEventType::SelectView || event.type == PerformanceEventType::ScrollView);
|
||||
}
|
||||
void PerformanceShower::notifyObservedUpdate(bool force) {
|
||||
std::uint64_t now = static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||
std::uint64_t last = mLastNotifyNs.load(std::memory_order_acquire);
|
||||
if(!force && now - last < 100000000) return;
|
||||
if(force) {
|
||||
mLastNotifyNs.store(now, std::memory_order_release);
|
||||
markRenderDirty();
|
||||
return;
|
||||
}
|
||||
if(force || mLastNotifyNs.compare_exchange_strong(last, now, std::memory_order_acq_rel, std::memory_order_acquire)) markRenderDirty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,21 @@
|
||||
#include "Core/Statistics/Statistics.h"
|
||||
#include "../RenderAble.h"
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <QSet>
|
||||
namespace YSG {
|
||||
inline QString performanceAlignText(const QString& text, int& width) {
|
||||
width = qMax(width, static_cast<int>(text.size()));
|
||||
return QString("%1").arg(text, width);
|
||||
}
|
||||
inline QString performanceNumber(double value, int& width, int precision = 2) {
|
||||
return performanceAlignText(QString::number(value, 'f', precision), width);
|
||||
}
|
||||
template <typename T>
|
||||
inline QString performanceCount(T value, int& width) {
|
||||
return performanceAlignText(QString::number(static_cast<qulonglong>(value)), width);
|
||||
}
|
||||
struct PerformanceLine {
|
||||
PerformanceLine() = default;
|
||||
explicit PerformanceLine(const QString& data) {
|
||||
@@ -13,10 +27,12 @@ namespace YSG {
|
||||
}
|
||||
mPlaceHolderNum = argIndex - 1;
|
||||
mValues.resize(mPlaceHolderNum);
|
||||
mValueWidths.resize(mPlaceHolderNum);
|
||||
}
|
||||
QString mData;
|
||||
int mPlaceHolderNum{};
|
||||
QVector<double> mValues;
|
||||
QVector<int> mValueWidths;
|
||||
Psc::Value_Statistics mStatistics;
|
||||
void update(double value) {
|
||||
mStatistics.update(value);
|
||||
@@ -24,46 +40,99 @@ namespace YSG {
|
||||
if(mValues.size() > 1) mValues[1] = mStatistics.smooth;
|
||||
if(mValues.size() > 2) mValues[2] = mStatistics.variation;
|
||||
}
|
||||
int& widthAt(int index) {
|
||||
if(mValueWidths.size() <= index) mValueWidths.resize(index + 1);
|
||||
return mValueWidths[index];
|
||||
}
|
||||
QString toString() {
|
||||
QString ret = mData;
|
||||
int argIndex = 1;
|
||||
for(const double& value : mValues) {
|
||||
ret = ret.replace("%" + QString::number(argIndex), QString::number(value, 'f', 2));
|
||||
ret = ret.replace("%" + QString::number(argIndex), performanceNumber(value, widthAt(argIndex - 1)));
|
||||
argIndex++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
struct CounterLine {
|
||||
QString mName;
|
||||
Psc::Speed_Statistics mStatistics;
|
||||
[[nodiscard]] QString toString() const {
|
||||
return QString("%1 :%2 avg:%3 (次/s)")
|
||||
.arg(mName, QString::number(mStatistics.instant_speed, 'f', 2), QString::number(mStatistics.average_speed, 'f', 2));
|
||||
}
|
||||
enum class PerformanceMetricType : std::uint8_t {
|
||||
Duration,
|
||||
Counter,
|
||||
Gauge,
|
||||
State,
|
||||
Line
|
||||
};
|
||||
enum class PerformanceEventType : std::uint8_t {
|
||||
Clear,
|
||||
Duration,
|
||||
Counter,
|
||||
Line,
|
||||
Info,
|
||||
Cost
|
||||
Gauge,
|
||||
State,
|
||||
SelectView,
|
||||
ScrollView
|
||||
};
|
||||
struct PerformanceEvent {
|
||||
PerformanceEventType type = PerformanceEventType::Clear;
|
||||
QString scope;
|
||||
QString key;
|
||||
QString value;
|
||||
PerformanceLine line;
|
||||
double number{};
|
||||
};
|
||||
struct PerformanceMetric {
|
||||
PerformanceMetricType type = PerformanceMetricType::State;
|
||||
Psc::Value_Statistics value;
|
||||
Psc::Speed_Statistics speed;
|
||||
PerformanceLine line;
|
||||
QString text;
|
||||
double gauge{};
|
||||
std::uint64_t count{};
|
||||
QVector<int> widths;
|
||||
int& widthAt(int index) {
|
||||
if(widths.size() <= index) widths.resize(index + 1);
|
||||
return widths[index];
|
||||
}
|
||||
[[nodiscard]] QString toString(const QString& name) {
|
||||
switch(type) {
|
||||
case PerformanceMetricType::Duration:
|
||||
if(name.contains("%1")) {
|
||||
QString ret = name;
|
||||
ret = ret.replace("%1", performanceNumber(value.instant, widthAt(0)));
|
||||
ret = ret.replace("%2", performanceNumber(value.smooth, widthAt(1)));
|
||||
ret = ret.replace("%3", performanceNumber(value.variation, widthAt(2)));
|
||||
ret = ret.replace("%4", performanceNumber(value.p95, widthAt(3)));
|
||||
return ret;
|
||||
}
|
||||
return QString("%1 last:%2ms srtt:%3ms rttvar:%4ms min:%5ms max:%6ms p95:%7ms count:%8")
|
||||
.arg(name, performanceNumber(value.instant, widthAt(0)), performanceNumber(value.smooth, widthAt(1)), performanceNumber(value.variation, widthAt(2)), performanceNumber(value.min, widthAt(3)), performanceNumber(value.max, widthAt(4)), performanceNumber(value.p95, widthAt(5)), performanceCount(value.times, widthAt(6)));
|
||||
case PerformanceMetricType::Counter:
|
||||
return QString("%1 speed:%2/s avg:%3/s count:%4")
|
||||
.arg(name, performanceNumber(speed.instant_speed, widthAt(0)), performanceNumber(speed.average_speed, widthAt(1)), performanceCount(speed.times, widthAt(2)));
|
||||
case PerformanceMetricType::Gauge:
|
||||
return QString("%1: %2").arg(name, performanceNumber(gauge, widthAt(0)));
|
||||
case PerformanceMetricType::State:
|
||||
return QString("%1: %2").arg(name, text);
|
||||
case PerformanceMetricType::Line:
|
||||
return line.toString();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
struct PerformanceShowerRenderState : RenderState {
|
||||
QFont font;
|
||||
int topMargin = 4;
|
||||
int leftMargin = 4;
|
||||
int rightMargin = 28;
|
||||
int bottomMargin = 8;
|
||||
int columnGap = 12;
|
||||
int groupSidebarWidth = 92;
|
||||
int targetSidebarWidth = 170;
|
||||
QColor background = Qt::white;
|
||||
QColor foreground = Qt::black;
|
||||
QColor sidebarBackground = QColor(235, 235, 235);
|
||||
QColor selectedBackground = QColor(210, 225, 245);
|
||||
QColor scrollbarBackground = QColor(210, 210, 210);
|
||||
QColor scrollbarHandle = QColor(120, 120, 120);
|
||||
};
|
||||
struct PerformanceShowerInputData : InputData {
|
||||
QVector<PerformanceEvent> events;
|
||||
@@ -83,19 +152,40 @@ namespace YSG {
|
||||
bool enabled() const;
|
||||
void refreshCounter();
|
||||
void counterIncrease(const QString& counterName);
|
||||
void incrementCounter(const QString& scope, const QString& name);
|
||||
void setPerformanceLine(const QString& key, const PerformanceLine& line);
|
||||
void setInfo(const QString& key, const QString& value);
|
||||
void setState(const QString& scope, const QString& name, const QString& value);
|
||||
void setGauge(const QString& scope, const QString& name, double value);
|
||||
void recordCost(const QString& key, double value);
|
||||
void recordDuration(const QString& scope, const QString& name, double value);
|
||||
void setViewFilter(const QString& name);
|
||||
void setViewFilter(const QString& group, const QString& name);
|
||||
void scrollView(int lines);
|
||||
private:
|
||||
void appendEvent(const PerformanceEvent& event);
|
||||
void notifyObservedUpdate(bool force);
|
||||
std::atomic_bool mEnabled{false};
|
||||
std::atomic<std::uint64_t> mLastNotifyNs{0};
|
||||
};
|
||||
struct PerformanceShowerPrivate : RenderData {
|
||||
D_Ptr(PerformanceShower)
|
||||
QMap<QString, PerformanceLine> mInfoMap;
|
||||
QMap<QString, CounterLine> mCounter;
|
||||
QMap<QString, QString> mInfos;
|
||||
QMap<QString, QMap<QString, PerformanceMetric>> mScopes;
|
||||
QVector<QString> mAllInfo;
|
||||
QVector<QString> mGroupItems;
|
||||
QVector<QString> mTargetItems;
|
||||
QVector<QRect> mGroupRects;
|
||||
QVector<QRect> mTargetRects;
|
||||
QRect mPanelRect;
|
||||
int mGroupSidebarWidth{};
|
||||
int mTargetSidebarWidth{};
|
||||
int mContentWidth{};
|
||||
int mScrollLine{};
|
||||
int mMaxScrollLine{};
|
||||
bool mSidebarWidthDirty = true;
|
||||
bool mContentWidthDirty = true;
|
||||
QString mSelectedGroup = "Plot";
|
||||
QString mSelectedTarget = "Overview";
|
||||
void prepareData() override {
|
||||
syncStatePipeline();
|
||||
auto input = renderInputData();
|
||||
@@ -105,73 +195,281 @@ namespace YSG {
|
||||
clearRenderInput();
|
||||
rebuildLines();
|
||||
}
|
||||
bool selectTest(const QPointF& pos) override {
|
||||
return q()->enabled() && mPanelRect.contains(pos.toPoint());
|
||||
}
|
||||
void mousePressEvent(QMouseEvent* event) override {
|
||||
for(int i = 0; i < mGroupRects.size(); ++i) {
|
||||
if(mGroupRects[i].contains(event->pos())) {
|
||||
q()->setViewFilter(mGroupItems[i], {});
|
||||
return;
|
||||
}
|
||||
}
|
||||
for(int i = 0; i < mTargetRects.size(); ++i) {
|
||||
if(mTargetRects[i].contains(event->pos())) {
|
||||
q()->setViewFilter(mSelectedGroup, mTargetItems[i]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
void wheelEvent(QWheelEvent* event) override {
|
||||
if(!mPanelRect.contains(event->pos())) return;
|
||||
int steps = event->angleDelta().y() > 0 ? -3 : 3;
|
||||
q()->scrollView(steps);
|
||||
}
|
||||
void draw(QPainter* painter) override {
|
||||
if(!q()->enabled() || mAllInfo.isEmpty()) return;
|
||||
if(!q()->enabled() || mGroupItems.isEmpty()) return;
|
||||
auto s = renderState();
|
||||
painter->save();
|
||||
painter->setFont(s->font);
|
||||
QFontMetrics fm(s->font);
|
||||
if(mSidebarWidthDirty) updateSidebarWidth(fm, s);
|
||||
if(mContentWidthDirty) updateContentWidth(fm, s);
|
||||
int sideLineCount = qMax(mGroupItems.size(), mTargetItems.size());
|
||||
int lineCount = qMax(mAllInfo.size(), sideLineCount);
|
||||
int maxHeight = painter->device()->height();
|
||||
int sidebarWidth = mGroupSidebarWidth + mTargetSidebarWidth;
|
||||
int contentX = sidebarWidth + s->columnGap;
|
||||
int width = sidebarWidth + s->columnGap + mContentWidth;
|
||||
int fullHeight = fm.lineSpacing() * lineCount + s->topMargin + s->bottomMargin;
|
||||
int height = qMin(fullHeight, maxHeight);
|
||||
int visibleLines = qMax(1, (height - s->topMargin - s->bottomMargin) / fm.lineSpacing());
|
||||
mMaxScrollLine = qMax(0, mAllInfo.size() - visibleLines);
|
||||
mScrollLine = qBound(0, mScrollLine, mMaxScrollLine);
|
||||
QRect rect(0, 0, width, height);
|
||||
QRect groupRect(0, 0, mGroupSidebarWidth, height);
|
||||
QRect targetRect(groupRect.right() + 1, 0, mTargetSidebarWidth, height);
|
||||
mPanelRect = rect;
|
||||
mGroupRects.clear();
|
||||
mTargetRects.clear();
|
||||
painter->fillRect(rect, s->background);
|
||||
painter->fillRect(groupRect, s->sidebarBackground);
|
||||
painter->fillRect(targetRect, s->sidebarBackground);
|
||||
painter->setPen(s->foreground);
|
||||
for(int i = 0; i < visibleLines && i + mScrollLine < mAllInfo.size(); ++i) {
|
||||
painter->drawText(QPointF(contentX + s->leftMargin, s->topMargin + i * fm.lineSpacing() + fm.lineSpacing()), mAllInfo[i + mScrollLine]);
|
||||
}
|
||||
for(int i = 0; i < mGroupItems.size() && i < visibleLines; ++i) {
|
||||
QRect itemRect(groupRect.left(), s->topMargin + i * fm.lineSpacing(), mGroupSidebarWidth, fm.lineSpacing());
|
||||
mGroupRects.append(itemRect);
|
||||
if(mGroupItems[i] == mSelectedGroup) painter->fillRect(itemRect, s->selectedBackground);
|
||||
painter->drawText(QPointF(itemRect.left() + s->leftMargin, itemRect.top() + fm.lineSpacing()), mGroupItems[i]);
|
||||
}
|
||||
for(int i = 0; i < mTargetItems.size() && i < visibleLines; ++i) {
|
||||
QRect itemRect(targetRect.left(), s->topMargin + i * fm.lineSpacing(), mTargetSidebarWidth, fm.lineSpacing());
|
||||
mTargetRects.append(itemRect);
|
||||
if(mTargetItems[i] == mSelectedTarget) painter->fillRect(itemRect, s->selectedBackground);
|
||||
painter->drawText(QPointF(itemRect.left() + s->leftMargin, itemRect.top() + fm.lineSpacing()), mTargetItems[i]);
|
||||
}
|
||||
if(mMaxScrollLine > 0) drawScrollbar(painter, s, QRect(width - 8, 0, 8, height), visibleLines);
|
||||
painter->restore();
|
||||
}
|
||||
void drawScrollbar(QPainter* painter, PerformanceShowerRenderState* state, const QRect& rect, int visibleLines) {
|
||||
painter->fillRect(rect, state->scrollbarBackground);
|
||||
double ratio = static_cast<double>(visibleLines) / static_cast<double>(mAllInfo.size());
|
||||
int handleHeight = qMax(16, qRound(rect.height() * ratio));
|
||||
int travel = qMax(1, rect.height() - handleHeight);
|
||||
int top = qRound(static_cast<double>(travel) * static_cast<double>(mScrollLine) / static_cast<double>(mMaxScrollLine));
|
||||
painter->fillRect(QRect(rect.left(), rect.top() + top, rect.width(), handleHeight), state->scrollbarHandle);
|
||||
}
|
||||
void updateSidebarWidth(const QFontMetrics& fm, PerformanceShowerRenderState* state) {
|
||||
int groupWidth = 0;
|
||||
int targetWidth = 0;
|
||||
for(const QString& item : mGroupItems) groupWidth = qMax(groupWidth, fm.horizontalAdvance(item));
|
||||
for(const QString& item : mTargetItems) targetWidth = qMax(targetWidth, fm.horizontalAdvance(item));
|
||||
mGroupSidebarWidth = qMax(state->groupSidebarWidth, groupWidth + state->leftMargin + state->rightMargin);
|
||||
mTargetSidebarWidth = qMax(state->targetSidebarWidth, targetWidth + state->leftMargin + state->rightMargin);
|
||||
mSidebarWidthDirty = false;
|
||||
}
|
||||
void updateContentWidth(const QFontMetrics& fm, PerformanceShowerRenderState* state) {
|
||||
int maxWidth = 0;
|
||||
for(const QString& line : mAllInfo) maxWidth = qMax(maxWidth, fm.horizontalAdvance(line));
|
||||
int width = maxWidth + s->leftMargin + s->rightMargin;
|
||||
int height = fm.lineSpacing() * mAllInfo.size() + s->topMargin + s->bottomMargin;
|
||||
QRect rect(0, 0, width, height);
|
||||
painter->fillRect(rect, s->background);
|
||||
painter->setPen(s->foreground);
|
||||
for(int i = 0; i < mAllInfo.size(); ++i) {
|
||||
painter->drawText(QPointF(s->leftMargin, s->topMargin + i * fm.lineSpacing() + fm.lineSpacing()), mAllInfo[i]);
|
||||
}
|
||||
painter->restore();
|
||||
mContentWidth = maxWidth + state->leftMargin + state->rightMargin;
|
||||
mContentWidthDirty = false;
|
||||
}
|
||||
void applyEvent(const PerformanceEvent& event) {
|
||||
switch(event.type) {
|
||||
case PerformanceEventType::Clear:
|
||||
mInfoMap.clear();
|
||||
mCounter.clear();
|
||||
mInfos.clear();
|
||||
mScopes.clear();
|
||||
mAllInfo.clear();
|
||||
mContentWidth = 0;
|
||||
mContentWidthDirty = true;
|
||||
break;
|
||||
case PerformanceEventType::Duration: {
|
||||
PerformanceMetric& metric = mScopes[event.scope][event.key];
|
||||
metric.type = PerformanceMetricType::Duration;
|
||||
metric.value.update(event.number);
|
||||
++metric.count;
|
||||
break;
|
||||
}
|
||||
case PerformanceEventType::Counter:
|
||||
if(!mCounter.contains(event.key)) mCounter[event.key].mName = event.key;
|
||||
mCounter[event.key].mStatistics.update(1);
|
||||
mScopes[event.scope][event.key].type = PerformanceMetricType::Counter;
|
||||
mScopes[event.scope][event.key].speed.update(1);
|
||||
++mScopes[event.scope][event.key].count;
|
||||
break;
|
||||
case PerformanceEventType::Line:
|
||||
mInfoMap[event.key] = event.line;
|
||||
mScopes[event.scope][event.key].type = PerformanceMetricType::Line;
|
||||
mScopes[event.scope][event.key].line = event.line;
|
||||
break;
|
||||
case PerformanceEventType::Info:
|
||||
mInfos[event.key] = event.value;
|
||||
case PerformanceEventType::Gauge:
|
||||
mScopes[event.scope][event.key].type = PerformanceMetricType::Gauge;
|
||||
mScopes[event.scope][event.key].gauge = event.number;
|
||||
++mScopes[event.scope][event.key].count;
|
||||
break;
|
||||
case PerformanceEventType::Cost:
|
||||
if(!mInfoMap.contains(event.key)) mInfoMap[event.key] = PerformanceLine(event.key);
|
||||
mInfoMap[event.key].update(event.number);
|
||||
case PerformanceEventType::State:
|
||||
mScopes[event.scope][event.key].type = PerformanceMetricType::State;
|
||||
mScopes[event.scope][event.key].text = event.value;
|
||||
break;
|
||||
case PerformanceEventType::SelectView:
|
||||
mSelectedGroup = event.scope.isEmpty() ? "Plot" : event.scope;
|
||||
mSelectedTarget = event.key;
|
||||
mScrollLine = 0;
|
||||
mContentWidth = 0;
|
||||
mContentWidthDirty = true;
|
||||
break;
|
||||
case PerformanceEventType::ScrollView:
|
||||
mScrollLine += static_cast<int>(event.number);
|
||||
mScrollLine = qBound(0, mScrollLine, mMaxScrollLine);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void rebuildLines() {
|
||||
QVector<QString> lines;
|
||||
for(PerformanceLine& line : mInfoMap) lines.append(line.toString());
|
||||
for(CounterLine& counter : mCounter) lines.append(counter.toString());
|
||||
for(auto it = mInfos.cbegin(); it != mInfos.cend(); ++it) {
|
||||
lines.append(QString("%1: %2").arg(it.key(), it.value()));
|
||||
static QString objectNameFromMetric(const QString& key) {
|
||||
int stateIndex = key.indexOf(".State.");
|
||||
if(stateIndex > 0) return key.left(stateIndex);
|
||||
int inputIndex = key.indexOf(".Input.");
|
||||
if(inputIndex > 0) return key.left(inputIndex);
|
||||
int prepareIndex = key.indexOf(".prepareData");
|
||||
if(prepareIndex > 0) return key.left(prepareIndex);
|
||||
int drawIndex = key.indexOf(".draw");
|
||||
if(drawIndex > 0) return key.left(drawIndex);
|
||||
int leaseIndex = key.indexOf(".stateLeaseMiss");
|
||||
if(leaseIndex > 0) return key.left(leaseIndex);
|
||||
int modeIndex = key.indexOf(".bufferAcquireMode");
|
||||
if(modeIndex > 0) return key.left(modeIndex);
|
||||
return {};
|
||||
}
|
||||
void rebuildViews() {
|
||||
QSet<QString> objectItems;
|
||||
QVector<QString> oldGroupItems = mGroupItems;
|
||||
QVector<QString> oldTargetItems = mTargetItems;
|
||||
QString oldSelectedTarget = mSelectedTarget;
|
||||
auto renderAbleScope = mScopes.constFind("RenderAble");
|
||||
if(renderAbleScope != mScopes.cend()) {
|
||||
for(auto metricIt = renderAbleScope.value().cbegin(); metricIt != renderAbleScope.value().cend(); ++metricIt) {
|
||||
QString objectName = objectNameFromMetric(metricIt.key());
|
||||
if(!objectName.isEmpty()) objectItems.insert(objectName);
|
||||
}
|
||||
}
|
||||
auto bufferScope = mScopes.constFind("Buffer");
|
||||
if(bufferScope != mScopes.cend()) {
|
||||
for(auto metricIt = bufferScope.value().cbegin(); metricIt != bufferScope.value().cend(); ++metricIt) {
|
||||
if(!metricIt.key().startsWith("Color.")) {
|
||||
QString objectName = objectNameFromMetric(metricIt.key());
|
||||
if(!objectName.isEmpty()) objectItems.insert(objectName);
|
||||
}
|
||||
}
|
||||
}
|
||||
mGroupItems.clear();
|
||||
mGroupItems.append("Plot");
|
||||
QVector<QString> objectNames = objectItems.values().toVector();
|
||||
std::sort(objectNames.begin(), objectNames.end());
|
||||
for(const QString& item : objectNames) mGroupItems.append(item);
|
||||
if(!mGroupItems.contains(mSelectedGroup)) mSelectedGroup = "Plot";
|
||||
if(mSelectedGroup == "Plot") {
|
||||
mTargetItems.clear();
|
||||
if(mScopes.contains("Pipeline")) mTargetItems.append("Pipeline");
|
||||
if(mScopes.contains("General")) mTargetItems.append("General");
|
||||
if(hasColorBufferMetrics()) mTargetItems.append("Color");
|
||||
} else {
|
||||
mTargetItems.clear();
|
||||
if(hasRenderAbleMetrics(mSelectedGroup)) mTargetItems.append("RenderAble");
|
||||
if(hasObjectBufferMetrics(mSelectedGroup, "State")) mTargetItems.append("State");
|
||||
if(hasObjectBufferMetrics(mSelectedGroup, "Input")) mTargetItems.append("Input");
|
||||
}
|
||||
if(mTargetItems.isEmpty()) mTargetItems.append("None");
|
||||
if(mSelectedTarget.isEmpty() || !mTargetItems.contains(mSelectedTarget)) mSelectedTarget = mTargetItems.front();
|
||||
if(oldSelectedTarget != mSelectedTarget) {
|
||||
mScrollLine = 0;
|
||||
mContentWidth = 0;
|
||||
mContentWidthDirty = true;
|
||||
}
|
||||
if(oldGroupItems != mGroupItems || oldTargetItems != mTargetItems) mSidebarWidthDirty = true;
|
||||
}
|
||||
bool hasColorBufferMetrics() const {
|
||||
auto bufferScope = mScopes.constFind("Buffer");
|
||||
if(bufferScope == mScopes.cend()) return false;
|
||||
for(auto metricIt = bufferScope.value().cbegin(); metricIt != bufferScope.value().cend(); ++metricIt) {
|
||||
if(metricIt.key().startsWith("Color.")) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool hasRenderAbleMetrics(const QString& objectName) const {
|
||||
auto renderAbleScope = mScopes.constFind("RenderAble");
|
||||
if(renderAbleScope == mScopes.cend()) return false;
|
||||
for(auto metricIt = renderAbleScope.value().cbegin(); metricIt != renderAbleScope.value().cend(); ++metricIt) {
|
||||
if(objectNameFromMetric(metricIt.key()) == objectName) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool hasObjectBufferMetrics(const QString& objectName, const QString& bufferName) const {
|
||||
auto bufferScope = mScopes.constFind("Buffer");
|
||||
if(bufferScope == mScopes.cend()) return false;
|
||||
QString prefix = objectName + "." + bufferName + ".";
|
||||
for(auto metricIt = bufferScope.value().cbegin(); metricIt != bufferScope.value().cend(); ++metricIt) {
|
||||
if(metricIt.key().startsWith(prefix)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool metricVisible(const QString& scope, const QString& key) const {
|
||||
if(mSelectedGroup == "Plot") {
|
||||
if(mSelectedTarget == "Pipeline") return scope == "Pipeline";
|
||||
if(mSelectedTarget == "General") return scope == "General";
|
||||
return mSelectedTarget == "Color" && scope == "Buffer" && key.startsWith("Color.");
|
||||
}
|
||||
if(mSelectedTarget == "RenderAble") return scope == "RenderAble" && objectNameFromMetric(key) == mSelectedGroup;
|
||||
return scope == "Buffer" && key.startsWith(mSelectedGroup + "." + mSelectedTarget + ".");
|
||||
}
|
||||
QString displayMetricName(const QString& key) const {
|
||||
if(mSelectedGroup == "Plot" && mSelectedTarget == "Color") return key.mid(QString("Color.").size());
|
||||
if(mSelectedGroup != "Plot" && mSelectedTarget == "RenderAble") return key.mid(mSelectedGroup.size() + 1);
|
||||
if(mSelectedGroup != "Plot") return key.mid((mSelectedGroup + "." + mSelectedTarget + ".").size());
|
||||
return key;
|
||||
}
|
||||
void rebuildLines() {
|
||||
rebuildViews();
|
||||
QVector<QString> lines;
|
||||
for(auto scopeIt = mScopes.begin(); scopeIt != mScopes.end(); ++scopeIt) {
|
||||
for(auto metricIt = scopeIt.value().begin(); metricIt != scopeIt.value().end(); ++metricIt) {
|
||||
if(metricVisible(scopeIt.key(), metricIt.key())) {
|
||||
lines.append(metricIt.value().toString(displayMetricName(metricIt.key())));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(mAllInfo != lines) {
|
||||
mAllInfo = lines;
|
||||
mContentWidthDirty = true;
|
||||
}
|
||||
mAllInfo = lines;
|
||||
}
|
||||
};
|
||||
class Cacl {
|
||||
public:
|
||||
std::chrono::system_clock::time_point mStart;
|
||||
std::chrono::steady_clock::time_point mStart;
|
||||
QString mScope;
|
||||
QString mName;
|
||||
PerformanceShower *mPerformanceShower{};
|
||||
Cacl(const QString& name, PerformanceShower* performanceShower) {
|
||||
Cacl(const QString& name, PerformanceShower* performanceShower) : Cacl("General", name, performanceShower) {
|
||||
}
|
||||
Cacl(const QString& scope, const QString& name, PerformanceShower* performanceShower) {
|
||||
if(!performanceShower || !performanceShower->enabled()) return;
|
||||
mStart = std::chrono::system_clock::now();
|
||||
mStart = std::chrono::steady_clock::now();
|
||||
mScope = scope;
|
||||
mName = name;
|
||||
mPerformanceShower = performanceShower;
|
||||
}
|
||||
~Cacl() {
|
||||
if(!mPerformanceShower || !mPerformanceShower->enabled()) return;
|
||||
double useTime = std::chrono::duration<double>(std::chrono::system_clock::now() - mStart).count() * 1000;
|
||||
mPerformanceShower->recordCost(mName, useTime);
|
||||
double useTime = std::chrono::duration<double>(std::chrono::steady_clock::now() - mStart).count() * 1000;
|
||||
mPerformanceShower->recordDuration(mScope, mName, useTime);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+204
-18
@@ -5,6 +5,63 @@
|
||||
#include <chrono>
|
||||
|
||||
namespace YSG {
|
||||
static std::uint64_t steadyNowNs() {
|
||||
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||
}
|
||||
static double elapsedMs(std::uint64_t startNs, std::uint64_t endNs) {
|
||||
return static_cast<double>(endNs - startNs) / 1000000.0;
|
||||
}
|
||||
static QString jobStateText(RenderAble::JobState state) {
|
||||
switch(state) {
|
||||
case RenderAble::JobState::Idle:
|
||||
return "Idle";
|
||||
case RenderAble::JobState::Queued:
|
||||
return "Queued";
|
||||
case RenderAble::JobState::Running:
|
||||
return "Running";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
static QString renderAbleName(RenderAble* able) {
|
||||
if(!able->mObjectName.isEmpty()) return able->mObjectName;
|
||||
return QString::number(reinterpret_cast<std::uintptr_t>(able), 16);
|
||||
}
|
||||
static QString acquireModeText(Plot_Buffer_Acquire_Mode mode) {
|
||||
return mode == Plot_Buffer_Acquire_Mode::Wait ? "Wait" : "Try";
|
||||
}
|
||||
static void publishTripleBufferStats(PerformanceShower* shower, const QString& name, const Triple_Buffer_Stats& stats) {
|
||||
if(!shower || !shower->enabled()) return;
|
||||
shower->setGauge("Buffer", name + ".markSuccess", static_cast<double>(stats.mark_success.load(std::memory_order_relaxed)));
|
||||
shower->setGauge("Buffer", name + ".markBusy", static_cast<double>(stats.mark_busy.load(std::memory_order_relaxed)));
|
||||
shower->setGauge("Buffer", name + ".markStateChanged", static_cast<double>(stats.mark_state_changed.load(std::memory_order_relaxed)));
|
||||
shower->setGauge("Buffer", name + ".swapSuccess", static_cast<double>(stats.swap_success.load(std::memory_order_relaxed)));
|
||||
shower->setGauge("Buffer", name + ".swapBusy", static_cast<double>(stats.swap_busy.load(std::memory_order_relaxed)));
|
||||
shower->setGauge("Buffer", name + ".swapStateChanged", static_cast<double>(stats.swap_state_changed.load(std::memory_order_relaxed)));
|
||||
shower->setGauge("Buffer", name + ".swapNoNeed", static_cast<double>(stats.swap_condition_failed.load(std::memory_order_relaxed)));
|
||||
shower->setGauge("Buffer", name + ".wait", static_cast<double>(stats.wait_count.load(std::memory_order_relaxed)));
|
||||
}
|
||||
static void publishRenderAbleBufferStats(PerformanceShower* shower, RenderAble* able) {
|
||||
if(!shower || !shower->enabled()) return;
|
||||
if(able == shower) return;
|
||||
QString name = renderAbleName(able);
|
||||
shower->setState("RenderAble", name + ".bufferAcquireMode", acquireModeText(able->bufferAcquireMode()));
|
||||
publishTripleBufferStats(shower, name + ".State", able->dPtr->mStateBufferStats);
|
||||
publishTripleBufferStats(shower, name + ".Input", able->dPtr->mInputBufferStats);
|
||||
}
|
||||
static void publishPipelineStats(PlotPrivate* data) {
|
||||
PerformanceShower* shower = data->mShower;
|
||||
if(!shower || !shower->enabled()) return;
|
||||
RenderAble::RenderPipeline& pipeline = data->mPipeline;
|
||||
shower->setState("Pipeline", "jobState", jobStateText(pipeline.jobState));
|
||||
shower->setState("Pipeline", "paintBufferAcquireMode", acquireModeText(data->mPaintBufferAcquireMode.load(std::memory_order_acquire)));
|
||||
shower->setState("Pipeline", "renderAbleDefaultAcquireMode", acquireModeText(data->mRenderAbleBufferAcquireMode.load(std::memory_order_acquire)));
|
||||
shower->setGauge("Pipeline", "activeRenderTasks", data->mActiveRenderTasks.load(std::memory_order_acquire));
|
||||
shower->setGauge("Pipeline", "renderEnabled", data->mRenderEnabled.load(std::memory_order_acquire) ? 1.0 : 0.0);
|
||||
shower->setGauge("Pipeline", "paintRequestPending", pipeline.paintRequestPending.load(std::memory_order_acquire) ? 1.0 : 0.0);
|
||||
shower->setGauge("Pipeline", "editStateVersion", static_cast<double>(pipeline.editStateVersion.load(std::memory_order_acquire)));
|
||||
shower->setGauge("Pipeline", "renderStateVersion", static_cast<double>(pipeline.renderStateVersion));
|
||||
publishTripleBufferStats(shower, "Color", pipeline.colorBufferStats);
|
||||
}
|
||||
static void notifyRenderTaskDone(PlotPrivate* data) {
|
||||
if(data->mActiveRenderTasks.fetch_sub(1, std::memory_order_acq_rel) == 1) data->mRenderTaskDone.notify_all();
|
||||
}
|
||||
@@ -92,15 +149,36 @@ namespace YSG {
|
||||
|
||||
void Plot::paintEvent(QPaintEvent* event) {
|
||||
Q_UNUSED(event)
|
||||
Cacl paintData("Pipeline", "paintEvent", d->mShower);
|
||||
std::uint64_t paintNs = steadyNowNs();
|
||||
std::uint64_t updateRequestNs = d->mLastUpdateRequestNs.exchange(0, std::memory_order_acq_rel);
|
||||
if(updateRequestNs && d->mShower) d->mShower->recordDuration("Pipeline", "updateToPaint", elapsedMs(updateRequestNs, paintNs));
|
||||
QPainter painter(this);
|
||||
auto mode = d->mPaintBufferAcquireMode.load(std::memory_order_acquire) == Plot_Buffer_Acquire_Mode::Wait ? Triple_Buffer_Mode::Wait : Triple_Buffer_Mode::Try;
|
||||
auto colorLease = d->mPipeline.consumeReadyColor(mode);
|
||||
auto mode = to_triple_buffer_mode(d->mPaintBufferAcquireMode.load(std::memory_order_acquire));
|
||||
RenderAble::ColorBufferLease colorLease;
|
||||
{
|
||||
Cacl consumeData("Buffer", "Color.consumeReady", d->mShower);
|
||||
colorLease = d->mPipeline.consumeReadyColor(mode);
|
||||
}
|
||||
RenderAble::ColorBuffer* color = colorLease.buffer;
|
||||
if(color && !color->image.isNull()) painter.drawImage(QPoint(0, 0), color->image);
|
||||
else painter.fillRect(rect(), d->mBackground);
|
||||
if(colorLease) d->mPipeline.unmarkColor(colorLease);
|
||||
for(Layer* layer : mLayerList) {
|
||||
for(RenderAble* able : layer->mRenderAbles) {
|
||||
if(!able->mVisable || !able->dPtr->independentPixelCache()) continue;
|
||||
auto pixelLease = able->dPtr->consumeReadyPixel(Triple_Buffer_Mode::Try);
|
||||
if(pixelLease && !pixelLease.buffer->image.isNull()) painter.drawImage(QPoint(0, 0), pixelLease.buffer->image);
|
||||
if(pixelLease) able->dPtr->unmarkPixel(pixelLease);
|
||||
}
|
||||
}
|
||||
painter.end();
|
||||
if(d->mShower) d->mShower->counterIncrease("plot paint");
|
||||
if(d->mShower) {
|
||||
if(colorLease) d->mShower->incrementCounter("Buffer", "Color.frontLeaseSuccess");
|
||||
else d->mShower->incrementCounter("Buffer", "Color.frontLeaseMiss");
|
||||
d->mShower->incrementCounter("Pipeline", "paint");
|
||||
publishPipelineStats(d);
|
||||
}
|
||||
}
|
||||
void Plot::render() {
|
||||
submitRender();
|
||||
@@ -111,18 +189,29 @@ namespace YSG {
|
||||
void Plot::setPaintBufferAcquireMode(Plot_Buffer_Acquire_Mode mode) {
|
||||
d->mPaintBufferAcquireMode.store(mode, std::memory_order_release);
|
||||
}
|
||||
Plot_Buffer_Acquire_Mode Plot::renderAbleBufferAcquireMode() const {
|
||||
return d->mRenderAbleBufferAcquireMode.load(std::memory_order_acquire);
|
||||
}
|
||||
void Plot::setRenderAbleBufferAcquireMode(Plot_Buffer_Acquire_Mode mode) {
|
||||
d->mRenderAbleBufferAcquireMode.store(mode, std::memory_order_release);
|
||||
}
|
||||
void Plot::submitRender() {
|
||||
RenderScheduler& scheduler = Global::instance()->renderScheduler();
|
||||
if(!scheduler.isSchedulerThread()) {
|
||||
PlotPrivate* privateData = d;
|
||||
std::uint64_t postNs = steadyNowNs();
|
||||
privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel);
|
||||
scheduler.post([this, privateData]() {
|
||||
scheduler.post([this, privateData, postNs]() {
|
||||
if(privateData->mShower) privateData->mShower->recordDuration("Pipeline", "schedulerQueueWait", elapsedMs(postNs, steadyNowNs()));
|
||||
if(!privateData->mDestroying.load(std::memory_order_acquire)) submitRender();
|
||||
notifyRenderTaskDone(privateData);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if(d->mDestroying.load(std::memory_order_acquire)) return;
|
||||
std::uint64_t submitNs = steadyNowNs();
|
||||
std::uint64_t requestNs = d->mLastRequestNs.exchange(0, std::memory_order_acq_rel);
|
||||
if(requestNs && d->mShower) d->mShower->recordDuration("Pipeline", "requestToSubmit", elapsedMs(requestNs, submitNs));
|
||||
if(mFirstResize) return;
|
||||
if(!d->mRenderEnabled.load(std::memory_order_acquire)) return;
|
||||
QSize renderSize = d->mRenderSize;
|
||||
@@ -133,24 +222,32 @@ namespace YSG {
|
||||
pipeline.jobState = RenderAble::JobState::Queued;
|
||||
pipeline.renderStateVersion = pipeline.editStateVersion.load(std::memory_order_acquire);
|
||||
if(d->mShower) {
|
||||
d->mShower->counterIncrease("渲染帧率");
|
||||
d->mShower->incrementCounter("Pipeline", "renderFrame");
|
||||
d->mShower->setInfo("interval", QString("渲染:%1, global:%2").arg(qRound(1000.0 / (double)d->mRefreshTimesPreSecond)).arg(Global::instance()->mInterval));
|
||||
publishPipelineStats(d);
|
||||
}
|
||||
{
|
||||
Cacl prepareData("准备数据耗时:%1 SRTT:%2 RTTVAR:%3", d->mShower);
|
||||
Cacl prepareData("Pipeline", "prepareData", d->mShower);
|
||||
for(Layer* layer : mLayerList) {
|
||||
for(RenderAble *cur : layer->mRenderAbles) {
|
||||
//qDebug() << "info: "<< layer->mLayerName << " " << cur->mObjectName;
|
||||
for(auto rely : cur->mBeRelyList) {
|
||||
if(rely->dPtr->independentPixelCache()) continue;
|
||||
if(!rely->mPrepared){
|
||||
// qDebug() << cur->mObjectName << "rely: " << mObjectName;
|
||||
rely->prepareData();
|
||||
{
|
||||
Cacl renderAblePrepare("RenderAble", renderAbleName(rely) + ".prepareData", d->mShower);
|
||||
rely->prepareData();
|
||||
}
|
||||
publishRenderAbleBufferStats(d->mShower, rely);
|
||||
rely->mPrepared = true;
|
||||
}
|
||||
}
|
||||
if(cur->dPtr->independentPixelCache()) continue;
|
||||
if(!cur->mPrepared){
|
||||
cur->prepareData();
|
||||
//qDebug() << cur->mObjectName << " prepareData";
|
||||
{
|
||||
Cacl renderAblePrepare("RenderAble", renderAbleName(cur) + ".prepareData", d->mShower);
|
||||
cur->prepareData();
|
||||
}
|
||||
publishRenderAbleBufferStats(d->mShower, cur);
|
||||
cur->mPrepared = true;
|
||||
}
|
||||
}
|
||||
@@ -162,14 +259,21 @@ namespace YSG {
|
||||
able->mPrepared = false;
|
||||
}
|
||||
}
|
||||
auto colorLease = pipeline.waitRenderColor();
|
||||
RenderAble::ColorBufferLease colorLease;
|
||||
{
|
||||
Cacl waitRenderColor("Buffer", "Color.waitRenderLease", d->mShower);
|
||||
colorLease = pipeline.waitRenderColor();
|
||||
}
|
||||
RenderAble::ColorBuffer* color = colorLease.buffer;
|
||||
QColor background = d->mBackground;
|
||||
std::uint64_t version = pipeline.renderStateVersion;
|
||||
pipeline.jobState = RenderAble::JobState::Running;
|
||||
if(d->mShower) publishPipelineStats(d);
|
||||
PlotPrivate* privateData = d;
|
||||
std::uint64_t cpuPostNs = steadyNowNs();
|
||||
privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel);
|
||||
scheduler.postCpu([this, privateData, colorLease, color, renderSize, background, version]() {
|
||||
scheduler.postCpu([this, privateData, colorLease, color, renderSize, background, version, cpuPostNs]() {
|
||||
if(privateData->mShower) privateData->mShower->recordDuration("Pipeline", "cpuQueueWait", elapsedMs(cpuPostNs, steadyNowNs()));
|
||||
if(privateData->mDestroying.load(std::memory_order_acquire)) {
|
||||
privateData->mPipeline.unmarkColor(colorLease);
|
||||
notifyRenderTaskDone(privateData);
|
||||
@@ -190,13 +294,18 @@ namespace YSG {
|
||||
void Plot::finishRender() {
|
||||
if(d->mDestroying.load(std::memory_order_acquire)) return;
|
||||
RenderAble::RenderPipeline& pipeline = d->mPipeline;
|
||||
QRegion updateRegion = pipeline.publishRenderColor();
|
||||
QRegion updateRegion;
|
||||
{
|
||||
Cacl publishData("Pipeline", "publishRenderColor", d->mShower);
|
||||
updateRegion = pipeline.publishRenderColor();
|
||||
}
|
||||
pipeline.jobState = RenderAble::JobState::Idle;
|
||||
if(d->mShower) publishPipelineStats(d);
|
||||
if(!updateRegion.isEmpty()) requestUpdate(updateRegion);
|
||||
if(pipeline.hasNewState()) submitRender();
|
||||
}
|
||||
void Plot::renderColor(RenderAble::ColorBuffer* color, QSize size, QColor background, std::uint64_t version) {
|
||||
Cacl renderData("整体渲染耗时:%1 SRTT:%2 RTTVAR:%3", d->mShower);
|
||||
Cacl renderData("Pipeline", "renderColor", d->mShower);
|
||||
if(color->image.size() != size || color->image.format() != QImage::Format_ARGB32) {
|
||||
color->image = QImage(size, QImage::Format_ARGB32);
|
||||
}
|
||||
@@ -206,17 +315,21 @@ namespace YSG {
|
||||
QVector<Triple_Buffer_Lease> stateLeases;
|
||||
QPainter painter(&color->image);
|
||||
{
|
||||
Cacl drawData("draw耗时:%1 SRTT:%2 RTTVAR:%3", d->mShower);
|
||||
Cacl drawData("Pipeline", "draw", d->mShower);
|
||||
for(Layer* layer : mLayerList) {
|
||||
for(RenderAble* cur : layer->mRenderAbles) {
|
||||
if(!cur->mVisable) continue;
|
||||
if(!cur->mVisable || cur->dPtr->independentPixelCache()) continue;
|
||||
auto lease = cur->dPtr->markStateRole(State_Render);
|
||||
if(!lease) continue;
|
||||
if(!lease) {
|
||||
if(d->mShower) d->mShower->incrementCounter("RenderAble", renderAbleName(cur) + ".stateLeaseMiss");
|
||||
continue;
|
||||
}
|
||||
leasedAbles.append(cur);
|
||||
stateLeases.append(lease);
|
||||
}
|
||||
}
|
||||
for(RenderAble* able : leasedAbles) {
|
||||
Cacl renderAbleDraw("RenderAble", renderAbleName(able) + ".draw", d->mShower);
|
||||
able->draw(&painter);
|
||||
}
|
||||
}
|
||||
@@ -227,6 +340,76 @@ namespace YSG {
|
||||
color->version.store(version, std::memory_order_release);
|
||||
color->dirtyRegion = QRegion(QRect(QPoint(0, 0), size));
|
||||
}
|
||||
void Plot::markIndependentRenderDirty(RenderAble* able) {
|
||||
if(d->mDestroying.load(std::memory_order_acquire)) return;
|
||||
PlotPrivate* privateData = d;
|
||||
privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel);
|
||||
Global::instance()->renderScheduler().post([this, privateData, able]() {
|
||||
if(!privateData->mDestroying.load(std::memory_order_acquire)) submitIndependentRender(able);
|
||||
notifyRenderTaskDone(privateData);
|
||||
});
|
||||
}
|
||||
void Plot::submitIndependentRender(RenderAble* able) {
|
||||
RenderScheduler& scheduler = Global::instance()->renderScheduler();
|
||||
if(!scheduler.isSchedulerThread()) {
|
||||
markIndependentRenderDirty(able);
|
||||
return;
|
||||
}
|
||||
if(d->mDestroying.load(std::memory_order_acquire)) return;
|
||||
if(!able || !able->mVisable || !able->dPtr->independentPixelCache()) return;
|
||||
if(able->dPtr->mIndependentRenderQueued.exchange(true, std::memory_order_acq_rel)) return;
|
||||
QSize renderSize = d->mRenderSize;
|
||||
if(renderSize.width() <= 0 || renderSize.height() <= 0) {
|
||||
able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
if(!able->dPtr->hasNewPixelState()) {
|
||||
able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release);
|
||||
return;
|
||||
}
|
||||
able->prepareData();
|
||||
auto pixelLease = able->dPtr->waitRenderPixel();
|
||||
PixelBufferSlot* pixel = pixelLease.buffer;
|
||||
std::uint64_t version = able->dPtr->mPixelEditVersion.load(std::memory_order_acquire);
|
||||
PlotPrivate* privateData = d;
|
||||
privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel);
|
||||
scheduler.postCpu([this, privateData, able, pixelLease, pixel, renderSize, version]() {
|
||||
if(privateData->mDestroying.load(std::memory_order_acquire)) {
|
||||
able->dPtr->unmarkPixel(pixelLease);
|
||||
notifyRenderTaskDone(privateData);
|
||||
return;
|
||||
}
|
||||
renderIndependentColor(able, pixel, renderSize, version);
|
||||
able->dPtr->unmarkPixel(pixelLease);
|
||||
Global::instance()->renderScheduler().post([this, privateData, able]() {
|
||||
if(!privateData->mDestroying.load(std::memory_order_acquire)) finishIndependentRender(able);
|
||||
notifyRenderTaskDone(privateData);
|
||||
});
|
||||
});
|
||||
}
|
||||
void Plot::renderIndependentColor(RenderAble* able, PixelBufferSlot* pixel, QSize size, std::uint64_t version) {
|
||||
if(pixel->image.size() != size || pixel->image.format() != QImage::Format_ARGB32) {
|
||||
pixel->image = QImage(size, QImage::Format_ARGB32);
|
||||
}
|
||||
pixel->image.detach();
|
||||
pixel->image.fill(Qt::transparent);
|
||||
QPainter painter(&pixel->image);
|
||||
auto lease = able->dPtr->markStateRole(State_Render);
|
||||
if(lease) {
|
||||
able->draw(&painter);
|
||||
able->dPtr->unmarkState(lease);
|
||||
}
|
||||
painter.end();
|
||||
pixel->version.store(version, std::memory_order_release);
|
||||
pixel->dirtyRegion = QRegion(QRect(QPoint(0, 0), size));
|
||||
able->dPtr->mPixelRenderVersion = version;
|
||||
}
|
||||
void Plot::finishIndependentRender(RenderAble* able) {
|
||||
QRegion updateRegion = able->dPtr->publishRenderPixel();
|
||||
able->dPtr->mIndependentRenderQueued.store(false, std::memory_order_release);
|
||||
if(!updateRegion.isEmpty()) requestUpdate(updateRegion);
|
||||
if(able->dPtr->hasNewPixelState()) submitIndependentRender(able);
|
||||
}
|
||||
void Plot::scheduleRenderTimer() {
|
||||
if(d->mDestroying.load(std::memory_order_acquire) || !d->mRenderTimer || !d->mRenderEnabled.load(std::memory_order_acquire)) return;
|
||||
int interval = std::max(1, qRound(1000.0 / (double)d->mRefreshTimesPreSecond));
|
||||
@@ -235,6 +418,7 @@ namespace YSG {
|
||||
privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel);
|
||||
d->mRenderTimer->async_wait([this, privateData](const asio::error_code& error) {
|
||||
if(!error && !d->mDestroying.load(std::memory_order_acquire)) {
|
||||
if(d->mShower) d->mShower->incrementCounter("Pipeline", "timerTick");
|
||||
submitRender();
|
||||
scheduleRenderTimer();
|
||||
}
|
||||
@@ -248,6 +432,7 @@ namespace YSG {
|
||||
}
|
||||
void Plot::requestRender() {
|
||||
if(d->mDestroying.load(std::memory_order_acquire)) return;
|
||||
d->mLastRequestNs.store(steadyNowNs(), std::memory_order_release);
|
||||
PlotPrivate* privateData = d;
|
||||
privateData->mActiveRenderTasks.fetch_add(1, std::memory_order_acq_rel);
|
||||
Global::instance()->renderScheduler().post([this, privateData]() {
|
||||
@@ -256,6 +441,7 @@ namespace YSG {
|
||||
});
|
||||
}
|
||||
void Plot::requestUpdate(const QRegion& region) {
|
||||
d->mLastUpdateRequestNs.store(steadyNowNs(), std::memory_order_release);
|
||||
QTimer::singleShot(0, this, [this, region]() {
|
||||
if(d->mDestroying.load(std::memory_order_acquire)) return;
|
||||
if(region.isEmpty()) update();
|
||||
|
||||
@@ -7,10 +7,6 @@ namespace YSG {
|
||||
class RenderScheduler;
|
||||
struct PlotPrivate;
|
||||
class PerformanceShower;
|
||||
enum class Plot_Buffer_Acquire_Mode : std::uint8_t {
|
||||
Try,
|
||||
Wait
|
||||
};
|
||||
class LIB_DECL Plot : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -26,6 +22,8 @@ namespace YSG {
|
||||
void setBackgroundColor(const QColor& color) const;
|
||||
Plot_Buffer_Acquire_Mode paintBufferAcquireMode() const;
|
||||
void setPaintBufferAcquireMode(Plot_Buffer_Acquire_Mode mode);
|
||||
Plot_Buffer_Acquire_Mode renderAbleBufferAcquireMode() const;
|
||||
void setRenderAbleBufferAcquireMode(Plot_Buffer_Acquire_Mode mode);
|
||||
void markRenderStateDirty();
|
||||
void requestRender();
|
||||
struct Layer {
|
||||
@@ -64,6 +62,10 @@ namespace YSG {
|
||||
void finishRender();
|
||||
void requestUpdate(const QRegion& region);
|
||||
void renderColor(RenderAble::ColorBuffer* color, QSize size, QColor background, std::uint64_t version);
|
||||
void markIndependentRenderDirty(RenderAble* able);
|
||||
void submitIndependentRender(RenderAble* able);
|
||||
void renderIndependentColor(RenderAble* able, PixelBufferSlot* pixel, QSize size, std::uint64_t version);
|
||||
void finishIndependentRender(RenderAble* able);
|
||||
friend class RenderScheduler;
|
||||
friend struct RenderAble;
|
||||
};
|
||||
|
||||
@@ -20,9 +20,12 @@ namespace YSG {
|
||||
std::mutex mRenderTaskMutex;
|
||||
std::atomic_int mPendingRenderWidth{0};
|
||||
std::atomic_int mPendingRenderHeight{0};
|
||||
std::atomic<std::uint64_t> mLastRequestNs{0};
|
||||
std::atomic<std::uint64_t> mLastUpdateRequestNs{0};
|
||||
QSize mRenderSize;
|
||||
RenderAble::RenderPipeline mPipeline;
|
||||
std::atomic<Plot_Buffer_Acquire_Mode> mPaintBufferAcquireMode{Plot_Buffer_Acquire_Mode::Try};
|
||||
std::atomic<Plot_Buffer_Acquire_Mode> mRenderAbleBufferAcquireMode{Plot_Buffer_Acquire_Mode::Wait};
|
||||
QColor mBackground = Qt::black;
|
||||
PerformanceShower *mShower{};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user