0029346: Visualization, TKOpenGl - collect frame statistics IR-2017-12-01
authorkgv <kgv@opencascade.com>
Fri, 24 Nov 2017 15:43:01 +0000 (18:43 +0300)
committerbugmaster <bugmaster@opencascade.com>
Fri, 1 Dec 2017 12:36:55 +0000 (15:36 +0300)
Graphic3d_RenderingParams::ToShowStats - new option displaying rendering statistics.

OpenGl_FrameStats - new class for accumulating frame statistics.
OpenGl_Context::FrameStats() provides an access to the frame stats
used for currently rendered context.
OpenGl_View::Redraw() and OpenGl_View::RedrawImmediate()
resets counters within OpenGl_Context::FrameStats().

OpenGl_Layer::UpdateCulling() - simplified resetting of culling state for cullable structures.

36 files changed:
samples/tcl/spheres.tcl
src/Graphic3d/Graphic3d_RenderingParams.hxx
src/OpenGl/FILES
src/OpenGl/OpenGl_BVHClipPrimitiveSet.hxx
src/OpenGl/OpenGl_BVHClipPrimitiveTrsfPersSet.hxx
src/OpenGl/OpenGl_CappingPlaneResource.hxx
src/OpenGl/OpenGl_Context.cxx
src/OpenGl/OpenGl_Context.hxx
src/OpenGl/OpenGl_Font.cxx
src/OpenGl/OpenGl_Font.hxx
src/OpenGl/OpenGl_FrameBuffer.cxx
src/OpenGl/OpenGl_FrameBuffer.hxx
src/OpenGl/OpenGl_FrameStats.cxx [new file with mode: 0644]
src/OpenGl/OpenGl_FrameStats.hxx [new file with mode: 0644]
src/OpenGl/OpenGl_FrameStatsPrs.cxx [new file with mode: 0644]
src/OpenGl/OpenGl_FrameStatsPrs.hxx [new file with mode: 0644]
src/OpenGl/OpenGl_Layer.cxx
src/OpenGl/OpenGl_Layer.hxx
src/OpenGl/OpenGl_LineAttributes.hxx
src/OpenGl/OpenGl_PrimitiveArray.cxx
src/OpenGl/OpenGl_PrimitiveArray.hxx
src/OpenGl/OpenGl_Resource.hxx
src/OpenGl/OpenGl_Sampler.hxx
src/OpenGl/OpenGl_ShaderObject.hxx
src/OpenGl/OpenGl_ShaderProgram.hxx
src/OpenGl/OpenGl_Structure.hxx
src/OpenGl/OpenGl_Text.hxx
src/OpenGl/OpenGl_Texture.cxx
src/OpenGl/OpenGl_Texture.hxx
src/OpenGl/OpenGl_VertexBuffer.hxx
src/OpenGl/OpenGl_View.cxx
src/OpenGl/OpenGl_View.hxx
src/OpenGl/OpenGl_View_Redraw.cxx
src/ViewerTest/ViewerTest_ViewerCommands.cxx
tests/bugs/vis/bug24307_1
tests/bugs/vis/bug24307_2

index 2267912..58ef64e 100644 (file)
@@ -9,6 +9,8 @@ pload VISUALIZATION
 
 vinit View1 w=1024 h=1024
 vclear
+vdefaults -autoTriang 0
+vrenderparams -stats basic
 
 # parameter NB defines number of spheres by each coordinate
 set NB 10
@@ -23,11 +25,12 @@ for {set i 0} {$i < $NB} {incr i} {
     }
   }
 }
+eval compound $slist c
+incmesh c 0.006
 
 puts "Measuring FPS of display of spheres as separate objects..."
 vaxo
-vsetdispmode 1
-eval vdisplay $slist
+eval vdisplay -dispMode 1 $slist
 vfit
 
 # measure FPS
@@ -35,16 +38,14 @@ puts [set fps_separate [vfps]]
 vclear
 
 puts "Measuring FPS of display of spheres as single object..."
-eval compound $slist c
-vdisplay c
+vdisplay -dispMode 1 c
 
 # measure FPS
 puts [set fps_compound [vfps]]
 vclear
 
 # redisplay individual spheres, trying to avoid unnecessary internal updates
