Merge branch 'devel/master' into tizen
authorJoogab Yun <joogab.yun@samsung.com>
Mon, 8 Jul 2019 07:16:23 +0000 (16:16 +0900)
committerJoogab Yun <joogab.yun@samsung.com>
Mon, 8 Jul 2019 07:16:23 +0000 (16:16 +0900)
22 files changed:
automated-tests/src/dali/CMakeLists.txt
automated-tests/src/dali/utc-Dali-Handle.cpp
automated-tests/src/dali/utc-Dali-KeyEvent.cpp
automated-tests/src/dali/utc-Dali-Scene.cpp
automated-tests/src/dali/utc-Dali-Texture.cpp
automated-tests/src/dali/utc-Dali-ThreadPool.cpp [new file with mode: 0644]
dali/devel-api/CMakeLists.txt
dali/devel-api/events/key-event-devel.cpp [new file with mode: 0644]
dali/devel-api/events/key-event-devel.h [new file with mode: 0644]
dali/devel-api/file.list
dali/devel-api/object/handle-devel.cpp
dali/devel-api/object/handle-devel.h
dali/devel-api/threading/thread-pool.cpp [new file with mode: 0644]
dali/devel-api/threading/thread-pool.h [new file with mode: 0644]
dali/internal/event/common/object-impl.cpp
dali/internal/event/common/object-impl.h
dali/internal/render/renderers/render-property-buffer.h
dali/internal/update/animation/scene-graph-animator.h
dali/internal/update/manager/update-manager.cpp
dali/public-api/common/dali-common.cpp
dali/public-api/dali-core-version.cpp
packaging/dali.spec

index 8267394..17c4767 100644 (file)
@@ -93,6 +93,7 @@ SET(TC_SOURCES
         utc-Dali-Texture.cpp
         utc-Dali-TextureSet.cpp
         utc-Dali-Thread.cpp
+        utc-Dali-ThreadPool.cpp
         utc-Dali-TouchEventCombiner.cpp
         utc-Dali-TouchProcessing.cpp
         utc-Dali-TouchDataProcessing.cpp
index 6c6c607..57a9b78 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2019 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.
@@ -1666,3 +1666,113 @@ int UtcDaliHandlePropertySetSignal03(void)
   DALI_TEST_EQUALS( propertySetCheck.mValue, Property::Value( 29 ), TEST_LOCATION );
   END_TEST;
 }
