0030483: Visualization, Path Tracing - make Tile Size configurable
[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
3a9b5dc8 21//=======================================================================
22//function : OpenGl_TileSampler
23//purpose :
24//=======================================================================
25OpenGl_TileSampler::OpenGl_TileSampler()
66d1cdc6 26: myLastSample (0),
27 myScaleFactor(1.0f),
28 myTileSize (0),
29 myViewSize (0, 0)
3a9b5dc8 30{
31 mySampler.initFaure();
32}
33
34//=======================================================================
35//function : GrabVarianceMap
36//purpose :
37//=======================================================================
66d1cdc6 38void OpenGl_TileSampler::GrabVarianceMap (const Handle(OpenGl_Context)& theContext,
39 const Handle(OpenGl_Texture)& theTexture)
3a9b5dc8 40{
66d1cdc6 41 if (theTexture.IsNull())
42 {
43 return;
44 }
3a9b5dc8 45
66d1cdc6 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());
3a9b5dc8 52 const GLenum anErr = theContext->core11fwd->glGetError();
66d1cdc6 53 theTexture->Unbind (theContext);
3a9b5dc8 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");
66d1cdc6 58 return;
3a9b5dc8 59 }
66d1cdc6 60#else
61 // glGetTexImage() is unavailable on OpenGL ES, FBO + glReadPixels() can be used instead
62 (void )theContext;
63#endif
3a9b5dc8 64
66d1cdc6 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)
3a9b5dc8 69 {
66d1cdc6 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)
3a9b5dc8 75 {
66d1cdc6 76 aTile += myVarianceMap.Value (aRowIter - 1, aColIter);
3a9b5dc8 77 }
78 }
66d1cdc6 79 }
3a9b5dc8 80
66d1cdc6 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)
3a9b5dc8 86 {
66d1cdc6 87 myMarginalMap[aX] += myMarginalMap[aX - 1];
3a9b5dc8 88 }
89 }
3a9b5dc8 90}
91
92//=======================================================================
66d1cdc6 93//function : nextTileToSample
3a9b5dc8 94//purpose :
95//=======================================================================
66d1cdc6 96Graphic3d_Vec2i OpenGl_TileSampler::nextTileToSample()
3a9b5dc8 97{
66d1cdc6 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())
3a9b5dc8 101 {
66d1cdc6 102 if (aKsiX <= myMarginalMap[aTile.x()])
3a9b5dc8 103 {
104 break;
105 }
106 }
107
66d1cdc6 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())
3a9b5dc8 110 {
66d1cdc6 111 if (aKsiY <= myVarianceMap.Value (aTile.y(), aTile.x()))
3a9b5dc8 112 {
113 break;
114 }
115 }
116
66d1cdc6 117 ++myLastSample;
118 return aTile;
3a9b5dc8 119}
120
121//=======================================================================
122//function : SetSize
123//purpose :
124//=======================================================================
66d1cdc6 125void OpenGl_TileSampler::SetSize (const Graphic3d_RenderingParams& theParams,
126 const Graphic3d_Vec2i& theSize)
3a9b5dc8 127{
66d1cdc6 128 if (theSize.x() <= 0
129 || theSize.y() <= 0)
3a9b5dc8 130 {
131 return;
132 }
133
66d1cdc6 134 myViewSize = theSize;
3a9b5dc8 135
66d1cdc6 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));
3a9b5dc8 145
66d1cdc6 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 }
3a9b5dc8 185}
186
187//=======================================================================
66d1cdc6 188//function : UploadOffsets
3a9b5dc8 189//purpose :
190//=======================================================================
66d1cdc6 191bool OpenGl_TileSampler::UploadOffsets (const Handle(OpenGl_Context)& theContext,
192 const Handle(OpenGl_Texture)& theOffsetsTexture,
193 const bool theAdaptive)
3a9b5dc8 194{
66d1cdc6 195 if (myTiles.IsEmpty())
3a9b5dc8 196 {
66d1cdc6 197 return false;
3a9b5dc8 198 }
199
66d1cdc6 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 }
4eaaf9d8 212
66d1cdc6 213 bool hasErrors = false;
214 if (!theOffsetsTexture.IsNull())
3a9b5dc8 215 {
66d1cdc6 216 if (theOffsetsTexture->SizeX() != (int )anOffsets.SizeX
217 || theOffsetsTexture->SizeY() != (int )anOffsets.SizeY
218 || !theOffsetsTexture->IsValid())
3a9b5dc8 219 {
66d1cdc6 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))
3a9b5dc8 223 {
66d1cdc6 224 hasErrors = true;
3a9b5dc8 225 }
66d1cdc6 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)
3a9b5dc8 236 {
66d1cdc6 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");
3a9b5dc8 240 }
66d1cdc6 241 theOffsetsTexture->Unbind (theContext);
3a9b5dc8 242 }
243 }
66d1cdc6 244 return !hasErrors;
3a9b5dc8 245}