-#vfrustumculling 0 ;# try to disable updates of frustum culling structures
-eval vdisplay -mutable $slist
+eval vdisplay -dispMode 1 $slist
 
 # auxiliary procedure to make random update of variable
 proc upd {theValueName theDeltaName theTime theToRand} {
@@ -69,8 +70,10 @@ proc animateSpheres {{theDuration 10.0}} {
   for {set i 0} {$i < $::NB} {incr i $nb} {
     for {set j 0} {$j < $::NB} {incr j $nb} {
       for {set k 0} {$k < $::NB} {incr k $nb} {
+        # mark animated spheres mutable for faster updates
+        uplevel #0 vdisplay -dispMode 1 -mutable s$i$j$k
 #       vaspects -noupdate s$i$j$k -setcolor red -setmaterial plastic
-        vaspects -noupdate s$i$j$k -setcolor red
+        uplevel #0 vaspects -noupdate s$i$j$k -setcolor red
         set x$i$j$k  0.0
         set y$i$j$k  0.0
         set z$i$j$k  0.0
@@ -142,4 +145,4 @@ puts "Animation FPS: $fps_animation"
 puts ""
 puts "Scene contains [lindex [trinfo c] 3] triangles"
 puts ""
-puts "Print 'animateSpheres 10.0' to restart animation"
\ No newline at end of file
+puts "Print 'animateSpheres 10.0' to restart animation"
index 5e7a362..17f4913 100644 (file)
@@ -16,7 +16,8 @@
 #ifndef _Graphic3d_RenderingParams_HeaderFile
 #define _Graphic3d_RenderingParams_HeaderFile
 
-#include <Graphic3d_Mat4.hxx>
+#include <Graphic3d_AspectText3d.hxx>
+#include <Graphic3d_TransformPers.hxx>
 #include <Graphic3d_RenderTransparentMethod.hxx>
 #include <Graphic3d_RenderingMode.hxx>
 #include <Graphic3d_StereoMode.hxx>
@@ -44,6 +45,31 @@ public:
     Anaglyph_UserDefined           //!< use externally specified matrices
   };
 
+  //! Statistics display flags.
+  enum PerfCounters
+  {
+    PerfCounters_NONE        = 0x000, //!< no stats
+    PerfCounters_FrameRate   = 0x001, //!< Frame Rate
+    PerfCounters_CPU         = 0x002, //!< CPU utilization
+    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)
+    //
+    PerfCounters_Groups      = 0x010, //!< count primitive Groups (1 Structure holds 1 or more primitive Group)
+    PerfCounters_GroupArrays = 0x020, //!< count Arrays within Primitive Groups (optimal primitive Group holds 1 Array)
+    //
+    PerfCounters_Triangles   = 0x040, //!< count Triangles
+    PerfCounters_Points      = 0x080, //!< count Points
+    //
+    PerfCounters_EstimMem    = 0x100, //!< estimated GPU memory usage
+    //! show basic statistics
+    PerfCounters_Basic = PerfCounters_FrameRate | PerfCounters_CPU | PerfCounters_Layers | PerfCounters_Structures,
+    //! extended (verbose) statistics
+    PerfCounters_Extended = PerfCounters_Basic
+                          | PerfCounters_Groups | PerfCounters_GroupArrays
+                          | PerfCounters_Triangles | PerfCounters_Points
+                          | PerfCounters_EstimMem,
+  };
+
 public:
 
   //! Creates default rendering parameters.
@@ -78,7 +104,14 @@ public:
     StereoMode (Graphic3d_StereoMode_QuadBuffer),
     AnaglyphFilter (Anaglyph_RedCyan_Optimized),
     ToReverseStereo (Standard_False),
-
+    //
+    StatsPosition (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_LEFT_UPPER, Graphic3d_Vec2i (20, 20))),
+    StatsTextAspect (new Graphic3d_AspectText3d()),
+    StatsUpdateInterval (1.0),
+    StatsTextHeight (16),
+    CollectedStats (PerfCounters_Basic),
+    ToShowStats (Standard_False),
+    //
     Resolution (THE_DEFAULT_RESOLUTION)
   {
     const Graphic3d_Vec4 aZero (0.0f, 0.0f, 0.0f, 0.0f);
@@ -90,6 +123,13 @@ public:
     AnaglyphRight.SetRow (1, Graphic3d_Vec4 (0.0f,  1.0f,  0.0f, 0.0f));
     AnaglyphRight.SetRow (2, Graphic3d_Vec4 (0.0f,  0.0f,  1.0f, 0.0f));
     AnaglyphRight.SetRow (3, aZero);
+
+    StatsTextAspect->SetColor (Quantity_NOC_WHITE);
+    StatsTextAspect->SetColorSubTitle (Quantity_NOC_BLACK);
+    StatsTextAspect->SetFont (Font_NOF_ASCII_MONO);
+    StatsTextAspect->SetDisplayType (Aspect_TODT_SHADOW);
+    StatsTextAspect->SetTextZoomable (Standard_False);
+    StatsTextAspect->SetTextFontAspect (Font_FA_Regular);
   }
 
   //! Returns resolution ratio.
@@ -136,6 +176,20 @@ public:
   Graphic3d_Mat4                    AnaglyphRight;               //!< right anaglyph filter (in normalized colorspace), Color = AnaglyphRight * theColorRight + AnaglyphLeft * theColorLeft;
   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_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
+  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)
+  Standard_Boolean                  ToShowStats;                 //!< display performance statistics, FALSE by default;
+                                                                 //!  note that counters specified within CollectedStats will be updated nevertheless
+                                                                 //!  of visibility of widget managed by ToShowStats flag (e.g. stats can be retrieved by application for displaying using other methods)
+
   unsigned int                      Resolution;                  //!< Pixels density (PPI), defines scaling factor for parameters like text size
                                                                  //!  (when defined in screen-space units rather than in 3D) to be properly displayed
                                                                  //!  on device (screen / printer). 72 is default value.
index 64fa0de..80a9aa0 100755 (executable)
@@ -13,6 +13,10 @@ OpenGl_AspectMarker.cxx
 OpenGl_AspectMarker.hxx
 OpenGl_AspectText.cxx
 OpenGl_AspectText.hxx
+OpenGl_FrameStats.cxx
+OpenGl_FrameStats.hxx
+OpenGl_FrameStatsPrs.cxx
+OpenGl_FrameStatsPrs.hxx
 OpenGl_Group.hxx
 OpenGl_Group.cxx
 OpenGl_Structure.hxx
index 64cd4ca..902444c 100644 (file)
@@ -63,6 +63,9 @@ public:
   //! Returns the structure corresponding to the given ID.
   const OpenGl_Structure* GetStructureById (Standard_Integer theId);
 
+  //! Access directly a collection of structures.
+  const NCollection_IndexedMap<const OpenGl_Structure*>& Structures() const { return myStructs; }
+
 private:
 
   NCollection_IndexedMap<const OpenGl_Structure*> myStructs;    //!< Indexed map of structures.
index aa73e30..6eedd2b 100644 (file)
@@ -69,6 +69,9 @@ public:
   //! Returns the structure corresponding to the given ID.
   const OpenGl_Structure* GetStructureById (Standard_Integer theId);
 
+  //! Access directly a collection of structures.
+  const NCollection_IndexedMap<const OpenGl_Structure*>& Structures() const { return myStructs; }
+
   //! Marks object state as outdated (needs BVH rebuilding).
   void MarkDirty()
   {
index 43b4c11..7ac029f 100755 (executable)
@@ -54,6 +54,9 @@ public:
   //! @param theContext [in] the resource context.
   Standard_EXPORT virtual void Release (OpenGl_Context* theContext) Standard_OVERRIDE;
 
+  //! Returns estimated GPU memory usage - not implemented.
+  virtual Standard_Size EstimatedDataSize() const Standard_OVERRIDE { return 0; }
+
   //! Return parent clipping plane structure.
   const Handle(Graphic3d_ClipPlane)& Plane() const { return myPlaneRoot; }
 
index 6fb217a..494ae5c 100644 (file)
@@ -28,6 +28,7 @@
 #include <OpenGl_ArbTexBindless.hxx>
 #include <OpenGl_GlCore44.hxx>
 #include <OpenGl_FrameBuffer.hxx>
+#include <OpenGl_FrameStats.hxx>
 #include <OpenGl_Sampler.hxx>
 #include <OpenGl_ShaderManager.hxx>
 #include <OpenGl_Workspace.hxx>
@@ -175,6 +176,7 @@ OpenGl_Context::OpenGl_Context (const Handle(OpenGl_Caps)& theCaps)
   myHasRayTracing (Standard_False),
   myHasRayTracingTextures (Standard_False),
   myHasRayTracingAdaptiveSampling (Standard_False),
+  myFrameStats (new OpenGl_FrameStats()),
 #if !defined(GL_ES_VERSION_2_0)
   myPointSpriteOrig (GL_UPPER_LEFT),
   myRenderMode (GL_RENDER),
index a0e2da7..b2f01c6 100644 (file)
@@ -138,6 +138,7 @@ class OpenGl_FrameBuffer;
 class OpenGl_Sampler;
 class OpenGl_ShaderProgram;
 class OpenGl_ShaderManager;
+class OpenGl_FrameStats;
 
 enum OpenGl_FeatureFlag
 {
@@ -200,6 +201,8 @@ class OpenGl_Context : public Standard_Transient
   friend class OpenGl_Window;
 public:
 
+  typedef NCollection_Shared< NCollection_DataMap<TCollection_AsciiString, Handle(OpenGl_Resource)> > OpenGl_ResourcesMap;
+
   //! Function for getting power of to number larger or equal to input number.
   //! @param theNumber    number to 'power of two'
   //! @param theThreshold upper threshold
@@ -444,6 +447,9 @@ public:
   //! Clean up the delayed release queue.
   Standard_EXPORT void ReleaseDelayed();
 
+  //! Return map of shared resources.
+  const OpenGl_ResourcesMap& SharedResources() const { return *mySharedResources; }
+
   //! @return tool for management of clippings within this context.
   inline OpenGl_Clipping& ChangeClipping() { return myClippingState; }
 
@@ -590,6 +596,9 @@ public:
 
 public: //! @name methods to alter or retrieve current state
 
+  //! Return structure holding frame statistics.
+  const Handle(OpenGl_FrameStats)& FrameStats() const { return myFrameStats; }
+
   //! Return cached viewport definition (x, y, width, height).
   const Standard_Integer* Viewport() const { return myViewport; }
 
@@ -862,7 +871,6 @@ private: // system-dependent fields
 private: // context info
 
   typedef NCollection_Shared< NCollection_DataMap<TCollection_AsciiString, Standard_Integer> > OpenGl_DelayReleaseMap;
-  typedef NCollection_Shared< NCollection_DataMap<TCollection_AsciiString, Handle(OpenGl_Resource)> > OpenGl_ResourcesMap;
   typedef NCollection_Shared< NCollection_List<Handle(OpenGl_Resource)> > OpenGl_ResourcesStack;
   typedef NCollection_SparseArray<Standard_Integer> OpenGl_DrawBuffers;
 
@@ -898,6 +906,7 @@ private: // context info
 
 private: //! @name fields tracking current state
 
+  Handle(OpenGl_FrameStats)     myFrameStats;      //!< structure accumulating frame statistics
   Handle(OpenGl_ShaderProgram)  myActiveProgram;   //!< currently active GLSL program
   Handle(OpenGl_TextureSet)     myActiveTextures;  //!< currently bound textures
                                                    //!< currently active sampler objects
index 8cbd467..0692e87 100755 (executable)
@@ -80,6 +80,20 @@ void OpenGl_Font::Release (OpenGl_Context* theCtx)
 }
 
 // =======================================================================
+// function : EstimatedDataSize
+// purpose  :
+// =======================================================================
+Standard_Size OpenGl_Font::EstimatedDataSize() const
+{
+  Standard_Size aSize = 0;
+  for (NCollection_Vector<Handle(OpenGl_Texture)>::Iterator aTexIter (myTextures); aTexIter.More(); aTexIter.Next())
+  {
+    aSize += aTexIter.Value()->EstimatedDataSize();
+  }
+  return aSize;
+}
+
+// =======================================================================
 // function : Init
 // purpose  :
 // =======================================================================
index 4977b3f..a588762 100755 (executable)
@@ -61,6 +61,9 @@ public:
   //! Destroy object - will release GPU memory if any
   Standard_EXPORT virtual void Release (OpenGl_Context* theCtx) Standard_OVERRIDE;
 
+  //! Returns estimated GPU memory usage.
+  Standard_EXPORT virtual Standard_Size EstimatedDataSize() const Standard_OVERRIDE;
+
   //! @return key of shared resource
   inline const TCollection_AsciiString& ResourceKey() const
   {
index 7f301c9..2fdf7bb 100644 (file)
@@ -1061,3 +1061,35 @@ Standard_Boolean OpenGl_FrameBuffer::BufferDump (const Handle(OpenGl_Context)& t
 
   return !hasErrors;
 }
+
+// =======================================================================
+// function : EstimatedDataSize
+// purpose  :
+// =======================================================================
+Standard_Size OpenGl_FrameBuffer::EstimatedDataSize() const
+{
+  if (!IsValid())
+  {
+    return 0;
+  }
+
+  Standard_Size aSize = 0;
+  for (OpenGl_TextureArray::Iterator aTextureIt (myColorTextures); aTextureIt.More(); aTextureIt.Next())
+  {
+    aSize += aTextureIt.Value()->EstimatedDataSize();
+  }
+  if (!myDepthStencilTexture.IsNull())
+  {
+    aSize += myDepthStencilTexture->EstimatedDataSize();
+  }
+  if (myGlColorRBufferId != NO_RENDERBUFFER
+  && !myColorFormats.IsEmpty())
+  {
+    aSize += OpenGl_Texture::PixelSizeOfPixelFormat (myColorFormats.First()) * myInitVPSizeX * myInitVPSizeY;
+  }
+  if (myGlDepthRBufferId != NO_RENDERBUFFER)
+  {
+    aSize += OpenGl_Texture::PixelSizeOfPixelFormat (myDepthFormat) * myInitVPSizeX * myInitVPSizeY;
+  }
+  return aSize;
+}
index 254c87d..4b7cd22 100644 (file)
@@ -259,6 +259,9 @@ public:
     return myGlDepthRBufferId;
   }
 
+  //! Returns estimated GPU memory usage for holding data without considering overheads and allocation alignment rules.
+  Standard_EXPORT virtual Standard_Size EstimatedDataSize() const Standard_OVERRIDE;
+
 protected:
 
   Standard_Boolean isValidFrameBuffer() const
diff --git a/src/OpenGl/OpenGl_FrameStats.cxx b/src/OpenGl/OpenGl_FrameStats.cxx
new file mode 100644 (file)
index 0000000..17b040d
--- /dev/null
@@ -0,0 +1,522 @@
+// 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 <OpenGl_FrameStats.hxx>
+
+#include <OpenGl_GlCore20.hxx>
+#include <OpenGl_View.hxx>
+#include <OpenGl_Workspace.hxx>
+
+IMPLEMENT_STANDARD_RTTIEXT(OpenGl_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;
+    }
+    if (thePostfix != NULL)
+    {
+      theStream << thePostfix;
+    }
+    return theStream;
+  }
+
+  //! Return estimated data size.
+  static Standard_Size estimatedDataSize (const Handle(OpenGl_Resource)& theRes)
+  {
+    return !theRes.IsNull() ? theRes->EstimatedDataSize() : 0;
+  }
+}
+
+// =======================================================================
+// function : OpenGl_FrameStats
+// 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));
+}
+
+// =======================================================================
+// function : ~OpenGl_FrameStats
+// purpose  :
+// =======================================================================
+OpenGl_FrameStats::~OpenGl_FrameStats()
+{
+  //
+}
+
+// =======================================================================
+// function : FormatStats
+// purpose  :
+// =======================================================================
+TCollection_AsciiString OpenGl_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
+  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)
+  {
+    formatCounter (aBuf, aValWidth, "    Points: ", myCounters[Counter_NbPointsNotCulled], "\n");
+  }
+  if ((theFlags & Graphic3d_RenderingParams::PerfCounters_EstimMem) != 0)
+  {
+    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 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;
+  }
+}
+
+// =======================================================================
+// function : FrameEnd
+// purpose  :
+// =======================================================================
+void OpenGl_FrameStats::FrameEnd (const Handle(OpenGl_Workspace)& theWorkspace)
+{
+  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)
+  {
+    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 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;
+  const Standard_Boolean toCountElems   = (aBits & Graphic3d_RenderingParams::PerfCounters_GroupArrays) != 0 || toCountTris || toCountMem;
+  const Standard_Boolean toCountGroups  = (aBits & Graphic3d_RenderingParams::PerfCounters_Groups)      != 0 || toCountElems;
+  const Standard_Boolean toCountStructs = (aBits & Graphic3d_RenderingParams::PerfCounters_Structures)  != 0 || toCountGroups;
+  
+  if (toCountStructs)
+  {
+    myCountersTmp[Counter_NbLayers] = theWorkspace->View()->LayerList().Layers().Size();
+    for (OpenGl_SequenceOfLayers::Iterator aLayerIter (theWorkspace->View()->LayerList().Layers()); aLayerIter.More(); aLayerIter.Next())
+    {
+      const Handle(OpenGl_Layer)& aLayer = aLayerIter.Value();
+      if (!aLayer->IsCulled())
+      {
+        ++myCountersTmp[Counter_NbLayersNotCulled];
+      }
+      myCountersTmp[Counter_NbStructs]          += aLayer->NbStructures();
+      myCountersTmp[Counter_NbStructsNotCulled] += aLayer->NbStructuresNotCulled();
+      if (toCountGroups)
+      {
+        updateStructures (aLayer->CullableStructuresBVH().Structures(), toCountStructs, toCountTris, toCountMem);
+        updateStructures (aLayer->CullableTrsfPersStructuresBVH().Structures(), toCountStructs, toCountTris, toCountMem);
+        updateStructures (aLayer->NonCullableStructures(), toCountStructs, toCountTris, toCountMem);
+      }
+    }
+  }
+  if (toCountMem
+  && !theWorkspace.IsNull())
+  {
+    for (OpenGl_Context::OpenGl_ResourcesMap::Iterator aResIter (theWorkspace->GetGlContext()->SharedResources());
+         aResIter.More(); aResIter.Next())
+    {
+      myCountersTmp[Counter_EstimatedBytesTextures] += aResIter.Value()->EstimatedDataSize();
+    }
+
+    const OpenGl_View* aView = theWorkspace->View();
+    {
+      Standard_Size& aMemFbos = myCountersTmp[Counter_EstimatedBytesFbos];
+      // main FBOs
+      aMemFbos += estimatedDataSize (aView->myMainSceneFbos[0]);
+      aMemFbos += estimatedDataSize (aView->myMainSceneFbos[1]);
+      aMemFbos += estimatedDataSize (aView->myImmediateSceneFbos[0]);
+      aMemFbos += estimatedDataSize (aView->myImmediateSceneFbos[1]);
+      // OIT FBOs
+      aMemFbos += estimatedDataSize (aView->myMainSceneFbosOit[0]);
+      aMemFbos += estimatedDataSize (aView->myMainSceneFbosOit[1]);
+      aMemFbos += estimatedDataSize (aView->myImmediateSceneFbosOit[0]);
+      aMemFbos += estimatedDataSize (aView->myImmediateSceneFbosOit[1]);
+      // dump FBO
+      aMemFbos += estimatedDataSize (aView->myFBO);
+      // RayTracing FBO
+      aMemFbos += estimatedDataSize (aView->myOpenGlFBO);
+      aMemFbos += estimatedDataSize (aView->myOpenGlFBO2);
+      aMemFbos += estimatedDataSize (aView->myRaytraceFBO1[0]);
+      aMemFbos += estimatedDataSize (aView->myRaytraceFBO1[1]);
+      aMemFbos += estimatedDataSize (aView->myRaytraceFBO2[0]);
+      aMemFbos += estimatedDataSize (aView->myRaytraceFBO2[1]);
+      // also RayTracing
+      aMemFbos += estimatedDataSize (aView->myRaytraceOutputTexture[0]);
+      aMemFbos += estimatedDataSize (aView->myRaytraceOutputTexture[1]);
+      aMemFbos += estimatedDataSize (aView->myRaytraceVisualErrorTexture[0]);
+      aMemFbos += estimatedDataSize (aView->myRaytraceVisualErrorTexture[1]);
+      aMemFbos += estimatedDataSize (aView->myRaytraceTileOffsetsTexture[0]);
+      aMemFbos += estimatedDataSize (aView->myRaytraceTileOffsetsTexture[1]);
+    }
+    {
+      // Ray Tracing geometry
+      Standard_Size& aMemGeom = myCountersTmp[Counter_EstimatedBytesGeom];
+      aMemGeom += estimatedDataSize (aView->mySceneNodeInfoTexture);
+      aMemGeom += estimatedDataSize (aView->mySceneMinPointTexture);
+      aMemGeom += estimatedDataSize (aView->mySceneMaxPointTexture);
+      aMemGeom += estimatedDataSize (aView->mySceneTransformTexture);
+      aMemGeom += estimatedDataSize (aView->myGeometryVertexTexture);
+      aMemGeom += estimatedDataSize (aView->myGeometryNormalTexture);
+      aMemGeom += estimatedDataSize (aView->myGeometryTexCrdTexture);
+      aMemGeom += estimatedDataSize (aView->myGeometryTriangTexture);
+      aMemGeom += estimatedDataSize (aView->myRaytraceMaterialTexture);
+      aMemGeom += estimatedDataSize (aView->myRaytraceLightSrcTexture);
+    }
+  }
+  memcpy (myCounters, myCountersTmp, sizeof(myCounters));
+}
+
+// =======================================================================
+// function : updateStructures
+// purpose  :
+// =======================================================================
+void OpenGl_FrameStats::updateStructures (const OpenGl_IndexedMapOfStructure& theStructures,
+                                          Standard_Boolean theToCountElems,
+                                          Standard_Boolean theToCountTris,
+                                          Standard_Boolean theToCountMem)
+{
+  for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (theStructures); aStructIter.More(); aStructIter.Next())
+  {
+    const OpenGl_Structure* aStruct = aStructIter.Value();
+    if (aStruct->IsCulled())
+    {
+      if (theToCountMem)
+      {
+        for (OpenGl_Structure::GroupIterator aGroupIter (aStruct->Groups()); aGroupIter.More(); aGroupIter.Next())
+        {
+          const OpenGl_Group* aGroup = aGroupIter.Value();
+          for (const OpenGl_ElementNode* aNodeIter = aGroup->FirstNode(); aNodeIter != NULL; aNodeIter = aNodeIter->next)
+          {
+            if (const OpenGl_PrimitiveArray* aPrim = dynamic_cast<const OpenGl_PrimitiveArray*> (aNodeIter->elem))
+            {
+              myCountersTmp[Counter_EstimatedBytesGeom] += estimatedDataSize (aPrim->AttributesVbo());
+              myCountersTmp[Counter_EstimatedBytesGeom] += estimatedDataSize (aPrim->IndexVbo());
+            }
+          }
+        }
+      }
+      continue;
+    }
+
+    myCountersTmp[Counter_NbGroupsNotCulled] += aStruct->Groups().Size();
+    if (!theToCountElems)
+    {
+      continue;
+    }
+
+    for (OpenGl_Structure::GroupIterator aGroupIter (aStruct->Groups()); aGroupIter.More(); aGroupIter.Next())
+    {
+      const OpenGl_Group* aGroup = aGroupIter.Value();
+      for (const OpenGl_ElementNode* aNodeIter = aGroup->FirstNode(); aNodeIter != NULL; aNodeIter = aNodeIter->next)
+      {
+        if (const OpenGl_PrimitiveArray* aPrim = dynamic_cast<const OpenGl_PrimitiveArray*> (aNodeIter->elem))
+        {
+          ++myCountersTmp[Counter_NbElemsNotCulled];
+          if (theToCountMem)
+          {
+            myCountersTmp[Counter_EstimatedBytesGeom] += estimatedDataSize (aPrim->AttributesVbo());
+            myCountersTmp[Counter_EstimatedBytesGeom] += estimatedDataSize (aPrim->IndexVbo());
+          }
+
+          if (aPrim->IsFillDrawMode())
+          {
+            ++myCountersTmp[Counter_NbElemsFillNotCulled];
+            if (!theToCountTris)
+            {
+              continue;
+            }
+
+            const Handle(OpenGl_VertexBuffer)& anAttribs = aPrim->AttributesVbo();
+            if (anAttribs.IsNull()
+            || !anAttribs->IsValid())
+            {
+              continue;
+            }
+
+            const Handle(OpenGl_VertexBuffer)& anIndices = aPrim->IndexVbo();
+            const Standard_Integer aNbIndices = !anIndices.IsNull() ? anIndices->GetElemsNb() : anAttribs->GetElemsNb();
+            const Standard_Integer aNbBounds  = !aPrim->Bounds().IsNull() ? aPrim->Bounds()->NbBounds : 1;
+            switch (aPrim->DrawMode())
+            {
+              case GL_TRIANGLES:
+              {
+                myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices / 3;
+                break;
+              }
+              case GL_TRIANGLE_STRIP:
+              case GL_TRIANGLE_FAN:
+              {
+                myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices - 2 * aNbBounds;
+                break;
+              }
+              case GL_TRIANGLES_ADJACENCY:
+              {
+                myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices / 6;
+                break;
+              }
+              case GL_TRIANGLE_STRIP_ADJACENCY:
+              {
+                myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices - 4 * aNbBounds;
+                break;
+              }
+            #if !defined(GL_ES_VERSION_2_0)
+              case GL_QUADS:
+              {
+                myCountersTmp[Counter_NbTrianglesNotCulled] += aNbIndices / 2;
+                break;
+              }
+              case GL_QUAD_STRIP:
+              {
+                myCountersTmp[Counter_NbTrianglesNotCulled] += (aNbIndices / 2 - aNbBounds) * 2;
+                break;
+              }
+            #endif
+            }
+          }
+          else if (aPrim->DrawMode() == GL_POINTS)
+          {
+            ++myCountersTmp[Counter_NbElemsPointNotCulled];
+            if (theToCountTris)
+            {
+              const Handle(OpenGl_VertexBuffer)& anAttribs = aPrim->AttributesVbo();
+              if (!anAttribs.IsNull()
+                && anAttribs->IsValid())
+              {
+                const Handle(OpenGl_VertexBuffer)& anIndices = aPrim->IndexVbo();
+                const Standard_Integer aNbIndices = !anIndices.IsNull() ? anIndices->GetElemsNb() : anAttribs->GetElemsNb();
+                myCountersTmp[Counter_NbPointsNotCulled] += aNbIndices;
+              }
+            }
+          }
+          else
+          {
+            ++myCountersTmp[Counter_NbElemsLineNotCulled];
+          }
+        }
+        else if (const OpenGl_Text* aText = dynamic_cast<const OpenGl_Text*> (aNodeIter->elem))
+        {
+          (void )aText;
+          ++myCountersTmp[Counter_NbElemsNotCulled];
+          ++myCountersTmp[Counter_NbElemsTextNotCulled];
+        }
+      }
+    }
+  }
+}
diff --git a/src/OpenGl/OpenGl_FrameStats.hxx b/src/OpenGl/OpenGl_FrameStats.hxx
new file mode 100644 (file)
index 0000000..0ed9361
--- /dev/null
@@ -0,0 +1,161 @@
+// 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 _OpenGl_FrameStats_HeaderFile
+#define _OpenGl_FrameStats_HeaderFile
+
+#include <Graphic3d_RenderingParams.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
+{
+  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 };
+
+public:
+
+  //! Default constructor.
+  Standard_EXPORT OpenGl_FrameStats();
+
+  //! 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]; }
+
+protected:
+
+  //! Updates counters for structures.
+  Standard_EXPORT virtual void updateStructures (const OpenGl_IndexedMapOfStructure& theStructures,
+                                                 Standard_Boolean theToCountElems,
+                                                 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)
+
+#endif // _OpenGl_FrameStats_HeaderFile
diff --git a/src/OpenGl/OpenGl_FrameStatsPrs.cxx b/src/OpenGl/OpenGl_FrameStatsPrs.cxx
new file mode 100644 (file)
index 0000000..8abdc66
--- /dev/null
@@ -0,0 +1,144 @@
+// 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 <OpenGl_FrameStatsPrs.hxx>
+
+#include <OpenGl_View.hxx>
+#include <OpenGl_ShaderManager.hxx>
+#include <OpenGl_Workspace.hxx>
+
+// =======================================================================
+// function : OpenGl_FrameStatsPrs
+// purpose  :
+// =======================================================================
+OpenGl_FrameStatsPrs::OpenGl_FrameStatsPrs()
+: myStatsPrev (new OpenGl_FrameStats())
+{
+  myParams.HAlign = Graphic3d_HTA_CENTER;
+  myParams.VAlign = Graphic3d_VTA_CENTER;
+  myHasPlane      = false;
+}
+
+// =======================================================================
+// function : ~OpenGl_FrameStatsPrs
+// purpose  :
+// =======================================================================
+OpenGl_FrameStatsPrs::~OpenGl_FrameStatsPrs()
+{
+  //
+}
+
+// =======================================================================
+// function : Release
+// purpose  :
+// =======================================================================
+void OpenGl_FrameStatsPrs::Release (OpenGl_Context* theCtx)
+{
+  OpenGl_Text::Release (theCtx);
+}
+
+// =======================================================================
+// function : Update
+// purpose  :
+// =======================================================================
+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;
+  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)
+  {
+    myParams.HAlign = Graphic3d_HTA_LEFT;
+  }
+  else if (!myTrsfPers.IsNull() && (myTrsfPers->Corner2d() & Aspect_TOTP_RIGHT) != 0)
+  {
+    myParams.HAlign = Graphic3d_HTA_RIGHT;
+  }
+  if (!myTrsfPers.IsNull() && (myTrsfPers->Corner2d() & Aspect_TOTP_TOP) != 0)
+  {
+    myParams.VAlign = Graphic3d_VTA_TOP;
+  }
+  else if (!myTrsfPers.IsNull() && (myTrsfPers->Corner2d() & Aspect_TOTP_BOTTOM) != 0)
+  {
+    myParams.VAlign = Graphic3d_VTA_BOTTOM;
+  }
+  if (myParams.Height != aParamsPrev.Height
+   || myParams.HAlign != aParamsPrev.HAlign
+   || myParams.VAlign != aParamsPrev.VAlign)
+  {
+    Release (aCtx.operator->());
+  }
+
+  if (myStatsPrev->IsEqual (aStats)
+  && !myString.IsEmpty())
+  {
+    return;
+  }
+
+  myStatsPrev->CopyFrom (aStats);
+  const TCollection_AsciiString aText = aStats->FormatStats (aRendParams.CollectedStats);
+
+  releaseVbos (aCtx.operator->());
+  myString.FromUnicode (aText.ToCString());
+}
+
+// =======================================================================
+// function : Render
+// purpose  :
+// =======================================================================
+void OpenGl_FrameStatsPrs::Render (const Handle(OpenGl_Workspace)& theWorkspace) const
+{
+  const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
+  const Standard_Boolean wasEnabledDepth = theWorkspace->UseDepthWrite();
+  if (theWorkspace->UseDepthWrite())
+  {
+    theWorkspace->UseDepthWrite() = Standard_False;
+    glDepthMask (GL_FALSE);
+  }
+
+  aCtx->ModelWorldState.Push();
+  aCtx->ModelWorldState.ChangeCurrent().InitIdentity();
+
+  aCtx->WorldViewState.Push();
+  OpenGl_Mat4& aWorldView = aCtx->WorldViewState.ChangeCurrent();
+  if (!myTrsfPers.IsNull())
+  {
+    myTrsfPers->Apply (theWorkspace->View()->Camera(),
+                      aCtx->ProjectionState.Current(), aWorldView,
+                      aCtx->VirtualViewport()[2], aCtx->VirtualViewport()[3]);
+  }
+
+  aCtx->ApplyModelViewMatrix();
+
+  const OpenGl_AspectText* aTextAspectBack = theWorkspace->SetAspectText (&myTextAspect);
+  OpenGl_Text::Render (theWorkspace);
+  theWorkspace->SetAspectText (aTextAspectBack);
+
+  aCtx->WorldViewState.Pop();
+  aCtx->ModelWorldState.Pop();
+  aCtx->ApplyWorldViewMatrix();
+
+  if (theWorkspace->UseDepthWrite() != wasEnabledDepth)
+  {
+    theWorkspace->UseDepthWrite() = wasEnabledDepth;
+    glDepthMask (wasEnabledDepth ? GL_TRUE : GL_FALSE);
+  }
+}
diff --git a/src/OpenGl/OpenGl_FrameStatsPrs.hxx b/src/OpenGl/OpenGl_FrameStatsPrs.hxx
new file mode 100644 (file)
index 0000000..f7c0f89
--- /dev/null
@@ -0,0 +1,53 @@
+// 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 _OpenGl_FrameStatsPrs_HeaderFile
+#define _OpenGl_FrameStatsPrs_HeaderFile
+
+#include <OpenGl_FrameStats.hxx>
+#include <OpenGl_Text.hxx>
+
+class Graphic3d_TransformPers;
+
+//! Element rendering frame statistics.
+class OpenGl_FrameStatsPrs : public OpenGl_Text
+{
+public:
+
+  //! Default constructor.
+  Standard_EXPORT OpenGl_FrameStatsPrs();
+
+  //! Destructor
+  Standard_EXPORT virtual ~OpenGl_FrameStatsPrs();
+
+  //! Render element.
+  Standard_EXPORT virtual void Render (const Handle(OpenGl_Workspace)& theWorkspace) const Standard_OVERRIDE;
+
+  //! Release OpenGL resources.
+  Standard_EXPORT virtual void Release (OpenGl_Context* theCtx) Standard_OVERRIDE;
+
+  //! Update text.
+  Standard_EXPORT void Update (const Handle(OpenGl_Workspace)& theWorkspace);
+
+  //! Assign text aspect.
+  void SetTextAspect (const Handle(Graphic3d_AspectText3d)& theAspect) { myTextAspect.SetAspect (theAspect); }
+
+protected:
+
+  OpenGl_AspectText               myTextAspect; //!< text aspect
+  Handle(Graphic3d_TransformPers) myTrsfPers;   //!< transformation persistence
+  Handle(OpenGl_FrameStats)       myStatsPrev;  //!< currently displayed stats
+
+};
+
+#endif // _OpenGl_FrameStatsPrs_HeaderFile
index 929c927..4f76811 100644 (file)
@@ -32,10 +32,10 @@ OpenGl_Layer::OpenGl_Layer (const Standard_Integer theNbPriorities,
                             const Handle(Select3D_BVHBuilder3d)& theBuilder)
 : myArray                     (0, theNbPriorities - 1),
   myNbStructures              (0),
