Make relayout temperal vector as member.
[platform/core/uifw/dali-core.git] / dali / internal / event / size-negotiation / relayout-controller-impl.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/event/size-negotiation/relayout-controller-impl.h>
20
21 // EXTERNAL INCLUDES
22 #if defined(DEBUG_ENABLED)
23 #include <sstream>
24 #endif // defined(DEBUG_ENABLED)
25
26 // INTERNAL INCLUDES
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>
35
36 namespace Dali
37 {
38 namespace Internal
39 {
40 namespace
41 {
42 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
43
44 #if defined(DEBUG_ENABLED)
45
46 Integration::Log::Filter* gLogFilter(Integration::Log::Filter::New(Debug::NoLogging, false, "LOG_RELAYOUT_CONTROLLER"));
47
48 /**
49  * Prints out all the children of the given actor when debug is enabled.
50  *
51  * @param[in]  actor  The actor whose children to print.
52  * @param[in]  level  The number of " | " to put in front of the children.
53  */
54 void PrintChildren(Dali::Actor actor, int level)
55 {
56   std::ostringstream output;
57
58   for(int t = 0; t < level; ++t)
59   {
60     output << " | ";
61   }
62
63   output << actor.GetTypeName();
64
65   output << ", " << actor.GetProperty<std::string>(Dali::Actor::Property::NAME);
66
67   output << " - Pos: " << actor.GetCurrentProperty<Vector3>(Dali::Actor::Property::POSITION) << " Size: " << actor.GetTargetSize();
68
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");
72
73   output << ", (" << actor.GetObjectPtr() << ")" << std::endl;
74
75   DALI_LOG_INFO(gLogFilter, Debug::Verbose, output.str().c_str());
76
77   ++level;
78   uint32_t numChildren = actor.GetChildCount();
79   for(uint32_t i = 0; i < numChildren; ++i)
80   {
81     PrintChildren(actor.GetChildAt(i), level);
82   }
83   --level;
84 }
85
86 /**
87  * Prints the entire hierarchy of the scene.
88  */
89 void PrintHierarchy()
90 {
91   if(gLogFilter->IsEnabledFor(Debug::Verbose))
92   {
93     DALI_LOG_INFO(gLogFilter, Debug::Verbose, "---------- ROOT LAYER ----------\n");
94
95     PrintChildren(Stage::GetCurrent()->GetRootLayer(), 0);
96   }
97 }
98
99 #define PRINT_HIERARCHY PrintHierarchy()
100
101 #else // defined(DEBUG_ENABLED)
102
103 #define PRINT_HIERARCHY
104
105 #endif // defined(DEBUG_ENABLED)
106
107 } // unnamed namespace
108
109 RelayoutController::RelayoutController(Integration::RenderController& controller)
110 : mRenderController(controller),
111   mRelayoutInfoAllocator(),
112   mSlotDelegate(this),
113   mRelayoutStack(new MemoryPoolRelayoutContainer(mRelayoutInfoAllocator)),
114   mRelayoutConnection(false),
115   mRelayoutFlag(false),
116   mEnabled(false),
117   mPerformingRelayout(false),
118   mProcessingCoreEvents(false)
119 {
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);
124 }
125
126 RelayoutController::~RelayoutController() = default;
127
128 RelayoutController* RelayoutController::Get()
129 {
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())
134   {
135     return &ThreadLocalStorage::Get().GetRelayoutController();
136   }
137
138   return nullptr;
139 }
140
141 void RelayoutController::QueueActor(Internal::Actor* actor, RelayoutContainer& actors, Vector2 size)
142 {
143   if(actor && actor->RelayoutRequired())
144   {
145     Dali::Actor actorHandle = Dali::Actor(actor);
146     actors.Add(actorHandle, size);
147   }
148 }
149
150 void RelayoutController::RequestRelayout(Dali::Actor& actor, Dimension::Type dimension)
151 {
152   if(!mEnabled)
153   {
154     return;
155   }
156
157   std::vector<Dali::Actor>& potentialRedundantSubRoots = mPotentialRedundantSubRoots;
158   std::vector<Dali::Actor>& topOfSubTreeStack          = mTopOfSubTreeStack;
159
160   DALI_ASSERT_ALWAYS(potentialRedundantSubRoots.empty() && "potentialRedundantSubRoots must be empty before RequestRelayout!");
161   DALI_ASSERT_ALWAYS(topOfSubTreeStack.empty() && "topOfSubTreeStack must be empty before RequestRelayout!");
162
163   topOfSubTreeStack.push_back(actor);
164
165   // Propagate on all dimensions
166   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
167   {
168     if(dimension & (1 << i))
169     {
170       // Do the propagation
171       PropagateAll(actor, static_cast<Dimension::Type>(1 << i), topOfSubTreeStack, potentialRedundantSubRoots);
172     }
173   }
174
175   while(!topOfSubTreeStack.empty())
176   {
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();
180
181     Dali::Actor parent = subTreeActor.GetParent();
182     if(!parent || !(GetImplementation(subTreeActor).RelayoutDependentOnParent() && GetImplementation(parent).RelayoutRequired()))
183     {
184       // Add sub tree root to relayout list
185       AddRequest(subTreeActor);
186
187       // Flag request for end of frame
188       Request();
189     }
190     else
191     {
192       potentialRedundantSubRoots.push_back(subTreeActor);
193     }
194   }
195
196   // Remove any redundant sub-tree heads
197   for(auto& subRoot : potentialRedundantSubRoots)
198   {
199     RemoveRequest(subRoot);
200   }
201
202   potentialRedundantSubRoots.clear();
203
204   if(!mProcessingCoreEvents)
205   {
206     mRenderController.RequestProcessEventsOnIdle(false);
207   }
208 }
209
210 void RelayoutController::OnApplicationSceneCreated()
211 {
212   DALI_LOG_INFO(gLogFilter, Debug::General, "[Internal::RelayoutController::OnApplicationSceneCreated]\n");
213
214   // Open relayout controller to receive relayout requests
215   mEnabled = true;
216
217   // Flag request for end of frame
218   Request();
219 }
220
221 void RelayoutController::RequestRelayoutTree(Dali::Actor& actor)
222 {
223   if(!mEnabled)
224   {
225     return;
226   }
227
228   // Only set dirty flag if doing relayout and not already marked as dirty
229   Actor& actorImpl = GetImplementation(actor);
230   if(actorImpl.RelayoutPossible())
231   {
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())
235     {
236       AddRequest(actor);
237     }
238
239     // Set dirty flag on actors that are enabled
240     actorImpl.SetLayoutDirty(true);
241     actorImpl.SetLayoutNegotiated(false); // Reset this flag ready for next relayout
242   }
243
244   // Propagate down to children
245   for(uint32_t i = 0; i < actor.GetChildCount(); ++i)
246   {
247     Dali::Actor child = actor.GetChildAt(i);
248
249     RequestRelayoutTree(child);
250   }
251 }
252
253 void RelayoutController::PropagateAll(Dali::Actor& actor, Dimension::Type dimension, std::vector<Dali::Actor>& topOfSubTreeStack, std::vector<Dali::Actor>& potentialRedundantSubRoots)
254 {
255   // Only set dirty flag if doing relayout and not already marked as dirty
256   Actor& actorImpl = GetImplementation(actor);
257   if(actorImpl.RelayoutPossible(dimension))
258   {
259     // Set dirty and negotiated flags
260     actorImpl.SetLayoutDirty(true, dimension);
261     actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
262
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)
266     {
267       Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
268
269       if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck) &&
270          !actorImpl.IsLayoutDirty(dimensionToCheck))
271       {
272         PropagateAll(actor, dimensionToCheck, topOfSubTreeStack, potentialRedundantSubRoots);
273       }
274     }
275
276     // Propagate up to parent
277     Dali::Actor parent = actor.GetParent();
278     if(parent)
279     {
280       Actor& parentImpl = GetImplementation(parent);
281       if(parentImpl.RelayoutDependentOnChildren(dimension) && !parentImpl.IsLayoutDirty(dimension))
282       {
283         // Store the highest parent reached
284         bool found = false;
285         for(auto&& element : topOfSubTreeStack)
286         {
287           if(element == parent)
288           {
289             found = true;
290             break;
291           }
292         }
293
294         if(!found)
295         {
296           topOfSubTreeStack.push_back(parent);
297         }
298
299         // Propagate up
300         PropagateAll(parent, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
301       }
302     }
303
304     // Propagate down to children
305     for(unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
306     {
307       Dali::Actor child     = actor.GetChildAt(i);
308       Actor&      childImpl = GetImplementation(child);
309
310       if(childImpl.IsRelayoutEnabled() && childImpl.RelayoutDependentOnParent(dimension))
311       {
312         if(childImpl.IsLayoutDirty(dimension))
313         {
314           // We have found a child that could potentially have already been collected for relayout
315           potentialRedundantSubRoots.push_back(child);
316         }
317         else
318         {
319           PropagateAll(child, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
320         }
321       }
322     }
323   }
324 }
325
326 void RelayoutController::PropagateFlags(Dali::Actor& actor, Dimension::Type dimension)
327 {
328   // Only set dirty flag if doing relayout and not already marked as dirty
329   Actor& actorImpl = GetImplementation(actor);
330   if(actorImpl.IsRelayoutEnabled())
331   {
332     // Set dirty and negotiated flags
333     actorImpl.SetLayoutDirty(true, dimension);
334     actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
335
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)
339     {
340       Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
341
342       if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck))
343       {
344         PropagateFlags(actor, dimensionToCheck);
345       }
346     }
347
348     // Propagate up to parent
349     Dali::Actor parent = actor.GetParent();
350     if(parent)
351     {
352       Actor& parentImpl = GetImplementation(parent);
353       if(parentImpl.RelayoutDependentOnChildren(dimension))
354       {
355         // Propagate up
356         PropagateFlags(parent, dimension);
357       }
358     }
359
360     // Propagate down to children
361     for(uint32_t i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
362     {
363       Dali::Actor child     = actor.GetChildAt(i);
364       Actor&      childImpl = GetImplementation(child);
365
366       if(childImpl.RelayoutDependentOnParent(dimension))
367       {
368         PropagateFlags(child, dimension);
369       }
370     }
371   }
372 }
373
374 void RelayoutController::AddRequest(Dali::Actor& actor)
375 {
376   Internal::Actor* actorPtr = &GetImplementation(actor);
377
378   // Only add the rootActor if it is not already recorded
379   auto iter = mDirtyLayoutSubTrees.Find(actorPtr);
380
381   if(iter == mDirtyLayoutSubTrees.End())
382   {
383     mDirtyLayoutSubTrees.PushBack(actorPtr);
384   }
385 }
386
387 void RelayoutController::RemoveRequest(Dali::Actor& actor)
388 {
389   Internal::Actor* actorPtr = &GetImplementation(actor);
390   mDirtyLayoutSubTrees.EraseObject(actorPtr);
391 }
392
393 void RelayoutController::Request()
394 {
395   mRelayoutFlag = true;
396
397   if(!mRelayoutConnection)
398   {
399     ThreadLocalStorage::Get().GetObjectRegistry().ObjectDestroyedSignal().Connect(mSlotDelegate, &RelayoutController::OnObjectDestroyed);
400
401     mRelayoutConnection = true;
402   }
403 }
404
405 void RelayoutController::OnObjectDestroyed(const Dali::RefObject* object)
406 {
407   mDirtyLayoutSubTrees.EraseObject(static_cast<const Dali::Internal::Actor*>(object));
408 }
409
410 void RelayoutController::Relayout()
411 {
412   // Only do something when requested
413   if(mRelayoutFlag)
414   {
415     mPerformingRelayout = true;
416
417     // Clear the flag as we're now doing the relayout
418     mRelayoutFlag = false;
419
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)
423     {
424       // Need to test if actor is valid (could have been deleted and had the pointer cleared)
425       if(dirtyActor)
426       {
427         // Only negotiate actors that are on the scene
428         if(dirtyActor->OnScene())
429         {
430           Internal::Actor* parent = dirtyActor->GetParent();
431           QueueActor(dirtyActor, *mRelayoutStack, (parent) ? Vector2(parent->GetTargetSize()) : dirtyActor->GetScene().GetSize());
432         }
433       }
434     }
435
436     mDirtyLayoutSubTrees.Clear();
437
438     // 2. Iterate through the stack until it's empty.
439     if(mRelayoutStack->Size() > 0)
440     {
441       DALI_TRACE_SCOPE(gTraceFilter, "DALI_RELAYOUT");
442       PRINT_HIERARCHY;
443
444       while(mRelayoutStack->Size() > 0)
445       {
446         Dali::Actor actor;
447         Vector2     size;
448
449         mRelayoutStack->GetBack(actor, size);
450         Actor& actorImpl = GetImplementation(actor);
451         mRelayoutStack->PopBack();
452
453         if(actorImpl.RelayoutRequired() && actorImpl.OnScene())
454         {
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);
456
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);
460         }
461       }
462
463       // We are done with the RelayoutInfos now so delete the pool
464       mRelayoutInfoAllocator.ResetMemoryPool();
465
466       PRINT_HIERARCHY;
467     }
468
469     mPerformingRelayout = false;
470   }
471   // should not disconnect the signal as that causes some control size negotiations to not work correctly
472   // this algorithm needs more optimization as well
473 }
474
475 void RelayoutController::SetEnabled(bool enabled)
476 {
477   mEnabled = enabled;
478 }
479
480 bool RelayoutController::IsPerformingRelayout() const
481 {
482   return mPerformingRelayout;
483 }
484
485 void RelayoutController::SetProcessingCoreEvents(bool processingEvents)
486 {
487   mProcessingCoreEvents = processingEvents;
488 }
489
490 uint32_t RelayoutController::GetMemoryPoolCapacity()
491 {
492   return mRelayoutInfoAllocator.GetCapacity();
493 }
494
495 } // namespace Internal
496
497 } // namespace Dali