0032713: Visualization, OpenGl_GlFunctions - unify OpenGL and OpenGL ES function...
[occt.git] / src / OpenGl / OpenGl_TileSampler.cxx
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>
18 #include <Graphic3d_RenderingParams.hxx>
19 #include <TCollection_ExtendedString.hxx>
20
21 // define to debug algorithm values
22 //#define RAY_TRACE_PRINT_DEBUG_INFO
23
24 //=======================================================================
25 //function : OpenGl_TileSampler
26 //purpose  :
27 //=======================================================================
28 OpenGl_TileSampler::OpenGl_TileSampler()
29 : myLastSample (0),
30   myScaleFactor(1.0f),
31   myTileSize   (0),
32   myViewSize   (0, 0)
33 {
34   //
35 }
36
37 //=======================================================================
38 //function : GrabVarianceMap
39 //purpose  :
40 //=======================================================================
41 void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theContext,
42                                           const Handle(OpenGl_Texture)& theTexture)
43 {
44   if (theTexture.IsNull())
45   {
46     return;
47   }
48
49   myVarianceRaw.Init (0);
50
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)
58   {
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));
61     return;
62   }
63
64   const float aFactor = 1.0f / myScaleFactor;
65   for (Standard_Size aColIter = 0; aColIter < myVarianceMap.SizeX; ++aColIter)
66   {
67     for (Standard_Size aRowIter = 0; aRowIter < myVarianceMap.SizeY; ++aRowIter)
68     {
69       const int aRawValue = myVarianceRaw.Value (aRowIter, aColIter);
70       Standard_RangeError_Raise_if (aRawValue < 0, "Internal Error: signed integer overflow within OpenGl_TileSampler");
71
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
75       if (aRowIter != 0)
76       {
77         aTile += myVarianceMap.Value (aRowIter - 1, aColIter);
78       }
79     }
80   }
81
82   // build marginal distribution
83   for (Standard_Size aX = 0; aX < myVarianceMap.SizeX; ++aX)
84   {
85     myMarginalMap[aX] = myVarianceMap.Value (myVarianceMap.SizeY - 1, aX);
86     if (aX != 0)
87     {
88       myMarginalMap[aX] += myMarginalMap[aX - 1];
89     }
90   }
91
92 #ifdef RAY_TRACE_PRINT_DEBUG_INFO
93   dumpMap (std::cerr, myVarianceRaw, "OpenGl_TileSampler, Variance map");
94 #endif
95 }
96
97 //=======================================================================
98 //function : dumpMap
99 //purpose  :
100 //=======================================================================
101 void OpenGl_TileSampler::dumpMap (std::ostream& theStream,
102                                   const Image_PixMapTypedData<int>& theMap,
103                                   const char* theTitle) const
104 {
105   theStream << theTitle << " " << theMap.SizeX << "x" << theMap.SizeY << " (tile " << myTileSize << "x" << myTileSize << ")" << ":\n";
106   for (Standard_Size aRowIter = 0; aRowIter < theMap.SizeY; ++aRowIter)
107   {
108     for (Standard_Size aColIter = 0; aColIter < theMap.SizeX; ++aColIter)
109     {
110       theStream << " [" << theMap.Value (aRowIter, aColIter) << "]";
111     }
112     theStream << "\n";
113   }
114 }
115
116 //=======================================================================
117 //function : nextTileToSample
118 //purpose  :
119 //=======================================================================
120 Graphic3d_Vec2i OpenGl_TileSampler::nextTileToSample()
121 {
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())
125   {
126     if (aKsiX <= myMarginalMap[aTile.x()])
127     {
128       break;
129     }
130   }
131
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())
134   {
135     if (aKsiY <= myVarianceMap.Value (aTile.y(), aTile.x()))
136     {
137       break;
138     }
139   }
140
141   ++myLastSample;
142   return aTile;
143 }
144
145 //=======================================================================
146 //function : SetSize
147 //purpose  :
148 //=======================================================================
149 void OpenGl_TileSampler::SetSize (const Graphic3d_RenderingParams& theParams,
150                                   const Graphic3d_Vec2i& theSize)
151 {
152   if (theSize.x() <= 0
153    || theSize.y() <= 0)
154   {
155     return;
156   }
157
158   myViewSize = theSize;
159
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)
166   {
167     myTileSize = aTileSize;
168     myScaleFactor = 1.0e6f * (1024.0f / float(myTileSize * myTileSize));
169
170     Handle(NCollection_BaseAllocator) anAlloc = NCollection_BaseAllocator::CommonBaseAllocator();
171     myTiles.SetTopDown (true);
172     myTiles.Init (anAlloc, aNbTilesX, aNbTilesY);
173     myTiles.Init (1);
174
175     myTileSamples.SetTopDown (true);
176     myTileSamples.Init (myTiles.Allocator(), aNbTilesX, aNbTilesY);
177     myTileSamples.Init (1);
178
179     myVarianceMap.SetTopDown (true);
180     myVarianceMap.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
181     myVarianceMap.Init (0.0f);
182
183     myVarianceRaw.SetTopDown (true);
184     myVarianceRaw.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
185     myVarianceRaw.Init (0);
186
187     myOffsets.SetTopDown (true);
188     myOffsets.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
189     myOffsets.Init (Graphic3d_Vec2i (-1, -1));
190
191     myMarginalMap.resize (myTiles.SizeX);
192     myMarginalMap.assign (myMarginalMap.size(), 0.0f);
193   }
194
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)
198   {
199     aNbShunkTilesX = 8;
200     aNbShunkTilesY = 8;
201     for (Standard_Integer anIdx = 0; aNbShunkTilesX * aNbShunkTilesY < theParams.NbRayTracingTiles; ++anIdx)
202     {
203       (anIdx % 2 == 0 ? aNbShunkTilesX : aNbShunkTilesY) <<= 1;
204     }
205   }
206   if ((int )myOffsetsShrunk.SizeX != aNbShunkTilesX
207    || (int )myOffsetsShrunk.SizeY != aNbShunkTilesY)
208   {
209     myOffsetsShrunk.SetTopDown (true);
210     myOffsetsShrunk.Init (myTiles.Allocator(), aNbShunkTilesX, aNbShunkTilesY);
211     myOffsetsShrunk.Init (Graphic3d_Vec2i (-1, -1));
212   }
213 }
214
215 //=======================================================================
216 //function : upload
217 //purpose  :
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)
223 {
224   if (myTiles.IsEmpty())
225   {
226     return false;
227   }
228
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.
233   //
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.
238   myTiles.Init (0);
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)
242   {
243     for (Standard_Size aColIter = 0; aColIter < anOffsets.SizeX; ++aColIter)
244     {
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;
248     }
249   }
250
251 #ifdef RAY_TRACE_PRINT_DEBUG_INFO
252   dumpMap (std::cerr, myTiles, "OpenGl_TileSampler, Samples");
253 #endif
254
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)
260   {
261     for (Standard_Size aColIter = 0; aColIter < myTiles.SizeX; ++aColIter)
262     {
263       myTileSamples.ChangeValue (aRowIter, aColIter) = tileArea ((int )aColIter, (int )aRowIter) * myTiles.Value (aRowIter, aColIter);
264     }
265   }
266
267   bool hasErrors = false;
268
269   if (!theSamplesTexture.IsNull())
270   {
271     theSamplesTexture->Bind (theContext);
272     theContext->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT,  1);
273     if (theContext->hasUnpackRowLength)
274     {
275       theContext->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
276     }
277     if (theSamplesTexture->SizeX() == (int )myTileSamples.SizeX
278      && theSamplesTexture->SizeY() == (int )myTileSamples.SizeY)
279     {
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)
282       {
283         hasErrors = true;
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");
286       }
287     }
288     else
289     {
290       hasErrors = true;
291     }
292     theSamplesTexture->Unbind (theContext);
293   }
294
295   if (!theOffsetsTexture.IsNull())
296   {
297     if (theOffsetsTexture->SizeX() != (int )anOffsets.SizeX
298     ||  theOffsetsTexture->SizeY() != (int )anOffsets.SizeY
299     || !theOffsetsTexture->IsValid())
300     {
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),
305                                     Graphic3d_TOT_2D))
306       {
307         hasErrors = true;
308       }
309     }
310     if (theOffsetsTexture->IsValid())
311     {
312       theOffsetsTexture->Bind (theContext);
313       theContext->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT,  1);
314       if (theContext->hasUnpackRowLength)
315       {
316         theContext->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
317       }
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)
320       {
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");
324       }
325       theOffsetsTexture->Unbind (theContext);
326     }
327   }
328   return !hasErrors;
329 }