// node is being used in a separate thread; queue a message to set the value & base value
SceneGraph::NodePropertyComponentMessage<Vector4>::Send( GetEventThreadServices(), &GetNode(), &GetNode().mColor, &AnimatableProperty<Vector4>::BakeW, opacity );
+
+ RequestRenderingMessage( GetEventThreadServices().GetUpdateManager() );
}
float Actor::GetCurrentOpacity() const
// node is being used in a separate thread; queue a message to set the value & base value
SceneGraph::NodePropertyMessage<Vector4>::Send( GetEventThreadServices(), &GetNode(), &GetNode().mColor, &AnimatableProperty<Vector4>::Bake, color );
+
+ RequestRenderingMessage( GetEventThreadServices().GetUpdateManager() );
}
void Actor::SetColorRed( float red )
// node is being used in a separate thread; queue a message to set the value & base value
SceneGraph::NodePropertyComponentMessage<Vector4>::Send( GetEventThreadServices(), &GetNode(), &GetNode().mColor, &AnimatableProperty<Vector4>::BakeX, red );
+
+ RequestRenderingMessage( GetEventThreadServices().GetUpdateManager() );
}
void Actor::SetColorGreen( float green )
// node is being used in a separate thread; queue a message to set the value & base value
SceneGraph::NodePropertyComponentMessage<Vector4>::Send( GetEventThreadServices(), &GetNode(), &GetNode().mColor, &AnimatableProperty<Vector4>::BakeY, green );
+
+ RequestRenderingMessage( GetEventThreadServices().GetUpdateManager() );
}
void Actor::SetColorBlue( float blue )
// node is being used in a separate thread; queue a message to set the value & base value
SceneGraph::NodePropertyComponentMessage<Vector4>::Send( GetEventThreadServices(), &GetNode(), &GetNode().mColor, &AnimatableProperty<Vector4>::BakeZ, blue );
+
+ RequestRenderingMessage( GetEventThreadServices().GetUpdateManager() );
}
const Vector4& Actor::GetCurrentColor() const
{
// node is being used in a separate thread; queue a message to set the value & base value
SceneGraph::NodePropertyMessage<bool>::Send( GetEventThreadServices(), &GetNode(), &GetNode().mVisible, &AnimatableProperty<bool>::Bake, visible );
+
+ RequestRenderingMessage( GetEventThreadServices().GetUpdateManager() );
}
mVisible = visible;
/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 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.
mDisconnectAction(disconnectAction),
mState(Stopped),
mProgressReachedSignalRequired( false ),
- mAutoReverseEnabled( false )
+ mAutoReverseEnabled( false ),
+ mIsActive{ false }
{
}
void Animation::UpdateAnimators( BufferIndex bufferIndex, bool bake, bool animationFinished )
{
+ mIsActive[bufferIndex] = false;
+
const Vector2 playRange( mPlayRange * mDurationSeconds );
float elapsedSecondsClamped = Clamp( mElapsedSeconds, playRange.x, playRange.y );
progress = Clamp((elapsedSecondsClamped - intervalDelay) / animatorDuration, 0.0f , 1.0f );
}
animator->Update(bufferIndex, progress, bake);
+
+ if (animatorDuration > 0.0f && (elapsedSecondsClamped - intervalDelay) <= animatorDuration)
+ {
+ mIsActive[bufferIndex] = true;
+ }
}
applied = true;
}
#define DALI_INTERNAL_SCENE_GRAPH_ANIMATION_H
/*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 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.
return mCurrentLoop;
}
+ /**
+ * Query whether the animation is currently active (i.e. at least one of the animators has been updated in either frame)
+ * @return True if the animation is currently active
+ */
+ bool IsActive() const
+ {
+ // As we have double buffering, if animator is updated in either frame, it needs to be rendered.
+ return mIsActive[0] || mIsActive[1];
+ }
+
/**
* @brief Sets the looping mode.
*
bool mProgressReachedSignalRequired; // Flag to indicate the progress marker was hit
bool mAutoReverseEnabled; // Flag to identify that the looping mode is auto reverse.
+ bool mIsActive[2]; // Flag to indicate whether the animation is active in the current frame (which is double buffered)
};
}; //namespace SceneGraph
/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 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.
}
}
-void TransformManager::Update()
+bool TransformManager::Update()
{
+ bool componentsChanged = false;
+
if( mReorder )
{
//If some transform component has change its parent or has been removed since last update
mBoundingSpheres[i] = mWorld[i].GetTranslation();
mBoundingSpheres[i].w = Length( centerToEdgeWorldSpace );
+ componentsChanged = componentsChanged || mComponentDirty[i];
mComponentDirty[i] = false;
}
+
+ return componentsChanged;
}
void TransformManager::SwapComponents( unsigned int i, unsigned int j )
case TRANSFORM_PROPERTY_POSITION:
{
TransformId index( mIds[id] );
- mComponentDirty[ index ] = true;
return mTxComponentAnimatable[ index ].mPosition;
}
case TRANSFORM_PROPERTY_SCALE:
{
TransformId index( mIds[id] );
- mComponentDirty[ index ] = true;
return mTxComponentAnimatable[ index ].mScale;
}
case TRANSFORM_PROPERTY_PARENT_ORIGIN:
{
TransformId index( mIds[id] );
- mComponentDirty[ index ] = true;
return mTxComponentStatic[ index ].mParentOrigin;
}
case TRANSFORM_PROPERTY_ANCHOR_POINT:
{
TransformId index( mIds[id] );
- mComponentDirty[ index ] = true;
return mTxComponentStatic[ index ].mAnchorPoint;
}
case TRANSFORM_PROPERTY_SIZE:
{
TransformId index( mIds[id] );
- mComponentDirty[ index ] = true;
return mSize[ index ];
}
default:
Quaternion& TransformManager::GetQuaternionPropertyValue( TransformId id )
{
TransformId index( mIds[id] );
- mComponentDirty[ index ] = true;
return mTxComponentAnimatable[ index ].mOrientation;
}
#define DALI_INTERNAL_TRANSFORM_MANAGER_H
/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 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.
/**
* Recomputes all world transform matrices
+ * @return true if any component has been changed in this frame, false otherwise
*/
- void Update();
+ bool Update();
/**
* Resets all the animatable properties to its base value
previousUpdateScene( false ),
renderTaskWaiting( false ),
renderersAdded( false ),
- surfaceRectChanged( false )
+ surfaceRectChanged( false ),
+ renderingRequired( false )
{
sceneController = new SceneControllerImpl( renderMessageDispatcher, renderQueue, discardQueue );
bool renderTaskWaiting; ///< A REFRESH_ONCE render task is waiting to be rendered
bool renderersAdded; ///< Flag to keep track when renderers have been added to avoid unnecessary processing
bool surfaceRectChanged; ///< True if the default surface rect is changed
+ bool renderingRequired; ///< True if required to render the current frame
private:
return gestureUpdated;
}
-void UpdateManager::Animate( BufferIndex bufferIndex, float elapsedSeconds )
+bool UpdateManager::Animate( BufferIndex bufferIndex, float elapsedSeconds )
{
+ bool animationActive = false;
+
auto&& iter = mImpl->animations.Begin();
bool animationLooped = false;
bool progressMarkerReached = false;
animation->Update( bufferIndex, elapsedSeconds, looped, finished, progressMarkerReached );
+ animationActive = animationActive || animation->IsActive();
+
if ( progressMarkerReached )
{
mImpl->notificationManager.QueueMessage( Internal::NotifyProgressReachedMessage( mImpl->animationPlaylist, animation ) );
// The application should be notified by NotificationManager, in another thread
mImpl->notificationManager.QueueCompleteNotification( &mImpl->animationPlaylist );
}
+
+ return animationActive;
}
void UpdateManager::ConstrainCustomObjects( BufferIndex bufferIndex )
//Apply constraints
ConstrainPropertyOwner( *renderer, bufferIndex );
- renderer->PrepareRender( bufferIndex );
+ mImpl->renderingRequired = renderer->PrepareRender( bufferIndex ) || mImpl->renderingRequired;
}
}
//Clear nodes/resources which were previously discarded
mImpl->discardQueue.Clear( bufferIndex );
+ bool isAnimationRunning = IsAnimationRunning();
+
//Process Touches & Gestures
const bool gestureUpdated = ProcessGestures( bufferIndex, lastVSyncTimeMilliseconds, nextVSyncTimeMilliseconds );
bool updateScene = // The scene-graph requires an update if..
(mImpl->nodeDirtyFlags & RenderableUpdateFlags) || // ..nodes were dirty in previous frame OR
- IsAnimationRunning() || // ..at least one animation is running OR
+ isAnimationRunning || // ..at least one animation is running OR
mImpl->messageQueue.IsSceneUpdateRequired() || // ..a message that modifies the scene graph node tree is queued OR
mImpl->frameCallbackProcessor || // ..a frame callback processor is existed OR
gestureUpdated; // ..a gesture property was updated
bool keepRendererRendering = false;
+ mImpl->renderingRequired = false;
// Although the scene-graph may not require an update, we still need to synchronize double-buffered
// values if the scene was updated in the previous frame.
if( updateScene || mImpl->previousUpdateScene )
{
//Animate
- Animate( bufferIndex, elapsedSeconds );
+ bool animationActive = Animate( bufferIndex, elapsedSeconds );
//Constraint custom objects
ConstrainCustomObjects( bufferIndex );
UpdateRenderers( bufferIndex );
//Update the transformations of all the nodes
- mImpl->transformManager.Update();
+ if ( mImpl->transformManager.Update() )
+ {
+ mImpl->nodeDirtyFlags |= NodePropertyFlags::TRANSFORM;
+ }
//Process Property Notifications
ProcessPropertyNotifications( bufferIndex );
}
}
-
std::size_t numberOfRenderInstructions = 0;
for ( auto&& scene : mImpl->scenes )
{
scene->scene->GetRenderInstructions().ResetAndReserve( bufferIndex,
static_cast<uint32_t>( scene->taskList->GetTasks().Count() ) );
- keepRendererRendering |= mImpl->renderTaskProcessor.Process( bufferIndex,
- *scene->taskList,
- *scene->root,
- scene->sortedLayerList,
- scene->scene->GetRenderInstructions(),
- renderToFboEnabled,
- isRenderingToFbo );
+ // If there are animations running, only add render instruction if at least one animation is currently active (i.e. not delayed)
+ // or the nodes are dirty
+ if ( !isAnimationRunning || animationActive || mImpl->renderingRequired || (mImpl->nodeDirtyFlags & RenderableUpdateFlags) )
+ {
+ keepRendererRendering |= mImpl->renderTaskProcessor.Process( bufferIndex,
+ *scene->taskList,
+ *scene->root,
+ scene->sortedLayerList,
+ scene->scene->GetRenderInstructions(),
+ renderToFboEnabled,
+ isRenderingToFbo );
+
+ }
numberOfRenderInstructions += scene->scene->GetRenderInstructions().Count( bufferIndex );
}
mImpl->renderingBehavior = renderingBehavior;
}
+void UpdateManager::RequestRendering()
+{
+ mImpl->renderingRequired = true;
+}
+
void UpdateManager::SetLayerDepths( const SortedLayerPointers& layers, const Layer* rootLayer )
{
for ( auto&& scene : mImpl->scenes )
*/
void SetRenderingBehavior( DevelStage::Rendering renderingBehavior );
+ /**
+ * Request to render the current frame
+ * @note This is a temporary workaround (to be removed in the future) to request the rendering of
+ * the current frame if the color or visibility of any actor is updated. It MUST NOT be used
+ * for any other purposes.
+ */
+ void RequestRendering();
+
/**
* Sets the depths of all layers.
* @param layers The layers in depth order.
* Perform animation updates
* @param[in] bufferIndex to use
* @param[in] elapsedSeconds time since last frame
+ * @return true if at least one animations is currently active or false otherwise
*/
- void Animate( BufferIndex bufferIndex, float elapsedSeconds );
+ bool Animate( BufferIndex bufferIndex, float elapsedSeconds );
/**
* Applies constraints to CustomObjects
new (slot) LocalType( &manager, &UpdateManager::SetRenderingBehavior, renderingBehavior );
}
+inline void RequestRenderingMessage( UpdateManager& manager )
+{
+ using LocalType = Message<UpdateManager>;
+
+ // Reserve some memory inside the message queue
+ uint32_t* slot = manager.ReserveMessageSlot( sizeof( LocalType ) );
+
+ // Construct message in the message queue memory; note that delete should not be called on the return value
+ new (slot) LocalType( &manager, &UpdateManager::RequestRendering );
+}
+
/**
* Create a message for setting the depth of a layer
* @param[in] manager The update manager
/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 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.
}
-void Renderer::PrepareRender( BufferIndex updateBufferIndex )
+bool Renderer::PrepareRender( BufferIndex updateBufferIndex )
{
if( mRegenerateUniformMap == UNIFORM_MAP_READY )
{
mRegenerateUniformMap--;
}
+ bool rendererUpdated = mUniformMapChanged[updateBufferIndex] || mResendFlag;
+
if( mResendFlag != 0 )
{
if( mResendFlag & RESEND_GEOMETRY )
mResendFlag = 0;
}
+
+ return rendererUpdated;
}
void Renderer::SetTextures( TextureSet* textureSet )
#define DALI_INTERNAL_SCENE_GRAPH_RENDERER_H
/*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2020 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.
* Prepare the object for rendering.
* This is called by the UpdateManager when an object is due to be rendered in the current frame.
* @param[in] updateBufferIndex The current update buffer index.
+ * @return Whether this renderer has been updated in the current frame
*/
- void PrepareRender( BufferIndex updateBufferIndex );
+ bool PrepareRender( BufferIndex updateBufferIndex );
/**
* Retrieve the Render thread renderer