0030483: Visualization, Path Tracing - make Tile Size configurable
[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 //=======================================================================
22 //function : OpenGl_TileSampler
23 //purpose  :
24 //=======================================================================
25 OpenGl_TileSampler::OpenGl_TileSampler()
26 : myLastSample (0),
27   myScaleFactor(1.0f),
28   myTileSize   (0),
29   myViewSize   (0, 0)
30 {
31   mySampler.initFaure();
32 }
33
34 //=======================================================================
35 //function : GrabVarianceMap
36 //purpose  :
37 //=======================================================================
38 void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theContext,
39                                           const Handle(OpenGl_Texture)& theTexture)
40 {
41   if (theTexture.IsNull())
42   {
43     return;
44   }
45
46   myVarianceRaw.Init (0);
47 #if !defined(GL_ES_VERSION_2_0)
48   theTexture->Bind (theContext);
49   theContext->core11fwd->glPixelStorei (GL_PACK_ALIGNMENT,  1);
50   theContext->core11fwd->glPixelStorei (GL_PACK_ROW_LENGTH, 0);
51   theContext->core11fwd->glGetTexImage (GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_INT, myVarianceRaw.ChangeData());
52   const GLenum anErr = theContext->core11fwd->glGetError();
53   theTexture->Unbind (theContext);
54   if (anErr != GL_NO_ERROR)
55   {
56     theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
57                              "Error! Failed to fetch visual error map from the GPU");
58     return;
59   }
60 #else
61   // glGetTexImage() is unavailable on OpenGL ES, FBO + glReadPixels() can be used instead
62   (void )theContext;
63 #endif
64
65   const float aFactor = 1.0f / myScaleFactor;
66   for (Standard_Size aColIter = 0; aColIter < myVarianceMap.SizeX; ++aColIter)
67   {
68     for (Standard_Size aRowIter = 0; aRowIter < myVarianceMap.SizeY; ++aRowIter)
69     {
70       const int aRawValue = myVarianceRaw.Value (aRowIter, aColIter);
71       float& aTile = myVarianceMap.ChangeValue (aRowIter, aColIter);
72       aTile = aFactor * float(aRawValue);
73       aTile *= 1.0f / tileArea ((int )aColIter, (int )aRowIter); // average error over the tile
74       if (aRowIter != 0)
75       {
76         aTile += myVarianceMap.Value (aRowIter - 1, aColIter);
77       }
78     }
79   }
80
81   // build marginal distribution
82   for (Standard_Size aX = 0; aX < myVarianceMap.SizeX; ++aX)
83   {
84     myMarginalMap[aX] = myVarianceMap.Value (myVarianceMap.SizeY - 1, aX);
85     if (aX != 0)
86     {
87       myMarginalMap[aX] += myMarginalMap[aX - 1];
88     }
89   }
90 }
91
92 //=======================================================================
93 //function : nextTileToSample
94 //purpose  :
95 //=======================================================================
96 Graphic3d_Vec2i OpenGl_TileSampler::nextTileToSample()
97 {
98   Graphic3d_Vec2i aTile (0, 0);
99   const float aKsiX = mySampler.sample (0, myLastSample) * myMarginalMap.back();
100   for (; (size_t )aTile.x() < myMarginalMap.size() - 1; ++aTile.x())
101   {
102     if (aKsiX <= myMarginalMap[aTile.x()])
103     {
104       break;
105     }
106   }
107
108   const float aKsiY = mySampler.sample (1, myLastSample) * myVarianceMap.Value (myVarianceMap.SizeY - 1, aTile.x());
109   for (; (size_t )aTile.y() < myVarianceMap.SizeY - 1; ++aTile.y())
110   {
111     if (aKsiY <= myVarianceMap.Value (aTile.y(), aTile.x()))
112     {
113       break;
114     }
115   }
116
117   ++myLastSample;
118   return aTile;
119 }
120
121 //=======================================================================
122 //function : SetSize
123 //purpose  :
124 //=======================================================================
125 void OpenGl_TileSampler::SetSize (const Graphic3d_RenderingParams& theParams,
126                                   const Graphic3d_Vec2i& theSize)
127 {
128   if (theSize.x() <= 0
129    || theSize.y() <= 0)
130   {
131     return;
132   }
133
134   myViewSize = theSize;
135
136   const int aTileSize = Max (theParams.RayTracingTileSize, 1);
137   const int aNbTilesX = Max (1, static_cast<int> (ceilf (static_cast<float> (theSize.x()) / aTileSize)));
138   const int aNbTilesY = Max (1, static_cast<int> (ceilf (static_cast<float> (theSize.y()) / aTileSize)));
139   if (myTileSize != aTileSize
140    || (int )myTiles.SizeX != aNbTilesX
141    || (int )myTiles.SizeY != aNbTilesY)
142   {
143     myTileSize = aTileSize;
144     myScaleFactor = 1.0e6f * (1024.0f / float(myTileSize * myTileSize));
145
146     Handle(NCollection_BaseAllocator) anAlloc = NCollection_BaseAllocator::CommonBaseAllocator();
147     myTiles.SetTopDown (true);
148     myTiles.Init (anAlloc, aNbTilesX, aNbTilesY);
149     myTiles.Init (1);
150
151     myVarianceMap.SetTopDown (true);
152     myVarianceMap.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
153     myVarianceMap.Init (0.0f);
154
155     myVarianceRaw.SetTopDown (true);
156     myVarianceRaw.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
157     myVarianceRaw.Init (0);
158
159     myOffsets.SetTopDown (true);
160     myOffsets.Init (myTiles.Allocator(), myTiles.SizeX, myTiles.SizeY);
161     myOffsets.Init (Graphic3d_Vec2i (-1, -1));
162
163     myMarginalMap.resize (myTiles.SizeX);
164     myMarginalMap.assign (myMarginalMap.size(), 0.0f);
165   }
166
167   // calculate a size of compact offsets texture optimal for rendering reduced number of tiles
168   Standard_Integer aNbShunkTilesX = (int )myTiles.SizeX, aNbShunkTilesY = (int )myTiles.SizeY;
169   if (theParams.NbRayTracingTiles > 0)
170   {
171     aNbShunkTilesX = 8;
172     aNbShunkTilesY = 8;
173     for (Standard_Integer anIdx = 0; aNbShunkTilesX * aNbShunkTilesY < theParams.NbRayTracingTiles; ++anIdx)
174     {
175       (anIdx % 2 == 0 ? aNbShunkTilesX : aNbShunkTilesY) <<= 1;
176     }
177   }
178   if ((int )myOffsetsShrunk.SizeX != aNbShunkTilesX
179    || (int )myOffsetsShrunk.SizeY != aNbShunkTilesY)
180   {
181     myOffsetsShrunk.SetTopDown (true);
182     myOffsetsShrunk.Init (myTiles.Allocator(), aNbShunkTilesX, aNbShunkTilesY);
183     myOffsetsShrunk.Init (Graphic3d_Vec2i (-1, -1));
184   }
185 }
186
187 //=======================================================================
188 //function : UploadOffsets
189 //purpose  :
190 //=======================================================================
191 bool OpenGl_TileSampler::UploadOffsets (const Handle(OpenGl_Context)& theContext,
192                                         const Handle(OpenGl_Texture)& theOffsetsTexture,
193                                         const bool theAdaptive)
194 {
195   if (myTiles.IsEmpty())
196   {
197     return false;
198   }
199
200   myTiles.Init (0);
201   Image_PixMapTypedData<Graphic3d_Vec2i>& anOffsets = theAdaptive ? myOffsetsShrunk : myOffsets;
202   anOffsets.Init (Graphic3d_Vec2i (-1, -1));
203   for (Standard_Size aRowIter = 0; aRowIter < anOffsets.SizeY; ++aRowIter)
204   {
205     for (Standard_Size aColIter = 0; aColIter < anOffsets.SizeX; ++aColIter)
206     {
207       Graphic3d_Vec2i& aRedirectTile = anOffsets.ChangeValue (aRowIter, aColIter);
208       aRedirectTile = theAdaptive ? nextTileToSample() : Graphic3d_Vec2i ((int )aColIter, (int )aRowIter);
209       myTiles.ChangeValue (aRedirectTile.y(), aRedirectTile.x()) += 1;
210     }
211   }
212
213   bool hasErrors = false;
214   if (!theOffsetsTexture.IsNull())
215   {
216     if (theOffsetsTexture->SizeX() != (int )anOffsets.SizeX
217     ||  theOffsetsTexture->SizeY() != (int )anOffsets.SizeY
218     || !theOffsetsTexture->IsValid())
219     {
220       theOffsetsTexture->Release (theContext.operator->());
221       if (!theOffsetsTexture->Init (theContext, GL_RG32I, GL_RG_INTEGER, GL_INT,
222                                     (int )anOffsets.SizeX, (int )anOffsets.SizeY, Graphic3d_TOT_2D))
223       {
224         hasErrors = true;
225       }
226     }
227     if (theOffsetsTexture->IsValid())
228     {
229       theOffsetsTexture->Bind (theContext);
230       theContext->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT,  1);
231     #if !defined(GL_ES_VERSION_2_0)
232       theContext->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
233     #endif
234       theContext->core11fwd->glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, (int )anOffsets.SizeX, (int )anOffsets.SizeY, GL_RG_INTEGER, GL_INT, anOffsets.Data());
235       if (theContext->core11fwd->glGetError() != GL_NO_ERROR)
236       {
237         hasErrors = true;
238         theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM,
239                                  "Error! Failed to upload tile offset map on the GPU");
240       }
241       theOffsetsTexture->Unbind (theContext);
242     }
243   }
244   return !hasErrors;
245 }