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