0030939: Draw Harness, ViewerTest - AIS_ViewCube animation does not work on Linux...
authorkgv <kgv@opencascade.com>
Sat, 20 Jun 2020 14:48:52 +0000 (17:48 +0300)
committerbugmaster <bugmaster@opencascade.com>
Thu, 25 Jun 2020 16:09:02 +0000 (19:09 +0300)
ViewerTest_EventManager::handleViewRedraw() now starts ViewerTest_ContinuousRedrawer
working thread to workaround Tcl event loop invalidation issue.

src/ViewerTest/FILES
src/ViewerTest/ViewerTest_ContinuousRedrawer.cxx [new file with mode: 0644]
src/ViewerTest/ViewerTest_ContinuousRedrawer.hxx [new file with mode: 0644]
src/ViewerTest/ViewerTest_EventManager.cxx
src/ViewerTest/ViewerTest_EventManager.hxx
src/ViewerTest/ViewerTest_ViewerCommands.cxx

index 2f929a1..0e03d74 100755 (executable)
@@ -4,6 +4,8 @@ ViewerTest_AutoUpdater.cxx
 ViewerTest_AutoUpdater.hxx
 ViewerTest_CmdParser.cxx
 ViewerTest_CmdParser.hxx
+ViewerTest_ContinuousRedrawer.cxx
+ViewerTest_ContinuousRedrawer.hxx
 ViewerTest_DoubleMapIteratorOfDoubleMapOfInteractiveAndName.hxx
 ViewerTest_DoubleMapOfInteractiveAndName.hxx
 ViewerTest_EventManager.cxx
