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