0030476: Visualization, Path Tracing - Adaptive Screen Sampling leads to unstable... IR-2019-02-15
authorkgv <kgv@opencascade.com>
Mon, 11 Feb 2019 15:00:35 +0000 (18:00 +0300)
committerapn <apn@opencascade.com>
Fri, 15 Feb 2019 14:16:12 +0000 (17:16 +0300)
OpenGl_View::runPathtrace() has been extended with alternative multi-pass
Adaptive Screen Sampling mode, not relying on atomic floating point operations.
Although atomic operations on floats allows single-pass rendering,
such operations leads to instability in case of different calculation order.
Atomic float operations are also currently supported only by single GPU vendor.

Fixed GLSL compilation on Intel drivers (follow ARB_shader_image_load_store
specs rather than EXT_shader_image_load_store).

Graphic3d_RenderingParams::AdaptiveScreenSamplingAtomic option has been added
to activate 1-pass Adaptive Screen Sampling mode when supported by hardware.

vfps command has been extended with -duration argument allowing to limit command execution time.
vactivate command has been extended with -noUpdate argument.

18 files changed:
src/Graphic3d/Graphic3d_RenderingParams.hxx
src/Graphic3d/Graphic3d_TypeOfLimit.hxx
src/OpenGl/OpenGl_Context.cxx
src/OpenGl/OpenGl_Context.hxx
src/OpenGl/OpenGl_FrameStats.cxx
src/OpenGl/OpenGl_GraphicDriver.cxx
src/OpenGl/OpenGl_TileSampler.cxx
src/OpenGl/OpenGl_TileSampler.hxx
src/OpenGl/OpenGl_View.hxx
src/OpenGl/OpenGl_View_Raytrace.cxx
src/Shaders/Display.fs
src/Shaders/RaytraceBase.fs
src/Shaders/RaytraceRender.fs
src/Shaders/Shaders_Display_fs.pxx
src/Shaders/Shaders_RaytraceBase_fs.pxx
src/Shaders/Shaders_RaytraceRender_fs.pxx
src/ViewerTest/ViewerTest_ViewerCommands.cxx
src/ViewerTest/ViewerTest_ViewerCommands_1.mm

index 9068b46..6042d89 100644 (file)
@@ -109,6 +109,7 @@ public:
     UseEnvironmentMapBackground (Standard_False),
     CoherentPathTracingMode     (Standard_False),
     AdaptiveScreenSampling      (Standard_False),
+    AdaptiveScreenSamplingAtomic(Standard_False),
     ShowSamplingTiles           (Standard_False),
     TwoSidedBsdfModels          (Standard_False),
     RadianceClampingValue       (30.0),
@@ -184,6 +185,7 @@ public:
   Standard_Boolean                  UseEnvironmentMapBackground; //!< enables/disables environment map background
   Standard_Boolean                  CoherentPathTracingMode;     //!< enables/disables 'coherent' tracing mode (single RNG seed within 16x16 image blocks)
   Standard_Boolean                  AdaptiveScreenSampling;      //!< enables/disables adaptive screen sampling mode for path tracing, FALSE by default
+  Standard_Boolean                  AdaptiveScreenSamplingAtomic;//!< enables/disables usage of atomic float operations within adaptive screen sampling, FALSE by default
   Standard_Boolean                  ShowSamplingTiles;           //!< enables/disables debug mode for adaptive screen sampling, FALSE by default
   Standard_Boolean                  TwoSidedBsdfModels;          //!< forces path tracing to use two-sided versions of original one-sided scattering models
   Standard_ShortReal                RadianceClampingValue;       //!< maximum radiance value used for clamping radiance estimation.
index 6c9f208..772b944 100644 (file)
@@ -28,6 +28,7 @@ enum Graphic3d_TypeOfLimit
   Graphic3d_TypeOfLimit_HasRayTracing,                  //!< indicates whether ray tracing is supported
   Graphic3d_TypeOfLimit_HasRayTracingTextures,          //!< indicates whether ray tracing textures are supported
   Graphic3d_TypeOfLimit_HasRayTracingAdaptiveSampling,  //!< indicates whether adaptive screen sampling is supported
+  Graphic3d_TypeOfLimit_HasRayTracingAdaptiveSamplingAtomic,//!< indicates whether optimized adaptive screen sampling is supported (hardware supports atomic float operations)
   Graphic3d_TypeOfLimit_HasBlendedOit,                  //!< indicates whether necessary GL extensions for Weighted, Blended OIT available (without MSAA).
   Graphic3d_TypeOfLimit_HasBlendedOitMsaa,              //!< indicates whether necessary GL extensions for Weighted, Blended OIT available (with MSAA).
   Graphic3d_TypeOfLimit_HasFlatShading,                 //!< indicates whether Flat shading (Graphic3d_TOSM_FACET) is supported
index 52509f4..b83a2dd 100644 (file)
@@ -172,6 +172,7 @@ OpenGl_Context::OpenGl_Context (const Handle(OpenGl_Caps)& theCaps)
   myHasRayTracing (Standard_False),
   myHasRayTracingTextures (Standard_False),
   myHasRayTracingAdaptiveSampling (Standard_False),
+  myHasRayTracingAdaptiveSamplingAtomic (Standard_False),
   myFrameStats (new OpenGl_FrameStats()),
 #if !defined(GL_ES_VERSION_2_0)
   myPointSpriteOrig (GL_UPPER_LEFT),
@@ -2516,8 +2517,9 @@ void OpenGl_Context::init (const Standard_Boolean theIsCoreProfile)
 
   // check whether adaptive screen sampling in ray tracing mode is supported
   myHasRayTracingAdaptiveSampling = myHasRayTracing
