[dali_1.9.33] Merge branch 'devel/master' 69/245469/1
authorGyörgy Straub <g.straub@partner.samsung.com>
Fri, 9 Oct 2020 13:06:40 +0000 (14:06 +0100)
committerGyörgy Straub <g.straub@partner.samsung.com>
Fri, 9 Oct 2020 13:06:40 +0000 (14:06 +0100)
Change-Id: I58ae05dfbcbb8b9a80a3548de1c051444e9c9f8f

automated-tests/src/dali/utc-Dali-Vector.cpp
dali/internal/render/common/render-algorithms.cpp
dali/internal/update/rendering/scene-graph-renderer.cpp
dali/public-api/common/dali-vector.h
dali/public-api/dali-core-version.cpp
dali/public-api/object/property-value.cpp
dali/public-api/object/property-value.h
packaging/dali.spec

index f0dd410..7616cc0 100644 (file)
@@ -1389,6 +1389,108 @@ int UtcDaliVectorMoveAssignment(void)
   END_TEST;
 }
 
+int UtcDaliVectorEraseFreeFunction(void)
+{
+  tet_infoline("Testing Dali::Erase<Vector<int>>");
+
+  Vector<int> vector;
+
+  // erasing from empty vector
+  Dali::Erase(vector, 2);
+  DALI_TEST_EQUALS(ZERO, vector.Count(), TEST_LOCATION);
+  DALI_TEST_EQUALS(ZERO, vector.Capacity(), TEST_LOCATION);
+
+
+  vector.PushBack(1);
+  vector.PushBack(2);
+  vector.PushBack(3);
+  vector.PushBack(4);
+  vector.PushBack(5);
+  vector.PushBack(3);
+
+  // erase multiple value
+  Dali::Erase(vector, 3);
+  DALI_TEST_EQUALS(static_cast<Dali::VectorBase::SizeType>(4), vector.Count(), TEST_LOCATION);
+
+  DALI_TEST_EQUALS(vector[0], 1, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[1], 2, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[2], 4, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[3], 5, TEST_LOCATION);
+
+
+  // erase an element present at start
+  Dali::Erase(vector, 1);
+  DALI_TEST_EQUALS(static_cast<Dali::VectorBase::SizeType>(3), vector.Count(), TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[0], 2, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[1], 4, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[2], 5, TEST_LOCATION);
+
+  // erase an element present at end
+  Dali::Erase(vector, 5);
+  DALI_TEST_EQUALS(static_cast<Dali::VectorBase::SizeType>(2), vector.Count(), TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[0], 2, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[1], 4, TEST_LOCATION);
+
+  // erase an element not present in the vector
+  Dali::Erase(vector, 42);
+  DALI_TEST_EQUALS(static_cast<Dali::VectorBase::SizeType>(2), vector.Count(), TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[0], 2, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[1], 4, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliVectorEraseIfFreeFunction(void)
+{
+  tet_infoline("Testing Dali::EraseIf<Vector<int>>");
+
+  Vector<int> vector;
+
+  // erasing from empty vector
+  Dali::EraseIf(vector, [](const auto & value) {return value == 2;});
+  DALI_TEST_EQUALS(ZERO, vector.Count(), TEST_LOCATION);
+  DALI_TEST_EQUALS(ZERO, vector.Capacity(), TEST_LOCATION);
+
+
+  vector.PushBack(1);
+  vector.PushBack(2);
+  vector.PushBack(3);
+  vector.PushBack(4);
+  vector.PushBack(5);
+  vector.PushBack(3);
+
+  // erase multiple value
+  Dali::EraseIf(vector, [](const auto & value) {return value == 3;});
+  DALI_TEST_EQUALS(static_cast<Dali::VectorBase::SizeType>(4), vector.Count(), TEST_LOCATION);
+
+  DALI_TEST_EQUALS(vector[0], 1, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[1], 2, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[2], 4, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[3], 5, TEST_LOCATION);
+
+
+  // erase an element present at start
+  Dali::EraseIf(vector, [](const auto & value) {return value == 1;});
+  DALI_TEST_EQUALS(static_cast<Dali::VectorBase::SizeType>(3), vector.Count(), TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[0], 2, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[1], 4, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[2], 5, TEST_LOCATION);
+
+  // erase an element present at end
+  Dali::EraseIf(vector, [](const auto & value) {return value == 5;});
+  DALI_TEST_EQUALS(static_cast<Dali::VectorBase::SizeType>(2), vector.Count(), TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[0], 2, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[1], 4, TEST_LOCATION);
+
+  // erase an element not present in the vector
+  Dali::EraseIf(vector, [](const auto & value) {return value == 42;});
+  DALI_TEST_EQUALS(static_cast<Dali::VectorBase::SizeType>(2), vector.Count(), TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[0], 2, TEST_LOCATION);
+  DALI_TEST_EQUALS(vector[1], 4, TEST_LOCATION);
+
+  END_TEST;
+}
+
 /*
  * this does not compile at the moment
  * Vector< Actor > classvector; this does not compile yet either
index f5d540c..d40614c 100755 (executable)
@@ -442,6 +442,22 @@ inline void RenderAlgorithms::ProcessRenderList( const RenderList& renderList,
   {
     const RenderItem& item = renderList.GetItem( index );
 
+    // Discard renderers outside the root clipping rect
+    bool skip = true;
+    if( !rootClippingRect.IsEmpty() )
+    {
+      auto rect = item.CalculateViewportSpaceAABB( item.mUpdateSize, mViewportRectangle.width, mViewportRectangle.height );
+
+      if(rect.Intersect( rootClippingRect ))
+      {
+        skip = false;
+      }
+    }
+    else
+    {
+      skip = false;
+    }
+
     DALI_PRINT_RENDER_ITEM( item );
 
     // Set up clipping based on both the Renderer and Actor APIs.
@@ -455,9 +471,9 @@ inline void RenderAlgorithms::ProcessRenderList( const RenderList& renderList,
       // draw-mode state, such as Overlays.
       // If the flags are set to "AUTO", the behavior then depends on the type of renderer. Overlay Renderers will always
       // disable depth testing and writing. Color Renderers will enable them if the Layer does.
-      if( depthBufferAvailable == Integration::DepthBufferAvailable::TRUE )
+      if (depthBufferAvailable == Integration::DepthBufferAvailable::TRUE)
       {
-        SetupDepthBuffer( item, context, autoDepthTestMode, firstDepthBufferUse );
+        SetupDepthBuffer(item, context, autoDepthTestMode, firstDepthBufferUse);
       }
 
       // Depending on whether the renderer has draw commands attached or not the rendering process will
@@ -465,12 +481,15 @@ inline void RenderAlgorithms::ProcessRenderList( const RenderList& renderList,
       // iteration must be done and the default behaviour of the renderer will be executed.
       // The queues allow to iterate over the same renderer multiple times changing the state of the renderer.
       // It is similar to the multi-pass rendering.
-      auto const MAX_QUEUE = item.mRenderer->GetDrawCommands().empty() ? 1 : DevelRenderer::RENDER_QUEUE_MAX;
-      for( auto queue = 0u; queue < MAX_QUEUE; ++queue )
+      if( !skip )
       {
-        // Render the item.
-        item.mRenderer->Render(context, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix,
-                               viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction, queue);
+        auto const MAX_QUEUE = item.mRenderer->GetDrawCommands().empty() ? 1 : DevelRenderer::RENDER_QUEUE_MAX;
+        for (auto queue = 0u; queue < MAX_QUEUE; ++queue)
+        {
+          // Render the item.
+          item.mRenderer->Render(context, bufferIndex, *item.mNode, item.mModelMatrix, item.mModelViewMatrix,
+                                 viewMatrix, projectionMatrix, item.mSize, !item.mIsOpaque, boundTextures, instruction, queue);
+        }
       }
     }
   }
index bbfa7ae..e821f6e 100644 (file)
@@ -49,8 +49,14 @@ MemoryPoolObjectAllocator<Renderer> gRendererMemoryPool;
 void AddMappings( CollectedUniformMap& localMap, const UniformMap& uniformMap )
 {
   // Iterate thru uniformMap.
-  //   Any maps that aren't in localMap should be added in a single step
-  CollectedUniformMap newUniformMappings;
+  // Any maps that aren't in localMap should be added in a single step
+
+  // keep a static vector to avoid temporary heap allocation.
+  // As this function gets called only from update thread we don't have to
+  // make it thread safe (so no need to keep a thread_local variable).
+  static CollectedUniformMap newUniformMappings;
+
+  newUniformMappings.Clear();
 
   for( UniformMap::SizeType i = 0, count=uniformMap.Count(); i<count; ++i )
   {
index e4bb8ce..5a61b64 100644 (file)
@@ -847,6 +847,38 @@ public: // API
 };
 
 /**
+ * @brief Erases all elements that compare equal to value from the vector.
+ *
+ * @SINCE_1_9.33
+ * @param[in] vector The vector
+ * @param[in] value The value to be removed.
+ */
+template <class T, class U>
+inline void Erase(Dali::Vector<T>& vector, const U& value)
+{
+  auto begin = vector.Begin();
+  auto end = vector.End();
+
+  vector.Erase(std::remove(begin, end, value), end);
+}
+
+/**
+ * @brief Erases all elements that satisfy the predicate from the vector.
+ *
+ * @SINCE_1_9.33
+ * @param[in] vector The vector
+ * @param[in] predicate The predicate
+ */
+template <class T, class Predicate>
+inline void EraseIf(Dali::Vector<T>& vector, Predicate predicate)
+{
+  auto begin = vector.Begin();
+  auto end = vector.End();
+
+  vector.Erase(std::remove_if(begin, end, predicate), end);
+}
+
+/**
  * @}
  */
 } // namespace Dali
index 7ee6ca0..d245797 100644 (file)
@@ -27,7 +27,7 @@ namespace Dali
 {
 const uint32_t    CORE_MAJOR_VERSION = 1;
 const uint32_t    CORE_MINOR_VERSION = 9;
-const uint32_t    CORE_MICRO_VERSION = 32;
+const uint32_t    CORE_MICRO_VERSION = 33;
 const char* const CORE_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index 09bb5c7..1af8e12 100644 (file)
@@ -19,6 +19,8 @@
 #include <dali/public-api/object/property-value.h>
 
 // EXTERNAL INCLUDES
+#include <algorithm>
+#include <cstring>
 #include <ostream>
 
 // INTERNAL INCLUDES
 #include <dali/public-api/object/property-map.h>
 #include <dali/public-api/object/property-types.h>
 
+/**
+ * As the Implementation is bit complex because of Small Buffer Optimization (SBO)
+ * In the Property::Value class and Tagged Union implementation in the Impl class.
+ * Here is the brief description of the motivation and internal of this implementation.
+ *
+ * The main motivation is to keep the value semantic without doing any heap allocation.
+ * 1. In the public class we keep a aligned buffer of 16byte instead of keeping the Pimpl pointer.
+ * 2. we create the Impl object inplace in that buffer to avoid the memory allocation.
+ * 3. we don't store the Impl* along with the SBO storage to save 8byte from the Property::Value object.
+ * 4. Every time we need to access  Impl object we access it through Read() and Write() which retrieves
+ *    the already constructed Impl object from the SBO storage.
+ * 5. with SBO , the move and assignment operator are tricky , so we use std::copy to move the object
+ *    and update the moved from object to NONE to keep it in a safe state.
+ * 6. In the Impl class, we keep a Uninitialized Union and manage lifecycle of objects by doing construct/destroy manually.
+ * 7. We keep the Type information along with the Object to keep the size as 16byte (using common initial sequence(CIS) ).
+ */
+
 namespace Dali
 {
 
 struct Property::Value::Impl
 {
+  template<typename... Args>
+  static Impl* New(Property::Value::Storage& buffer, Args... args)
+  {
+    return new(&buffer) Impl(std::forward<Args>(args)...);
+  }
+
+  static void Delete(Property::Value::Impl& impl)
+  {
+    impl.~Impl();
+  }
+
   ~Impl()
   {
     // Destroy the current object stored in Data union memory.
@@ -226,11 +256,21 @@ struct Property::Value::Impl
     return &(mData.mMap.member);
   }
 
+  const Property::Map* GetMapPtr() const
+  {
+    return &(mData.mMap.member);
+  }
+
   Property::Array* GetArrayPtr()
   {
     return &(mData.mArray.member);
   }
 
+  const Property::Array* GetArrayPtr() const
+  {
+    return &(mData.mArray.member);
+  }
+
   Impl& operator=(const Impl& other)
   {
     const bool isSameType = GetType() == other.GetType();
@@ -368,12 +408,12 @@ struct Property::Value::Impl
     return *this;
   }
 
-private:
   void SetType(Type typeValue)
   {
     mData.mType.type = typeValue;
   }
 
+private:
   /**
    * This helper function takes a typed(Tp) memory location( member)
    * and a object of same type( val ) and move constructs a new object of
@@ -524,205 +564,223 @@ private:
   Data mData;
 };
 
+/*
+ * Safe way to read an already constructed Object from a buffer.
+ */
+const Property::Value::Impl& Property::Value::Read() const
+{
+  return *(std::launder(reinterpret_cast<const Property::Value::Impl*>(mStorage.buffer)));
+}
+
+Property::Value::Impl& Property::Value::Write()
+{
+  return *(std::launder(reinterpret_cast<Property::Value::Impl*>(mStorage.buffer)));
+}
+
 Property::Value::Value()
-: mImpl(nullptr)
 {
+  Impl::New(mStorage);
+
+  static_assert(sizeof(Value) == 16);
+  static_assert(alignof(Value) == alignof(Value*));
 }
 
 Property::Value::Value(bool booleanValue)
-: mImpl(new Impl(booleanValue))
 {
+  Impl::New(mStorage, booleanValue);
 }
 
 Property::Value::Value(float floatValue)
-: mImpl(new Impl(floatValue))
 {
+  Impl::New(mStorage, floatValue);
 }
 
 Property::Value::Value(int32_t integerValue)
-: mImpl(new Impl(integerValue))
 {
+  Impl::New(mStorage, integerValue);
 }
 
 Property::Value::Value(const Vector2& vectorValue)
-: mImpl(new Impl(vectorValue))
 {
+  Impl::New(mStorage, vectorValue);
 }
 
 Property::Value::Value(const Vector3& vectorValue)
-: mImpl(new Impl(vectorValue))
 {
+  Impl::New(mStorage, vectorValue);
 }
 
 Property::Value::Value(const Vector4& vectorValue)
-: mImpl(new Impl(vectorValue))
 {
+  Impl::New(mStorage, vectorValue);
 }
 
 Property::Value::Value(const Matrix3& matrixValue)
-: mImpl(new Impl(matrixValue))
 {
+  Impl::New(mStorage, matrixValue);
 }
 
 Property::Value::Value(const Matrix& matrixValue)
-: mImpl(new Impl(matrixValue))
 {
+  Impl::New(mStorage, matrixValue);
 }
 
 Property::Value::Value(const Rect<int32_t>& rectValue)
-: mImpl(new Impl(rectValue))
 {
+  Impl::New(mStorage, rectValue);
 }
 
 Property::Value::Value(const Rect<float>& rectValue)
-: mImpl(new Impl(Vector4(rectValue.x, rectValue.y, rectValue.width, rectValue.height)))
 {
+  Impl::New(mStorage, Vector4(rectValue.x, rectValue.y, rectValue.width, rectValue.height));
 }
 
 Property::Value::Value(const AngleAxis& angleAxisValue)
-: mImpl(new Impl(angleAxisValue))
 {
+  Impl::New(mStorage, angleAxisValue);
 }
 
 Property::Value::Value(const Quaternion& quaternionValue)
 {
   AngleAxis angleAxisValue;
   quaternionValue.ToAxisAngle(angleAxisValue.axis, angleAxisValue.angle);
-  mImpl = new Impl(std::move(angleAxisValue));
+  Impl::New(mStorage, std::move(angleAxisValue));
 }
 
 Property::Value::Value(std::string stringValue)
-: mImpl(new Impl(std::move(stringValue)))
 {
+  Impl::New(mStorage, std::move(stringValue));
 }
 
 Property::Value::Value(const char* stringValue)
-: mImpl(nullptr)
 {
   if(stringValue) // string constructor is undefined with nullptr
   {
-    mImpl = new Impl(std::string(stringValue));
+    Impl::New(mStorage, std::string(stringValue));
   }
   else
   {
-    mImpl = new Impl(std::string());
+    Impl::New(mStorage, std::string());
   }
 }
 
 Property::Value::Value(Property::Array arrayValue)
-: mImpl(new Impl(std::move(arrayValue)))
 {
+  Impl::New(mStorage, std::move(arrayValue));
 }
 
 Property::Value::Value(Property::Map mapValue)
-: mImpl(new Impl(std::move(mapValue)))
 {
+  Impl::New(mStorage, std::move(mapValue));
 }
 
 Property::Value::Value(const Extents& extentsValue)
-: mImpl(new Impl(extentsValue))
 {
+  Impl::New(mStorage, extentsValue);
 }
 
 Property::Value::Value(const std::initializer_list<KeyValuePair>& values)
-: mImpl(new Impl(Property::Map(values)))
 {
+  Impl::New(mStorage, Property::Map(values));
 }
 
 Property::Value::Value(Type type)
-: mImpl(nullptr)
 {
   switch(type)
   {
     case Property::BOOLEAN:
     {
-      mImpl = new Impl(false);
+      Impl::New(mStorage, false);
       break;
     }
     case Property::FLOAT:
     {
-      mImpl = new Impl(0.f);
+      Impl::New(mStorage, 0.f);
       break;
     }
     case Property::INTEGER:
     {
-      mImpl = new Impl(0);
+      Impl::New(mStorage, 0);
       break;
     }
     case Property::VECTOR2:
     {
-      mImpl = new Impl(Vector2::ZERO);
+      Impl::New(mStorage, Vector2::ZERO);
       break;
     }
     case Property::VECTOR3:
     {
-      mImpl = new Impl(Vector3::ZERO);
+      Impl::New(mStorage, Vector3::ZERO);
       break;
     }
     case Property::VECTOR4:
     {
-      mImpl = new Impl(Vector4::ZERO);
+      Impl::New(mStorage, Vector4::ZERO);
       break;
     }
     case Property::RECTANGLE:
     {
-      mImpl = new Impl(Rect<int32_t>());
+      Impl::New(mStorage, Rect<int32_t>());
       break;
     }
     case Property::ROTATION:
     {
-      mImpl = new Impl(AngleAxis());
+      Impl::New(mStorage, AngleAxis());
       break;
     }
     case Property::STRING:
     {
-      mImpl = new Impl(std::string());
+      Impl::New(mStorage, std::string());
       break;
     }
     case Property::MATRIX:
     {
-      mImpl = new Impl(Matrix());
+      Impl::New(mStorage, Matrix());
       break;
     }
     case Property::MATRIX3:
     {
-      mImpl = new Impl(Matrix3());
+      Impl::New(mStorage, Matrix3());
       break;
     }
     case Property::ARRAY:
     {
-      mImpl = new Impl(Property::Array());
+      Impl::New(mStorage, Property::Array());
       break;
     }
     case Property::MAP:
     {
-      mImpl = new Impl(Property::Map());
+      Impl::New(mStorage, Property::Map());
       break;
     }
     case Property::EXTENTS:
     {
-      mImpl = new Impl(Extents());
+      Impl::New(mStorage, Extents());
       break;
     }
     case Property::NONE:
     {
-      // No need to create an Impl
+      Impl::New(mStorage);
       break;
     }
   }
 }
 
 Property::Value::Value(const Property::Value& value)
-: mImpl(nullptr)
 {
+  Impl::New(mStorage);
+
   // reuse assignment operator
   operator=(value);
 }
 
 Property::Value::Value(Property::Value&& value) noexcept
-: mImpl(value.mImpl)
 {
-  value.mImpl = nullptr;
+  // steal the Impl object by doing a copy.
+  std::copy(std::begin(value.mStorage.buffer), std::end(value.mStorage.buffer), std::begin(mStorage.buffer));
+
+  // update the moved from object to NONE state.
+  value.Write().SetType(Property::NONE);
 }
 
 Property::Value& Property::Value::operator=(const Property::Value& value)
@@ -733,20 +791,8 @@ Property::Value& Property::Value::operator=(const Property::Value& value)
     return *this;
   }
 
-  if(value.mImpl)
-  {
-    if(!mImpl)
-    {
-      mImpl = new Impl();
-    }
-
-    *mImpl = *(value.mImpl);
-  }
-  else
-  {
-    delete mImpl;
-    mImpl = nullptr;
-  }
+  // this will call the Impl operator=()
+  Write() = value.Read();
 
   return *this;
 }
@@ -755,9 +801,14 @@ Property::Value& Property::Value::operator=(Property::Value&& value) noexcept
 {
   if(this != &value)
   {
-    delete mImpl;
-    mImpl       = value.mImpl;
-    value.mImpl = nullptr;
+    // delete the existing Impl object
+    Impl::Delete(Write());
+
+    // steal the Impl object by doing a copy.
+    std::copy(std::begin(value.mStorage.buffer), std::end(value.mStorage.buffer), std::begin(mStorage.buffer));
+
+    // update the moved from object to NONE state.
+    value.Write().SetType(Property::NONE);
   }
 
   return *this;
@@ -765,159 +816,168 @@ Property::Value& Property::Value::operator=(Property::Value&& value) noexcept
 
 Property::Value::~Value()
 {
-  delete mImpl;
+  Impl::Delete(Write());
 }
 
 Property::Type Property::Value::GetType() const
 {
-  return mImpl ? mImpl->GetType() : Property::NONE;
+  return Read().GetType();
 }
 
 bool Property::Value::Get(bool& booleanValue) const
 {
   bool converted = false;
-  if(mImpl)
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == BOOLEAN)
   {
-    if(mImpl->GetType() == BOOLEAN)
-    {
-      booleanValue = mImpl->GetBool();
-      converted    = true;
-    }
-    else if(mImpl->GetType() == INTEGER)
-    {
-      booleanValue = mImpl->GetInt();
-      converted    = true;
-    }
+    booleanValue = obj.GetBool();
+    converted    = true;
   }
+  else if(obj.GetType() == INTEGER)
+  {
+    booleanValue = obj.GetInt();
+    converted    = true;
+  }
+
   return converted;
 }
 
 bool Property::Value::Get(float& floatValue) const
 {
   bool converted = false;
-  if(mImpl)
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == FLOAT)
   {
-    if(mImpl->GetType() == FLOAT)
-    {
-      floatValue = mImpl->GetFloat();
-      converted  = true;
-    }
-    else if(mImpl->GetType() == BOOLEAN)
-    {
-      floatValue = static_cast<float>(mImpl->GetBool());
-      converted  = true;
-    }
-    else if(mImpl->GetType() == INTEGER)
-    {
-      floatValue = static_cast<float>(mImpl->GetInt());
-      converted  = true;
-    }
+    floatValue = obj.GetFloat();
+    converted  = true;
+  }
+  else if(obj.GetType() == BOOLEAN)
+  {
+    floatValue = static_cast<float>(obj.GetBool());
+    converted  = true;
+  }
+  else if(obj.GetType() == INTEGER)
+  {
+    floatValue = static_cast<float>(obj.GetInt());
+    converted  = true;
   }
+
   return converted;
 }
 
 bool Property::Value::Get(int32_t& integerValue) const
 {
   bool converted = false;
-  if(mImpl)
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == INTEGER)
   {
-    if(mImpl->GetType() == INTEGER)
-    {
-      integerValue = mImpl->GetInt();
-      converted    = true;
-    }
-    else if(mImpl->GetType() == BOOLEAN)
-    {
-      integerValue = mImpl->GetBool();
-      converted    = true;
-    }
-    else if(mImpl->GetType() == FLOAT)
-    {
-      integerValue = static_cast<int32_t>(mImpl->GetFloat());
-      converted    = true;
-    }
+    integerValue = obj.GetInt();
+    converted    = true;
+  }
+  else if(obj.GetType() == BOOLEAN)
+  {
+    integerValue = obj.GetBool();
+    converted    = true;
+  }
+  else if(obj.GetType() == FLOAT)
+  {
+    integerValue = static_cast<int32_t>(obj.GetFloat());
+    converted    = true;
   }
+
   return converted;
 }
 
 bool Property::Value::Get(Vector2& vectorValue) const
 {
   bool converted = false;
-  if(mImpl)
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == VECTOR4)
   {
-    if(mImpl->GetType() == VECTOR4)
-    {
-      vectorValue = mImpl->GetVector4();
-      converted   = true;
-    }
-    else if(mImpl->GetType() == VECTOR2)
-    {
-      vectorValue = mImpl->GetVector2();
-      converted   = true;
-    }
-    else if(mImpl->GetType() == VECTOR3)
-    {
-      vectorValue = mImpl->GetVector3();
-      converted   = true;
-    }
+    vectorValue = obj.GetVector4();
+    converted   = true;
+  }
+  else if(obj.GetType() == VECTOR2)
+  {
+    vectorValue = obj.GetVector2();
+    converted   = true;
+  }
+  else if(obj.GetType() == VECTOR3)
+  {
+    vectorValue = obj.GetVector3();
+    converted   = true;
   }
