1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-08-14 13:30:48 +03:00

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.
This commit is contained in:
kgv
2019-02-11 18:00:35 +03:00
committed by apn
parent 66d1cdc65d
commit e084dbbc20
18 changed files with 488 additions and 147 deletions

View File

@@ -1136,10 +1136,14 @@ TCollection_AsciiString OpenGl_View::generateShaderPrefix (const Handle(OpenGl_C
if (myRaytraceParameters.AdaptiveScreenSampling) // adaptive screen sampling requested
{
// to activate the feature we need OpenGL 4.4 and GL_NV_shader_atomic_float extension
if (theGlContext->IsGlGreaterEqual (4, 4) && theGlContext->CheckExtension ("GL_NV_shader_atomic_float"))
if (theGlContext->IsGlGreaterEqual (4, 4))
{
aPrefixString += TCollection_AsciiString ("\n#define ADAPTIVE_SAMPLING");
if (myRaytraceParameters.AdaptiveScreenSamplingAtomic
&& theGlContext->CheckExtension ("GL_NV_shader_atomic_float"))
{
aPrefixString += TCollection_AsciiString ("\n#define ADAPTIVE_SAMPLING_ATOMIC");
}
}
}
@@ -1387,9 +1391,11 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Standard_Integer theS
aToRebuildShaders = Standard_True;
}
if (myRenderParams.AdaptiveScreenSampling != myRaytraceParameters.AdaptiveScreenSampling)
if (myRenderParams.AdaptiveScreenSampling != myRaytraceParameters.AdaptiveScreenSampling
|| myRenderParams.AdaptiveScreenSamplingAtomic != myRaytraceParameters.AdaptiveScreenSamplingAtomic)
{
myRaytraceParameters.AdaptiveScreenSampling = myRenderParams.AdaptiveScreenSampling;
myRaytraceParameters.AdaptiveScreenSampling = myRenderParams.AdaptiveScreenSampling;
myRaytraceParameters.AdaptiveScreenSamplingAtomic = myRenderParams.AdaptiveScreenSamplingAtomic;
if (myRenderParams.AdaptiveScreenSampling) // adaptive sampling was requested
{
if (!theGlContext->HasRayTracingAdaptiveSampling())
@@ -1397,7 +1403,15 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Standard_Integer theS
// disable the feature if it is not supported
myRaytraceParameters.AdaptiveScreenSampling = myRenderParams.AdaptiveScreenSampling = Standard_False;
theGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW,
"Adaptive sampling not supported (OpenGL 4.4 or GL_NV_shader_atomic_float is missing)");
"Adaptive sampling is not supported (OpenGL 4.4 is missing)");
}
else if (myRaytraceParameters.AdaptiveScreenSamplingAtomic
&& !theGlContext->HasRayTracingAdaptiveSamplingAtomic())
{
// disable the feature if it is not supported
myRaytraceParameters.AdaptiveScreenSamplingAtomic = myRenderParams.AdaptiveScreenSamplingAtomic = Standard_False;
theGlContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_PORTABILITY, 0, GL_DEBUG_SEVERITY_LOW,
"Atomic adaptive sampling is not supported (GL_NV_shader_atomic_float is missing)");
}
}
@@ -1726,6 +1740,8 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Standard_Integer theS
myUniformLocations[anIndex][OpenGl_RT_uRenderImage] =
aShaderProgram->GetUniformLocation (theGlContext, "uRenderImage");
myUniformLocations[anIndex][OpenGl_RT_uTilesImage] =
aShaderProgram->GetUniformLocation (theGlContext, "uTilesImage");
myUniformLocations[anIndex][OpenGl_RT_uOffsetImage] =
aShaderProgram->GetUniformLocation (theGlContext, "uOffsetImage");
myUniformLocations[anIndex][OpenGl_RT_uTileSize] =
@@ -1814,6 +1830,8 @@ void OpenGl_View::releaseRaytraceResources (const Handle(OpenGl_Context)& theGlC
nullifyResource (theGlContext, myRaytraceTileOffsetsTexture[1]);
nullifyResource (theGlContext, myRaytraceVisualErrorTexture[0]);
nullifyResource (theGlContext, myRaytraceVisualErrorTexture[1]);
nullifyResource (theGlContext, myRaytraceTileSamplesTexture[0]);
nullifyResource (theGlContext, myRaytraceTileSamplesTexture[1]);
nullifyResource (theGlContext, mySceneNodeInfoTexture);
nullifyResource (theGlContext, mySceneMinPointTexture);
@@ -1874,6 +1892,7 @@ Standard_Boolean OpenGl_View::updateRaytraceBuffers (const Standard_Integer
{
myRaytraceOutputTexture[aViewIter] = new OpenGl_Texture();
myRaytraceVisualErrorTexture[aViewIter] = new OpenGl_Texture();
myRaytraceTileSamplesTexture[aViewIter] = new OpenGl_Texture();
myRaytraceTileOffsetsTexture[aViewIter] = new OpenGl_Texture();
}
@@ -1895,7 +1914,15 @@ Standard_Boolean OpenGl_View::updateRaytraceBuffers (const Standard_Integer
&& myRaytraceVisualErrorTexture[aViewIter]->SizeX() == myTileSampler.NbTilesX()
&& myRaytraceVisualErrorTexture[aViewIter]->SizeY() == myTileSampler.NbTilesY())
{
continue;
if (myRaytraceParameters.AdaptiveScreenSamplingAtomic)
{
continue; // offsets texture is dynamically resized
}
else if (myRaytraceTileSamplesTexture[aViewIter]->SizeX() == myTileSampler.NbTilesX()
&& myRaytraceTileSamplesTexture[aViewIter]->SizeY() == myTileSampler.NbTilesY())
{
continue;
}
}
myAccumFrames = 0;
@@ -1913,8 +1940,14 @@ Standard_Boolean OpenGl_View::updateRaytraceBuffers (const Standard_Integer
// workaround for some NVIDIA drivers
myRaytraceVisualErrorTexture[aViewIter]->Release (theGlContext.operator->());
myRaytraceTileSamplesTexture[aViewIter]->Release (theGlContext.operator->());
myRaytraceVisualErrorTexture[aViewIter]->Init (theGlContext, GL_R32I, GL_RED_INTEGER, GL_INT,
myTileSampler.NbTilesX(), myTileSampler.NbTilesY(), Graphic3d_TOT_2D);
if (!myRaytraceParameters.AdaptiveScreenSamplingAtomic)
{
myRaytraceTileSamplesTexture[aViewIter]->Init (theGlContext, GL_R32I, GL_RED_INTEGER, GL_INT,
myTileSampler.NbTilesX(), myTileSampler.NbTilesY(), Graphic3d_TOT_2D);
}
}
else // non-adaptive mode
{
@@ -2684,8 +2717,16 @@ void OpenGl_View::bindRaytraceTextures (const Handle(OpenGl_Context)& theGlConte
myRaytraceOutputTexture[theStereoView]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32F);
theGlContext->core42->glBindImageTexture (OpenGl_RT_VisualErrorImage,
myRaytraceVisualErrorTexture[theStereoView]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32I);
theGlContext->core42->glBindImageTexture (OpenGl_RT_TileOffsetsImage,
myRaytraceTileOffsetsTexture[theStereoView]->TextureId(), 0, GL_TRUE, 0, GL_READ_ONLY, GL_RG32I);
if (myRaytraceParameters.AdaptiveScreenSamplingAtomic)
{
theGlContext->core42->glBindImageTexture (OpenGl_RT_TileOffsetsImage,
myRaytraceTileOffsetsTexture[theStereoView]->TextureId(), 0, GL_TRUE, 0, GL_READ_ONLY, GL_RG32I);
}
else
{
theGlContext->core42->glBindImageTexture (OpenGl_RT_TileSamplesImage,
myRaytraceTileSamplesTexture[theStereoView]->TextureId(), 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32I);
}
#else
(void )theStereoView;
#endif
@@ -2911,7 +2952,14 @@ Standard_Boolean OpenGl_View::runPathtrace (const Standard_Integer
myTileSampler.Reset(); // reset tile sampler to its initial state
// Adaptive sampling is starting at the second frame
myTileSampler.UploadOffsets (theGlContext, myRaytraceTileOffsetsTexture[aFBOIdx], false);
if (myRaytraceParameters.AdaptiveScreenSamplingAtomic)
{
myTileSampler.UploadOffsets (theGlContext, myRaytraceTileOffsetsTexture[aFBOIdx], false);
}
else
{
myTileSampler.UploadSamples (theGlContext, myRaytraceTileSamplesTexture[aFBOIdx], false);
}
#if !defined(GL_ES_VERSION_2_0)
theGlContext->core44->glClearTexImage (myRaytraceOutputTexture[aFBOIdx]->TextureId(), 0, GL_RED, GL_FLOAT, NULL);
@@ -2932,24 +2980,19 @@ Standard_Boolean OpenGl_View::runPathtrace (const Standard_Integer
// Set frame accumulation weight
myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uAccumSamples], myAccumFrames);
// Set random number generator seed
if (myAccumFrames == 0)
{
myRNG.SetSeed(); // start RNG from beginning
}
myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uFrameRndSeed], static_cast<Standard_Integer> (myRNG.NextInt() >> 2));
// Set image uniforms for render program
if (myRaytraceParameters.AdaptiveScreenSampling)
{
myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uRenderImage], OpenGl_RT_OutputImage);
myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uTilesImage], OpenGl_RT_TileSamplesImage);
myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uOffsetImage], OpenGl_RT_TileOffsetsImage);
myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uTileSize], myTileSampler.TileSize());
}
const Handle(OpenGl_FrameBuffer)& aRenderImageFramebuffer = myAccumFrames % 2 ? myRaytraceFBO1[aFBOIdx] : myRaytraceFBO2[aFBOIdx];
aRenderImageFramebuffer->BindBuffer (theGlContext);
if (myRaytraceParameters.AdaptiveScreenSampling)
if (myRaytraceParameters.AdaptiveScreenSampling
&& myRaytraceParameters.AdaptiveScreenSamplingAtomic)
{
// extend viewport here, so that tiles at boundaries (cut tile size by target rendering viewport)
// redirected to inner tiles (full tile size) are drawn entirely
@@ -2959,11 +3002,41 @@ Standard_Boolean OpenGl_View::runPathtrace (const Standard_Integer
// Generate for the given RNG seed
glDisable (GL_DEPTH_TEST);
theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6);
// Adaptive Screen Sampling computes the same overall amount of samples per frame redraw as normal Path Tracing,
// but distributes them unequally across pixels (grouped in tiles), so that some pixels do not receive new samples at all.
//
// Offsets map (redirecting currently rendered tile to another tile) allows performing Adaptive Screen Sampling in single pass,
// but current implementation relies on atomic float operations (AdaptiveScreenSamplingAtomic) for this.
// So that when atomic floats are not supported by GPU, multi-pass rendering is used instead.
//
// Single-pass rendering is more optimal due to smaller amount of draw calls,
// memory synchronization barriers, discarding most of the fragments and bad parallelization in case of very small amount of tiles requiring more samples.
// However, atomic operations on float values still produces different result (close, but not bit exact) making non-regression testing not robust.
// It should be possible following single-pass rendering approach but using extra accumulation buffer and resolving pass as possible improvement.
const int aNbPasses = myRaytraceParameters.AdaptiveScreenSampling
&& !myRaytraceParameters.AdaptiveScreenSamplingAtomic
? myTileSampler.MaxTileSamples()
: 1;
if (myAccumFrames == 0)
{
myRNG.SetSeed(); // start RNG from beginning
}
for (int aPassIter = 0; aPassIter < aNbPasses; ++aPassIter)
{
myRaytraceProgram->SetUniform (theGlContext, myUniformLocations[0][OpenGl_RT_uFrameRndSeed], static_cast<Standard_Integer> (myRNG.NextInt() >> 2));
theGlContext->core20fwd->glDrawArrays (GL_TRIANGLES, 0, 6);
if (myRaytraceParameters.AdaptiveScreenSampling)
{
#if !defined(GL_ES_VERSION_2_0)
theGlContext->core44->glMemoryBarrier (GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
#endif
}
}
aRenderImageFramebuffer->UnbindBuffer (theGlContext);
if (myRaytraceParameters.AdaptiveScreenSampling)
if (myRaytraceParameters.AdaptiveScreenSampling
&& myRaytraceParameters.AdaptiveScreenSamplingAtomic)
{
glViewport (0, 0, theSizeX, theSizeY);
}
@@ -3026,7 +3099,14 @@ Standard_Boolean OpenGl_View::runPathtraceOut (const Graphic3d_Camera::Projectio
{
// Download visual error map from the GPU and build adjusted tile offsets for optimal image sampling
myTileSampler.GrabVarianceMap (theGlContext, myRaytraceVisualErrorTexture[aFBOIdx]);
myTileSampler.UploadOffsets (theGlContext, myRaytraceTileOffsetsTexture[aFBOIdx], myAccumFrames != 0);
if (myRaytraceParameters.AdaptiveScreenSamplingAtomic)
{
myTileSampler.UploadOffsets (theGlContext, myRaytraceTileOffsetsTexture[aFBOIdx], myAccumFrames != 0);
}
else
{
myTileSampler.UploadSamples (theGlContext, myRaytraceTileSamplesTexture[aFBOIdx], myAccumFrames != 0);
}
}
unbindRaytraceTextures (theGlContext);