From 2570bf335346c62bc7229d89fa8a44e54b11abf1 Mon Sep 17 00:00:00 2001
From: "David C. Lonie" <david.lonie@kitware.com>
Date: Tue, 20 Sep 2016 12:15:21 -0400
Subject: [PATCH] Add vtkOpenGLRenderTimer.

Uses GL_TIMESTAMP queries to provide asynch, nestable GPU timings.
---
 Rendering/OpenGL2/CMakeLists.txt           |   1 +
 Rendering/OpenGL2/vtkOpenGLFXAAFilter.cxx  | 166 ++++++--------------
 Rendering/OpenGL2/vtkOpenGLFXAAFilter.h    |  14 +-
 Rendering/OpenGL2/vtkOpenGLRenderTimer.cxx | 171 +++++++++++++++++++++
 Rendering/OpenGL2/vtkOpenGLRenderTimer.h   |  85 ++++++++++
 5 files changed, 309 insertions(+), 128 deletions(-)
 create mode 100644 Rendering/OpenGL2/vtkOpenGLRenderTimer.cxx
 create mode 100644 Rendering/OpenGL2/vtkOpenGLRenderTimer.h

diff --git a/Rendering/OpenGL2/CMakeLists.txt b/Rendering/OpenGL2/CMakeLists.txt
index 3e16cae7c2b..b9f0ef9b2bb 100644
--- a/Rendering/OpenGL2/CMakeLists.txt
+++ b/Rendering/OpenGL2/CMakeLists.txt
@@ -50,6 +50,7 @@ set(Module_SRCS
   vtkOpenGLPolyDataMapper2D.cxx
   vtkOpenGLProperty.cxx
   vtkOpenGLRenderPass.cxx
+  vtkOpenGLRenderTimer.cxx
   vtkOpenGLRenderUtilities.cxx
   vtkOpenGLRenderWindow.cxx
   vtkOpenGLRenderer.cxx
diff --git a/Rendering/OpenGL2/vtkOpenGLFXAAFilter.cxx b/Rendering/OpenGL2/vtkOpenGLFXAAFilter.cxx
index c2d77dd2752..cae8676745b 100644
--- a/Rendering/OpenGL2/vtkOpenGLFXAAFilter.cxx
+++ b/Rendering/OpenGL2/vtkOpenGLFXAAFilter.cxx
@@ -22,6 +22,7 @@
 #include "vtkOpenGLBufferObject.h"
 #include "vtkOpenGLError.h"
 #include "vtkOpenGLRenderer.h"
+#include "vtkOpenGLRenderTimer.h"
 #include "vtkOpenGLRenderUtilities.h"
 #include "vtkOpenGLRenderWindow.h"
 #include "vtkOpenGLShaderCache.h"
@@ -97,21 +98,17 @@ void vtkOpenGLFXAAFilter::Execute(vtkOpenGLRenderer *ren)
   assert(ren);
   this->Renderer = ren;
 
-  this->StartTimeQuery(this->PreparationTimeQuery,
-                       this->PreparationTimeQueryActive);
-
+  this->StartTimeQuery(this->PreparationTimer);
   this->Prepare();
   this->LoadInput();
+  this->EndTimeQuery(this->PreparationTimer);
 
-  this->EndTimeQuery(this->PreparationTimeQuery,
-                     this->PreparationTimeQueryActive);
-  this->StartTimeQuery(this->FXAATimeQuery, this->FXAATimeQueryActive);
-
+  this->StartTimeQuery(this->FXAATimer);
   this->ApplyFilter();
-
-  this->EndTimeQuery(this->FXAATimeQuery, this->FXAATimeQueryActive);
+  this->EndTimeQuery(this->FXAATimer);
 
   this->Finalize();
+  this->PrintBenchmark();
 
   this->Renderer = NULL;
 }
@@ -162,12 +159,8 @@ void vtkOpenGLFXAAFilter::SetDebugOptionValue(vtkFXAAOptions::DebugOption opt)
 vtkOpenGLFXAAFilter::vtkOpenGLFXAAFilter()
   : BlendState(false),
     DepthTestState(false),
