0030748: Visualization - Marker displayed in immediate layer ruins QT Quick view...
[occt.git] / src / OpenGl / OpenGl_TileSampler.cxx
CommitLineData
3a9b5dc8 1// Created on: 2016-06-16
2// Created by: Denis BOGOLEPOV & Danila ULYANOV
3// Copyright (c) 2016 OPEN CASCADE SAS
4//
5// This file is part of Open CASCADE Technology software library.
6//
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.
12//
13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
15
16#include <OpenGl_Context.hxx>
17#include <OpenGl_TileSampler.hxx>
66d1cdc6 18#include <Graphic3d_RenderingParams.hxx>
3a9b5dc8 19#include <TCollection_ExtendedString.hxx>
20
e084dbbc 21// define to debug algorithm values
22//#define RAY_TRACE_PRINT_DEBUG_INFO
23
3a9b5dc8 24//=======================================================================
25//function : OpenGl_TileSampler
26//purpose :
27//=======================================================================
28OpenGl_TileSampler::OpenGl_TileSampler()
66d1cdc6 29: myLastSample (0),
30 myScaleFactor(1.0f),
31 myTileSize (0),
32 myViewSize (0, 0)
3a9b5dc8 33{
34 mySampler.initFaure();
35}
36
37//=======================================================================
38//function : GrabVarianceMap
39//purpose :
40//=======================================================================
66d1cdc6 41void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theContext,
42 const Handle(OpenGl_Texture)& theTexture)
3a9b5dc8 43{
66d1cdc6 44 if (theTexture.IsNull())
45 {
46 return;
47 }
3a9b5dc8 48
66d1cdc6 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());
3a9b5dc8 55 const GLenum anErr = theContext->core11fwd->glGetError();
66d1cdc6 56 theTexture->Unbind (theContext);
3a9b5dc8 57 if (anErr != GL_NO_ERROR)
58 {
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");
66d1cdc6 61 return;
3a9b5dc8 62 }
66d1cdc6 63#else
64 // glGetTexImage() is unavailable on OpenGL ES, FBO + glReadPixels() can be used instead
65 (void )theContext;
66#endif
3a9b5dc8 67
66d1cdc6 68 const float aFactor = 1.0f / myScaleFactor;
69 for (Standard_Size aColIter = 0; aColIter < myVarianceMap.SizeX; ++aColIter)
70 {
71 for (Standard_Size aRowIter = 0; aRowIter < myVarianceMap.SizeY; ++aRowIter)
3a9b5dc8 72 {
66d1cdc6 73 const int aRawValue = myVarianceRaw.Value (aRowIter, aColIter);
e084dbbc 74 Standard_RangeError_Raise_if (aRawValue < 0, "Internal Error: signed integer overflow within OpenGl_TileSampler");
75
66d1cdc6 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
79 if (aRowIter != 0)
3a9b5dc8 80 {
66d1cdc6 81 aTile += myVarianceMap.Value (aRowIter - 1, aColIter);
3a9b5dc8 82 }
83 }
66d1cdc6 84 }
3a9b5dc8 85
66d1cdc6 86 // build marginal distribution
87 for (Standard_Size aX = 0; aX < myVarianceMap.SizeX; ++aX)
88 {
89 myMarginalMap[aX] = myVarianceMap.Value (myVarianceMap.SizeY - 1, aX);
90 if (aX != 0)
3a9b5dc8 91 {
66d1cdc6 92 myMarginalMap[aX] += myMarginalMap[aX - 1];
3a9b5dc8 93 }
94 }
e084dbbc 95
96#ifdef RAY_TRACE_PRINT_DEBUG_INFO
97 dumpMap (std::cerr, myVarianceRaw, "OpenGl_TileSampler, Variance map");
98#endif
99}
100
101//=======================================================================
102//function : dumpMap
103//purpose :
104//=======================================================================
105void OpenGl_TileSampler::dumpMap (std::ostream& theStream,
106 const Image_PixMapTypedData<int>& theMap,
107 const char* theTitle) const
108{
109 theStream << theTitle << " " << theMap.SizeX << "x" << theMap.SizeY << " (tile " << myTileSize << "x" << myTileSize << ")" << ":\n";
110 for (Standard_Size aRowIter = 0; aRowIter < theMap.SizeY; ++aRowIter)
111 {
112 for (Standard_Size aColIter = 0; aColIter < theMap.SizeX; ++aColIter)
113 {
114 theStream << " [" << theMap.Value (aRowIter, aColIter) << "]";
115 }
116 theStream << "\n";
117 }
3a9b5dc8 118}
119
120//=======================================================================
66d1cdc6 121//function : nextTileToSample
3a9b5dc8 122//purpose :
123//=======================================================================
66d1cdc6 124Graphic3d_Vec2i OpenGl_TileSampler::nextTileToSample()
3a9b5dc8 125{
66d1cdc6 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())
3a9b5dc8 129 {
66d1cdc6 130 if (aKsiX <= myMarginalMap[aTile.x()])
3a9b5dc8 131 {
132 break;
133 }
134 }
135
66d1cdc6 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())
3a9b5dc8 138 {
66d1cdc6 139 if (aKsiY <= myVarianceMap.Value (aTile.y(), aTile.x()))
3a9b5dc8 140 {
141 break;
142 }
143 }
144
66d1cdc6 145 ++myLastSample;
146 return aTile;
3a9b5dc8 147}
148
149//=======================================================================
150//function : SetSize
151//purpose :
152//=======================================================================
66d1cdc6 153void OpenGl_TileSampler::SetSize (const Graphic3d_RenderingParams& theParams,
154 const Graphic3d_Vec2i& theSize)
3a9b5dc8 155{
66d1cdc6 156 if (theSize.x() <= 0
157 || theSize.y() <= 0)
3a9b5dc8 158 {
159 return;
160 }
161
66d1cdc6 162 myViewSize = theSize;
3a9b5dc8 163
66d1cdc6 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)
170 {
171 myTileSize = aTileSize;
172 myScaleFactor = 1.0e6f * (1024.0f / float(myTileSize * myTileSize));
3a9b5dc8 173
66d1cdc6 174 Handle(NCollection_BaseAllocator) anAlloc = NCollection_BaseAllocator::CommonBaseAllocator();
175 myTiles.SetTopDown (true);
176 myTiles.Init (anAlloc, aNbTilesX, aNbTilesY);
177 myTiles.Init (1);
178
e084dbbc 179 myTileSamples.SetTopDown (true);
180 myTileSamples.Init (myTiles.Allocator(), aNbTilesX, aNbTilesY);
181 myTileSamples.Init (1);
182
66d1cdc6 183 myVarianceMap.SetTopDown (true);
184 myVarianceMap.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
185 myVarianceMap.Init (0.0f);
186
187 myVarianceRaw.SetTopDown (true);
188 myVarianceRaw.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
189 myVarianceRaw.Init (0);
190
191 myOffsets.SetTopDown (true);
192 myOffsets.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
193 myOffsets.Init (Graphic3d_Vec2i (-1, -1));
194
195 myMarginalMap.resize (myTiles.SizeX);
196 myMarginalMap.assign (myMarginalMap.size(), 0.0f);
197 }
198
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)
202 {
203 aNbShunkTilesX = 8;
204 aNbShunkTilesY = 8;
205 for (Standard_Integer anIdx = 0; aNbShunkTilesX * aNbShunkTilesY < theParams.NbRayTracingTiles; ++anIdx)
206 {
207 (anIdx % 2 == 0 ? aNbShunkTilesX : aNbShunkTilesY) <<= 1;
208 }
209 }
210 if ((int )myOffsetsShrunk.SizeX != aNbShunkTilesX
211 || (int )myOffsetsShrunk.SizeY != aNbShunkTilesY)
212 {
213 myOffsetsShrunk.SetTopDown (true);
214 myOffsetsShrunk.Init (myTiles.Allocator(), aNbShunkTilesX, aNbShunkTilesY);
215 myOffsetsShrunk.Init (Graphic3d_Vec2i (-1, -1));
216 }
3a9b5dc8 217}
218
219//=======================================================================
e084dbbc 220//function : upload
3a9b5dc8 221//purpose :
222//=======================================================================
e084dbbc 223bool OpenGl_TileSampler::upload (const Handle(OpenGl_Context)& theContext,
224 const Handle(OpenGl_Texture)& theSamplesTexture,
225 const Handle(OpenGl_Texture)& theOffsetsTexture,
226 const bool theAdaptive)
3a9b5dc8 227{
66d1cdc6 228 if (myTiles.IsEmpty())
3a9b5dc8 229 {
66d1cdc6 230 return false;
3a9b5dc8 231 }
232
e084dbbc 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.
237 //
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.
66d1cdc6 242 myTiles.Init (0);
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)
246 {
247 for (Standard_Size aColIter = 0; aColIter < anOffsets.SizeX; ++aColIter)
248 {
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;
252 }
253 }
4eaaf9d8 254
e084dbbc 255#ifdef RAY_TRACE_PRINT_DEBUG_INFO
256 dumpMap (std::cerr, myTiles, "OpenGl_TileSampler, Samples");
257#endif
258
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)
264 {
265 for (Standard_Size aColIter = 0; aColIter < myTiles.SizeX; ++aColIter)
266 {
267 myTileSamples.ChangeValue (aRowIter, aColIter) = tileArea ((int )aColIter, (int )aRowIter) * myTiles.Value (aRowIter, aColIter);
268 }
269 }
270
66d1cdc6 271 bool hasErrors = false;
e084dbbc 272
273 if (!theSamplesTexture.IsNull())
274 {
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);
279 #endif
280 if (theSamplesTexture->SizeX() == (int )myTileSamples.SizeX
281 && theSamplesTexture->SizeY() == (int )myTileSamples.SizeY)
282 {
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)
285 {
286 hasErrors = true;
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");
289 }
290 }
291 else
292 {
293 hasErrors = true;
294 }
295 theSamplesTexture->Unbind (theContext);
296 }
297
66d1cdc6 298 if (!theOffsetsTexture.IsNull())
3a9b5dc8 299 {
66d1cdc6 300 if (theOffsetsTexture->SizeX() != (int )anOffsets.SizeX
301 || theOffsetsTexture->SizeY() != (int )anOffsets.SizeY
302 || !theOffsetsTexture->IsValid())
3a9b5dc8 303 {
66d1cdc6 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))
3a9b5dc8 307 {
66d1cdc6 308 hasErrors = true;
3a9b5dc8 309 }
66d1cdc6 310 }
311 if (theOffsetsTexture->IsValid())
312 {
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);
317 #endif
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)
3a9b5dc8 320 {
66d1cdc6 321 hasErrors = true;
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");
3a9b5dc8 324 }
66d1cdc6 325 theOffsetsTexture->Unbind (theContext);
3a9b5dc8 326 }
327 }
66d1cdc6 328 return !hasErrors;
3a9b5dc8 329}