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
index da51fb7..f685837 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <dali/internal/event/events/hit-test-algorithm-impl.h>
 
 // INTERNAL INCLUDES
-#include <dali/integration-api/system-overlay.h>
 #include <dali/public-api/actors/layer.h>
 #include <dali/public-api/math/vector2.h>
 #include <dali/public-api/math/vector4.h>
 #include <dali/integration-api/debug.h>
 #include <dali/internal/event/actors/actor-impl.h>
 #include <dali/internal/event/actors/camera-actor-impl.h>
-#include <dali/internal/event/actors/image-actor-impl.h>
 #include <dali/internal/event/actors/layer-impl.h>
 #include <dali/internal/event/actors/layer-list.h>
-#include <dali/internal/event/common/system-overlay-impl.h>
-#include <dali/internal/event/common/stage-impl.h>
 #include <dali/internal/event/common/projection.h>
 #include <dali/internal/event/images/frame-buffer-image-impl.h>
 #include <dali/internal/event/render-tasks/render-task-impl.h>
@@ -52,19 +48,15 @@ struct HitActor
 {
   HitActor()
   : actor( NULL ),
-    x( 0 ),
-    y( 0 ),
     distance( std::numeric_limits<float>::max() ),
     depth( std::numeric_limits<int>::min() )
   {
   }
 
-  Actor *actor;                         ///< the actor hit. (if actor hit, then initialised)
-  float x;                              ///< x position of hit (only valid if actor valid)
-  float y;                              ///< y position of hit (only valid if actor valid)
-  float distance;                       ///< distance from ray origin to hit actor
-  int depth;                            ///< depth index of this actor
-
+  Actor *actor;        ///< The actor hit (if actor is hit, then this is initialised).
+  Vector2 hitPosition; ///< Position of hit (only valid if actor valid).
+  float distance;      ///< Distance from ray origin to hit actor.
+  int32_t depth;       ///< Depth index of this actor.
 };
 
 /**
@@ -132,19 +124,16 @@ struct ActorTouchableCheck : public HitTestInterface
  */
 bool IsActorExclusiveToAnotherRenderTask( const Actor& actor,
                                           const RenderTask& renderTask,
-                                          const Vector< RenderTaskList::Exclusive >& exclusives )
+                                          const RenderTaskList::ExclusivesContainer& exclusives )
 
 {
-  if ( exclusives.Size() )
+  if( exclusives.size() )
   {
-    for ( Vector< RenderTaskList::Exclusive >::Iterator exclusiveIt = exclusives.Begin(); exclusives.End() != exclusiveIt; ++exclusiveIt )
+    for( const auto& exclusive : exclusives )
     {
-      if ( exclusiveIt->renderTaskPtr != &renderTask )
+      if( ( exclusive.renderTaskPtr != &renderTask ) && ( exclusive.actor.GetActor() == &actor ) )
       {
-        if ( exclusiveIt->actorPtr == &actor )
-        {
-          return true;
-        }
+        return true;
       }
     }
   }
@@ -162,42 +151,45 @@ bool IsActorExclusiveToAnotherRenderTask( const Actor& actor,
  */
 HitActor HitTestWithinLayer( Actor& actor,
                              const RenderTask& renderTask,
-                             const Vector< RenderTaskList::Exclusive >& exclusives,
+                             const RenderTaskList::ExclusivesContainer& exclusives,
                              const Vector4& rayOrigin,
                              const Vector4& rayDir,
                              float& nearClippingPlane,
                              float& farClippingPlane,
                              HitTestInterface& hitCheck,
-                             bool& stencilOnLayer,
-                             bool& stencilHit,
-                             bool parentIsStencil,
-                             bool layerIs3d )
+                             bool& overlayHit,
+                             bool layerIs3d,
+                             uint32_t clippingDepth,
+                             uint32_t clippingBitPlaneMask )
 {
   HitActor hit;
 
-  if ( IsActorExclusiveToAnotherRenderTask( actor, renderTask, exclusives ) )
+  if( IsActorExclusiveToAnotherRenderTask( actor, renderTask, exclusives ) )
   {
     return hit;
   }
 
-  // Children should inherit the stencil draw mode
-  bool isStencil = parentIsStencil;
-
-  if ( actor.GetDrawMode() == DrawMode::STENCIL && actor.IsVisible() )
+  // For clipping, regardless of whether we have hit this actor or not,
+  // we increase the clipping depth if we have hit a clipping actor.
+  // This is used later to ensure all nested clipped children have hit
+  // all clipping actors also for them to be counted as hit.
+  uint32_t newClippingDepth = clippingDepth;
+  bool clippingActor = actor.GetClippingMode() != ClippingMode::DISABLED;
+  if( clippingActor )
   {
-    isStencil = true;
-    stencilOnLayer = true;
+    ++newClippingDepth;
   }
 
-  // If we are a stencil or hittable...
-  if ( isStencil || hitCheck.IsActorHittable( &actor ) )
+  // If we are a clipping actor or hittable...
+  if( clippingActor || hitCheck.IsActorHittable( &actor ) )
   {
     Vector3 size( actor.GetCurrentSize() );
 
-    if ( size.x > 0.0f && size.y > 0.0f &&          // Ensure the actor has a valid size.
-         actor.RaySphereTest( rayOrigin, rayDir ) ) // Perform quicker ray sphere test to see if our ray is close to the actor.
+    // Ensure the actor has a valid size.
+    // If so, perform a quick ray sphere test to see if our ray is close to the actor.
+    if( size.x > 0.0f && size.y > 0.0f && actor.RaySphereTest( rayOrigin, rayDir ) )
     {
-      Vector4 hitPointLocal;
+      Vector2 hitPointLocal;
       float distance;
 
       // Finally, perform a more accurate ray test to see if our ray actually hits the actor.
@@ -205,27 +197,71 @@ HitActor HitTestWithinLayer( Actor& actor,
       {
         if( distance >= nearClippingPlane && distance <= farClippingPlane )
         {
-          // If the hit has happened on a stencil then register, but don't record as hit result
-          if ( isStencil )
+          // If the hit has happened on a clipping actor, then add this clipping depth to the mask of hit clipping depths.
+          // This mask shows all the actors that have been hit at different clipping depths.
+          if( clippingActor )
+          {
+            clippingBitPlaneMask |= 1u << clippingDepth;
+          }
+
+          if( overlayHit && !actor.IsOverlay() )
           {
-            stencilHit = true;
+            // If we have already hit an overlay and current actor is not an overlay ignore current actor.
           }
           else
           {
-            hit.actor = &actor;
-            hit.x = hitPointLocal.x;
-            hit.y = hitPointLocal.y;
-            hit.distance = distance;
-            hit.depth = actor.GetHierarchyDepth() * Dali::Layer::TREE_DEPTH_MULTIPLIER;
-
-            // Is this actor an Image Actor or contains a renderer?
-            if ( ImageActor* imageActor = dynamic_cast< ImageActor* >( &actor ) )
+            if( actor.IsOverlay() )
             {
-              hit.depth += imageActor->GetDepthIndex();
+              overlayHit = true;
             }
-            else if ( actor.GetRendererCount() )
+
+            // At this point we have hit an actor.
+            // Now perform checks for clipping.
+            // Assume we have hit the actor first as if it is not clipped this would be the case.
+            bool haveHitActor = true;
+
+            // Check if we are performing clipping. IE. if any actors so far have clipping enabled - not necessarily this one.
+            // We can do this by checking the clipping depth has a value 1 or above.
+            if( newClippingDepth >= 1u )
             {
-              hit.depth += actor.GetRendererAt( 0 ).GetDepthIndex();
+              // Now for us to count this actor as hit, we must have also hit
+              // all CLIPPING actors up to this point in the hierarchy as well.
+              // This information is stored in the clippingBitPlaneMask we updated above.
+              // Here we calculate a comparison mask by setting all the bits up to the current depth value.
+              // EG. a depth of 4 (10000 binary) = a mask of 1111 binary.
+              // This allows us a fast way of comparing all bits are set up to this depth.
+              // Note: If the current Actor has clipping, that is included in the depth mask too.
+              uint32_t clippingDepthMask = ( 1u << newClippingDepth ) - 1u;
+
+              // The two masks must be equal to be a hit, as we are already assuming a hit
+              // (for non-clipping mode) then they must be not-equal to disqualify the hit.
+              if( clippingBitPlaneMask != clippingDepthMask )
+              {
+                haveHitActor = false;
+              }
+            }
+
+            if( haveHitActor )
+            {
+              hit.actor = &actor;
+              hit.hitPosition = hitPointLocal;
+              hit.distance = distance;
+              hit.depth = actor.GetSortingDepth() ;
+
+              if( actor.GetRendererCount() > 0 )
+              {
+                //Get renderer with maximum depth
+                int rendererMaxDepth(actor.GetRendererAt( 0 ).Get()->GetDepthIndex());
+                for( uint32_t i(1); i < actor.GetRendererCount(); ++i )
+                {
+                  int depth = actor.GetRendererAt( i ).Get()->GetDepthIndex();
+                  if( depth > rendererMaxDepth )
+                  {
+                    rendererMaxDepth = depth;
+                  }
+                }
+                hit.depth += rendererMaxDepth;
+              }
             }
           }
         }
@@ -233,18 +269,12 @@ HitActor HitTestWithinLayer( Actor& actor,
     }
   }
 
-  // If we are a stencil (or a child of a stencil) and we have already ascertained that the stencil has been hit then there is no need to hit-test the children of this stencil-actor
-  if ( isStencil && stencilHit  )
-  {
-    return hit;
-  }
-
   // Find a child hit, until we run out of actors in the current layer.
   HitActor childHit;
   if( actor.GetChildCount() > 0 )
   {
     childHit.distance = std::numeric_limits<float>::max();
-    childHit.depth = std::numeric_limits<int>::min();
+    childHit.depth = std::numeric_limits<int32_t>::min();
     ActorContainer& children = actor.GetChildrenInternal();
 
     // Hit test ALL children and calculate their distance.
@@ -253,29 +283,29 @@ HitActor HitTestWithinLayer( Actor& actor,
     for( ActorIter iter = children.begin(), endIter = children.end(); iter != endIter; ++iter )
     {
       // Descend tree only if...
-      if ( !(*iter)->IsLayer() &&    // Child is NOT a layer, hit testing current layer only or Child is not a layer and we've inherited the stencil draw mode
-           ( isStencil || hitCheck.DescendActorHierarchy( ( *iter ).Get() ) ) ) // We are a stencil OR we can descend into child hierarchy
+      if ( !( *iter )->IsLayer() &&                                 // Child is NOT a layer, hit testing current layer only
+            ( hitCheck.DescendActorHierarchy( ( *iter ).Get() ) ) ) // We can descend into child hierarchy
       {
-        HitActor currentHit( HitTestWithinLayer(  (*iter->Get()),
-                                                  renderTask,
-                                                  exclusives,
-                                                  rayOrigin,
-                                                  rayDir,
-                                                  nearClippingPlane,
-                                                  farClippingPlane,
-                                                  hitCheck,
-                                                  stencilOnLayer,
-                                                  stencilHit,
-                                                  isStencil,
-                                                  layerIs3d) );
+        HitActor currentHit( HitTestWithinLayer( ( *iter->Get() ),
+                                                 renderTask,
+                                                 exclusives,
+                                                 rayOrigin,
+                                                 rayDir,
+                                                 nearClippingPlane,
+                                                 farClippingPlane,
+                                                 hitCheck,
+                                                 overlayHit,
+                                                 layerIs3d,
+                                                 newClippingDepth,
+                                                 clippingBitPlaneMask ) );
 
         bool updateChildHit = false;
-        if ( currentHit.distance >= 0.0f )
+        if( currentHit.distance >= 0.0f )
         {
           if( layerIs3d )
           {
             updateChildHit = ( ( currentHit.depth > childHit.depth ) ||
-                ( ( currentHit.depth == childHit.depth ) && ( currentHit.distance < childHit.distance ) ) );
+                               ( ( currentHit.depth == childHit.depth ) && ( currentHit.distance < childHit.distance ) ) );
           }
           else
           {
@@ -283,17 +313,18 @@ HitActor HitTestWithinLayer( Actor& actor,
           }
         }
 
-        if ( updateChildHit )
+        if( updateChildHit )
         {
           if( !parentIsRenderable || currentHit.depth > hit.depth ||
             ( layerIs3d && ( currentHit.depth == hit.depth && currentHit.distance < hit.distance )) )
-            {
-              childHit = currentHit;
-            }
+          {
+            childHit = currentHit;
+          }
         }
       }
     }
   }
+
   return ( childHit.actor ) ? childHit : hit;
 }
 
@@ -306,13 +337,11 @@ bool IsWithinSourceActors( const Actor& sourceActor, const Actor& actor )
   {
     return true;
   }
-  else
+
+  Actor* parent = actor.GetParent();
+  if ( parent )
   {
-    Actor* parent = actor.GetParent();
-    if ( parent )
-    {
-      return IsWithinSourceActors( sourceActor, *parent );
-    }
+    return IsWithinSourceActors( sourceActor, *parent );
   }
 
   // Not within source actors
@@ -326,28 +355,28 @@ inline bool IsActuallyHittable( Layer& layer, const Vector2& screenCoordinates,
 {
   bool hittable( true );
 
-  if(layer.IsClipping())
+  if( layer.IsClipping() )
   {
     ClippingBox box = layer.GetClippingBox();
 
-    if( screenCoordinates.x < box.x ||
-        screenCoordinates.x > box.x + box.width ||
-        screenCoordinates.y < stageSize.y - (box.y + box.height) ||
-        screenCoordinates.y > stageSize.y - box.y)
+    if( screenCoordinates.x < static_cast<float>( box.x )||
+        screenCoordinates.x > static_cast<float>( box.x + box.width )||
+        screenCoordinates.y < stageSize.y - static_cast<float>( box.y + box.height ) ||
+        screenCoordinates.y > stageSize.y - static_cast<float>( box.y ) )
     {
       // Not touchable if clipping is enabled in the layer and the screen coordinate is outside the clip region.
       hittable = false;
     }
   }
 
-  if(hittable)
+  if( hittable )
   {
     Actor* actor( &layer );
 
     // Ensure that we can descend into the layer's (or any of its parent's) hierarchy.
-    while ( actor && hittable )
+    while( actor && hittable )
     {
-      if ( ! hitCheck.DescendActorHierarchy( actor ) )
+      if( ! hitCheck.DescendActorHierarchy( actor ) )
       {
         hittable = false;
         break;
@@ -372,8 +401,8 @@ void GetCameraClippingPlane( RenderTask& renderTask, float& nearClippingPlane, f
 /**
  * Hit test a RenderTask
  */
-bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
-                        Stage& stage,
+bool HitTestRenderTask( const RenderTaskList::ExclusivesContainer& exclusives,
+                        const Vector2& sceneSize,
                         LayerList& layers,
                         RenderTask& renderTask,
                         Vector2 screenCoordinates,
@@ -384,26 +413,26 @@ bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
   {
     Viewport viewport;
     renderTask.GetViewport( viewport );
-    if( screenCoordinates.x < viewport.x ||
-        screenCoordinates.x > viewport.x + viewport.width ||
-        screenCoordinates.y < viewport.y ||
-        screenCoordinates.y > viewport.y + viewport.height )
+    if( screenCoordinates.x < static_cast<float>( viewport.x ) ||
+        screenCoordinates.x > static_cast<float>( viewport.x + viewport.width ) ||
+        screenCoordinates.y < static_cast<float>( viewport.y ) ||
+        screenCoordinates.y > static_cast<float>( viewport.y + viewport.height ) )
     {
       // The screen coordinate is outside the viewport of render task. The viewport clips all layers.
       return false;
     }
 
     float nearClippingPlane, farClippingPlane;
-    GetCameraClippingPlane(renderTask, nearClippingPlane, farClippingPlane);
+    GetCameraClippingPlane( renderTask, nearClippingPlane, farClippingPlane );
 
     // Determine the layer depth of the source actor
     Actor* sourceActor( renderTask.GetSourceActor() );
-    if ( sourceActor )
+    if( sourceActor )
     {
       Dali::Layer layer( sourceActor->GetLayer() );
-      if ( layer )
+      if( layer )
       {
-        const unsigned int sourceActorDepth( layer.GetDepth() );
+        const uint32_t sourceActorDepth( layer.GetDepth() );
 
         CameraActor* cameraActor = renderTask.GetCameraActor();
         bool pickingPossible = cameraActor->BuildPickingRay(
@@ -418,24 +447,19 @@ bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
 
         // Hit test starting with the top layer, working towards the bottom layer.
         HitActor hit;
-        bool stencilOnLayer = false;
-        bool stencilHit = false;
+        bool overlayHit = false;
         bool layerConsumesHit = false;
 
-        const Vector2& stageSize = stage.GetSize();
-
-        for (int i=layers.GetLayerCount()-1; i>=0 && !(hit.actor); --i)
+        for( int32_t i = layers.GetLayerCount() - 1; i >= 0 && !( hit.actor ); --i )
         {
-          Layer* layer( layers.GetLayer(i) );
-          HitActor previousHit = hit;
-          stencilOnLayer = false;
-          stencilHit = false;
+          Layer* layer( layers.GetLayer( i ) );
+          overlayHit = false;
 
           // Ensure layer is touchable (also checks whether ancestors are also touchable)
-          if ( IsActuallyHittable ( *layer, screenCoordinates, stageSize, hitCheck ) )
+          if( IsActuallyHittable( *layer, screenCoordinates, sceneSize, hitCheck ) )
           {
             // Always hit-test the source actor; otherwise test whether the layer is below the source actor in the hierarchy
-            if ( sourceActorDepth == static_cast<unsigned int>(i) )
+            if( sourceActorDepth == static_cast<uint32_t>( i ) )
             {
               // Recursively hit test the source actor & children, without crossing into other layers.
               hit = HitTestWithinLayer( *sourceActor,
@@ -446,12 +470,12 @@ bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
                                         nearClippingPlane,
                                         farClippingPlane,
                                         hitCheck,
-                                        stencilOnLayer,
-                                        stencilHit,
-                                        false,
-                                        layer->GetBehavior() == Dali::Layer::LAYER_3D);
+                                        overlayHit,
+                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
+                                        0u,
+                                        0u );
             }
-            else if ( IsWithinSourceActors( *sourceActor, *layer ) )
+            else if( IsWithinSourceActors( *sourceActor, *layer ) )
             {
               // Recursively hit test all the actors, without crossing into other layers.
               hit = HitTestWithinLayer( *layer,
@@ -462,36 +486,31 @@ bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
                                         nearClippingPlane,
                                         farClippingPlane,
                                         hitCheck,
-                                        stencilOnLayer,
-                                        stencilHit,
-                                        false,
-                                        layer->GetBehavior() == Dali::Layer::LAYER_3D);
-            }
-
-            // If a stencil on this layer hasn't been hit, then discard hit results for this layer if our current hit actor is renderable
-            if ( stencilOnLayer && !stencilHit &&
-                 hit.actor && hit.actor->IsRenderable() )
-            {
-              hit = previousHit;
+                                        overlayHit,
+                                        layer->GetBehavior() == Dali::Layer::LAYER_3D,
+                                        0u,
+                                        0u );
             }
 
             // If this layer is set to consume the hit, then do not check any layers behind it
-            if ( hitCheck.DoesLayerConsumeHit( layer ) )
+            if( hitCheck.DoesLayerConsumeHit( layer ) )
             {
               layerConsumesHit = true;
               break;
             }
           }
         }
-        if ( hit.actor )
+
+        if( hit.actor )
         {
-          results.renderTask = Dali::RenderTask(&renderTask);
-          results.actor = Dali::Actor(hit.actor);
-          results.actorCoordinates.x = hit.x;
-          results.actorCoordinates.y = hit.y;
+          results.renderTask = RenderTaskPtr( &renderTask );
+          results.actor = Dali::Actor( hit.actor );
+          results.actorCoordinates = hit.hitPosition;
+
           return true; // Success
         }
-        else if ( layerConsumesHit )
+
+        if( layerConsumesHit )
         {
           return true; // Also success if layer is consuming the hit
         }
@@ -502,90 +521,93 @@ bool HitTestRenderTask( const Vector< RenderTaskList::Exclusive >& exclusives,
 }
 
 /**
- * Iterate through RenderTaskList and perform hit test.
+ * Iterate through the RenderTaskList and perform hit testing.
  *
- * @return true if we have a hit, false otherwise
+ * @param[in] sceneSize The scene size the tests will be performed in
+ * @param[in] layers The list of layers to test
+ * @param[in] taskList The list of render tasks
+ * @param[out] results Ray information calculated by the camera
+ * @param[in] hitCheck The hit testing interface object to use
+ * @param[in] onScreen True to test on-screen, false to test off-screen
+ * @return True if we have a hit, false otherwise
  */
-bool HitTestForEachRenderTask( Stage& stage,
-                               LayerList& layers,
-                               RenderTaskList& taskList,
-                               const Vector2& screenCoordinates,
-                               Results& results,
-                               HitTestInterface& hitCheck )
+bool HitTestRenderTaskList( const Vector2& sceneSize,
+                            LayerList& layers,
+                            RenderTaskList& taskList,
+                            const Vector2& screenCoordinates,
+                            Results& results,
+                            HitTestInterface& hitCheck,
+                            bool onScreen )
 {
   RenderTaskList::RenderTaskContainer& tasks = taskList.GetTasks();
   RenderTaskList::RenderTaskContainer::reverse_iterator endIter = tasks.rend();
+  const auto& exclusives = taskList.GetExclusivesList();
 
-  const Vector< RenderTaskList::Exclusive >& exclusives = taskList.GetExclusivesList();
-
-  // Check onscreen tasks before offscreen ones, hit test order should be reverse of draw order (see ProcessRenderTasks() where offscreen tasks are drawn first).
-
-  // on screen
-  for ( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
+  for( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
   {
-    RenderTask& renderTask = GetImplementation( *iter );
-    Dali::FrameBufferImage frameBufferImage = renderTask.GetTargetFrameBuffer();
-
-    // Note that if frameBufferImage is NULL we are using the default (on screen) render target
-    if(frameBufferImage)
+    RenderTask& renderTask = *iter->Get();
+    bool isOffscreenRenderTask = ( renderTask.GetTargetFrameBuffer() || renderTask.GetFrameBuffer() );
+    if( (onScreen && isOffscreenRenderTask) || (!onScreen && !isOffscreenRenderTask) )
     {
-      ResourceId id = GetImplementation(frameBufferImage).GetResourceId();
-
-      // on screen only
-      if(0 != id)
-      {
-        // Skip to next task
-        continue;
-      }
+      // Skip to next task
+      continue;
     }
 
-    if ( HitTestRenderTask( exclusives, stage, layers, renderTask, screenCoordinates, results, hitCheck ) )
+    if( HitTestRenderTask( exclusives, sceneSize, layers, renderTask, screenCoordinates, results, hitCheck ) )
     {
       // Return true when an actor is hit (or layer in our render-task consumes the hit)
       return true; // don't bother checking off screen tasks
     }
   }
 
-  // off screen
-  for ( RenderTaskList::RenderTaskContainer::reverse_iterator iter = tasks.rbegin(); endIter != iter; ++iter )
-  {
-    RenderTask& renderTask = GetImplementation( *iter );
-    Dali::FrameBufferImage frameBufferImage = renderTask.GetTargetFrameBuffer();
-
-    // Note that if frameBufferImage is NULL we are using the default (on screen) render target
-    if(frameBufferImage)
-    {
-      ResourceId id = GetImplementation(frameBufferImage).GetResourceId();
+  return false;
+}
 
-      // off screen only
-      if(0 == id)
-      {
-        // Skip to next task
-        continue;
-      }
+/**
+ * Iterate through the RenderTaskList and perform hit testing for both on-screen and off-screen.
+ *
+ * @param[in] sceneSize The scene size the tests will be performed in
+ * @param[in] layers The list of layers to test
+ * @param[in] taskList The list of render tasks
+ * @param[out] results Ray information calculated by the camera
+ * @param[in] hitCheck The hit testing interface object to use
+ * @param[in] onScreen True to test on-screen, false to test off-screen
+ * @return True if we have a hit, false otherwise
+ */
+bool HitTestForEachRenderTask( const Vector2& sceneSize,
+                               LayerList& layers,
+                               RenderTaskList& taskList,
+                               const Vector2& screenCoordinates,
+                               Results& results,
+                               HitTestInterface& hitCheck )
+{
+  bool result = false;
 
-      if ( HitTestRenderTask( exclusives, stage, layers, renderTask, screenCoordinates, results, hitCheck ) )
-      {
-        // Return true when an actor is hit (or a layer in our render-task consumes the hit)
-        return true;
-      }
-    }
+  // Check on-screen tasks before off-screen ones.
+  // Hit test order should be reverse of draw order (see ProcessRenderTasks() where off-screen tasks are drawn first).
+  if( HitTestRenderTaskList( sceneSize, layers, taskList, screenCoordinates, results, hitCheck, true  ) ||
+      HitTestRenderTaskList( sceneSize, layers, taskList, screenCoordinates, results, hitCheck, false ) )
+  {
+    // Found hit.
+    result = true;
   }
-  return false;
+
+  return result;
 }
 
 } // unnamed namespace
 
-bool HitTest( Stage& stage, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
+HitTestInterface::~HitTestInterface()
 {
-  bool wasHit( false );
-  // Hit-test the regular on-stage actors
-  RenderTaskList& taskList = stage.GetRenderTaskList();
-  LayerList& layerList = stage.GetLayerList();
+}
 
+bool HitTest( const Vector2& sceneSize, RenderTaskList& taskList, LayerList& layerList, const Vector2& screenCoordinates, Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
+{
+  bool wasHit( false );
+  // Hit-test the regular on-scene actors
   Results hitTestResults;
   HitTestFunctionWrapper hitTestFunctionWrapper( func );
-  if (  HitTestForEachRenderTask( stage, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
+  if( HitTestForEachRenderTask( sceneSize, layerList, taskList, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
   {
     results.actor = hitTestResults.actor;
     results.actorCoordinates = hitTestResults.actorCoordinates;
@@ -594,53 +616,22 @@ bool HitTest( Stage& stage, const Vector2& screenCoordinates, Dali::HitTestAlgor
   return wasHit;
 }
 
-bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface )
+bool HitTest( const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results, HitTestInterface& hitTestInterface )
 {
   bool wasHit( false );
 
-  // Hit-test the system-overlay actors first
-  SystemOverlay* systemOverlay = stage.GetSystemOverlayInternal();
-
-  if ( systemOverlay )
+  // Hit-test the regular on-scene actors
+  if( !wasHit )
   {
-    RenderTaskList& overlayTaskList = systemOverlay->GetOverlayRenderTasks();
-    LayerList& overlayLayerList = systemOverlay->GetLayerList();
-
-    wasHit = HitTestForEachRenderTask( stage, overlayLayerList, overlayTaskList, screenCoordinates, results, hitTestInterface );
-  }
-
-  // Hit-test the regular on-stage actors
-  if ( !wasHit )
-  {
-    RenderTaskList& taskList = stage.GetRenderTaskList();
-    LayerList& layerList = stage.GetLayerList();
-
-    wasHit = HitTestForEachRenderTask( stage, layerList, taskList, screenCoordinates, results, hitTestInterface );
+    wasHit = HitTestForEachRenderTask( sceneSize, layerList, renderTaskList, screenCoordinates, results, hitTestInterface );
   }
   return wasHit;
 }
 
-bool HitTest( Stage& stage, const Vector2& screenCoordinates, Results& results )
+bool HitTest( const Vector2& sceneSize, RenderTaskList& renderTaskList, LayerList& layerList, const Vector2& screenCoordinates, Results& results )
 {
   ActorTouchableCheck actorTouchableCheck;
-  return HitTest( stage, screenCoordinates, results, actorTouchableCheck );
-}
-
-bool HitTest( Stage& stage, RenderTask& renderTask, const Vector2& screenCoordinates,
-              Dali::HitTestAlgorithm::Results& results, Dali::HitTestAlgorithm::HitTestFunction func )
-{
-  bool wasHit( false );
-  Results hitTestResults;
-
-  const Vector< RenderTaskList::Exclusive >& exclusives = Stage::GetCurrent()->GetRenderTaskList().GetExclusivesList();
-  HitTestFunctionWrapper hitTestFunctionWrapper( func );
-  if ( HitTestRenderTask( exclusives, stage, stage.GetLayerList(), renderTask, screenCoordinates, hitTestResults, hitTestFunctionWrapper ) )
-  {
-    results.actor = hitTestResults.actor;
-    results.actorCoordinates = hitTestResults.actorCoordinates;
-    wasHit = true;
-  }
-  return wasHit;
+  return HitTest( sceneSize, renderTaskList, layerList, screenCoordinates, results, actorTouchableCheck );
 }
 
 } // namespace HitTestAlgorithm