#include <dali-test-suite-utils.h>
#include <dali/public-api/dali-core.h>
+#include <mesh-builder.h>
#include <stdlib.h>
#include <iostream>
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
mRemoveAction(Dali::Constraint::DEFAULT_REMOVE_ACTION),
mTag(0),
mApplied(false),
- mSourceDestroyed(false)
+ mSourceDestroyed(false),
+ mIsPreConstraint(true)
{
ObserveObject(object);
}
}
}
-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()
{
mTargetObject->RemoveConstraint(*this);
}
+ mIsPreConstraint = true;
}
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;
}
(nullptr == mSceneGraphConstraint) &&
mTargetObject)
{
- ConnectConstraint();
+ ConnectConstraint(mIsPreConstraint);
}
}
/**
* @copydoc Dali::Constraint::Apply()
*/
- void Apply();
+ void Apply(bool isPreConstraint = true);
+
+ /**
+ * @copydoc Dali::Constraint::ApplyPost()
+ */
+ void ApplyPost();
/**
* @copydoc Dali::Constraint::Remove()
/**
* Connect the constraint
*/
- virtual void ConnectConstraint() = 0;
+ virtual void ConnectConstraint(bool isPreConstraint = true) = 0;
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
/**
* @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);
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);
/**
* @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);
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);
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>;
// Remove all constraints when disconnected from scene-graph
mConstraints.Clear();
+ mPostConstraints.Clear();
}
void PropertyOwner::ConnectToSceneGraph()
// Remove all constraints when disconnected from scene-graph
mConstraints.Clear();
+ mPostConstraints.Clear();
}
void PropertyOwner::ReserveProperties(int propertyCount)
//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)
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);
ObserverContainer mObservers; ///< Container of observer raw-pointers (not owned)
ConstraintOwnerContainer mConstraints; ///< Container of owned constraints
+ ConstraintOwnerContainer mPostConstraints; ///< Container of owned constraints
};
} // namespace SceneGraph
* @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)
/**
* 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);
nodeDirtyFlags,
updateBufferIndex,
renderQueue,
+ postPropertyOwners,
updated);
}
/**
* 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());
nodeDirtyFlags,
updateBufferIndex,
renderQueue,
+ postPropertyOwners,
updated);
}
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
* @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.
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)
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);
+ }
}
}
}
}
-void UpdateManager::UpdateRenderers(BufferIndex bufferIndex)
+void UpdateManager::UpdateRenderers(PropertyOwnerContainer& postPropertyOwners, BufferIndex bufferIndex)
{
for(auto&& renderer : mImpl->renderers)
{
//Apply constraints
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;
// 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);
}
}
}
//Animate
bool animationActive = Animate(bufferIndex, elapsedSeconds);
- //Constraint custom objects
- ConstrainCustomObjects(bufferIndex);
+ PropertyOwnerContainer postPropertyOwners;
+ // Constraint custom objects
+ ConstrainCustomObjects(postPropertyOwners, bufferIndex);
//Clear the lists of renderers from the previous update
for(auto&& scene : mImpl->scenes)
mImpl->frameCallbackProcessor->Update(bufferIndex, elapsedSeconds);
}
- //Update node hierarchy, apply constraints,
- UpdateNodes(bufferIndex);
+ // Update node hierarchy, apply constraints,
+ UpdateNodes(postPropertyOwners, bufferIndex);
- //Apply constraints to RenderTasks, shaders
- ConstrainRenderTasks(bufferIndex);
- ConstrainShaders(bufferIndex);
+ // Apply constraints to RenderTasks, shaders
+ ConstrainRenderTasks(postPropertyOwners, bufferIndex);
+ ConstrainShaders(postPropertyOwners, bufferIndex);
- //Update renderers and apply constraints
- UpdateRenderers(bufferIndex);
+ // Update renderers and apply constraints
+ UpdateRenderers(postPropertyOwners, bufferIndex);
//Update the transformations of all the nodes
if(mImpl->transformManager.Update())
mImpl->nodeDirtyFlags |= NodePropertyFlags::TRANSFORM;
}
- //Initialise layer renderable reuse
+ // 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);
//Process Property Notifications
/**
* 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
/**
* 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
/**
* 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
GetImplementation(*this).Apply();
}
+void Constraint::ApplyPost()
+{
+ GetImplementation(*this).ApplyPost();
+}
+
void Constraint::Remove()
{
GetImplementation(*this).Remove();
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.
+ *
+ * @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
*/