Apply offscreen rendering to control's each visual 69/324369/12
authorjmm <j0064423.lee@samsung.com>
Fri, 16 May 2025 04:24:03 +0000 (13:24 +0900)
committerjmm <j0064423.lee@samsung.com>
Tue, 10 Jun 2025 04:31:06 +0000 (13:31 +0900)
Change-Id: Ib97cef454155a4ccfcdf169c411ae61691186b07
Signed-off-by: jmm <j0064423.lee@samsung.com>
automated-tests/src/dali-toolkit/utc-Dali-Control.cpp
automated-tests/src/dali-toolkit/utc-Dali-RenderEffect.cpp
dali-toolkit/internal/controls/control/control-data-impl.cpp
dali-toolkit/internal/controls/control/control-visual-data.cpp
dali-toolkit/internal/controls/render-effects/gaussian-blur-effect-impl.cpp
dali-toolkit/internal/controls/render-effects/mask-effect-impl.cpp
dali-toolkit/internal/controls/render-effects/offscreen-rendering-impl.cpp

index 181f32917cfcbf29cd31c9c5b24faf8978aaf8ea..9f5b4ee09632ac734f243d694fabe7ed784de85f 100644 (file)
@@ -1538,6 +1538,10 @@ int UtcDaliControlOffScreenRendering(void)
   control.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f));
   control.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
 
+  control.SetProperty(DevelControl::Property::BORDERLINE_WIDTH, 10.0f);
+  control.SetProperty(DevelControl::Property::BORDERLINE_COLOR, Color::RED);
+  control.SetProperty(DevelControl::Property::BORDERLINE_OFFSET, 10.0f);
+
   application.GetScene().Add(control);
 
   control.SetBackgroundColor(Color::RED);
@@ -1548,6 +1552,15 @@ int UtcDaliControlOffScreenRendering(void)
   DALI_TEST_EQUALS(control.GetProperty(DevelControl::Property::OFFSCREEN_RENDERING).Get<int>(), (int)DevelControl::OffScreenRenderingType::REFRESH_ALWAYS, TEST_LOCATION);
   tet_infoline("Set offscreen rendering : refresh always");
 
+  const Property::Value SHADOW{
+    {Visual::Property::TYPE, Visual::COLOR},
+    {Visual::Property::MIX_COLOR, Vector4(0.0f, 0.0f, 0.0f, 0.5f)},
+    {Visual::Property::TRANSFORM,
+     Property::Map{{Visual::Transform::Property::SIZE, Vector2(1.05f, 1.05f)},
+                   {Visual::Transform::Property::ORIGIN, Align::CENTER},
+                   {Visual::Transform::Property::ANCHOR_POINT, Align::CENTER}}}};
+  control.SetProperty(DevelControl::Property::SHADOW, SHADOW);
+
   control.SetProperty(DevelControl::Property::OFFSCREEN_RENDERING, DevelControl::OffScreenRenderingType::REFRESH_ONCE);
   DALI_TEST_EQUALS(control.GetProperty(DevelControl::Property::OFFSCREEN_RENDERING).Get<int>(), (int)DevelControl::OffScreenRenderingType::REFRESH_ONCE, TEST_LOCATION);
   tet_infoline("Set offscreen rendering : refresh once");
@@ -1560,6 +1573,12 @@ int UtcDaliControlOffScreenRendering(void)
   application.SendNotification();
   application.Render();
 
+  control.Unparent();
+  application.GetScene().Add(control);
+
+  application.SendNotification();
+  application.Render();
+
   control.SetProperty(DevelControl::Property::OFFSCREEN_RENDERING, DevelControl::OffScreenRenderingType::NONE);
   DALI_TEST_EQUALS(control.GetProperty(DevelControl::Property::OFFSCREEN_RENDERING).Get<int>(), (int)DevelControl::OffScreenRenderingType::NONE, TEST_LOCATION);
   tet_infoline("Turn off offscreen rendering");
