0025414: Visualization - Optimize ray-tracing performance
[occt.git] / src / OpenGl / OpenGl_SceneGeometry.cxx
1 // Created on: 2013-08-27
2 // Created by: Denis BOGOLEPOV
3 // Copyright (c) 2013 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 #ifdef HAVE_TBB
17   // On Windows, function TryEnterCriticalSection has appeared in Windows NT
18   // and is surrounded by #ifdef in MS VC++ 7.1 headers.
19   // Thus to use it we need to define appropriate macro saying that we will
20   // run on Windows NT 4.0 at least
21   #if defined(_WIN32) && !defined(_WIN32_WINNT)
22     #define _WIN32_WINNT 0x0501
23   #endif
24
25   #include <tbb/tbb.h>
26 #endif
27
28 #include <OpenGl_SceneGeometry.hxx>
29
30 #include <OpenGl_ArbTexBindless.hxx>
31 #include <OpenGl_PrimitiveArray.hxx>
32 #include <OpenGl_Structure.hxx>
33 #include <OSD_Timer.hxx>
34 #include <Standard_Assert.hxx>
35
36 //! Use this macro to output BVH profiling info
37 // #define RAY_TRACE_PRINT_INFO
38
39 namespace
40 {
41   //! Useful constant for null floating-point 4D vector.
42   static const BVH_Vec4f ZERO_VEC_4F;
43 }
44
45 // =======================================================================
46 // function : OpenGl_RaytraceMaterial
47 // purpose  : Creates new default material
48 // =======================================================================
49 OpenGl_RaytraceMaterial::OpenGl_RaytraceMaterial()
50 : Ambient      (ZERO_VEC_4F),
51   Diffuse      (ZERO_VEC_4F),
52   Specular     (ZERO_VEC_4F),
53   Emission     (ZERO_VEC_4F),
54   Reflection   (ZERO_VEC_4F),
55   Refraction   (ZERO_VEC_4F),
56   Transparency (ZERO_VEC_4F)
57 { }
58
59 // =======================================================================
60 // function : OpenGl_RaytraceMaterial
61 // purpose  : Creates new material with specified properties
62 // =======================================================================
63 OpenGl_RaytraceMaterial::OpenGl_RaytraceMaterial (const BVH_Vec4f& theAmbient,
64                                                   const BVH_Vec4f& theDiffuse,
65                                                   const BVH_Vec4f& theSpecular)
66 : Ambient      (theAmbient),
67   Diffuse      (theDiffuse),
68   Specular     (theSpecular),
69   Emission     (ZERO_VEC_4F),
70   Reflection   (ZERO_VEC_4F),
71   Refraction   (ZERO_VEC_4F),
72   Transparency (ZERO_VEC_4F)
73 {
74   //
75 }
76
77 // =======================================================================
78 // function : OpenGl_RaytraceMaterial
79 // purpose  : Creates new material with specified properties
80 // =======================================================================
81 OpenGl_RaytraceMaterial::OpenGl_RaytraceMaterial (const BVH_Vec4f& theAmbient,
82                                                   const BVH_Vec4f& theDiffuse,
83                                                   const BVH_Vec4f& theSpecular,
84                                                   const BVH_Vec4f& theEmission,
85                                                   const BVH_Vec4f& theTranspar)
86 : Ambient      (theAmbient),
87   Diffuse      (theDiffuse),
88   Specular     (theSpecular),
89   Emission     (theEmission),
90   Reflection   (ZERO_VEC_4F),
91   Refraction   (ZERO_VEC_4F),
92   Transparency (theTranspar)
93 {
94   //
95 }
96
97 // =======================================================================
98 // function : OpenGl_RaytraceMaterial
99 // purpose  : Creates new material with specified properties
100 // =======================================================================
101 OpenGl_RaytraceMaterial::OpenGl_RaytraceMaterial (const BVH_Vec4f& theAmbient,
102                                                   const BVH_Vec4f& theDiffuse,
103                                                   const BVH_Vec4f& theSpecular,
104                                                   const BVH_Vec4f& theEmission,
105                                                   const BVH_Vec4f& theTranspar,
106                                                   const BVH_Vec4f& theReflection,
107                                                   const BVH_Vec4f& theRefraction)
108 : Ambient      (theAmbient),
109   Diffuse      (theDiffuse),
110   Specular     (theSpecular),
111   Emission     (theEmission),
112   Reflection   (theReflection),
113   Refraction   (theRefraction),
114   Transparency (theTranspar)
115 {
116   //
117 }
118
119 // =======================================================================
120 // function : OpenGl_LightSource
121 // purpose  : Creates new light source
122 // =======================================================================
123 OpenGl_RaytraceLight::OpenGl_RaytraceLight (const BVH_Vec4f& theDiffuse,
124                                             const BVH_Vec4f& thePosition)
125 : Diffuse (theDiffuse),
126   Position (thePosition)
127 {
128   //
129 }
130
131 // =======================================================================
132 // function : Center
133 // purpose  : Returns centroid position along the given axis
134 // =======================================================================
135 Standard_ShortReal OpenGl_TriangleSet::Center (
136   const Standard_Integer theIndex, const Standard_Integer theAxis) const
137 {
138   // Note: Experiments show that the use of the box centroid (instead
139   // of the triangle centroid) increases render performance up to 12%
140
141   const BVH_Vec4i& aTriangle = Elements[theIndex];
142
143   const Standard_ShortReal aVertex0 =
144     BVH::VecComp<Standard_ShortReal, 3>::Get (Vertices[aTriangle.x()], theAxis);
145   const Standard_ShortReal aVertex1 =
146     BVH::VecComp<Standard_ShortReal, 3>::Get (Vertices[aTriangle.y()], theAxis);
147   const Standard_ShortReal aVertex2 =
148     BVH::VecComp<Standard_ShortReal, 3>::Get (Vertices[aTriangle.z()], theAxis);
149
150   return (Min (Min (aVertex0, aVertex1), aVertex2) +
151           Max (Max (aVertex0, aVertex1), aVertex2)) * 0.5f;
152 }
153
154 // =======================================================================
155 // function : Box
156 // purpose  : Returns AABB of primitive set
157 // =======================================================================
158 OpenGl_TriangleSet::BVH_BoxNt OpenGl_TriangleSet::Box() const
159 {
160   const BVH_Transform<Standard_ShortReal, 4>* aTransform =
161     dynamic_cast<const BVH_Transform<Standard_ShortReal, 4>* > (Properties().operator->());
162
163   BVH_BoxNt aBox = BVH_PrimitiveSet<Standard_ShortReal, 3>::Box();
164
165   if (aTransform != NULL)
166   {
167     BVH_BoxNt aTransformedBox;
168
169     for (Standard_Integer aX = 0; aX <= 1; ++aX)
170     {
171       for (Standard_Integer aY = 0; aY <= 1; ++aY)
172       {
173         for (Standard_Integer aZ = 0; aZ <= 1; ++aZ)
174         {
175           BVH_Vec4f aCorner = aTransform->Transform() * BVH_Vec4f (
176             aX == 0 ? aBox.CornerMin().x() : aBox.CornerMax().x(),
177             aY == 0 ? aBox.CornerMin().y() : aBox.CornerMax().y(),
178             aZ == 0 ? aBox.CornerMin().z() : aBox.CornerMax().z(),
179             1.f);
180
181           aTransformedBox.Add (reinterpret_cast<BVH_Vec3f&> (aCorner));
182         }
183       }
184     }
185
186     return aTransformedBox;
187   }
188
189   return aBox;
190 }
191
192 // =======================================================================
193 // function : Clear
194 // purpose  : Clears ray-tracing geometry
195 // =======================================================================
196 void OpenGl_RaytraceGeometry::Clear()
197 {
198   BVH_Geometry<Standard_ShortReal, 3>::BVH_Geometry::Clear();
199
200   std::vector<OpenGl_RaytraceLight,
201     NCollection_StdAllocator<OpenGl_RaytraceLight> > anEmptySources;
202
203   Sources.swap (anEmptySources);
204
205   std::vector<OpenGl_RaytraceMaterial,
206     NCollection_StdAllocator<OpenGl_RaytraceMaterial> > anEmptyMaterials;
207
208   Materials.swap (anEmptyMaterials);
209 }
210
211 #ifdef HAVE_TBB
212
213 struct OpenGL_BVHParallelBuilder
214 {
215   BVH_ObjectSet<Standard_ShortReal, 3>* Set;
216
217   OpenGL_BVHParallelBuilder (BVH_ObjectSet<Standard_ShortReal, 3>* theSet)
218     : Set (theSet)
219   {
220     //
221   }
222
223   void operator() (const tbb::blocked_range<size_t>& theRange) const
224   {
225     for (size_t anObjectIdx = theRange.begin(); anObjectIdx != theRange.end(); ++anObjectIdx)
226     {
227       OpenGl_TriangleSet* aTriangleSet = dynamic_cast<OpenGl_TriangleSet*> (
228         Set->Objects().ChangeValue (static_cast<Standard_Integer> (anObjectIdx)).operator->());
229
230       if (aTriangleSet != NULL)
231       {
232         aTriangleSet->BVH();
233       }
234     }
235   }
236 };
237
238 #endif
239
240 // =======================================================================
241 // function : ProcessAcceleration
242 // purpose  : Performs post-processing of high-level BVH
243 // =======================================================================
244 Standard_Boolean OpenGl_RaytraceGeometry::ProcessAcceleration()
245 {
246 #ifdef RAY_TRACE_PRINT_INFO
247     OSD_Timer aTimer;
248 #endif
249
250   MarkDirty(); // force BVH rebuilding
251
252 #ifdef RAY_TRACE_PRINT_INFO
253   aTimer.Reset();
254   aTimer.Start();
255 #endif
256
257 #ifdef HAVE_TBB
258   // If Intel TBB is available, perform the preliminary
259   // construction of bottom-level scene BVHs
260   tbb::parallel_for (tbb::blocked_range<size_t> (0, Size()),
261     OpenGL_BVHParallelBuilder (this));
262 #endif
263
264   myBottomLevelTreeDepth = 0;
265
266   for (Standard_Integer anObjectIdx = 0; anObjectIdx < Size(); ++anObjectIdx)
267   {
268     OpenGl_TriangleSet* aTriangleSet = dynamic_cast<OpenGl_TriangleSet*> (
269       myObjects.ChangeValue (anObjectIdx).operator->());
270
271     Standard_ASSERT_RETURN (aTriangleSet != NULL,
272       "Error! Failed to get triangulation of OpenGL element", Standard_False);
273
274     Standard_ASSERT_RETURN (!aTriangleSet->BVH().IsNull(),
275       "Error! Failed to update bottom-level BVH of OpenGL element", Standard_False);
276
277     myBottomLevelTreeDepth = Max (myBottomLevelTreeDepth, aTriangleSet->BVH()->Depth());
278   }
279
280 #ifdef RAY_TRACE_PRINT_INFO
281   aTimer.Stop();
282
283   std::cout << "Updating bottom-level BVHs (sec): " <<
284     aTimer.ElapsedTime() << std::endl;
285 #endif
286
287 #ifdef RAY_TRACE_PRINT_INFO
288   aTimer.Reset();
289   aTimer.Start();
290 #endif
291
292   NCollection_Handle<BVH_Tree<Standard_ShortReal, 3> > aBVH = BVH();
293
294 #ifdef RAY_TRACE_PRINT_INFO
295   aTimer.Stop();
296
297   std::cout << "Updating high-level BVH (sec): " <<
298     aTimer.ElapsedTime() << std::endl;
299 #endif
300
301   Standard_ASSERT_RETURN (!aBVH.IsNull(),
302     "Error! Failed to update high-level BVH of ray-tracing scene", Standard_False);
303
304   myHighLevelTreeDepth = aBVH->Depth();
305
306   Standard_Integer aVerticesOffset = 0;
307   Standard_Integer aElementsOffset = 0;
308   Standard_Integer aBVHNodesOffset = 0;
309
310   for (Standard_Integer aNodeIdx = 0; aNodeIdx < aBVH->Length(); ++aNodeIdx)
311   {
312     if (!aBVH->IsOuter (aNodeIdx))
313       continue;
314
315     Standard_ASSERT_RETURN (aBVH->BegPrimitive (aNodeIdx) == aBVH->EndPrimitive (aNodeIdx),
316       "Error! Invalid leaf node in high-level BVH (contains several objects)", Standard_False);
317
318     Standard_Integer anObjectIdx = aBVH->BegPrimitive (aNodeIdx);
319
320     Standard_ASSERT_RETURN (anObjectIdx < myObjects.Size(),
321       "Error! Invalid leaf node in high-level BVH (contains out-of-range object)", Standard_False);
322
323     OpenGl_TriangleSet* aTriangleSet = dynamic_cast<OpenGl_TriangleSet*> (
324       myObjects.ChangeValue (anObjectIdx).operator->());
325
326     // Note: We overwrite node info record to store parameters
327     // of bottom-level BVH and triangulation of OpenGL element
328
329     aBVH->NodeInfoBuffer().at (aNodeIdx) = BVH_Vec4i (
330       anObjectIdx + 1 /* to keep leaf flag */, aBVHNodesOffset, aVerticesOffset, aElementsOffset);
331
332     aVerticesOffset += (int)aTriangleSet->Vertices.size();
333     aElementsOffset += (int)aTriangleSet->Elements.size();
334     aBVHNodesOffset += aTriangleSet->BVH()->Length();
335   }
336
337   return Standard_True;
338 }
339
340 // =======================================================================
341 // function : AccelerationOffset
342 // purpose  : Returns offset of bottom-level BVH for given leaf node
343 // =======================================================================
344 Standard_Integer OpenGl_RaytraceGeometry::AccelerationOffset (Standard_Integer theNodeIdx)
345 {
346   const NCollection_Handle<BVH_Tree<Standard_ShortReal, 3> >& aBVH = BVH();
347
348   if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
349     return INVALID_OFFSET;
350
351   return aBVH->NodeInfoBuffer().at (theNodeIdx).y();
352 }
353
354 // =======================================================================
355 // function : VerticesOffset
356 // purpose  : Returns offset of triangulation vertices for given leaf node
357 // =======================================================================
358 Standard_Integer OpenGl_RaytraceGeometry::VerticesOffset (Standard_Integer theNodeIdx)
359 {
360   const NCollection_Handle<BVH_Tree<Standard_ShortReal, 3> >& aBVH = BVH();
361
362   if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
363     return INVALID_OFFSET;
364
365   return aBVH->NodeInfoBuffer().at (theNodeIdx).z();
366 }
367
368 // =======================================================================
369 // function : ElementsOffset
370 // purpose  : Returns offset of triangulation elements for given leaf node
371 // =======================================================================
372 Standard_Integer OpenGl_RaytraceGeometry::ElementsOffset (Standard_Integer theNodeIdx)
373 {
374   const NCollection_Handle<BVH_Tree<Standard_ShortReal, 3> >& aBVH = BVH();
375
376   if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
377     return INVALID_OFFSET;
378
379   return aBVH->NodeInfoBuffer().at (theNodeIdx).w();
380 }
381
382 // =======================================================================
383 // function : TriangleSet
384 // purpose  : Returns triangulation data for given leaf node
385 // =======================================================================
386 OpenGl_TriangleSet* OpenGl_RaytraceGeometry::TriangleSet (Standard_Integer theNodeIdx)
387 {
388   const NCollection_Handle<BVH_Tree<Standard_ShortReal, 3> >& aBVH = BVH();
389
390   if (theNodeIdx >= aBVH->Length() || !aBVH->IsOuter (theNodeIdx))
391     return NULL;
392
393   if (aBVH->NodeInfoBuffer().at (theNodeIdx).x() > myObjects.Size())
394     return NULL;
395
396   return dynamic_cast<OpenGl_TriangleSet*> (myObjects.ChangeValue (
397     aBVH->NodeInfoBuffer().at (theNodeIdx).x() - 1).operator->());
398 }
399
400 // =======================================================================
401 // function : AcquireTextures
402 // purpose  : Makes the OpenGL texture handles resident
403 // =======================================================================
404 Standard_Boolean OpenGl_RaytraceGeometry::AcquireTextures (const Handle(OpenGl_Context)& theContext) const
405 {
406   if (theContext->arbTexBindless == NULL)
407   {
408     return Standard_True;
409   }
410
411 #if !defined(GL_ES_VERSION_2_0)
412   for (Standard_Integer anIdx = 0; anIdx < myTextures.Size(); ++anIdx)
413   {
414     theContext->arbTexBindless->glMakeTextureHandleResidentARB (myTextureHandles[anIdx]);
415
416     if (glGetError() != GL_NO_ERROR)
417     {
418 #ifdef RAY_TRACE_PRINT_INFO
419       std::cout << "Error: Failed to make OpenGL texture resident" << std::endl;
420 #endif
421       return Standard_False;
422     }
423   }
424 #endif
425
426   return Standard_True;
427 }
428
429 // =======================================================================
430 // function : ReleaseTextures
431 // purpose  : Makes the OpenGL texture handles non-resident
432 // =======================================================================
433 Standard_Boolean OpenGl_RaytraceGeometry::ReleaseTextures (const Handle(OpenGl_Context)& theContext) const
434 {
435   if (theContext->arbTexBindless == NULL)
436   {
437     return Standard_True;
438   }
439
440 #if !defined(GL_ES_VERSION_2_0)
441   for (Standard_Integer anIdx = 0; anIdx < myTextures.Size(); ++anIdx)
442   {
443     theContext->arbTexBindless->glMakeTextureHandleNonResidentARB (myTextureHandles[anIdx]);
444
445     if (glGetError() != GL_NO_ERROR)
446     {
447 #ifdef RAY_TRACE_PRINT_INFO
448       std::cout << "Error: Failed to make OpenGL texture non-resident" << std::endl;
449 #endif
450       return Standard_False;
451     }
452   }
453 #endif
454
455   return Standard_True;
456 }
457
458 // =======================================================================
459 // function : AddTexture
460 // purpose  : Adds new OpenGL texture to the scene and returns its index
461 // =======================================================================
462 Standard_Integer OpenGl_RaytraceGeometry::AddTexture (const Handle(OpenGl_Texture)& theTexture)
463 {
464   NCollection_Vector<Handle (OpenGl_Texture)>::iterator anIter =
465     std::find (myTextures.begin(), myTextures.end(), theTexture);
466
467   if (anIter == myTextures.end())
468   {
469     if (myTextures.Size() >= MAX_TEX_NUMBER)
470     {
471       return -1;
472     }
473
474     myTextures.Append (theTexture);
475   }
476
477   return static_cast<Standard_Integer> (anIter - myTextures.begin());
478 }
479
480 // =======================================================================
481 // function : UpdateTextureHandles
482 // purpose  : Updates unique 64-bit texture handles to use in shaders
483 // =======================================================================
484 Standard_Boolean OpenGl_RaytraceGeometry::UpdateTextureHandles (const Handle(OpenGl_Context)& theContext)
485 {
486   if (theContext->arbTexBindless == NULL)
487   {
488     return Standard_False;
489   }
490
491   myTextureHandles.clear();
492
493 #if !defined(GL_ES_VERSION_2_0)
494   for (Standard_Integer anIdx = 0; anIdx < myTextures.Size(); ++anIdx)
495   {
496     const GLuint64 aHandle = theContext->arbTexBindless->glGetTextureHandleARB (
497       myTextures.Value (anIdx)->TextureId());
498
499     if (glGetError() != GL_NO_ERROR)
500     {
501 #ifdef RAY_TRACE_PRINT_INFO
502       std::cout << "Error: Failed to get 64-bit handle of OpenGL texture" << std::endl;
503 #endif
504       return Standard_False;
505     }
506
507     myTextureHandles.push_back (aHandle);
508   }
509 #endif
510
511   return Standard_True;
512 }
513
514 namespace OpenGl_Raytrace
515 {
516   // =======================================================================
517   // function : IsRaytracedElement
518   // purpose  : Checks to see if the element contains ray-trace geometry
519   // =======================================================================
520   Standard_Boolean IsRaytracedElement (const OpenGl_ElementNode* theNode)
521   {
522     OpenGl_PrimitiveArray* anArray = dynamic_cast<OpenGl_PrimitiveArray*> (theNode->elem);
523     return anArray != NULL
524         && anArray->DrawMode() >= GL_TRIANGLES;
525   }
526
527   // =======================================================================
528   // function : IsRaytracedElement
529   // purpose  : Checks to see if the element contains ray-trace geometry
530   // =======================================================================
531   Standard_Boolean IsRaytracedElement (const OpenGl_Element* theElement)
532   {
533     const OpenGl_PrimitiveArray* anArray = dynamic_cast<const OpenGl_PrimitiveArray*> (theElement);
534     return anArray != NULL
535         && anArray->DrawMode() >= GL_TRIANGLES;
536   }
537
538   // =======================================================================
539   // function : IsRaytracedGroup
540   // purpose  : Checks to see if the group contains ray-trace geometry
541   // =======================================================================
542   Standard_Boolean IsRaytracedGroup (const OpenGl_Group *theGroup)
543   {
544     const OpenGl_ElementNode* aNode;
545     for (aNode = theGroup->FirstNode(); aNode != NULL; aNode = aNode->next)
546     {
547       if (IsRaytracedElement (aNode))
548       {
549         return Standard_True;
550       }
551     }
552     return Standard_False;
553   }
554
555   // =======================================================================
556   // function : IsRaytracedStructure
557   // purpose  : Checks to see if the structure contains ray-trace geometry
558   // =======================================================================
559   Standard_Boolean IsRaytracedStructure (const OpenGl_Structure* theStructure)
560   {
561     for (OpenGl_Structure::GroupIterator aGroupIter (theStructure->DrawGroups());
562          aGroupIter.More(); aGroupIter.Next())
563     {
564       if (aGroupIter.Value()->IsRaytracable())
565         return Standard_True;
566     }
567     for (OpenGl_ListOfStructure::Iterator anIts (theStructure->ConnectedStructures());
568          anIts.More(); anIts.Next())
569     {
570       if (IsRaytracedStructure (anIts.Value()))
571         return Standard_True;
572     }
573     return Standard_False;
574   }
575 }