[SRUK] Initial copy from Tizen 2.2 version
[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   if( mLogOptions.IsFilterEnabled(Debug::LogRenderThread) )
192   {
193     // install a function for logging
194     mLogOptions.InstallLogFunction();
195   }
196
197   InitializeEgl();
198
199   bool running( true );
200
201   // Wait for first update
202   mUpdateRenderSync.RenderSyncWithUpdate();
203
204   Dali::Integration::RenderStatus renderStatus;
205
206   uint64_t currentTime( 0 );
207
208   // render loop, we stay inside here when rendering
209   while( running )
210   {
211     // Check if we've got updates from the main thread
212     CheckForUpdates();
213
214     // perform any pre-render operations
215     if(PreRender() == true)
216     {
217        // Render
218       mCore.Render( renderStatus );
219
220       // Notify the update-thread that a render has completed
221       mUpdateRenderSync.RenderFinished( renderStatus.NeedsUpdate() );
222
223       uint64_t newTime( mUpdateRenderSync.GetTimeMicroseconds() );
224
225       // perform any post-render operations
226       if ( renderStatus.HasRendered() )
227       {
228         PostRender( static_cast< unsigned int >(newTime - currentTime) );
229       }
230
231       if(mSurfaceReplacing)
232       {
233         // Notify main thread that surface was changed so it can release the old one
234         NotifySurfaceChangeCompleted();
235         mSurfaceReplacing = false;
236       }
237
238       currentTime = newTime;
239     }
240
241     // Wait until another frame has been updated
242     running = mUpdateRenderSync.RenderSyncWithUpdate();
243   }
244
245   // shut down egl
246   ShutdownEgl();
247
248   if( mLogOptions.IsFilterEnabled(Debug::LogRenderThread) )
249   {
250     // install a function for logging
251     mLogOptions.UnInstallLogFunction();
252   }
253
254
255   return true;
256 }
257
258 void RenderThread::InitializeEgl()
259 {
260   mEGL = mEglFactory->Create();
261
262   DALI_ASSERT_ALWAYS( mCurrent.surface && "NULL surface" );
263
264   // initialize egl & OpenGL
265   mCurrent.surface->InitializeEgl( *mEGL );
266
267   // create the OpenGL context
268   mEGL->CreateContext();
269
270   // create the OpenGL surface
271   mCurrent.surface->CreateEglSurface( *mEGL );
272
273   // Make it current
274   mEGL->MakeContextCurrent();
275
276   if( !mUsingPixmap )
277   {
278     // set the initial sync mode
279     mEGL->SetRefreshSync( mCurrent.syncMode );
280   }
281
282   // tell core it has a context
283   mCore.ContextCreated();
284
285   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VENDOR : %s ***\n", mGLES.GetString(GL_VENDOR));
286   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_RENDERER : %s ***\n", mGLES.GetString(GL_RENDERER));
287   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VERSION : %s ***\n", mGLES.GetString(GL_VERSION));
288   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_SHADING_LANGUAGE_VERSION : %s***\n", mGLES.GetString(GL_SHADING_LANGUAGE_VERSION));
289   DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** Supported Extensions ***\n%s\n\n", mGLES.GetString(GL_EXTENSIONS));
290 }
291
292 void RenderThread::CheckForUpdates()
293 {
294   // atomic check to see if we've got updates, resets the flag int
295   if( __sync_fetch_and_and( &mNewDataAvailable, 0 ) )
296   {
297     // scope for lock
298     // NOTE! This block is the only place in render side where mNewValues can be used inside render thread !!!
299     {
300       // need to lock to access new values
301       boost::unique_lock< boost::mutex > lock( mThreadDataLock );
302
303       // did the sync mode change
304       if( mCurrent.syncMode != mNewValues.syncMode )
305       {
306         mCurrent.syncMode = mNewValues.syncMode;
307         mEGL->SetRefreshSync( mCurrent.syncMode );
308       }
309
310       // check if the surface needs replacing
311       if( mNewValues.replaceSurface )
312       {
313         mNewValues.replaceSurface = false; // reset the flag
314         // change the surface
315         ChangeSurface( mNewValues.surface );
316         mNewValues.surface = NULL;
317       }
318     }
319   }
320 }
321
322 void RenderThread::ChangeSurface( RenderSurface* newSurface )
323 {
324   // This is designed for replacing pixmap surfaces, but should work for window as well
325   // we need to delete the egl surface and renderable (pixmap / window)
326   // Then create a new pixmap/window and new egl surface
327   // If the new surface has a different display connection, then the context will be lost
328   DALI_ASSERT_ALWAYS( newSurface && "NULL surface" )
329   bool contextLost = newSurface->ReplaceEGLSurface( *mEGL );
330
331   if( contextLost )
332   {
333     DALI_LOG_WARNING("Context lost\n");
334     mCore.ContextToBeDestroyed();
335     mCore.ContextCreated();
336   }
337
338   // if both new and old surface are using the same display, and the display
339   // connection was created by Dali, then transfer
340   // display owner ship to the new surface.
341   mCurrent.surface->TransferDisplayOwner( *newSurface );
342
343   // use the new surface from now on
344   mCurrent.surface = newSurface;
345
346   // after rendering, NotifySurfaceChangeCompleted will be called
347   mSurfaceReplacing = true;
348 }
349
350 void RenderThread::NotifySurfaceChangeCompleted()
351 {
352   {
353     boost::unique_lock< boost::mutex > lock( mSurfaceChangedMutex );
354     mSurfaceReplaceCompleted = true;
355   }
356   // notify main thread
357   mSurfaceChangedNotify.notify_all();
358 }
359
360 void RenderThread::ShutdownEgl()
361 {
362   // inform core the context is about to be destroyed,
363   mCore.ContextToBeDestroyed();
364
365   // give a chance to destroy the OpenGL surface that created externally
366   mCurrent.surface->DestroyEglSurface( *mEGL );
367
368   // delete the GL context / egl surface
369   mEGL->TerminateGles();
370 }
371
372 bool RenderThread::PreRender()
373 {
374   return mCurrent.surface->PreRender( *mEGL, mGLES );
375 }
376
377 void RenderThread::PostRender( unsigned int timeDelta )
378 {
379   const bool waitForSync = mCurrent.surface->PostRender( *mEGL, mGLES, timeDelta );
380
381   if( waitForSync )
382   {
383     boost::unique_lock< boost::mutex > lock( mPixmapSyncMutex );
384
385     // wait until synced,
386     // this blocks the thread here and releases the mPixmapSyncMutex (so the main thread can use it)
387     // if surface is replacing, forcely update without pixmap sync
388     if( mPixmapSyncRunning && !mPixmapSyncReceived && !mSurfaceReplacing )
389     {
390       mPixmapSyncNotify.wait( lock );
391     }
392     mPixmapSyncReceived = false;
393   }
394 }
395
396 } // namespace Adaptor
397
398 } // namespace Internal
399
400 } // namespace Dali