+
   return converted;
 }
 
 bool Property::Value::Get(Vector3& vectorValue) const
 {
   bool converted = false;
-  if(mImpl)
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == VECTOR4)
   {
-    if(mImpl->GetType() == VECTOR4)
-    {
-      vectorValue = mImpl->GetVector4();
-      converted   = true;
-    }
-    else if(mImpl->GetType() == VECTOR2)
-    {
-      vectorValue = mImpl->GetVector2();
-      converted   = true;
-    }
-    else if(mImpl->GetType() == VECTOR3)
-    {
-      vectorValue = mImpl->GetVector3();
-      converted   = true;
-    }
+    vectorValue = obj.GetVector4();
+    converted   = true;
+  }
+  else if(obj.GetType() == VECTOR2)
+  {
+    vectorValue = obj.GetVector2();
+    converted   = true;
+  }
+  else if(obj.GetType() == VECTOR3)
+  {
+    vectorValue = obj.GetVector3();
+    converted   = true;
   }
+
   return converted;
 }
 
 bool Property::Value::Get(Vector4& vectorValue) const
 {
   bool converted = false;
-  if(mImpl)
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == VECTOR4)
   {
-    if(mImpl->GetType() == VECTOR4)
-    {
-      vectorValue = mImpl->GetVector4();
-      converted   = true;
-    }
-    else if(mImpl->GetType() == VECTOR2)
-    {
-      vectorValue = mImpl->GetVector2();
-      converted   = true;
-    }
-    else if(mImpl->GetType() == VECTOR3)
-    {
-      vectorValue = mImpl->GetVector3();
-      converted   = true;
-    }
+    vectorValue = obj.GetVector4();
+    converted   = true;
   }
