0030144: Visualization, TKOpenGl - extend OpenGl_FrameStats with frame timers IR-WEEK41
authorkgv <kgv@opencascade.com>
Mon, 17 Sep 2018 15:11:05 +0000 (18:11 +0300)
committerapn <apn@opencascade.com>
Thu, 11 Oct 2018 16:13:23 +0000 (19:13 +0300)
18 files changed:
src/Graphic3d/FILES
src/Graphic3d/Graphic3d_FrameStats.cxx [new file with mode: 0644]
src/Graphic3d/Graphic3d_FrameStats.hxx [new file with mode: 0644]
src/Graphic3d/Graphic3d_FrameStatsCounter.hxx [new file with mode: 0644]
src/Graphic3d/Graphic3d_FrameStatsData.cxx [new file with mode: 0644]
src/Graphic3d/Graphic3d_FrameStatsData.hxx [new file with mode: 0644]
src/Graphic3d/Graphic3d_FrameStatsTimer.hxx [new file with mode: 0644]
src/Graphic3d/Graphic3d_RenderingParams.hxx
src/OpenGl/OpenGl_Context.hxx
src/OpenGl/OpenGl_FrameStats.cxx
src/OpenGl/OpenGl_FrameStats.hxx
src/OpenGl/OpenGl_FrameStatsPrs.cxx
src/OpenGl/OpenGl_FrameStatsPrs.hxx
src/OpenGl/OpenGl_LayerList.cxx
src/OpenGl/OpenGl_Text.hxx
src/OpenGl/OpenGl_View.hxx
src/OpenGl/OpenGl_View_Redraw.cxx
src/ViewerTest/ViewerTest_ViewerCommands.cxx

index 2c15a0d..9cbee62 100755 (executable)
@@ -43,6 +43,12 @@ Graphic3d_CView.hxx
 Graphic3d_DataStructureManager.cxx
 Graphic3d_DataStructureManager.hxx
 Graphic3d_DiagnosticInfo.hxx
+Graphic3d_FrameStats.cxx
+Graphic3d_FrameStats.hxx
+Graphic3d_FrameStatsCounter.hxx
+Graphic3d_FrameStatsData.cxx
+Graphic3d_FrameStatsData.hxx
+Graphic3d_FrameStatsTimer.hxx
 Graphic3d_GraduatedTrihedron.hxx
 Graphic3d_GraphicDriver.cxx
 Graphic3d_GraphicDriver.hxx
