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 if( mLogOptions.IsFilterEnabled(Debug::LogRenderThread) )
193 // install a function for logging
194 mLogOptions.InstallLogFunction();
199 bool running( true );
201 // Wait for first update
202 mUpdateRenderSync.RenderSyncWithUpdate();
204 Dali::Integration::RenderStatus renderStatus;
206 uint64_t currentTime( 0 );
208 // render loop, we stay inside here when rendering
211 // Check if we've got updates from the main thread
214 // perform any pre-render operations
215 if(PreRender() == true)
218 mCore.Render( renderStatus );
220 // Notify the update-thread that a render has completed
221 mUpdateRenderSync.RenderFinished( renderStatus.NeedsUpdate() );
223 uint64_t newTime( mUpdateRenderSync.GetTimeMicroseconds() );
225 // perform any post-render operations
226 if ( renderStatus.HasRendered() )
228 PostRender( static_cast< unsigned int >(newTime - currentTime) );
231 if(mSurfaceReplacing)
233 // Notify main thread that surface was changed so it can release the old one
234 NotifySurfaceChangeCompleted();
235 mSurfaceReplacing = false;
238 currentTime = newTime;
241 // Wait until another frame has been updated
242 running = mUpdateRenderSync.RenderSyncWithUpdate();
248 if( mLogOptions.IsFilterEnabled(Debug::LogRenderThread) )
250 // install a function for logging
251 mLogOptions.UnInstallLogFunction();
258 void RenderThread::InitializeEgl()
260 mEGL = mEglFactory->Create();
262 DALI_ASSERT_ALWAYS( mCurrent.surface && "NULL surface" );
264 // initialize egl & OpenGL
265 mCurrent.surface->InitializeEgl( *mEGL );
267 // create the OpenGL context
268 mEGL->CreateContext();
270 // create the OpenGL surface
271 mCurrent.surface->CreateEglSurface( *mEGL );
274 mEGL->MakeContextCurrent();
278 // set the initial sync mode
279 mEGL->SetRefreshSync( mCurrent.syncMode );
282 // tell core it has a context
283 mCore.ContextCreated();
285 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VENDOR : %s ***\n", mGLES.GetString(GL_VENDOR));
286 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_RENDERER : %s ***\n", mGLES.GetString(GL_RENDERER));
287 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VERSION : %s ***\n", mGLES.GetString(GL_VERSION));
288 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_SHADING_LANGUAGE_VERSION : %s***\n", mGLES.GetString(GL_SHADING_LANGUAGE_VERSION));
289 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** Supported Extensions ***\n%s\n\n", mGLES.GetString(GL_EXTENSIONS));
292 void RenderThread::CheckForUpdates()
294 // atomic check to see if we've got updates, resets the flag int
295 if( __sync_fetch_and_and( &mNewDataAvailable, 0 ) )
298 // NOTE! This block is the only place in render side where mNewValues can be used inside render thread !!!
300 // need to lock to access new values
301 boost::unique_lock< boost::mutex > lock( mThreadDataLock );
303 // did the sync mode change
304 if( mCurrent.syncMode != mNewValues.syncMode )
306 mCurrent.syncMode = mNewValues.syncMode;
307 mEGL->SetRefreshSync( mCurrent.syncMode );
310 // check if the surface needs replacing
311 if( mNewValues.replaceSurface )
313 mNewValues.replaceSurface = false; // reset the flag
314 // change the surface
315 ChangeSurface( mNewValues.surface );
316 mNewValues.surface = NULL;
322 void RenderThread::ChangeSurface( RenderSurface* newSurface )
324 // This is designed for replacing pixmap surfaces, but should work for window as well
325 // we need to delete the egl surface and renderable (pixmap / window)
326 // Then create a new pixmap/window and new egl surface
327 // If the new surface has a different display connection, then the context will be lost
328 DALI_ASSERT_ALWAYS( newSurface && "NULL surface" )
329 bool contextLost = newSurface->ReplaceEGLSurface( *mEGL );
333 DALI_LOG_WARNING("Context lost\n");
334 mCore.ContextToBeDestroyed();
335 mCore.ContextCreated();
338 // if both new and old surface are using the same display, and the display
339 // connection was created by Dali, then transfer
340 // display owner ship to the new surface.
341 mCurrent.surface->TransferDisplayOwner( *newSurface );
343 // use the new surface from now on
344 mCurrent.surface = newSurface;
346 // after rendering, NotifySurfaceChangeCompleted will be called
347 mSurfaceReplacing = true;
350 void RenderThread::NotifySurfaceChangeCompleted()
353 boost::unique_lock< boost::mutex > lock( mSurfaceChangedMutex );
354 mSurfaceReplaceCompleted = true;
356 // notify main thread
357 mSurfaceChangedNotify.notify_all();
360 void RenderThread::ShutdownEgl()
362 // inform core the context is about to be destroyed,
363 mCore.ContextToBeDestroyed();
365 // give a chance to destroy the OpenGL surface that created externally
366 mCurrent.surface->DestroyEglSurface( *mEGL );
368 // delete the GL context / egl surface
369 mEGL->TerminateGles();
372 bool RenderThread::PreRender()
374 return mCurrent.surface->PreRender( *mEGL, mGLES );
377 void RenderThread::PostRender( unsigned int timeDelta )
379 const bool waitForSync = mCurrent.surface->PostRender( *mEGL, mGLES, timeDelta );
383 boost::unique_lock< boost::mutex > lock( mPixmapSyncMutex );
385 // wait until synced,
386 // this blocks the thread here and releases the mPixmapSyncMutex (so the main thread can use it)
387 // if surface is replacing, forcely update without pixmap sync
388 if( mPixmapSyncRunning && !mPixmapSyncReceived && !mSurfaceReplacing )
390 mPixmapSyncNotify.wait( lock );
392 mPixmapSyncReceived = false;
396 } // namespace Adaptor
398 } // namespace Internal