Add Offscreen rendering context 54/315454/16
authorjmm <j0064423.lee@samsung.com>
Mon, 2 Dec 2024 05:58:58 +0000 (14:58 +0900)
committerjmm <j0064423.lee@samsung.com>
Wed, 11 Dec 2024 05:29:11 +0000 (14:29 +0900)
Change-Id: I817640fd91b798afb5dd680fe8f7e47bf862e85a
Signed-off-by: jmm <j0064423.lee@samsung.com>
automated-tests/src/dali-toolkit/utc-Dali-Control.cpp
automated-tests/src/dali-toolkit/utc-Dali-ControlImpl.cpp
dali-toolkit/devel-api/controls/control-devel.h
dali-toolkit/internal/controls/control/control-data-impl.cpp
dali-toolkit/internal/controls/control/control-data-impl.h
dali-toolkit/internal/controls/render-effects/offscreen-rendering-context.cpp [new file with mode: 0644]
dali-toolkit/internal/controls/render-effects/offscreen-rendering-context.h [new file with mode: 0644]
dali-toolkit/internal/file.list
dali-toolkit/public-api/controls/control-impl.cpp

index 8000f62b11427e0aacce89f910d3b2a329c7e2fa..52c7e0fb26a1dbf224d11739111305462c3b1a69 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.
@@ -1491,4 +1491,32 @@ int UtcDaliControlDoActionMultipleWhenNotStage03(void)
   }
 
   END_TEST;
-}
\ No newline at end of file
+}
+
+int UtcDaliControlOffScreenRendering(void)
+{
+  ToolkitTestApplication application;
+
+  Control control = Control::New();
+  control.SetProperty(Actor::Property::SIZE, Vector2(100.0f, 100.0f));
+  control.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
+  application.GetScene().Add(control);
+  control.SetBackgroundColor(Color::RED);
+  DALI_TEST_EQUALS(control.GetProperty(DevelControl::Property::OFFSCREEN_RENDERING).Get<int>(), (int)DevelControl::OffScreenRenderingType::NONE, TEST_LOCATION);
+
+  control.SetProperty(DevelControl::Property::OFFSCREEN_RENDERING, DevelControl::OffScreenRenderingType::REFRESH_ALWAYS);
+  DALI_TEST_EQUALS(control.GetProperty(DevelControl::Property::OFFSCREEN_RENDERING).Get<int>(), (int)DevelControl::OffScreenRenderingType::REFRESH_ALWAYS, TEST_LOCATION);
+  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);
+
+  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);
+
+  control.Unparent(); // Disconnect fron scene.
+  application.GetScene().Add(control);
+
+  END_TEST;
+}
index 089fd8f4e30c50cc888464dfca749c668118f816..9ad093dbed69942b32756585774fc426fae4caaa 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.
index 3fe85d970f7c9622acc4880c00f5a67eba64cf00..4a9fbdac54f9d49d037326bcf42e97edca72c210 100644 (file)
@@ -71,6 +71,14 @@ typedef Signal<void(std::pair<Dali::Accessibility::GestureInfo, bool>&)> Accessi
 /// @brief AccessibilityAction signal type.
 typedef Signal<bool(const Dali::Accessibility::ActionInfo&)> AccessibilityActionSignalType;
 
+enum OffScreenRenderingType
+{
+  NONE,
+  REFRESH_ONCE,
+  REFRESH_ALWAYS
+};
+constexpr unsigned int OffScreenRenderingTypeCount = 3u;
+
 enum State
 {
   NORMAL,
@@ -249,6 +257,13 @@ enum
    * @details Name "accessibilityIsModal", type Property::BOOLEAN.
    */
   ACCESSIBILITY_IS_MODAL,
+
+  /**
+   * @brief Whether to draw on offscreen of not.
+   * @details Name "offscreenRendering", type Property::BOOLEAN
+   * @note Default is false.
+   */
+  OFFSCREEN_RENDERING
 };
 
 } // namespace Property
