[Tizen] Implement partial update
[platform/core/uifw/dali-adaptor.git] / dali / internal / window-system / common / window-render-surface.cpp
1 /*
2  * Copyright (c) 2019 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/trigger-event-factory-interface.h>
27 #include <dali/integration-api/thread-synchronization-interface.h>
28 #include <dali/internal/graphics/gles/egl-implementation.h>
29 #include <dali/internal/adaptor/common/adaptor-impl.h>
30 #include <dali/internal/adaptor/common/adaptor-internal-services.h>
31 #include <dali/internal/window-system/common/window-base.h>
32 #include <dali/internal/window-system/common/window-factory.h>
33 #include <dali/internal/window-system/common/window-system.h>
34 #include <dali/internal/graphics/gles/egl-graphics.h>
35 #include <dali/internal/system/common/environment-variables.h>
36
37
38 namespace Dali
39 {
40 namespace Internal
41 {
42 namespace Adaptor
43 {
44
45 namespace
46 {
47
48 const int MINIMUM_DIMENSION_CHANGE( 1 ); ///< Minimum change for window to be considered to have moved
49 const int TILE_SIZE = 16u;  ///< Unit of tile size at GPU driver
50
51 #if defined(DEBUG_ENABLED)
52 Debug::Filter* gWindowRenderSurfaceLogFilter = Debug::Filter::New(Debug::Verbose, false, "LOG_WINDOW_RENDER_SURFACE");
53 #endif
54
55 } // unnamed namespace
56
57 WindowRenderSurface::WindowRenderSurface( Dali::PositionSize positionSize, Any surface, bool isTransparent )
58 : mEGL( nullptr ),
59   mDisplayConnection( nullptr ),
60   mPositionSize( positionSize ),
61   mWindowBase(),
62   mThreadSynchronization( NULL ),
63   mRenderNotification( NULL ),
64   mRotationTrigger( NULL ),
65   mGraphics( nullptr ),
66   mEGLSurface( nullptr ),
67   mEGLContext( nullptr ),
68   mColorDepth( isTransparent ? COLOR_DEPTH_32 : COLOR_DEPTH_24 ),
69   mOutputTransformedSignal(),
70   mRotationAngle( 0 ),
71   mScreenRotationAngle( 0 ),
72   mBufferAge( 0 ),
73   mPreBufferAge( 0 ),
74   mOwnSurface( false ),
75   mRotationSupported( false ),
76   mRotationFinished( true ),
77   mScreenRotationFinished( true ),
78   mResizeFinished( true ),
79   mDpiHorizontal( 0 ),
80   mDpiVertical( 0 ),
81   mPreDamagedRect()
82 {
83   DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "Creating Window\n" );
84   Initialize( surface );
85 }
86
87 WindowRenderSurface::~WindowRenderSurface()
88 {
89   if( mRotationTrigger )
90   {
91     delete mRotationTrigger;
92   }
93
94   if ( mEGLSurface )
95   {
96     DestroySurface();
97   }
98 }
99
100 void WindowRenderSurface::Initialize( Any surface )
101 {
102   // If width or height are zero, go full screen.
103   if ( (mPositionSize.width == 0) || (mPositionSize.height == 0) )
104   {
105     // Default window size == screen size
106     mPositionSize.x = 0;
107     mPositionSize.y = 0;
108
109     WindowSystem::GetScreenSize( mPositionSize.width, mPositionSize.height );
110   }
111
112   // Create a window base
113   auto windowFactory = Dali::Internal::Adaptor::GetWindowFactory();
114   mWindowBase = windowFactory->CreateWindowBase( mPositionSize, surface, ( mColorDepth == COLOR_DEPTH_32 ? true : false ) );
115
116   // Connect signals
117   mWindowBase->OutputTransformedSignal().Connect( this, &WindowRenderSurface::OutputTransformed );
118
119   // Check screen rotation
120   mScreenRotationAngle = mWindowBase->GetScreenRotationAngle();
121   if( mScreenRotationAngle != 0 )
122   {
123     mScreenRotationFinished = false;
124     mResizeFinished = false;
125   }
126 }
127
128 Any WindowRenderSurface::GetNativeWindow()
129 {
130   return mWindowBase->GetNativeWindow();
131 }
132
133 int WindowRenderSurface::GetNativeWindowId()
134 {
135   return mWindowBase->GetNativeWindowId();
136 }
137
138 void WindowRenderSurface::Map()
139 {
140   mWindowBase->Show();
141 }
142
143 void WindowRenderSurface::SetRenderNotification( TriggerEventInterface* renderNotification )
144 {
145   mRenderNotification = renderNotification;
146 }
147
148 void WindowRenderSurface::SetTransparency( bool transparent )
149 {
150   mWindowBase->SetTransparency( transparent );
151 }
152
153 void WindowRenderSurface::RequestRotation( int angle, int width, int height )
154 {
155   if( !mRotationTrigger )
156   {
157     TriggerEventFactoryInterface& triggerFactory = Internal::Adaptor::Adaptor::GetImplementation( Adaptor::Get() ).GetTriggerEventFactoryInterface();
158     mRotationTrigger = triggerFactory.CreateTriggerEvent( MakeCallback( this, &WindowRenderSurface::ProcessRotationRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
159   }
160
161   mPositionSize.width = width;
162   mPositionSize.height = height;
163
164   mRotationAngle = angle;
165   mRotationFinished = false;
166
167   mWindowBase->SetWindowRotationAngle( mRotationAngle );
168
169   DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::Rotate: angle = %d screen rotation = %d\n", mRotationAngle, mScreenRotationAngle );
170 }
171
172 WindowBase* WindowRenderSurface::GetWindowBase()
173 {
174   return mWindowBase.get();
175 }
176
177 WindowBase::OutputSignalType& WindowRenderSurface::OutputTransformedSignal()
178 {
179   return mOutputTransformedSignal;
180 }
181
182 PositionSize WindowRenderSurface::GetPositionSize() const
183 {
184   return mPositionSize;
185 }
186
187 void WindowRenderSurface::GetDpi( unsigned int& dpiHorizontal, unsigned int& dpiVertical )
188 {
189   if( mDpiHorizontal == 0 || mDpiVertical == 0 )
190   {
191     const char* environmentDpiHorizontal = std::getenv( DALI_ENV_DPI_HORIZONTAL );
192     mDpiHorizontal = environmentDpiHorizontal ? std::atoi( environmentDpiHorizontal ) : 0;
193
194     const char* environmentDpiVertical = std::getenv( DALI_ENV_DPI_VERTICAL );
195     mDpiVertical = environmentDpiVertical ? std::atoi( environmentDpiVertical ) : 0;
196
197     if( mDpiHorizontal == 0 || mDpiVertical == 0 )
198     {
199       mWindowBase->GetDpi( mDpiHorizontal, mDpiVertical );
200     }
201   }
202
203   dpiHorizontal = mDpiHorizontal;
204   dpiVertical = mDpiVertical;
205 }
206
207 int WindowRenderSurface::GetOrientation() const
208 {
209   return mWindowBase->GetOrientation();
210 }
211
212 void WindowRenderSurface::InitializeGraphics()
213 {
214
215   mGraphics = &mAdaptor->GetGraphicsInterface();
216
217   auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
218   mEGL = &eglGraphics->GetEglInterface();
219
220   if ( mEGLContext == NULL )
221   {
222     // Create the OpenGL context for this window
223     Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>(*mEGL);
224     eglImpl.ChooseConfig(true, mColorDepth);
225     eglImpl.CreateWindowContext( mEGLContext );
226
227     // Create the OpenGL surface
228     CreateSurface();
229   }
230 }
231
232 void WindowRenderSurface::CreateSurface()
233 {
234   DALI_LOG_TRACE_METHOD( gWindowRenderSurfaceLogFilter );
235
236   int width, height;
237   if( mScreenRotationAngle == 0 || mScreenRotationAngle == 180 )
238   {
239     width = mPositionSize.width;
240     height = mPositionSize.height;
241   }
242   else
243   {
244     width = mPositionSize.height;
245     height = mPositionSize.width;
246   }
247
248   // Create the EGL window
249   EGLNativeWindowType window = mWindowBase->CreateEglWindow( width, height );
250
251   auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
252
253   Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
254   mEGLSurface = eglImpl.CreateSurfaceWindow( window, mColorDepth );
255
256   // Check rotation capability
257   mRotationSupported = mWindowBase->IsEglWindowRotationSupported();
258
259   DALI_LOG_RELEASE_INFO("WindowRenderSurface::CreateSurface: w = %d h = %d angle = %d screen rotation = %d\n",
260       mPositionSize.width, mPositionSize.height, mRotationAngle, mScreenRotationAngle );
261 }
262
263 void WindowRenderSurface::DestroySurface()
264 {
265   DALI_LOG_TRACE_METHOD( gWindowRenderSurfaceLogFilter );
266
267   auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
268
269   Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
270   eglImpl.DestroySurface( mEGLSurface );
271
272   mWindowBase->DestroyEglWindow();
273 }
274
275 bool WindowRenderSurface::ReplaceGraphicsSurface()
276 {
277   DALI_LOG_TRACE_METHOD( gWindowRenderSurfaceLogFilter );
278
279   // Destroy the old one
280   mWindowBase->DestroyEglWindow();
281
282   int width, height;
283   if( mScreenRotationAngle == 0 || mScreenRotationAngle == 180 )
284   {
285     width = mPositionSize.width;
286     height = mPositionSize.height;
287   }
288   else
289   {
290     width = mPositionSize.height;
291     height = mPositionSize.width;
292   }
293
294   // Create the EGL window
295   EGLNativeWindowType window = mWindowBase->CreateEglWindow( width, height );
296
297   // Set screen rotation
298   mScreenRotationFinished = false;
299
300   auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
301
302   Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
303   return eglImpl.ReplaceSurfaceWindow( window, mEGLSurface, mEGLContext );
304 }
305
306 void WindowRenderSurface::MoveResize( Dali::PositionSize positionSize )
307 {
308   bool needToMove = false;
309   bool needToResize = false;
310
311   // Check moving
312   if( (fabs(positionSize.x - mPositionSize.x) > MINIMUM_DIMENSION_CHANGE) ||
313       (fabs(positionSize.y - mPositionSize.y) > MINIMUM_DIMENSION_CHANGE) )
314   {
315     needToMove = true;
316   }
317
318   // Check resizing
319   if( (fabs(positionSize.width - mPositionSize.width) > MINIMUM_DIMENSION_CHANGE) ||
320       (fabs(positionSize.height - mPositionSize.height) > MINIMUM_DIMENSION_CHANGE) )
321   {
322     needToResize = true;
323   }
324
325   if( needToResize )
326   {
327     if( needToMove )
328     {
329       mWindowBase->MoveResize( positionSize );
330     }
331     else
332     {
333       mWindowBase->Resize( positionSize );
334     }
335
336     mResizeFinished = false;
337     mPositionSize = positionSize;
338   }
339   else
340   {
341     if( needToMove )
342     {
343       mWindowBase->Move( positionSize );
344
345       mPositionSize = positionSize;
346     }
347   }
348
349   DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::MoveResize: %d, %d, %d, %d\n", mPositionSize.x, mPositionSize.y, mPositionSize.width, mPositionSize.height );
350 }
351
352 void WindowRenderSurface::StartRender()
353 {
354 }
355
356 bool WindowRenderSurface::PreRender( bool resizingSurface )
357 {
358   MakeContextCurrent();
359
360   if( resizingSurface )
361   {
362     int totalAngle = (mRotationAngle + mScreenRotationAngle) % 360;
363
364     // Window rotate or screen rotate
365     if( !mRotationFinished || !mScreenRotationFinished )
366     {
367       mWindowBase->SetEglWindowRotation( totalAngle );
368       mWindowBase->SetEglWindowBufferTransform( totalAngle );
369
370       // Reset only screen rotation flag
371       mScreenRotationFinished = true;
372
373       DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::PreRender: Set rotation [%d] [%d]\n", mRotationAngle, mScreenRotationAngle );
374     }
375
376     // Only window rotate
377     if( !mRotationFinished )
378     {
379       mWindowBase->SetEglWindowTransform( mRotationAngle );
380     }
381
382     // Resize case
383     if ( !mResizeFinished )
384     {
385       Dali::PositionSize positionSize;
386       positionSize.x = mPositionSize.x;
387       positionSize.y = mPositionSize.y;
388       if( totalAngle == 0 || totalAngle == 180 )
389       {
390         positionSize.width = mPositionSize.width;
391         positionSize.height = mPositionSize.height;
392       }
393       else
394       {
395         positionSize.width = mPositionSize.height;
396         positionSize.height = mPositionSize.width;
397       }
398
399       mWindowBase->ResizeEglWindow( positionSize );
400       mResizeFinished = true;
401
402       DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::PreRender: Set resize\n" );
403     }
404   }
405
406   auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
407   if ( eglGraphics )
408   {
409     GlImplementation& mGLES = eglGraphics->GetGlesInterface();
410     mGLES.PreRender();
411   }
412
413   return true;
414 }
415
416 std::vector<int32_t> WindowRenderSurface::MergeRect( const Rect<int32_t>& damagedRect, int bufferAge )
417 {
418   std::vector<int32_t> mergedRectArray;
419   // merge bounding
420   int dx1 = mPositionSize.width, dx2 = 0, dy1 = mPositionSize.height, dy2 = 0;
421   int checkWidth = mPositionSize.width - TILE_SIZE;
422   int checkHeight = mPositionSize.height - TILE_SIZE;
423
424   dx1 = std::min( damagedRect.x, dx1 );
425   dx2 = std::max( damagedRect.x + damagedRect.width, dx2);
426   dy1 = std::min( damagedRect.y, dy1 );
427   dy2 = std::max( damagedRect.y + damagedRect.height, dy2 );
428
429   for( int j = 0; j <= bufferAge; j++ )
430   {
431     if( !mPreDamagedRect[j].IsEmpty() )
432     {
433       dx1 = std::min( mPreDamagedRect[j].x, dx1 );
434       dx2 = std::max( mPreDamagedRect[j].x + mPreDamagedRect[j].width, dx2);
435       dy1 = std::min( mPreDamagedRect[j].y, dy1 );
436       dy2 = std::max( mPreDamagedRect[j].y + mPreDamagedRect[j].height, dy2 );
437
438       if( dx1 < TILE_SIZE && dx2 > checkWidth && dy1 < TILE_SIZE && dy2 > checkHeight )
439       {
440         dx1 = 0, dx2 = mPositionSize.width, dy1 = 0, dy2 = mPositionSize.height;
441         break;
442       }
443     }
444   }
445
446   dx1 = TILE_SIZE * (dx1 / TILE_SIZE);
447   dy1 = TILE_SIZE * (dy1 / TILE_SIZE);
448   dx2 = TILE_SIZE * ((dx2 + TILE_SIZE - 1) / TILE_SIZE);
449   dy2 = TILE_SIZE * ((dy2 + TILE_SIZE - 1) / TILE_SIZE);
450
451   mergedRectArray.push_back( dx1 );
452   mergedRectArray.push_back( dy1 );
453   mergedRectArray.push_back( dx2 - dx1 );
454   mergedRectArray.push_back( dy2 - dy1 );
455
456   return mergedRectArray;
457 }
458
459
460 void WindowRenderSurface::SetDamagedRect( const Dali::DamagedRect& damagedRect, Dali::DamagedRect& mergedRect )
461 {
462   auto eglGraphics = static_cast<EglGraphics *>( mGraphics );
463   std::vector<int32_t> rectArray;
464   if( eglGraphics )
465   {
466     Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
467
468     rectArray = MergeRect( damagedRect, mBufferAge );
469
470     mPreDamagedRect[4] = std::move( mPreDamagedRect[3] );
471     mPreDamagedRect[3] = std::move( mPreDamagedRect[2] );
472     mPreDamagedRect[2] = std::move( mPreDamagedRect[1] );
473     mPreDamagedRect[1] = std::move( mPreDamagedRect[0] );
474     mPreDamagedRect[0] = std::move( damagedRect );
475
476     eglImpl.SetDamagedRect( rectArray, mEGLSurface );
477   }
478
479   if( !rectArray.empty() )
480   {
481     mergedRect.x = rectArray[0];
482     mergedRect.y = rectArray[1];
483     mergedRect.width = rectArray[2];
484     mergedRect.height = rectArray[3];
485   }
486 }
487
488 int32_t WindowRenderSurface::GetBufferAge()
489 {
490   int result = mBufferAge = 0;
491   auto eglGraphics = static_cast<EglGraphics *>( mGraphics );
492   if( eglGraphics )
493   {
494     Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
495     mBufferAge = eglImpl.GetBufferAge( mEGLSurface );;
496     result = ( mBufferAge != mPreBufferAge ) ? 0 : mBufferAge;
497     mPreBufferAge = mBufferAge;
498   }
499   return result;
500 }
501
502 void WindowRenderSurface::PostRender( bool renderToFbo, bool replacingSurface, bool resizingSurface )
503 {
504   // Inform the gl implementation that rendering has finished before informing the surface
505   auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
506   if ( eglGraphics )
507   {
508     GlImplementation& mGLES = eglGraphics->GetGlesInterface();
509     mGLES.PostRender();
510
511     if( renderToFbo )
512     {
513       mGLES.Flush();
514       mGLES.Finish();
515     }
516     else
517     {
518       if( resizingSurface )
519       {
520         if( !mRotationFinished )
521         {
522           DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::PostRender: Trigger rotation event\n" );
523
524           mRotationTrigger->Trigger();
525
526           if( mThreadSynchronization )
527           {
528             // Wait until the event-thread complete the rotation event processing
529             mThreadSynchronization->PostRenderWaitForCompletion();
530           }
531         }
532       }
533     }
534
535     Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
536
537     eglImpl.SwapBuffers( mEGLSurface );
538
539     if( mRenderNotification )
540     {
541       mRenderNotification->Trigger();
542     }
543   }
544 }
545
546 void WindowRenderSurface::StopRender()
547 {
548 }
549
550 void WindowRenderSurface::SetThreadSynchronization( ThreadSynchronizationInterface& threadSynchronization )
551 {
552   DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::SetThreadSynchronization: called\n" );
553
554   mThreadSynchronization = &threadSynchronization;
555 }
556
557 void WindowRenderSurface::ReleaseLock()
558 {
559   // Nothing to do.
560 }
561
562 Integration::RenderSurface::Type WindowRenderSurface::GetSurfaceType()
563 {
564   return RenderSurface::WINDOW_RENDER_SURFACE;
565 }
566
567 void WindowRenderSurface::MakeContextCurrent()
568 {
569   if ( mEGL != nullptr )
570   {
571     mEGL->MakeContextCurrent( mEGLSurface, mEGLContext );
572   }
573 }
574
575 Integration::DepthBufferAvailable WindowRenderSurface::GetDepthBufferRequired()
576 {
577   return mGraphics ? mGraphics->GetDepthBufferRequired() : Integration::DepthBufferAvailable::FALSE;
578 }
579
580 Integration::StencilBufferAvailable WindowRenderSurface::GetStencilBufferRequired()
581 {
582   return mGraphics ? mGraphics->GetStencilBufferRequired() : Integration::StencilBufferAvailable::FALSE;
583 }
584
585 void WindowRenderSurface::OutputTransformed()
586 {
587   int screenRotationAngle = mWindowBase->GetScreenRotationAngle();
588
589   if( mScreenRotationAngle != screenRotationAngle )
590   {
591     mScreenRotationAngle = screenRotationAngle;
592     mScreenRotationFinished = false;
593     mResizeFinished = false;
594
595     mOutputTransformedSignal.Emit();
596
597     DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::OutputTransformed: angle = %d screen rotation = %d\n", mRotationAngle, mScreenRotationAngle );
598   }
599   else
600   {
601     DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::OutputTransformed: Ignore output transform [%d]\n", mScreenRotationAngle );
602   }
603 }
604
605 void WindowRenderSurface::ProcessRotationRequest()
606 {
607   mRotationFinished = true;
608
609   mWindowBase->WindowRotationCompleted( mRotationAngle, mPositionSize.width, mPositionSize.height );
610
611   DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::ProcessRotationRequest: Rotation Done\n" );
612
613   if( mThreadSynchronization )
614   {
615     mThreadSynchronization->PostRenderComplete();
616   }
617 }
618
619 } // namespace Adaptor
620
621 } // namespace internal
622
623 } // namespace Dali