diff --git a/src/ViewerTest/ViewerTest_ContinuousRedrawer.cxx b/src/ViewerTest/ViewerTest_ContinuousRedrawer.cxx
new file mode 100644 (file)
index 0000000..40fe4fe
--- /dev/null
@@ -0,0 +1,167 @@
+// Copyright (c) 2019-2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <ViewerTest_ContinuousRedrawer.hxx>
+
+#include <Aspect_DisplayConnection.hxx>
+#include <Aspect_Window.hxx>
+#include <OSD.hxx>
+#include <OSD_Timer.hxx>
+
+// =======================================================================
+// function : Instance
+// purpose  :
+// =======================================================================
+ViewerTest_ContinuousRedrawer& ViewerTest_ContinuousRedrawer::Instance()
+{
+  static ViewerTest_ContinuousRedrawer aRedrawer;
+  return aRedrawer;
+}
+
+// =======================================================================
+// function : ViewerTest_ContinuousRedrawer
+// purpose  :
+// =======================================================================
+ViewerTest_ContinuousRedrawer::ViewerTest_ContinuousRedrawer()
+: myThread (doThreadWrapper),
+  myWakeEvent (false),
+  myTargetFps (0.0),
+  myToStop (false),
+  myToPause (false)
+{
+  //
+}
+
+// =======================================================================
+// function : ~ViewerTest_ContinuousRedrawer
+// purpose  :
+// =======================================================================
+ViewerTest_ContinuousRedrawer::~ViewerTest_ContinuousRedrawer()
+{
+  Stop();
+}
+
+// =======================================================================
+// function : Start
+// purpose  :
+// =======================================================================
+void ViewerTest_ContinuousRedrawer::Start (const Handle(Aspect_Window)& theWindow,
+                                           Standard_Real theTargetFps)
+{
+  if (myWindow != theWindow
+   || myTargetFps != theTargetFps)
+  {
+    Stop();
+    myWindow = theWindow;
+    myTargetFps = theTargetFps;
+  }
+
+  if (myThread.GetId() == 0)
+  {
+    myToStop = false;
+    myToPause = false;
+    myThread.Run (this);
+  }
+  else
+  {
+    {
+      Standard_Mutex::Sentry aLock (myMutex);
+      myToStop = false;
+      myToPause = false;
+    }
+    myWakeEvent.Set();
+  }
+}
+
+// =======================================================================
+// function : Start
+// purpose  :
+// =======================================================================
+void ViewerTest_ContinuousRedrawer::Stop (const Handle(Aspect_Window)& theWindow)
+{
+  if (!theWindow.IsNull()
+    && myWindow != theWindow)
+  {
+    return;
+  }
+
+  {
+    Standard_Mutex::Sentry aLock (myMutex);
+    myToStop = true;
+    myToPause = false;
+  }
+  myWakeEvent.Set();
+  myThread.Wait();
+  myToStop = false;
+  myWindow.Nullify();
+}
+
+// =======================================================================
+// function : doThreadLoop
+// purpose  :
+// =======================================================================
+void ViewerTest_ContinuousRedrawer::Pause()
+{
+  if (!myToPause)
+  {
+    Standard_Mutex::Sentry aLock (myMutex);
+    myToPause = true;
+  }
+}
+
+// =======================================================================
+// function : doThreadLoop
+// purpose  :
+// =======================================================================
+void ViewerTest_ContinuousRedrawer::doThreadLoop()
+{
+  Handle(Aspect_DisplayConnection) aDisp = new Aspect_DisplayConnection();
+  OSD_Timer aTimer;
+  aTimer.Start();
+  Standard_Real aTimeOld = 0.0;
+  const Standard_Real aTargetDur = myTargetFps > 0.0 ? 1.0 / myTargetFps : -1.0;
+  for (;;)
+  {
+    bool toPause = false;
+    {
+      Standard_Mutex::Sentry aLock (myMutex);
+      if (myToStop)
+      {
+        return;
+      }
+      toPause = myToPause;
+    }
+    if (toPause)
+    {
+      myWakeEvent.Wait();
+      myWakeEvent.Reset();
+    }
+
+    if (myTargetFps > 0.0)
+    {
+      const Standard_Real aTimeNew  = aTimer.ElapsedTime();
+      const Standard_Real aDuration = aTimeNew - aTimeOld;
+      if (aDuration >= aTargetDur)
+      {
+        myWindow->InvalidateContent (aDisp);
+        aTimeOld = aTimeNew;
+      }
+    }
+    else
+    {
+      myWindow->InvalidateContent (aDisp);
+    }
+
+    OSD::MilliSecSleep (1);
+  }
+}
diff --git a/src/ViewerTest/ViewerTest_ContinuousRedrawer.hxx b/src/ViewerTest/ViewerTest_ContinuousRedrawer.hxx
new file mode 100644 (file)
index 0000000..5b15ed1
--- /dev/null
@@ -0,0 +1,80 @@
+// Copyright (c) 2019-2020 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _ViewerTest_ContinuousRedrawer_HeaderFile
+#define _ViewerTest_ContinuousRedrawer_HeaderFile
+
+#include <OSD_Thread.hxx>
+#include <Standard_Condition.hxx>
+#include <Standard_Mutex.hxx>
+#include <Standard_Type.hxx>
+
+class Aspect_Window;
+
+//! Auxiliary tool performing continuous redraws of specified window.
+//! Tool creates an extra working thread pushing content invalidation messages to specific window using Aspect_Window::InvalidateContent() method.
+//! Normally, GUI application should done continuous rendering in simple fashion - just by drawing next frame without waiting for new events from windowing system;
+//! however, implementation of this approach is problematic in context of ViewerTest due to message loop binding mechanism implied by Tcl/Tk.
+class ViewerTest_ContinuousRedrawer
+{
+public:
+  //! Return global instance.
+  Standard_EXPORT static ViewerTest_ContinuousRedrawer& Instance();
+public:
+
+  //! Destructor.
+  Standard_EXPORT ~ViewerTest_ContinuousRedrawer();
+
+  //! Return TRUE if redrawer thread is started.
+  bool IsStarted() const { return myThread.GetId() != 0; }
+
+  //! Start thread.
+  Standard_EXPORT void Start (const Handle(Aspect_Window)& theWindow,
+                              Standard_Real theTargetFps);
+
+  //! Stop thread.
+  Standard_EXPORT void Stop (const Handle(Aspect_Window)& theWindow = NULL);
+
+  //! Return TRUE if redrawer thread is in paused state.
+  bool IsPaused() const { return myToPause; }
+
+  //! Pause working thread, but does not terminate it.
+  Standard_EXPORT void Pause();
+
+private:
+
+  //! Thread loop.
+  void doThreadLoop();
+
+  //! Thread creation callback.
+  static Standard_Address doThreadWrapper (Standard_Address theData)
+  {
+    ViewerTest_ContinuousRedrawer* aThis = (ViewerTest_ContinuousRedrawer* )theData;
+    aThis->doThreadLoop();
+    return 0;
+  }
+
+  //! Empty constructor.
+  ViewerTest_ContinuousRedrawer();
+
+private:
+  Handle(Aspect_Window) myWindow; //!< window to invalidate
+  OSD_Thread         myThread;    //!< working thread
+  Standard_Mutex     myMutex;     //!< mutex for accessing common variables
+  Standard_Condition myWakeEvent; //!< event to wake up working thread
+  Standard_Real      myTargetFps; //!< desired update framerate
+  volatile bool      myToStop;    //!< flag to stop working thread
+  volatile bool      myToPause;   //!< flag to put  working thread asleep without stopping
+};
+
+#endif // _ViewerTest_ContinuousRedrawer_HeaderFile
index 4dffd89..cb97a78 100644 (file)
@@ -21,6 +21,7 @@
 #include <AIS_Shape.hxx>
 #include <Aspect_Grid.hxx>
 #include <Draw.hxx>
