Support screen and client rotation
[platform/core/uifw/dali-adaptor.git] / dali / internal / window-system / common / window-render-surface.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/window-system/common/window-render-surface.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/integration-api/gl-abstraction.h>
23 #include <dali/integration-api/debug.h>
24
25 // INTERNAL INCLUDES
26 #include <dali/integration-api/adaptor-framework/thread-synchronization-interface.h>
27 #include <dali/integration-api/adaptor-framework/trigger-event-factory.h>
28 #include <dali/internal/adaptor/common/adaptor-impl.h>
29 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
30 #include <dali/internal/graphics/gles/egl-graphics.h>
31 #include <dali/internal/graphics/gles/egl-implementation.h>
32 #include <dali/internal/window-system/common/window-base.h>
33 #include <dali/internal/window-system/common/window-factory.h>
34 #include <dali/internal/window-system/common/window-system.h>
35 #include <dali/internal/system/common/environment-variables.h>
36
37 namespace Dali
38 {
39 namespace Internal
40 {
41 namespace Adaptor
42 {
43
44 namespace
45 {
46
47 const int MINIMUM_DIMENSION_CHANGE( 1 ); ///< Minimum change for window to be considered to have moved
48 const float FULL_UPDATE_RATIO( 0.8f );   ///< Force full update when the dirty area is larget than this ratio
49
50 #if defined(DEBUG_ENABLED)
51 Debug::Filter* gWindowRenderSurfaceLogFilter = Debug::Filter::New(Debug::Verbose, false, "LOG_WINDOW_RENDER_SURFACE");
52 #endif
53
54 void MergeRects( Rect< int >& mergingRect, const std::vector< Rect< int > >& rects )
55 {
56   uint32_t i = 0;
57   if( mergingRect.IsEmpty() )
58   {
59     for( ; i < rects.size(); i++ )
60     {
61       if( !rects[i].IsEmpty() )
62       {
63         mergingRect = rects[i];
64         break;
65       }
66     }
67   }
68
69   for( ; i < rects.size(); i++ )
70   {
71     mergingRect.Merge( rects[i] );
72   }
73 }
74
75 void InsertRects( WindowRenderSurface::DamagedRectsContainer& damagedRectsList, const std::vector< Rect< int > >& damagedRects )
76 {
77   damagedRectsList.push_front( damagedRects );
78   if( damagedRectsList.size() > 4 ) // past triple buffers + current
79   {
80     damagedRectsList.pop_back();
81   }
82 }
83
84 } // unnamed namespace
85
86 WindowRenderSurface::WindowRenderSurface(Dali::PositionSize positionSize, Any surface, bool isTransparent)
87 : mEGL(nullptr),
88   mDisplayConnection(nullptr),
89   mPositionSize(positionSize),
90   mWindowBase(),
91   mThreadSynchronization(nullptr),
92   mRenderNotification(nullptr),
93   mRotationTrigger(nullptr),
94   mFrameRenderedTrigger(),
95   mGraphics(nullptr),
96   mEGLSurface(nullptr),
97   mEGLContext(nullptr),
98   mColorDepth(isTransparent ? COLOR_DEPTH_32 : COLOR_DEPTH_24),
99   mOutputTransformedSignal(),
100   mFrameCallbackInfoContainer(),
101   mBufferDamagedRects(),
102   mMutex(),
103   mWindowRotationAngle(0),
104   mScreenRotationAngle(0),
105   mDpiHorizontal(0),
106   mDpiVertical(0),
107   mOwnSurface(false),
108   mWindowRotationFinished(true),
109   mScreenRotationFinished(true),
110   mResizeFinished(true),
111   mDefaultScreenRotationAvailable(false)
112 {
113   DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "Creating Window\n" );
114   Initialize( surface );
115 }
116
117 WindowRenderSurface::~WindowRenderSurface()
118 {
119   if( mRotationTrigger )
120   {
121     delete mRotationTrigger;
122   }
123 }
124
125 void WindowRenderSurface::Initialize( Any surface )
126 {
127   // If width or height are zero, go full screen.
128   if ( (mPositionSize.width == 0) || (mPositionSize.height == 0) )
129   {
130     // Default window size == screen size
131     mPositionSize.x = 0;
132     mPositionSize.y = 0;
133     WindowSystem::GetScreenSize( mPositionSize.width, mPositionSize.height );
134   }
135
136   // Create a window base
137   auto windowFactory = Dali::Internal::Adaptor::GetWindowFactory();
138   mWindowBase = windowFactory->CreateWindowBase( mPositionSize, surface, ( mColorDepth == COLOR_DEPTH_32 ? true : false ) );
139
140   // Connect signals
141   mWindowBase->OutputTransformedSignal().Connect( this, &WindowRenderSurface::OutputTransformed );
142
143   // Check screen rotation
144   mScreenRotationAngle = mWindowBase->GetScreenRotationAngle();
145   if( mScreenRotationAngle != 0 )
146   {
147     mScreenRotationFinished = false;
148     mResizeFinished = false;
149     mDefaultScreenRotationAvailable = true;
150     DALI_LOG_RELEASE_INFO("WindowRenderSurface::Initialize, screen rotation is enabled, screen rotation angle:[%d]\n", mScreenRotationAngle );
151   }
152 }
153
154 Any WindowRenderSurface::GetNativeWindow()
155 {
156   return mWindowBase->GetNativeWindow();
157 }
158
159 int WindowRenderSurface::GetNativeWindowId()
160 {
161   return mWindowBase->GetNativeWindowId();
162 }
163
164 void WindowRenderSurface::Map()
165 {
166   mWindowBase->Show();
167 }
168
169 void WindowRenderSurface::SetRenderNotification( TriggerEventInterface* renderNotification )
170 {
171   mRenderNotification = renderNotification;
172 }
173
174 void WindowRenderSurface::SetTransparency( bool transparent )
175 {
176   mWindowBase->SetTransparency( transparent );
177 }
178
179 void WindowRenderSurface::RequestRotation(int angle, int width, int height)
180 {
181   if(!mRotationTrigger)
182   {
183     mRotationTrigger = TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &WindowRenderSurface::ProcessRotationRequest), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER);
184   }
185
186   mPositionSize.width  = width;
187   mPositionSize.height = height;
188
189   mWindowRotationAngle    = angle;
190   mWindowRotationFinished = false;
191
192   mWindowBase->SetWindowRotationAngle(mWindowRotationAngle);
193
194   DALI_LOG_INFO(gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::Rotate: angle = %d screen rotation = %d\n", mWindowRotationAngle, mScreenRotationAngle);
195 }
196
197 WindowBase* WindowRenderSurface::GetWindowBase()
198 {
199   return mWindowBase.get();
200 }
201
202 WindowBase::OutputSignalType& WindowRenderSurface::OutputTransformedSignal()
203 {
204   return mOutputTransformedSignal;
205 }
206
207 PositionSize WindowRenderSurface::GetPositionSize() const
208 {
209   return mPositionSize;
210 }
211
212 void WindowRenderSurface::GetDpi( unsigned int& dpiHorizontal, unsigned int& dpiVertical )
213 {
214   if( mDpiHorizontal == 0 || mDpiVertical == 0 )
215   {
216     const char* environmentDpiHorizontal = std::getenv( DALI_ENV_DPI_HORIZONTAL );
217     mDpiHorizontal = environmentDpiHorizontal ? std::atoi( environmentDpiHorizontal ) : 0;
218
219     const char* environmentDpiVertical = std::getenv( DALI_ENV_DPI_VERTICAL );
220     mDpiVertical = environmentDpiVertical ? std::atoi( environmentDpiVertical ) : 0;
221
222     if( mDpiHorizontal == 0 || mDpiVertical == 0 )
223     {
224       mWindowBase->GetDpi( mDpiHorizontal, mDpiVertical );
225     }
226   }
227
228   dpiHorizontal = mDpiHorizontal;
229   dpiVertical = mDpiVertical;
230 }
231
232 int WindowRenderSurface::GetOrientation() const
233 {
234   return mWindowBase->GetOrientation();
235 }
236
237 void WindowRenderSurface::InitializeGraphics()
238 {
239   mGraphics = &mAdaptor->GetGraphicsInterface();
240
241   DALI_ASSERT_ALWAYS( mGraphics && "Graphics interface is not created" );
242
243   auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
244   mEGL = &eglGraphics->GetEglInterface();
245
246   if ( mEGLContext == NULL )
247   {
248     // Create the OpenGL context for this window
249     Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>(*mEGL);
250     eglImpl.ChooseConfig(true, mColorDepth);
251     eglImpl.CreateWindowContext( mEGLContext );
252
253     // Create the OpenGL surface
254     CreateSurface();
255   }
256 }
257
258 void WindowRenderSurface::CreateSurface()
259 {
260   DALI_LOG_TRACE_METHOD(gWindowRenderSurfaceLogFilter);
261
262   int width, height;
263   if(mScreenRotationAngle == 0 || mScreenRotationAngle == 180)
264   {
265     width  = mPositionSize.width;
266     height = mPositionSize.height;
267   }
268   else
269   {
270     width  = mPositionSize.height;
271     height = mPositionSize.width;
272   }
273
274   // Create the EGL window
275   EGLNativeWindowType window = mWindowBase->CreateEglWindow(width, height);
276
277   auto eglGraphics = static_cast<EglGraphics*>(mGraphics);
278
279   Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
280   mEGLSurface                                   = eglImpl.CreateSurfaceWindow(window, mColorDepth);
281
282   DALI_LOG_RELEASE_INFO("WindowRenderSurface::CreateSurface: WinId (%d), w = %d h = %d angle = %d screen rotation = %d\n",
283                         mWindowBase->GetNativeWindowId(),
284                         mPositionSize.width,
285                         mPositionSize.height,
286                         mWindowRotationAngle,
287                         mScreenRotationAngle);
288 }
289
290 void WindowRenderSurface::DestroySurface()
291 {
292   DALI_LOG_TRACE_METHOD( gWindowRenderSurfaceLogFilter );
293
294   auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
295   if( eglGraphics )
296   {
297     DALI_LOG_RELEASE_INFO("WindowRenderSurface::DestroySurface: WinId (%d)\n", mWindowBase->GetNativeWindowId() );
298
299     Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
300
301     eglImpl.DestroySurface( mEGLSurface );
302     mEGLSurface = nullptr;
303
304     // Destroy context also
305     eglImpl.DestroyContext( mEGLContext );
306     mEGLContext = nullptr;
307
308     mWindowBase->DestroyEglWindow();
309   }
310 }
311
312 bool WindowRenderSurface::ReplaceGraphicsSurface()
313 {
314   DALI_LOG_TRACE_METHOD( gWindowRenderSurfaceLogFilter );
315
316   // Destroy the old one
317   mWindowBase->DestroyEglWindow();
318
319   int width, height;
320   if( mScreenRotationAngle == 0 || mScreenRotationAngle == 180 )
321   {
322     width = mPositionSize.width;
323     height = mPositionSize.height;
324   }
325   else
326   {
327     width = mPositionSize.height;
328     height = mPositionSize.width;
329   }
330
331   // Create the EGL window
332   EGLNativeWindowType window = mWindowBase->CreateEglWindow( width, height );
333
334   // Set screen rotation
335   mScreenRotationFinished = false;
336
337   auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
338
339   Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
340   return eglImpl.ReplaceSurfaceWindow( window, mEGLSurface, mEGLContext );
341 }
342
343 void WindowRenderSurface::MoveResize( Dali::PositionSize positionSize )
344 {
345   bool needToMove = false;
346   bool needToResize = false;
347
348   // Check moving
349   if( (fabs(positionSize.x - mPositionSize.x) > MINIMUM_DIMENSION_CHANGE) ||
350       (fabs(positionSize.y - mPositionSize.y) > MINIMUM_DIMENSION_CHANGE) )
351   {
352     needToMove = true;
353   }
354
355   // Check resizing
356   if( (fabs(positionSize.width - mPositionSize.width) > MINIMUM_DIMENSION_CHANGE) ||
357       (fabs(positionSize.height - mPositionSize.height) > MINIMUM_DIMENSION_CHANGE) )
358   {
359     needToResize = true;
360   }
361
362   if( needToResize )
363   {
364     if( needToMove )
365     {
366       mWindowBase->MoveResize( positionSize );
367     }
368     else
369     {
370       mWindowBase->Resize( positionSize );
371     }
372
373     mResizeFinished = false;
374     mPositionSize = positionSize;
375   }
376   else
377   {
378     if( needToMove )
379     {
380       mWindowBase->Move( positionSize );
381
382       mPositionSize = positionSize;
383     }
384   }
385
386   DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::MoveResize: %d, %d, %d, %d\n", mPositionSize.x, mPositionSize.y, mPositionSize.width, mPositionSize.height );
387 }
388
389 void WindowRenderSurface::StartRender()
390 {
391 }
392
393 bool WindowRenderSurface::PreRender(bool resizingSurface, const std::vector<Rect<int>>& damagedRects, Rect<int>& clippingRect)
394 {
395   Dali::Integration::Scene::FrameCallbackContainer callbacks;
396
397   Dali::Integration::Scene scene = mScene.GetHandle();
398   if(scene)
399   {
400     bool needFrameRenderedTrigger = false;
401
402     scene.GetFrameRenderedCallback(callbacks);
403     if(!callbacks.empty())
404     {
405       int frameRenderedSync = mWindowBase->CreateFrameRenderedSyncFence();
406       if(frameRenderedSync != -1)
407       {
408         Dali::Mutex::ScopedLock lock(mMutex);
409
410         DALI_LOG_RELEASE_INFO( "WindowRenderSurface::PreRender: CreateFrameRenderedSyncFence [%d]\n", frameRenderedSync );
411
412         mFrameCallbackInfoContainer.push_back(std::unique_ptr<FrameCallbackInfo>(new FrameCallbackInfo(callbacks, frameRenderedSync)));
413
414         needFrameRenderedTrigger = true;
415       }
416       else
417       {
418         DALI_LOG_ERROR("WindowRenderSurface::PreRender: CreateFrameRenderedSyncFence is failed\n");
419       }
420
421       // Clear callbacks
422       callbacks.clear();
423     }
424
425     scene.GetFramePresentedCallback(callbacks);
426     if(!callbacks.empty())
427     {
428       int framePresentedSync = mWindowBase->CreateFramePresentedSyncFence();
429       if(framePresentedSync != -1)
430       {
431         Dali::Mutex::ScopedLock lock(mMutex);
432
433         DALI_LOG_RELEASE_INFO( "WindowRenderSurface::PreRender: CreateFramePresentedSyncFence [%d]\n", framePresentedSync );
434
435         mFrameCallbackInfoContainer.push_back(std::unique_ptr<FrameCallbackInfo>(new FrameCallbackInfo(callbacks, framePresentedSync)));
436
437         needFrameRenderedTrigger = true;
438       }
439       else
440       {
441         DALI_LOG_ERROR("WindowRenderSurface::PreRender: CreateFramePresentedSyncFence is failed\n");
442       }
443
444       // Clear callbacks
445       callbacks.clear();
446     }
447
448     if(needFrameRenderedTrigger)
449     {
450       if(!mFrameRenderedTrigger)
451       {
452         mFrameRenderedTrigger = std::unique_ptr<TriggerEventInterface>(TriggerEventFactory::CreateTriggerEvent(MakeCallback(this, &WindowRenderSurface::ProcessFrameCallback),
453                                                                                                                TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER));
454       }
455       mFrameRenderedTrigger->Trigger();
456     }
457   }
458
459   MakeContextCurrent();
460
461   /**
462     * wl_egl_window_tizen_set_rotation(SetEglWindowRotation)                -> PreRotation
463     * wl_egl_window_tizen_set_buffer_transform(SetEglWindowBufferTransform) -> Screen Rotation
464     * wl_egl_window_tizen_set_window_transform(SetEglWindowTransform)       -> Window Rotation
465     * These function should be called before calling first drawing gl Function.
466     * Notice : PreRotation is not used in the latest tizen,
467     *          because output transform event should be occured before egl window is not created.
468     */
469
470   if(resizingSurface || mDefaultScreenRotationAvailable)
471   {
472     int totalAngle = (mWindowRotationAngle + mScreenRotationAngle) % 360;
473
474     // Window rotate or screen rotate
475     if(!mWindowRotationFinished || !mScreenRotationFinished)
476     {
477       mWindowBase->SetEglWindowBufferTransform(totalAngle);
478
479       // Reset only screen rotation flag
480       mScreenRotationFinished = true;
481
482       DALI_LOG_RELEASE_INFO("WindowRenderSurface::PreRender: Set rotation [%d] [%d]\n", mWindowRotationAngle, mScreenRotationAngle);
483     }
484
485     // Only window rotate
486     if(!mWindowRotationFinished)
487     {
488       mWindowBase->SetEglWindowTransform(mWindowRotationAngle);
489     }
490
491     // Resize case
492     if(!mResizeFinished)
493     {
494       Dali::PositionSize positionSize;
495       positionSize.x = mPositionSize.x;
496       positionSize.y = mPositionSize.y;
497       if(totalAngle == 0 || totalAngle == 180)
498       {
499         positionSize.width  = mPositionSize.width;
500         positionSize.height = mPositionSize.height;
501       }
502       else
503       {
504         positionSize.width  = mPositionSize.height;
505         positionSize.height = mPositionSize.width;
506       }
507       mWindowBase->ResizeEglWindow(positionSize);
508       mResizeFinished = true;
509
510       DALI_LOG_RELEASE_INFO("WindowRenderSurface::PreRender: Set resize\n");
511     }
512
513     SetFullSwapNextFrame();
514     mDefaultScreenRotationAvailable = false;
515   }
516
517   SetBufferDamagedRects(damagedRects, clippingRect);
518
519   return true;
520 }
521
522 void WindowRenderSurface::PostRender(bool renderToFbo, bool replacingSurface, bool resizingSurface, const std::vector<Rect<int>>& damagedRects)
523 {
524   // Inform the gl implementation that rendering has finished before informing the surface
525   auto eglGraphics = static_cast<EglGraphics*>(mGraphics);
526   if(eglGraphics)
527   {
528     GlImplementation& mGLES = eglGraphics->GetGlesInterface();
529     mGLES.PostRender();
530
531     if(renderToFbo)
532     {
533       mGLES.Flush();
534       mGLES.Finish();
535     }
536     else
537     {
538       if(resizingSurface)
539       {
540         if(!mWindowRotationFinished)
541         {
542           if(mThreadSynchronization)
543           {
544             // Enable PostRender flag
545             mThreadSynchronization->PostRenderStarted();
546           }
547
548           DALI_LOG_RELEASE_INFO("WindowRenderSurface::PostRender: Trigger rotation event\n");
549
550           mRotationTrigger->Trigger();
551
552           if(mThreadSynchronization)
553           {
554             // Wait until the event-thread complete the rotation event processing
555             mThreadSynchronization->PostRenderWaitForCompletion();
556           }
557         }
558       }
559     }
560
561     SwapBuffers(damagedRects);
562
563     if(mRenderNotification)
564     {
565       mRenderNotification->Trigger();
566     }
567   }
568 }
569
570 void WindowRenderSurface::StopRender()
571 {
572 }
573
574 void WindowRenderSurface::SetThreadSynchronization( ThreadSynchronizationInterface& threadSynchronization )
575 {
576   DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::SetThreadSynchronization: called\n" );
577
578   mThreadSynchronization = &threadSynchronization;
579 }
580
581 void WindowRenderSurface::ReleaseLock()
582 {
583   // Nothing to do.
584 }
585
586 Dali::RenderSurfaceInterface::Type WindowRenderSurface::GetSurfaceType()
587 {
588   return Dali::RenderSurfaceInterface::WINDOW_RENDER_SURFACE;
589 }
590
591 void WindowRenderSurface::MakeContextCurrent()
592 {
593   if ( mEGL != nullptr )
594   {
595     mEGL->MakeContextCurrent( mEGLSurface, mEGLContext );
596   }
597 }
598
599 Integration::DepthBufferAvailable WindowRenderSurface::GetDepthBufferRequired()
600 {
601   return mGraphics ? mGraphics->GetDepthBufferRequired() : Integration::DepthBufferAvailable::FALSE;
602 }
603
604 Integration::StencilBufferAvailable WindowRenderSurface::GetStencilBufferRequired()
605 {
606   return mGraphics ? mGraphics->GetStencilBufferRequired() : Integration::StencilBufferAvailable::FALSE;
607 }
608
609 void WindowRenderSurface::OutputTransformed()
610 {
611   int screenRotationAngle = mWindowBase->GetScreenRotationAngle();
612
613   if(mScreenRotationAngle != screenRotationAngle)
614   {
615     mScreenRotationAngle    = screenRotationAngle;
616     mScreenRotationFinished = false;
617     mResizeFinished         = false;
618
619     mOutputTransformedSignal.Emit();
620
621     DALI_LOG_RELEASE_INFO("WindowRenderSurface::OutputTransformed: window = %d screen = %d\n", mWindowRotationAngle, mScreenRotationAngle);
622   }
623   else
624   {
625     DALI_LOG_RELEASE_INFO("WindowRenderSurface::OutputTransformed: Ignore output transform [%d]\n", mScreenRotationAngle);
626   }
627 }
628
629 void WindowRenderSurface::ProcessRotationRequest()
630 {
631   mWindowRotationFinished = true;
632
633   mWindowBase->WindowRotationCompleted(mWindowRotationAngle, mPositionSize.width, mPositionSize.height);
634
635   DALI_LOG_INFO(gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::ProcessRotationRequest: Rotation Done\n");
636
637   if(mThreadSynchronization)
638   {
639     mThreadSynchronization->PostRenderComplete();
640   }
641 }
642
643 void WindowRenderSurface::ProcessFrameCallback()
644 {
645   Dali::Mutex::ScopedLock lock( mMutex );
646
647   for( auto&& iter : mFrameCallbackInfoContainer )
648   {
649     if( !iter->fileDescriptorMonitor )
650     {
651       iter->fileDescriptorMonitor = std::unique_ptr< FileDescriptorMonitor >( new FileDescriptorMonitor( iter->fileDescriptor,
652                                                                              MakeCallback( this, &WindowRenderSurface::OnFileDescriptorEventDispatched ), FileDescriptorMonitor::FD_READABLE ) );
653
654       DALI_LOG_RELEASE_INFO( "WindowRenderSurface::ProcessFrameCallback: Add handler [%d]\n", iter->fileDescriptor );
655     }
656   }
657 }
658
659 void WindowRenderSurface::OnFileDescriptorEventDispatched( FileDescriptorMonitor::EventType eventBitMask, int fileDescriptor )
660 {
661   if( !( eventBitMask & FileDescriptorMonitor::FD_READABLE ) )
662   {
663     DALI_LOG_ERROR( "WindowRenderSurface::OnFileDescriptorEventDispatched: file descriptor error [%d]\n", eventBitMask );
664     close( fileDescriptor );
665     return;
666   }
667
668   DALI_LOG_RELEASE_INFO( "WindowRenderSurface::OnFileDescriptorEventDispatched: Frame rendered [%d]\n", fileDescriptor );
669
670   std::unique_ptr< FrameCallbackInfo > callbackInfo;
671   {
672     Dali::Mutex::ScopedLock lock( mMutex );
673     auto frameCallbackInfo = std::find_if( mFrameCallbackInfoContainer.begin(), mFrameCallbackInfoContainer.end(),
674                                       [fileDescriptor]( std::unique_ptr< FrameCallbackInfo >& callbackInfo )
675                                       {
676                                         return callbackInfo->fileDescriptor == fileDescriptor;
677                                       } );
678     if( frameCallbackInfo != mFrameCallbackInfoContainer.end() )
679     {
680       callbackInfo = std::move( *frameCallbackInfo );
681
682       mFrameCallbackInfoContainer.erase( frameCallbackInfo );
683     }
684   }
685
686   // Call the connected callback
687   if( callbackInfo )
688   {
689     for( auto&& iter : ( callbackInfo )->callbacks )
690     {
691       CallbackBase::Execute( *( iter.first ), iter.second );
692     }
693   }
694 }
695
696 void WindowRenderSurface::SetBufferDamagedRects( const std::vector< Rect< int > >& damagedRects, Rect< int >& clippingRect )
697 {
698   auto eglGraphics = static_cast< EglGraphics* >( mGraphics );
699   if ( eglGraphics )
700   {
701     Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
702     if( !eglImpl.IsPartialUpdateRequired() )
703     {
704       return;
705     }
706
707     Rect< int > surfaceRect( 0, 0, mPositionSize.width, mPositionSize.height );
708
709     if( mFullSwapNextFrame )
710     {
711       InsertRects( mBufferDamagedRects, std::vector< Rect< int > >( 1, surfaceRect ) );
712       clippingRect = Rect< int >();
713       return;
714     }
715
716     EGLint bufferAge = eglImpl.GetBufferAge( mEGLSurface );
717
718     // Buffer age 0 means the back buffer in invalid and requires full swap
719     if( !damagedRects.size() || bufferAge == 0 )
720     {
721       InsertRects( mBufferDamagedRects, std::vector< Rect< int > >( 1, surfaceRect ) );
722       clippingRect = Rect< int >();
723       return;
724     }
725
726     // We push current frame damaged rects here, zero index for current frame
727     InsertRects( mBufferDamagedRects, damagedRects );
728
729     // Merge damaged rects into clipping rect
730     auto bufferDamagedRects = mBufferDamagedRects.begin();
731     while( bufferAge-- >= 0 && bufferDamagedRects != mBufferDamagedRects.end() )
732     {
733       const std::vector< Rect< int > >& rects = *bufferDamagedRects++;
734       MergeRects( clippingRect, rects );
735     }
736
737     if( !clippingRect.Intersect( surfaceRect ) || clippingRect.Area() > surfaceRect.Area() * FULL_UPDATE_RATIO )
738     {
739       // clipping area too big or doesn't intersect surface rect
740       clippingRect = Rect< int >();
741       return;
742     }
743
744     std::vector< Rect< int > > damagedRegion;
745     damagedRegion.push_back( clippingRect );
746
747     eglImpl.SetDamageRegion( mEGLSurface, damagedRegion );
748   }
749 }
750
751 void WindowRenderSurface::SwapBuffers( const std::vector<Rect<int>>& damagedRects )
752 {
753   auto eglGraphics = static_cast< EglGraphics* >( mGraphics );
754   if( eglGraphics )
755   {
756     Rect< int > surfaceRect( 0, 0, mPositionSize.width, mPositionSize.height );
757
758     Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
759
760     if( !eglImpl.IsPartialUpdateRequired() || mFullSwapNextFrame || !damagedRects.size() || (damagedRects[0].Area() > surfaceRect.Area() * FULL_UPDATE_RATIO) )
761     {
762       mFullSwapNextFrame = false;
763       eglImpl.SwapBuffers( mEGLSurface );
764       return;
765     }
766
767     mFullSwapNextFrame = false;
768
769     std::vector< Rect< int > > mergedRects = damagedRects;
770
771     // Merge intersecting rects, form an array of non intersecting rects to help driver a bit
772     // Could be optional and can be removed, needs to be checked with and without on platform
773     const int n = mergedRects.size();
774     for( int i = 0; i < n - 1; i++ )
775     {
776       if( mergedRects[i].IsEmpty() )
777       {
778         continue;
779       }
780
781       for( int j = i + 1; j < n; j++ )
782       {
783         if( mergedRects[j].IsEmpty() )
784         {
785           continue;
786         }
787
788         if( mergedRects[i].Intersects( mergedRects[j] ) )
789         {
790           mergedRects[i].Merge( mergedRects[j] );
791           mergedRects[j].width = 0;
792           mergedRects[j].height = 0;
793         }
794       }
795     }
796
797     int j = 0;
798     for( int i = 0; i < n; i++ )
799     {
800       if( !mergedRects[i].IsEmpty() )
801       {
802         mergedRects[j++] = mergedRects[i];
803       }
804     }
805
806     if( j != 0 )
807     {
808       mergedRects.resize( j );
809     }
810
811     if( !mergedRects.size() || ( mergedRects[0].Area() > surfaceRect.Area() * FULL_UPDATE_RATIO ) )
812     {
813       eglImpl.SwapBuffers( mEGLSurface );
814     }
815     else
816     {
817       eglImpl.SwapBuffers( mEGLSurface, mergedRects );
818     }
819   }
820 }
821
822 } // namespace Adaptor
823
824 } // namespace internal
825
826 } // namespace Dali