+  else if(obj.GetType() == VECTOR2)
+  {
+    vectorValue = obj.GetVector2();
+    converted   = true;
+  }
+  else if(obj.GetType() == VECTOR3)
+  {
+    vectorValue = obj.GetVector3();
+    converted   = true;
+  }
+
   return converted;
 }
 
 bool Property::Value::Get(Matrix3& matrixValue) const
 {
   bool converted = false;
-  if(mImpl && (mImpl->GetType() == MATRIX3))
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == MATRIX3)
   {
-    matrixValue = mImpl->GetMatrix3();
+    matrixValue = obj.GetMatrix3();
     converted   = true;
   }
   return converted;
@@ -926,9 +986,12 @@ bool Property::Value::Get(Matrix3& matrixValue) const
 bool Property::Value::Get(Matrix& matrixValue) const
 {
   bool converted = false;
-  if(mImpl && (mImpl->GetType() == MATRIX))
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == MATRIX)
   {
-    matrixValue = mImpl->GetMatrix();
+    matrixValue = obj.GetMatrix();
     converted   = true;
   }
   return converted;
@@ -937,9 +1000,12 @@ bool Property::Value::Get(Matrix& matrixValue) const
 bool Property::Value::Get(Rect<int32_t>& rectValue) const
 {
   bool converted = false;
-  if(mImpl && (mImpl->GetType() == RECTANGLE))
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == RECTANGLE)
   {
-    rectValue = mImpl->GetRect();
+    rectValue = obj.GetRect();
     converted = true;
   }
   return converted;