-                                 && has44
-                                 && CheckExtension ("GL_NV_shader_atomic_float");
+                                 && has44;
+  myHasRayTracingAdaptiveSamplingAtomic = myHasRayTracingAdaptiveSampling
+                                       && CheckExtension ("GL_NV_shader_atomic_float");
 
   if (!has32)
   {
index 13bf652..f9e94d4 100644 (file)
@@ -503,6 +503,9 @@ public:
   //! @return TRUE if adaptive screen sampling in ray tracing mode is supported
   Standard_Boolean HasRayTracingAdaptiveSampling() const { return myHasRayTracingAdaptiveSampling; }
 
+  //! @return TRUE if atomic adaptive screen sampling in ray tracing mode is supported
+  Standard_Boolean HasRayTracingAdaptiveSamplingAtomic() const { return myHasRayTracingAdaptiveSamplingAtomic; }
+
   //! Returns true if VBO is supported and permitted.
   inline bool ToUseVbo() const
   {
@@ -928,9 +931,10 @@ private: // context info
   Standard_Boolean myIsGlNormalizeEnabled; //!< GL_NORMALIZE flag
                                            //!< Used to tell OpenGl that normals should be normalized
 
-  Standard_Boolean myHasRayTracing;                 //! indicates whether ray tracing mode is supported 
-  Standard_Boolean myHasRayTracingTextures;         //! indicates whether textures in ray tracing mode are supported 
-  Standard_Boolean myHasRayTracingAdaptiveSampling; //! indicates whether adaptive screen sampling in ray tracing mode is supported 
+  Standard_Boolean myHasRayTracing;                 //! indicates whether ray tracing mode is supported
+  Standard_Boolean myHasRayTracingTextures;         //! indicates whether textures in ray tracing mode are supported
+  Standard_Boolean myHasRayTracingAdaptiveSampling; //! indicates whether adaptive screen sampling in ray tracing mode is supported
+  Standard_Boolean myHasRayTracingAdaptiveSamplingAtomic; //! indicates whether atomic adaptive screen sampling in ray tracing mode is supported
 
   Handle(OpenGl_ShaderManager) myShaderManager; //! support object for managing shader programs
 
index cb64c5a..c43b16c 100644 (file)
@@ -161,6 +161,8 @@ void OpenGl_FrameStats::updateStatistics (const Handle(Graphic3d_CView)& theView
       aMemFbos += estimatedDataSize (aView->myRaytraceVisualErrorTexture[1]);
       aMemFbos += estimatedDataSize (aView->myRaytraceTileOffsetsTexture[0]);
       aMemFbos += estimatedDataSize (aView->myRaytraceTileOffsetsTexture[1]);
+      aMemFbos += estimatedDataSize (aView->myRaytraceTileSamplesTexture[0]);
+      aMemFbos += estimatedDataSize (aView->myRaytraceTileSamplesTexture[1]);
     }
     {
       // Ray Tracing geometry
index ee2ea15..6908aa8 100644 (file)
@@ -451,6 +451,8 @@ Standard_Integer OpenGl_GraphicDriver::InquireLimit (const Graphic3d_TypeOfLimit
       return (!aCtx.IsNull() && aCtx->HasRayTracingTextures()) ? 1 : 0;
     case Graphic3d_TypeOfLimit_HasRayTracingAdaptiveSampling:
       return (!aCtx.IsNull() && aCtx->HasRayTracingAdaptiveSampling()) ? 1 : 0;
+    case Graphic3d_TypeOfLimit_HasRayTracingAdaptiveSamplingAtomic:
+      return (!aCtx.IsNull() && aCtx->HasRayTracingAdaptiveSamplingAtomic()) ? 1 : 0;
     case Graphic3d_TypeOfLimit_HasBlendedOit:
       return (!aCtx.IsNull()
             && aCtx->hasDrawBuffers != OpenGl_FeatureNotAvailable
index 9ccd4c9..c8bbd17 100644 (file)
@@ -18,6 +18,9 @@
 #include <Graphic3d_RenderingParams.hxx>
 #include <TCollection_ExtendedString.hxx>
 
+// define to debug algorithm values
+//#define RAY_TRACE_PRINT_DEBUG_INFO
+
 //=======================================================================
 //function : OpenGl_TileSampler
 //purpose  :
@@ -68,6 +71,8 @@ void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theConte
     for (Standard_Size aRowIter = 0; aRowIter < myVarianceMap.SizeY; ++aRowIter)
     {
       const int aRawValue = myVarianceRaw.Value (aRowIter, aColIter);
+      Standard_RangeError_Raise_if (aRawValue < 0, "Internal Error: signed integer overflow within OpenGl_TileSampler");
+
       float& aTile = myVarianceMap.ChangeValue (aRowIter, aColIter);
       aTile = aFactor * float(aRawValue);
       aTile *= 1.0f / tileArea ((int )aColIter, (int )aRowIter); // average error over the tile
@@ -87,6 +92,29 @@ void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theConte
       myMarginalMap[aX] += myMarginalMap[aX - 1];
     }
   }
+
+#ifdef RAY_TRACE_PRINT_DEBUG_INFO
+  dumpMap (std::cerr, myVarianceRaw, "OpenGl_TileSampler, Variance map");
+#endif
+}
+
+//=======================================================================
+//function : dumpMap
+//purpose  :
+//=======================================================================
+void OpenGl_TileSampler::dumpMap (std::ostream& theStream,
+                                  const Image_PixMapTypedData<int>& theMap,
+                                  const char* theTitle) const
+{
+  theStream << theTitle << " " << theMap.SizeX << "x" << theMap.SizeY << " (tile " << myTileSize << "x" << myTileSize << ")" << ":\n";
+  for (Standard_Size aRowIter = 0; aRowIter < theMap.SizeY; ++aRowIter)
+  {
+    for (Standard_Size aColIter = 0; aColIter < theMap.SizeX; ++aColIter)
+    {
+      theStream << " [" << theMap.Value (aRowIter, aColIter) << "]";
+    }
+    theStream << "\n";
+  }
 }
 
 //=======================================================================
@@ -148,6 +176,10 @@ void OpenGl_TileSampler::SetSize (const Graphic3d_RenderingParams& theParams,
     myTiles.Init (anAlloc, aNbTilesX, aNbTilesY);
     myTiles.Init (1);
 
+    myTileSamples.SetTopDown (true);
+    myTileSamples.Init (myTiles.Allocator(), aNbTilesX, aNbTilesY);
+    myTileSamples.Init (1);
+
     myVarianceMap.SetTopDown (true);
     myVarianceMap.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
     myVarianceMap.Init (0.0f);
@@ -185,18 +217,28 @@ void OpenGl_TileSampler::SetSize (const Graphic3d_RenderingParams& theParams,
 }
 
 //=======================================================================
-//function : UploadOffsets
+//function : upload
 //purpose  :
 //=======================================================================
-bool OpenGl_TileSampler::UploadOffsets (const Handle(OpenGl_Context)& theContext,
-                                        const Handle(OpenGl_Texture)& theOffsetsTexture,
-                                        const bool theAdaptive)
+bool OpenGl_TileSampler::upload (const Handle(OpenGl_Context)& theContext,
+                                 const Handle(OpenGl_Texture)& theSamplesTexture,
+                                 const Handle(OpenGl_Texture)& theOffsetsTexture,
+                                 const bool theAdaptive)
 {
   if (myTiles.IsEmpty())
   {
     return false;
   }
 
+  // Fill in myTiles map with a number of passes (samples) per tile.
+  // By default, all tiles receive 1 sample, but basing on visual error level (myVarianceMap),
+  // this amount is re-distributed from tiles having smallest error take 0 samples to tiles having larger error.
+  // This redistribution is smoothed by Halton sampler.
+  //
+  // myOffsets map is filled as redirection of currently rendered tile to another one
+  // so that tiles having smallest error level have 0 tiles redirected from,
+  // while tiles with great error level might be rendered more than 1.
+  // This map is used within single-pass rendering method requiring atomic float operation support from hardware.
   myTiles.Init (0);
   Image_PixMapTypedData<Graphic3d_Vec2i>& anOffsets = theAdaptive ? myOffsetsShrunk : myOffsets;
   anOffsets.Init (Graphic3d_Vec2i (-1, -1));
@@ -210,7 +252,49 @@ bool OpenGl_TileSampler::UploadOffsets (const Handle(OpenGl_Context)& theContext
     }
   }
 
+#ifdef RAY_TRACE_PRINT_DEBUG_INFO
+  dumpMap (std::cerr, myTiles, "OpenGl_TileSampler, Samples");
+#endif
+
+  // Fill in myTileSamples map from myTiles with an actual number of Samples per Tile as multiple of Tile Area
+  // (e.g. tile that should be rendered ones will have amount of samples equal to its are 4x4=16).
+  // This map is used for discarding tile fragments having <=0 of samples left within multi-pass rendering.
+  myTileSamples.Init (0);
+  for (Standard_Size aRowIter = 0; aRowIter < myTiles.SizeY; ++aRowIter)
+  {
+    for (Standard_Size aColIter = 0; aColIter < myTiles.SizeX; ++aColIter)
+    {
+      myTileSamples.ChangeValue (aRowIter, aColIter) = tileArea ((int )aColIter, (int )aRowIter) * myTiles.Value (aRowIter, aColIter);
+    }
+  }
+
   bool hasErrors = false;
+
+  if (!theSamplesTexture.IsNull())
+  {
+    theSamplesTexture->Bind (theContext);
+    theContext->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT,  1);
+  #if !defined(GL_ES_VERSION_2_0)
+    theContext->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+  #endif
+    if (theSamplesTexture->SizeX() == (int )myTileSamples.SizeX
+     && theSamplesTexture->SizeY() == (int )myTileSamples.SizeY)
+    {
+      theContext->core11fwd->glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, (int )myTileSamples.SizeX, (int )myTileSamples.SizeY, GL_RED_INTEGER, GL_INT, myTileSamples.Data());
+      if (theContext->core11fwd->glGetError() != GL_NO_ERROR)
+      {
+        hasErrors = true;
+        theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
+                                 "Error! Failed to upload tile samples map on the GPU");
+      }
+    }
+    else
+    {
+      hasErrors = true;
+    }
+    theSamplesTexture->Unbind (theContext);
+  }
+
   if (!theOffsetsTexture.IsNull())
   {
     if (theOffsetsTexture->SizeX() != (int )anOffsets.SizeX
index f956507..875416a 100644 (file)
@@ -74,6 +74,20 @@ public:
   //! Maximum viewport for rendering using offsets texture.
   Graphic3d_Vec2i OffsetTilesViewportMax() const { return NbOffsetTilesMax() * myTileSize; }
 
+  //! Return maximum number of samples per tile.
+  int MaxTileSamples() const
+  {
+    int aNbSamples = 0;
+    for (Standard_Size aRowIter = 0; aRowIter < myTiles.SizeY; ++aRowIter)
+    {
+      for (Standard_Size aColIter = 0; aColIter < myTiles.SizeX; ++aColIter)
+      {
+        aNbSamples = Max (aNbSamples, myTiles.Value (aRowIter, aColIter));
+      }
+    }
+    return aNbSamples;
+  }
+
   //! Specifies size of ray-tracing viewport and recomputes tile size.
   Standard_EXPORT void SetSize (const Graphic3d_RenderingParams& theParams,
                                 const Graphic3d_Vec2i& theSize);
@@ -86,10 +100,21 @@ public:
   //! Resets (restart) tile sampler to initial state.
   void Reset() { myLastSample = 0; }
 
+  //! Uploads tile samples to the given OpenGL texture.
+  bool UploadSamples (const Handle(OpenGl_Context)& theContext,
+                      const Handle(OpenGl_Texture)& theSamplesTexture,
+                      const bool theAdaptive)
+  {
+    return upload (theContext, theSamplesTexture, Handle(OpenGl_Texture)(), theAdaptive);
+  }
+
   //! Uploads offsets of sampled tiles to the given OpenGL texture.
-  Standard_EXPORT bool UploadOffsets (const Handle(OpenGl_Context)& theContext,
-                                      const Handle(OpenGl_Texture)& theOffsetsTexture,
-                                      const bool theAdaptive);
+  bool UploadOffsets (const Handle(OpenGl_Context)& theContext,
+                      const Handle(OpenGl_Texture)& theOffsetsTexture,
+                      const bool theAdaptive)
+  {
+    return upload (theContext, Handle(OpenGl_Texture)(), theOffsetsTexture, theAdaptive);
+  }
 
 protected:
 
@@ -104,9 +129,21 @@ protected:
   //! Samples tile location according to estimated error.
   Standard_EXPORT Graphic3d_Vec2i nextTileToSample();
 
+  //! Uploads offsets of sampled tiles to the given OpenGL texture.
+  Standard_EXPORT bool upload (const Handle(OpenGl_Context)& theContext,
+                               const Handle(OpenGl_Texture)& theSamplesTexture,
+                               const Handle(OpenGl_Texture)& theOffsetsTexture,
+                               const bool theAdaptive);
+
+  //! Auxiliary method for dumping 2D image map into stream (e.g. for debugging).
+  Standard_EXPORT void dumpMap (std::ostream& theStream,
+                                const Image_PixMapTypedData<int>& theMap,
+                                const char* theTitle) const;
+
 protected:
 
   Image_PixMapTypedData<unsigned int>    myTiles;         //!< number of samples per tile (initially all 1)
+  Image_PixMapTypedData<unsigned int>    myTileSamples;   //!< number of samples for all pixels within the tile (initially equals to Tile area)
   Image_PixMapTypedData<float>           myVarianceMap;   //!< Estimation of visual error per tile
   Image_PixMapTypedData<int>             myVarianceRaw;   //!< Estimation of visual error per tile (raw data)
   Image_PixMapTypedData<Graphic3d_Vec2i> myOffsets;       //!< 2D array of tiles redirecting to another tile
index cc9a564..4155221 100644 (file)
@@ -592,6 +592,7 @@ protected: //! @name data types related to ray-tracing
 
     // images used by ISS mode
     OpenGl_RT_uRenderImage,
+    OpenGl_RT_uTilesImage,
     OpenGl_RT_uOffsetImage,
     OpenGl_RT_uTileSize,
     OpenGl_RT_uVarianceScaleFactor,
@@ -608,6 +609,7 @@ protected: //! @name data types related to ray-tracing
     OpenGl_RT_OutputImage = 0,
     OpenGl_RT_VisualErrorImage = 1,
     OpenGl_RT_TileOffsetsImage = 2,
+    OpenGl_RT_TileSamplesImage = 3
   };
 
   //! Tool class for management of shader sources.
@@ -691,6 +693,9 @@ protected: //! @name data types related to ray-tracing
     //! Enables/disables adaptive screen sampling for path tracing.
     Standard_Boolean AdaptiveScreenSampling;
 
+    //! Enables/disables 1-pass atomic mode for AdaptiveScreenSampling.
+    Standard_Boolean AdaptiveScreenSamplingAtomic;
+
     //! Enables/disables environment map for background.
     Standard_Boolean UseEnvMapForBackground;
 
@@ -712,6 +717,7 @@ protected: //! @name data types related to ray-tracing
       UseBindlessTextures    (Standard_False),
       TwoSidedBsdfModels     (Standard_False),
       AdaptiveScreenSampling (Standard_False),
+      AdaptiveScreenSamplingAtomic (Standard_False),
       UseEnvMapForBackground (Standard_False),
       RadianceClampingValue  (30.0),
       DepthOfField           (Standard_False),
@@ -1008,6 +1014,9 @@ protected: //! @name fields related to ray-tracing
   //! Texture containing offsets of sampled screen tiles (2 textures are used in stereo mode).
   //! Used if adaptive screen sampling is activated.
   Handle(OpenGl_Texture) myRaytraceTileOffsetsTexture[2];
+  //! Texture containing amount of extra per-tile samples (2 textures are used in stereo mode).
+  //! Used if adaptive screen sampling is activated.
+  Handle(OpenGl_Texture) myRaytraceTileSamplesTexture[2];
 
   //! Vertex buffer (VBO) for drawing dummy quad.
   OpenGl_VertexBuffer myRaytraceScreenQuad;
index 12177f3..e2f15e6 100644 (file)
@@ -1136,10 +1136,14 @@ TCollection_AsciiString OpenGl_View::generateShaderPrefix (const Handle(OpenGl_C
 
     if (myRaytraceParameters.AdaptiveScreenSampling) // adaptive screen sampling requested
     {
-      // to activate the feature we need OpenGL 4.4 and GL_NV_shader_atomic_float extension
-      if (theGlContext->IsGlGreaterEqual (4, 4) && theGlContext->CheckExtension ("GL_NV_shader_atomic_float"))
+      if (theGlContext->IsGlGreaterEqual (4, 4))
       {
         aPrefixString += TCollection_AsciiString ("\n#define ADAPTIVE_SAMPLING");
+        if (myRaytraceParameters.AdaptiveScreenSamplingAtomic
+         && theGlContext->CheckExtension ("GL_NV_shader_atomic_float"))
+        {
+          aPrefixString += TCollection_AsciiString ("\n#define ADAPTIVE_SAMPLING_ATOMIC");
+        }
       }
     }
 
@@ -1387,9 +1391,11 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Standard_Integer theS
       aToRebuildShaders = Standard_True;
     }
 
-    if (myRenderParams.AdaptiveScreenSampling != myRaytraceParameters.AdaptiveScreenSampling)
+    if (myRenderParams.AdaptiveScreenSampling       != myRaytraceParameters.AdaptiveScreenSampling
+     || myRenderParams.AdaptiveScreenSamplingAtomic != myRaytraceParameters.AdaptiveScreenSamplingAtomic)
     {
-      myRaytraceParameters.AdaptiveScreenSampling = myRenderParams.AdaptiveScreenSampling;
+      myRaytraceParameters.AdaptiveScreenSampling       = myRenderParams.AdaptiveScreenSampling;
+      myRaytraceParameters.AdaptiveScreenSamplingAtomic = myRenderParams.AdaptiveScreenSamplingAtomic;
       if (myRenderParams.AdaptiveScreenSampling) // adaptive sampling was requested
       {
         if (!theGlContext->HasRayTracingAdaptiveSampling())
@@ -1397,7 +1403,15 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Standard_Integer theS
           // disable the feature if it is not supported
           myRaytraceParameters.AdaptiveScreenSampling = myRenderParams.AdaptiveScreenSampling = Standard_False;
           theGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW,
-                                     "Adaptive sampling not supported (OpenGL 4.4 or GL_NV_shader_atomic_float is missing)");
+                                     "Adaptive sampling is not supported (OpenGL 4.4 is missing)");
+        }
+        else if (myRaytraceParameters.AdaptiveScreenSamplingAtomic
+             && !theGlContext->HasRayTracingAdaptiveSamplingAtomic())
+        {
+          // disable the feature if it is not supported
+          myRaytraceParameters.AdaptiveScreenSamplingAtomic = myRenderParams.AdaptiveScreenSamplingAtomic = Standard_False;
+          theGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW,
+                                     "Atomic adaptive sampling is not supported (GL_NV_shader_atomic_float is missing)");
         }
       }
 
