Do not observe when Reload 86/293386/8
authorEunki, Hong <eunkiki.hong@samsung.com>
Thu, 25 May 2023 09:51:20 +0000 (18:51 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Wed, 31 May 2023 04:10:13 +0000 (13:10 +0900)
Previously, we add same observer multiple times when image is loading state
and user do Reload action multiple times.

In that case, there might be problem if visual destroyed.

TODO : We should consider when we call Reload during ResourceReady()
TODO : We should consider when reload visual has mask texture id

Change-Id: Iab8b193281649783b80a6e9fb89e86c625b4388a
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali-toolkit/utc-Dali-Control.cpp
dali-toolkit/internal/texture-manager/texture-manager-impl.cpp
dali-toolkit/internal/texture-manager/texture-manager-impl.h

index fbca3b6..8000f62 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 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.
@@ -1245,3 +1245,250 @@ int UtcDaliControlDoActionWhenNotStage(void)
 
   END_TEST;
 }
+
+int UtcDaliControlDoActionMultipleWhenNotStage01(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("DoAction on a visual registered with a control multiple times but not staged");
+
+  // Set up trace debug
+  TestGlAbstraction& gl           = application.GetGlAbstraction();
+  TraceCallStack&    textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  //Created AnimatedImageVisual
+  VisualFactory factory     = VisualFactory::Get();
+  Visual::Base  imageVisual = factory.CreateVisual(TEST_IMAGE_FILE_NAME, ImageDimensions());
+
+  DummyControl        dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl    = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
+  dummyControl.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(textureTrace.CountMethod("DeleteTextures"), 0, TEST_LOCATION);
+  DALI_TEST_EQUALS(textureTrace.FindMethod("GenTextures"), false, TEST_LOCATION);
+  textureTrace.Reset();
+
+  Property::Map  attributes;
+  const uint32_t repeatMax = 10u;
+  for(uint32_t repeatCnt = 0u; repeatCnt < repeatMax; ++repeatCnt)
+  {
+    // DoAction multiple times.
+    DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelImageVisual::Action::RELOAD, attributes);
+  }
+
+  tet_infoline("Perform RELOAD action. should reload Image and generate a texture");
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+  DALI_TEST_EQUALS(textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION);
+  textureTrace.Reset();
+
+  tet_infoline("Do not load image on more time even we request reload multiple times.");
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1, 1), false, TEST_LOCATION);
+
+  tet_infoline("Adding control to stage will in turn add the visual to the stage");
+
+  application.GetScene().Add(dummyControl);
+
+  application.SendNotification();
+  application.Render();
+  tet_infoline("No change in textures could occurs as already loaded and cached texture will be used");
+
+  DALI_TEST_EQUALS(textureTrace.CountMethod("DeleteTextures"), 0, TEST_LOCATION);
+  DALI_TEST_EQUALS(textureTrace.FindMethod("GenTextures"), false, TEST_LOCATION);
+  textureTrace.Reset();
+
+  dummyControl.Unparent();
+  dummyControl.Reset();
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcDaliControlDoActionMultipleWhenNotStage02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("DoAction on a visual registered with a control multiple times but not staged");
+
+  // Set up trace debug
+  TestGlAbstraction& gl           = application.GetGlAbstraction();
+  TraceCallStack&    textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  //Created AnimatedImageVisual
+  VisualFactory factory      = VisualFactory::Get();
+  Visual::Base  imageVisual  = factory.CreateVisual(TEST_IMAGE_FILE_NAME, ImageDimensions());
+  Visual::Base  imageVisual2 = factory.CreateVisual(TEST_IMAGE_FILE_NAME, ImageDimensions());
+
+  DummyControl        dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl    = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+
+  gResourceReadySignalFired = false;
+
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
+  dummyControl.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+  dummyControl.ResourceReadySignal().Connect(&ResourceReadySignal);
+
+  application.SendNotification();
+  application.Render();
+
+  // Dummy control to keep cache
+  DummyControl        keepCacheControl = DummyControl::New(true);
+  Impl::DummyControl& keepCacheImpl    = static_cast<Impl::DummyControl&>(keepCacheControl.GetImplementation());
+
+  keepCacheImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual2);
+  keepCacheControl.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+
+  // Load request for keep cache control.
+  application.GetScene().Add(keepCacheControl);
+
+  Property::Map  attributes;
+  const uint32_t repeatMax = 10u;
+  for(uint32_t repeatCnt = 0u; repeatCnt < repeatMax; ++repeatCnt)
+  {
+    // DoAction multiple times.
+    DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelImageVisual::Action::RELOAD, attributes);
+  }
+
+  application.SendNotification();
+  application.Render();
+
+  try
+  {
+    application.SendNotification();
+    application.Render();
+
+    tet_infoline("Async load completed. Sigabort should not be occured");
+    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+    application.SendNotification();
+    application.Render();
+
+    tet_infoline("ResourceReady signal must be fired!");
+    DALI_TEST_EQUALS(gResourceReadySignalFired, true, TEST_LOCATION);
+
+    tet_infoline("Texture generation occured");
+    DALI_TEST_EQUALS(textureTrace.CountMethod("DeleteTextures"), 0, TEST_LOCATION);
+    DALI_TEST_EQUALS(textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION);
+    textureTrace.Reset();
+
+    tet_result(TET_PASS);
+  }
+  catch(...)
+  {
+    // Must not be throw exception.
+    tet_infoline("Exception occured!");
+    tet_result(TET_FAIL);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliControlDoActionMultipleWhenNotStage03(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("DoAction on a visual registered with a control multiple times but not staged");
+
+  // Set up trace debug
+  TestGlAbstraction& gl           = application.GetGlAbstraction();
+  TraceCallStack&    textureTrace = gl.GetTextureTrace();
+  textureTrace.Enable(true);
+
+  //Created AnimatedImageVisual
+  VisualFactory factory      = VisualFactory::Get();
+  Visual::Base  imageVisual  = factory.CreateVisual(TEST_IMAGE_FILE_NAME, ImageDimensions());
+  Visual::Base  imageVisual2 = factory.CreateVisual(TEST_IMAGE_FILE_NAME, ImageDimensions());
+
+  DummyControl        dummyControl = DummyControl::New(true);
+  Impl::DummyControl& dummyImpl    = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+
+  gResourceReadySignalFired = false;
+
+  dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
+  dummyControl.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+  dummyControl.ResourceReadySignal().Connect(&ResourceReadySignal);
+
+  application.SendNotification();
+  application.Render();
+
+  // Dummy control to keep cache
+  DummyControl        keepCacheControl = DummyControl::New(true);
+  Impl::DummyControl& keepCacheImpl    = static_cast<Impl::DummyControl&>(keepCacheControl.GetImplementation());
+
+  keepCacheImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual2);
+  keepCacheControl.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+
+  // Load request for keep cache control.
+  application.GetScene().Add(keepCacheControl);
+
+  Property::Map  attributes;
+  const uint32_t repeatMax = 10u;
+  for(uint32_t repeatCnt = 0u; repeatCnt < repeatMax; ++repeatCnt)
+  {
+    // DoAction multiple times.
+    DevelControl::DoAction(dummyControl, DummyControl::Property::TEST_VISUAL, DevelImageVisual::Action::RELOAD, attributes);
+  }
+
+  application.SendNotification();
+  application.Render();
+
+  try
+  {
+    tet_infoline("Destroy control without stage on. And create new object that as same visual pointer as previous control");
+
+    const auto*    imageVisualObjectPtr = imageVisual.GetObjectPtr();
+    const uint32_t tryCountMax          = 100u;
+    uint32_t       tryCount             = 0u;
+    do
+    {
+      dummyControl.Reset();
+      imageVisual.Reset();
+
+      imageVisual  = factory.CreateVisual(TEST_IMAGE_FILE_NAME, ImageDimensions());
+      dummyControl = DummyControl::New(true);
+
+      Impl::DummyControl& dummyImpl = static_cast<Impl::DummyControl&>(dummyControl.GetImplementation());
+
+      dummyImpl.RegisterVisual(DummyControl::Property::TEST_VISUAL, imageVisual);
+      dummyControl.SetProperty(Actor::Property::SIZE, Vector2(200.f, 200.f));
+    } while(++tryCount < tryCountMax && imageVisualObjectPtr != imageVisual.GetObjectPtr());
+
+    tet_printf("Luck-trial count : %u. Success? %d\n", tryCount, imageVisualObjectPtr == imageVisual.GetObjectPtr());
+
+    // Connect signal
+    dummyControl.ResourceReadySignal().Connect(&ResourceReadySignal);
+
+    application.SendNotification();
+    application.Render();
+
+    tet_infoline("Async load completed after control destroyed. Sigabort should not be occured");
+    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+    application.SendNotification();
+    application.Render();
+
+    tet_infoline("ResourceReady signal must not be fired!");
+    DALI_TEST_EQUALS(gResourceReadySignalFired, false, TEST_LOCATION);
+
+    tet_infoline("Texture generation occured");
+    DALI_TEST_EQUALS(textureTrace.CountMethod("DeleteTextures"), 0, TEST_LOCATION);
+    DALI_TEST_EQUALS(textureTrace.FindMethod("GenTextures"), true, TEST_LOCATION);
+    textureTrace.Reset();
+
+    tet_result(TET_PASS);
+  }
+  catch(...)
+  {
+    // Must not be throw exception.
+    tet_infoline("Exception occured!");
+    tet_result(TET_FAIL);
+  }
+
+  END_TEST;
+}
\ No newline at end of file
index 74463da..086ec56 100644 (file)
@@ -223,7 +223,7 @@ TextureSet TextureManager::LoadAnimatedImageTexture(
       }
     }
 
