add missing update keep updating reasons to logging
[platform/core/uifw/dali-adaptor.git] / adaptors / base / update-thread.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 "update-thread.h"
20
21 // EXTERNAL INCLUDES
22 #include <cstdlib>
23 #include <fstream>
24 #include <sstream>
25 #include <boost/thread.hpp>
26
27 // INTERNAL INCLUDES
28 #include <dali/integration-api/debug.h>
29 #include <base/interfaces/adaptor-internal-services.h>
30 #include <base/update-render-synchronization.h>
31 #include <base/environment-options.h>
32
33 namespace Dali
34 {
35
36 namespace Internal
37 {
38
39 namespace Adaptor
40 {
41
42 namespace
43 {
44 const unsigned int MICROSECONDS_PER_MILLISECOND( 1000 );
45
46 #if defined(DEBUG_ENABLED)
47 Integration::Log::Filter* gUpdateLogFilter = Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_UPDATE_THREAD");
48 #endif
49 } // unnamed namespace
50
51 UpdateThread::UpdateThread( UpdateRenderSynchronization& sync,
52                             AdaptorInternalServices& adaptorInterfaces,
53                             const EnvironmentOptions& environmentOptions )
54 : mUpdateRenderSync( sync ),
55   mCore( adaptorInterfaces.GetCore()),
56   mFpsTrackingSeconds( environmentOptions.GetFrameRateLoggingFrequency() ),
57   mElapsedTime( 0.0f ),
58   mElapsedSeconds( 0u ),
59   mStatusLogInterval( environmentOptions.GetUpdateStatusLoggingFrequency() ),
60   mStatusLogCount( 0u ),
61   mNotificationTrigger( adaptorInterfaces.GetTriggerEventInterface() ),
62   mThread( NULL ),
63   mEnvironmentOptions( environmentOptions )
64 {
65   if( mFpsTrackingSeconds > 0 )
66   {
67     mFpsRecord.resize( mFpsTrackingSeconds, 0.0f );
68   }
69 }
70
71 UpdateThread::~UpdateThread()
72 {
73   if(mFpsTrackingSeconds > 0)
74   {
75     OutputFPSRecord();
76   }
77   Stop();
78 }
79
80 void UpdateThread::Start()
81 {
82   DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Start()\n");
83   if ( !mThread )
84   {
85     // Create and run the update-thread
86     mThread = new boost::thread( boost::bind( &UpdateThread::Run, this ) );
87   }
88 }
89
90 void UpdateThread::Stop()
91 {
92   DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Stop()\n");
93   if( mThread )
94   {
95     // wait for the thread to finish
96     mThread->join();
97
98     delete mThread;
99     mThread = NULL;
100   }
101 }
102
103 bool UpdateThread::Run()
104 {
105   DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run()\n");
106   Integration::UpdateStatus status;
107
108   // install a function for logging
109   mEnvironmentOptions.InstallLogFunction();
110
111   bool running( true );
112
113   // Update loop, we stay inside here while the update-thread is running
114   while ( running )
115   {
116     DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run. 1 - Sync()\n");
117
118     // Inform synchronization object update is ready to run, this will pause update thread if required.
119     mUpdateRenderSync.UpdateReadyToRun();
120     DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run. 2 - Ready()\n");
121
122     // get the last delta and the predict when this update will be rendered
123     float lastFrameDelta( 0.0f );
124     unsigned int lastSyncTime( 0 );
125     unsigned int nextSyncTime( 0 );
126     mUpdateRenderSync.PredictNextSyncTime( lastFrameDelta, lastSyncTime, nextSyncTime );
127
128     DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run. 3 - Update(delta:%f, lastSync:%u, nextSync:%u)\n", lastFrameDelta, lastSyncTime, nextSyncTime);
129
130     mCore.Update( lastFrameDelta, lastSyncTime, nextSyncTime, status );
131
132     if( mFpsTrackingSeconds > 0 )
133     {
134       FPSTracking(status.SecondsFromLastFrame());
135     }
136
137     // Do the notifications first so the actor-thread can start processing them
138     if( status.NeedsNotification() )
139     {
140       // Tell the event-thread to wake up (if asleep) and send a notification event to Core
141       mNotificationTrigger.Trigger();
142     }
143
144     bool renderNeedsUpdate;
145
146     // tell the synchronisation class that a buffer has been written to,
147     // and to wait until there is a free buffer to write to
148     running = mUpdateRenderSync.UpdateSyncWithRender( renderNeedsUpdate );
149     DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run. 4 - UpdateSyncWithRender complete\n");
150
151     if( running )
152     {
153       unsigned int keepUpdatingStatus = status.KeepUpdating();
154
155       // Optional logging of update/render status
156       if ( mStatusLogInterval )
157       {
158         UpdateStatusLogging( keepUpdatingStatus, renderNeedsUpdate );
159       }
160
161       //  2 things can keep update running.
162       // - The status of the last update
163       // - The status of the last render
164       bool runUpdate = (Integration::KeepUpdating::NOT_REQUESTED != keepUpdatingStatus) || renderNeedsUpdate;
165
166       if( !runUpdate )
167       {
168         DALI_LOG_INFO( gUpdateLogFilter, Debug::Verbose, "UpdateThread::Run. 5 - Nothing to update, trying to sleep\n");
169
170         running = mUpdateRenderSync.UpdateTryToSleep();
171       }
172     }
173   }
174
175   // uninstall a function for logging
176   mEnvironmentOptions.UnInstallLogFunction();
177
178   return true;
179 }
180
181 void UpdateThread::FPSTracking(float secondsFromLastFrame)
182 {
183   if (mElapsedSeconds < mFpsTrackingSeconds)
184   {
185     mElapsedTime += secondsFromLastFrame;
186     if( secondsFromLastFrame  > 1.0 )
187     {
188       int seconds = floor(mElapsedTime);
189       mElapsedSeconds += seconds;
190       mElapsedTime -= static_cast<float>(seconds);
191     }
192     else
193     {
194       if( mElapsedTime>=1.0f )
195       {
196         mElapsedTime -= 1.0f;
197         mFpsRecord[mElapsedSeconds] += 1.0f - mElapsedTime/secondsFromLastFrame;
198         mElapsedSeconds++;
199         mFpsRecord[mElapsedSeconds] += mElapsedTime/secondsFromLastFrame;
200       }
201       else
202       {
203         mFpsRecord[mElapsedSeconds] += 1.0f;
204       }
205     }
206   }
207   else
208   {
209     OutputFPSRecord();
210     mFpsRecord.clear();
211     mFpsTrackingSeconds = 0;
212   }
213 }
214
215 void UpdateThread::OutputFPSRecord()
216 {
217   for(unsigned int i = 0; i < mElapsedSeconds; i++)
218   {
219     DALI_LOG_FPS("fps( %d ):%f\n",i ,mFpsRecord[i]);
220   }
221   std::ofstream outFile("/tmp/dalifps.txt");
222   if(outFile.is_open())
223   {
224     for(unsigned int i = 0; i < mElapsedSeconds; i++)
225     {
226       outFile << mFpsRecord[i]<<std::endl;
227     }
228     outFile.close();
229   }
230 }
231
232 void UpdateThread::UpdateStatusLogging( unsigned int keepUpdatingStatus, bool renderNeedsUpdate )
233 {
234   DALI_ASSERT_ALWAYS( mStatusLogInterval );
235
236   std::ostringstream oss;
237
238   if ( !(++mStatusLogCount % mStatusLogInterval) )
239   {
240     oss << "UpdateStatusLogging keepUpdating: " << keepUpdatingStatus << " ";
241
242     if ( keepUpdatingStatus )
243     {
244       oss << "because: ";
245     }
246
247     if ( keepUpdatingStatus & Integration::KeepUpdating::STAGE_KEEP_RENDERING )
248     {
249       oss << "<Stage::KeepRendering() used> ";
250     }
251
252     if ( keepUpdatingStatus & Integration::KeepUpdating::INCOMING_MESSAGES )
253     {
254       oss << "<Messages sent to Update> ";
255     }
256
257     if ( keepUpdatingStatus & Integration::KeepUpdating::ANIMATIONS_RUNNING )
258     {
259       oss << "<Animations running> ";
260     }
261
262     if ( keepUpdatingStatus & Integration::KeepUpdating::DYNAMICS_CHANGED )
263     {
264       oss << "<Dynamics running> ";
265     }
266
267     if ( keepUpdatingStatus & Integration::KeepUpdating::LOADING_RESOURCES )
268     {
269       oss << "<Resources loading> ";
270     }
271
272     if ( keepUpdatingStatus & Integration::KeepUpdating::NOTIFICATIONS_PENDING )
273     {
274       oss << "<Notifications pending> ";
275     }
276
277     if ( keepUpdatingStatus & Integration::KeepUpdating::MONITORING_PERFORMANCE )
278     {
279       oss << "<Monitoring performance> ";
280     }
281
282     if ( keepUpdatingStatus & Integration::KeepUpdating::RENDER_TASK_SYNC )
283     {
284       oss << "<Render task waiting for completion> ";
285     }
286
287     if ( renderNeedsUpdate )
288     {
289       oss << "<Render needs Update> ";
290     }
291
292     DALI_LOG_UPDATE_STATUS( "%s\n", oss.str().c_str() );
293   }
294 }
295
296 } // namespace Adaptor
297
298 } // namespace Internal
299
300 } // namespace Dali