+
+int UtcDaliHandlePropertySetProperties(void)
+{
+  TestApplication application;
+  const Vector3 actorSize( 10.0f, 20.0f, 30.0f );
+  const Vector3 anchorPoint( 1.0f, 0.5f, 0.0f );
+  const Vector4 color( 0.1f, 0.2, 0.3f, 0.4f );
+
+  Handle handle = Actor::New();
+  DevelHandle::SetProperties(
+    handle,
+    Property::Map
+    {
+      { Actor::Property::SIZE, actorSize },
+      { Actor::Property::ANCHOR_POINT, anchorPoint },
+      { "color", color },
+      { "invalid", Vector2::ZERO } // It should quietly ignore invalid data
+    }
+  );
+  DALI_TEST_EQUALS( handle.GetProperty( Actor::Property::SIZE ).Get< Vector3 >(), actorSize, TEST_LOCATION );
+  DALI_TEST_EQUALS( handle.GetProperty( Actor::Property::ANCHOR_POINT ).Get< Vector3 >(), anchorPoint, TEST_LOCATION );
+  DALI_TEST_EQUALS( handle.GetProperty( Actor::Property::COLOR ).Get< Vector4 >(), color, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliHandleTemplateNew(void)
+{
+  TestApplication application;
+  const Vector3 actorSize( 10.0f, 20.0f, 30.0f );
+  const Vector3 anchorPoint( 1.0f, 0.5f, 0.0f );
+  const Vector4 color( 0.1f, 0.2, 0.3f, 0.4f );
+
+  Handle handle = DevelHandle::New< Actor >(
+    Property::Map
+    {
+      { Actor::Property::SIZE, actorSize },
+      { Actor::Property::ANCHOR_POINT, anchorPoint },
+      { "color", color },
+      { "invalid", Vector2::ZERO } // It should quietly ignore invalid data
+    }
+  );
+
+  DALI_TEST_EQUALS( handle.GetProperty( Actor::Property::SIZE ).Get< Vector3 >(), actorSize, TEST_LOCATION );
+  DALI_TEST_EQUALS( handle.GetProperty( Actor::Property::ANCHOR_POINT ).Get< Vector3 >(), anchorPoint, TEST_LOCATION );
+  DALI_TEST_EQUALS( handle.GetProperty( Actor::Property::COLOR ).Get< Vector4 >(), color, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliHandleGetProperties(void)
+{
+  TestApplication application;
+
+  Handle handle = Actor::New();
+  DevelHandle::SetProperties(
+    handle,
+    Property::Map
+    {
+      { Actor::Property::SIZE, Vector3( 400.0f, 200.0f, 100.0f ) },
+      { Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_CENTER },
+      { Actor::Property::PARENT_ORIGIN, ParentOrigin::BOTTOM_CENTER },
+      { Actor::Property::NAME, "Actor" },
+      { Actor::Property::LEAVE_REQUIRED, true },
+      { "color", Color::RED },
+    }
+  );
+
+  Property::Map map;
+  DevelHandle::GetProperties( handle, map );
+
+  // Get all the properties and ensure they match
+
+  DALI_TEST_EQUALS( handle.GetPropertyCount(), map.Count(), TEST_LOCATION );
+
+  for( auto position = 0u; position < map.Count(); ++position )
+  {
+    auto keyValuePair = map.GetKeyValue( position );
+    const auto& index = keyValuePair.first.indexKey;
+    const auto& value = keyValuePair.second;
+    auto handleValue = handle.GetProperty( index );
+
+    switch( value.GetType() )
+    {
+      case Property::NONE:       break;
+      case Property::BOOLEAN:    DALI_TEST_EQUALS( value.Get< bool >(), handleValue.Get< bool >(), TEST_LOCATION ); break;
+      case Property::FLOAT:      DALI_TEST_EQUALS( value.Get< float >(), handleValue.Get< float >(), TEST_LOCATION ); break;
+      case Property::INTEGER:    DALI_TEST_EQUALS( value.Get< int >(), handleValue.Get< int >(), TEST_LOCATION ); break;
+      case Property::VECTOR2:    DALI_TEST_EQUALS( value.Get< Vector2 >(), handleValue.Get< Vector2 >(), TEST_LOCATION ); break;
+      case Property::VECTOR3:    DALI_TEST_EQUALS( value.Get< Vector3 >(), handleValue.Get< Vector3 >(), TEST_LOCATION ); break;
+      case Property::VECTOR4:    DALI_TEST_EQUALS( value.Get< Vector4 >(), handleValue.Get< Vector4 >(), TEST_LOCATION ); break;
+      case Property::MATRIX3:    DALI_TEST_EQUALS( value.Get< Matrix3 >(), handleValue.Get< Matrix3 >(), TEST_LOCATION ); break;
+      case Property::MATRIX:     DALI_TEST_EQUALS( value.Get< Matrix >(), handleValue.Get< Matrix >(), TEST_LOCATION ); break;
+      case Property::RECTANGLE:  DALI_TEST_EQUALS( value.Get< Rect< int > >(), handleValue.Get< Rect< int > >(), TEST_LOCATION ); break;
+      case Property::ROTATION:   DALI_TEST_EQUALS( value.Get< Quaternion >(), handleValue.Get< Quaternion >(), TEST_LOCATION ); break;
+      case Property::STRING:     DALI_TEST_EQUALS( value.Get< std::string >(), handleValue.Get< std::string >(), TEST_LOCATION ); break;
+      case Property::ARRAY:      DALI_TEST_EQUALS( value.GetArray()->Count(), handleValue.GetArray()->Count(), TEST_LOCATION ); break;
+      case Property::MAP:        DALI_TEST_EQUALS( value.GetMap()->Count(), handleValue.GetMap()->Count(), TEST_LOCATION ); break;
+      case Property::EXTENTS:    DALI_TEST_EQUALS( value.Get< Extents >(), handleValue.Get< Extents >(), TEST_LOCATION ); break;
+    }
+  }
+
+  // Add a custom property and ensure the count goes up by one.
+  const auto countBefore = map.Count();
+  handle.RegisterProperty( "tempProperty", Color::GREEN );
+  DevelHandle::GetProperties( handle, map );
+  DALI_TEST_EQUALS( countBefore + 1, map.Count(), TEST_LOCATION );
+
+  END_TEST;
+}
index e2de6e5..bdc7d4a 100755 (executable)
@@ -20,6 +20,7 @@
 #include <stdlib.h>
 #include <dali/public-api/dali-core.h>
 #include <dali/integration-api/events/key-event-integ.h>
+#include <dali/devel-api/events/key-event-devel.h>
 
 #include <dali-test-suite-utils.h>
 
@@ -400,3 +401,14 @@ int UtcDaliKeyEventSetDeviceSubclass(void)
 
   END_TEST;
 }
+
+int UtcDaliKeyEventSetLogicalKey(void)
+{
+  TestApplication application;
+
+  KeyEvent event(TEST_STRING_1,"i", 99, SHIFT_MODIFIER, 0lu, KeyEvent::Down);
+
+  DALI_TEST_EQUALS( DevelKeyEvent::GetLogicalKey( event ), "", TEST_LOCATION );
+
+  END_TEST;
+}
index 2b78fea..5f9e9d2 100644 (file)
@@ -426,6 +426,63 @@ int UtcDaliSceneCreateNewSceneDuringCoreEventProcessing(void)
   END_TEST;
 }
 
+int UtcDaliSceneRootLayerAndSceneAlignment(void)
+{
+  TestApplication application;
+
+  // Create a Scene
+  Dali::Integration::Scene scene = Dali::Integration::Scene::New( Vector2( 480.0f, 800.0f ) );
+  DALI_TEST_CHECK( scene );
+
+  // One reference of scene kept here and the other one kept in the Core
+  DALI_TEST_CHECK( scene.GetBaseObject().ReferenceCount() == 2 );
+
+  // Render and notify.
+  application.SendNotification();
+  application.Render(0);
+
+  // Keep the reference of the root layer handle so it will still be alive after the scene is deleted
+  Layer rootLayer = scene.GetRootLayer();
+  DALI_TEST_CHECK( rootLayer );
+  DALI_TEST_CHECK( rootLayer.GetBaseObject().ReferenceCount() == 2 );
+
+  // Request to discard the scene from the Core
+  scene.Discard();
+  DALI_TEST_CHECK( scene.GetBaseObject().ReferenceCount() == 1 );
+
+  // Reset the scene handle
+  scene.Reset();
+
+  // Render and notify.
+  application.SendNotification();
+  application.Render(0);
+
+  // At this point, the scene should have been automatically deleted
+  // To prove this, the ref count of the root layer handle should be decremented to 1
+  DALI_TEST_CHECK( rootLayer.GetBaseObject().ReferenceCount() == 1 );
+
+  // Create a new Scene while the root layer of the deleted scene is still alive
+  Dali::Integration::Scene newScene = Dali::Integration::Scene::New( Vector2( 480.0f, 800.0f ) );
+  DALI_TEST_CHECK( newScene );
+
+  // Render and notify.
+  application.SendNotification();
+  application.Render(0);
+
+  // At this point, we have only one scene but two root layers
+  // The root layer of the deleted scene is still alive
+  DALI_TEST_CHECK( rootLayer.GetBaseObject().ReferenceCount() == 1 );
+
+  // Delete the root layer of the deleted scene
+  rootLayer.Reset();
+
+  // Render and notify.
+  application.SendNotification();
+  application.Render(0);
+
+  END_TEST;
+}
+
 int UtcDaliSceneEventProcessingFinishedP(void)
 {
   TestApplication application;
index 30d0a7f..7b2152b 100644 (file)
@@ -509,6 +509,8 @@ int UtcDaliTextureUpload05(void)
       out << GL_TEXTURE_2D <<", "<< 0u << ", " << width/2 << ", " <<  height/2 << ", " << width/2 << ", " <<  height/2;
       DALI_TEST_CHECK( callStack.FindMethodAndParams("CompressedTexSubImage2D", out.str().c_str() ) );
     }
+
+    application.GetGlAbstraction().ResetTextureCallStack();
   }
 
   END_TEST;
diff --git a/automated-tests/src/dali/utc-Dali-ThreadPool.cpp b/automated-tests/src/dali/utc-Dali-ThreadPool.cpp
new file mode 100644 (file)
index 0000000..c7c6335
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2017 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 <iostream>
+#include <stdlib.h>
+#include <unistd.h>
+#include <type_traits>
+#include <dali-test-suite-utils.h>
+#include <dali/devel-api/threading/thread-pool.h>
+
+namespace
+{
+Dali::ThreadPool gThreadPool;
+
+// Helper function dividing workload into N batches
+// the loop lambda contains
+Dali::UniqueFutureGroup ForEachMT( Dali::ThreadPool* pThreadPool,
+                                   uint32_t first,
+                                   uint32_t size,
+                                   std::function<void( uint32_t, uint32_t, uint32_t )> task )
+{
+  uint32_t i = 0;
+  uint32_t j = 0;
+  const auto workerCount = uint32_t(pThreadPool->GetWorkerCount());
+  const auto step = size / workerCount;
+  j = workerCount + step;
+
+  std::vector<Dali::Task> tasks;
+  tasks.reserve( workerCount );
+
+  for( auto threadIndex = 0u; threadIndex < workerCount; ++threadIndex )
+  {
+    Dali::Task lambda = [task, i, j]( int workerIndex )
+    {
+      task( uint32_t(workerIndex), i, j );
+    };
+    tasks.emplace_back( lambda );
+    i = j;
+    j = i + step;
+    if( j > size )
+      j = size;
+  }
+  return pThreadPool->SubmitTasks( tasks, workerCount );
+}
+
+}
+
+int UtcDaliThreadPoolMultipleTasks(void)
+{
+  // initialise global thread pool
+  if( !gThreadPool.GetWorkerCount() )
+  {
+    gThreadPool.Initialize( 0u );
+  }
+
+  // populate inputs
+  std::array<int, 8192> inputs;
+  int checksum = 0;
+  for( auto i = 0; i < decltype(i)(inputs.size()); ++i )
+  {
+    inputs[i] = i;
+    checksum += i;
+  }
+
+  // allocate outputs ( number of outputs equals number of worker threads
+  auto workerCount = gThreadPool.GetWorkerCount();
+
+  std::vector<int> outputs;
+  outputs.resize( workerCount );
+  std::fill( outputs.begin(), outputs.end(), 0 );
+
+  // submit
+  auto future = ForEachMT( &gThreadPool, 0, inputs.size(), [&inputs, &outputs]( uint32_t workerIndex, uint32_t begin, uint32_t end )
+  {
+    for( auto i = begin; i < end; ++i )
+    {
+      outputs[workerIndex] += inputs[i];
+    }
+  });
+
+  future->Wait();
+
+  // check outputs
+  int checksum2 = 0;
+  for( auto output : outputs )
+  {
+    checksum2 += output;
+  }
+
+  printf("sum: %d, sum2: %d\n", checksum, checksum2);
+
+
+  DALI_TEST_EQUALS( checksum, checksum2, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliThreadPoolSingleTask(void)
+{
+  // initialise global thread pool
+  if( !gThreadPool.GetWorkerCount() )
+  {
+    gThreadPool.Initialize( 0u );
+  }
+
+  // some long lasting task
+  int counter = 0;
+  auto task = [&counter]( int workerIndex ){
+    for( int i = 0; i < 10; ++i )
+    {
+      counter++;
+      usleep( 16 * 1000 );
+    }
+  };
+
+  auto future = gThreadPool.SubmitTask( 0, task );
+  future->Wait();
+  DALI_TEST_EQUALS( counter, 10, TEST_LOCATION );
+
+  END_TEST;
+}
+
+int UtcDaliThreadPoolSubmitTasksCopyArray(void)
+{
+  // initialise global thread pool
+  if( !gThreadPool.GetWorkerCount() )
+  {
+    gThreadPool.Initialize( 0u );
+  }
+
+  std::array<uint8_t, 1024*1024> dataSrc;
+  for( auto i = 0; i < decltype(i)(dataSrc.size()); ++i)
+  {
+    dataSrc[i] = (std::rand() % 0xff);
+  }
+
+  std::array<uint8_t, 1024*1024> dataDst;
+
+  // each task copies 1kb od data
+  std::vector<Dali::Task> tasks;
+  for( int i = 0; i < 1024; ++i )
+  {
+    auto task = [&dataSrc, &dataDst, i ]( int workerIndex )
+    {
+      for( int k = 0; k < 1024; ++k )
+      {
+        dataDst[i*1024+k] = dataSrc[i*1024+k];
+      }
+    };
+    tasks.push_back( task );
+  }
+
+  DALI_TEST_EQUALS( 1024, tasks.size(), TEST_LOCATION );
+
+  gThreadPool.SubmitTasks( tasks );
+
+  // wait for pool to finish
+  gThreadPool.Wait();
+
+  // compare arrays
+  for( auto i = 0; i < decltype(i)(dataSrc.size()); ++i )
+  {
+    DALI_TEST_EQUALS( dataSrc[i], dataDst[i], TEST_LOCATION );
+    if( dataSrc[i] != dataDst[i]  )
+    {
+      break;
+    }
+  }
+
+  END_TEST;
+}
\ No newline at end of file
index 0aca8ad..c9c559e 100644 (file)
@@ -8,6 +8,7 @@ SET( SOURCES ${SOURCES}
   ${CMAKE_CURRENT_SOURCE_DIR}/common/hash.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/common/stage-devel.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/events/hit-test-algorithm.cpp
+  ${CMAKE_CURRENT_SOURCE_DIR}/events/key-event-devel.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/events/touch-data-devel.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/images/distance-field.cpp
   ${CMAKE_CURRENT_SOURCE_DIR}/images/texture-set-image.cpp
@@ -44,6 +45,7 @@ SET( DEVEL_API_HEADERS
   ${CMAKE_CURRENT_SOURCE_DIR}/common/stage-devel.h
 
   ${CMAKE_CURRENT_SOURCE_DIR}/events/hit-test-algorithm.h
+  ${CMAKE_CURRENT_SOURCE_DIR}/events/key-event-devel.h
   ${CMAKE_CURRENT_SOURCE_DIR}/events/touch-data-devel.h
 
   ${CMAKE_CURRENT_SOURCE_DIR}/images/distance-field.h
diff --git a/dali/devel-api/events/key-event-devel.cpp b/dali/devel-api/events/key-event-devel.cpp
new file mode 100644 (file)
index 0000000..502404e
--- /dev/null
@@ -0,0 +1,36 @@
+/*\r
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ */\r
+\r
+// INTERNAL INCLUDES\r
+#include <dali/devel-api/events/key-event-devel.h>\r
+#include <dali/internal/event/events/key-event-impl.h>\r
+\r
+namespace Dali\r
+{\r
+\r
+namespace DevelKeyEvent\r
+{\r
+\r
+std::string GetLogicalKey( KeyEvent keyEvent )\r
+{\r
+  return GetImplementation( &keyEvent )->GetLogicalKey();\r
+}\r
+\r
+} // namespace DevelKeyEvent\r
+\r
+} // namespace Dali\r
+\r
diff --git a/dali/devel-api/events/key-event-devel.h b/dali/devel-api/events/key-event-devel.h
new file mode 100644 (file)
index 0000000..bdf1680
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef DALI_KEY_EVENT_DEVEL_H\r
+#define DALI_KEY_EVENT_DEVEL_H\r
+\r
+/*\r
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ */\r
+\r
+// INTERNAL INCLUDES\r
+#include <dali/public-api/events/key-event.h>\r
+\r
+namespace Dali\r
+{\r
+\r
+namespace DevelKeyEvent\r
+{\r
+\r
+/**\r
+ * @brief Gets the logical key string.\r
+ *\r
+ * For example, when the user presses 'shift' key and '1' key together, the logical key is "exclamation".\r
+ * Plus, the keyPressedName is "1", and the keyPressed is "!".\r
+ *\r
+ * @param[in] keyEvent The instance of KeyEvent.\r
+ * @return The logical key symbol\r
+ */\r
+DALI_CORE_API std::string GetLogicalKey( KeyEvent keyEvent );\r
+\r
+} // namespace DevelKeyEvent\r
+\r
+} // namespace Dali\r
+\r
+#endif // DALI_KEY_EVENT_DEVEL_H\r
index bd74e49..42b3256 100755 (executable)
@@ -9,6 +9,7 @@ devel_api_src_files = \
   $(devel_api_src_dir)/common/stage-devel.cpp \
   $(devel_api_src_dir)/events/hit-test-algorithm.cpp \
   $(devel_api_src_dir)/events/touch-data-devel.cpp \
+  $(devel_api_src_dir)/events/key-event-devel.cpp \
   $(devel_api_src_dir)/images/distance-field.cpp \
   $(devel_api_src_dir)/images/texture-set-image.cpp \
   $(devel_api_src_dir)/images/nine-patch-image.cpp \
@@ -20,6 +21,7 @@ devel_api_src_files = \
   $(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-pool.cpp \
   $(devel_api_src_dir)/update/frame-callback-interface.cpp \
   $(devel_api_src_dir)/update/update-proxy.cpp
 
@@ -47,7 +49,8 @@ devel_api_core_common_header_files = \
 
 devel_api_core_events_header_files = \
   $(devel_api_src_dir)/events/hit-test-algorithm.h \
-  $(devel_api_src_dir)/events/touch-data-devel.h
+  $(devel_api_src_dir)/events/touch-data-devel.h \
+  $(devel_api_src_dir)/events/key-event-devel.h
 
 devel_api_core_images_header_files = \
   $(devel_api_src_dir)/images/distance-field.h \
@@ -75,7 +78,8 @@ devel_api_core_scripting_header_files = \
 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_src_dir)/threading/thread.h \
+  $(devel_api_src_dir)/threading/thread-pool.h
 
 devel_api_core_update_header_files = \
   $(devel_api_src_dir)/update/frame-callback-interface.h \
index f5e8951..6a01e45 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2019 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.
@@ -44,6 +44,16 @@ Property::Index RegisterProperty( Handle handle, Property::Index key, const std:
   return GetImplementation( handle ).RegisterProperty( name, key, propertyValue );
 }
 
+void SetProperties( Handle handle, const Property::Map& properties )
+{
+  GetImplementation( handle ).SetProperties( properties );
+}
+
+void GetProperties( Handle handle, Property::Map& properties )
+{
+  GetImplementation( handle ).GetProperties( properties );
+}
+
 void SetTypeInfo( Handle& handle, const TypeInfo& typeInfo )
 {
   GetImplementation( handle ).SetTypeInfo( &GetImplementation( typeInfo ) );
index e8d3803..df6a4d6 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_HANDLE_DEVEL_H
 
 /*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2019 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.
@@ -99,6 +99,24 @@ DALI_CORE_API Property::Index GetPropertyIndex( const Handle& handle, Property::
 DALI_CORE_API Property::Index RegisterProperty( Handle handle, Property::Index key, const std::string& name, const Property::Value& propertyValue );
 
 /**
+ * @brief Sets all the properties in the given property map.
+ *
+ * @param[in] handle The handle to set the properties on
+ * @param[in] properties The properties to set
+ */
+DALI_CORE_API void SetProperties( Handle handle, const Property::Map& properties );
+
+/**
+ * @brief Retrieves all the properties and the values for a handle.
+ *
+ * @param[in] handle The handle to retrieve properties from
+ * @param[out] properties A map which is populated with the index-value pairs
+ *
+ * @note The properties map will be cleared by this method first.
+ */
+DALI_CORE_API void GetProperties( Handle handle, Property::Map& properties );
+
+/**
  * @brief Set the type-info that the object is created by.
  *
  * @note This is particularly useful to link C# custom control with its correct type-info in the native side
@@ -131,6 +149,20 @@ using PropertySetSignalType = Signal< void( Handle& handle, Property::Index inde
  */
 DALI_CORE_API PropertySetSignalType& PropertySetSignal( Handle handle );
 
+/**
+ * @brief Template to create a derived handle and set properties on it.
+ *
+ * @tparam T The derived class to create
+ *
+ * @param[in] properties The properties to set
+ */
+template< typename T >
+DALI_INTERNAL T New( const Property::Map& properties )
+{
+  T handle = T::New();
+  SetProperties( handle, properties );
+  return handle;
+}
 
 } // namespace DevelHandle
 
diff --git a/dali/devel-api/threading/thread-pool.cpp b/dali/devel-api/threading/thread-pool.cpp
new file mode 100644 (file)
index 0000000..bf55715
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * 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 "thread-pool.h"
+#include <cmath>
+
+
+
+namespace Dali
+{
+namespace
+{
+template<typename T, typename... Args>
+std::unique_ptr<T> make_unique(Args&&... args)
+{
+  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+}
+
+/**
+ * WorkerThread executes tasks submitted to the pool
+ */
+class WorkerThread
+{
+public:
+
+  /**
+   * @brief Constructor of worker thread
+   * @param index Thread index assigned to the object during pool initialisation
+   */
+  explicit WorkerThread( uint32_t index );
+
+  /**
+   * @brief Destructor of the worker thread
+   */
+  ~WorkerThread();
+
+  WorkerThread(const WorkerThread &other) = delete;
+  WorkerThread &operator=(const WorkerThread &other) = delete;
+
+  /**
+   * @brief Adds task to the task queue
+   * @param task Task to be executed by the thread
+   */
+  void AddTask( Task task );
+
+  /**
+   * @brief Wakes up thread.
+   */
+  void Notify();
+
+  /**
+   * @brief Waits for the thread to complete all the tasks currently in the queue.
+   */
+  void Wait();
+
+private:
+
+  /**
+   * @brief Internal thread loop function
+   */
+  void WaitAndExecute();
+
+  std::thread   mWorker;
+  uint32_t      mIndex;
+  TaskQueue     mTaskQueue;
+  std::mutex    mTaskQueueMutex;
+  std::condition_variable mConditionVariable;
+
+  bool mTerminating {false} ;
+};
+
+void WorkerThread::WaitAndExecute()
+{
+  while( true )
+  {
+    Task task;
+
+    {
+      std::unique_lock< std::mutex > lock{ mTaskQueueMutex };
+
+      mConditionVariable.wait( lock, [ this ]() -> bool {
+        return !mTaskQueue.empty() || mTerminating;
+      } );
+
+      if( mTerminating )
+      {
+        break;
+      }
+
+      task = mTaskQueue.front();
+    }
+
+    task( mIndex );
+
+    {
+      std::lock_guard< std::mutex > lock{ mTaskQueueMutex };
+
+      mTaskQueue.pop();
+
+      mConditionVariable.notify_one();
+    }
+  }
+}
+
+WorkerThread::WorkerThread(uint32_t index) : mIndex( index )
+{
+  // Have to pass "this" as an argument because WaitAndExecute is a member function.
+  mWorker = std::thread{ &WorkerThread::WaitAndExecute, this };
+}
+
+WorkerThread::~WorkerThread()
+{
+  if( mWorker.joinable() )
+  {
+    Notify();
+    Wait();
+
+    {
+      std::lock_guard< std::mutex > lock{ mTaskQueueMutex };
+      mTerminating = true;
+      mConditionVariable.notify_one();
+    }
+
+    mWorker.join();
+  }
+}
+
+void WorkerThread::AddTask( Task task )
+{
+  std::lock_guard< std::mutex > lock{ mTaskQueueMutex };
+  mTaskQueue.push( std::move( task ) );
+  mConditionVariable.notify_one();
+}
+
+void WorkerThread::Notify()
+{
+  std::lock_guard< std::mutex > lock{ mTaskQueueMutex };
+  mConditionVariable.notify_one();
+}
+
+void WorkerThread::Wait()
+{
+  std::unique_lock< std::mutex > lock{ mTaskQueueMutex };
+  mConditionVariable.wait( lock, [ this ]() -> bool {
+    return mTaskQueue.empty();
+  } );
+}
+
+// ThreadPool -----------------------------------------------------------------------------------------------
+
+struct ThreadPool::Impl
+{
+  std::vector<std::unique_ptr<WorkerThread>> mWorkers;
+  uint32_t                                   mWorkerIndex{ 0u };
+};
+
+ThreadPool::ThreadPool()
+{
+  mImpl = make_unique<Impl>();
+}
+
+ThreadPool::~ThreadPool() = default;
+
+bool ThreadPool::Initialize( uint32_t threadCount )
+{
+  /**
+   * Get the system's supported thread count.
+   */
+  auto thread_count = threadCount + 1;
+  if( !threadCount )
+  {
+    thread_count = std::thread::hardware_concurrency();
+    if( !thread_count )
+    {
+      return false;
+    }
+  }
+
+  /**
+   * Spawn the worker threads.
+   */
+  for( auto i = 0u; i < thread_count - 1; i++ )
+  {
+    /**
+    * The workers will execute an infinite loop function
+    * and will wait for a job to enter the job queue. Once a job is in the the queue
+    * the threads will wake up to acquire and execute it.
+    */
+    mImpl->mWorkers.push_back( make_unique< WorkerThread >( i ) );
+  }
+
+  return true;
+}
+
+
+void ThreadPool::Wait()
+{
+  for( auto& worker : mImpl->mWorkers )
+  {
+    worker->Wait();
+  }
+}
+
+SharedFuture ThreadPool::SubmitTask( uint32_t workerIndex, const Task& task )
+{
+  auto future = std::shared_ptr< Future< void > >( new Future< void > );
+  mImpl->mWorkers[workerIndex]->AddTask( [task, future]( uint32_t index )
+                                  {
+                                    task( index );
+
+                                    future->mPromise.set_value();
+                                  });
+
+  return future;
+}
+
+SharedFuture ThreadPool::SubmitTasks( const std::vector< Task >& tasks )
+{
+  auto future = std::shared_ptr< Future< void > >( new Future< void > );
+
+  mImpl->mWorkers[ mImpl->mWorkerIndex++ % static_cast< uint32_t >( mImpl->mWorkers.size() )]->AddTask(
+    [ future, tasks ]( uint32_t index ) {
+      for( auto& task : tasks )
+      {
+        task( index );
+      }
+
+      future->mPromise.set_value();
+
+    } );
+
+  return future;
+}
+
+UniqueFutureGroup ThreadPool::SubmitTasks( const std::vector< Task >& tasks, uint32_t threadMask )
+{
+  auto retval = make_unique<FutureGroup<void>>();
+
+  /**
+   * Use square root of number of sumbitted tasks to estimate optimal number of threads
+   * used to execute jobs
+   */
+  auto threads = uint32_t(std::log2(float(tasks.size())));
+
+  if( threadMask != 0 )
+  {
+    threads = threadMask;
+  }
+
+  if( threads > mImpl->mWorkers.size() )
+  {
+    threads = uint32_t(mImpl->mWorkers.size());
+  }
+  else if( !threads )
+  {
+    threads = 1;
+  }
+
+  auto payloadPerThread = uint32_t(tasks.size() / threads);
+  auto remaining = uint32_t(tasks.size() % threads);
+
+  uint32_t taskIndex = 0;
+  uint32_t taskSize = uint32_t(remaining + payloadPerThread); // add 'remaining' tasks to the very first job list
+
+  for( auto wt = 0u; wt < threads; ++wt )
+  {
+    auto future = std::shared_ptr< Future< void > >( new Future< void > );
+    retval->mFutures.emplace_back( future );
+    mImpl->mWorkers[ mImpl->mWorkerIndex++ % static_cast< uint32_t >( mImpl->mWorkers.size() )]->AddTask(
+      [ future, tasks, taskIndex, taskSize ]( uint32_t index ) {
+        auto begin = tasks.begin() + int(taskIndex);
+        auto end = begin + int(taskSize);
+        for( auto it = begin; it < end; ++it )
+        {
+          (*it)( index );
+        }
+        future->mPromise.set_value();
+      } );
+
+    taskIndex += taskSize;
+    taskSize = payloadPerThread;
+  }
+
+  return retval;
+}
+
+size_t ThreadPool::GetWorkerCount() const
+{
+  return mImpl->mWorkers.size();
+}
+
+} //namespace Dali
diff --git a/dali/devel-api/threading/thread-pool.h b/dali/devel-api/threading/thread-pool.h
new file mode 100644 (file)
index 0000000..b90fb28
--- /dev/null
@@ -0,0 +1,213 @@
+#ifndef DALI_THREAD_POOL_H
+#define DALI_THREAD_POOL_H
+
+/*
+ * Copyright (c) 2019 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 <dali/public-api/common/dali-common.h>
+
+// EXTERNAL INCLUDES
+#include <thread>
+#include <functional>
+#include <mutex>
+#include <queue>
+#include <condition_variable>
+#include <future>
+#include <algorithm>
+#include <iostream>
+#include <memory>
+
+namespace Dali
+{
+using Task = std::function<void(uint32_t)>;
+
+using TaskQueue = std::queue<Task>;
+
+/**
+ * Future contains the result of submitted task. When queried
+ * it applies internal synchronization mechanism to make sure
+ * the value is available.
+ */
+template<typename T>
+class DALI_INTERNAL Future final
+{
+  friend class ThreadPool;
+
+public:
+
+  /**
+   * @brief Constructor of Future
+   */
+  Future()
+  {
+    mFuture = mPromise.get_future();
+  }
+
+  /**
+   * @brief Destructor of Future
+   */
+  ~Future()
+  {
+    Wait();
+  }
+
+  /**
+   * @brief Returns value of future, blocks if needed.
+   * @return Value stored by the future
+   */
+  T Get() const
+  {
+    return mFuture.get();
+  }
+
+  /**
+   * @brief Waits until the value of future is ready. This function
+   * is a fencing mechanism.
+   */
+  void Wait() const
+  {
+    if( IsValid() )
+    {
+      mFuture.wait();
+    }
+  }
+
+  /**
+   * @brief Tests whether the future is valid
+   * @return True if valid, False otherwise
+   */
+  bool IsValid() const
+  {
+    return mFuture.valid();
+  }
+
+  /**
+   * @brief Resets the future bringing it to the initial state.
+   * It's required in order to reuse the same Future object.
+   */
+  void Reset()
+  {
+    mPromise = std::promise<T>();
+    mFuture  = mPromise.get_future();
+  }
+
+private:
+
+  std::promise<T> mPromise{};
+  std::future<T>  mFuture{};
+};
+
+using SharedFuture = std::shared_ptr<Future<void>>;
+
+/**
+ * FutureGroup binds many Future objects and applies synchronization.
+ */
+template<typename T>
+class FutureGroup final
+{
+  friend class ThreadPool;
+
+public:
+
+  /**
+   * Waits for all the Futures to complete.
+   */
+  void Wait()
+  {
+    for (auto &future : mFutures)
+    {
+      future->Wait();
+    }
+  }
+
+private:
+
+  std::vector<std::shared_ptr<Future<T> > > mFutures;
+};
+
+using UniqueFutureGroup = std::unique_ptr<FutureGroup<void>>;
+
+
+
+/**
+ * ThreadPool creates and manages worker threads and tasks submitted for execution.
+ */
+class DALI_CORE_API ThreadPool final
+{
+public:
+
+  /**
+   * @brief Constructor of thread pool.
+   */
+  ThreadPool();
+
+  /**
+   * @brief Destructor of thread pool.
+   */
+  ~ThreadPool();
+
+  /**
+   * @brief Intializes thread pool
+   * @param threadCount Number of worker threads to use. If 0 then thread count equals hardware thread count.
+   * @return True if success
+   */
+  bool Initialize( uint32_t threadCount = 0u );
+
+  /**
+   * @brief Waits until all threads finish execution and go back to the idle state.
+   */
+  void Wait();
+
+  /**
+   * @brief Submits a single task to specified ( by the index ) worker thread.
+   * @param workerIndex Index of thread to be used
+   * @param task Task submitted for execution
+   * @return Shared pointer to the Future object
+   */
+  SharedFuture SubmitTask(uint32_t workerIndex, const Task &task);
+
+  /**
+   * @brief Submits vector of tasks to the pool
+   * @param tasks Vector containing tasks to be executed
+   * @return Shared pointer to the Future object
+   */
+  SharedFuture SubmitTasks(const std::vector<Task>& tasks);
+
+  /**
+   * @brief Submits tasks to threads specified by thread mask.
+   * @param tasks Vector of tasks
+   * @param threadMask Mask of threads to be used or 0 to use all available threads
+   * @return Unique pointer to the FutureGroup object
+   */
+  UniqueFutureGroup SubmitTasks(const std::vector<Task>& tasks, uint32_t threadMask);
+
+  /**
+   * @brief Returns number of worker threads
+   * @return Number of worker threads
+   */
+  size_t GetWorkerCount() const;
+
+private:
+
+  struct Impl;
+  std::unique_ptr<Impl> mImpl;
+};
+
+} //namespace Dali
+
+#endif // DALI_THREAD_POOL_H
index d6c5cea..bf521bb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2019 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.
@@ -581,6 +581,38 @@ Property::Index Object::RegisterProperty( const std::string& name, Property::Ind
   return RegisterProperty( name, key, propertyValue, Property::ANIMATABLE );
 }
 
+void Object::SetProperties( const Property::Map& properties )
+{
+  const auto count = properties.Count();
+  for( auto position = 0u; position < count; ++position )
+  {
+    // GetKeyAt and GetValue both return references which means no potential copying of maps/arrays.
+    // Iterating twice to get the value we want should still be fairly quick in a Property::Map.
+
+    const auto& key = properties.GetKeyAt( position );
+    const auto propertyIndex = ( key.type == Property::Key::INDEX ) ? key.indexKey : GetPropertyIndex( key.stringKey );
+
+    if( propertyIndex != Property::INVALID_INDEX )
+    {
+      const auto& value = properties.GetValue( position );
+      SetProperty( propertyIndex, value );
+    }
+  }
+}
+
+void Object::GetProperties( Property::Map& properties )
+{
+  properties.Clear();
+
+  Property::IndexContainer indexContainer;
+  GetPropertyIndices( indexContainer );
+
+  for( auto index : indexContainer )
+  {
+    properties[ index ] = GetProperty( index );
+  }
+}
+
 Property::Index Object::RegisterProperty( const std::string& name, const Property::Value& propertyValue, Property::AccessMode accessMode )
 {
   return RegisterProperty( name, Property::INVALID_KEY, propertyValue, accessMode );
index 95033e7..e2c5d9f 100644 (file)
@@ -30,6 +30,7 @@
 #include <dali/public-api/object/property.h>
 #include <dali/public-api/object/property-index-ranges.h>
 #include <dali/public-api/object/property-input.h>
+#include <dali/public-api/object/property-map.h>
 #include <dali/public-api/object/property-notification.h>
 #include <dali/devel-api/common/owner-container.h>
 #include <dali/devel-api/object/handle-devel.h>
@@ -214,6 +215,16 @@ public:
   Property::Index RegisterProperty( const std::string& name, Property::Index key, const Property::Value& propertyValue );
 
   /**
+   * @copydoc Dali::DevelHandle::SetProperties()
+   */
+  void SetProperties( const Property::Map& properties );
+
+  /**
+   * @copydoc Dali::DevelHandle::GetProperties()
+   */
+  void GetProperties( Property::Map& properties );
+
+  /**
    * @copydoc Dali::Handle::RegisterProperty(std::string name, Property::Value propertyValue, Property::AccessMode accessMode)
    */
   Property::Index RegisterProperty( const std::string& name, const Property::Value& propertyValue, Property::AccessMode accessMode );
index 13771f5..c0f82fa 100644 (file)
@@ -175,7 +175,7 @@ public:
   template <typename T>
   inline T* GetDataTypedPtr()
   {
-    Dali::Vector< char >* data = mData.Release();
+    Dali::Vector< uint8_t >* data = mData.Release();
     mData = data;
     return reinterpret_cast<T*>( &data->operator[]( 0 ) );
   }
index 06679a2..0b3ff50 100644 (file)
@@ -18,6 +18,9 @@
  *
  */
 
+// EXTERNAL INCLUDES
+#include <cmath>
+
 // INTERNAL INCLUDES
 #include <dali/public-api/animation/alpha-function.h>
 #include <dali/public-api/animation/animation.h>
index d2311f9..ce5cfb8 100644 (file)
 const uint32_t FRAME_COUNT_TRIGGER = 16;\
 if( mImpl->frameCounter >= FRAME_COUNT_TRIGGER )\
   {\
-    for( auto root : mImpl->roots )
+    for( auto&& scene : mImpl->scenes )
     {\
-      if ( NULL != root )\
+      if ( scene && scene->root )\
       {\
         mImpl->frameCounter = 0;\
-        PrintNodeTree( *root, mSceneGraphBuffers.GetUpdateBufferIndex(), "" );\
+        PrintNodeTree( *scene->root, mSceneGraphBuffers.GetUpdateBufferIndex(), "" );\
       }\
     }\
   }\
@@ -156,6 +156,25 @@ void SortSiblingNodesRecursively( Node& node )
  */
 struct UpdateManager::Impl
 {
+  // SceneInfo keeps the root node of the Scene, its scene graph render task list, and the list of Layer pointers sorted by depth
+  struct SceneInfo
+  {
+    SceneInfo( Layer* root )                             ///< Constructor
+    : root( root )
+    {
+    }
+
+    ~SceneInfo() = default;                                   ///< Default non-virtual destructor
+    SceneInfo( SceneInfo&& rhs ) = default;                   ///< Move constructor
+    SceneInfo& operator=( SceneInfo&& rhs ) = default;        ///< Move assignment operator
+    SceneInfo& operator=( const SceneInfo& rhs ) = delete;    ///< Assignment operator
+    SceneInfo( const SceneInfo& rhs ) = delete;               ///< Copy constructor
+
+    Layer* root{ nullptr };                                   ///< Root node (root is a layer). The layer is not stored in the node memory pool.
+    OwnerPointer< RenderTaskList > taskList;                  ///< Scene graph render task list
+    SortedLayerPointers sortedLayerList;                      ///< List of Layer pointers sorted by depth (one list of sorted layers per root)
+  };
+
   Impl( NotificationManager& notificationManager,
         CompleteNotificationInterface& animationPlaylist,
         PropertyNotifier& propertyNotifier,
@@ -204,12 +223,15 @@ struct UpdateManager::Impl
   ~Impl()
   {
     // Disconnect render tasks from nodes, before destroying the nodes
-    for( auto taskList : taskLists )
+    for( auto&& scene : scenes )
     {
-      RenderTaskList::RenderTaskContainer& tasks = taskList->GetTasks();
-      for ( auto&& task : tasks )
+      if ( scene && scene->taskList )
       {
-        task->SetSourceNode( NULL );
+        RenderTaskList::RenderTaskContainer& tasks = scene->taskList->GetTasks();
+        for ( auto&& task : tasks )
+        {
+          task->SetSourceNode( NULL );
+        }
       }
     }
 
@@ -223,11 +245,15 @@ struct UpdateManager::Impl
       Node::Delete(*iter);
     }
 
-    for( auto root : roots )
+    for( auto&& scene : scenes )
     {
-      root->OnDestroy();
-      Node::Delete(root);
+      if ( scene && scene->root )
+      {
+        scene->root->OnDestroy();
+        Node::Delete( scene->root );
+      }
     }
+    scenes.clear();
 
     delete sceneController;
   }
@@ -262,14 +288,11 @@ struct UpdateManager::Impl
 
   Vector4                              backgroundColor;               ///< The glClear color used at the beginning of each frame.
 
-  OwnerContainer<RenderTaskList*>      taskLists;                     ///< A container of scene graph render task lists
-
-  Vector<Layer*>                       roots;                         ///< A container of root nodes (root is a layer). The layers are not stored in the node memory pool.
+  using SceneInfoPtr = std::unique_ptr< SceneInfo >;
+  std::vector< SceneInfoPtr >          scenes;                        ///< A container of SceneInfo.
 
   Vector<Node*>                        nodes;                         ///< A container of all instantiated nodes
 
-  std::vector<SortedLayerPointers>     sortedLayerLists;              ///< A container of lists of Layer pointers sorted by depth (one list of sorted layers per root)
-
   OwnerContainer< Camera* >            cameras;                       ///< A container of cameras
   OwnerContainer< PropertyOwner* >     customObjects;                 ///< A container of owned objects (with custom properties)
 
@@ -340,11 +363,17 @@ void UpdateManager::InstallRoot( OwnerPointer<Layer>& layer )
 
   Layer* rootLayer = layer.Release();
 
-  DALI_ASSERT_DEBUG( std::find( mImpl->roots.begin(), mImpl->roots.end(), rootLayer ) == mImpl->roots.end() && "Root Node already installed" );
+  DALI_ASSERT_DEBUG( std::find_if( mImpl->scenes.begin(), mImpl->scenes.end(),
+                                   [rootLayer]( Impl::SceneInfoPtr& scene )
+                                   {
+                                     return scene && scene->root == rootLayer;
+                                   }
+                                 ) == mImpl->scenes.end() && "Root Node already installed" );
 
   rootLayer->CreateTransform( &mImpl->transformManager );
   rootLayer->SetRoot(true);
-  mImpl->roots.PushBack( rootLayer );
+
+  mImpl->scenes.emplace_back( new Impl::SceneInfo( rootLayer ) );
 }
 
 void UpdateManager::UninstallRoot( Layer* layer )
@@ -352,11 +381,11 @@ void UpdateManager::UninstallRoot( Layer* layer )
   DALI_ASSERT_DEBUG( layer->IsLayer() );
   DALI_ASSERT_DEBUG( layer->GetParent() == NULL );
 
-  for ( auto&& iter : mImpl->roots )
+  for (auto iter = mImpl->scenes.begin(); iter != mImpl->scenes.end(); ++iter)
   {
-    if( ( *iter ) == layer )
+    if( (*iter) && (*iter)->root == layer )
     {
-      mImpl->roots.Erase( &iter );
+      mImpl->scenes.erase( iter );
       break;
     }
   }
@@ -470,12 +499,20 @@ void UpdateManager::AddRenderTaskList( OwnerPointer<RenderTaskList>& taskList )
 {
   RenderTaskList* taskListPointer = taskList.Release();
   taskListPointer->SetRenderMessageDispatcher( &mImpl->renderMessageDispatcher );
-  mImpl->taskLists.PushBack( taskListPointer );
+
+  mImpl->scenes.back()->taskList = taskListPointer;
 }
 
 void UpdateManager::RemoveRenderTaskList( RenderTaskList* taskList )
 {
-  mImpl->taskLists.EraseObject( taskList );
+  for ( auto&& scene : mImpl->scenes )
+  {
+    if ( scene && scene->taskList == taskList )
+    {
+      scene->taskList.Reset();
+      break;
+    }
+  }
 }
 
 void UpdateManager::AddAnimation( OwnerPointer< SceneGraph::Animation >& animation )
@@ -731,12 +768,15 @@ void UpdateManager::ConstrainCustomObjects( BufferIndex bufferIndex )
 void UpdateManager::ConstrainRenderTasks( BufferIndex bufferIndex )
 {
   // Constrain render-tasks
-  for( auto taskList : mImpl->taskLists )
+  for ( auto&& scene : mImpl->scenes )
   {
-    RenderTaskList::RenderTaskContainer& tasks = taskList->GetTasks();
-    for ( auto&& task : tasks )
+    if ( scene && scene->taskList )
     {
-      ConstrainPropertyOwner( *task, bufferIndex );
+      RenderTaskList::RenderTaskContainer& tasks = scene->taskList->GetTasks();
+      for ( auto&& task : tasks )
+      {
+        ConstrainPropertyOwner( *task, bufferIndex );
+      }
     }
   }
 }
@@ -802,13 +842,16 @@ void UpdateManager::UpdateNodes( BufferIndex bufferIndex )
 {
   mImpl->nodeDirtyFlags = NodePropertyFlags::NOTHING;
 
-  for( auto&& rootLayer : mImpl->roots )
+  for ( auto&& scene : mImpl->scenes )
   {
-    // Prepare resources, update shaders, for each node
-    // And add the renderers to the sorted layers. Start from root, which is also a layer
-    mImpl->nodeDirtyFlags |= UpdateNodeTree( *rootLayer,
-                                            bufferIndex,
-                                            mImpl->renderQueue );
+    if ( scene && scene->root )
+    {
+      // Prepare resources, update shaders, for each node
+      // And add the renderers to the sorted layers. Start from root, which is also a layer
+      mImpl->nodeDirtyFlags |= UpdateNodeTree( *scene->root,
+                                              bufferIndex,
+                                              mImpl->renderQueue );
+    }
   }
 }
 
@@ -863,11 +906,17 @@ uint32_t UpdateManager::Update( float elapsedSeconds,
     ConstrainCustomObjects( bufferIndex );
 
     //Clear the lists of renderers from the previous update
-    for( auto sortedLayers : mImpl->sortedLayerLists )
+    for( auto&& scene : mImpl->scenes )
     {
-      for( auto&& layer : sortedLayers )
+      if ( scene )
       {
-        layer->ClearRenderables();
+        for( auto&& layer : scene->sortedLayerList )
+        {
+          if ( layer )
+          {
+            layer->ClearRenderables();
+          }
+        }
       }
     }
 
@@ -905,25 +954,26 @@ uint32_t UpdateManager::Update( float elapsedSeconds,
     if( mImpl->renderersAdded )
     {
       // Calculate how many render tasks we have in total
-      VectorBase::SizeType numberOfRenderTasks = 0;
-
-      const VectorBase::SizeType taskListCount = mImpl->taskLists.Count();
-      for ( VectorBase::SizeType index = 0u; index < taskListCount; index++ )
+      std::size_t numberOfRenderTasks = 0;
+      for (auto&& scene : mImpl->scenes )
       {
-        numberOfRenderTasks += mImpl->taskLists[index]->GetTasks().Count();
+        if ( scene && scene->taskList )
+        {
+          numberOfRenderTasks += scene->taskList->GetTasks().Count();
+        }
       }
 
       mImpl->renderInstructions.ResetAndReserve( bufferIndex,
                                                  static_cast<uint32_t>( numberOfRenderTasks ) );
 
-      for ( VectorBase::SizeType index = 0u; index < taskListCount; index++ )
+      for ( auto&& scene : mImpl->scenes )
       {
-        if ( NULL != mImpl->roots[index] )
+        if ( scene && scene->root && scene->taskList )
         {
           keepRendererRendering |= mImpl->renderTaskProcessor.Process( bufferIndex,
-                                              *mImpl->taskLists[index],
-                                              *mImpl->roots[index],
-                                              mImpl->sortedLayerLists[index],
+                                              *scene->taskList,
+                                              *scene->root,
+                                              scene->sortedLayerList,
                                               mImpl->renderInstructions,
                                               renderToFboEnabled,
                                               isRenderingToFbo );
@@ -931,8 +981,8 @@ uint32_t UpdateManager::Update( float elapsedSeconds,
       }
 
       DALI_LOG_INFO( gLogFilter, Debug::General,
-                     "Update: numberOfRenderTasks(%d), taskListCount(%d), Render Instructions(%d)\n",
-                     numberOfRenderTasks, taskListCount, mImpl->renderInstructions.Count( bufferIndex ) );
+                     "Update: numberOfRenderTasks(%d), Render Instructions(%d)\n",
+                     numberOfRenderTasks, mImpl->renderInstructions.Count( bufferIndex ) );
 
 
 
@@ -945,34 +995,37 @@ uint32_t UpdateManager::Update( float elapsedSeconds,
     }
   }
 
-  for( auto taskList : mImpl->taskLists )
+  for ( auto&& scene : mImpl->scenes )
   {
-    RenderTaskList::RenderTaskContainer& tasks = taskList->GetTasks();
-
-    // check the countdown and notify
-    bool doRenderOnceNotify = false;
-    mImpl->renderTaskWaiting = false;
-    for ( auto&& renderTask : tasks )
+    if ( scene && scene->root && scene->taskList )
     {
-      renderTask->UpdateState();
+      RenderTaskList::RenderTaskContainer& tasks = scene->taskList->GetTasks();
 
-      if( renderTask->IsWaitingToRender() &&
-          renderTask->ReadyToRender( bufferIndex ) /*avoid updating forever when source actor is off-stage*/ )
+      // check the countdown and notify
+      bool doRenderOnceNotify = false;
+      mImpl->renderTaskWaiting = false;
+      for ( auto&& renderTask : tasks )
       {
-        mImpl->renderTaskWaiting = true; // keep update/render threads alive
+        renderTask->UpdateState();
+
+        if( renderTask->IsWaitingToRender() &&
+            renderTask->ReadyToRender( bufferIndex ) /*avoid updating forever when source actor is off-stage*/ )
+        {
+          mImpl->renderTaskWaiting = true; // keep update/render threads alive
+        }
+
+        if( renderTask->HasRendered() )
+        {
+          doRenderOnceNotify = true;
+        }
       }
 
-      if( renderTask->HasRendered() )
+      if( doRenderOnceNotify )
       {
-        doRenderOnceNotify = true;
+        DALI_LOG_INFO(gRenderTaskLogFilter, Debug::General, "Notify a render task has finished\n");
+        mImpl->notificationManager.QueueCompleteNotification( scene->taskList->GetCompleteNotificationInterface() );
       }
     }
-
-    if( doRenderOnceNotify )
-    {
-      DALI_LOG_INFO(gRenderTaskLogFilter, Debug::General, "Notify a render task has finished\n");
-      mImpl->notificationManager.QueueCompleteNotification( taskList->GetCompleteNotificationInterface() );
-    }
   }
 
   // Macro is undefined in release build.
@@ -1071,18 +1124,11 @@ void UpdateManager::SetRenderingBehavior( DevelStage::Rendering renderingBehavio
 
 void UpdateManager::SetLayerDepths( const SortedLayerPointers& layers, const Layer* rootLayer )
 {
-  const VectorBase::SizeType rootCount = mImpl->roots.Count();
-
-  // Make sure we reserve the correct size for the container so that
-  // we can save the sorted layers in the same order as the root layer
-  mImpl->sortedLayerLists.resize( rootCount );
-
-  for ( VectorBase::SizeType rootIndex = 0u; rootIndex < rootCount; rootIndex++ )
+  for ( auto&& scene : mImpl->scenes )
   {
-    Layer* root = mImpl->roots[rootIndex];
-    if ( root == rootLayer )
+    if ( scene && scene->root == rootLayer )
     {
-      mImpl->sortedLayerLists[rootIndex] = layers;
+      scene->sortedLayerList = layers;
       break;
     }
   }
@@ -1097,10 +1143,13 @@ void UpdateManager::SetDepthIndices( OwnerPointer< NodeDepths >& nodeDepths )
     iter.node->SetDepthIndex( iter.sortedDepth );
   }
 
-  for( auto root : mImpl->roots )
+  for ( auto&& scene : mImpl->scenes )
   {
-    // Go through node hierarchy and rearrange siblings according to depth-index
-    SortSiblingNodesRecursively( *root );
+    if ( scene )
+    {
+      // Go through node hierarchy and rearrange siblings according to depth-index
+      SortSiblingNodesRecursively( *scene->root );
+    }
   }
 }
 
index 42d1331..2db0945 100644 (file)
 #include <string>
 #include <cstdio>
 
+#if defined(BACKTRACE_ENABLED)
+#if defined(__GLIBC__)
 #include <execinfo.h>
+#endif
 #include <cxxabi.h>
-
+#endif
 #include <cstring>
 
 // INTERNAL INCLUDES
index 2ff48a3..2a2f439 100644 (file)
@@ -28,7 +28,7 @@ namespace Dali
 
 const uint32_t CORE_MAJOR_VERSION = 1;
 const uint32_t CORE_MINOR_VERSION = 4;
-const uint32_t CORE_MICRO_VERSION = 26;
+const uint32_t CORE_MICRO_VERSION = 27;
 const char * const CORE_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index 95b55ae..3ae63b9 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali
 Summary:    DALi 3D Engine
-Version:    1.4.26
+Version:    1.4.27
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT