1 // Created on: 2016-06-16
2 // Created by: Denis BOGOLEPOV & Danila ULYANOV
3 // Copyright (c) 2016 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
16 #include <OpenGl_Context.hxx>
17 #include <OpenGl_TileSampler.hxx>
18 #include <Graphic3d_RenderingParams.hxx>
19 #include <TCollection_ExtendedString.hxx>
21 // define to debug algorithm values
22 //#define RAY_TRACE_PRINT_DEBUG_INFO
24 //=======================================================================
25 //function : OpenGl_TileSampler
27 //=======================================================================
28 OpenGl_TileSampler::OpenGl_TileSampler()
37 //=======================================================================
38 //function : GrabVarianceMap
40 //=======================================================================
41 void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theContext,
42 const Handle(OpenGl_Texture)& theTexture)
44 if (theTexture.IsNull())
49 myVarianceRaw.Init (0);
51 theTexture->Bind (theContext);
52 theContext->core11fwd->glPixelStorei (GL_PACK_ALIGNMENT, 1);
53 theContext->core11fwd->glPixelStorei (GL_PACK_ROW_LENGTH, 0);
54 theContext->core11fwd->glGetTexImage (GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_INT, myVarianceRaw.ChangeData());
55 const GLenum anErr = theContext->core11fwd->glGetError();
56 theTexture->Unbind (theContext);
57 if (anErr != GL_NO_ERROR)
59 theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
60 TCollection_AsciiString ("Error! Failed to fetch visual error map from the GPU ") + OpenGl_Context::FormatGlError (anErr));
64 const float aFactor = 1.0f / myScaleFactor;
65 for (Standard_Size aColIter = 0; aColIter < myVarianceMap.SizeX; ++aColIter)
67 for (Standard_Size aRowIter = 0; aRowIter < myVarianceMap.SizeY; ++aRowIter)
69 const int aRawValue = myVarianceRaw.Value (aRowIter, aColIter);
70 Standard_RangeError_Raise_if (aRawValue < 0, "Internal Error: signed integer overflow within OpenGl_TileSampler");
72 float& aTile = myVarianceMap.ChangeValue (aRowIter, aColIter);
73 aTile = aFactor * float(aRawValue);
74 aTile *= 1.0f / tileArea ((int )aColIter, (int )aRowIter); // average error over the tile
77 aTile += myVarianceMap.Value (aRowIter - 1, aColIter);
82 // build marginal distribution
83 for (Standard_Size aX = 0; aX < myVarianceMap.SizeX; ++aX)
85 myMarginalMap[aX] = myVarianceMap.Value (myVarianceMap.SizeY - 1, aX);
88 myMarginalMap[aX] += myMarginalMap[aX - 1];
92 #ifdef RAY_TRACE_PRINT_DEBUG_INFO
93 dumpMap (std::cerr, myVarianceRaw, "OpenGl_TileSampler, Variance map");
97 //=======================================================================
100 //=======================================================================
101 void OpenGl_TileSampler::dumpMap (std::ostream& theStream,
102 const Image_PixMapTypedData<int>& theMap,
103 const char* theTitle) const
105 theStream << theTitle << " " << theMap.SizeX << "x" << theMap.SizeY << " (tile " << myTileSize << "x" << myTileSize << ")" << ":\n";
106 for (Standard_Size aRowIter = 0; aRowIter < theMap.SizeY; ++aRowIter)
108 for (Standard_Size aColIter = 0; aColIter < theMap.SizeX; ++aColIter)
110 theStream << " [" << theMap.Value (aRowIter, aColIter) << "]";
116 //=======================================================================
117 //function : nextTileToSample
119 //=======================================================================
120 Graphic3d_Vec2i OpenGl_TileSampler::nextTileToSample()
122 Graphic3d_Vec2i aTile (0, 0);
123 const float aKsiX = mySampler.sample (0, myLastSample) * myMarginalMap.back();
124 for (; (size_t )aTile.x() < myMarginalMap.size() - 1; ++aTile.x())
126 if (aKsiX <= myMarginalMap[aTile.x()])
132 const float aKsiY = mySampler.sample (1, myLastSample) * myVarianceMap.Value (myVarianceMap.SizeY - 1, aTile.x());
133 for (; (size_t )aTile.y() < myVarianceMap.SizeY - 1; ++aTile.y())
135 if (aKsiY <= myVarianceMap.Value (aTile.y(), aTile.x()))
145 //=======================================================================
148 //=======================================================================
149 void OpenGl_TileSampler::SetSize (const Graphic3d_RenderingParams& theParams,
150 const Graphic3d_Vec2i& theSize)
158 myViewSize = theSize;
160 const int aTileSize = Max (theParams.RayTracingTileSize, 1);
161 const int aNbTilesX = Max (1, static_cast<int> (ceilf (static_cast<float> (theSize.x()) / aTileSize)));
162 const int aNbTilesY = Max (1, static_cast<int> (ceilf (static_cast<float> (theSize.y()) / aTileSize)));
163 if (myTileSize != aTileSize
164 || (int )myTiles.SizeX != aNbTilesX
165 || (int )myTiles.SizeY != aNbTilesY)
167 myTileSize = aTileSize;
168 myScaleFactor = 1.0e6f * (1024.0f / float(myTileSize * myTileSize));
170 Handle(NCollection_BaseAllocator) anAlloc = NCollection_BaseAllocator::CommonBaseAllocator();
171 myTiles.SetTopDown (true);
172 myTiles.Init (anAlloc, aNbTilesX, aNbTilesY);
175 myTileSamples.SetTopDown (true);
176 myTileSamples.Init (myTiles.Allocator(), aNbTilesX, aNbTilesY);
177 myTileSamples.Init (1);
179 myVarianceMap.SetTopDown (true);
180 myVarianceMap.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
181 myVarianceMap.Init (0.0f);
183 myVarianceRaw.SetTopDown (true);
184 myVarianceRaw.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
185 myVarianceRaw.Init (0);
187 myOffsets.SetTopDown (true);
188 myOffsets.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
189 myOffsets.Init (Graphic3d_Vec2i (-1, -1));
191 myMarginalMap.resize (myTiles.SizeX);
192 myMarginalMap.assign (myMarginalMap.size(), 0.0f);
195 // calculate a size of compact offsets texture optimal for rendering reduced number of tiles
196 Standard_Integer aNbShunkTilesX = (int )myTiles.SizeX, aNbShunkTilesY = (int )myTiles.SizeY;
197 if (theParams.NbRayTracingTiles > 0)
201 for (Standard_Integer anIdx = 0; aNbShunkTilesX * aNbShunkTilesY < theParams.NbRayTracingTiles; ++anIdx)
203 (anIdx % 2 == 0 ? aNbShunkTilesX : aNbShunkTilesY) <<= 1;
206 if ((int )myOffsetsShrunk.SizeX != aNbShunkTilesX
207 || (int )myOffsetsShrunk.SizeY != aNbShunkTilesY)
209 myOffsetsShrunk.SetTopDown (true);
210 myOffsetsShrunk.Init (myTiles.Allocator(), aNbShunkTilesX, aNbShunkTilesY);
211 myOffsetsShrunk.Init (Graphic3d_Vec2i (-1, -1));
215 //=======================================================================
218 //=======================================================================
219 bool OpenGl_TileSampler::upload (const Handle(OpenGl_Context)& theContext,
220 const Handle(OpenGl_Texture)& theSamplesTexture,
221 const Handle(OpenGl_Texture)& theOffsetsTexture,
222 const bool theAdaptive)
224 if (myTiles.IsEmpty())
229 // Fill in myTiles map with a number of passes (samples) per tile.
230 // By default, all tiles receive 1 sample, but basing on visual error level (myVarianceMap),
231 // this amount is re-distributed from tiles having smallest error take 0 samples to tiles having larger error.
232 // This redistribution is smoothed by Halton sampler.
234 // myOffsets map is filled as redirection of currently rendered tile to another one
235 // so that tiles having smallest error level have 0 tiles redirected from,
236 // while tiles with great error level might be rendered more than 1.
237 // This map is used within single-pass rendering method requiring atomic float operation support from hardware.
239 Image_PixMapTypedData<Graphic3d_Vec2i>& anOffsets = theAdaptive ? myOffsetsShrunk : myOffsets;
240 anOffsets.Init (Graphic3d_Vec2i (-1, -1));
241 for (Standard_Size aRowIter = 0; aRowIter < anOffsets.SizeY; ++aRowIter)
243 for (Standard_Size aColIter = 0; aColIter < anOffsets.SizeX; ++aColIter)
245 Graphic3d_Vec2i& aRedirectTile = anOffsets.ChangeValue (aRowIter, aColIter);
246 aRedirectTile = theAdaptive ? nextTileToSample() : Graphic3d_Vec2i ((int )aColIter, (int )aRowIter);
247 myTiles.ChangeValue (aRedirectTile.y(), aRedirectTile.x()) += 1;
251 #ifdef RAY_TRACE_PRINT_DEBUG_INFO
252 dumpMap (std::cerr, myTiles, "OpenGl_TileSampler, Samples");
255 // Fill in myTileSamples map from myTiles with an actual number of Samples per Tile as multiple of Tile Area
256 // (e.g. tile that should be rendered ones will have amount of samples equal to its are 4x4=16).
257 // This map is used for discarding tile fragments having <=0 of samples left within multi-pass rendering.
258 myTileSamples.Init (0);
259 for (Standard_Size aRowIter = 0; aRowIter < myTiles.SizeY; ++aRowIter)
261 for (Standard_Size aColIter = 0; aColIter < myTiles.SizeX; ++aColIter)
263 myTileSamples.ChangeValue (aRowIter, aColIter) = tileArea ((int )aColIter, (int )aRowIter) * myTiles.Value (aRowIter, aColIter);
267 bool hasErrors = false;
269 if (!theSamplesTexture.IsNull())
271 theSamplesTexture->Bind (theContext);
272 theContext->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
273 if (theContext->hasUnpackRowLength)
275 theContext->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
277 if (theSamplesTexture->SizeX() == (int )myTileSamples.SizeX
278 && theSamplesTexture->SizeY() == (int )myTileSamples.SizeY)
280 theContext->core11fwd->glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, (int )myTileSamples.SizeX, (int )myTileSamples.SizeY, GL_RED_INTEGER, GL_INT, myTileSamples.Data());
281 if (theContext->core11fwd->glGetError() != GL_NO_ERROR)
284 theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
285 "Error! Failed to upload tile samples map on the GPU");
292 theSamplesTexture->Unbind (theContext);
295 if (!theOffsetsTexture.IsNull())
297 if (theOffsetsTexture->SizeX() != (int )anOffsets.SizeX
298 || theOffsetsTexture->SizeY() != (int )anOffsets.SizeY
299 || !theOffsetsTexture->IsValid())
301 theOffsetsTexture->Release (theContext.get());
302 if (!theOffsetsTexture->Init (theContext,
303 OpenGl_TextureFormat::FindSizedFormat (theContext, GL_RG32I),
304 Graphic3d_Vec2i ((int )anOffsets.SizeX, (int )anOffsets.SizeY),
310 if (theOffsetsTexture->IsValid())
312 theOffsetsTexture->Bind (theContext);
313 theContext->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
314 if (theContext->hasUnpackRowLength)
316 theContext->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
318 theContext->core11fwd->glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, (int )anOffsets.SizeX, (int )anOffsets.SizeY, GL_RG_INTEGER, GL_INT, anOffsets.Data());
319 if (theContext->core11fwd->glGetError() != GL_NO_ERROR)
322 theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
323 "Error! Failed to upload tile offset map on the GPU");
325 theOffsetsTexture->Unbind (theContext);