Support to build against ubuntu environment.
[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 core what the minimum frame interval is
75   mCore.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   mEGL->SetRefreshSync( mCurrent.syncMode );
241
242   // tell core it has a context
243   mCore.ContextCreated();
244
245 }
246
247 void RenderThread::ConsumeEvents()
248 {
249   // tell surface to consume any events to avoid memory leaks
250   mCurrent.surface->ConsumeEvents();
251 }
252
253 void RenderThread::CheckForUpdates()
254 {
255   // atomic check to see if we've got updates, resets the flag int
256   if( __sync_fetch_and_and( &mNewDataAvailable, 0 ) )
257   {
258     // scope for lock
259     // NOTE! This block is the only place in render side where mNewValues can be used inside render thread !!!
260     {
261       // need to lock to access new values
262       boost::unique_lock< boost::mutex > lock( mThreadDataLock );
263
264       // did the sync mode change
265       if( mCurrent.syncMode != mNewValues.syncMode )
266       {
267         mCurrent.syncMode = mNewValues.syncMode;
268         mEGL->SetRefreshSync( mCurrent.syncMode );
269       }
270
271       // check if the surface needs replacing
272       if( mNewValues.replaceSurface )
273       {
274         mNewValues.replaceSurface = false; // reset the flag
275         // change the surface
276         ChangeSurface( mNewValues.surface );
277         mNewValues.surface = NULL;
278       }
279     }
280   }
281 }
282
283 void RenderThread::ChangeSurface( RenderSurface* newSurface )
284 {
285   // This is designed for replacing pixmap surfaces, but should work for window as well
286   // we need to delete the egl surface and renderable (pixmap / window)
287   // Then create a new pixmap/window and new egl surface
288   // If the new surface has a different display connection, then the context will be lost
289   DALI_ASSERT_ALWAYS( newSurface && "NULL surface" )
290   bool contextLost = newSurface->ReplaceEGLSurface( *mEGL );
291
292   if( contextLost )
293   {
294     DALI_LOG_WARNING("Context lost\n");
295     mCore.ContextToBeDestroyed();
296     mCore.ContextCreated();
297   }
298
299   // if both new and old surface are using the same display, and the display
300   // connection was created by Dali, then transfer
301   // display owner ship to the new surface.
302   mCurrent.surface->TransferDisplayOwner( *newSurface );
303
304   // use the new surface from now on
305   mCurrent.surface = newSurface;
306
307   // after rendering, NotifySurfaceChangeCompleted will be called
308   mSurfaceReplacing = true;
309 }
310
311 void RenderThread::NotifySurfaceChangeCompleted()
312 {
313   {
314     boost::unique_lock< boost::mutex > lock( mSurfaceChangedMutex );
315     mSurfaceReplaceCompleted = true;
316   }
317   // notify main thread
318   mSurfaceChangedNotify.notify_all();
319 }
320
321 void RenderThread::ShutdownEgl()
322 {
323   // inform core the context is about to be destroyed,
324   mCore.ContextToBeDestroyed();
325
326   // give a chance to destroy the OpenGL surface that created externally
327   mCurrent.surface->DestroyEglSurface( *mEGL );
328
329   // delete the GL context / egl surface
330   mEGL->TerminateGles();
331 }
332
333 bool RenderThread::PreRender()
334 {
335   bool success = mCurrent.surface->PreRender( *mEGL, mGLES );
336   if( success )
337   {
338     mGLES.PreRender();
339   }
340   return success;
341 }
342
343 void RenderThread::PostRender( unsigned int timeDelta )
344 {
345   // Inform the gl implementation that rendering has finished before informing the surface
346   mGLES.PostRender(timeDelta);
347
348   // Inform the surface that rendering this frame has finished.
349   mCurrent.surface->PostRender( *mEGL, mGLES, timeDelta,
350                                 mSurfaceReplacing ? RenderSurface::SYNC_MODE_NONE : RenderSurface::SYNC_MODE_WAIT );
351 }
352
353 } // namespace Adaptor
354
355 } // namespace Internal
356
357 } // namespace Dali