+#include <ViewerTest_ContinuousRedrawer.hxx>
 #include <ViewerTest_V3dView.hxx>
 
 Standard_IMPORT Standard_Boolean Draw_Interprete (const char* theCommand);
@@ -45,7 +46,8 @@ ViewerTest_EventManager::ViewerTest_EventManager (const Handle(V3d_View)&
                                                   const Handle(AIS_InteractiveContext)& theCtx)
 : myCtx  (theCtx),
   myView (theView),
-  myToPickPnt (Standard_False)
+  myToPickPnt (Standard_False),
+  myIsTmpContRedraw (Standard_False)
 {
   myViewAnimation = GlobalViewAnimation();
 }
@@ -105,6 +107,40 @@ void ViewerTest_EventManager::ProcessExpose()
 }
 
 //==============================================================================
+//function : handleViewRedraw
+//purpose  :
+//==============================================================================
+void ViewerTest_EventManager::handleViewRedraw (const Handle(AIS_InteractiveContext)& theCtx,
+                                                const Handle(V3d_View)& theView)
+{
+  AIS_ViewController::handleViewRedraw (theCtx, theView);
+
+  // On non-Windows platforms Aspect_Window::InvalidateContent() from rendering thread does not work as expected
+  // as in Tcl event loop the new message might go to sleep with new event remaining in queue.
+  // As a workaround - use dedicated background thread to ping Tcl event loop.
+  if (myToAskNextFrame)
+  {
+    ViewerTest_ContinuousRedrawer& aRedrawer = ViewerTest_ContinuousRedrawer::Instance();
+    if (!myIsTmpContRedraw
+     && (!aRedrawer.IsStarted() || aRedrawer.IsPaused()))
+    {
+      myIsTmpContRedraw = true;
+    #ifndef _WIN32
+      aRedrawer.Start (theView->Window(), 60.0);
+    #endif
+    }
+  }
+  else if (myIsTmpContRedraw)
+  {
+    myIsTmpContRedraw = false;
+  #ifndef _WIN32
+    ViewerTest_ContinuousRedrawer& aRedrawer = ViewerTest_ContinuousRedrawer::Instance();
+    aRedrawer.Pause();
+  #endif
+  }
+}
+
+//==============================================================================
 //function : ProcessConfigure
 //purpose  :
 //==============================================================================
index 6d2fed7..8c2be2b 100644 (file)
@@ -88,6 +88,10 @@ public:
   //! Redraw the View on an Expose Event
   Standard_EXPORT virtual void ProcessExpose();
 
+  //! Handle redraw.
+  Standard_EXPORT virtual void handleViewRedraw (const Handle(AIS_InteractiveContext)& theCtx,
+                                                 const Handle(V3d_View)& theView) Standard_OVERRIDE;
+
   //! Resize View.
   Standard_EXPORT virtual void ProcessConfigure();
 
@@ -101,6 +105,7 @@ private:
 
   TCollection_AsciiString myPickPntArgVec[3];
   Standard_Boolean myToPickPnt;