diff --git a/src/Graphic3d/Graphic3d_FrameStats.cxx b/src/Graphic3d/Graphic3d_FrameStats.cxx
new file mode 100644 (file)
index 0000000..59045e1
--- /dev/null
@@ -0,0 +1,431 @@
+// Copyright (c) 2017 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <Graphic3d_FrameStats.hxx>
+
+#include <Graphic3d_CView.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(Graphic3d_FrameStats, Standard_Transient)
+
+namespace
+{
+  //! Format counter.
+  static std::ostream& formatCounter (std::ostream& theStream,
+                                      Standard_Integer theWidth,
+                                      const char* thePrefix,
+                                      Standard_Size theValue,
+                                      const char* thePostfix = NULL)
+  {
+    if (thePrefix != NULL)
+    {
+      theStream << thePrefix;
+    }
+    theStream << std::setfill(' ') << std::setw (theWidth);
+    if (theValue >= 1000000000)
+    {
+      Standard_Real aValM = Standard_Real(theValue) / 1000000000.0;
+      theStream << std::fixed << std::setprecision (1) << aValM << "G";
+    }
+    else if (theValue >= 1000000)
+    {
+      Standard_Real aValM = Standard_Real(theValue) / 1000000.0;
+      theStream << std::fixed << std::setprecision (1) << aValM << "M";
+    }
+    else if (theValue >= 1000)
+    {
+      Standard_Real aValK = Standard_Real(theValue) / 1000.0;
+      theStream << std::fixed << std::setprecision (1) << aValK << "k";
+    }
+    else
+    {
+      theStream << theValue;
+    }
+    if (thePostfix != NULL)
+    {
+      theStream << thePostfix;
+    }
+    return theStream;
+  }
+
+  //! Format memory counter.
+  static std::ostream& formatBytes (std::ostream& theStream,
+                                    Standard_Integer theWidth,
+                                    const char* thePrefix,
+                                    Standard_Size theValue,
+                                    const char* thePostfix = NULL)
+  {
+    if (thePrefix != NULL)
+    {
+      theStream << thePrefix;
+    }
+    theStream << std::setfill(' ') << std::setw (theWidth);
+    if (theValue >= 1024 * 1024 * 1024)
+    {
+      Standard_Real aValM = Standard_Real(theValue) / (1024.0 * 1024.0 * 1024.0);
+      theStream << std::fixed << std::setprecision (1) << aValM << " GiB";
+    }
+    else if (theValue >= 1024 * 1024)
+    {
+      Standard_Real aValM = Standard_Real(theValue) / (1024.0 * 1024.0);
+      theStream << std::fixed << std::setprecision (1) << aValM << " MiB";
+    }
+    else if (theValue >= 1024)
+    {
+      Standard_Real aValK = Standard_Real(theValue) / 1024.0;
+      theStream << std::fixed << std::setprecision (1) << aValK << " KiB";
+    }
+    else
+    {
+      theStream << theValue << " B";
+    }
+    if (thePostfix != NULL)
+    {
+      theStream << thePostfix;
+    }
+    return theStream;
+  }
+
+  static const Standard_Real THE_SECONDS_IN_HOUR = 3600.0;
+  static const Standard_Real THE_SECONDS_IN_MINUTE = 60.0;
+  static const Standard_Real THE_SECOND_IN_HOUR   = 1.0 / THE_SECONDS_IN_HOUR;
+  static const Standard_Real THE_SECOND_IN_MINUTE = 1.0 / THE_SECONDS_IN_MINUTE;
+
+  //! Format time.
+  static std::ostream& formatTime (std::ostream& theStream,
+                                   Standard_Integer theWidth,
+                                   const char* thePrefix,
+                                   Standard_Real theSeconds,
+                                   const char* thePostfix = NULL)
+  {
+    if (thePrefix != NULL)
+    {
+      theStream << thePrefix;
+    }
+
+    Standard_Real aSecIn = theSeconds;
+    unsigned int aHours   = (unsigned int )(aSecIn * THE_SECOND_IN_HOUR);
+    aSecIn -= Standard_Real(aHours) * THE_SECONDS_IN_HOUR;
+    unsigned int aMinutes = (unsigned int )(aSecIn * THE_SECOND_IN_MINUTE);
+    aSecIn -= Standard_Real(aMinutes) * THE_SECONDS_IN_MINUTE;
+    unsigned int aSeconds = (unsigned int )aSecIn;
+    aSecIn -= Standard_Real(aSeconds);
+    Standard_Real aMilliSeconds = 1000.0 * aSecIn;
+
+    char aBuffer[64];
+    theStream << std::setfill(' ') << std::setw (theWidth);
+    if (aHours > 0)
+    {
+      Sprintf (aBuffer, "%02u:%02u:%02u", aHours, aMinutes, aSeconds);
+      theStream << aBuffer;
+    }
+    else if (aMinutes > 0)
+    {
+      Sprintf (aBuffer, "%02u:%02u", aMinutes, aSeconds);
+      theStream << aBuffer;
+    }
+    else if (aSeconds > 0)
+    {
+      Sprintf (aBuffer, "%2u s", aSeconds);
+      theStream << aBuffer;
+    }
+    else
+    {
+      theStream << std::fixed << std::setprecision (1) << aMilliSeconds << " ms";
+    }
+
+    if (thePostfix != NULL)
+    {
+      theStream << thePostfix;
+    }
+    return theStream;
+  }
+}
+
+// =======================================================================
+// function : Graphic3d_FrameStats
+// purpose  :
+// =======================================================================
+Graphic3d_FrameStats::Graphic3d_FrameStats()
+: myFpsTimer (Standard_True),
+  myFrameStartTime (0.0),
+  myFrameDuration  (0.0),
+  myUpdateInterval (1.0),
+  myFpsFrameCount (0),
+  myCounters (0, 0),
+  myLastFrameIndex (0),
+  myIsLongLineFormat (Standard_False)
+{
+  //
+}
+
+// =======================================================================
+// function : ~Graphic3d_FrameStats
+// purpose  :
+// =======================================================================
+Graphic3d_FrameStats::~Graphic3d_FrameStats()
+{
+  //
+}
+
+// =======================================================================
+// function : FormatStats
+// purpose  :
+// =======================================================================
+TCollection_AsciiString Graphic3d_FrameStats::FormatStats (Graphic3d_RenderingParams::PerfCounters theFlags) const
+{
+  const Standard_Integer aValWidth = 5;
+  std::stringstream aBuf;
+  const Standard_Boolean isCompact = theFlags == Graphic3d_RenderingParams::PerfCounters_FrameRate; // only FPS is displayed
+  const Graphic3d_FrameStatsData& aStats = LastDataFrame();
+  if (myIsLongLineFormat
+   && (theFlags & Graphic3d_RenderingParams::PerfCounters_FrameRate) != 0
+   && (theFlags & Graphic3d_RenderingParams::PerfCounters_CPU) != 0)
+  {
+    aBuf << "FPS: "     << std::setfill(' ') << std::setw (isCompact ? aValWidth : 9)  << std::fixed << std::setprecision (1) << aStats.FrameRate()
+         << " [CPU: "   << std::setfill(' ') << std::setw (isCompact ? aValWidth : 10) << std::fixed << std::setprecision (1) << aStats.FrameRateCpu() << "]\n";
+  }
+  else
+  {
+    if ((theFlags & Graphic3d_RenderingParams::PerfCounters_FrameRate) != 0)
+    {
+      aBuf << "FPS:     " << std::setfill(' ') << std::setw (isCompact ? aValWidth : aValWidth + 3) << std::fixed << std::setprecision (1) << aStats.FrameRate()  << "\n";
+    }
+    if ((theFlags & Graphic3d_RenderingParams::PerfCounters_CPU) != 0)
+    {
+      aBuf << "CPU FPS: " << std::setfill(' ') << std::setw (isCompact ? aValWidth : aValWidth + 3) << std::fixed << std::setprecision (1) << aStats.FrameRateCpu() << "\n";
+    }
+  }
+  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Layers) != 0)
+  {
+    if (myIsLongLineFormat)
+    {
+      formatCounter (aBuf, aValWidth, "Layers:  ", aStats[Graphic3d_FrameStatsCounter_NbLayers]);
+      if (HasCulledLayers())
+      {
+        formatCounter (aBuf, aValWidth, " [rendered: ", aStats[Graphic3d_FrameStatsCounter_NbLayersNotCulled], "]");
+      }
+      aBuf << "\n";
+    }
+    else
+    {
+      formatCounter (aBuf, aValWidth + 3, "Layers:  ", aStats[Graphic3d_FrameStatsCounter_NbLayers], "\n");
+    }
+  }
+  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Structures) != 0)
+  {
+    if (myIsLongLineFormat)
+    {
+      formatCounter (aBuf, aValWidth, "Structs: ", aStats[Graphic3d_FrameStatsCounter_NbStructs]);
+      if (HasCulledStructs())
+      {
+        formatCounter (aBuf, aValWidth, " [rendered: ", aStats[Graphic3d_FrameStatsCounter_NbStructsNotCulled], "]");
+      }
+      aBuf << "\n";
+    }
+    else
+    {
+      formatCounter (aBuf, aValWidth + 3, "Structs: ", aStats[Graphic3d_FrameStatsCounter_NbStructs], "\n");
+    }
+  }
+  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Groups) != 0
+   || (theFlags & Graphic3d_RenderingParams::PerfCounters_GroupArrays) != 0
+   || (theFlags & Graphic3d_RenderingParams::PerfCounters_Triangles) != 0
+   || (theFlags & Graphic3d_RenderingParams::PerfCounters_Points) != 0
+   || (!myIsLongLineFormat
+    && ((theFlags & Graphic3d_RenderingParams::PerfCounters_Structures) != 0
+     || (theFlags & Graphic3d_RenderingParams::PerfCounters_Layers) != 0)))
+  {
+    aBuf << "Rendered\n";
+  }
+  if (!myIsLongLineFormat
+   && (theFlags & Graphic3d_RenderingParams::PerfCounters_Layers) != 0)
+  {
+    formatCounter (aBuf, aValWidth, "    Layers: ", aStats[Graphic3d_FrameStatsCounter_NbLayersNotCulled], "\n");
+  }
+  if (!myIsLongLineFormat
+   && (theFlags & Graphic3d_RenderingParams::PerfCounters_Structures) != 0)
+  {
+    formatCounter (aBuf, aValWidth, "   Structs: ", aStats[Graphic3d_FrameStatsCounter_NbStructsNotCulled], "\n");
+  }
+  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Groups) != 0)
+  {
+    formatCounter (aBuf, aValWidth, "    Groups: ", aStats[Graphic3d_FrameStatsCounter_NbGroupsNotCulled], "\n");
+  }
+  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_GroupArrays) != 0)
+  {
+    formatCounter (aBuf, aValWidth, "    Arrays: ", aStats[Graphic3d_FrameStatsCounter_NbElemsNotCulled], "\n");
+    formatCounter (aBuf, aValWidth, "    [fill]: ", aStats[Graphic3d_FrameStatsCounter_NbElemsFillNotCulled], "\n");
+    formatCounter (aBuf, aValWidth, "    [line]: ", aStats[Graphic3d_FrameStatsCounter_NbElemsLineNotCulled], "\n");
+    formatCounter (aBuf, aValWidth, "   [point]: ", aStats[Graphic3d_FrameStatsCounter_NbElemsPointNotCulled], "\n");
+    formatCounter (aBuf, aValWidth, "    [text]: ", aStats[Graphic3d_FrameStatsCounter_NbElemsTextNotCulled], "\n");
+  }
+  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Triangles) != 0)
+  {
+    formatCounter (aBuf, aValWidth, " Triangles: ", aStats[Graphic3d_FrameStatsCounter_NbTrianglesNotCulled], "\n");
+  }
+  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Points) != 0)
+  {
+    formatCounter (aBuf, aValWidth, "    Points: ", aStats[Graphic3d_FrameStatsCounter_NbPointsNotCulled], "\n");
+  }
+  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_EstimMem) != 0)
+  {
+    aBuf << "GPU Memory\n";
+    formatBytes (aBuf, aValWidth, "  Geometry: ", aStats[Graphic3d_FrameStatsCounter_EstimatedBytesGeom], "\n");
+    formatBytes (aBuf, aValWidth, "  Textures: ", aStats[Graphic3d_FrameStatsCounter_EstimatedBytesTextures], "\n");
+    formatBytes (aBuf, aValWidth, "    Frames: ", aStats[Graphic3d_FrameStatsCounter_EstimatedBytesFbos], "\n");
+  }
+
+  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_FrameTime) != 0)
+  {
+    aBuf << "Timers Average\n";
+    formatTime (aBuf, aValWidth, " Elapsed Frame: ", aStats[Graphic3d_FrameStatsTimer_ElapsedFrame], "\n");
+    formatTime (aBuf, aValWidth, "     CPU Frame: ", aStats[Graphic3d_FrameStatsTimer_CpuFrame], "\n");
+    if (myCountersMax[Graphic3d_FrameStatsTimer_CpuPicking] > 0.0)
+    {
+      formatTime (aBuf, aValWidth, "   CPU Picking: ", aStats[Graphic3d_FrameStatsTimer_CpuPicking], "\n");
+    }
+    if (myCountersMax[Graphic3d_FrameStatsTimer_CpuCulling] > 0.0)
+    {
+      formatTime (aBuf, aValWidth, "   CPU Culling: ", aStats[Graphic3d_FrameStatsTimer_CpuCulling], "\n");
+    }
+    if (myCountersMax[Graphic3d_FrameStatsTimer_CpuDynamics])
+    {
+      formatTime (aBuf, aValWidth, "  CPU Dynamics: ", aStats[Graphic3d_FrameStatsTimer_CpuDynamics], "\n");
+    }
+    if ((theFlags & Graphic3d_RenderingParams::PerfCounters_FrameTimeMax) != 0)
+    {
+      aBuf << "Timers Max\n";
+      formatTime (aBuf, aValWidth, "     CPU Frame: ", myCountersMax[Graphic3d_FrameStatsTimer_CpuFrame], "\n");
+      if (myCountersMax[Graphic3d_FrameStatsTimer_CpuPicking] > 0.0)
+      {
+        formatTime (aBuf, aValWidth, "   CPU Picking: ", myCountersMax[Graphic3d_FrameStatsTimer_CpuPicking], "\n");
+      }
+      if (myCountersMax[Graphic3d_FrameStatsTimer_CpuCulling] > 0.0)
+      {
+        formatTime (aBuf, aValWidth, "   CPU Culling: ", myCountersMax[Graphic3d_FrameStatsTimer_CpuCulling], "\n");
+      }
+      if (myCountersMax[Graphic3d_FrameStatsTimer_CpuDynamics])
+      {
+        formatTime (aBuf, aValWidth, "  CPU Dynamics: ", myCountersMax[Graphic3d_FrameStatsTimer_CpuDynamics], "\n");
+      }
+    }
+  }
+
+  return TCollection_AsciiString (aBuf.str().c_str());
+}
+
+// =======================================================================
+// function : FrameStart
+// purpose  :
+// =======================================================================
+void Graphic3d_FrameStats::FrameStart (const Handle(Graphic3d_CView)& theView,
+                                       bool theIsImmediateOnly)
+{
+  const Graphic3d_RenderingParams::PerfCounters aBits = !theView.IsNull()
+                                                      ? theView->RenderingParams().CollectedStats
+                                                      : Graphic3d_RenderingParams::PerfCounters_NONE;
+  if (theIsImmediateOnly
+   && (aBits & Graphic3d_RenderingParams::PerfCounters_SkipImmediate) != 0)
+  {
+    return;
+  }
+
+  const Standard_Integer aNbFrames = Max (!theView.IsNull()
+                                         ? theView->RenderingParams().StatsNbFrames
+                                         : 1, 1);
+  if (myCounters.Size() != aNbFrames)
+  {
+    myCounters.Resize (0, aNbFrames - 1, false);
+    myCounters.Init (Graphic3d_FrameStatsData());
+    myLastFrameIndex = myCounters.Upper();
+  }
+
+  // reset values at the end of frame (after data has been flushed),
+  // so that application can put some counters (like picking time) before FrameStart().
+  //myCountersTmp.Reset();
+
+  myFrameStartTime = myFpsTimer.ElapsedTime();
+  if (!myFpsTimer.IsStarted())
+  {
+    myFpsTimer.Reset();
+    myFpsTimer.Start();
+    myFpsFrameCount = 0;
+  }
+}
+
+// =======================================================================
+// function : FrameEnd
+// purpose  :
+// =======================================================================
+void Graphic3d_FrameStats::FrameEnd (const Handle(Graphic3d_CView)& theView,
+                                     bool theIsImmediateOnly)
+{
+  const Graphic3d_RenderingParams::PerfCounters aBits = !theView.IsNull()
+                                                      ? theView->RenderingParams().CollectedStats
+                                                      : Graphic3d_RenderingParams::PerfCounters_NONE;
+  if (theIsImmediateOnly
+   && (aBits & Graphic3d_RenderingParams::PerfCounters_SkipImmediate) != 0)
+  {
+    return;
+  }
+
+  const double aTime = myFpsTimer.ElapsedTime();
+  myFrameDuration = aTime - myFrameStartTime;
+  ++myFpsFrameCount;
+  if (!theView.IsNull())
+  {
+    myUpdateInterval = theView->RenderingParams().StatsUpdateInterval;
+  }
+
+  if (aTime < myUpdateInterval)
+  {
+    myCountersTmp.FlushTimers (myFpsFrameCount, false);
+    return;
+  }
+
+  if (aTime > gp::Resolution())
+  {
+    // update FPS
+    myFpsTimer.Stop();
+    const double aCpuSec = myFpsTimer.UserTimeCPU();
+
+    myCountersTmp[Graphic3d_FrameStatsTimer_ElapsedFrame]  = aTime;
+    myCountersTmp[Graphic3d_FrameStatsTimer_CpuFrame]      = aCpuSec;
+    myCountersTmp.ChangeFrameRate()    = double(myFpsFrameCount) / aTime;
+    myCountersTmp.ChangeFrameRateCpu() = aCpuSec > gp::Resolution()
+                                       ? double(myFpsFrameCount) / aCpuSec
+                                       : -1.0;
+    myCountersTmp.FlushTimers (myFpsFrameCount, true);
+    myCountersMax.FillMax (myCountersTmp);
+    myFpsTimer.Reset();
+    myFpsTimer.Start();
+    myFpsFrameCount = 0;
+  }
+
+  // update structure counters
+  if (theView.IsNull())
+  {
+    myCounters.SetValue (myLastFrameIndex, myCountersTmp);
+    myCountersTmp.Reset();
+    return;
+  }
+
+  updateStatistics (theView, theIsImmediateOnly);
+
+  if (++myLastFrameIndex > myCounters.Upper())
+  {
+    myLastFrameIndex = myCounters.Lower();
+  }
+  myCounters.SetValue (myLastFrameIndex, myCountersTmp);
+  myCountersTmp.Reset();
+}
diff --git a/src/Graphic3d/Graphic3d_FrameStats.hxx b/src/Graphic3d/Graphic3d_FrameStats.hxx
new file mode 100644 (file)
index 0000000..56cc1ba
--- /dev/null
@@ -0,0 +1,136 @@
+// Copyright (c) 2017 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Graphic3d_FrameStats_HeaderFile
+#define _Graphic3d_FrameStats_HeaderFile
+
+#include <Graphic3d_FrameStatsData.hxx>
+#include <Graphic3d_RenderingParams.hxx>
+#include <Standard_Type.hxx>
+#include <Standard_Transient.hxx>
+
+class Graphic3d_CView;
+
+//! Class storing the frame statistics.
+class Graphic3d_FrameStats : public Standard_Transient
+{
+  DEFINE_STANDARD_RTTIEXT(Graphic3d_FrameStats, Standard_Transient)
+public:
+
+  //! Default constructor.
+  Standard_EXPORT Graphic3d_FrameStats();
+
+  //! Destructor.
+  Standard_EXPORT virtual ~Graphic3d_FrameStats();
+
+  //! Returns interval in seconds for updating meters across several frames; 1 second by default.
+  Standard_Real UpdateInterval() const { return myUpdateInterval; }
+
+  //! Sets interval in seconds for updating values.
+  void SetUpdateInterval (Standard_Real theInterval) { myUpdateInterval = theInterval; }
+
+  //! Prefer longer lines over more greater of lines.
+  Standard_Boolean IsLongLineFormat() const { return myIsLongLineFormat; }
+
+  //! Set if format should prefer longer lines over greater number of lines.
+  void SetLongLineFormat (Standard_Boolean theValue) { myIsLongLineFormat = theValue; }
+
+  //! Frame redraw started.
+  Standard_EXPORT virtual void FrameStart (const Handle(Graphic3d_CView)& theView,
+                                           bool theIsImmediateOnly);
+
+  //! Frame redraw finished.
+  Standard_EXPORT virtual void FrameEnd (const Handle(Graphic3d_CView)& theView,
+                                         bool theIsImmediateOnly);
+
+public:
+
+  //! Returns formatted string.
+  Standard_EXPORT virtual TCollection_AsciiString FormatStats (Graphic3d_RenderingParams::PerfCounters theFlags) const;
+
+  //! Returns duration of the last frame in seconds.
+  Standard_Real FrameDuration() const { return myFrameDuration; }
+
+  //! Returns FPS (frames per seconds, elapsed time).
+  //! This number indicates an actual frame rate averaged for several frames within UpdateInterval() duration,
+  //! basing on a real elapsed time between updates.
+  Standard_Real FrameRate() const { return LastDataFrame().FrameRate(); }
+
+  //! Returns CPU FPS (frames per seconds, CPU time).
+  //! This number indicates a PREDICTED frame rate,
+  //! basing on CPU elapsed time between updates and NOT real elapsed time (which might include periods of CPU inactivity).
+  //! Number is expected to be greater then actual frame rate returned by FrameRate().
+  //! Values significantly greater actual frame rate indicate that rendering is limited by GPU performance (CPU is stalled in-between),
+  //! while values around actual frame rate indicate rendering being limited by CPU performance (GPU is stalled in-between).
+  Standard_Real FrameRateCpu() const { return LastDataFrame().FrameRateCpu(); }
+
+  //! Returns value of specified counter, cached between stats updates.
+  //! Should NOT be called between ::FrameStart() and ::FrameEnd() calls.
+  Standard_Size CounterValue (Graphic3d_FrameStatsCounter theCounter) const { return LastDataFrame()[theCounter]; }
+
+  //! Returns value of specified timer for modification, should be called between ::FrameStart() and ::FrameEnd() calls.
+  //! Should NOT be called between ::FrameStart() and ::FrameEnd() calls.
+  Standard_Real TimerValue (Graphic3d_FrameStatsTimer theTimer) const { return LastDataFrame()[theTimer]; }
+
+  //! Returns TRUE if some Layers have been culled.
+  Standard_Boolean HasCulledLayers() const { return LastDataFrame()[Graphic3d_FrameStatsCounter_NbLayersNotCulled] != LastDataFrame()[Graphic3d_FrameStatsCounter_NbLayers]; }
+
+  //! Returns TRUE if some structures have been culled.
+  Standard_Boolean HasCulledStructs() const { return LastDataFrame()[Graphic3d_FrameStatsCounter_NbStructsNotCulled] != LastDataFrame()[Graphic3d_FrameStatsCounter_NbStructs]; }
+
+  //! Returns last data frame, cached between stats updates.
+  //! Should NOT be called between ::FrameStart() and ::FrameEnd() calls.
+  const Graphic3d_FrameStatsData& LastDataFrame() const { return myCounters.Value (myLastFrameIndex); }
+
+  //! Returns last data frame index.
+  Standard_Integer LastDataFrameIndex() const { return myLastFrameIndex; }
+
+  //! Returns data frames.
+  const NCollection_Array1<Graphic3d_FrameStatsData>& DataFrames() const { return myCounters; }
+
+  //! Returns data frames.
+  NCollection_Array1<Graphic3d_FrameStatsData>& ChangeDataFrames() { return myCounters; }
+
+public:
+
+  //! Returns value of specified counter for modification, should be called between ::FrameStart() and ::FrameEnd() calls.
+  Standard_Size& ChangeCounter (Graphic3d_FrameStatsCounter theCounter) { return ActiveDataFrame()[theCounter]; }
+
+  //! Returns value of specified timer for modification, should be called between ::FrameStart() and ::FrameEnd() calls.
+  Standard_Real& ChangeTimer (Graphic3d_FrameStatsTimer theTimer) { return ActiveDataFrame()[theTimer]; }
+
+  //! Returns currently filling data frame for modification, should be called between ::FrameStart() and ::FrameEnd() calls.
+  Graphic3d_FrameStatsDataTmp& ActiveDataFrame() { return myCountersTmp; }
+
+protected:
+
+  //! Method to collect statistics from the View; called by FrameEnd().
+  virtual void updateStatistics (const Handle(Graphic3d_CView)& theView,
+                                 bool theIsImmediateOnly) = 0;
+
+protected:
+
+  OSD_Timer        myFpsTimer;                //!< timer for FPS measurements
+  Standard_Real    myFrameStartTime;          //!< time at the beginning of frame redraw
+  Standard_Real    myFrameDuration;           //!< frame duration
+  Standard_Real    myUpdateInterval;          //!< interval to update meters
+  Standard_Size    myFpsFrameCount;           //!< FPS counter (within short measurement time slice)
+  NCollection_Array1<Graphic3d_FrameStatsData> myCounters; //!< data frames history
+  Graphic3d_FrameStatsDataTmp myCountersTmp;  //!< data frame values filled to be filled between FrameStart() and FrameEnd() calls
+  Graphic3d_FrameStatsData    myCountersMax;  //!< data frame values with absolute maximum values in the history
+  Standard_Integer myLastFrameIndex;          //!< last data frame index
+  Standard_Boolean myIsLongLineFormat;        //!< prefer longer lines over greater number of lines
+
+};
+
+#endif // _Graphic3d_FrameStats_HeaderFile
diff --git a/src/Graphic3d/Graphic3d_FrameStatsCounter.hxx b/src/Graphic3d/Graphic3d_FrameStatsCounter.hxx
new file mode 100644 (file)
index 0000000..f0a41e5
--- /dev/null
@@ -0,0 +1,38 @@
+// Copyright (c) 2018 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Graphic3d_FrameStatsCounter_HeaderFile
+#define _Graphic3d_FrameStatsCounter_HeaderFile
+
+//! Stats counter.
+enum Graphic3d_FrameStatsCounter
+{
+  Graphic3d_FrameStatsCounter_NbLayers = 0,           //!< number of ZLayers
+  Graphic3d_FrameStatsCounter_NbLayersNotCulled,      //!< number of not culled ZLayers
+  Graphic3d_FrameStatsCounter_NbStructs,              //!< number of defined OpenGl_Structure
+  Graphic3d_FrameStatsCounter_NbStructsNotCulled,     //!< number of not culled OpenGl_Structure
+  Graphic3d_FrameStatsCounter_NbGroupsNotCulled,      //!< number of not culled OpenGl_Group
+  Graphic3d_FrameStatsCounter_NbElemsNotCulled,       //!< number of not culled OpenGl_Element
+  Graphic3d_FrameStatsCounter_NbElemsFillNotCulled,   //!< number of not culled OpenGl_PrimitiveArray drawing triangles
+  Graphic3d_FrameStatsCounter_NbElemsLineNotCulled,   //!< number of not culled OpenGl_PrimitiveArray drawing lines
+  Graphic3d_FrameStatsCounter_NbElemsPointNotCulled,  //!< number of not culled OpenGl_PrimitiveArray drawing points
+  Graphic3d_FrameStatsCounter_NbElemsTextNotCulled,   //!< number of not culled OpenGl_Text
+  Graphic3d_FrameStatsCounter_NbTrianglesNotCulled,   //!< number of not culled (as structure) triangles
+  Graphic3d_FrameStatsCounter_NbPointsNotCulled,      //!< number of not culled (as structure) points
+  Graphic3d_FrameStatsCounter_EstimatedBytesGeom,     //!< estimated GPU memory used for geometry
+  Graphic3d_FrameStatsCounter_EstimatedBytesFbos,     //!< estimated GPU memory used for FBOs
+  Graphic3d_FrameStatsCounter_EstimatedBytesTextures, //!< estimated GPU memory used for textures
+};
+enum { Graphic3d_FrameStatsCounter_NB = Graphic3d_FrameStatsCounter_EstimatedBytesTextures + 1 };
+
+#endif // _Graphic3d_FrameStatsCounter_HeaderFile
diff --git a/src/Graphic3d/Graphic3d_FrameStatsData.cxx b/src/Graphic3d/Graphic3d_FrameStatsData.cxx
new file mode 100644 (file)
index 0000000..df50b3d
--- /dev/null
@@ -0,0 +1,126 @@
+// Copyright (c) 2018 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <Graphic3d_FrameStatsData.hxx>
+
+// =======================================================================
+// function : Graphic3d_FrameStatsData
+// purpose  :
+// =======================================================================
+Graphic3d_FrameStatsData::Graphic3d_FrameStatsData()
+: myFps (-1.0),
+  myFpsCpu (-1.0)
+{
+  myCounters .resize (Graphic3d_FrameStatsCounter_NB, 0);
+  myTimers   .resize (Graphic3d_FrameStatsTimer_NB, 0.0);
+  myTimersMin.resize (Graphic3d_FrameStatsTimer_NB, RealLast());
+  myTimersMax.resize (Graphic3d_FrameStatsTimer_NB, 0.0);
+  Reset();
+}
+
+// =======================================================================
+// function : operator=
+// purpose  :
+// =======================================================================
+Graphic3d_FrameStatsData& Graphic3d_FrameStatsData::operator= (const Graphic3d_FrameStatsData& theOther)
+{
+  myFps         = theOther.myFps;
+  myFpsCpu      = theOther.myFpsCpu;
+  myCounters    = theOther.myCounters;
+  myTimers      = theOther.myTimers;
+  myTimersMin   = theOther.myTimersMin;
+  myTimersMax   = theOther.myTimersMax;
+  return *this;
+}
+
+// =======================================================================
+// function : Reset
+// purpose  :
+// =======================================================================
+void Graphic3d_FrameStatsData::Reset()
+{
+  myFps    = -1.0;
+  myFpsCpu = -1.0;
+  myCounters .assign (myCounters.size(),  0);
+  myTimers   .assign (myTimers.size(),    0.0);
+  myTimersMin.assign (myTimersMin.size(), RealLast());
+  myTimersMax.assign (myTimersMax.size(), 0.0);
+}
+
+// =======================================================================
+// function : FillMax
+// purpose  :
+// =======================================================================
+void Graphic3d_FrameStatsData::FillMax (const Graphic3d_FrameStatsData& theOther)
+{
+  myFps    = Max (myFps,    theOther.myFps);
+  myFpsCpu = Max (myFpsCpu, theOther.myFpsCpu);
+  for (size_t aCounterIter = 0; aCounterIter < myCounters.size(); ++aCounterIter)
+  {
+    myCounters[aCounterIter] = myCounters[aCounterIter] > theOther.myCounters[aCounterIter] ? myCounters[aCounterIter] : theOther.myCounters[aCounterIter];
+  }
+  for (size_t aTimerIter = 0; aTimerIter < myTimers.size(); ++aTimerIter)
+  {
+    myTimersMax[aTimerIter] = Max (myTimersMax[aTimerIter], theOther.myTimersMax[aTimerIter]);
+    myTimersMin[aTimerIter] = Min (myTimersMin[aTimerIter], theOther.myTimersMin[aTimerIter]);
+    myTimers   [aTimerIter] = myTimersMax[aTimerIter];
+  }
+}
+
+// =======================================================================
+// function : Graphic3d_FrameStatsDataTmp
+// purpose  :
+// =======================================================================
+Graphic3d_FrameStatsDataTmp::Graphic3d_FrameStatsDataTmp()
+{
+  myOsdTimers .resize (Graphic3d_FrameStatsTimer_NB, OSD_Timer (true));
+  myTimersPrev.resize (Graphic3d_FrameStatsTimer_NB, 0.0);
+}
+
+// =======================================================================
+// function : FlushTimers
+// purpose  :
+// =======================================================================
+void Graphic3d_FrameStatsDataTmp::FlushTimers (Standard_Size theNbFrames, bool theIsFinal)
+{
+  for (size_t aTimerIter = 0; aTimerIter < myTimers.size(); ++aTimerIter)
+  {
+    const Standard_Real aFrameTime = myTimers[aTimerIter] - myTimersPrev[aTimerIter];
+    myTimersMax [aTimerIter] = Max (myTimersMax[aTimerIter], aFrameTime);
+    myTimersMin [aTimerIter] = Min (myTimersMin[aTimerIter], aFrameTime);
+    myTimersPrev[aTimerIter] = myTimers[aTimerIter];
+  }
+
+  if (theIsFinal)
+  {
+    const Standard_Real aNbFrames = (Standard_Real )theNbFrames;
+    for (size_t aTimerIter = 0; aTimerIter < myTimers.size(); ++aTimerIter)
+    {
+      myTimers[aTimerIter] /= aNbFrames;
+    }
+  }
+}
+
+// =======================================================================
+// function : Reset
+// purpose  :
+// =======================================================================
+void Graphic3d_FrameStatsDataTmp::Reset()
+{
+  Graphic3d_FrameStatsData::Reset();
+  myTimersPrev.assign (myTimersPrev.size(), 0.0);
+  for (size_t aTimerIter = 0; aTimerIter < myOsdTimers.size(); ++aTimerIter)
+  {
+    myOsdTimers[aTimerIter].Reset();
+  }
+}
diff --git a/src/Graphic3d/Graphic3d_FrameStatsData.hxx b/src/Graphic3d/Graphic3d_FrameStatsData.hxx
new file mode 100644 (file)
index 0000000..f84f342
--- /dev/null
@@ -0,0 +1,118 @@
+// Copyright (c) 2018 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Graphic3d_FrameStatsData_HeaderFile
+#define _Graphic3d_FrameStatsData_HeaderFile
+
+#include <NCollection_Array1.hxx>
+#include <Graphic3d_FrameStatsCounter.hxx>
+#include <Graphic3d_FrameStatsTimer.hxx>
+#include <OSD_Timer.hxx>
+
+#include <vector>
+
+//! Data frame definition.
+class Graphic3d_FrameStatsData
+{
+public:
+  DEFINE_STANDARD_ALLOC
+
+  //! Returns FPS (frames per seconds, elapsed time).
+  //! This number indicates an actual frame rate averaged for several frames within UpdateInterval() duration,
+  //! basing on a real elapsed time between updates.
+  Standard_Real FrameRate() const { return myFps; }
+
+  //! Returns CPU FPS (frames per seconds, CPU time).
+  //! This number indicates a PREDICTED frame rate,
+  //! basing on CPU elapsed time between updates and NOT real elapsed time (which might include periods of CPU inactivity).
+  //! Number is expected to be greater then actual frame rate returned by FrameRate().
+  //! Values significantly greater actual frame rate indicate that rendering is limited by GPU performance (CPU is stalled in-between),
+  //! while values around actual frame rate indicate rendering being limited by CPU performance (GPU is stalled in-between).
+  Standard_Real FrameRateCpu() const { return myFpsCpu; }
+
+  //! Get counter value.
+  Standard_Size CounterValue (Graphic3d_FrameStatsCounter theIndex) const { return myCounters[theIndex]; }
+
+  //! Get counter value.
+  Standard_Size operator[] (Graphic3d_FrameStatsCounter theIndex) const { return CounterValue (theIndex); }
+
+  //! Get timer value.
+  Standard_Real TimerValue (Graphic3d_FrameStatsTimer theIndex) const { return myTimers[theIndex]; }
+
+  //! Get timer value.
+  Standard_Real operator[] (Graphic3d_FrameStatsTimer theIndex) const { return TimerValue (theIndex); }
+
+  //! Empty constructor.
+  Standard_EXPORT Graphic3d_FrameStatsData();
+
+  //! Assignment operator.
+  Standard_EXPORT Graphic3d_FrameStatsData& operator= (const Graphic3d_FrameStatsData& theOther);
+
+  //! Reset data.
+  Standard_EXPORT void Reset();
+
+  //! Fill with maximum values.
+  Standard_EXPORT void FillMax (const Graphic3d_FrameStatsData& theOther);
+
+protected:
+  std::vector<Standard_Size> myCounters;  //!< counters
+  std::vector<Standard_Real> myTimers;    //!< timers
+  std::vector<Standard_Real> myTimersMin; //!< minimal values of timers
+  std::vector<Standard_Real> myTimersMax; //!< maximum values of timers
+  Standard_Real              myFps;       //!< FPS     meter (frames per seconds, elapsed time)
+  Standard_Real              myFpsCpu;    //!< CPU FPS meter (frames per seconds, CPU time)
+};
+
+//! Temporary data frame definition.
+class Graphic3d_FrameStatsDataTmp : public Graphic3d_FrameStatsData
+{
+public:
+  //! Empty constructor.
+  Standard_EXPORT Graphic3d_FrameStatsDataTmp();
+
+  //! Compute average data considering the amount of rendered frames.
+  Standard_EXPORT void FlushTimers (Standard_Size theNbFrames, bool theIsFinal);
+
+  //! Reset data.
+  Standard_EXPORT void Reset();
+
+  //! Assignment operator (skip copying irrelevant properties).
+  void operator= (const Graphic3d_FrameStatsData& theOther) { Graphic3d_FrameStatsData::operator= (theOther); }
+
+  //! Returns FPS (frames per seconds, elapsed time).
+  Standard_Real& ChangeFrameRate() { return myFps; }
+
+  //! Returns CPU FPS (frames per seconds, CPU time).
+  Standard_Real& ChangeFrameRateCpu() { return myFpsCpu; }
+
+  //! Return a timer object for time measurements.
+  OSD_Timer& ChangeTimer (Graphic3d_FrameStatsTimer theTimer) { return myOsdTimers[theTimer]; }
+
+  //! Get counter value.
+  Standard_Size& ChangeCounterValue (Graphic3d_FrameStatsCounter theIndex) { return myCounters[theIndex]; }
+
+  //! Modify counter value.
+  Standard_Size& operator[] (Graphic3d_FrameStatsCounter theIndex) { return ChangeCounterValue (theIndex); }
+
+  //! Modify timer value.
+  Standard_Real& ChangeTimerValue (Graphic3d_FrameStatsTimer theIndex) { return myTimers[theIndex]; }
+
+  //! Modify timer value.
+  Standard_Real& operator[] (Graphic3d_FrameStatsTimer theIndex) { return ChangeTimerValue (theIndex); }
+
+protected:
+  std::vector<OSD_Timer>     myOsdTimers;  //!< precise timers for time measurements
+  std::vector<Standard_Real> myTimersPrev; //!< previous timers values
+};
+
+#endif // _Graphic3d_FrameStatsData_HeaderFile
diff --git a/src/Graphic3d/Graphic3d_FrameStatsTimer.hxx b/src/Graphic3d/Graphic3d_FrameStatsTimer.hxx
new file mode 100644 (file)
index 0000000..a4aff6f
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright (c) 2018 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Graphic3d_FrameStatsTimer_HeaderFile
+#define _Graphic3d_FrameStatsTimer_HeaderFile
+
+//! Timers for collecting frame performance statistics.
+enum Graphic3d_FrameStatsTimer
+{
+  Graphic3d_FrameStatsTimer_ElapsedFrame,
+  Graphic3d_FrameStatsTimer_CpuFrame,
+  Graphic3d_FrameStatsTimer_CpuCulling,
+  Graphic3d_FrameStatsTimer_CpuPicking,
+  Graphic3d_FrameStatsTimer_CpuDynamics,
+};
+enum { Graphic3d_FrameStatsTimer_NB = Graphic3d_FrameStatsTimer_CpuDynamics + 1 };
+
+#endif // _Graphic3d_FrameStatsTimer_HeaderFile
index 6c53b3f..001cef2 100644 (file)
@@ -46,11 +46,12 @@ public:
   };
 
   //! Statistics display flags.
