Pārlūkot izejas kodu

BUG: Fixed maximum update rate computation in ctkVTKAbstractView

Render window's DesiredUpdateRate property was used to determine maximum update rate of ctkVTKAbstractView. This had two issues:

1. VTK often significantly overestimates rendering time and so DesiredUpdateRate have to be set to quite low value (few fps) to get acceptable quality rendering. However, if ctkVTKAbstractView uses this value as maximum update rate, it would severely limit the achievable actual refresh rate. Separating the two values allow controlling quality settings and event compression separately.

2. When DesiredUpdateRate was set to "still update rate" (0.0001 fps; this is the default value) then rendering was never enforced. This caused indefinitely delayed rendering updates on MacOSX, when events were continuously generated on the GUI (e.g., by moving image slice selector slider continuously, see https://issues.slicer.org/view.php?id=4496). DesiredUpdateRate is set to 60.0 by default now, which results in smooth updates, while still effectively suppressing rendering request submitted in quick succession.
Andras Lasso 7 gadi atpakaļ
vecāks
revīzija
650ec821d0

+ 22 - 11
Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.cpp

@@ -56,6 +56,7 @@ ctkVTKAbstractViewPrivate::ctkVTKAbstractViewPrivate(ctkVTKAbstractView& object)
   this->CornerAnnotation = vtkSmartPointer<vtkCornerAnnotation>::New();
   this->RequestTimer = 0;
   this->RenderEnabled = true;
+  this->MaximumUpdateRate = 60.0;
   this->FPSVisible = false;
   this->FPSTimer = 0;
   this->FPS = 0;
@@ -185,7 +186,13 @@ void ctkVTKAbstractView::scheduleRender()
     return;
     }
 
-  double msecsBeforeRender = 1000. / d->RenderWindow->GetDesiredUpdateRate();
+  double msecsBeforeRender = 0;
+  // If the MaximumUpdateRate is set to 0 then it indicates that rendering is done next time
+  // the application is idle.
+  if (d->MaximumUpdateRate > 0.0)
+    {
+    msecsBeforeRender = 1000. / d->MaximumUpdateRate;
+    }
   if(d->VTKWidget->testAttribute(Qt::WA_WState_InPaintEvent))
     {
     // If the request comes from the system (widget exposed, resized...), the
@@ -194,16 +201,6 @@ void ctkVTKAbstractView::scheduleRender()
     }
   else if (!d->RequestTime.isValid())
     {
-    // If the DesiredUpdateRate is in "still mode", the requested framerate
-    // is fake, it is just a way to allocate as much time as possible for the
-    // rendering, it doesn't really mean that rendering must occur only once
-    // every couple seconds. It just means it should be done when there is
-    // time to do it. A timer of 0, kind of mean a rendering is done next time
-    // it is idle.
-    if (msecsBeforeRender > 10000)
-      {
-      msecsBeforeRender = 0;
-      }
     d->RequestTime.start();
     d->RequestTimer->start(static_cast<int>(msecsBeforeRender));
     }
@@ -499,3 +496,17 @@ void ctkVTKAbstractView::setMultiSamples(int number)
 {
   ctkVTKAbstractViewPrivate::MultiSamples = number;
 }
+
+//----------------------------------------------------------------------------
+double ctkVTKAbstractView::maximumUpdateRate()const
+{
+  Q_D(const ctkVTKAbstractView);
+  return d->MaximumUpdateRate;
+}
+
+//----------------------------------------------------------------------------
+void ctkVTKAbstractView::setMaximumUpdateRate(double fps)
+{
+  Q_D(ctkVTKAbstractView);
+  d->MaximumUpdateRate = fps;
+}

+ 38 - 6
Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.h

@@ -59,6 +59,9 @@ class CTK_VISUALIZATION_VTK_WIDGETS_EXPORT ctkVTKAbstractView : public QWidget
   /// not.
   /// false by default.
   Q_PROPERTY(bool useDepthPeeling READ useDepthPeeling WRITE setUseDepthPeeling)