-    textureId = RequestLoadInternal(url, alphaMaskId, contentScaleFactor, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, cropToMask, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiplyOnLoad, animatedImageLoading, frameIndex, false);
+    textureId = RequestLoadInternal(url, alphaMaskId, textureId, contentScaleFactor, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, cropToMask, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiplyOnLoad, animatedImageLoading, frameIndex, false);
 
     TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId);
     if(loadState == TextureManager::LoadState::UPLOADED)
@@ -271,7 +271,7 @@ Devel::PixelBuffer TextureManager::LoadPixelBuffer(
   }
   else
   {
-    RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false);
+    RequestLoadInternal(url, INVALID_TEXTURE_ID, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false);
   }
 
   return pixelBuffer;
@@ -310,8 +310,6 @@ TextureSet TextureManager::LoadTexture(
       auto      externalTextureInfo = mTextureCacheManager.GetExternalTextureInfo(id);
       if(externalTextureInfo.textureSet)
       {
-        textureId = id;
-
         if(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD)
         {
           // Change preMultiplyOnLoad value so make caller determine to preMultiplyAlpha or not.
@@ -324,7 +322,9 @@ TextureSet TextureManager::LoadTexture(
         {
           maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, StorageType::KEEP_TEXTURE, synchronousLoading);
           alphaMaskId            = maskInfo->mAlphaMaskId;
-          textureId              = RequestLoad(url, alphaMaskId, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad, synchronousLoading);
+
+          // Create new textureId. this textureId is not same as location
+          textureId = RequestLoad(url, alphaMaskId, textureId, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad, synchronousLoading);
 
           TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId);
           if(loadState == TextureManager::LoadState::UPLOADED)
@@ -334,6 +334,9 @@ TextureSet TextureManager::LoadTexture(
         }
         else
         {
+          // TextureId is same as location
+          textureId = id;
+
           textureSet = TextureSet::New();
           textureSet.SetTexture(TEXTURE_INDEX, externalTextureInfo.textureSet.GetTexture(TEXTURE_INDEX));
         }
@@ -432,6 +435,7 @@ TextureSet TextureManager::LoadTexture(
         textureId = RequestLoad(
           url,
           alphaMaskId,
+          textureId,
           contentScaleFactor,
           desiredSize,
           fittingMode,
@@ -487,12 +491,13 @@ TextureManager::TextureId TextureManager::RequestLoad(
   TextureManager::MultiplyOnLoad&     preMultiplyOnLoad,
   const bool&                         synchronousLoading)
 {
-  return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
+  return RequestLoadInternal(url, INVALID_TEXTURE_ID, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
 }
 
 TextureManager::TextureId TextureManager::RequestLoad(
   const VisualUrl&                    url,
   const TextureManager::TextureId&    maskTextureId,
+  const TextureManager::TextureId&    previousTextureId,
   const float&                        contentScale,
   const Dali::ImageDimensions&        desiredSize,
   const Dali::FittingMode::Type&      fittingMode,
@@ -505,7 +510,7 @@ TextureManager::TextureId TextureManager::RequestLoad(
   TextureManager::MultiplyOnLoad&     preMultiplyOnLoad,
   const bool&                         synchronousLoading)
 {
-  return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
+  return RequestLoadInternal(url, maskTextureId, previousTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
 }
 
 TextureManager::TextureId TextureManager::RequestMaskLoad(
@@ -515,12 +520,13 @@ TextureManager::TextureId TextureManager::RequestMaskLoad(
 {
   // Use the normal load procedure to get the alpha mask.
   auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
-  return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, UseAtlas::NO_ATLAS, false, storageType, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
+  return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, UseAtlas::NO_ATLAS, false, storageType, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading);
 }
 
 TextureManager::TextureId TextureManager::RequestLoadInternal(
   const VisualUrl&                    url,
   const TextureManager::TextureId&    maskTextureId,
+  const TextureManager::TextureId&    previousTextureId,
   const float&                        contentScale,
   const Dali::ImageDimensions&        desiredSize,
   const Dali::FittingMode::Type&      fittingMode,
@@ -552,9 +558,10 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
   // Check if the requested Texture exists in the cache.
   if(cacheIndex != INVALID_CACHE_INDEX)
   {
-    if(TextureManager::ReloadPolicy::CACHED == reloadPolicy)
+    if(TextureManager::ReloadPolicy::CACHED == reloadPolicy || TextureManager::INVALID_TEXTURE_ID == previousTextureId)
     {
-      // Mark this texture being used by another client resource. Forced reload would replace the current texture
+      // Mark this texture being used by another client resource, or Reload forced without request load before.
+      // Forced reload which have current texture before, would replace the current texture.
       // without the need for incrementing the reference count.
       ++(mTextureCacheManager[cacheIndex].referenceCount);
     }
@@ -562,7 +569,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
 
     // Update preMultiplyOnLoad value. It should be changed according to preMultiplied value of the cached info.
     preMultiplyOnLoad = mTextureCacheManager[cacheIndex].preMultiplied ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture id@%d, textureId=%d, maskTextureId=%d, frameindex=%d, premultiplied=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, frameIndex, mTextureCacheManager[cacheIndex].preMultiplied ? 1 : 0);
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture id@%d, textureId=%d, maskTextureId=%d, prevTextureId=%d, frameindex=%d, premultiplied=%d, refCount=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, previousTextureId, frameIndex, mTextureCacheManager[cacheIndex].preMultiplied ? 1 : 0, static_cast<int>(mTextureCacheManager[cacheIndex].referenceCount));
   }
 
   if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required
@@ -604,7 +611,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
      TextureManager::LoadState::CANCELLED != textureInfo.loadState &&
      TextureManager::LoadState::MASK_CANCELLED != textureInfo.loadState)
   {
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::RequestLoad( url=%s observer=%p ) ForcedReload cacheIndex:%d, textureId=%d, maskTextureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, maskTextureId);
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::RequestLoad( url=%s observer=%p ) ForcedReload cacheIndex:%d, textureId=%d, maskTextureId=%d, prevTextureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, previousTextureId);
     textureInfo.loadState = TextureManager::LoadState::NOT_STARTED;
   }
 
@@ -625,7 +632,11 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
       case TextureManager::LoadState::MASK_APPLYING:
       case TextureManager::LoadState::MASK_APPLIED:
       {
-        ObserveTexture(textureInfo, observer);
+        // Do not observe even we reload forced when texture is already loading state.
+        if(TextureManager::ReloadPolicy::CACHED == reloadPolicy || TextureManager::INVALID_TEXTURE_ID == previousTextureId)
+        {
+          ObserveTexture(textureInfo, observer);
+        }
         break;
       }
       case TextureManager::LoadState::UPLOADED:
@@ -782,7 +793,7 @@ void TextureManager::Remove(const TextureManager::TextureId& textureId)
         }
       }
 
-      DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::Remove( textureId=%d ) cacheIndex:%d removal maskTextureId=%d, loadingQueueTextureId=%d, loadState=%s\n", textureId, textureCacheIndex.GetIndex(), maskTextureId, mLoadingQueueTextureId, GET_LOAD_STATE_STRING(textureInfo.loadState));
+      DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::Remove( textureId=%d ) cacheIndex:%d removal maskTextureId=%d, loadState=%s\n", textureId, textureCacheIndex.GetIndex(), maskTextureId, GET_LOAD_STATE_STRING(textureInfo.loadState));
 
       // Remove textureId in CacheManager. Now, textureInfo is invalidate.
       mTextureCacheManager.RemoveCache(textureInfo);
@@ -794,6 +805,9 @@ void TextureManager::Remove(const TextureManager::TextureId& textureId)
         if(maskCacheIndex != INVALID_CACHE_INDEX)
         {
           TextureInfo& maskTextureInfo(mTextureCacheManager[maskCacheIndex]);
+
+          DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::Remove mask texture( maskTextureId=%d ) cacheIndex:%d, loadState=%s\n", maskTextureId, maskCacheIndex.GetIndex(), GET_LOAD_STATE_STRING(maskTextureInfo.loadState));
+
           mTextureCacheManager.RemoveCache(maskTextureInfo);
         }
       }
@@ -913,7 +927,10 @@ void TextureManager::LoadOrQueueTexture(TextureManager::TextureInfo& textureInfo
       {
         // The Texture has already loaded. The other observers have already been notified.
         // We need to send a "late" loaded notification for this observer.
-        EmitLoadComplete(observer, textureInfo, true);
+        if(observer)
+        {
+          EmitLoadComplete(observer, textureInfo, true);
+        }
       }
       break;
     }
@@ -937,6 +954,7 @@ void TextureManager::QueueLoadTexture(const TextureManager::TextureInfo& texture
 
   if(observer)
   {
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "  Connect DestructionSignal to observer:%p\n", observer);
     observer->DestructionSignal().Connect(this, &TextureManager::ObserverDestroyed);
   }
 }
@@ -973,6 +991,9 @@ void TextureManager::ProcessLoadQueue()
     if(cacheIndex != INVALID_CACHE_INDEX)
     {
       TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]);