+  Standard_Boolean myIsTmpContRedraw;
 
 };
 
index 6e92439..fc16d0f 100644 (file)
@@ -71,6 +71,7 @@
 #include <TColgp_Array1OfPnt2d.hxx>
 #include <TColStd_MapOfAsciiString.hxx>
 #include <ViewerTest_AutoUpdater.hxx>
+#include <ViewerTest_ContinuousRedrawer.hxx>
 #include <ViewerTest_EventManager.hxx>
 #include <ViewerTest_DoubleMapOfInteractiveAndName.hxx>
 #include <ViewerTest_DoubleMapIteratorOfDoubleMapOfInteractiveAndName.hxx>
@@ -1640,120 +1641,6 @@ TCollection_AsciiString ViewerTest::GetCurrentViewName ()
   return ViewerTest_myViews.Find2( ViewerTest::CurrentView());
 }
 
-//! Auxiliary tool performing continuous redraws of specified window.
-class ViewerTest_ContinuousRedrawer
-{
-public:
-  //! Return global instance.
-  static ViewerTest_ContinuousRedrawer& Instance()
-  {
-    static ViewerTest_ContinuousRedrawer aRedrawer;
-    return aRedrawer;
-  }
-public:
-
-  //! Destructor.
-  ~ViewerTest_ContinuousRedrawer()
-  {
-    Stop();
-  }
-
-  //! Start thread.
-  void Start (const Handle(Aspect_Window)& theWindow,
-              Standard_Real theTargetFps)
-  {
-    if (myWindow != theWindow
-     || myTargetFps != theTargetFps)
-    {
-      Stop();
-      myWindow = theWindow;
-      myTargetFps = theTargetFps;
-    }
-    if (myThread.GetId() == 0)
-    {
-      myToStop = false;
-      myThread.Run (this);
-    }
-  }
-
-  //! Stop thread.
-  void Stop (const Handle(Aspect_Window)& theWindow = NULL)
-  {
-    if (!theWindow.IsNull()
-      && myWindow != theWindow)
-    {
-      return;
-    }
-
-    {
-      Standard_Mutex::Sentry aLock (myMutex);
-      myToStop = true;
-    }
-    myThread.Wait();
-    myToStop = false;
-    myWindow.Nullify();
-  }
-
-private:
-
-  //! Thread loop.
-  void doThreadLoop()
-  {
-    Handle(Aspect_DisplayConnection) aDisp = new Aspect_DisplayConnection();
-    OSD_Timer aTimer;
-    aTimer.Start();
-    Standard_Real aTimeOld = 0.0;
-    const Standard_Real aTargetDur = myTargetFps > 0.0 ? 1.0 / myTargetFps : -1.0;
-    for (;;)
-    {
-      {
-        Standard_Mutex::Sentry aLock (myMutex);
-        if (myToStop)
-        {
-          return;
-        }
-      }
-      if (myTargetFps > 0.0)
-      {
-        const Standard_Real aTimeNew  = aTimer.ElapsedTime();
-        const Standard_Real aDuration = aTimeNew - aTimeOld;
-        if (aDuration >= aTargetDur)
-        {
-          myWindow->InvalidateContent (aDisp);
-          aTimeOld = aTimeNew;
-        }
-      }
-      else
-      {
-        myWindow->InvalidateContent (aDisp);
-      }
-
-      OSD::MilliSecSleep (1);
-    }
-  }
-
-  //! Thread creation callback.
-  static Standard_Address doThreadWrapper (Standard_Address theData)
-  {
-    ViewerTest_ContinuousRedrawer* aThis = (ViewerTest_ContinuousRedrawer* )theData;
-    aThis->doThreadLoop();
-    return 0;
-  }
-
-  //! Empty constructor.
-  ViewerTest_ContinuousRedrawer()
-  : myThread (doThreadWrapper),
-    myTargetFps (0.0),
-    myToStop (false) {}
-
-private:
-  Handle(Aspect_Window) myWindow;
-  OSD_Thread      myThread;
-  Standard_Mutex  myMutex;
-  Standard_Real   myTargetFps;
-  volatile bool   myToStop;
-};
-
 //==============================================================================
 //function : ViewerInit
 //purpose  : Create the window viewer and initialize all the global variable