2 * Copyright (c) 2022 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()
126 delete mRelayoutStack;
129 RelayoutController* RelayoutController::Get()
131 // There was crash when destroying actors and the ResizePolicy is USE_NATURAL_SIZE
132 // The ThreadLocalStorage::Get() only retrieve STL without checking if it exists.
133 // The caller of RelayoutController::Get() should check if RelayoutController is not null.
134 if(ThreadLocalStorage::Created())
136 return &ThreadLocalStorage::Get().GetRelayoutController();
142 void RelayoutController::QueueActor(Internal::Actor* actor, RelayoutContainer& actors, Vector2 size)
144 if(actor && actor->RelayoutRequired())
146 Dali::Actor actorHandle = Dali::Actor(actor);
147 actors.Add(actorHandle, size);
151 void RelayoutController::RequestRelayout(Dali::Actor& actor, Dimension::Type dimension)
158 std::vector<Dali::Actor> potentialRedundantSubRoots;
159 std::vector<Dali::Actor> topOfSubTreeStack;
161 topOfSubTreeStack.push_back(actor);
163 // Propagate on all dimensions
164 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
166 if(dimension & (1 << i))
168 // Do the propagation
169 PropagateAll(actor, static_cast<Dimension::Type>(1 << i), topOfSubTreeStack, potentialRedundantSubRoots);
173 while(!topOfSubTreeStack.empty())
175 // Request this actor as head of sub-tree if it is not dependent on a parent that is dirty
176 Dali::Actor subTreeActor = topOfSubTreeStack.back();
177 topOfSubTreeStack.pop_back();
179 Dali::Actor parent = subTreeActor.GetParent();
180 if(!parent || !(GetImplementation(subTreeActor).RelayoutDependentOnParent() && GetImplementation(parent).RelayoutRequired()))
182 // Add sub tree root to relayout list
183 AddRequest(subTreeActor);
185 // Flag request for end of frame
190 potentialRedundantSubRoots.push_back(subTreeActor);
194 // Remove any redundant sub-tree heads
195 for(auto& subRoot : potentialRedundantSubRoots)
197 RemoveRequest(subRoot);
200 if(!mProcessingCoreEvents)
202 mRenderController.RequestProcessEventsOnIdle(false);
206 void RelayoutController::OnApplicationSceneCreated()
208 DALI_LOG_INFO(gLogFilter, Debug::General, "[Internal::RelayoutController::OnApplicationSceneCreated]\n");
210 // Open relayout controller to receive relayout requests
213 // Flag request for end of frame
217 void RelayoutController::RequestRelayoutTree(Dali::Actor& actor)
224 // Only set dirty flag if doing relayout and not already marked as dirty
225 Actor& actorImpl = GetImplementation(actor);
226 if(actorImpl.RelayoutPossible())
228 // If parent is not in relayout we are at the top of a new sub-tree
229 Dali::Actor parent = actor.GetParent();
230 if(!parent || !GetImplementation(parent).IsRelayoutEnabled())
235 // Set dirty flag on actors that are enabled
236 actorImpl.SetLayoutDirty(true);
237 actorImpl.SetLayoutNegotiated(false); // Reset this flag ready for next relayout
240 // Propagate down to children
241 for(uint32_t i = 0; i < actor.GetChildCount(); ++i)
243 Dali::Actor child = actor.GetChildAt(i);
245 RequestRelayoutTree(child);
249 void RelayoutController::PropagateAll(Dali::Actor& actor, Dimension::Type dimension, std::vector<Dali::Actor>& topOfSubTreeStack, std::vector<Dali::Actor>& potentialRedundantSubRoots)
251 // Only set dirty flag if doing relayout and not already marked as dirty
252 Actor& actorImpl = GetImplementation(actor);
253 if(actorImpl.RelayoutPossible(dimension))
255 // Set dirty and negotiated flags
256 actorImpl.SetLayoutDirty(true, dimension);
257 actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
259 // Check for dimension dependecy: width for height/height for width etc
260 // Check each possible dimension and see if it is dependent on the input one
261 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
263 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
265 if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck) &&
266 !actorImpl.IsLayoutDirty(dimensionToCheck))
268 PropagateAll(actor, dimensionToCheck, topOfSubTreeStack, potentialRedundantSubRoots);
272 // Propagate up to parent
273 Dali::Actor parent = actor.GetParent();
276 Actor& parentImpl = GetImplementation(parent);
277 if(parentImpl.RelayoutDependentOnChildren(dimension) && !parentImpl.IsLayoutDirty(dimension))
279 // Store the highest parent reached
281 for(auto&& element : topOfSubTreeStack)
283 if(element == parent)
292 topOfSubTreeStack.push_back(parent);
296 PropagateAll(parent, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
300 // Propagate down to children
301 for(unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
303 Dali::Actor child = actor.GetChildAt(i);
304 Actor& childImpl = GetImplementation(child);
306 if(childImpl.IsRelayoutEnabled() && childImpl.RelayoutDependentOnParent(dimension))
308 if(childImpl.IsLayoutDirty(dimension))
310 // We have found a child that could potentially have already been collected for relayout
311 potentialRedundantSubRoots.push_back(child);
315 PropagateAll(child, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
322 void RelayoutController::PropagateFlags(Dali::Actor& actor, Dimension::Type dimension)
324 // Only set dirty flag if doing relayout and not already marked as dirty
325 Actor& actorImpl = GetImplementation(actor);
326 if(actorImpl.IsRelayoutEnabled())
328 // Set dirty and negotiated flags
329 actorImpl.SetLayoutDirty(true, dimension);
330 actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
332 // Check for dimension dependecy: width for height/height for width etc
333 // Check each possible dimension and see if it is dependent on the input one
334 for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
336 Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
338 if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck))
340 PropagateFlags(actor, dimensionToCheck);
344 // Propagate up to parent
345 Dali::Actor parent = actor.GetParent();
348 Actor& parentImpl = GetImplementation(parent);
349 if(parentImpl.RelayoutDependentOnChildren(dimension))
352 PropagateFlags(parent, dimension);
356 // Propagate down to children
357 for(uint32_t i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
359 Dali::Actor child = actor.GetChildAt(i);
360 Actor& childImpl = GetImplementation(child);
362 if(childImpl.RelayoutDependentOnParent(dimension))
364 PropagateFlags(child, dimension);
370 void RelayoutController::AddRequest(Dali::Actor& actor)
372 Internal::Actor* actorPtr = &GetImplementation(actor);
374 // Only add the rootActor if it is not already recorded
375 auto itr = std::find(mDirtyLayoutSubTrees.begin(), mDirtyLayoutSubTrees.end(), actorPtr);
377 if(itr == mDirtyLayoutSubTrees.end())
379 mDirtyLayoutSubTrees.PushBack(actorPtr);
383 void RelayoutController::RemoveRequest(Dali::Actor& actor)
385 Internal::Actor* actorPtr = &GetImplementation(actor);
387 mDirtyLayoutSubTrees.Erase(std::remove(mDirtyLayoutSubTrees.begin(),
388 mDirtyLayoutSubTrees.end(),
390 mDirtyLayoutSubTrees.end());
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 // Search for and null the object if found in the following lists
408 FindAndZero(mDirtyLayoutSubTrees, object);
411 void RelayoutController::Relayout()
413 // Only do something when requested
416 mPerformingRelayout = true;
418 // Clear the flag as we're now doing the relayout
419 mRelayoutFlag = false;
421 // 1. Finds all top-level controls from the dirty list and allocate them the size of the scene
422 // These controls are paired with the parent/scene size and added to the stack.
423 for(auto& dirtyActor : mDirtyLayoutSubTrees)
425 // Need to test if actor is valid (could have been deleted and had the pointer cleared)
428 // Only negotiate actors that are on the scene
429 if(dirtyActor->OnScene())
431 Internal::Actor* parent = dirtyActor->GetParent();
432 QueueActor(dirtyActor, *mRelayoutStack, (parent) ? Vector2(parent->GetTargetSize()) : dirtyActor->GetScene().GetSize());
437 mDirtyLayoutSubTrees.Clear();
439 // 2. Iterate through the stack until it's empty.
440 if(mRelayoutStack->Size() > 0)
442 DALI_TRACE_BEGIN(gTraceFilter, "DALI_RELAYOUT");
445 while(mRelayoutStack->Size() > 0)
449 mRelayoutStack->Get(mRelayoutStack->Size() - 1, actor, size);
450 Actor& actorImpl = GetImplementation(actor);
451 mRelayoutStack->PopBack();
453 if(actorImpl.RelayoutRequired() && actorImpl.OnScene())
455 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);
457 // 3. Negotiate the size with the current actor. Pass it an empty container which the actor
458 // has to fill with all the actors it has not done any size negotiation for.
459 actorImpl.NegotiateSize(size, *mRelayoutStack);
463 // We are done with the RelayoutInfos now so delete the pool
464 mRelayoutInfoAllocator.ResetMemoryPool();
467 DALI_TRACE_END(gTraceFilter, "DALI_RELAYOUT");
470 mPerformingRelayout = false;
472 // should not disconnect the signal as that causes some control size negotiations to not work correctly
473 // this algorithm needs more optimization as well
476 void RelayoutController::SetEnabled(bool enabled)
481 bool RelayoutController::IsPerformingRelayout() const
483 return mPerformingRelayout;
486 void RelayoutController::SetProcessingCoreEvents(bool processingEvents)
488 mProcessingCoreEvents = processingEvents;
491 void RelayoutController::FindAndZero(const RawActorList& list, const Dali::RefObject* object)
493 // Object has been destroyed so clear it from this list
494 for(auto& actor : list)
496 if(actor && (actor == object))
498 actor = nullptr; // Reset the pointer in the list. We don't want to remove it in case something is iterating over the list.
503 uint32_t RelayoutController::GetMemoryPoolCapacity()
505 return mRelayoutInfoAllocator.GetCapacity();
508 } // namespace Internal