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