1+ #include " performance_counter.h"
2+
3+ #include < QQuickWindow>
4+
5+ #include < QQmlContext>
6+
7+ namespace project {
8+
9+ PerformanceCounter::PerformanceCounter (QObject* parent, QQuickWindow* window) : QObject(parent), m_window(window) {
10+
11+ }
12+
13+ void PerformanceCounter::ExportContextPropertiesToQml ( QQmlEngine* engine) {
14+ engine->rootContext ()->setContextProperty ( " performanceCounter" , this );
15+ }
16+
17+ void PerformanceCounter::ConnectToWindowIfNecessary () {
18+ if (m_connectedToWindow) {
19+ return ;
20+ }
21+ connect (m_window, &QQuickWindow::afterAnimating, this , &PerformanceCounter::AfterAnimating);
22+ connect (m_window, &QQuickWindow::beforeRendering, this , &PerformanceCounter::BeforeRendering);
23+ connect (m_window, &QQuickWindow::afterRendering, this , &PerformanceCounter::AfterRendering);
24+ }
25+
26+ void PerformanceCounter::StartReport (const QString& name) {
27+ for (auto & reportRequest : m_reports) {
28+ if (reportRequest.m_name .data () == nullptr ) {
29+ reportRequest = ReportRequest (name);
30+ return ;
31+ }
32+ }
33+ }
34+
35+ void PerformanceCounter::AfterAnimating () {
36+ for (auto & reportRequest : m_reports) {
37+ reportRequest.Synchronize ();
38+ if (reportRequest.m_report .has_value ()) {
39+ reportRequest.m_report ->GetFrameReportToWriteInto ().m_afterAnimating = static_cast <qint32>(reportRequest.m_timer .elapsed ());
40+ }
41+ }
42+ }
43+
44+ void PerformanceCounter::BeforeRendering () {
45+ for (auto & reportRequest : m_reports) {
46+ if (reportRequest.m_report .has_value ()) {
47+ reportRequest.m_report ->GetFrameReportToWriteInto ().m_beforeRendering = static_cast <qint32>(reportRequest.m_timer .elapsed ());
48+ }
49+ }
50+ }
51+
52+ void PerformanceCounter::AfterRendering () {
53+ for (auto & reportRequest : m_reports) {
54+ if (reportRequest.m_report .has_value ()) {
55+ reportRequest.m_report ->GetFrameReportToWriteInto ().m_beforeRendering = static_cast <qint32>(reportRequest.m_timer .elapsed ());
56+ reportRequest.m_report ->FinishFrame ();
57+ if (reportRequest.m_requestedToStopRenderThread ) {
58+ reportRequest.Finalize ();
59+ }
60+ }
61+ }
62+ }
63+
64+ ReportRequest::ReportRequest () : m_name(), m_timer() {}
65+ ReportRequest::ReportRequest (QString name) : m_name(name), m_timer() {
66+ m_timer.start ();
67+ }
68+
69+ // Called automatically from the GUI thread while the render thread is waiting
70+ void ReportRequest::Synchronize () {
71+ if (m_finalized) {
72+ *this = ReportRequest ();
73+ } else {
74+ if ((m_name.data () != nullptr ) && !m_report.has_value ()) {
75+ m_report.emplace ();
76+ }
77+ m_requestedToStopRenderThread = m_requestedToStopGUIThread;
78+ }
79+ }
80+
81+ // Called by users from GUI thread
82+ void ReportRequest::RequestStop () {
83+ m_requestedToStopGUIThread = true ;
84+ }
85+
86+ // Called automatically from the render thread
87+ void ReportRequest::Finalize () {
88+ ExportReport ();
89+ m_finalized = true ;
90+ }
91+
92+ struct MinMax {
93+ qint32 m_min = std::numeric_limits < qint32 >::max();
94+ qint32 m_max = std::numeric_limits < qint32 >::min();
95+
96+ void Update (qint32 val) {
97+ m_min = std::min (m_min, val);
98+ m_max = std::max (m_max, val);
99+ }
100+ };
101+
102+ void ReportRequest::ExportReport () {
103+ auto log = qDebug ();
104+ log = log << " Exporting report for: " << m_name << Qt::endl;
105+ log = log << Qt::right << qSetFieldWidth (3 );
106+ qint32 previousGuiUpdate = 0 ;
107+ qint32 previousRenderFinish = 0 ;
108+
109+ MinMax guiFrameDurationExtents;
110+ MinMax renderFrameDurationExtents;
111+ MinMax synchronizingExtents;
112+ MinMax renderingExtents;
113+
114+ for (const FrameReport& frame : m_report->m_backloggedFrames ) {
115+ qint32 guiFrameDuration = frame.m_afterAnimating - previousGuiUpdate;
116+ previousGuiUpdate = frame.m_afterAnimating ;
117+ qint32 renderFrameDuration = frame.m_afterRendering - previousRenderFinish;
118+ previousRenderFinish = frame.m_afterRendering ;
119+ qint32 synchronizing = frame.m_beforeRendering - frame.m_afterAnimating ;
120+ qint32 rendering = frame.m_afterRendering - frame.m_beforeRendering ;
121+ log << synchronizing << " , " << rendering << " , " << guiFrameDuration << " , " << renderFrameDuration;
122+ guiFrameDurationExtents.Update (guiFrameDuration);
123+ renderFrameDurationExtents.Update (renderFrameDuration);
124+ synchronizingExtents.Update (synchronizing);
125+ renderingExtents.Update (rendering);
126+ }
127+ for (size_t i = 0 ; i < m_report->m_framesFilledCount ; i++) {
128+ const FrameReport& frame = m_report->m_frames [i];
129+ qint32 guiFrameDuration = frame.m_afterAnimating - previousGuiUpdate;
130+ previousGuiUpdate = frame.m_afterAnimating ;
131+ qint32 renderFrameDuration = frame.m_afterRendering - previousRenderFinish;
132+ previousRenderFinish = frame.m_afterRendering ;
133+ qint32 synchronizing = frame.m_beforeRendering - frame.m_afterAnimating ;
134+ qint32 rendering = frame.m_afterRendering - frame.m_beforeRendering ;
135+ log << synchronizing << " , " << rendering << " , " << guiFrameDuration << " , " << renderFrameDuration;
136+ guiFrameDurationExtents.Update (guiFrameDuration);
137+ renderFrameDurationExtents.Update (renderFrameDuration);
138+ synchronizingExtents.Update (synchronizing);
139+ renderingExtents.Update (rendering);
140+ }
141+
142+ log << " GUI Frame Duration Extents " << guiFrameDurationExtents.m_min << " , " << guiFrameDurationExtents.m_max ;
143+ log << " Render Frame Duration Extents " << renderFrameDurationExtents.m_min << " , " << renderFrameDurationExtents.m_max ;
144+ log << " Synchronizing Extents " << synchronizingExtents.m_min << " , " << synchronizingExtents.m_max ;
145+ log << " Rendering Extents " << renderingExtents.m_min << " , " << renderingExtents.m_max ;
146+ }
147+
148+ FrameReport& Report::GetFrameReportToWriteInto () {
149+ return m_frames[m_framesFilledCount];
150+ }
151+
152+ void Report::FinishFrame () {
153+ m_framesFilledCount++;
154+ if (m_framesFilledCount == m_frames.size ()) {
155+ size_t currentBacklogSize = m_backloggedFrames.size ();
156+ m_backloggedFrames.resize (currentBacklogSize + m_frames.size ());
157+ memcpy (m_frames.data (), m_backloggedFrames.data () + currentBacklogSize, sizeof (FrameReport) * m_frames.size ());
158+ m_framesFilledCount = 0 ;
159+ }
160+ }
161+ }
0 commit comments