Add Post Constraint that works after transform 74/291574/6
authorseungho baek <sbsh.baek@samsung.com>
Tue, 18 Apr 2023 11:05:14 +0000 (20:05 +0900)
committerseungho baek <sbsh.baek@samsung.com>
Wed, 19 Apr 2023 06:23:13 +0000 (15:23 +0900)
Change-Id: I90a7b2d731623691915ed5c01500ac6928d25112
Signed-off-by: seungho baek <sbsh.baek@samsung.com>
13 files changed:
automated-tests/src/dali/utc-Dali-Constraint.cpp
dali/internal/event/animation/constraint-base.cpp
dali/internal/event/animation/constraint-base.h
dali/internal/event/animation/constraint-impl.h
dali/internal/update/common/property-owner-messages.h
dali/internal/update/common/property-owner.cpp
dali/internal/update/common/property-owner.h
dali/internal/update/manager/update-algorithms.cpp
dali/internal/update/manager/update-algorithms.h
dali/internal/update/manager/update-manager.cpp
dali/internal/update/manager/update-manager.h
dali/public-api/animation/constraint.cpp
dali/public-api/animation/constraint.h

index 30b361f..e0255b2 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <dali-test-suite-utils.h>
 #include <dali/public-api/dali-core.h>
+#include <mesh-builder.h>
 #include <stdlib.h>
 
 #include <iostream>
@@ -1628,4 +1629,67 @@ int UtcDaliConstraintComponentNonTransformPropertyConstraintP(void)
   ComponentTest::CheckComponentProperty(application, actor, Actor::Property::COLOR_ALPHA); // Component 3
 
   END_TEST;
+}
+
+
+namespace PostConstraintTest
+{
+void CheckComponentProperty(TestApplication& application, Actor& actor, Handle target)
+{
+  actor.SetProperty(Actor::Property::POSITION, Vector3::ONE);
+  DALI_TEST_EQUALS(actor.GetProperty<Vector3>(Actor::Property::POSITION), Vector3::ONE, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  actor.SetProperty(Actor::Property::POSITION, Vector3::ONE * 2.0f);
+
+  DALI_TEST_EQUALS(actor.GetProperty<Vector3>(Actor::Property::POSITION), Vector3::ONE * 2.0f, TEST_LOCATION);
+  DALI_TEST_EQUALS(actor.GetCurrentProperty<Vector3>(Actor::Property::POSITION), Vector3::ONE, TEST_LOCATION);
+
+  Property::Index prePropertyIndex = target.RegisterProperty("testPreProperty", Vector3::ZERO);
+  Constraint preConstraint = Constraint::New<Vector3>(target, prePropertyIndex, [](Vector3& output, const PropertyInputContainer& inputs) {
+    output = inputs[0]->GetVector3();
+  });
+  preConstraint.AddSource(Source{actor, Actor::Property::WORLD_POSITION});
+  preConstraint.Apply();
+
+  Property::Index postPropertyIndex = target.RegisterProperty("testPostProperty", Vector3::ZERO);
+  Constraint postConstraint = Constraint::New<Vector3>(target, postPropertyIndex, [](Vector3& output, const PropertyInputContainer& inputs) {
+    output = inputs[0]->GetVector3();
+  });
+  postConstraint.AddSource(Source{actor, Actor::Property::WORLD_POSITION});
+  postConstraint.ApplyPost();
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(target.GetCurrentProperty<Vector3>(prePropertyIndex), Vector3(-239.0, -399.0, 1.0), TEST_LOCATION);
+  DALI_TEST_EQUALS(target.GetCurrentProperty<Vector3>(postPropertyIndex), Vector3(-238.0, -398.0, 2.0), TEST_LOCATION);
+
+  preConstraint.Remove();
+  postConstraint.Remove();
+}
+}
+
+int UtcDaliConstraintApplyPost(void)
+{
+  TestApplication application;
+
+  Actor actor = Actor::New();
+  application.GetScene().Add(actor);
+
+  Geometry       targetGeometry = CreateQuadGeometry();
+  Shader         targetShader   = CreateShader();
+  Renderer       targetRenderer = Renderer::New(targetGeometry, targetShader);
+  Actor          targetActor    = Actor::New();
+  RenderTaskList taskList       = application.GetScene().GetRenderTaskList();
+
+  application.GetScene().Add(targetActor);
+  PostConstraintTest::CheckComponentProperty(application, actor, targetShader);         // Shader
+  PostConstraintTest::CheckComponentProperty(application, actor, targetRenderer);       // Renderer
+  PostConstraintTest::CheckComponentProperty(application, actor, targetActor);          // Actor(Node)
+  PostConstraintTest::CheckComponentProperty(application, actor, taskList.GetTask(0u)); // RenderTask
+
+  END_TEST;
 }
\ No newline at end of file
index 24230a7..d41825d 100644 (file)
@@ -62,7 +62,8 @@ ConstraintBase::ConstraintBase(Object& object, Property::Index targetPropertyInd
   mRemoveAction(Dali::Constraint::DEFAULT_REMOVE_ACTION),
   mTag(0),
   mApplied(false),
-  mSourceDestroyed(false)
+  mSourceDestroyed(false),
+  mIsPreConstraint(true)
 {
   ObserveObject(object);
 }
@@ -103,15 +104,25 @@ void ConstraintBase::AddSource(Source source)
   }
 }
 
