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");
+ }
}
}
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())
// 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)");
}
}
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] =
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);
{
myRaytraceOutputTexture[aViewIter] = new OpenGl_Texture();
myRaytraceVisualErrorTexture[aViewIter] = new OpenGl_Texture();
+ myRaytraceTileSamplesTexture[aViewIter] = new OpenGl_Texture();
myRaytraceTileOffsetsTexture[aViewIter] = new OpenGl_Texture();
}
&& 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;
// 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
{
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
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);
// 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
// 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);
}
{
// 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);