Selaa lähdekoodia

Merge branch 'ctkvtkabstractview-update-rate'

* ctkvtkabstractview-update-rate:
  ctkVTKAbstractView controls the update rate
Julien Finet 13 vuotta sitten
vanhempi
commit
612dd6f0ef

+ 59 - 7
Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.cpp

@@ -46,7 +46,7 @@ ctkVTKAbstractViewPrivate::ctkVTKAbstractViewPrivate(ctkVTKAbstractView& object)
 {
   this->RenderWindow = vtkSmartPointer<vtkRenderWindow>::New();
   this->CornerAnnotation = vtkSmartPointer<vtkCornerAnnotation>::New();
-  this->RenderPending = false;
+  this->RequestTimer = 0;
   this->RenderEnabled = true;
 }
 
@@ -63,8 +63,16 @@ void ctkVTKAbstractViewPrivate::init()
   q->layout()->setSpacing(0);
   q->layout()->addWidget(this->VTKWidget);
 
+  this->RequestTimer = new QTimer(q);
+  this->RequestTimer->setSingleShot(true);
+  QObject::connect(this->RequestTimer, SIGNAL(timeout()),
+                   q, SLOT(forceRender()));
+
   this->setupCornerAnnotation();
   this->setupRendering();
+
+  // block renders and observe interactor to enforce framerate
+  q->setInteractor(this->RenderWindow->GetInteractor());
 }
 
 // --------------------------------------------------------------------------
@@ -143,18 +151,44 @@ void ctkVTKAbstractView::scheduleRender()
 {
   Q_D(ctkVTKAbstractView);
 
-  logger.trace(QString("scheduleRender - RenderEnabled: %1 - RenderPending: %2").
+  logger.trace(QString("scheduleRender - RenderEnabled: %1 - Request render elapsed: %2ms").
                arg(d->RenderEnabled ? "true" : "false")
-               .arg(d->RenderPending ? "true:" : "false"));
+               .arg(d->RequestTime.elapsed()));
 
   if (!d->RenderEnabled)
     {
     return;
     }
-  if (!d->RenderPending)
+
+  double msecsBeforeRender = 100. / d->RenderWindow->GetDesiredUpdateRate();
+  if(d->VTKWidget->testAttribute(Qt::WA_WState_InPaintEvent))
+    {
+    // If the request comes from the system (widget exposed, resized...), the
+    // render must be done immediately.
+    this->forceRender();
+    }
+  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));
+    }
+  else if (d->RequestTime.elapsed() > msecsBeforeRender)
     {
-    d->RenderPending = true;
-    QTimer::singleShot(0, this, SLOT(forceRender()));
+    // The rendering hasn't still be done, but msecsBeforeRender milliseconds
+    // have already been elapsed, it is likely that RequestTimer has already
+    // timed out, but the event queue hasn't been processed yet, rendering is
+    // done now to ensure the desired framerate is respected.
+    this->forceRender();
     }
 }
 
@@ -163,7 +197,19 @@ void ctkVTKAbstractView::forceRender()
 {
   Q_D(ctkVTKAbstractView);
 
-  d->RenderPending = false;
+  if (this->sender() == d->RequestTimer  &&
+      !d->RequestTime.isValid())
+    {
+    // The slot associated to the timeout signal is now called, however the
+    // render has already been executed meanwhile. There is no need to do it
+    // again.
+    return;
+    }
+
+  // The timer can be stopped if it hasn't timed out yet.
+  d->RequestTimer->stop();
+  d->RequestTime = QTime();
+
   logger.trace(QString("forceRender - RenderEnabled: %1")
                .arg(d->RenderEnabled ? "true" : "false"));
 
@@ -185,6 +231,12 @@ void ctkVTKAbstractView::setInteractor(vtkRenderWindowInteractor* newInteractor)
   logger.trace("setInteractor");
 
   d->RenderWindow->SetInteractor(newInteractor);
+  // Prevent the interactor to call Render() on the render window; only
+  // scheduleRender() and forceRender() can Render() the window.
+  // This is done to ensure the desired framerate is respected.
+  newInteractor->SetEnableRender(false);
+  qvtkReconnect(d->RenderWindow->GetInteractor(), newInteractor,
+                vtkCommand::RenderEvent, this, SLOT(scheduleRender()));
 }
 
 //----------------------------------------------------------------------------