@@ -1726,6 +1740,8 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Standard_Integer theS
 
       myUniformLocations[anIndex][OpenGl_RT_uRenderImage] =
         aShaderProgram->GetUniformLocation (theGlContext, "uRenderImage");
+      myUniformLocations[anIndex][OpenGl_RT_uTilesImage] =
+        aShaderProgram->GetUniformLocation (theGlContext, "uTilesImage");
       myUniformLocations[anIndex][OpenGl_RT_uOffsetImage] =
         aShaderProgram->GetUniformLocation (theGlContext, "uOffsetImage");
       myUniformLocations[anIndex][OpenGl_RT_uTileSize] =
@@ -1814,6 +1830,8 @@ void OpenGl_View::releaseRaytraceResources (const Handle(OpenGl_Context)& theGlC
     nullifyResource (theGlContext, myRaytraceTileOffsetsTexture[1]);
     nullifyResource (theGlContext, myRaytraceVisualErrorTexture[0]);
     nullifyResource (theGlContext, myRaytraceVisualErrorTexture[1]);
+    nullifyResource (theGlContext, myRaytraceTileSamplesTexture[0]);
+    nullifyResource (theGlContext, myRaytraceTileSamplesTexture[1]);
 
     nullifyResource (theGlContext, mySceneNodeInfoTexture);
     nullifyResource (theGlContext, mySceneMinPointTexture);
@@ -1874,6 +1892,7 @@ Standard_Boolean OpenGl_View::updateRaytraceBuffers (const Standard_Integer
     {
       myRaytraceOutputTexture[aViewIter] = new OpenGl_Texture();
       myRaytraceVisualErrorTexture[aViewIter] = new OpenGl_Texture();
+      myRaytraceTileSamplesTexture[aViewIter] = new OpenGl_Texture();
       myRaytraceTileOffsetsTexture[aViewIter] = new OpenGl_Texture();
     }
 
@@ -1895,7 +1914,15 @@ Standard_Boolean OpenGl_View::updateRaytraceBuffers (const Standard_Integer
        && myRaytraceVisualErrorTexture[aViewIter]->SizeX() == myTileSampler.NbTilesX()
        && myRaytraceVisualErrorTexture[aViewIter]->SizeY() == myTileSampler.NbTilesY())
       {
-        continue;
+        if (myRaytraceParameters.AdaptiveScreenSamplingAtomic)
+        {
+          continue; // offsets texture is dynamically resized
+        }
+        else if (myRaytraceTileSamplesTexture[aViewIter]->SizeX() == myTileSampler.NbTilesX()
+              && myRaytraceTileSamplesTexture[aViewIter]->SizeY() == myTileSampler.NbTilesY())
+        {
+          continue;
+        }
       }
 
       myAccumFrames = 0;
@@ -1913,8 +1940,14 @@ Standard_Boolean OpenGl_View::updateRaytraceBuffers (const Standard_Integer
 
       // workaround for some NVIDIA drivers
       myRaytraceVisualErrorTexture[aViewIter]->Release (theGlContext.operator->());
+      myRaytraceTileSamplesTexture[aViewIter]->Release (theGlContext.operator->());
       myRaytraceVisualErrorTexture[aViewIter]->Init (theGlContext, GL_R32I, GL_RED_INTEGER, GL_INT,
                                                      myTileSampler.NbTilesX(), myTileSampler.NbTilesY(), Graphic3d_TOT_2D);
+      if (!myRaytraceParameters.AdaptiveScreenSamplingAtomic)
+      {
+        myRaytraceTileSamplesTexture[aViewIter]->Init (theGlContext, GL_R32I, GL_RED_INTEGER, GL_INT,
+                                                       myTileSampler.NbTilesX(), myTileSampler.NbTilesY(), Graphic3d_TOT_2D);
+      }
     }
     else // non-adaptive mode
     {
@@ -2684,8 +2717,16 @@ void OpenGl_View::bindRaytraceTextures (const Handle(OpenGl_Context)& theGlConte
                                               myRaytraceOutputTexture[theStereoView]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32F);
     theGlContext->core42->glBindImageTexture (OpenGl_RT_VisualErrorImage,
                                               myRaytraceVisualErrorTexture[theStereoView]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32I);
-    theGlContext->core42->glBindImageTexture (OpenGl_RT_TileOffsetsImage,
-                                              myRaytraceTileOffsetsTexture[theStereoView]->TextureId(), 0, GL_TRUE, 0, GL_READ_ONLY, GL_RG32I);
+    if (myRaytraceParameters.AdaptiveScreenSamplingAtomic)
+    {
+      theGlContext->core42->glBindImageTexture (OpenGl_RT_TileOffsetsImage,
+                                                myRaytraceTileOffsetsTexture[theStereoView]->TextureId(), 0, GL_TRUE, 0, GL_READ_ONLY, GL_RG32I);
+    }
+    else
+    {
+      theGlContext->core42->glBindImageTexture (OpenGl_RT_TileSamplesImage,
+                                                myRaytraceTileSamplesTexture[theStereoView]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32I);
+    }
   #else
     (void )theStereoView;
   #endif
@@ -2911,7 +2952,14 @@ Standard_Boolean OpenGl_View::runPathtrace (const Standard_Integer
       myTileSampler.Reset(); // reset tile sampler to its initial state
 
       // Adaptive sampling is starting at the second frame
-      myTileSampler.UploadOffsets (theGlContext, myRaytraceTileOffsetsTexture[aFBOIdx], false);
+      if (myRaytraceParameters.AdaptiveScreenSamplingAtomic)
+      {
+        myTileSampler.UploadOffsets (theGlContext, myRaytraceTileOffsetsTexture[aFBOIdx], false);
+      }
+      else
+      {
+        myTileSampler.UploadSamples (theGlContext, myRaytraceTileSamplesTexture[aFBOIdx], false);
+      }
 
     #if !defined(GL_ES_VERSION_2_0)
       theGlContext->core44->glClearTexImage (myRaytraceOutputTexture[aFBOIdx]->TextureId(), 0, GL_RED, GL_FLOAT, NULL);
@@ -2932,24 +2980,19 @@ Standard_Boolean OpenGl_View::runPathtrace (const Standard_Integer
   // Set frame accumulation weight
   myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uAccumSamples], myAccumFrames);
 
-  // Set random number generator seed
-  if (myAccumFrames == 0)
-  {
-    myRNG.SetSeed(); // start RNG from beginning
-  }
-  myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uFrameRndSeed], static_cast<Standard_Integer> (myRNG.NextInt() >> 2));
-
   // Set image uniforms for render program
   if (myRaytraceParameters.AdaptiveScreenSampling)
   {
     myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uRenderImage], OpenGl_RT_OutputImage);
