From e084dbbc20b81ede8d400adf41881166845c694c Mon Sep 17 00:00:00 2001 From: kgv Date: Mon, 11 Feb 2019 18:00:35 +0300 Subject: [PATCH] 0030476: Visualization, Path Tracing - Adaptive Screen Sampling leads to unstable results 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. --- src/Graphic3d/Graphic3d_RenderingParams.hxx | 2 + src/Graphic3d/Graphic3d_TypeOfLimit.hxx | 1 + src/OpenGl/OpenGl_Context.cxx | 6 +- src/OpenGl/OpenGl_Context.hxx | 10 +- src/OpenGl/OpenGl_FrameStats.cxx | 2 + src/OpenGl/OpenGl_GraphicDriver.cxx | 2 + src/OpenGl/OpenGl_TileSampler.cxx | 92 +++++++- src/OpenGl/OpenGl_TileSampler.hxx | 43 +++- src/OpenGl/OpenGl_View.hxx | 9 + src/OpenGl/OpenGl_View_Raytrace.cxx | 120 ++++++++-- src/Shaders/Display.fs | 4 +- src/Shaders/RaytraceBase.fs | 13 +- src/Shaders/RaytraceRender.fs | 47 ++-- src/Shaders/Shaders_Display_fs.pxx | 4 +- src/Shaders/Shaders_RaytraceBase_fs.pxx | 13 +- src/Shaders/Shaders_RaytraceRender_fs.pxx | 47 ++-- src/ViewerTest/ViewerTest_ViewerCommands.cxx | 217 +++++++++++------- src/ViewerTest/ViewerTest_ViewerCommands_1.mm | 3 +- 18 files changed, 488 insertions(+), 147 deletions(-) diff --git a/src/Graphic3d/Graphic3d_RenderingParams.hxx b/src/Graphic3d/Graphic3d_RenderingParams.hxx index 9068b46bee..6042d89543 100644 --- a/src/Graphic3d/Graphic3d_RenderingParams.hxx +++ b/src/Graphic3d/Graphic3d_RenderingParams.hxx @@ -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. diff --git a/src/Graphic3d/Graphic3d_TypeOfLimit.hxx b/src/Graphic3d/Graphic3d_TypeOfLimit.hxx index 6c9f208dc2..772b9443d9 100644 --- a/src/Graphic3d/Graphic3d_TypeOfLimit.hxx +++ b/src/Graphic3d/Graphic3d_TypeOfLimit.hxx @@ -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 diff --git a/src/OpenGl/OpenGl_Context.cxx b/src/OpenGl/OpenGl_Context.cxx index 52509f4c15..b83a2ddcd3 100644 --- a/src/OpenGl/OpenGl_Context.cxx +++ b/src/OpenGl/OpenGl_Context.cxx @@ -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) { diff --git a/src/OpenGl/OpenGl_Context.hxx b/src/OpenGl/OpenGl_Context.hxx index 13bf652ba2..f9e94d48a1 100644 --- a/src/OpenGl/OpenGl_Context.hxx +++ b/src/OpenGl/OpenGl_Context.hxx @@ -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 diff --git a/src/OpenGl/OpenGl_FrameStats.cxx b/src/OpenGl/OpenGl_FrameStats.cxx index cb64c5a186..c43b16cce8 100644 --- a/src/OpenGl/OpenGl_FrameStats.cxx +++ b/src/OpenGl/OpenGl_FrameStats.cxx @@ -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 diff --git a/src/OpenGl/OpenGl_GraphicDriver.cxx b/src/OpenGl/OpenGl_GraphicDriver.cxx index ee2ea156c1..6908aa8efb 100644 --- a/src/OpenGl/OpenGl_GraphicDriver.cxx +++ b/src/OpenGl/OpenGl_GraphicDriver.cxx @@ -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 diff --git a/src/OpenGl/OpenGl_TileSampler.cxx b/src/OpenGl/OpenGl_TileSampler.cxx index 9ccd4c9274..c8bbd17619 100644 --- a/src/OpenGl/OpenGl_TileSampler.cxx +++ b/src/OpenGl/OpenGl_TileSampler.cxx @@ -18,6 +18,9 @@ #include #include +// 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& 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& 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 diff --git a/src/OpenGl/OpenGl_TileSampler.hxx b/src/OpenGl/OpenGl_TileSampler.hxx index f9565073ed..875416a84a 100644 --- a/src/OpenGl/OpenGl_TileSampler.hxx +++ b/src/OpenGl/OpenGl_TileSampler.hxx @@ -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& theMap, + const char* theTitle) const; + protected: Image_PixMapTypedData myTiles; //!< number of samples per tile (initially all 1) + Image_PixMapTypedData myTileSamples; //!< number of samples for all pixels within the tile (initially equals to Tile area) Image_PixMapTypedData myVarianceMap; //!< Estimation of visual error per tile Image_PixMapTypedData myVarianceRaw; //!< Estimation of visual error per tile (raw data) Image_PixMapTypedData myOffsets; //!< 2D array of tiles redirecting to another tile diff --git a/src/OpenGl/OpenGl_View.hxx b/src/OpenGl/OpenGl_View.hxx index cc9a56490e..41552217b1 100644 --- a/src/OpenGl/OpenGl_View.hxx +++ b/src/OpenGl/OpenGl_View.hxx @@ -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; diff --git a/src/OpenGl/OpenGl_View_Raytrace.cxx b/src/OpenGl/OpenGl_View_Raytrace.cxx index 12177f3537..e2f15e6f13 100644 --- a/src/OpenGl/OpenGl_View_Raytrace.cxx +++ b/src/OpenGl/OpenGl_View_Raytrace.cxx @@ -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 (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 (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); diff --git a/src/Shaders/Display.fs b/src/Shaders/Display.fs index ecb82cc08e..8067fb0f38 100644 --- a/src/Shaders/Display.fs +++ b/src/Shaders/Display.fs @@ -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; diff --git a/src/Shaders/RaytraceBase.fs b/src/Shaders/RaytraceBase.fs index f9b9637582..261327c019 100644 --- a/src/Shaders/RaytraceBase.fs +++ b/src/Shaders/RaytraceBase.fs @@ -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); diff --git a/src/Shaders/RaytraceRender.fs b/src/Shaders/RaytraceRender.fs index bc626907fa..83bbb7ec62 100644 --- a/src/Shaders/RaytraceRender.fs +++ b/src/Shaders/RaytraceRender.fs @@ -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 diff --git a/src/Shaders/Shaders_Display_fs.pxx b/src/Shaders/Shaders_Display_fs.pxx index 281fdcc00f..59193993c0 100644 --- a/src/Shaders/Shaders_Display_fs.pxx +++ b/src/Shaders/Shaders_Display_fs.pxx @@ -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" diff --git a/src/Shaders/Shaders_RaytraceBase_fs.pxx b/src/Shaders/Shaders_RaytraceBase_fs.pxx index eeab5a98e6..43c36ba64a 100644 --- a/src/Shaders/Shaders_RaytraceBase_fs.pxx +++ b/src/Shaders/Shaders_RaytraceBase_fs.pxx @@ -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" diff --git a/src/Shaders/Shaders_RaytraceRender_fs.pxx b/src/Shaders/Shaders_RaytraceRender_fs.pxx index 1fe09ac7a4..1d2f6dba24 100644 --- a/src/Shaders/Shaders_RaytraceRender_fs.pxx +++ b/src/Shaders/Shaders_RaytraceRender_fs.pxx @@ -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" diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx index bc12b99e13..4fae1a9d23 100644 --- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx +++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx @@ -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}]" diff --git a/src/ViewerTest/ViewerTest_ViewerCommands_1.mm b/src/ViewerTest/ViewerTest_ViewerCommands_1.mm index b727b1f6cb..7d0eb93ad6 100644 --- a/src/ViewerTest/ViewerTest_ViewerCommands_1.mm +++ b/src/ViewerTest/ViewerTest_ViewerCommands_1.mm @@ -37,7 +37,8 @@ @interface Cocoa_WindowController : NSObject @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);