Added SingleThreadController 17/50517/6
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Thu, 29 Oct 2015 10:34:59 +0000 (10:34 +0000)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Thu, 5 Nov 2015 17:06:55 +0000 (17:06 +0000)
Change-Id: Id88b7bc9c2192bc7782f4e664ca0b5903a947fb0

adaptors/base/file.list
adaptors/base/single-threaded/single-thread-controller.cpp [new file with mode: 0644]
adaptors/base/single-threaded/single-thread-controller.h [new file with mode: 0644]
adaptors/base/thread-controller.cpp

index f940b67..f583fa1 100644 (file)
@@ -18,7 +18,8 @@ base_adaptor_src_files = \
   $(base_adaptor_src_dir)/separate-update-render/render-thread.cpp \
   $(base_adaptor_src_dir)/separate-update-render/thread-synchronization.cpp \
   $(base_adaptor_src_dir)/separate-update-render/update-thread.cpp \
-  $(base_adaptor_src_dir)/separate-update-render/vsync-notifier.cpp
+  $(base_adaptor_src_dir)/separate-update-render/vsync-notifier.cpp \
+  $(base_adaptor_src_dir)/single-threaded/single-thread-controller.cpp
 
 base_adaptor_networking_src_files = \
   $(base_adaptor_src_dir)/performance-logging/networking/network-performance-protocol.cpp \