-    PreparationTimeQueryActive(false),
-    FXAATimeQueryActive(false),
-    PreparationTimeQuery(0),
-    FXAATimeQuery(0),
-    PreparationTime(0),
-    FXAATime(0),
+    PreparationTimer(new vtkOpenGLRenderTimer),
+    FXAATimer(new vtkOpenGLRenderTimer),
     RelativeContrastThreshold(1.f/8.f),
     HardContrastThreshold(1.f/16.f),
     SubpixelBlendLimit(3.f/4.f),
@@ -189,6 +182,8 @@ vtkOpenGLFXAAFilter::vtkOpenGLFXAAFilter()
 vtkOpenGLFXAAFilter::~vtkOpenGLFXAAFilter()
 {
   this->FreeGLObjects();
+  delete PreparationTimer;
+  delete FXAATimer;
 }
 
 //------------------------------------------------------------------------------
@@ -243,17 +238,6 @@ void vtkOpenGLFXAAFilter::FreeGLObjects()
 //  DeleteHelper(this->Program); // Managed by the shader cache
   DeleteHelper(this->VAO);
   DeleteHelper(this->VBO);
-
-  if (this->FXAATimeQuery > 0)
-    {
-    glDeleteQueries(1, &this->FXAATimeQuery);
-    this->FXAATimeQuery = 0;
-    }
-  if (this->PreparationTimeQuery > 0)
-    {
-    glDeleteQueries(1, &this->PreparationTimeQuery);
-    this->PreparationTimeQuery = 0;
-    }
 }
 
 //------------------------------------------------------------------------------
@@ -393,116 +377,60 @@ void vtkOpenGLFXAAFilter::Finalize()
     }
 
   vtkOpenGLCheckErrorMacro("Error after restoring GL state.");
-
-#ifdef FXAA_BENCHMARK
-  if (this->GetTimeQueryResult(this->PreparationTimeQuery,
-                               this->PreparationTime) ||
-      this->GetTimeQueryResult(this->FXAATimeQuery,
-                               this->FXAATime))
-    {
-    this->PrintBenchmark();
-    }
-#endif // FXAA_BENCHMARK
 }
 
 //------------------------------------------------------------------------------
-void vtkOpenGLFXAAFilter::StartTimeQuery(unsigned int &queryId, bool &tracker)
+void vtkOpenGLFXAAFilter::StartTimeQuery(vtkOpenGLRenderTimer *timer)
 {
-#ifdef FXAA_BENCHMARK
-  // Don't start a new query until the old results are in:
-  if (tracker || queryId != 0)
+  // Since it may take a few frames for the results to become available,
+  // check if we've started the timer already.
+  if (!timer->Started())
     {
-    return;
-    }
-
-  glGenQueries(1, &queryId);
-  glBeginQuery(GL_TIME_ELAPSED, queryId);
-  tracker = true;
-
-  vtkOpenGLCheckErrorMacro("Error after starting GL_TIME_ELAPSED query.");
-#else // FXAA_BENCHMARK
-  (void)queryId;
-  (void)tracker;
-#endif // FXAA_BENCHMARK
-}
-
-//------------------------------------------------------------------------------
-void vtkOpenGLFXAAFilter::EndTimeQuery(unsigned int &queryId, bool &tracker)
-{
-#ifdef FXAA_BENCHMARK
-  if (!tracker || queryId == 0)
-    { // Shouldn't happen, but just in case...
-    return;
+    timer->Start();
     }
-
-  glEndQuery(GL_TIME_ELAPSED);
-  tracker = false;
-
-  vtkOpenGLCheckErrorMacro("Error after ending GL_TIME_ELAPSED query.");
-#else // FXAA_BENCHMARK
-  (void)queryId;
-  (void)tracker;
-#endif // FXAA_BENCHMARK
 }
 
 //------------------------------------------------------------------------------
