From 08b7a39f75899eb36147b38d86a1aed654917c2d Mon Sep 17 00:00:00 2001 From: kgv Date: Sat, 20 Jun 2020 17:48:52 +0300 Subject: [PATCH] 0030939: Draw Harness, ViewerTest - AIS_ViewCube animation does not work on Linux and macOS ViewerTest_EventManager::handleViewRedraw() now starts ViewerTest_ContinuousRedrawer working thread to workaround Tcl event loop invalidation issue. --- src/ViewerTest/FILES | 2 + .../ViewerTest_ContinuousRedrawer.cxx | 167 ++++++++++++++++++ .../ViewerTest_ContinuousRedrawer.hxx | 80 +++++++++ src/ViewerTest/ViewerTest_EventManager.cxx | 38 +++- src/ViewerTest/ViewerTest_EventManager.hxx | 5 + src/ViewerTest/ViewerTest_ViewerCommands.cxx | 115 +----------- 6 files changed, 292 insertions(+), 115 deletions(-) create mode 100644 src/ViewerTest/ViewerTest_ContinuousRedrawer.cxx create mode 100644 src/ViewerTest/ViewerTest_ContinuousRedrawer.hxx diff --git a/src/ViewerTest/FILES b/src/ViewerTest/FILES index 2f929a17f5..0e03d748b7 100755 --- a/src/ViewerTest/FILES +++ b/src/ViewerTest/FILES @@ -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 index 0000000000..40fe4feeb5 --- /dev/null +++ b/src/ViewerTest/ViewerTest_ContinuousRedrawer.cxx @@ -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 + +#include +#include +#include +#include + +// ======================================================================= +// 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 index 0000000000..5b15ed1ff2 --- /dev/null +++ b/src/ViewerTest/ViewerTest_ContinuousRedrawer.hxx @@ -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 +#include +#include +#include + +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 diff --git a/src/ViewerTest/ViewerTest_EventManager.cxx b/src/ViewerTest/ViewerTest_EventManager.cxx index 4dffd89768..cb97a78dca 100644 --- a/src/ViewerTest/ViewerTest_EventManager.cxx +++ b/src/ViewerTest/ViewerTest_EventManager.cxx @@ -21,6 +21,7 @@ #include #include #include +#include #include 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(); } @@ -104,6 +106,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 : diff --git a/src/ViewerTest/ViewerTest_EventManager.hxx b/src/ViewerTest/ViewerTest_EventManager.hxx index 6d2fed7caf..8c2be2b4ad 100644 --- a/src/ViewerTest/ViewerTest_EventManager.hxx +++ b/src/ViewerTest/ViewerTest_EventManager.hxx @@ -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; }; diff --git a/src/ViewerTest/ViewerTest_ViewerCommands.cxx b/src/ViewerTest/ViewerTest_ViewerCommands.cxx index 6e924399a7..fc16d0fc3b 100644 --- a/src/ViewerTest/ViewerTest_ViewerCommands.cxx +++ b/src/ViewerTest/ViewerTest_ViewerCommands.cxx @@ -71,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -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