Moved ECore specific indicator-impl out of common
[platform/core/uifw/dali-adaptor.git] / adaptors / base / separate-update-render / frame-time.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include "frame-time.h"
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23 #include <dali/integration-api/platform-abstraction.h>
24
25 // INTERNAL INCLUDES
26 #include <base/time-service.h>
27
28 namespace Dali
29 {
30
31 using Integration::PlatformAbstraction;
32
33 namespace Internal
34 {
35 namespace Adaptor
36 {
37
38 namespace
39 {
40 #if defined(DEBUG_ENABLED)
41 Integration::Log::Filter* gLogFilter = Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_FRAME_TIME");
42 #endif
43
44 const unsigned int DEFAULT_MINIMUM_FRAME_TIME_INTERVAL( 16667u );
45
46 const unsigned int MICROSECONDS_PER_SECOND( 1000000u );
47 const unsigned int MICROSECONDS_PER_MILLISECOND( 1000u );
48 const unsigned int NANOSECONDS_PER_MICROSECOND( 1000u);
49
50 const float        MICROSECONDS_TO_SECONDS( 0.000001f );
51
52 const unsigned int HISTORY_SIZE(3);
53
54 // constants to keep code readability with unsigned int has to be used as boolean (due to multithreading)
55 const unsigned int TRUE = 1u;
56 const unsigned int FALSE = 0u;
57 } // unnamed namespace
58
59
60 FrameTime::FrameTime()
61 : mMinimumFrameTimeInterval( DEFAULT_MINIMUM_FRAME_TIME_INTERVAL ),
62   mLastSyncTime( 0u ),
63   mLastSyncTimeAtUpdate( 0u ),
64   mLastSyncFrameNumber( 0u ),
65   mLastUpdateFrameNumber( 0u ),
66   mRunning( TRUE ),
67   mFirstFrame( TRUE ),
68   writePos( 0u ),
69   mExtraUpdatesSinceSync( 0u )
70 {
71   // Clear buffer
72   for ( unsigned int i = 0; i < HISTORY_SIZE; ++i )
73   {
74     mPreviousUpdateFrames[i] = 0;
75   }
76
77   SetLastSyncTime();
78   mLastSyncTimeAtUpdate = mLastSyncTime;
79
80   DALI_LOG_INFO( gLogFilter, Debug::Concise, "FrameTime Initialized\n" );
81 }
82
83 FrameTime::~FrameTime()
84 {
85 }
86
87 void FrameTime::SetMinimumFrameTimeInterval( unsigned int interval )
88 {
89   mMinimumFrameTimeInterval = interval;
90 }
91
92 void FrameTime::SetSyncTime( unsigned int frameNumber )
93 {
94   // Only set the render time if we are running
95   if ( mRunning )
96   {
97     SetLastSyncTime();
98
99     mLastSyncFrameNumber = frameNumber;
100
101     DALI_LOG_INFO( gLogFilter, Debug::General, "FrameTime: SetSyncTime(): Frame: %u: Time: %u\n", mLastSyncFrameNumber, (unsigned int) ( mLastSyncTime / MICROSECONDS_PER_MILLISECOND ) );
102   }
103 }
104
105 void FrameTime::Suspend()
106 {
107   mRunning = FALSE;
108
109   // Reset members
110   mLastSyncFrameNumber = 0;
111   mLastUpdateFrameNumber = 0;
112   writePos = 0;
113   mExtraUpdatesSinceSync = 0;
114
115   // Clear buffer
116   for ( unsigned int i = 0; i < HISTORY_SIZE; ++i )
117   {
118     mPreviousUpdateFrames[i] = 0;
119   }
120
121   DALI_LOG_INFO( gLogFilter, Debug::Concise, "FrameTime: Suspended\n" );
122 }
123
124 void FrameTime::Resume()
125 {
126   DALI_LOG_INFO( gLogFilter, Debug::Concise, "FrameTime: Resuming\n" );
127
128   SetLastSyncTime();   // Should only update the last Sync time so the elapsed time during suspension is taken into consideration when we next update.
129   mFirstFrame = TRUE;
130
131   mRunning = TRUE;
132 }
133
134 void FrameTime::Sleep()
135 {
136   DALI_LOG_INFO( gLogFilter, Debug::Concise, "FrameTime: Sleeping\n" );
137
138   // Mimic Suspend behaviour
139   Suspend();
140 }
141
142 void FrameTime::WakeUp()
143 {
144   DALI_LOG_INFO( gLogFilter, Debug::Concise, "FrameTime: Waking Up\n" );
145
146   SetLastSyncTime();
147   mLastSyncTimeAtUpdate = mLastSyncTime; // We do not want any animations to progress as we have just been woken up.
148   mFirstFrame = TRUE;
149   mRunning = TRUE;
150 }
151
152 void FrameTime::PredictNextSyncTime( float& lastFrameDeltaSeconds, unsigned int& lastSyncTimeMilliseconds, unsigned int& nextSyncTimeMilliseconds )
153 {
154   if ( mRunning )
155   {
156     const unsigned int minimumFrameTimeInterval( mMinimumFrameTimeInterval );
157     const uint64_t lastSyncTime( mLastSyncTime );
158     const unsigned int lastSyncFrameNumber( mLastSyncFrameNumber );
159
160     float lastFrameDelta( 0.0f ); // Assume the last update frame delta is 0.
161     unsigned int framesTillNextSync( 1 ); // Assume next render will be in one Sync frame time.
162
163     unsigned int framesInLastUpdate( lastSyncFrameNumber - mLastUpdateFrameNumber );
164     lastFrameDelta = lastSyncTime - mLastSyncTimeAtUpdate;
165
166     // We should only evaluate the previous frame values if this is not the first frame.
167     if ( !mFirstFrame )
168     {
169       // Check whether we have had any Syncs since we last did an Update.
170       if ( framesInLastUpdate == 0 )
171       {
172         // We have had another update before a Sync, increment counter.
173         ++mExtraUpdatesSinceSync;
174
175         // This update frame will be rendered mUpdatesSinceSync later.
176         framesTillNextSync += mExtraUpdatesSinceSync;
177         DALI_LOG_INFO(gLogFilter, Debug::Concise, "PredictNextSyncTime UpdateBeforeSync\n");
178       }
179       else
180       {
181         mExtraUpdatesSinceSync = 0;
182       }
183
184       // If more than one frame elapsed since last Update, then check if this is a recurring theme so we can accurately predict when this Update is rendered.
185       if ( framesInLastUpdate > 1 )
186       {
187         DALI_LOG_INFO(gLogFilter, Debug::Concise, "PredictNextSyncTime framesInLastUpdate:%u\n", framesInLastUpdate);
188         unsigned int average(0);
189         for ( unsigned int i = 0; i < HISTORY_SIZE; ++i )
190         {
191           average += mPreviousUpdateFrames[i];
192         }
193         average /= HISTORY_SIZE;
194
195         if ( average > 1 )
196         {
197           // Our average shows a recurring theme, we are missing frames when rendering so calculate number of frames this will take.
198           framesTillNextSync = average;
199         }
200       }
201
202       // Write the number of frames the last update took to the array.
203       mPreviousUpdateFrames[writePos] = framesInLastUpdate;
204       writePos = ( writePos + 1 ) % HISTORY_SIZE;
205     }
206
207     mLastUpdateFrameNumber = lastSyncFrameNumber;
208     mLastSyncTimeAtUpdate = lastSyncTime;
209     mFirstFrame = FALSE;
210
211     // Calculate the time till the next render
212     unsigned int timeTillNextRender( minimumFrameTimeInterval * framesTillNextSync );
213
214     // Set the input variables
215     lastFrameDeltaSeconds = lastFrameDelta * MICROSECONDS_TO_SECONDS;
216     lastSyncTimeMilliseconds = lastSyncTime / MICROSECONDS_PER_MILLISECOND;
217     nextSyncTimeMilliseconds = ( lastSyncTime + timeTillNextRender ) / MICROSECONDS_PER_MILLISECOND;
218
219     DALI_LOG_INFO( gLogFilter, Debug::General, "FrameTime: Frame: %u, Time: %u, NextTime: %u, LastDelta: %f\n", mLastUpdateFrameNumber, lastSyncTimeMilliseconds, nextSyncTimeMilliseconds, lastFrameDeltaSeconds );
220     DALI_LOG_INFO( gLogFilter, Debug::Verbose, "                      FramesInLastUpdate: %u, FramesTillNextSync: %u\n", framesInLastUpdate, framesTillNextSync );
221   }
222 }
223
224 inline void FrameTime::SetLastSyncTime()
225 {
226   uint64_t nanoseconds( 0u );
227   TimeService::GetNanoseconds( nanoseconds );
228
229   mLastSyncTime = nanoseconds / NANOSECONDS_PER_MICROSECOND;
230 }
231
232 } // namespace Adaptor
233 } // namespace Internal
234 } // namespace Dali