+  //! If not specified otherwise, the counter value is computed for a single rendered frame.
   enum PerfCounters
   {
     PerfCounters_NONE        = 0x000, //!< no stats
-    PerfCounters_FrameRate   = 0x001, //!< Frame Rate
-    PerfCounters_CPU         = 0x002, //!< CPU utilization
+    PerfCounters_FrameRate   = 0x001, //!< Frame Rate,        frames per second (number of frames within elapsed time)
+    PerfCounters_CPU         = 0x002, //!< CPU utilization as frames per second (number of frames within CPU utilization time (rendering thread))
     PerfCounters_Layers      = 0x004, //!< count layers (groups of structures)
     PerfCounters_Structures  = 0x008, //!< count low-level Structures (normal unhighlighted Presentable Object is usually represented by 1 Structure)
     //
@@ -61,6 +62,11 @@ public:
     PerfCounters_Points      = 0x080, //!< count Points
     //
     PerfCounters_EstimMem    = 0x100, //!< estimated GPU memory usage
+    //
+    PerfCounters_FrameTime   = 0x200, //!< frame CPU utilization time (rendering thread); @sa Graphic3d_FrameStatsTimer
+    PerfCounters_FrameTimeMax= 0x400, //!< maximum frame times
+    //
+    PerfCounters_SkipImmediate = 0x800, //!< do not include immediate viewer updates (e.g. lazy updates without redrawing entire view content)
     //! show basic statistics
     PerfCounters_Basic = PerfCounters_FrameRate | PerfCounters_CPU | PerfCounters_Layers | PerfCounters_Structures,
     //! extended (verbose) statistics
@@ -68,6 +74,10 @@ public:
                           | PerfCounters_Groups | PerfCounters_GroupArrays
                           | PerfCounters_Triangles | PerfCounters_Points
                           | PerfCounters_EstimMem,
+    //! all counters
+    PerfCounters_All = PerfCounters_Extended
+                     | PerfCounters_FrameTime
+                     | PerfCounters_FrameTimeMax,
   };
 
 public:
