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 mSurfaceReplacing( false ),
54 mNewDataAvailable( false ),
55 mSurfaceReplaceCompleted( false ),
56 mLogOptions( logOptions )
58 // set the initial values before render thread starts
59 mCurrent.surface = adaptorInterfaces.GetRenderSurfaceInterface();
62 RenderThread::~RenderThread()
64 DALI_ASSERT_ALWAYS( mThread == NULL && "RenderThread is still alive");
65 mEglFactory->Destroy();
68 void RenderThread::Start()
70 // initialise GL and kick off render thread
71 DALI_ASSERT_ALWAYS( !mEGL && "Egl already initialized" );
73 // Tell core what the minimum frame interval is
74 mCore.SetMinimumFrameTimeInterval( mCurrent.syncMode * TIME_PER_FRAME_IN_MICROSECONDS );
76 // create the render thread, initially we are rendering
77 mThread = new boost::thread(boost::bind(&RenderThread::Run, this));
79 // Inform surface to block waiting for RenderSync
80 mCurrent.surface->SetSyncMode( RenderSurface::SYNC_MODE_WAIT );
83 void RenderThread::Stop()
85 // shutdown the render thread and destroy the opengl context
88 // Tell surface we have stopped rendering
89 mCurrent.surface->StopRender();
91 // wait for the thread to finish
99 void RenderThread::ReplaceSurface( RenderSurface* surface )
101 // Make sure it's a new surface. Note! we are reading the current value of render thread here, but reading is ok
102 DALI_ASSERT_ALWAYS( surface != mCurrent.surface && "Trying to replace surface with itself" );
104 // lock and set to false
106 boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
107 mSurfaceReplaceCompleted = false;
110 // lock cache and set update flag at the end of function
112 SendMessageGuard msg( *this );
113 // set new values to cache
114 mNewValues.replaceSurface = true;
115 mNewValues.surface = surface;
119 * Reset the mPixmapFlushed condition if surface was changed.
120 * : in this case, client can not handle the previous damage because surface was changed.
125 void RenderThread::WaitForSurfaceReplaceComplete()
127 boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
129 // if already completed no need to wait
130 while( !mSurfaceReplaceCompleted )
132 mSurfaceChangedNotify.wait( lock ); // Block the main thread here and releases mSurfaceChangedMutex so the render-thread can notify us
136 void RenderThread::SetVSyncMode( EglInterface::SyncMode syncMode )
138 // lock cache and set update flag at the end of function
139 SendMessageGuard msg( *this );
140 // set new values to cache
141 mNewValues.syncMode = syncMode;
144 void RenderThread::RenderSync()
146 mCurrent.surface->RenderSync();
149 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
150 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
151 // The following methods are all executed inside render thread !!!
152 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
153 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
155 bool RenderThread::Run()
157 // install a function for logging
158 mLogOptions.InstallLogFunction();
162 bool running( true );
164 // Wait for first update
165 mUpdateRenderSync.RenderSyncWithUpdate();
167 Dali::Integration::RenderStatus renderStatus;
169 uint64_t currentTime( 0 );
171 // render loop, we stay inside here when rendering
174 // Consume any pending events
177 // Check if we've got updates from the main thread
180 // perform any pre-render operations
181 if(PreRender() == true)
184 mCore.Render( renderStatus );
186 // Notify the update-thread that a render has completed
187 mUpdateRenderSync.RenderFinished( renderStatus.NeedsUpdate() );
189 uint64_t newTime( mUpdateRenderSync.GetTimeMicroseconds() );
191 // perform any post-render operations
192 if ( renderStatus.HasRendered() )
194 PostRender( static_cast< unsigned int >(newTime - currentTime) );
197 if(mSurfaceReplacing)
199 // Notify main thread that surface was changed so it can release the old one
200 NotifySurfaceChangeCompleted();
201 mSurfaceReplacing = false;
204 currentTime = newTime;
207 // Wait until another frame has been updated
208 running = mUpdateRenderSync.RenderSyncWithUpdate();
214 // install a function for logging
215 mLogOptions.UnInstallLogFunction();
220 void RenderThread::InitializeEgl()
222 mEGL = mEglFactory->Create();
224 DALI_ASSERT_ALWAYS( mCurrent.surface && "NULL surface" );
226 // initialize egl & OpenGL
227 mCurrent.surface->InitializeEgl( *mEGL );
229 // create the OpenGL context
230 mEGL->CreateContext();
232 // create the OpenGL surface
233 mCurrent.surface->CreateEglSurface( *mEGL );
236 mEGL->MakeContextCurrent();
238 // set the initial sync mode
239 mEGL->SetRefreshSync( mCurrent.syncMode );
241 // tell core it has a context
242 mCore.ContextCreated();
244 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VENDOR : %s ***\n", mGLES.GetString(GL_VENDOR));
245 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_RENDERER : %s ***\n", mGLES.GetString(GL_RENDERER));
246 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_VERSION : %s ***\n", mGLES.GetString(GL_VERSION));
247 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** GL_SHADING_LANGUAGE_VERSION : %s***\n", mGLES.GetString(GL_SHADING_LANGUAGE_VERSION));
248 DALI_LOG_INFO(Debug::Filter::gShader, Debug::General, "*** Supported Extensions ***\n%s\n\n", mGLES.GetString(GL_EXTENSIONS));
251 void RenderThread::ConsumeEvents()
253 // tell surface to consume any events to avoid memory leaks
254 mCurrent.surface->ConsumeEvents();
257 void RenderThread::CheckForUpdates()
259 // atomic check to see if we've got updates, resets the flag int
260 if( __sync_fetch_and_and( &mNewDataAvailable, 0 ) )
263 // NOTE! This block is the only place in render side where mNewValues can be used inside render thread !!!
265 // need to lock to access new values
266 boost::unique_lock< boost::mutex > lock( mThreadDataLock );
268 // did the sync mode change
269 if( mCurrent.syncMode != mNewValues.syncMode )
271 mCurrent.syncMode = mNewValues.syncMode;
272 mEGL->SetRefreshSync( mCurrent.syncMode );
275 // check if the surface needs replacing
276 if( mNewValues.replaceSurface )
278 mNewValues.replaceSurface = false; // reset the flag
279 // change the surface
280 ChangeSurface( mNewValues.surface );
281 mNewValues.surface = NULL;
287 void RenderThread::ChangeSurface( RenderSurface* newSurface )
289 // This is designed for replacing pixmap surfaces, but should work for window as well
290 // we need to delete the egl surface and renderable (pixmap / window)
291 // Then create a new pixmap/window and new egl surface
292 // If the new surface has a different display connection, then the context will be lost
293 DALI_ASSERT_ALWAYS( newSurface && "NULL surface" )
294 bool contextLost = newSurface->ReplaceEGLSurface( *mEGL );
298 DALI_LOG_WARNING("Context lost\n");
299 mCore.ContextToBeDestroyed();
300 mCore.ContextCreated();
303 // if both new and old surface are using the same display, and the display
304 // connection was created by Dali, then transfer
305 // display owner ship to the new surface.
306 mCurrent.surface->TransferDisplayOwner( *newSurface );
308 // use the new surface from now on
309 mCurrent.surface = newSurface;
311 // after rendering, NotifySurfaceChangeCompleted will be called
312 mSurfaceReplacing = true;
315 void RenderThread::NotifySurfaceChangeCompleted()
318 boost::unique_lock< boost::mutex > lock( mSurfaceChangedMutex );
319 mSurfaceReplaceCompleted = true;
321 // notify main thread
322 mSurfaceChangedNotify.notify_all();
325 void RenderThread::ShutdownEgl()
327 // inform core the context is about to be destroyed,
328 mCore.ContextToBeDestroyed();
330 // give a chance to destroy the OpenGL surface that created externally
331 mCurrent.surface->DestroyEglSurface( *mEGL );
333 // delete the GL context / egl surface
334 mEGL->TerminateGles();
337 bool RenderThread::PreRender()
339 return mCurrent.surface->PreRender( *mEGL, mGLES );
342 void RenderThread::PostRender( unsigned int timeDelta )
344 mCurrent.surface->PostRender( *mEGL, mGLES, timeDelta,
345 mSurfaceReplacing ? RenderSurface::SYNC_MODE_NONE : RenderSurface::SYNC_MODE_WAIT );
348 } // namespace Adaptor
350 } // namespace Internal