index f5d56cd6ed70698c288361ce879b5d6868fb3c05..7fbfa666c27502c5fe980029ce1aae271f98170c 100644 (file)
@@ -1043,15 +1043,11 @@ int UtcDaliRenderEffectBlurOnce(void)
 
     // Render effect activated.
     DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
-    tet_printf("order : %d\n", taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex());
-    DALI_TEST_EQUALS(0u, taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex(), TEST_LOCATION);
 
     effect.SetBlurOnce(false);
     DALI_TEST_EQUALS(effect.GetBlurOnce(), false, TEST_LOCATION);
 
     DALI_TEST_EQUALS(4u, taskList.GetTaskCount(), TEST_LOCATION);
-    tet_printf("order : %d\n", taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex());
-    DALI_TEST_EQUALS(0u, taskList.GetTask(taskList.GetTaskCount() - 1).GetOrderIndex(), TEST_LOCATION);
   }
 
   END_TEST;
index 4e09bacf92d080d1198bf10403bb42e48654e71c..b10fe4af3d8fe0a3b31a1555c7098ee76c020f83 100644 (file)
@@ -1978,22 +1978,45 @@ void Control::Impl::SetOffScreenRendering(int32_t offScreenRenderingType)
   }
 
   DevelControl::OffScreenRenderingType newType = static_cast<DevelControl::OffScreenRenderingType>(offScreenRenderingType);
+
+  Dali::Toolkit::Control handle(mControlImpl.GetOwner());
+
   if(newType == DevelControl::OffScreenRenderingType::NONE)
   {
     if(mOffScreenRenderingImpl)
     {
       auto tempOffscreenRenderingImpl = std::move(mOffScreenRenderingImpl);
       tempOffscreenRenderingImpl->ClearOwnerControl();
+
+      RegisteredVisualContainer& visuals = mVisualData->mVisuals;
+      for(auto it = visuals.begin(); it != visuals.end(); it++)
+      {
+        if((*it)->visual.GetDepthIndex() <= DepthIndex::BACKGROUND_EFFECT)
+        {
+          Renderer renderer = Toolkit::GetImplementation((*it)->visual).GetRenderer();
+          handle.RemoveCacheRenderer(renderer);
+          handle.AddRenderer(renderer);
+        }
+      }
     }
   }
   else if(mOffScreenRenderingType == DevelControl::OffScreenRenderingType::NONE)
   {
     mOffScreenRenderingImpl = std::make_unique<OffScreenRenderingImpl>(newType);
-
-    Dali::Toolkit::Control handle(mControlImpl.GetOwner());
     mOffScreenRenderingImpl->SetOwnerControl(handle);
+
+    RegisteredVisualContainer& visuals = mVisualData->mVisuals;
+    for(auto it = visuals.begin(); it != visuals.end(); it++)
+    {
+      if((*it)->visual.GetDepthIndex() <= DepthIndex::BACKGROUND_EFFECT)
+      {
+        Renderer renderer = Toolkit::GetImplementation((*it)->visual).GetRenderer();
+        handle.RemoveRenderer(renderer);
+        handle.AddCacheRenderer(renderer);
+      }
+    }
   }
-  else
+  else if(mOffScreenRenderingType != newType)
   {
     mOffScreenRenderingImpl->SetType(newType);
   }
index 2233900b23f1f8873c8b3d43a6a0095df0ef4e67..73e8e6c85f83cf6c3f0c131c764e4dbb461115f7 100644 (file)
@@ -210,20 +210,69 @@ void DiscardVisual(RegisteredVisualContainer::Iterator sourceIter, RegisteredVis
   source.Erase(sourceIter);
 }
 