-bool vtkOpenGLFXAAFilter::GetTimeQueryResult(unsigned int &queryId,
-                                             unsigned int &result)
+void vtkOpenGLFXAAFilter::EndTimeQuery(vtkOpenGLRenderTimer *timer)
 {
-#ifdef FXAA_BENCHMARK
-  if (queryId == 0)
+  // Since it may take a few frames for the results to become available,
+  // check if we've stopped the timer already.
+  if (!timer->Stopped())
     {
-    return false;
+    timer->Stop();
     }
-
-  GLint ready;
-  glGetQueryObjectiv(queryId, GL_QUERY_RESULT_AVAILABLE, &ready);
-  vtkOpenGLCheckErrorMacro("Error after checking GL_TIME_ELAPSED status.");
-  if (!ready)
-    {
-    return false;
-    }
-
-  glGetQueryObjectuiv(queryId, GL_QUERY_RESULT, &result);
-  vtkOpenGLCheckErrorMacro("Error after checking GL_TIME_ELAPSED result.");
-
-  // Free the query:
-  glDeleteQueries(1, &queryId);
-  queryId = 0;
-
-  return true;
-#else // FXAA_BENCHMARK
-  (void)queryId;
-  (void)result;
-  return false;
-#endif // FXAA_BENCHMARK
 }
 
 //------------------------------------------------------------------------------
 void vtkOpenGLFXAAFilter::PrintBenchmark()
 {
+  if (this->PreparationTimer->Ready() &&
+      this->FXAATimer->Ready())
+    {
+
 #ifdef FXAA_BENCHMARK
-  int numPixels = this->Input->GetWidth() * this->Input->GetHeight();
-  float ptime = static_cast<float>(this->PreparationTime);
-  float ftime = static_cast<float>(this->FXAATime);
-  float ttime = ptime + ftime;
-
-  float ptimePerPixel = ptime / numPixels;
-  float ftimePerPixel = ftime / numPixels;
-  float ttimePerPixel = ttime / numPixels;
-
-  // Convert the non-per-pixel times to ms:
-  ptime *= 1e-6f;
-  ftime *= 1e-6f;
-  ttime *= 1e-6f;
-
-  std::cerr << "FXAA Info:\n"
-            << " - Number of pixels: " << numPixels << "\n"
-            << " - Preparation time: " << ptime << "ms ("
-            << ptimePerPixel << "ns per pixel)\n"
-            << " - FXAA time: " << ftime << "ms ("
-            << ftimePerPixel << "ns per pixel)\n"
-            << " - Total time: " << ttime << "ms ("
-            << ttimePerPixel << "ns per pixel)\n";
+    int numPixels = this->Input->GetWidth() * this->Input->GetHeight();
+    float ptime = this->PreparationTimer->GetElapsedMilliseconds();
+    float ftime = this->FXAATimer->GetElapsedMilliseconds();
+    float ttime = ptime + ftime;
+
+    float ptimePerPixel = (this->PreparationTimer->GetElapsedNanoseconds() /
+                           static_cast<float>(numPixels));
+    float ftimePerPixel = (this->FXAATimer->GetElapsedNanoseconds() /
+                           static_cast<float>(numPixels));
+    float ttimePerPixel =  ptimePerPixel + ftimePerPixel;
+
+    std::cerr << "FXAA Info:\n"
+              << " - Number of pixels: " << numPixels << "\n"
+              << " - Preparation time: " << ptime << "ms ("
+              << ptimePerPixel << "ns per pixel)\n"
+              << " - FXAA time: " << ftime << "ms ("
+              << ftimePerPixel << "ns per pixel)\n"
+              << " - Total time: " << ttime << "ms ("
+              << ttimePerPixel << "ns per pixel)\n";
 #endif // FXAA_BENCHMARK
+
+    this->PreparationTimer->Reset();
+    this->FXAATimer->Reset();
+    }
 }
diff --git a/Rendering/OpenGL2/vtkOpenGLFXAAFilter.h b/Rendering/OpenGL2/vtkOpenGLFXAAFilter.h
index 2e917cff5a1..9f51b600f97 100644
--- a/Rendering/OpenGL2/vtkOpenGLFXAAFilter.h
+++ b/Rendering/OpenGL2/vtkOpenGLFXAAFilter.h
@@ -45,6 +45,7 @@ class vtkFXAAOptions;
 class vtkOpenGLBufferObject;
 class vtkOpenGLVertexArrayObject;
 class vtkOpenGLRenderer;
+class vtkOpenGLRenderTimer;
 class vtkShaderProgram;
 class vtkTextureObject;
 
@@ -100,9 +101,8 @@ protected:
   void SubstituteFragmentShader(std::string &fragShader);
   void Finalize();
 
-  void StartTimeQuery(unsigned int &queryId, bool &tracker);
-  void EndTimeQuery(unsigned int &queryId, bool &tracker);
-  bool GetTimeQueryResult(unsigned int &queryId, unsigned int &result);
+  void StartTimeQuery(vtkOpenGLRenderTimer *timer);
+  void EndTimeQuery(vtkOpenGLRenderTimer *timer);
   void PrintBenchmark();
 
   // Cache GL state that we modify