-void ConstraintBase::Apply()
+void ConstraintBase::Apply(bool isPreConstraint)
 {
   if(mTargetObject && !mApplied && !mSourceDestroyed)
   {
     mApplied = true;
-    ConnectConstraint();
+    mIsPreConstraint = isPreConstraint;
+    ConnectConstraint(mIsPreConstraint);
 
     mTargetObject->ApplyConstraint(*this);
   }
+  else
+  {
+    DALI_LOG_ERROR("Fail to apply constraint\n");
+  }
+}
+
+void ConstraintBase::ApplyPost()
+{
+  Apply(false);
 }
 
 void ConstraintBase::Remove()
@@ -122,6 +133,7 @@ void ConstraintBase::Remove()
   {
     mTargetObject->RemoveConstraint(*this);
   }
+  mIsPreConstraint = true;
 }
 
 void ConstraintBase::RemoveInternal()
@@ -137,7 +149,14 @@ void ConstraintBase::RemoveInternal()
       {
         const SceneGraph::PropertyOwner& propertyOwner = mTargetObject->GetSceneObject();
         // Remove from scene-graph
-        RemoveConstraintMessage(GetEventThreadServices(), propertyOwner, *(mSceneGraphConstraint));
+        if(mIsPreConstraint)
+        {
+          RemoveConstraintMessage(GetEventThreadServices(), propertyOwner, *(mSceneGraphConstraint));
+        }
+        else
+        {
+          RemovePostConstraintMessage(GetEventThreadServices(), propertyOwner, *(mSceneGraphConstraint));
+        }
         // mSceneGraphConstraint will be deleted in update-thread, remove dangling pointer
         mSceneGraphConstraint = nullptr;
       }
@@ -186,7 +205,7 @@ void ConstraintBase::SceneObjectAdded(Object& object)
      (nullptr == mSceneGraphConstraint) &&
      mTargetObject)
   {
-    ConnectConstraint();
+    ConnectConstraint(mIsPreConstraint);
   }
 }
 
index 2b8ba21..e286e43 100644 (file)
@@ -82,7 +82,12 @@ public:
   /**
    * @copydoc Dali::Constraint::Apply()
    */
-  void Apply();
+  void Apply(bool isPreConstraint = true);
+
+  /**
+   * @copydoc Dali::Constraint::ApplyPost()
+   */
+  void ApplyPost();
 
   /**
    * @copydoc Dali::Constraint::Remove()
@@ -172,7 +177,7 @@ private:
   /**
    * Connect the constraint
    */
-  virtual void ConnectConstraint() = 0;
+  virtual void ConnectConstraint(bool isPreConstraint = true) = 0;
 
 protected:
   /**
@@ -221,6 +226,7 @@ protected:
   uint32_t                          mTag;
   bool                              mApplied : 1;         ///< Whether the constraint has been applied
   bool                              mSourceDestroyed : 1; ///< Is set to true if any of our input source objects are destroyed
+  bool                              mIsPreConstraint : 1; ///< Is set to true if this constraint is run before transform.
 };
 
 } // namespace Internal
index 7b7ec26..1fdb68e 100644 (file)
@@ -106,7 +106,7 @@ private:
   /**
    * @copydoc ConstraintBase::ConnectConstraint()
    */