+    myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uTilesImage],  OpenGl_RT_TileSamplesImage);
     myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uOffsetImage], OpenGl_RT_TileOffsetsImage);
     myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uTileSize], myTileSampler.TileSize());
   }
 
   const Handle(OpenGl_FrameBuffer)& aRenderImageFramebuffer = myAccumFrames % 2 ? myRaytraceFBO1[aFBOIdx] : myRaytraceFBO2[aFBOIdx];
   aRenderImageFramebuffer->BindBuffer (theGlContext);
-  if (myRaytraceParameters.AdaptiveScreenSampling)
+  if (myRaytraceParameters.AdaptiveScreenSampling
+   && myRaytraceParameters.AdaptiveScreenSamplingAtomic)
   {
     // extend viewport here, so that tiles at boundaries (cut tile size by target rendering viewport)
     // redirected to inner tiles (full tile size) are drawn entirely
@@ -2959,11 +3002,41 @@ Standard_Boolean OpenGl_View::runPathtrace (const Standard_Integer
 
   // Generate for the given RNG seed
   glDisable (GL_DEPTH_TEST);
-  theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6);
 
+  // Adaptive Screen Sampling computes the same overall amount of samples per frame redraw as normal Path Tracing,
+  // but distributes them unequally across pixels (grouped in tiles), so that some pixels do not receive new samples at all.
+  //
+  // Offsets map (redirecting currently rendered tile to another tile) allows performing Adaptive Screen Sampling in single pass,
+  // but current implementation relies on atomic float operations (AdaptiveScreenSamplingAtomic) for this.
+  // So that when atomic floats are not supported by GPU, multi-pass rendering is used instead.
+  //
+  // Single-pass rendering is more optimal due to smaller amount of draw calls,
+  // memory synchronization barriers, discarding most of the fragments and bad parallelization in case of very small amount of tiles requiring more samples.
+  // However, atomic operations on float values still produces different result (close, but not bit exact) making non-regression testing not robust.
+  // It should be possible following single-pass rendering approach but using extra accumulation buffer and resolving pass as possible improvement.
+  const int aNbPasses = myRaytraceParameters.AdaptiveScreenSampling
+                    && !myRaytraceParameters.AdaptiveScreenSamplingAtomic
+                      ? myTileSampler.MaxTileSamples()
+                      : 1;
+  if (myAccumFrames == 0)
+  {
+    myRNG.SetSeed(); // start RNG from beginning
+  }
+  for (int aPassIter = 0; aPassIter < aNbPasses; ++aPassIter)
+  {
+    myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uFrameRndSeed], static_cast<Standard_Integer> (myRNG.NextInt() >> 2));
+    theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6);
+    if (myRaytraceParameters.AdaptiveScreenSampling)
+    {
+    #if !defined(GL_ES_VERSION_2_0)
+      theGlContext->core44->glMemoryBarrier (GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
+    #endif
+    }
+  }
   aRenderImageFramebuffer->UnbindBuffer (theGlContext);
 