index 1cd01b0c09e2c0dc1fabe68456c0319d59f20ecc..480d834ed6c2921ed3ec8aaf057f84ecd1bb03ff 100644 (file)
@@ -372,6 +372,7 @@ const PropertyRegistration Control::Impl::PROPERTY_27(typeRegistration, "accessi
 const PropertyRegistration Control::Impl::PROPERTY_28(typeRegistration, "accessibilityScrollable",        Toolkit::DevelControl::Property::ACCESSIBILITY_SCROLLABLE,         Property::BOOLEAN, &Control::Impl::SetProperty, &Control::Impl::GetProperty);
 const PropertyRegistration Control::Impl::PROPERTY_29(typeRegistration, "accessibilityStates",            Toolkit::DevelControl::Property::ACCESSIBILITY_STATES,             Property::INTEGER, &Control::Impl::SetProperty, &Control::Impl::GetProperty);
 const PropertyRegistration Control::Impl::PROPERTY_30(typeRegistration, "accessibilityIsModal",           Toolkit::DevelControl::Property::ACCESSIBILITY_IS_MODAL,           Property::BOOLEAN, &Control::Impl::SetProperty, &Control::Impl::GetProperty);
+const PropertyRegistration Control::Impl::PROPERTY_31(typeRegistration, "offScreenRendering",             Toolkit::DevelControl::Property::OFFSCREEN_RENDERING,              Property::INTEGER, &Control::Impl::SetProperty, &Control::Impl::GetProperty);
 
 // clang-format on
 
@@ -401,6 +402,8 @@ Control::Impl::Impl(Control& controlImpl)
   mPanGestureDetector(),
   mTapGestureDetector(),
   mLongPressGestureDetector(),
+  mOffScreenRenderingContext(nullptr),
+  mOffScreenRenderingType(DevelControl::OffScreenRenderingType::NONE),
   mTooltip(NULL),
   mInputMethodContext(),
   mIdleCallback(nullptr),
@@ -412,8 +415,8 @@ Control::Impl::Impl(Control& controlImpl)
   mDispatchKeyEvents(true),
   mProcessorRegistered(false)
 {
-  mAccessibilityData = new AccessibilityData(mControlImpl);
-  mVisualData        = new VisualData(*this);
+  mAccessibilityData = std::make_unique<AccessibilityData>(mControlImpl);
+  mVisualData        = std::make_unique<VisualData>(*this);
 }
 
 Control::Impl::~Impl()
@@ -434,9 +437,6 @@ Control::Impl::~Impl()
     // Removes the callback from the callback manager in case the control is destroyed before the callback is executed.
     Adaptor::Get().RemoveIdle(mIdleCallback);
   }
-
-  delete mAccessibilityData;
-  delete mVisualData;
 }
 
 Control::Impl& Control::Impl::Get(Internal::Control& internalControl)
@@ -520,10 +520,37 @@ bool Control::Impl::IsResourceReady() const
   return mVisualData->IsResourceReady();
 }
 
+void Control::Impl::OnSceneConnection()
+{
+  DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Control::OnSceneConnection number of registered visuals(%d)\n", mVisualData->mVisuals.Size());
+
+  Actor self = mControlImpl.Self();
+
+  for(RegisteredVisualContainer::Iterator iter = mVisualData->mVisuals.Begin(); iter != mVisualData->mVisuals.End(); iter++)
+  {
+    // Check whether the visual is empty and enabled
+    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(self);
+    }
+  }
+
+  if(mOffScreenRenderingContext) // mOffScreenRenderingType != NONE
+  {
+    mOffScreenRenderingContext->Enable(Toolkit::Control(mControlImpl.GetOwner()), mOffScreenRenderingType);
+  }
+}
+
 void Control::Impl::OnSceneDisconnection()
 {
   Actor self = mControlImpl.Self();
   mVisualData->ClearScene(self);
+
+  if(mOffScreenRenderingContext)
+  {
+    mOffScreenRenderingContext->Disable(Toolkit::Control(mControlImpl.GetOwner()));
+  }
 }
 
 void Control::Impl::EnableReadyTransitionOverriden(Toolkit::Visual::Base& visual, bool enable)
@@ -968,6 +995,16 @@ void Control::Impl::SetProperty(BaseObject* object, Property::Index index, const
         }
         break;
       }
