From: Adeel Kazmi Date: Mon, 17 Jul 2017 18:27:49 +0000 (+0100) Subject: Add a mechanism to specify a callback on every frame X-Git-Tag: dali_1.3.40~1 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-core.git;a=commitdiff_plain;h=d9c4cc847342c76fe95aab860dab4b4f0beba59e Add a mechanism to specify a callback on every frame Change-Id: I71642330a7e5c2fc69b2661648d698a6dad8caf9 --- diff --git a/automated-tests/src/dali/CMakeLists.txt b/automated-tests/src/dali/CMakeLists.txt index 6fdf651..2c8e72e 100644 --- a/automated-tests/src/dali/CMakeLists.txt +++ b/automated-tests/src/dali/CMakeLists.txt @@ -30,6 +30,7 @@ SET(TC_SOURCES utc-Dali-Extents.cpp utc-Dali-FrameBuffer.cpp utc-Dali-FrameBufferImage.cpp + utc-Dali-FrameCallbackInterface.cpp utc-Dali-Geometry.cpp utc-Dali-Gesture.cpp utc-Dali-GestureDetector.cpp diff --git a/automated-tests/src/dali/utc-Dali-FrameCallbackInterface.cpp b/automated-tests/src/dali/utc-Dali-FrameCallbackInterface.cpp new file mode 100644 index 0000000..0a3afb2 --- /dev/null +++ b/automated-tests/src/dali/utc-Dali-FrameCallbackInterface.cpp @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2018 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace Dali; + +void utc_dali_frame_callback_interface_startup(void) +{ + test_return_value = TET_UNDEF; +} + +void utc_dali_frame_callback_interface_cleanup(void) +{ + test_return_value = TET_PASS; +} + +/////////////////////////////////////////////////////////////////////////////// +namespace +{ + +class FrameCallbackBasic : public FrameCallbackInterface +{ +public: + + FrameCallbackBasic() + : mCalled( false ) + { + } + + virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds ) + { + mCalled = true; + } + + bool mCalled; +}; + +} // anon namespace + +class FrameCallbackOneActor : public FrameCallbackBasic +{ +public: + + FrameCallbackOneActor( unsigned int actorId ) + : mActorId( actorId ) + { + } + + virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds ) + { + FrameCallbackBasic::Update( updateProxy, elapsedSeconds ); + updateProxy.GetWorldMatrixAndSize( mActorId, mWorldMatrix, mSize ); + mWorldMatrixGetWorldMatrixCall = updateProxy.GetWorldMatrix( mActorId ); + mSizeGetSizeCall = updateProxy.GetSize( mActorId ); + mPositionGetPositionCall = updateProxy.GetPosition( mActorId ); + updateProxy.GetPositionAndSize( mActorId, mPositionGetPositionAndSizeCall, mSizeGetPositionAndSizeCall ); + mWorldColor = updateProxy.GetWorldColor( mActorId ); + } + + const unsigned int mActorId; + + Matrix mWorldMatrix; + Matrix mWorldMatrixGetWorldMatrixCall; + Vector3 mSize; + Vector3 mSizeGetSizeCall; + Vector3 mPositionGetPositionCall; + Vector3 mPositionGetPositionAndSizeCall; + Vector3 mSizeGetPositionAndSizeCall; + Vector4 mWorldColor; +}; + +class FrameCallbackSetter : public FrameCallbackBasic +{ +public: + + FrameCallbackSetter( + unsigned int actorId, + const Matrix& matrixToSet, + const Vector3& sizeToSet, + const Vector3& positionToSet, + const Vector4& colorToSet ) + : mActorId( actorId ), + mMatrixToSet( matrixToSet ), + mSizeToSet( sizeToSet ), + mPositionToSet( positionToSet ), + mColorToSet( colorToSet ) + { + } + + virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds ) + { + FrameCallbackBasic::Update( updateProxy, elapsedSeconds ); + updateProxy.SetWorldMatrix( mActorId, mMatrixToSet ); + updateProxy.SetSize( mActorId, mSizeToSet ); + updateProxy.GetWorldMatrixAndSize( mActorId, mWorldMatrixAfterSetting, mSizeAfterSetting ); + updateProxy.SetPosition( mActorId, mPositionToSet ); + mPositionAfterSetting = updateProxy.GetPosition( mActorId ); + updateProxy.SetWorldColor( mActorId, mColorToSet ); + mColorAfterSetting = updateProxy.GetWorldColor( mActorId ); + } + + const unsigned int mActorId; + const Matrix& mMatrixToSet; + const Vector3& mSizeToSet; + const Vector3& mPositionToSet; + const Vector4& mColorToSet; + + Matrix mWorldMatrixAfterSetting; + Vector3 mSizeAfterSetting; + Vector3 mPositionAfterSetting; + Vector4 mColorAfterSetting; +}; + +class FrameCallbackMultipleActors : public FrameCallbackBasic +{ +public: + + FrameCallbackMultipleActors() + { + } + + virtual void Update( Dali::UpdateProxy& updateProxy, float elapsedSeconds ) + { + FrameCallbackBasic::Update( updateProxy, elapsedSeconds ); + for( auto&& i : mActorIds ) + { + Matrix matrix( false ); + Vector3 size; + updateProxy.GetWorldMatrixAndSize( i, matrix, size ); + mWorldMatrices[ i ] = matrix; + mSizes[ i ] = size; + } + } + + Vector< unsigned int > mActorIds; + + std::map< unsigned int, Matrix > mWorldMatrices; + std::map< unsigned int, Vector3 > mSizes; +}; + +/////////////////////////////////////////////////////////////////////////////// + +int UtcDaliFrameCallbackCheckInstallationAndRemoval(void) +{ + TestApplication application; + + FrameCallbackBasic frameCallback; + + Stage stage = Stage::GetCurrent(); + DevelStage::AddFrameCallback( stage, frameCallback, stage.GetRootLayer() ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( frameCallback.mCalled, true, TEST_LOCATION ); + + frameCallback.mCalled = false; + + DevelStage::RemoveFrameCallback( stage, frameCallback ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( frameCallback.mCalled, false, TEST_LOCATION ); + + END_TEST; +} + +int UtcDaliFrameCallbackGetters(void) +{ + TestApplication application; + Vector2 actorSize( 200, 300 ); + Vector4 color( 0.5f, 0.6f, 0.7f, 0.8f ); + Vector3 position( 10.0f, 20.0f, 30.0f ); + + Actor actor = Actor::New(); + actor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + actor.SetSize( actorSize ); + actor.SetColor( color ); + actor.SetPosition( position ); + + Stage stage = Stage::GetCurrent(); + stage.Add( actor ); + Vector2 stageSize = stage.GetSize(); + + FrameCallbackOneActor frameCallback( actor.GetId() ); + DevelStage::AddFrameCallback( stage, frameCallback, stage.GetRootLayer() ); + + application.SendNotification(); + application.Render(); + + Vector3 expectedPosition( -stageSize.width * 0.5f + actorSize.width * 0.5f + position.x, + -stageSize.height * 0.5f + actorSize.height * 0.5f + position.y, + 0.0f + position.z ); + + Matrix expectedWorldMatrix( false ); + expectedWorldMatrix.SetIdentity(); + expectedWorldMatrix.SetTranslation( expectedPosition ); + + DALI_TEST_EQUALS( frameCallback.mCalled, true, TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mWorldMatrix, expectedWorldMatrix, TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mWorldMatrixGetWorldMatrixCall, expectedWorldMatrix, TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mSize, Vector3( actorSize.width, actorSize.height, 0.0f ), TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mSizeGetSizeCall, Vector3( actorSize.width, actorSize.height, 0.0f ), TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mPositionGetPositionCall, expectedPosition, TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mPositionGetPositionAndSizeCall, expectedPosition, TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mSizeGetPositionAndSizeCall, Vector3( actorSize.width, actorSize.height, 0.0f ), TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mWorldColor, color, TEST_LOCATION ); + + END_TEST; +} + +int UtcDaliFrameCallbackSetters(void) +{ + TestApplication application; + Vector2 actorSize( 200, 300 ); + + Actor actor = Actor::New(); + actor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + actor.SetSize( actorSize ); + + Stage stage = Stage::GetCurrent(); + stage.Add( actor ); + Vector2 stageSize = stage.GetSize(); + + Matrix matrixToSet( Matrix::IDENTITY ); + matrixToSet.SetTranslation( Vector3( 100.0f, 500.0f, 50.0f ) ); + Vector3 sizeToSet( 1.0f, 2.0f, 3.0f ); + Vector3 positionToSet( 10.0f, 20.0f, 30.0f ); + Vector4 colorToSet( Color::MAGENTA ); + + FrameCallbackSetter frameCallback( actor.GetId(), matrixToSet, sizeToSet, positionToSet, colorToSet ); + DevelStage::AddFrameCallback( stage, frameCallback, stage.GetRootLayer() ); + + application.SendNotification(); + application.Render(); + + Matrix expectedWorldMatrix( false ); + expectedWorldMatrix.SetIdentity(); + expectedWorldMatrix.SetTranslation( Vector3( -stageSize.width * 0.5f + actorSize.width * 0.5f, + -stageSize.height * 0.5f + actorSize.height * 0.5f, + 0.0f ) ); + + DALI_TEST_EQUALS( frameCallback.mCalled, true, TEST_LOCATION ); + DALI_TEST_CHECK( expectedWorldMatrix != matrixToSet ); + DALI_TEST_EQUALS( frameCallback.mWorldMatrixAfterSetting, matrixToSet, TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mSizeAfterSetting, sizeToSet, TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mPositionAfterSetting, positionToSet, TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mColorAfterSetting, colorToSet, TEST_LOCATION ); + + END_TEST; +} + +int UtcDaliFrameCallbackMultipleActors(void) +{ + /** + * Tree: + * root-layer + * / \ + * / \ + * / \ + * / \ + * actorA actorE + * / \ / \ + * / \ / \ + * actorB actorD actorF actorG + * / \ + * actorC actorH + * + * Screen positions: + * ----------------------- + * |actorA|actorD | + * | actorB | + * | actorC | + * | | + * | | + * | | + * | | + * | | + * |actorF actorH | + * |actorE|actorG | + * ----------------------- + */ + + TestApplication application; + Stage stage = Stage::GetCurrent(); + const Vector2 stageSize = stage.GetSize(); + + std::map< char, Vector3 > sizes; + sizes['A'] = Vector3( 50.0f, 50.0f, 0.0f ); + sizes['B'] = Vector3( 100.0f, 100.0f, 0.0f ); + sizes['C'] = Vector3( 150.0f, 150.0f, 0.0f ); + sizes['D'] = Vector3( 200.0f, 200.0f, 0.0f ); + sizes['E'] = Vector3( 250.0f, 250.0f, 0.0f ); + sizes['F'] = Vector3( 300.0f, 300.0f, 0.0f ); + sizes['G'] = Vector3( 350.0f, 350.0f, 0.0f ); + sizes['H'] = Vector3( 400.0f, 350.0f, 0.0f ); + + std::map< char, Matrix > matrices; + for( char i = 'A'; i <= 'H'; ++i ) + { + matrices[i] = Matrix::IDENTITY; + } + + matrices['A'].SetTranslation( Vector3( -stageSize.width * 0.5f + sizes['A'].width * 0.5f, + -stageSize.height * 0.5f + sizes['A'].height * 0.5f, + 0.0f ) ); + matrices['B'].SetTranslation( Vector3( matrices['A'].GetTranslation3() + sizes['A'] * 0.5f + sizes['B'] * 0.5f ) ); + matrices['C'].SetTranslation( Vector3( matrices['B'].GetTranslation3().x, + matrices['B'].GetTranslation3().y + sizes['B'].height * 0.5f + sizes['C'].height * 0.5f, + 0.0f ) ); + matrices['D'].SetTranslation( Vector3( matrices['A'].GetTranslation3().x + sizes['A'].width * 0.5f + sizes['D'].width * 0.5f, + matrices['A'].GetTranslation3().y, + 0.0f ) ); + matrices['E'].SetTranslation( Vector3( -stageSize.width * 0.5f + sizes['E'].width * 0.5f, + stageSize.height * 0.5f - sizes['E'].height * 0.5f, + 0.0f ) ); + matrices['F'].SetTranslation( Vector3( matrices['E'].GetTranslation3().x, + matrices['E'].GetTranslation3().y - sizes['E'].height * 0.5f - sizes['F'].height * 0.5f, + 0.0f ) ); + matrices['G'].SetTranslation( Vector3( matrices['E'].GetTranslation3().x + sizes['E'].width * 0.5f + sizes['G'].width * 0.5f, + matrices['E'].GetTranslation3().y, + 0.0f ) ); + matrices['H'].SetTranslation( Vector3( matrices['G'].GetTranslation3().x + sizes['G'].width * 0.5f + sizes['H'].width * 0.5f, + matrices['G'].GetTranslation3().y - sizes['G'].height * 0.5f - sizes['H'].height * 0.5f, + 0.0f ) ); + + Actor actorA = Actor::New(); + actorA.SetParentOrigin( ParentOrigin::TOP_LEFT ); + actorA.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + actorA.SetSize( sizes['A'] ); + stage.Add( actorA ); + + Actor actorB = Actor::New(); + actorB.SetParentOrigin( ParentOrigin::BOTTOM_RIGHT ); + actorB.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + actorB.SetSize( sizes['B'] ); + actorA.Add( actorB ); + + Actor actorC = Actor::New(); + actorC.SetParentOrigin( ParentOrigin::BOTTOM_CENTER ); + actorC.SetAnchorPoint( AnchorPoint::TOP_CENTER ); + actorC.SetSize( sizes['C'] ); + actorB.Add( actorC ); + + Actor actorD = Actor::New(); + actorD.SetParentOrigin( ParentOrigin::CENTER_RIGHT ); + actorD.SetAnchorPoint( AnchorPoint::CENTER_LEFT ); + actorD.SetSize( sizes['D'] ); + actorA.Add( actorD ); + + Actor actorE = Actor::New(); + actorE.SetParentOrigin( ParentOrigin::BOTTOM_LEFT ); + actorE.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT ); + actorE.SetSize( sizes['E'] ); + stage.Add( actorE ); + + Actor actorF = Actor::New(); + actorF.SetParentOrigin( ParentOrigin::TOP_CENTER ); + actorF.SetAnchorPoint( AnchorPoint::BOTTOM_CENTER ); + actorF.SetSize( sizes['F'] ); + actorE.Add( actorF ); + + Actor actorG = Actor::New(); + actorG.SetParentOrigin( ParentOrigin::CENTER_RIGHT ); + actorG.SetAnchorPoint( AnchorPoint::CENTER_LEFT ); + actorG.SetSize( sizes['G'] ); + actorE.Add( actorG ); + + Actor actorH = Actor::New(); + actorH.SetParentOrigin( ParentOrigin::TOP_RIGHT ); + actorH.SetAnchorPoint( AnchorPoint::BOTTOM_LEFT ); + actorH.SetSize( sizes['H'] ); + actorG.Add( actorH ); + + std::map< char, unsigned int > actorIds; + actorIds['A'] = actorA.GetId(); + actorIds['B'] = actorB.GetId(); + actorIds['C'] = actorC.GetId(); + actorIds['D'] = actorD.GetId(); + actorIds['E'] = actorE.GetId(); + actorIds['F'] = actorF.GetId(); + actorIds['G'] = actorG.GetId(); + actorIds['H'] = actorH.GetId(); + + FrameCallbackMultipleActors frameCallback; + for( auto&& i : actorIds ) + { + frameCallback.mActorIds.PushBack( i.second ); + } + + DevelStage::AddFrameCallback( stage, frameCallback, stage.GetRootLayer() ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( frameCallback.mCalled, true, TEST_LOCATION ); + + for( char i = 'A'; i <= 'H'; ++i ) + { + DALI_TEST_EQUALS( frameCallback.mWorldMatrices[ actorIds[ i ] ], matrices[ i ], TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mSizes[ actorIds[ i ] ], sizes[ i ], TEST_LOCATION ); + } + + // Render again to make sure it still gets called and gives the correct values (in case any optimisations break this) + frameCallback.mCalled = false; + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( frameCallback.mCalled, true, TEST_LOCATION ); + + for( char i = 'A'; i <= 'H'; ++i ) + { + DALI_TEST_EQUALS( frameCallback.mWorldMatrices[ actorIds[ i ] ], matrices[ i ], TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mSizes[ actorIds[ i ] ], sizes[ i ], TEST_LOCATION ); + } + + END_TEST; +} + +int UtcDaliFrameCallbackCheckActorNotAdded(void) +{ + TestApplication application; + + Actor actor = Actor::New(); + actor.SetParentOrigin( ParentOrigin::TOP_LEFT ); + actor.SetAnchorPoint( AnchorPoint::TOP_LEFT ); + actor.SetSize( 200, 300 ); + + Stage stage = Stage::GetCurrent(); + FrameCallbackOneActor frameCallback( actor.GetId() ); + DevelStage::AddFrameCallback( stage, frameCallback, stage.GetRootLayer() ); + + application.SendNotification(); + application.Render(); + + DALI_TEST_EQUALS( frameCallback.mCalled, true, TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mWorldMatrix, Matrix(true) /* Unchanged Matrix */, TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mWorldMatrixGetWorldMatrixCall, Matrix::IDENTITY, TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mSize, Vector3::ZERO, TEST_LOCATION ); + DALI_TEST_EQUALS( frameCallback.mSizeGetSizeCall, Vector3::ZERO, TEST_LOCATION ); + + END_TEST; +} diff --git a/build/tizen/dali-core/Makefile.am b/build/tizen/dali-core/Makefile.am index 5ab447e..c9f9f07 100644 --- a/build/tizen/dali-core/Makefile.am +++ b/build/tizen/dali-core/Makefile.am @@ -1,4 +1,4 @@ -# Copyright (c) 2016 Samsung Electronics Co., Ltd. +# Copyright (c) 2018 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. @@ -91,6 +91,7 @@ develapirenderingdir = $(develapidir)/rendering develapiscriptingdir = $(develapidir)/scripting develapisignalsdir = $(develapidir)/signals develapithreadingdir = $(develapidir)/threading +develapiupdatedir = $(develapidir)/update develapi_HEADERS = $(devel_api_header_files) develapiactors_HEADERS = $(devel_api_core_actors_header_files) @@ -104,6 +105,7 @@ develapirendering_HEADERS = $(devel_api_core_rendering_header_files) develapiscripting_HEADERS = $(devel_api_core_scripting_header_files) develapisignals_HEADERS = $(devel_api_core_signals_header_files) develapithreading_HEADERS = $(devel_api_core_threading_header_files) +develapiupdate_HEADERS = $(devel_api_core_update_header_files) #public api publicapidir = $(devincludepath)/dali/public-api diff --git a/dali/devel-api/common/stage-devel.cpp b/dali/devel-api/common/stage-devel.cpp index e4c23ad..0046d40 100644 --- a/dali/devel-api/common/stage-devel.cpp +++ b/dali/devel-api/common/stage-devel.cpp @@ -40,6 +40,16 @@ Rendering GetRenderingBehavior( Dali::Stage stage ) return GetImplementation( stage ).GetRenderingBehavior(); } +void AddFrameCallback( Dali::Stage stage, FrameCallbackInterface& frameCallback, Actor rootActor ) +{ + GetImplementation( stage ).AddFrameCallback( frameCallback, GetImplementation( rootActor ) ); +} + +void RemoveFrameCallback( Dali::Stage stage, FrameCallbackInterface& frameCallback ) +{ + GetImplementation( stage ).RemoveFrameCallback( frameCallback ); +} + } // namespace DevelStage } // namespace Dali diff --git a/dali/devel-api/common/stage-devel.h b/dali/devel-api/common/stage-devel.h index a0e19f4..3735f04 100644 --- a/dali/devel-api/common/stage-devel.h +++ b/dali/devel-api/common/stage-devel.h @@ -24,6 +24,8 @@ namespace Dali { +class FrameCallbackInterface; + namespace DevelStage { @@ -65,6 +67,27 @@ DALI_CORE_API void SetRenderingBehavior( Dali::Stage stage, Rendering renderingB */ DALI_CORE_API Rendering GetRenderingBehavior( Dali::Stage stage ); +/* + * @brief The FrameCallbackInterface implementation added gets called on every frame from the update-thread. + * + * @param[in] stage The stage to set the FrameCallbackInterface implementation on + * @param[in] frameCallback An implementation of the FrameCallbackInterface + * @param[in] rootActor The root-actor in the scene that the callback applies to + * + * @note Only the rootActor and it's children will be parsed by the UpdateProxy. + * @note If the rootActor is destroyed, then the callback is automatically removed + * @see FrameCallbackInterface + */ +DALI_IMPORT_API void AddFrameCallback( Dali::Stage stage, FrameCallbackInterface& frameCallback, Actor rootActor ); + +/** + * @brief Removes the specified FrameCallbackInterface implementation from being called on every frame. + * + * @param[in] stage The stage to clear the FrameCallbackInterface on + * @param[in] frameCallback The FrameCallbackInterface implementation to remove + */ +DALI_IMPORT_API void RemoveFrameCallback( Dali::Stage stage, FrameCallbackInterface& frameCallback ); + } // namespace DevelStage } // namespace Dali diff --git a/dali/devel-api/file.list b/dali/devel-api/file.list index d8c9ef3..0487c33 100644 --- a/dali/devel-api/file.list +++ b/dali/devel-api/file.list @@ -18,7 +18,8 @@ devel_api_src_files = \ $(devel_api_src_dir)/signals/signal-delegate.cpp \ $(devel_api_src_dir)/threading/conditional-wait.cpp \ $(devel_api_src_dir)/threading/mutex.cpp \ - $(devel_api_src_dir)/threading/thread.cpp + $(devel_api_src_dir)/threading/thread.cpp \ + $(devel_api_src_dir)/update/update-proxy.cpp # Add devel header files here DALi internal developer files used by Adaptor & Toolkit @@ -71,3 +72,7 @@ devel_api_core_threading_header_files = \ $(devel_api_src_dir)/threading/conditional-wait.h \ $(devel_api_src_dir)/threading/mutex.h \ $(devel_api_src_dir)/threading/thread.h + +devel_api_core_update_header_files = \ + $(devel_api_src_dir)/update/frame-callback-interface.h \ + $(devel_api_src_dir)/update/update-proxy.h diff --git a/dali/devel-api/update/frame-callback-interface.h b/dali/devel-api/update/frame-callback-interface.h new file mode 100644 index 0000000..d92c7be --- /dev/null +++ b/dali/devel-api/update/frame-callback-interface.h @@ -0,0 +1,72 @@ +#ifndef DALI_FRAME_CALLBACK_INTERFACE_H +#define DALI_FRAME_CALLBACK_INTERFACE_H + +/* + * Copyright (c) 2018 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +class UpdateProxy; + +/** + * @brief This interface should be implemented if a callback is required on every frame. + * + * The Update() method is called from the update-thread after the scene has been updated, and is ready to render. + * + * As this method is called from the update-thread, no event thread APIs (e.g. Actor::Get...) can be called. + * This will invariably lead to crashes. + * + * Instead, setting and retrieving the values should be done by using the UpdateProxy class returned as a parameter to + * the Update() method. + * + * Actors can be identified using Actor IDs which can be retrieved using Actor::GetId() in the event-thread. + * However, calling Actor::GetId() will lead to problems if it is called from the update-thread. + * Instead, the Actor IDs should be stored by the implementation of this class or passed via a thread-safe manner from + * the event-thread. + */ +class DALI_CORE_API FrameCallbackInterface +{ +public: + + /** + * @brief Called from the update-thread after the scene has been updated, and is ready to render. + * @param[in] updateProxy Use this to get/set required values for the Actor. + * @param[in] elapsedSeconds Time elapsed time since the last frame (in seconds) + * @see FrameCallbackInterface + */ + virtual void Update( UpdateProxy& updateProxy, float elapsedSeconds ) = 0; + +protected: + + /** + * @brief Protected constructor. + */ + FrameCallbackInterface() {} + + /** + * @brief Protected virtual destructor. + */ + virtual ~FrameCallbackInterface() {} +}; + +} // namespace Dali + +#endif // DALI_FRAME_CALLBACK_INTERFACE_H diff --git a/dali/devel-api/update/update-proxy.cpp b/dali/devel-api/update/update-proxy.cpp new file mode 100644 index 0000000..67511b4 --- /dev/null +++ b/dali/devel-api/update/update-proxy.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// INTERNAL INCLUDES +#include + +namespace Dali +{ + +Vector3 UpdateProxy::GetPosition( unsigned int id ) const +{ + return mImpl.GetPosition( id ); +} + +void UpdateProxy::SetPosition( unsigned int id, const Vector3& position ) +{ + mImpl.SetPosition( id, position ); +} + +const Vector3& UpdateProxy::GetSize( unsigned int id ) const +{ + return mImpl.GetSize( id ); +} + +void UpdateProxy::SetSize( unsigned int id, const Vector3& size ) +{ + mImpl.SetSize( id, size ); +} + +void UpdateProxy::GetPositionAndSize( unsigned int id, Vector3& position, Vector3& size ) const +{ + mImpl.GetPositionAndSize( id, position, size ); +} + +Vector4 UpdateProxy::GetWorldColor( unsigned int id ) const +{ + return mImpl.GetWorldColor( id ); +} + +void UpdateProxy::SetWorldColor( unsigned int id, const Vector4& color ) const +{ + mImpl.SetWorldColor( id, color ); +} + +void UpdateProxy::GetWorldMatrixAndSize( unsigned int id, Matrix& worldMatrix, Vector3& size ) const +{ + mImpl.GetWorldMatrixAndSize( id, worldMatrix, size ); +} + +const Matrix& UpdateProxy::GetWorldMatrix( unsigned int id ) const +{ + return mImpl.GetWorldMatrix( id ); +} + +void UpdateProxy::SetWorldMatrix( unsigned int id, const Matrix& worldMatrix ) +{ + mImpl.SetWorldMatrix( id, worldMatrix ); +} + +UpdateProxy::UpdateProxy( Internal::UpdateProxy& impl ) +: mImpl( impl ) +{ +} + +UpdateProxy::~UpdateProxy() +{ +} + +} // namespace Dali diff --git a/dali/devel-api/update/update-proxy.h b/dali/devel-api/update/update-proxy.h new file mode 100644 index 0000000..2ea1194 --- /dev/null +++ b/dali/devel-api/update/update-proxy.h @@ -0,0 +1,159 @@ +#ifndef DALI_UPDATE_PROXY_H +#define DALI_UPDATE_PROXY_H + +/* + * Copyright (c) 2018 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Internal DALI_INTERNAL +{ +class UpdateProxy; +} + +/** + * @brief This class is used to access data of the actors from the update-thread. + * + * The Actor API _CANNOT_ be called directly from the update-thread. + * This class can be used as a proxy to that required Actor data. + * An actor's data can be accessed using the Actor's Unique ID. + * The unique ID should be passed to the callback using this class in a thread-safe manner + * (as you cannot call Actor::GetId from the update-thread). + */ +class DALI_CORE_API UpdateProxy +{ +public: + + /** + * @brief Given the Actor ID, this retrieves that Actor's position. + * @param[in] id The Actor ID + * @return If valid Actor ID, then the Actor's position is returned. + */ + Vector3 GetPosition( unsigned int id ) const; + + /** + * @brief Allows setting an Actor's position from the Frame callback function. + * @param[in] id The Actor ID + * @param[in] position The position to set. + * @note This will get reset to the internally calculated value in the next frame, so will have to be set again. + */ + void SetPosition( unsigned int id, const Vector3& position ); + + /** + * @brief Given the Actor ID, this retrieves that Actor's size. + * @param[in] id The Actor ID + * @return If valid Actor ID, then Actor's size is returned, otherwise Vector3::ZERO. + */ + const Vector3& GetSize( unsigned int id ) const; + + /** + * @brief Allows setting an Actor's size from the Frame callback function. + * @param[in] id The Actor ID + * @param[in] size The size to set. + * @note This will get reset to the internally calculated value in the next frame, so will have to be set again. + */ + void SetSize( unsigned int id, const Vector3& size ); + + /** + * @brief Given the Actor ID, this retrieves that Actor's position and size. + * @param[in] id The Actor ID + * @param[out] position If valid Actor ID, then Actor's position is set + * @param[out] size If valid Actor ID, then Actor's size is set + */ + void GetPositionAndSize( unsigned int id, Vector3& position, Vector3& size ) const; + + /** + * @brief Given the Actor ID, this retrieves that Actor's color. + * @param[in] id The Actor ID + * @return If valid Actor ID, then Actor's color is returned, otherwise Vector4::ZERO. + */ + Vector4 GetWorldColor( unsigned int id ) const; + + /** + * @brief Allows setting an Actor's color from the Frame callback function. + * @param[in] id The Actor ID + * @param[in] color The color to set + * @note This will get reset to the internally calculated value in the next frame, so will have to be set again. + */ + void SetWorldColor( unsigned int id, const Vector4& color ) const; + + /** + * @brief Given the Actor ID, this retrieves that Actor's world-matrix and size. + * @param[in] id The Actor ID + * @param[out] worldMatrix If valid Actor ID, then Actor's world matrix is set + * @param[out] size If valid Actor ID, then Actor's size is set + */ + void GetWorldMatrixAndSize( unsigned int id, Matrix& worldMatrix, Vector3& size ) const; + + /** + * @brief Given the Actor ID, this retrieves that Actor's world-matrix. + * @param[in] id The Actor ID + * @return If valid Actor ID, then Actor's world matrix is returned, otherwise Matrix::IDENTITY. + */ + const Matrix& GetWorldMatrix( unsigned int id ) const; + + /** + * @brief Allows the setting an Actor's world-matrix from the Frame callback function. + * @param[in] id The Actor ID + * @param[in] worldMatrix The world matrix to set. + * @note This will get reset to the internally calculated value in the next frame, so will have to be set again. + * @note This will only set the world matrix for that particular actor. + * The world matrices of the children will not change and will have to be manually changed in the callback + * as well (if required). + */ + void SetWorldMatrix( unsigned int id, const Matrix& worldMatrix ); + +public: // Not intended for application developers + + /// @cond internal + + /** + * @brief Constructor. + * @param[in] impl A reference to the internal object. + */ + DALI_INTERNAL UpdateProxy( Internal::UpdateProxy& impl ); + + /** + * @brief Destructor. + */ + DALI_INTERNAL ~UpdateProxy(); + + // Not copyable or movable + + UpdateProxy( const UpdateProxy& ) = delete; ///< Deleted copy constructor + UpdateProxy( UpdateProxy&& ) = delete; ///< Deleted move constructor + UpdateProxy& operator=( const UpdateProxy& ) = delete; ///< Deleted copy assignment operator + UpdateProxy& operator=( UpdateProxy&& ) = delete; ///< Deleted move assignment operator + + /// @endcond + +private: + + /// @cond internal + Internal::UpdateProxy& mImpl; + /// @endcond +}; + +} // namespace Dali + +#endif // DALI_UPDATE_PROXY_H diff --git a/dali/internal/event/actors/actor-impl.cpp b/dali/internal/event/actors/actor-impl.cpp index 147fcf8..bbf91e8 100644 --- a/dali/internal/event/actors/actor-impl.cpp +++ b/dali/internal/event/actors/actor-impl.cpp @@ -3893,7 +3893,7 @@ void Actor::SetParent( Actor* parent ) SceneGraph::Node* Actor::CreateNode() const { - return Node::New(); + return Node::New( mId ); } bool Actor::DoAction( BaseObject* object, const std::string& actionName, const Property::Map& /* attributes */ ) diff --git a/dali/internal/event/actors/actor-impl.h b/dali/internal/event/actors/actor-impl.h index 69c14bc..9cd762b 100644 --- a/dali/internal/event/actors/actor-impl.h +++ b/dali/internal/event/actors/actor-impl.h @@ -1719,6 +1719,15 @@ public: virtual int GetPropertyComponentIndex( Property::Index index ) const; /** + * Retrieve the actor's node. + * @return The node used by this actor + */ + const SceneGraph::Node& GetNode() const + { + return *mNode; + } + + /** * @copydoc Dali::DevelActor::Raise() */ void Raise(); diff --git a/dali/internal/event/actors/layer-impl.cpp b/dali/internal/event/actors/layer-impl.cpp index 3e1236b..5e51230 100644 --- a/dali/internal/event/actors/layer-impl.cpp +++ b/dali/internal/event/actors/layer-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Samsung Electronics Co., Ltd. + * Copyright (c) 2018 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. @@ -314,7 +314,7 @@ bool Layer::IsHoverConsumed() const SceneGraph::Node* Layer::CreateNode() const { - return SceneGraph::Layer::New(); + return SceneGraph::Layer::New( mId ); } void Layer::OnStageConnectionInternal() diff --git a/dali/internal/event/common/stage-impl.cpp b/dali/internal/event/common/stage-impl.cpp index 0ef555b..bc4ed02 100644 --- a/dali/internal/event/common/stage-impl.cpp +++ b/dali/internal/event/common/stage-impl.cpp @@ -656,6 +656,16 @@ Dali::DevelStage::KeyEventGeneratedSignalType& Stage::KeyEventGeneratedSignal() return mKeyEventGeneratedSignal; } +void Stage::AddFrameCallback( FrameCallbackInterface& frameCallback, Actor& rootActor ) +{ + AddFrameCallbackMessage( mUpdateManager, frameCallback, rootActor.GetNode() ); +} + +void Stage::RemoveFrameCallback( FrameCallbackInterface& frameCallback ) +{ + RemoveFrameCallbackMessage( mUpdateManager, frameCallback ); +} + Dali::Stage::EventProcessingFinishedSignalType& Stage::EventProcessingFinishedSignal() { return mEventProcessingFinishedSignal; diff --git a/dali/internal/event/common/stage-impl.h b/dali/internal/event/common/stage-impl.h index 0f036e3..f64343c 100644 --- a/dali/internal/event/common/stage-impl.h +++ b/dali/internal/event/common/stage-impl.h @@ -399,6 +399,16 @@ public: Dali::DevelStage::KeyEventGeneratedSignalType& KeyEventGeneratedSignal(); /** + * @copydoc Dali::DevelStage::AddFrameCallback() + */ + void AddFrameCallback( FrameCallbackInterface& frameCallback, Actor& rootActor ); + + /** + * @copydoc Dali::DevelStage::RemoveFrameCallback() + */ + void RemoveFrameCallback( FrameCallbackInterface& frameCallback ); + + /** * Connects a callback function with the object's signals. * @param[in] object The object providing the signal. * @param[in] tracker Used to disconnect the signal. diff --git a/dali/internal/file.list b/dali/internal/file.list index bae7b47..72e8e0f 100644 --- a/dali/internal/file.list +++ b/dali/internal/file.list @@ -127,12 +127,14 @@ internal_src_files = \ $(internal_src_dir)/update/gestures/pan-gesture-profiling.cpp \ $(internal_src_dir)/update/gestures/scene-graph-pan-gesture.cpp \ $(internal_src_dir)/update/queue/update-message-queue.cpp \ + $(internal_src_dir)/update/manager/frame-callback-processor.cpp \ $(internal_src_dir)/update/manager/render-instruction-processor.cpp \ $(internal_src_dir)/update/manager/render-task-processor.cpp \ $(internal_src_dir)/update/manager/transform-manager.cpp \ $(internal_src_dir)/update/manager/update-algorithms.cpp \ $(internal_src_dir)/update/manager/update-manager.cpp \ $(internal_src_dir)/update/manager/update-manager-debug.cpp \ + $(internal_src_dir)/update/manager/update-proxy-impl.cpp \ $(internal_src_dir)/update/render-tasks/scene-graph-camera.cpp \ $(internal_src_dir)/update/nodes/node.cpp \ $(internal_src_dir)/update/nodes/node-messages.cpp \ diff --git a/dali/internal/update/manager/frame-callback-processor.cpp b/dali/internal/update/manager/frame-callback-processor.cpp new file mode 100644 index 0000000..87f3b89 --- /dev/null +++ b/dali/internal/update/manager/frame-callback-processor.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +namespace Internal +{ + +namespace SceneGraph +{ + +namespace +{ + +template< typename FrameCallbackInfoT > +class MatchRootNode +{ +public: + + MatchRootNode( PropertyOwner& rootNode ) + : mRootNode( rootNode ) + { + } + + bool operator() ( const FrameCallbackInfoT& info ) + { + return &info.updateProxyImpl->GetRootNode() == &mRootNode; + } + +private: + + PropertyOwner& mRootNode; +}; + +} // unnamed namespace + +FrameCallbackProcessor::FrameCallbackProcessor( TransformManager& transformManager, Node& rootNode ) +: mFrameCallbacks(), + mTransformManager( transformManager ), + mRootNode( rootNode ) +{ +} + +FrameCallbackProcessor::~FrameCallbackProcessor() +{ +} + +void FrameCallbackProcessor::AddFrameCallback( FrameCallbackInterface* frameCallback, const Node* rootNode ) +{ + Node& node = const_cast< Node& >( *rootNode ); // Was sent as const from event thread, we need to be able to use non-const version here. + + FrameCallbackInfo info; + info.frameCallback = frameCallback; + info.updateProxyImpl = new UpdateProxy( mTransformManager, node ); + + // We want to be notified when the node is destroyed (if we're not observing it already) + auto iter = std::find_if( mFrameCallbacks.begin(), mFrameCallbacks.end(), MatchRootNode< FrameCallbackInfo >( node ) ); + if( iter == mFrameCallbacks.end() ) + { + node.AddObserver( *this ); + } + + mFrameCallbacks.push_back( info ); +} + +void FrameCallbackProcessor::RemoveFrameCallback( FrameCallbackInterface* frameCallback ) +{ + auto iter = std::remove_if( mFrameCallbacks.begin(), mFrameCallbacks.end(), + [ this, frameCallback ] + ( const FrameCallbackInfo& info ) + { + info.updateProxyImpl->GetRootNode().RemoveObserver( *this ); + delete info.updateProxyImpl; + return info.frameCallback == frameCallback; + } ); + mFrameCallbacks.erase( iter, mFrameCallbacks.end() ); +} + +void FrameCallbackProcessor::Update( BufferIndex bufferIndex, float elapsedSeconds ) +{ + for( auto&& iter : mFrameCallbacks ) + { + UpdateProxy& updateProxyImpl = *iter.updateProxyImpl; + updateProxyImpl.SetCurrentBufferIndex( bufferIndex ); + Dali::UpdateProxy updateProxy( updateProxyImpl ); + iter.frameCallback->Update( updateProxy, elapsedSeconds ); + } +} + +void FrameCallbackProcessor::PropertyOwnerDestroyed( PropertyOwner& owner ) +{ + auto iter = std::remove_if( mFrameCallbacks.begin(), mFrameCallbacks.end(), MatchRootNode< FrameCallbackInfo >( owner ) ); + mFrameCallbacks.erase( iter, mFrameCallbacks.end() ); +} + +} // namespace SceneGraph + +} // namespace Internal + +} // namespace Dali diff --git a/dali/internal/update/manager/frame-callback-processor.h b/dali/internal/update/manager/frame-callback-processor.h new file mode 100644 index 0000000..90d9c7d --- /dev/null +++ b/dali/internal/update/manager/frame-callback-processor.h @@ -0,0 +1,127 @@ +#ifndef DALI_INTERNAL_SCENE_GRAPH_FRAME_CALLBACK_PROCESSOR_H +#define DALI_INTERNAL_SCENE_GRAPH_FRAME_CALLBACK_PROCESSOR_H + +/* + * Copyright (c) 2018 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// INTERNAL INCLUDES +#include +#include +#include + +namespace Dali +{ + +class FrameCallbackInterface; + +namespace Internal +{ + +class UpdateProxy; + +namespace SceneGraph +{ + +class Node; +class TransformManager; + +/** + * This class processes all the registered frame-callbacks. + */ +class FrameCallbackProcessor : public PropertyOwner::Observer +{ +public: + + /** + * Construct a new FrameCallbackProcessor. + */ + FrameCallbackProcessor( TransformManager& transformManager, Node& rootNode ); + + /** + * Non-virtual Destructor. + */ + ~FrameCallbackProcessor(); + + // Movable but not copyable + + FrameCallbackProcessor( const FrameCallbackProcessor& ) = delete; ///< Deleted copy constructor. + FrameCallbackProcessor( FrameCallbackProcessor&& ) = default; ///< Default move constructor. + FrameCallbackProcessor& operator=( const FrameCallbackProcessor& ) = delete; ///< Deleted copy assignment operator. + FrameCallbackProcessor& operator=( FrameCallbackProcessor&& ) = default; ///< Default move assignment operator. + + /** + * Adds an implementation of the FrameCallbackInterface. + * @param[in] frameCallback A pointer to the implementation of the FrameCallbackInterface + * @param[in] rootNode A pointer to the root node to apply the FrameCallback to + */ + void AddFrameCallback( FrameCallbackInterface* frameCallback, const Node* rootNode ); + + /** + * Removes the specified implementation of FrameCallbackInterface. + * @param[in] frameCallback A pointer to the implementation of the FrameCallbackInterface to remove. + */ + void RemoveFrameCallback( FrameCallbackInterface* frameCallback ); + + /** + * Called on Update by the UpdateManager. + * @param[in] bufferIndex The bufferIndex to use + * @param[in] elapsedSeconds Time elapsed time since the last frame (in seconds) + */ + void Update( BufferIndex bufferIndex, float elapsedSeconds ); + +private: + + // From PropertyOwner::Observer + + /** + * @copydoc PropertyOwner::Observer::PropertyOwnerConnected() + */ + virtual void PropertyOwnerConnected( PropertyOwner& owner ) { /* Nothing to do */ } + + /** + * @copydoc PropertyOwner::Observer::PropertyOwnerDisconnected() + */ + virtual void PropertyOwnerDisconnected( BufferIndex updateBufferIndex, PropertyOwner& owner ) { /* Nothing to do */ } + + /** + * @copydoc PropertyOwner::Observer::PropertyOwnerDisconnected() + * + * Will use this to disconnect the frame-callback if the accompanying node is destroyed + */ + virtual void PropertyOwnerDestroyed( PropertyOwner& owner ); + +private: + + struct FrameCallbackInfo + { + FrameCallbackInterface* frameCallback; + UpdateProxy* updateProxyImpl; + }; + + std::vector< FrameCallbackInfo > mFrameCallbacks; + + TransformManager& mTransformManager; + Node& mRootNode; +}; + +} // namespace SceneGraph + +} // namespace Internal + +} // namespace Dali + +#endif // DALI_INTERNAL_SCENE_GRAPH_FRAME_CALLBACK_PROCESSOR_H diff --git a/dali/internal/update/manager/update-manager.cpp b/dali/internal/update/manager/update-manager.cpp index 49a28a4..10f290f 100644 --- a/dali/internal/update/manager/update-manager.cpp +++ b/dali/internal/update/manager/update-manager.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,7 @@ struct UpdateManager::Impl shaders(), panGestureProcessor( NULL ), messageQueue( renderController, sceneGraphBuffers ), + frameCallbackProcessor( NULL ), keepRenderingSeconds( 0.0f ), nodeDirtyFlags( TransformFlag ), // set to TransformFlag to ensure full update the first time through Update() frameCounter( 0 ), @@ -256,6 +258,18 @@ struct UpdateManager::Impl delete sceneController; } + /** + * Lazy init for FrameCallbackProcessor. + */ + FrameCallbackProcessor& GetFrameCallbackProcessor() + { + if( ! frameCallbackProcessor ) + { + frameCallbackProcessor = new FrameCallbackProcessor( transformManager, *root ); + } + return *frameCallbackProcessor; + } + SceneGraphBuffers sceneGraphBuffers; ///< Used to keep track of which buffers are being written or read RenderMessageDispatcher renderMessageDispatcher; ///< Used for passing messages to the render-thread NotificationManager& notificationManager; ///< Queues notification messages for the event-thread. @@ -300,6 +314,8 @@ struct UpdateManager::Impl std::vector updateCompiledShaders; ///< Shaders to be sent from Update to Event Mutex compiledShaderMutex; ///< lock to ensure no corruption on the renderCompiledShaders + OwnerPointer frameCallbackProcessor; ///< Owned FrameCallbackProcessor, only created if required. + float keepRenderingSeconds; ///< Set via Dali::Stage::KeepRendering int nodeDirtyFlags; ///< cumulative node dirty flags from previous frame int frameCounter; ///< Frame counter used in debugging to choose which frame to debug and which to ignore. @@ -879,6 +895,12 @@ unsigned int UpdateManager::Update( float elapsedSeconds, //Update the transformations of all the nodes mImpl->transformManager.Update(); + // Call the frame-callback-processor if set + if( mImpl->frameCallbackProcessor ) + { + mImpl->frameCallbackProcessor->Update( bufferIndex, elapsedSeconds ); + } + //Process Property Notifications ProcessPropertyNotifications( bufferIndex ); @@ -1070,6 +1092,16 @@ bool UpdateManager::IsDefaultSurfaceRectChanged() return surfaceRectChanged; } +void UpdateManager::AddFrameCallback( FrameCallbackInterface* frameCallback, const Node* rootNode ) +{ + mImpl->GetFrameCallbackProcessor().AddFrameCallback( frameCallback, rootNode ); +} + +void UpdateManager::RemoveFrameCallback( FrameCallbackInterface* frameCallback ) +{ + mImpl->GetFrameCallbackProcessor().RemoveFrameCallback( frameCallback ); +} + void UpdateManager::AddSampler( OwnerPointer< Render::Sampler >& sampler ) { // Message has ownership of Sampler while in transit from update to render diff --git a/dali/internal/update/manager/update-manager.h b/dali/internal/update/manager/update-manager.h index 705eecf..d87d280 100644 --- a/dali/internal/update/manager/update-manager.h +++ b/dali/internal/update/manager/update-manager.h @@ -45,6 +45,8 @@ namespace Dali { +class FrameCallbackInterface; + namespace Integration { class GlSyncAbstraction; @@ -610,6 +612,19 @@ public: */ bool IsDefaultSurfaceRectChanged(); + /** + * Adds an implementation of the FrameCallbackInterface. + * @param[in] frameCallback A pointer to the implementation of the FrameCallbackInterface + * @param[in] rootNode A pointer to the root node to apply the FrameCallback to + */ + void AddFrameCallback( FrameCallbackInterface* frameCallback, const Node* rootNode ); + + /** + * Removes the specified implementation of FrameCallbackInterface. + * @param[in] frameCallback A pointer to the implementation of the FrameCallbackInterface to remove. + */ + void RemoveFrameCallback( FrameCallbackInterface* frameCallback ); + private: // Undefined @@ -1337,6 +1352,27 @@ inline void AddResetterMessage( UpdateManager& manager, OwnerPointer LocalType; + + // Reserve some memory inside the message queue + unsigned int* 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::AddFrameCallback, &frameCallback, &rootNode ); +} + +inline void RemoveFrameCallbackMessage( UpdateManager& manager, FrameCallbackInterface& frameCallback ) +{ + typedef MessageValue1< UpdateManager, FrameCallbackInterface* > LocalType; + + // Reserve some memory inside the message queue + unsigned int* 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::RemoveFrameCallback, &frameCallback ); +} } // namespace SceneGraph diff --git a/dali/internal/update/manager/update-proxy-impl.cpp b/dali/internal/update/manager/update-proxy-impl.cpp new file mode 100644 index 0000000..85404a9 --- /dev/null +++ b/dali/internal/update/manager/update-proxy-impl.cpp @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2018 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// CLASS HEADER +#include + +namespace Dali +{ + +namespace Internal +{ + +namespace +{ + +SceneGraph::Node* FindNodeInSceneGraph( unsigned int id, SceneGraph::Node& node ) +{ + SceneGraph::Node* matchingNode = NULL; + + if( node.mId == id ) + { + matchingNode = &node; + } + else + { + for( auto&& i : node.GetChildren() ) + { + matchingNode = FindNodeInSceneGraph( id, *i ); + if( matchingNode ) + { + break; + } + } + } + + return matchingNode; +} + +} // unnamed namespace + +UpdateProxy::UpdateProxy( SceneGraph::TransformManager& transformManager, SceneGraph::Node& rootNode ) +: mNodeContainer(), + mLastCachedIdNodePair( { 0u, NULL } ), + mCurrentBufferIndex( 0u ), + mTransformManager( transformManager ), + mRootNode( rootNode ) +{ +} + +UpdateProxy::~UpdateProxy() +{ +} + +Vector3 UpdateProxy::GetPosition( unsigned int id ) const +{ + const Dali::Matrix& matrix = GetWorldMatrix( id ); + return matrix.GetTranslation3(); +} + +void UpdateProxy::SetPosition( unsigned int id, const Vector3& position ) +{ + const SceneGraph::Node* node = GetNodeWithId( id ); + if( node ) + { + Matrix& matrix = mTransformManager.GetWorldMatrix( node->mTransformId ); + matrix.SetTranslation( position ); + } +} + +const Vector3& UpdateProxy::GetSize( unsigned int id ) const +{ + const SceneGraph::Node* node = GetNodeWithId( id ); + if( node ) + { + return mTransformManager.GetVector3PropertyValue( node->mTransformId, SceneGraph::TRANSFORM_PROPERTY_SIZE ); + } + + return Vector3::ZERO; +} + +void UpdateProxy::SetSize( unsigned int id, const Vector3& size ) +{ + SceneGraph::Node* node = GetNodeWithId( id ); + if( node ) + { + mTransformManager.SetVector3PropertyValue( node->mTransformId, SceneGraph::TRANSFORM_PROPERTY_SIZE, size ); + } +} + +void UpdateProxy::GetPositionAndSize( unsigned int id, Vector3& position, Vector3& size ) const +{ + Matrix worldMatrix( false ); + GetWorldMatrixAndSize( id, worldMatrix, size ); + position = worldMatrix.GetTranslation3(); +} + +Vector4 UpdateProxy::GetWorldColor( unsigned int id ) const +{ + SceneGraph::Node* node = GetNodeWithId( id ); + if( node ) + { + return node->mWorldColor.Get( mCurrentBufferIndex ); + } + + return Vector4::ZERO; +} + +void UpdateProxy::SetWorldColor( unsigned int id, const Vector4& color ) const +{ + SceneGraph::Node* node = GetNodeWithId( id ); + if( node ) + { + Vector4& currentColor = node->mWorldColor.Get( mCurrentBufferIndex ); + currentColor = color; + } +} + +void UpdateProxy::GetWorldMatrixAndSize( unsigned int id, Matrix& worldMatrix, Vector3& size ) const +{ + const SceneGraph::Node* node = GetNodeWithId( id ); + if( node ) + { + mTransformManager.GetWorldMatrixAndSize( node->mTransformId, worldMatrix, size ); + } +} + +const Matrix& UpdateProxy::GetWorldMatrix( unsigned int id ) const +{ + const SceneGraph::Node* node = GetNodeWithId( id ); + if( node ) + { + return mTransformManager.GetWorldMatrix( node->mTransformId ); + } + + return Matrix::IDENTITY; +} + +void UpdateProxy::SetWorldMatrix( unsigned int id, const Matrix& worldMatrix ) +{ + SceneGraph::Node* node = GetNodeWithId( id ); + if( node ) + { + Matrix& currentMatrix = mTransformManager.GetWorldMatrix( node->mTransformId ); + currentMatrix = worldMatrix; + } +} + +SceneGraph::Node* UpdateProxy::GetNodeWithId( unsigned int id ) const +{ + SceneGraph::Node* node = NULL; + + // Cache the last accessed node so we don't have to traverse + if( mLastCachedIdNodePair.node && mLastCachedIdNodePair.id == id ) + { + node = mLastCachedIdNodePair.node; + } + else + { + // Find node in vector + for( auto&& pair : mNodeContainer ) + { + if( pair.id == id ) + { + node = pair.node; + mLastCachedIdNodePair = pair; + break; + } + } + + if( ! node ) + { + // Node not in vector, find in scene-graph + node = FindNodeInSceneGraph( id, mRootNode ); + if( node ) + { + mNodeContainer.push_back( { id, node } ); + mLastCachedIdNodePair = *mNodeContainer.rbegin(); + } + } + } + + return node; +} + +} // namespace Internal + +} // namespace Dali diff --git a/dali/internal/update/manager/update-proxy-impl.h b/dali/internal/update/manager/update-proxy-impl.h new file mode 100644 index 0000000..f730a75 --- /dev/null +++ b/dali/internal/update/manager/update-proxy-impl.h @@ -0,0 +1,164 @@ +#ifndef DALI_INTERNAL_UPDATE_PROXY_IMPL_H +#define DALI_INTERNAL_UPDATE_PROXY_IMPL_H + +/* + * Copyright (c) 2018 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include + +// INTERNAL INCLUDES +#include +#include +#include +#include +#include + +namespace Dali +{ + +namespace Internal +{ + +/** + * @brief The implementation of Dali::UpdateProxy. + * + * Ref-counting is not required for this object. + * + * @see Dali::UpdateProxy + */ +class UpdateProxy +{ +public: + + /** + * @brief Constructor. + * @param[in] transformManager Ref to the TransformManager in order to set/get transform properties of nodes + * @param[in] rootNode The root node for this proxy + */ + UpdateProxy( SceneGraph::TransformManager& transformManager, SceneGraph::Node& rootNode ); + + /** + * @brief Destructor. + */ + ~UpdateProxy(); + + // Movable but not copyable + + UpdateProxy( const UpdateProxy& ) = delete; ///< Deleted copy constructor. + UpdateProxy( UpdateProxy&& ) = default; ///< Default move constructor. + UpdateProxy& operator=( const UpdateProxy& ) = delete; ///< Deleted copy assignment operator. + UpdateProxy& operator=( UpdateProxy&& ) = default; ///< Default move assignment operator. + + /** + * @copydoc Dali::UpdateProxy::GetPosition() + */ + Vector3 GetPosition( unsigned int id ) const; + + /** + * @copydoc Dali::UpdateProxy::SetPosition() + */ + void SetPosition( unsigned int id, const Vector3& position ); + + /** + * @copydoc Dali::UpdateProxy::GetSize() + */ + const Vector3& GetSize( unsigned int id ) const; + + /** + * @copydoc Dali::UpdateProxy::SetSize() + */ + void SetSize( unsigned int id, const Vector3& size ); + + /** + * @copydoc Dali::UpdateProxy::GetPositionAndSize() + */ + void GetPositionAndSize( unsigned int id, Vector3& position, Vector3& size ) const; + + /** + * @copydoc Dali::UpdateProxy::GetColor() + */ + Vector4 GetWorldColor( unsigned int id ) const; + + /** + * @copydoc Dali::UpdateProxy::SetColor() + */ + void SetWorldColor( unsigned int id, const Vector4& color ) const; + + /** + * @copydoc Dali::UpdateProxy::GetWorldMatrixAndSize() + */ + void GetWorldMatrixAndSize( unsigned int id, Matrix& worldMatrix, Vector3& size ) const; + + /** + * @copydoc Dali::UpdateProxy::GetWorldMatrix() + */ + const Matrix& GetWorldMatrix( unsigned int id ) const; + + /** + * @copydoc Dali::UpdateProxy::SetWorldMatrix() + */ + void SetWorldMatrix( unsigned int id, const Matrix& worldMatrix ); + + /** + * @brief Retrieves the root-node used by this class + * @return The root node used by this class. + */ + SceneGraph::Node& GetRootNode() const + { + return mRootNode; + } + + void SetCurrentBufferIndex( BufferIndex bufferIndex ) + { + mCurrentBufferIndex = bufferIndex; + } + +private: + + /** + * @brief Retrieves the node with the specified ID. + * @param[in] id The ID of the node required + * @return A pointer to the required node if found. + * @note This caches the last accessed node. + */ + SceneGraph::Node* GetNodeWithId( unsigned int id ) const; + +private: + + /** + * Structure to store the ID & Node pair + */ + struct IdNodePair + { + unsigned int id; ///< The ID of the node + SceneGraph::Node* node; ///< The node itself + }; + + mutable std::vector< IdNodePair > mNodeContainer; ///< Used to store cached pointers to already searched for Nodes. + mutable IdNodePair mLastCachedIdNodePair; ///< Used to cache the last retrieved id-node pair. + unsigned int mCurrentBufferIndex; + + SceneGraph::TransformManager& mTransformManager; ///< Reference to the Transform Manager. + SceneGraph::Node& mRootNode; ///< The root node of this update proxy. +}; + +} // namespace Internal + +} // namespace Dali + +#endif // DALI_INTERNAL_UPDATE_PROXY_IMPL_H diff --git a/dali/internal/update/nodes/node.cpp b/dali/internal/update/nodes/node.cpp index aa7f777..52f443a 100644 --- a/dali/internal/update/nodes/node.cpp +++ b/dali/internal/update/nodes/node.cpp @@ -55,9 +55,9 @@ const PositionInheritanceMode Node::DEFAULT_POSITION_INHERITANCE_MODE( INHERIT_P const ColorMode Node::DEFAULT_COLOR_MODE( USE_OWN_MULTIPLY_PARENT_ALPHA ); -Node* Node::New() +Node* Node::New( unsigned int id ) { - return new ( gNodeMemoryPool.AllocateRawThreadSafe() ) Node(); + return new ( gNodeMemoryPool.AllocateRawThreadSafe() ) Node( id ); } void Node::Delete( Node* node ) @@ -78,7 +78,7 @@ void Node::Delete( Node* node ) } } -Node::Node() +Node::Node( unsigned int id ) : mTransformManager( NULL ), mTransformId( INVALID_TRANSFORM_ID ), mParentOrigin( TRANSFORM_PROPERTY_PARENT_ORIGIN ), @@ -95,6 +95,7 @@ Node::Node() mWorldMatrix(), mWorldColor( Color::WHITE ), mClippingSortModifier( 0u ), + mId( id ), mParent( NULL ), mExclusiveRenderTask( NULL ), mChildren(), diff --git a/dali/internal/update/nodes/node.h b/dali/internal/update/nodes/node.h index 71102fb..b7631ca 100644 --- a/dali/internal/update/nodes/node.h +++ b/dali/internal/update/nodes/node.h @@ -102,8 +102,9 @@ public: /** * Construct a new Node. + * @param[in] id The unique ID of the node */ - static Node* New(); + static Node* New( unsigned int id ); /** * Deletes a Node. @@ -785,8 +786,9 @@ protected: /** * Protected constructor; See also Node::New() + * @param[in] id The Unique ID of the actor creating the node */ - Node(); + Node( unsigned int id ); /** * Protected virtual destructor; See also Node::Delete( Node* ) @@ -867,6 +869,7 @@ public: // Default properties InheritedColor mWorldColor; ///< Full inherited color uint32_t mClippingSortModifier; ///< Contains bit-packed clipping information for quick access when sorting + const unsigned int mId; ///< The Unique ID of the node. protected: diff --git a/dali/internal/update/nodes/scene-graph-layer.cpp b/dali/internal/update/nodes/scene-graph-layer.cpp index 46bb14c..73709d2 100644 --- a/dali/internal/update/nodes/scene-graph-layer.cpp +++ b/dali/internal/update/nodes/scene-graph-layer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2018 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. @@ -31,15 +31,16 @@ namespace Internal namespace SceneGraph { -SceneGraph::Layer* Layer::New() +SceneGraph::Layer* Layer::New( unsigned int id ) { // Layers are currently heap allocated, unlike Nodes which are in a memory pool // However Node::Delete( layer ) will correctly delete a layer / node depending on type - return new Layer(); + return new Layer( id ); } -Layer::Layer() -: mSortFunction( Internal::Layer::ZValue ), +Layer::Layer( unsigned int id ) +: Node( id ), + mSortFunction( Internal::Layer::ZValue ), mClippingBox( 0,0,0,0 ), mLastCamera( NULL ), mBehavior( Dali::Layer::LAYER_2D ), diff --git a/dali/internal/update/nodes/scene-graph-layer.h b/dali/internal/update/nodes/scene-graph-layer.h index f56bdd3..eec47c4 100644 --- a/dali/internal/update/nodes/scene-graph-layer.h +++ b/dali/internal/update/nodes/scene-graph-layer.h @@ -2,7 +2,7 @@ #define DALI_INTERNAL_SCENE_GRAPH_LAYER_H /* - * Copyright (c) 2016 Samsung Electronics Co., Ltd. + * Copyright (c) 2018 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. @@ -77,9 +77,10 @@ public: /** * Construct a new Layer. + * @param[in] id The Unique ID of the actor creating the node * @return A smart-pointer to a newly allocated Node */ - static SceneGraph::Layer* New(); + static SceneGraph::Layer* New( unsigned int id ); /** * From Node, to convert a node to a layer. @@ -205,9 +206,10 @@ private: /** * Private constructor. + * @param[in] id The Unique ID of the actor creating the node * See also Layer::New() */ - Layer(); + Layer( unsigned int id ); // Undefined Layer(const Layer&);