Merge "Added memory pool logging" into devel/master
[platform/core/uifw/dali-core.git] / dali / internal / event / size-negotiation / relayout-controller-impl.cpp
1 /*
2  * Copyright (c) 2022 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 "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 }
123
124 RelayoutController::~RelayoutController()
125 {
126   delete mRelayoutStack;
127 }
128
129 RelayoutController* RelayoutController::Get()
130 {
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())
135   {
136     return &ThreadLocalStorage::Get().GetRelayoutController();
137   }
138
139   return nullptr;
140 }
141
142 void RelayoutController::QueueActor(Internal::Actor* actor, RelayoutContainer& actors, Vector2 size)
143 {
144   if(actor && actor->RelayoutRequired())
145   {
146     Dali::Actor actorHandle = Dali::Actor(actor);
147     actors.Add(actorHandle, size);
148   }
149 }
150
151 void RelayoutController::RequestRelayout(Dali::Actor& actor, Dimension::Type dimension)
152 {
153   if(!mEnabled)
154   {
155     return;
156   }
157
158   std::vector<Dali::Actor> potentialRedundantSubRoots;
159   std::vector<Dali::Actor> topOfSubTreeStack;
160
161   topOfSubTreeStack.push_back(actor);
162
163   // Propagate on all dimensions
164   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
165   {
166     if(dimension & (1 << i))
167     {
168       // Do the propagation
169       PropagateAll(actor, static_cast<Dimension::Type>(1 << i), topOfSubTreeStack, potentialRedundantSubRoots);
170     }
171   }
172
173   while(!topOfSubTreeStack.empty())
174   {
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();
178
179     Dali::Actor parent = subTreeActor.GetParent();
180     if(!parent || !(GetImplementation(subTreeActor).RelayoutDependentOnParent() && GetImplementation(parent).RelayoutRequired()))
181     {
182       // Add sub tree root to relayout list
183       AddRequest(subTreeActor);
184
185       // Flag request for end of frame
186       Request();
187     }
188     else
189     {
190       potentialRedundantSubRoots.push_back(subTreeActor);
191     }
192   }
193
194   // Remove any redundant sub-tree heads
195   for(auto& subRoot : potentialRedundantSubRoots)
196   {
197     RemoveRequest(subRoot);
198   }
199
200   if(!mProcessingCoreEvents)
201   {
202     mRenderController.RequestProcessEventsOnIdle(false);
203   }
204 }
205
206 void RelayoutController::OnApplicationSceneCreated()
207 {
208   DALI_LOG_INFO(gLogFilter, Debug::General, "[Internal::RelayoutController::OnApplicationSceneCreated]\n");
209
210   // Open relayout controller to receive relayout requests
211   mEnabled = true;
212
213   // Flag request for end of frame
214   Request();
215 }
216
217 void RelayoutController::RequestRelayoutTree(Dali::Actor& actor)
218 {
219   if(!mEnabled)
220   {
221     return;
222   }
223
224   // Only set dirty flag if doing relayout and not already marked as dirty
225   Actor& actorImpl = GetImplementation(actor);
226   if(actorImpl.RelayoutPossible())
227   {
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())
231     {
232       AddRequest(actor);
233     }
234
235     // Set dirty flag on actors that are enabled
236     actorImpl.SetLayoutDirty(true);
237     actorImpl.SetLayoutNegotiated(false); // Reset this flag ready for next relayout
238   }
239
240   // Propagate down to children
241   for(uint32_t i = 0; i < actor.GetChildCount(); ++i)
242   {
243     Dali::Actor child = actor.GetChildAt(i);
244
245     RequestRelayoutTree(child);
246   }
247 }
248
249 void RelayoutController::PropagateAll(Dali::Actor& actor, Dimension::Type dimension, std::vector<Dali::Actor>& topOfSubTreeStack, std::vector<Dali::Actor>& potentialRedundantSubRoots)
250 {
251   // Only set dirty flag if doing relayout and not already marked as dirty
252   Actor& actorImpl = GetImplementation(actor);
253   if(actorImpl.RelayoutPossible(dimension))
254   {
255     // Set dirty and negotiated flags
256     actorImpl.SetLayoutDirty(true, dimension);
257     actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
258
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)
262     {
263       Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
264
265       if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck) &&
266          !actorImpl.IsLayoutDirty(dimensionToCheck))
267       {
268         PropagateAll(actor, dimensionToCheck, topOfSubTreeStack, potentialRedundantSubRoots);
269       }
270     }
271
272     // Propagate up to parent
273     Dali::Actor parent = actor.GetParent();
274     if(parent)
275     {
276       Actor& parentImpl = GetImplementation(parent);
277       if(parentImpl.RelayoutDependentOnChildren(dimension) && !parentImpl.IsLayoutDirty(dimension))
278       {
279         // Store the highest parent reached
280         bool found = false;
281         for(auto&& element : topOfSubTreeStack)
282         {
283           if(element == parent)
284           {
285             found = true;
286             break;
287           }
288         }
289
290         if(!found)
291         {
292           topOfSubTreeStack.push_back(parent);
293         }
294
295         // Propagate up
296         PropagateAll(parent, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
297       }
298     }
299
300     // Propagate down to children
301     for(unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
302     {
303       Dali::Actor child     = actor.GetChildAt(i);
304       Actor&      childImpl = GetImplementation(child);
305
306       if(childImpl.IsRelayoutEnabled() && childImpl.RelayoutDependentOnParent(dimension))
307       {
308         if(childImpl.IsLayoutDirty(dimension))
309         {
310           // We have found a child that could potentially have already been collected for relayout
311           potentialRedundantSubRoots.push_back(child);
312         }
313         else
314         {
315           PropagateAll(child, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
316         }
317       }
318     }
319   }
320 }
321
322 void RelayoutController::PropagateFlags(Dali::Actor& actor, Dimension::Type dimension)
323 {
324   // Only set dirty flag if doing relayout and not already marked as dirty
325   Actor& actorImpl = GetImplementation(actor);
326   if(actorImpl.IsRelayoutEnabled())
327   {
328     // Set dirty and negotiated flags
329     actorImpl.SetLayoutDirty(true, dimension);
330     actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
331
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)
335     {
336       Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
337
338       if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck))
339       {
340         PropagateFlags(actor, dimensionToCheck);
341       }
342     }
343
344     // Propagate up to parent
345     Dali::Actor parent = actor.GetParent();
346     if(parent)
347     {
348       Actor& parentImpl = GetImplementation(parent);
349       if(parentImpl.RelayoutDependentOnChildren(dimension))
350       {
351         // Propagate up
352         PropagateFlags(parent, dimension);
353       }
354     }
355
356     // Propagate down to children
357     for(uint32_t i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
358     {
359       Dali::Actor child     = actor.GetChildAt(i);
360       Actor&      childImpl = GetImplementation(child);
361
362       if(childImpl.RelayoutDependentOnParent(dimension))
363       {
364         PropagateFlags(child, dimension);
365       }
366     }
367   }
368 }
369
370 void RelayoutController::AddRequest(Dali::Actor& actor)
371 {
372   Internal::Actor* actorPtr = &GetImplementation(actor);
373
374   // Only add the rootActor if it is not already recorded
375   auto itr = std::find(mDirtyLayoutSubTrees.begin(), mDirtyLayoutSubTrees.end(), actorPtr);
376
377   if(itr == mDirtyLayoutSubTrees.end())
378   {
379     mDirtyLayoutSubTrees.PushBack(actorPtr);
380   }
381 }
382
383 void RelayoutController::RemoveRequest(Dali::Actor& actor)
384 {
385   Internal::Actor* actorPtr = &GetImplementation(actor);
386
387   mDirtyLayoutSubTrees.Erase(std::remove(mDirtyLayoutSubTrees.begin(),
388                                          mDirtyLayoutSubTrees.end(),
389                                          actorPtr),
390                              mDirtyLayoutSubTrees.end());
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   // Search for and null the object if found in the following lists
408   FindAndZero(mDirtyLayoutSubTrees, object);
409 }
410
411 void RelayoutController::Relayout()
412 {
413   // Only do something when requested
414   if(mRelayoutFlag)
415   {
416     mPerformingRelayout = true;
417
418     // Clear the flag as we're now doing the relayout
419     mRelayoutFlag = false;
420
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)
424     {
425       // Need to test if actor is valid (could have been deleted and had the pointer cleared)
426       if(dirtyActor)
427       {
428         // Only negotiate actors that are on the scene
429         if(dirtyActor->OnScene())
430         {
431           Internal::Actor* parent = dirtyActor->GetParent();
432           QueueActor(dirtyActor, *mRelayoutStack, (parent) ? Vector2(parent->GetTargetSize()) : dirtyActor->GetScene().GetSize());
433         }
434       }
435     }
436
437     mDirtyLayoutSubTrees.Clear();
438
439     // 2. Iterate through the stack until it's empty.
440     if(mRelayoutStack->Size() > 0)
441     {
442       DALI_TRACE_BEGIN(gTraceFilter, "DALI_RELAYOUT");
443       PRINT_HIERARCHY;
444
445       while(mRelayoutStack->Size() > 0)
446       {
447         Dali::Actor actor;
448         Vector2     size;
449         mRelayoutStack->Get(mRelayoutStack->Size() - 1, 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       DALI_TRACE_END(gTraceFilter, "DALI_RELAYOUT");
468     }
469
470     mPerformingRelayout = false;
471   }
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
474 }
475
476 void RelayoutController::SetEnabled(bool enabled)
477 {
478   mEnabled = enabled;
479 }
480
481 bool RelayoutController::IsPerformingRelayout() const
482 {
483   return mPerformingRelayout;
484 }
485
486 void RelayoutController::SetProcessingCoreEvents(bool processingEvents)
487 {
488   mProcessingCoreEvents = processingEvents;
489 }
490
491 void RelayoutController::FindAndZero(const RawActorList& list, const Dali::RefObject* object)
492 {
493   // Object has been destroyed so clear it from this list
494   for(auto& actor : list)
495   {
496     if(actor && (actor == object))
497     {
498       actor = nullptr; // Reset the pointer in the list. We don't want to remove it in case something is iterating over the list.
499     }
500   }
501 }
502
503 uint32_t RelayoutController::GetMemoryPoolCapacity()
504 {
505   return mRelayoutInfoAllocator.GetCapacity();
506 }
507
508 } // namespace Internal
509
510 } // namespace Dali