Moved FrameTime from Core to Adaptor
[platform/core/uifw/dali-adaptor.git] / adaptors / base / render-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 "render-thread.h"
20
21
22 // INTERNAL INCLUDES
23 #include <dali/integration-api/debug.h>
24 #include <base/interfaces/adaptor-internal-services.h>
25 #include <base/update-render-synchronization.h>
26 #include <base/environment-options.h>
27
28
29 namespace Dali
30 {
31
32 namespace Internal
33 {
34
35 namespace Adaptor
36 {
37
38 namespace
39 {
40
41 const unsigned int TIME_PER_FRAME_IN_MICROSECONDS = 16667;
42
43 } // unnamed namespace
44
45 RenderThread::RenderThread( UpdateRenderSynchronization& sync,
46                             AdaptorInternalServices& adaptorInterfaces,
47                             const EnvironmentOptions& environmentOptions )
48 : mUpdateRenderSync( sync ),
49   mCore( adaptorInterfaces.GetCore() ),
50   mGLES( adaptorInterfaces.GetGlesInterface() ),
51   mEglFactory( &adaptorInterfaces.GetEGLFactoryInterface()),
52   mEGL( NULL ),
53   mThread( NULL ),
54   mSurfaceReplacing( false ),
55   mNewDataAvailable( false ),
56   mSurfaceReplaceCompleted( false ),
57   mEnvironmentOptions( environmentOptions )
58 {
59   // set the initial values before render thread starts
60   mCurrent.surface = adaptorInterfaces.GetRenderSurfaceInterface();
61 }
62
63 RenderThread::~RenderThread()
64 {
65   DALI_ASSERT_ALWAYS( mThread == NULL && "RenderThread is still alive");
66   mEglFactory->Destroy();
67 }
68
69 void RenderThread::Start()
70 {
71   // initialise GL and kick off render thread
72   DALI_ASSERT_ALWAYS( !mEGL && "Egl already initialized" );
73
74   // Tell frame timer what the minimum frame interval is
75   mUpdateRenderSync.SetMinimumFrameTimeInterval( mCurrent.syncMode * TIME_PER_FRAME_IN_MICROSECONDS );
76
77   // create the render thread, initially we are rendering
78   mThread = new boost::thread(boost::bind(&RenderThread::Run, this));
79
80   // Inform surface to block waiting for RenderSync
81   mCurrent.surface->SetSyncMode( RenderSurface::SYNC_MODE_WAIT );
82 }
83
84 void RenderThread::Stop()
85 {
86   // shutdown the render thread and destroy the opengl context
87   if( mThread )
88   {
89     // Tell surface we have stopped rendering
90     mCurrent.surface->StopRender();
91
92     // wait for the thread to finish
93     mThread->join();
94
95     delete mThread;
96     mThread = NULL;
97   }
98 }
99
100 void RenderThread::ReplaceSurface( RenderSurface* surface )
101 {
102   // Make sure it's a new surface. Note! we are reading the current value of render thread here, but reading is ok
103   DALI_ASSERT_ALWAYS( surface != mCurrent.surface && "Trying to replace surface with itself" );
104
105   // lock and set to false
106   {
107     boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
108     mSurfaceReplaceCompleted = false;
109   }
110
111   // lock cache and set update flag at the end of function
112   {
113     SendMessageGuard msg( *this );
114     // set new values to cache
115     mNewValues.replaceSurface = true;
116     mNewValues.surface = surface;
117   }
118
119   /*
120    * Reset the mPixmapFlushed condition if surface was changed.
121    * : in this case, client can not handle the previous damage because surface was changed.
122    */
123   RenderSync();
124 }
125
126 void RenderThread::WaitForSurfaceReplaceComplete()
127 {
128   boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
129
130   // if already completed no need to wait
131   while( !mSurfaceReplaceCompleted )
132   {
133     mSurfaceChangedNotify.wait( lock ); // Block the main thread here and releases mSurfaceChangedMutex so the render-thread can notify us
134   }
135 }
136
137 void RenderThread::SetVSyncMode( EglInterface::SyncMode syncMode )
138 {
139   // lock cache and set update flag at the end of function
140   SendMessageGuard msg( *this );
141   // set new values to cache
142   mNewValues.syncMode = syncMode;
143 }
144
145 void RenderThread::RenderSync()
146 {
147   mCurrent.surface->RenderSync();
148 }
149
150 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
151 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
152 // The following methods are all executed inside render thread !!!
153 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
154 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
155
156 bool RenderThread::Run()
157 {
158   // install a function for logging
159   mEnvironmentOptions.InstallLogFunction();
160
161   InitializeEgl();
162
163   bool running( true );
164
165   // Wait for first update
166   mUpdateRenderSync.RenderSyncWithUpdate();
167
168   Dali::Integration::RenderStatus renderStatus;
169
170   uint64_t currentTime( 0 );
171
172   // render loop, we stay inside here when rendering
173   while( running )
174   {
175     // Consume any pending events
176     ConsumeEvents();
177
178     // Check if we've got updates from the main thread
179     CheckForUpdates();
180
181     // perform any pre-render operations
182     if(PreRender() == true)
183     {
184        // Render
185       mCore.Render( renderStatus );
186
187       // Notify the update-thread that a render has completed
188       mUpdateRenderSync.RenderFinished( renderStatus.NeedsUpdate() );
189
190       uint64_t newTime( mUpdateRenderSync.GetTimeMicroseconds() );
191
192       // perform any post-render operations
193       if ( renderStatus.HasRendered() )
194       {
195         PostRender( static_cast< unsigned int >(newTime - currentTime) );
196       }
197
198       if(mSurfaceReplacing)
199       {
200         // Notify main thread that surface was changed so it can release the old one
201         NotifySurfaceChangeCompleted();
202         mSurfaceReplacing = false;
203       }
204
205       currentTime = newTime;
206     }
207
208     // Wait until another frame has been updated
209     running = mUpdateRenderSync.RenderSyncWithUpdate();
210   }
211
212   // shut down egl
213   ShutdownEgl();
214
215   // install a function for logging
216   mEnvironmentOptions.UnInstallLogFunction();
217
218   return true;
219 }
220
221 void RenderThread::InitializeEgl()
222 {
223   mEGL = mEglFactory->Create();
224
225   DALI_ASSERT_ALWAYS( mCurrent.surface && "NULL surface" );
226
227   // initialize egl & OpenGL
228   mCurrent.surface->InitializeEgl( *mEGL );
229
230   // create the OpenGL context
231   mEGL->CreateContext();
232
233   // create the OpenGL surface
234   mCurrent.surface->CreateEglSurface( *mEGL );
235
236   // Make it current
237   mEGL->MakeContextCurrent();
238
239   // set the initial sync mode
240
241   //@todo This needs to call the render surface instead
242   mEGL->SetRefreshSync( mCurrent.syncMode );
243
244   // tell core it has a context
245   mCore.ContextCreated();
246
247 }
248
249 void RenderThread::ConsumeEvents()
250 {
251   // tell surface to consume any events to avoid memory leaks
252   mCurrent.surface->ConsumeEvents();
253 }
254
255 void RenderThread::CheckForUpdates()
256 {
257   // atomic check to see if we've got updates, resets the flag int
258   if( __sync_fetch_and_and( &mNewDataAvailable, 0 ) )
259   {
260     // scope for lock
261     // NOTE! This block is the only place in render side where mNewValues can be used inside render thread !!!
262     {
263       // need to lock to access new values
264       boost::unique_lock< boost::mutex > lock( mThreadDataLock );
265
266       // did the sync mode change
267       if( mCurrent.syncMode != mNewValues.syncMode )
268       {
269         mCurrent.syncMode = mNewValues.syncMode;
270
271         //@todo This needs to call the render surface instead
272         mEGL->SetRefreshSync( mCurrent.syncMode );
273       }
274
275       // check if the surface needs replacing
276       if( mNewValues.replaceSurface )
277       {
278         mNewValues.replaceSurface = false; // reset the flag
279         // change the surface
280         ChangeSurface( mNewValues.surface );
281         mNewValues.surface = NULL;
282       }
283     }
284   }
285 }
286
287 void RenderThread::ChangeSurface( RenderSurface* newSurface )
288 {
289   // This is designed for replacing pixmap surfaces, but should work for window as well
290   // we need to delete the egl surface and renderable (pixmap / window)
291   // Then create a new pixmap/window and new egl surface
292   // If the new surface has a different display connection, then the context will be lost
293   DALI_ASSERT_ALWAYS( newSurface && "NULL surface" )
294   bool contextLost = newSurface->ReplaceEGLSurface( *mEGL );
295
296   if( contextLost )
297   {
298     DALI_LOG_WARNING("Context lost\n");
299     mCore.ContextToBeDestroyed();
300     mCore.ContextCreated();
301   }
302
303   // if both new and old surface are using the same display, and the display
304   // connection was created by Dali, then transfer
305   // display owner ship to the new surface.
306   mCurrent.surface->TransferDisplayOwner( *newSurface );
307
308   // use the new surface from now on
309   mCurrent.surface = newSurface;
310
311   // after rendering, NotifySurfaceChangeCompleted will be called
312   mSurfaceReplacing = true;
313 }
314
315 void RenderThread::NotifySurfaceChangeCompleted()
316 {
317   {
318     boost::unique_lock< boost::mutex > lock( mSurfaceChangedMutex );
319     mSurfaceReplaceCompleted = true;
320   }
321   // notify main thread
322   mSurfaceChangedNotify.notify_all();
323 }
324
325 void RenderThread::ShutdownEgl()
326 {
327   // inform core the context is about to be destroyed,
328   mCore.ContextToBeDestroyed();
329
330   // give a chance to destroy the OpenGL surface that created externally
331   mCurrent.surface->DestroyEglSurface( *mEGL );
332
333   // delete the GL context / egl surface
334   mEGL->TerminateGles();
335 }
336
337 bool RenderThread::PreRender()
338 {
339   bool success = mCurrent.surface->PreRender( *mEGL, mGLES );
340   if( success )
341   {
342     mGLES.PreRender();
343   }
344   return success;
345 }
346
347 void RenderThread::PostRender( unsigned int timeDelta )
348 {
349   // Inform the gl implementation that rendering has finished before informing the surface
350   mGLES.PostRender(timeDelta);
351
352   // Inform the surface that rendering this frame has finished.
353   mCurrent.surface->PostRender( *mEGL, mGLES, timeDelta,
354                                 mSurfaceReplacing ? RenderSurface::SYNC_MODE_NONE : RenderSurface::SYNC_MODE_WAIT );
355 }
356
357 } // namespace Adaptor
358
359 } // namespace Internal
360
361 } // namespace Dali