-  void ConnectConstraint() final
+  void ConnectConstraint(bool isPreConstraint) final
   {
     // Should not come here if target object has been destroyed
     DALI_ASSERT_DEBUG(nullptr != mTargetObject);
@@ -148,7 +148,14 @@ private:
         resetter              = SceneGraph::ConstraintResetter::New(targetObject, *targetProperty, *mSceneGraphConstraint);
       }
       OwnerPointer<SceneGraph::ConstraintBase> transferOwnership(const_cast<SceneGraph::ConstraintBase*>(mSceneGraphConstraint));
-      ApplyConstraintMessage(GetEventThreadServices(), targetObject, transferOwnership);
+      if(isPreConstraint)
+      {
+        ApplyConstraintMessage(GetEventThreadServices(), targetObject, transferOwnership);
+      }
+      else
+      {
+        ApplyPostConstraintMessage(GetEventThreadServices(), targetObject, transferOwnership);
+      }
       if(resetter)
       {
         AddResetterMessage(GetEventThreadServices().GetUpdateManager(), resetter);
@@ -256,7 +263,7 @@ private:
   /**
    * @copydoc ConstraintBase::ConnectConstraint()
    */
-  void ConnectConstraint() final
+  void ConnectConstraint(bool isPreConstraint) final
   {
     // Should not come here if target object has been destroyed
     DALI_ASSERT_DEBUG(nullptr != mTargetObject);
@@ -295,7 +302,14 @@ private:
       if(mSceneGraphConstraint)
       {
         OwnerPointer<SceneGraph::ConstraintBase> transferOwnership(const_cast<SceneGraph::ConstraintBase*>(mSceneGraphConstraint));
-        ApplyConstraintMessage(GetEventThreadServices(), targetObject, transferOwnership);
+        if(isPreConstraint)
+        {
+          ApplyConstraintMessage(GetEventThreadServices(), targetObject, transferOwnership);
+        }
+        else
+        {
+          ApplyPostConstraintMessage(GetEventThreadServices(), targetObject, transferOwnership);
+        }
         if(resetterRequired)
         {
           OwnerPointer<SceneGraph::PropertyResetterBase> resetter = SceneGraph::ConstraintResetter::New(targetObject, *targetProperty, *mSceneGraphConstraint);
index d6d24e2..9dce203 100644 (file)
@@ -248,6 +248,31 @@ inline void RemoveConstraintMessage(EventThreadServices& eventThreadServices, co
   new(slot) LocalType(&owner, &PropertyOwner::RemoveConstraint, &constraint);
 }
 
+inline void ApplyPostConstraintMessage(EventThreadServices& eventThreadServices, const PropertyOwner& owner, OwnerPointer<ConstraintBase>& constraint)
+{
+  using LocalType = MessageValue1<PropertyOwner, OwnerPointer<ConstraintBase> >;
+
+  // Reserve some memory inside the message queue
+  uint32_t* slot = eventThreadServices.ReserveMessageSlot(sizeof(LocalType));
+
+  // Construct message in the message queue memory; note that delete should not be called on the return value
+  new(slot) LocalType(&owner, &PropertyOwner::ApplyPostConstraint, constraint);
+}
+
+inline void RemovePostConstraintMessage(EventThreadServices& eventThreadServices, const PropertyOwner& owner, const ConstraintBase& constConstraint)
+{
+  // The update-thread can modify this object.
+  ConstraintBase& constraint = const_cast<ConstraintBase&>(constConstraint);
+
+  using LocalType = MessageValue1<PropertyOwner, ConstraintBase*>;
+
+  // Reserve some memory inside the message queue
+  uint32_t* slot = eventThreadServices.ReserveMessageSlot(sizeof(LocalType));
+
+  // Construct message in the message queue memory; note that delete should not be called on the return value
+  new(slot) LocalType(&owner, &PropertyOwner::RemovePostConstraint, &constraint);
+}
+
 inline void AddUniformMapMessage(EventThreadServices& eventThreadServices, const PropertyOwner& owner, UniformPropertyMapping map)
 {
   using LocalType = MessageValue1<PropertyOwner, UniformPropertyMapping>;
index 1111f94..314e335 100644 (file)
@@ -83,6 +83,7 @@ void PropertyOwner::Destroy()
 
   // Remove all constraints when disconnected from scene-graph
   mConstraints.Clear();
+  mPostConstraints.Clear();
 }
 
 void PropertyOwner::ConnectToSceneGraph()
@@ -111,6 +112,7 @@ void PropertyOwner::DisconnectFromSceneGraph(BufferIndex updateBufferIndex)
 
   // Remove all constraints when disconnected from scene-graph
   mConstraints.Clear();
+  mPostConstraints.Clear();
 }
 
 void PropertyOwner::ReserveProperties(int propertyCount)
@@ -149,6 +151,32 @@ void PropertyOwner::RemoveConstraint(ConstraintBase* constraint)
   //it may be that the constraint has already been removed e.g. from disconnection from scene graph, so nothing needs to be done
 }
 
+ConstraintOwnerContainer& PropertyOwner::GetPostConstraints()
+{
+  return mPostConstraints;
+}
+
+void PropertyOwner::ApplyPostConstraint(OwnerPointer<ConstraintBase>& constraint)
+{
+  constraint->OnConnect();
+  mPostConstraints.PushBack(constraint.Release());
+}
+
+void PropertyOwner::RemovePostConstraint(ConstraintBase* constraint)
+{
+  const ConstraintIter constraintEndIter = mPostConstraints.End();
+  for(ConstraintIter iter = mPostConstraints.Begin(); constraintEndIter != iter; ++iter)
+  {
+    if(*iter == constraint)
+    {
+      mPostConstraints.Erase(iter);
+      return; // We're finished
+    }
+  }
+
+  //it may be that the constraint has already been removed e.g. from disconnection from scene graph, so nothing needs to be done
+}
+
 PropertyOwner::PropertyOwner()
 : mUpdated(false),
   mIsConnectedToSceneGraph(false)
index 769dc35..9850e2f 100644 (file)
@@ -192,12 +192,30 @@ public:
   void RemoveConstraint(ConstraintBase* constraint);
 
   /**
+   * Apply a post constraint.
+   * @param[in] constraint The constraint to apply.
+   */
+  void ApplyPostConstraint(OwnerPointer<ConstraintBase>& constraint);
+
+  /**
+   * Begin removal of post constraints.
+   * @param[in] constraint The constraint to remove.
+   */
+  void RemovePostConstraint(ConstraintBase* constraint);
+
+  /**
    * Retrieve the constraints that are currently applied.
    * @return A container of constraints.
    */
   ConstraintOwnerContainer& GetConstraints();
 
   /**
+   * Retrieve the post constraints that are currently applied.
+   * @return A container of post constraints.
+   */
+  ConstraintOwnerContainer& GetPostConstraints();
+
+  /**
    * @copydoc UniformMap::Add
    */
   virtual void AddUniformMapping(const UniformPropertyMapping& map);
@@ -267,6 +285,7 @@ private:
   ObserverContainer mObservers; ///< Container of observer raw-pointers (not owned)
 
   ConstraintOwnerContainer mConstraints; ///< Container of owned constraints
+  ConstraintOwnerContainer mPostConstraints; ///< Container of owned constraints
 };
 
 } // namespace SceneGraph
