2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "render-thread.h"
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>
40 #if defined(DEBUG_ENABLED)
41 Integration::Log::Filter* gRenderLogFilter = Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_RENDER_THREAD");
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()),
54 mSurfaceReplacing( false ),
55 mNewDataAvailable( false ),
56 mSurfaceReplaceCompleted( false ),
57 mEnvironmentOptions( environmentOptions )
59 // set the initial values before render thread starts
60 mCurrent.surface = adaptorInterfaces.GetRenderSurfaceInterface();
63 RenderThread::~RenderThread()
65 DALI_ASSERT_ALWAYS( mThread == NULL && "RenderThread is still alive");
66 mEglFactory->Destroy();
69 void RenderThread::Start()
71 DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Start()\n");
73 // initialise GL and kick off render thread
74 DALI_ASSERT_ALWAYS( !mEGL && "Egl already initialized" );
76 // create the render thread, initially we are rendering
77 mThread = new boost::thread(boost::bind(&RenderThread::Run, this));
79 mCurrent.surface->StartRender();
82 void RenderThread::Stop()
84 DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Stop()\n");
86 // shutdown the render thread and destroy the opengl context
89 // Tell surface we have stopped rendering
90 mCurrent.surface->StopRender();
92 // wait for the thread to finish
100 void RenderThread::ReplaceSurface( RenderSurface* surface )
102 DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::ReplaceSurface()\n");
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" );
107 // lock and set to false
109 boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
110 mSurfaceReplaceCompleted = false;
113 // lock cache and set update flag at the end of function
115 SendMessageGuard msg( *this );
116 // set new values to cache
117 mNewValues.replaceSurface = true;
118 mNewValues.surface = surface;
121 // Ensure the current surface releases any locks.
122 mCurrent.surface->StopRender();
125 void RenderThread::WaitForSurfaceReplaceComplete()
127 DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::WaitForSurfaceReplaceComplete()\n");
129 boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
131 // if already completed no need to wait
132 while( !mSurfaceReplaceCompleted )
134 mSurfaceChangedNotify.wait( lock ); // Block the main thread here and releases mSurfaceChangedMutex so the render-thread can notify us
138 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
139 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
140 // The following methods are all executed inside render thread !!!
141 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
142 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
144 bool RenderThread::Run()
146 // install a function for logging
147 mEnvironmentOptions.InstallLogFunction();
151 bool running( true );
153 // Wait for first update
154 mUpdateRenderSync.RenderSyncWithUpdate();
156 Dali::Integration::RenderStatus renderStatus;
158 uint64_t currentTime( 0 );
160 // render loop, we stay inside here when rendering
163 DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 1 - Begin loop\n");
165 // Consume any pending events
168 // Check if we've got updates from the main thread
171 // perform any pre-render operations
172 DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 2 - PreRender\n");
173 if(PreRender() == true)
176 DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 3 - Core.Render()\n");
177 mCore.Render( renderStatus );
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() );
183 uint64_t newTime( mUpdateRenderSync.GetTimeMicroseconds() );
185 // perform any post-render operations
186 if ( renderStatus.HasRendered() )
188 DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 5 - PostRender()\n");
189 PostRender( static_cast< unsigned int >(newTime - currentTime) );
192 if(mSurfaceReplacing)
194 // Notify main thread that surface was changed so it can release the old one
195 NotifySurfaceChangeCompleted();
196 mSurfaceReplacing = false;
199 currentTime = newTime;
202 DALI_LOG_INFO( gRenderLogFilter, Debug::Verbose, "RenderThread::Run. 6 - RenderSyncWithUpdate()\n");
204 // Wait until another frame has been updated
205 running = mUpdateRenderSync.RenderSyncWithUpdate();
211 // install a function for logging
212 mEnvironmentOptions.UnInstallLogFunction();
217 void RenderThread::InitializeEgl()
219 mEGL = mEglFactory->Create();
221 DALI_ASSERT_ALWAYS( mCurrent.surface && "NULL surface" );
223 // initialize egl & OpenGL
224 mCurrent.surface->InitializeEgl( *mEGL );
226 // create the OpenGL context
227 mEGL->CreateContext();
229 // create the OpenGL surface
230 mCurrent.surface->CreateEglSurface( *mEGL );
233 mEGL->MakeContextCurrent();
235 // set the initial sync mode
238 // tell core it has a context
239 mCore.ContextCreated();
243 void RenderThread::ConsumeEvents()
245 // tell surface to consume any events to avoid memory leaks
246 mCurrent.surface->ConsumeEvents();
249 void RenderThread::CheckForUpdates()
251 // atomic check to see if we've got updates, resets the flag int
252 if( __sync_fetch_and_and( &mNewDataAvailable, 0 ) )
255 // NOTE! This block is the only place in render side where mNewValues can be used inside render thread !!!
257 // need to lock to access new values
258 boost::unique_lock< boost::mutex > lock( mThreadDataLock );
260 // check if the surface needs replacing
261 if( mNewValues.replaceSurface )
263 mNewValues.replaceSurface = false; // reset the flag
264 // change the surface
265 ChangeSurface( mNewValues.surface );
266 mNewValues.surface = NULL;
272 void RenderThread::ChangeSurface( RenderSurface* newSurface )
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 );
283 DALI_LOG_WARNING("Context lost\n");
284 mCore.ContextToBeDestroyed();
285 mCore.ContextCreated();
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 );
293 // use the new surface from now on
294 mCurrent.surface = newSurface;
296 // after rendering, NotifySurfaceChangeCompleted will be called
297 mSurfaceReplacing = true;
300 void RenderThread::NotifySurfaceChangeCompleted()
303 boost::unique_lock< boost::mutex > lock( mSurfaceChangedMutex );
304 mSurfaceReplaceCompleted = true;
306 // notify main thread
307 mSurfaceChangedNotify.notify_all();
310 void RenderThread::ShutdownEgl()
312 // inform core the context is about to be destroyed,
313 mCore.ContextToBeDestroyed();
315 // give a chance to destroy the OpenGL surface that created externally
316 mCurrent.surface->DestroyEglSurface( *mEGL );
318 // delete the GL context / egl surface
319 mEGL->TerminateGles();
322 bool RenderThread::PreRender()
324 bool success = mCurrent.surface->PreRender( *mEGL, mGLES );
332 void RenderThread::PostRender( unsigned int timeDelta )
334 // Inform the gl implementation that rendering has finished before informing the surface
335 mGLES.PostRender(timeDelta);
337 // Inform the surface that rendering this frame has finished.
338 mCurrent.surface->PostRender( *mEGL, mGLES, timeDelta, mSurfaceReplacing );
341 } // namespace Adaptor
343 } // namespace Internal