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 | //======================================================================= |
28 | OpenGl_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 |
41 | void 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 | //======================================================================= |
105 | void 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 |
124 | Graphic3d_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 |
153 | void 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 |
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) |
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 | { |
ba00aab7 |
304 | theOffsetsTexture->Release (theContext.get()); |
305 | if (!theOffsetsTexture->Init (theContext, |
306 | OpenGl_TextureFormat::FindSizedFormat (theContext, GL_RG32I), |
307 | Graphic3d_Vec2i ((int )anOffsets.SizeX, (int )anOffsets.SizeY), |
308 | Graphic3d_TOT_2D)) |
3a9b5dc8 |
309 | { |
66d1cdc6 |
310 | hasErrors = true; |
3a9b5dc8 |
311 | } |
66d1cdc6 |
312 | } |
313 | if (theOffsetsTexture->IsValid()) |
314 | { |
315 | theOffsetsTexture->Bind (theContext); |
316 | theContext->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, 1); |
317 | #if !defined(GL_ES_VERSION_2_0) |
318 | theContext->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); |
319 | #endif |
320 | theContext->core11fwd->glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, (int )anOffsets.SizeX, (int )anOffsets.SizeY, GL_RG_INTEGER, GL_INT, anOffsets.Data()); |
321 | if (theContext->core11fwd->glGetError() != GL_NO_ERROR) |
3a9b5dc8 |
322 | { |
66d1cdc6 |
323 | hasErrors = true; |
324 | theContext->PushMessage (GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_MEDIUM, |
325 | "Error! Failed to upload tile offset map on the GPU"); |
3a9b5dc8 |
326 | } |
66d1cdc6 |
327 | theOffsetsTexture->Unbind (theContext); |
3a9b5dc8 |
328 | } |
329 | } |
66d1cdc6 |
330 | return !hasErrors; |
3a9b5dc8 |
331 | } |