-  if (myRaytraceParameters.AdaptiveScreenSampling)
+  if (myRaytraceParameters.AdaptiveScreenSampling
+   && myRaytraceParameters.AdaptiveScreenSamplingAtomic)
   {
     glViewport (0, 0, theSizeX, theSizeY);
   }
@@ -3026,7 +3099,14 @@ Standard_Boolean OpenGl_View::runPathtraceOut (const Graphic3d_Camera::Projectio
   {
     // Download visual error map from the GPU and build adjusted tile offsets for optimal image sampling
     myTileSampler.GrabVarianceMap (theGlContext, myRaytraceVisualErrorTexture[aFBOIdx]);
-    myTileSampler.UploadOffsets (theGlContext, myRaytraceTileOffsetsTexture[aFBOIdx], myAccumFrames != 0);
+    if (myRaytraceParameters.AdaptiveScreenSamplingAtomic)
+    {
+      myTileSampler.UploadOffsets (theGlContext, myRaytraceTileOffsetsTexture[aFBOIdx], myAccumFrames != 0);
+    }
+    else
+    {
+      myTileSampler.UploadSamples (theGlContext, myRaytraceTileSamplesTexture[aFBOIdx], myAccumFrames != 0);
+    }
   }
 
   unbindRaytraceTextures (theGlContext);
index ecb82cc..8067fb0 100644 (file)
@@ -5,10 +5,10 @@
   #extension GL_ARB_shader_image_size : enable
 
   //! OpenGL image used for accumulating rendering result.
-  volatile restrict layout(size1x32) uniform image2D uRenderImage;
+  volatile restrict layout(r32f) uniform image2D uRenderImage;
 
   //! OpenGL image storing variance of sampled pixels blocks.
-  volatile restrict layout(size1x32) uniform iimage2D uVarianceImage;
+  volatile restrict layout(r32i) uniform iimage2D uVarianceImage;
 
   //! Scale factor used to quantize visual error (float) into signed integer.
   uniform float uVarianceScaleFactor;
index f9b9637..261327c 100644 (file)
@@ -1,5 +1,7 @@
 #ifdef ADAPTIVE_SAMPLING
   #extension GL_ARB_shader_image_load_store : require
+#endif
+#ifdef ADAPTIVE_SAMPLING_ATOMIC
   #extension GL_NV_shader_atomic_float : require
 #endif
 
@@ -97,10 +99,15 @@ uniform float uSceneEpsilon;
 
 #ifdef ADAPTIVE_SAMPLING
   //! OpenGL image used for accumulating rendering result.
-  volatile restrict layout(size1x32) uniform image2D  uRenderImage;
+  volatile restrict layout(r32f) uniform image2D  uRenderImage;
 
+#ifdef ADAPTIVE_SAMPLING_ATOMIC
   //! OpenGL image storing offsets of sampled pixels blocks.
-  coherent restrict layout(size2x32) uniform iimage2D uOffsetImage;
+  coherent restrict layout(rg32i) uniform iimage2D uOffsetImage;
+#else
+  //! OpenGL image defining per-tile amount of samples.
+  volatile restrict layout(r32i) uniform iimage2D uTilesImage;
+#endif
 
   //! Screen space tile size.
   uniform ivec2 uTileSize;
@@ -274,7 +281,7 @@ vec3 InverseDirection (in vec3 theInput)
 //=======================================================================
 vec4 BackgroundColor()
 {
-#ifdef ADAPTIVE_SAMPLING
+#ifdef ADAPTIVE_SAMPLING_ATOMIC
 
   ivec2 aFragCoord = ivec2 (gl_FragCoord.xy);
 
index bc62690..83bbb7e 100644 (file)
@@ -20,6 +20,26 @@ uniform int uAccumSamples;
 //! Decreases noise level, but introduces some bias.
 uniform float uMaxRadiance = 50.f;
 
+#ifdef ADAPTIVE_SAMPLING
+//! Wrapper over imageLoad()+imageStore() having similar syntax as imageAtomicAdd().
+//! Modifies one component of 3Wx2H uRenderImage:
+//! |RGL| Red, Green, Luminance
+//! |SBH| Samples, Blue, Hit time transformed into OpenGL NDC space
+//! Returns previous value of the component.
+float addRenderImageComp (in ivec2 theFrag, in ivec2 theComp, in float theVal)
+{
+  ivec2 aCoord = ivec2 (3 * theFrag.x + theComp.x,
+                        2 * theFrag.y + theComp.y);
+#ifdef ADAPTIVE_SAMPLING_ATOMIC
+  return imageAtomicAdd (uRenderImage, aCoord, theVal);
+#else
+  float aVal = imageLoad (uRenderImage, aCoord).x;
+  imageStore (uRenderImage, aCoord, vec4 (aVal + theVal));
+  return aVal;
+#endif
+}
+#endif
+
 // =======================================================================
 // function : main
 // purpose  :
@@ -38,6 +58,7 @@ void main (void)
 
 #ifdef ADAPTIVE_SAMPLING
 
+#ifdef ADAPTIVE_SAMPLING_ATOMIC
   ivec2 aTileXY = imageLoad (uOffsetImage, aFragCoord / uTileSize).xy * uTileSize;
   if (aTileXY.x < 0) { discard; }
 
@@ -46,6 +67,13 @@ void main (void)
 
   aFragCoord.x = aTileXY.x + (aFragCoord.x % aRealBlockSize.x);
   aFragCoord.y = aTileXY.y + (aFragCoord.y % aRealBlockSize.y);
+#else
+  int aNbTileSamples = imageAtomicAdd (uTilesImage, aFragCoord / uTileSize, int(-1));
+  if (aNbTileSamples <= 0)
+  {
+    discard;
+  }
+#endif
 
 #endif // ADAPTIVE_SAMPLING
 
@@ -66,9 +94,7 @@ void main (void)
 
 #else
 
-  float aNbSamples = imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 0,
-                                                          2 * aFragCoord.y + 1), 1.0);
-
+  float aNbSamples = addRenderImageComp (aFragCoord, ivec2 (0, 1), 1.0);
   vec4 aColor = PathTrace (aRay, aInvDirect, int (aNbSamples));
 
 #endif
