{
const int MINIMUM_DIMENSION_CHANGE( 1 ); ///< Minimum change for window to be considered to have moved
+const float FULL_UPDATE_RATIO( 0.8f ); ///< Force full update when the dirty area is larget than this ratio
#if defined(DEBUG_ENABLED)
Debug::Filter* gWindowRenderSurfaceLogFilter = Debug::Filter::New(Debug::Verbose, false, "LOG_WINDOW_RENDER_SURFACE");
#endif
+void MergeRects( Rect< int >& mergingRect, const std::vector< Rect< int > >& rects )
+{
+ uint32_t i = 0;
+ if( mergingRect.IsEmpty() )
+ {
+ for( ; i < rects.size(); i++ )
+ {
+ if( !rects[i].IsEmpty() )
+ {
+ mergingRect = rects[i];
+ break;
+ }
+ }
+ }
+
+ for( ; i < rects.size(); i++ )
+ {
+ mergingRect.Merge( rects[i] );
+ }
+}
+
+void InsertRects( WindowRenderSurface::DamagedRectsContainer& damagedRectsList, const std::vector< Rect< int > >& damagedRects )
+{
+ damagedRectsList.push_front( damagedRects );
+ if( damagedRectsList.size() > 4 ) // past triple buffers + current
+ {
+ damagedRectsList.pop_back();
+ }
+}
+
} // unnamed namespace
WindowRenderSurface::WindowRenderSurface( Dali::PositionSize positionSize, Any surface, bool isTransparent )
mColorDepth( isTransparent ? COLOR_DEPTH_32 : COLOR_DEPTH_24 ),
mOutputTransformedSignal(),
mFrameCallbackInfoContainer(),
+ mBufferDamagedRects(),
+ mMutex(),
mRotationAngle( 0 ),
mScreenRotationAngle( 0 ),
+ mDpiHorizontal( 0 ),
+ mDpiVertical( 0 ),
mOwnSurface( false ),
mRotationSupported( false ),
mRotationFinished( true ),
mScreenRotationFinished( true ),
- mResizeFinished( true ),
- mDpiHorizontal( 0 ),
- mDpiVertical( 0 )
+ mResizeFinished( true )
{
DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "Creating Window\n" );
Initialize( surface );
Dali::Integration::Scene scene = mScene.GetHandle();
if( scene )
{
+ bool needFrameRenderedTrigger = false;
+
scene.GetFrameRenderedCallback( callbacks );
if( !callbacks.empty() )
{
int frameRenderedSync = mWindowBase->CreateFrameRenderedSyncFence();
if( frameRenderedSync != -1 )
{
+ Dali::Mutex::ScopedLock lock( mMutex );
+
DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::PreRender: CreateFrameRenderedSyncFence [%d]\n", frameRenderedSync );
mFrameCallbackInfoContainer.push_back( std::unique_ptr< FrameCallbackInfo >( new FrameCallbackInfo( callbacks, frameRenderedSync ) ) );
- if( !mFrameRenderedTrigger )
- {
- mFrameRenderedTrigger = std::unique_ptr< TriggerEventInterface >( TriggerEventFactory::CreateTriggerEvent( MakeCallback( this, &WindowRenderSurface::ProcessFrameCallback ),
- TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER ) );
- }
- mFrameRenderedTrigger->Trigger();
+ needFrameRenderedTrigger = true;
}
else
{
int framePresentedSync = mWindowBase->CreateFramePresentedSyncFence();
if( framePresentedSync != -1 )
{
+ Dali::Mutex::ScopedLock lock( mMutex );
+
DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::PreRender: CreateFramePresentedSyncFence [%d]\n", framePresentedSync );
mFrameCallbackInfoContainer.push_back( std::unique_ptr< FrameCallbackInfo >( new FrameCallbackInfo( callbacks, framePresentedSync ) ) );
- if( !mFrameRenderedTrigger )
- {
- mFrameRenderedTrigger = std::unique_ptr< TriggerEventInterface >( TriggerEventFactory::CreateTriggerEvent( MakeCallback( this, &WindowRenderSurface::ProcessFrameCallback ),
- TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER ) );
- }
- mFrameRenderedTrigger->Trigger();
+ needFrameRenderedTrigger = true;
}
else
{
// Clear callbacks
callbacks.clear();
}
+
+ if( needFrameRenderedTrigger )
+ {
+ if( !mFrameRenderedTrigger )
+ {
+ mFrameRenderedTrigger = std::unique_ptr< TriggerEventInterface >( TriggerEventFactory::CreateTriggerEvent( MakeCallback( this, &WindowRenderSurface::ProcessFrameCallback ),
+ TriggerEventInterface::KEEP_ALIVE_AFTER_TRIGGER ) );
+ }
+ mFrameRenderedTrigger->Trigger();
+ }
}
MakeContextCurrent();
- auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
-
if( resizingSurface )
{
int totalAngle = (mRotationAngle + mScreenRotationAngle) % 360;
DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::PreRender: Set resize\n" );
}
- if (eglGraphics)
- {
- Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
- eglImpl.SetFullSwapNextFrame();
- }
+ SetFullSwapNextFrame();
}
- if (eglGraphics)
- {
- Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
- eglImpl.SetDamage( mEGLSurface, damagedRects, clippingRect );
- }
+ SetBufferDamagedRects( damagedRects, clippingRect );
return true;
}
}
}
- Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
- eglImpl.SwapBuffers( mEGLSurface, damagedRects );
+ SwapBuffers( damagedRects );
if( mRenderNotification )
{
void WindowRenderSurface::ProcessFrameCallback()
{
+ Dali::Mutex::ScopedLock lock( mMutex );
+
for( auto&& iter : mFrameCallbackInfoContainer )
{
if( !iter->fileDescriptorMonitor )
DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::OnFileDescriptorEventDispatched: Frame rendered [%d]\n", fileDescriptor );
- auto frameCallbackInfo = std::find_if( mFrameCallbackInfoContainer.begin(), mFrameCallbackInfoContainer.end(),
- [fileDescriptor]( std::unique_ptr< FrameCallbackInfo >& callbackInfo )
- {
- return callbackInfo->fileDescriptor == fileDescriptor;
- } );
- if( frameCallbackInfo != mFrameCallbackInfoContainer.end() )
+ std::unique_ptr< FrameCallbackInfo > callbackInfo;
+ {
+ Dali::Mutex::ScopedLock lock( mMutex );
+ auto frameCallbackInfo = std::find_if( mFrameCallbackInfoContainer.begin(), mFrameCallbackInfoContainer.end(),
+ [fileDescriptor]( std::unique_ptr< FrameCallbackInfo >& callbackInfo )
+ {
+ return callbackInfo->fileDescriptor == fileDescriptor;
+ } );
+ if( frameCallbackInfo != mFrameCallbackInfoContainer.end() )
+ {
+ callbackInfo = std::move( *frameCallbackInfo );
+
+ mFrameCallbackInfoContainer.erase( frameCallbackInfo );
+ }
+ }
+
+ // Call the connected callback
+ if( callbackInfo )
{
- // Call the connected callback
- for( auto&& iter : ( *frameCallbackInfo )->callbacks )
+ for( auto&& iter : ( callbackInfo )->callbacks )
{
CallbackBase::Execute( *( iter.first ), iter.second );
}
- mFrameCallbackInfoContainer.erase( frameCallbackInfo );
+ }
+}
+
+void WindowRenderSurface::SetBufferDamagedRects( const std::vector< Rect< int > >& damagedRects, Rect< int >& clippingRect )
+{
+ auto eglGraphics = static_cast< EglGraphics* >( mGraphics );
+ if ( eglGraphics )
+ {
+ Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
+ if( !eglImpl.IsPartialUpdateRequired() )
+ {
+ return;
+ }
+
+ Rect< int > surfaceRect( 0, 0, mPositionSize.width, mPositionSize.height );
+
+ if( mFullSwapNextFrame )
+ {
+ InsertRects( mBufferDamagedRects, std::vector< Rect< int > >( 1, surfaceRect ) );
+ clippingRect = Rect< int >();
+ return;
+ }
+
+ EGLint bufferAge = eglImpl.GetBufferAge( mEGLSurface );
+
+ // Buffer age 0 means the back buffer in invalid and requires full swap
+ if( !damagedRects.size() || bufferAge == 0 )
+ {
+ InsertRects( mBufferDamagedRects, std::vector< Rect< int > >( 1, surfaceRect ) );
+ clippingRect = Rect< int >();
+ return;
+ }
+
+ // We push current frame damaged rects here, zero index for current frame
+ InsertRects( mBufferDamagedRects, damagedRects );
+
+ // Merge damaged rects into clipping rect
+ auto bufferDamagedRects = mBufferDamagedRects.begin();
+ while( bufferAge-- >= 0 && bufferDamagedRects != mBufferDamagedRects.end() )
+ {
+ const std::vector< Rect< int > >& rects = *bufferDamagedRects++;
+ MergeRects( clippingRect, rects );
+ }
+
+ if( !clippingRect.Intersect( surfaceRect ) || clippingRect.Area() > surfaceRect.Area() * FULL_UPDATE_RATIO )
+ {
+ // clipping area too big or doesn't intersect surface rect
+ clippingRect = Rect< int >();
+ return;
+ }
+
+ std::vector< Rect< int > > damagedRegion;
+ damagedRegion.push_back( clippingRect );
+
+ eglImpl.SetDamageRegion( mEGLSurface, damagedRegion );
+ }
+}
+
+void WindowRenderSurface::SwapBuffers( const std::vector<Rect<int>>& damagedRects )
+{
+ auto eglGraphics = static_cast< EglGraphics* >( mGraphics );
+ if( eglGraphics )
+ {
+ Rect< int > surfaceRect( 0, 0, mPositionSize.width, mPositionSize.height );
+
+ Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
+
+ if( !eglImpl.IsPartialUpdateRequired() || mFullSwapNextFrame || !damagedRects.size() || (damagedRects[0].Area() > surfaceRect.Area() * FULL_UPDATE_RATIO) )
+ {
+ mFullSwapNextFrame = false;
+ eglImpl.SwapBuffers( mEGLSurface );
+ return;
+ }
+
+ mFullSwapNextFrame = false;
+
+ std::vector< Rect< int > > mergedRects = damagedRects;
+
+ // Merge intersecting rects, form an array of non intersecting rects to help driver a bit
+ // Could be optional and can be removed, needs to be checked with and without on platform
+ const int n = mergedRects.size();
+ for( int i = 0; i < n - 1; i++ )
+ {
+ if( mergedRects[i].IsEmpty() )
+ {
+ continue;
+ }
+
+ for( int j = i + 1; j < n; j++ )
+ {
+ if( mergedRects[j].IsEmpty() )
+ {
+ continue;
+ }
+
+ if( mergedRects[i].Intersects( mergedRects[j] ) )
+ {
+ mergedRects[i].Merge( mergedRects[j] );
+ mergedRects[j].width = 0;
+ mergedRects[j].height = 0;
+ }
+ }
+ }
+
+ int j = 0;
+ for( int i = 0; i < n; i++ )
+ {
+ if( !mergedRects[i].IsEmpty() )
+ {
+ mergedRects[j++] = mergedRects[i];
+ }
+ }
+
+ if( j != 0 )
+ {
+ mergedRects.resize( j );
+ }
+
+ if( !mergedRects.size() || ( mergedRects[0].Area() > surfaceRect.Area() * FULL_UPDATE_RATIO ) )
+ {
+ eglImpl.SwapBuffers( mEGLSurface );
+ }
+ else
+ {
+ eglImpl.SwapBuffers( mEGLSurface, mergedRects );
+ }
}
}