+
+      DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::ProcessLoadQueue() textureId=%d, observer=%p, cacheIndex=@%d, loadState:%s\n", element.mTextureId, element.mObserver, cacheIndex.GetIndex(), GET_LOAD_STATE_STRING(textureInfo.loadState));
+
       if((textureInfo.loadState == LoadState::UPLOADED) || (textureInfo.loadState == LoadState::LOAD_FINISHED && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER))
       {
         if(element.mObserver)
@@ -1003,6 +1024,8 @@ void TextureManager::ObserveTexture(TextureManager::TextureInfo& textureInfo,
   if(observer)
   {
     textureInfo.observerList.PushBack(observer);
+
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "  Connect DestructionSignal to observer:%p\n", observer);
     observer->DestructionSignal().Connect(this, &TextureManager::ObserverDestroyed);
   }
 }
@@ -1345,9 +1368,10 @@ void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, c
     // invalidating the reference to the textureInfo struct.
     // Texture load requests for the same URL are deferred until the end of this
     // method.
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::NotifyObservers() textureId:%d url:%s loadState:%s\n", textureId, info->url.GetUrl().c_str(), GET_LOAD_STATE_STRING(info->loadState));
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::NotifyObservers() observer:%p textureId:%d url:%s loadState:%s\n", observer, textureId, info->url.GetUrl().c_str(), GET_LOAD_STATE_STRING(info->loadState));
     // It is possible for the observer to be deleted.
     // Disconnect and remove the observer first.
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "  Disconnect DestructionSignal to observer:%p\n", observer);
     observer->DestructionSignal().Disconnect(this, &TextureManager::ObserverDestroyed);
 
     info->observerList.Erase(info->observerList.End() - 1u);
