[Tizen] Support for recognizing legacy shaders.
[platform/core/uifw/dali-core.git] / dali / internal / update / manager / update-manager.cpp
index a5ecd9a..3c2a422 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
 // CLASS HEADER
 #include <dali/internal/update/manager/update-manager.h>
 
+// EXTERNAL INCLUDES
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+#include <dali/devel-api/common/map-wrapper.h>
+#include <dali/devel-api/common/set-wrapper.h>
+#else
+#include <unordered_map>
+#include <unordered_set>
+#endif
+
 // INTERNAL INCLUDES
 #include <dali/integration-api/core.h>
+#include <dali/integration-api/trace.h>
 
 #include <dali/internal/common/owner-key-container.h>
 
@@ -42,6 +52,7 @@
 
 #include <dali/internal/render/common/render-manager.h>
 #include <dali/internal/render/queue/render-queue.h>
+#include <dali/internal/render/renderers/render-vertex-buffer.h>
 
 // Un-comment to enable node tree debug logging
 //#define NODE_TREE_LOGGING 1
@@ -72,6 +83,12 @@ namespace
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_UPDATE_MANAGER");
 } // unnamed namespace
 #endif
+namespace
+{
+// TODO : The name of trace marker is from VD specific.
+// We might need to change it as DALI_TRACE_UPDATE_PROCESS.
+DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_COMBINED, false);
+} // namespace
 
 using namespace Dali::Integration;
 using Dali::Internal::Update::MessageQueue;