+  myNbStructuresNotCulled     (0),
   myBVHPrimitivesTrsfPers     (theBuilder),
   myBVHIsLeftChildQueuedFirst (Standard_True),
-  myIsBVHPrimitivesNeedsReset (Standard_False),
-  myIsCulled (Standard_False)
+  myIsBVHPrimitivesNeedsReset (Standard_False)
 {
   myIsBoundingBoxNeedsReset[0] = myIsBoundingBoxNeedsReset[1] = true;
 }
@@ -488,15 +488,16 @@ void OpenGl_Layer::UpdateCulling (const OpenGl_BVHTreeSelector& theSelector,
 {
   updateBVH();
 
-  myIsCulled = false;
-  for (OpenGl_ArrayOfIndexedMapOfStructure::Iterator aMapIter (myArray); aMapIter.More(); aMapIter.Next())
+  myNbStructuresNotCulled = myNbStructures;
+  for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (myBVHPrimitives.Structures()); aStructIter.More(); aStructIter.Next())
   {
-    const OpenGl_IndexedMapOfStructure& aStructures = aMapIter.Value();
-    for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (aStructures); aStructIter.More(); aStructIter.Next())
-    {
-      const OpenGl_Structure* aStruct = aStructIter.Value();
-      aStruct->SetCulled (theToTraverse);
-    }
+    const OpenGl_Structure* aStruct = aStructIter.Value();
+    aStruct->SetCulled (theToTraverse);
+  }
+  for (OpenGl_IndexedMapOfStructure::Iterator aStructIter (myBVHPrimitivesTrsfPers.Structures()); aStructIter.More(); aStructIter.Next())
+  {
+    const OpenGl_Structure* aStruct = aStructIter.Value();
+    aStruct->SetCulled (theToTraverse);
   }
 
   if (!theToTraverse)
