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