2 * Copyright (c) 2024 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 <dali/internal/event/size-negotiation/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(true /* Forcibly use memory pool */),
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);
122 mPotentialRedundantSubRoots.reserve(32);
123 mTopOfSubTreeStack.reserve(32);
126 RelayoutController::~RelayoutController() = default;
128 RelayoutController* RelayoutController::Get()
130 // There was crash when destroying actors and the ResizePolicy is USE_NATURAL_SIZE
131 // The ThreadLocalStorage::Get() only retrieve STL without checking if it exists.
132 // The caller of RelayoutController::Get() should check if RelayoutController is not null.
133 if(ThreadLocalStorage::Created())
135 return &ThreadLocalStorage::Get().GetRelayoutController();
141 void RelayoutController::QueueActor(Internal::Actor* actor, RelayoutContainer& actors, Vector2 size)
143 if(actor && actor->RelayoutRequired())
145 Dali::Actor actorHandle = Dali::Actor(actor);
146 actors.Add(actorHandle, size);
150 void RelayoutController::RequestRelayout(Dali::Actor& actor, Dimension::Type dimension)
157 std::vector<Dali::Actor>& potentialRedundantSubRoots = mPotentialRedundantSubRoots;
158 std::vector<Dali::Actor>& topOfSubTreeStack = mTopOfSubTreeStack;
160 DALI_ASSERT_ALWAYS(potentialRedundantSubRoots.empty() && "potentialRedundantSubRoots must be empty before RequestRelayout!");
161 DALI_ASSERT_ALWAYS(topOfSubTreeStack.empty() && "topOfSubTreeStack must be empty before RequestRelayout!");
163 topOfSubTreeStack.push_back(actor);
165 // Propagate on all dimensions
166 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
168 if(dimension & (1 << i))
170 // Do the propagation
171 PropagateAll(actor, static_cast<Dimension::Type>(1 << i), topOfSubTreeStack, potentialRedundantSubRoots);
175 while(!topOfSubTreeStack.empty())
177 // Request this actor as head of sub-tree if it is not dependent on a parent that is dirty
178 Dali::Actor subTreeActor = topOfSubTreeStack.back();
179 topOfSubTreeStack.pop_back();
181 Dali::Actor parent = subTreeActor.GetParent();
182 if(!parent || !(GetImplementation(subTreeActor).RelayoutDependentOnParent() && GetImplementation(parent).RelayoutRequired()))
184 // Add sub tree root to relayout list
185 AddRequest(subTreeActor);
187 // Flag request for end of frame
192 potentialRedundantSubRoots.push_back(subTreeActor);
196 // Remove any redundant sub-tree heads
197 for(auto& subRoot : potentialRedundantSubRoots)
199 RemoveRequest(subRoot);
202 potentialRedundantSubRoots.clear();
204 if(!mProcessingCoreEvents)
206 mRenderController.RequestProcessEventsOnIdle();
210 void RelayoutController::OnApplicationSceneCreated()
212 DALI_LOG_INFO(gLogFilter, Debug::General, "[Internal::RelayoutController::OnApplicationSceneCreated]\n");
214 // Open relayout controller to receive relayout requests
217 // Flag request for end of frame
221 void RelayoutController::RequestRelayoutTree(Dali::Actor& actor)
228 // Only set dirty flag if doing relayout and not already marked as dirty
229 Actor& actorImpl = GetImplementation(actor);
230 if(actorImpl.RelayoutPossible())
232 // If parent is not in relayout we are at the top of a new sub-tree
233 Dali::Actor parent = actor.GetParent();
234 if(!parent || !GetImplementation(parent).IsRelayoutEnabled())
239 // Set dirty flag on actors that are enabled
240 actorImpl.SetLayoutDirty(true);
241 actorImpl.SetLayoutNegotiated(false); // Reset this flag ready for next relayout
244 // Propagate down to children
245 for(uint32_t i = 0; i < actor.GetChildCount(); ++i)
247 Dali::Actor child = actor.GetChildAt(i);
249 RequestRelayoutTree(child);
253 void RelayoutController::PropagateAll(Dali::Actor& actor, Dimension::Type dimension, std::vector<Dali::Actor>& topOfSubTreeStack, std::vector<Dali::Actor>& potentialRedundantSubRoots)
255 // Only set dirty flag if doing relayout and not already marked as dirty
256 Actor& actorImpl = GetImplementation(actor);
257 if(actorImpl.RelayoutPossible(dimension))
259 // Set dirty and negotiated flags
260 actorImpl.SetLayoutDirty(true, dimension);
261 actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
263 // Check for dimension dependecy: width for height/height for width etc
264 // Check each possible dimension and see if it is dependent on the input one
265 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
267 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
269 if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck) &&
270 !actorImpl.IsLayoutDirty(dimensionToCheck))
272 PropagateAll(actor, dimensionToCheck, topOfSubTreeStack, potentialRedundantSubRoots);
276 // Propagate up to parent
277 Dali::Actor parent = actor.GetParent();
280 Actor& parentImpl = GetImplementation(parent);
281 if(parentImpl.RelayoutDependentOnChildren(dimension) && !parentImpl.IsLayoutDirty(dimension))
283 // Store the highest parent reached
285 for(auto&& element : topOfSubTreeStack)
287 if(element == parent)
296 topOfSubTreeStack.push_back(parent);
300 PropagateAll(parent, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
304 // Propagate down to children
305 for(unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
307 Dali::Actor child = actor.GetChildAt(i);
308 Actor& childImpl = GetImplementation(child);
310 if(childImpl.IsRelayoutEnabled() && childImpl.RelayoutDependentOnParent(dimension))
312 if(childImpl.IsLayoutDirty(dimension))
314 // We have found a child that could potentially have already been collected for relayout
315 potentialRedundantSubRoots.push_back(child);
319 PropagateAll(child, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
326 void RelayoutController::PropagateFlags(Dali::Actor& actor, Dimension::Type dimension)
328 // Only set dirty flag if doing relayout and not already marked as dirty
329 Actor& actorImpl = GetImplementation(actor);
330 if(actorImpl.IsRelayoutEnabled())
332 // Set dirty and negotiated flags
333 actorImpl.SetLayoutDirty(true, dimension);
334 actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
336 // Check for dimension dependecy: width for height/height for width etc
337 // Check each possible dimension and see if it is dependent on the input one
338 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
340 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
342 if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck))
344 PropagateFlags(actor, dimensionToCheck);
348 // Propagate up to parent
349 Dali::Actor parent = actor.GetParent();
352 Actor& parentImpl = GetImplementation(parent);
353 if(parentImpl.RelayoutDependentOnChildren(dimension))
356 PropagateFlags(parent, dimension);
360 // Propagate down to children
361 for(uint32_t i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
363 Dali::Actor child = actor.GetChildAt(i);
364 Actor& childImpl = GetImplementation(child);
366 if(childImpl.RelayoutDependentOnParent(dimension))
368 PropagateFlags(child, dimension);
374 void RelayoutController::AddRequest(Dali::Actor& actor)
376 Internal::Actor* actorPtr = &GetImplementation(actor);
378 // Only add the rootActor if it is not already recorded
379 auto iter = mDirtyLayoutSubTrees.Find(actorPtr);
381 if(iter == mDirtyLayoutSubTrees.End())
383 mDirtyLayoutSubTrees.PushBack(actorPtr);
387 void RelayoutController::RemoveRequest(Dali::Actor& actor)
389 Internal::Actor* actorPtr = &GetImplementation(actor);
390 mDirtyLayoutSubTrees.EraseObject(actorPtr);
393 void RelayoutController::Request()
395 mRelayoutFlag = true;
397 if(!mRelayoutConnection)
399 ThreadLocalStorage::Get().GetObjectRegistry().ObjectDestroyedSignal().Connect(mSlotDelegate, &RelayoutController::OnObjectDestroyed);
401 mRelayoutConnection = true;
405 void RelayoutController::OnObjectDestroyed(const Dali::RefObject* object)
407 mDirtyLayoutSubTrees.EraseObject(static_cast<const Dali::Internal::Actor*>(object));
410 void RelayoutController::Relayout()
412 // Only do something when requested
415 mPerformingRelayout = true;
417 // Clear the flag as we're now doing the relayout
418 mRelayoutFlag = false;
420 // 1. Finds all top-level controls from the dirty list and allocate them the size of the scene
421 // These controls are paired with the parent/scene size and added to the stack.
422 for(auto& dirtyActor : mDirtyLayoutSubTrees)
424 // Need to test if actor is valid (could have been deleted and had the pointer cleared)
427 // Only negotiate actors that are on the scene
428 if(dirtyActor->OnScene())
430 Internal::Actor* parent = dirtyActor->GetParent();
431 QueueActor(dirtyActor, *mRelayoutStack, (parent) ? Vector2(parent->GetTargetSize()) : dirtyActor->GetScene().GetSize());
436 mDirtyLayoutSubTrees.Clear();
438 // 2. Iterate through the stack until it's empty.
439 if(mRelayoutStack->Size() > 0)
441 DALI_TRACE_BEGIN(gTraceFilter, "DALI_RELAYOUT");
444 uint32_t relayoutActorCount = 0u;
445 uint32_t negotiatedActorCount = 0u;
448 while(mRelayoutStack->Size() > 0)
454 ++relayoutActorCount;
457 mRelayoutStack->GetBack(actor, size);
458 Actor& actorImpl = GetImplementation(actor);
459 mRelayoutStack->PopBack();
461 if(actorImpl.RelayoutRequired() && actorImpl.OnScene())
464 ++negotiatedActorCount;
466 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);
468 // 3. Negotiate the size with the current actor. Pass it an empty container which the actor
469 // has to fill with all the actors it has not done any size negotiation for.
470 actorImpl.NegotiateSize(size, *mRelayoutStack);
474 // We are done with the RelayoutInfos now so delete the pool
475 mRelayoutInfoAllocator.ResetMemoryPool();
480 if(gTraceFilter && gTraceFilter->IsTraceEnabled())
482 std::ostringstream stream;
483 stream << "[relayoutActor:" << relayoutActorCount << " negotiatedActor:" << negotiatedActorCount << "]";
484 DALI_TRACE_END_WITH_MESSAGE(gTraceFilter, "DALI_RELAYOUT", stream.str().c_str());
489 mPerformingRelayout = false;
491 // should not disconnect the signal as that causes some control size negotiations to not work correctly
492 // this algorithm needs more optimization as well
495 void RelayoutController::SetEnabled(bool enabled)
500 bool RelayoutController::IsPerformingRelayout() const
502 return mPerformingRelayout;
505 void RelayoutController::SetProcessingCoreEvents(bool processingEvents)
507 mProcessingCoreEvents = processingEvents;
510 uint32_t RelayoutController::GetMemoryPoolCapacity()
512 return mRelayoutInfoAllocator.GetCapacity();
515 } // namespace Internal