0030144: Visualization, TKOpenGl - extend OpenGl_FrameStats with frame timers
[occt.git] / src / Graphic3d / Graphic3d_FrameStats.cxx
1 // Copyright (c) 2017 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 #include <Graphic3d_FrameStats.hxx>
15
16 #include <Graphic3d_CView.hxx>
17
18 IMPLEMENT_STANDARD_RTTIEXT(Graphic3d_FrameStats, Standard_Transient)
19
20 namespace
21 {
22   //! Format counter.
23   static std::ostream& formatCounter (std::ostream& theStream,
24                                       Standard_Integer theWidth,
25                                       const char* thePrefix,
26                                       Standard_Size theValue,
27                                       const char* thePostfix = NULL)
28   {
29     if (thePrefix != NULL)
30     {
31       theStream << thePrefix;
32     }
33     theStream << std::setfill(' ') << std::setw (theWidth);
34     if (theValue >= 1000000000)
35     {
36       Standard_Real aValM = Standard_Real(theValue) / 1000000000.0;
37       theStream << std::fixed << std::setprecision (1) << aValM << "G";
38     }
39     else if (theValue >= 1000000)
40     {
41       Standard_Real aValM = Standard_Real(theValue) / 1000000.0;
42       theStream << std::fixed << std::setprecision (1) << aValM << "M";
43     }
44     else if (theValue >= 1000)
45     {
46       Standard_Real aValK = Standard_Real(theValue) / 1000.0;
47       theStream << std::fixed << std::setprecision (1) << aValK << "k";
48     }
49     else
50     {
51       theStream << theValue;
52     }
53     if (thePostfix != NULL)
54     {
55       theStream << thePostfix;
56     }
57     return theStream;
58   }
59
60   //! Format memory counter.
61   static std::ostream& formatBytes (std::ostream& theStream,
62                                     Standard_Integer theWidth,
63                                     const char* thePrefix,
64                                     Standard_Size theValue,
65                                     const char* thePostfix = NULL)
66   {
67     if (thePrefix != NULL)
68     {
69       theStream << thePrefix;
70     }
71     theStream << std::setfill(' ') << std::setw (theWidth);
72     if (theValue >= 1024 * 1024 * 1024)
73     {
74       Standard_Real aValM = Standard_Real(theValue) / (1024.0 * 1024.0 * 1024.0);
75       theStream << std::fixed << std::setprecision (1) << aValM << " GiB";
76     }
77     else if (theValue >= 1024 * 1024)
78     {
79       Standard_Real aValM = Standard_Real(theValue) / (1024.0 * 1024.0);
80       theStream << std::fixed << std::setprecision (1) << aValM << " MiB";
81     }
82     else if (theValue >= 1024)
83     {
84       Standard_Real aValK = Standard_Real(theValue) / 1024.0;
85       theStream << std::fixed << std::setprecision (1) << aValK << " KiB";
86     }
87     else
88     {
89       theStream << theValue << " B";
90     }
91     if (thePostfix != NULL)
92     {
93       theStream << thePostfix;
94     }
95     return theStream;
96   }
97
98   static const Standard_Real THE_SECONDS_IN_HOUR = 3600.0;
99   static const Standard_Real THE_SECONDS_IN_MINUTE = 60.0;
100   static const Standard_Real THE_SECOND_IN_HOUR   = 1.0 / THE_SECONDS_IN_HOUR;
101   static const Standard_Real THE_SECOND_IN_MINUTE = 1.0 / THE_SECONDS_IN_MINUTE;
102
103   //! Format time.
104   static std::ostream& formatTime (std::ostream& theStream,
105                                    Standard_Integer theWidth,
106                                    const char* thePrefix,
107                                    Standard_Real theSeconds,
108                                    const char* thePostfix = NULL)
109   {
110     if (thePrefix != NULL)
111     {
112       theStream << thePrefix;
113     }
114
115     Standard_Real aSecIn = theSeconds;
116     unsigned int aHours   = (unsigned int )(aSecIn * THE_SECOND_IN_HOUR);
117     aSecIn -= Standard_Real(aHours) * THE_SECONDS_IN_HOUR;
118     unsigned int aMinutes = (unsigned int )(aSecIn * THE_SECOND_IN_MINUTE);
119     aSecIn -= Standard_Real(aMinutes) * THE_SECONDS_IN_MINUTE;
120     unsigned int aSeconds = (unsigned int )aSecIn;
121     aSecIn -= Standard_Real(aSeconds);
122     Standard_Real aMilliSeconds = 1000.0 * aSecIn;
123
124     char aBuffer[64];
125     theStream << std::setfill(' ') << std::setw (theWidth);
126     if (aHours > 0)
127     {
128       Sprintf (aBuffer, "%02u:%02u:%02u", aHours, aMinutes, aSeconds);
129       theStream << aBuffer;
130     }
131     else if (aMinutes > 0)
132     {
133       Sprintf (aBuffer, "%02u:%02u", aMinutes, aSeconds);
134       theStream << aBuffer;
135     }
136     else if (aSeconds > 0)
137     {
138       Sprintf (aBuffer, "%2u s", aSeconds);
139       theStream << aBuffer;
140     }
141     else
142     {
143       theStream << std::fixed << std::setprecision (1) << aMilliSeconds << " ms";
144     }
145
146     if (thePostfix != NULL)
147     {
148       theStream << thePostfix;
149     }
150     return theStream;
151   }
152 }
153
154 // =======================================================================
155 // function : Graphic3d_FrameStats
156 // purpose  :
157 // =======================================================================
158 Graphic3d_FrameStats::Graphic3d_FrameStats()
159 : myFpsTimer (Standard_True),
160   myFrameStartTime (0.0),
161   myFrameDuration  (0.0),
162   myUpdateInterval (1.0),
163   myFpsFrameCount (0),
164   myCounters (0, 0),
165   myLastFrameIndex (0),
166   myIsLongLineFormat (Standard_False)
167 {
168   //
169 }
170
171 // =======================================================================
172 // function : ~Graphic3d_FrameStats
173 // purpose  :
174 // =======================================================================
175 Graphic3d_FrameStats::~Graphic3d_FrameStats()
176 {
177   //
178 }
179
180 // =======================================================================
181 // function : FormatStats
182 // purpose  :
183 // =======================================================================
184 TCollection_AsciiString Graphic3d_FrameStats::FormatStats (Graphic3d_RenderingParams::PerfCounters theFlags) const
185 {
186   const Standard_Integer aValWidth = 5;
187   std::stringstream aBuf;
188   const Standard_Boolean isCompact = theFlags == Graphic3d_RenderingParams::PerfCounters_FrameRate; // only FPS is displayed
189   const Graphic3d_FrameStatsData& aStats = LastDataFrame();
190   if (myIsLongLineFormat
191    && (theFlags & Graphic3d_RenderingParams::PerfCounters_FrameRate) != 0
192    && (theFlags & Graphic3d_RenderingParams::PerfCounters_CPU) != 0)
193   {
194     aBuf << "FPS: "     << std::setfill(' ') << std::setw (isCompact ? aValWidth : 9)  << std::fixed << std::setprecision (1) << aStats.FrameRate()
195          << " [CPU: "   << std::setfill(' ') << std::setw (isCompact ? aValWidth : 10) << std::fixed << std::setprecision (1) << aStats.FrameRateCpu() << "]\n";
196   }
197   else
198   {
199     if ((theFlags & Graphic3d_RenderingParams::PerfCounters_FrameRate) != 0)
200     {
201       aBuf << "FPS:     " << std::setfill(' ') << std::setw (isCompact ? aValWidth : aValWidth + 3) << std::fixed << std::setprecision (1) << aStats.FrameRate()  << "\n";
202     }
203     if ((theFlags & Graphic3d_RenderingParams::PerfCounters_CPU) != 0)
204     {
205       aBuf << "CPU FPS: " << std::setfill(' ') << std::setw (isCompact ? aValWidth : aValWidth + 3) << std::fixed << std::setprecision (1) << aStats.FrameRateCpu() << "\n";
206     }
207   }
208   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Layers) != 0)
209   {
210     if (myIsLongLineFormat)
211     {
212       formatCounter (aBuf, aValWidth, "Layers:  ", aStats[Graphic3d_FrameStatsCounter_NbLayers]);
213       if (HasCulledLayers())
214       {
215         formatCounter (aBuf, aValWidth, " [rendered: ", aStats[Graphic3d_FrameStatsCounter_NbLayersNotCulled], "]");
216       }
217       aBuf << "\n";
218     }
219     else
220     {
221       formatCounter (aBuf, aValWidth + 3, "Layers:  ", aStats[Graphic3d_FrameStatsCounter_NbLayers], "\n");
222     }
223   }
224   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Structures) != 0)
225   {
226     if (myIsLongLineFormat)
227     {
228       formatCounter (aBuf, aValWidth, "Structs: ", aStats[Graphic3d_FrameStatsCounter_NbStructs]);
229       if (HasCulledStructs())
230       {
231         formatCounter (aBuf, aValWidth, " [rendered: ", aStats[Graphic3d_FrameStatsCounter_NbStructsNotCulled], "]");
232       }
233       aBuf << "\n";
234     }
235     else
236     {
237       formatCounter (aBuf, aValWidth + 3, "Structs: ", aStats[Graphic3d_FrameStatsCounter_NbStructs], "\n");
238     }
239   }
240   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Groups) != 0
241    || (theFlags & Graphic3d_RenderingParams::PerfCounters_GroupArrays) != 0
242    || (theFlags & Graphic3d_RenderingParams::PerfCounters_Triangles) != 0
243    || (theFlags & Graphic3d_RenderingParams::PerfCounters_Points) != 0
244    || (!myIsLongLineFormat
245     && ((theFlags & Graphic3d_RenderingParams::PerfCounters_Structures) != 0
246      || (theFlags & Graphic3d_RenderingParams::PerfCounters_Layers) != 0)))
247   {
248     aBuf << "Rendered\n";
249   }
250   if (!myIsLongLineFormat
251    && (theFlags & Graphic3d_RenderingParams::PerfCounters_Layers) != 0)
252   {
253     formatCounter (aBuf, aValWidth, "    Layers: ", aStats[Graphic3d_FrameStatsCounter_NbLayersNotCulled], "\n");
254   }
255   if (!myIsLongLineFormat
256    && (theFlags & Graphic3d_RenderingParams::PerfCounters_Structures) != 0)
257   {
258     formatCounter (aBuf, aValWidth, "   Structs: ", aStats[Graphic3d_FrameStatsCounter_NbStructsNotCulled], "\n");
259   }
260   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Groups) != 0)
261   {
262     formatCounter (aBuf, aValWidth, "    Groups: ", aStats[Graphic3d_FrameStatsCounter_NbGroupsNotCulled], "\n");
263   }
264   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_GroupArrays) != 0)
265   {
266     formatCounter (aBuf, aValWidth, "    Arrays: ", aStats[Graphic3d_FrameStatsCounter_NbElemsNotCulled], "\n");
267     formatCounter (aBuf, aValWidth, "    [fill]: ", aStats[Graphic3d_FrameStatsCounter_NbElemsFillNotCulled], "\n");
268     formatCounter (aBuf, aValWidth, "    [line]: ", aStats[Graphic3d_FrameStatsCounter_NbElemsLineNotCulled], "\n");
269     formatCounter (aBuf, aValWidth, "   [point]: ", aStats[Graphic3d_FrameStatsCounter_NbElemsPointNotCulled], "\n");
270     formatCounter (aBuf, aValWidth, "    [text]: ", aStats[Graphic3d_FrameStatsCounter_NbElemsTextNotCulled], "\n");
271   }
272   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Triangles) != 0)
273   {
274     formatCounter (aBuf, aValWidth, " Triangles: ", aStats[Graphic3d_FrameStatsCounter_NbTrianglesNotCulled], "\n");
275   }
276   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Points) != 0)
277   {
278     formatCounter (aBuf, aValWidth, "    Points: ", aStats[Graphic3d_FrameStatsCounter_NbPointsNotCulled], "\n");
279   }
280   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_EstimMem) != 0)
281   {
282     aBuf << "GPU Memory\n";
283     formatBytes (aBuf, aValWidth, "  Geometry: ", aStats[Graphic3d_FrameStatsCounter_EstimatedBytesGeom], "\n");
284     formatBytes (aBuf, aValWidth, "  Textures: ", aStats[Graphic3d_FrameStatsCounter_EstimatedBytesTextures], "\n");
285     formatBytes (aBuf, aValWidth, "    Frames: ", aStats[Graphic3d_FrameStatsCounter_EstimatedBytesFbos], "\n");
286   }
287
288   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_FrameTime) != 0)
289   {
290     aBuf << "Timers Average\n";
291     formatTime (aBuf, aValWidth, " Elapsed Frame: ", aStats[Graphic3d_FrameStatsTimer_ElapsedFrame], "\n");
292     formatTime (aBuf, aValWidth, "     CPU Frame: ", aStats[Graphic3d_FrameStatsTimer_CpuFrame], "\n");
293     if (myCountersMax[Graphic3d_FrameStatsTimer_CpuPicking] > 0.0)
294     {
295       formatTime (aBuf, aValWidth, "   CPU Picking: ", aStats[Graphic3d_FrameStatsTimer_CpuPicking], "\n");
296     }
297     if (myCountersMax[Graphic3d_FrameStatsTimer_CpuCulling] > 0.0)
298     {
299       formatTime (aBuf, aValWidth, "   CPU Culling: ", aStats[Graphic3d_FrameStatsTimer_CpuCulling], "\n");
300     }
301     if (myCountersMax[Graphic3d_FrameStatsTimer_CpuDynamics])
302     {
303       formatTime (aBuf, aValWidth, "  CPU Dynamics: ", aStats[Graphic3d_FrameStatsTimer_CpuDynamics], "\n");
304     }
305     if ((theFlags & Graphic3d_RenderingParams::PerfCounters_FrameTimeMax) != 0)
306     {
307       aBuf << "Timers Max\n";
308       formatTime (aBuf, aValWidth, "     CPU Frame: ", myCountersMax[Graphic3d_FrameStatsTimer_CpuFrame], "\n");
309       if (myCountersMax[Graphic3d_FrameStatsTimer_CpuPicking] > 0.0)
310       {
311         formatTime (aBuf, aValWidth, "   CPU Picking: ", myCountersMax[Graphic3d_FrameStatsTimer_CpuPicking], "\n");
312       }
313       if (myCountersMax[Graphic3d_FrameStatsTimer_CpuCulling] > 0.0)
314       {
315         formatTime (aBuf, aValWidth, "   CPU Culling: ", myCountersMax[Graphic3d_FrameStatsTimer_CpuCulling], "\n");
316       }
317       if (myCountersMax[Graphic3d_FrameStatsTimer_CpuDynamics])
318       {
319         formatTime (aBuf, aValWidth, "  CPU Dynamics: ", myCountersMax[Graphic3d_FrameStatsTimer_CpuDynamics], "\n");
320       }
321     }
322   }
323
324   return TCollection_AsciiString (aBuf.str().c_str());
325 }
326
327 // =======================================================================
328 // function : FrameStart
329 // purpose  :
330 // =======================================================================
331 void Graphic3d_FrameStats::FrameStart (const Handle(Graphic3d_CView)& theView,
332                                        bool theIsImmediateOnly)
333 {
334   const Graphic3d_RenderingParams::PerfCounters aBits = !theView.IsNull()
335                                                       ? theView->RenderingParams().CollectedStats
336                                                       : Graphic3d_RenderingParams::PerfCounters_NONE;
337   if (theIsImmediateOnly
338    && (aBits & Graphic3d_RenderingParams::PerfCounters_SkipImmediate) != 0)
339   {
340     return;
341   }
342
343   const Standard_Integer aNbFrames = Max (!theView.IsNull()
344                                          ? theView->RenderingParams().StatsNbFrames
345                                          : 1, 1);
346   if (myCounters.Size() != aNbFrames)
347   {
348     myCounters.Resize (0, aNbFrames - 1, false);
349     myCounters.Init (Graphic3d_FrameStatsData());
350     myLastFrameIndex = myCounters.Upper();
351   }
352
353   // reset values at the end of frame (after data has been flushed),
354   // so that application can put some counters (like picking time) before FrameStart().
355   //myCountersTmp.Reset();
356
357   myFrameStartTime = myFpsTimer.ElapsedTime();
358   if (!myFpsTimer.IsStarted())
359   {
360     myFpsTimer.Reset();
361     myFpsTimer.Start();
362     myFpsFrameCount = 0;
363   }
364 }
365
366 // =======================================================================
367 // function : FrameEnd
368 // purpose  :
369 // =======================================================================
370 void Graphic3d_FrameStats::FrameEnd (const Handle(Graphic3d_CView)& theView,
371                                      bool theIsImmediateOnly)
372 {
373   const Graphic3d_RenderingParams::PerfCounters aBits = !theView.IsNull()
374                                                       ? theView->RenderingParams().CollectedStats
375                                                       : Graphic3d_RenderingParams::PerfCounters_NONE;
376   if (theIsImmediateOnly
377    && (aBits & Graphic3d_RenderingParams::PerfCounters_SkipImmediate) != 0)
378   {
379     return;
380   }
381
382   const double aTime = myFpsTimer.ElapsedTime();
383   myFrameDuration = aTime - myFrameStartTime;
384   ++myFpsFrameCount;
385   if (!theView.IsNull())
386   {
387     myUpdateInterval = theView->RenderingParams().StatsUpdateInterval;
388   }
389
390   if (aTime < myUpdateInterval)
391   {
392     myCountersTmp.FlushTimers (myFpsFrameCount, false);
393     return;
394   }
395
396   if (aTime > gp::Resolution())
397   {
398     // update FPS
399     myFpsTimer.Stop();
400     const double aCpuSec = myFpsTimer.UserTimeCPU();
401
402     myCountersTmp[Graphic3d_FrameStatsTimer_ElapsedFrame]  = aTime;
403     myCountersTmp[Graphic3d_FrameStatsTimer_CpuFrame]      = aCpuSec;
404     myCountersTmp.ChangeFrameRate()    = double(myFpsFrameCount) / aTime;
405     myCountersTmp.ChangeFrameRateCpu() = aCpuSec > gp::Resolution()
406                                        ? double(myFpsFrameCount) / aCpuSec
407                                        : -1.0;
408     myCountersTmp.FlushTimers (myFpsFrameCount, true);
409     myCountersMax.FillMax (myCountersTmp);
410     myFpsTimer.Reset();
411     myFpsTimer.Start();
412     myFpsFrameCount = 0;
413   }
414
415   // update structure counters
416   if (theView.IsNull())
417   {
418     myCounters.SetValue (myLastFrameIndex, myCountersTmp);
419     myCountersTmp.Reset();
420     return;
421   }
422
423   updateStatistics (theView, theIsImmediateOnly);
424
425   if (++myLastFrameIndex > myCounters.Upper())
426   {
427     myLastFrameIndex = myCounters.Lower();
428   }
429   myCounters.SetValue (myLastFrameIndex, myCountersTmp);
430   myCountersTmp.Reset();
431 }