]> OCCT Git - occt.git/commitdiff
0029346: Visualization, TKOpenGl - collect frame statistics
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 226791202568c1de4595e18b437d1df7be0e7f92..58ef64e8708f29db847059e9c9a97770d2e80b53 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 5e7a362973969e1de6c8e41bc0e829d2e931acb8..17f4913bcd98d9dcfc183d2e8ad3f7857d28626b 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 64fa0dece8224d56a59f93f988e19d42fa41416f..80a9aa0fd6113d54a4eade4e494832adb3fc097a 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 64cd4ca16a0a1c51feafc73ab6f954f925704f11..902444c76887ec963d962a8d65f74041bafc0ad6 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 aa73e3031fae9a68c48fa0ac495d3b8051df10eb..6eedd2b3f646a593e4e89d0510b709661a51b255 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 43b4c115b822d888f8c870febdacae6ad2b44364..7ac029f9f785a7ce382098c776719cc4c883346b 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 6fb217a2182ae9a08bb40b45d36ffae070b94b4f..494ae5cdda9e4532afde0bccc7ae511718ba07bb 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 a0e2da762f97f5f54939c1589fbf199367fc6bc5..b2f01c6afb7d7992356f767ba77423dfdac0ed0c 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 8cbd467cbe6cdce5dd272c084519ee543ddf7098..0692e87b56fe9769a50f6a5f63067a0ce0f670e6 100755 (executable)
@@ -79,6 +79,20 @@ void OpenGl_Font::Release (OpenGl_Context* theCtx)
   myTextures.Clear();
 }
 
+// =======================================================================
+// 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 4977b3f1256446c183971a320ab55b5e279e9ca2..a588762a5b64bfa3d8c47809337a050df07b76dc 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 7f301c902ad0fcb8aa79c988977e5754c1834a50..2fdf7bb18833ebbea3c6e78493cec9eb8ef0b7a4 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 254c87d63640a729ffde239c674e242bd315f12d..4b7cd222af0dc514ce6a13955740c2605555ab56 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 929c9271358efd5dd82df07ad52816caac47d08d..4f768116c07cd83baee1119f177b0573299b750c 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 ee96e8eff2d2ab6a31212a66f6104f13e550c97a..636ec6fbb673e41da7417e7da4c9f1cc7abd33b1 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 5247955d66c677342e2f89e0e98914812491c3d2..0b579cf89c97d1d68f49aa2d4ddbbd9480a89e20 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 84030e15bfbc1f4e033e5ce1baa2e099956f0ca4..1153c5b10b81b299be3aa991c5e264bcf00426ca 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 ddeff49591734eceb10061f2a502a1e5a82bca43..258497db75a7edea44969ca4bea28e9a8bf7e262 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 0ebf4b3609b6388f403f3d0fcc3a4f3771c95b4f..452e0182f40efa28e6dcd1a10cf9b5bd7c4154cf 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 af740e4a2dad07d2de053ae03885d21e46470df2..bb742351f2695eeef8587dbc6cac4daa205b684b 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 6b433f92a8c8224e4e6507b399c21cb966c30b77..41120d3d9a2cd8660e1b8204139ed49527e68b15 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 99474d87c4ead32ee32c801320d7ef55fa402277..33d8e7d4be9ce97264699401795b14a39b75fcf7 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 e7f679b352edaf6adac669249306502f677bcc29..33a05cdd88c3aa071c484bc34a0cec4cf248aa1a 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 7907fccd6bac58039afd68e241e1ee54bc5e2fe3..ccfd921d078569f3fa7da255da50268af0c2f710 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 fa0b87d26cf5ff438770a2a125ce0837ec3fd5c2..c4de2537140c48472c7d2a0f92d30c25a2741d2b 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 debbd6d097b2e5f8be9c844afc3d6f02197ce111..da9ec7bb786803bf399877ef1841350978166dcc 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 0cbc59631b5efc3b14d7bd26b0200bd87b45ab51..2558a0c3ae56dbb3555007c56751285533a42f6f 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 1735b1d4135c0b655f4f5cb3a9c8a577f8056078..e4e82c09d82b4c3cb06a5aa6436e04f94e6df5b7 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 200f34baa9b551cc45836969bcdd8ff42c61d302..04ae6a5a4409b8d5246e8ee32a59b90539685f55 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 9899c81da3081c997e78821088f4185804c4ac18..a12078cda81ee5a9fbddb562d65f08745a8aacaf 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 6069f55da496cc94c7cec8812b72ef8ec41dddfe..946ac8efa180713d974724c40d97cc07e67124a7 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 114bafee1a52275dfb6ac9e5b1b97d9c18862c72..5cf52b2b4c904473093d092c653169b3c2475ac0 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 8e2576770fba56f5290502943b9a64022d7611f7..f0e909ffc5833707cd27653c7d6aeacd0103d700 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