Redesigning render sync to handle reduced frequency
[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 #if defined(DEBUG_ENABLED)
41 Integration::Log::Filter* gRenderLogFilter = Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_RENDER_THREAD");
42 #endif
43 }
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   DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Start()\n");
72
73   // initialise GL and kick off render thread
74   DALI_ASSERT_ALWAYS( !mEGL && "Egl already initialized" );
75
76   // create the render thread, initially we are rendering
77   mThread = new boost::thread(boost::bind(&RenderThread::Run, this));
78
79   mCurrent.surface->StartRender();
80 }
81
82 void RenderThread::Stop()
83 {
84   DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Stop()\n");
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   DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::ReplaceSurface()\n");
103
104   // Make sure it's a new surface. Note! we are reading the current value of render thread here, but reading is ok
105   DALI_ASSERT_ALWAYS( surface != mCurrent.surface && "Trying to replace surface with itself" );
106
107   // lock and set to false
108   {
109     boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
110     mSurfaceReplaceCompleted = false;
111   }
112
113   // lock cache and set update flag at the end of function
114   {
115     SendMessageGuard msg( *this );
116     // set new values to cache
117     mNewValues.replaceSurface = true;
118     mNewValues.surface = surface;
119   }
120
121   // Ensure the current surface releases any locks.
122   mCurrent.surface->StopRender();
123 }
124
125 void RenderThread::WaitForSurfaceReplaceComplete()
126 {
127   DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::WaitForSurfaceReplaceComplete()\n");
128
129   boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
130
131   // if already completed no need to wait
132   while( !mSurfaceReplaceCompleted )
133   {
134     mSurfaceChangedNotify.wait( lock ); // Block the main thread here and releases mSurfaceChangedMutex so the render-thread can notify us
135   }
136 }
137
138 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
139 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
140 // The following methods are all executed inside render thread !!!
141 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
142 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
143
144 bool RenderThread::Run()
145 {
146   // install a function for logging
147   mEnvironmentOptions.InstallLogFunction();
148
149   InitializeEgl();
150
151   bool running( true );
152
153   // Wait for first update
154   mUpdateRenderSync.RenderSyncWithUpdate();
155
156   Dali::Integration::RenderStatus renderStatus;
157
158   uint64_t currentTime( 0 );
159
160   // render loop, we stay inside here when rendering
161   while( running )
162   {
163     DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 1 - Begin loop\n");
164
165     // Consume any pending events
166     ConsumeEvents();
167
168     // Check if we've got updates from the main thread
169     CheckForUpdates();
170
171     // perform any pre-render operations
172     DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 2 - PreRender\n");
173     if(PreRender() == true)
174     {
175        // Render
176       DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 3 - Core.Render()\n");
177       mCore.Render( renderStatus );
178
179       // Notify the update-thread that a render has completed
180       DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 4 - Sync.RenderFinished()\n");
181       mUpdateRenderSync.RenderFinished( renderStatus.NeedsUpdate() );
182
183       uint64_t newTime( mUpdateRenderSync.GetTimeMicroseconds() );
184
185       // perform any post-render operations
186       if ( renderStatus.HasRendered() )
187       {
188         DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 5 - PostRender()\n");
189         PostRender( static_cast< unsigned int >(newTime - currentTime) );
190       }
191
192       if(mSurfaceReplacing)
193       {
194         // Notify main thread that surface was changed so it can release the old one
195         NotifySurfaceChangeCompleted();
196         mSurfaceReplacing = false;
197       }
198
199       currentTime = newTime;
200     }
201
202     DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 6 - RenderSyncWithUpdate()\n");
203
204     // Wait until another frame has been updated
205     running = mUpdateRenderSync.RenderSyncWithUpdate();
206   }
207
208   // shut down egl
209   ShutdownEgl();
210
211   // install a function for logging
212   mEnvironmentOptions.UnInstallLogFunction();
213
214   return true;
215 }
216
217 void RenderThread::InitializeEgl()
218 {
219   mEGL = mEglFactory->Create();
220
221   DALI_ASSERT_ALWAYS( mCurrent.surface && "NULL surface" );
222
223   // initialize egl & OpenGL
224   mCurrent.surface->InitializeEgl( *mEGL );
225
226   // create the OpenGL context
227   mEGL->CreateContext();
228
229   // create the OpenGL surface
230   mCurrent.surface->CreateEglSurface( *mEGL );
231
232   // Make it current
233   mEGL->MakeContextCurrent();
234
235   // set the initial sync mode
236
237
238   // tell core it has a context
239   mCore.ContextCreated();
240
241 }
242
243 void RenderThread::ConsumeEvents()
244 {
245   // tell surface to consume any events to avoid memory leaks
246   mCurrent.surface->ConsumeEvents();
247 }
248
249 void RenderThread::CheckForUpdates()
250 {
251   // atomic check to see if we've got updates, resets the flag int
252   if( __sync_fetch_and_and( &mNewDataAvailable, 0 ) )
253   {
254     // scope for lock
255     // NOTE! This block is the only place in render side where mNewValues can be used inside render thread !!!
256     {
257       // need to lock to access new values
258       boost::unique_lock< boost::mutex > lock( mThreadDataLock );
259
260       // check if the surface needs replacing
261       if( mNewValues.replaceSurface )
262       {
263         mNewValues.replaceSurface = false; // reset the flag
264         // change the surface
265         ChangeSurface( mNewValues.surface );
266         mNewValues.surface = NULL;
267       }
268     }
269   }
270 }
271
272 void RenderThread::ChangeSurface( RenderSurface* newSurface )
273 {
274   // This is designed for replacing pixmap surfaces, but should work for window as well
275   // we need to delete the egl surface and renderable (pixmap / window)
276   // Then create a new pixmap/window and new egl surface
277   // If the new surface has a different display connection, then the context will be lost
278   DALI_ASSERT_ALWAYS( newSurface && "NULL surface" )
279   bool contextLost = newSurface->ReplaceEGLSurface( *mEGL );
280
281   if( contextLost )
282   {
283     DALI_LOG_WARNING("Context lost\n");
284     mCore.ContextToBeDestroyed();
285     mCore.ContextCreated();
286   }
287
288   // if both new and old surface are using the same display, and the display
289   // connection was created by Dali, then transfer
290   // display owner ship to the new surface.
291   mCurrent.surface->TransferDisplayOwner( *newSurface );
292
293   // use the new surface from now on
294   mCurrent.surface = newSurface;
295
296   // after rendering, NotifySurfaceChangeCompleted will be called
297   mSurfaceReplacing = true;
298 }
299
300 void RenderThread::NotifySurfaceChangeCompleted()
301 {
302   {
303     boost::unique_lock< boost::mutex > lock( mSurfaceChangedMutex );
304     mSurfaceReplaceCompleted = true;
305   }
306   // notify main thread
307   mSurfaceChangedNotify.notify_all();
308 }
309
310 void RenderThread::ShutdownEgl()
311 {
312   // inform core the context is about to be destroyed,
313   mCore.ContextToBeDestroyed();
314
315   // give a chance to destroy the OpenGL surface that created externally
316   mCurrent.surface->DestroyEglSurface( *mEGL );
317
318   // delete the GL context / egl surface
319   mEGL->TerminateGles();
320 }
321
322 bool RenderThread::PreRender()
323 {
324   bool success = mCurrent.surface->PreRender( *mEGL, mGLES );
325   if( success )
326   {
327     mGLES.PreRender();
328   }
329   return success;
330 }
331
332 void RenderThread::PostRender( unsigned int timeDelta )
333 {
334   // Inform the gl implementation that rendering has finished before informing the surface
335   mGLES.PostRender(timeDelta);
336
337   // Inform the surface that rendering this frame has finished.
338   mCurrent.surface->PostRender( *mEGL, mGLES, timeDelta, mSurfaceReplacing );
339 }
340
341 } // namespace Adaptor
342
343 } // namespace Internal
344
345 } // namespace Dali