diff --git a/adaptors/base/single-threaded/single-thread-controller.cpp b/adaptors/base/single-threaded/single-thread-controller.cpp
new file mode 100644 (file)
index 0000000..f0c3398
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "single-thread-controller.h"
+
+// EXTERNAL INCLUDES
+#include <iostream>
+#include <dali/integration-api/platform-abstraction.h>
+
+// INTERNAL INCLUDES
+#include <base/interfaces/adaptor-internal-services.h>
+#include <base/environment-options.h>
+
+namespace Dali
+{
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+namespace
+{
+const unsigned int MILLISECONDS_PER_FRAME = 17u;
+const float SECONDS_PER_FRAME = MILLISECONDS_PER_FRAME * 0.001f;
+
+const unsigned int NANOSECONDS_PER_MICROSECOND( 1000u );
+const unsigned int MICROSECONDS_PER_SECOND( 1000000u );
+const float        MICROSECONDS_TO_SECONDS( 0.000001f );
+
+#if defined(DEBUG_ENABLED)
+Integration::Log::Filter* gLogFilter = Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_THREAD_SYNC");
+#endif
+
+} // unnamed namespace
+
+SingleThreadController::SingleThreadController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions )
+: ConnectionTracker(),
+  ThreadControllerInterface(),
+  mTimer(),
+  mFpsTracker( environmentOptions ),
+  mUpdateStatusLogger( environmentOptions ),
+  mRenderHelper( adaptorInterfaces ),
+  mCore( adaptorInterfaces.GetCore()),
+  mPlatformAbstraction( adaptorInterfaces.GetPlatformAbstractionInterface() ),
+  mPerformanceInterface( adaptorInterfaces.GetPerformanceInterface() ),
+  mLastUpdateRenderTime( 0 ),
+  mSystemTime( 0 ),
+  mRefreshRate( 1 ),
+  mState( State::STOPPED ),
+  mUpdatingAndRendering( false ),
+  mStopRequestedWhileRendering( false )
+{
+  DALI_LOG_INFO( gLogFilter, Debug::General, "%s()\n", __FUNCTION__ );
+}
+
+SingleThreadController::~SingleThreadController()
+{
+  DALI_LOG_INFO( gLogFilter, Debug::General, "%s()\n", __FUNCTION__ );
+
+  Stop();
+}
+
+void SingleThreadController::Initialize()
+{
+  DALI_LOG_INFO( gLogFilter, Debug::General, "%s()\n", __FUNCTION__ );
+
+  mTimer = Dali::Timer::New( mRefreshRate * MILLISECONDS_PER_FRAME );
+
+  // Create a tick-signal so that we can update and render every frame
+  mTimer.TickSignal().Connect( this, &SingleThreadController::OnTimerTick );
+}
+
+void SingleThreadController::Start()
+{
+  DALI_LOG_INFO( gLogFilter, Debug::General, "%s()\n", __FUNCTION__ );
+
+  mRenderHelper.Start();
+  mRenderHelper.InitializeEgl();
+
+  // tell core it has a context
+  mCore.ContextCreated();
+
+  // Do an update/render straight away
+  UpdateTimeSinceLastRender();
+  UpdateRender( false );
+
+  ChangeState( State::RUNNING );
+}
+
+void SingleThreadController::Pause()
+{
+  if( mState == State::RUNNING )
+  {
+    DALI_LOG_INFO( gLogFilter, Debug::General, "%s()\n", __FUNCTION__ );
+
+    ChangeState( State::PAUSED );
+
+    AddPerformanceMarker( PerformanceInterface::PAUSED );
+  }
+}
+
+void SingleThreadController::Resume()
+{
+  if( mState == State::PAUSED )
+  {
+    DALI_LOG_INFO( gLogFilter, Debug::General, "%s()\n", __FUNCTION__ );
+
+    // Do an update/render straight away
+    UpdateTimeSinceLastRender();
+    UpdateRender( false );
+
+    ChangeState( State::RUNNING );
+
+    AddPerformanceMarker( PerformanceInterface::RESUME );
+  }
+}
+
+void SingleThreadController::Stop()
+{
+  if( mState != State::STOPPED )
+  {
+    DALI_LOG_INFO( gLogFilter, Debug::General, "%s()\n", __FUNCTION__ );
+
+    ChangeState( State::STOPPED );
+
+    if( mUpdatingAndRendering )
+    {
+      // If we interrupted an update/render for this stop, then we should NOT terminate GL just yet
+      mStopRequestedWhileRendering = true;
+    }
+    else
+    {
+      StopRendering();
+    }
+  }
+}
+
+void SingleThreadController::RequestUpdate()
+{
+  if( mState == State::SLEEPING )
+  {
+    DALI_LOG_INFO( gLogFilter, Debug::General, "%s()\n", __FUNCTION__ );
+
+    // Do an update/render straight away
+    UpdateTimeSinceLastRender();
+    UpdateRender( false );
+
+    ChangeState( State::RUNNING );
+  }
+}
+
+void SingleThreadController::RequestUpdateOnce()
+{
+  if( mState == State::SLEEPING || mState == State::PAUSED )
+  {
+    DALI_LOG_INFO( gLogFilter, Debug::General, "%s()\n", __FUNCTION__ );
+
+    // Just do one update and render
+
+    Integration::UpdateStatus status;
+    mCore.Update( 0.0f, mLastUpdateRenderTime, mLastUpdateRenderTime + mRefreshRate * MILLISECONDS_PER_FRAME, status );
+
+    Integration::RenderStatus renderStatus;
+    mRenderHelper.PreRender();
+    mCore.Render( renderStatus );
+    if( renderStatus.HasRendered() )
+    {
+      mRenderHelper.PostRender();
+    }
+  }
+}
+
+void SingleThreadController::ReplaceSurface( RenderSurface* newSurface )
+{
+  DALI_LOG_INFO( gLogFilter, Debug::General, "%s()\n", __FUNCTION__ );
+  mRenderHelper.ReplaceSurface( newSurface );
+}
+
+void SingleThreadController::SetRenderRefreshRate( unsigned int refreshRate )
+{
+  if ( refreshRate != mRefreshRate )
+  {
+    DALI_LOG_INFO( gLogFilter, Debug::General, "%s()\n", __FUNCTION__ );
+
+    mRefreshRate = refreshRate;
+
+    if( mTimer )
+    {
+      mTimer.SetInterval( mRefreshRate * MILLISECONDS_PER_FRAME );
+    }
+  }
+}
+
+bool SingleThreadController::OnTimerTick()
+{
+  DALI_LOG_INFO( gLogFilter, Debug::General, "%s()\n", __FUNCTION__ );
+
+  if( mState == State::RUNNING )
+  {
+    UpdateRender( true );
+  }
+  else if( mState == State::STOPPED &&
+           mStopRequestedWhileRendering )
+  {
+    DALI_LOG_INFO( gLogFilter, Debug::General, "%s(): STOPPING\n", __FUNCTION__ );
+
+    StopRendering();
+
+    mStopRequestedWhileRendering = false;
+
+    return false; // Stop the timer
+  }
+  return true;
+}
+
+void SingleThreadController::UpdateRender( bool incrementTime )
+{
+  DALI_LOG_INFO( gLogFilter, Debug::General, "%s():START\n", __FUNCTION__ );
+
+  mUpdatingAndRendering = true;
+
+  float lastFrameDelta( 0.0f );
+
+  if( incrementTime )
+  {
+    // Use our usual time per frame for smoother animations rather than the real elapsed time
+
+    lastFrameDelta = mRefreshRate * SECONDS_PER_FRAME;
+    mLastUpdateRenderTime += mRefreshRate * MILLISECONDS_PER_FRAME;
+  }
+
+  Integration::UpdateStatus updateStatus;
+  AddPerformanceMarker( PerformanceInterface::UPDATE_START );
+  mCore.Update( lastFrameDelta, mLastUpdateRenderTime, mLastUpdateRenderTime + mRefreshRate * MILLISECONDS_PER_FRAME, updateStatus );
+  AddPerformanceMarker( PerformanceInterface::UPDATE_END );
+
+  mFpsTracker.Track( UpdateTimeSinceLastRender() );
+
+  unsigned int keepUpdatingStatus = updateStatus.KeepUpdating();
+
+  // Optional logging of update/render status
+  mUpdateStatusLogger.Log( keepUpdatingStatus );
+
+  // Ensure we did not get interrupted an STOPPED
+  if( mState != State::STOPPED )
+  {
+    mRenderHelper.ConsumeEvents();
+    mRenderHelper.PreRender();
+
+    Integration::RenderStatus renderStatus;
+    AddPerformanceMarker( PerformanceInterface::RENDER_START );
+    mCore.Render( renderStatus );
+    AddPerformanceMarker( PerformanceInterface::RENDER_END );
+
+    if( renderStatus.HasRendered() )
+    {
+      mRenderHelper.PostRender();
+    }
+
+    if( ! keepUpdatingStatus &&
+        ! renderStatus.NeedsUpdate() )
+    {
+      ChangeState( State::SLEEPING );
+    }
+  }
+
+  mUpdatingAndRendering = false;
+
+  DALI_LOG_INFO( gLogFilter, Debug::General, "%s():END\n", __FUNCTION__ );
+}
+
+float SingleThreadController::UpdateTimeSinceLastRender()
+{
+  float timeSinceLastRender = 0.0f;
+
+  // No need calculating if FPS tracking is NOT enabled
+  if( mFpsTracker.Enabled() )
+  {
+    uint64_t seconds = 0;
+    uint64_t microSeconds = 0;
+
+    mPlatformAbstraction.GetTimeNanoseconds( seconds, microSeconds );
+    microSeconds /= NANOSECONDS_PER_MICROSECOND;
+
+    uint64_t currentTime = ( seconds * MICROSECONDS_PER_SECOND ) + microSeconds;
+
+    uint64_t delta = currentTime - mSystemTime;
+    mSystemTime = currentTime;
+
+    timeSinceLastRender = delta * MICROSECONDS_TO_SECONDS;
+  }
+
+  return timeSinceLastRender;
+}
+
+
+void SingleThreadController::AddPerformanceMarker( PerformanceInterface::MarkerType type )
+{
+  if( mPerformanceInterface )
+  {
+    mPerformanceInterface->AddMarker( type );
+  }
+}
+
+void SingleThreadController::ChangeState( State::Type state )
+{
+  mState = state;
+
+  switch( state )
+  {
+    case State::RUNNING:
+    {
+      mTimer.Start();
+      break;
+    }
+
+    case State::STOPPED:
+    case State::PAUSED:
+    case State::SLEEPING:
+    {
+      mTimer.Stop();
+    }
+  }
+}
+
+void SingleThreadController::StopRendering()
+{
+  mRenderHelper.Stop();
+
+  // Inform core of context destruction & shutdown EGL
+  mCore.ContextDestroyed();
+  mRenderHelper.ShutdownEgl();
+}
+
+} // namespace Adaptor
+
+} // namespace Internal
+
+} // namespace Dali
diff --git a/adaptors/base/single-threaded/single-thread-controller.h b/adaptors/base/single-threaded/single-thread-controller.h
new file mode 100644 (file)
index 0000000..64a6a8e
--- /dev/null
@@ -0,0 +1,195 @@
+#ifndef __DALI_INTERNAL_SINGLE_THREAD_CONTROLLER_H__
+#define __DALI_INTERNAL_SINGLE_THREAD_CONTROLLER_H__
+
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <stdint.h>
+#include <dali/public-api/signals/connection-tracker.h>
+#include <dali/integration-api/core.h>
+
+// INTERNAL INCLUDES
+#include <timer.h>
+#include <base/interfaces/performance-interface.h>
+#include <base/fps-tracker.h>
+#include <base/render-helper.h>
+#include <base/thread-controller-interface.h>
+#include <base/update-status-logger.h>
+
+namespace Dali
+{
+
+class RenderSurface;
+
+namespace Internal
+{
+
+namespace Adaptor
+{
+
+class AdaptorInternalServices;
+class EnvironmentOptions;
+
+/**
+ * Single Thread Controller, where events, updates & renders ALL occur on the same thread.
+ */
+class SingleThreadController : public ConnectionTracker,
+                               public ThreadControllerInterface
+{
+public:
+
+  /**
+   * Constructor
+   */
+  SingleThreadController( AdaptorInternalServices& adaptorInterfaces, const EnvironmentOptions& environmentOptions );
+
+  /**
+   * Non virtual destructor. Not intended as base class.
+   */
+  ~SingleThreadController();
+
+  /**
+   * @copydoc ThreadControllerInterface::Initialize()
+   */
+  void Initialize();
+
+  /**
+   * @copydoc ThreadControllerInterface::Start()
+   */
+  void Start();
+
+  /**
+   * @copydoc ThreadControllerInterface::Pause()
+   */
+  void Pause();
+
+  /**
+   * @copydoc ThreadControllerInterface::Resume()
+   */
+  void Resume();
+
+  /**
+   * @copydoc ThreadControllerInterface::Stop()
+   */
+  void Stop();
+
+  /**
+   * @copydoc ThreadControllerInterface::RequestUpdate()
+   */
+  void RequestUpdate();
+
+  /**
+   * @copydoc ThreadControllerInterface::RequestUpdateOnce()
+   */
+  void RequestUpdateOnce();
+
+  /**
+   * @copydoc ThreadControllerInterface::ReplaceSurface()
+   */
+  void ReplaceSurface( RenderSurface* surface );
+
+  /**
+   * @copydoc ThreadControllerInterface::SetRenderRefreshRate()
+   */
+  void SetRenderRefreshRate( unsigned int refreshRate );
+
+private:
+
+  /**
+   * State Machine
+   */
+  struct State
+  {
+    enum Type
+    {
+      STOPPED,
+      RUNNING,
+      PAUSED,
+      SLEEPING
+    };
+  };
+
+  // Undefined copy constructor.
+  SingleThreadController( const SingleThreadController& );
+
+  // Undefined assignment operator.
+  SingleThreadController& operator=( const SingleThreadController& );
+
+  /**
+   * Ticks whenever the timer expires
+   */
+  bool OnTimerTick();
+
+  /**
+   * Runs the update and render
+   *
+   * @param[in] incrementTime If true, then the animation times are incremented.
+   */
+  void UpdateRender( bool incrementTime );
+
+  /**
+   * Updates mCurrentTime and gets the time elapsed (in seconds) since last time this function was called.
+   *
+   * @return time elapsed (in seconds) since last call.
+   */
+  float UpdateTimeSinceLastRender();
+
+  /**
+   * Helper to add a performance marker to the performance server (if it's active)
+   * @param type performance marker type
+   */
+  void AddPerformanceMarker( PerformanceInterface::MarkerType type );
+
+  /**
+   * Changes the state and performs any other state-change related functionality.
+   * @param[in] state The new state
+   */
+  void ChangeState( State::Type state );
+
+  /**
+   * Performs operations to stop rendering, e.g. informing Core of context being destroyed & shutting down EGL.
+   */
+  void StopRendering();
+
+private:
+
+  Dali::Timer                       mTimer;                           ///< Ensures an update & render is run every frame.
+  FpsTracker                        mFpsTracker;                      ///< Object that tracks the FPS
+  UpdateStatusLogger                mUpdateStatusLogger;              ///< Object that logs the update-status as required.
+
+  RenderHelper                      mRenderHelper;                    ///< Helper class for EGL, pre & post rendering
+
+  Integration::Core&                mCore;                            ///< DALi core reference
+  Integration::PlatformAbstraction& mPlatformAbstraction;             ///< To get the current time
+  PerformanceInterface*             mPerformanceInterface;            ///< The performance logging interface
+
+  uint64_t                          mLastUpdateRenderTime;            ///< Last time we did an update and render
+  uint64_t                          mSystemTime;                      ///< The current system time for FPS calculations
+  unsigned int                      mRefreshRate;                     ///< Frame skipping count
+  State::Type                       mState;                           ///< The state
+  bool                              mUpdatingAndRendering:1;          ///< Set to true when we are updating and rendering.
+  bool                              mStopRequestedWhileRendering:1;   ///< Set to true if we were told to stop while we were in the middle of a render
+};
+
+} // namespace Adaptor
+
+} // namespace Internal
+
+} // namespace Dali
+
+#endif // __DALI_INTERNAL_SINGLE_THREAD_CONTROLLER_H__
index 3fa4641..3c8aa5f 100644 (file)
@@ -22,6 +22,7 @@
 #include <base/environment-options.h>
 #include <base/thread-controller-interface.h>
 #include <base/separate-update-render/separate-update-render-controller.h>
+#include <base/single-threaded/single-thread-controller.h>
 
 namespace Dali
 {
@@ -39,9 +40,15 @@ ThreadController::ThreadController( AdaptorInternalServices& adaptorInterfaces,
   {
     case ThreadingMode::SEPARATE_UPDATE_RENDER:
     case ThreadingMode::COMBINED_UPDATE_RENDER:
-    case ThreadingMode::SINGLE_THREADED:
     {
       mThreadControllerInterface = new SeparateUpdateRenderController( adaptorInterfaces, environmentOptions );
+      break;
+    }
+
+    case ThreadingMode::SINGLE_THREADED:
+    {
+      mThreadControllerInterface = new SingleThreadController( adaptorInterfaces, environmentOptions );
+      break;
     }
   }
 }