Merge branch 'devel/tizen' into tizen
[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.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     mRotationTrigger = TriggerEventFactory::CreateTriggerEvent( MakeCallback( this, &WindowRenderSurface::ProcessRotationRequest ), TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER );
156   }
157
158   mPositionSize.width = width;
159   mPositionSize.height = height;
160
161   mRotationAngle = angle;
162   mRotationFinished = false;
163
164   mWindowBase->SetWindowRotationAngle( mRotationAngle );
165
166   DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::Rotate: angle = %d screen rotation = %d\n", mRotationAngle, mScreenRotationAngle );
167 }
168
169 WindowBase* WindowRenderSurface::GetWindowBase()
170 {
171   return mWindowBase.get();
172 }
173
174 WindowBase::OutputSignalType& WindowRenderSurface::OutputTransformedSignal()
175 {
176   return mOutputTransformedSignal;
177 }
178
179 PositionSize WindowRenderSurface::GetPositionSize() const
180 {
181   return mPositionSize;
182 }
183
184 void WindowRenderSurface::GetDpi( unsigned int& dpiHorizontal, unsigned int& dpiVertical )
185 {
186   if( mDpiHorizontal == 0 || mDpiVertical == 0 )
187   {
188     const char* environmentDpiHorizontal = std::getenv( DALI_ENV_DPI_HORIZONTAL );
189     mDpiHorizontal = environmentDpiHorizontal ? std::atoi( environmentDpiHorizontal ) : 0;
190
191     const char* environmentDpiVertical = std::getenv( DALI_ENV_DPI_VERTICAL );
192     mDpiVertical = environmentDpiVertical ? std::atoi( environmentDpiVertical ) : 0;
193
194     if( mDpiHorizontal == 0 || mDpiVertical == 0 )
195     {
196       mWindowBase->GetDpi( mDpiHorizontal, mDpiVertical );
197     }
198   }
199
200   dpiHorizontal = mDpiHorizontal;
201   dpiVertical = mDpiVertical;
202 }
203
204 int WindowRenderSurface::GetOrientation() const
205 {
206   return mWindowBase->GetOrientation();
207 }
208
209 void WindowRenderSurface::InitializeGraphics()
210 {
211
212   mGraphics = &mAdaptor->GetGraphicsInterface();
213
214   auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
215   mEGL = &eglGraphics->GetEglInterface();
216
217   if ( mEGLContext == NULL )
218   {
219     // Create the OpenGL context for this window
220     Internal::Adaptor::EglImplementation& eglImpl = static_cast<Internal::Adaptor::EglImplementation&>(*mEGL);
221     eglImpl.ChooseConfig(true, mColorDepth);
222     eglImpl.CreateWindowContext( mEGLContext );
223
224     // Create the OpenGL surface
225     CreateSurface();
226   }
227 }
228
229 void WindowRenderSurface::CreateSurface()
230 {
231   DALI_LOG_TRACE_METHOD( gWindowRenderSurfaceLogFilter );
232
233   int width, height;
234   if( mScreenRotationAngle == 0 || mScreenRotationAngle == 180 )
235   {
236     width = mPositionSize.width;
237     height = mPositionSize.height;
238   }
239   else
240   {
241     width = mPositionSize.height;
242     height = mPositionSize.width;
243   }
244
245   // Create the EGL window
246   EGLNativeWindowType window = mWindowBase->CreateEglWindow( width, height );
247
248   auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
249
250   Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
251   mEGLSurface = eglImpl.CreateSurfaceWindow( window, mColorDepth );
252
253   // Check rotation capability
254   mRotationSupported = mWindowBase->IsEglWindowRotationSupported();
255
256   DALI_LOG_RELEASE_INFO("WindowRenderSurface::CreateSurface: WinId (%d), w = %d h = %d angle = %d screen rotation = %d\n",
257       mWindowBase->GetNativeWindowId(), mPositionSize.width, mPositionSize.height, mRotationAngle, mScreenRotationAngle );
258 }
259
260 void WindowRenderSurface::DestroySurface()
261 {
262   DALI_LOG_TRACE_METHOD( gWindowRenderSurfaceLogFilter );
263
264   auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
265
266   DALI_LOG_RELEASE_INFO("WindowRenderSurface::DestroySurface: WinId (%d)\n", mWindowBase->GetNativeWindowId() );
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           if( mThreadSynchronization )
522           {
523             // Enable PostRender flag
524             mThreadSynchronization->PostRenderStarted();
525           }
526
527           DALI_LOG_RELEASE_INFO("WindowRenderSurface::PostRender: Trigger rotation event\n" );
528
529           mRotationTrigger->Trigger();
530
531           if( mThreadSynchronization )
532           {
533             // Wait until the event-thread complete the rotation event processing
534             mThreadSynchronization->PostRenderWaitForCompletion();
535           }
536         }
537       }
538     }
539
540     Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
541
542     eglImpl.SwapBuffers( mEGLSurface );
543
544     if( mRenderNotification )
545     {
546       mRenderNotification->Trigger();
547     }
548   }
549 }
550
551 void WindowRenderSurface::StopRender()
552 {
553 }
554
555 void WindowRenderSurface::SetThreadSynchronization( ThreadSynchronizationInterface& threadSynchronization )
556 {
557   DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::SetThreadSynchronization: called\n" );
558
559   mThreadSynchronization = &threadSynchronization;
560 }
561
562 void WindowRenderSurface::ReleaseLock()
563 {
564   // Nothing to do.
565 }
566
567 Integration::RenderSurface::Type WindowRenderSurface::GetSurfaceType()
568 {
569   return RenderSurface::WINDOW_RENDER_SURFACE;
570 }
571
572 void WindowRenderSurface::MakeContextCurrent()
573 {
574   if ( mEGL != nullptr )
575   {
576     mEGL->MakeContextCurrent( mEGLSurface, mEGLContext );
577   }
578 }
579
580 Integration::DepthBufferAvailable WindowRenderSurface::GetDepthBufferRequired()
581 {
582   return mGraphics ? mGraphics->GetDepthBufferRequired() : Integration::DepthBufferAvailable::FALSE;
583 }
584
585 Integration::StencilBufferAvailable WindowRenderSurface::GetStencilBufferRequired()
586 {
587   return mGraphics ? mGraphics->GetStencilBufferRequired() : Integration::StencilBufferAvailable::FALSE;
588 }
589
590 void WindowRenderSurface::OutputTransformed()
591 {
592   int screenRotationAngle = mWindowBase->GetScreenRotationAngle();
593
594   if( mScreenRotationAngle != screenRotationAngle )
595   {
596     mScreenRotationAngle = screenRotationAngle;
597     mScreenRotationFinished = false;
598     mResizeFinished = false;
599
600     mOutputTransformedSignal.Emit();
601
602     DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::OutputTransformed: angle = %d screen rotation = %d\n", mRotationAngle, mScreenRotationAngle );
603   }
604   else
605   {
606     DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::OutputTransformed: Ignore output transform [%d]\n", mScreenRotationAngle );
607   }
608 }
609
610 void WindowRenderSurface::ProcessRotationRequest()
611 {
612   mRotationFinished = true;
613
614   mWindowBase->WindowRotationCompleted( mRotationAngle, mPositionSize.width, mPositionSize.height );
615
616   DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::ProcessRotationRequest: Rotation Done\n" );
617
618   if( mThreadSynchronization )
619   {
620     mThreadSynchronization->PostRenderComplete();
621   }
622 }
623
624 } // namespace Adaptor
625
626 } // namespace internal
627
628 } // namespace Dali