更新前

This commit is contained in:
2026-07-23 15:15:26 +08:00
parent 94bb3d4f89
commit 6569084179
7 changed files with 795 additions and 87 deletions
+19 -5
View File
@@ -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
View File
@@ -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;
+58 -7
View File
@@ -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();
}
}
+344 -46
View File
@@ -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
View File
@@ -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();
+6 -4
View File
@@ -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;
};
+3
View File
@@ -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{};
};