]> OCCT Git - occt.git/commitdiff
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 9068b46bee6f0ea5e38c1ff88af2d0629f36e540..6042d89543a029143831dd2ebcf56dd30acf2360 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 6c9f208dc2e32f4446a827be7413a8f96f3f45f2..772b9443d932ff5112f60ddeadcbf83441cf5463 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 52509f4c150e8488fec4058cb6f720b9091b3f06..b83a2ddcd3611e512b99d4b425cb3b692197528b 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 13bf652ba2d1db3ce3306261981269b9e508d57a..f9e94d48a1d4ef05a53fe55706974edb0602de6a 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 cb64c5a186005e6031962ff3209453283eec5a1e..c43b16cce8f63c878ad1e810fe09ac75a283d6b9 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 ee2ea156c1fbbb7ee42cfdfac40ec28430046f25..6908aa8efb862e424ea7ac259bf87848f37c8a40 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 9ccd4c9274c7bb2354b12645139d38263947321d..c8bbd176192f2d8141d168a338fb05b184201162 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 f9565073ed3dd82349f4889e51ebc5130b7bd757..875416a84a273cb8b021157e2ae2e8e9f5f6713f 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 cc9a56490e259f34b4ed729b125b9251983f05e1..41552217b1abf6704f21fba643c160da8d957b9b 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 12177f3537ae40ad6d9ee2539e8c20a08d3a973f..e2f15e6f13e28375608edebf2c96f353b21f1f51 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 ecb82cc08efe47a2ee88434c3ddd8cdffdea6314..8067fb0f38b7dd0bcc66ea7fa5955d1f14dd3b5b 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 f9b963758259aae33ba3cdcfdcf2d3545b03f941..261327c019c2e1de466627fcdd397dbf99686f5a 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 bc626907fa44d3f486a2810171f54101f8075b05..83bbb7ec62bfcf304476f4e6896b311911d9cc88 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 281fdcc00f1972c2b96560d2d5ab50c395474c14..59193993c04d7ce5bf0963ab874ec54646fd5a01 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 eeab5a98e6d425baba0e5213d110896d0f96c672..43c36ba64a1fafb1be8db015b1b25d426b8bfaa3 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 1fe09ac7a4c250406f52f61dca9f64ec47f5f859..1d2f6dba24f61674a21249c1fb2e80f5659a8435 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 bc12b99e13788cdba0b2b9515b285b823fac7bbf..4fae1a9d2372a5fed2cec97e94f085468f029cb8 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 b727b1f6cbb4e479c0098fa9ba36fd13b04ce101..7d0eb93ad6ea3e18f98dfeba0acf0c8ff1d03e5a 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);