(Scene3D) Ensure remove image-resource-loader cache when app terminated 20/302020/4
authorEunki Hong <eunkiki.hong@samsung.com>
Wed, 29 Nov 2023 14:02:08 +0000 (23:02 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Fri, 1 Dec 2023 05:36:25 +0000 (14:36 +0900)
Since ImageResourceLoader use static global value internally,
it might have some problem when we use OffscreenApplication multiple time.

Since even Dali::Application destroyed, the static value can be alived.
At this time, If we create new Dali::Application one more time, the cache
system break down.

To ensure we follow up the cache system, let we connect the signal to
LifecycleController, and then delete self.
It will ensure we can re-create this cache after Dali::Application recreated.

Change-Id: I6178a91dfff2a435eb95d11e7ca8ef7479c125ed
Signed-off-by: Eunki Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-lifecycle-controller.cpp
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-lifecycle-controller.h
automated-tests/src/dali-toolkit/dali-toolkit-test-utils/toolkit-test-application.cpp
dali-scene3d/internal/common/image-resource-loader.cpp
dali-scene3d/internal/common/image-resource-loader.h
dali-scene3d/internal/common/model-cache-manager.cpp

index 1e29a1c..e05f9f7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -23,7 +23,6 @@
 
 namespace Dali
 {
-
 /********************************************************************************
  * Stub for Dali::Internal::Adaptor::LifecycleController
  ********************************************************************************/
@@ -34,18 +33,18 @@ namespace Adaptor
 class LifecycleController : public BaseObject
 {
 public: // Creation & Destruction
-
   LifecycleController();
   ~LifecycleController();
   static Dali::LifecycleController Get();
 
-
 public: // Signals
   Dali::LifecycleController::LifecycleSignalType& InitSignal();
+  Dali::LifecycleController::LifecycleSignalType& TerminateSignal();
 
 private:
   Dali::LifecycleController::LifecycleSignalType mInitSignal;
-  static Dali::LifecycleController mLifecycleController;
+  Dali::LifecycleController::LifecycleSignalType mTerminateSignal;
+  static Dali::LifecycleController               mLifecycleController;
 };
 
 Dali::LifecycleController LifecycleController::mLifecycleController;
@@ -60,7 +59,7 @@ LifecycleController::~LifecycleController()
 
 Dali::LifecycleController LifecycleController::Get()
 {
-  if( ! mLifecycleController )
+  if(!mLifecycleController)
   {
     mLifecycleController = Dali::LifecycleController(new Internal::Adaptor::LifecycleController());
   }
@@ -72,16 +71,24 @@ Dali::LifecycleController::LifecycleSignalType& LifecycleController::InitSignal(
   return mInitSignal;
 }
 
+Dali::LifecycleController::LifecycleSignalType& LifecycleController::TerminateSignal()
+{
+  return mTerminateSignal;
+}
+
 } // namespace Adaptor
 } // namespace Internal
 
-
 /********************************************************************************
  * Stub for Dali::LifecycleController
  ********************************************************************************/
 
-LifecycleController::LifecycleController(){}
-LifecycleController::~LifecycleController(){}
+LifecycleController::LifecycleController()
+{
+}
+LifecycleController::~LifecycleController()
+{
+}
 
 LifecycleController LifecycleController::Get()
 {
@@ -92,12 +99,19 @@ LifecycleController LifecycleController::Get()
 
 LifecycleController::LifecycleSignalType& LifecycleController::InitSignal()
 {
-  BaseObject& object = GetBaseObject();
-  Internal::Adaptor::LifecycleController& controller = static_cast< Internal::Adaptor::LifecycleController& >( object );
+  BaseObject&                             object     = GetBaseObject();
+  Internal::Adaptor::LifecycleController& controller = static_cast<Internal::Adaptor::LifecycleController&>(object);
   return controller.InitSignal();
 }
 
-LifecycleController::LifecycleController( Internal::Adaptor::LifecycleController *impl )
+LifecycleController::LifecycleSignalType& LifecycleController::TerminateSignal()
+{
+  BaseObject&                             object     = GetBaseObject();
+  Internal::Adaptor::LifecycleController& controller = static_cast<Internal::Adaptor::LifecycleController&>(object);
+  return controller.TerminateSignal();
+}
+
+LifecycleController::LifecycleController(Internal::Adaptor::LifecycleController* impl)
 : BaseHandle(impl)
 {
 }
index 3069b15..ec3c64e 100644 (file)
@@ -2,7 +2,7 @@
 #define TOOLKIT_LIFECYCLE_CONTROLLER_H
 
 /*
- * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 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.
@@ -19,9 +19,9 @@
  */
 
 // EXTERNAL INCLUDES
-#include <string>
 #include <dali/public-api/object/base-handle.h>
 #include <dali/public-api/signals/dali-signal.h>
+#include <string>
 
 namespace Dali
 {
@@ -31,20 +31,20 @@ namespace Adaptor
 {
 class LifecycleController;
 }
-}
+} // namespace Internal
 
 class LifecycleController : public BaseHandle
 {
 public:
-  typedef Signal< void (void) > LifecycleSignalType;
+  typedef Signal<void(void)> LifecycleSignalType;
   LifecycleController();
   ~LifecycleController();
   static LifecycleController Get();
-  LifecycleSignalType& InitSignal();
-  LifecycleController( Internal::Adaptor::LifecycleController* impl );
+  LifecycleSignalType&       InitSignal();
+  LifecycleSignalType&       TerminateSignal();
+  LifecycleController(Internal::Adaptor::LifecycleController* impl);
 };
 
-
 } // namespace Dali
 
 #endif // TOOLKIT_LIFECYCLE_CONTROLLER_H
