#include <OpenGl_Context.hxx>
#include <OpenGl_TileSampler.hxx>
+#include <Graphic3d_RenderingParams.hxx>
#include <TCollection_ExtendedString.hxx>
-namespace
-{
- //! Scale factor for estimating visual error.
- static const float THE_SCALE_FACTOR = 1.0f / 1e6f;
-}
-
//=======================================================================
//function : OpenGl_TileSampler
//purpose :
//=======================================================================
OpenGl_TileSampler::OpenGl_TileSampler()
-: mySample (0),
- mySizeX (0),
- mySizeY (0),
- myTilesX (0),
- myTilesY (0)
+: myLastSample (0),
+ myScaleFactor(1.0f),
+ myTileSize (0),
+ myViewSize (0, 0)
{
mySampler.initFaure();
}
//function : GrabVarianceMap
//purpose :
//=======================================================================
-void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theContext)
+void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theContext,
+ const Handle(OpenGl_Texture)& theTexture)
{
-#if !defined(GL_ES_VERSION_2_0)
- std::vector<GLint> aRawData (NbTiles(), 0);
+ if (theTexture.IsNull())
+ {
+ return;
+ }
- theContext->core11fwd->glGetTexImage (GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_INT, &aRawData.front());
+ myVarianceRaw.Init (0);
+#if !defined(GL_ES_VERSION_2_0)
+ theTexture->Bind (theContext);
+ theContext->core11fwd->glPixelStorei (GL_PACK_ALIGNMENT, 1);
+ theContext->core11fwd->glPixelStorei (GL_PACK_ROW_LENGTH, 0);
+ theContext->core11fwd->glGetTexImage (GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_INT, myVarianceRaw.ChangeData());
const GLenum anErr = theContext->core11fwd->glGetError();
+ theTexture->Unbind (theContext);
if (anErr != GL_NO_ERROR)
{
theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
"Error! Failed to fetch visual error map from the GPU");
+ return;
}
- else
- {
- for (int aTileIdx = 0, aNbTiles = NbTiles(); aTileIdx < aNbTiles; ++aTileIdx)
- {
- myVarianceMap[aTileIdx] = aRawData[aTileIdx] * THE_SCALE_FACTOR;
- }
+#else
+ // glGetTexImage() is unavailable on OpenGL ES, FBO + glReadPixels() can be used instead
+ (void )theContext;
+#endif
- for (int aX = 0; aX < myTilesX; ++aX)
+ const float aFactor = 1.0f / myScaleFactor;
+ for (Standard_Size aColIter = 0; aColIter < myVarianceMap.SizeX; ++aColIter)
+ {
+ for (Standard_Size aRowIter = 0; aRowIter < myVarianceMap.SizeY; ++aRowIter)
{
- for (int aY = 0; aY < myTilesY; ++aY)
+ const int aRawValue = myVarianceRaw.Value (aRowIter, aColIter);
+ float& aTile = myVarianceMap.ChangeValue (aRowIter, aColIter);
+ aTile = aFactor * float(aRawValue);
+ aTile *= 1.0f / tileArea ((int )aColIter, (int )aRowIter); // average error over the tile
+ if (aRowIter != 0)
{
- ChangeTile (aX, aY) *= 1.0f / TileArea (aX, aY); // average error over the tile
-
- if (aY > 0)
- {
- ChangeTile (aX, aY) += Tile (aX, aY - 1);
- }
+ aTile += myVarianceMap.Value (aRowIter - 1, aColIter);
}
}
+ }
- myMarginalMap.resize (myTilesX); // build marginal distribution
- for (int aX = 0; aX < myTilesX; ++aX)
+ // build marginal distribution
+ for (Standard_Size aX = 0; aX < myVarianceMap.SizeX; ++aX)
+ {
+ myMarginalMap[aX] = myVarianceMap.Value (myVarianceMap.SizeY - 1, aX);
+ if (aX != 0)
{
- myMarginalMap[aX] = Tile (aX, myTilesY - 1);
-
- if (aX > 0)
- myMarginalMap[aX] += myMarginalMap[aX - 1];
+ myMarginalMap[aX] += myMarginalMap[aX - 1];
}
}
-#else
- // glGetTexImage() is unavailable on OpenGL ES, FBO + glReadPixels() can be used instead
- (void )theContext;
-#endif
}
//=======================================================================
-//function : Sample
+//function : nextTileToSample
//purpose :
//=======================================================================
-void OpenGl_TileSampler::Sample (int& theOffsetX,
- int& theOffsetY)
+Graphic3d_Vec2i OpenGl_TileSampler::nextTileToSample()
{
- int aX = 0;
- int aY = 0;
-
- const float aKsiX = mySampler.sample (0, mySample) * myMarginalMap.back();
- for (; aX < myTilesX - 1; ++aX)
+ Graphic3d_Vec2i aTile (0, 0);
+ const float aKsiX = mySampler.sample (0, myLastSample) * myMarginalMap.back();
+ for (; (size_t )aTile.x() < myMarginalMap.size() - 1; ++aTile.x())
{
- if (aKsiX <= myMarginalMap[aX])
+ if (aKsiX <= myMarginalMap[aTile.x()])
{
break;
}
}
- const float aKsiY = mySampler.sample (1, mySample) * Tile (aX, myTilesY - 1);
- for (; aY < myTilesY - 1; ++aY)
+ const float aKsiY = mySampler.sample (1, myLastSample) * myVarianceMap.Value (myVarianceMap.SizeY - 1, aTile.x());
+ for (; (size_t )aTile.y() < myVarianceMap.SizeY - 1; ++aTile.y())
{
- if (aKsiY <= Tile (aX, aY))
+ if (aKsiY <= myVarianceMap.Value (aTile.y(), aTile.x()))
{
break;
}
}
- theOffsetX = aX * TileSize();
- theOffsetY = aY * TileSize();
-
- ++mySample;
+ ++myLastSample;
+ return aTile;
}
//=======================================================================
//function : SetSize
//purpose :
//=======================================================================
-void OpenGl_TileSampler::SetSize (const int theSizeX,
- const int theSizeY)
+void OpenGl_TileSampler::SetSize (const Graphic3d_RenderingParams& theParams,
+ const Graphic3d_Vec2i& theSize)
{
- if (mySizeX == theSizeX
- && mySizeY == theSizeY)
+ if (theSize.x() <= 0
+ || theSize.y() <= 0)
{
return;
}
- mySizeX = theSizeX;
- mySizeY = theSizeY;
+ myViewSize = theSize;
- myTilesX = static_cast<int> (ceilf (static_cast<float> (mySizeX) / TileSize()));
- myTilesY = static_cast<int> (ceilf (static_cast<float> (mySizeY) / TileSize()));
+ const int aTileSize = Max (theParams.RayTracingTileSize, 1);
+ const int aNbTilesX = Max (1, static_cast<int> (ceilf (static_cast<float> (theSize.x()) / aTileSize)));
+ const int aNbTilesY = Max (1, static_cast<int> (ceilf (static_cast<float> (theSize.y()) / aTileSize)));
+ if (myTileSize != aTileSize
+ || (int )myTiles.SizeX != aNbTilesX
+ || (int )myTiles.SizeY != aNbTilesY)
+ {
+ myTileSize = aTileSize;
+ myScaleFactor = 1.0e6f * (1024.0f / float(myTileSize * myTileSize));
- myVarianceMap.resize (myTilesX * myTilesY);
+ Handle(NCollection_BaseAllocator) anAlloc = NCollection_BaseAllocator::CommonBaseAllocator();
+ myTiles.SetTopDown (true);
+ myTiles.Init (anAlloc, aNbTilesX, aNbTilesY);
+ myTiles.Init (1);
+
+ myVarianceMap.SetTopDown (true);
+ myVarianceMap.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
+ myVarianceMap.Init (0.0f);
+
+ myVarianceRaw.SetTopDown (true);
+ myVarianceRaw.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
+ myVarianceRaw.Init (0);
+
+ myOffsets.SetTopDown (true);
+ myOffsets.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
+ myOffsets.Init (Graphic3d_Vec2i (-1, -1));
+
+ myMarginalMap.resize (myTiles.SizeX);
+ myMarginalMap.assign (myMarginalMap.size(), 0.0f);
+ }
+
+ // calculate a size of compact offsets texture optimal for rendering reduced number of tiles
+ Standard_Integer aNbShunkTilesX = (int )myTiles.SizeX, aNbShunkTilesY = (int )myTiles.SizeY;
+ if (theParams.NbRayTracingTiles > 0)
+ {
+ aNbShunkTilesX = 8;
+ aNbShunkTilesY = 8;
+ for (Standard_Integer anIdx = 0; aNbShunkTilesX * aNbShunkTilesY < theParams.NbRayTracingTiles; ++anIdx)
+ {
+ (anIdx % 2 == 0 ? aNbShunkTilesX : aNbShunkTilesY) <<= 1;
+ }
+ }
+ if ((int )myOffsetsShrunk.SizeX != aNbShunkTilesX
+ || (int )myOffsetsShrunk.SizeY != aNbShunkTilesY)
+ {
+ myOffsetsShrunk.SetTopDown (true);
+ myOffsetsShrunk.Init (myTiles.Allocator(), aNbShunkTilesX, aNbShunkTilesY);
+ myOffsetsShrunk.Init (Graphic3d_Vec2i (-1, -1));
+ }
}
//=======================================================================
-//function : Upload
+//function : UploadOffsets
//purpose :
//=======================================================================
-void OpenGl_TileSampler::Upload (const Handle(OpenGl_Context)& theContext,
- const Handle(OpenGl_Texture)& theTexture,
- const int theNbTilesX,
- const int theNbTilesY,
- const bool theAdaptive)
+bool OpenGl_TileSampler::UploadOffsets (const Handle(OpenGl_Context)& theContext,
+ const Handle(OpenGl_Texture)& theOffsetsTexture,
+ const bool theAdaptive)
{
- if (theTexture.IsNull())
+ if (myTiles.IsEmpty())
{
- return;
+ return false;
}
- const int aNbTilesX = theAdaptive ? theNbTilesX : myTilesX;
- const int aNbTilesY = theAdaptive ? theNbTilesY : myTilesY;
-
- Standard_ASSERT_RAISE (aNbTilesX * aNbTilesY > 0,
- "Error! Number of sampling tiles should be positive");
-
- std::vector<GLint> aData (aNbTilesX * aNbTilesY * 2);
+ myTiles.Init (0);
+ Image_PixMapTypedData<Graphic3d_Vec2i>& anOffsets = theAdaptive ? myOffsetsShrunk : myOffsets;
+ anOffsets.Init (Graphic3d_Vec2i (-1, -1));
+ for (Standard_Size aRowIter = 0; aRowIter < anOffsets.SizeY; ++aRowIter)
+ {
+ for (Standard_Size aColIter = 0; aColIter < anOffsets.SizeX; ++aColIter)
+ {
+ Graphic3d_Vec2i& aRedirectTile = anOffsets.ChangeValue (aRowIter, aColIter);
+ aRedirectTile = theAdaptive ? nextTileToSample() : Graphic3d_Vec2i ((int )aColIter, (int )aRowIter);
+ myTiles.ChangeValue (aRedirectTile.y(), aRedirectTile.x()) += 1;
+ }
+ }
- for (int aX = 0; aX < aNbTilesX; ++aX)
+ bool hasErrors = false;
+ if (!theOffsetsTexture.IsNull())
{
- for (int aY = 0; aY < aNbTilesY; ++aY)
+ if (theOffsetsTexture->SizeX() != (int )anOffsets.SizeX
+ || theOffsetsTexture->SizeY() != (int )anOffsets.SizeY
+ || !theOffsetsTexture->IsValid())
{
- if (!theAdaptive)
+ theOffsetsTexture->Release (theContext.operator->());
+ if (!theOffsetsTexture->Init (theContext, GL_RG32I, GL_RG_INTEGER, GL_INT,
+ (int )anOffsets.SizeX, (int )anOffsets.SizeY, Graphic3d_TOT_2D))
{
- aData[(aY * aNbTilesX + aX) * 2 + 0] = aX * TileSize();
- aData[(aY * aNbTilesX + aX) * 2 + 1] = aY * TileSize();
+ hasErrors = true;
}
- else
+ }
+ if (theOffsetsTexture->IsValid())
+ {
+ theOffsetsTexture->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
+ theContext->core11fwd->glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, (int )anOffsets.SizeX, (int )anOffsets.SizeY, GL_RG_INTEGER, GL_INT, anOffsets.Data());
+ if (theContext->core11fwd->glGetError() != GL_NO_ERROR)
{
- Sample (aData[(aY * aNbTilesX + aX) * 2 + 0],
- aData[(aY * aNbTilesX + aX) * 2 + 1]);
+ hasErrors = true;
+ theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
+ "Error! Failed to upload tile offset map on the GPU");
}
+ theOffsetsTexture->Unbind (theContext);
}
}
-
- theTexture->Bind (theContext);
-
- theContext->core11fwd->glTexImage2D (GL_TEXTURE_2D, 0, GL_RG32I, aNbTilesX, aNbTilesY, 0, GL_RG_INTEGER, GL_UNSIGNED_INT, &aData.front());
-
- if (theContext->core11fwd->glGetError() != GL_NO_ERROR)
- {
- theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
- "Error! Failed to upload tile offset map on the GPU");
- }
+ return !hasErrors;
}