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>
41 const unsigned int TIME_PER_FRAME_IN_MICROSECONDS = 16667;
43 } // unnamed namespace
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 // initialise GL and kick off render thread
72 DALI_ASSERT_ALWAYS( !mEGL && "Egl already initialized" );
74 // Tell frame timer what the minimum frame interval is
75 mUpdateRenderSync.SetMinimumFrameTimeInterval( mCurrent.syncMode * TIME_PER_FRAME_IN_MICROSECONDS );
77 // create the render thread, initially we are rendering
78 mThread = new boost::thread(boost::bind(&RenderThread::Run, this));
80 // Inform surface to block waiting for RenderSync
81 mCurrent.surface->SetSyncMode( RenderSurface::SYNC_MODE_WAIT );
84 void RenderThread::Stop()
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 // Make sure it's a new surface. Note! we are reading the current value of render thread here, but reading is ok
103 DALI_ASSERT_ALWAYS( surface != mCurrent.surface && "Trying to replace surface with itself" );
105 // lock and set to false
107 boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
108 mSurfaceReplaceCompleted = false;
111 // lock cache and set update flag at the end of function
113 SendMessageGuard msg( *this );
114 // set new values to cache
115 mNewValues.replaceSurface = true;
116 mNewValues.surface = surface;
120 * Reset the mPixmapFlushed condition if surface was changed.
121 * : in this case, client can not handle the previous damage because surface was changed.
126 void RenderThread::WaitForSurfaceReplaceComplete()
128 boost::unique_lock<boost::mutex> lock( mSurfaceChangedMutex );
130 // if already completed no need to wait
131 while( !mSurfaceReplaceCompleted )
133 mSurfaceChangedNotify.wait( lock ); // Block the main thread here and releases mSurfaceChangedMutex so the render-thread can notify us
137 void RenderThread::SetVSyncMode( EglInterface::SyncMode syncMode )
139 // lock cache and set update flag at the end of function
140 SendMessageGuard msg( *this );
141 // set new values to cache
142 mNewValues.syncMode = syncMode;
145 void RenderThread::RenderSync()
147 mCurrent.surface->RenderSync();
150 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
151 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
152 // The following methods are all executed inside render thread !!!
153 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
154 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
156 bool RenderThread::Run()
158 // install a function for logging
159 mEnvironmentOptions.InstallLogFunction();
163 bool running( true );
165 // Wait for first update
166 mUpdateRenderSync.RenderSyncWithUpdate();
168 Dali::Integration::RenderStatus renderStatus;
170 uint64_t currentTime( 0 );
172 // render loop, we stay inside here when rendering
175 // Consume any pending events
178 // Check if we've got updates from the main thread
181 // perform any pre-render operations
182 if(PreRender() == true)
185 mCore.Render( renderStatus );
187 // Notify the update-thread that a render has completed
188 mUpdateRenderSync.RenderFinished( renderStatus.NeedsUpdate() );
190 uint64_t newTime( mUpdateRenderSync.GetTimeMicroseconds() );
192 // perform any post-render operations
193 if ( renderStatus.HasRendered() )
195 PostRender( static_cast< unsigned int >(newTime - currentTime) );
198 if(mSurfaceReplacing)
200 // Notify main thread that surface was changed so it can release the old one
201 NotifySurfaceChangeCompleted();
202 mSurfaceReplacing = false;
205 currentTime = newTime;
208 // Wait until another frame has been updated
209 running = mUpdateRenderSync.RenderSyncWithUpdate();
215 // install a function for logging
216 mEnvironmentOptions.UnInstallLogFunction();
221 void RenderThread::InitializeEgl()
223 mEGL = mEglFactory->Create();
225 DALI_ASSERT_ALWAYS( mCurrent.surface && "NULL surface" );
227 // initialize egl & OpenGL
228 mCurrent.surface->InitializeEgl( *mEGL );
230 // create the OpenGL context
231 mEGL->CreateContext();
233 // create the OpenGL surface
234 mCurrent.surface->CreateEglSurface( *mEGL );
237 mEGL->MakeContextCurrent();
239 // set the initial sync mode
241 //@todo This needs to call the render surface instead
242 mEGL->SetRefreshSync( mCurrent.syncMode );
244 // tell core it has a context
245 mCore.ContextCreated();
249 void RenderThread::ConsumeEvents()
251 // tell surface to consume any events to avoid memory leaks
252 mCurrent.surface->ConsumeEvents();
255 void RenderThread::CheckForUpdates()
257 // atomic check to see if we've got updates, resets the flag int
258 if( __sync_fetch_and_and( &mNewDataAvailable, 0 ) )
261 // NOTE! This block is the only place in render side where mNewValues can be used inside render thread !!!
263 // need to lock to access new values
264 boost::unique_lock< boost::mutex > lock( mThreadDataLock );
266 // did the sync mode change
267 if( mCurrent.syncMode != mNewValues.syncMode )
269 mCurrent.syncMode = mNewValues.syncMode;
271 //@todo This needs to call the render surface instead
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 bool success = mCurrent.surface->PreRender( *mEGL, mGLES );
347 void RenderThread::PostRender( unsigned int timeDelta )
349 // Inform the gl implementation that rendering has finished before informing the surface
350 mGLES.PostRender(timeDelta);
352 // Inform the surface that rendering this frame has finished.
353 mCurrent.surface->PostRender( *mEGL, mGLES, timeDelta,
354 mSurfaceReplacing ? RenderSurface::SYNC_MODE_NONE : RenderSurface::SYNC_MODE_WAIT );
357 } // namespace Adaptor
359 } // namespace Internal