index ac967f8..840cc13 100644 (file)
@@ -42,7 +42,7 @@ ToolkitTestApplication::ToolkitTestApplication(size_t surfaceWidth, size_t surfa
   // Override Scene creation in TestApplication by creating a window.
   // The window will create a Scene & surface and set up the scene's surface appropriately.
   mMainWindow = Window::New(PositionSize(0, 0, surfaceWidth, surfaceHeight), "");
-  mScene       = AdaptorImpl::GetScene(mMainWindow);
+  mScene      = AdaptorImpl::GetScene(mMainWindow);
   mScene.SetDpi(Vector2(horizontalDpi, verticalDpi));
 
   // Create render target for the scene
@@ -73,6 +73,9 @@ ToolkitTestApplication::ToolkitTestApplication(size_t surfaceWidth, size_t surfa
 
 ToolkitTestApplication::~ToolkitTestApplication()
 {
+  Dali::LifecycleController lifecycleController = Dali::LifecycleController::Get();
+  lifecycleController.TerminateSignal().Emit();
+
   // Need to delete core before we delete the adaptor.
   delete mCore;
   mCore = NULL;
index 7677e30..f5d15c5 100644 (file)
@@ -20,6 +20,7 @@
 
 // EXTERNAL INCLUDES
 #include <dali-toolkit/public-api/image-loader/sync-image-loader.h>
+#include <dali/devel-api/adaptor-framework/lifecycle-controller.h>
 #include <dali/devel-api/common/hash.h>
 #include <dali/devel-api/common/map-wrapper.h>
 #include <dali/devel-api/threading/mutex.h>
@@ -31,6 +32,7 @@
 #include <dali/public-api/signals/connection-tracker.h>
 
 #include <functional> ///< for std::function
+#include <memory> ///< for std::shared_ptr
 #include <mutex>
 #include <string>
 #include <utility> ///< for std::pair
@@ -172,6 +174,10 @@ Dali::Texture CreateCubeTextureFromPixelDataList(const std::vector<std::vector<D
 
   return texture;
 }
+
+// Forward declare, for signal connection.
+void DestroyCacheImpl();
+
 class CacheImpl : public Dali::ConnectionTracker
 {
 public:
@@ -193,6 +199,10 @@ public:
     mDestroyed{false},
     mFullCollectRequested{false}
   {
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Create CacheImpl\n");
+
+    // We should create CacheImpl at main thread, To ensure delete this cache impl
+    Dali::LifecycleController::Get().TerminateSignal().Connect(DestroyCacheImpl);
   }
 
   /**
@@ -200,6 +210,7 @@ public:
    */
   ~CacheImpl()
   {
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "Destroy CacheImpl\n");
     {
       mDataMutex.lock();
 
@@ -398,7 +409,7 @@ public: // Called by main thread.
    */
   void RequestGarbageCollect(bool fullCollect)
   {
-    if(DALI_LIKELY(!mDestroyed && Dali::Adaptor::IsAvailable()))
+    if(DALI_LIKELY(Dali::Adaptor::IsAvailable()))
     {
       if(!mTimer)
       {
@@ -456,20 +467,18 @@ private: // Called by main thread
   {
     bool continueTimer = false;
 
-    if(DALI_LIKELY(!mDestroyed))
-    {
-      // Try to collect Texture GC first, due to the reference count of pixelData who become key of textures.
-      // After all texture GC finished, then check PixelData cache.
-      uint32_t checkedCount = 0u;
-      // GC Cube Texture
-      continueTimer |= CollectGarbages<false>(mCubeTextureCache, fullCollect, mCubeTextureContainerUpdated, mLatestCollectedCubeTextureIter, checkedCount);
+    // Try to collect Texture GC first, due to the reference count of pixelData who become key of textures.
+    // After all texture GC finished, then check PixelData cache.
+    uint32_t checkedCount = 0u;
 
-      // GC Texture
-      continueTimer |= CollectGarbages<false>(mTextureCache, fullCollect, mTextureContainerUpdated, mLatestCollectedTextureIter, checkedCount);
+    // GC Cube Texture
+    continueTimer |= CollectGarbages<false>(mCubeTextureCache, fullCollect, mCubeTextureContainerUpdated, mLatestCollectedCubeTextureIter, checkedCount);
 
-      // GC PixelData. We should lock mutex during GC pixelData.
-      continueTimer |= CollectGarbages<true>(mPixelDataCache, fullCollect, mPixelDataContainerUpdated, mLatestCollectedPixelDataIter, checkedCount);
-    }
+    // GC Texture
+    continueTimer |= CollectGarbages<false>(mTextureCache, fullCollect, mTextureContainerUpdated, mLatestCollectedTextureIter, checkedCount);
+
+    // GC PixelData. We should lock mutex during GC pixelData.
+    continueTimer |= CollectGarbages<true>(mPixelDataCache, fullCollect, mPixelDataContainerUpdated, mLatestCollectedPixelDataIter, checkedCount);
 
     return continueTimer;
   }
@@ -496,12 +505,22 @@ private:
   bool mFullCollectRequested : 1;
 };
 
-CacheImpl& GetCacheImpl()
+static std::shared_ptr<CacheImpl> gCacheImpl{nullptr};
+
+std::shared_ptr<CacheImpl> GetCacheImpl()
 {
-  static CacheImpl gCacheImpl;
+  if(DALI_UNLIKELY(!gCacheImpl))
+  {
+    gCacheImpl = std::make_shared<CacheImpl>();
+  }
   return gCacheImpl;
 }
 
+void DestroyCacheImpl()
+{
+  gCacheImpl.reset();
+}
+
 } // namespace
 
 namespace Dali::Scene3D::Internal
@@ -529,17 +548,22 @@ Dali::Texture GetEmptyTextureWhiteRGB()
 
 Dali::Texture GetCachedTexture(Dali::PixelData pixelData, bool mipmapRequired)
 {
-  return GetCacheImpl().GetOrCreateCachedTexture(pixelData, mipmapRequired);
+  return GetCacheImpl()->GetOrCreateCachedTexture(pixelData, mipmapRequired);
 }
 
 Dali::Texture GetCachedCubeTexture(const std::vector<std::vector<Dali::PixelData>>& pixelDataList, bool mipmapRequired)
 {
-  return GetCacheImpl().GetOrCreateCachedCubeTexture(pixelDataList, mipmapRequired);
+  return GetCacheImpl()->GetOrCreateCachedCubeTexture(pixelDataList, mipmapRequired);
 }
 
 void RequestGarbageCollect(bool fullCollect)
 {
-  GetCacheImpl().RequestGarbageCollect(fullCollect);
+  GetCacheImpl()->RequestGarbageCollect(fullCollect);
+}
+
+void EnsureResourceLoaderCreated()
+{
+  GetCacheImpl();
 }
 
 // Can be called by worker thread.
@@ -555,7 +579,15 @@ Dali::PixelData GetCachedPixelData(const std::string& url,
                                    bool               orientationCorrection)
 {
   ImageInformation info(url, dimensions, fittingMode, samplingMode, orientationCorrection);
-  return GetCacheImpl().GetOrCreateCachedPixelData(info);
+  if(gCacheImpl == nullptr)
+  {
+    DALI_LOG_INFO(gLogFilter, Debug::Verbose, "CacheImpl not prepared! load PixelData without cache.\n");
+    return CreatePixelDataFromImageInfo(info, false);
+  }
+  else
+  {
+    return GetCacheImpl()->GetOrCreateCachedPixelData(info);
+  }
 }
 } // namespace ImageResourceLoader
 } // namespace Dali::Scene3D::Internal
index a7f10d4..ee6d048 100644 (file)
@@ -41,13 +41,6 @@ namespace Internal
 namespace ImageResourceLoader
 {
 // Called by main thread.
-
-/**
- * @brief Get cached pixelData handle filled as white with RGB888 format.
- * @return A PixelData object containing the white RGB888 color.
- */
-Dali::PixelData GetEmptyPixelDataWhiteRGB();
-
 /**
  * @brief Get cached texture handle filled as white with RGB888 format.
  * @return A Texture object containing the white RGB888 color.
@@ -77,10 +70,21 @@ Dali::Texture GetCachedCubeTexture(const std::vector<std::vector<Dali::PixelData
  */
 void RequestGarbageCollect(bool fullCollect = false);
 
+/**
+ * @brief Let we ensure to create a ResourceLoader cache handler.
+ */
+void EnsureResourceLoaderCreated();
+
 // Can be called by worker thread.
+/**
+ * @brief Get cached pixelData handle filled as white with RGB888 format.
+ * @return A PixelData object containing the white RGB888 color.
+ */
+Dali::PixelData GetEmptyPixelDataWhiteRGB();
 
 /**
  * @brief Get cached image, or loads an image synchronously.
+ * @note If cache handler is not created yet, or destroyed due to app terminated, it will load image synchronously without cache.
  * @param[in] url The URL of the image file to load
  * @return A PixelData object containing the image, or an invalid object on failure
  */
@@ -88,6 +92,7 @@ Dali::PixelData GetCachedPixelData(const std::string& url);
 
 /**
  * @brief Get cached image, or loads an image synchronously by specifying the target dimensions and options.
+ * @note If cache handler is not created yet, or destroyed due to app terminated, it will load image synchronously without cache.
  * @param[in] url The URL of the image file to load
  * @param[in] dimensions The width and height to fit the loaded image to
  * @param[in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter
index bb826fb..976a224 100644 (file)
@@ -41,6 +41,8 @@ public:
    */
   Impl()
   {
+    // Try to create ResourceLoader here, to ensure that we create it on main thread.
+    Dali::Scene3D::Internal::ImageResourceLoader::EnsureResourceLoaderCreated();
   }
 
   Dali::Scene3D::Loader::LoadResult GetModelLoadResult(std::string modelUri)