X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali%2Finternal%2Frender%2Fcommon%2Frender-manager.cpp;h=09aa37334de654382a0921e43e16a76c0e7253d0;hb=0fce133ce4b5e3b643040960d4d66d23fc0253b1;hp=fbad2b303ad769acdbfb0f8ee70ddc899c1100ad;hpb=81e1315dc54867f504c974e64b04bfdd03d7977f;p=platform%2Fcore%2Fuifw%2Fdali-core.git diff --git a/dali/internal/render/common/render-manager.cpp b/dali/internal/render/common/render-manager.cpp old mode 100755 new mode 100644 index fbad2b3..09aa373 --- a/dali/internal/render/common/render-manager.cpp +++ b/dali/internal/render/common/render-manager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2020 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,28 +22,14 @@ #include // INTERNAL INCLUDES -#include -#include -#include -#include #include -#include #include #include -#include +#include #include #include #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include #include namespace Dali @@ -62,36 +48,52 @@ Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_REN } // unnamed namespace #endif -namespace -{ -const float partialUpdateRatio = 0.8f; // If the partial update area exceeds 80%, change to full update. - -/** - * @brief Find the intersection of two AABB rectangles. - * This is a logical AND operation. IE. The intersection is the area overlapped by both rectangles. - * @param[in] aabbA Rectangle A - * @param[in] aabbB Rectangle B - * @return The intersection of rectangle A & B (result is a rectangle) - */ -inline ClippingBox IntersectAABB( const ClippingBox& aabbA, const ClippingBox& aabbB ) +struct DirtyRect { - ClippingBox intersectionBox; + DirtyRect(Node* node, Render::Renderer* renderer, int frame, Rect& rect) + : node(node), + renderer(renderer), + frame(frame), + rect(rect), + visited(true) + { + } - // First calculate the largest starting positions in X and Y. - intersectionBox.x = std::max( aabbA.x, aabbB.x ); - intersectionBox.y = std::max( aabbA.y, aabbB.y ); + DirtyRect() + : node(nullptr), + renderer(nullptr), + frame(0), + rect(), + visited(true) + { + } - // Now calculate the smallest ending positions, and take the largest starting - // positions from the result, to get the width and height respectively. - // If the two boxes do not intersect at all, then we need a 0 width and height clipping area. - // We use max here to clamp both width and height to >= 0 for this use-case. - intersectionBox.width = std::max( std::min( aabbA.x + aabbA.width, aabbB.x + aabbB.width ) - intersectionBox.x, 0 ); - intersectionBox.height = std::max( std::min( aabbA.y + aabbA.height, aabbB.y + aabbB.height ) - intersectionBox.y, 0 ); + bool operator<(const DirtyRect& rhs) const + { + if (node == rhs.node) + { + if (renderer == rhs.renderer) + { + return frame > rhs.frame; // Most recent rects come first + } + else + { + return renderer < rhs.renderer; + } + } + else + { + return node < rhs.node; + } + } - return intersectionBox; -} + Node* node; + Render::Renderer* renderer; + int frame; -} + Rect rect; + bool visited; +}; /** * Structure to contain internal data @@ -104,15 +106,13 @@ struct RenderManager::Impl Integration::DepthBufferAvailable depthBufferAvailableParam, Integration::StencilBufferAvailable stencilBufferAvailableParam, Integration::PartialUpdateAvailable partialUpdateAvailableParam ) - : context( glAbstraction, &surfaceContextContainer ), + : context( glAbstraction, &sceneContextContainer ), currentContext( &context ), glAbstraction( glAbstraction ), glSyncAbstraction( glSyncAbstraction ), glContextHelperAbstraction( glContextHelperAbstraction ), renderQueue(), - instructions(), renderAlgorithms(), - backgroundColor( Dali::Stage::DEFAULT_BACKGROUND_COLOR ), frameCount( 0u ), renderBufferIndex( SceneGraphBuffers::INITIAL_UPDATE_BUFFER_INDEX ), defaultSurfaceRect(), @@ -125,7 +125,7 @@ struct RenderManager::Impl depthBufferAvailable( depthBufferAvailableParam ), stencilBufferAvailable( stencilBufferAvailableParam ), partialUpdateAvailable( partialUpdateAvailableParam ), - defaultSurfaceOrientation( 0 ) + itemsCheckSum(0) { // Create thread pool with just one thread ( there may be a need to create more threads in the future ). threadPool = std::unique_ptr( new Dali::ThreadPool() ); @@ -148,15 +148,26 @@ struct RenderManager::Impl mRenderTrackers.EraseObject( renderTracker ); } - Context* CreateSurfaceContext() + Context* CreateSceneContext() { - surfaceContextContainer.PushBack( new Context( glAbstraction ) ); - return surfaceContextContainer[ surfaceContextContainer.Count() - 1 ]; + sceneContextContainer.push_back( new Context( glAbstraction ) ); + return sceneContextContainer[ sceneContextContainer.size() - 1 ]; } - void DestroySurfaceContext( Context* surfaceContext ) + void DestroySceneContext( Context* sceneContext ) { - surfaceContextContainer.EraseObject( surfaceContext ); + auto iter = std::find( sceneContextContainer.begin(), sceneContextContainer.end(), sceneContext ); + if( iter != sceneContextContainer.end() ) + { + sceneContextContainer.erase( iter ); + } + } + + Context* ReplaceSceneContext( Context* oldSceneContext ) + { + Context* newContext = new Context( glAbstraction ); + std::replace( sceneContextContainer.begin(), sceneContextContainer.end(), oldSceneContext, newContext ); + return newContext; } void UpdateTrackers() @@ -171,18 +182,15 @@ struct RenderManager::Impl // programs are owned by context at the moment. Context context; ///< Holds the GL state of the share resource context Context* currentContext; ///< Holds the GL state of the current context for rendering - OwnerContainer< Context* > surfaceContextContainer; ///< List of owned contexts holding the GL state per surface + std::vector< Context* > sceneContextContainer; ///< List of owned contexts holding the GL state per scene Integration::GlAbstraction& glAbstraction; ///< GL abstraction Integration::GlSyncAbstraction& glSyncAbstraction; ///< GL sync abstraction Integration::GlContextHelperAbstraction& glContextHelperAbstraction; ///< GL context helper abstraction RenderQueue renderQueue; ///< A message queue for receiving messages from the update-thread. - // Render instructions describe what should be rendered during RenderManager::Render() - // Owned by RenderManager. Update manager updates instructions for the next frame while we render the current one - RenderInstructionContainer instructions; - Render::RenderAlgorithms renderAlgorithms; ///< The RenderAlgorithms object is used to action the renders required by a RenderInstruction + std::vector< SceneGraph::Scene* > sceneContainer; ///< List of pointers to the scene graph objects of the scenes - Vector4 backgroundColor; ///< The glClear color used at the beginning of each frame. + Render::RenderAlgorithms renderAlgorithms; ///< The RenderAlgorithms object is used to action the renders required by a RenderInstruction uint32_t frameCount; ///< The current frame count BufferIndex renderBufferIndex; ///< The index of the buffer to read from; this is opposite of the "update" buffer @@ -209,8 +217,8 @@ struct RenderManager::Impl std::unique_ptr threadPool; ///< The thread pool Vector boundTextures; ///< The textures bound for rendering Vector textureDependencyList; ///< The dependency list of binded textures - int defaultSurfaceOrientation; ///< defaultSurfaceOrientation for the default surface we are rendering to - + std::size_t itemsCheckSum; ///< The damaged render items checksum from previous prerender phase. + std::vector itemsDirtyRects; }; RenderManager* RenderManager::New( Integration::GlAbstraction& glAbstraction, @@ -276,6 +284,12 @@ void RenderManager::ContextDestroyed() { renderer->GlContextDestroyed(); } + + // inform scenes + for( auto&& scene : mImpl->sceneContainer ) + { + scene->GlContextDestroyed(); + } } void RenderManager::SetShaderSaver( ShaderSaver& upstream ) @@ -283,26 +297,11 @@ void RenderManager::SetShaderSaver( ShaderSaver& upstream ) mImpl->programController.SetShaderSaver( upstream ); } -RenderInstructionContainer& RenderManager::GetRenderInstructionContainer() -{ - return mImpl->instructions; -} - -void RenderManager::SetBackgroundColor( const Vector4& color ) -{ - mImpl->backgroundColor = color; -} - void RenderManager::SetDefaultSurfaceRect(const Rect& rect) { mImpl->defaultSurfaceRect = rect; } -void RenderManager::SetDefaultSurfaceOrientation( int orientation ) -{ - mImpl->defaultSurfaceOrientation = orientation; -} - void RenderManager::AddRenderer( OwnerPointer< Render::Renderer >& renderer ) { // Initialize the renderer as we are now in render thread @@ -375,14 +374,7 @@ void RenderManager::AddFrameBuffer( OwnerPointer< Render::FrameBuffer >& frameBu { Render::FrameBuffer* frameBufferPtr = frameBuffer.Release(); mImpl->frameBufferContainer.PushBack( frameBufferPtr ); - if ( frameBufferPtr->IsSurfaceBacked() ) - { - frameBufferPtr->Initialize( *mImpl->CreateSurfaceContext() ); - } - else - { - frameBufferPtr->Initialize( mImpl->context ); - } + frameBufferPtr->Initialize( mImpl->context ); } void RenderManager::RemoveFrameBuffer( Render::FrameBuffer* frameBuffer ) @@ -395,29 +387,50 @@ void RenderManager::RemoveFrameBuffer( Render::FrameBuffer* frameBuffer ) if ( iter == frameBuffer ) { frameBuffer->Destroy( mImpl->context ); - - if ( frameBuffer->IsSurfaceBacked() ) - { - auto surfaceFrameBuffer = static_cast( frameBuffer ); - mImpl->DestroySurfaceContext( surfaceFrameBuffer->GetContext() ); - } - mImpl->frameBufferContainer.Erase( &iter ); // frameBuffer found; now destroy it break; } } } +void RenderManager::InitializeScene( SceneGraph::Scene* scene ) +{ + scene->Initialize( *mImpl->CreateSceneContext() ); + mImpl->sceneContainer.push_back( scene ); +} -void RenderManager::AttachColorTextureToFrameBuffer( Render::FrameBuffer* frameBuffer, Render::Texture* texture, uint32_t mipmapLevel, uint32_t layer ) +void RenderManager::UninitializeScene( SceneGraph::Scene* scene ) { - if ( !frameBuffer->IsSurfaceBacked() ) + mImpl->DestroySceneContext( scene->GetContext() ); + + auto iter = std::find( mImpl->sceneContainer.begin(), mImpl->sceneContainer.end(), scene ); + if( iter != mImpl->sceneContainer.end() ) { - auto textureFrameBuffer = static_cast( frameBuffer ); - textureFrameBuffer->AttachColorTexture( mImpl->context, texture, mipmapLevel, layer ); + mImpl->sceneContainer.erase( iter ); } } +void RenderManager::SurfaceReplaced( SceneGraph::Scene* scene ) +{ + Context* newContext = mImpl->ReplaceSceneContext( scene->GetContext() ); + scene->Initialize( *newContext ); +} + +void RenderManager::AttachColorTextureToFrameBuffer( Render::FrameBuffer* frameBuffer, Render::Texture* texture, uint32_t mipmapLevel, uint32_t layer ) +{ + frameBuffer->AttachColorTexture( mImpl->context, texture, mipmapLevel, layer ); +} + +void RenderManager::AttachDepthTextureToFrameBuffer( Render::FrameBuffer* frameBuffer, Render::Texture* texture, uint32_t mipmapLevel ) +{ + frameBuffer->AttachDepthTexture( mImpl->context, texture, mipmapLevel ); +} + +void RenderManager::AttachDepthStencilTextureToFrameBuffer( Render::FrameBuffer* frameBuffer, Render::Texture* texture, uint32_t mipmapLevel ) +{ + frameBuffer->AttachDepthStencilTexture( mImpl->context, texture, mipmapLevel ); +} + void RenderManager::AddPropertyBuffer( OwnerPointer< Render::PropertyBuffer >& propertyBuffer ) { mImpl->propertyBufferContainer.PushBack( propertyBuffer.Release() ); @@ -503,7 +516,7 @@ ProgramCache* RenderManager::GetProgramCache() return &(mImpl->programController); } -void RenderManager::Render( Integration::RenderStatus& status, bool forceClear, bool uploadOnly ) +void RenderManager::PreRender( Integration::RenderStatus& status, bool forceClear, bool uploadOnly ) { DALI_PRINT_RENDER_START( mImpl->renderBufferIndex ); @@ -516,7 +529,12 @@ void RenderManager::Render( Integration::RenderStatus& status, bool forceClear, // Process messages queued during previous update mImpl->renderQueue.ProcessMessages( mImpl->renderBufferIndex ); - const uint32_t count = mImpl->instructions.Count( mImpl->renderBufferIndex ); + uint32_t count = 0u; + for( uint32_t i = 0; i < mImpl->sceneContainer.size(); ++i ) + { + count += mImpl->sceneContainer[i]->GetRenderInstructions().Count( mImpl->renderBufferIndex ); + } + const bool haveInstructions = count > 0u; DALI_LOG_INFO( gLogFilter, Debug::General, @@ -530,12 +548,6 @@ void RenderManager::Render( Integration::RenderStatus& status, bool forceClear, { DALI_LOG_INFO( gLogFilter, Debug::General, "Render: Processing\n" ); - if ( !uploadOnly ) - { - // Mark that we will require a post-render step to be performed (includes swap-buffers). - status.SetNeedsPostRender( true ); - } - // Switch to the shared context if ( mImpl->currentContext != &mImpl->context ) { @@ -551,353 +563,394 @@ void RenderManager::Render( Integration::RenderStatus& status, bool forceClear, } // Upload the geometries - for( uint32_t i = 0; i < count; ++i ) + for( uint32_t i = 0; i < mImpl->sceneContainer.size(); ++i ) { - RenderInstruction& instruction = mImpl->instructions.At( mImpl->renderBufferIndex, i ); - - const Matrix* viewMatrix = instruction.GetViewMatrix( mImpl->renderBufferIndex ); - const Matrix* projectionMatrix = instruction.GetProjectionMatrix( mImpl->renderBufferIndex ); + RenderInstructionContainer& instructions = mImpl->sceneContainer[i]->GetRenderInstructions(); + for ( uint32_t j = 0; j < instructions.Count( mImpl->renderBufferIndex ); ++j ) + { + RenderInstruction& instruction = instructions.At( mImpl->renderBufferIndex, j ); - DALI_ASSERT_DEBUG( viewMatrix ); - DALI_ASSERT_DEBUG( projectionMatrix ); + const Matrix* viewMatrix = instruction.GetViewMatrix( mImpl->renderBufferIndex ); + const Matrix* projectionMatrix = instruction.GetProjectionMatrix( mImpl->renderBufferIndex ); - if( viewMatrix && projectionMatrix ) - { - const RenderListContainer::SizeType renderListCount = instruction.RenderListCount(); + DALI_ASSERT_DEBUG( viewMatrix ); + DALI_ASSERT_DEBUG( projectionMatrix ); - // Iterate through each render list. - for( RenderListContainer::SizeType index = 0; index < renderListCount; ++index ) + if( viewMatrix && projectionMatrix ) { - const RenderList* renderList = instruction.GetRenderList( index ); + const RenderListContainer::SizeType renderListCount = instruction.RenderListCount(); - if( renderList && !renderList->IsEmpty() ) + // Iterate through each render list. + for( RenderListContainer::SizeType index = 0; index < renderListCount; ++index ) { - const std::size_t itemCount = renderList->Count(); - for( uint32_t itemIndex = 0u; itemIndex < itemCount; ++itemIndex ) + const RenderList* renderList = instruction.GetRenderList( index ); + + if( renderList && !renderList->IsEmpty() ) { - const RenderItem& item = renderList->GetItem( itemIndex ); - if( DALI_LIKELY( item.mRenderer ) ) + const std::size_t itemCount = renderList->Count(); + for( uint32_t itemIndex = 0u; itemIndex < itemCount; ++itemIndex ) { - item.mRenderer->Upload( *mImpl->currentContext ); + const RenderItem& item = renderList->GetItem( itemIndex ); + if( DALI_LIKELY( item.mRenderer ) ) + { + item.mRenderer->Upload( *mImpl->currentContext ); + } } } } } } } + } +} - if ( !uploadOnly ) - { - for( uint32_t i = 0; i < count; ++i ) - { - RenderInstruction& instruction = mImpl->instructions.At( mImpl->renderBufferIndex, i ); - - DoRender( instruction ); - } - - if ( mImpl->currentContext->IsSurfacelessContextSupported() ) - { - mImpl->glContextHelperAbstraction.MakeSurfacelessContextCurrent(); - } +void RenderManager::PreRender( Integration::Scene& scene, std::vector>& damagedRects ) +{ + if (mImpl->partialUpdateAvailable != Integration::PartialUpdateAvailable::TRUE) + { + return; + } - GLenum attachments[] = { GL_DEPTH, GL_STENCIL }; - mImpl->context.InvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments); - for ( auto&& context : mImpl->surfaceContextContainer ) - { - context->InvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments); - } + class DamagedRectsCleaner + { + public: + DamagedRectsCleaner(std::vector>& damagedRects) + : mDamagedRects(damagedRects), + mCleanOnReturn(true) + { } - //Notify RenderGeometries that rendering has finished - for ( auto&& iter : mImpl->geometryContainer ) + void SetCleanOnReturn(bool cleanOnReturn) { - iter->OnRenderFinished(); + mCleanOnReturn = cleanOnReturn; } - } - else - { - DALI_LOG_RELEASE_INFO( "RenderManager::Render: Skip rendering [%d, %d, %d]\n", haveInstructions, mImpl->lastFrameWasRendered, forceClear ); - } - mImpl->UpdateTrackers(); + ~DamagedRectsCleaner() + { + if (mCleanOnReturn) + { + mDamagedRects.clear(); + } + } - // If this frame was rendered due to instructions existing, we mark this so we know to clear the next frame. - mImpl->lastFrameWasRendered = haveInstructions; + private: + std::vector>& mDamagedRects; + bool mCleanOnReturn; + }; - /** - * The rendering has finished; swap to the next buffer. - * Ideally the update has just finished using this buffer; otherwise the render thread - * should block until the update has finished. - */ - mImpl->renderBufferIndex = (0 != mImpl->renderBufferIndex) ? 0 : 1; + Rect surfaceRect = Rect(0, 0, static_cast( scene.GetSize().width ), static_cast( scene.GetSize().height )); - DALI_PRINT_RENDER_END(); -} + // Clean collected dirty/damaged rects on exit if 3d layer or 3d node or other conditions. + DamagedRectsCleaner damagedRectCleaner(damagedRects); -bool GetDamagedRect( Rect &viewportRect, RenderInstruction& instruction, Rect &damagedRect ) -{ - // merge bounding - int dx1 = viewportRect.width, dx2 = 0, dy1 = viewportRect.height, dy2 = 0; - int checkWidth = static_cast( static_cast( viewportRect.width ) * partialUpdateRatio ); - int checkHeight = static_cast( static_cast( viewportRect.height ) * partialUpdateRatio ); - Rect screenRect; + // Mark previous dirty rects in the sorted array. The array is already sorted by node and renderer, frame number. + // so you don't need to sort: std::stable_sort(mImpl->itemsDirtyRects.begin(), mImpl->itemsDirtyRects.end()); + for (DirtyRect& dirtyRect : mImpl->itemsDirtyRects) + { + dirtyRect.visited = false; + } - bool isPartialUpdate = false; + Internal::Scene& sceneInternal = GetImplementation(scene); + SceneGraph::Scene* sceneObject = sceneInternal.GetSceneObject(); + uint32_t count = sceneObject->GetRenderInstructions().Count( mImpl->renderBufferIndex ); + for (uint32_t i = 0; i < count; ++i) + { + RenderInstruction& instruction = sceneObject->GetRenderInstructions().At( mImpl->renderBufferIndex, i ); - const RenderListContainer::SizeType renderListCount = instruction.RenderListCount(); - // Iterate through each render list. + if (instruction.mFrameBuffer) + { + return; // TODO: reset, we don't deal with render tasks with framebuffers (for now) + } - for( RenderListContainer::SizeType index = 0; index < renderListCount; ++index ) - { - const RenderList* renderList = instruction.GetRenderList( index ); + const Camera* camera = instruction.GetCamera(); + if (camera->mType == Camera::DEFAULT_TYPE && camera->mTargetPosition == Camera::DEFAULT_TARGET_POSITION) + { + const Node* node = instruction.GetCamera()->GetNode(); + if (node) + { + Vector3 position; + Vector3 scale; + Quaternion orientation; + node->GetWorldMatrix(mImpl->renderBufferIndex).GetTransformComponents(position, orientation, scale); + + Vector3 orientationAxis; + Radian orientationAngle; + orientation.ToAxisAngle( orientationAxis, orientationAngle ); + + if (position.x > Math::MACHINE_EPSILON_10000 || + position.y > Math::MACHINE_EPSILON_10000 || + orientationAxis != Vector3(0.0f, 1.0f, 0.0f) || + orientationAngle != ANGLE_180 || + scale != Vector3(1.0f, 1.0f, 1.0f)) + { + return; + } + } + } + else + { + return; + } - if( renderList && !renderList->IsEmpty() && renderList->IsPartialUpdateEnabled() ) + Rect viewportRect; + if (instruction.mIsViewportSet) { - const std::size_t itemCount = renderList->Count(); - for( uint32_t itemIndex = 0u; itemIndex < itemCount; ++itemIndex ) + const int32_t y = (surfaceRect.height - instruction.mViewport.height) - instruction.mViewport.y; + viewportRect.Set(instruction.mViewport.x, y, instruction.mViewport.width, instruction.mViewport.height); + if (viewportRect.IsEmpty() || !viewportRect.IsValid()) { - const RenderItem& item = renderList->GetItem( itemIndex ); + return; // just skip funny use cases for now, empty viewport means it is set somewhere else + } + } + else + { + viewportRect = surfaceRect; + } - if( item.mPartialUpdateEnabled ) + const Matrix* viewMatrix = instruction.GetViewMatrix(mImpl->renderBufferIndex); + const Matrix* projectionMatrix = instruction.GetProjectionMatrix(mImpl->renderBufferIndex); + if (viewMatrix && projectionMatrix) + { + const RenderListContainer::SizeType count = instruction.RenderListCount(); + for (RenderListContainer::SizeType index = 0u; index < count; ++index) + { + const RenderList* renderList = instruction.GetRenderList( index ); + if (renderList && !renderList->IsEmpty()) { - isPartialUpdate = true; + const std::size_t count = renderList->Count(); + for (uint32_t index = 0u; index < count; ++index) + { + RenderItem& item = renderList->GetItem( index ); + // If the item does 3D transformation, do early exit and clean the damaged rect array + if (item.mUpdateSize == Vector3::ZERO) + { + return; + } - screenRect = item.CalculateViewportSpaceAABB( viewportRect.width, viewportRect.height, true ); + Rect rect; + DirtyRect dirtyRect(item.mNode, item.mRenderer, mImpl->frameCount, rect); + // If the item refers to updated node or renderer. + if (item.mIsUpdated || + (item.mNode && + (item.mNode->Updated() || (item.mRenderer && item.mRenderer->Updated(mImpl->renderBufferIndex, item.mNode))))) + { + item.mIsUpdated = false; + item.mNode->SetUpdated(false); - dx1 = std::min( screenRect.x, dx1 ); - dx2 = std::max( screenRect.x + screenRect.width, dx2); - dy1 = std::min( screenRect.y, dy1 ); - dy2 = std::max( screenRect.y + screenRect.height, dy2 ); + rect = item.CalculateViewportSpaceAABB(item.mUpdateSize, viewportRect.width, viewportRect.height); + if (rect.IsValid() && rect.Intersect(viewportRect) && !rect.IsEmpty()) + { + const int left = rect.x; + const int top = rect.y; + const int right = rect.x + rect.width; + const int bottom = rect.y + rect.height; + rect.x = (left / 16) * 16; + rect.y = (top / 16) * 16; + rect.width = ((right + 16) / 16) * 16 - rect.x; + rect.height = ((bottom + 16) / 16) * 16 - rect.y; + + // Found valid dirty rect. + // 1. Insert it in the sorted array of the dirty rects. + // 2. Mark the related dirty rects as visited so they will not be removed below. + // 3. Keep only last 3 dirty rects for the same node and renderer (Tizen uses 3 back buffers, Ubuntu 1). + dirtyRect.rect = rect; + auto dirtyRectPos = std::lower_bound(mImpl->itemsDirtyRects.begin(), mImpl->itemsDirtyRects.end(), dirtyRect); + dirtyRectPos = mImpl->itemsDirtyRects.insert(dirtyRectPos, dirtyRect); + + int c = 1; + while (++dirtyRectPos != mImpl->itemsDirtyRects.end()) + { + if (dirtyRectPos->node != item.mNode || dirtyRectPos->renderer != item.mRenderer) + { + break; + } + + dirtyRectPos->visited = true; + Rect& dirtRect = dirtyRectPos->rect; + rect.Merge(dirtRect); + + c++; + if (c > 3) // no more then 3 previous rects + { + mImpl->itemsDirtyRects.erase(dirtyRectPos); + break; + } + } + + damagedRects.push_back(rect); + } + } + else + { + // 1. The item is not dirty, the node and renderer referenced by the item are still exist. + // 2. Mark the related dirty rects as visited so they will not be removed below. + auto dirtyRectPos = std::lower_bound(mImpl->itemsDirtyRects.begin(), mImpl->itemsDirtyRects.end(), dirtyRect); + while (dirtyRectPos != mImpl->itemsDirtyRects.end()) + { + if (dirtyRectPos->node != item.mNode || dirtyRectPos->renderer != item.mRenderer) + { + break; + } - if( ( dx2 - dx1 ) > checkWidth && ( dy2 - dy1 ) > checkHeight ) - { - return false; + dirtyRectPos->visited = true; + dirtyRectPos++; + } + } } } } } } - if( isPartialUpdate ) + // Check removed nodes or removed renderers dirty rects + auto i = mImpl->itemsDirtyRects.begin(); + auto j = mImpl->itemsDirtyRects.begin(); + while (i != mImpl->itemsDirtyRects.end()) { - if( dx1 < 0.0f ) - { - dx1 = 0.0f; - } - if( dy1 < 0.0f ) - { - dy1 = 0.0f; - } - if( dx2 > viewportRect.width ) + if (i->visited) { - dx2 = viewportRect.width; + *j++ = *i; } - if( dy2 > viewportRect.height ) + else { - dy2 = viewportRect.height; + Rect& dirtRect = i->rect; + damagedRects.push_back(dirtRect); } - - damagedRect.x = dx1; - damagedRect.y = dy1; - damagedRect.width = dx2 - dx1; - damagedRect.height = dy2 - dy1; + i++; } - return isPartialUpdate; + mImpl->itemsDirtyRects.resize(j - mImpl->itemsDirtyRects.begin()); + damagedRectCleaner.SetCleanOnReturn(false); } -void RenderManager::DoRender( RenderInstruction& instruction ) +void RenderManager::RenderScene( Integration::RenderStatus& status, Integration::Scene& scene, bool renderToFbo ) { - Rect viewportRect; - Vector4 clearColor; - bool isPartialUpdate = false; - Dali::DamagedRect damagedRect; - Dali::DamagedRect mergedRect; - Dali::ClippingBox scissorBox; - Dali::ClippingBox intersectRect; - - if ( instruction.mIsClearColorSet ) - { - clearColor = instruction.mClearColor; - } - else - { - clearColor = Dali::RenderTask::DEFAULT_CLEAR_COLOR; - } + Rect clippingRect; + RenderScene( status, scene, renderToFbo, clippingRect); +} - Rect surfaceRect = mImpl->defaultSurfaceRect; - int surfaceOrientation = mImpl->defaultSurfaceOrientation; - Vector4 backgroundColor = mImpl->backgroundColor; - Integration::DepthBufferAvailable depthBufferAvailable = mImpl->depthBufferAvailable; - Integration::StencilBufferAvailable stencilBufferAvailable = mImpl->stencilBufferAvailable; - Integration::PartialUpdateAvailable partialUpdateAvailable = mImpl->partialUpdateAvailable; +void RenderManager::RenderScene( Integration::RenderStatus& status, Integration::Scene& scene, bool renderToFbo, Rect& clippingRect ) +{ + Internal::Scene& sceneInternal = GetImplementation( scene ); + SceneGraph::Scene* sceneObject = sceneInternal.GetSceneObject(); - Render::SurfaceFrameBuffer* surfaceFrameBuffer = nullptr; - if ( instruction.mFrameBuffer != 0 ) + uint32_t count = sceneObject->GetRenderInstructions().Count( mImpl->renderBufferIndex ); + + for( uint32_t i = 0; i < count; ++i ) { - if ( instruction.mFrameBuffer->IsSurfaceBacked() ) + RenderInstruction& instruction = sceneObject->GetRenderInstructions().At( mImpl->renderBufferIndex, i ); + + if ( ( renderToFbo && !instruction.mFrameBuffer ) || ( !renderToFbo && instruction.mFrameBuffer ) ) { - surfaceFrameBuffer = static_cast( instruction.mFrameBuffer ); + continue; // skip + } + + // Mark that we will require a post-render step to be performed (includes swap-buffers). + status.SetNeedsPostRender( true ); + + Rect viewportRect; + Vector4 clearColor; - if ( !surfaceFrameBuffer->IsSurfaceValid() ) + if ( instruction.mIsClearColorSet ) + { + clearColor = instruction.mClearColor; + } + else + { + clearColor = Dali::RenderTask::DEFAULT_CLEAR_COLOR; + } + + Rect surfaceRect = mImpl->defaultSurfaceRect; + Integration::DepthBufferAvailable depthBufferAvailable = mImpl->depthBufferAvailable; + Integration::StencilBufferAvailable stencilBufferAvailable = mImpl->stencilBufferAvailable; + + if ( instruction.mFrameBuffer ) + { + // offscreen buffer + if ( mImpl->currentContext != &mImpl->context ) { - // Skip rendering the frame buffer if the render surface becomes invalid - return; - } + // Switch to shared context for off-screen buffer + mImpl->currentContext = &mImpl->context; + if ( mImpl->currentContext->IsSurfacelessContextSupported() ) + { + mImpl->glContextHelperAbstraction.MakeSurfacelessContextCurrent(); + } + + // Clear the current cached program when the context is switched + mImpl->programController.ClearCurrentProgram(); + } + } + else + { if ( mImpl->currentContext->IsSurfacelessContextSupported() ) { - Context* surfaceContext = surfaceFrameBuffer->GetContext(); - if ( mImpl->currentContext != surfaceContext ) + if ( mImpl->currentContext != sceneObject->GetContext() ) { // Switch the correct context if rendering to a surface - mImpl->currentContext = surfaceContext; - surfaceFrameBuffer->MakeContextCurrent(); + mImpl->currentContext = sceneObject->GetContext(); // Clear the current cached program when the context is switched mImpl->programController.ClearCurrentProgram(); } } - surfaceRect = Rect( 0, 0, static_cast( surfaceFrameBuffer->GetWidth() ), static_cast( surfaceFrameBuffer->GetHeight() ) ); - backgroundColor = surfaceFrameBuffer->GetBackgroundColor(); - } - else - { - // Switch to shared context for off-screen buffer - mImpl->currentContext = &mImpl->context; - - if ( mImpl->currentContext->IsSurfacelessContextSupported() ) - { - mImpl->glContextHelperAbstraction.MakeSurfacelessContextCurrent(); - } + surfaceRect = Rect( 0, 0, static_cast( scene.GetSize().width ), static_cast( scene.GetSize().height ) ); } - } - DALI_ASSERT_DEBUG( mImpl->currentContext->IsGlContextCreated() ); + DALI_ASSERT_DEBUG( mImpl->currentContext->IsGlContextCreated() ); - // reset the program matrices for all programs once per frame - // this ensures we will set view and projection matrix once per program per camera - mImpl->programController.ResetProgramMatrices(); + // reset the program matrices for all programs once per frame + // this ensures we will set view and projection matrix once per program per camera + mImpl->programController.ResetProgramMatrices(); - if( instruction.mFrameBuffer ) - { - instruction.mFrameBuffer->Bind( *mImpl->currentContext ); - - if ( !instruction.mFrameBuffer->IsSurfaceBacked() ) + if( instruction.mFrameBuffer ) { + instruction.mFrameBuffer->Bind( *mImpl->currentContext ); + // For each offscreen buffer, update the dependency list with the new texture id used by this frame buffer. - Render::TextureFrameBuffer* textureFrameBuffer = static_cast( instruction.mFrameBuffer ); - mImpl->textureDependencyList.PushBack( textureFrameBuffer->GetTextureId() ); + for (unsigned int i0 = 0, i1 = instruction.mFrameBuffer->GetColorAttachmentCount(); i0 < i1; ++i0) + { + mImpl->textureDependencyList.PushBack( instruction.mFrameBuffer->GetTextureId(i0) ); + } } - } - else - { - mImpl->currentContext->BindFramebuffer( GL_FRAMEBUFFER, 0u ); - } - - - if( surfaceFrameBuffer && - partialUpdateAvailable == Integration::PartialUpdateAvailable::TRUE ) - { - const RenderListContainer::SizeType renderListCount = instruction.RenderListCount(); - // Iterate through each render list. - if( surfaceFrameBuffer->IsPartialUpdateEnabled() ) + else { - isPartialUpdate = GetDamagedRect( surfaceRect, instruction, damagedRect ) ; + mImpl->currentContext->BindFramebuffer( GL_FRAMEBUFFER, 0u ); } - if( !isPartialUpdate ) + if ( !instruction.mFrameBuffer ) { - damagedRect = surfaceRect; + mImpl->currentContext->Viewport( surfaceRect.x, + surfaceRect.y, + surfaceRect.width, + surfaceRect.height ); } - surfaceFrameBuffer->SetDamagedRect( damagedRect, mergedRect ); + // Clear the entire color, depth and stencil buffers for the default framebuffer, if required. + // It is important to clear all 3 buffers when they are being used, for performance on deferred renderers + // e.g. previously when the depth & stencil buffers were NOT cleared, it caused the DDK to exceed a "vertex count limit", + // and then stall. That problem is only noticeable when rendering a large number of vertices per frame. + GLbitfield clearMask = GL_COLOR_BUFFER_BIT; - if( mergedRect.IsEmpty() ) - { - isPartialUpdate = false; - } - else + mImpl->currentContext->ColorMask( true ); + + if( depthBufferAvailable == Integration::DepthBufferAvailable::TRUE ) { - scissorBox.x = mergedRect.x; - scissorBox.y = mergedRect.y; - scissorBox.width = mergedRect.width; - scissorBox.height = mergedRect.height; + mImpl->currentContext->DepthMask( true ); + clearMask |= GL_DEPTH_BUFFER_BIT; } - } - if ( surfaceFrameBuffer ) - { - mImpl->currentContext->Viewport( surfaceRect.x, - surfaceRect.y, - surfaceRect.width, - surfaceRect.height ); - - - mImpl->currentContext->ClearColor( backgroundColor.r, - backgroundColor.g, - backgroundColor.b, - backgroundColor.a ); - } - - // Clear the entire color, depth and stencil buffers for the default framebuffer, if required. - // It is important to clear all 3 buffers when they are being used, for performance on deferred renderers - // e.g. previously when the depth & stencil buffers were NOT cleared, it caused the DDK to exceed a "vertex count limit", - // and then stall. That problem is only noticeable when rendering a large number of vertices per frame. - if( isPartialUpdate ) - { - mImpl->currentContext->SetScissorTest( true ); - mImpl->currentContext->Scissor( scissorBox.x, scissorBox.y, scissorBox.width, scissorBox.height ); - } - else - { - mImpl->currentContext->SetScissorTest( false ); - } - - GLbitfield clearMask = GL_COLOR_BUFFER_BIT; - - mImpl->currentContext->ColorMask( true ); - - if( depthBufferAvailable == Integration::DepthBufferAvailable::TRUE ) - { - mImpl->currentContext->DepthMask( true ); - clearMask |= GL_DEPTH_BUFFER_BIT; - } - - if( stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE) - { - mImpl->currentContext->ClearStencil( 0 ); - mImpl->currentContext->StencilMask( 0xFF ); // 8 bit stencil mask, all 1's - clearMask |= GL_STENCIL_BUFFER_BIT; - } - - mImpl->currentContext->Clear( clearMask, Context::FORCE_CLEAR ); - - if( isPartialUpdate ) - { - mImpl->currentContext->SetScissorTest( false ); - } - - - if( !instruction.mIgnoreRenderToFbo && ( instruction.mFrameBuffer != 0 ) ) - { - if ( instruction.mFrameBuffer->IsSurfaceBacked() ) // Surface rendering + if( stencilBufferAvailable == Integration::StencilBufferAvailable::TRUE) { - if ( instruction.mIsViewportSet ) - { - // For glViewport the lower-left corner is (0,0) - // For glViewport the lower-left corner is (0,0) - const int32_t y = ( surfaceRect.height - instruction.mViewport.height ) - instruction.mViewport.y; - viewportRect.Set( instruction.mViewport.x, y, instruction.mViewport.width, instruction.mViewport.height ); - } - else - { - viewportRect = surfaceRect; - } + mImpl->currentContext->ClearStencil( 0 ); + mImpl->currentContext->StencilMask( 0xFF ); // 8 bit stencil mask, all 1's + clearMask |= GL_STENCIL_BUFFER_BIT; } - else // Offscreen buffer rendering + + if( !instruction.mIgnoreRenderToFbo && ( instruction.mFrameBuffer != 0 ) ) { + // Offscreen buffer rendering if ( instruction.mIsViewportSet ) { // For glViewport the lower-left corner is (0,0) @@ -908,156 +961,207 @@ void RenderManager::DoRender( RenderInstruction& instruction ) { viewportRect.Set( 0, 0, instruction.mFrameBuffer->GetWidth(), instruction.mFrameBuffer->GetHeight() ); } - surfaceOrientation = 0; } - } - else // No Offscreen frame buffer rendering - { - // Check whether a viewport is specified, otherwise the full surface size is used - if ( instruction.mFrameBuffer != 0 ) + else // No Offscreen frame buffer rendering { + // Check whether a viewport is specified, otherwise the full surface size is used if ( instruction.mIsViewportSet ) { // For glViewport the lower-left corner is (0,0) - const int32_t y = ( instruction.mFrameBuffer->GetHeight() - instruction.mViewport.height ) - instruction.mViewport.y; - viewportRect.Set( instruction.mViewport.x, y, instruction.mViewport.width, instruction.mViewport.height ); + const int32_t y = ( surfaceRect.height - instruction.mViewport.height ) - instruction.mViewport.y; + viewportRect.Set( instruction.mViewport.x, y, instruction.mViewport.width, instruction.mViewport.height ); } else { - viewportRect.Set( 0, 0, instruction.mFrameBuffer->GetWidth(), instruction.mFrameBuffer->GetHeight() ); + viewportRect = surfaceRect; } } - else - { - viewportRect = surfaceRect; - } - } - - if ( surfaceOrientation == 90 || surfaceOrientation == 270 ) - { - int temp = viewportRect.width; - viewportRect.width = viewportRect.height; - viewportRect.height = temp; - } - mImpl->currentContext->Viewport(viewportRect.x, viewportRect.y, viewportRect.width, viewportRect.height); - - if ( instruction.mIsClearColorSet ) - { - mImpl->currentContext->ClearColor( clearColor.r, - clearColor.g, - clearColor.b, - clearColor.a ); - - // Clear the viewport area only - mImpl->currentContext->SetScissorTest( true ); - if( isPartialUpdate ) + bool clearFullFrameRect = true; + if( instruction.mFrameBuffer != 0 ) { - intersectRect = IntersectAABB( scissorBox, viewportRect ); - mImpl->currentContext->Scissor( intersectRect.x, intersectRect.y, intersectRect.width, intersectRect.height ); + Viewport frameRect( 0, 0, instruction.mFrameBuffer->GetWidth(), instruction.mFrameBuffer->GetHeight() ); + clearFullFrameRect = ( frameRect == viewportRect ); } else { - mImpl->currentContext->Scissor( viewportRect.x, viewportRect.y, viewportRect.width, viewportRect.height ); + clearFullFrameRect = ( surfaceRect == viewportRect ); } - mImpl->currentContext->ColorMask( true ); - mImpl->currentContext->Clear( GL_COLOR_BUFFER_BIT , Context::CHECK_CACHED_VALUES ); - mImpl->currentContext->SetScissorTest( false ); - } - - // Clear the list of bound textures - mImpl->boundTextures.Clear(); - - mImpl->renderAlgorithms.ProcessRenderInstruction( - instruction, - *mImpl->currentContext, - mImpl->renderBufferIndex, - depthBufferAvailable, - stencilBufferAvailable, - mImpl->boundTextures, - surfaceOrientation, - scissorBox ); - - // Synchronise the FBO/Texture access when there are multiple contexts - if ( mImpl->currentContext->IsSurfacelessContextSupported() ) - { - // Check whether any binded texture is in the dependency list - bool textureFound = false; - if ( mImpl->boundTextures.Count() > 0u && mImpl->textureDependencyList.Count() > 0u ) + if (!clippingRect.IsEmpty()) { - for ( auto textureId : mImpl->textureDependencyList ) + if (!clippingRect.Intersect(viewportRect)) { - - textureFound = std::find_if( mImpl->boundTextures.Begin(), mImpl->boundTextures.End(), - [textureId]( GLuint id ) - { - return textureId == id; - } ) != mImpl->boundTextures.End(); + DALI_LOG_ERROR("Invalid clipping rect %d %d %d %d\n", clippingRect.x, clippingRect.y, clippingRect.width, clippingRect.height); + clippingRect = Rect(); } + clearFullFrameRect = false; } - if ( textureFound ) - { + mImpl->currentContext->Viewport(viewportRect.x, viewportRect.y, viewportRect.width, viewportRect.height); - if ( !instruction.mFrameBuffer || !instruction.mFrameBuffer->IsSurfaceBacked() ) + if (instruction.mIsClearColorSet) + { + mImpl->currentContext->ClearColor(clearColor.r, + clearColor.g, + clearColor.b, + clearColor.a); + if (!clearFullFrameRect) { - // For off-screen buffer - - // Wait until all rendering calls for the currently context are executed - mImpl->glContextHelperAbstraction.WaitClient(); - - // Clear the dependency list - mImpl->textureDependencyList.Clear(); + if (!clippingRect.IsEmpty()) + { + mImpl->currentContext->SetScissorTest(true); + mImpl->currentContext->Scissor(clippingRect.x, clippingRect.y, clippingRect.width, clippingRect.height); + mImpl->currentContext->Clear(clearMask, Context::FORCE_CLEAR); + mImpl->currentContext->SetScissorTest(false); + } + else + { + mImpl->currentContext->SetScissorTest(true); + mImpl->currentContext->Scissor(viewportRect.x, viewportRect.y, viewportRect.width, viewportRect.height); + mImpl->currentContext->Clear(clearMask, Context::FORCE_CLEAR); + mImpl->currentContext->SetScissorTest(false); + } } else { - // For surface-backed frame buffer + mImpl->currentContext->SetScissorTest(false); + mImpl->currentContext->Clear(clearMask, Context::FORCE_CLEAR); + } + } - // Worker thread lambda function - auto& glContextHelperAbstraction = mImpl->glContextHelperAbstraction; - auto workerFunction = [&glContextHelperAbstraction]( int workerThread ) - { - // Switch to the shared context in the worker thread - glContextHelperAbstraction.MakeSurfacelessContextCurrent(); + // Clear the list of bound textures + mImpl->boundTextures.Clear(); + + mImpl->renderAlgorithms.ProcessRenderInstruction( + instruction, + *mImpl->currentContext, + mImpl->renderBufferIndex, + depthBufferAvailable, + stencilBufferAvailable, + mImpl->boundTextures, + clippingRect ); + + // Synchronise the FBO/Texture access when there are multiple contexts + if ( mImpl->currentContext->IsSurfacelessContextSupported() ) + { + // Check whether any binded texture is in the dependency list + bool textureFound = false; - // Wait until all rendering calls for the shared context are executed - glContextHelperAbstraction.WaitClient(); + if ( mImpl->boundTextures.Count() > 0u && mImpl->textureDependencyList.Count() > 0u ) + { + for ( auto textureId : mImpl->textureDependencyList ) + { - // Must clear the context in the worker thread - // Otherwise the shared context cannot be switched to from the render thread - glContextHelperAbstraction.MakeContextNull(); - }; + textureFound = std::find_if( mImpl->boundTextures.Begin(), mImpl->boundTextures.End(), + [textureId]( GLuint id ) + { + return textureId == id; + } ) != mImpl->boundTextures.End(); + } + } - auto future = mImpl->threadPool->SubmitTask( 0u, workerFunction ); - if ( future ) + if ( textureFound ) + { + if ( instruction.mFrameBuffer ) { - mImpl->threadPool->Wait(); + // For off-screen buffer + + // Wait until all rendering calls for the currently context are executed + mImpl->glContextHelperAbstraction.WaitClient(); // Clear the dependency list mImpl->textureDependencyList.Clear(); } + else + { + // Worker thread lambda function + auto& glContextHelperAbstraction = mImpl->glContextHelperAbstraction; + auto workerFunction = [&glContextHelperAbstraction]( int workerThread ) + { + // Switch to the shared context in the worker thread + glContextHelperAbstraction.MakeSurfacelessContextCurrent(); + + // Wait until all rendering calls for the shared context are executed + glContextHelperAbstraction.WaitClient(); + + // Must clear the context in the worker thread + // Otherwise the shared context cannot be switched to from the render thread + glContextHelperAbstraction.MakeContextNull(); + }; + + auto future = mImpl->threadPool->SubmitTask( 0u, workerFunction ); + if ( future ) + { + mImpl->threadPool->Wait(); + + // Clear the dependency list + mImpl->textureDependencyList.Clear(); + } + } } } + + if( instruction.mRenderTracker && instruction.mFrameBuffer ) + { + // This will create a sync object every frame this render tracker + // is alive (though it should be now be created only for + // render-once render tasks) + instruction.mRenderTracker->CreateSyncObject( mImpl->glSyncAbstraction ); + instruction.mRenderTracker = nullptr; // Only create once. + } + + if ( renderToFbo ) + { + mImpl->currentContext->Flush(); + } } - if( instruction.mRenderTracker && ( instruction.mFrameBuffer != 0 ) ) + GLenum attachments[] = { GL_DEPTH, GL_STENCIL }; + mImpl->currentContext->InvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments); +} + +void RenderManager::PostRender( bool uploadOnly ) +{ + if ( !uploadOnly ) { - // This will create a sync object every frame this render tracker - // is alive (though it should be now be created only for - // render-once render tasks) - instruction.mRenderTracker->CreateSyncObject( mImpl->glSyncAbstraction ); - instruction.mRenderTracker = NULL; // Only create once. + if ( mImpl->currentContext->IsSurfacelessContextSupported() ) + { + mImpl->glContextHelperAbstraction.MakeSurfacelessContextCurrent(); + } + + GLenum attachments[] = { GL_DEPTH, GL_STENCIL }; + mImpl->context.InvalidateFramebuffer(GL_FRAMEBUFFER, 2, attachments); } - if ( surfaceFrameBuffer ) + //Notify RenderGeometries that rendering has finished + for ( auto&& iter : mImpl->geometryContainer ) { - surfaceFrameBuffer->PostRender(); + iter->OnRenderFinished(); } - else + + mImpl->UpdateTrackers(); + + + uint32_t count = 0u; + for( uint32_t i = 0; i < mImpl->sceneContainer.size(); ++i ) { - mImpl->currentContext->Flush(); + count += mImpl->sceneContainer[i]->GetRenderInstructions().Count( mImpl->renderBufferIndex ); } + + const bool haveInstructions = count > 0u; + + // If this frame was rendered due to instructions existing, we mark this so we know to clear the next frame. + mImpl->lastFrameWasRendered = haveInstructions; + + /** + * The rendering has finished; swap to the next buffer. + * Ideally the update has just finished using this buffer; otherwise the render thread + * should block until the update has finished. + */ + mImpl->renderBufferIndex = (0 != mImpl->renderBufferIndex) ? 0 : 1; + + DALI_PRINT_RENDER_END(); } } // namespace SceneGraph