@@ -948,9 +1014,12 @@ bool Property::Value::Get(Rect<int32_t>& rectValue) const
 bool Property::Value::Get(AngleAxis& angleAxisValue) const
 {
   bool converted = false;
-  if(mImpl && (mImpl->GetType() == ROTATION))
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == ROTATION)
   {
-    angleAxisValue = mImpl->GetAngleAxis();
+    angleAxisValue = obj.GetAngleAxis();
     converted      = true;
   }
   return converted;
@@ -959,10 +1028,13 @@ bool Property::Value::Get(AngleAxis& angleAxisValue) const
 bool Property::Value::Get(Quaternion& quaternionValue) const
 {
   bool converted = false;
-  if(mImpl && (mImpl->GetType() == ROTATION))
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == ROTATION)
   {
-    auto& obj       = mImpl->GetAngleAxis();
-    quaternionValue = Quaternion(obj.angle, obj.axis);
+    auto& angleAxis = obj.GetAngleAxis();
+    quaternionValue = Quaternion(angleAxis.angle, angleAxis.axis);
     converted       = true;
   }
   return converted;
@@ -971,9 +1043,12 @@ bool Property::Value::Get(Quaternion& quaternionValue) const
 bool Property::Value::Get(std::string& stringValue) const
 {
   bool converted = false;
-  if(mImpl && (mImpl->GetType() == STRING))
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == STRING)
   {
-    stringValue.assign(mImpl->GetString());
+    stringValue.assign(obj.GetString());
     converted = true;
   }
   return converted;
