/*
- * Copyright (c) 2022 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.
#include "dali-test-suite-utils/dali-test-suite-utils.h"
#include "test-custom-actor.h"
+#include <mesh-builder.h>
+
using namespace Dali;
namespace Test
namespace Impl
{
-
class OffScreenCustomActor : public UnregisteredCustomActor
{
public:
Dali::Integration::Scene scene = Dali::Integration::Scene::Get(Self());
if(scene)
{
- mScene = scene;
- RenderTaskList taskList = scene.GetRenderTaskList();
- mForwardRenderTask = taskList.CreateTask();
- mBackwardRenderTask = taskList.CreateTask();
- FrameBuffer forwardFrameBuffer = FrameBuffer::New(1, 1);
+ mScene = scene;
+ RenderTaskList taskList = scene.GetRenderTaskList();
+ mForwardRenderTask = taskList.CreateTask();
+ mBackwardRenderTask = taskList.CreateTask();
+ FrameBuffer forwardFrameBuffer = FrameBuffer::New(1, 1);
FrameBuffer backwardFrameBuffer = FrameBuffer::New(1, 1);
mForwardRenderTask.SetFrameBuffer(forwardFrameBuffer);
* G(Forward) - F(Backward) - E(Forward) - J(BACKWARD) - B(BACKWARD) - E(BACKWARD)
*/
- Layer A_layer = Layer::New();
+ Layer A_layer = Layer::New();
OffScreenCustomActor B_offScreenCustomActor = OffScreenCustomActor::New(OffScreenRenderable::Type::BACKWARD);
- Layer C_layer = Layer::New();
- Actor D_actor = Actor::New();
+ Layer C_layer = Layer::New();
+ Actor D_actor = Actor::New();
OffScreenCustomActor E_offScreenCustomActor = OffScreenCustomActor::New(OffScreenRenderable::Type::BOTH);
OffScreenCustomActor F_offScreenCustomActor = OffScreenCustomActor::New(OffScreenRenderable::Type::BACKWARD);
OffScreenCustomActor G_offScreenCustomActor = OffScreenCustomActor::New(OffScreenRenderable::Type::FORWARD);
- Actor H_actor = Actor::New();
- Actor I_actor = Actor::New();
+ Actor H_actor = Actor::New();
+ Actor I_actor = Actor::New();
OffScreenCustomActor J_offScreenCustomActor = OffScreenCustomActor::New(OffScreenRenderable::Type::BACKWARD);
A_layer.Add(B_offScreenCustomActor);
* C(Backward) - B(Backward)
*/
- Layer A_layer = Layer::New();
+ Layer A_layer = Layer::New();
OffScreenCustomActor B_offScreenCustomActor = OffScreenCustomActor::New(OffScreenRenderable::Type::BACKWARD);
OffScreenCustomActor C_offScreenCustomActor = OffScreenCustomActor::New(OffScreenRenderable::Type::BACKWARD);
DALI_TEST_CHECK(B_offScreenCustomActor.GetBackwardRenderTask().GetOrderIndex() < C_offScreenCustomActor.GetBackwardRenderTask().GetOrderIndex());
END_TEST;
-}
\ No newline at end of file
+}
+
+int UtcDaliCustomActorImplSetRemoveCacheRenderer(void)
+{
+ TestApplication application;
+ DALI_TEST_EQUALS(application.GetScene().GetRenderTaskList().GetTaskCount(), 1, TEST_LOCATION);
+
+ DerivedCustomActor customActor = DerivedCustomActor::New();
+
+ Geometry geometry = CreateQuadGeometry();
+ Shader shader = CreateShader();
+ Renderer renderer = Renderer::New(geometry, shader);
+ Renderer renderer2 = Renderer::New(geometry, shader);
+
+ customActor.AddRenderer(renderer);
+ customActor.GetImplementation().SetCacheRenderer(renderer);
+ customActor.GetImplementation().SetCacheRenderer(renderer2);
+ customActor.GetImplementation().SetCacheRenderer(renderer2);
+ DALI_TEST_EQUALS(customActor.GetRendererCount(), 1, TEST_LOCATION); // cache renderer is not counted.
+
+ customActor.RemoveRenderer(0u);
+ customActor.GetImplementation().SetCacheRenderer(renderer);
+
+ application.SendNotification();
+ application.Render();
+
+ customActor.GetImplementation().RemoveCacheRenderer();
+ customActor.GetImplementation().RemoveCacheRenderer();
+ application.SendNotification();
+ application.Render();
+
+ customActor.GetImplementation().SetCacheRenderer(renderer2);
+
+ tet_result(TET_PASS);
+ END_TEST;
+}
}
}
+void Actor::SetCacheRenderer(Renderer& renderer)
+{
+ const SceneGraph::Node& node = GetNode();
+
+ if(mCacheRenderer)
+ {
+ if(mCacheRenderer == RendererPtr(&renderer))
+ {
+ return;
+ }
+ RemoveCacheRenderer();
+ }
+
+ if(mIsBlendEquationSet)
+ {
+ renderer.SetBlendEquation(mBlendEquation);
+ }
+ mCacheRenderer = RendererPtr(&renderer);
+ AttachCacheRendererMessage(GetEventThreadServices().GetUpdateManager(), node, renderer.GetRendererSceneObject());
+}
+
+void Actor::RemoveCacheRenderer()
+{
+ if(DALI_LIKELY(mCacheRenderer))
+ {
+ const SceneGraph::Node& node = GetNode();
+ DetachCacheRendererMessage(GetEventThreadServices(), node, mCacheRenderer->GetRendererSceneObject());
+ mCacheRenderer.Reset();
+ }
+}
+
Actor::Actor(DerivedType derivedType, const SceneGraph::Node& node)
: Object(&node),
mParentImpl(*this),
// Detach all renderers before delete container.
mRenderers->RemoveAll(GetNode());
}
+ if(mCacheRenderer)
+ {
+ RemoveCacheRenderer();
+ }
// Root layer will destroy its node in its own destructor
if(!mIsRoot)
// Instruct each actor to create a corresponding node in the scene graph
ConnectToScene(parentActor->GetHierarchyDepth(), parentActor->GetLayer3DParentCount(), notify);
- Actor* actor = this;
+ Actor* actor = this;
// OnScene should be checked, this actor can be removed during OnSceneConnection.
emitInheritedVisible = OnScene() && mScene->IsVisible();
while(emitInheritedVisible && actor)
*/
void RequestRenderTaskReorder();
+ // For offscreen rendering
+ /**
+ * @brief Set renderer drawing cached output to this Actor.
+ * @SINCE_2_3.54
+ * @param[in] renderer Renderer to set to the Actor
+ * @pre The renderer must be initialized.
+ */
+ void SetCacheRenderer(Renderer& renderer);
+
+ /**
+ * @brief Removes cache renderer from the Actor.
+ * @SINCE_2_3.54
+ */
+ void RemoveCacheRenderer();
+
protected:
enum DerivedType
{
void SetUpdateAreaHint(const Vector4& updateAreaHint);
protected:
- ActorParentImpl mParentImpl; ///< Implementation of ActorParent;
- ActorSizer mSizer; ///< Implementation for managing actor size
- ActorParent* mParent; ///< Each actor (except the root) can have one parent
- Scene* mScene; ///< The scene the actor is added to
- RendererContainer* mRenderers; ///< Renderer container
- Vector3* mParentOrigin; ///< NULL means ParentOrigin::DEFAULT. ParentOrigin is non-animatable
- Vector3* mAnchorPoint; ///< NULL means AnchorPoint::DEFAULT. AnchorPoint is non-animatable
- ActorGestureData* mGestureData; ///< Optional Gesture data. Only created when actor requires gestures
+ ActorParentImpl mParentImpl; ///< Implementation of ActorParent;
+ ActorSizer mSizer; ///< Implementation for managing actor size
+ ActorParent* mParent; ///< Each actor (except the root) can have one parent
+ Scene* mScene; ///< The scene the actor is added to
+ RendererContainer* mRenderers; ///< Renderer container
+ RendererPtr mCacheRenderer; ///< Result of offscreen rendering
+ Vector3* mParentOrigin; ///< NULL means ParentOrigin::DEFAULT. ParentOrigin is non-animatable
+ Vector3* mAnchorPoint; ///< NULL means AnchorPoint::DEFAULT. AnchorPoint is non-animatable
+ ActorGestureData* mGestureData; ///< Optional Gesture data. Only created when actor requires gestures
// Signals
Dali::Actor::TouchEventSignalType mInterceptTouchedSignal;
/*
- * 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/event/actors/custom-actor-internal.h>
+
+// INTERNAL INCLUDES
#include <dali/public-api/object/type-info.h>
namespace Dali
#define DALI_INTERNAL_CUSTOM_ACTOR_H
/*
- * Copyright (c) 2021 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.
mImpl->OnSizeAnimation(animationHandle, targetSize);
}
- void GetOffScreenRenderTasks(std::vector<Dali::RenderTask>& tasks, bool isForward)
+ void GetOffScreenRenderTasks(std::vector<Dali::RenderTask>& tasks, bool isForward) override
{
mImpl->GetOffScreenRenderTasks(tasks, isForward);
}
node.SetUpdated(true);
}
+ RendererKey cacheRenderer = node.GetCacheRenderer();
+ const bool isNodeExclusiveAtAnotherRenderTask = node.GetExclusiveRenderTaskCount() && !node.IsExclusiveRenderTask(&renderTask);
+
// Check whether node is exclusive to a different render-task
- if(node.GetExclusiveRenderTaskCount() && !node.IsExclusiveRenderTask(&renderTask))
+ // Check if node has pre-drawn cache to draw
+ if(isNodeExclusiveAtAnotherRenderTask && !cacheRenderer)
{
return;
}
node.SetClippingInformation(currentClippingId, clippingDepth, scissorDepth);
RenderableContainer& target = DALI_LIKELY(inheritedDrawMode == DrawMode::NORMAL) ? layer->colorRenderables : layer->overlayRenderables;
+
+ if(isNodeExclusiveAtAnotherRenderTask && cacheRenderer)
+ {
+ target.PushBack(Renderable(&node, cacheRenderer));
+ return;
+ }
+
for(uint32_t i = 0; i < count; ++i)
{
SceneGraph::RendererKey rendererKey = node.GetRendererAt(i);
mImpl->renderersAdded = true;
}
+void UpdateManager::AttachCacheRenderer(Node* node, Renderer* renderer)
+{
+ node->SetCacheRenderer(Renderer::GetKey(renderer));
+ mImpl->renderersAdded = true;
+}
+
void UpdateManager::SetPanGestureProcessor(PanGesture* panGestureProcessor)
{
DALI_ASSERT_DEBUG(NULL != panGestureProcessor);
*/
void AttachRenderer(Node* node, Renderer* renderer);
+ /**
+ * Attach cache renderer to node
+ * @param[in] node target
+ * @param[in] renderer to attach
+ */
+ void AttachCacheRenderer(Node* node, Renderer* renderer);
+
// Gestures
/**
new(slot) LocalType(&manager, &UpdateManager::AttachRenderer, const_cast<Node*>(&node), const_cast<Renderer*>(&renderer));
}
+inline void AttachCacheRendererMessage(UpdateManager& manager, const Node& node, const Renderer& renderer)
+{
+ using LocalType = MessageValue2<UpdateManager, Node*, Renderer*>;
+
+ // Reserve some memory inside the message queue
+ uint32_t* slot = manager.ReserveMessageSlot(sizeof(LocalType));
+ // Construct message in the message queue memory; note that delete should not be called on the return value
+ new(slot) LocalType(&manager, &UpdateManager::AttachCacheRenderer, const_cast<Node*>(&node), const_cast<Renderer*>(&renderer));
+}
+
// The render thread can safely change the Shader
inline void AddTextureSetMessage(UpdateManager& manager, OwnerPointer<TextureSet>& textureSet)
{
}
}
+void Node::SetCacheRenderer(const RendererKey& renderer)
+{
+ if(DALI_UNLIKELY(mCacheRenderer))
+ {
+ mCacheRenderer->DetachFromNodeDataProvider(*this);
+ }
+ SetUpdated(true);
+ mCacheRenderer = renderer;
+}
+
+void Node::RemoveCacheRenderer()
+{
+ if(DALI_LIKELY(mCacheRenderer))
+ {
+ mCacheRenderer->DetachFromNodeDataProvider(*this);
+ SetUpdated(true);
+ mCacheRenderer = RendererKey{};
+ }
+}
+
NodePropertyFlags Node::GetDirtyFlags() const
{
// get initial dirty flags, they are reset ResetDefaultProperties, but setters may have made the node dirty already
return static_cast<uint32_t>(mRenderers.Size());
}
+ /**
+ * Set cache renderer which draws output of offscreen rendering
+ * Draws only when mOffScreenRendering is true
+ */
+ void SetCacheRenderer(const RendererKey& renderer);
+
+ /**
+ * Remove cache renderer
+ */
+ void RemoveCacheRenderer();
+
+ /**
+ * Get cache renderer
+ */
+ RendererKey GetCacheRenderer() const
+ {
+ return mCacheRenderer;
+ }
+
// Containment methods
/**
Node* mParent; ///< Pointer to parent node (a child is owned by its parent)
RenderTaskContainer mExclusiveRenderTasks; ///< Nodes can be marked as exclusive to multiple RenderTasks
- RendererContainer mRenderers; ///< Container of renderers; not owned
+ RendererContainer mRenderers; ///< Container of renderers; not owned
+ RendererKey mCacheRenderer; ///< Result of offscreen rendering
NodeContainer mChildren; ///< Container of children; not owned
new(slot) LocalType(&node, &Node::RemoveRenderer, Renderer::GetKey(renderer));
}
+inline void DetachCacheRendererMessage(EventThreadServices& eventThreadServices, const Node& node, const Renderer& renderer)
+{
+ using LocalType = Message<Node>;
+
+ // Reserve some memory inside the message queue
+ uint32_t* slot = eventThreadServices.ReserveMessageSlot(sizeof(LocalType));
+
+ // Construct message in the message queue memory; note that delete should not be called on the return value
+ new(slot) LocalType(&node, &Node::RemoveCacheRenderer);
+}
+
inline void SetDepthIndexMessage(EventThreadServices& eventThreadServices, const Node& node, uint32_t depthIndex)
{
using LocalType = MessageValue1<Node, uint32_t>;
/*
- * Copyright (c) 2021 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.
// INTERNAL INCLUDES
#include <dali/internal/event/actors/custom-actor-internal.h>
+#include <dali/internal/event/rendering/renderer-impl.h>
#include <dali/public-api/common/dali-common.h>
namespace Dali
mOwner->RequestRenderTaskReorder();
}
+void CustomActorImpl::SetCacheRenderer(Renderer& renderer)
+{
+ mOwner->SetCacheRenderer(GetImplementation(renderer));
+}
+
+void CustomActorImpl::RemoveCacheRenderer()
+{
+ mOwner->RemoveCacheRenderer();
+}
+
CustomActorImpl::CustomActorImpl(ActorFlags flags)
: mOwner(nullptr),
mFlags(flags)
#define DALI_CUSTOM_ACTOR_IMPL_H
/*
- * Copyright (c) 2021 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.
*/
void RequestRenderTaskReorder();
+ /**
+ * @brief Set renderer drawing cached output to this Actor.
+ * @SINCE_2_3.54
+ * @param[in] renderer Renderer to set to the Actor
+ * @pre The renderer must be initialized.
+ */
+ void SetCacheRenderer(Renderer& renderer);
+
+ /**
+ * @brief Removes cache renderer from the Actor.
+ * @SINCE_2_3.54
+ */
+ void RemoveCacheRenderer();
+
/**
* @brief Called after the size negotiation has been finished for this control.
*