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