@@ -84,6 +101,27 @@ namespace SceneGraph
 {
 namespace
 {
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+/**
+ * Flag whether property has changed, during the Update phase.
+ */
+enum ContainerRemovedFlagBits
+{
+  NOTHING               = 0x00,
+  NODE                  = 0x01,
+  RENDERER              = 0x02,
+  SHADER                = 0x04,
+  TEXTURE_SET           = 0x08,
+  ANIMATION             = 0x10,
+  PROPERTY_NOTIFICATION = 0x20,
+  CUSTOM_OBJECT         = 0x40,
+};
+
+/**
+ * @brief ContainerRemovedFlags alters behaviour of implementation
+ */
+using ContainerRemovedFlags = uint8_t;
+#endif
 /**
  * Helper to Erase an object from OwnerContainer using discard queue
  * @param container to remove from
@@ -184,10 +222,13 @@ struct UpdateManager::Impl
     panGestureProcessor(nullptr),
     messageQueue(renderController, sceneGraphBuffers),
     frameCallbackProcessor(nullptr),
-    keepRenderingSeconds(0.0f),
     nodeDirtyFlags(NodePropertyFlags::TRANSFORM), // set to TransformFlag to ensure full update the first time through Update()
     frameCounter(0),
     renderingBehavior(DevelStage::Rendering::IF_REQUIRED),
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+    containerRemovedFlags(ContainerRemovedFlagBits::NOTHING),
+#endif
+    discardAnimationFinishedAge(0u),
     animationFinishedDuringUpdate(false),
     previousUpdateScene(false),
     renderTaskWaiting(false),
@@ -224,6 +265,7 @@ struct UpdateManager::Impl
       (*iter)->OnDestroy();
       Node::Delete(*iter);
     }
+    nodeIdMap.clear();
 
     for(auto&& scene : scenes)
     {
@@ -275,6 +317,17 @@ struct UpdateManager::Impl
 
   Vector<Node*> nodes; ///< A container of all instantiated nodes
 
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+  using NodeIdMap = std::map<uint32_t, Node*>;
+
+  using PropertyBaseResetRequestedContainer = std::set<PropertyBase*>;
+#else
+  using NodeIdMap = std::unordered_map<uint32_t, Node*>;
+
+  using PropertyBaseResetRequestedContainer = std::unordered_set<PropertyBase*>;
+#endif
+  NodeIdMap nodeIdMap; ///< A container of nodes map by id.
+
   Vector<Camera*> cameras; ///< A container of cameras. Note : these cameras are owned by Impl::nodes.
 
   OwnerContainer<PropertyOwner*> customObjects; ///< A container of owned objects (with custom properties)
@@ -283,6 +336,8 @@ struct UpdateManager::Impl
   ResetterContainer<NodeResetter>         nodeResetters;     ///< A container of node resetters
   ResetterContainer<RendererResetter>     rendererResetters; ///< A container of renderer resetters
 
+  PropertyBaseResetRequestedContainer resetRequestedPropertyBases; ///< A container of property base to be resets
+
   OwnerContainer<Animation*>            animations;            ///< A container of owned animations
   OwnerContainer<PropertyNotification*> propertyNotifications; ///< A container of owner property notifications.
   OwnerKeyContainer<Renderer>           renderers;             ///< A container of owned renderers
@@ -294,6 +349,8 @@ struct UpdateManager::Impl
   DiscardQueue<MemoryPoolKey<Renderer>, OwnerKeyContainer<Renderer>> rendererDiscardQueue;
   DiscardQueue<Scene*, OwnerContainer<Scene*>>                       sceneDiscardQueue;
 
+  CompleteNotificationInterface::ParameterList notifyRequiredAnimations; ///< A temperal container of complete notify required animations, like animation finished, stopped, or loop completed.
+
   OwnerPointer<PanGesture> panGestureProcessor; ///< Owned pan gesture processor; it lives for the lifecycle of UpdateManager
 
   MessageQueue                         messageQueue;          ///< The messages queued from the event-thread
@@ -304,16 +361,21 @@ struct UpdateManager::Impl
   OwnerPointer<FrameCallbackProcessor> frameCallbackProcessor; ///< Owned FrameCallbackProcessor, only created if required.
 
   std::atomic<std::size_t> renderInstructionCapacity{0u};
-  float                    keepRenderingSeconds; ///< Set via Dali::Stage::KeepRendering
-  NodePropertyFlags        nodeDirtyFlags;       ///< cumulative node dirty flags from previous frame
-  uint32_t                 frameCounter;         ///< Frame counter used in debugging to choose which frame to debug and which to ignore.
-  DevelStage::Rendering    renderingBehavior;    ///< Set via DevelStage::SetRenderingBehavior
+  NodePropertyFlags        nodeDirtyFlags;    ///< cumulative node dirty flags from previous frame
+  uint32_t                 frameCounter;      ///< Frame counter used in debugging to choose which frame to debug and which to ignore.
+  DevelStage::Rendering    renderingBehavior; ///< Set via DevelStage::SetRenderingBehavior
+
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+  ContainerRemovedFlags containerRemovedFlags; ///< cumulative container removed flags during current frame
+#endif
 
-  bool animationFinishedDuringUpdate; ///< Flag whether any animations finished during the Update()
-  bool previousUpdateScene;           ///< True if the scene was updated in the previous frame (otherwise it was optimized out)
-  bool renderTaskWaiting;             ///< A REFRESH_ONCE render task is waiting to be rendered
-  bool renderersAdded;                ///< Flag to keep track when renderers have been added to avoid unnecessary processing
-  bool renderingRequired;             ///< True if required to render the current frame
+  uint8_t discardAnimationFinishedAge : 2; ///< Age of EndAction::Discard animation Stop/Finished. It will make we call ResetToBaseValue at least 2 frames.
+
+  bool animationFinishedDuringUpdate : 1; ///< Flag whether any animations finished during the Update()
+  bool previousUpdateScene : 1;           ///< True if the scene was updated in the previous frame (otherwise it was optimized out)
+  bool renderTaskWaiting : 1;             ///< A REFRESH_ONCE render task is waiting to be rendered
+  bool renderersAdded : 1;                ///< Flag to keep track when renderers have been added to avoid unnecessary processing
+  bool renderingRequired : 1;             ///< True if required to render the current frame
 
 private:
   Impl(const Impl&);            ///< Undefined
@@ -329,6 +391,8 @@ UpdateManager::UpdateManager(NotificationManager&           notificationManager,
                              RenderTaskProcessor&           renderTaskProcessor)
 : mImpl(nullptr)
 {
+  PropertyBase::RegisterResetterManager(*this);
+
   mImpl = new Impl(notificationManager,
                    animationFinishedNotifier,
                    propertyNotifier,
@@ -342,6 +406,7 @@ UpdateManager::UpdateManager(NotificationManager&           notificationManager,
 UpdateManager::~UpdateManager()
 {
   delete mImpl;
+  PropertyBase::UnregisterResetterManager();
 }
 
 void UpdateManager::InstallRoot(OwnerPointer<Layer>& layer)
@@ -359,6 +424,10 @@ void UpdateManager::InstallRoot(OwnerPointer<Layer>& layer)
 
   rootLayer->AddInitializeResetter(*this);
 
+  // Do not allow to insert duplicated nodes.
+  // It could be happened if node id is overflowed.
+  DALI_ASSERT_ALWAYS(mImpl->nodeIdMap.insert({rootLayer->GetId(), rootLayer}).second);
+
   mImpl->scenes.emplace_back(new Impl::SceneInfo(rootLayer));
 }
 
@@ -376,6 +445,8 @@ void UpdateManager::UninstallRoot(Layer* layer)
     }
   }
 
+  mImpl->nodeIdMap.erase(layer->GetId());
+
   mImpl->nodeDiscardQueue.Add(mSceneGraphBuffers.GetUpdateBufferIndex(), layer);
 
   // Notify the layer about impending destruction
@@ -395,7 +466,12 @@ void UpdateManager::AddNode(OwnerPointer<Node>& node)
     AddCamera(static_cast<Camera*>(rawNode));
   }
 
+  // Do not allow to insert duplicated nodes.
+  // It could be happened if node id is overflowed.
+  DALI_ASSERT_ALWAYS(mImpl->nodeIdMap.insert({rawNode->GetId(), rawNode}).second);
+
   mImpl->nodes.PushBack(rawNode);
+
   rawNode->CreateTransform(&mImpl->transformManager);
 }
 
@@ -407,6 +483,7 @@ void UpdateManager::ConnectNode(Node* parent, Node* node)
 
   DALI_LOG_INFO(gLogFilter, Debug::General, "[%x] ConnectNode\n", node);
 
+  parent->SetDirtyFlag(NodePropertyFlags::DESCENDENT_HIERARCHY_CHANGED); // make parent dirty so that render items dont get reused
   parent->ConnectChild(node);
 
   node->AddInitializeResetter(*this);
@@ -424,7 +501,7 @@ void UpdateManager::DisconnectNode(Node* node)
 
   Node* parent = node->GetParent();
   DALI_ASSERT_ALWAYS(nullptr != parent);
-  parent->SetDirtyFlag(NodePropertyFlags::CHILD_DELETED); // make parent dirty so that render items dont get reused
+  parent->SetDirtyFlag(NodePropertyFlags::CHILD_DELETED | NodePropertyFlags::DESCENDENT_HIERARCHY_CHANGED); // make parent dirty so that render items dont get reused
 
   parent->DisconnectChild(mSceneGraphBuffers.GetUpdateBufferIndex(), *node);
 
@@ -449,6 +526,9 @@ void UpdateManager::DestroyNode(Node* node)
     if((*iter) == node)
     {
       mImpl->nodes.Erase(iter);
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+      mImpl->containerRemovedFlags |= ContainerRemovedFlagBits::NODE;
+#endif
       break;
     }
   }
@@ -459,6 +539,8 @@ void UpdateManager::DestroyNode(Node* node)
     RemoveCamera(static_cast<Camera*>(node));
   }
 
+  mImpl->nodeIdMap.erase(node->GetId());
+
   mImpl->nodeDiscardQueue.Add(mSceneGraphBuffers.GetUpdateBufferIndex(), node);
 
   // Notify the Node about impending destruction
@@ -497,6 +579,9 @@ void UpdateManager::AddObject(OwnerPointer<PropertyOwner>& object)
 void UpdateManager::RemoveObject(PropertyOwner* object)
 {
   mImpl->customObjects.EraseObject(object);
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+  mImpl->containerRemovedFlags |= ContainerRemovedFlagBits::CUSTOM_OBJECT;
+#endif
 }
 
 void UpdateManager::AddRenderTaskList(OwnerPointer<RenderTaskList>& taskList)
@@ -575,6 +660,13 @@ void UpdateManager::StopAnimation(Animation* animation)
   mImpl->animationFinishedDuringUpdate = mImpl->animationFinishedDuringUpdate || animationFinished;
 }
 
+void UpdateManager::ClearAnimation(Animation* animation)
+{
+  DALI_ASSERT_DEBUG(animation && "NULL animation called to clear");
+
+  animation->ClearAnimator(mSceneGraphBuffers.GetUpdateBufferIndex());
+}
+
 void UpdateManager::RemoveAnimation(Animation* animation)
 {
   DALI_ASSERT_DEBUG(animation && "NULL animation called to remove");
@@ -582,6 +674,8 @@ void UpdateManager::RemoveAnimation(Animation* animation)
   animation->OnDestroy(mSceneGraphBuffers.GetUpdateBufferIndex());
 
   DALI_ASSERT_DEBUG(animation->GetState() == Animation::Destroyed);
+
+  // Do not remove from container now. Destroyed animation will be removed at Animate.
 }
 
 bool UpdateManager::IsAnimationRunning() const
@@ -621,6 +715,11 @@ void UpdateManager::AddRendererResetter(const Renderer& renderer)
   mImpl->rendererResetters.PushBack(rendererResetter.Release());
 }
 
+void UpdateManager::RequestPropertyBaseResetToBaseValue(PropertyBase* propertyBase)
+{
+  mImpl->resetRequestedPropertyBases.insert(propertyBase);
+}
+
 void UpdateManager::AddPropertyNotification(OwnerPointer<PropertyNotification>& propertyNotification)
 {
   mImpl->propertyNotifications.PushBack(propertyNotification.Release());
@@ -628,7 +727,14 @@ void UpdateManager::AddPropertyNotification(OwnerPointer<PropertyNotification>&
 
 void UpdateManager::RemovePropertyNotification(PropertyNotification* propertyNotification)
 {
-  mImpl->propertyNotifications.EraseObject(propertyNotification);
+  auto iter = std::find(mImpl->propertyNotifications.Begin(), mImpl->propertyNotifications.End(), propertyNotification);
+  if(iter != mImpl->propertyNotifications.End())
+  {
+    mImpl->propertyNotifications.Erase(iter);
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+    mImpl->containerRemovedFlags |= ContainerRemovedFlagBits::PROPERTY_NOTIFICATION;
+#endif
+  }
 }
 
 void UpdateManager::PropertyNotificationSetNotify(PropertyNotification* propertyNotification, PropertyNotification::NotifyMode notifyMode)
@@ -646,6 +752,9 @@ void UpdateManager::RemoveShader(Shader* shader)
 {
   // Find the shader and destroy it
   EraseUsingDiscardQueue(mImpl->shaders, shader, mImpl->shaderDiscardQueue, mSceneGraphBuffers.GetUpdateBufferIndex());
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+  mImpl->containerRemovedFlags |= ContainerRemovedFlagBits::SHADER;
+#endif
 }
 
 void UpdateManager::SaveBinary(Internal::ShaderDataPtr shaderData)
@@ -684,6 +793,10 @@ void UpdateManager::RemoveRenderer(const RendererKey& rendererKey)
   EraseUsingDiscardQueue(mImpl->renderers, rendererKey, mImpl->rendererDiscardQueue, mSceneGraphBuffers.GetUpdateBufferIndex());
   // Need to remove the render object as well
   rendererKey->DisconnectFromSceneGraph(*mImpl->sceneController, mSceneGraphBuffers.GetUpdateBufferIndex());
+
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+  mImpl->containerRemovedFlags |= ContainerRemovedFlagBits::RENDERER;
+#endif
 }
 
 void UpdateManager::AttachRenderer(Node* node, Renderer* renderer)
@@ -708,6 +821,9 @@ void UpdateManager::AddTextureSet(OwnerPointer<TextureSet>& textureSet)
 void UpdateManager::RemoveTextureSet(TextureSet* textureSet)
 {
   mImpl->textureSets.EraseObject(textureSet);
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+  mImpl->containerRemovedFlags |= ContainerRemovedFlagBits::TEXTURE_SET;
+#endif
 }
 
 uint32_t* UpdateManager::ReserveMessageSlot(uint32_t size, bool updateScene)
@@ -735,6 +851,11 @@ void UpdateManager::EventProcessingStarted()
   mImpl->messageQueue.EventProcessingStarted();
 }
 
+void UpdateManager::EventProcessingFinished()
+{
+  mImpl->messageQueue.EventProcessingFinished();
+}
+
 bool UpdateManager::FlushQueue()
 {
   return mImpl->messageQueue.FlushQueue();
@@ -745,14 +866,30 @@ void UpdateManager::ResetProperties(BufferIndex bufferIndex)
   // Clear the "animations finished" flag; This should be set if any (previously playing) animation is stopped
   mImpl->animationFinishedDuringUpdate = false;
 
+  // Age down discard animations.
+  mImpl->discardAnimationFinishedAge >>= 1;
+
+  // Ensure that their was no request to reset to base values during the previous update
+  // (Since requested property base doesn't consider the lifecycle of PropertyBase,
+  // It might be invalid after the previous update finished)
+  DALI_ASSERT_DEBUG(mImpl->resetRequestedPropertyBases.empty() && "Reset to base values requested during the previous update!");
+  mImpl->resetRequestedPropertyBases.clear();
+
   // Reset node properties
-  mImpl->nodeResetters.ResetToBaseValues(bufferIndex);
+  mImpl->nodeResetters.RequestResetToBaseValues();
 
   // Reset renderer properties
-  mImpl->rendererResetters.ResetToBaseValues(bufferIndex);
+  mImpl->rendererResetters.RequestResetToBaseValues();
 
   // Reset all animating / constrained properties
-  mImpl->propertyResetters.ResetToBaseValues(bufferIndex);
+  mImpl->propertyResetters.RequestResetToBaseValues();
+
+  // Actual reset to base values here
+  for(auto&& propertyBase : mImpl->resetRequestedPropertyBases)
+  {
+    propertyBase->ResetToBaseValue(bufferIndex);
+  }
+  mImpl->resetRequestedPropertyBases.clear();
 
   // Clear all root nodes dirty flags
   for(auto& scene : mImpl->scenes)
@@ -788,31 +925,53 @@ bool UpdateManager::Animate(BufferIndex bufferIndex, float elapsedSeconds)
 {
   bool animationActive = false;
 
-  auto&& iter            = mImpl->animations.Begin();
-  bool   animationLooped = false;
+  if(mImpl->animations.Empty())
+  {
+    return animationActive;
+  }
+
+  auto&& iter = mImpl->animations.Begin();
+
+  DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_ANIMATION_ANIMATE", [&](std::ostringstream& oss) {
+    oss << "[" << mImpl->animations.Count() << "]";
+  });
 
   while(iter != mImpl->animations.End())
   {
     Animation* animation             = *iter;
     bool       finished              = false;
-    bool       looped                = false;
+    bool       stopped               = false;
     bool       progressMarkerReached = false;
-    animation->Update(bufferIndex, elapsedSeconds, looped, finished, progressMarkerReached);
+    animation->Update(bufferIndex, elapsedSeconds, stopped, finished, progressMarkerReached);
 
     animationActive = animationActive || animation->IsActive();
 
     if(progressMarkerReached)
     {
-      mImpl->notificationManager.QueueMessage(Internal::NotifyProgressReachedMessage(mImpl->animationPlaylist, animation));
+      mImpl->notificationManager.QueueMessage(Internal::NotifyProgressReachedMessage(mImpl->animationPlaylist, animation->GetNotifyId()));
     }
 
     mImpl->animationFinishedDuringUpdate = mImpl->animationFinishedDuringUpdate || finished;
-    animationLooped                      = animationLooped || looped;
+
+    // Check whether finished animation is Discard type. If then, we should update scene at least 2 frames.
+    if(finished && animation->GetEndAction() == Animation::EndAction::DISCARD)
+    {
+      mImpl->discardAnimationFinishedAge |= 2u;
+    }
+
+    // queue the notification on finished or stopped
+    if(finished || stopped)
+    {
+      mImpl->notifyRequiredAnimations.PushBack(animation->GetNotifyId());
+    }
 
     // Remove animations that had been destroyed but were still waiting for an update
     if(animation->GetState() == Animation::Destroyed)
     {
       iter = mImpl->animations.Erase(iter);
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+      mImpl->containerRemovedFlags |= ContainerRemovedFlagBits::ANIMATION;
+#endif
     }
     else
     {
@@ -820,13 +979,16 @@ bool UpdateManager::Animate(BufferIndex bufferIndex, float elapsedSeconds)
     }
   }
 
-  // queue the notification on finished or looped (to update loop count)
-  if(mImpl->animationFinishedDuringUpdate || animationLooped)
+  // The application should be notified by NotificationManager, in another thread
+  if(!mImpl->notifyRequiredAnimations.Empty())
   {
-    // The application should be notified by NotificationManager, in another thread
-    mImpl->notificationManager.QueueCompleteNotification(&mImpl->animationPlaylist);
+    mImpl->notificationManager.QueueNotification(&mImpl->animationPlaylist, std::move(mImpl->notifyRequiredAnimations));
   }
 
+  DALI_TRACE_END_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_ANIMATION_ANIMATE", [&](std::ostringstream& oss) {
+    oss << "[" << mImpl->animations.Count() << "]";
+  });
+
   return animationActive;
 }
 
@@ -883,7 +1045,7 @@ void UpdateManager::ProcessPropertyNotifications(BufferIndex bufferIndex)
     bool valid = notification->Check(bufferIndex);
     if(valid)
     {
-      mImpl->notificationManager.QueueMessage(PropertyChangedMessage(mImpl->propertyNotifier, notification, notification->GetValidity()));
+      mImpl->notificationManager.QueueMessage(PropertyChangedMessage(mImpl->propertyNotifier, notification->GetNotifyId(), notification->GetValidity()));
     }
   }
 }
@@ -915,6 +1077,15 @@ void UpdateManager::ForwardCompiledShadersToEventThread()
 
 void UpdateManager::UpdateRenderers(PropertyOwnerContainer& postPropertyOwners, BufferIndex bufferIndex)
 {
+  if(mImpl->renderers.Empty())
+  {
+    return;
+  }
+
+  DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_UPDATE_RENDERERS", [&](std::ostringstream& oss) {
+    oss << "[" << mImpl->renderers.Count() << "]";
+  });
+
   for(const auto& rendererKey : mImpl->renderers)
   {
     // Apply constraints
@@ -927,6 +1098,8 @@ void UpdateManager::UpdateRenderers(PropertyOwnerContainer& postPropertyOwners,
 
     mImpl->renderingRequired = renderer->PrepareRender(bufferIndex) || mImpl->renderingRequired;
   }
+
+  DALI_TRACE_END(gTraceFilter, "DALI_UPDATE_RENDERERS");
 }
 
 void UpdateManager::UpdateNodes(PropertyOwnerContainer& postPropertyOwners, BufferIndex bufferIndex)
@@ -983,10 +1156,11 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
     isAnimationRunning ||                              // ..at least one animation is running OR
     mImpl->messageQueue.IsSceneUpdateRequired() ||     // ..a message that modifies the scene graph node tree is queued OR
     mImpl->frameCallbackProcessor ||                   // ..a frame callback processor is existed OR
+    mImpl->discardAnimationFinishedAge > 0u ||         // ..at least one animation with EndAction::DISCARD finished
     gestureUpdated;                                    // ..a gesture property was updated
 
-  bool keepRendererRendering = false;
-  mImpl->renderingRequired   = false;
+  uint32_t keepUpdating    = 0;
+  mImpl->renderingRequired = false;
 
   // Although the scene-graph may not require an update, we still need to synchronize double-buffered
   // values if the scene was updated in the previous frame.
@@ -1010,6 +1184,15 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
   // We should not start skipping update steps or reusing lists until there has been two frames where nothing changes
   if(updateScene || mImpl->previousUpdateScene)
   {
+    DALI_TRACE_BEGIN_WITH_MESSAGE_GENERATOR(gTraceFilter, "DALI_UPDATE_INTERNAL", [&](std::ostringstream& oss) {
+      oss << "[n:" << mImpl->nodes.Size() << ",";
+      oss << "c:" << mImpl->customObjects.Size() << ",";
+      oss << "a:" << mImpl->animations.Size() << ",";
+      oss << "r:" << mImpl->renderers.Size() << ",";
+      oss << "t:" << mImpl->textureSets.Size() << ",";
+      oss << "s:" << mImpl->shaders.Size() << "]";
+    });
+
     // Animate
     bool animationActive = Animate(bufferIndex, elapsedSeconds);
 
@@ -1035,7 +1218,10 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
     // Call the frame-callback-processor if set
     if(mImpl->frameCallbackProcessor)
     {
-      mImpl->frameCallbackProcessor->Update(bufferIndex, elapsedSeconds);
+      if(mImpl->frameCallbackProcessor->Update(bufferIndex, elapsedSeconds))
+      {
+        keepUpdating |= KeepUpdating::FRAME_UPDATE_CALLBACK;
+      }
     }
 
     // Update node hierarchy, apply constraints,
@@ -1077,7 +1263,9 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
     if(mImpl->renderersAdded)
     {
       // Calculate how many render tasks we have in total
-      std::size_t numberOfRenderTasks = 0;
+      std::size_t numberOfRenderTasks        = 0;
+      std::size_t numberOfRenderInstructions = 0;
+      bool        renderContinuously         = false;
       for(auto&& scene : mImpl->scenes)
       {
         if(scene && scene->taskList)
@@ -1086,8 +1274,7 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
         }
       }
 
-      std::size_t numberOfRenderInstructions = 0;
-      mImpl->renderInstructionCapacity       = 0u;
+      mImpl->renderInstructionCapacity = 0u;
       for(auto&& scene : mImpl->scenes)
       {
         if(scene && scene->root && scene->taskList && scene->scene)
@@ -1095,17 +1282,23 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
           scene->scene->GetRenderInstructions().ResetAndReserve(bufferIndex,
                                                                 static_cast<uint32_t>(scene->taskList->GetTasks().Count()));
 
+          bool sceneKeepUpdating = scene->scene->KeepRenderingCheck(elapsedSeconds);
+          if(sceneKeepUpdating)
+          {
+            keepUpdating |= KeepUpdating::STAGE_KEEP_RENDERING;
+          }
+
           // If there are animations running, only add render instruction if at least one animation is currently active (i.e. not delayed)
           // or the nodes are dirty
-          if(!isAnimationRunning || animationActive || mImpl->renderingRequired || (mImpl->nodeDirtyFlags & RenderableUpdateFlags))
+          // or keep rendering is requested
+          if(!isAnimationRunning || animationActive || mImpl->renderingRequired || (mImpl->nodeDirtyFlags & RenderableUpdateFlags) || sceneKeepUpdating)
           {
-            keepRendererRendering |= mImpl->renderTaskProcessor.Process(bufferIndex,
-                                                                        *scene->taskList,
-                                                                        *scene->root,
-                                                                        scene->sortedLayerList,
-                                                                        scene->scene->GetRenderInstructions(),
-                                                                        renderToFboEnabled,
-                                                                        isRenderingToFbo);
+            renderContinuously |= mImpl->renderTaskProcessor.Process(bufferIndex,
+                                                                     *scene->taskList,
+                                                                     scene->sortedLayerList,
+                                                                     scene->scene->GetRenderInstructions(),
+                                                                     renderToFboEnabled,
+                                                                     isRenderingToFbo);
 
             mImpl->renderInstructionCapacity += scene->scene->GetRenderInstructions().GetCapacity();
             scene->scene->SetSkipRendering(false);
@@ -1119,21 +1312,30 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
         }
       }
 
+      if(renderContinuously)
+      {
+        keepUpdating |= KeepUpdating::RENDERER_CONTINUOUSLY;
+      }
+
       DALI_LOG_INFO(gLogFilter, Debug::General, "Update: numberOfRenderTasks(%d), Render Instructions(%d)\n", numberOfRenderTasks, numberOfRenderInstructions);
     }
+
+    DALI_TRACE_END(gTraceFilter, "DALI_UPDATE_INTERNAL");
   }
 
   if(!uploadOnly)
   {
+    // check the countdown and notify
+    mImpl->renderTaskWaiting = false;
+
     for(auto&& scene : mImpl->scenes)
     {
       if(scene && scene->root && scene->taskList)
       {
         RenderTaskList::RenderTaskContainer& tasks = scene->taskList->GetTasks();
 
-        // check the countdown and notify
-        bool doRenderOnceNotify  = false;
-        mImpl->renderTaskWaiting = false;
+        CompleteNotificationInterface::ParameterList notifyRequiredRenderTasks;
+
         for(auto&& renderTask : tasks)
         {
           renderTask->UpdateState();
@@ -1146,14 +1348,14 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
 
           if(renderTask->HasRendered())
           {
-            doRenderOnceNotify = true;
+            notifyRequiredRenderTasks.PushBack(renderTask->GetNotifyId());
           }
         }
 
-        if(doRenderOnceNotify)
+        if(!notifyRequiredRenderTasks.Empty())
         {
           DALI_LOG_INFO(gRenderTaskLogFilter, Debug::General, "Notify a render task has finished\n");
-          mImpl->notificationManager.QueueCompleteNotification(scene->taskList->GetCompleteNotificationInterface());
+          mImpl->notificationManager.QueueNotification(scene->taskList->GetCompleteNotificationInterface(), std::move(notifyRequiredRenderTasks));
         }
       }
     }
@@ -1162,16 +1364,49 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
   // Macro is undefined in release build.
   SNAPSHOT_NODE_LOGGING;
 
+#if defined(LOW_SPEC_MEMORY_MANAGEMENT_ENABLED)
+  // Shrink relevant containers if required.
+  if(mImpl->containerRemovedFlags & ContainerRemovedFlagBits::NODE)
+  {
+    mImpl->nodes.ShrinkToFitIfNeeded();
+  }
+  if(mImpl->containerRemovedFlags & ContainerRemovedFlagBits::RENDERER)
+  {
+    mImpl->renderers.ShrinkToFitIfNeeded();
+  }
+  if(mImpl->containerRemovedFlags & ContainerRemovedFlagBits::SHADER)
+  {
+    mImpl->shaders.ShrinkToFitIfNeeded();
+  }
+  if(mImpl->containerRemovedFlags & ContainerRemovedFlagBits::TEXTURE_SET)
+  {
+    mImpl->textureSets.ShrinkToFitIfNeeded();
+  }
+  if(mImpl->containerRemovedFlags & ContainerRemovedFlagBits::ANIMATION)
+  {
+    mImpl->animations.ShrinkToFitIfNeeded();
+  }
+  if(mImpl->containerRemovedFlags & ContainerRemovedFlagBits::PROPERTY_NOTIFICATION)
+  {
+    mImpl->propertyNotifications.ShrinkToFitIfNeeded();
+  }
+  if(mImpl->containerRemovedFlags & ContainerRemovedFlagBits::CUSTOM_OBJECT)
+  {
+    mImpl->customObjects.ShrinkToFitIfNeeded();
+  }
+
+  // Reset flag
+  mImpl->containerRemovedFlags = ContainerRemovedFlagBits::NOTHING;
+#endif
+
   // A ResetProperties() may be required in the next frame
   mImpl->previousUpdateScene = updateScene;
 
   // Check whether further updates are required
-  uint32_t keepUpdating = KeepUpdatingCheck(elapsedSeconds);
+  keepUpdating |= KeepUpdatingCheck(elapsedSeconds);
 
-  if(keepRendererRendering)
+  if(keepUpdating & (KeepUpdating::STAGE_KEEP_RENDERING | KeepUpdating::FRAME_UPDATE_CALLBACK | KeepUpdating::RENDERER_CONTINUOUSLY))
   {
-    keepUpdating |= KeepUpdating::STAGE_KEEP_RENDERING;
-
     // Set dirty flags for next frame to continue rendering
     mImpl->nodeDirtyFlags |= RenderableUpdateFlags;
   }
@@ -1190,7 +1425,7 @@ void UpdateManager::PostRender()
   // Reset dirty flag
   for(auto&& renderer : mImpl->renderers)
   {
-    renderer->ResetDirtyFlag();
+    renderer->SetUpdated(false);
   }
 
   for(auto&& shader : mImpl->shaders)
@@ -1206,29 +1441,22 @@ void UpdateManager::PostRender()
 
 uint32_t UpdateManager::KeepUpdatingCheck(float elapsedSeconds) const
 {
-  // Update the duration set via Stage::KeepRendering()
-  if(mImpl->keepRenderingSeconds > 0.0f)
-  {
-    mImpl->keepRenderingSeconds -= elapsedSeconds;
-  }
-
   uint32_t keepUpdatingRequest = KeepUpdating::NOT_REQUESTED;
 
   // If the rendering behavior is set to continuously render, then continue to render.
-  // If Stage::KeepRendering() has been called, then continue until the duration has elapsed.
   // Keep updating until no messages are received and no animations are running.
-  // If an animation has just finished, update at least once more for Discard end-actions.
+  // If an animation has just finished, update at least two frames more for Discard end-actions.
   // No need to check for renderQueue as there is always a render after update and if that
   // render needs another update it will tell the adaptor to call update again
 
-  if((mImpl->renderingBehavior == DevelStage::Rendering::CONTINUOUSLY) ||
-     (mImpl->keepRenderingSeconds > 0.0f))
+  if(mImpl->renderingBehavior == DevelStage::Rendering::CONTINUOUSLY)
   {
     keepUpdatingRequest |= KeepUpdating::STAGE_KEEP_RENDERING;
   }
 
   if(IsAnimationRunning() ||
-     mImpl->animationFinishedDuringUpdate)
+     mImpl->animationFinishedDuringUpdate ||
+     mImpl->discardAnimationFinishedAge > 0u)
   {
     keepUpdatingRequest |= KeepUpdating::ANIMATIONS_RUNNING;
   }
@@ -1254,7 +1482,13 @@ void UpdateManager::SurfaceReplaced(Scene* scene)
 
 void UpdateManager::KeepRendering(float durationSeconds)
 {
-  mImpl->keepRenderingSeconds = std::max(mImpl->keepRenderingSeconds, durationSeconds);
+  for(auto&& scene : mImpl->scenes)
+  {
+    if(scene->scene)
+    {
+      scene->scene->KeepRendering(durationSeconds);
+    }
+  }
 }
 
 void UpdateManager::SetRenderingBehavior(DevelStage::Rendering renderingBehavior)
@@ -1267,6 +1501,17 @@ void UpdateManager::RequestRendering()
   mImpl->renderingRequired = true;
 }
 
+Node* UpdateManager::GetNodePointerById(uint32_t nodeId) const
+{
+  Node* foundNodePointer = nullptr;
+  auto  iter             = mImpl->nodeIdMap.find(nodeId);
+  if(iter != mImpl->nodeIdMap.end())
+  {
+    foundNodePointer = iter->second;
+  }
+  return foundNodePointer;
+}
+
 void UpdateManager::SetLayerDepths(const SortedLayerPointers& layers, const Layer* rootLayer)
 {
   for(auto&& scene : mImpl->scenes)
@@ -1284,9 +1529,13 @@ void UpdateManager::SetDepthIndices(OwnerPointer<NodeDepths>& nodeDepths)
   // note, this vector is already in depth order.
   // So if we reverse iterate, we can assume that
   // my descendant node's depth index are updated.
+
+  // And also, This API is the last flushed message.
+  // We can now setup the DESCENDENT_HIERARCHY_CHANGED flag here.
   for(auto rIter = nodeDepths->nodeDepths.rbegin(), rEndIter = nodeDepths->nodeDepths.rend(); rIter != rEndIter; rIter++)
   {
     auto* node = rIter->node;
+    node->PropagateDescendentFlags();
     node->SetDepthIndex(rIter->sortedDepth);
     if(node->IsChildrenReorderRequired())
     {
@@ -1307,6 +1556,11 @@ void UpdateManager::RemoveFrameCallback(FrameCallbackInterface* frameCallback)
   mImpl->GetFrameCallbackProcessor(*this).RemoveFrameCallback(frameCallback);
 }
 
+void UpdateManager::NotifyFrameCallback(FrameCallbackInterface* frameCallback, Dali::UpdateProxy::NotifySyncPoint syncPoint)
+{
+  mImpl->GetFrameCallbackProcessor(*this).NotifyFrameCallback(frameCallback, syncPoint);
+}
+
 void UpdateManager::AddSampler(OwnerPointer<Render::Sampler>& sampler)
 {
   // Message has ownership of Sampler while in transit from update to render
@@ -1399,6 +1653,25 @@ void UpdateManager::SetVertexBufferData(Render::VertexBuffer* vertexBuffer, Owne
   new(slot) DerivedType(&mImpl->renderManager, &RenderManager::SetVertexBufferData, vertexBuffer, data, size);
 }
 
+void UpdateManager::SetVertexBufferDivisor(Render::VertexBuffer* vertexBuffer, uint32_t divisor)
+{
+  using LocalType = MessageValue1<Render::VertexBuffer, uint32_t>;
+  uint32_t* slot  = mImpl->renderQueue.ReserveMessageSlot(mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof(LocalType));
+  new(slot) LocalType(vertexBuffer, &Render::VertexBuffer::SetDivisor, divisor);
+}
+
+void UpdateManager::SetVertexBufferUpdateCallback(Render::VertexBuffer* vertexBuffer, Dali::VertexBufferUpdateCallback* callback)
+{
+  // Message has ownership of format while in transit from update -> render
+  using DerivedType = MessageValue2<RenderManager, Render::VertexBuffer*, Dali::VertexBufferUpdateCallback*>;
+
+  // Reserve some memory inside the render queue
+  uint32_t* slot = mImpl->renderQueue.ReserveMessageSlot(mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof(DerivedType));
+
+  // Construct message in the render queue memory; note that delete should not be called on the return value
+  new(slot) DerivedType(&mImpl->renderManager, &RenderManager::SetVertexBufferUpdateCallback, vertexBuffer, callback);
+}
+
 void UpdateManager::AddGeometry(OwnerPointer<Render::Geometry>& geometry)
 {
   // Message has ownership of format while in transit from update -> render
@@ -1499,9 +1772,9 @@ void UpdateManager::RemoveTexture(const Render::TextureKey& texture)
   new(slot) DerivedType(&mImpl->renderManager, &RenderManager::RemoveTexture, texture);
 }
 
-void UpdateManager::UploadTexture(const Render::TextureKey& texture, PixelDataPtr pixelData, const Texture::UploadParams& params)
+void UpdateManager::UploadTexture(const Render::TextureKey& texture, PixelDataPtr pixelData, const Graphics::UploadParams& params)
 {
-  using DerivedType = MessageValue3<RenderManager, Render::TextureKey, PixelDataPtr, Texture::UploadParams>;
+  using DerivedType = MessageValue3<RenderManager, Render::TextureKey, PixelDataPtr, Graphics::UploadParams>;
 
   // Reserve some memory inside the message queue
   uint32_t* slot = mImpl->renderQueue.ReserveMessageSlot(mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof(DerivedType));
@@ -1521,6 +1794,28 @@ void UpdateManager::GenerateMipmaps(const Render::TextureKey& texture)
   new(slot) DerivedType(&mImpl->renderManager, &RenderManager::GenerateMipmaps, texture);
 }
 
+void UpdateManager::SetTextureSize(const Render::TextureKey& texture, const Dali::ImageDimensions& size)
+{
+  using DerivedType = MessageValue2<RenderManager, Render::TextureKey, Dali::ImageDimensions>;
+
+  // Reserve some memory inside the render queue
+  uint32_t* slot = mImpl->renderQueue.ReserveMessageSlot(mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof(DerivedType));
+
+  // Construct message in the render queue memory; note that delete should not be called on the return value
+  new(slot) DerivedType(&mImpl->renderManager, &RenderManager::SetTextureSize, texture, size);
+}
+
+void UpdateManager::SetTextureFormat(const Render::TextureKey& texture, Dali::Pixel::Format pixelFormat)
+{
+  using DerivedType = MessageValue2<RenderManager, Render::TextureKey, Dali::Pixel::Format>;
+
+  // Reserve some memory inside the render queue
+  uint32_t* slot = mImpl->renderQueue.ReserveMessageSlot(mSceneGraphBuffers.GetUpdateBufferIndex(), sizeof(DerivedType));
+
+  // Construct message in the render queue memory; note that delete should not be called on the return value
+  new(slot) DerivedType(&mImpl->renderManager, &RenderManager::SetTextureFormat, texture, pixelFormat);
+}
+
 void UpdateManager::AddFrameBuffer(OwnerPointer<Render::FrameBuffer>& frameBuffer)
 {
   using DerivedType = MessageValue1<RenderManager, OwnerPointer<Render::FrameBuffer>>;