@@ -106,10 +116,14 @@ public:
     AnaglyphFilter (Anaglyph_RedCyan_Optimized),
     ToReverseStereo (Standard_False),
     //
-    StatsPosition (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_LEFT_UPPER, Graphic3d_Vec2i (20, 20))),
+    StatsPosition (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_LEFT_UPPER,  Graphic3d_Vec2i (20, 20))),
+    ChartPosition (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_RIGHT_UPPER, Graphic3d_Vec2i (20, 20))),
+    ChartSize (-1, -1),
     StatsTextAspect (new Graphic3d_AspectText3d()),
     StatsUpdateInterval (1.0),
     StatsTextHeight (16),
+    StatsNbFrames (1),
+    StatsMaxChartTime (0.1f),
     CollectedStats (PerfCounters_Basic),
     ToShowStats (Standard_False),
     //
@@ -179,12 +193,16 @@ public:
   Standard_Boolean                  ToReverseStereo;             //!< flag to reverse stereo pair, FALSE by default
 
   Handle(Graphic3d_TransformPers)   StatsPosition;               //!< location of stats, upper-left position by default
+  Handle(Graphic3d_TransformPers)   ChartPosition;               //!< location of stats chart, upper-right position by default
+  Graphic3d_Vec2i                   ChartSize;                   //!< chart size in pixels, (-1, -1) by default which means that chart will occupy a portion of viewport
   Handle(Graphic3d_AspectText3d)    StatsTextAspect;             //!< stats text aspect
   Standard_ShortReal                StatsUpdateInterval;         //!< time interval between stats updates in seconds, 1.0 second by default;
                                                                  //!  too often updates might impact performance and will smear text within widgets
                                                                  //!  (especially framerate, which is better averaging);
                                                                  //!  0.0 interval will force updating on each frame
   Standard_Integer                  StatsTextHeight;             //!< stats text size; 16 by default
+  Standard_Integer                  StatsNbFrames;               //!< number of data frames to collect history; 1 by default
+  Standard_ShortReal                StatsMaxChartTime;           //!< upper time limit within frame chart in seconds; 0.1 seconds by default (100 ms or 10 FPS)
   PerfCounters                      CollectedStats;              //!< performance counters to collect, PerfCounters_Basic by default;
                                                                  //!  too verbose options might impact rendering performance,
                                                                  //!  because some counters might lack caching optimization (and will require expensive iteration through all data structures)
index 064873e..13bf652 100644 (file)
@@ -611,6 +611,10 @@ public: //! @name methods to alter or retrieve current state
   //! Return structure holding frame statistics.
   const Handle(OpenGl_FrameStats)& FrameStats() const { return myFrameStats; }
 
+  //! Set structure holding frame statistics.
+  //! This call makes sense only if application defines OpenGl_FrameStats sub-class.
+  void SetFrameStats (const Handle(OpenGl_FrameStats)& theStats) { myFrameStats = theStats; }
+
   //! Return cached viewport definition (x, y, width, height).
   const Standard_Integer* Viewport() const { return myViewport; }
 
index f1189cd..cb64c5a 100644 (file)
 #include <OpenGl_View.hxx>
 #include <OpenGl_Workspace.hxx>
 
-IMPLEMENT_STANDARD_RTTIEXT(OpenGl_FrameStats, Standard_Transient)
+IMPLEMENT_STANDARD_RTTIEXT(OpenGl_FrameStats, Graphic3d_FrameStats)
 
 namespace
 {
-  //! Format counter.
-  static std::ostream& formatCounter (std::ostream& theStream,
-                                      Standard_Integer theWidth,
-                                      const char* thePrefix,
-                                      Standard_Size theValue,
-                                      const char* thePostfix = NULL)
-  {
-    if (thePrefix != NULL)
-    {
-      theStream << thePrefix;
-    }
-    theStream << std::setfill(' ') << std::setw (theWidth);
-    if (theValue >= 1000000000)
-    {
-      Standard_Real aValM = Standard_Real(theValue) / 1000000000.0;
-      theStream << std::fixed << std::setprecision (1) << aValM << "G";
-    }
-    else if (theValue >= 1000000)
-    {
-      Standard_Real aValM = Standard_Real(theValue) / 1000000.0;
-      theStream << std::fixed << std::setprecision (1) << aValM << "M";
-    }
-    else if (theValue >= 1000)
-    {
-      Standard_Real aValK = Standard_Real(theValue) / 1000.0;
-      theStream << std::fixed << std::setprecision (1) << aValK << "k";
-    }
-    else
-    {
-      theStream << theValue;
-    }
-    if (thePostfix != NULL)
-    {
-      theStream << thePostfix;
-    }
-    return theStream;
-  }
-
-  //! Format memory counter.
-  static std::ostream& formatBytes (std::ostream& theStream,
-                                    Standard_Integer theWidth,
-                                    const char* thePrefix,
-                                    Standard_Size theValue,
-                                    const char* thePostfix = NULL)
-  {
-    if (thePrefix != NULL)
-    {
-      theStream << thePrefix;
-    }
-    theStream << std::setfill(' ') << std::setw (theWidth);
-    if (theValue >= 1024 * 1024 * 1024)
-    {
-      Standard_Real aValM = Standard_Real(theValue) / (1024.0 * 1024.0 * 1024.0);
-      theStream << std::fixed << std::setprecision (1) << aValM << " GiB";
-    }
-    else if (theValue >= 1024 * 1024)
-    {
-      Standard_Real aValM = Standard_Real(theValue) / (1024.0 * 1024.0);
-      theStream << std::fixed << std::setprecision (1) << aValM << " MiB";
-    }
-    else if (theValue >= 1024)
-    {
-      Standard_Real aValK = Standard_Real(theValue) / 1024.0;
-      theStream << std::fixed << std::setprecision (1) << aValK << " KiB";
-    }
-    else
-    {
-      theStream << theValue;
-    }
-    if (thePostfix != NULL)
-    {
-      theStream << thePostfix;
-    }
-    return theStream;
-  }
-
   //! Return estimated data size.
   static Standard_Size estimatedDataSize (const Handle(OpenGl_Resource)& theRes)
   {
@@ -109,17 +33,8 @@ namespace
 // purpose  :
 // =======================================================================
 OpenGl_FrameStats::OpenGl_FrameStats()
-: myFpsTimer (Standard_True),
-  myFrameStartTime (0.0),
-  myFrameDuration  (0.0),
-  myFps    (-1.0),
-  myFpsCpu (-1.0),
-  myUpdateInterval (1.0),
-  myFpsFrameCount (0),
-  myIsLongLineFormat (Standard_False)
 {
-  memset (myCounters,    0, sizeof(myCounters));
-  memset (myCountersTmp, 0, sizeof(myCountersTmp));
+  //
 }
 
 // =======================================================================
@@ -132,173 +47,49 @@ OpenGl_FrameStats::~OpenGl_FrameStats()
 }
 
 // =======================================================================
-// function : FormatStats
+// function : IsFrameUpdated
 // purpose  :
 // =======================================================================
-TCollection_AsciiString OpenGl_FrameStats::FormatStats (Graphic3d_RenderingParams::PerfCounters theFlags) const
+bool OpenGl_FrameStats::IsFrameUpdated (Handle(OpenGl_FrameStats)& thePrev) const
 {
-  const Standard_Integer aValWidth = 5;
-  std::stringstream aBuf;
-  const Standard_Boolean isCompact = theFlags == Graphic3d_RenderingParams::PerfCounters_FrameRate; // only FPS is displayed
-  if (myIsLongLineFormat
-   && (theFlags & Graphic3d_RenderingParams::PerfCounters_FrameRate) != 0
-   && (theFlags & Graphic3d_RenderingParams::PerfCounters_CPU) != 0)
-  {
-    aBuf << "FPS: "     << std::setfill(' ') << std::setw (isCompact ? aValWidth : 9)  << std::fixed << std::setprecision (1) << myFps
-         << " [CPU: "   << std::setfill(' ') << std::setw (isCompact ? aValWidth : 10) << std::fixed << std::setprecision (1) << myFpsCpu << "]\n";
-  }
-  else
-  {
-    if ((theFlags & Graphic3d_RenderingParams::PerfCounters_FrameRate) != 0)
-    {
-      aBuf << "FPS:     " << std::setfill(' ') << std::setw (isCompact ? aValWidth : aValWidth + 3) << std::fixed << std::setprecision (1) << myFps  << "\n";
-    }
-    if ((theFlags & Graphic3d_RenderingParams::PerfCounters_CPU) != 0)
-    {
-      aBuf << "CPU FPS: " << std::setfill(' ') << std::setw (isCompact ? aValWidth : aValWidth + 3) << std::fixed << std::setprecision (1) << myFpsCpu << "\n";
-    }
-  }
-  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Layers) != 0)
-  {
-    if (myIsLongLineFormat)
-    {
-      formatCounter (aBuf, aValWidth, "Layers:  ", myCounters[Counter_NbLayers]);
-      if (HasCulledLayers())
-      {
-        formatCounter (aBuf, aValWidth, " [rendered: ", myCounters[Counter_NbLayersNotCulled], "]");
-      }
-      aBuf << "\n";
-    }
-    else
-    {
-      formatCounter (aBuf, aValWidth + 3, "Layers:  ", myCounters[Counter_NbLayers], "\n");
-    }
-  }
-  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Structures) != 0)
-  {
-    if (myIsLongLineFormat)
-    {
-      formatCounter (aBuf, aValWidth, "Structs: ", myCounters[Counter_NbStructs]);
-      if (HasCulledStructs())
-      {
-        formatCounter (aBuf, aValWidth, " [rendered: ", myCounters[Counter_NbStructsNotCulled], "]");
-      }
-      aBuf << "\n";
-    }
-    else
-    {
-      formatCounter (aBuf, aValWidth + 3, "Structs: ", myCounters[Counter_NbStructs], "\n");
-    }
-  }
-  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Groups) != 0
-   || (theFlags & Graphic3d_RenderingParams::PerfCounters_GroupArrays) != 0
-   || (theFlags & Graphic3d_RenderingParams::PerfCounters_Triangles) != 0
-   || (theFlags & Graphic3d_RenderingParams::PerfCounters_Points) != 0
-   || (!myIsLongLineFormat
-    && ((theFlags & Graphic3d_RenderingParams::PerfCounters_Structures) != 0
-     || (theFlags & Graphic3d_RenderingParams::PerfCounters_Layers) != 0)))
-  {
-    aBuf << "Rendered\n";
-  }
-  if (!myIsLongLineFormat
-   && (theFlags & Graphic3d_RenderingParams::PerfCounters_Layers) != 0)
-  {
-    formatCounter (aBuf, aValWidth, "    Layers: ", myCounters[Counter_NbLayersNotCulled], "\n");
-  }
-  if (!myIsLongLineFormat
-   && (theFlags & Graphic3d_RenderingParams::PerfCounters_Structures) != 0)
-  {
-    formatCounter (aBuf, aValWidth, "   Structs: ", myCounters[Counter_NbStructsNotCulled], "\n");
-  }
-  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Groups) != 0)
-  {
-    formatCounter (aBuf, aValWidth, "    Groups: ", myCounters[Counter_NbGroupsNotCulled], "\n");
-  }
-  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_GroupArrays) != 0)
-  {
-    formatCounter (aBuf, aValWidth, "    Arrays: ", myCounters[Counter_NbElemsNotCulled], "\n");
-    formatCounter (aBuf, aValWidth, "    [fill]: ", myCounters[Counter_NbElemsFillNotCulled], "\n");
-    formatCounter (aBuf, aValWidth, "    [line]: ", myCounters[Counter_NbElemsLineNotCulled], "\n");
-    formatCounter (aBuf, aValWidth, "   [point]: ", myCounters[Counter_NbElemsPointNotCulled], "\n");
-    formatCounter (aBuf, aValWidth, "    [text]: ", myCounters[Counter_NbElemsTextNotCulled], "\n");
-  }
-  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Triangles) != 0)
-  {
-    formatCounter (aBuf, aValWidth, " Triangles: ", myCounters[Counter_NbTrianglesNotCulled], "\n");
-  }
-  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_Points) != 0)
+  const Graphic3d_FrameStatsData& aFrame = LastDataFrame();
+  if (thePrev.IsNull())
   {
-    formatCounter (aBuf, aValWidth, "    Points: ", myCounters[Counter_NbPointsNotCulled], "\n");
+    thePrev = new OpenGl_FrameStats();
   }
-  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_EstimMem) != 0)
+  // check just a couple of major counters
+  else if (myLastFrameIndex == thePrev->myLastFrameIndex
+        && Abs (aFrame.FrameRate()    - thePrev->myCountersTmp.FrameRate())    <= 0.001
+        && Abs (aFrame.FrameRateCpu() - thePrev->myCountersTmp.FrameRateCpu()) <= 0.001
+        && aFrame[Graphic3d_FrameStatsCounter_NbLayers]           == thePrev->myCountersTmp[Graphic3d_FrameStatsCounter_NbLayers]
+        && aFrame[Graphic3d_FrameStatsCounter_NbLayersNotCulled]  == thePrev->myCountersTmp[Graphic3d_FrameStatsCounter_NbLayersNotCulled]
+        && aFrame[Graphic3d_FrameStatsCounter_NbStructs]          == thePrev->myCountersTmp[Graphic3d_FrameStatsCounter_NbStructs]
+        && aFrame[Graphic3d_FrameStatsCounter_NbStructsNotCulled] == thePrev->myCountersTmp[Graphic3d_FrameStatsCounter_NbStructsNotCulled])
   {
-    aBuf << "GPU Memory\n";
-    formatBytes (aBuf, aValWidth, "  Geometry: ", myCounters[Counter_EstimatedBytesGeom], "\n");
-    formatBytes (aBuf, aValWidth, "  Textures: ", myCounters[Counter_EstimatedBytesTextures], "\n");
-    formatBytes (aBuf, aValWidth, "    Frames: ", myCounters[Counter_EstimatedBytesFbos], "\n");
+    return false;
   }