@@ -83,19 +109,14 @@ void main (void)
 #ifdef ADAPTIVE_SAMPLING
 
   // accumulate RGB color and depth
-  imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 0,
-                                       2 * aFragCoord.y + 0), aColor.r);
-  imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 1,
-                                       2 * aFragCoord.y + 0), aColor.g);
-  imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 1,
-                                       2 * aFragCoord.y + 1), aColor.b);
-  imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 2,
-                                       2 * aFragCoord.y + 1), aColor.w);
+  addRenderImageComp (aFragCoord, ivec2 (0, 0), aColor.r);
+  addRenderImageComp (aFragCoord, ivec2 (1, 0), aColor.g);
+  addRenderImageComp (aFragCoord, ivec2 (1, 1), aColor.b);
+  addRenderImageComp (aFragCoord, ivec2 (2, 1), aColor.w);
 
   if (int (aNbSamples) % 2 == 0) // accumulate luminance for even samples only
   {
-    imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 2,
-                                         2 * aFragCoord.y + 0), dot (LUMA, aColor.rgb));
+    addRenderImageComp (aFragCoord, ivec2 (2, 0), dot (LUMA, aColor.rgb));
   }
 
 #else
index 281fdcc..5919399 100644 (file)
@@ -8,10 +8,10 @@ static const char Shaders_Display_fs[] =
   "  #extension GL_ARB_shader_image_size : enable\n"
   "\n"
   "  //! OpenGL image used for accumulating rendering result.\n"
-  "  volatile restrict layout(size1x32) uniform image2D uRenderImage;\n"
+  "  volatile restrict layout(r32f) uniform image2D uRenderImage;\n"
   "\n"
   "  //! OpenGL image storing variance of sampled pixels blocks.\n"
-  "  volatile restrict layout(size1x32) uniform iimage2D uVarianceImage;\n"
+  "  volatile restrict layout(r32i) uniform iimage2D uVarianceImage;\n"
   "\n"
   "  //! Scale factor used to quantize visual error (float) into signed integer.\n"
   "  uniform float uVarianceScaleFactor;\n"
index eeab5a9..43c36ba 100644 (file)
@@ -3,6 +3,8 @@
 static const char Shaders_RaytraceBase_fs[] =
   "#ifdef ADAPTIVE_SAMPLING\n"
   "  #extension GL_ARB_shader_image_load_store : require\n"
+  "#endif\n"
+  "#ifdef ADAPTIVE_SAMPLING_ATOMIC\n"
   "  #extension GL_NV_shader_atomic_float : require\n"
   "#endif\n"
   "\n"
@@ -100,10 +102,15 @@ static const char Shaders_RaytraceBase_fs[] =
   "\n"
   "#ifdef ADAPTIVE_SAMPLING\n"
   "  //! OpenGL image used for accumulating rendering result.\n"
-  "  volatile restrict layout(size1x32) uniform image2D  uRenderImage;\n"
+  "  volatile restrict layout(r32f) uniform image2D  uRenderImage;\n"
   "\n"
+  "#ifdef ADAPTIVE_SAMPLING_ATOMIC\n"
   "  //! OpenGL image storing offsets of sampled pixels blocks.\n"
-  "  coherent restrict layout(size2x32) uniform iimage2D uOffsetImage;\n"
+  "  coherent restrict layout(rg32i) uniform iimage2D uOffsetImage;\n"
+  "#else\n"
+  "  //! OpenGL image defining per-tile amount of samples.\n"
+  "  volatile restrict layout(r32i) uniform iimage2D uTilesImage;\n"
+  "#endif\n"
   "\n"
   "  //! Screen space tile size.\n"
   "  uniform ivec2 uTileSize;\n"
@@ -277,7 +284,7 @@ static const char Shaders_RaytraceBase_fs[] =
   "//=======================================================================\n"
   "vec4 BackgroundColor()\n"
   "{\n"
-  "#ifdef ADAPTIVE_SAMPLING\n"
+  "#ifdef ADAPTIVE_SAMPLING_ATOMIC\n"
   "\n"
   "  ivec2 aFragCoord = ivec2 (gl_FragCoord.xy);\n"
   "\n"
index 1fe09ac..1d2f6db 100644 (file)
@@ -23,6 +23,26 @@ static const char Shaders_RaytraceRender_fs[] =
   "//! Decreases noise level, but introduces some bias.\n"
   "uniform float uMaxRadiance = 50.f;\n"
   "\n"
+  "#ifdef ADAPTIVE_SAMPLING\n"
+  "//! Wrapper over imageLoad()+imageStore() having similar syntax as imageAtomicAdd().\n"
+  "//! Modifies one component of 3Wx2H uRenderImage:\n"
+  "//! |RGL| Red, Green, Luminance\n"
+  "//! |SBH| Samples, Blue, Hit time transformed into OpenGL NDC space\n"
+  "//! Returns previous value of the component.\n"
+  "float addRenderImageComp (in ivec2 theFrag, in ivec2 theComp, in float theVal)\n"
+  "{\n"
+  "  ivec2 aCoord = ivec2 (3 * theFrag.x + theComp.x,\n"
+  "                        2 * theFrag.y + theComp.y);\n"
+  "#ifdef ADAPTIVE_SAMPLING_ATOMIC\n"
+  "  return imageAtomicAdd (uRenderImage, aCoord, theVal);\n"
+  "#else\n"
+  "  float aVal = imageLoad (uRenderImage, aCoord).x;\n"
+  "  imageStore (uRenderImage, aCoord, vec4 (aVal + theVal));\n"
+  "  return aVal;\n"
+  "#endif\n"
+  "}\n"
+  "#endif\n"
+  "\n"
   "// =======================================================================\n"
   "// function : main\n"
   "// purpose  :\n"
@@ -41,6 +61,7 @@ static const char Shaders_RaytraceRender_fs[] =
   "\n"
   "#ifdef ADAPTIVE_SAMPLING\n"
   "\n"
+  "#ifdef ADAPTIVE_SAMPLING_ATOMIC\n"
   "  ivec2 aTileXY = imageLoad (uOffsetImage, aFragCoord / uTileSize).xy * uTileSize;\n"
   "  if (aTileXY.x < 0) { discard; }\n"
   "\n"
@@ -49,6 +70,13 @@ static const char Shaders_RaytraceRender_fs[] =
   "\n"
   "  aFragCoord.x = aTileXY.x + (aFragCoord.x % aRealBlockSize.x);\n"
   "  aFragCoord.y = aTileXY.y + (aFragCoord.y % aRealBlockSize.y);\n"
+  "#else\n"
+  "  int aNbTileSamples = imageAtomicAdd (uTilesImage, aFragCoord / uTileSize, int(-1));\n"
+  "  if (aNbTileSamples <= 0)\n"
+  "  {\n"
+  "    discard;\n"
+  "  }\n"
+  "#endif\n"
   "\n"
   "#endif // ADAPTIVE_SAMPLING\n"
   "\n"
@@ -69,9 +97,7 @@ static const char Shaders_RaytraceRender_fs[] =
   "\n"
   "#else\n"
   "\n"
-  "  float aNbSamples = imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 0,\n"
-  "                                                          2 * aFragCoord.y + 1), 1.0);\n"
-  "\n"
+  "  float aNbSamples = addRenderImageComp (aFragCoord, ivec2 (0, 1), 1.0);\n"
   "  vec4 aColor = PathTrace (aRay, aInvDirect, int (aNbSamples));\n"
   "\n"
   "#endif\n"
