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()
34 mySampler.initFaure();
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);
50 #if !defined(GL_ES_VERSION_2_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 "Error! Failed to fetch visual error map from the GPU");
64 // glGetTexImage() is unavailable on OpenGL ES, FBO + glReadPixels() can be used instead
68 const float aFactor = 1.0f / myScaleFactor;
69 for (Standard_Size aColIter = 0; aColIter < myVarianceMap.SizeX; ++aColIter)
71 for (Standard_Size aRowIter = 0; aRowIter < myVarianceMap.SizeY; ++aRowIter)
73 const int aRawValue = myVarianceRaw.Value (aRowIter, aColIter);
74 Standard_RangeError_Raise_if (aRawValue < 0, "Internal Error: signed integer overflow within OpenGl_TileSampler");
76 float& aTile = myVarianceMap.ChangeValue (aRowIter, aColIter);
77 aTile = aFactor * float(aRawValue);
78 aTile *= 1.0f / tileArea ((int )aColIter, (int )aRowIter); // average error over the tile
81 aTile += myVarianceMap.Value (aRowIter - 1, aColIter);
86 // build marginal distribution
87 for (Standard_Size aX = 0; aX < myVarianceMap.SizeX; ++aX)
89 myMarginalMap[aX] = myVarianceMap.Value (myVarianceMap.SizeY - 1, aX);
92 myMarginalMap[aX] += myMarginalMap[aX - 1];
96 #ifdef RAY_TRACE_PRINT_DEBUG_INFO
97 dumpMap (std::cerr, myVarianceRaw, "OpenGl_TileSampler, Variance map");
101 //=======================================================================
104 //=======================================================================
105 void OpenGl_TileSampler::dumpMap (std::ostream& theStream,
106 const Image_PixMapTypedData<int>& theMap,
107 const char* theTitle) const
109 theStream << theTitle << " " << theMap.SizeX << "x" << theMap.SizeY << " (tile " << myTileSize << "x" << myTileSize << ")" << ":\n";
110 for (Standard_Size aRowIter = 0; aRowIter < theMap.SizeY; ++aRowIter)
112 for (Standard_Size aColIter = 0; aColIter < theMap.SizeX; ++aColIter)
114 theStream << " [" << theMap.Value (aRowIter, aColIter) << "]";
120 //=======================================================================
121 //function : nextTileToSample
123 //=======================================================================
124 Graphic3d_Vec2i OpenGl_TileSampler::nextTileToSample()
126 Graphic3d_Vec2i aTile (0, 0);
127 const float aKsiX = mySampler.sample (0, myLastSample) * myMarginalMap.back();
128 for (; (size_t )aTile.x() < myMarginalMap.size() - 1; ++aTile.x())
130 if (aKsiX <= myMarginalMap[aTile.x()])
136 const float aKsiY = mySampler.sample (1, myLastSample) * myVarianceMap.Value (myVarianceMap.SizeY - 1, aTile.x());
137 for (; (size_t )aTile.y() < myVarianceMap.SizeY - 1; ++aTile.y())
139 if (aKsiY <= myVarianceMap.Value (aTile.y(), aTile.x()))
149 //=======================================================================
152 //=======================================================================
153 void OpenGl_TileSampler::SetSize (const Graphic3d_RenderingParams& theParams,
154 const Graphic3d_Vec2i& theSize)
162 myViewSize = theSize;
164 const int aTileSize = Max (theParams.RayTracingTileSize, 1);
165 const int aNbTilesX = Max (1, static_cast<int> (ceilf (static_cast<float> (theSize.x()) / aTileSize)));
166 const int aNbTilesY = Max (1, static_cast<int> (ceilf (static_cast<float> (theSize.y()) / aTileSize)));
167 if (myTileSize != aTileSize
168 || (int )myTiles.SizeX != aNbTilesX
169 || (int )myTiles.SizeY != aNbTilesY)
171 myTileSize = aTileSize;
172 myScaleFactor = 1.0e6f * (1024.0f / float(myTileSize * myTileSize));
174 Handle(NCollection_BaseAllocator) anAlloc = NCollection_BaseAllocator::CommonBaseAllocator();
175 myTiles.SetTopDown (true);
176 myTiles.Init (anAlloc, aNbTilesX, aNbTilesY);
179 myTileSamples.SetTopDown (true);
180 myTileSamples.Init (myTiles.Allocator(), aNbTilesX, aNbTilesY);
181 myTileSamples.Init (1);
183 myVarianceMap.SetTopDown (true);
184 myVarianceMap.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
185 myVarianceMap.Init (0.0f);
187 myVarianceRaw.SetTopDown (true);
188 myVarianceRaw.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
189 myVarianceRaw.Init (0);
191 myOffsets.SetTopDown (true);
192 myOffsets.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
193 myOffsets.Init (Graphic3d_Vec2i (-1, -1));
195 myMarginalMap.resize (myTiles.SizeX);
196 myMarginalMap.assign (myMarginalMap.size(), 0.0f);
199 // calculate a size of compact offsets texture optimal for rendering reduced number of tiles
200 Standard_Integer aNbShunkTilesX = (int )myTiles.SizeX, aNbShunkTilesY = (int )myTiles.SizeY;
201 if (theParams.NbRayTracingTiles > 0)
205 for (Standard_Integer anIdx = 0; aNbShunkTilesX * aNbShunkTilesY < theParams.NbRayTracingTiles; ++anIdx)
207 (anIdx % 2 == 0 ? aNbShunkTilesX : aNbShunkTilesY) <<= 1;
210 if ((int )myOffsetsShrunk.SizeX != aNbShunkTilesX
211 || (int )myOffsetsShrunk.SizeY != aNbShunkTilesY)
213 myOffsetsShrunk.SetTopDown (true);
214 myOffsetsShrunk.Init (myTiles.Allocator(), aNbShunkTilesX, aNbShunkTilesY);
215 myOffsetsShrunk.Init (Graphic3d_Vec2i (-1, -1));
219 //=======================================================================
222 //=======================================================================
223 bool OpenGl_TileSampler::upload (const Handle(OpenGl_Context)& theContext,
224 const Handle(OpenGl_Texture)& theSamplesTexture,
225 const Handle(OpenGl_Texture)& theOffsetsTexture,
226 const bool theAdaptive)
228 if (myTiles.IsEmpty())
233 // Fill in myTiles map with a number of passes (samples) per tile.
234 // By default, all tiles receive 1 sample, but basing on visual error level (myVarianceMap),
235 // this amount is re-distributed from tiles having smallest error take 0 samples to tiles having larger error.
236 // This redistribution is smoothed by Halton sampler.
238 // myOffsets map is filled as redirection of currently rendered tile to another one
239 // so that tiles having smallest error level have 0 tiles redirected from,
240 // while tiles with great error level might be rendered more than 1.
241 // This map is used within single-pass rendering method requiring atomic float operation support from hardware.
243 Image_PixMapTypedData<Graphic3d_Vec2i>& anOffsets = theAdaptive ? myOffsetsShrunk : myOffsets;
244 anOffsets.Init (Graphic3d_Vec2i (-1, -1));
245 for (Standard_Size aRowIter = 0; aRowIter < anOffsets.SizeY; ++aRowIter)
247 for (Standard_Size aColIter = 0; aColIter < anOffsets.SizeX; ++aColIter)
249 Graphic3d_Vec2i& aRedirectTile = anOffsets.ChangeValue (aRowIter, aColIter);
250 aRedirectTile = theAdaptive ? nextTileToSample() : Graphic3d_Vec2i ((int )aColIter, (int )aRowIter);
251 myTiles.ChangeValue (aRedirectTile.y(), aRedirectTile.x()) += 1;
255 #ifdef RAY_TRACE_PRINT_DEBUG_INFO
256 dumpMap (std::cerr, myTiles, "OpenGl_TileSampler, Samples");
259 // Fill in myTileSamples map from myTiles with an actual number of Samples per Tile as multiple of Tile Area
260 // (e.g. tile that should be rendered ones will have amount of samples equal to its are 4x4=16).
261 // This map is used for discarding tile fragments having <=0 of samples left within multi-pass rendering.
262 myTileSamples.Init (0);
263 for (Standard_Size aRowIter = 0; aRowIter < myTiles.SizeY; ++aRowIter)
265 for (Standard_Size aColIter = 0; aColIter < myTiles.SizeX; ++aColIter)
267 myTileSamples.ChangeValue (aRowIter, aColIter) = tileArea ((int )aColIter, (int )aRowIter) * myTiles.Value (aRowIter, aColIter);
271 bool hasErrors = false;
273 if (!theSamplesTexture.IsNull())
275 theSamplesTexture->Bind (theContext);
276 theContext->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
277 #if !defined(GL_ES_VERSION_2_0)
278 theContext->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
280 if (theSamplesTexture->SizeX() == (int )myTileSamples.SizeX
281 && theSamplesTexture->SizeY() == (int )myTileSamples.SizeY)
283 theContext->core11fwd->glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, (int )myTileSamples.SizeX, (int )myTileSamples.SizeY, GL_RED_INTEGER, GL_INT, myTileSamples.Data());
284 if (theContext->core11fwd->glGetError() != GL_NO_ERROR)
287 theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
288 "Error! Failed to upload tile samples map on the GPU");
295 theSamplesTexture->Unbind (theContext);
298 if (!theOffsetsTexture.IsNull())
300 if (theOffsetsTexture->SizeX() != (int )anOffsets.SizeX
301 || theOffsetsTexture->SizeY() != (int )anOffsets.SizeY
302 || !theOffsetsTexture->IsValid())
304 theOffsetsTexture->Release (theContext.operator->());
305 if (!theOffsetsTexture->Init (theContext, GL_RG32I, GL_RG_INTEGER, GL_INT,
306 (int )anOffsets.SizeX, (int )anOffsets.SizeY, Graphic3d_TOT_2D))
311 if (theOffsetsTexture->IsValid())
313 theOffsetsTexture->Bind (theContext);
314 theContext->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
315 #if !defined(GL_ES_VERSION_2_0)
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);