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);