Remove useless KeyFrame if we need 85/304385/5
authorEunki Hong <eunkiki.hong@samsung.com>
Tue, 16 Jan 2024 16:12:44 +0000 (01:12 +0900)
committerEunki Hong <eunkiki.hong@samsung.com>
Mon, 22 Jan 2024 08:17:49 +0000 (08:17 +0000)
Since it might heavy if the number of frame is quite large.

To reduce CPU overhead, let we make some way to remove and optimize
if user want.

Change-Id: Ia465012ead42c1a083be9a2f62d840f5ac158d00
Signed-off-by: Eunki Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali/utc-Dali-Animation.cpp
dali/devel-api/animation/key-frames-devel.cpp
dali/devel-api/animation/key-frames-devel.h
dali/internal/event/animation/key-frame-channel.h
dali/internal/event/animation/key-frames-impl.cpp
dali/internal/event/animation/key-frames-impl.h

index 6e35611..a4cfd8d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -9454,6 +9454,146 @@ int UtcDaliAnimationKeyFramesSetKeyFrameP(void)
   END_TEST;
 }
 
+int UtcDaliAnimationKeyFramesOptimizeKeyFramesLinearP(void)
+{
+  TestApplication application;
+
+  KeyFrames keyFrames = KeyFrames::New();
+
+  float firstExpectTime   = 0.0f;
+  float firstExpectValue  = 0.0f;
+  float secondExpectTime  = 0.4f;
+  float secondExpectValue = 1.2f;
+  float thirdExpectTime   = 0.8f;
+  float thirdExpectValue  = 0.4f;
+  float fourthExpectTime  = 1.0f;
+  float fourthExpectValue = 0.7f;
+
+  float interpolateLate = 0.5f;
+
+  tet_printf("first - second phase test\n");
+
+  DALI_TEST_EQUALS(false, DevelKeyFrames::OptimizeKeyFramesLinear(keyFrames), TEST_LOCATION);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 0, TEST_LOCATION);
+
+  keyFrames.Add(firstExpectTime, firstExpectValue);
+  DALI_TEST_EQUALS(false, DevelKeyFrames::OptimizeKeyFramesLinear(keyFrames), TEST_LOCATION);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 1, TEST_LOCATION);
+
+  keyFrames.Add(firstExpectTime * (1.0f - interpolateLate) + secondExpectTime * interpolateLate, firstExpectValue * (1.0f - interpolateLate) + secondExpectValue * interpolateLate);
+  DALI_TEST_EQUALS(false, DevelKeyFrames::OptimizeKeyFramesLinear(keyFrames), TEST_LOCATION);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 2, TEST_LOCATION);
+
+  keyFrames.Add(secondExpectTime, secondExpectValue);
+
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 3, TEST_LOCATION);
+  DALI_TEST_EQUALS(true, DevelKeyFrames::OptimizeKeyFramesLinear(keyFrames), TEST_LOCATION);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 2, TEST_LOCATION);
+
+  float           outputTime;
+  Property::Value outputValue;
+
+  DevelKeyFrames::GetKeyFrame(keyFrames, 0, outputTime, outputValue);
+
+  DALI_TEST_EQUALS(outputTime, firstExpectTime, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.GetType(), Property::Type::FLOAT, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.Get<float>(), firstExpectValue, TEST_LOCATION);
+
+  DevelKeyFrames::GetKeyFrame(keyFrames, 1, outputTime, outputValue);
+
+  DALI_TEST_EQUALS(outputTime, secondExpectTime, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.GetType(), Property::Type::FLOAT, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.Get<float>(), secondExpectValue, TEST_LOCATION);
+
+  tet_printf("second - third phase test\n");
+
+  interpolateLate = 0.3f;
+  keyFrames.Add(secondExpectTime * (1.0f - interpolateLate) + thirdExpectTime * interpolateLate, secondExpectValue * (1.0f - interpolateLate) + thirdExpectValue * interpolateLate);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 3, TEST_LOCATION);
+
+  interpolateLate = 0.4f;
+  keyFrames.Add(secondExpectTime * (1.0f - interpolateLate) + thirdExpectTime * interpolateLate, secondExpectValue * (1.0f - interpolateLate) + thirdExpectValue * interpolateLate);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 4, TEST_LOCATION);
+
+  interpolateLate = 0.5f;
+  keyFrames.Add(secondExpectTime * (1.0f - interpolateLate) + thirdExpectTime * interpolateLate, secondExpectValue * (1.0f - interpolateLate) + thirdExpectValue * interpolateLate);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 5, TEST_LOCATION);
+
+  interpolateLate = 0.8f;
+  keyFrames.Add(secondExpectTime * (1.0f - interpolateLate) + thirdExpectTime * interpolateLate, secondExpectValue * (1.0f - interpolateLate) + thirdExpectValue * interpolateLate);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 6, TEST_LOCATION);
+
+  keyFrames.Add(thirdExpectTime, thirdExpectValue);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 7, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(true, DevelKeyFrames::OptimizeKeyFramesLinear(keyFrames), TEST_LOCATION);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 3, TEST_LOCATION);
+
+  DevelKeyFrames::GetKeyFrame(keyFrames, 0, outputTime, outputValue);
+
+  DALI_TEST_EQUALS(outputTime, firstExpectTime, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.GetType(), Property::Type::FLOAT, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.Get<float>(), firstExpectValue, TEST_LOCATION);
+
+  DevelKeyFrames::GetKeyFrame(keyFrames, 1, outputTime, outputValue);
+
+  DALI_TEST_EQUALS(outputTime, secondExpectTime, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.GetType(), Property::Type::FLOAT, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.Get<float>(), secondExpectValue, TEST_LOCATION);
+
+  DevelKeyFrames::GetKeyFrame(keyFrames, 2, outputTime, outputValue);
+
+  DALI_TEST_EQUALS(outputTime, thirdExpectTime, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.GetType(), Property::Type::FLOAT, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.Get<float>(), thirdExpectValue, TEST_LOCATION);
+
+  tet_printf("third - fourth phase test. Test what we skip same progress.\n");
+
+  interpolateLate = 0.3f;
+  keyFrames.Add(thirdExpectTime * (1.0f - interpolateLate) + fourthExpectTime * interpolateLate, thirdExpectValue * (1.0f - interpolateLate) + fourthExpectValue * interpolateLate);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 4, TEST_LOCATION);
+
+  interpolateLate = 0.5f;
+  keyFrames.Add(thirdExpectTime * (1.0f - interpolateLate) + fourthExpectTime * interpolateLate, thirdExpectValue * (1.0f - interpolateLate) + fourthExpectValue * interpolateLate);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 5, TEST_LOCATION);
+
+  interpolateLate = 0.8f;
+  keyFrames.Add(thirdExpectTime * (1.0f - interpolateLate) + fourthExpectTime * interpolateLate, thirdExpectValue * (1.0f - interpolateLate) + fourthExpectValue * interpolateLate);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 6, TEST_LOCATION);
+
+  keyFrames.Add(fourthExpectTime, fourthExpectValue);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 7, TEST_LOCATION);
+
+  DALI_TEST_EQUALS(true, DevelKeyFrames::OptimizeKeyFramesLinear(keyFrames), TEST_LOCATION);
+  DALI_TEST_EQUALS(DevelKeyFrames::GetKeyFrameCount(keyFrames), 4, TEST_LOCATION);
+
+  DevelKeyFrames::GetKeyFrame(keyFrames, 0, outputTime, outputValue);
+
+  DALI_TEST_EQUALS(outputTime, firstExpectTime, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.GetType(), Property::Type::FLOAT, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.Get<float>(), firstExpectValue, TEST_LOCATION);
+
+  DevelKeyFrames::GetKeyFrame(keyFrames, 1, outputTime, outputValue);
+
+  DALI_TEST_EQUALS(outputTime, secondExpectTime, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.GetType(), Property::Type::FLOAT, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.Get<float>(), secondExpectValue, TEST_LOCATION);
+
+  DevelKeyFrames::GetKeyFrame(keyFrames, 2, outputTime, outputValue);
+
+  DALI_TEST_EQUALS(outputTime, thirdExpectTime, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.GetType(), Property::Type::FLOAT, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.Get<float>(), thirdExpectValue, TEST_LOCATION);
+
+  DevelKeyFrames::GetKeyFrame(keyFrames, 3, outputTime, outputValue);
+
+  DALI_TEST_EQUALS(outputTime, fourthExpectTime, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.GetType(), Property::Type::FLOAT, TEST_LOCATION);
+  DALI_TEST_EQUALS(outputValue.Get<float>(), fourthExpectValue, TEST_LOCATION);
+
+  END_TEST;
+}
+
 int UtcDaliAnimationAnimateBetweenActorColorAlphaP(void)
 {
   TestApplication application;
index 37ec9a1..f79b32e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -38,6 +38,11 @@ void SetKeyFrameValue(KeyFrames keyFrames, std::size_t index, const Property::Va
   GetImplementation(keyFrames).SetKeyFrameValue(index, value);
 }
 
+bool OptimizeKeyFramesLinear(KeyFrames keyFrames)
+{
+  return GetImplementation(keyFrames).OptimizeKeyFramesLinear();
+}
+
 } // namespace DevelKeyFrames
 
 } // namespace Dali