+ 12 - 2
Libs/Visualization/VTK/Widgets/ctkVTKAbstractView.h

@@ -28,6 +28,7 @@
 #include <QVTKWidget.h>
 
 // CTK includes
+#include "ctkVTKObject.h"
 #include "ctkVisualizationVTKWidgetsExport.h"
 class ctkVTKAbstractViewPrivate;
 
@@ -39,6 +40,7 @@ class vtkRenderWindow;
 class CTK_VISUALIZATION_VTK_WIDGETS_EXPORT ctkVTKAbstractView : public QWidget
 {
   Q_OBJECT
+  QVTK_OBJECT
   Q_PROPERTY(QString cornerAnnotationText READ cornerAnnotationText WRITE setCornerAnnotationText)
   Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
   Q_PROPERTY(QColor backgroundColor2 READ backgroundColor2 WRITE setBackgroundColor)
@@ -51,11 +53,15 @@ public:
   virtual ~ctkVTKAbstractView();
 
 public slots:
-
-  /// If a render has already been scheduled, this called is a no-op
+  /// 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.
   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.
   void forceRender();
 
   /// Set the background color of the rendering screen.
@@ -81,6 +87,10 @@ public:
 
   /// Set/Get window interactor
   Q_INVOKABLE vtkRenderWindowInteractor* interactor()const;
+  /// QVTKWidget catches all render requests, and ensure the desired framerate
+  /// is respected.
+  /// The interactor never calls Render() on the render window.
+  /// TBD: can we only set a QVTKRenderWindowInteractor ?
   virtual void setInteractor(vtkRenderWindowInteractor* interactor);
 
   /// Get current interactor style

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

@@ -23,6 +23,8 @@
 
 // Qt includes
 #include <QObject>
+#include <QTime>
+class QTimer;
 
 // CTK includes
 #include "ctkVTKAbstractView.h"
@@ -56,7 +58,8 @@ public:
 
   QVTKWidget*                                   VTKWidget;
   vtkSmartPointer<vtkRenderWindow>              RenderWindow;
-  bool                                          RenderPending;
+  QTimer*                                       RequestTimer;
+  QTime                                         RequestTime;
   bool                                          RenderEnabled;
 
   vtkSmartPointer<vtkCornerAnnotation>          CornerAnnotation;

+ 4 - 0
Libs/Visualization/VTK/Widgets/ctkVTKRenderView.cpp

@@ -147,6 +147,7 @@ void ctkVTKRenderViewPrivate::yaw(int rotateDegrees,
 //---------------------------------------------------------------------------
 void ctkVTKRenderViewPrivate::doSpin()
 {
+  Q_Q(ctkVTKRenderView);
   if (!this->SpinEnabled)
     {
     return;
@@ -168,12 +169,14 @@ void ctkVTKRenderViewPrivate::doSpin()
       break;
     }
 
+  q->forceRender();
   QTimer::singleShot(this->AnimationIntervalMs, this, SLOT(doSpin()));
 }
 
 //---------------------------------------------------------------------------
 void ctkVTKRenderViewPrivate::doRock()
 {
+  Q_Q(ctkVTKRenderView);
   Q_ASSERT(this->Renderer->IsActiveCameraCreated());
 
   if (!this->RockEnabled)
@@ -195,6 +198,7 @@ void ctkVTKRenderViewPrivate::doRock()
   //Make the lighting follow the camera to avoid illumination changes
   this->Renderer->UpdateLightsGeometryToFollowCamera();
 
+  q->forceRender();
   QTimer::singleShot(this->AnimationIntervalMs, this, SLOT(doRock()));
 }