-  return TCollection_AsciiString (aBuf.str().c_str());
-}
 
-// =======================================================================
-// function : FrameStart
-// purpose  :
-// =======================================================================
-void OpenGl_FrameStats::FrameStart (const Handle(OpenGl_Workspace)& )
-{
-  memset (myCountersTmp, 0, sizeof(myCountersTmp));
-  myFrameStartTime = myFpsTimer.ElapsedTime();
-  if (!myFpsTimer.IsStarted())
-  {
-    myFpsTimer.Reset();
-    myFpsTimer.Start();
-    myFpsFrameCount = 0;
-  }
+  thePrev->myLastFrameIndex = myLastFrameIndex;
+  thePrev->myCountersTmp = aFrame;
+  return true;
 }
 
 // =======================================================================
-// function : FrameEnd
+// function : updateStatistics
 // purpose  :
 // =======================================================================
-void OpenGl_FrameStats::FrameEnd (const Handle(OpenGl_Workspace)& theWorkspace)
+void OpenGl_FrameStats::updateStatistics (const Handle(Graphic3d_CView)& theView,
+                                          bool theIsImmediateOnly)
 {
-  const Graphic3d_RenderingParams::PerfCounters aBits = !theWorkspace.IsNull()
-                                                      ? theWorkspace->View()->RenderingParams().CollectedStats
-                                                      : Graphic3d_RenderingParams::PerfCounters_NONE;
-  const double aTime = myFpsTimer.ElapsedTime();
-  myFrameDuration = aTime - myFrameStartTime;
-  ++myFpsFrameCount;
-  if (!theWorkspace.IsNull())
-  {
-    myUpdateInterval = theWorkspace->View()->RenderingParams().StatsUpdateInterval;
-  }
-
-  if (aTime < myUpdateInterval)
+  const OpenGl_View* aView = dynamic_cast<const OpenGl_View*> (theView.get());
+  if (aView == NULL)
   {
+    myCounters.SetValue (myLastFrameIndex, myCountersTmp);
+    myCountersTmp.Reset();
     return;
   }
 
-  if (aTime > gp::Resolution())
-  {
-    // update FPS
-    myFpsTimer.Stop();
-    const double aCpuSec = myFpsTimer.UserTimeCPU();
-    myFps    = double(myFpsFrameCount) / aTime;
-    myFpsCpu = aCpuSec > gp::Resolution()
-             ? double(myFpsFrameCount) / aCpuSec
-             : -1.0;
-    myFpsTimer.Reset();
-    myFpsTimer.Start();
-    myFpsFrameCount = 0;
-  }
-
-  // update structure counters
-  if (theWorkspace.IsNull())
-  {
-    memcpy (myCounters, myCountersTmp, sizeof(myCounters));
-    return;
-  }
-
+  const Graphic3d_RenderingParams::PerfCounters aBits = theView->RenderingParams().CollectedStats;
   const Standard_Boolean toCountMem     = (aBits & Graphic3d_RenderingParams::PerfCounters_EstimMem)  != 0;
   const Standard_Boolean toCountTris    = (aBits & Graphic3d_RenderingParams::PerfCounters_Triangles) != 0
                                        || (aBits & Graphic3d_RenderingParams::PerfCounters_Points)    != 0;
@@ -307,20 +98,25 @@ void OpenGl_FrameStats::FrameEnd (const Handle(OpenGl_Workspace)& theWorkspace)
   const Standard_Boolean toCountStructs = (aBits & Graphic3d_RenderingParams::PerfCounters_Structures)  != 0
                                        || (aBits & Graphic3d_RenderingParams::PerfCounters_Layers)      != 0 || toCountGroups;
 
-  myCountersTmp[Counter_NbLayers] = theWorkspace->View()->LayerList().Layers().Size();
+  myCountersTmp[Graphic3d_FrameStatsCounter_NbLayers] = aView->LayerList().Layers().Size();
   if (toCountStructs
    || (aBits & Graphic3d_RenderingParams::PerfCounters_Layers)    != 0)
   {
-    const Standard_Integer aViewId = theWorkspace->View()->Identification();
-    for (OpenGl_SequenceOfLayers::Iterator aLayerIter (theWorkspace->View()->LayerList().Layers()); aLayerIter.More(); aLayerIter.Next())
+    const Standard_Integer aViewId = aView->Identification();
+    for (OpenGl_SequenceOfLayers::Iterator aLayerIter (aView->LayerList().Layers()); aLayerIter.More(); aLayerIter.Next())
     {
       const Handle(OpenGl_Layer)& aLayer = aLayerIter.Value();
+      myCountersTmp[Graphic3d_FrameStatsCounter_NbStructs] += aLayer->NbStructures();
+      if (theIsImmediateOnly && !aLayer->LayerSettings().IsImmediate())
+      {
+        continue;
+      }
+
       if (!aLayer->IsCulled())
       {
-        ++myCountersTmp[Counter_NbLayersNotCulled];
+        ++myCountersTmp[Graphic3d_FrameStatsCounter_NbLayersNotCulled];
       }
-      myCountersTmp[Counter_NbStructs]          += aLayer->NbStructures();
-      myCountersTmp[Counter_NbStructsNotCulled] += aLayer->NbStructuresNotCulled();
+      myCountersTmp[Graphic3d_FrameStatsCounter_NbStructsNotCulled] += aLayer->NbStructuresNotCulled();
       if (toCountGroups)
       {
         updateStructures (aViewId, aLayer->CullableStructuresBVH().Structures(), toCountElems, toCountTris, toCountMem);
@@ -329,18 +125,16 @@ void OpenGl_FrameStats::FrameEnd (const Handle(OpenGl_Workspace)& theWorkspace)
       }
     }
   }
-  if (toCountMem
-  && !theWorkspace.IsNull())
+  if (toCountMem)
   {
-    for (OpenGl_Context::OpenGl_ResourcesMap::Iterator aResIter (theWorkspace->GetGlContext()->SharedResources());
+    for (OpenGl_Context::OpenGl_ResourcesMap::Iterator aResIter (aView->GlWindow()->GetGlContext()->SharedResources());
          aResIter.More(); aResIter.Next())
     {
-      myCountersTmp[Counter_EstimatedBytesTextures] += aResIter.Value()->EstimatedDataSize();
+      myCountersTmp[Graphic3d_FrameStatsCounter_EstimatedBytesTextures] += aResIter.Value()->EstimatedDataSize();
     }
 
-    const OpenGl_View* aView = theWorkspace->View();
     {
-      Standard_Size& aMemFbos = myCountersTmp[Counter_EstimatedBytesFbos];
+      Standard_Size& aMemFbos = myCountersTmp[Graphic3d_FrameStatsCounter_EstimatedBytesFbos];
       // main FBOs
       aMemFbos += estimatedDataSize (aView->myMainSceneFbos[0]);
       aMemFbos += estimatedDataSize (aView->myMainSceneFbos[1]);
@@ -370,7 +164,7 @@ void OpenGl_FrameStats::FrameEnd (const Handle(OpenGl_Workspace)& theWorkspace)
     }
     {
       // Ray Tracing geometry
-      Standard_Size& aMemGeom = myCountersTmp[Counter_EstimatedBytesGeom];
+      Standard_Size& aMemGeom = myCountersTmp[Graphic3d_FrameStatsCounter_EstimatedBytesGeom];
       aMemGeom += estimatedDataSize (aView->mySceneNodeInfoTexture);
       aMemGeom += estimatedDataSize (aView->mySceneMinPointTexture);
       aMemGeom += estimatedDataSize (aView->mySceneMaxPointTexture);
@@ -383,7 +177,6 @@ void OpenGl_FrameStats::FrameEnd (const Handle(OpenGl_Workspace)& theWorkspace)
       aMemGeom += estimatedDataSize (aView->myRaytraceLightSrcTexture);
     }
   }
-  memcpy (myCounters, myCountersTmp, sizeof(myCounters));
 }
 
 // =======================================================================
@@ -414,8 +207,8 @@ void OpenGl_FrameStats::updateStructures (Standard_Integer theViewId,
             {
               if (const OpenGl_PrimitiveArray* aPrim = dynamic_cast<const OpenGl_PrimitiveArray*> (aNodeIter->elem))
               {
-                myCountersTmp[Counter_EstimatedBytesGeom] += estimatedDataSize (aPrim->AttributesVbo());
-                myCountersTmp[Counter_EstimatedBytesGeom] += estimatedDataSize (aPrim->IndexVbo());
+                myCountersTmp[Graphic3d_FrameStatsCounter_EstimatedBytesGeom] += estimatedDataSize (aPrim->AttributesVbo());
+                myCountersTmp[Graphic3d_FrameStatsCounter_EstimatedBytesGeom] += estimatedDataSize (aPrim->IndexVbo());
               }
             }
           }
@@ -423,7 +216,7 @@ void OpenGl_FrameStats::updateStructures (Standard_Integer theViewId,
         continue;
       }
 
-      myCountersTmp[Counter_NbGroupsNotCulled] += aStruct->Groups().Size();
+      myCountersTmp[Graphic3d_FrameStatsCounter_NbGroupsNotCulled] += aStruct->Groups().Size();
       if (!theToCountElems)
       {
         continue;
@@ -436,16 +229,16 @@ void OpenGl_FrameStats::updateStructures (Standard_Integer theViewId,
         {
           if (const OpenGl_PrimitiveArray* aPrim = dynamic_cast<const OpenGl_PrimitiveArray*> (aNodeIter->elem))
           {
-            ++myCountersTmp[Counter_NbElemsNotCulled];
+            ++myCountersTmp[Graphic3d_FrameStatsCounter_NbElemsNotCulled];
             if (theToCountMem)
             {
-              myCountersTmp[Counter_EstimatedBytesGeom] += estimatedDataSize (aPrim->AttributesVbo());
-              myCountersTmp[Counter_EstimatedBytesGeom] += estimatedDataSize (aPrim->IndexVbo());
+              myCountersTmp[Graphic3d_FrameStatsCounter_EstimatedBytesGeom] += estimatedDataSize (aPrim->AttributesVbo());
+              myCountersTmp[Graphic3d_FrameStatsCounter_EstimatedBytesGeom] += estimatedDataSize (aPrim->IndexVbo());
             }
 
             if (aPrim->IsFillDrawMode())
             {
-              ++myCountersTmp[Counter_NbElemsFillNotCulled];
+              ++myCountersTmp[Graphic3d_FrameStatsCounter_NbElemsFillNotCulled];
               if (!theToCountTris)
               {
                 continue;
@@ -465,34 +258,34 @@ void OpenGl_FrameStats::updateStructures (Standard_Integer theViewId,
               {
                 case GL_TRIANGLES:
                 {
-                  myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices / 3;
+                  myCountersTmp[Graphic3d_FrameStatsCounter_NbTrianglesNotCulled] += aNbIndices / 3;
                   break;
                 }
                 case GL_TRIANGLE_STRIP:
                 case GL_TRIANGLE_FAN:
                 {
-                  myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices - 2 * aNbBounds;
+                  myCountersTmp[Graphic3d_FrameStatsCounter_NbTrianglesNotCulled] += aNbIndices - 2 * aNbBounds;
                   break;
                 }
                 case GL_TRIANGLES_ADJACENCY:
                 {
-                  myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices / 6;
+                  myCountersTmp[Graphic3d_FrameStatsCounter_NbTrianglesNotCulled] += aNbIndices / 6;
                   break;
                 }
                 case GL_TRIANGLE_STRIP_ADJACENCY:
                 {
-                  myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices - 4 * aNbBounds;
+                  myCountersTmp[Graphic3d_FrameStatsCounter_NbTrianglesNotCulled] += aNbIndices - 4 * aNbBounds;
                   break;
                 }
               #if !defined(GL_ES_VERSION_2_0)
                 case GL_QUADS:
                 {
-                  myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices / 2;
+                  myCountersTmp[Graphic3d_FrameStatsCounter_NbTrianglesNotCulled] += aNbIndices / 2;
                   break;
                 }
                 case GL_QUAD_STRIP:
                 {
-                  myCountersTmp[Counter_NbTrianglesNotCulled] += (aNbIndices / 2 - aNbBounds) * 2;
+                  myCountersTmp[Graphic3d_FrameStatsCounter_NbTrianglesNotCulled] += (aNbIndices / 2 - aNbBounds) * 2;
                   break;
                 }
               #endif
@@ -500,7 +293,7 @@ void OpenGl_FrameStats::updateStructures (Standard_Integer theViewId,
             }
             else if (aPrim->DrawMode() == GL_POINTS)
             {
-              ++myCountersTmp[Counter_NbElemsPointNotCulled];
+              ++myCountersTmp[Graphic3d_FrameStatsCounter_NbElemsPointNotCulled];
               if (theToCountTris)
               {
                 const Handle(OpenGl_VertexBuffer)& anAttribs = aPrim->AttributesVbo();
@@ -509,20 +302,20 @@ void OpenGl_FrameStats::updateStructures (Standard_Integer theViewId,
                 {
                   const Handle(OpenGl_VertexBuffer)& anIndices = aPrim->IndexVbo();
                   const Standard_Integer aNbIndices = !anIndices.IsNull() ? anIndices->GetElemsNb() : anAttribs->GetElemsNb();
-                  myCountersTmp[Counter_NbPointsNotCulled] += aNbIndices;
+                  myCountersTmp[Graphic3d_FrameStatsCounter_NbPointsNotCulled] += aNbIndices;
                 }
               }
             }
             else
             {
-              ++myCountersTmp[Counter_NbElemsLineNotCulled];
+              ++myCountersTmp[Graphic3d_FrameStatsCounter_NbElemsLineNotCulled];
             }
           }
           else if (const OpenGl_Text* aText = dynamic_cast<const OpenGl_Text*> (aNodeIter->elem))
           {
             (void )aText;
-            ++myCountersTmp[Counter_NbElemsNotCulled];
-            ++myCountersTmp[Counter_NbElemsTextNotCulled];
+            ++myCountersTmp[Graphic3d_FrameStatsCounter_NbElemsNotCulled];
+            ++myCountersTmp[Graphic3d_FrameStatsCounter_NbElemsTextNotCulled];
           }
         }
       }
index adcbb01..364d573 100644 (file)
 #ifndef _OpenGl_FrameStats_HeaderFile
 #define _OpenGl_FrameStats_HeaderFile
 
-#include <Graphic3d_RenderingParams.hxx>
+#include <Graphic3d_FrameStats.hxx>
 #include <NCollection_IndexedMap.hxx>
-#include <OSD_Timer.hxx>
-#include <Standard_Type.hxx>
-#include <Standard_Transient.hxx>
 
 class OpenGl_Workspace;
 class OpenGl_Structure;
 typedef NCollection_IndexedMap<const OpenGl_Structure*> OpenGl_IndexedMapOfStructure;
 
 //! Class storing the frame statistics.
-class OpenGl_FrameStats : public Standard_Transient
+class OpenGl_FrameStats : public Graphic3d_FrameStats
 {
-  DEFINE_STANDARD_RTTIEXT(OpenGl_FrameStats, Standard_Transient)
-public:
-
-  //! Stats counter.
-  enum Counter
-  {
-    Counter_NbLayers = 0,           //!< number of ZLayers
-    Counter_NbLayersNotCulled,      //!< number of not culled ZLayers
-    Counter_NbStructs,              //!< number of defined OpenGl_Structure
-    Counter_NbStructsNotCulled,     //!< number of not culled OpenGl_Structure
-    Counter_NbGroupsNotCulled,      //!< number of not culled OpenGl_Group
-    Counter_NbElemsNotCulled,       //!< number of not culled OpenGl_Element
-    Counter_NbElemsFillNotCulled,   //!< number of not culled OpenGl_PrimitiveArray drawing triangles
-    Counter_NbElemsLineNotCulled,   //!< number of not culled OpenGl_PrimitiveArray drawing lines
-    Counter_NbElemsPointNotCulled,  //!< number of not culled OpenGl_PrimitiveArray drawing points
-    Counter_NbElemsTextNotCulled,   //!< number of not culled OpenGl_Text
-    Counter_NbTrianglesNotCulled,   //!< number of not culled (as structure) triangles
-    Counter_NbPointsNotCulled,      //!< number of not culled (as structure) points
-    Counter_EstimatedBytesGeom,     //!< estimated GPU memory used for geometry
-    Counter_EstimatedBytesFbos,     //!< estimated GPU memory used for FBOs
-    Counter_EstimatedBytesTextures, //!< estimated GPU memory used for textures
-  };
-  enum { Counter_NB = Counter_EstimatedBytesTextures + 1 };
-
+  DEFINE_STANDARD_RTTIEXT(OpenGl_FrameStats, Graphic3d_FrameStats)
 public:
 
   //! Default constructor.
@@ -59,82 +33,19 @@ public:
   //! Destructor.
   Standard_EXPORT virtual ~OpenGl_FrameStats();
 
-  //! Returns interval in seconds for updating meters across several frames; 1 second by default.
-  Standard_Real UpdateInterval() const { return myUpdateInterval; }
-
-  //! Sets interval in seconds for updating values.
-  void SetUpdateInterval (Standard_Real theInterval) { myUpdateInterval = theInterval; }
-
-  //! Prefer longer lines over more greater of lines.
-  Standard_Boolean IsLongLineFormat() const { return myIsLongLineFormat; }
-
-  //! Set if format should prefer longer lines over greater number of lines.
-  void SetLongLineFormat (Standard_Boolean theValue) { myIsLongLineFormat = theValue; }
-
-  //! Frame redraw started.
-  Standard_EXPORT virtual void FrameStart (const Handle(OpenGl_Workspace)& theWorkspace = Handle(OpenGl_Workspace)());
-
-  //! Frame redraw finished.
-  Standard_EXPORT virtual void FrameEnd (const Handle(OpenGl_Workspace)& theWorkspace = Handle(OpenGl_Workspace)());
-
-public:
-
-  //! Returns formatted string.
-  Standard_EXPORT virtual TCollection_AsciiString FormatStats (Graphic3d_RenderingParams::PerfCounters theFlags) const;
-
-  //! Returns duration of the last frame in seconds.
-  Standard_Real FrameDuration() const { return myFrameDuration; }
-
-  //! Returns FPS (frames per seconds, elapsed time).
-  //! This number indicates an actual frame rate averaged for several frames within UpdateInterval() duration,
-  //! basing on a real elapsed time between updates.
-  Standard_Real FrameRate() const { return myFps; }
-
-  //! Returns CPU FPS (frames per seconds, CPU time).
-  //! This number indicates a PREDICTED frame rate,
-  //! basing on CPU elapsed time between updates and NOT real elapsed time (which might include periods of CPU inactivity).
-  //! Number is expected to be greater then actual frame rate returned by FrameRate().
-  //! Values significantly greater actual frame rate indicate that rendering is limited by GPU performance (CPU is stalled in-between),
-  //! while values around actual frame rate indicate rendering being limited by CPU performance (GPU is stalled in-between).
-  Standard_Real FrameRateCpu() const { return myFpsCpu; }
-
-  //! Returns value of specified counter, cached between stats updates.
-  //! Should NOT be called between ::FrameStart() and ::FrameEnd() calls.
-  Standard_Size CounterValue (OpenGl_FrameStats::Counter theCounter) const { return myCounters[theCounter]; }
-
-  //! Returns TRUE if some Layers have been culled.
-  Standard_Boolean HasCulledLayers() const { return myCounters[Counter_NbLayersNotCulled] != myCounters[Counter_NbLayers]; }
-
-  //! Returns TRUE if some structures have been culled.
-  Standard_Boolean HasCulledStructs() const { return myCounters[Counter_NbStructsNotCulled] != myCounters[Counter_NbStructs]; }
-
 public:
 
-  //! Returns TRUE if this stats are equal to another.
-  virtual Standard_Boolean IsEqual (const Handle(OpenGl_FrameStats)& theOther) const
-  {
-    // check just a couple of major counters
-    return Abs (myFps    - theOther->myFps)    <= 0.001
-        && Abs (myFpsCpu - theOther->myFpsCpu) <= 0.001
-        && myCounters[Counter_NbLayers] == theOther->myCounters[Counter_NbLayers]
-        && myCounters[Counter_NbLayersNotCulled] == theOther->myCounters[Counter_NbLayersNotCulled]
-        && myCounters[Counter_NbStructs] == theOther->myCounters[Counter_NbStructs]
-        && myCounters[Counter_NbStructsNotCulled] == theOther->myCounters[Counter_NbStructsNotCulled];
-  }
-
-  //! Copy stats values from another instance
-  virtual void CopyFrom (const Handle(OpenGl_FrameStats)& theOther)
-  {
-    myFps    = theOther->myFps;
-    myFpsCpu = theOther->myFpsCpu;
-    memcpy (myCounters, theOther->myCounters, sizeof(myCounters));
-  }
-
-  //! Returns value of specified counter for modification, should be called between ::FrameStart() and ::FrameEnd() calls.
-  Standard_Size& ChangeCounter (OpenGl_FrameStats::Counter theCounter) { return myCountersTmp[theCounter]; }
+  //! Copy stats values into another instance (create new instance, if not exists).
+  //! The main use of this method is to track changes in statistics (e.g. in conjunction with IsEqual() method).
+  //! @return TRUE if frame data has been changed so that the presentation should be updated
+  Standard_EXPORT virtual bool IsFrameUpdated (Handle(OpenGl_FrameStats)& thePrev) const;
 
 protected:
 
+  //! Method to collect statistics from the View; called by FrameEnd().
+  Standard_EXPORT virtual void updateStatistics (const Handle(Graphic3d_CView)& theView,
+                                                 bool theIsImmediateOnly) Standard_OVERRIDE;
+
   //! Updates counters for structures.
   Standard_EXPORT virtual void updateStructures (Standard_Integer theViewId,
                                                  const OpenGl_IndexedMapOfStructure& theStructures,
@@ -142,21 +53,8 @@ protected:
                                                  Standard_Boolean theToCountTris,
                                                  Standard_Boolean theToCountMem);
 
-protected:
-
-  OSD_Timer        myFpsTimer;                //!< timer for FPS measurements
-  Standard_Real    myFrameStartTime;          //!< time at the beginning of frame redraw
-  Standard_Real    myFrameDuration;           //!< frame duration
-  Standard_Real    myFps;                     //!< FPS     meter (frames per seconds, elapsed time)
-  Standard_Real    myFpsCpu;                  //!< CPU FPS meter (frames per seconds, CPU time)
-  Standard_Real    myUpdateInterval;          //!< interval to update meters
-  Standard_Size    myFpsFrameCount;           //!< FPS counter (within short measurement time slice)
-  Standard_Size    myCounters   [Counter_NB]; //!< counter values cached between updates
-  Standard_Size    myCountersTmp[Counter_NB]; //!< counter values filled during
-  Standard_Boolean myIsLongLineFormat;        //!< prefer longer lines over greater number of lines
-
 };
 
-DEFINE_STANDARD_HANDLE(OpenGl_FrameStats, Standard_Transient)
+DEFINE_STANDARD_HANDLE(OpenGl_FrameStats, Graphic3d_FrameStats)
 
 #endif // _OpenGl_FrameStats_HeaderFile
index 8abdc66..eb35b2e 100644 (file)
 #include <OpenGl_ShaderManager.hxx>
 #include <OpenGl_Workspace.hxx>
 
+#include <Graphic3d_ArrayOfTriangles.hxx>
+
+namespace
+{
+  //! Auxiliary structure defining vertex with two attributes.
+  struct OpenGl_Vec3Vec4ub
+  {
+    Graphic3d_Vec3   Pos;
+    Graphic3d_Vec4ub Color;
+  };
+
+  //! Auxiliary function formatting rendering time in " 10 ms (100 FPS)" format.
+  static TCollection_AsciiString formatTimeMs (Standard_Real theSeconds)
+  {
+    const Standard_Real aFpsVal = theSeconds != 0.0 ? 1.0 / theSeconds : 0.0;
+    char aFps[50];
+    Sprintf (aFps, "%.1f", aFpsVal);
+    return TCollection_AsciiString() + Standard_Integer(theSeconds * 1000.0) + " ms (" + aFps + " FPS)";
+  }
+}
+
 // =======================================================================
 // function : OpenGl_FrameStatsPrs
 // purpose  :
 // =======================================================================
 OpenGl_FrameStatsPrs::OpenGl_FrameStatsPrs()
-: myStatsPrev (new OpenGl_FrameStats())
+: myStatsPrev (new OpenGl_FrameStats()),
+  myCountersTrsfPers (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_LEFT_UPPER,  Graphic3d_Vec2i (20, 20))),
+  myChartTrsfPers    (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_RIGHT_UPPER, Graphic3d_Vec2i (20, 20))),
+  myChartVertices (new OpenGl_VertexBuffer()),
+  myChartIndices (new OpenGl_IndexBuffer()),
+  myChartLines (new OpenGl_VertexBuffer())
 {
-  myParams.HAlign = Graphic3d_HTA_CENTER;
-  myParams.VAlign = Graphic3d_VTA_CENTER;
-  myHasPlane      = false;
+  //
 }
 
 // =======================================================================
@@ -44,7 +68,13 @@ OpenGl_FrameStatsPrs::~OpenGl_FrameStatsPrs()
 // =======================================================================
 void OpenGl_FrameStatsPrs::Release (OpenGl_Context* theCtx)
 {
-  OpenGl_Text::Release (theCtx);
+  myCountersText.Release (theCtx);
+  myChartLabels[0].Release (theCtx);
+  myChartLabels[1].Release (theCtx);
+  myChartLabels[2].Release (theCtx);
+  myChartVertices->Release (theCtx);
+  myChartIndices->Release (theCtx);
+  myChartLines->Release (theCtx);
 }
 
 // =======================================================================
@@ -56,48 +86,262 @@ void OpenGl_FrameStatsPrs::Update (const Handle(OpenGl_Workspace)& theWorkspace)
   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
   const Handle(OpenGl_FrameStats)& aStats = aCtx->FrameStats();
   const Graphic3d_RenderingParams& aRendParams = theWorkspace->View()->RenderingParams();
-  myTrsfPers = theWorkspace->View()->RenderingParams().StatsPosition;
+  myCountersTrsfPers = theWorkspace->View()->RenderingParams().StatsPosition;
+  myChartTrsfPers    = theWorkspace->View()->RenderingParams().ChartPosition;
   myTextAspect.SetAspect (aRendParams.StatsTextAspect);
 
   // adjust text alignment depending on corner
-  const OpenGl_TextParam aParamsPrev = myParams;
-  myParams.Height = aRendParams.StatsTextHeight;
-  myParams.HAlign = Graphic3d_HTA_CENTER;
-  myParams.VAlign = Graphic3d_VTA_CENTER;
-  if (!myTrsfPers.IsNull() && (myTrsfPers->Corner2d() & Aspect_TOTP_LEFT) != 0)
+  OpenGl_TextParam aParams;
+  aParams.Height = aRendParams.StatsTextHeight;
+  aParams.HAlign = Graphic3d_HTA_CENTER;
+  aParams.VAlign = Graphic3d_VTA_CENTER;
+  if (!myCountersTrsfPers.IsNull() && (myCountersTrsfPers->Corner2d() & Aspect_TOTP_LEFT) != 0)
   {
-    myParams.HAlign = Graphic3d_HTA_LEFT;
+    aParams.HAlign = Graphic3d_HTA_LEFT;
   }
-  else if (!myTrsfPers.IsNull() && (myTrsfPers->Corner2d() & Aspect_TOTP_RIGHT) != 0)
+  else if (!myCountersTrsfPers.IsNull() && (myCountersTrsfPers->Corner2d() & Aspect_TOTP_RIGHT) != 0)
   {
-    myParams.HAlign = Graphic3d_HTA_RIGHT;
+    aParams.HAlign = Graphic3d_HTA_RIGHT;
   }
-  if (!myTrsfPers.IsNull() && (myTrsfPers->Corner2d() & Aspect_TOTP_TOP) != 0)
+  if (!myCountersTrsfPers.IsNull() && (myCountersTrsfPers->Corner2d() & Aspect_TOTP_TOP) != 0)
   {
-    myParams.VAlign = Graphic3d_VTA_TOP;
+    aParams.VAlign = Graphic3d_VTA_TOP;
   }
-  else if (!myTrsfPers.IsNull() && (myTrsfPers->Corner2d() & Aspect_TOTP_BOTTOM) != 0)
+  else if (!myCountersTrsfPers.IsNull() && (myCountersTrsfPers->Corner2d() & Aspect_TOTP_BOTTOM) != 0)
   {
-    myParams.VAlign = Graphic3d_VTA_BOTTOM;
+    aParams.VAlign = Graphic3d_VTA_BOTTOM;
   }
-  if (myParams.Height != aParamsPrev.Height
-   || myParams.HAlign != aParamsPrev.HAlign
-   || myParams.VAlign != aParamsPrev.VAlign)
+  if (aParams.Height != myCountersText.FormatParams().Height
+   || aParams.HAlign != myCountersText.FormatParams().HAlign
+   || aParams.VAlign != myCountersText.FormatParams().VAlign)
   {
-    Release (aCtx.operator->());
+    myCountersText.Release (aCtx.operator->());
   }
 
-  if (myStatsPrev->IsEqual (aStats)
-  && !myString.IsEmpty())
+  if (!aStats->IsFrameUpdated (myStatsPrev)
+   && !myCountersText.Text().IsEmpty())
   {
     return;
   }
 
-  myStatsPrev->CopyFrom (aStats);
-  const TCollection_AsciiString aText = aStats->FormatStats (aRendParams.CollectedStats);
+  TCollection_AsciiString aText = aStats->FormatStats (aRendParams.CollectedStats);
+  myCountersText.Init (aCtx, aText.ToCString(), OpenGl_Vec3 (0.0f, 0.0f, 0.0f), aParams);
+
+  updateChart (theWorkspace);
+}
+
+// =======================================================================
+// function : updateChart
+// purpose  :
+// =======================================================================
+void OpenGl_FrameStatsPrs::updateChart (const Handle(OpenGl_Workspace)& theWorkspace)
+{
+  const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
+  const Handle(OpenGl_FrameStats)& aStats = aCtx->FrameStats();
+  const Graphic3d_RenderingParams& aRendParams = theWorkspace->View()->RenderingParams();
+
+  const Standard_Integer aNbBins = aStats->DataFrames().Size();
+  if (aNbBins <= 1)
+  {
+    return;
+  }
+
+  Standard_Real aMaxDuration = aRendParams.StatsMaxChartTime;
+  if (aMaxDuration <= 0.0f)
+  {
+    for (Standard_Integer aFrameIter = aStats->DataFrames().Lower(); aFrameIter <= aStats->DataFrames().Upper(); ++aFrameIter)
+    {
+      const Graphic3d_FrameStatsData& aFrame = aStats->DataFrames().Value (aFrameIter);
+      aMaxDuration = Max (aMaxDuration, aFrame.TimerValue (Graphic3d_FrameStatsTimer_ElapsedFrame));
+    }
+    aMaxDuration = Ceiling (aMaxDuration * 1000.0 * 0.1) * 0.001 * 10.0; // round number
+    aMaxDuration = Max (Min (aMaxDuration, 0.1), 0.005); // limit by 100 ms (10 FPS) and 5 ms (200 FPS)
+  }
+
+  const Standard_Integer aNbTimers = 4;
+  const Graphic3d_FrameStatsTimer aTimers[4] =
+  {
+    Graphic3d_FrameStatsTimer_CpuDynamics,
+    Graphic3d_FrameStatsTimer_CpuPicking,
+    Graphic3d_FrameStatsTimer_CpuCulling,
+    Graphic3d_FrameStatsTimer_ElapsedFrame,
+  };
+  const Graphic3d_Vec4ub aColors[4] =
+  {
+    Graphic3d_Vec4ub (255, 0, 0, 127),
+    Graphic3d_Vec4ub (255, 127, 39, 127),
+    Graphic3d_Vec4ub (255, 0, 0, 127),
+    Graphic3d_Vec4ub (0, 255, 0, 127),
+  };
+
+  const Standard_Integer aNbVerts   = aNbBins * 4 * aNbTimers;
+  const Standard_Integer aNbIndexes = aNbBins * 2 * 3 * aNbTimers;
+  bool toFillEdges = false;
+  if (myChartArray.IsNull()
+   || myChartArray->VertexNumber() != aNbVerts
+   || myChartArray->EdgeNumber()   != aNbIndexes)
+  {
+    myChartArray = new Graphic3d_ArrayOfTriangles (aNbVerts, aNbIndexes, false, true);
+    toFillEdges = true;
+  }
+
+  const Graphic3d_Vec2i aViewSize (aCtx->VirtualViewport()[2], aCtx->VirtualViewport()[3]);
+  Graphic3d_Vec2i aCharSize (aRendParams.ChartSize);
+  if (aCharSize.x() <= 0)
+  {
+    aCharSize.x() = aViewSize.x() / 2;
+  }
+  if (aCharSize.y() <= 0)
+  {
+    aCharSize.y() = Standard_Integer(0.15 * aViewSize.y());
+  }
+
+  const Graphic3d_Vec2d aBinSize  (Standard_Real(aCharSize.x()) / Standard_Real(aNbBins), 0.15 * aViewSize.y());
+  Graphic3d_Vec2i anOffset;
+  if (!myChartTrsfPers.IsNull()
+    && myChartTrsfPers->IsTrihedronOr2d())
+  {
+    if ((myChartTrsfPers->Corner2d() & Aspect_TOTP_LEFT) != 0)
+    {
+      anOffset.x() = 0;
+    }
+    else if ((myChartTrsfPers->Corner2d() & Aspect_TOTP_RIGHT) != 0)
+    {
+      anOffset.x() = -aCharSize.x();
+    }
+    else
+    {
+      anOffset.x() = -aCharSize.x() / 2;
+    }
+
+    if ((myChartTrsfPers->Corner2d() & Aspect_TOTP_BOTTOM) != 0)
+    {
+      anOffset.y() = aCharSize.y();
+    }
+    else if ((myChartTrsfPers->Corner2d() & Aspect_TOTP_TOP) != 0)
+    {
+      anOffset.y() = 0;
+    }
+    else
+    {
+      anOffset.y() = aCharSize.y() / 2;
+    }
+  }
+
+  Standard_Integer aVertLast = 1;
+  const bool isTopDown = false;
+  for (Standard_Integer aFrameIter = 0; aFrameIter < aNbBins; ++aFrameIter)
+  {
+    Standard_Integer aFrameIndex = aStats->DataFrames().Lower() + aStats->LastDataFrameIndex() + 1 + aFrameIter;
+    if (aFrameIndex > aStats->DataFrames().Upper())
+    {
+      aFrameIndex -= aNbBins;
+    }
+
+    const Graphic3d_FrameStatsData& aFrame = aStats->DataFrames().Value (aFrameIndex);
+    Standard_Real aTimeElapsed = 0.0;
+    Standard_Real aCurrY = 0.0;
+    for (Standard_Integer aTimerIter = 0; aTimerIter < aNbTimers; ++aTimerIter)
+    {
+      if (aTimers[aTimerIter] == Graphic3d_FrameStatsTimer_ElapsedFrame)
+      {
+        aTimeElapsed = aFrame.TimerValue (aTimers[aTimerIter]);
+      }
+      else
+      {
+        aTimeElapsed += aFrame.TimerValue (aTimers[aTimerIter]);
+      }
+
+      const Standard_Real aBinX1 = anOffset.x() + Standard_Real(aFrameIter) * aBinSize.x();
+      const Standard_Real aBinX2 = aBinX1 + aBinSize.x();
+      const Standard_Real aCurrSizeY = Min (aTimeElapsed / aMaxDuration, 1.2) * aBinSize.y();
+      const Standard_Real aBinY1 = isTopDown ? (anOffset.y() - aCurrY)     : (anOffset.y() - aBinSize.y() + aCurrY);
+      const Standard_Real aBinY2 = isTopDown ? (anOffset.y() - aCurrSizeY) : (anOffset.y() - aBinSize.y() + aCurrSizeY);
+      myChartArray->SetVertice (aVertLast + 0, gp_Pnt (aBinX1, aBinY2, 0.0));
+      myChartArray->SetVertice (aVertLast + 1, gp_Pnt (aBinX1, aBinY1, 0.0));
+      myChartArray->SetVertice (aVertLast + 2, gp_Pnt (aBinX2, aBinY1, 0.0));
+      myChartArray->SetVertice (aVertLast + 3, gp_Pnt (aBinX2, aBinY2, 0.0));
+
+      if (toFillEdges)
+      {
+        const Graphic3d_Vec4ub& aTimerColor = aColors[aTimerIter];
+        myChartArray->SetVertexColor (aVertLast + 0, aTimerColor);
+        myChartArray->SetVertexColor (aVertLast + 1, aTimerColor);
+        myChartArray->SetVertexColor (aVertLast + 2, aTimerColor);
+        myChartArray->SetVertexColor (aVertLast + 3, aTimerColor);
+        myChartArray->AddEdges (aVertLast + 0, aVertLast + 1, aVertLast + 3);
+        myChartArray->AddEdges (aVertLast + 1, aVertLast + 2, aVertLast + 3);
+      }
+      aVertLast += 4;
+
+      if (aTimers[aTimerIter] == Graphic3d_FrameStatsTimer_ElapsedFrame)
+      {
+        aTimeElapsed = 0.0;
+        aCurrY = 0.0;
+      }
+      else
+      {
+        aCurrY = aCurrSizeY;
+      }
+    }
+  }
+
+  myChartVertices->init (aCtx,
+                         myChartArray->Attributes()->Stride,
+                         myChartArray->Attributes()->NbElements,
+                         myChartArray->Attributes()->Data(),
+                         GL_UNSIGNED_BYTE,
+                         myChartArray->Attributes()->Stride);
+  if (myChartArray->Indices()->Stride == 2)
+  {
+    myChartIndices ->Init (aCtx,
+                           1,
+                           myChartArray->Indices()->NbElements,
+                           (const GLushort* )myChartArray->Indices()->Data());
+  }
+  else if (myChartArray->Indices()->Stride == 4)
+  {
+    myChartIndices ->Init (aCtx,
+                           1,
+                           myChartArray->Indices()->NbElements,
+                           (const GLuint* )myChartArray->Indices()->Data());
+  }
 
-  releaseVbos (aCtx.operator->());
-  myString.FromUnicode (aText.ToCString());
+  {
+    const Graphic3d_Vec4ub aWhite (255, 255, 255, 255);
+    const OpenGl_Vec3Vec4ub aLines[4] =
+    {
+      { Graphic3d_Vec3((float )anOffset.x(), (float )anOffset.y(),                0.0f), aWhite },
+      { Graphic3d_Vec3(float(anOffset.x() + aCharSize.x()), (float )anOffset.y(), 0.0f), aWhite },
+      { Graphic3d_Vec3((float )anOffset.x(), float(anOffset.y() - aBinSize.y()),  0.0f), aWhite },
+      { Graphic3d_Vec3(float(anOffset.x() + aCharSize.x()), float(anOffset.y() - aBinSize.y()),+ 0.0f), aWhite },
+    };
+    myChartLines->init (aCtx, sizeof(OpenGl_Vec3Vec4ub), 4, aLines, GL_UNSIGNED_BYTE, sizeof(OpenGl_Vec3Vec4ub));
+  }
+
+  {
+    OpenGl_TextParam aParams;
+    aParams.Height = aRendParams.StatsTextHeight;
+    aParams.HAlign = (!myChartTrsfPers.IsNull()
+                    && myChartTrsfPers->IsTrihedronOr2d()
+                    && (myChartTrsfPers->Corner2d() & Aspect_TOTP_RIGHT) != 0)
+                    ? Graphic3d_HTA_RIGHT
+                    : Graphic3d_HTA_LEFT;
+    aParams.VAlign = Graphic3d_VTA_CENTER;
+    TCollection_AsciiString aLabels[3] =
+    {
+      TCollection_AsciiString() + 0 + " ms",
+      formatTimeMs(aMaxDuration * 0.5),
+      formatTimeMs(aMaxDuration)
+    };
+
+    const float aLabX = aParams.HAlign == Graphic3d_HTA_RIGHT
+                      ? float(anOffset.x())
+                      : float(anOffset.x() + aCharSize.x());
+    myChartLabels[0].Init (aCtx, aLabels[isTopDown ? 0 : 2].ToCString(), OpenGl_Vec3 (aLabX, float(anOffset.y()),                    0.0f), aParams);
+    myChartLabels[1].Init (aCtx, aLabels[isTopDown ? 1 : 1].ToCString(), OpenGl_Vec3 (aLabX, float(anOffset.y() - aBinSize.y() / 2), 0.0f), aParams);
+    myChartLabels[2].Init (aCtx, aLabels[isTopDown ? 2 : 0].ToCString(), OpenGl_Vec3 (aLabX, float(anOffset.y() - aBinSize.y()),     0.0f), aParams);
+  }
 }
 
 // =======================================================================
@@ -114,28 +358,75 @@ void OpenGl_FrameStatsPrs::Render (const Handle(OpenGl_Workspace)& theWorkspace)
     glDepthMask (GL_FALSE);
   }
 
+  const OpenGl_AspectText* aTextAspectBack = theWorkspace->SetAspectText (&myTextAspect);
+
   aCtx->ModelWorldState.Push();
   aCtx->ModelWorldState.ChangeCurrent().InitIdentity();
 
-  aCtx->WorldViewState.Push();
-  OpenGl_Mat4& aWorldView = aCtx->WorldViewState.ChangeCurrent();
-  if (!myTrsfPers.IsNull())
+  // draw counters
   {
-    myTrsfPers->Apply (theWorkspace->View()->Camera(),
-                      aCtx->ProjectionState.Current(), aWorldView,
-                      aCtx->VirtualViewport()[2], aCtx->VirtualViewport()[3]);
+    aCtx->WorldViewState.Push();
+    if (!myCountersTrsfPers.IsNull())
+    {
+      myCountersTrsfPers->Apply (theWorkspace->View()->Camera(),
+                                 aCtx->ProjectionState.Current(), aCtx->WorldViewState.ChangeCurrent(),
+                                 aCtx->VirtualViewport()[2], aCtx->VirtualViewport()[3]);
+    }
+    aCtx->ApplyModelViewMatrix();
+    myCountersText.Render (theWorkspace);
+    aCtx->WorldViewState.Pop();
   }
 
-  aCtx->ApplyModelViewMatrix();
+  // draw chart
+  if (myChartIndices->IsValid()
+   && myChartIndices->GetElemsNb() > 0)
+  {
+    aCtx->WorldViewState.Push();
+    if (!myChartTrsfPers.IsNull())
+    {
+      myChartTrsfPers->Apply (theWorkspace->View()->Camera(),
+                              aCtx->ProjectionState.Current(), aCtx->WorldViewState.ChangeCurrent(),
+                              aCtx->VirtualViewport()[2], aCtx->VirtualViewport()[3]);
+    }
+    aCtx->ApplyModelViewMatrix();
 
-  const OpenGl_AspectText* aTextAspectBack = theWorkspace->SetAspectText (&myTextAspect);
-  OpenGl_Text::Render (theWorkspace);
-  theWorkspace->SetAspectText (aTextAspectBack);
+    aCtx->ShaderManager()->BindFaceProgram (Handle(OpenGl_TextureSet)(), Graphic3d_TOSM_UNLIT,
+                                            Graphic3d_AlphaMode_Blend, true, false,
+                                            Handle(OpenGl_ShaderProgram)());
+    aCtx->SetColor4fv (OpenGl_Vec4 (1.0f, 1.0f, 1.0f, 1.0f));
+    glEnable (GL_BLEND);
+    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    myChartVertices->Bind (aCtx);
+    myChartVertices->bindAttribute (aCtx, Graphic3d_TOA_POS,   3, GL_FLOAT,         myChartVertices->GetComponentsNb(), NULL);
+    myChartVertices->bindAttribute (aCtx, Graphic3d_TOA_COLOR, 4, GL_UNSIGNED_BYTE, myChartVertices->GetComponentsNb(), (void* )sizeof(Graphic3d_Vec3));
+
+    myChartIndices->Bind (aCtx);
+    aCtx->core15fwd->glDrawElements (GL_TRIANGLES, myChartIndices->GetElemsNb(), myChartIndices->GetDataType(), NULL);
+    myChartIndices->Unbind (aCtx);
+    myChartVertices->Unbind (aCtx);
+    myChartVertices->unbindAttribute (aCtx, Graphic3d_TOA_COLOR);
+    myChartVertices->unbindAttribute (aCtx, Graphic3d_TOA_POS);
+    glDisable (GL_BLEND);
+
+    myChartLines->Bind (aCtx);
+    myChartLines->bindAttribute (aCtx, Graphic3d_TOA_POS,   3, GL_FLOAT,         myChartLines->GetComponentsNb(), NULL);
+    myChartLines->bindAttribute (aCtx, Graphic3d_TOA_COLOR, 4, GL_UNSIGNED_BYTE, myChartLines->GetComponentsNb(), (void* )sizeof(Graphic3d_Vec3));
+    aCtx->core15fwd->glDrawArrays (GL_LINES, 0, myChartLines->GetElemsNb());
+    myChartLines->Unbind (aCtx);
+    myChartLines->unbindAttribute (aCtx, Graphic3d_TOA_COLOR);
+    myChartLines->unbindAttribute (aCtx, Graphic3d_TOA_POS);
+
+    myChartLabels[0].Render (theWorkspace);
+    myChartLabels[1].Render (theWorkspace);
+    myChartLabels[2].Render (theWorkspace);
+
+    aCtx->WorldViewState.Pop();
+  }
 
-  aCtx->WorldViewState.Pop();
   aCtx->ModelWorldState.Pop();
   aCtx->ApplyWorldViewMatrix();
 
+  theWorkspace->SetAspectText (aTextAspectBack);
   if (theWorkspace->UseDepthWrite() != wasEnabledDepth)
   {
     theWorkspace->UseDepthWrite() = wasEnabledDepth;
index f7c0f89..3523164 100644 (file)
 #include <OpenGl_FrameStats.hxx>
 #include <OpenGl_Text.hxx>
 
+class Graphic3d_ArrayOfTriangles;
 class Graphic3d_TransformPers;
+class OpenGl_IndexBuffer;
+class OpenGl_VertexBuffer;
 
 //! Element rendering frame statistics.
-class OpenGl_FrameStatsPrs : public OpenGl_Text
+class OpenGl_FrameStatsPrs : public OpenGl_Element
 {
 public:
 
@@ -44,9 +47,21 @@ public:
 
 protected:
 
-  OpenGl_AspectText               myTextAspect; //!< text aspect
-  Handle(Graphic3d_TransformPers) myTrsfPers;   //!< transformation persistence
-  Handle(OpenGl_FrameStats)       myStatsPrev;  //!< currently displayed stats
+  //! Update chart presentation.
+  Standard_EXPORT void updateChart (const Handle(OpenGl_Workspace)& theWorkspace);
+
+protected:
+
+  Handle(OpenGl_FrameStats)          myStatsPrev;         //!< currently displayed stats
+  Handle(Graphic3d_TransformPers)    myCountersTrsfPers;  //!< transformation persistence for counters presentation
+  OpenGl_Text                        myCountersText;      //!< counters presentation
+  OpenGl_AspectText                  myTextAspect;        //!< text aspect
+  Handle(Graphic3d_TransformPers)    myChartTrsfPers;     //!< transformation persistence for chart presentation
+  Handle(Graphic3d_ArrayOfTriangles) myChartArray;        //!< array of chart triangles
+  Handle(OpenGl_VertexBuffer)        myChartVertices;     //!< VBO with chart triangles
+  Handle(OpenGl_IndexBuffer)         myChartIndices;      //!< VBO with chart triangle indexes
+  Handle(OpenGl_VertexBuffer)        myChartLines;        //!< array of chart lines
+  OpenGl_Text                        myChartLabels[3];    //!< chart labels
 
 };
 
index c19ad51..43e9eda 100644 (file)
@@ -514,6 +514,10 @@ void OpenGl_LayerList::SetLayerSettings (const Graphic3d_ZLayerId        theLaye
 void OpenGl_LayerList::UpdateCulling (const Handle(OpenGl_Workspace)& theWorkspace,
                                       const Standard_Boolean theToDrawImmediate)
 {
+  const Handle(OpenGl_FrameStats)& aStats = theWorkspace->GetGlContext()->FrameStats();
+  OSD_Timer& aTimer = aStats->ActiveDataFrame().ChangeTimer (Graphic3d_FrameStatsTimer_CpuCulling);
+  aTimer.Start();
+
   const Standard_Integer aViewId = theWorkspace->View()->Identification();
   const OpenGl_BVHTreeSelector& aSelector = theWorkspace->View()->BVHTreeSelector();
   for (OpenGl_IndexedLayerIterator anIts (myLayers); anIts.More(); anIts.Next())
@@ -526,6 +530,9 @@ void OpenGl_LayerList::UpdateCulling (const Handle(OpenGl_Workspace)& theWorkspa
 
     aLayer.UpdateCulling (aViewId, aSelector, theWorkspace->IsCullingEnabled());
   }
+
+  aTimer.Stop();
+  aStats->ActiveDataFrame()[Graphic3d_FrameStatsTimer_CpuCulling] = aTimer.UserTimeCPU();
 }
 
 //=======================================================================
index ccfd921..e076ae1 100755 (executable)
@@ -47,6 +47,9 @@ public:
                                const OpenGl_TextParam&  theParams,
                                const bool               theHasOwnAnchor = true);
 
+  //! Destructor
+  Standard_EXPORT virtual ~OpenGl_Text();
+
   //! Setup new string and position
   Standard_EXPORT void Init (const Handle(OpenGl_Context)& theCtx,
                              const Standard_Utf8Char*      theText,
@@ -68,6 +71,12 @@ public:
   Standard_EXPORT virtual void Render  (const Handle(OpenGl_Workspace)& theWorkspace) const;
   Standard_EXPORT virtual void Release (OpenGl_Context* theContext);
 
+  //! Return defined text.
+  const NCollection_String& Text() const { return myString; }
+
+  //! Return text formatting parameters.
+  const OpenGl_TextParam& FormatParams() const { return myParams; }
+
 public: //! @name methods for compatibility with layers
 
   //! Empty constructor
@@ -108,9 +117,6 @@ public: //! @name methods for compatibility with layers
 
 protected:
 
-  //! Destructor
-  Standard_EXPORT virtual ~OpenGl_Text();
-
   friend class OpenGl_Trihedron;
   friend class OpenGl_GraduatedTrihedron;
 
index 5154272..c24873b 100644 (file)
@@ -312,7 +312,7 @@ public:
   const OpenGl_LayerList& LayerList() const { return myZLayers; }
 
   //! Returns OpenGL window implementation.
-  const Handle(OpenGl_Window) GlWindow() const { return myWindow; }
+  const Handle(OpenGl_Window)& GlWindow() const { return myWindow; }
 
   //! Returns OpenGL environment map.
   const Handle(OpenGl_TextureSet)& GlTextureEnv() const { return myTextureEnv; }
index ff5ce5a..b88fcd1 100644 (file)
@@ -166,7 +166,7 @@ void OpenGl_View::Redraw()
   const Graphic3d_StereoMode   aStereoMode  = myRenderParams.StereoMode;
   Graphic3d_Camera::Projection aProjectType = myCamera->ProjectionType();
   Handle(OpenGl_Context)       aCtx         = myWorkspace->GetGlContext();
-  aCtx->FrameStats()->FrameStart (myWorkspace);
+  aCtx->FrameStats()->FrameStart (myWorkspace->View(), false);
 
   // release pending GL resources
   aCtx->ReleaseDelayed();
@@ -566,7 +566,7 @@ void OpenGl_View::Redraw()
 
   // reset render mode state
   aCtx->FetchState();
-  aCtx->FrameStats()->FrameEnd (myWorkspace);
+  aCtx->FrameStats()->FrameEnd (myWorkspace->View(), false);
 
   myWasRedrawnGL = Standard_True;
 }
@@ -592,7 +592,7 @@ void OpenGl_View::RedrawImmediate()
   const Graphic3d_StereoMode   aStereoMode  = myRenderParams.StereoMode;
   Graphic3d_Camera::Projection aProjectType = myCamera->ProjectionType();
   OpenGl_FrameBuffer*          aFrameBuffer = myFBO.operator->();
-  aCtx->FrameStats()->FrameStart (myWorkspace);
+  aCtx->FrameStats()->FrameStart (myWorkspace->View(), true);
 
   if ( aFrameBuffer == NULL
    && !aCtx->DefaultFrameBuffer().IsNull()
@@ -733,7 +733,7 @@ void OpenGl_View::RedrawImmediate()
   {
     aCtx->core11fwd->glFlush();
   }
-  aCtx->FrameStats()->FrameEnd (myWorkspace);
+  aCtx->FrameStats()->FrameEnd (myWorkspace->View(), true);
 
   myWasRedrawnGL = Standard_True;
 }
index 73b88b8..14b3d91 100644 (file)
@@ -10174,10 +10174,17 @@ static Standard_Boolean parsePerfStatsFlag (const TCollection_AsciiString& theVa
   else if (aVal == "mem"
         || aVal == "gpumem"
         || aVal == "estimmem")   aFlag = Graphic3d_RenderingParams::PerfCounters_EstimMem;
+  else if (aVal == "skipimmediate"
+        || aVal == "noimmediate") aFlag = Graphic3d_RenderingParams::PerfCounters_SkipImmediate;
+  else if (aVal == "frametime"
+        || aVal == "frametimers"
+        || aVal == "time")       aFlag = Graphic3d_RenderingParams::PerfCounters_FrameTime;
   else if (aVal == "basic")      aFlag = Graphic3d_RenderingParams::PerfCounters_Basic;
   else if (aVal == "extended"
         || aVal == "verbose"
         || aVal == "extra")      aFlag = Graphic3d_RenderingParams::PerfCounters_Extended;
+  else if (aVal == "full"
+        || aVal == "all")        aFlag = Graphic3d_RenderingParams::PerfCounters_All;
   else
   {
     return Standard_False;
@@ -10363,6 +10370,14 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
       {
         theDI << " gpumem";
       }
+      if ((aParams.CollectedStats & Graphic3d_RenderingParams::PerfCounters_FrameTime) != 0)
+      {
+        theDI << " frameTime";
+      }
+      if ((aParams.CollectedStats & Graphic3d_RenderingParams::PerfCounters_SkipImmediate) != 0)
+      {
+        theDI << " skipimmediate";
+      }
       if (aParams.CollectedStats == Graphic3d_RenderingParams::PerfCounters_NONE)
       {
         theDI << " none";
@@ -11024,6 +11039,26 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
       }
       aView->ChangeRenderingParams().StatsUpdateInterval = (Standard_ShortReal )Draw::Atof (theArgVec[anArgIter]);
     }
+    else if (aFlag == "-perfchart"
+          || aFlag == "-statschart")
+    {
+      if (++anArgIter >= theArgNb)
+      {
+        std::cout << "Error: wrong syntax at argument '" << anArg << "'\n";
+        return 1;
+      }
+      aView->ChangeRenderingParams().StatsNbFrames = Draw::Atoi (theArgVec[anArgIter]);
+    }
+    else if (aFlag == "-perfchartmax"
+          || aFlag == "-statschartmax")
+    {
+      if (++anArgIter >= theArgNb)
+      {
+        std::cout << "Error: wrong syntax at argument '" << anArg << "'\n";
+        return 1;
+      }
+      aView->ChangeRenderingParams().StatsMaxChartTime = (Standard_ShortReal )Draw::Atof (theArgVec[anArgIter]);
+    }
     else
     {
       std::cout << "Error: wrong syntax, unknown flag '" << anArg << "'\n";
@@ -12542,9 +12577,12 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
     "\n      '-exposure     value'       Exposure value for tone mapping (0.0 value disables the effect)"
     "\n      '-whitepoint   value'       White point value for filmic tone mapping"
     "\n      '-tonemapping  mode'        Tone mapping mode (disabled, filmic)"
-    "\n      '-perfCounters none|fps|cpu|layers|structures|groups|arrays|triagles|points|gpuMem|basic|extended|nofps'"
+    "\n      '-perfCounters none|fps|cpu|layers|structures|groups|arrays|triagles|points"
+    "\n      '              |gpuMem|frameTime|basic|extended|full|nofps|skipImmediate'"
     "\n                                  Show/hide performance counters (flags can be combined)"
     "\n      '-perfUpdateInterval nbSeconds' Performance counters update interval"
+    "\n      '-perfChart    nbFrames'    Show frame timers chart limited by specified number of frames"
+    "\n      '-perfChartMax seconds'     Maximum time in seconds with the chart"
     "\n    Unlike vcaps, these parameters dramatically change visual properties."
     "\n    Command is intended to control presentation quality depending on"
     "\n    hardware capabilities and performance.",