@@ -112,12 +112,8 @@ protected:
   int Viewport[4]; // x, y, width, height
 
   // Used to measure execution time:
-  bool PreparationTimeQueryActive;
-  bool FXAATimeQueryActive;
-  unsigned int PreparationTimeQuery;
-  unsigned int FXAATimeQuery;
-  unsigned int PreparationTime;
-  unsigned int FXAATime;
+  vtkOpenGLRenderTimer *PreparationTimer;
+  vtkOpenGLRenderTimer *FXAATimer;
 
   // Parameters:
   float RelativeContrastThreshold;
diff --git a/Rendering/OpenGL2/vtkOpenGLRenderTimer.cxx b/Rendering/OpenGL2/vtkOpenGLRenderTimer.cxx
new file mode 100644
index 00000000000..512b49d0f25
--- /dev/null
+++ b/Rendering/OpenGL2/vtkOpenGLRenderTimer.cxx
@@ -0,0 +1,171 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkOpenGLRenderTimer.cxx
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+
+#include "vtkOpenGLRenderTimer.h"
+
+#include "vtkObjectFactory.h"
+
+#include "vtk_glew.h"
+
+//------------------------------------------------------------------------------
+vtkOpenGLRenderTimer::vtkOpenGLRenderTimer()
+  : StartReady(false),
+    EndReady(false),
+    StartQuery(0),
+    EndQuery(0),
+    StartTime(0),
+    EndTime(0)
+{
+}
+
+//------------------------------------------------------------------------------
+vtkOpenGLRenderTimer::~vtkOpenGLRenderTimer()
+{
+  this->Reset();
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLRenderTimer::Reset()
+{
+  if (this->StartQuery != 0)
+    {
+    glDeleteQueries(1, static_cast<GLuint*>(&this->StartQuery));
+    this->StartQuery = 0;
+    }
+
+  if (this->EndQuery != 0)
+    {
+    glDeleteQueries(1, static_cast<GLuint*>(&this->EndQuery));
+    this->EndQuery = 0;
+    }
+
+  this->StartReady = false;
+  this->EndReady = false;
+  this->StartTime = 0;
+  this->EndTime = 0;
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLRenderTimer::Start()
+{
+  this->Reset();
+
+  glGenQueries(1, static_cast<GLuint*>(&this->StartQuery));
+  glQueryCounter(static_cast<GLuint>(this->StartQuery), GL_TIMESTAMP);
+}
+
+//------------------------------------------------------------------------------
+void vtkOpenGLRenderTimer::Stop()
+{
+  if (this->EndQuery != 0)
+    {
+    vtkGenericWarningMacro("vtkOpenGLRenderTimer::Stop called before "
+                           "resetting. Ignoring.");
+    return;
+    }
+
+  if (this->StartQuery == 0)
+    {
+    vtkGenericWarningMacro("vtkOpenGLRenderTimer::Stop called before "
+                           "vtkOpenGLRenderTimer::Start. Ignoring.");
+    return;
+    }
+
+  glGenQueries(1, static_cast<GLuint*>(&this->EndQuery));
+  glQueryCounter(static_cast<GLuint>(this->EndQuery), GL_TIMESTAMP);
+}
+
+//------------------------------------------------------------------------------
+bool vtkOpenGLRenderTimer::Started()
+{
+  return this->StartQuery != 0;
+}
+
+//------------------------------------------------------------------------------
+bool vtkOpenGLRenderTimer::Stopped()
+{
+  return this->EndQuery != 0;
+}
+
+//------------------------------------------------------------------------------
+bool vtkOpenGLRenderTimer::Ready()
+{
+  if (!this->StartReady)
+    {
+    GLint ready;
+    glGetQueryObjectiv(static_cast<GLuint>(this->StartQuery),
+                       GL_QUERY_RESULT_AVAILABLE, &ready);
+    if (!ready)
+      {
+      return false;
+      }
+
+    this->StartReady = true;
+    glGetQueryObjectui64v(static_cast<GLuint>(this->StartQuery),
+                          GL_QUERY_RESULT,
+                          reinterpret_cast<GLuint64*>(&this->StartTime));
+    }
+
+  if (!this->EndReady)
+    {
+    GLint ready;
+    glGetQueryObjectiv(static_cast<GLuint>(this->EndQuery),
+                       GL_QUERY_RESULT_AVAILABLE, &ready);
+    if (!ready)
+      {
+      return false;
+      }
+
+    this->EndReady = true;
+    glGetQueryObjectui64v(static_cast<GLuint>(this->EndQuery),
+                          GL_QUERY_RESULT,
+                          reinterpret_cast<GLuint64*>(&this->EndTime));
+    }
+
+  return true;
+}
+
+//------------------------------------------------------------------------------
+float vtkOpenGLRenderTimer::GetElapsedSeconds()
+{
+  if (!this->Ready())
+    {
+    return 0.f;
+    }
+
+  return (this->EndTime - this->StartTime) * 1e-9f;
+}
+
+//------------------------------------------------------------------------------
+float vtkOpenGLRenderTimer::GetElapsedMilliseconds()
+{
+  if (!this->Ready())
+    {
+    return 0.f;
+    }
+
+  return (this->EndTime - this->StartTime) * 1e-6f;
+}
+
+//------------------------------------------------------------------------------
+vtkTypeUInt64 vtkOpenGLRenderTimer::GetElapsedNanoseconds()
+{
+  if (!this->Ready())
+    {
+    return 0;
+    }
+
+  return (this->EndTime - this->StartTime);
+}
diff --git a/Rendering/OpenGL2/vtkOpenGLRenderTimer.h b/Rendering/OpenGL2/vtkOpenGLRenderTimer.h
new file mode 100644
index 00000000000..a9715250a41
--- /dev/null
+++ b/Rendering/OpenGL2/vtkOpenGLRenderTimer.h
@@ -0,0 +1,85 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    vtkOpenGLRenderTimer.h
+
+  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
+  All rights reserved.
+  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notice for more information.
+
+=========================================================================*/
+
+// .NAME vtkOpenGLRenderTimer - Asynchronously measures GPU execution time.
+//
+// .SECTION Description
+// This class posts events to the OpenGL server to measure execution times
+// of GPU processes. The queries are asynchronous and multiple
+// vtkOpenGLRenderTimers may overlap / be nested.
+//
+// This uses GL_TIMESTAMP rather than GL_ELAPSED_TIME, since only one
+// GL_ELAPSED_TIME query may be active at a time.
+
+#ifndef vtkOpenGLRenderTimer_h
+#define vtkOpenGLRenderTimer_h
+
+#include "vtkRenderingOpenGL2Module.h" // For export macro
+#include "vtkType.h" // For vtkTypeUint64, etc
+
+class VTKRENDERINGOPENGL2_EXPORT vtkOpenGLRenderTimer
+{
+public:
+  vtkOpenGLRenderTimer();
+  ~vtkOpenGLRenderTimer();
+
+  // Description:
+  // Clear out any previous results and prepare for a new query.
+  void Reset();
+
+  // Description:
+  // Mark the start of a timed event.
+  void Start();
+
+  // Description:
+  // Mark the end of a timed event.
+  void Stop();
+
+  // Description:
+  // Returns true if the timer has been started. The query may not be ready yet.
+  bool Started();
+
+  // Description:
+  // Returns true if the timer has been stopped. The query may not be ready yet.
+  bool Stopped();
+
+  // Description:
+  // Returns true when the timing results are available.
+  bool Ready();
+
+  // Description:
+  // If Ready() returns true, get the elapsed time in the requested units.
+  float GetElapsedSeconds();
+  float GetElapsedMilliseconds();
+  vtkTypeUInt64 GetElapsedNanoseconds();
+
+protected:
+  bool StartReady;
+  bool EndReady;
+
+  vtkTypeUInt32 StartQuery;
+  vtkTypeUInt32 EndQuery;
+
+  vtkTypeUInt64 StartTime;
+  vtkTypeUInt64 EndTime;
+
+private:
+  vtkOpenGLRenderTimer(const vtkOpenGLRenderTimer&) VTK_DELETE_FUNCTION;
+  void operator=(const vtkOpenGLRenderTimer&) VTK_DELETE_FUNCTION;
+};
+
+#endif // vtkOpenGLRenderTimer_h
+
+// VTK-HeaderTest-Exclude: vtkOpenGLRenderTimer.h
-- 
GitLab