index 936cf44..9acdb53 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_KEY_FRAMES_DEVEL_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -20,6 +20,7 @@
 
 // INTERNAL INCLUDES
 #include <dali/public-api/animation/key-frames.h>
+#include <dali/public-api/common/constants.h>
 
 namespace Dali
 {
@@ -51,6 +52,15 @@ DALI_CORE_API void GetKeyFrame(KeyFrames keyFrames, std::size_t index, float& ti
  */
 DALI_CORE_API void SetKeyFrameValue(KeyFrames keyFrames, std::size_t index, const Property::Value& value);
 
+/**
+ * Optimize keyframes. Remove some frames what we can remove that might show same result.
+ * If two keyframe has same progress, we will remain only latest one.
+ * @warning It will be works well only if we use keyFrames animate as Dali::Animation::LINEAR interpolation
+ * @param[in] keyFrames The KeyFrames object to perform this operation on.
+ * @return True if keyframe optimized. False otherwise.
+ */
+DALI_CORE_API bool OptimizeKeyFramesLinear(KeyFrames keyFrames);
+
 } // namespace DevelKeyFrames
 
 } // namespace Dali
index c9ba8b3..e8bf162 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_KEY_FRAME_CHANNEL_H
 
 /*
- * Copyright (c) 2022 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -100,6 +100,65 @@ struct KeyFrameChannel
     return interpolatedV;
   }
 
+  bool OptimizeValuesLinear()
+  {
+    ProgressValues optimizedValues;
+    bool           optimized = false;
+
+    // Optimize works only if value has more or equal than 3 values.
+    if(mValues.size() < 3u)
+    {
+      return optimized;
+    }
+
+    auto iter = mValues.begin();
+    for(; iter + 1 != mValues.end();)
+    {
+      // Insert iter, which is the first value, or jter what we fail to ignore previous loops.
+      optimizedValues.push_back(*iter);
+      const float iterProgress = iter->GetProgress();
+
+      auto jter = iter + 1;
+
+      for(; jter + 1 != mValues.end();)
+      {
+        // Check whether we can ignore jter now.
+        const auto  kter         = jter + 1;
+        const float jterProgress = jter->GetProgress();
+        const float kterProgress = kter->GetProgress();
+
+        float frameProgress = (jterProgress - iterProgress) / (kterProgress - iterProgress);
+
+        // Interpolate with iter and kter.
+        // Check value between interpolatedV and jter->GetValue().
+        // If two values are similar, we can skip jter!
+        V interpolatedV{};
+        Interpolate(interpolatedV, iter->GetValue(), kter->GetValue(), frameProgress);
+
+        // TODO : We might need to find more good way to compare two values
+        if(Property::Value(interpolatedV) != Property::Value(jter->GetValue()))
+        {
+          break;
+        }
+
+        optimized = true;
+
+        // Keep checking for the next jter
+        ++jter;
+      }
+      iter = jter;
+    }
+
+    // Insert last value.
+    optimizedValues.push_back(*iter);
+
+    if(optimized)
+    {
+      mValues = std::move(optimizedValues);
+    }
+    return optimized;
+  }
+
   ProgressValues mValues;
 };
 
index 2a73025..4ffd180 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -185,5 +185,14 @@ void KeyFrames::SetKeyFrameValue(std::size_t index, const Property::Value& value
   }
 }
 
+bool KeyFrames::OptimizeKeyFramesLinear()
+{
+  if(mKeyFrames)
+  {
+    return mKeyFrames->OptimizeValuesLinear();
+  }
+  return false;
+}
+
 } // namespace Internal
 } // namespace Dali
index 597fb4c..b566782 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_KEY_FRAMES_H
 
 /*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
@@ -97,6 +97,11 @@ public:
    */
   void SetKeyFrameValue(std::size_t index, const Property::Value& value);
 
+  /**
+   * @copydoc Dali::DevelKeyFrames::OptimizeKeyFramesLinear()
+   */
+  bool OptimizeKeyFramesLinear();
+
 private:
   Dali::Property::Type          mType{Property::NONE}; // Type of the specialization
   std::unique_ptr<KeyFrameSpec> mKeyFrames;            // Pointer to the specialized key frame object
@@ -127,6 +132,12 @@ public:
    * @param[in] value The value of the given key frame
    */
   virtual void SetKeyFrameValue(std::size_t index, const Property::Value& value) = 0;
+
+  /**
+   * Optimize key frame value as linear interpolation.
+   * @return True if optimize successfully, so the total frame reduced. False otherwise.
+   */
+  virtual bool OptimizeValuesLinear() = 0;
 };
 
 /**
@@ -193,6 +204,14 @@ public:
   }
 
   /**
+   * @copydoc KeyFrameSpec::OptimizeValuesLinear()
+   */
+  bool OptimizeValuesLinear() override
+  {
+    return mChannel.OptimizeValuesLinear();
+  }
+
+  /**
    * Return whether the progress is valid for the range of keyframes. (The first
    * keyframe doesn't have to start at 0, and the last doesn't have to end at 1.0)
    * @param[in] progress The progress to test