2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "relayout-controller-impl.h"
22 #if defined(DEBUG_ENABLED)
24 #endif // defined(DEBUG_ENABLED)
27 #include <dali/integration-api/debug.h>
28 #include <dali/integration-api/render-controller.h>
29 #include <dali/integration-api/trace.h>
30 #include <dali/internal/event/actors/actor-impl.h>
31 #include <dali/internal/event/common/stage-impl.h>
32 #include <dali/internal/event/common/thread-local-storage.h>
33 #include <dali/public-api/object/object-registry.h>
34 #include <dali/public-api/object/type-registry.h>
42 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
44 #if defined(DEBUG_ENABLED)
46 Integration::Log::Filter* gLogFilter(Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_RELAYOUT_CONTROLLER"));
49 * Prints out all the children of the given actor when debug is enabled.
51 * @param[in] actor The actor whose children to print.
52 * @param[in] level The number of " | " to put in front of the children.
54 void PrintChildren(Dali::Actor actor, int level)
56 std::ostringstream output;
58 for(int t = 0; t < level; ++t)
63 output << actor.GetTypeName();
65 output << ", " << actor.GetProperty<std::string>(Dali::Actor::Property::NAME);
67 output << " - Pos: " << actor.GetCurrentProperty<Vector3>(Dali::Actor::Property::POSITION) << " Size: " << actor.GetTargetSize();
69 output << ", Dirty: (" << (GetImplementation(actor).IsLayoutDirty(Dimension::WIDTH) ? "TRUE" : "FALSE") << "," << (GetImplementation(actor).IsLayoutDirty(Dimension::HEIGHT) ? "TRUE" : "FALSE") << ")";
70 output << ", Negotiated: (" << (GetImplementation(actor).IsLayoutNegotiated(Dimension::WIDTH) ? "TRUE" : "FALSE") << "," << (GetImplementation(actor).IsLayoutNegotiated(Dimension::HEIGHT) ? "TRUE" : "FALSE") << ")";
71 output << ", Enabled: " << (GetImplementation(actor).IsRelayoutEnabled() ? "TRUE" : "FALSE");
73 output << ", (" << actor.GetObjectPtr() << ")" << std::endl;
75 DALI_LOG_INFO(gLogFilter, Debug::Verbose, output.str().c_str());
78 uint32_t numChildren = actor.GetChildCount();
79 for(uint32_t i = 0; i < numChildren; ++i)
81 PrintChildren(actor.GetChildAt(i), level);
87 * Prints the entire hierarchy of the scene.
91 if(gLogFilter->IsEnabledFor(Debug::Verbose))
93 DALI_LOG_INFO(gLogFilter, Debug::Verbose, "---------- ROOT LAYER ----------\n");
95 PrintChildren(Stage::GetCurrent()->GetRootLayer(), 0);
99 #define PRINT_HIERARCHY PrintHierarchy()
101 #else // defined(DEBUG_ENABLED)
103 #define PRINT_HIERARCHY
105 #endif // defined(DEBUG_ENABLED)
107 } // unnamed namespace
109 RelayoutController::RelayoutController(Integration::RenderController& controller)
110 : mRenderController(controller),
111 mRelayoutInfoAllocator(),
113 mRelayoutStack(new MemoryPoolRelayoutContainer(mRelayoutInfoAllocator)),
114 mRelayoutConnection(false),
115 mRelayoutFlag(false),
117 mPerformingRelayout(false),
118 mProcessingCoreEvents(false)
120 // Make space for 32 controls to avoid having to copy construct a lot in the beginning
121 mRelayoutStack->Reserve(32);
124 RelayoutController::~RelayoutController() = default;
126 RelayoutController* RelayoutController::Get()
128 // There was crash when destroying actors and the ResizePolicy is USE_NATURAL_SIZE
129 // The ThreadLocalStorage::Get() only retrieve STL without checking if it exists.
130 // The caller of RelayoutController::Get() should check if RelayoutController is not null.
131 if(ThreadLocalStorage::Created())
133 return &ThreadLocalStorage::Get().GetRelayoutController();
139 void RelayoutController::QueueActor(Internal::Actor* actor, RelayoutContainer& actors, Vector2 size)
141 if(actor && actor->RelayoutRequired())
143 Dali::Actor actorHandle = Dali::Actor(actor);
144 actors.Add(actorHandle, size);
148 void RelayoutController::RequestRelayout(Dali::Actor& actor, Dimension::Type dimension)
155 std::vector<Dali::Actor> potentialRedundantSubRoots;
156 std::vector<Dali::Actor> topOfSubTreeStack;
158 topOfSubTreeStack.push_back(actor);
160 // Propagate on all dimensions
161 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
163 if(dimension & (1 << i))
165 // Do the propagation
166 PropagateAll(actor, static_cast<Dimension::Type>(1 << i), topOfSubTreeStack, potentialRedundantSubRoots);
170 while(!topOfSubTreeStack.empty())
172 // Request this actor as head of sub-tree if it is not dependent on a parent that is dirty
173 Dali::Actor subTreeActor = topOfSubTreeStack.back();
174 topOfSubTreeStack.pop_back();
176 Dali::Actor parent = subTreeActor.GetParent();
177 if(!parent || !(GetImplementation(subTreeActor).RelayoutDependentOnParent() && GetImplementation(parent).RelayoutRequired()))
179 // Add sub tree root to relayout list
180 AddRequest(subTreeActor);
182 // Flag request for end of frame
187 potentialRedundantSubRoots.push_back(subTreeActor);
191 // Remove any redundant sub-tree heads
192 for(auto& subRoot : potentialRedundantSubRoots)
194 RemoveRequest(subRoot);
197 if(!mProcessingCoreEvents)
199 mRenderController.RequestProcessEventsOnIdle(false);
203 void RelayoutController::OnApplicationSceneCreated()
205 DALI_LOG_INFO(gLogFilter, Debug::General, "[Internal::RelayoutController::OnApplicationSceneCreated]\n");
207 // Open relayout controller to receive relayout requests
210 // Flag request for end of frame
214 void RelayoutController::RequestRelayoutTree(Dali::Actor& actor)
221 // Only set dirty flag if doing relayout and not already marked as dirty
222 Actor& actorImpl = GetImplementation(actor);
223 if(actorImpl.RelayoutPossible())
225 // If parent is not in relayout we are at the top of a new sub-tree
226 Dali::Actor parent = actor.GetParent();
227 if(!parent || !GetImplementation(parent).IsRelayoutEnabled())
232 // Set dirty flag on actors that are enabled
233 actorImpl.SetLayoutDirty(true);
234 actorImpl.SetLayoutNegotiated(false); // Reset this flag ready for next relayout
237 // Propagate down to children
238 for(uint32_t i = 0; i < actor.GetChildCount(); ++i)
240 Dali::Actor child = actor.GetChildAt(i);
242 RequestRelayoutTree(child);
246 void RelayoutController::PropagateAll(Dali::Actor& actor, Dimension::Type dimension, std::vector<Dali::Actor>& topOfSubTreeStack, std::vector<Dali::Actor>& potentialRedundantSubRoots)
248 // Only set dirty flag if doing relayout and not already marked as dirty
249 Actor& actorImpl = GetImplementation(actor);
250 if(actorImpl.RelayoutPossible(dimension))
252 // Set dirty and negotiated flags
253 actorImpl.SetLayoutDirty(true, dimension);
254 actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
256 // Check for dimension dependecy: width for height/height for width etc
257 // Check each possible dimension and see if it is dependent on the input one
258 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
260 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
262 if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck) &&
263 !actorImpl.IsLayoutDirty(dimensionToCheck))
265 PropagateAll(actor, dimensionToCheck, topOfSubTreeStack, potentialRedundantSubRoots);
269 // Propagate up to parent
270 Dali::Actor parent = actor.GetParent();
273 Actor& parentImpl = GetImplementation(parent);
274 if(parentImpl.RelayoutDependentOnChildren(dimension) && !parentImpl.IsLayoutDirty(dimension))
276 // Store the highest parent reached
278 for(auto&& element : topOfSubTreeStack)
280 if(element == parent)
289 topOfSubTreeStack.push_back(parent);
293 PropagateAll(parent, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
297 // Propagate down to children
298 for(unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
300 Dali::Actor child = actor.GetChildAt(i);
301 Actor& childImpl = GetImplementation(child);
303 if(childImpl.IsRelayoutEnabled() && childImpl.RelayoutDependentOnParent(dimension))
305 if(childImpl.IsLayoutDirty(dimension))
307 // We have found a child that could potentially have already been collected for relayout
308 potentialRedundantSubRoots.push_back(child);
312 PropagateAll(child, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
319 void RelayoutController::PropagateFlags(Dali::Actor& actor, Dimension::Type dimension)
321 // Only set dirty flag if doing relayout and not already marked as dirty
322 Actor& actorImpl = GetImplementation(actor);
323 if(actorImpl.IsRelayoutEnabled())
325 // Set dirty and negotiated flags
326 actorImpl.SetLayoutDirty(true, dimension);
327 actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
329 // Check for dimension dependecy: width for height/height for width etc
330 // Check each possible dimension and see if it is dependent on the input one
331 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
333 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
335 if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck))
337 PropagateFlags(actor, dimensionToCheck);
341 // Propagate up to parent
342 Dali::Actor parent = actor.GetParent();
345 Actor& parentImpl = GetImplementation(parent);
346 if(parentImpl.RelayoutDependentOnChildren(dimension))
349 PropagateFlags(parent, dimension);
353 // Propagate down to children
354 for(uint32_t i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
356 Dali::Actor child = actor.GetChildAt(i);
357 Actor& childImpl = GetImplementation(child);
359 if(childImpl.RelayoutDependentOnParent(dimension))
361 PropagateFlags(child, dimension);
367 void RelayoutController::AddRequest(Dali::Actor& actor)
369 Internal::Actor* actorPtr = &GetImplementation(actor);
371 // Only add the rootActor if it is not already recorded
372 auto iter = mDirtyLayoutSubTrees.Find(actorPtr);
374 if(iter == mDirtyLayoutSubTrees.End())
376 mDirtyLayoutSubTrees.PushBack(actorPtr);
380 void RelayoutController::RemoveRequest(Dali::Actor& actor)
382 Internal::Actor* actorPtr = &GetImplementation(actor);
383 mDirtyLayoutSubTrees.EraseObject(actorPtr);
386 void RelayoutController::Request()
388 mRelayoutFlag = true;
390 if(!mRelayoutConnection)
392 ThreadLocalStorage::Get().GetObjectRegistry().ObjectDestroyedSignal().Connect(mSlotDelegate, &RelayoutController::OnObjectDestroyed);
394 mRelayoutConnection = true;
398 void RelayoutController::OnObjectDestroyed(const Dali::RefObject* object)
400 mDirtyLayoutSubTrees.EraseObject(static_cast<const Dali::Internal::Actor*>(object));
403 void RelayoutController::Relayout()
405 // Only do something when requested
408 mPerformingRelayout = true;
410 // Clear the flag as we're now doing the relayout
411 mRelayoutFlag = false;
413 // 1. Finds all top-level controls from the dirty list and allocate them the size of the scene
414 // These controls are paired with the parent/scene size and added to the stack.
415 for(auto& dirtyActor : mDirtyLayoutSubTrees)
417 // Need to test if actor is valid (could have been deleted and had the pointer cleared)
420 // Only negotiate actors that are on the scene
421 if(dirtyActor->OnScene())
423 Internal::Actor* parent = dirtyActor->GetParent();
424 QueueActor(dirtyActor, *mRelayoutStack, (parent) ? Vector2(parent->GetTargetSize()) : dirtyActor->GetScene().GetSize());
429 mDirtyLayoutSubTrees.Clear();
431 // 2. Iterate through the stack until it's empty.
432 if(mRelayoutStack->Size() > 0)
434 DALI_TRACE_SCOPE(gTraceFilter, "DALI_RELAYOUT");
437 while(mRelayoutStack->Size() > 0)
442 mRelayoutStack->GetBack(actor, size);
443 Actor& actorImpl = GetImplementation(actor);
444 mRelayoutStack->PopBack();
446 if(actorImpl.RelayoutRequired() && actorImpl.OnScene())
448 DALI_LOG_INFO(gLogFilter, Debug::General, "[Internal::RelayoutController::Relayout] Negotiating %p %s %s (%.2f, %.2f)\n", &actorImpl, actor.GetTypeName().c_str(), actor.GetProperty<std::string>(Dali::Actor::Property::NAME).c_str(), size.width, size.height);
450 // 3. Negotiate the size with the current actor. Pass it an empty container which the actor
451 // has to fill with all the actors it has not done any size negotiation for.
452 actorImpl.NegotiateSize(size, *mRelayoutStack);
456 // We are done with the RelayoutInfos now so delete the pool
457 mRelayoutInfoAllocator.ResetMemoryPool();
462 mPerformingRelayout = false;
464 // should not disconnect the signal as that causes some control size negotiations to not work correctly
465 // this algorithm needs more optimization as well
468 void RelayoutController::SetEnabled(bool enabled)
473 bool RelayoutController::IsPerformingRelayout() const
475 return mPerformingRelayout;
478 void RelayoutController::SetProcessingCoreEvents(bool processingEvents)
480 mProcessingCoreEvents = processingEvents;
483 uint32_t RelayoutController::GetMemoryPoolCapacity()
485 return mRelayoutInfoAllocator.GetCapacity();
488 } // namespace Internal