index 9cdd211..0cfd6dd 100644 (file)
@@ -51,9 +51,9 @@ Debug::Filter* gUpdateFilter = Debug::Filter::New(Debug::Concise, false, "LOG_UP
  * @param propertyOwner to constrain
  * @param updateBufferIndex buffer index to use
  */
-void ConstrainPropertyOwner(PropertyOwner& propertyOwner, BufferIndex updateBufferIndex)
+void ConstrainPropertyOwner(PropertyOwner& propertyOwner, BufferIndex updateBufferIndex, bool isPreConstraint)
 {
-  ConstraintOwnerContainer& constraints = propertyOwner.GetConstraints();
+  ConstraintOwnerContainer& constraints = (isPreConstraint) ? propertyOwner.GetConstraints() : propertyOwner.GetPostConstraints();
 
   const ConstraintIter endIter = constraints.End();
   for(ConstraintIter iter = constraints.Begin(); iter != endIter; ++iter)
@@ -97,14 +97,19 @@ inline void UpdateNodeOpacity(Node& node, NodePropertyFlags nodeDirtyFlags, Buff
 /**
  * This is called recursively for all children of the root Node
  */
-inline NodePropertyFlags UpdateNodes(Node&             node,
-                                     NodePropertyFlags parentFlags,
-                                     BufferIndex       updateBufferIndex,
-                                     RenderQueue&      renderQueue,
-                                     bool              updated)
+inline NodePropertyFlags UpdateNodes(Node&                   node,
+                                     NodePropertyFlags       parentFlags,
+                                     BufferIndex             updateBufferIndex,
+                                     RenderQueue&            renderQueue,
+                                     PropertyOwnerContainer& postPropertyOwners,
+                                     bool                    updated)
 {
   // Apply constraints to the node
   ConstrainPropertyOwner(node, updateBufferIndex);
+  if(!node.GetPostConstraints().Empty())
+  {
+    postPropertyOwners.PushBack(&node);
+  }
 
   // Some dirty flags are inherited from parent
   NodePropertyFlags nodeDirtyFlags = node.GetDirtyFlags() | node.GetInheritedDirtyFlags(parentFlags);
@@ -133,6 +138,7 @@ inline NodePropertyFlags UpdateNodes(Node&             node,
                                         nodeDirtyFlags,
                                         updateBufferIndex,
                                         renderQueue,
+                                        postPropertyOwners,
                                         updated);
   }
 
@@ -142,9 +148,10 @@ inline NodePropertyFlags UpdateNodes(Node&             node,
 /**
  * The root node is treated separately; it cannot inherit values since it has no parent
  */
-NodePropertyFlags UpdateNodeTree(Layer&       rootNode,
-                                 BufferIndex  updateBufferIndex,
-                                 RenderQueue& renderQueue)
+NodePropertyFlags UpdateNodeTree(Layer&                  rootNode,
+                                 BufferIndex             updateBufferIndex,
+                                 RenderQueue&            renderQueue,
+                                 PropertyOwnerContainer& postPropertyOwners)
 {
   DALI_ASSERT_DEBUG(rootNode.IsRoot());
 
@@ -180,6 +187,7 @@ NodePropertyFlags UpdateNodeTree(Layer&       rootNode,
                                         nodeDirtyFlags,
                                         updateBufferIndex,
                                         renderQueue,
+                                        postPropertyOwners,
                                         updated);
   }
 
index eeeb09e..ce0ed89 100644 (file)
@@ -32,12 +32,15 @@ class Layer;
 class PropertyOwner;
 class RenderQueue;
 
+using PropertyOwnerContainer = Dali::Vector<PropertyOwner*>;
+
 /**
  * Constrain the local properties of the PropertyOwner.
  * @param[in] propertyOwner The PropertyOwner to constrain
  * @param[in] updateBufferIndex The current update buffer index.
+ * @param[in] isPreConstraint True if the constraint is performed before transform.
  */
-void ConstrainPropertyOwner(PropertyOwner& propertyOwner, BufferIndex updateBufferIndex);
+void ConstrainPropertyOwner(PropertyOwner& propertyOwner, BufferIndex updateBufferIndex, bool isPreConstraint = true);
 
 /**
  * Update a tree of nodes
@@ -45,11 +48,13 @@ void ConstrainPropertyOwner(PropertyOwner& propertyOwner, BufferIndex updateBuff
  * @param[in] rootNode The root of a tree of nodes.
  * @param[in] updateBufferIndex The current update buffer index.
  * @param[in] renderQueue Used to query messages for the next Render.
+ * @param[out] postPropertyOwner property owners those have post constraint.
  * @return The cumulative (ORed) dirty flags for the updated nodes
  */
-NodePropertyFlags UpdateNodeTree(Layer&       rootNode,
-                                 BufferIndex  updateBufferIndex,
-                                 RenderQueue& renderQueue);
+NodePropertyFlags UpdateNodeTree(Layer&                  rootNode,
+                                 BufferIndex             updateBufferIndex,
+                                 RenderQueue&            renderQueue,
+                                 PropertyOwnerContainer& postPropertyOwners);
 /**
  * This updates all the sub-layer's reusability flags without affecting
  * the root layer.
index 4a2f1bb..a5ecd9a 100644 (file)
@@ -830,16 +830,20 @@ bool UpdateManager::Animate(BufferIndex bufferIndex, float elapsedSeconds)
   return animationActive;
 }
 
-void UpdateManager::ConstrainCustomObjects(BufferIndex bufferIndex)
+void UpdateManager::ConstrainCustomObjects(PropertyOwnerContainer& postPropertyOwners, BufferIndex bufferIndex)
 {
   // Constrain custom objects (in construction order)
   for(auto&& object : mImpl->customObjects)
   {
     ConstrainPropertyOwner(*object, bufferIndex);
+    if(!object->GetPostConstraints().Empty())
+    {
+      postPropertyOwners.PushBack(object);
+    }
   }
 }
 
-void UpdateManager::ConstrainRenderTasks(BufferIndex bufferIndex)
+void UpdateManager::ConstrainRenderTasks(PropertyOwnerContainer& postPropertyOwners, BufferIndex bufferIndex)
 {
   // Constrain render-tasks
   for(auto&& scene : mImpl->scenes)
@@ -850,17 +854,25 @@ void UpdateManager::ConstrainRenderTasks(BufferIndex bufferIndex)
       for(auto&& task : tasks)
       {
         ConstrainPropertyOwner(*task, bufferIndex);
+        if(!task->GetPostConstraints().Empty())
+        {
+          postPropertyOwners.PushBack(task);
+        }
       }
     }
   }
 }
 
-void UpdateManager::ConstrainShaders(BufferIndex bufferIndex)
+void UpdateManager::ConstrainShaders(PropertyOwnerContainer& postPropertyOwners, BufferIndex bufferIndex)
 {
   // constrain shaders... (in construction order)
   for(auto&& shader : mImpl->shaders)
   {
     ConstrainPropertyOwner(*shader, bufferIndex);
+    if(!shader->GetPostConstraints().Empty())
+    {
+      postPropertyOwners.PushBack(shader);
+    }
   }
 }
 
@@ -901,19 +913,23 @@ void UpdateManager::ForwardCompiledShadersToEventThread()
   }
 }
 
-void UpdateManager::UpdateRenderers(BufferIndex bufferIndex)
+void UpdateManager::UpdateRenderers(PropertyOwnerContainer& postPropertyOwners, BufferIndex bufferIndex)
 {
   for(const auto& rendererKey : mImpl->renderers)
   {
     // Apply constraints
     auto renderer = rendererKey.Get();
     ConstrainPropertyOwner(*renderer, bufferIndex);
+    if(!renderer->GetPostConstraints().Empty())
+    {
+      postPropertyOwners.PushBack(renderer);
+    }
 
     mImpl->renderingRequired = renderer->PrepareRender(bufferIndex) || mImpl->renderingRequired;
   }
 }
 
-void UpdateManager::UpdateNodes(BufferIndex bufferIndex)
+void UpdateManager::UpdateNodes(PropertyOwnerContainer& postPropertyOwners, BufferIndex bufferIndex)
 {
   mImpl->nodeDirtyFlags = NodePropertyFlags::NOTHING;
 
@@ -925,7 +941,8 @@ void UpdateManager::UpdateNodes(BufferIndex bufferIndex)
       // And add the renderers to the sorted layers. Start from root, which is also a layer
       mImpl->nodeDirtyFlags |= UpdateNodeTree(*scene->root,
                                               bufferIndex,
-                                              mImpl->renderQueue);
+                                              mImpl->renderQueue,
+                                              postPropertyOwners);
     }
   }
 }
@@ -996,8 +1013,9 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
     // Animate
     bool animationActive = Animate(bufferIndex, elapsedSeconds);
 
+    PropertyOwnerContainer postPropertyOwners;
     // Constraint custom objects
-    ConstrainCustomObjects(bufferIndex);
+    ConstrainCustomObjects(postPropertyOwners, bufferIndex);
 
     // Clear the lists of renderers from the previous update
     for(auto&& scene : mImpl->scenes)
@@ -1021,14 +1039,14 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
     }
 
     // Update node hierarchy, apply constraints,
-    UpdateNodes(bufferIndex);
+    UpdateNodes(postPropertyOwners, bufferIndex);
 
     // Apply constraints to RenderTasks, shaders
-    ConstrainRenderTasks(bufferIndex);
-    ConstrainShaders(bufferIndex);
+    ConstrainRenderTasks(postPropertyOwners, bufferIndex);
+    ConstrainShaders(postPropertyOwners, bufferIndex);
 
     // Update renderers and apply constraints
-    UpdateRenderers(bufferIndex);
+    UpdateRenderers(postPropertyOwners, bufferIndex);
 
     // Update the transformations of all the nodes
     if(mImpl->transformManager.Update())
@@ -1036,6 +1054,12 @@ uint32_t UpdateManager::Update(float    elapsedSeconds,
       mImpl->nodeDirtyFlags |= NodePropertyFlags::TRANSFORM;
     }
 
+    // Constraint applied after transform manager updated. Only required property owner processed.
+    for(auto&& propertyOwner : postPropertyOwners)
+    {
+      ConstrainPropertyOwner(*propertyOwner, bufferIndex, false);
+    }
+
     // Initialise layer renderable reuse
     UpdateLayers(bufferIndex);
 
index 908f827..abf85a8 100644 (file)
@@ -739,21 +739,24 @@ private:
 
   /**
    * Applies constraints to CustomObjects
+   * @param[out] postPropertyOwner property owners those have post constraint.
    * @param[in] bufferIndex to use
    */
-  void ConstrainCustomObjects(BufferIndex bufferIndex);
+  void ConstrainCustomObjects(PropertyOwnerContainer& postPropertyOwners, BufferIndex bufferIndex);
 
   /**
    * Applies constraints to RenderTasks
+   * @param[out] postPropertyOwner property owners those have post constraint.
    * @param[in] bufferIndex to use
    */
-  void ConstrainRenderTasks(BufferIndex bufferIndex);
+  void ConstrainRenderTasks(PropertyOwnerContainer& postPropertyOwners, BufferIndex bufferIndex);
 
   /**
    * Applies constraints to Shaders
+   * @param[out] postPropertyOwner property owners those have post constraint.
    * @param[in] bufferIndex to use
    */
-  void ConstrainShaders(BufferIndex bufferIndex);
+  void ConstrainShaders(PropertyOwnerContainer& postPropertyOwners, BufferIndex bufferIndex);
 
   /**
    * Perform property notification updates
@@ -768,9 +771,10 @@ private:
 
   /**
    * Update node shaders, opacity, geometry etc.
+   * @param[out] postPropertyOwner property owners those have post constraint.
    * @param[in] bufferIndex to use
    */
-  void UpdateNodes(BufferIndex bufferIndex);
+  void UpdateNodes(PropertyOwnerContainer& postPropertyOwners, BufferIndex bufferIndex);
 
   /**
    * initialize layer renderables
@@ -780,9 +784,10 @@ private:
 
   /**
    * Update Renderers
+   * @param[out] postPropertyOwner property owners those have post constraint.
    * @param[in] bufferIndex to use
    */
-  void UpdateRenderers(BufferIndex bufferIndex);
+  void UpdateRenderers(PropertyOwnerContainer& postPropertyOwners, BufferIndex bufferIndex);
 
 private:
   // needs to be direct member so that getter for event buffer can be inlined
index 8fdc606..e8b5ea5 100644 (file)
@@ -70,6 +70,11 @@ void Constraint::Apply()
   GetImplementation(*this).Apply();
 }
 
+void Constraint::ApplyPost()
+{
+  GetImplementation(*this).ApplyPost();
+}
+
 void Constraint::Remove()
 {
   GetImplementation(*this).Remove();
index 8df0446..51a4f72 100644 (file)
@@ -454,16 +454,30 @@ public:
   void AddSource(ConstraintSource source);
 
   /**
-   * @brief Applies this constraint.
+   * @brief Applies this constraint to be computed before transform.
    *
    * @SINCE_1_0.0
    * @pre The constraint must be initialized.
    * @pre The target object must still be alive.
    * @pre The source inputs should not have been destroyed.
+   *
+   * @note This method cannot be called with ApplyPost at the same time.
    */
   void Apply();
 
   /**
+   * @brief Applies this constraint to be computed after transform.
+   *
+   * @SINCE_2_2.23
+   * @pre The constraint must be initialized.
+   * @pre The target object must still be alive.
+   * @pre The source inputs should not have been destroyed.
+   *
+   * @note This method cannot be called with Apply at the same time.
+   */
+  void ApplyPost();
+
+  /**
    * @brief Removes this constraint.
    * @SINCE_1_0.0
    */