@@ -86,19 +112,14 @@ static const char Shaders_RaytraceRender_fs[] =
   "#ifdef ADAPTIVE_SAMPLING\n"
   "\n"
   "  // accumulate RGB color and depth\n"
-  "  imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 0,\n"
-  "                                       2 * aFragCoord.y + 0), aColor.r);\n"
-  "  imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 1,\n"
-  "                                       2 * aFragCoord.y + 0), aColor.g);\n"
-  "  imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 1,\n"
-  "                                       2 * aFragCoord.y + 1), aColor.b);\n"
-  "  imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 2,\n"
-  "                                       2 * aFragCoord.y + 1), aColor.w);\n"
+  "  addRenderImageComp (aFragCoord, ivec2 (0, 0), aColor.r);\n"
+  "  addRenderImageComp (aFragCoord, ivec2 (1, 0), aColor.g);\n"
+  "  addRenderImageComp (aFragCoord, ivec2 (1, 1), aColor.b);\n"
+  "  addRenderImageComp (aFragCoord, ivec2 (2, 1), aColor.w);\n"
   "\n"
   "  if (int (aNbSamples) % 2 == 0) // accumulate luminance for even samples only\n"
   "  {\n"
-  "    imageAtomicAdd (uRenderImage, ivec2 (3 * aFragCoord.x + 2,\n"
-  "                                         2 * aFragCoord.y + 0), dot (LUMA, aColor.rgb));\n"
+  "    addRenderImageComp (aFragCoord, ivec2 (2, 0), dot (LUMA, aColor.rgb));\n"
   "  }\n"
   "\n"
   "#else\n"
index bc12b99..4fae1a9 100644 (file)
@@ -1264,38 +1264,40 @@ TCollection_AsciiString FindViewIdByWindowHandle(const Aspect_Handle theWindowHa
 }
 #endif
 
