0032349: Visualization, TKOpenGl - move base buffer interface out from OpenGl_VertexB...
[occt.git] / src / OpenGl / OpenGl_FrameStatsPrs.cxx
1 // Copyright (c) 2017 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 #include <OpenGl_FrameStatsPrs.hxx>
15
16 #include <OpenGl_View.hxx>
17 #include <OpenGl_IndexBuffer.hxx>
18 #include <OpenGl_VertexBuffer.hxx>
19 #include <OpenGl_ShaderManager.hxx>
20 #include <OpenGl_Workspace.hxx>
21
22 #include <Graphic3d_ArrayOfTriangles.hxx>
23
24 namespace
25 {
26   //! Auxiliary structure defining vertex with two attributes.
27   struct OpenGl_Vec3Vec4ub
28   {
29     Graphic3d_Vec3   Pos;
30     Graphic3d_Vec4ub Color;
31   };
32
33   //! Auxiliary function formatting rendering time in " 10 ms (100 FPS)" format.
34   static TCollection_AsciiString formatTimeMs (Standard_Real theSeconds)
35   {
36     const Standard_Real aFpsVal = theSeconds != 0.0 ? 1.0 / theSeconds : 0.0;
37     char aFps[50];
38     Sprintf (aFps, "%.1f", aFpsVal);
39     return TCollection_AsciiString() + Standard_Integer(theSeconds * 1000.0) + " ms (" + aFps + " FPS)";
40   }
41 }
42
43 // =======================================================================
44 // function : OpenGl_FrameStatsPrs
45 // purpose  :
46 // =======================================================================
47 OpenGl_FrameStatsPrs::OpenGl_FrameStatsPrs()
48 : myStatsPrev (new OpenGl_FrameStats()),
49   myCountersTrsfPers (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_LEFT_UPPER,  Graphic3d_Vec2i (20, 20))),
50   myChartTrsfPers    (new Graphic3d_TransformPers (Graphic3d_TMF_2d, Aspect_TOTP_RIGHT_UPPER, Graphic3d_Vec2i (20, 20))),
51   myChartVertices (new OpenGl_VertexBuffer()),
52   myChartIndices (new OpenGl_IndexBuffer()),
53   myChartLines (new OpenGl_VertexBuffer())
54 {
55   //
56 }
57
58 // =======================================================================
59 // function : ~OpenGl_FrameStatsPrs
60 // purpose  :
61 // =======================================================================
62 OpenGl_FrameStatsPrs::~OpenGl_FrameStatsPrs()
63 {
64   //
65 }
66
67 // =======================================================================
68 // function : Release
69 // purpose  :
70 // =======================================================================
71 void OpenGl_FrameStatsPrs::Release (OpenGl_Context* theCtx)
72 {
73   myCountersText.Release (theCtx);
74   myChartLabels[0].Release (theCtx);
75   myChartLabels[1].Release (theCtx);
76   myChartLabels[2].Release (theCtx);
77   myChartVertices->Release (theCtx);
78   myChartIndices->Release (theCtx);
79   myChartLines->Release (theCtx);
80 }
81
82 // =======================================================================
83 // function : Update
84 // purpose  :
85 // =======================================================================
86 void OpenGl_FrameStatsPrs::Update (const Handle(OpenGl_Workspace)& theWorkspace)
87 {
88   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
89   const Handle(OpenGl_FrameStats)& aStats = aCtx->FrameStats();
90   const Graphic3d_RenderingParams& aRendParams = theWorkspace->View()->RenderingParams();
91   myCountersTrsfPers = theWorkspace->View()->RenderingParams().StatsPosition;
92   myChartTrsfPers    = theWorkspace->View()->RenderingParams().ChartPosition;
93   myTextAspect.SetAspect (aRendParams.StatsTextAspect);
94
95   // adjust text alignment depending on corner
96   Graphic3d_Text aParams ((Standard_ShortReal)aRendParams.StatsTextHeight);
97   aParams.SetHorizontalAlignment (Graphic3d_HTA_CENTER);
98   aParams.SetVerticalAlignment (Graphic3d_VTA_CENTER);
99   if (!myCountersTrsfPers.IsNull() && (myCountersTrsfPers->Corner2d() & Aspect_TOTP_LEFT) != 0)
100   {
101     aParams.SetHorizontalAlignment (Graphic3d_HTA_LEFT);
102   }
103   else if (!myCountersTrsfPers.IsNull() && (myCountersTrsfPers->Corner2d() & Aspect_TOTP_RIGHT) != 0)
104   {
105     aParams.SetHorizontalAlignment (Graphic3d_HTA_RIGHT);
106   }
107   if (!myCountersTrsfPers.IsNull() && (myCountersTrsfPers->Corner2d() & Aspect_TOTP_TOP) != 0)
108   {
109     aParams.SetVerticalAlignment (Graphic3d_VTA_TOP);
110   }
111   else if (!myCountersTrsfPers.IsNull() && (myCountersTrsfPers->Corner2d() & Aspect_TOTP_BOTTOM) != 0)
112   {
113     aParams.SetVerticalAlignment (Graphic3d_VTA_BOTTOM);
114   }
115   if (aParams.Height() != myCountersText.Text()->Height()
116    || aParams.HorizontalAlignment() != myCountersText.Text()->HorizontalAlignment()
117    || aParams.VerticalAlignment() != myCountersText.Text()->VerticalAlignment())
118   {
119     myCountersText.Release (aCtx.operator->());
120   }
121
122   if (!aStats->IsFrameUpdated (myStatsPrev)
123    && !myCountersText.Text()->Text().IsEmpty())
124   {
125     return;
126   }
127
128   Handle(Graphic3d_Text) aText = myCountersText.Text();
129   aText->SetText (aStats->FormatStats (aRendParams.CollectedStats).ToCString());
130   aText->SetHeight (aParams.Height());
131   aText->SetPosition (gp_Pnt());
132   aText->SetHorizontalAlignment (aParams.HorizontalAlignment());
133   aText->SetVerticalAlignment (aParams.VerticalAlignment());
134   myCountersText.Reset (aCtx);
135
136   updateChart (theWorkspace);
137 }
138
139 // =======================================================================
140 // function : updateChart
141 // purpose  :
142 // =======================================================================
143 void OpenGl_FrameStatsPrs::updateChart (const Handle(OpenGl_Workspace)& theWorkspace)
144 {
145   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
146   const Handle(OpenGl_FrameStats)& aStats = aCtx->FrameStats();
147   const Graphic3d_RenderingParams& aRendParams = theWorkspace->View()->RenderingParams();
148
149   const Standard_Integer aNbBins = aStats->DataFrames().Size();
150   if (aNbBins <= 1)
151   {
152     myChartIndices ->Release (aCtx.get());
153     myChartVertices->Release (aCtx.get());
154     myChartLines   ->Release (aCtx.get());
155     return;
156   }
157
158   Standard_Real aMaxDuration = aRendParams.StatsMaxChartTime;
159   if (aMaxDuration <= 0.0f)
160   {
161     for (Standard_Integer aFrameIter = aStats->DataFrames().Lower(); aFrameIter <= aStats->DataFrames().Upper(); ++aFrameIter)
162     {
163       const Graphic3d_FrameStatsData& aFrame = aStats->DataFrames().Value (aFrameIter);
164       aMaxDuration = Max (aMaxDuration, aFrame.TimerValue (Graphic3d_FrameStatsTimer_ElapsedFrame));
165     }
166     aMaxDuration = Ceiling (aMaxDuration * 1000.0 * 0.1) * 0.001 * 10.0; // round number
167     aMaxDuration = Max (Min (aMaxDuration, 0.1), 0.005); // limit by 100 ms (10 FPS) and 5 ms (200 FPS)
168   }
169
170   const Standard_Integer aNbTimers = 4;
171   const Graphic3d_FrameStatsTimer aTimers[4] =
172   {
173     Graphic3d_FrameStatsTimer_CpuDynamics,
174     Graphic3d_FrameStatsTimer_CpuPicking,
175     Graphic3d_FrameStatsTimer_CpuCulling,
176     Graphic3d_FrameStatsTimer_ElapsedFrame,
177   };
178   const Graphic3d_Vec4ub aColors[4] =
179   {
180     Graphic3d_Vec4ub (255, 0, 0, 127),
181     Graphic3d_Vec4ub (255, 127, 39, 127),
182     Graphic3d_Vec4ub (255, 0, 0, 127),
183     Graphic3d_Vec4ub (0, 255, 0, 127),
184   };
185
186   const Standard_Integer aNbVerts   = aNbBins * 4 * aNbTimers;
187   const Standard_Integer aNbIndexes = aNbBins * 2 * 3 * aNbTimers;
188   bool toFillEdges = false;
189   if (myChartArray.IsNull()
190    || myChartArray->VertexNumber() != aNbVerts
191    || myChartArray->EdgeNumber()   != aNbIndexes)
192   {
193     myChartArray = new Graphic3d_ArrayOfTriangles (aNbVerts, aNbIndexes, false, true);
194     toFillEdges = true;
195   }
196
197   const Graphic3d_Vec2i aViewSize (aCtx->VirtualViewport()[2], aCtx->VirtualViewport()[3]);
198   Graphic3d_Vec2i aCharSize (aRendParams.ChartSize);
199   if (aCharSize.x() <= 0)
200   {
201     aCharSize.x() = aViewSize.x() / 2;
202   }
203   if (aCharSize.y() <= 0)
204   {
205     aCharSize.y() = Standard_Integer(0.15 * aViewSize.y());
206   }
207
208   const Graphic3d_Vec2d aBinSize  (Standard_Real(aCharSize.x()) / Standard_Real(aNbBins), 0.15 * aViewSize.y());
209   Graphic3d_Vec2i anOffset;
210   if (!myChartTrsfPers.IsNull()
211     && myChartTrsfPers->IsTrihedronOr2d())
212   {
213     if ((myChartTrsfPers->Corner2d() & Aspect_TOTP_LEFT) != 0)
214     {
215       anOffset.x() = 0;
216     }
217     else if ((myChartTrsfPers->Corner2d() & Aspect_TOTP_RIGHT) != 0)
218     {
219       anOffset.x() = -aCharSize.x();
220     }
221     else
222     {
223       anOffset.x() = -aCharSize.x() / 2;
224     }
225
226     if ((myChartTrsfPers->Corner2d() & Aspect_TOTP_BOTTOM) != 0)
227     {
228       anOffset.y() = aCharSize.y();
229     }
230     else if ((myChartTrsfPers->Corner2d() & Aspect_TOTP_TOP) != 0)
231     {
232       anOffset.y() = 0;
233     }
234     else
235     {
236       anOffset.y() = aCharSize.y() / 2;
237     }
238   }
239
240   Standard_Integer aVertLast = 1;
241   const bool isTopDown = false;
242   for (Standard_Integer aFrameIter = 0; aFrameIter < aNbBins; ++aFrameIter)
243   {
244     Standard_Integer aFrameIndex = aStats->DataFrames().Lower() + aStats->LastDataFrameIndex() + 1 + aFrameIter;
245     if (aFrameIndex > aStats->DataFrames().Upper())
246     {
247       aFrameIndex -= aNbBins;
248     }
249
250     const Graphic3d_FrameStatsData& aFrame = aStats->DataFrames().Value (aFrameIndex);
251     Standard_Real aTimeElapsed = 0.0;
252     Standard_Real aCurrY = 0.0;
253     for (Standard_Integer aTimerIter = 0; aTimerIter < aNbTimers; ++aTimerIter)
254     {
255       if (aTimers[aTimerIter] == Graphic3d_FrameStatsTimer_ElapsedFrame)
256       {
257         aTimeElapsed = aFrame.TimerValue (aTimers[aTimerIter]);
258       }
259       else
260       {
261         aTimeElapsed += aFrame.TimerValue (aTimers[aTimerIter]);
262       }
263
264       const Standard_Real aBinX1 = anOffset.x() + Standard_Real(aFrameIter) * aBinSize.x();
265       const Standard_Real aBinX2 = aBinX1 + aBinSize.x();
266       const Standard_Real aCurrSizeY = Min (aTimeElapsed / aMaxDuration, 1.2) * aBinSize.y();
267       const Standard_Real aBinY1 = isTopDown ? (anOffset.y() - aCurrY)     : (anOffset.y() - aBinSize.y() + aCurrY);
268       const Standard_Real aBinY2 = isTopDown ? (anOffset.y() - aCurrSizeY) : (anOffset.y() - aBinSize.y() + aCurrSizeY);
269       myChartArray->SetVertice (aVertLast + 0, gp_Pnt (aBinX1, aBinY2, 0.0));
270       myChartArray->SetVertice (aVertLast + 1, gp_Pnt (aBinX1, aBinY1, 0.0));
271       myChartArray->SetVertice (aVertLast + 2, gp_Pnt (aBinX2, aBinY1, 0.0));
272       myChartArray->SetVertice (aVertLast + 3, gp_Pnt (aBinX2, aBinY2, 0.0));
273
274       if (toFillEdges)
275       {
276         const Graphic3d_Vec4ub& aTimerColor = aColors[aTimerIter];
277         myChartArray->SetVertexColor (aVertLast + 0, aTimerColor);
278         myChartArray->SetVertexColor (aVertLast + 1, aTimerColor);
279         myChartArray->SetVertexColor (aVertLast + 2, aTimerColor);
280         myChartArray->SetVertexColor (aVertLast + 3, aTimerColor);
281         myChartArray->AddEdges (aVertLast + 0, aVertLast + 1, aVertLast + 3);
282         myChartArray->AddEdges (aVertLast + 1, aVertLast + 2, aVertLast + 3);
283       }
284       aVertLast += 4;
285
286       if (aTimers[aTimerIter] == Graphic3d_FrameStatsTimer_ElapsedFrame)
287       {
288         aTimeElapsed = 0.0;
289         aCurrY = 0.0;
290       }
291       else
292       {
293         aCurrY = aCurrSizeY;
294       }
295     }
296   }
297
298   myChartVertices->init (aCtx,
299                          myChartArray->Attributes()->Stride,
300                          myChartArray->Attributes()->NbElements,
301                          myChartArray->Attributes()->Data(),
302                          GL_UNSIGNED_BYTE,
303                          myChartArray->Attributes()->Stride);
304   if (myChartArray->Indices()->Stride == 2)
305   {
306     myChartIndices ->Init (aCtx,
307                            1,
308                            myChartArray->Indices()->NbElements,
309                            (const GLushort* )myChartArray->Indices()->Data());
310   }
311   else if (myChartArray->Indices()->Stride == 4)
312   {
313     myChartIndices ->Init (aCtx,
314                            1,
315                            myChartArray->Indices()->NbElements,
316                            (const GLuint* )myChartArray->Indices()->Data());
317   }
318
319   {
320     const Graphic3d_Vec4ub aWhite (255, 255, 255, 255);
321     const OpenGl_Vec3Vec4ub aLines[4] =
322     {
323       { Graphic3d_Vec3((float )anOffset.x(), (float )anOffset.y(),                0.0f), aWhite },
324       { Graphic3d_Vec3(float(anOffset.x() + aCharSize.x()), (float )anOffset.y(), 0.0f), aWhite },
325       { Graphic3d_Vec3((float )anOffset.x(), float(anOffset.y() - aBinSize.y()),  0.0f), aWhite },
326       { Graphic3d_Vec3(float(anOffset.x() + aCharSize.x()), float(anOffset.y() - aBinSize.y()),+ 0.0f), aWhite },
327     };
328     myChartLines->init (aCtx, sizeof(OpenGl_Vec3Vec4ub), 4, aLines, GL_UNSIGNED_BYTE, sizeof(OpenGl_Vec3Vec4ub));
329   }
330
331   {
332     Graphic3d_Text aParams ((Standard_ShortReal)aRendParams.StatsTextHeight);
333     aParams.SetHorizontalAlignment ((!myChartTrsfPers.IsNull()
334                     && myChartTrsfPers->IsTrihedronOr2d()
335                     && (myChartTrsfPers->Corner2d() & Aspect_TOTP_RIGHT) != 0)
336                     ? Graphic3d_HTA_RIGHT
337                     : Graphic3d_HTA_LEFT);
338     aParams.SetVerticalAlignment (Graphic3d_VTA_CENTER);
339     TCollection_AsciiString aLabels[3] =
340     {
341       TCollection_AsciiString() + 0 + " ms",
342       formatTimeMs(aMaxDuration * 0.5),
343       formatTimeMs(aMaxDuration)
344     };
345
346     const float aLabX = aParams.HorizontalAlignment() == Graphic3d_HTA_RIGHT
347                       ? float(anOffset.x())
348                       : float(anOffset.x() + aCharSize.x());
349
350     myChartLabels[0].Text()->SetText (aLabels[isTopDown ? 0 : 2].ToCString());
351     myChartLabels[0].Text()->SetPosition (gp_Pnt (aLabX, float(anOffset.y()), 0.0f));
352
353     myChartLabels[1].Text()->SetText (aLabels[isTopDown ? 1 : 1].ToCString());
354     myChartLabels[1].Text()->SetPosition (gp_Pnt (aLabX, float(anOffset.y() - aBinSize.y() / 2), 0.0f));
355
356     myChartLabels[2].Text()->SetText (aLabels[isTopDown ? 2 : 0].ToCString());
357     myChartLabels[2].Text()->SetPosition (gp_Pnt (aLabX, float(anOffset.y() - aBinSize.y()), 0.0f));
358
359     for (int i = 0; i < 3; i++)
360     {
361       myChartLabels[i].Text()->SetHeight (aParams.Height());
362       myChartLabels[i].Text()->SetHorizontalAlignment (aParams.HorizontalAlignment());
363       myChartLabels[i].Text()->SetVerticalAlignment (aParams.VerticalAlignment());
364
365       myChartLabels[i].Reset(aCtx);
366     }
367   }
368 }
369
370 // =======================================================================
371 // function : Render
372 // purpose  :
373 // =======================================================================
374 void OpenGl_FrameStatsPrs::Render (const Handle(OpenGl_Workspace)& theWorkspace) const
375 {
376   const Handle(OpenGl_Context)& aCtx = theWorkspace->GetGlContext();
377   const Standard_Boolean wasEnabledDepth = theWorkspace->UseDepthWrite();
378   if (theWorkspace->UseDepthWrite())
379   {
380     theWorkspace->UseDepthWrite() = Standard_False;
381     glDepthMask (GL_FALSE);
382   }
383
384   const OpenGl_Aspects* aTextAspectBack = theWorkspace->SetAspects (&myTextAspect);
385
386   aCtx->ModelWorldState.Push();
387   aCtx->ModelWorldState.ChangeCurrent().InitIdentity();
388
389   // draw counters
390   {
391     aCtx->WorldViewState.Push();
392     if (!myCountersTrsfPers.IsNull())
393     {
394       myCountersTrsfPers->Apply (aCtx->Camera(),
395                                  aCtx->ProjectionState.Current(), aCtx->WorldViewState.ChangeCurrent(),
396                                  aCtx->VirtualViewport()[2], aCtx->VirtualViewport()[3]);
397     }
398     aCtx->ApplyModelViewMatrix();
399     myCountersText.Render (theWorkspace);
400     aCtx->WorldViewState.Pop();
401   }
402
403   // draw chart
404   if (myChartIndices->IsValid()
405    && myChartIndices->GetElemsNb() > 0)
406   {
407     aCtx->WorldViewState.Push();
408     if (!myChartTrsfPers.IsNull())
409     {
410       myChartTrsfPers->Apply (aCtx->Camera(),
411                               aCtx->ProjectionState.Current(), aCtx->WorldViewState.ChangeCurrent(),
412                               aCtx->VirtualViewport()[2], aCtx->VirtualViewport()[3]);
413     }
414     aCtx->ApplyModelViewMatrix();
415
416     aCtx->ShaderManager()->BindFaceProgram (Handle(OpenGl_TextureSet)(), Graphic3d_TOSM_UNLIT,
417                                             Graphic3d_AlphaMode_Blend, true, false,
418                                             Handle(OpenGl_ShaderProgram)());
419     aCtx->SetColor4fv (OpenGl_Vec4 (1.0f, 1.0f, 1.0f, 1.0f));
420     glEnable (GL_BLEND);
421     glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
422     myChartVertices->Bind (aCtx);
423     myChartVertices->bindAttribute (aCtx, Graphic3d_TOA_POS,   3, GL_FLOAT,         myChartVertices->GetComponentsNb(), NULL);
424     myChartVertices->bindAttribute (aCtx, Graphic3d_TOA_COLOR, 4, GL_UNSIGNED_BYTE, myChartVertices->GetComponentsNb(), (void* )sizeof(Graphic3d_Vec3));
425
426     myChartIndices->Bind (aCtx);
427     aCtx->core15fwd->glDrawElements (GL_TRIANGLES, myChartIndices->GetElemsNb(), myChartIndices->GetDataType(), NULL);
428     myChartIndices->Unbind (aCtx);
429     myChartVertices->Unbind (aCtx);
430     myChartVertices->unbindAttribute (aCtx, Graphic3d_TOA_COLOR);
431     myChartVertices->unbindAttribute (aCtx, Graphic3d_TOA_POS);
432     glDisable (GL_BLEND);
433
434     myChartLines->Bind (aCtx);
435     myChartLines->bindAttribute (aCtx, Graphic3d_TOA_POS,   3, GL_FLOAT,         myChartLines->GetComponentsNb(), NULL);
436     myChartLines->bindAttribute (aCtx, Graphic3d_TOA_COLOR, 4, GL_UNSIGNED_BYTE, myChartLines->GetComponentsNb(), (void* )sizeof(Graphic3d_Vec3));
437     aCtx->core15fwd->glDrawArrays (GL_LINES, 0, myChartLines->GetElemsNb());
438     myChartLines->Unbind (aCtx);
439     myChartLines->unbindAttribute (aCtx, Graphic3d_TOA_COLOR);
440     myChartLines->unbindAttribute (aCtx, Graphic3d_TOA_POS);
441
442     myChartLabels[0].Render (theWorkspace);
443     myChartLabels[1].Render (theWorkspace);
444     myChartLabels[2].Render (theWorkspace);
445
446     aCtx->WorldViewState.Pop();
447   }
448
449   aCtx->ModelWorldState.Pop();
450   aCtx->ApplyWorldViewMatrix();
451
452   theWorkspace->SetAspects (aTextAspectBack);
453   if (theWorkspace->UseDepthWrite() != wasEnabledDepth)
454   {
455     theWorkspace->UseDepthWrite() = wasEnabledDepth;
456     glDepthMask (wasEnabledDepth ? GL_TRUE : GL_FALSE);
457   }
458 }
459
460 // =======================================================================
461 // function : DumpJson
462 // purpose  :
463 // =======================================================================
464 void OpenGl_FrameStatsPrs::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const
465 {
466   OCCT_DUMP_CLASS_BEGIN (theOStream, OpenGl_FrameStatsPrs)
467
468   OCCT_DUMP_BASE_CLASS (theOStream, theDepth, OpenGl_Element)
469 }