@@ -509,7 +510,7 @@ void OpenGl_Layer::UpdateCulling (const OpenGl_BVHTreeSelector& theSelector,
     return;
   }
 
-  myIsCulled = myAlwaysRenderedMap.IsEmpty();
+  myNbStructuresNotCulled = myAlwaysRenderedMap.Extent();
   OpenGl_BVHTreeSelector::CullingContext aCullCtx;
   theSelector.SetCullingDistance(aCullCtx, myLayerSettings.CullingDistance());
   theSelector.SetCullingSize    (aCullCtx, myLayerSettings.CullingSize());
@@ -543,7 +544,6 @@ void OpenGl_Layer::UpdateCulling (const OpenGl_BVHTreeSelector& theSelector,
       continue;
     }
 
-    myIsCulled = false;
     Standard_Integer aStack[BVH_Constants_MaxTreeDepth];
     Standard_Integer aHead = -1;
     Standard_Integer aNode = 0; // a root node
@@ -584,6 +584,7 @@ void OpenGl_Layer::UpdateCulling (const OpenGl_BVHTreeSelector& theSelector,
                                         ? myBVHPrimitivesTrsfPers.GetStructureById (aIdx)
                                         : myBVHPrimitives.GetStructureById (aIdx);
         aStruct->MarkAsNotCulled();
+        ++myNbStructuresNotCulled;
         if (aHead < 0)
         {
           break;
index ee96e8e..636ec6f 100644 (file)
@@ -80,6 +80,9 @@ public:
   //! @return the number of structures
   Standard_Integer NbStructures() const { return myNbStructures; }
 
+  //! Number of NOT culled structures in the layer.
+  Standard_Integer NbStructuresNotCulled() const { return myNbStructuresNotCulled; }
+
   //! Returns the number of available priority levels
   Standard_Integer NbPriorities() const { return myArray.Length(); }
 
@@ -125,7 +128,7 @@ public:
                       const Standard_Boolean theToTraverse);
 
   //! Returns TRUE if layer is empty or has been discarded entirely by culling test.
-  bool IsCulled() const { return myNbStructures == 0 || myIsCulled; }
+  bool IsCulled() const { return myNbStructuresNotCulled == 0; }
 
   // Render all structures.
   void Render (const Handle(OpenGl_Workspace)&   theWorkspace,
@@ -137,13 +140,24 @@ public:
     return myBVHPrimitivesTrsfPers.Size();
   }
 
+public:
+
+  //! Returns set of OpenGl_Structures structures for building BVH tree.
+  const OpenGl_BVHClipPrimitiveSet& CullableStructuresBVH() const { return myBVHPrimitives; }
+
+  //! Returns set of transform persistent OpenGl_Structures for building BVH tree.
+  const OpenGl_BVHClipPrimitiveTrsfPersSet& CullableTrsfPersStructuresBVH() const { return myBVHPrimitivesTrsfPers; }
+
+  //! Returns indexed map of always rendered structures.
+  const NCollection_IndexedMap<const OpenGl_Structure*>& NonCullableStructures() const { return myAlwaysRenderedMap; }
+
 protected:
 
   //! Updates BVH trees if their state has been invalidated.
-  void updateBVH() const;
+  Standard_EXPORT void updateBVH() const;
 
   //! Iterates through the hierarchical list of existing structures and renders them all.
-  void renderAll (const Handle(OpenGl_Workspace)& theWorkspace) const;
+  Standard_EXPORT void renderAll (const Handle(OpenGl_Workspace)& theWorkspace) const;
 
 private:
 
@@ -153,6 +167,9 @@ private:
   //! Overall number of structures rendered in the layer.
   Standard_Integer myNbStructures;
 
+  //! Number of NOT culled structures in the layer.
+  Standard_Integer myNbStructuresNotCulled;
+
   //! Layer setting flags.
   Graphic3d_ZLayerSettings myLayerSettings;
 
@@ -174,9 +191,6 @@ private:
   //! Defines if the cached bounding box is outdated.
   mutable bool myIsBoundingBoxNeedsReset[2];
 
-  //! Flag indicating that this layer is marked culled as whole
-  bool myIsCulled;
-
   //! Cached layer bounding box.
   mutable Bnd_Box myBoundingBox[2];
 
index 5247955..0b579cf 100644 (file)
@@ -56,6 +56,9 @@ public:
   //! Release GL resources.
   virtual void Release (OpenGl_Context* theGlCtx) Standard_OVERRIDE;
 
+  //! Returns estimated GPU memory usage - not implemented.
+  virtual Standard_Size EstimatedDataSize() const Standard_OVERRIDE { return 0; }
+
   //! Index of currently selected type of hatch.
   int TypeOfHatch() const { return myTypeOfHatch; }
 
index 84030e1..1153c5b 100644 (file)
@@ -224,7 +224,8 @@ Standard_Boolean OpenGl_PrimitiveArray::initNormalVbo (const Handle(OpenGl_Conte
     case 10: myVboAttribs = new OpenGl_VertexBufferT<OpenGl_VertexBuffer, 10>(*myAttribs); break;
   }
 
-  if (!myVboAttribs->init (theCtx, 0, myAttribs->NbElements, myAttribs->Data(), GL_NONE, myAttribs->Stride))
+  // specify data type as Byte and NbComponents as Stride, so that OpenGl_VertexBuffer::EstimatedDataSize() will return correct value
+  if (!myVboAttribs->init (theCtx, myAttribs->Stride, myAttribs->NbElements, myAttribs->Data(), GL_UNSIGNED_BYTE, myAttribs->Stride))
   {
     TCollection_ExtendedString aMsg;
     aMsg += "VBO creation for Primitive Array has failed for ";
index ddeff49..258497d 100644 (file)
@@ -91,6 +91,14 @@ public:
                                     const Handle(Graphic3d_Buffer)&      theAttribs,
                                     const Handle(Graphic3d_BoundBuffer)& theBounds);
 
+public:
+
+  //! Returns index VBO.
+  const Handle(OpenGl_VertexBuffer)& IndexVbo() const { return  myVboIndices; }
+
+  //! Returns attributes VBO.
+  const Handle(OpenGl_VertexBuffer)& AttributesVbo() const { return myVboAttribs; }
+
 protected:
 
   //! VBO initialization procedures
index 0ebf4b3..452e018 100644 (file)
@@ -45,6 +45,9 @@ public:
   //! @param theGlCtx - bound GL context, shouldn't be NULL.
   Standard_EXPORT virtual void Release (OpenGl_Context* theGlCtx) = 0;
 
+  //! Returns estimated GPU memory usage for holding data without considering overheads and allocation alignment rules.
+  virtual Standard_Size EstimatedDataSize() const = 0;
+
 private:
 
   //! Copy should be performed only within Handles!
index af740e4..bb74235 100644 (file)
@@ -44,6 +44,9 @@ public:
   //! Destroys object - will release GPU memory if any.
   Standard_EXPORT virtual void Release (OpenGl_Context* theContext) Standard_OVERRIDE;
 
+  //! Returns estimated GPU memory usage - not implemented.
+  virtual Standard_Size EstimatedDataSize() const Standard_OVERRIDE { return 0; }
+
   //! Creates an uninitialized sampler object.
   Standard_EXPORT Standard_Boolean Create (const Handle(OpenGl_Context)& theContext);
 
index 6b433f9..41120d3 100755 (executable)
@@ -55,6 +55,9 @@ public:
   //! Destroys shader object.
   Standard_EXPORT virtual void Release (OpenGl_Context* theCtx) Standard_OVERRIDE;
 
+  //! Returns estimated GPU memory usage - not implemented.
+  virtual Standard_Size EstimatedDataSize() const Standard_OVERRIDE { return 0; }
+
   //! Returns type of shader object.
   GLenum Type() const { return myType; }
   
index 99474d8..33d8e7d 100755 (executable)
@@ -176,6 +176,9 @@ public:
   //! Destroys shader program.
   Standard_EXPORT virtual void Release (OpenGl_Context* theCtx) Standard_OVERRIDE;
 
+  //! Returns estimated GPU memory usage - cannot be easily estimated.
+  virtual Standard_Size EstimatedDataSize() const Standard_OVERRIDE { return 0; }
+
   //! Attaches shader object to the program object.
   Standard_EXPORT Standard_Boolean AttachShader (const Handle(OpenGl_Context)&      theCtx,
                                                  const Handle(OpenGl_ShaderObject)& theShader);
index e7f679b..33a05cd 100644 (file)
@@ -119,11 +119,8 @@ public:
   //! Releases structure resources.
   virtual void Release (const Handle(OpenGl_Context)& theGlCtx);
 
-  //! Marks structure as culled/not culled.
-  void SetCulled (Standard_Boolean theIsCulled) const
-  {
-    myIsCulled = theIsCulled && !IsAlwaysRendered();
-  }
+  //! Marks structure as culled/not culled - note that IsAlwaysRendered() is ignored here!
+  void SetCulled (Standard_Boolean theIsCulled) const { myIsCulled = theIsCulled; }
 
   //! Marks structure as overlapping the current view volume one.
   //! The method is called during traverse of BVH tree.
index 7907fcc..ccfd921 100755 (executable)
@@ -114,10 +114,10 @@ protected:
   friend class OpenGl_Trihedron;
   friend class OpenGl_GraduatedTrihedron;
 
-private:
-
   //! Release cached VBO resources
-  void releaseVbos (OpenGl_Context* theCtx);
+  Standard_EXPORT void releaseVbos (OpenGl_Context* theCtx);
+
+private:
 
   //! Setup matrix.
   void setupMatrix (const Handle(OpenGl_Context)& theCtx,
index fa0b87d..c4de253 100644 (file)
@@ -60,6 +60,8 @@ OpenGl_Texture::OpenGl_Texture (const TCollection_AsciiString& theResourceId,
   mySizeY (0),
   mySizeZ (0),
   myTextFormat (GL_RGBA),
+  mySizedFormat(GL_RGBA8),
+  myNbSamples  (1),
   myHasMipmaps (Standard_False),
   myIsAlpha    (false)
 {
@@ -433,6 +435,8 @@ bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
 
   myHasMipmaps             = toCreateMipMaps;
   myTextFormat             = thePixelFormat;
+  mySizedFormat            = theTextFormat;
+  myNbSamples              = 1;
 #if !defined(GL_ES_VERSION_2_0)
   const GLint anIntFormat  = theTextFormat;
 #else
@@ -546,6 +550,7 @@ bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
                     theSizeX, 0,
                     thePixelFormat, theDataType, NULL);
       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_1D, 0, GL_TEXTURE_WIDTH, &aTestWidth);
+      glGetTexLevelParameteriv (GL_PROXY_TEXTURE_1D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
       if (aTestWidth == 0)
       {
         // no memory or broken input parameters
@@ -597,6 +602,7 @@ bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
                     thePixelFormat, theDataType, NULL);
       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &aTestWidth);
       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
+      glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
       if (aTestWidth == 0 || aTestHeight == 0)
       {
         // no memory or broken input parameters
@@ -660,6 +666,7 @@ bool OpenGl_Texture::Init (const Handle(OpenGl_Context)& theCtx,
                     thePixelFormat, theDataType, NULL);
       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,  &aTestWidth);
       glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &aTestHeight);
+      glGetTexLevelParameteriv (GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
       if (aTestWidth == 0 || aTestHeight == 0)
       {
         // no memory or broken input parameters
@@ -768,8 +775,9 @@ bool OpenGl_Texture::Init2DMultisample (const Handle(OpenGl_Context)& theCtx,
     return false;
   }
 
-  const GLsizei aNbSamples = OpenGl_Context::GetPowerOfTwo (theNbSamples, theCtx->MaxMsaaSamples());
+  myNbSamples = OpenGl_Context::GetPowerOfTwo (theNbSamples, theCtx->MaxMsaaSamples());
   myTarget = GL_TEXTURE_2D_MULTISAMPLE;
+  myHasMipmaps = false;
   if(theSizeX > theCtx->MaxTextureSize()
   || theSizeY > theCtx->MaxTextureSize())
   {
@@ -778,17 +786,18 @@ bool OpenGl_Texture::Init2DMultisample (const Handle(OpenGl_Context)& theCtx,
 
   Bind (theCtx);
   //myTextFormat = theTextFormat;
+  mySizedFormat = theTextFormat;
 #if !defined(GL_ES_VERSION_2_0)
   if (theCtx->Functions()->glTexStorage2DMultisample != NULL)
   {
-    theCtx->Functions()->glTexStorage2DMultisample (myTarget, aNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
+    theCtx->Functions()->glTexStorage2DMultisample (myTarget, myNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
   }
   else
   {
-    theCtx->Functions()->glTexImage2DMultisample   (myTarget, aNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
+    theCtx->Functions()->glTexImage2DMultisample   (myTarget, myNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
   }
 #else
-  theCtx->Functions()  ->glTexStorage2DMultisample (myTarget, aNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
+  theCtx->Functions()  ->glTexStorage2DMultisample (myTarget, myNbSamples, theTextFormat, theSizeX, theSizeY, GL_FALSE);
 #endif
   if (theCtx->core11fwd->glGetError() != GL_NO_ERROR)
   {
@@ -819,6 +828,8 @@ bool OpenGl_Texture::InitRectangle (const Handle(OpenGl_Context)& theCtx,
 
 #if !defined(GL_ES_VERSION_2_0)
   myTarget = GL_TEXTURE_RECTANGLE;
+  myNbSamples = 1;
+  myHasMipmaps = false;
 
   const GLsizei aSizeX    = Min (theCtx->MaxTextureSize(), theSizeX);
   const GLsizei aSizeY    = Min (theCtx->MaxTextureSize(), theSizeY);
@@ -826,26 +837,19 @@ bool OpenGl_Texture::InitRectangle (const Handle(OpenGl_Context)& theCtx,
   Bind (theCtx);
   applyDefaultSamplerParams (theCtx);
 
-  const GLint anIntFormat = theFormat.Internal();
-  myTextFormat = theFormat.Format();
+  myTextFormat  = theFormat.Format();
+  mySizedFormat = theFormat.Internal();
 
-  glTexImage2D (GL_PROXY_TEXTURE_RECTANGLE,
-                0,
-                anIntFormat,
-                aSizeX,
-                aSizeY,
-                0,
-                theFormat.Format(),
-                GL_FLOAT,
-                NULL);
+  glTexImage2D (GL_PROXY_TEXTURE_RECTANGLE, 0, mySizedFormat,
+                aSizeX, aSizeY, 0,
+                myTextFormat, GL_FLOAT, NULL);
 
   GLint aTestSizeX = 0;
   GLint aTestSizeY = 0;
 
-  glGetTexLevelParameteriv (
-    GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_WIDTH,  &aTestSizeX);
-  glGetTexLevelParameteriv (
-    GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_HEIGHT, &aTestSizeY);
+  glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_WIDTH,  &aTestSizeX);
+  glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_HEIGHT, &aTestSizeY);
+  glGetTexLevelParameteriv (GL_PROXY_TEXTURE_RECTANGLE, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
 
   if (aTestSizeX == 0 || aTestSizeY == 0)
   {
@@ -853,15 +857,9 @@ bool OpenGl_Texture::InitRectangle (const Handle(OpenGl_Context)& theCtx,
     return false;
   }
 
-  glTexImage2D (myTarget,
-                0,
-                anIntFormat,
-                aSizeX,
-                aSizeY,
-                0,
-                theFormat.Format(),
-                GL_FLOAT,
-                NULL);
+  glTexImage2D (myTarget, 0, mySizedFormat,
+                aSizeX, aSizeY, 0,
+                myTextFormat, GL_FLOAT, NULL);
 
   if (glGetError() != GL_NO_ERROR)
   {
@@ -914,6 +912,8 @@ bool OpenGl_Texture::Init3D (const Handle(OpenGl_Context)& theCtx,
   }
 
   myTarget = GL_TEXTURE_3D;
+  myNbSamples = 1;
+  myHasMipmaps = false;
 
   const GLsizei aSizeX = Min (theCtx->MaxTextureSize(), theSizeX);
   const GLsizei aSizeY = Min (theCtx->MaxTextureSize(), theSizeY);
@@ -936,19 +936,12 @@ bool OpenGl_Texture::Init3D (const Handle(OpenGl_Context)& theCtx,
     return false;
   }
 
-  const GLint anIntFormat = theTextFormat;
+  mySizedFormat = theTextFormat;
 
 #if !defined (GL_ES_VERSION_2_0)
-  theCtx->core15fwd->glTexImage3D (GL_PROXY_TEXTURE_3D,
-                                   0,
-                                   anIntFormat,
-                                   aSizeX,
-                                   aSizeY,
-                                   aSizeZ,
-                                   0,
-                                   thePixelFormat,
-                                   theDataType,
-                                   NULL);
+  theCtx->core15fwd->glTexImage3D (GL_PROXY_TEXTURE_3D, 0, mySizedFormat,
+                                   aSizeX, aSizeY, aSizeZ, 0,
+                                   thePixelFormat, theDataType, NULL);
 
   GLint aTestSizeX = 0;
   GLint aTestSizeY = 0;
@@ -957,6 +950,7 @@ bool OpenGl_Texture::Init3D (const Handle(OpenGl_Context)& theCtx,
   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_WIDTH, &aTestSizeX);
   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_HEIGHT, &aTestSizeY);
   glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_DEPTH, &aTestSizeZ);
+  glGetTexLevelParameteriv (GL_PROXY_TEXTURE_3D, 0, GL_TEXTURE_INTERNAL_FORMAT, &mySizedFormat);
 
   if (aTestSizeX == 0 || aTestSizeY == 0 || aTestSizeZ == 0)
   {
@@ -967,16 +961,9 @@ bool OpenGl_Texture::Init3D (const Handle(OpenGl_Context)& theCtx,
 #endif
 
   applyDefaultSamplerParams (theCtx);
-  theCtx->Functions()->glTexImage3D (myTarget,
-                                     0,
-                                     anIntFormat,
-                                     aSizeX,
-                                     aSizeY,
-                                     aSizeZ,
-                                     0,
-                                     thePixelFormat,
-                                     theDataType,
-                                     thePixels);
+  theCtx->Functions()->glTexImage3D (myTarget, 0, mySizedFormat,
+                                     aSizeX, aSizeY, aSizeZ, 0,
+                                     thePixelFormat, theDataType, thePixels);
 
   if (glGetError() != GL_NO_ERROR)
   {
@@ -992,3 +979,76 @@ bool OpenGl_Texture::Init3D (const Handle(OpenGl_Context)& theCtx,
   Unbind (theCtx);
   return true;
 }
+
+// =======================================================================
+// function : PixelSizeOfPixelFormat
+// purpose  :
+// =======================================================================
+Standard_Size OpenGl_Texture::PixelSizeOfPixelFormat (Standard_Integer theInternalFormat)
+{
+  switch(theInternalFormat)
+  {
+    // RED variations (GL_RED, OpenGL 3.0+)
+    case GL_RED:
+    case GL_R8:       return 1;
+    case GL_R16:      return 2;
+    case GL_R16F:     return 2;
+    case GL_R32F:     return 4;
+    // RGB variations
+    case GL_RGB:      return 3;
+    case GL_RGB8:     return 3;
+    case GL_RGB16:    return 6;
+    case GL_RGB16F:   return 6;
+    case GL_RGB32F:   return 12;
+    // RGBA variations
+    case GL_RGBA:     return 4;
+    case GL_RGBA8:    return 4;
+    case GL_RGB10_A2: return 4;
+    case GL_RGBA12:   return 6;
+    case GL_RGBA16:   return 8;
+    case GL_RGBA16F:  return 8;
+    case GL_RGBA32F:  return 16;
+    //
+    case GL_BGRA_EXT:  return 4;
+    // ALPHA variations (deprecated)
+    case GL_ALPHA:
+    case GL_ALPHA8:    return 1;
+    case GL_ALPHA16:   return 2;
+    case GL_LUMINANCE: return 1;
+    case GL_LUMINANCE_ALPHA: return 2;
+    // depth-stencil
+    case GL_DEPTH24_STENCIL8:   return 4;
+    case GL_DEPTH32F_STENCIL8:  return 8;
+    case GL_DEPTH_COMPONENT16:  return 2;
+    case GL_DEPTH_COMPONENT24:  return 3;
+    case GL_DEPTH_COMPONENT32F: return 4;
+  }
+  return 0;
+}
+
+// =======================================================================
+// function : EstimatedDataSize
+// purpose  :
+// =======================================================================
+Standard_Size OpenGl_Texture::EstimatedDataSize() const
+{
+  if (!IsValid())
+  {
+    return 0;
+  }
+
+  Standard_Size aSize = PixelSizeOfPixelFormat (mySizedFormat) * mySizeX * myNbSamples;
+  if (mySizeY != 0)
+  {
+    aSize *= Standard_Size(mySizeY);
+  }
+  if (mySizeZ != 0)
+  {
+    aSize *= Standard_Size(mySizeZ);
+  }
+  if (myHasMipmaps)
+  {
+    aSize = aSize + aSize / 3;
+  }
+  return aSize;
+}
index debbd6d..da9ec7b 100644 (file)
@@ -289,6 +289,11 @@ public:
   //! Helpful constants
   static const GLuint NO_TEXTURE = 0;
 
+  //! Return pixel size of pixel format in bytes.
+  //! Note that this method considers that OpenGL natively supports this pixel format,
+  //! which might be not the case - in the latter case, actual pixel size might differ!
+  Standard_EXPORT static Standard_Size PixelSizeOfPixelFormat (Standard_Integer theInternalFormat);
+
 public:
 
   //! Create uninitialized texture.
@@ -445,6 +450,9 @@ public:
                                              GLenum&                       thePixelFormat,
                                              GLenum&                       theDataType);
 
+  //! Returns estimated GPU memory usage for holding data without considering overheads and allocation alignment rules.
+  Standard_EXPORT virtual Standard_Size EstimatedDataSize() const Standard_OVERRIDE;
+
 protected:
 
   //! Apply default sampler parameters after texture creation.
@@ -460,6 +468,8 @@ protected:
   GLsizei          mySizeY;      //!< texture height
   GLsizei          mySizeZ;      //!< texture depth
   GLenum           myTextFormat; //!< texture format - GL_RGB, GL_RGBA,...
+  GLint            mySizedFormat;//!< internal (sized) texture format
+  Standard_Integer myNbSamples;  //!< number of MSAA samples
   Standard_Boolean myHasMipmaps; //!< flag indicates that texture was uploaded with mipmaps
   bool             myIsAlpha;    //!< indicates alpha format
 
index 0cbc596..2558a0c 100644 (file)
@@ -225,6 +225,14 @@ public:
 
 public: //! @name advanced methods
 
+  //! Returns estimated GPU memory usage for holding data without considering overheads and allocation alignment rules.
+  virtual Standard_Size EstimatedDataSize() const Standard_OVERRIDE
+  {
+    return IsValid()
+         ? sizeOfGlType (myDataType) * myComponentsNb * myElemsNb
+         : 0;
+  }
+
   //! @return size of specified GL type
   static size_t sizeOfGlType (const GLenum theType)
   {
index 1735b1d..e4e82c0 100644 (file)
@@ -141,6 +141,7 @@ OpenGl_View::~OpenGl_View()
 void OpenGl_View::ReleaseGlResources (const Handle(OpenGl_Context)& theCtx)
 {
   myGraduatedTrihedron.Release (theCtx.operator->());
+  myFrameStatsPrs.Release (theCtx.operator->());
 
   if (!myTextureEnv.IsNull())
   {
index 200f34b..04ae6a5 100644 (file)
@@ -40,6 +40,7 @@
 #include <OpenGl_BVHTreeSelector.hxx>
 #include <OpenGl_Context.hxx>
 #include <OpenGl_FrameBuffer.hxx>
+#include <OpenGl_FrameStatsPrs.hxx>
 #include <OpenGl_GraduatedTrihedron.hxx>
 #include <OpenGl_LayerList.hxx>
 #include <OpenGl_LineAttributes.hxx>
@@ -416,6 +417,9 @@ protected: //! @name Rendering of GL graphics (with prepared drawing buffer).
   //! Renders trihedron.
   void renderTrihedron (const Handle(OpenGl_Workspace) &theWorkspace);
 
+  //! Renders frame statistics.
+  void renderFrameStats();
+
 private:
 
   //! Adds the structure to display lists of the view.
@@ -499,6 +503,7 @@ protected:
   OpenGl_BVHTreeSelector myBVHSelector;
 
   OpenGl_GraduatedTrihedron myGraduatedTrihedron;
+  OpenGl_FrameStatsPrs      myFrameStatsPrs;
 
   Handle(OpenGl_TextureSet) myTextureEnv;
 
@@ -1087,6 +1092,7 @@ public:
   friend class OpenGl_GraphicDriver;
   friend class OpenGl_Workspace;
   friend class OpenGl_LayerList;
+  friend class OpenGl_FrameStats;
 };
 
 #endif // _OpenGl_View_Header
index 9899c81..a12078c 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <OpenGl_AspectLine.hxx>
 #include <OpenGl_Context.hxx>
+#include <OpenGl_FrameStats.hxx>
 #include <OpenGl_Matrix.hxx>
 #include <OpenGl_Workspace.hxx>
 #include <OpenGl_View.hxx>
@@ -165,6 +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);
 
   // release pending GL resources
   aCtx->ReleaseDelayed();
@@ -564,6 +566,7 @@ void OpenGl_View::Redraw()
 
   // reset render mode state
   aCtx->FetchState();
+  aCtx->FrameStats()->FrameEnd (myWorkspace);
 
   myWasRedrawnGL = Standard_True;
 }
@@ -589,6 +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);
 
   if ( aFrameBuffer == NULL
    && !aCtx->DefaultFrameBuffer().IsNull()
@@ -729,6 +733,7 @@ void OpenGl_View::RedrawImmediate()
   {
     aCtx->core11fwd->glFlush();
   }
+  aCtx->FrameStats()->FrameEnd (myWorkspace);
 
   myWasRedrawnGL = Standard_True;
 }
@@ -1022,6 +1027,10 @@ void OpenGl_View::render (Graphic3d_Camera::Projection theProjection,
         glDisable (GL_CULL_FACE);
     }
   }
+  else
+  {
+    renderFrameStats();
+  }
 
   // reset FFP state for safety
   aContext->BindProgram (Handle(OpenGl_ShaderProgram)());
@@ -1157,6 +1166,20 @@ void OpenGl_View::renderTrihedron (const Handle(OpenGl_Workspace) &theWorkspace)
   }
 }
 
+//=======================================================================
+//function : renderFrameStats
+//purpose  :
+//=======================================================================
+void OpenGl_View::renderFrameStats()
+{
+  if (myRenderParams.ToShowStats
+   && myRenderParams.CollectedStats != Graphic3d_RenderingParams::PerfCounters_NONE)
+  {
+    myFrameStatsPrs.Update (myWorkspace);
+    myFrameStatsPrs.Render (myWorkspace);
+  }
+}
+
 // =======================================================================
 // function : Invalidate
 // purpose  :
index 6069f55..946ac8e 100644 (file)
@@ -9822,6 +9822,113 @@ static int VLight (Draw_Interpretor& theDi,
   return 0;
 }
 
+//! Read Graphic3d_RenderingParams::PerfCounters flag.
+static Standard_Boolean parsePerfStatsFlag (const TCollection_AsciiString& theValue,
+                                            Standard_Boolean& theToReset,
+                                            Graphic3d_RenderingParams::PerfCounters& theFlagsRem,
+                                            Graphic3d_RenderingParams::PerfCounters& theFlagsAdd)
+{
+  Graphic3d_RenderingParams::PerfCounters aFlag = Graphic3d_RenderingParams::PerfCounters_NONE;
+  TCollection_AsciiString aVal = theValue;
+  Standard_Boolean toReverse = Standard_False;
+  if (aVal == "none")
+  {
+    theToReset = Standard_True;
+    return Standard_True;
+  }
+  else if (aVal.StartsWith ("-"))
+  {
+    toReverse = Standard_True;
+    aVal = aVal.SubString (2, aVal.Length());
+  }
+  else if (aVal.StartsWith ("no"))
+  {
+    toReverse = Standard_True;
+    aVal = aVal.SubString (3, aVal.Length());
+  }
+  else if (aVal.StartsWith ("+"))
+  {
+    aVal = aVal.SubString (2, aVal.Length());
+  }
+  else
+  {
+    theToReset = Standard_True;
+  }
+
+  if (     aVal == "fps"
+        || aVal == "framerate")  aFlag = Graphic3d_RenderingParams::PerfCounters_FrameRate;
+  else if (aVal == "cpu")        aFlag = Graphic3d_RenderingParams::PerfCounters_CPU;
+  else if (aVal == "layers")     aFlag = Graphic3d_RenderingParams::PerfCounters_Layers;
+  else if (aVal == "structs"
+        || aVal == "structures"
+        || aVal == "objects")    aFlag = Graphic3d_RenderingParams::PerfCounters_Structures;
+  else if (aVal == "groups")     aFlag = Graphic3d_RenderingParams::PerfCounters_Groups;
+  else if (aVal == "arrays")     aFlag = Graphic3d_RenderingParams::PerfCounters_GroupArrays;
+  else if (aVal == "tris"
+        || aVal == "triangles")  aFlag = Graphic3d_RenderingParams::PerfCounters_Triangles;
+  else if (aVal == "pnts"
+        || aVal == "points")     aFlag = Graphic3d_RenderingParams::PerfCounters_Points;
+  else if (aVal == "mem"
+        || aVal == "gpumem"
+        || aVal == "estimmem")   aFlag = Graphic3d_RenderingParams::PerfCounters_EstimMem;
+  else if (aVal == "basic")      aFlag = Graphic3d_RenderingParams::PerfCounters_Basic;
+  else if (aVal == "extended"
+        || aVal == "verbose"
+        || aVal == "extra")      aFlag = Graphic3d_RenderingParams::PerfCounters_Extended;
+  else
+  {
+    return Standard_False;
+  }
+
+  if (toReverse)
+  {
+    theFlagsRem = Graphic3d_RenderingParams::PerfCounters(theFlagsRem | aFlag);
+  }
+  else
+  {
+    theFlagsAdd = Graphic3d_RenderingParams::PerfCounters(theFlagsAdd | aFlag);
+  }
+  return Standard_True;
+}
+
+//! Read Graphic3d_RenderingParams::PerfCounters flags.
+static Standard_Boolean convertToPerfStatsFlags (const TCollection_AsciiString& theValue,
+                                                 Graphic3d_RenderingParams::PerfCounters& theFlags)
+{
+  TCollection_AsciiString aValue = theValue;
+  Graphic3d_RenderingParams::PerfCounters aFlagsRem = Graphic3d_RenderingParams::PerfCounters_NONE;
+  Graphic3d_RenderingParams::PerfCounters aFlagsAdd = Graphic3d_RenderingParams::PerfCounters_NONE;
+  Standard_Boolean toReset = Standard_False;
+  for (;;)
+  {
+    Standard_Integer aSplitPos = aValue.Search ("|");
+    if (aSplitPos <= 0)
+    {
+      if (!parsePerfStatsFlag (aValue, toReset, aFlagsRem, aFlagsAdd))
+      {
+        return Standard_False;
+      }
+      if (toReset)
+      {
+        theFlags = Graphic3d_RenderingParams::PerfCounters_NONE;
+      }
+      theFlags = Graphic3d_RenderingParams::PerfCounters(theFlags |  aFlagsAdd);
+      theFlags = Graphic3d_RenderingParams::PerfCounters(theFlags & ~aFlagsRem);
+      return Standard_True;
+    }
+
+    if (aSplitPos > 1)
+    {
+      TCollection_AsciiString aSubValue = aValue.SubString (1, aSplitPos - 1);
+      if (!parsePerfStatsFlag (aSubValue, toReset, aFlagsRem, aFlagsAdd))
+      {
+        return Standard_False;
+      }
+    }
+    aValue = aValue.SubString (aSplitPos + 1, aValue.Length());
+  }
+}
+
 //=======================================================================
 //function : VRenderParams
 //purpose  : Enables/disables rendering features
@@ -9918,6 +10025,46 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
       case V3d_GOURAUD: theDI << "gouraud"; break;
       case V3d_PHONG:   theDI << "phong";   break;
     }
+    {
+      theDI << "perfCounters:";
+      if ((aParams.CollectedStats & Graphic3d_RenderingParams::PerfCounters_FrameRate) != 0)
+      {
+        theDI << " fps";
+      }
+      if ((aParams.CollectedStats & Graphic3d_RenderingParams::PerfCounters_CPU) != 0)
+      {
+        theDI << " cpu";
+      }
+      if ((aParams.CollectedStats & Graphic3d_RenderingParams::PerfCounters_Structures) != 0)
+      {
+        theDI << " structs";
+      }
+      if ((aParams.CollectedStats & Graphic3d_RenderingParams::PerfCounters_Groups) != 0)
+      {
+        theDI << " groups";
+      }
+      if ((aParams.CollectedStats & Graphic3d_RenderingParams::PerfCounters_GroupArrays) != 0)
+      {
+        theDI << " arrays";
+      }
+      if ((aParams.CollectedStats & Graphic3d_RenderingParams::PerfCounters_Triangles) != 0)
+      {
+        theDI << " tris";
+      }
+      if ((aParams.CollectedStats & Graphic3d_RenderingParams::PerfCounters_Points) != 0)
+      {
+        theDI << " pnts";
+      }
+      if ((aParams.CollectedStats & Graphic3d_RenderingParams::PerfCounters_EstimMem) != 0)
+      {
+        theDI << " gpumem";
+      }
+      if (aParams.CollectedStats == Graphic3d_RenderingParams::PerfCounters_NONE)
+      {
+        theDI << " none";
+      }
+      theDI << "\n";
+    }
     theDI << "depth pre-pass: " << (aParams.ToEnableDepthPrepass        ? "on" : "off") << "\n";
     theDI << "\n";
     return 0;
@@ -10542,6 +10689,39 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
         return 1;
       }
     }
+    else if (aFlag == "-performancestats"
+          || aFlag == "-performancecounters"
+          || aFlag == "-perfstats"
+          || aFlag == "-perfcounters"
+          || aFlag == "-stats")
+    {
+      if (++anArgIter >= theArgNb)
+      {
+        std::cout << "Error: wrong syntax at argument '" << anArg << "'\n";
+        return 1;
+      }
+
+      TCollection_AsciiString aFlagsStr (theArgVec[anArgIter]);
+      aFlagsStr.LowerCase();
+      Graphic3d_RenderingParams::PerfCounters aFlags = aView->ChangeRenderingParams().CollectedStats;
+      if (!convertToPerfStatsFlags (aFlagsStr, aFlags))
+      {
+        std::cout << "Error: wrong syntax at argument '" << anArg << "'\n";
+        return 1;
+      }
+      aView->ChangeRenderingParams().CollectedStats = aFlags;
+      aView->ChangeRenderingParams().ToShowStats = aFlags != Graphic3d_RenderingParams::PerfCounters_NONE;
+    }
+    else if (aFlag == "-perfupdateinterval"
+          || aFlag == "-statsupdateinterval")
+    {
+      if (++anArgIter >= theArgNb)
+      {
+        std::cout << "Error: wrong syntax at argument '" << anArg << "'\n";
+        return 1;
+      }
+      aView->ChangeRenderingParams().StatsUpdateInterval = (Standard_ShortReal )Draw::Atof (theArgVec[anArgIter]);
+    }
     else
     {
       std::cout << "Error: wrong syntax, unknown flag '" << anArg << "'\n";
@@ -12062,6 +12242,9 @@ 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                                  Show/hide performance counters (flags can be combined)"
+    "\n      '-perfUpdateInterval nbSeconds' Performance counters update interval"
     "\n    Unlike vcaps, these parameters dramatically change visual properties."
     "\n    Command is intended to control presentation quality depending on"
     "\n    hardware capabilities and performance.",
index 114bafe..5cf52b2 100644 (file)
@@ -15,18 +15,15 @@ set SMALL_WIN_HEIGHT 512
 # other
 array set aSphereNames {}
 
-set aWithoutClippingSnapshot $imagedir/${casename}_without.png
-set aWithClippingSnapshot $imagedir/${casename}_with.png
-set aDiffImage $imagedir/${casename}_diff.png
-
 pload VISUALIZATION MODELING
+vclear
 vinit name=small_wnd l=32 t=32 w=$SMALL_WIN_WIDTH h=$SMALL_WIN_HEIGHT
 vactivate small_wnd
+vrenderparams -perfUpdateInterval 0
 vfrustumculling 0
 vautozfit 0
 vviewparams -scale 1.953125 -eye 0.57735026918962573 -0.57735026918962573 0.57735026918962573
 vzrange 1 512
-vclear
 vremove -all -noinfo
 
 puts [vdrawsphere tmp_sph $SPHERE_FINENESS]
@@ -60,7 +57,10 @@ for {set i $aInnerSpheresNum} {$i < $SPHERES_NUM} {incr i} {
   set aSphereNames($i) $aCurrName
 }
 puts [vfps]
-vdump $aWithoutClippingSnapshot
+vrenderparams -perfCounters none
+vdump $imagedir/${casename}_cull_off_ref.png
+vrenderparams -perfCounters verbose|nofps|nocpu
+vdump $imagedir/${casename}_cull_off.png
 puts "All spheres were displayed."
 puts ""
 
@@ -70,13 +70,14 @@ puts "Start displaying spheres with clipping..."
 vfrustumculling 1
 vdisplayall
 puts [vfps]
-vdump $aWithClippingSnapshot
+vrenderparams -perfCounters none
+vdump $imagedir/${casename}_cull_on_ref.png
+vrenderparams -perfCounters verbose|nofps|nocpu
+vdump $imagedir/${casename}_cull_on.png
 puts "All spheres were displayed."
 puts ""
 
-set aDiffImageResult [diffimage $aWithClippingSnapshot $aWithoutClippingSnapshot 0.1 0 0 $aDiffImage]
+set aDiffImageResult [diffimage $imagedir/${casename}_cull_on_ref.png $imagedir/${casename}_cull_off_ref.png 0.1 0 0 $imagedir/${casename}_diff.png]
 if {$aDiffImageResult != 0} {
   puts "ERROR : Test failed: there is a difference between images rendered with and without clipping"
 }
-
-verase
index 8e25767..f0e909f 100644 (file)
@@ -14,18 +14,15 @@ set SMALL_WIN_HEIGHT 512
 # other
 array set aBoxNames {}
 
-set aWithoutClippingSnapshot $imagedir/${casename}_without.png
-set aWithClippingSnapshot $imagedir/${casename}_with.png
-set aDiffImage $imagedir/${casename}_diff.png
-
 pload VISUALIZATION MODELING
+vclear
 vinit name=small_wnd l=32 t=32 w=$SMALL_WIN_WIDTH h=$SMALL_WIN_HEIGHT
 vactivate small_wnd
+vrenderparams -perfUpdateInterval 0
 vfrustumculling 0
 vautozfit 0
 vviewparams -scale 1.953125 -eye 0.57735026918962573 -0.57735026918962573 0.57735026918962573
 vzrange 1 512
-vclear
 vremove -all -noinfo
 
 set aInnerBoxesNum [expr $BOXES_NUM * $PERCENT_OF_INNER_BOXES / 100]
@@ -63,7 +60,10 @@ for {set i 0} {$i < $BOXES_NUM} {incr i} {
   vdisplay -noupdate $aBoxNames($i)
 }
 puts [vfps]
-vdump $aWithoutClippingSnapshot
+vrenderparams -perfCounters none
+vdump $imagedir/${casename}_cull_off_ref.png
+vrenderparams -perfCounters verbose|nofps|nocpu
+vdump $imagedir/${casename}_cull_off.png
 puts "All boxes were displayed."
 puts ""
 
@@ -75,12 +75,13 @@ for {set i 0} {$i < $BOXES_NUM} {incr i} {
   vdisplay -noupdate $aBoxNames($i)
 }
 puts [vfps]
-vdump $aWithClippingSnapshot
+vrenderparams -perfCounters none
+vdump $imagedir/${casename}_cull_on_ref.png
+vrenderparams -perfCounters verbose|nofps|nocpu
+vdump $imagedir/${casename}_cull_on.png
 puts "All boxes were displayed."
 
-set aDiffImageResult [diffimage $aWithClippingSnapshot $aWithoutClippingSnapshot 0.1 0 0 $aDiffImage]
+set aDiffImageResult [diffimage $imagedir/${casename}_cull_on_ref.png $imagedir/${casename}_cull_off_ref.png 0.1 0 0 $imagedir/${casename}_diff.png]
 if {$aDiffImageResult != 0} {
   puts "ERROR : Test failed: there is a difference between images rendered with and without clipping"
 }
-
-verase