@@ -982,9 +1057,12 @@ bool Property::Value::Get(std::string& stringValue) const
 bool Property::Value::Get(Property::Array& arrayValue) const
 {
   bool converted = false;
-  if(mImpl && (mImpl->GetType() == ARRAY))
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == ARRAY)
   {
-    arrayValue = mImpl->GetArray();
+    arrayValue = obj.GetArray();
     converted  = true;
   }
   return converted;
@@ -993,9 +1071,12 @@ bool Property::Value::Get(Property::Array& arrayValue) const
 bool Property::Value::Get(Property::Map& mapValue) const
 {
   bool converted = false;
-  if(mImpl && (mImpl->GetType() == MAP))
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == MAP)
   {
-    mapValue  = mImpl->GetMap();
+    mapValue  = obj.GetMap();
     converted = true;
   }
   return converted;
@@ -1003,38 +1084,44 @@ bool Property::Value::Get(Property::Map& mapValue) const
 
 Property::Array const* Property::Value::GetArray() const
 {
-  if(mImpl && (mImpl->GetType() == ARRAY))
+  const auto& obj = Read();
+
+  if(obj.GetType() == ARRAY)
   {
-    return mImpl->GetArrayPtr();
+    return obj.GetArrayPtr();
   }
   return nullptr;
 }
 
 Property::Array* Property::Value::GetArray()
 {
-  Property::Array* array = nullptr;
-  if(mImpl && (mImpl->GetType() == ARRAY)) // type cannot change in mImpl so array is allocated
+  auto& obj = Write();
+
+  if(obj.GetType() == ARRAY)
   {
-    array = mImpl->GetArrayPtr();
+    return obj.GetArrayPtr();
   }
-  return array;
+  return nullptr;
 }
 
 Property::Map const* Property::Value::GetMap() const
 {
-  Property::Map* map = nullptr;
-  if(mImpl && (mImpl->GetType() == MAP)) // type cannot change in mImpl so map is allocated
+  const auto& obj = Read();
+
+  if(obj.GetType() == MAP)
   {
-    map = mImpl->GetMapPtr();
+    return obj.GetMapPtr();
   }
-  return map;
+  return nullptr;
 }
 
 Property::Map* Property::Value::GetMap()
 {
-  if(mImpl && (mImpl->GetType() == MAP))
+  auto& obj = Write();
+
+  if(obj.GetType() == MAP)
   {
-    return mImpl->GetMapPtr();
+    return obj.GetMapPtr();
   }
   return nullptr;
 }
