If a RenderTask's exclusive actor is destoryed, then ensure the RenderTaskList of...
[platform/core/uifw/dali-core.git] / dali / internal / event / events / hit-test-algorithm-impl.cpp
1 /*
2  * Copyright (c) 2019 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/events/hit-test-algorithm-impl.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/public-api/actors/layer.h>
23 #include <dali/public-api/math/vector2.h>
24 #include <dali/public-api/math/vector4.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/internal/event/actors/actor-impl.h>
27 #include <dali/internal/event/actors/camera-actor-impl.h>
28 #include <dali/internal/event/actors/layer-impl.h>
29 #include <dali/internal/event/actors/layer-list.h>
30 #include <dali/internal/event/common/projection.h>
31 #include <dali/internal/event/images/frame-buffer-image-impl.h>
32 #include <dali/internal/event/render-tasks/render-task-impl.h>
33 #include <dali/internal/event/render-tasks/render-task-list-impl.h>
34
35 namespace Dali
36 {
37
38 namespace Internal
39 {
40
41 namespace HitTestAlgorithm
42 {
43
44 namespace
45 {
46
47 struct HitActor
48 {
49   HitActor()
50   : actor( NULL ),
51     distance( std::numeric_limits<float>::max() ),
52     depth( std::numeric_limits<int>::min() )
53   {
54   }
55
56   Actor *actor;        ///< The actor hit (if actor is hit, then this is initialised).
57   Vector2 hitPosition; ///< Position of hit (only valid if actor valid).
58   float distance;      ///< Distance from ray origin to hit actor.
59   int32_t depth;       ///< Depth index of this actor.
60 };
61
62 /**
63  * Creates an Actor handle so that a HitTestFunction provided via the public API can be called.
64  */
65 struct HitTestFunctionWrapper : public HitTestInterface
66 {
67   /**
68    * Constructor
69    *
70    * @param[in] func HitTestFunction to call with an Actor handle.
71    */
72   HitTestFunctionWrapper( Dali::HitTestAlgorithm::HitTestFunction func )
73   : mFunc( func )
74   {
75   }
76
77   virtual bool IsActorHittable( Actor* actor )
78   {
79     return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::CHECK_ACTOR );
80   }
81
82   virtual bool DescendActorHierarchy( Actor* actor )
83   {
84     return mFunc( Dali::Actor( actor ), Dali::HitTestAlgorithm::DESCEND_ACTOR_TREE );
85   }
86
87   virtual bool DoesLayerConsumeHit( Layer* layer )
88   {
89     // Layer::IsTouchConsumed() focuses on touch only. Here we are a wrapper for the public-api
90     // where the caller may want to check for something completely different.
91     // TODO: Should provide a means to let caller decide. For now do not allow layers to consume
92     return false;
93   }
94
95   Dali::HitTestAlgorithm::HitTestFunction mFunc;
96 };
97
98 /**
99  * Used in the hit-test algorithm to check whether the actor is touchable.
100  * It is used by the touch event processor.
101  */
102 struct ActorTouchableCheck : public HitTestInterface
103 {
104   virtual bool IsActorHittable( Actor* actor )
105   {
106     return actor->GetTouchRequired() && // Does the Application or derived actor type require a touch event?
107            actor->IsHittable();         // Is actor sensitive, visible and on the scene?
108   }
109
110   virtual bool DescendActorHierarchy( Actor* actor )
111   {
112     return actor->IsVisible() && // Actor is visible, if not visible then none of its children are visible.
113            actor->IsSensitive(); // Actor is sensitive, if insensitive none of its children should be hittable either.
114   }
115
116   virtual bool DoesLayerConsumeHit( Layer* layer )
117   {
118     return layer->IsTouchConsumed();
119   }
120 };
121
122 /**
123  * Check to see if the actor we're about to hit test is exclusively owned by another rendertask?
124  */
125 bool IsActorExclusiveToAnotherRenderTask( const Actor& actor,
126                                           const RenderTask& renderTask,
127                                           const RenderTaskList::ExclusivesContainer& exclusives )
128
129 {
130   if( exclusives.size() )
131   {
132     for( const auto& exclusive : exclusives )
133     {
134       if( ( exclusive.renderTaskPtr != &renderTask ) && ( exclusive.actor.GetActor() == &actor ) )
135       {
136         return true;
137       }
138     }
139   }
140   return false;
141 }
142
143 /**
144  * Recursively hit test all the actors, without crossing into other layers.
145  * This algorithm performs a Depth-First-Search (DFS) on all Actors within Layer.
146  * Hit-Testing each Actor, noting the distance from the Ray-Origin (3D origin
147  * of touch vector). The closest Hit-Tested Actor is that which is returned.
148  * Exceptions to this rule are:
149  * - When comparing against renderable parents, if Actor is the same distance
150  * or closer than it's renderable parent, then it takes priority.
151  */
152 HitActor HitTestWithinLayer( Actor& actor,
153                              const RenderTask& renderTask,
154                              const RenderTaskList::ExclusivesContainer& exclusives,
155                              const Vector4& rayOrigin,
156                              const Vector4& rayDir,
157                              float& nearClippingPlane,
158                              float& farClippingPlane,
159                              HitTestInterface& hitCheck,
160                              bool& overlayHit,
161                              bool layerIs3d,
162                              uint32_t clippingDepth,
163                              uint32_t clippingBitPlaneMask )
164 {
165   HitActor hit;
166
167   if( IsActorExclusiveToAnotherRenderTask( actor, renderTask, exclusives ) )
168   {
169     return hit;
170   }
171
172   // For clipping, regardless of whether we have hit this actor or not,
173   // we increase the clipping depth if we have hit a clipping actor.
174   // This is used later to ensure all nested clipped children have hit
175   // all clipping actors also for them to be counted as hit.
176   uint32_t newClippingDepth = clippingDepth;
177   bool clippingActor = actor.GetClippingMode() != ClippingMode::DISABLED;
178   if( clippingActor )
179   {
180     ++newClippingDepth;
181   }
182
183   // If we are a clipping actor or hittable...
184   if( clippingActor || hitCheck.IsActorHittable( &actor ) )
185   {
186     Vector3 size( actor.GetCurrentSize() );
187
188     // Ensure the actor has a valid size.
189     // If so, perform a quick ray sphere test to see if our ray is close to the actor.
190     if( size.x > 0.0f && size.y > 0.0f && actor.RaySphereTest( rayOrigin, rayDir ) )
191     {
192       Vector2 hitPointLocal;
193       float distance;
194
195       // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
196       if( actor.RayActorTest( rayOrigin, rayDir, hitPointLocal, distance ) )
197       {
198         if( distance >= nearClippingPlane && distance <= farClippingPlane )
199         {
200           // If the hit has happened on a clipping actor, then add this clipping depth to the mask of hit clipping depths.
201           // This mask shows all the actors that have been hit at different clipping depths.
202           if( clippingActor )
203           {
204             clippingBitPlaneMask |= 1u << clippingDepth;
205           }
206
207           if( overlayHit && !actor.IsOverlay() )
208           {
209             // If we have already hit an overlay and current actor is not an overlay ignore current actor.
210           }
211           else
212           {
213             if( actor.IsOverlay() )
214             {
215               overlayHit = true;
216             }
217
218             // At this point we have hit an actor.
219             // Now perform checks for clipping.
220             // Assume we have hit the actor first as if it is not clipped this would be the case.
221             bool haveHitActor = true;
222
223             // Check if we are performing clipping. IE. if any actors so far have clipping enabled - not necessarily this one.
224             // We can do this by checking the clipping depth has a value 1 or above.
225             if( newClippingDepth >= 1u )
226             {
227               // Now for us to count this actor as hit, we must have also hit
228               // all CLIPPING actors up to this point in the hierarchy as well.
229               // This information is stored in the clippingBitPlaneMask we updated above.
230               // Here we calculate a comparison mask by setting all the bits up to the current depth value.
231               // EG. a depth of 4 (10000 binary) = a mask of 1111 binary.
232               // This allows us a fast way of comparing all bits are set up to this depth.
233               // Note: If the current Actor has clipping, that is included in the depth mask too.
234               uint32_t clippingDepthMask = ( 1u << newClippingDepth ) - 1u;
235
236               // The two masks must be equal to be a hit, as we are already assuming a hit
237               // (for non-clipping mode) then they must be not-equal to disqualify the hit.
238               if( clippingBitPlaneMask != clippingDepthMask )
239               {
240                 haveHitActor = false;
241               }
242             }
243
244             if( haveHitActor )
245             {
246               hit.actor = &actor;
247               hit.hitPosition = hitPointLocal;
248               hit.distance = distance;
249               hit.depth = actor.GetSortingDepth() ;
250
251               if( actor.GetRendererCount() > 0 )
252               {
253                 //Get renderer with maximum depth
254                 int rendererMaxDepth(actor.GetRendererAt( 0 ).Get()->GetDepthIndex());
255                 for( uint32_t i(1); i < actor.GetRendererCount(); ++i )
256                 {
257                   int depth = actor.GetRendererAt( i ).Get()->GetDepthIndex();
258                   if( depth > rendererMaxDepth )
259                   {
260                     rendererMaxDepth = depth;
261                   }
262                 }
263                 hit.depth += rendererMaxDepth;
264               }
265             }
266           }
267         }
268       }
269     }
270   }
271
272   // Find a child hit, until we run out of actors in the current layer.
273   HitActor childHit;
274   if( actor.GetChildCount() > 0 )
275   {
276     childHit.distance = std::numeric_limits<float>::max();
277     childHit.depth = std::numeric_limits<int32_t>::min();
278     ActorContainer& children = actor.GetChildrenInternal();
279
280     // Hit test ALL children and calculate their distance.
281     bool parentIsRenderable = actor.IsRenderable();
282
283     for( ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter )
284     {
285       // Descend tree only if...
286       if ( !( *iter )->IsLayer() &&                                 // Child is NOT a layer, hit testing current layer only
287             ( hitCheck.DescendActorHierarchy( ( *iter ).Get() ) ) ) // We can descend into child hierarchy
288       {
289         HitActor currentHit( HitTestWithinLayer( ( *iter->Get() ),
290                                                  renderTask,
291                                                  exclusives,
292                                                  rayOrigin,
293                                                  rayDir,
294                                                  nearClippingPlane,
295                                                  farClippingPlane,
296                                                  hitCheck,
297                                                  overlayHit,
298                                                  layerIs3d,
299                                                  newClippingDepth,
300                                                  clippingBitPlaneMask ) );
301
302         bool updateChildHit = false;
303         if( currentHit.distance >= 0.0f )
304         {
305           if( layerIs3d )
306           {
307             updateChildHit = ( ( currentHit.depth > childHit.depth ) ||
308                                ( ( currentHit.depth == childHit.depth ) && ( currentHit.distance < childHit.distance ) ) );
309           }
310           else
311           {
312             updateChildHit = currentHit.depth >= childHit.depth;
313           }
314         }
315
316         if( updateChildHit )
317         {
318           if( !parentIsRenderable || currentHit.depth > hit.depth ||
319             ( layerIs3d && ( currentHit.depth == hit.depth && currentHit.distance < hit.distance )) )
320           {
321             childHit = currentHit;
322           }
323         }
324       }
325     }
326   }
327
328   return ( childHit.actor ) ? childHit : hit;
329 }
330
331 /**
332  * Return true if actor is sourceActor or a descendent of sourceActor
333  */
334 bool IsWithinSourceActors( const Actor& sourceActor, const Actor& actor )
335 {
336   if ( &sourceActor == &actor )
337   {
338     return true;
339   }
340
341   Actor* parent = actor.GetParent();
342   if ( parent )
343   {
344     return IsWithinSourceActors( sourceActor, *parent );
345   }
346
347   // Not within source actors
348   return false;
349 }
350
351 /**
352  * Returns true if the layer and all of the layer's parents are visible and sensitive.
353  */
354 inline bool IsActuallyHittable( Layer& layer, const Vector2& screenCoordinates, const Vector2& stageSize, HitTestInterface& hitCheck )
355 {
356   bool hittable( true );
357
358   if( layer.IsClipping() )
359   {
360     ClippingBox box = layer.GetClippingBox();
361
362     if( screenCoordinates.x < static_cast<float>( box.x )||
363         screenCoordinates.x > static_cast<float>( box.x + box.width )||
364         screenCoordinates.y < stageSize.y - static_cast<float>( box.y + box.height ) ||
365         screenCoordinates.y > stageSize.y - static_cast<float>( box.y ) )
366     {
367       // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
368       hittable = false;
369     }
370   }
371
372   if( hittable )
373   {
374     Actor* actor( &layer );
375
376     // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
377     while( actor && hittable )
378     {
379       if( ! hitCheck.DescendActorHierarchy( actor ) )
380       {
381         hittable = false;
382         break;
383       }
384       actor = actor->GetParent();
385     }
386   }
387
388   return hittable;
389 }
390
391 /**
392  * Gets the near and far clipping planes of the camera from which the scene is viewed in the render task.
393  */
394 void GetCameraClippingPlane( RenderTask& renderTask, float& nearClippingPlane, float& farClippingPlane )
395 {
396   CameraActor* cameraActor = renderTask.GetCameraActor();
397   nearClippingPlane = cameraActor->GetNearClippingPlane();
398   farClippingPlane = cameraActor->GetFarClippingPlane();
399 }
400
401 /**
402  * Hit test a RenderTask
403  */
404 bool HitTestRenderTask( const RenderTaskList::ExclusivesContainer& exclusives,
405                         const Vector2& sceneSize,
406                         LayerList& layers,
407                         RenderTask& renderTask,
408                         Vector2 screenCoordinates,
409                         Results& results,
410                         HitTestInterface& hitCheck )
411 {
412   if ( renderTask.IsHittable( screenCoordinates ) )
413   {
414     Viewport viewport;
415     renderTask.GetViewport( viewport );
416     if( screenCoordinates.x < static_cast<float>( viewport.x ) ||
417         screenCoordinates.x > static_cast<float>( viewport.x + viewport.width ) ||
418         screenCoordinates.y < static_cast<float>( viewport.y ) ||
419         screenCoordinates.y > static_cast<float>( viewport.y + viewport.height ) )
420     {
421       // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
422       return false;
423     }
424
425     float nearClippingPlane, farClippingPlane;
426     GetCameraClippingPlane( renderTask, nearClippingPlane, farClippingPlane );
427
428     // Determine the layer depth of the source actor
429     Actor* sourceActor( renderTask.GetSourceActor() );
430     if( sourceActor )
431     {
432       Dali::Layer layer( sourceActor->GetLayer() );
433       if( layer )
434       {
435         const uint32_t sourceActorDepth( layer.GetDepth() );
436
437         CameraActor* cameraActor = renderTask.GetCameraActor();
438         bool pickingPossible = cameraActor->BuildPickingRay(
439             screenCoordinates,
440             viewport,
441             results.rayOrigin,
442             results.rayDirection );
443         if( !pickingPossible )
444         {
445           return false;
446         }
447
448         // Hit test starting with the top layer, working towards the bottom layer.
449         HitActor hit;
450         bool overlayHit = false;
451         bool layerConsumesHit = false;
452
453         for( int32_t i = layers.GetLayerCount() - 1; i >= 0 && !( hit.actor ); --i )
454         {
455           Layer* layer( layers.GetLayer( i ) );
456           overlayHit = false;
457
458           // Ensure layer is touchable (also checks whether ancestors are also touchable)
459           if( IsActuallyHittable( *layer, screenCoordinates, sceneSize, hitCheck ) )
460           {
461             // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
462             if( sourceActorDepth == static_cast<uint32_t>( i ) )
463             {
464               // Recursively hit test the source actor & children, without crossing into other layers.
465               hit = HitTestWithinLayer( *sourceActor,
466                                         renderTask,
467                                         exclusives,
468                                         results.rayOrigin,
469                                         results.rayDirection,
470                                         nearClippingPlane,
471                                         farClippingPlane,
472                                         hitCheck,
473                                         overlayHit,
474                                         layer->GetBehavior() == Dali::Layer::LAYER_3D,
475                                         0u,
476                                         0u );
477             }
478             else if( IsWithinSourceActors( *sourceActor, *layer ) )
479             {
480               // Recursively hit test all the actors, without crossing into other layers.
481               hit = HitTestWithinLayer( *layer,
482                                         renderTask,
483                                         exclusives,
484                                         results.rayOrigin,
485                                         results.rayDirection,
486                                         nearClippingPlane,
487                                         farClippingPlane,
488                                         hitCheck,
489                                         overlayHit,
490                                         layer->GetBehavior() == Dali::Layer::LAYER_3D,
491                                         0u,
492                                         0u );
493             }
494
495             // If this layer is set to consume the hit, then do not check any layers behind it
496             if( hitCheck.DoesLayerConsumeHit( layer ) )
497             {
498               layerConsumesHit = true;
499               break;
500             }
501           }
502         }
503
504         if( hit.actor )
505         {
506           results.renderTask = RenderTaskPtr( &renderTask );
507           results.actor = Dali::Actor( hit.actor );
508           results.actorCoordinates = hit.hitPosition;
509
510           return true; // Success
511         }
512
513         if( layerConsumesHit )
514         {
515           return true; // Also success if layer is consuming the hit
516         }
517       }
518     }
519   }
520   return false;
521 }
522
523 /**
524  * Iterate through the RenderTaskList and perform hit testing.
525  *
526  * @param[in] sceneSize The scene size the tests will be performed in
527  * @param[in] layers The list of layers to test
528  * @param[in] taskList The list of render tasks
529  * @param[out] results Ray information calculated by the camera
530  * @param[in] hitCheck The hit testing interface object to use
531  * @param[in] onScreen True to test on-screen, false to test off-screen
532  * @return True if we have a hit, false otherwise
533  */
534 bool HitTestRenderTaskList( const Vector2& sceneSize,
535                             LayerList& layers,
536                             RenderTaskList& taskList,
537                             const Vector2& screenCoordinates,
538                             Results& results,
539                             HitTestInterface& hitCheck,
540                             bool onScreen )
541 {
542   RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
543   RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
544   const auto& exclusives = taskList.GetExclusivesList();
545
546   for( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
547   {
548     RenderTask& renderTask = *iter->Get();
549     bool isOffscreenRenderTask = ( renderTask.GetTargetFrameBuffer() || renderTask.GetFrameBuffer() );
550     if( (onScreen && isOffscreenRenderTask) || (!onScreen && !isOffscreenRenderTask) )
551     {
552       // Skip to next task
553       continue;
554     }
555
556     if( HitTestRenderTask( exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck ) )
557     {
558       // Return true when an actor is hit (or layer in our render-task consumes the hit)
559       return true; // don't bother checking off screen tasks
560     }
561   }
562
563   return false;
564 }
565
566 /**
567  * Iterate through the RenderTaskList and perform hit testing for both on-screen and off-screen.
568  *
569  * @param[in] sceneSize The scene size the tests will be performed in
570  * @param[in] layers The list of layers to test
571  * @param[in] taskList The list of render tasks
572  * @param[out] results Ray information calculated by the camera
573  * @param[in] hitCheck The hit testing interface object to use
574  * @param[in] onScreen True to test on-screen, false to test off-screen
575  * @return True if we have a hit, false otherwise
576  */
577 bool HitTestForEachRenderTask( const Vector2& sceneSize,
578                                LayerList& layers,
579                                RenderTaskList& taskList,
580                                const Vector2& screenCoordinates,
581                                Results& results,
582                                HitTestInterface& hitCheck )
583 {
584   bool result = false;
585
586   // Check on-screen tasks before off-screen ones.
587   // Hit test order should be reverse of draw order (see ProcessRenderTasks() where off-screen tasks are drawn first).
588   if( HitTestRenderTaskList( sceneSize, layers, taskList, screenCoordinates, results, hitCheck, true  ) ||
589       HitTestRenderTaskList( sceneSize, layers, taskList, screenCoordinates, results, hitCheck, false ) )
590   {
591     // Found hit.
592     result = true;
593   }
594
595   return result;
596 }
597
598 } // unnamed namespace
599
600 HitTestInterface::~HitTestInterface()
601 {
602 }
603
604 bool HitTest( const Vector2& sceneSize, RenderTaskList& taskList, LayerList& layerList, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
605 {
606   bool wasHit( false );
607   // Hit-test the regular on-scene actors
608   Results hitTestResults;
609   HitTestFunctionWrapper hitTestFunctionWrapper( func );
610   if( HitTestForEachRenderTask( sceneSize, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
611   {
612     results.actor = hitTestResults.actor;
613     results.actorCoordinates = hitTestResults.actorCoordinates;
614     wasHit = true;
615   }
616   return wasHit;
617 }
618
619 bool HitTest( const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface )
620 {
621   bool wasHit( false );
622
623   // Hit-test the regular on-scene actors
624   if( !wasHit )
625   {
626     wasHit = HitTestForEachRenderTask( sceneSize, layerList, renderTaskList, screenCoordinates, results, hitTestInterface );
627   }
628   return wasHit;
629 }
630
631 bool HitTest( const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results )
632 {
633   ActorTouchableCheck actorTouchableCheck;
634   return HitTest( sceneSize, renderTaskList, layerList, screenCoordinates, results, actorTouchableCheck );
635 }
636
637 } // namespace HitTestAlgorithm
638
639 } // namespace Internal
640
641 } // namespace Dali