@@ -1374,6 +1398,8 @@ void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, c
 
 void TextureManager::ObserverDestroyed(TextureUploadObserver* observer)
 {
+  DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::ObserverDestroyed() observer:%p\n", observer);
+
   const std::size_t size = mTextureCacheManager.size();
   for(TextureCacheIndex cacheIndex = TextureCacheIndex(TextureManagerType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, 0u); cacheIndex.GetIndex() < size; ++cacheIndex.detailValue.index)
   {
@@ -1495,6 +1521,7 @@ void TextureManager::RemoveTextureObserver(TextureManager::TextureInfo& textureI
     if(iter != iterEnd)
     {
       // Disconnect and remove the observer.
+      DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "  Disconnect DestructionSignal to observer:%p\n", observer);
       observer->DestructionSignal().Disconnect(this, &TextureManager::ObserverDestroyed);
       textureInfo.observerList.Erase(iter);
     }
index 68bf165..a753099 100644 (file)
@@ -186,7 +186,8 @@ public:
    * @param[in] samplingMode          The SamplingMode to use
    * @param[in, out] maskInfo         Mask info structure
    * @param[in] synchronousLoading    true if the URL should be loaded synchronously
-   * @param[out] textureId,           The textureId of the URL
+   * @param[in, out] textureId        The textureId of the URL. It is also be used to check the previous textureId
+   *                                  what requestor had. It will be used only ReloadPolicy::FORCED for now.
    * @param[out] textureRect          The rectangle within the texture atlas that this URL occupies,
    *                                  this is the rectangle in normalized coordinates.
    * @param[out] textureRectSize      The rectangle within the texture atlas that this URL occupies,
@@ -365,6 +366,7 @@ public: // Load Request API
     TextureManager::MultiplyOnLoad&     preMultiplyOnLoad,
     const bool&                         synchronousLoading = false);
 
+private: // Internal Load Request API
   /**
    * @brief Requests an image load of the given URL, when the texture has
    * have loaded, it will perform a blend with the image mask, and upload
@@ -378,6 +380,7 @@ public: // Load Request API
    * @param[in] url                   The URL of the image to load
    * @param[in] maskTextureId         The texture id of an image to mask this with
    *                                  (can be INVALID if no masking required)
+   * @param[in] previousTextureId     The texture id of an image which the requestor already has before
    * @param[in] contentScale          The scale factor to apply to the image before masking
    * @param[in] desiredSize           The size the image is likely to appear at. This can be set to 0,0 for automatic
    * @param[in] fittingMode           The FittingMode to use
@@ -402,6 +405,7 @@ public: // Load Request API
   TextureId RequestLoad(
     const VisualUrl&                    url,
     const TextureManager::TextureId&    maskTextureId,
+    const TextureManager::TextureId&    previousTextureId,
     const float&                        contentScale,
     const ImageDimensions&              desiredSize,
     const Dali::FittingMode::Type&      fittingMode,
@@ -428,7 +432,6 @@ public: // Load Request API
     StorageType      storageType,
     const bool&      synchronousLoading = false);
 
-private:
   /**
    * @brief Requests an image load of the given URL, when the texture has
    * have loaded, if there is a valid maskTextureId, it will perform a
@@ -442,6 +445,8 @@ private:
    * @param[in] url                   The URL of the image to load
    * @param[in] maskTextureId         The texture id of an image to use as a mask. If no mask is required, then set
    *                                  to INVALID_TEXTURE_ID
+   * @param[in] previousTextureId     The texture id of an image which the requestor already has before. It will be used
+   *                                  when reloadPolicy is FORCED.
    * @param[in] contentScale          The scaling factor to apply to the content when masking
    * @param[in] desiredSize           The size the image is likely to appear at. This can be set to 0,0 for automatic
    * @param[in] fittingMode           The FittingMode to use
@@ -468,6 +473,7 @@ private:
   TextureId RequestLoadInternal(
     const VisualUrl&                    url,
     const TextureManager::TextureId&    maskTextureId,
+    const TextureManager::TextureId&    previousTextureId,
     const float&                        contentScale,
     const Dali::ImageDimensions&        desiredSize,
     const Dali::FittingMode::Type&      fittingMode,