@@ -1042,112 +1129,107 @@ Property::Map* Property::Value::GetMap()
 bool Property::Value::Get(Extents& extentsValue) const
 {
   bool converted = false;
-  if(mImpl)
+
+  const auto& obj = Read();
+
+  if(obj.GetType() == EXTENTS)
   {
-    if(mImpl->GetType() == EXTENTS)
-    {
-      extentsValue = mImpl->GetExtents();
-      converted    = true;
-    }
-    else if(mImpl->GetType() == VECTOR4)
-    {
-      auto& obj           = mImpl->GetVector4();
-      extentsValue.start  = static_cast<uint16_t>(obj.x);
-      extentsValue.end    = static_cast<uint16_t>(obj.y);
-      extentsValue.top    = static_cast<uint16_t>(obj.z);
-      extentsValue.bottom = static_cast<uint16_t>(obj.w);
-      converted           = true;
-    }
+    extentsValue = obj.GetExtents();
+    converted    = true;
+  }
+  else if(obj.GetType() == VECTOR4)
+  {
+    auto& vec4          = obj.GetVector4();
+    extentsValue.start  = static_cast<uint16_t>(vec4.x);
+    extentsValue.end    = static_cast<uint16_t>(vec4.y);
+    extentsValue.top    = static_cast<uint16_t>(vec4.z);
+    extentsValue.bottom = static_cast<uint16_t>(vec4.w);
+    converted           = true;
   }
+
   return converted;
 }
 
 std::ostream& operator<<(std::ostream& stream, const Property::Value& value)
 {
-  if(value.mImpl)
-  {
-    auto obj = value.mImpl;
+  const auto& obj = value.Read();
 
-    switch(obj->GetType())
+  switch(obj.GetType())
+  {
+    case Dali::Property::BOOLEAN:
     {
-      case Dali::Property::BOOLEAN:
-      {
-        stream << obj->GetBool();
-        break;
-      }
-      case Dali::Property::FLOAT:
-      {
-        stream << obj->GetFloat();
-        break;
-      }
-      case Dali::Property::INTEGER:
-      {
-        stream << obj->GetInt();
-        break;
-      }
-      case Dali::Property::VECTOR2:
-      {
-        stream << obj->GetVector2();
-        break;
-      }
-      case Dali::Property::VECTOR3:
-      {
-        stream << obj->GetVector3();
-        break;
-      }
-      case Dali::Property::VECTOR4:
-      {
-        stream << obj->GetVector4();
-        break;
-      }
-      case Dali::Property::MATRIX3:
-      {
-        stream << obj->GetMatrix3();
-        break;
-      }
-      case Dali::Property::MATRIX:
-      {
-        stream << obj->GetMatrix();
-        break;
-      }
-      case Dali::Property::RECTANGLE:
-      {
-        stream << obj->GetRect();
-        break;
-      }
-      case Dali::Property::ROTATION:
-      {
-        stream << obj->GetAngleAxis();
-        break;
-      }
-      case Dali::Property::STRING:
-      {
-        stream << obj->GetString();
-        break;
-      }
-      case Dali::Property::ARRAY:
-      {
-        stream << obj->GetArray();
-        break;
-      }
-      case Dali::Property::MAP:
-      {
-        stream << obj->GetMap();
-        break;
-      }
-      case Dali::Property::EXTENTS:
-      {
-        stream << obj->GetExtents();
-        break;
-      }
-      case Dali::Property::NONE:
-      { // mImpl will be a nullptr, there's no way to get to this case
-      }
+      stream << obj.GetBool();
+      break;
+    }
+    case Dali::Property::FLOAT:
+    {
+      stream << obj.GetFloat();
+      break;
+    }
+    case Dali::Property::INTEGER:
+    {
+      stream << obj.GetInt();
+      break;
+    }
+    case Dali::Property::VECTOR2:
+    {
+      stream << obj.GetVector2();
+      break;
+    }
+    case Dali::Property::VECTOR3:
+    {
+      stream << obj.GetVector3();
+      break;
+    }
+    case Dali::Property::VECTOR4:
+    {
+      stream << obj.GetVector4();
+      break;
+    }
+    case Dali::Property::MATRIX3:
+    {
+      stream << obj.GetMatrix3();
+      break;
+    }
+    case Dali::Property::MATRIX:
+    {
+      stream << obj.GetMatrix();
+      break;
+    }
+    case Dali::Property::RECTANGLE:
+    {
+      stream << obj.GetRect();
+      break;
+    }
+    case Dali::Property::ROTATION:
+    {
+      stream << obj.GetAngleAxis();
+      break;
+    }
+    case Dali::Property::STRING:
+    {
+      stream << obj.GetString();
+      break;
+    }
+    case Dali::Property::ARRAY:
+    {
+      stream << obj.GetArray();
+      break;
+    }
+    case Dali::Property::MAP:
+    {
+      stream << obj.GetMap();
+      break;
+    }
+    case Dali::Property::EXTENTS:
+    {
+      stream << obj.GetExtents();
+      break;
+    }
+    case Dali::Property::NONE:
+    {
+      stream << "undefined type";
     }
-  }
-  else
-  {
-    stream << "undefined type";
   }
   return stream;
 }
index d9e6f7d..f9d6269 100644 (file)
@@ -518,8 +518,31 @@ public:
   friend DALI_CORE_API std::ostream& operator<<(std::ostream& ouputStream, const Property::Value& value);
 
 private:
+  /// @cond internal
   struct DALI_INTERNAL Impl;
-  Impl*                mImpl; ///< Pointer to the implementation
+
+  /**
+   * @brief Retrieves an already constructed Impl object from the storage buffer.
+   * @return A const reference to the Impl object
+   */
+  DALI_INTERNAL const Impl& Read() const;
+
+  /**
+   * @brief Retrieves an already constructed Impl object from the storage buffer.
+   * @return A non const reference to the Impl object
+   */
+  DALI_INTERNAL Impl& Write();
+
+  /**
+   * @brief An aligned storage buffer to create Impl object inplace.
+   */
+  struct DALI_INTERNAL Storage
+  {
+    alignas(sizeof(Impl*)) unsigned char buffer[16];
+  };
+
+  Storage mStorage;
+  /// @endcond
 };
 
 /**
index 25b5042..18d3582 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2
 Summary:    DALi 3D Engine
-Version:    1.9.32
+Version:    1.9.33
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT