From 15669413da726f1e569eaf3b85b6bcb6825187af Mon Sep 17 00:00:00 2001 From: kgv Date: Fri, 24 Nov 2017 18:43:01 +0300 Subject: [PATCH] 0029346: Visualization, TKOpenGl - collect frame statistics 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. --- samples/tcl/spheres.tcl | 19 +- src/Graphic3d/Graphic3d_RenderingParams.hxx | 58 +- src/OpenGl/FILES | 4 + src/OpenGl/OpenGl_BVHClipPrimitiveSet.hxx | 3 + .../OpenGl_BVHClipPrimitiveTrsfPersSet.hxx | 3 + src/OpenGl/OpenGl_CappingPlaneResource.hxx | 3 + src/OpenGl/OpenGl_Context.cxx | 2 + src/OpenGl/OpenGl_Context.hxx | 11 +- src/OpenGl/OpenGl_Font.cxx | 14 + src/OpenGl/OpenGl_Font.hxx | 3 + src/OpenGl/OpenGl_FrameBuffer.cxx | 32 ++ src/OpenGl/OpenGl_FrameBuffer.hxx | 3 + src/OpenGl/OpenGl_FrameStats.cxx | 522 ++++++++++++++++++ src/OpenGl/OpenGl_FrameStats.hxx | 161 ++++++ src/OpenGl/OpenGl_FrameStatsPrs.cxx | 144 +++++ src/OpenGl/OpenGl_FrameStatsPrs.hxx | 53 ++ src/OpenGl/OpenGl_Layer.cxx | 25 +- src/OpenGl/OpenGl_Layer.hxx | 26 +- src/OpenGl/OpenGl_LineAttributes.hxx | 3 + src/OpenGl/OpenGl_PrimitiveArray.cxx | 3 +- src/OpenGl/OpenGl_PrimitiveArray.hxx | 8 + src/OpenGl/OpenGl_Resource.hxx | 3 + src/OpenGl/OpenGl_Sampler.hxx | 3 + src/OpenGl/OpenGl_ShaderObject.hxx | 3 + src/OpenGl/OpenGl_ShaderProgram.hxx | 3 + src/OpenGl/OpenGl_Structure.hxx | 7 +- src/OpenGl/OpenGl_Text.hxx | 6 +- src/OpenGl/OpenGl_Texture.cxx | 158 ++++-- src/OpenGl/OpenGl_Texture.hxx | 10 + src/OpenGl/OpenGl_VertexBuffer.hxx | 8 + src/OpenGl/OpenGl_View.cxx | 1 + src/OpenGl/OpenGl_View.hxx | 6 + src/OpenGl/OpenGl_View_Redraw.cxx | 23 + src/ViewerTest/ViewerTest_ViewerCommands.cxx | 183 ++++++ tests/bugs/vis/bug24307_1 | 21 +- tests/bugs/vis/bug24307_2 | 21 +- 36 files changed, 1449 insertions(+), 107 deletions(-) create mode 100644 src/OpenGl/OpenGl_FrameStats.cxx create mode 100644 src/OpenGl/OpenGl_FrameStats.hxx create mode 100644 src/OpenGl/OpenGl_FrameStatsPrs.cxx create mode 100644 src/OpenGl/OpenGl_FrameStatsPrs.hxx diff --git a/samples/tcl/spheres.tcl b/samples/tcl/spheres.tcl index 2267912025..58ef64e870 100644 --- a/samples/tcl/spheres.tcl +++ b/samples/tcl/spheres.tcl @@ -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" diff --git a/src/Graphic3d/Graphic3d_RenderingParams.hxx b/src/Graphic3d/Graphic3d_RenderingParams.hxx index 5e7a362973..17f4913bcd 100644 --- a/src/Graphic3d/Graphic3d_RenderingParams.hxx +++ b/src/Graphic3d/Graphic3d_RenderingParams.hxx @@ -16,7 +16,8 @@ #ifndef _Graphic3d_RenderingParams_HeaderFile #define _Graphic3d_RenderingParams_HeaderFile -#include +#include +#include #include #include #include @@ -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. diff --git a/src/OpenGl/FILES b/src/OpenGl/FILES index 64fa0dece8..80a9aa0fd6 100755 --- a/src/OpenGl/FILES +++ b/src/OpenGl/FILES @@ -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 diff --git a/src/OpenGl/OpenGl_BVHClipPrimitiveSet.hxx b/src/OpenGl/OpenGl_BVHClipPrimitiveSet.hxx index 64cd4ca16a..902444c768 100644 --- a/src/OpenGl/OpenGl_BVHClipPrimitiveSet.hxx +++ b/src/OpenGl/OpenGl_BVHClipPrimitiveSet.hxx @@ -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& Structures() const { return myStructs; } + private: NCollection_IndexedMap myStructs; //!< Indexed map of structures. diff --git a/src/OpenGl/OpenGl_BVHClipPrimitiveTrsfPersSet.hxx b/src/OpenGl/OpenGl_BVHClipPrimitiveTrsfPersSet.hxx index aa73e3031f..6eedd2b3f6 100644 --- a/src/OpenGl/OpenGl_BVHClipPrimitiveTrsfPersSet.hxx +++ b/src/OpenGl/OpenGl_BVHClipPrimitiveTrsfPersSet.hxx @@ -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& Structures() const { return myStructs; } + //! Marks object state as outdated (needs BVH rebuilding). void MarkDirty() { diff --git a/src/OpenGl/OpenGl_CappingPlaneResource.hxx b/src/OpenGl/OpenGl_CappingPlaneResource.hxx index 43b4c115b8..7ac029f9f7 100755 --- a/src/OpenGl/OpenGl_CappingPlaneResource.hxx +++ b/src/OpenGl/OpenGl_CappingPlaneResource.hxx @@ -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; } diff --git a/src/OpenGl/OpenGl_Context.cxx b/src/OpenGl/OpenGl_Context.cxx index 6fb217a218..494ae5cdda 100644 --- a/src/OpenGl/OpenGl_Context.cxx +++ b/src/OpenGl/OpenGl_Context.cxx @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -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), diff --git a/src/OpenGl/OpenGl_Context.hxx b/src/OpenGl/OpenGl_Context.hxx index a0e2da762f..b2f01c6afb 100644 --- a/src/OpenGl/OpenGl_Context.hxx +++ b/src/OpenGl/OpenGl_Context.hxx @@ -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 > 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 > OpenGl_DelayReleaseMap; - typedef NCollection_Shared< NCollection_DataMap > OpenGl_ResourcesMap; typedef NCollection_Shared< NCollection_List > OpenGl_ResourcesStack; typedef NCollection_SparseArray 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 diff --git a/src/OpenGl/OpenGl_Font.cxx b/src/OpenGl/OpenGl_Font.cxx index 8cbd467cbe..0692e87b56 100755 --- a/src/OpenGl/OpenGl_Font.cxx +++ b/src/OpenGl/OpenGl_Font.cxx @@ -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::Iterator aTexIter (myTextures); aTexIter.More(); aTexIter.Next()) + { + aSize += aTexIter.Value()->EstimatedDataSize(); + } + return aSize; +} + // ======================================================================= // function : Init // purpose : diff --git a/src/OpenGl/OpenGl_Font.hxx b/src/OpenGl/OpenGl_Font.hxx index 4977b3f125..a588762a5b 100755 --- a/src/OpenGl/OpenGl_Font.hxx +++ b/src/OpenGl/OpenGl_Font.hxx @@ -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 { diff --git a/src/OpenGl/OpenGl_FrameBuffer.cxx b/src/OpenGl/OpenGl_FrameBuffer.cxx index 7f301c902a..2fdf7bb188 100644 --- a/src/OpenGl/OpenGl_FrameBuffer.cxx +++ b/src/OpenGl/OpenGl_FrameBuffer.cxx @@ -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; +} diff --git a/src/OpenGl/OpenGl_FrameBuffer.hxx b/src/OpenGl/OpenGl_FrameBuffer.hxx index 254c87d636..4b7cd222af 100644 --- a/src/OpenGl/OpenGl_FrameBuffer.hxx +++ b/src/OpenGl/OpenGl_FrameBuffer.hxx @@ -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 index 0000000000..17b040d9dc --- /dev/null +++ b/src/OpenGl/OpenGl_FrameStats.cxx @@ -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 + +#include +#include +#include + +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 (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 (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 (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 index 0000000000..0ed9361aee --- /dev/null +++ b/src/OpenGl/OpenGl_FrameStats.hxx @@ -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 +#include +#include +#include +#include + +class OpenGl_Workspace; +class OpenGl_Structure; +typedef NCollection_IndexedMap 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 index 0000000000..8abdc66623 --- /dev/null +++ b/src/OpenGl/OpenGl_FrameStatsPrs.cxx @@ -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 + +#include +#include +#include + +// ======================================================================= +// 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 index 0000000000..f7c0f8995e --- /dev/null +++ b/src/OpenGl/OpenGl_FrameStatsPrs.hxx @@ -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 +#include + +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 diff --git a/src/OpenGl/OpenGl_Layer.cxx b/src/OpenGl/OpenGl_Layer.cxx index 929c927135..4f768116c0 100644 --- a/src/OpenGl/OpenGl_Layer.cxx +++ b/src/OpenGl/OpenGl_Layer.cxx @@ -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; diff --git a/src/OpenGl/OpenGl_Layer.hxx b/src/OpenGl/OpenGl_Layer.hxx index ee96e8eff2..636ec6fbb6 100644 --- a/src/OpenGl/OpenGl_Layer.hxx +++ b/src/OpenGl/OpenGl_Layer.hxx @@ -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& 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]; diff --git a/src/OpenGl/OpenGl_LineAttributes.hxx b/src/OpenGl/OpenGl_LineAttributes.hxx index 5247955d66..0b579cf89c 100644 --- a/src/OpenGl/OpenGl_LineAttributes.hxx +++ b/src/OpenGl/OpenGl_LineAttributes.hxx @@ -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; } diff --git a/src/OpenGl/OpenGl_PrimitiveArray.cxx b/src/OpenGl/OpenGl_PrimitiveArray.cxx index 84030e15bf..1153c5b10b 100644 --- a/src/OpenGl/OpenGl_PrimitiveArray.cxx +++ b/src/OpenGl/OpenGl_PrimitiveArray.cxx @@ -224,7 +224,8 @@ Standard_Boolean OpenGl_PrimitiveArray::initNormalVbo (const Handle(OpenGl_Conte case 10: myVboAttribs = new OpenGl_VertexBufferT(*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 "; diff --git a/src/OpenGl/OpenGl_PrimitiveArray.hxx b/src/OpenGl/OpenGl_PrimitiveArray.hxx index ddeff49591..258497db75 100644 --- a/src/OpenGl/OpenGl_PrimitiveArray.hxx +++ b/src/OpenGl/OpenGl_PrimitiveArray.hxx @@ -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 diff --git a/src/OpenGl/OpenGl_Resource.hxx b/src/OpenGl/OpenGl_Resource.hxx index 0ebf4b3609..452e0182f4 100644 --- a/src/OpenGl/OpenGl_Resource.hxx +++ b/src/OpenGl/OpenGl_Resource.hxx @@ -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! diff --git a/src/OpenGl/OpenGl_Sampler.hxx b/src/OpenGl/OpenGl_Sampler.hxx index af740e4a2d..bb742351f2 100644 --- a/src/OpenGl/OpenGl_Sampler.hxx +++ b/src/OpenGl/OpenGl_Sampler.hxx @@ -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); diff --git a/src/OpenGl/OpenGl_ShaderObject.hxx b/src/OpenGl/OpenGl_ShaderObject.hxx index 6b433f92a8..41120d3d9a 100755 --- a/src/OpenGl/OpenGl_ShaderObject.hxx +++ b/src/OpenGl/OpenGl_ShaderObject.hxx @@ -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; } diff --git a/src/OpenGl/OpenGl_ShaderProgram.hxx b/src/OpenGl/OpenGl_ShaderProgram.hxx index 99474d87c4..33d8e7d4be 100755 --- a/src/OpenGl/OpenGl_ShaderProgram.hxx +++ b/src/OpenGl/OpenGl_ShaderProgram.hxx @@ -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); diff --git a/src/OpenGl/OpenGl_Structure.hxx b/src/OpenGl/OpenGl_Structure.hxx index e7f679b352..33a05cdd88 100644 --- a/src/OpenGl/OpenGl_Structure.hxx +++ b/src/OpenGl/OpenGl_Structure.hxx @@ -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. diff --git a/src/OpenGl/OpenGl_Text.hxx b/src/OpenGl/OpenGl_Text.hxx index 7907fccd6b..ccfd921d07 100755 --- a/src/OpenGl/OpenGl_Text.hxx +++ b/src/OpenGl/OpenGl_Text.hxx @@ -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, diff --git a/src/OpenGl/OpenGl_Texture.cxx b/src/OpenGl/OpenGl_Texture.cxx index fa0b87d26c..c4de253714 100644 --- a/src/OpenGl/OpenGl_Texture.cxx +++ b/src/OpenGl/OpenGl_Texture.cxx @@ -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; +} diff --git a/src/OpenGl/OpenGl_Texture.hxx b/src/OpenGl/OpenGl_Texture.hxx index debbd6d097..da9ec7bb78 100644 --- a/src/OpenGl/OpenGl_Texture.hxx +++ b/src/OpenGl/OpenGl_Texture.hxx @@ -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 diff --git a/src/OpenGl/OpenGl_VertexBuffer.hxx b/src/OpenGl/OpenGl_VertexBuffer.hxx index 0cbc59631b..2558a0c3ae 100644 --- a/src/OpenGl/OpenGl_VertexBuffer.hxx +++ b/src/OpenGl/OpenGl_VertexBuffer.hxx @@ -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) { diff --git a/src/OpenGl/OpenGl_View.cxx b/src/OpenGl/OpenGl_View.cxx index 1735b1d413..e4e82c09d8 100644 --- a/src/OpenGl/OpenGl_View.cxx +++ b/src/OpenGl/OpenGl_View.cxx @@ -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()) { diff --git a/src/OpenGl/OpenGl_View.hxx b/src/OpenGl/OpenGl_View.hxx index 200f34baa9..04ae6a5a44 100644 --- a/src/OpenGl/OpenGl_View.hxx +++ b/src/OpenGl/OpenGl_View.hxx @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -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 diff --git a/src/OpenGl/OpenGl_View_Redraw.cxx b/src/OpenGl/OpenGl_View_Redraw.cxx index 9899c81da3..a12078cda8 100644 --- a/src/OpenGl/OpenGl_View_Redraw.cxx +++ b/src/OpenGl/OpenGl_View_Redraw.cxx @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -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 : diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx index 6069f55da4..946ac8efa1 100644 --- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx +++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx @@ -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.", diff --git a/tests/bugs/vis/bug24307_1 b/tests/bugs/vis/bug24307_1 index 114bafee1a..5cf52b2b4c 100644 --- a/tests/bugs/vis/bug24307_1 +++ b/tests/bugs/vis/bug24307_1 @@ -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 diff --git a/tests/bugs/vis/bug24307_2 b/tests/bugs/vis/bug24307_2 index 8e2576770f..f0e909ffc5 100644 --- a/tests/bugs/vis/bug24307_2 +++ b/tests/bugs/vis/bug24307_2 @@ -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 -- 2.20.1