+/**
+ * @brief Set visual on scene
+ * When offscreen rendering(using cache renderer) is on, it only includes control's context area.
+ * Swap the renderer's location to cache renderers for outer-drawn visuals.
+ * When depth index is lower than background effect(i.e. shadow visual), it is probably drawn out of control's context area.
+ * @param[in] visualImpl The visual
+ * @param[in] controlImpl Actor with renderers
+ * @note Changing depth index may not instantaneously update the visuals. (i.e. Turn offscreen rendering off and on again.)
+ */
+void SetVisualOnScene(Internal::Visual::Base& visualImpl, Internal::Control& controlImpl)
+{
+  Actor self = controlImpl.Self();
+  visualImpl.SetOnScene(self);
+
+  Toolkit::Control                     handle                 = Toolkit::Control(controlImpl.GetOwner());
+  DevelControl::OffScreenRenderingType offscreenRenderingType = DevelControl::OffScreenRenderingType(handle.GetProperty<int32_t>(DevelControl::Property::OFFSCREEN_RENDERING));
+  if(offscreenRenderingType != DevelControl::OffScreenRenderingType::NONE)
+  {
+    if(visualImpl.GetDepthIndex() <= DepthIndex::BACKGROUND_EFFECT)
+    {
+      Renderer renderer = visualImpl.GetRenderer();
+      self.RemoveRenderer(renderer);
+      self.AddCacheRenderer(renderer);
+    }
+  }
+}
+
+/**
+ * @brief Remove visual's renderer from cache renderers(offscreen renderers)
+ * @param[in] visualImpl The visual
+ * @param[in] controlImpl Actor with renderers
+ */
+void SetVisualOffScene(Internal::Visual::Base& visualImpl, Internal::Control& controlImpl)
+{
+  Actor self = controlImpl.Self();
+  visualImpl.SetOffScene(self);
+
+  Toolkit::Control                     handle                 = Toolkit::Control(controlImpl.GetOwner());
+  DevelControl::OffScreenRenderingType offscreenRenderingType = DevelControl::OffScreenRenderingType(handle.GetProperty<int32_t>(DevelControl::Property::OFFSCREEN_RENDERING));
+  if(offscreenRenderingType != DevelControl::OffScreenRenderingType::NONE)
+  {
+    if(visualImpl.GetDepthIndex() <= DepthIndex::BACKGROUND_EFFECT)
+    {
+      Renderer renderer = visualImpl.GetRenderer();
+      self.RemoveCacheRenderer(renderer);
+    }
+  }
+}
+
 /**
  * @brief Iterate through given container and setOffScene any visual found
  *
  * @param[in] container Container of visuals
- * @param[in] parent Parent actor to remove visuals from
+ * @param[in] controlImpl Actor to remove visuals from
  */
-void SetVisualsOffScene(const RegisteredVisualContainer& container, Actor parent)
+void SetVisualsOffScene(const RegisteredVisualContainer& container, Internal::Control& controlImpl)
 {
   for(auto iter = container.Begin(), end = container.End(); iter != end; iter++)
   {
     if((*iter)->visual)
     {
       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Control::SetOffScene Setting visual(%d) off stage\n", (*iter)->index);
-      Toolkit::GetImplementation((*iter)->visual).SetOffScene(parent);
+      SetVisualOffScene(Toolkit::GetImplementation((*iter)->visual), controlImpl);
     }
   }
 }
