{
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 )
mThreadSynchronization( NULL ),
mRenderNotification( NULL ),
mRotationTrigger( NULL ),
+ mFrameRenderedTrigger(),
mGraphics( nullptr ),
mEGLSurface( nullptr ),
mEGLContext( nullptr ),
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 )
+ mDefaultScreenRotationAvailable( false )
{
DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "Creating Window\n" );
Initialize( surface );
{
mScreenRotationFinished = false;
mResizeFinished = false;
+ mDefaultScreenRotationAvailable = true;
+ DALI_LOG_RELEASE_INFO("WindowRenderSurface::Initialize, screen rotation is enabled, screen rotation angle:[%d]\n", mScreenRotationAngle );
}
}
bool WindowRenderSurface::PreRender( bool resizingSurface, const std::vector<Rect<int>>& damagedRects, Rect<int>& clippingRect )
{
- MakeContextCurrent();
+ Dali::Integration::Scene::FrameCallbackContainer callbacks;
- auto eglGraphics = static_cast<EglGraphics *>(mGraphics);
+ 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_RELEASE_INFO( "WindowRenderSurface::PreRender: CreateFrameRenderedSyncFence [%d]\n", frameRenderedSync );
+
+ mFrameCallbackInfoContainer.push_back( std::unique_ptr< FrameCallbackInfo >( new FrameCallbackInfo( callbacks, frameRenderedSync ) ) );
+
+ needFrameRenderedTrigger = true;
+ }
+ else
+ {
+ DALI_LOG_ERROR( "WindowRenderSurface::PreRender: CreateFrameRenderedSyncFence is failed\n" );
+ }
+
+ // Clear callbacks
+ callbacks.clear();
+ }
+
+ scene.GetFramePresentedCallback( callbacks );
+ if( !callbacks.empty() )
+ {
+ int framePresentedSync = mWindowBase->CreateFramePresentedSyncFence();
+ if( framePresentedSync != -1 )
+ {
+ Dali::Mutex::ScopedLock lock( mMutex );
+
+ DALI_LOG_RELEASE_INFO( "WindowRenderSurface::PreRender: CreateFramePresentedSyncFence [%d]\n", framePresentedSync );
- if( resizingSurface )
+ mFrameCallbackInfoContainer.push_back( std::unique_ptr< FrameCallbackInfo >( new FrameCallbackInfo( callbacks, framePresentedSync ) ) );
+
+ needFrameRenderedTrigger = true;
+ }
+ else
+ {
+ DALI_LOG_ERROR( "WindowRenderSurface::PreRender: CreateFramePresentedSyncFence is failed\n" );
+ }
+
+ // 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();
+
+ if( resizingSurface || mDefaultScreenRotationAvailable )
{
int totalAngle = (mRotationAngle + mScreenRotationAngle) % 360;
// Window rotate or screen rotate
if( !mRotationFinished || !mScreenRotationFinished )
{
- mWindowBase->SetEglWindowRotation( totalAngle );
mWindowBase->SetEglWindowBufferTransform( totalAngle );
// Reset only screen rotation flag
mScreenRotationFinished = true;
- DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::PreRender: Set rotation [%d] [%d]\n", mRotationAngle, mScreenRotationAngle );
+ DALI_LOG_RELEASE_INFO( "WindowRenderSurface::PreRender: Set rotation [%d] [%d]\n", mRotationAngle, mScreenRotationAngle );
}
// Only window rotate
mWindowBase->ResizeEglWindow( positionSize );
mResizeFinished = true;
- DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::PreRender: Set resize\n" );
+ DALI_LOG_RELEASE_INFO( "WindowRenderSurface::PreRender: Set resize\n" );
}
- if (eglGraphics)
- {
- Internal::Adaptor::EglImplementation& eglImpl = eglGraphics->GetEglImplementation();
- eglImpl.SetFullSwapNextFrame();
- }
+ SetFullSwapNextFrame();
+ mDefaultScreenRotationAvailable = false;
}
- 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 )
{
mOutputTransformedSignal.Emit();
- DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::OutputTransformed: angle = %d screen rotation = %d\n", mRotationAngle, mScreenRotationAngle );
+ DALI_LOG_RELEASE_INFO( "WindowRenderSurface::OutputTransformed: window = %d screen = %d\n", mRotationAngle, mScreenRotationAngle );
}
else
{
- DALI_LOG_INFO( gWindowRenderSurfaceLogFilter, Debug::Verbose, "WindowRenderSurface::OutputTransformed: Ignore output transform [%d]\n", mScreenRotationAngle );
+ DALI_LOG_RELEASE_INFO( "WindowRenderSurface::OutputTransformed: Ignore output transform [%d]\n", mScreenRotationAngle );
}
}
}
}
+void WindowRenderSurface::ProcessFrameCallback()
+{
+ Dali::Mutex::ScopedLock lock( mMutex );
+
+ for( auto&& iter : mFrameCallbackInfoContainer )
+ {
+ if( !iter->fileDescriptorMonitor )
+ {
+ iter->fileDescriptorMonitor = std::unique_ptr< FileDescriptorMonitor >( new FileDescriptorMonitor( iter->fileDescriptor,
+ MakeCallback( this, &WindowRenderSurface::OnFileDescriptorEventDispatched ), FileDescriptorMonitor::FD_READABLE ) );
+
+ DALI_LOG_RELEASE_INFO( "WindowRenderSurface::ProcessFrameCallback: Add handler [%d]\n", iter->fileDescriptor );
+ }
+ }
+}
+
+void WindowRenderSurface::OnFileDescriptorEventDispatched( FileDescriptorMonitor::EventType eventBitMask, int fileDescriptor )
+{
+ if( !( eventBitMask & FileDescriptorMonitor::FD_READABLE ) )
+ {
+ DALI_LOG_ERROR( "WindowRenderSurface::OnFileDescriptorEventDispatched: file descriptor error [%d]\n", eventBitMask );
+ close( fileDescriptor );
+ return;
+ }
+
+ DALI_LOG_RELEASE_INFO( "WindowRenderSurface::OnFileDescriptorEventDispatched: Frame rendered [%d]\n", fileDescriptor );
+
+ 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 )
+ {
+ for( auto&& iter : ( callbackInfo )->callbacks )
+ {
+ CallbackBase::Execute( *( iter.first ), iter.second );
+ }
+ }
+}
+
+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 );
+ }
+ }
+}
+
} // namespace Adaptor
} // namespace internal