b5d5af1420a16af76e390e8708295eb91be3db56
[platform/core/uifw/dali-adaptor.git] / adaptors / base / render-thread.cpp
1 /*
2  * Copyright (c) 2015 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 // EXTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23
24 // INTERNAL INCLUDES
25 #include <base/interfaces/adaptor-internal-services.h>
26 #include <base/thread-synchronization.h>
27 #include <base/environment-options.h>
28 #include <base/display-connection.h>
29
30 namespace Dali
31 {
32
33 namespace Internal
34 {
35
36 namespace Adaptor
37 {
38
39 namespace
40 {
41 #if defined(DEBUG_ENABLED)
42 Integration::Log::Filter* gRenderLogFilter = Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_RENDER_THREAD");
43 #endif
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( ThreadSynchronization& sync,
85                             AdaptorInternalServices& adaptorInterfaces,
86                             const EnvironmentOptions& environmentOptions )
87 : mThreadSynchronization( 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   mDisplayConnection = Dali::DisplayConnection::New();
100 }
101
102 RenderThread::~RenderThread()
103 {
104   if (mDisplayConnection)
105   {
106     delete mDisplayConnection;
107     mDisplayConnection = NULL;
108   }
109
110   DALI_ASSERT_ALWAYS( mThread == NULL && "RenderThread is still alive");
111   mEglFactory->Destroy();
112 }
113
114 void RenderThread::Start()
115 {
116   DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Start()\n");
117
118   // initialise GL and kick off render thread
119   DALI_ASSERT_ALWAYS( !mEGL && "Egl already initialized" );
120
121   // create the render thread, initially we are rendering
122   mThread = new pthread_t();
123   int error = pthread_create( mThread, NULL, InternalThreadEntryFunc, this );
124   DALI_ASSERT_ALWAYS( !error && "Return code from pthread_create() in RenderThread" );
125
126   if( mSurface )
127   {
128     mSurface->StartRender();
129   }
130 }
131
132 void RenderThread::Stop()
133 {
134   DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Stop()\n");
135
136   if( mSurface )
137   {
138     // Tell surface we have stopped rendering
139     mSurface->StopRender();
140
141     // The surface will be destroyed soon; this pointer will become invalid
142     mSurface = NULL;
143   }
144
145   // shutdown the render thread and destroy the opengl context
146   if( mThread )
147   {
148     // wait for the thread to finish
149     pthread_join(*mThread, NULL);
150
151     delete mThread;
152     mThread = NULL;
153   }
154 }
155
156 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
157 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
158 // The following methods are all executed inside render thread !!!
159 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
160 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
161
162 bool RenderThread::Run()
163 {
164   DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run\n");
165
166   // Install a function for logging
167   mEnvironmentOptions.InstallLogFunction();
168
169   InitializeEgl();
170
171   Dali::Integration::RenderStatus renderStatus;
172   RenderRequest* request = NULL;
173
174   // Render loop, we stay inside here when rendering
175   while( mThreadSynchronization.RenderReady( request ) )
176   {
177     DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 1 - RenderReady\n");
178
179     // Consume any pending events to avoid memory leaks
180     DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 2 - ConsumeEvents\n");
181     mDisplayConnection->ConsumeEvents();
182
183     // Check if we've got a request from the main thread (e.g. replace surface)
184     if( request )
185     {
186       // Process the request, we should NOT render when we have a request
187       DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 3 - Process requests\n");
188       ProcessRequest( request );
189     }
190     else
191     {
192       // No request to process so we render
193       if( PreRender() ) // Returns false if no surface onto which to render
194       {
195         // Render
196         DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 3 - Core.Render()\n");
197
198         mThreadSynchronization.AddPerformanceMarker( PerformanceInterface::RENDER_START );
199         mCore.Render( renderStatus );
200         mThreadSynchronization.AddPerformanceMarker( PerformanceInterface::RENDER_END );
201
202         // Decrement the count of how far update is ahead of render
203         mThreadSynchronization.RenderFinished();
204
205         // Perform any post-render operations
206         if ( renderStatus.HasRendered() )
207         {
208           DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 4 - PostRender()\n");
209           PostRender();
210         }
211       }
212     }
213
214     request = NULL; // Clear the request if it was set, no need to release memory
215   }
216
217   // Shut down EGL
218   ShutdownEgl();
219
220   // Uninstall the logging function
221   mEnvironmentOptions.UnInstallLogFunction();
222
223   return true;
224 }
225
226 void RenderThread::InitializeEgl()
227 {
228   mEGL = mEglFactory->Create();
229
230   DALI_ASSERT_ALWAYS( mSurface && "NULL surface" );
231
232   // initialize egl & OpenGL
233   mDisplayConnection->InitializeEgl( *mEGL );
234   mSurface->InitializeEgl( *mEGL );
235
236   // create the OpenGL context
237   mEGL->CreateContext();
238
239   // create the OpenGL surface
240   mSurface->CreateEglSurface(*mEGL);
241
242   // Make it current
243   mEGL->MakeContextCurrent();
244
245   // set the initial sync mode
246
247   // tell core it has a context
248   mCore.ContextCreated();
249
250 }
251
252 void RenderThread::ProcessRequest( RenderRequest* request )
253 {
254   if( request != NULL )
255   {
256     switch(request->GetType())
257     {
258       case RenderRequest::REPLACE_SURFACE:
259       {
260         // change the surface
261         ReplaceSurfaceRequest* replaceSurfaceRequest = static_cast<ReplaceSurfaceRequest*>(request);
262         ReplaceSurface( replaceSurfaceRequest->GetSurface() );
263         replaceSurfaceRequest->ReplaceCompleted();
264         mThreadSynchronization.RenderInformSurfaceReplaced();
265         break;
266       }
267     }
268   }
269 }
270
271 void RenderThread::ReplaceSurface( RenderSurface* newSurface )
272 {
273   // This is designed for replacing pixmap surfaces, but should work for window as well
274   // we need to delete the egl surface and renderable (pixmap / window)
275   // Then create a new pixmap/window and new egl surface
276   // If the new surface has a different display connection, then the context will be lost
277   DALI_ASSERT_ALWAYS(newSurface && "NULL surface");
278
279   mDisplayConnection->InitializeEgl(*mEGL);
280
281   newSurface->ReplaceEGLSurface(*mEGL);
282
283   // use the new surface from now on
284   mSurface = newSurface;
285   mSurfaceReplaced = true;
286 }
287
288 void RenderThread::ShutdownEgl()
289 {
290   // inform core of context destruction
291   mCore.ContextDestroyed();
292
293   if( mSurface )
294   {
295     // give a chance to destroy the OpenGL surface that created externally
296     mSurface->DestroyEglSurface( *mEGL );
297   }
298
299   // delete the GL context / egl surface
300   mEGL->TerminateGles();
301 }
302
303 bool RenderThread::PreRender()
304 {
305   bool success( false );
306   if( mSurface )
307   {
308     success = mSurface->PreRender( *mEGL, mGLES );
309   }
310
311   if( success )
312   {
313     mGLES.PreRender();
314   }
315   return success;
316 }
317
318 void RenderThread::PostRender()
319 {
320   // Inform the gl implementation that rendering has finished before informing the surface
321   mGLES.PostRender();
322
323   if( mSurface )
324   {
325     // Inform the surface that rendering this frame has finished.
326     mSurface->PostRender( *mEGL, mGLES, mDisplayConnection, mSurfaceReplaced );
327   }
328   mSurfaceReplaced = false;
329 }
330
331 } // namespace Adaptor
332
333 } // namespace Internal
334
335 } // namespace Dali