+
+      case Toolkit::DevelControl::Property::OFFSCREEN_RENDERING:
+      {
+        int32_t offscreenRenderingType;
+        if(value.Get(offscreenRenderingType))
+        {
+          controlImpl.mImpl->SetOffScreenRendering(offscreenRenderingType);
+        }
+        break;
+      }
     }
   }
 }
@@ -1166,6 +1203,12 @@ Property::Value Control::Impl::GetProperty(BaseObject* object, Property::Index i
         value = controlImpl.mImpl->mAccessibilityData->mAccessibilityProps.isModal;
         break;
       }
+
+      case Toolkit::DevelControl::Property::OFFSCREEN_RENDERING:
+      {
+        value = controlImpl.mImpl->mOffScreenRenderingType;
+        break;
+      }
     }
   }
 
@@ -1501,6 +1544,46 @@ void Control::Impl::RegisterProcessorOnce()
   }
 }
 
+void Control::Impl::SetOffScreenRendering(int32_t offScreenRenderingType)
+{
+  // Validate input
+  {
+    constexpr int32_t count = static_cast<int32_t>(DevelControl::OffScreenRenderingTypeCount);
+    if(0 > offScreenRenderingType || offScreenRenderingType >= count)
+    {
+      DALI_LOG_ERROR("Failed to set offscreen rendering. Type index is out of bound.\n");
+      return;
+    }
+  }
+
+  mOffScreenRenderingType = static_cast<DevelControl::OffScreenRenderingType>(offScreenRenderingType);
+  Dali::Toolkit::Control handle(mControlImpl.GetOwner());
+
+  // Disable
+  if(mOffScreenRenderingType == DevelControl::OffScreenRenderingType::NONE)
+  {
+    if(mOffScreenRenderingContext)
+    {
+      mOffScreenRenderingContext->Disable(handle);
+      mOffScreenRenderingContext.reset();
+    }
+  }
+  // Enable
+  else
+  {
+    if(mOffScreenRenderingContext)
+    {
+      mOffScreenRenderingContext->Disable(handle);
+    }
+    else
+    {
+      Dali::Toolkit::Control handle(mControlImpl.GetOwner());
+      mOffScreenRenderingContext = std::make_unique<OffScreenRenderingContext>();
+    }
+    mOffScreenRenderingContext->Enable(handle, mOffScreenRenderingType);
+  }
+}
+
 void Control::Impl::Process(bool postProcessor)
 {
   // Call ApplyFittingMode
index b789cca38ce1000012c26ee72da6253b7f03349c..202e3e4caeaa52253b27a883faabdea8e23be372 100644 (file)
@@ -27,6 +27,7 @@
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/devel-api/controls/control-devel.h>
+#include <dali-toolkit/internal/controls/render-effects/offscreen-rendering-context.h>
 #include <dali-toolkit/internal/controls/tooltip/tooltip.h>
 #include <dali-toolkit/public-api/controls/control-impl.h>
 #include <dali-toolkit/public-api/controls/render-effects/render-effect.h>
@@ -229,6 +230,11 @@ public:
    */
   bool IsResourceReady() const;
 
+  /**
+   * @copydoc CustomActorImpl::OnSceneConnection()
+   */
+  void OnSceneConnection();
+
   /**
    * @copydoc CustomActorImpl::OnSceneDisconnection()
    */
@@ -390,6 +396,12 @@ public:
   */
   void RegisterProcessorOnce();
 
+  /**
+   * Set off-screen rendering.
+   * @param[in] offScreenRenderingType enum OffScreenRenderingType
+   */
+  void SetOffScreenRendering(int32_t offScreenRenderingType);
+
 protected: // From processor-interface
   /**
    * @copydoc Dali::Integration::Processor::Process()
@@ -455,8 +467,8 @@ public:
   DevelControl::State mState;
   std::string         mSubStateName;
 
-  AccessibilityData* mAccessibilityData;
-  VisualData*        mVisualData;
+  std::unique_ptr<AccessibilityData> mAccessibilityData;
+  std::unique_ptr<VisualData>        mVisualData;
 
   int mLeftFocusableActorId;             ///< Actor ID of Left focusable control.
   int mRightFocusableActorId;            ///< Actor ID of Right focusable control.
@@ -483,6 +495,10 @@ public:
   TapGestureDetector       mTapGestureDetector;
   LongPressGestureDetector mLongPressGestureDetector;
 
+  // Off screen rendering context
+  std::unique_ptr<OffScreenRenderingContext> mOffScreenRenderingContext;
+  DevelControl::OffScreenRenderingType       mOffScreenRenderingType;
+
   // Tooltip
   TooltipPtr mTooltip;
 
@@ -528,6 +544,7 @@ public:
   static const PropertyRegistration PROPERTY_28;
   static const PropertyRegistration PROPERTY_29;
   static const PropertyRegistration PROPERTY_30;
+  static const PropertyRegistration PROPERTY_31;
 };
 
 } // namespace Internal
diff --git a/dali-toolkit/internal/controls/render-effects/offscreen-rendering-context.cpp b/dali-toolkit/internal/controls/render-effects/offscreen-rendering-context.cpp
new file mode 100644 (file)
index 0000000..a057add
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/controls/render-effects/offscreen-rendering-context.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/control/control-renderers.h>
+#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
+
+// EXTERNAL INCLUDES
+#include <integration-api/debug.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+bool OffScreenRenderingContext::IsEnabled()
+{
+  return mCamera && mRenderer;
+}
+
+void OffScreenRenderingContext::Enable(Toolkit::Control control, DevelControl::OffScreenRenderingType type)
+{
+  if(type == DevelControl::OffScreenRenderingType::NONE)
+  {
+    return;
+  }
+
+  // Create resources
+  if(!IsEnabled())
+  {
+    Integration::SceneHolder sceneHolder = Integration::SceneHolder::Get(control);
+    if(DALI_UNLIKELY(!sceneHolder))
+    {
+      DALI_LOG_ERROR("Could not enable offscreen rendering. The control is not connected to a scene.\n");
+      return;
+    }
+    mSceneHolder = sceneHolder;
+
+    const Size size = control.GetProperty<Size>(Actor::Property::SIZE);
+
+    mCamera = CameraActor::New(size);
+    mCamera.SetInvertYAxis(true);
+    mCamera.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+    mCamera.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+    mCamera.SetType(Camera::FREE_LOOK);
+    mCamera.SetPerspectiveProjection(size);
+    control.Add(mCamera);
+
+    mFrameBuffer    = FrameBuffer::New(size.width, size.height, FrameBuffer::Attachment::DEPTH_STENCIL);
+    Texture texture = Texture::New(TextureType::TEXTURE_2D, Pixel::RGBA8888, size.width, size.height);
+    mFrameBuffer.AttachColorTexture(texture);
+
+    RenderTaskList taskList = sceneHolder.GetRenderTaskList();
+    mRenderTask             = taskList.CreateTask();
+    mRenderTask.SetSourceActor(control);
+    mRenderTask.SetCameraActor(mCamera);
+    mRenderTask.SetExclusive(true);
+    mRenderTask.SetInputEnabled(false);
+    mRenderTask.SetFrameBuffer(mFrameBuffer);
+    mRenderTask.SetClearEnabled(true);
+    mRenderTask.SetClearColor(sceneHolder.GetBackgroundColor());
+
+    mRenderer = CreateRenderer(SHADER_CONTROL_RENDERERS_VERT, SHADER_CONTROL_RENDERERS_FRAG);
+    SetRendererTexture(mRenderer, mFrameBuffer);
+
+    control.GetImplementation().SetCacheRenderer(mRenderer);
+    control.GetImplementation().SetOffScreenRenderableType(OffScreenRenderable::Type::FORWARD);
+  }
+
+  // Set refresh rate
+  if(type == DevelControl::OffScreenRenderingType::REFRESH_ALWAYS)
+  {
+    mRenderTask.SetRefreshRate(RenderTask::REFRESH_ALWAYS);
+  }
+  else if(type == DevelControl::OffScreenRenderingType::REFRESH_ONCE)
+  {
+    mRenderTask.SetRefreshRate(RenderTask::REFRESH_ONCE);
+  }
+}
+
+void OffScreenRenderingContext::Disable(Toolkit::Control control)
+{
+  if(IsEnabled())
+  {
+    if(DALI_LIKELY(control))
+    {
+      control.GetImplementation().RemoveCacheRenderer();
+
+      mCamera.Unparent();
+      control.GetImplementation().SetOffScreenRenderableType(OffScreenRenderable::Type::NONE);
+    }
+
+    auto sceneHolder = mSceneHolder.GetHandle();
+    if(DALI_LIKELY(sceneHolder))
+    {
+      RenderTaskList taskList = sceneHolder.GetRenderTaskList();
+      taskList.RemoveTask(mRenderTask);
+    }
+    mSceneHolder.Reset();
+
+    mFrameBuffer.Reset();
+    mRenderTask.Reset();
+    mCamera.Reset();
+    mRenderer.Reset();
+  }
+}
+
+} // namespace Internal
+} // namespace Toolkit
+} // namespace Dali
diff --git a/dali-toolkit/internal/controls/render-effects/offscreen-rendering-context.h b/dali-toolkit/internal/controls/render-effects/offscreen-rendering-context.h
new file mode 100644 (file)
index 0000000..6299d12
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef DALI_TOOLKIT_INTERNAL_OFFSCREEN_RENDERING_CONTEXT
+#define DALI_TOOLKIT_INTERNAL_OFFSCREEN_RENDERING_CONTEXT
+
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/adaptor-framework/scene-holder.h>
+#include <dali/public-api/actors/actor.h>
+#include <dali/public-api/actors/camera-actor.h>
+#include <dali/public-api/object/weak-handle.h>
+#include <dali/public-api/render-tasks/render-task-list.h>
+#include <dali/public-api/render-tasks/render-task.h>
+#include <dali/public-api/rendering/frame-buffer.h>
+#include <dali/public-api/rendering/renderer.h>
+#include <integration-api/adaptor-framework/scene-holder.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/devel-api/controls/control-devel.h>
+#include <dali-toolkit/public-api/controls/control.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+class OffScreenRenderingContext
+{
+public:
+  OffScreenRenderingContext() = default;
+  void Enable(Toolkit::Control control, DevelControl::OffScreenRenderingType type);
+  void Disable(Toolkit::Control control);
+
+private:
+  bool IsEnabled();
+
+private:
+  RenderTask  mRenderTask;
+  CameraActor mCamera;
+  FrameBuffer mFrameBuffer;
+  Renderer    mRenderer;
+
+  WeakHandle<Integration::SceneHolder> mSceneHolder;
+};
+} // namespace Internal
+} // namespace Toolkit
+} // namespace Dali
+#endif //DALI_TOOLKIT_INTERNAL_OFFSCREEN_RENDERING_CONTEXT
index 54ac246b1629689727fa6e37e369c94676fe60e0..12d902d03177de82f2e29d027f6f5edcd5691d41 100644 (file)
@@ -71,6 +71,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/controls/alignment/alignment-impl.cpp
    ${toolkit_src_dir}/controls/render-effects/render-effect-impl.cpp
    ${toolkit_src_dir}/controls/render-effects/blur-effect-impl.cpp
+   ${toolkit_src_dir}/controls/render-effects/offscreen-rendering-context.cpp
    ${toolkit_src_dir}/controls/bloom-view/bloom-view-impl.cpp
    ${toolkit_src_dir}/controls/bubble-effect/bubble-emitter-impl.cpp
    ${toolkit_src_dir}/controls/bubble-effect/bubble-renderer.cpp
index db04318050ea9d6649817bd5873c44be3bb5bc26..827a3955ba14630a4b78d1250b57a1017882703e 100644 (file)
@@ -574,19 +574,7 @@ void Control::EmitKeyInputFocusSignal(bool focusGained)
 
 void Control::OnSceneConnection(int depth)
 {
-  DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Control::OnSceneConnection number of registered visuals(%d)\n", mImpl->mVisualData->mVisuals.Size());
-
-  Actor self(Self());
-
-  for(RegisteredVisualContainer::Iterator iter = mImpl->mVisualData->mVisuals.Begin(); iter != mImpl->mVisualData->mVisuals.End(); iter++)
-  {
-    // Check whether the visual is empty and enabled
-    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(self);
-    }
-  }
+  mImpl->OnSceneConnection();
 
   // The clipping renderer is only created if required.
   CreateClippingRenderer(*this);