@@ -247,14 +296,14 @@ void Control::Impl::VisualData::ConnectScene(Actor parent)
     if((*iter)->visual && (*iter)->enabled)
     {
       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Control::OnSceneConnection Setting visual(%d) on scene\n", (*iter)->index);
-      Toolkit::GetImplementation((*iter)->visual).SetOnScene(parent);
+      SetVisualOnScene(Toolkit::GetImplementation((*iter)->visual), mOuter.mControlImpl);
     }
   }
 }
 
 void Control::Impl::VisualData::ClearScene(Actor parent)
 {
-  SetVisualsOffScene(mVisuals, parent);
+  SetVisualsOffScene(mVisuals, mOuter.mControlImpl);
 
   if(!mRemoveVisuals.Empty())
   {
@@ -263,7 +312,7 @@ void Control::Impl::VisualData::ClearScene(Actor parent)
     while(!mRemoveVisuals.Empty())
     {
       auto removalIter = mRemoveVisuals.End() - 1u;
-      Toolkit::GetImplementation((*removalIter)->visual).SetOffScene(parent);
+      SetVisualOffScene(Toolkit::GetImplementation((*removalIter)->visual), mOuter.mControlImpl);
 
       // Discard removed visual. It will be destroyed at next Idle time.
       DiscardVisual(removalIter, mRemoveVisuals);
@@ -281,8 +330,6 @@ void Control::Impl::VisualData::ResourceReady(Visual::Base& object)
 {
   DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Control::Impl::VisualData::ResourceReady() replacements pending[%d]\n", mRemoveVisuals.Count());
 
-  Actor self = mOuter.mControlImpl.Self();
-
   RegisteredVisualContainer::Iterator registeredIter;
 
   // A resource is ready, find resource in the registered visuals container and get its index
@@ -301,7 +348,7 @@ void Control::Impl::VisualData::ResourceReady(Visual::Base& object)
     (*registeredIter)->pending = false;
     if(!((*visualToRemoveIter)->overideReadyTransition))
     {
-      Toolkit::GetImplementation((*visualToRemoveIter)->visual).SetOffScene(self);
+      SetVisualOffScene(Toolkit::GetImplementation((*visualToRemoveIter)->visual), mOuter.mControlImpl);
     }
 
     // Discard removed visual. It will be destroyed at next Idle time.
@@ -454,7 +501,8 @@ void Control::Impl::VisualData::RegisterVisual(Property::Index index, Toolkit::V
           {
             // Visual with same index is already in removal container so current visual pending
             // Only the the last requested visual will be displayed so remove current visual which is staged but not ready.
-            Toolkit::GetImplementation(currentRegisteredVisual).SetOffScene(self);
+            SetVisualOffScene(Toolkit::GetImplementation(currentRegisteredVisual), mOuter.mControlImpl);
+
             mVisuals.Erase(registeredVisualsiter);
           }
           else
@@ -540,7 +588,7 @@ void Control::Impl::VisualData::RegisterVisual(Property::Index index, Toolkit::V
     // Put on stage if enabled and the control is already on the stage
     if((enabled == VisualState::ENABLED) && self.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
     {
-      visualImpl.SetOnScene(self);
+      SetVisualOnScene(visualImpl, mOuter.mControlImpl);
     }
     else if(enabled && visualImpl.IsResourceReady()) // When not being staged, check if visual already 'ResourceReady' before it was Registered. ( Resource may have been loaded already )
     {
@@ -561,16 +609,16 @@ void Control::Impl::VisualData::UnregisterVisual(Property::Index index)
     // stop observing visual
     StopObservingVisual((*iter)->visual);
 
-    Actor self(mOuter.mControlImpl.Self());
-    Toolkit::GetImplementation((*iter)->visual).SetOffScene(self);
+    SetVisualOffScene(Toolkit::GetImplementation((*iter)->visual), mOuter.mControlImpl);
+
     (*iter)->visual.Reset();
     mVisuals.Erase(iter);
   }
 
   if(FindVisual(index, mRemoveVisuals, iter))
   {
-    Actor self(mOuter.mControlImpl.Self());
-    Toolkit::GetImplementation((*iter)->visual).SetOffScene(self);
+    SetVisualOffScene(Toolkit::GetImplementation((*iter)->visual), mOuter.mControlImpl);
+
     (*iter)->pending = false;
 
     // Discard removed visual. It will be destroyed at next Idle time.
@@ -611,19 +659,19 @@ void Control::Impl::VisualData::EnableVisual(Property::Index index, bool enable)
       return;
     }
 
-    (*iter)->enabled  = enable;
-    Actor parentActor = mOuter.mControlImpl.Self();
+    (*iter)->enabled = enable;
     if(mOuter.mControlImpl.Self().GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE)) // If control not on Scene then Visual will be added when SceneConnection is called.
     {
       if(enable)
       {
         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Control::EnableVisual Setting %s(%d) on stage \n", (*iter)->visual.GetName().c_str(), index);
-        Toolkit::GetImplementation((*iter)->visual).SetOnScene(parentActor);
+        SetVisualOnScene(Toolkit::GetImplementation((*iter)->visual), mOuter.mControlImpl);
       }
       else
       {
         DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Control::EnableVisual Setting %s(%d) off stage \n", (*iter)->visual.GetName().c_str(), index);
-        Toolkit::GetImplementation((*iter)->visual).SetOffScene(parentActor); // No need to call if control not staged.
+
+        SetVisualOffScene(Toolkit::GetImplementation((*iter)->visual), mOuter.mControlImpl); // No need to call if control not staged.
       }
     }
   }
@@ -688,8 +736,6 @@ bool Control::Impl::VisualData::IsVisualEnabled(Property::Index index) const
 
 void Control::Impl::VisualData::RemoveVisual(RegisteredVisualContainer& visuals, const std::string& visualName)
 {
-  Actor self(mOuter.mControlImpl.Self());
-
   for(RegisteredVisualContainer::Iterator visualIter = visuals.Begin();
       visualIter != visuals.End();
       ++visualIter)
@@ -697,7 +743,8 @@ void Control::Impl::VisualData::RemoveVisual(RegisteredVisualContainer& visuals,
     Toolkit::Visual::Base visual = (*visualIter)->visual;
     if(visual && visual.GetName() == visualName)
     {
-      Toolkit::GetImplementation(visual).SetOffScene(self);
+      SetVisualOffScene(Toolkit::GetImplementation(visual), mOuter.mControlImpl);
+
       (*visualIter)->visual.Reset();
       visuals.Erase(visualIter);
       break;
@@ -707,7 +754,6 @@ void Control::Impl::VisualData::RemoveVisual(RegisteredVisualContainer& visuals,
 
 void Control::Impl::VisualData::RemoveVisuals(RegisteredVisualContainer& visuals, DictionaryKeys& removeVisuals)
 {
-  Actor self(mOuter.mControlImpl.Self());
   for(DictionaryKeys::iterator iter = removeVisuals.begin(); iter != removeVisuals.end(); ++iter)
   {
     const std::string visualName = *iter;
@@ -726,7 +772,6 @@ void Control::Impl::VisualData::RecreateChangedVisuals(Dictionary<Property::Map>
     const std::string&   visualName = (*iter).key;
     const Property::Map& toMap      = (*iter).entry;
 
-    Actor                               self = mOuter.mControlImpl.Self();
     RegisteredVisualContainer::Iterator registeredVisualsiter;
     // Check if visual (visualName) is already registered, this is the current visual.
     if(FindVisual(visualName, mVisuals, registeredVisualsiter))
@@ -738,6 +783,7 @@ void Control::Impl::VisualData::RecreateChangedVisuals(Dictionary<Property::Map>
         StopObservingVisual(visual);
 
         // If control staged then visuals will be swapped once ready
+        Actor self = mOuter.mControlImpl.Self();
         if(self.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
         {
           // Check if visual is currently in the process of being replaced ( is in removal container )
@@ -746,7 +792,8 @@ void Control::Impl::VisualData::RecreateChangedVisuals(Dictionary<Property::Map>
           {
             // Visual with same visual name is already in removal container so current visual pending
             // Only the the last requested visual will be displayed so remove current visual which is staged but not ready.
-            Toolkit::GetImplementation(visual).SetOffScene(self);
+            SetVisualOffScene(Toolkit::GetImplementation(visual), mOuter.mControlImpl);
+
             (*registeredVisualsiter)->visual.Reset();
             mVisuals.Erase(registeredVisualsiter);
           }
index e62bae4e16db14ea554f738a2d2a3db4d798d908..bc58f9eae00a3b4b2619bdb284105b77fe4cb466 100644 (file)
@@ -106,6 +106,22 @@ OffScreenRenderable::Type GaussianBlurEffectImpl::GetOffScreenRenderableType()
 
 void GaussianBlurEffectImpl::GetOffScreenRenderTasks(std::vector<Dali::RenderTask>& tasks, bool isForward)
 {
+  tasks.clear();
+  if(isForward)
+  {
+    if(mSourceRenderTask)
+    {
+      tasks.push_back(mSourceRenderTask);
+    }
+    if(mHorizontalBlurTask)
+    {
+      tasks.push_back(mHorizontalBlurTask);
+    }
+    if(mVerticalBlurTask)
+    {
+      tasks.push_back(mVerticalBlurTask);
+    }
+  }
 }
 
 void GaussianBlurEffectImpl::SetBlurOnce(bool blurOnce)
@@ -335,7 +351,7 @@ void GaussianBlurEffectImpl::OnActivate()
   // Inject blurred output to control
   Renderer renderer = GetTargetRenderer();
   renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, Dali::Toolkit::DepthIndex::FOREGROUND_EFFECT);
-  ownerControl.GetImplementation().SetCacheRenderer(renderer);
+  ownerControl.AddCacheRenderer(renderer);
   ownerControl.GetImplementation().RegisterOffScreenRenderableType(OffScreenRenderable::Type::FORWARD);
   SetRendererTexture(renderer, mBlurredOutputFrameBuffer);
 
@@ -356,7 +372,8 @@ void GaussianBlurEffectImpl::OnDeactivate()
   auto ownerControl = GetOwnerControl();
   if(DALI_LIKELY(ownerControl))
   {
-    ownerControl.GetImplementation().RemoveCacheRenderer();
+    Renderer renderer = GetTargetRenderer();
+    ownerControl.RemoveCacheRenderer(renderer);
     ownerControl.GetImplementation().UnregisterOffScreenRenderableType(OffScreenRenderable::Type::FORWARD);
   }
   DALI_LOG_INFO(gRenderEffectLogFilter, Debug::General, "[BlurEffect:%p] OnDeactivated! [ID:%d]\n", this, ownerControl ? ownerControl.GetProperty<int>(Actor::Property::ID) : -1);
index 5387bd7d61e47d12307248d157a98ac48d8256c7..3b32f38ea0c354d43905ad8f4d45cbbab7e1948e 100644 (file)
@@ -141,7 +141,7 @@ void MaskEffectImpl::OnActivate()
 
   ownerControl.Add(mCamera);
   Renderer maskRenderer = GetTargetRenderer();
-  ownerControl.GetImplementation().SetCacheRenderer(maskRenderer);
+  ownerControl.AddCacheRenderer(maskRenderer);
   ownerControl.GetImplementation().RegisterOffScreenRenderableType(OffScreenRenderable::Type::FORWARD);
 
   Vector2 size = GetTargetSize();
@@ -169,7 +169,8 @@ void MaskEffectImpl::OnDeactivate()
   Toolkit::Control control = GetOwnerControl();
   if(DALI_LIKELY(control))
   {
-    control.GetImplementation().RemoveCacheRenderer();
+    Renderer maskRenderer = GetTargetRenderer();
+    control.RemoveCacheRenderer(maskRenderer);
     control.GetImplementation().UnregisterOffScreenRenderableType(OffScreenRenderable::Type::FORWARD);
   }
 
index f9574743f289671a764c85eace67e3edd8183534..0e64c5d429a1f6e90c5244465faf6138004ddb40 100644 (file)
@@ -93,7 +93,7 @@ void OffScreenRenderingImpl::OnActivate()
   SetRendererTexture(renderer, mFrameBuffer);
 
   Toolkit::Control control = GetOwnerControl();
-  control.GetImplementation().SetCacheRenderer(renderer);
+  control.AddCacheRenderer(renderer);
   control.GetImplementation().RegisterOffScreenRenderableType(OffScreenRenderable::Type::FORWARD);
   mRenderTask.SetScreenToFrameBufferMappingActor(control);
 }
@@ -103,7 +103,8 @@ void OffScreenRenderingImpl::OnDeactivate()
   Toolkit::Control control = GetOwnerControl();
   if(DALI_LIKELY(control))
   {
-    control.GetImplementation().RemoveCacheRenderer();
+    Renderer renderer = GetTargetRenderer();
+    control.RemoveCacheRenderer(renderer);
     control.GetImplementation().UnregisterOffScreenRenderableType(OffScreenRenderable::Type::FORWARD);
 
     mCamera.Unparent();