From 2a23d7041f113122c3255f4acfe9c68bf61d1279 Mon Sep 17 00:00:00 2001 From: Adeel Kazmi Date: Thu, 29 Oct 2015 10:34:59 +0000 Subject: [PATCH] Added SingleThreadController Change-Id: Id88b7bc9c2192bc7782f4e664ca0b5903a947fb0 --- adaptors/base/file.list | 3 +- .../single-threaded/single-thread-controller.cpp | 356 +++++++++++++++++++++ .../single-threaded/single-thread-controller.h | 195 +++++++++++ adaptors/base/thread-controller.cpp | 9 +- 4 files changed, 561 insertions(+), 2 deletions(-) create mode 100644 adaptors/base/single-threaded/single-thread-controller.cpp create mode 100644 adaptors/base/single-threaded/single-thread-controller.h diff --git a/adaptors/base/file.list b/adaptors/base/file.list index f940b67..f583fa1 100644 --- a/adaptors/base/file.list +++ b/adaptors/base/file.list @@ -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 index 0000000..f0c3398 --- /dev/null +++ b/adaptors/base/single-threaded/single-thread-controller.cpp @@ -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 +#include + +// INTERNAL INCLUDES +#include +#include + +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 index 0000000..64a6a8e --- /dev/null +++ b/adaptors/base/single-threaded/single-thread-controller.h @@ -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 +#include +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include +#include + +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__ diff --git a/adaptors/base/thread-controller.cpp b/adaptors/base/thread-controller.cpp index 3fa4641..3c8aa5f 100644 --- a/adaptors/base/thread-controller.cpp +++ b/adaptors/base/thread-controller.cpp @@ -22,6 +22,7 @@ #include #include #include +#include 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; } } } -- 2.7.4