2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 // Licensed under the Flora License, Version 1.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://floralicense.org/license/
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.
18 #include "render-thread.h"
22 #include <dali/integration-api/debug.h>
23 #include <base/interfaces/adaptor-internal-services.h>
24 #include <base/update-render-synchronization.h>
25 #include <base/log-options.h>
40 const unsigned int TIME_PER_FRAME_IN_MICROSECONDS = 16667;
42 } // unnamed namespace
44 RenderThread::RenderThread( UpdateRenderSynchronization& sync,
45 AdaptorInternalServices& adaptorInterfaces,
46 const LogOptions& logOptions )
47 : mUpdateRenderSync( sync ),
48 mCore( adaptorInterfaces.GetCore() ),
49 mGLES( adaptorInterfaces.GetGlesInterface() ),
50 mEglFactory( &adaptorInterfaces.GetEGLFactoryInterface()),
53 mUsingPixmap( false ),
54 mSurfaceReplacing( false ),
55 mNewDataAvailable( false ),
56 mPixmapSyncReceived( false ),
57 mPixmapSyncRunning( false ),
59 mSurfaceReplaceCompleted( false ),
60 mLogOptions( logOptions )
62 // set the initial values before render thread starts
63 mCurrent.surface = adaptorInterfaces.GetRenderSurfaceInterface();
65 // set the pixmap flag, if we're using one
66 if( (mCurrent.surface->GetType() == Dali::RenderSurface::PIXMAP ) ||
67 (mCurrent.surface->GetType() == Dali::RenderSurface::NATIVE_BUFFER ) )
73 RenderThread::~RenderThread()
75 DALI_ASSERT_ALWAYS( mThread == NULL && "RenderThread is still alive");
76 mEglFactory->Destroy();
79 void RenderThread::Start()
81 // initialise GL and kick off render thread
82 DALI_ASSERT_ALWAYS( !mEGL && "Egl already initialized" );
84 // Tell core what the minimum frame interval is
85 mCore.SetMinimumFrameTimeInterval( mCurrent.syncMode * TIME_PER_FRAME_IN_MICROSECONDS );
87 // create the render thread, initially we are rendering
88 mThread = new boost::thread(boost::bind(&RenderThread::Run, this));
90 // Inform render thread to block waiting for PixmapSync
93 boost::unique_lock< boost::mutex > lock( mPixmapSyncMutex );
94 mPixmapSyncRunning = true;
98 void RenderThread::Stop()
100 // shutdown the render thread and destroy the opengl context
103 // Inform render thread not to block waiting for PixmapSync
106 boost::unique_lock< boost::mutex > lock( mPixmapSyncMutex );
107 mPixmapSyncRunning = false;
110 mCurrent.surface->StopRender();
112 // need to simulate a RenderSync to get render-thread to finish
115 // wait for the thread to finish
123 void RenderThread::ReplaceSurface( RenderSurface* surface )
125 // Make sure it's a new surface. Note! we are reading the current value of render thread here, but reading is ok
126 DALI_ASSERT_ALWAYS( surface != mCurrent.surface && "Trying to replace surface with itself" );
128 // lock and set to false
130 boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
131 mSurfaceReplaceCompleted = false;
134 // lock cache and set update flag at the end of function
136 SendMessageGuard msg( *this );
137 // set new values to cache
138 mNewValues.replaceSurface = true;
139 mNewValues.surface = surface;
143 * Reset the mPixmapFlushed condition if surface was changed.
144 * : in this case, client can not handle the previous damage because surface was changed.
149 void RenderThread::WaitForSurfaceReplaceComplete()
151 boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
153 // if already completed no need to wait
154 while( !mSurfaceReplaceCompleted )
156 mSurfaceChangedNotify.wait( lock ); // Block the main thread here and releases mSurfaceChangedMutex so the render-thread can notify us
160 void RenderThread::SetVSyncMode( EglInterface::SyncMode syncMode )
162 // lock cache and set update flag at the end of function
163 SendMessageGuard msg( *this );
164 // set new values to cache
165 mNewValues.syncMode = syncMode;
168 void RenderThread::RenderSync()
175 boost::unique_lock< boost::mutex > lock( mPixmapSyncMutex );
176 mPixmapSyncReceived = true;
179 // awake render thread if it was waiting for the notify
180 mPixmapSyncNotify.notify_all();
183 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
184 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
185 // The following methods are all executed inside render thread !!!
186 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
187 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
189 bool RenderThread::Run()
191 // install a function for logging
192 mLogOptions.InstallLogFunction();
196 bool running( true );
198 // Wait for first update
199 mUpdateRenderSync.RenderSyncWithUpdate();
201 Dali::Integration::RenderStatus renderStatus;
203 uint64_t currentTime( 0 );
205 // render loop, we stay inside here when rendering
208 // Check if we've got updates from the main thread
211 // perform any pre-render operations
212 if(PreRender() == true)
215 mCore.Render( renderStatus );
217 // Notify the update-thread that a render has completed
218 mUpdateRenderSync.RenderFinished( renderStatus.NeedsUpdate() );
220 uint64_t newTime( mUpdateRenderSync.GetTimeMicroseconds() );
222 // perform any post-render operations
223 if ( renderStatus.HasRendered() )
225 PostRender( static_cast< unsigned int >(newTime - currentTime) );
228 if(mSurfaceReplacing)
230 // Notify main thread that surface was changed so it can release the old one
231 NotifySurfaceChangeCompleted();
232 mSurfaceReplacing = false;
235 currentTime = newTime;
238 // Wait until another frame has been updated
239 running = mUpdateRenderSync.RenderSyncWithUpdate();
245 // install a function for logging
246 mLogOptions.UnInstallLogFunction();
251 void RenderThread::InitializeEgl()
253 mEGL = mEglFactory->Create();
255 DALI_ASSERT_ALWAYS( mCurrent.surface && "NULL surface" );
257 // initialize egl & OpenGL
258 mCurrent.surface->InitializeEgl( *mEGL );
260 // create the OpenGL context
261 mEGL->CreateContext();
263 // create the OpenGL surface
264 mCurrent.surface->CreateEglSurface( *mEGL );
267 mEGL->MakeContextCurrent();
271 // set the initial sync mode
272 mEGL->SetRefreshSync( mCurrent.syncMode );
275 // tell core it has a context
276 mCore.ContextCreated();
278 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VENDOR : %s ***\n", mGLES.GetString(GL_VENDOR));
279 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_RENDERER : %s ***\n", mGLES.GetString(GL_RENDERER));
280 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VERSION : %s ***\n", mGLES.GetString(GL_VERSION));
281 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_SHADING_LANGUAGE_VERSION : %s***\n", mGLES.GetString(GL_SHADING_LANGUAGE_VERSION));
282 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** Supported Extensions ***\n%s\n\n", mGLES.GetString(GL_EXTENSIONS));
285 void RenderThread::CheckForUpdates()
287 // atomic check to see if we've got updates, resets the flag int
288 if( __sync_fetch_and_and( &mNewDataAvailable, 0 ) )
291 // NOTE! This block is the only place in render side where mNewValues can be used inside render thread !!!
293 // need to lock to access new values
294 boost::unique_lock< boost::mutex > lock( mThreadDataLock );
296 // did the sync mode change
297 if( mCurrent.syncMode != mNewValues.syncMode )
299 mCurrent.syncMode = mNewValues.syncMode;
300 mEGL->SetRefreshSync( mCurrent.syncMode );
303 // check if the surface needs replacing
304 if( mNewValues.replaceSurface )
306 mNewValues.replaceSurface = false; // reset the flag
307 // change the surface
308 ChangeSurface( mNewValues.surface );
309 mNewValues.surface = NULL;
315 void RenderThread::ChangeSurface( RenderSurface* newSurface )
317 // This is designed for replacing pixmap surfaces, but should work for window as well
318 // we need to delete the egl surface and renderable (pixmap / window)
319 // Then create a new pixmap/window and new egl surface
320 // If the new surface has a different display connection, then the context will be lost
321 DALI_ASSERT_ALWAYS( newSurface && "NULL surface" )
322 bool contextLost = newSurface->ReplaceEGLSurface( *mEGL );
326 DALI_LOG_WARNING("Context lost\n");
327 mCore.ContextToBeDestroyed();
328 mCore.ContextCreated();
331 // if both new and old surface are using the same display, and the display
332 // connection was created by Dali, then transfer
333 // display owner ship to the new surface.
334 mCurrent.surface->TransferDisplayOwner( *newSurface );
336 // use the new surface from now on
337 mCurrent.surface = newSurface;
339 // after rendering, NotifySurfaceChangeCompleted will be called
340 mSurfaceReplacing = true;
343 void RenderThread::NotifySurfaceChangeCompleted()
346 boost::unique_lock< boost::mutex > lock( mSurfaceChangedMutex );
347 mSurfaceReplaceCompleted = true;
349 // notify main thread
350 mSurfaceChangedNotify.notify_all();
353 void RenderThread::ShutdownEgl()
355 // inform core the context is about to be destroyed,
356 mCore.ContextToBeDestroyed();
358 // give a chance to destroy the OpenGL surface that created externally
359 mCurrent.surface->DestroyEglSurface( *mEGL );
361 // delete the GL context / egl surface
362 mEGL->TerminateGles();
365 bool RenderThread::PreRender()
367 return mCurrent.surface->PreRender( *mEGL, mGLES );
370 void RenderThread::PostRender( unsigned int timeDelta )
372 const bool waitForSync = mCurrent.surface->PostRender( *mEGL, mGLES, timeDelta );
376 boost::unique_lock< boost::mutex > lock( mPixmapSyncMutex );
378 // wait until synced,
379 // this blocks the thread here and releases the mPixmapSyncMutex (so the main thread can use it)
380 // if surface is replacing, forcely update without pixmap sync
381 if( mPixmapSyncRunning && !mPixmapSyncReceived && !mSurfaceReplacing )
383 mPixmapSyncNotify.wait( lock );
385 mPixmapSyncReceived = false;
389 } // namespace Adaptor
391 } // namespace Internal