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