-//==============================================================================
-//function : ActivateView
-//purpose  : Make the view active
-//==============================================================================
-
-void ActivateView (const TCollection_AsciiString& theViewName)
+//! Make the view active
+void ActivateView (const TCollection_AsciiString& theViewName,
+                   Standard_Boolean theToUpdate = Standard_True)
 {
   const Handle(V3d_View) aView = ViewerTest_myViews.Find1(theViewName);
-  if (!aView.IsNull())
+  if (aView.IsNull())
   {
-    Handle(AIS_InteractiveContext) anAISContext = FindContextByView(aView);
-    if (!anAISContext.IsNull())
-    {
-      if (!ViewerTest::CurrentView().IsNull())
-      {
-        TCollection_AsciiString aTitle("3D View - ");
-        aTitle = aTitle + ViewerTest_myViews.Find2 (ViewerTest::CurrentView());
-        SetWindowTitle (ViewerTest::CurrentView()->Window(), aTitle.ToCString());
-      }
+    return;
+  }
 
-      ViewerTest::CurrentView (aView);
-      ViewerTest::SetAISContext (anAISContext);
-      TCollection_AsciiString aTitle = TCollection_AsciiString("3D View - ") + theViewName + "(*)";
+  Handle(AIS_InteractiveContext) anAISContext = FindContextByView(aView);
+  if (!anAISContext.IsNull())
+  {
+    if (!ViewerTest::CurrentView().IsNull())
+    {
+      TCollection_AsciiString aTitle("3D View - ");
+      aTitle = aTitle + ViewerTest_myViews.Find2 (ViewerTest::CurrentView());
       SetWindowTitle (ViewerTest::CurrentView()->Window(), aTitle.ToCString());
+    }
+
+    ViewerTest::CurrentView (aView);
+    ViewerTest::SetAISContext (anAISContext);
+    TCollection_AsciiString aTitle = TCollection_AsciiString("3D View - ") + theViewName + "(*)";
+    SetWindowTitle (ViewerTest::CurrentView()->Window(), aTitle.ToCString());
 #if defined(_WIN32)
-      VT_GetWindow() = Handle(WNT_Window)::DownCast(ViewerTest::CurrentView()->Window());
+    VT_GetWindow() = Handle(WNT_Window)::DownCast(ViewerTest::CurrentView()->Window());
 #elif defined(__APPLE__) && !defined(MACOSX_USE_GLX)
-      VT_GetWindow() = Handle(Cocoa_Window)::DownCast(ViewerTest::CurrentView()->Window());
+    VT_GetWindow() = Handle(Cocoa_Window)::DownCast(ViewerTest::CurrentView()->Window());
 #else
-      VT_GetWindow() = Handle(Xw_Window)::DownCast(ViewerTest::CurrentView()->Window());
+    VT_GetWindow() = Handle(Xw_Window)::DownCast(ViewerTest::CurrentView()->Window());
 #endif
-      SetDisplayConnection(ViewerTest::CurrentView()->Viewer()->Driver()->GetDisplayConnection());
+    SetDisplayConnection(ViewerTest::CurrentView()->Viewer()->Driver()->GetDisplayConnection());
+    if (theToUpdate)
+    {
       ViewerTest::CurrentView()->Redraw();
     }
   }
@@ -1348,16 +1350,8 @@ void ViewerTest::RemoveView (const TCollection_AsciiString& theViewName, const S
     }
     else
     {
-      Handle(V3d_View) anEmptyView;
-#if defined(_WIN32) || defined(__WIN32__)
-      Handle(WNT_Window) anEmptyWindow;
-#elif defined(__APPLE__) && !defined(MACOSX_USE_GLX)
-      Handle(Cocoa_Window) anEmptyWindow;
-#else
-      Handle(Xw_Window) anEmptyWindow;
-#endif
-      VT_GetWindow() = anEmptyWindow;
-      ViewerTest::CurrentView (anEmptyView);
+      VT_GetWindow().Nullify();
+      ViewerTest::CurrentView (Handle(V3d_View)());
       if (isContextRemoved)
       {
         Handle(AIS_InteractiveContext) anEmptyContext;
@@ -1490,45 +1484,64 @@ static int VClose (Draw_Interpretor& /*theDi*/,
 
 static int VActivate (Draw_Interpretor& theDi, Standard_Integer theArgsNb, const char** theArgVec)
 {
-  if (theArgsNb > 2)
-  {
-    theDi << theArgVec[0] << ": wrong number of command arguments.\n"
-    << "Usage: " << theArgVec[0] << " ViewID\n";
-    return 1;
-  }
-  if(theArgsNb == 1)
+  if (theArgsNb == 1)
   {
     theDi.Eval("vviewlist");
     return 0;
   }
 
-  TCollection_AsciiString aNameString(theArgVec[1]);
-  if ( strcasecmp( aNameString.ToCString(), "NONE" ) == 0 )
+  TCollection_AsciiString aNameString;
+  Standard_Boolean toUpdate = Standard_True;
+  Standard_Boolean toActivate = Standard_True;
+  for (Standard_Integer anArgIter = 1; anArgIter < theArgsNb; ++anArgIter)
   {
-    TCollection_AsciiString aTitle("3D View - ");
-    aTitle = aTitle + ViewerTest_myViews.Find2(ViewerTest::CurrentView());
-    SetWindowTitle (ViewerTest::CurrentView()->Window(), aTitle.ToCString());
-    Handle(V3d_View) anEmptyView;
-#if defined(_WIN32) || defined(__WIN32__)
-    Handle(WNT_Window) anEmptyWindow;
-#elif defined(__APPLE__) && !defined(MACOSX_USE_GLX)
-    Handle(Cocoa_Window) anEmptyWindow;
-#else
-    Handle(Xw_Window) anEmptyWindow;
-#endif
-    VT_GetWindow() = anEmptyWindow;
-    ViewerTest::CurrentView (anEmptyView);
-    ViewerTest::ResetEventManager();
-    theDi << theArgVec[0] << ": all views are inactive\n";
-    return 0;
+    TCollection_AsciiString anArg (theArgVec[anArgIter]);
+    anArg.LowerCase();
+    if (toUpdate
+     && anArg == "-noupdate")
+    {
+      toUpdate = Standard_False;
+    }
+    else if (toActivate
+          && aNameString.IsEmpty()
+          && anArg == "none")
+    {
+      TCollection_AsciiString aTitle("3D View - ");
+      aTitle = aTitle + ViewerTest_myViews.Find2(ViewerTest::CurrentView());
+      SetWindowTitle (ViewerTest::CurrentView()->Window(), aTitle.ToCString());
+      VT_GetWindow().Nullify();
+      ViewerTest::CurrentView (Handle(V3d_View)());
+      ViewerTest::ResetEventManager();
+      theDi << theArgVec[0] << ": all views are inactive\n";
+      toActivate = Standard_False;
+    }
+    else if (toActivate
+          && aNameString.IsEmpty())
+    {
+      aNameString = theArgVec[anArgIter];
+    }
+    else
+    {
+      std::cout << "Syntax error at '" << theArgVec[anArgIter] << "'\n";
+      return 1;
+    }
   }
 
-  ViewerTest_Names aViewNames(aNameString);
+  if (!toActivate)
+  {
+    return 0;
+  }
+  else if (aNameString.IsEmpty())
+  {
+    std::cout << "Syntax error: wrong number of arguments\n";
+    return 1;
+  }
 
   // Check if this view exists in the viewer with the driver
+  ViewerTest_Names aViewNames (aNameString);
   if (!ViewerTest_myViews.IsBound1(aViewNames.GetViewName()))
   {
-    theDi << "Wrong view name\n";
+    theDi << "Syntax error: wrong view name '" << aNameString << "'\n";
     return 1;
   }
 
@@ -1539,7 +1552,7 @@ static int VActivate (Draw_Interpretor& theDi, Standard_Integer theArgsNb, const
     return 0;
   }
 
-  ActivateView (aViewNames.GetViewName());
+  ActivateView (aViewNames.GetViewName(), toUpdate);
   return 0;
 }
 
@@ -5556,11 +5569,39 @@ static int VFps (Draw_Interpretor& theDI,
     return 1;
   }
 
-  Standard_Integer aFramesNb = (theArgNb > 1) ? Draw::Atoi(theArgVec[1]) : 100;
-  if (aFramesNb <= 0)
+  Standard_Integer aFramesNb = -1;
+  Standard_Real aDuration = -1.0;
+  for (Standard_Integer anArgIter = 1; anArgIter < theArgNb; ++anArgIter)
   {
-    std::cerr << "Incorrect arguments!\n";
-    return 1;
+    TCollection_AsciiString anArg (theArgVec[anArgIter]);
+    anArg.LowerCase();
+    if (aDuration < 0.0
+     && anArgIter + 1 < theArgNb
+     && (anArg == "-duration"
+      || anArg == "-dur"
+      || anArg == "-time"))
+    {
+      aDuration = Draw::Atof (theArgVec[++anArgIter]);
+    }
+    else if (aFramesNb < 0
+          && anArg.IsIntegerValue())
+    {
+      aFramesNb = anArg.IntegerValue();
+      if (aFramesNb <= 0)
+      {
+        std::cerr << "Syntax error at '" << anArg << "'\n";
+        return 1;
+      }
+    }
+    else
+    {
+      std::cerr << "Syntax error at '" << anArg << "'\n";
+      return 1;
+    }
+  }
+  if (aFramesNb < 0 && aDuration < 0.0)
+  {
+    aFramesNb = 100;
   }
 
   // the time is meaningless for first call
@@ -5570,35 +5611,39 @@ static int VFps (Draw_Interpretor& theDI,
   // redraw view in loop to estimate average values
   OSD_Timer aTimer;
   aTimer.Start();
-  for (Standard_Integer anInter = 0; anInter < aFramesNb; ++anInter)
+  Standard_Integer aFrameIter = 1;
+  for (;; ++aFrameIter)
   {
     aView->Redraw();
+    if ((aFramesNb > 0
+      && aFrameIter >= aFramesNb)
+     || (aDuration > 0.0
+      && aTimer.ElapsedTime() >= aDuration))
+    {
+      break;
+    }
   }
   aTimer.Stop();
   Standard_Real aCpu;
   const Standard_Real aTime = aTimer.ElapsedTime();
   aTimer.OSD_Chronometer::Show (aCpu);
 
-  const Standard_Real aFpsAver = Standard_Real(aFramesNb) / aTime;
-  const Standard_Real aCpuAver = aCpu / Standard_Real(aFramesNb);
+  const Standard_Real aFpsAver = Standard_Real(aFrameIter) / aTime;
+  const Standard_Real aCpuAver = aCpu / Standard_Real(aFrameIter);
 
   // return statistics
   theDI << "FPS: " << aFpsAver << "\n"
         << "CPU: " << (1000.0 * aCpuAver) << " msec\n";
 
   // compute additional statistics in ray-tracing mode
-  Graphic3d_RenderingParams& aParams = aView->ChangeRenderingParams();
-
+  const Graphic3d_RenderingParams& aParams = aView->RenderingParams();
   if (aParams.Method == Graphic3d_RM_RAYTRACING)
   {
-    Standard_Integer aSizeX;
-    Standard_Integer aSizeY;
-
-    aView->Window()->Size (aSizeX, aSizeY);
+    Graphic3d_Vec2i aWinSize (0, 0);
+    aView->Window()->Size (aWinSize.x(), aWinSize.y());
 
     // 1 shadow ray and 1 secondary ray pew each bounce
-    const Standard_Real aMRays = aSizeX * aSizeY * aFpsAver * aParams.RaytracingDepth * 2 / 1.0e6f;
-
+    const Standard_Real aMRays = aWinSize.x() * aWinSize.y() * aFpsAver * aParams.RaytracingDepth * 2 / 1.0e6f;
     theDI << "MRays/sec (upper bound): " << aMRays << "\n";
   }
 
@@ -10797,6 +10842,22 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
       }
       aParams.AdaptiveScreenSampling = toEnable;
     }
+    else if (aFlag == "-issatomic")
+    {
+      if (toPrint)
+      {
+        theDI << (aParams.AdaptiveScreenSamplingAtomic ? "on" : "off") << " ";
+        continue;
+      }
+
+      Standard_Boolean toEnable = Standard_True;
+      if (++anArgIter < theArgNb
+      && !ViewerTest::ParseOnOff (theArgVec[anArgIter], toEnable))
+      {
+        --anArgIter;
+      }
+      aParams.AdaptiveScreenSamplingAtomic = toEnable;
+    }
     else if (aFlag == "-issd")
     {
       if (toPrint)
@@ -12279,7 +12340,7 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
     " the current context is not removed.",
     __FILE__,VClose,group);
   theCommands.Add("vactivate" ,
-    "view_id"
+    "vactivate view_id [-noUpdate]"
     " - activates view(viewer window) defined by its view_id",
     __FILE__,VActivate,group);
   theCommands.Add("vviewlist",
@@ -12492,7 +12553,7 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
     "\n\t\t: Converts the given coordinates to window/view/model space.",
     __FILE__, VConvert, group);
   theCommands.Add ("vfps",
-    "vfps [framesNb=100] : estimate average frame rate for active view",
+    "vfps [framesNb=100] [-duration seconds] : estimate average frame rate for active view",
     __FILE__, VFps, group);
   theCommands.Add ("vgldebug",
             "vgldebug [-sync {0|1}] [-debug {0|1}] [-glslWarn {0|1}]"
index b727b1f..7d0eb93 100644 (file)
@@ -37,7 +37,8 @@
 @interface Cocoa_WindowController : NSObject <NSWindowDelegate>
 @end
 
-extern void ActivateView (const TCollection_AsciiString& theViewName);
+extern void ActivateView (const TCollection_AsciiString& theViewName,
+                          Standard_Boolean theToUpdate = Standard_True);
 extern void VT_ProcessExpose();
 extern void VT_ProcessConfigure();
 extern void VT_ProcessKeyPress (const char* theBuffer);