+  /// Set a maximum rate (in frames per second) for rendering.
+  Q_PROPERTY(double maximumUpdateRate READ maximumUpdateRate WRITE setMaximumUpdateRate)
+
 public:
 
   typedef QWidget Superclass;
@@ -67,16 +70,41 @@ public:
 
 public Q_SLOTS:
   /// Notify QVTKWidget that the view needs to be rendered.
-  /// scheduleRender() respects the desired framerate of the render window,
-  /// it won't render the window more than what the current render window
-  /// framerate is.
+  /// scheduleRender() respects the maximum update rate of the view,
+  /// it won't render the window more frequently than what the maximum
+  /// update rate is.
+  /// \sa setMaximumUpdateRate
   virtual void scheduleRender();
 
   /// Force a render even if a render is already ocurring
   /// Be careful when calling forceRender() as it can slow down your
   /// application. It is preferable to use scheduleRender() instead.
+  /// \sa scheduleRender
   virtual void forceRender();
 
+  /// Set maximum rate for rendering (in frames per second).
+  /// If rendering is requested more frequently than this rate using scheduleRender,
+  /// actual rendering will happen at this rate.
+  /// This mechanism prevents repeated rendering caused by cluster of rendering requests.
+  ///
+  /// If maximum update rate is set to 0 then it indicates that rendering is done next time
+  /// the application is idle, i.e., pending timer events are processed. This option should be used
+  /// with caution, as policy of timer event processing may differ between operating systems.
+  /// Specifically, on macOS, timer events may be indefinitely delayed if user interface continuously
+  /// generates events.
+  ///
+  /// RenderWindow's DesiredUpdateRate property is intended for determining rendering quality settings,
+  /// and is not suitable to be used as maximum update rate. The main reason is that VTK usually makes the
+  /// rendering much faster and lower quality than DesiredUpdateRate would dictate, and so it would
+  /// unnecessarily decrease the actual refresh rate.
+  ///
+  /// By default maximum update rate is set to 60FPS, which allows smooth updates, while effectively
+  /// suppressing repeated update requests (after a rendering has been completed,
+  /// repeated rendering requests will be ignored for 17 milliseconds).
+  ///
+  /// \sa scheduleRender
+  void setMaximumUpdateRate(double fps);
+
   /// Set the background color of the rendering screen.
   virtual void setBackgroundColor(const QColor& newBackgroundColor);
 
@@ -84,7 +112,7 @@ public Q_SLOTS:
   /// backgrounds.
   virtual void setBackgroundColor2(const QColor& newBackgroundColor);
 
-  /// Set/Get whether this view should have a gradient background using the
+  /// Set whether this view should have a gradient background using the
   /// Background (top) and Background2 (bottom) colors. Default is off.
   virtual void setGradientBackground(bool enable);
 
@@ -145,8 +173,12 @@ public:
   /// Return the current FPS
   double fps()const;
 
-  /// Return the useDepthPeeling property value.
-  /// \sa useDepthPeeling
+  /// Returns maximum rate for rendering (in frames per second).
+  /// \\sa setMaximumUpdateRate
+  double maximumUpdateRate()const;
+
+  /// Returns true if depth peeling is enabled.
+  /// \sa setUseDepthPeeling
   bool useDepthPeeling()const;
 
   /// Set the default number of multisamples to use. Note that a negative

+ 1 - 0
Libs/Visualization/VTK/Widgets/ctkVTKAbstractView_p.h

@@ -67,6 +67,7 @@ public:
   QTimer*                                       RequestTimer;
   QTime                                         RequestTime;
   bool                                          RenderEnabled;
+  double                                        MaximumUpdateRate;
   bool                                          FPSVisible;
   QTimer*                                       FPSTimer;
   int                                           FPS;