17b040d9dcc228077d72dbf8613de01f5f67cf60
[occt.git] / src / OpenGl / OpenGl_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 <OpenGl_FrameStats.hxx>
15
16 #include <OpenGl_GlCore20.hxx>
17 #include <OpenGl_View.hxx>
18 #include <OpenGl_Workspace.hxx>
19
20 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_FrameStats, Standard_Transient)
21
22 namespace
23 {
24   //! Format counter.
25   static std::ostream& formatCounter (std::ostream& theStream,
26                                       Standard_Integer theWidth,
27                                       const char* thePrefix,
28                                       Standard_Size theValue,
29                                       const char* thePostfix = NULL)
30   {
31     if (thePrefix != NULL)
32     {
33       theStream << thePrefix;
34     }
35     theStream << std::setfill(' ') << std::setw (theWidth);
36     if (theValue >= 1000000000)
37     {
38       Standard_Real aValM = Standard_Real(theValue) / 1000000000.0;
39       theStream << std::fixed << std::setprecision (1) << aValM << "G";
40     }
41     else if (theValue >= 1000000)
42     {
43       Standard_Real aValM = Standard_Real(theValue) / 1000000.0;
44       theStream << std::fixed << std::setprecision (1) << aValM << "M";
45     }
46     else if (theValue >= 1000)
47     {
48       Standard_Real aValK = Standard_Real(theValue) / 1000.0;
49       theStream << std::fixed << std::setprecision (1) << aValK << "k";
50     }
51     else
52     {
53       theStream << theValue;
54     }
55     if (thePostfix != NULL)
56     {
57       theStream << thePostfix;
58     }
59     return theStream;
60   }
61
62   //! Format memory counter.
63   static std::ostream& formatBytes (std::ostream& theStream,
64                                     Standard_Integer theWidth,
65                                     const char* thePrefix,
66                                     Standard_Size theValue,
67                                     const char* thePostfix = NULL)
68   {
69     if (thePrefix != NULL)
70     {
71       theStream << thePrefix;
72     }
73     theStream << std::setfill(' ') << std::setw (theWidth);
74     if (theValue >= 1024 * 1024 * 1024)
75     {
76       Standard_Real aValM = Standard_Real(theValue) / (1024.0 * 1024.0 * 1024.0);
77       theStream << std::fixed << std::setprecision (1) << aValM << " GiB";
78     }
79     else if (theValue >= 1024 * 1024)
80     {
81       Standard_Real aValM = Standard_Real(theValue) / (1024.0 * 1024.0);
82       theStream << std::fixed << std::setprecision (1) << aValM << " MiB";
83     }
84     else if (theValue >= 1024)
85     {
86       Standard_Real aValK = Standard_Real(theValue) / 1024.0;
87       theStream << std::fixed << std::setprecision (1) << aValK << " KiB";
88     }
89     else
90     {
91       theStream << theValue;
92     }
93     if (thePostfix != NULL)
94     {
95       theStream << thePostfix;
96     }
97     return theStream;
98   }
99
100   //! Return estimated data size.
101   static Standard_Size estimatedDataSize (const Handle(OpenGl_Resource)& theRes)
102   {
103     return !theRes.IsNull() ? theRes->EstimatedDataSize() : 0;
104   }
105 }
106
107 // =======================================================================
108 // function : OpenGl_FrameStats
109 // purpose  :
110 // =======================================================================
111 OpenGl_FrameStats::OpenGl_FrameStats()
112 : myFpsTimer (Standard_True),
113   myFrameStartTime (0.0),
114   myFrameDuration  (0.0),
115   myFps    (-1.0),
116   myFpsCpu (-1.0),
117   myUpdateInterval (1.0),
118   myFpsFrameCount (0),
119   myIsLongLineFormat (Standard_False)
120 {
121   memset (myCounters,    0, sizeof(myCounters));
122   memset (myCountersTmp, 0, sizeof(myCountersTmp));
123 }
124
125 // =======================================================================
126 // function : ~OpenGl_FrameStats
127 // purpose  :
128 // =======================================================================
129 OpenGl_FrameStats::~OpenGl_FrameStats()
130 {
131   //
132 }
133
134 // =======================================================================
135 // function : FormatStats
136 // purpose  :
137 // =======================================================================
138 TCollection_AsciiString OpenGl_FrameStats::FormatStats (Graphic3d_RenderingParams::PerfCounters theFlags) const
139 {
140   const Standard_Integer aValWidth = 5;
141   std::stringstream aBuf;
142   const Standard_Boolean isCompact = theFlags == Graphic3d_RenderingParams::PerfCounters_FrameRate; // only FPS is displayed
143   if (myIsLongLineFormat
144    && (theFlags & Graphic3d_RenderingParams::PerfCounters_FrameRate) != 0
145    && (theFlags & Graphic3d_RenderingParams::PerfCounters_CPU) != 0)
146   {
147     aBuf << "FPS: "     << std::setfill(' ') << std::setw (isCompact ? aValWidth : 9)  << std::fixed << std::setprecision (1) << myFps
148          << " [CPU: "   << std::setfill(' ') << std::setw (isCompact ? aValWidth : 10) << std::fixed << std::setprecision (1) << myFpsCpu << "]\n";
149   }
150   else
151   {
152     if ((theFlags & Graphic3d_RenderingParams::PerfCounters_FrameRate) != 0)
153     {
154       aBuf << "FPS:     " << std::setfill(' ') << std::setw (isCompact ? aValWidth : aValWidth + 3) << std::fixed << std::setprecision (1) << myFps  << "\n";
155     }
156     if ((theFlags & Graphic3d_RenderingParams::PerfCounters_CPU) != 0)
157     {
158       aBuf << "CPU FPS: " << std::setfill(' ') << std::setw (isCompact ? aValWidth : aValWidth + 3) << std::fixed << std::setprecision (1) << myFpsCpu << "\n";
159     }
160   }
161   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Layers) != 0)
162   {
163     if (myIsLongLineFormat)
164     {
165       formatCounter (aBuf, aValWidth, "Layers:  ", myCounters[Counter_NbLayers]);
166       if (HasCulledLayers())
167       {
168         formatCounter (aBuf, aValWidth, " [rendered: ", myCounters[Counter_NbLayersNotCulled], "]");
169       }
170       aBuf << "\n";
171     }
172     else
173     {
174       formatCounter (aBuf, aValWidth + 3, "Layers:  ", myCounters[Counter_NbLayers], "\n");
175     }
176   }
177   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Structures) != 0)
178   {
179     if (myIsLongLineFormat)
180     {
181       formatCounter (aBuf, aValWidth, "Structs: ", myCounters[Counter_NbStructs]);
182       if (HasCulledStructs())
183       {
184         formatCounter (aBuf, aValWidth, " [rendered: ", myCounters[Counter_NbStructsNotCulled], "]");
185       }
186       aBuf << "\n";
187     }
188     else
189     {
190       formatCounter (aBuf, aValWidth + 3, "Structs: ", myCounters[Counter_NbStructs], "\n");
191     }
192   }
193   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Groups) != 0
194    || (theFlags & Graphic3d_RenderingParams::PerfCounters_GroupArrays) != 0
195    || (theFlags & Graphic3d_RenderingParams::PerfCounters_Triangles) != 0
196    || (theFlags & Graphic3d_RenderingParams::PerfCounters_Points) != 0
197    || (!myIsLongLineFormat
198     && ((theFlags & Graphic3d_RenderingParams::PerfCounters_Structures) != 0
199      || (theFlags & Graphic3d_RenderingParams::PerfCounters_Layers) != 0)))
200   {
201     aBuf << "Rendered\n";
202   }
203   if (!myIsLongLineFormat
204    && (theFlags & Graphic3d_RenderingParams::PerfCounters_Layers) != 0)
205   {
206     formatCounter (aBuf, aValWidth, "    Layers: ", myCounters[Counter_NbLayersNotCulled], "\n");
207   }
208   if (!myIsLongLineFormat
209    && (theFlags & Graphic3d_RenderingParams::PerfCounters_Structures) != 0)
210   {
211     formatCounter (aBuf, aValWidth, "   Structs: ", myCounters[Counter_NbStructsNotCulled], "\n");
212   }
213   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Groups) != 0)
214   {
215     formatCounter (aBuf, aValWidth, "    Groups: ", myCounters[Counter_NbGroupsNotCulled], "\n");
216   }
217   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_GroupArrays) != 0)
218   {
219     formatCounter (aBuf, aValWidth, "    Arrays: ", myCounters[Counter_NbElemsNotCulled], "\n");
220     formatCounter (aBuf, aValWidth, "    [fill]: ", myCounters[Counter_NbElemsFillNotCulled], "\n");
221     formatCounter (aBuf, aValWidth, "    [line]: ", myCounters[Counter_NbElemsLineNotCulled], "\n");
222     formatCounter (aBuf, aValWidth, "   [point]: ", myCounters[Counter_NbElemsPointNotCulled], "\n");
223     formatCounter (aBuf, aValWidth, "    [text]: ", myCounters[Counter_NbElemsTextNotCulled], "\n");
224   }
225   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Triangles) != 0)
226   {
227     formatCounter (aBuf, aValWidth, " Triangles: ", myCounters[Counter_NbTrianglesNotCulled], "\n");
228   }
229   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Points) != 0)
230   {
231     formatCounter (aBuf, aValWidth, "    Points: ", myCounters[Counter_NbPointsNotCulled], "\n");
232   }
233   if ((theFlags & Graphic3d_RenderingParams::PerfCounters_EstimMem) != 0)
234   {
235     aBuf << "GPU Memory\n";
236     formatBytes (aBuf, aValWidth, "  Geometry: ", myCounters[Counter_EstimatedBytesGeom], "\n");
237     formatBytes (aBuf, aValWidth, "  Textures: ", myCounters[Counter_EstimatedBytesTextures], "\n");
238     formatBytes (aBuf, aValWidth, "    Frames: ", myCounters[Counter_EstimatedBytesFbos], "\n");
239   }
240   return TCollection_AsciiString (aBuf.str().c_str());
241 }
242
243 // =======================================================================
244 // function : FrameStart
245 // purpose  :
246 // =======================================================================
247 void OpenGl_FrameStats::FrameStart (const Handle(OpenGl_Workspace)& )
248 {
249   memset (myCountersTmp, 0, sizeof(myCountersTmp));
250   myFrameStartTime = myFpsTimer.ElapsedTime();
251   if (!myFpsTimer.IsStarted())
252   {
253     myFpsTimer.Reset();
254     myFpsTimer.Start();
255     myFpsFrameCount = 0;
256   }
257 }
258
259 // =======================================================================
260 // function : FrameEnd
261 // purpose  :
262 // =======================================================================
263 void OpenGl_FrameStats::FrameEnd (const Handle(OpenGl_Workspace)& theWorkspace)
264 {
265   const Graphic3d_RenderingParams::PerfCounters aBits = !theWorkspace.IsNull()
266                                                       ? theWorkspace->View()->RenderingParams().CollectedStats
267                                                       : Graphic3d_RenderingParams::PerfCounters_NONE;
268   const double aTime = myFpsTimer.ElapsedTime();
269   myFrameDuration = aTime - myFrameStartTime;
270   ++myFpsFrameCount;
271   if (!theWorkspace.IsNull())
272   {
273     myUpdateInterval = theWorkspace->View()->RenderingParams().StatsUpdateInterval;
274   }
275
276   if (aTime < myUpdateInterval)
277   {
278     return;
279   }
280
281   if (aTime > gp::Resolution())
282   {
283     // update FPS
284     myFpsTimer.Stop();
285     const double aCpuSec = myFpsTimer.UserTimeCPU();
286     myFps    = double(myFpsFrameCount) / aTime;
287     myFpsCpu = aCpuSec > gp::Resolution()
288              ? double(myFpsFrameCount) / aCpuSec
289              : -1.0;
290     myFpsTimer.Reset();
291     myFpsTimer.Start();
292     myFpsFrameCount = 0;
293   }
294
295   // update structure counters
296   if (theWorkspace.IsNull())
297   {
298     memcpy (myCounters, myCountersTmp, sizeof(myCounters));
299     return;
300   }
301
302   const Standard_Boolean toCountMem     = (aBits & Graphic3d_RenderingParams::PerfCounters_EstimMem)  != 0;
303   const Standard_Boolean toCountTris    = (aBits & Graphic3d_RenderingParams::PerfCounters_Triangles) != 0
304                                        || (aBits & Graphic3d_RenderingParams::PerfCounters_Points)    != 0;
305   const Standard_Boolean toCountElems   = (aBits & Graphic3d_RenderingParams::PerfCounters_GroupArrays) != 0 || toCountTris || toCountMem;
306   const Standard_Boolean toCountGroups  = (aBits & Graphic3d_RenderingParams::PerfCounters_Groups)      != 0 || toCountElems;
307   const Standard_Boolean toCountStructs = (aBits & Graphic3d_RenderingParams::PerfCounters_Structures)  != 0 || toCountGroups;
308   
309   if (toCountStructs)
310   {
311     myCountersTmp[Counter_NbLayers] = theWorkspace->View()->LayerList().Layers().Size();
312     for (OpenGl_SequenceOfLayers::Iterator aLayerIter (theWorkspace->View()->LayerList().Layers()); aLayerIter.More(); aLayerIter.Next())
313     {
314       const Handle(OpenGl_Layer)& aLayer = aLayerIter.Value();
315       if (!aLayer->IsCulled())
316       {
317         ++myCountersTmp[Counter_NbLayersNotCulled];
318       }
319       myCountersTmp[Counter_NbStructs]          += aLayer->NbStructures();
320       myCountersTmp[Counter_NbStructsNotCulled] += aLayer->NbStructuresNotCulled();
321       if (toCountGroups)
322       {
323         updateStructures (aLayer->CullableStructuresBVH().Structures(), toCountStructs, toCountTris, toCountMem);
324         updateStructures (aLayer->CullableTrsfPersStructuresBVH().Structures(), toCountStructs, toCountTris, toCountMem);
325         updateStructures (aLayer->NonCullableStructures(), toCountStructs, toCountTris, toCountMem);
326       }
327     }
328   }
329   if (toCountMem
330   && !theWorkspace.IsNull())
331   {
332     for (OpenGl_Context::OpenGl_ResourcesMap::Iterator aResIter (theWorkspace->GetGlContext()->SharedResources());
333          aResIter.More(); aResIter.Next())
334     {
335       myCountersTmp[Counter_EstimatedBytesTextures] += aResIter.Value()->EstimatedDataSize();
336     }
337
338     const OpenGl_View* aView = theWorkspace->View();
339     {
340       Standard_Size& aMemFbos = myCountersTmp[Counter_EstimatedBytesFbos];
341       // main FBOs
342       aMemFbos += estimatedDataSize (aView->myMainSceneFbos[0]);
343       aMemFbos += estimatedDataSize (aView->myMainSceneFbos[1]);
344       aMemFbos += estimatedDataSize (aView->myImmediateSceneFbos[0]);
345       aMemFbos += estimatedDataSize (aView->myImmediateSceneFbos[1]);
346       // OIT FBOs
347       aMemFbos += estimatedDataSize (aView->myMainSceneFbosOit[0]);
348       aMemFbos += estimatedDataSize (aView->myMainSceneFbosOit[1]);
349       aMemFbos += estimatedDataSize (aView->myImmediateSceneFbosOit[0]);
350       aMemFbos += estimatedDataSize (aView->myImmediateSceneFbosOit[1]);
351       // dump FBO
352       aMemFbos += estimatedDataSize (aView->myFBO);
353       // RayTracing FBO
354       aMemFbos += estimatedDataSize (aView->myOpenGlFBO);
355       aMemFbos += estimatedDataSize (aView->myOpenGlFBO2);
356       aMemFbos += estimatedDataSize (aView->myRaytraceFBO1[0]);
357       aMemFbos += estimatedDataSize (aView->myRaytraceFBO1[1]);
358       aMemFbos += estimatedDataSize (aView->myRaytraceFBO2[0]);
359       aMemFbos += estimatedDataSize (aView->myRaytraceFBO2[1]);
360       // also RayTracing
361       aMemFbos += estimatedDataSize (aView->myRaytraceOutputTexture[0]);
362       aMemFbos += estimatedDataSize (aView->myRaytraceOutputTexture[1]);
363       aMemFbos += estimatedDataSize (aView->myRaytraceVisualErrorTexture[0]);
364       aMemFbos += estimatedDataSize (aView->myRaytraceVisualErrorTexture[1]);
365       aMemFbos += estimatedDataSize (aView->myRaytraceTileOffsetsTexture[0]);
366       aMemFbos += estimatedDataSize (aView->myRaytraceTileOffsetsTexture[1]);
367     }
368     {
369       // Ray Tracing geometry
370       Standard_Size& aMemGeom = myCountersTmp[Counter_EstimatedBytesGeom];
371       aMemGeom += estimatedDataSize (aView->mySceneNodeInfoTexture);
372       aMemGeom += estimatedDataSize (aView->mySceneMinPointTexture);
373       aMemGeom += estimatedDataSize (aView->mySceneMaxPointTexture);
374       aMemGeom += estimatedDataSize (aView->mySceneTransformTexture);
375       aMemGeom += estimatedDataSize (aView->myGeometryVertexTexture);
376       aMemGeom += estimatedDataSize (aView->myGeometryNormalTexture);
377       aMemGeom += estimatedDataSize (aView->myGeometryTexCrdTexture);
378       aMemGeom += estimatedDataSize (aView->myGeometryTriangTexture);
379       aMemGeom += estimatedDataSize (aView->myRaytraceMaterialTexture);
380       aMemGeom += estimatedDataSize (aView->myRaytraceLightSrcTexture);
381     }
382   }
383   memcpy (myCounters, myCountersTmp, sizeof(myCounters));
384 }
385
386 // =======================================================================
387 // function : updateStructures
388 // purpose  :
389 // =======================================================================
390 void OpenGl_FrameStats::updateStructures (const OpenGl_IndexedMapOfStructure& theStructures,
391                                           Standard_Boolean theToCountElems,
392                                           Standard_Boolean theToCountTris,
393                                           Standard_Boolean theToCountMem)
394 {
395   for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (theStructures); aStructIter.More(); aStructIter.Next())
396   {
397     const OpenGl_Structure* aStruct = aStructIter.Value();
398     if (aStruct->IsCulled())
399     {
400       if (theToCountMem)
401       {
402         for (OpenGl_Structure::GroupIterator aGroupIter (aStruct->Groups()); aGroupIter.More(); aGroupIter.Next())
403         {
404           const OpenGl_Group* aGroup = aGroupIter.Value();
405           for (const OpenGl_ElementNode* aNodeIter = aGroup->FirstNode(); aNodeIter != NULL; aNodeIter = aNodeIter->next)
406           {
407             if (const OpenGl_PrimitiveArray* aPrim = dynamic_cast<const OpenGl_PrimitiveArray*> (aNodeIter->elem))
408             {
409               myCountersTmp[Counter_EstimatedBytesGeom] += estimatedDataSize (aPrim->AttributesVbo());
410               myCountersTmp[Counter_EstimatedBytesGeom] += estimatedDataSize (aPrim->IndexVbo());
411             }
412           }
413         }
414       }
415       continue;
416     }
417
418     myCountersTmp[Counter_NbGroupsNotCulled] += aStruct->Groups().Size();
419     if (!theToCountElems)
420     {
421       continue;
422     }
423
424     for (OpenGl_Structure::GroupIterator aGroupIter (aStruct->Groups()); aGroupIter.More(); aGroupIter.Next())
425     {
426       const OpenGl_Group* aGroup = aGroupIter.Value();
427       for (const OpenGl_ElementNode* aNodeIter = aGroup->FirstNode(); aNodeIter != NULL; aNodeIter = aNodeIter->next)
428       {
429         if (const OpenGl_PrimitiveArray* aPrim = dynamic_cast<const OpenGl_PrimitiveArray*> (aNodeIter->elem))
430         {
431           ++myCountersTmp[Counter_NbElemsNotCulled];
432           if (theToCountMem)
433           {
434             myCountersTmp[Counter_EstimatedBytesGeom] += estimatedDataSize (aPrim->AttributesVbo());
435             myCountersTmp[Counter_EstimatedBytesGeom] += estimatedDataSize (aPrim->IndexVbo());
436           }
437
438           if (aPrim->IsFillDrawMode())
439           {
440             ++myCountersTmp[Counter_NbElemsFillNotCulled];
441             if (!theToCountTris)
442             {
443               continue;
444             }
445
446             const Handle(OpenGl_VertexBuffer)& anAttribs = aPrim->AttributesVbo();
447             if (anAttribs.IsNull()
448             || !anAttribs->IsValid())
449             {
450               continue;
451             }
452
453             const Handle(OpenGl_VertexBuffer)& anIndices = aPrim->IndexVbo();
454             const Standard_Integer aNbIndices = !anIndices.IsNull() ? anIndices->GetElemsNb() : anAttribs->GetElemsNb();
455             const Standard_Integer aNbBounds  = !aPrim->Bounds().IsNull() ? aPrim->Bounds()->NbBounds : 1;
456             switch (aPrim->DrawMode())
457             {
458               case GL_TRIANGLES:
459               {
460                 myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices / 3;
461                 break;
462               }
463               case GL_TRIANGLE_STRIP:
464               case GL_TRIANGLE_FAN:
465               {
466                 myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices - 2 * aNbBounds;
467                 break;
468               }
469               case GL_TRIANGLES_ADJACENCY:
470               {
471                 myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices / 6;
472                 break;
473               }
474               case GL_TRIANGLE_STRIP_ADJACENCY:
475               {
476                 myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices - 4 * aNbBounds;
477                 break;
478               }
479             #if !defined(GL_ES_VERSION_2_0)
480               case GL_QUADS:
481               {
482                 myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices / 2;
483                 break;
484               }
485               case GL_QUAD_STRIP:
486               {
487                 myCountersTmp[Counter_NbTrianglesNotCulled] += (aNbIndices / 2 - aNbBounds) * 2;
488                 break;
489               }
490             #endif
491             }
492           }
493           else if (aPrim->DrawMode() == GL_POINTS)
494           {
495             ++myCountersTmp[Counter_NbElemsPointNotCulled];
496             if (theToCountTris)
497             {
498               const Handle(OpenGl_VertexBuffer)& anAttribs = aPrim->AttributesVbo();
499               if (!anAttribs.IsNull()
500                 && anAttribs->IsValid())
501               {
502                 const Handle(OpenGl_VertexBuffer)& anIndices = aPrim->IndexVbo();
503                 const Standard_Integer aNbIndices = !anIndices.IsNull() ? anIndices->GetElemsNb() : anAttribs->GetElemsNb();
504                 myCountersTmp[Counter_NbPointsNotCulled] += aNbIndices;
505               }
506             }
507           }
508           else
509           {
510             ++myCountersTmp[Counter_NbElemsLineNotCulled];
511           }
512         }
513         else if (const OpenGl_Text* aText = dynamic_cast<const OpenGl_Text*> (aNodeIter->elem))
514         {
515           (void )aText;
516           ++myCountersTmp[Counter_NbElemsNotCulled];
517           ++myCountersTmp[Counter_NbElemsTextNotCulled];
518         }
519       }
520     }
521   }
522 }