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 // Consume any pending events
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 // install a function for logging
249 mLogOptions.UnInstallLogFunction();
254 void RenderThread::InitializeEgl()
256 mEGL = mEglFactory->Create();
258 DALI_ASSERT_ALWAYS( mCurrent.surface && "NULL surface" );
260 // initialize egl & OpenGL
261 mCurrent.surface->InitializeEgl( *mEGL );
263 // create the OpenGL context
264 mEGL->CreateContext();
266 // create the OpenGL surface
267 mCurrent.surface->CreateEglSurface( *mEGL );
270 mEGL->MakeContextCurrent();
274 // set the initial sync mode
275 mEGL->SetRefreshSync( mCurrent.syncMode );
278 // tell core it has a context
279 mCore.ContextCreated();
281 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VENDOR : %s ***\n", mGLES.GetString(GL_VENDOR));
282 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_RENDERER : %s ***\n", mGLES.GetString(GL_RENDERER));
283 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VERSION : %s ***\n", mGLES.GetString(GL_VERSION));
284 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_SHADING_LANGUAGE_VERSION : %s***\n", mGLES.GetString(GL_SHADING_LANGUAGE_VERSION));
285 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** Supported Extensions ***\n%s\n\n", mGLES.GetString(GL_EXTENSIONS));
288 void RenderThread::ConsumeEvents()
290 // tell surface to consume any events to avoid memory leaks
291 mCurrent.surface->ConsumeEvents();
294 void RenderThread::CheckForUpdates()
296 // atomic check to see if we've got updates, resets the flag int
297 if( __sync_fetch_and_and( &mNewDataAvailable, 0 ) )
300 // NOTE! This block is the only place in render side where mNewValues can be used inside render thread !!!
302 // need to lock to access new values
303 boost::unique_lock< boost::mutex > lock( mThreadDataLock );
305 // did the sync mode change
306 if( mCurrent.syncMode != mNewValues.syncMode )
308 mCurrent.syncMode = mNewValues.syncMode;
309 mEGL->SetRefreshSync( mCurrent.syncMode );
312 // check if the surface needs replacing
313 if( mNewValues.replaceSurface )
315 mNewValues.replaceSurface = false; // reset the flag
316 // change the surface
317 ChangeSurface( mNewValues.surface );
318 mNewValues.surface = NULL;
324 void RenderThread::ChangeSurface( RenderSurface* newSurface )
326 // This is designed for replacing pixmap surfaces, but should work for window as well
327 // we need to delete the egl surface and renderable (pixmap / window)
328 // Then create a new pixmap/window and new egl surface
329 // If the new surface has a different display connection, then the context will be lost
330 DALI_ASSERT_ALWAYS( newSurface && "NULL surface" )
331 bool contextLost = newSurface->ReplaceEGLSurface( *mEGL );
335 DALI_LOG_WARNING("Context lost\n");
336 mCore.ContextToBeDestroyed();
337 mCore.ContextCreated();
340 // if both new and old surface are using the same display, and the display
341 // connection was created by Dali, then transfer
342 // display owner ship to the new surface.
343 mCurrent.surface->TransferDisplayOwner( *newSurface );
345 // use the new surface from now on
346 mCurrent.surface = newSurface;
348 // after rendering, NotifySurfaceChangeCompleted will be called
349 mSurfaceReplacing = true;
352 void RenderThread::NotifySurfaceChangeCompleted()
355 boost::unique_lock< boost::mutex > lock( mSurfaceChangedMutex );
356 mSurfaceReplaceCompleted = true;
358 // notify main thread
359 mSurfaceChangedNotify.notify_all();
362 void RenderThread::ShutdownEgl()
364 // inform core the context is about to be destroyed,
365 mCore.ContextToBeDestroyed();
367 // give a chance to destroy the OpenGL surface that created externally
368 mCurrent.surface->DestroyEglSurface( *mEGL );
370 // delete the GL context / egl surface
371 mEGL->TerminateGles();
374 bool RenderThread::PreRender()
376 return mCurrent.surface->PreRender( *mEGL, mGLES );
379 void RenderThread::PostRender( unsigned int timeDelta )
381 const bool waitForSync = mCurrent.surface->PostRender( *mEGL, mGLES, timeDelta );
385 boost::unique_lock< boost::mutex > lock( mPixmapSyncMutex );
387 // wait until synced,
388 // this blocks the thread here and releases the mPixmapSyncMutex (so the main thread can use it)
389 // if surface is replacing, forcely update without pixmap sync
390 if( mPixmapSyncRunning && !mPixmapSyncReceived && !mSurfaceReplacing )
392 mPixmapSyncNotify.wait( lock );
394 mPixmapSyncReceived = false;
398 } // namespace Adaptor
400 } // namespace Internal