Add handling for context loss and regain behaviour
[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
46 RenderRequest::RenderRequest(RenderRequest::Request type)
47 : mRequestType(type)
48 {
49 }
50
51 RenderRequest::Request RenderRequest::GetType()
52 {
53   return mRequestType;
54 }
55
56 ReplaceSurfaceRequest::ReplaceSurfaceRequest()
57 : RenderRequest(RenderRequest::REPLACE_SURFACE),
58   mNewSurface( NULL ),
59   mReplaceCompleted(false)
60 {
61 }
62
63 void ReplaceSurfaceRequest::SetSurface(RenderSurface* newSurface)
64 {
65   mNewSurface = newSurface;
66 }
67
68 RenderSurface* ReplaceSurfaceRequest::GetSurface()
69 {
70   return mNewSurface;
71 }
72
73 void ReplaceSurfaceRequest::ReplaceCompleted()
74 {
75   mReplaceCompleted = true;
76 }
77
78 bool ReplaceSurfaceRequest::GetReplaceCompleted()
79 {
80   return mReplaceCompleted != 0u;
81 }
82
83
84 RenderThread::RenderThread( UpdateRenderSynchronization& sync,
85                             AdaptorInternalServices& adaptorInterfaces,
86                             const EnvironmentOptions& environmentOptions )
87 : mUpdateRenderSync( sync ),
88   mCore( adaptorInterfaces.GetCore() ),
89   mGLES( adaptorInterfaces.GetGlesInterface() ),
90   mEglFactory( &adaptorInterfaces.GetEGLFactoryInterface()),
91   mEGL( NULL ),
92   mThread( NULL ),
93   mEnvironmentOptions( environmentOptions ),
94   mSurfaceReplaced(false)
95 {
96   // set the initial values before render thread starts
97   mSurface = adaptorInterfaces.GetRenderSurfaceInterface();
98 }
99
100 RenderThread::~RenderThread()
101 {
102   DALI_ASSERT_ALWAYS( mThread == NULL && "RenderThread is still alive");
103   mEglFactory->Destroy();
104 }
105
106 void RenderThread::Start()
107 {
108   DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Start()\n");
109
110   // initialise GL and kick off render thread
111   DALI_ASSERT_ALWAYS( !mEGL && "Egl already initialized" );
112
113   // create the render thread, initially we are rendering
114   mThread = new boost::thread(boost::bind(&RenderThread::Run, this));
115
116   mSurface->StartRender();
117 }
118
119 void RenderThread::Stop()
120 {
121   DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Stop()\n");
122
123   // shutdown the render thread and destroy the opengl context
124   if( mThread )
125   {
126     // Tell surface we have stopped rendering
127     mSurface->StopRender();
128
129     // wait for the thread to finish
130     mThread->join();
131
132     delete mThread;
133     mThread = NULL;
134   }
135 }
136
137 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
138 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
139 // The following methods are all executed inside render thread !!!
140 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
141 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
142
143 bool RenderThread::Run()
144 {
145   // install a function for logging
146   mEnvironmentOptions.InstallLogFunction();
147
148   InitializeEgl();
149
150   bool running( true );
151
152   Dali::Integration::RenderStatus renderStatus;
153
154   uint64_t currentTime( 0 );
155
156   // render loop, we stay inside here when rendering
157   while( running )
158   {
159     // Sync with update thread and get any outstanding requests from UpdateRenderSynchronization
160     DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 1 - RenderSyncWithUpdate()\n");
161     RenderRequest* request = NULL;
162     running = mUpdateRenderSync.RenderSyncWithUpdate( request );
163
164     DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 2 - Process requests\n");
165
166     // Consume any pending events
167     ConsumeEvents();
168
169     // Check if we've got any requests from the main thread (e.g. replace surface)
170     bool requestProcessed = ProcessRequest( request );
171
172     // perform any pre-render operations
173     DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 3 - PreRender\n");
174     if( running && PreRender() == true)
175     {
176        // Render
177       DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 4 - Core.Render()\n");
178       mCore.Render( renderStatus );
179
180       // Notify the update-thread that a render has completed
181       DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 5 - Sync.RenderFinished()\n");
182       mUpdateRenderSync.RenderFinished( renderStatus.NeedsUpdate(), requestProcessed );
183
184       uint64_t newTime( mUpdateRenderSync.GetTimeMicroseconds() );
185
186       // perform any post-render operations
187       if ( renderStatus.HasRendered() )
188       {
189         DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 6 - PostRender()\n");
190         PostRender( static_cast< unsigned int >(newTime - currentTime) );
191       }
192
193       currentTime = newTime;
194     }
195   }
196
197   // shut down egl
198   ShutdownEgl();
199
200   // install a function for logging
201   mEnvironmentOptions.UnInstallLogFunction();
202
203   return true;
204 }
205
206 void RenderThread::InitializeEgl()
207 {
208   mEGL = mEglFactory->Create();
209
210   DALI_ASSERT_ALWAYS( mSurface && "NULL surface" );
211
212   // initialize egl & OpenGL
213   mSurface->InitializeEgl( *mEGL );
214
215   // create the OpenGL context
216   mEGL->CreateContext();
217
218   // create the OpenGL surface
219   mSurface->CreateEglSurface( *mEGL );
220
221   // Make it current
222   mEGL->MakeContextCurrent();
223
224   // set the initial sync mode
225
226
227   // tell core it has a context
228   mCore.ContextCreated();
229
230 }
231
232 void RenderThread::ConsumeEvents()
233 {
234   // tell surface to consume any events to avoid memory leaks
235   mSurface->ConsumeEvents();
236 }
237
238 bool RenderThread::ProcessRequest( RenderRequest* request )
239 {
240   bool processedRequest = false;
241
242   if( request != NULL )
243   {
244     switch(request->GetType())
245     {
246       case RenderRequest::REPLACE_SURFACE:
247       {
248         // change the surface
249         ReplaceSurfaceRequest* replaceSurfaceRequest = static_cast<ReplaceSurfaceRequest*>(request);
250         ReplaceSurface( replaceSurfaceRequest->GetSurface() );
251         replaceSurfaceRequest->ReplaceCompleted();
252         processedRequest = true;
253         break;
254       }
255     }
256   }
257   return processedRequest;
258 }
259
260 void RenderThread::ReplaceSurface( RenderSurface* newSurface )
261 {
262   // This is designed for replacing pixmap surfaces, but should work for window as well
263   // we need to delete the egl surface and renderable (pixmap / window)
264   // Then create a new pixmap/window and new egl surface
265   // If the new surface has a different display connection, then the context will be lost
266   DALI_ASSERT_ALWAYS( newSurface && "NULL surface" );
267
268   bool contextLost = newSurface->ReplaceEGLSurface( *mEGL );
269
270   if( contextLost )
271   {
272     DALI_LOG_WARNING("Context lost\n");
273     mCore.ContextDestroyed();
274     mCore.ContextCreated();
275   }
276
277   // if both new and old surface are using the same display, and the display
278   // connection was created by Dali, then transfer
279   // display owner ship to the new surface.
280   mSurface->TransferDisplayOwner( *newSurface );
281
282   // use the new surface from now on
283   mSurface = newSurface;
284   mSurfaceReplaced = true;
285 }
286
287
288 void RenderThread::ShutdownEgl()
289 {
290   // inform core of context destruction
291   mCore.ContextDestroyed();
292
293   // give a chance to destroy the OpenGL surface that created externally
294   mSurface->DestroyEglSurface( *mEGL );
295
296   // delete the GL context / egl surface
297   mEGL->TerminateGles();
298 }
299
300 bool RenderThread::PreRender()
301 {
302   bool success = mSurface->PreRender( *mEGL, mGLES );
303   if( success )
304   {
305     mGLES.PreRender();
306   }
307   return success;
308 }
309
310 void RenderThread::PostRender( unsigned int timeDelta )
311 {
312   // Inform the gl implementation that rendering has finished before informing the surface
313   mGLES.PostRender(timeDelta);
314
315   // Inform the surface that rendering this frame has finished.
316   mSurface->PostRender( *mEGL, mGLES, timeDelta, mSurfaceReplaced );
317   mSurfaceReplaced = false;
318 }
319
320
321 } // namespace Adaptor
322
323 } // namespace Internal
324
325 } // namespace Dali