93585f6b63ae57a9b7cc7ba4904f906650419194
[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 "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() = default;
125
126 RelayoutController* RelayoutController::Get()
127 {
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())
132   {
133     return &ThreadLocalStorage::Get().GetRelayoutController();
134   }
135
136   return nullptr;
137 }
138
139 void RelayoutController::QueueActor(Internal::Actor* actor, RelayoutContainer& actors, Vector2 size)
140 {
141   if(actor && actor->RelayoutRequired())
142   {
143     Dali::Actor actorHandle = Dali::Actor(actor);
144     actors.Add(actorHandle, size);
145   }
146 }
147
148 void RelayoutController::RequestRelayout(Dali::Actor& actor, Dimension::Type dimension)
149 {
150   if(!mEnabled)
151   {
152     return;
153   }
154
155   std::vector<Dali::Actor> potentialRedundantSubRoots;
156   std::vector<Dali::Actor> topOfSubTreeStack;
157
158   topOfSubTreeStack.push_back(actor);
159
160   // Propagate on all dimensions
161   for(uint32_t i = 0; i < Dimension::DIMENSION_COUNT; ++i)
162   {
163     if(dimension & (1 << i))
164     {
165       // Do the propagation
166       PropagateAll(actor, static_cast<Dimension::Type>(1 << i), topOfSubTreeStack, potentialRedundantSubRoots);
167     }
168   }
169
170   while(!topOfSubTreeStack.empty())
171   {
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();
175
176     Dali::Actor parent = subTreeActor.GetParent();
177     if(!parent || !(GetImplementation(subTreeActor).RelayoutDependentOnParent() && GetImplementation(parent).RelayoutRequired()))
178     {
179       // Add sub tree root to relayout list
180       AddRequest(subTreeActor);
181
182       // Flag request for end of frame
183       Request();
184     }
185     else
186     {
187       potentialRedundantSubRoots.push_back(subTreeActor);
188     }
189   }
190
191   // Remove any redundant sub-tree heads
192   for(auto& subRoot : potentialRedundantSubRoots)
193   {
194     RemoveRequest(subRoot);
195   }
196
197   if(!mProcessingCoreEvents)
198   {
199     mRenderController.RequestProcessEventsOnIdle(false);
200   }
201 }
202
203 void RelayoutController::OnApplicationSceneCreated()
204 {
205   DALI_LOG_INFO(gLogFilter, Debug::General, "[Internal::RelayoutController::OnApplicationSceneCreated]\n");
206
207   // Open relayout controller to receive relayout requests
208   mEnabled = true;
209
210   // Flag request for end of frame
211   Request();
212 }
213
214 void RelayoutController::RequestRelayoutTree(Dali::Actor& actor)
215 {
216   if(!mEnabled)
217   {
218     return;
219   }
220
221   // Only set dirty flag if doing relayout and not already marked as dirty
222   Actor& actorImpl = GetImplementation(actor);
223   if(actorImpl.RelayoutPossible())
224   {
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())
228     {
229       AddRequest(actor);
230     }
231
232     // Set dirty flag on actors that are enabled
233     actorImpl.SetLayoutDirty(true);
234     actorImpl.SetLayoutNegotiated(false); // Reset this flag ready for next relayout
235   }
236
237   // Propagate down to children
238   for(uint32_t i = 0; i < actor.GetChildCount(); ++i)
239   {
240     Dali::Actor child = actor.GetChildAt(i);
241
242     RequestRelayoutTree(child);
243   }
244 }
245
246 void RelayoutController::PropagateAll(Dali::Actor& actor, Dimension::Type dimension, std::vector<Dali::Actor>& topOfSubTreeStack, std::vector<Dali::Actor>& potentialRedundantSubRoots)
247 {
248   // Only set dirty flag if doing relayout and not already marked as dirty
249   Actor& actorImpl = GetImplementation(actor);
250   if(actorImpl.RelayoutPossible(dimension))
251   {
252     // Set dirty and negotiated flags
253     actorImpl.SetLayoutDirty(true, dimension);
254     actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
255
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)
259     {
260       Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
261
262       if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck) &&
263          !actorImpl.IsLayoutDirty(dimensionToCheck))
264       {
265         PropagateAll(actor, dimensionToCheck, topOfSubTreeStack, potentialRedundantSubRoots);
266       }
267     }
268
269     // Propagate up to parent
270     Dali::Actor parent = actor.GetParent();
271     if(parent)
272     {
273       Actor& parentImpl = GetImplementation(parent);
274       if(parentImpl.RelayoutDependentOnChildren(dimension) && !parentImpl.IsLayoutDirty(dimension))
275       {
276         // Store the highest parent reached
277         bool found = false;
278         for(auto&& element : topOfSubTreeStack)
279         {
280           if(element == parent)
281           {
282             found = true;
283             break;
284           }
285         }
286
287         if(!found)
288         {
289           topOfSubTreeStack.push_back(parent);
290         }
291
292         // Propagate up
293         PropagateAll(parent, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
294       }
295     }
296
297     // Propagate down to children
298     for(unsigned int i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
299     {
300       Dali::Actor child     = actor.GetChildAt(i);
301       Actor&      childImpl = GetImplementation(child);
302
303       if(childImpl.IsRelayoutEnabled() && childImpl.RelayoutDependentOnParent(dimension))
304       {
305         if(childImpl.IsLayoutDirty(dimension))
306         {
307           // We have found a child that could potentially have already been collected for relayout
308           potentialRedundantSubRoots.push_back(child);
309         }
310         else
311         {
312           PropagateAll(child, dimension, topOfSubTreeStack, potentialRedundantSubRoots);
313         }
314       }
315     }
316   }
317 }
318
319 void RelayoutController::PropagateFlags(Dali::Actor& actor, Dimension::Type dimension)
320 {
321   // Only set dirty flag if doing relayout and not already marked as dirty
322   Actor& actorImpl = GetImplementation(actor);
323   if(actorImpl.IsRelayoutEnabled())
324   {
325     // Set dirty and negotiated flags
326     actorImpl.SetLayoutDirty(true, dimension);
327     actorImpl.SetLayoutNegotiated(false, dimension); // Reset this flag ready for next relayout
328
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)
332     {
333       Dimension::Type dimensionToCheck = static_cast<Dimension::Type>(1 << i);
334
335       if(actorImpl.RelayoutDependentOnDimension(dimension, dimensionToCheck))
336       {
337         PropagateFlags(actor, dimensionToCheck);
338       }
339     }
340
341     // Propagate up to parent
342     Dali::Actor parent = actor.GetParent();
343     if(parent)
344     {
345       Actor& parentImpl = GetImplementation(parent);
346       if(parentImpl.RelayoutDependentOnChildren(dimension))
347       {
348         // Propagate up
349         PropagateFlags(parent, dimension);
350       }
351     }
352
353     // Propagate down to children
354     for(uint32_t i = 0, childCount = actor.GetChildCount(); i < childCount; ++i)
355     {
356       Dali::Actor child     = actor.GetChildAt(i);
357       Actor&      childImpl = GetImplementation(child);
358
359       if(childImpl.RelayoutDependentOnParent(dimension))
360       {
361         PropagateFlags(child, dimension);
362       }
363     }
364   }
365 }
366
367 void RelayoutController::AddRequest(Dali::Actor& actor)
368 {
369   Internal::Actor* actorPtr = &GetImplementation(actor);
370
371   // Only add the rootActor if it is not already recorded
372   auto iter = mDirtyLayoutSubTrees.Find(actorPtr);
373
374   if(iter == mDirtyLayoutSubTrees.End())
375   {
376     mDirtyLayoutSubTrees.PushBack(actorPtr);
377   }
378 }
379
380 void RelayoutController::RemoveRequest(Dali::Actor& actor)
381 {
382   Internal::Actor* actorPtr = &GetImplementation(actor);
383   mDirtyLayoutSubTrees.EraseObject(actorPtr);
384 }
385
386 void RelayoutController::Request()
387 {
388   mRelayoutFlag = true;
389
390   if(!mRelayoutConnection)
391   {
392     ThreadLocalStorage::Get().GetObjectRegistry().ObjectDestroyedSignal().Connect(mSlotDelegate, &RelayoutController::OnObjectDestroyed);
393
394     mRelayoutConnection = true;
395   }
396 }
397
398 void RelayoutController::OnObjectDestroyed(const Dali::RefObject* object)
399 {
400   mDirtyLayoutSubTrees.EraseObject(static_cast<const Dali::Internal::Actor*>(object));
401 }
402
403 void RelayoutController::Relayout()
404 {
405   // Only do something when requested
406   if(mRelayoutFlag)
407   {
408     mPerformingRelayout = true;
409
410     // Clear the flag as we're now doing the relayout
411     mRelayoutFlag = false;
412
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)
416     {
417       // Need to test if actor is valid (could have been deleted and had the pointer cleared)
418       if(dirtyActor)
419       {
420         // Only negotiate actors that are on the scene
421         if(dirtyActor->OnScene())
422         {
423           Internal::Actor* parent = dirtyActor->GetParent();
424           QueueActor(dirtyActor, *mRelayoutStack, (parent) ? Vector2(parent->GetTargetSize()) : dirtyActor->GetScene().GetSize());
425         }
426       }
427     }
428
429     mDirtyLayoutSubTrees.Clear();
430
431     // 2. Iterate through the stack until it's empty.
432     if(mRelayoutStack->Size() > 0)
433     {
434       DALI_TRACE_SCOPE(gTraceFilter, "DALI_RELAYOUT");
435       PRINT_HIERARCHY;
436
437       while(mRelayoutStack->Size() > 0)
438       {
439         Dali::Actor actor;
440         Vector2     size;
441
442         mRelayoutStack->GetBack(actor, size);
443         Actor& actorImpl = GetImplementation(actor);
444         mRelayoutStack->PopBack();
445
446         if(actorImpl.RelayoutRequired() && actorImpl.OnScene())
447         {
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);
449
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);
453         }
454       }
455
456       // We are done with the RelayoutInfos now so delete the pool
457       mRelayoutInfoAllocator.ResetMemoryPool();
458
459       PRINT_HIERARCHY;
460     }
461
462     mPerformingRelayout = false;
463   }
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
466 }
467
468 void RelayoutController::SetEnabled(bool enabled)
469 {
470   mEnabled = enabled;
471 }
472
473 bool RelayoutController::IsPerformingRelayout() const
474 {
475   return mPerformingRelayout;
476 }
477
478 void RelayoutController::SetProcessingCoreEvents(bool processingEvents)
479 {
480   mProcessingCoreEvents = processingEvents;
481 }
482
483 uint32_t RelayoutController::GetMemoryPoolCapacity()
484 {
485   return mRelayoutInfoAllocator.GetCapacity();
486 }
487
488 } // namespace Internal
489
490 } // namespace Dali