Refactoring TextureManager cache as Dali::FreeList 69/270969/53
authorEunki, Hong <eunkiki.hong@samsung.com>
Fri, 11 Feb 2022 13:36:39 +0000 (22:36 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Fri, 29 Apr 2022 14:07:14 +0000 (23:07 +0900)
Apply additional container as Dali::FreeList as TextureIdConverter;
From TextureId to TextureCacheIndex.
and add mTextureHashContainer, key is TextureHash and value is list of TextureId.
These things will make us access mTextureInfoContainer more faster.

Previous code iterate whole std::vector list to find cached texture.
This patch make we can get cacheIndex from textureId as O(1) by Dali::FreeList
and get textureId from textureHash as O(1 * collision)
by std::unordered_map<TextureHash, vector<TextureId>>.

This patch make TextureCacheIndex as a kind of structure. named TextureCacheIndexData.
This data use 4 bit as "TextueCacheIndexType" and 28 bit as "Index".
By this structure, we can use only one TextureIdConverter.
We can know what this TextureId come from ;
NormalImage or ExternalTextures or EncodedImageBuffer.

So now, we can do all API things as near O(1). Without useless iteratating.

It will reduce ImageView's SceneOn and SceneOff time.
And also, reduce AsyncLoadComplete API runtime, that animated image used
every image frame.

Note : After this patch, TextureId will not be unique anymore.
TODO : Masking feature still works on O(N). We need to make new container for it in future.

Change-Id: I37777ccc05fc0541e1f0e65f1d6306d9436c1245
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali-toolkit-internal/utc-Dali-TextureManager.cpp
automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp
dali-toolkit/internal/image-loader/image-url-impl.cpp
dali-toolkit/internal/texture-manager/texture-cache-manager.cpp
dali-toolkit/internal/texture-manager/texture-cache-manager.h
dali-toolkit/internal/texture-manager/texture-manager-impl.cpp
dali-toolkit/internal/texture-manager/texture-manager-impl.h
dali-toolkit/internal/texture-manager/texture-manager-type.h
dali-toolkit/internal/visuals/image/image-visual.cpp

index e8088a3..4b917a0 100644 (file)
@@ -55,8 +55,7 @@ void utc_dali_toolkit_texture_manager_cleanup(void)
 namespace
 {
 const char* TEST_IMAGE_FILE_NAME = TEST_RESOURCE_DIR "/gallery-small-1.jpg";
-
-}
+const char* TEST_MASK_FILE_NAME  = TEST_RESOURCE_DIR "/mask.png";
 
 class TestObserver : public Dali::Toolkit::TextureUploadObserver
 {
@@ -98,6 +97,45 @@ public:
   TextureSet   mTextureSet;
 };
 
+class TestObserverRemoveAndGenerateUrl : public TestObserver
+{
+public:
+  TestObserverRemoveAndGenerateUrl(TextureManager* textureManagerPtr)
+  : TestObserver(),
+    mTextureManagerPtr(textureManagerPtr)
+  {
+  }
+
+  virtual void LoadComplete(bool loadSuccess, TextureInformation textureInformation) override
+  {
+    if(textureInformation.returnType == TextureUploadObserver::ReturnType::TEXTURE)
+    {
+      mCompleteType = CompleteType::UPLOAD_COMPLETE;
+    }
+    else
+    {
+      mCompleteType = CompleteType::LOAD_COMPLETE;
+    }
+    mLoaded         = loadSuccess;
+    mObserverCalled = true;
+    mTextureSet     = textureInformation.textureSet;
+
+    // Remove during LoadComplete
+    mTextureManagerPtr->Remove(textureInformation.textureId, nullptr);
+
+    // ...And generate string which using texture id.
+    mGeneratedExternalUrl = mTextureManagerPtr->AddExternalTexture(mTextureSet);
+  }
+
+public:
+  std::string mGeneratedExternalUrl;
+
+protected:
+  TextureManager* mTextureManagerPtr; // Keep the pointer of texture manager.
+};
+
+} // namespace
+
 int UtcTextureManagerRequestLoad(void)
 {
   ToolkitTestApplication application;
@@ -164,18 +202,18 @@ int UtcTextureManagerEncodedImageBuffer(void)
   EncodedImageBuffer buffer1 = ConvertFileToEncodedImageBuffer(TEST_IMAGE_FILE_NAME);
   EncodedImageBuffer buffer2 = ConvertFileToEncodedImageBuffer(TEST_IMAGE_FILE_NAME);
 
-  std::string url1 = textureManager.AddExternalEncodedImageBuffer(buffer1);
-  std::string url2 = textureManager.AddExternalEncodedImageBuffer(buffer1);
+  std::string url1 = textureManager.AddEncodedImageBuffer(buffer1);
+  std::string url2 = textureManager.AddEncodedImageBuffer(buffer1);
   std::string url3 = VisualUrl::CreateBufferUrl(""); ///< Impossible Buffer URL. for coverage
 
   // Check if same EncodedImageBuffer get same url
   DALI_TEST_CHECK(url1 == url2);
   // Reduce reference count
-  textureManager.RemoveExternalEncodedImageBuffer(url1);
+  textureManager.RemoveEncodedImageBuffer(url1);
   // Check whethere url1 still valid
   DALI_TEST_CHECK(textureManager.GetEncodedImageBuffer(url1));
 
-  url2 = textureManager.AddExternalEncodedImageBuffer(buffer2);
+  url2 = textureManager.AddEncodedImageBuffer(buffer2);
   // Check if difference EncodedImageBuffer get difference url
   DALI_TEST_CHECK(url1 != url2);
 
@@ -250,8 +288,8 @@ int UtcTextureManagerEncodedImageBuffer(void)
   DALI_TEST_EQUALS(observer2.mObserverCalled, true, TEST_LOCATION);
   DALI_TEST_EQUALS(observer2.mCompleteType, TestObserver::CompleteType::LOAD_COMPLETE, TEST_LOCATION);
 
-  textureManager.RemoveExternalEncodedImageBuffer(url1);
-  textureManager.RemoveExternalEncodedImageBuffer(url2);
+  textureManager.RemoveEncodedImageBuffer(url1);
+  textureManager.RemoveEncodedImageBuffer(url2);
 
   // Now url1 and url2 is invalid type. mLoaded will return false
 
@@ -357,19 +395,19 @@ int UtcTextureManagerEncodedImageBufferReferenceCount(void)
   EncodedImageBuffer buffer1 = ConvertFileToEncodedImageBuffer(TEST_IMAGE_FILE_NAME);
   EncodedImageBuffer buffer2 = ConvertFileToEncodedImageBuffer(TEST_IMAGE_FILE_NAME);
 
-  std::string url1 = textureManager.AddExternalEncodedImageBuffer(buffer1);
-  std::string url2 = textureManager.AddExternalEncodedImageBuffer(buffer1);
+  std::string url1 = textureManager.AddEncodedImageBuffer(buffer1);
+  std::string url2 = textureManager.AddEncodedImageBuffer(buffer1);
 
   // Check if same EncodedImageBuffer get same url
   DALI_TEST_CHECK(url1 == url2);
 
   // Reduce reference count
-  textureManager.RemoveExternalEncodedImageBuffer(url1);
+  textureManager.RemoveEncodedImageBuffer(url1);
   // Check whethere url1 still valid
   DALI_TEST_CHECK(textureManager.GetEncodedImageBuffer(url1));
 
   // Reduce reference count
-  textureManager.RemoveExternalEncodedImageBuffer(url1);
+  textureManager.RemoveEncodedImageBuffer(url1);
   // Check whethere url1 is not valid anymore
   DALI_TEST_CHECK(!textureManager.GetEncodedImageBuffer(url1));
 
@@ -378,12 +416,9 @@ int UtcTextureManagerEncodedImageBufferReferenceCount(void)
   textureManager.UseExternalResource(url1);
   DALI_TEST_CHECK(!textureManager.GetEncodedImageBuffer(url1));
 
-  url1 = textureManager.AddExternalEncodedImageBuffer(buffer1);
-  // Check if difference EncodedImageBuffer get difference url
-  // Previous EncodedImageBuffer was deleted, so we get new url even same buffer.
-  DALI_TEST_CHECK(url1 != url2);
+  url1 = textureManager.AddEncodedImageBuffer(buffer1);
 
-  url2 = textureManager.AddExternalEncodedImageBuffer(buffer2);
+  url2 = textureManager.AddEncodedImageBuffer(buffer2);
   // Check if difference EncodedImageBuffer get difference url
   DALI_TEST_CHECK(url1 != url2);
 
@@ -445,8 +480,8 @@ int UtcTextureManagerEncodedImageBufferReferenceCount(void)
   DALI_TEST_EQUALS(observer2.mCompleteType, TestObserver::CompleteType::LOAD_COMPLETE, TEST_LOCATION);
 
   // Decrease each url's reference count.
-  textureManager.RemoveExternalEncodedImageBuffer(url1);
-  textureManager.RemoveExternalEncodedImageBuffer(url2);
+  textureManager.RemoveEncodedImageBuffer(url1);
+  textureManager.RemoveEncodedImageBuffer(url2);
 
   // url1 buffer is still have 1 reference count because it is cached.
   // But url2 not valid because it is not cached.
@@ -454,7 +489,7 @@ int UtcTextureManagerEncodedImageBufferReferenceCount(void)
   DALI_TEST_CHECK(!textureManager.GetEncodedImageBuffer(url2));
 
   // Check url1 buffer have 1 reference count because it is cached.
-  textureManager.RemoveExternalEncodedImageBuffer(url1);
+  textureManager.RemoveEncodedImageBuffer(url1);
   DALI_TEST_CHECK(!textureManager.GetEncodedImageBuffer(url1));
 
   END_TEST;
@@ -1034,3 +1069,174 @@ int UtcTextureManagerAsyncSyncAsync(void)
 
   END_TEST;
 }
+
+int UtcTextureManagerQueueRemoveDuringObserve(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcTextureManagerQueueRemoveDuringObserve");
+
+  TextureManager textureManager; // Create new texture manager
+
+  TestObserverRemoveAndGenerateUrl observer(&textureManager); // special observer for this UTC.
+
+  std::string filename(TEST_IMAGE_FILE_NAME);
+  auto        preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+
+  TextureManager::TextureId textureId = textureManager.RequestLoad(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    TextureManager::UseAtlas::NO_ATLAS,
+    &observer,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply);
+
+  DALI_TEST_EQUALS(observer.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer.mObserverCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(observer.mLoaded, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer.mObserverCalled, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION);
+
+  tet_printf("loaded textureId is %d, generated url is %s\n", static_cast<int>(textureId), observer.mGeneratedExternalUrl.c_str());
+
+  DALI_TEST_CHECK(static_cast<int>(textureId) != std::stoi(VisualUrl::GetLocation(observer.mGeneratedExternalUrl))); // Check we don't reuse textureId during observe
+
+  // Decrease external texture reference count who observer created
+  textureManager.RemoveExternalTexture(observer.mGeneratedExternalUrl);
+
+  application.SendNotification();
+  application.Render();
+
+  END_TEST;
+}
+
+int UtcTextureManagerRemoveDuringApplyMasking(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline("UtcTextureManagerRemoveDuringApplyMasking");
+
+  TextureManager textureManager; // Create new texture manager
+
+  TestObserver observer1;
+  TestObserver observer2;
+
+  std::string                        filename(TEST_IMAGE_FILE_NAME);
+  std::string                        maskname(TEST_MASK_FILE_NAME);
+  TextureManager::MaskingDataPointer maskInfo = nullptr;
+  maskInfo.reset(new TextureManager::MaskingData());
+  maskInfo->mAlphaMaskUrl       = maskname;
+  maskInfo->mAlphaMaskId        = TextureManager::INVALID_TEXTURE_ID;
+  maskInfo->mCropToMask         = true;
+  maskInfo->mContentScaleFactor = 1.0f;
+
+  auto                          textureId1(TextureManager::INVALID_TEXTURE_ID);
+  Vector4                       atlasRect(0.f, 0.f, 1.f, 1.f);
+  Dali::ImageDimensions         atlasRectSize(0, 0);
+  bool                          synchronousLoading(false);
+  bool                          atlasingStatus(false);
+  bool                          loadingStatus(false);
+  auto                          preMultiply         = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+  ImageAtlasManagerPtr          atlasManager        = nullptr;
+  Toolkit::AtlasUploadObserver* atlasUploadObserver = nullptr;
+
+  textureManager.LoadTexture(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    maskInfo,
+    synchronousLoading,
+    textureId1,
+    atlasRect,
+    atlasRectSize,
+    atlasingStatus,
+    loadingStatus,
+    WrapMode::DEFAULT,
+    WrapMode::DEFAULT,
+    &observer1,
+    atlasUploadObserver,
+    atlasManager,
+    true,
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply);
+
+  DALI_TEST_EQUALS(observer1.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION);
+
+  application.SendNotification();
+  application.Render();
+
+  // Load image and mask image.
+  // Now, LoadState become MASK_APPLYING
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+
+  tet_printf("Current textureId1:%d's state become MASK_APPLYING\n", static_cast<int>(textureId1));
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(observer1.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION);
+
+  // Remove current textureId1. and request new texture again.
+  textureManager.Remove(textureId1, &observer1);
+  auto textureId2 = textureManager.RequestLoad(
+    filename,
+    ImageDimensions(),
+    FittingMode::SCALE_TO_FILL,
+    SamplingMode::BOX_THEN_LINEAR,
+    TextureManager::UseAtlas::NO_ATLAS,
+    &observer2,
+    true, ///< orientationCorrection
+    TextureManager::ReloadPolicy::CACHED,
+    preMultiply,
+    synchronousLoading);
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(observer1.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer2.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer2.mObserverCalled, false, TEST_LOCATION);
+
+  tet_printf("textureId1:%d removed and textureId2:%d requested\n", static_cast<int>(textureId1), static_cast<int>(textureId2));
+
+  // ApplyMask event come back, and do nothing.
+  // CAPTION : HARD-CODING.
+  {
+    textureManager.AsyncLoadComplete(textureId1, Dali::Devel::PixelBuffer());
+    textureManager.Remove(maskInfo->mAlphaMaskId, nullptr);
+  }
+
+  application.SendNotification();
+  application.Render();
+
+  DALI_TEST_EQUALS(observer1.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer1.mObserverCalled, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer2.mLoaded, false, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer2.mObserverCalled, false, TEST_LOCATION);
+
+  // CAPTION : HARD-CODING.
+  {
+    textureManager.AsyncLoadComplete(textureId2, Dali::Devel::PixelBuffer());
+    textureManager.Remove(textureId2, &observer2);
+  }
+
+  DALI_TEST_EQUALS(observer2.mLoaded, false, TEST_LOCATION); ///< Note that we call AsyncLoadComplete hardly with empty pixelbuffer.
+  DALI_TEST_EQUALS(observer2.mObserverCalled, true, TEST_LOCATION);
+  DALI_TEST_EQUALS(observer2.mCompleteType, TestObserver::CompleteType::UPLOAD_COMPLETE, TEST_LOCATION);
+
+  END_TEST;
+}
\ No newline at end of file
index ab50286..5694d9f 100644 (file)
@@ -59,6 +59,7 @@ namespace
 const char* TEST_IMAGE_FILE_NAME  = "gallery_image_01.jpg";
 const char* TEST_IMAGE_FILE_NAME2 = "gallery_image_02.jpg";
 
+// resolution: 1024*1024
 const char* TEST_IMAGE_1 = TEST_RESOURCE_DIR "/TB-gloss.png";
 const char* TEST_IMAGE_2 = TEST_RESOURCE_DIR "/tb-norm.png";
 
@@ -3105,6 +3106,7 @@ void OnResourceReadySignal02(Control control)
 ImageView gImageView1;
 ImageView gImageView2;
 ImageView gImageView3;
+ImageView gImageView4;
 
 void OnResourceReadySignal03(Control control)
 {
@@ -3136,6 +3138,66 @@ void OnSimpleResourceReadySignal(Control control)
   gResourceReadySignalCounter++;
 }
 
+int gResourceReadySignal04ComesOrder = 0;
+
+void OnResourceReadySignal04(Control control)
+{
+  gResourceReadySignalCounter++;
+  tet_printf("rc %d\n", gResourceReadySignalCounter);
+  if(gResourceReadySignalCounter == 1)
+  {
+    auto scene = gImageView1.GetParent();
+
+    // Request load something
+    // We hope this request result is return later than gImageView2.
+    gImageView3 = ImageView::New(TEST_IMAGE_1);
+    gImageView3.ResourceReadySignal().Connect(&OnResourceReadySignal04);
+    scene.Add(gImageView3);
+    gImageView4 = ImageView::New(TEST_IMAGE_2);
+    gImageView4.ResourceReadySignal().Connect(&OnResourceReadySignal04);
+    scene.Add(gImageView4);
+
+    if(control == gImageView1)
+    {
+      gResourceReadySignal04ComesOrder = 1;
+    }
+    else
+    {
+      gResourceReadySignal04ComesOrder = 2;
+    }
+  }
+  if(gResourceReadySignalCounter == 2)
+  {
+    if(gResourceReadySignal04ComesOrder == 1 && control == gImageView2)
+    {
+      // Scene off first one.
+      gImageView1.Unparent();
+
+      // Scene off second one.
+      gImageView2.Unparent();
+    }
+    else if(gResourceReadySignal04ComesOrder == 2 && control == gImageView1)
+    {
+      // Scene off first one.
+      gImageView2.Unparent();
+
+      // Scene off second one.
+      gImageView1.Unparent();
+    }
+    else
+    {
+      // We can't check that this utc fail case. just pass always when we come here.
+      gResourceReadySignal04ComesOrder = -1;
+    }
+
+    // If we don't seperate index of FreeList area
+    // and if we don't queue remove during obversing,
+    // cache index become something invalid data.
+    // In this case, some strange observer can be called.
+    // For example, gImageView4.LoadComplete will be called.
+  }
+}
+
 } // namespace
 
 int UtcDaliImageViewSetImageOnResourceReadySignal01(void)
@@ -3468,3 +3530,59 @@ int UtcDaliImageViewCheckVariousCaseSendOnResourceReadySignal(void)
 
   END_TEST;
 }
+
+int UtcDaliImageViewSetImageOnResourceReadySignal04(void)
+{
+  tet_infoline("Test texturemanager's remove queue works well within signal handler.");
+
+  ToolkitTestApplication application;
+
+  gResourceReadySignalCounter      = 0;
+  gResourceReadySignal04ComesOrder = 0;
+
+  gImageView1 = ImageView::New("invalid.jpg"); // request invalid image, to make loading failed fast.
+  gImageView1.ResourceReadySignal().Connect(&OnResourceReadySignal04);
+  application.GetScene().Add(gImageView1);
+
+  gImageView2 = ImageView::New("invalid.png"); // request invalid image, to make loading failed fast.
+  gImageView2.ResourceReadySignal().Connect(&OnResourceReadySignal04);
+  application.GetScene().Add(gImageView2);
+
+  application.SendNotification();
+  application.Render();
+
+  tet_infoline("Try to load 2 invalid image");
+
+  DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(gResourceReadySignalCounter, 2, TEST_LOCATION);
+
+  tet_infoline("load done");
+
+  // We can test this UTC only if gImageView1 and gImageView2 loaded done.
+  if(gResourceReadySignal04ComesOrder == -1)
+  {
+    tet_infoline("Bad news.. gImageView3 or gImageView4 loaded faster than others. just skip this UTC");
+  }
+  else
+  {
+    // gImageView3 and gImageView4 load must not be successed yet.
+    DALI_TEST_EQUALS(gImageView3.GetRendererCount(), 0u, TEST_LOCATION);
+    DALI_TEST_EQUALS(gImageView4.GetRendererCount(), 0u, TEST_LOCATION);
+
+    application.SendNotification();
+    application.Render();
+
+    tet_infoline("Try to load 2 valid image");
+
+    DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(2), true, TEST_LOCATION);
+    DALI_TEST_EQUALS(gResourceReadySignalCounter, 4, TEST_LOCATION);
+
+    tet_infoline("load done");
+
+    // gImageView3 and gImageView4 load must be successed now.
+    DALI_TEST_EQUALS(gImageView3.GetRendererAt(0).GetTextures().GetTextureCount(), 1u, TEST_LOCATION);
+    DALI_TEST_EQUALS(gImageView4.GetRendererAt(0).GetTextures().GetTextureCount(), 1u, TEST_LOCATION);
+  }
+
+  END_TEST;
+}
index 0742d3c..fae9fc1 100644 (file)
@@ -42,7 +42,7 @@ ImageUrl::ImageUrl(const EncodedImageBuffer& encodedImageBuffer)
   if(visualFactory)
   {
     auto& textureManager = GetImplementation(visualFactory).GetTextureManager();
-    mUrl                 = textureManager.AddExternalEncodedImageBuffer(encodedImageBuffer);
+    mUrl                 = textureManager.AddEncodedImageBuffer(encodedImageBuffer);
   }
 }
 
@@ -60,7 +60,7 @@ ImageUrl::~ImageUrl()
       }
       else if(VisualUrl::BUFFER == VisualUrl::GetProtocolType(mUrl))
       {
-        textureManager.RemoveExternalEncodedImageBuffer(mUrl);
+        textureManager.RemoveEncodedImageBuffer(mUrl);
       }
     }
   }
index cc90fd8..a561161 100644 (file)
@@ -47,12 +47,43 @@ extern Debug::Filter* gTextureManagerLogFilter; ///< Define at texture-manager-i
                                                                  "Unknown"
 // clang-format on
 #endif
-namespace
+
+// Due to the compile issue, this specialized template code must be defined top of this code.
+template<>
+void TextureCacheManager::RemoveTextureInfoByIndex<TextureCacheManager::EncodedImageBufferInfoContainerType>(TextureCacheManager::EncodedImageBufferInfoContainerType& cacheContainer, const TextureCacheManager::TextureCacheIndex& removeContainerIndex)
 {
-} // Anonymous namespace
+  // Swap last data of cacheContainer.
+  if(static_cast<std::size_t>(removeContainerIndex.GetIndex() + 1) < cacheContainer.size())
+  {
+    // First, change the cache index infomations inside of converter
+    mTextureIdConverter[cacheContainer.back().bufferId] = static_cast<std::uint32_t>(removeContainerIndex);
+
+    // After change converter, swap the value between current data and last data.
+    std::swap(cacheContainer[removeContainerIndex.GetIndex()], cacheContainer.back());
+  }
+
+  // Now we can assume that latest data should be removed. pop_back.
+  cacheContainer.pop_back();
+}
+
+template<class ContainerType>
+void TextureCacheManager::RemoveTextureInfoByIndex(ContainerType& cacheContainer, const TextureCacheManager::TextureCacheIndex& removeContainerIndex)
+{
+  // Swap last data of cacheContainer.
+  if(static_cast<std::size_t>(removeContainerIndex.GetIndex() + 1) < cacheContainer.size())
+  {
+    // First, change the cache index infomations inside of converter
+    mTextureIdConverter[cacheContainer.back().textureId] = static_cast<std::uint32_t>(removeContainerIndex);
+
+    // After change converter, swap the value between current data and last data.
+    std::swap(cacheContainer[removeContainerIndex.GetIndex()], cacheContainer.back());
+  }
+
+  // Now we can assume that latest data should be removed. pop_back.
+  cacheContainer.pop_back();
+}
 
 TextureCacheManager::TextureCacheManager()
-: mCurrentTextureId(0)
 {
 }
 
@@ -63,50 +94,73 @@ TextureCacheManager::~TextureCacheManager()
 VisualUrl TextureCacheManager::GetVisualUrl(const TextureCacheManager::TextureId& textureId)
 {
   VisualUrl         visualUrl("");
-  TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
+  TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(textureId)]);
 
-  if(cacheIndex != INVALID_CACHE_INDEX)
+  switch(static_cast<TextureCacheIndexType>(cacheIndex.detailValue.type))
   {
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached texture id=%d, textureId=%d\n", cacheIndex, textureId);
+    case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL:
+    {
+      DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached texture index=%d, textureId=%d\n", cacheIndex.GetIndex(), textureId);
 
-    TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]);
-    visualUrl = cachedTextureInfo.url;
+      TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
+      visualUrl = cachedTextureInfo.url;
+      break;
+    }
+    case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE:
+    {
+      DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached external texture index=%d, textureId=%d\n", cacheIndex.GetIndex(), textureId);
+      visualUrl = VisualUrl::CreateTextureUrl(std::to_string(textureId));
+      break;
+    }
+    case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER:
+    {
+      DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached buffer index=%d, bufferId=%d\n", cacheIndex.GetIndex(), textureId);
+      visualUrl = VisualUrl::CreateBufferUrl(std::to_string(textureId));
+      break;
+    }
+    default:
+    {
+      break;
+    }
   }
+
   return visualUrl;
 }
 
 TextureCacheManager::LoadState TextureCacheManager::GetTextureState(const TextureCacheManager::TextureId& textureId)
 {
-  LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED;
+  LoadState         loadState  = TextureCacheManager::LoadState::NOT_STARTED;
+  TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(textureId)]);
 
-  TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
-  if(cacheIndex != INVALID_CACHE_INDEX)
-  {
-    TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]);
-    loadState = cachedTextureInfo.loadState;
-  }
-  else
+  switch(static_cast<TextureCacheIndexType>(cacheIndex.detailValue.type))
   {
-    for(auto&& elem : mExternalTextures)
+    case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL:
     {
-      if(elem.textureId == textureId)
-      {
-        loadState = LoadState::UPLOADED;
-        break;
-      }
+      TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
+      loadState = cachedTextureInfo.loadState;
+      break;
+    }
+    case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE:
+    {
+      loadState = LoadState::UPLOADED;
+      break;
+    }
+    default:
+    {
+      break;
     }
   }
+
   return loadState;
 }
 
 TextureCacheManager::LoadState TextureCacheManager::GetTextureStateInternal(const TextureCacheManager::TextureId& textureId)
 {
-  LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED;
-
+  LoadState         loadState  = TextureCacheManager::LoadState::NOT_STARTED;
   TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
   if(cacheIndex != INVALID_CACHE_INDEX)
   {
-    TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]);
+    TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
     loadState = cachedTextureInfo.loadState;
   }
 
@@ -115,66 +169,63 @@ TextureCacheManager::LoadState TextureCacheManager::GetTextureStateInternal(cons
 
 TextureSet TextureCacheManager::GetTextureSet(const TextureCacheManager::TextureId& textureId)
 {
-  TextureSet textureSet; // empty handle
+  TextureSet        textureSet; // empty handle
+  TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(textureId)]);
 
-  TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
-  if(cacheIndex != INVALID_CACHE_INDEX)
+  switch(static_cast<TextureCacheIndexType>(cacheIndex.detailValue.type))
   {
-    TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]);
-    textureSet = cachedTextureInfo.textureSet;
-  }
-  else
-  {
-    for(auto&& elem : mExternalTextures)
+    case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL:
     {
-      if(elem.textureId == textureId)
-      {
-        textureSet = elem.textureSet;
-        break;
-      }
+      TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
+      textureSet = cachedTextureInfo.textureSet;
+      break;
+    }
+    case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE:
+    {
+      textureSet = mExternalTextures[cacheIndex.GetIndex()].textureSet;
+      break;
+    }
+    default:
+    {
+      break;
     }
   }
+
   return textureSet;
 }
 
 TextureSet TextureCacheManager::GetExternalTextureSet(const TextureCacheManager::TextureId& textureId)
 {
-  TextureSet textureSet; // empty handle
-  for(auto&& elem : mExternalTextures)
+  TextureSet        textureSet; // empty handle
+  TextureCacheIndex cacheIndex = GetCacheIndexFromExternalTextureId(textureId);
+  if(cacheIndex != INVALID_CACHE_INDEX)
   {
-    if(elem.textureId == textureId)
-    {
-      textureSet = elem.textureSet;
-      break;
-    }
+    textureSet = mExternalTextures[cacheIndex.GetIndex()].textureSet;
   }
   return textureSet;
 }
 
-EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const TextureCacheManager::TextureId& textureId)
+EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const TextureCacheManager::TextureId& bufferId)
 {
   EncodedImageBuffer encodedImageBuffer; // empty handle
-  for(auto&& elem : mEncodedBufferTextures)
+  TextureCacheIndex  cacheIndex = GetCacheIndexFromEncodedImageBufferId(bufferId);
+  if(cacheIndex != INVALID_CACHE_INDEX)
   {
-    if(elem.textureId == textureId)
-    {
-      encodedImageBuffer = elem.encodedImageBuffer;
-      break;
-    }
+    encodedImageBuffer = mEncodedImageBuffers[cacheIndex.GetIndex()].encodedImageBuffer;
   }
   return encodedImageBuffer;
 }
 
-EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const std::string& url)
+EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const VisualUrl& url)
 {
   EncodedImageBuffer encodedImageBuffer; // empty handle
-  if(url.size() > 0 && VisualUrl::BUFFER == VisualUrl::GetProtocolType(url))
+  if(url.IsValid() && VisualUrl::BUFFER == url.GetProtocolType())
   {
-    std::string location = VisualUrl::GetLocation(url);
+    std::string location = url.GetLocation();
     if(location.size() > 0u)
     {
-      TextureId targetId = std::stoi(location);
-      return GetEncodedImageBuffer(targetId);
+      TextureId bufferId = std::stoi(location);
+      return GetEncodedImageBuffer(bufferId);
     }
   }
   return encodedImageBuffer;
@@ -182,86 +233,130 @@ EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const std::string&
 
 std::string TextureCacheManager::AddExternalTexture(const TextureSet& textureSet)
 {
-  TextureCacheManager::ExternalTextureInfo info(GenerateUniqueTextureId(), textureSet);
-  mExternalTextures.emplace_back(info);
-  return VisualUrl::CreateTextureUrl(std::to_string(info.textureId));
+  TextureId textureId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE, mExternalTextures.size()));
+
+  TextureCacheManager::ExternalTextureInfo textureInfo(textureId, textureSet);
+  mExternalTextures.emplace_back(textureInfo);
+
+  DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalTexture() : New texture registered. textureId:%d\n", textureInfo.textureId);
+
+  return VisualUrl::CreateTextureUrl(std::to_string(textureInfo.textureId));
 }
 
-std::string TextureCacheManager::AddExternalEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer)
+std::string TextureCacheManager::AddEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer)
 {
   // Duplication check
-  for(auto&& elem : mEncodedBufferTextures)
+  TextureHash       bufferHash       = static_cast<TextureHash>(encodedImageBuffer.GetHash());
+  TextureCacheIndex bufferCacheIndex = FindCachedEncodedImageBuffer(bufferHash, encodedImageBuffer);
+  if(bufferCacheIndex != INVALID_CACHE_INDEX)
   {
-    if(elem.encodedImageBuffer == encodedImageBuffer)
-    {
-      // If same buffer added, increase reference count and return.
-      elem.referenceCount++;
-      return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId));
-    }
+    EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[bufferCacheIndex.GetIndex()]);
+
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalEncodedImageBuffer() : Increase reference. bufferId:%d, cache index:%d, reference:%d\n", bufferInfo.bufferId, bufferCacheIndex.GetIndex(), static_cast<int>(bufferInfo.referenceCount));
+
+    // If same buffer added, increase reference count and return.
+    bufferInfo.referenceCount++;
+    return VisualUrl::CreateBufferUrl(std::to_string(bufferInfo.bufferId));
   }
-  TextureCacheManager::EncodedBufferTextureInfo info(GenerateUniqueTextureId(), encodedImageBuffer);
-  mEncodedBufferTextures.emplace_back(info);
-  return VisualUrl::CreateBufferUrl(std::to_string(info.textureId));
+
+  TextureId bufferId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER, mEncodedImageBuffers.size()));
+
+  TextureCacheManager::EncodedImageBufferInfo info(bufferId, bufferHash, encodedImageBuffer);
+  mEncodedImageBuffers.emplace_back(info);
+
+  // Insert TextureHashContainer
+  // Find exist list -or- Create new list.
+  std::vector<TextureId>& idList = mTextureHashContainer[bufferHash];
+  // We already assume that list doesn't contain id. just emplace back
+  idList.emplace_back(bufferId);
+
+  DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalEncodedImageBuffer() : New buffer regested. bufferId:%d\n", info.bufferId);
+
+  return VisualUrl::CreateBufferUrl(std::to_string(info.bufferId));
 }
 
-TextureSet TextureCacheManager::RemoveExternalTexture(const std::string& url)
+TextureSet TextureCacheManager::RemoveExternalTexture(const VisualUrl& url)
 {
-  if(url.size() > 0u)
+  TextureSet        textureSet;
+  bool              removeTextureInfo  = false;
+  TextureCacheIndex removeTextureIndex = INVALID_CACHE_INDEX;
+  if(url.IsValid())
   {
-    if(VisualUrl::TEXTURE == VisualUrl::GetProtocolType(url))
+    if(VisualUrl::TEXTURE == url.GetProtocolType())
     {
       // get the location from the Url
-      std::string location = VisualUrl::GetLocation(url);
+      std::string location = url.GetLocation();
       if(location.size() > 0u)
       {
-        TextureId  id  = std::stoi(location);
-        const auto end = mExternalTextures.end();
-        for(auto iter = mExternalTextures.begin(); iter != end; ++iter)
+        TextureId textureId = std::stoi(location);
+        removeTextureIndex  = GetCacheIndexFromExternalTextureId(textureId);
+        if(removeTextureIndex != INVALID_CACHE_INDEX)
         {
-          if(iter->textureId == id)
+          ExternalTextureInfo& textureInfo(mExternalTextures[removeTextureIndex.GetIndex()]);
+          DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::RemoveExternalTexture(url:%s) textureId:%d reference:%d\n", url.GetUrl().c_str(), textureId, static_cast<int>(textureInfo.referenceCount));
+          textureSet = textureInfo.textureSet;
+          if(--(textureInfo.referenceCount) <= 0)
           {
-            auto textureSet = iter->textureSet;
-            if(--(iter->referenceCount) <= 0)
-            {
-              mExternalTextures.erase(iter);
-            }
-            return textureSet;
+            removeTextureInfo = true;
+            // id life is finished. Remove it at converter
+            mTextureIdConverter.Remove(textureId);
           }
         }
       }
     }
   }
-  return TextureSet();
+
+  // Post removal process to avoid mExternalTextures reference problems.
+  if(removeTextureInfo)
+  {
+    // Swap last data of mExternalTextures, and pop_back.
+    RemoveTextureInfoByIndex(mExternalTextures, removeTextureIndex);
+  }
+  return textureSet;
 }
 
-EncodedImageBuffer TextureCacheManager::RemoveExternalEncodedImageBuffer(const std::string& url)
+EncodedImageBuffer TextureCacheManager::RemoveEncodedImageBuffer(const VisualUrl& url)
 {
-  if(url.size() > 0u)
+  EncodedImageBuffer encodedImageBuffer;
+  bool               removeBufferInfo  = false;
+  TextureCacheIndex  removeBufferIndex = INVALID_CACHE_INDEX;
+  if(url.IsValid())
   {
-    if(VisualUrl::BUFFER == VisualUrl::GetProtocolType(url))
+    if(VisualUrl::BUFFER == url.GetProtocolType())
     {
       // get the location from the Url
-      std::string location = VisualUrl::GetLocation(url);
+      std::string location = url.GetLocation();
       if(location.size() > 0u)
       {
-        TextureId  id  = std::stoi(location);
-        const auto end = mEncodedBufferTextures.end();
-        for(auto iter = mEncodedBufferTextures.begin(); iter != end; ++iter)
+        TextureId bufferId = std::stoi(location);
+        removeBufferIndex  = GetCacheIndexFromEncodedImageBufferId(bufferId);
+
+        if(removeBufferIndex != INVALID_CACHE_INDEX)
         {
-          if(iter->textureId == id)
+          EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[removeBufferIndex.GetIndex()]);
+          DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::RemoveEncodedImageBuffer(url:%s) bufferId:%d reference:%d\n", url.GetUrl().c_str(), bufferId, static_cast<int>(bufferInfo.referenceCount));
+
+          encodedImageBuffer = bufferInfo.encodedImageBuffer;
+          if(--(bufferInfo.referenceCount) <= 0)
           {
-            auto encodedImageBuffer = iter->encodedImageBuffer;
-            if(--(iter->referenceCount) <= 0)
-            {
-              mEncodedBufferTextures.erase(iter);
-            }
-            return encodedImageBuffer;
+            removeBufferInfo = true;
+            // Step 1. remove current textureId information in mTextureHashContainer.
+            RemoveHashId(bufferInfo.bufferHash, bufferId);
+            // Step 2. id life is finished. Remove it at converter
+            mTextureIdConverter.Remove(bufferId);
           }
         }
       }
     }
   }
-  return EncodedImageBuffer();
+
+  // Post removal process to avoid mEncodedImageBuffers reference problems.
+  if(removeBufferInfo)
+  {
+    // Step 3. swap last data of mEncodedImageBuffers, and pop_back.
+    RemoveTextureInfoByIndex(mEncodedImageBuffers, removeBufferIndex);
+  }
+  return encodedImageBuffer;
 }
 
 void TextureCacheManager::UseExternalResource(const VisualUrl& url)
@@ -271,14 +366,15 @@ void TextureCacheManager::UseExternalResource(const VisualUrl& url)
     std::string location = url.GetLocation();
     if(location.size() > 0u)
     {
-      TextureId id = std::stoi(location);
-      for(auto&& elem : mExternalTextures)
+      TextureId         id         = std::stoi(location);
+      TextureCacheIndex cacheIndex = GetCacheIndexFromExternalTextureId(id);
+      if(cacheIndex != INVALID_CACHE_INDEX)
       {
-        if(elem.textureId == id)
-        {
-          elem.referenceCount++;
-          return;
-        }
+        ExternalTextureInfo& textureInfo(mExternalTextures[cacheIndex.GetIndex()]);
+        DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::UseExternalResource(url:%s) type:TEXTURE, location:%s, reference:%d\n", url.GetUrl().c_str(), url.GetLocation().c_str(), static_cast<int>(textureInfo.referenceCount));
+
+        textureInfo.referenceCount++;
+        return;
       }
     }
   }
@@ -287,41 +383,99 @@ void TextureCacheManager::UseExternalResource(const VisualUrl& url)
     std::string location = url.GetLocation();
     if(location.size() > 0u)
     {
-      TextureId id = std::stoi(location);
-      for(auto&& elem : mEncodedBufferTextures)
+      TextureId         id         = std::stoi(location);
+      TextureCacheIndex cacheIndex = GetCacheIndexFromEncodedImageBufferId(id);
+      if(cacheIndex != INVALID_CACHE_INDEX)
       {
-        if(elem.textureId == id)
-        {
-          elem.referenceCount++;
-          return;
-        }
+        EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]);
+        DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::UseExternalResource(url:%s) type:BUFFER, location:%s, reference:%d\n", url.GetUrl().c_str(), url.GetLocation().c_str(), static_cast<int>(bufferInfo.referenceCount));
+
+        bufferInfo.referenceCount++;
+        return;
       }
     }
   }
 }
 
-TextureCacheManager::TextureId TextureCacheManager::GenerateUniqueTextureId()
+TextureCacheManager::TextureId TextureCacheManager::GenerateTextureId(const TextureCacheIndex& textureCacheIndex)
 {
-  return mCurrentTextureId++;
+  return mTextureIdConverter.Add(static_cast<std::uint32_t>(textureCacheIndex.indexValue));
 }
 
 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromId(const TextureCacheManager::TextureId& textureId)
 {
-  const TextureCacheIndex size = static_cast<TextureCacheIndex>(mTextureInfoContainer.size());
+  if(textureId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
 
-  for(TextureCacheIndex i = 0; i < size; ++i)
+  TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(textureId)]);
+  if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL))
   {
-    if(mTextureInfoContainer[i].textureId == textureId)
+    return INVALID_CACHE_INDEX;
+  }
+
+  DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mTextureInfoContainer.size());
+
+  return cacheIndex;
+}
+
+TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromExternalTextureId(const TextureCacheManager::TextureId& textureId)
+{
+  if(textureId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
+
+  TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(textureId)]);
+  if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE))
+  {
+    return INVALID_CACHE_INDEX;
+  }
+
+  DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mExternalTextures.size());
+
+  return cacheIndex;
+}
+
+TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromEncodedImageBufferId(const TextureCacheManager::TextureId& bufferId)
+{
+  if(bufferId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
+
+  TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(bufferId)]);
+  if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER))
+  {
+    return INVALID_CACHE_INDEX;
+  }
+
+  DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mEncodedImageBuffers.size());
+
+  return cacheIndex;
+}
+
+TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedEncodedImageBuffer(const TextureCacheManager::TextureHash& hash, const EncodedImageBuffer& encodedImageBuffer)
+{
+  // Iterate through our hashes to find a match.
+  const auto& hashIterator = mTextureHashContainer.find(hash);
+  if(hashIterator != mTextureHashContainer.cend())
+  {
+    for(const auto& id : hashIterator->second)
     {
-      return i;
+      // We have a match, now we check all the original parameters in case of a hash collision.
+      TextureCacheIndex cacheIndex = GetCacheIndexFromEncodedImageBufferId(id);
+      if(cacheIndex != INVALID_CACHE_INDEX)
+      {
+        EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]);
+
+        if(bufferInfo.encodedImageBuffer == encodedImageBuffer)
+        {
+          // The found encoded image buffer.
+          return cacheIndex;
+        }
+      }
     }
   }
 
+  // Default to an invalid ID, in case we do not find a match.
   return INVALID_CACHE_INDEX;
 }
 
 TextureCacheManager::TextureHash TextureCacheManager::GenerateHash(
-  const std::string&                    url,
+  const VisualUrl&                      url,
   const Dali::ImageDimensions&          size,
   const Dali::FittingMode::Type&        fittingMode,
   const Dali::SamplingMode::Type&       samplingMode,
@@ -329,8 +483,8 @@ TextureCacheManager::TextureHash TextureCacheManager::GenerateHash(
   const TextureCacheManager::TextureId& maskTextureId,
   const bool&                           cropToMask)
 {
-  std::vector<std::uint8_t> hashTarget(url.begin(), url.end());
-  const size_t              urlLength = url.length();
+  std::vector<std::uint8_t> hashTarget(url.GetUrl().begin(), url.GetUrl().end());
+  const size_t              urlLength = hashTarget.size();
   const uint16_t            width     = size.GetWidth();
   const uint16_t            height    = size.GetWidth();
 
@@ -394,57 +548,80 @@ TextureCacheManager::TextureHash TextureCacheManager::GenerateHash(
 
 TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedTexture(
   const TextureCacheManager::TextureHash&    hash,
-  const std::string&                         url,
+  const VisualUrl&                           url,
   const Dali::ImageDimensions&               size,
   const Dali::FittingMode::Type&             fittingMode,
   const Dali::SamplingMode::Type&            samplingMode,
   const TextureCacheManager::UseAtlas&       useAtlas,
   const TextureCacheManager::TextureId&      maskTextureId,
+  const bool&                                cropToMask,
   const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad,
-  bool                                       isAnimatedImage,
-  const bool&                                cropToMask)
+  const bool&                                isAnimatedImage)
 {
-  // Default to an invalid ID, in case we do not find a match.
-  TextureCacheIndex cacheIndex = INVALID_CACHE_INDEX;
-
   // Iterate through our hashes to find a match.
-  const TextureCacheIndex count = static_cast<TextureCacheIndex>(mTextureInfoContainer.size());
-  for(TextureCacheIndex i = 0u; i < count; ++i)
+  const auto& hashIterator = mTextureHashContainer.find(hash);
+  if(hashIterator != mTextureHashContainer.cend())
   {
-    if(mTextureInfoContainer[i].hash == hash)
+    for(const auto& textureId : hashIterator->second)
     {
       // We have a match, now we check all the original parameters in case of a hash collision.
-      TextureInfo& textureInfo(mTextureInfoContainer[i]);
-
-      if((url == textureInfo.url.GetUrl()) &&
-         (useAtlas == textureInfo.useAtlas) &&
-         (maskTextureId == textureInfo.maskTextureId) &&
-         (cropToMask == textureInfo.cropToMask) &&
-         (size == textureInfo.desiredSize) &&
-         (isAnimatedImage == textureInfo.isAnimatedImageFormat) &&
-         ((size.GetWidth() == 0 && size.GetHeight() == 0) ||
-          (fittingMode == textureInfo.fittingMode &&
-           samplingMode == textureInfo.samplingMode)))
+      TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
+      if(cacheIndex != INVALID_CACHE_INDEX)
       {
-        // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different.
-        // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false.
-        if((preMultiplyOnLoad == MultiplyOnLoad::MULTIPLY_ON_LOAD && textureInfo.preMultiplyOnLoad) || (preMultiplyOnLoad == MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY && !textureInfo.preMultiplied))
+        TextureInfo& textureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
+
+        if((url.GetUrl() == textureInfo.url.GetUrl()) &&
+           (useAtlas == textureInfo.useAtlas) &&
+           (maskTextureId == textureInfo.maskTextureId) &&
+           (cropToMask == textureInfo.cropToMask) &&
+           (size == textureInfo.desiredSize) &&
+           (isAnimatedImage == textureInfo.isAnimatedImageFormat) &&
+           ((size.GetWidth() == 0 && size.GetHeight() == 0) ||
+            (fittingMode == textureInfo.fittingMode &&
+             samplingMode == textureInfo.samplingMode)))
         {
-          // The found Texture is a match.
-          cacheIndex = i;
-          break;
+          // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different.
+          // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false.
+          if((preMultiplyOnLoad == MultiplyOnLoad::MULTIPLY_ON_LOAD && textureInfo.preMultiplyOnLoad) || (preMultiplyOnLoad == MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY && !textureInfo.preMultiplied))
+          {
+            // The found Texture is a match.
+            return cacheIndex;
+          }
         }
       }
     }
   }
 
-  return cacheIndex;
+  // Default to an invalid ID, in case we do not find a match.
+  return INVALID_CACHE_INDEX;
 }
 
 TextureCacheManager::TextureCacheIndex TextureCacheManager::AppendCache(const TextureCacheManager::TextureInfo& textureInfo)
 {
-  TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureInfoContainer.size());
+  // If we use EncodedImageBuffer, increase reference during it contains mTextureInfoContainer.
+  // This reference will be decreased when we call RemoveCache
+  if(textureInfo.url.GetProtocolType() == VisualUrl::BUFFER)
+  {
+    UseExternalResource(textureInfo.url);
+  }
+
+  TextureHash hash = textureInfo.hash;
+  TextureId   id   = textureInfo.textureId;
+
+  // Insert TextureHash container first
+  // Find exist list -or- Create new list.
+  std::vector<TextureId>& idList = mTextureHashContainer[hash];
+  // We already assume that list doesn't contain id. just emplace back
+  idList.emplace_back(id);
+
+  // Insert TextureInfo back of mTextureInfoContainer.
+  TextureCacheIndex cacheIndex = TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, mTextureInfoContainer.size());
   mTextureInfoContainer.emplace_back(textureInfo);
+
+  // Add converter id --> cacheIndex
+  // NOTE : We should assume that id already generated by GenerateTextureId function.
+  mTextureIdConverter[id] = cacheIndex;
+
   return cacheIndex;
 }
 
@@ -452,17 +629,19 @@ void TextureCacheManager::RemoveCache(const TextureCacheManager::TextureId& text
 {
   TextureCacheIndex textureInfoIndex = GetCacheIndexFromId(textureId);
 
+  bool removeTextureInfo = false;
+
   if(textureInfoIndex != INVALID_CACHE_INDEX)
   {
-    TextureInfo& textureInfo(mTextureInfoContainer[textureInfoIndex]);
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::Remove(textureId:%d) url:%s\n  cacheIdx:%d loadState:%s reference count = %d\n", textureId, textureInfo.url.GetUrl().c_str(), textureInfoIndex, GET_LOAD_STATE_STRING(textureInfo.loadState), textureInfo.referenceCount);
+    TextureInfo& textureInfo(mTextureInfoContainer[textureInfoIndex.GetIndex()]);
+
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::Remove(textureId:%d) url:%s\n  cacheIdx:%d loadState:%s reference count = %d\n", textureId, textureInfo.url.GetUrl().c_str(), textureInfoIndex.GetIndex(), GET_LOAD_STATE_STRING(textureInfo.loadState), textureInfo.referenceCount);
 
     // Decrement the reference count and check if this is the last user of this Texture.
     if(--textureInfo.referenceCount <= 0)
     {
       // This is the last remove for this Texture.
       textureInfo.referenceCount = 0;
-      bool removeTextureInfo     = false;
 
       // If loaded, we can remove the TextureInfo and the Atlas (if atlased).
       if(textureInfo.loadState == LoadState::UPLOADED)
@@ -473,7 +652,7 @@ void TextureCacheManager::RemoveCache(const TextureCacheManager::TextureId& text
         }
         removeTextureInfo = true;
       }
-      else if(textureInfo.loadState == LoadState::LOADING)
+      else if(textureInfo.loadState == LoadState::LOADING || textureInfo.loadState == LoadState::MASK_APPLYING)
       {
         // We mark the textureInfo for removal.
         // Once the load has completed, this method will be called again.
@@ -491,10 +670,41 @@ void TextureCacheManager::RemoveCache(const TextureCacheManager::TextureId& text
         // If url location is BUFFER, decrease reference count of EncodedImageBuffer.
         if(textureInfo.url.IsBufferResource())
         {
-          RemoveExternalEncodedImageBuffer(textureInfo.url.GetUrl());
+          RemoveEncodedImageBuffer(textureInfo.url.GetUrl());
         }
+
         // Permanently remove the textureInfo struct.
-        mTextureInfoContainer.erase(mTextureInfoContainer.begin() + textureInfoIndex);
+
+        // Step 1. remove current textureId information in mTextureHashContainer.
+        RemoveHashId(textureInfo.hash, textureId);
+        // Step 2. make textureId is not using anymore. After this job, we can reuse textureId.
+        mTextureIdConverter.Remove(textureId);
+      }
+    }
+  }
+
+  // Post removal process to avoid mTextureInfoContainer reference problems.
+  if(removeTextureInfo)
+  {
+    // Step 3. swap last data of TextureInfoContainer, and pop_back.
+    RemoveTextureInfoByIndex(mTextureInfoContainer, textureInfoIndex);
+  }
+}
+
+void TextureCacheManager::RemoveHashId(const TextureCacheManager::TextureHash& textureHash, const TextureCacheManager::TextureId& textureId)
+{
+  auto hashIterator = mTextureHashContainer.find(textureHash);
+  if(hashIterator != mTextureHashContainer.end())
+  {
+    auto        hashIdList     = hashIterator->second;
+    const auto& hashIdIterator = std::find(hashIdList.cbegin(), hashIdList.cend(), textureId);
+    if(hashIdIterator != hashIdList.cend())
+    {
+      hashIdList.erase(hashIdIterator);
+      if(hashIdList.size() == 0)
+      {
+        // If id list in current hash is empty, remove it self in the container.
+        mTextureHashContainer.erase(hashIterator);
       }
     }
   }
index 3196e24..dbd3e62 100644 (file)
@@ -18,7 +18,9 @@
  */
 
 // EXTERNAL INCLUDES
+#include <dali/devel-api/common/free-list.h>
 #include <dali/public-api/adaptor-framework/encoded-image-buffer.h>
+#include <unordered_map>
 
 // INTERNAL INCLUDES
 #include <dali-toolkit/internal/texture-manager/texture-manager-type.h>
@@ -38,11 +40,29 @@ namespace Internal
  * You can Convert TextureId into TextureCacheIndex by this class.
  *
  * Also, You can store external TextureSet or EncodedImageBuffer here.
+ *
+ * There are 3 type of CachedContainer in this manager
+ *  - mTextureInfoContainer : Cache all kind of textures that need some load/upload jobs.
+ *                            All kind of images that visual using (not vector image) will be stored here.
+ *                            This container will use TEXTURE_CACHE_INDEX_TYPE_LOCAL
+ *
+ *  - mExternalTextures : External appended TextureSet cache container.
+ *                        External TextureSet can be Something like NativeImageSource, FrameBuffer and PixelData.
+ *                        This container will use TEXTURE_CACHE_INDEX_TYPE_TEXTURE
+ *                        The textureId will be used for VisualUrl. ex) dali://1
+ *
+ *  - mEncodedImageBuffers : External appended EncodedImageBuffer cache container.
+ *                           This container will use TEXTURE_CACHE_INDEX_TYPE_BUFFER
+ *                           The bufferId will be used for VisualUrl. ex) enbuf://1
+ *                           Note that this bufferId is not equal with textureId in mTextureInfoContainer.
  */
 class TextureCacheManager
 {
 public:
   // Copy enum and types and const values that TextureCacheManager will use.
+  using TextureCacheIndexType = TextureManagerType::TextureCacheIndexType;
+  using TextureCacheIndexData = TextureManagerType::TextureCacheIndexData;
+
   using TextureId         = TextureManagerType::TextureId;
   using TextureCacheIndex = TextureManagerType::TextureCacheIndex;
   using TextureHash       = TextureManagerType::TextureHash;
@@ -81,6 +101,7 @@ public:
 
   /**
    * @brief Get the current state of a texture
+   * @note This API doesn't consider encodedimagebuffer.
    * @param[in] textureId The texture id to query
    * @return The loading state if the texture is valid, or NOT_STARTED if the textureId
    * is not valid.
@@ -89,6 +110,7 @@ public:
 
   /**
    * @brief Get the current state of a texture
+   * @note This API doesn't consider external & encodedimagebuffer.
    * @param[in] textureId The texture id to query
    * @return The loading state if the texture is valid, or NOT_STARTED if the textureId
    * is not valid.
@@ -111,17 +133,17 @@ public:
 
   /**
    * @brief Get the encoded image buffer
-   * @param[in] textureId The textureId to look up
-   * @return the encoded image buffer, or an empty handle if textureId is not valid
+   * @param[in] bufferId The bufferId to look up
+   * @return the encoded image buffer, or an empty handle if bufferId is not valid
    */
-  EncodedImageBuffer GetEncodedImageBuffer(const TextureCacheManager::TextureId& textureId);
+  EncodedImageBuffer GetEncodedImageBuffer(const TextureCacheManager::TextureId& bufferId);
 
   /**
    * @brief Get the encoded image buffer by VisualUrl
    * @param[in] url The url to look up
    * @return the encoded image buffer, or an empty handle if url is not buffer resource or buffer is not valid
    */
-  EncodedImageBuffer GetEncodedImageBuffer(const std::string& url);
+  EncodedImageBuffer GetEncodedImageBuffer(const VisualUrl& url);
 
   /**
    * Adds an external texture to the texture manager
@@ -131,28 +153,28 @@ public:
   std::string AddExternalTexture(const TextureSet& texture);
 
   /**
-   * Adds an external encoded image buffer to the texture manager
+   * Adds an encoded image buffer to the texture manager
    * @param[in] encodedImageBuffer The image buffer to add
    * @return string containing the URL for the texture
    */
-  std::string AddExternalEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer);
+  std::string AddEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer);
 
   /**
    * Removes an external texture from texture manager
    * @param[in] url The string containing the texture to remove
    * @return handle to the texture
    */
-  TextureSet RemoveExternalTexture(const std::string& url);
+  TextureSet RemoveExternalTexture(const VisualUrl& url);
 
   /**
    * Removes an external encoded image buffer from texture manager
    * @param[in] url The string containing the encoded image buffer to remove
    * @return handle to the encoded image buffer
    */
-  EncodedImageBuffer RemoveExternalEncodedImageBuffer(const std::string& url);
+  EncodedImageBuffer RemoveEncodedImageBuffer(const VisualUrl& url);
 
   /**
-   * @brief Notify that external textures or external encoded image buffers are used.
+   * @brief Notify that external textures or encoded image buffers are used.
    * @param[in] url The URL of the texture to use.
    */
   void UseExternalResource(const VisualUrl& url);
@@ -161,10 +183,11 @@ public:
   // To Generate / Get / Remove TextureId.
 
   /**
-   * @brief Generates a new, unique TextureId
-   * @return A unique TextureId
+   * @brief Generates a new valid TextureId.
+   * @param[in] textureCacheIndex the index of the cache container. If we don't setup this value, default is INVALID_CACHE_INDEX
+   * @return A TextureId
    */
-  TextureCacheManager::TextureId GenerateUniqueTextureId();
+  TextureCacheManager::TextureId GenerateTextureId(const TextureCacheIndex& textureCacheIndex = INVALID_CACHE_INDEX);
 
   /**
    * @brief Used to lookup an index into the TextureInfoContainer from a TextureId
@@ -188,7 +211,7 @@ public:
    * @return                     A hash of the provided data for caching.
    */
   TextureCacheManager::TextureHash GenerateHash(
-    const std::string&                    url,
+    const VisualUrl&                      url,
     const Dali::ImageDimensions&          size,
     const Dali::FittingMode::Type&        fittingMode,
     const Dali::SamplingMode::Type&       samplingMode,
@@ -206,26 +229,27 @@ public:
    * @param[in] samplingMode      The SamplingMode to use
    * @param[in] useAtlas          True if atlased
    * @param[in] maskTextureId     Optional texture ID to use to mask this image
-   * @param[in] preMultiplyOnLoad If the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha.
+   * @param[in] preMultiplyOnLoad if the image's color should be multiplied by it's alpha. Set to OFF if there is no alpha.
    * @param[in] isAnimatedImage   True if the texture is from animated image.
    * @param[in] cropToMask        True if crop to mask
-   * @return                      A TextureCacheId of a cached Texture if found. Or INVALID_CACHE_INDEX if not found.
+   * @return                      A TextureCacheIndex of a cached Texture if found. Or INVALID_CACHE_INDEX if not found.
    */
   TextureCacheManager::TextureCacheIndex FindCachedTexture(
     const TextureCacheManager::TextureHash&    hash,
-    const std::string&                         url,
+    const VisualUrl&                           url,
     const Dali::ImageDimensions&               size,
     const Dali::FittingMode::Type&             fittingMode,
     const Dali::SamplingMode::Type&            samplingMode,
     const TextureCacheManager::UseAtlas&       useAtlas,
     const TextureCacheManager::TextureId&      maskTextureId,
+    const bool&                                cropToMask,
     const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad,
-    bool                                       isAnimatedImage,
-    const bool&                                cropToMask);
+    const bool&                                isAnimatedImage);
 
   /**
    * @brief Append a Texture to the TextureCacheManager.
    * @note This API doesn't check duplication of TextureId.
+   * @note This API doesn't consider external & encodedimagebuffer.
    *
    * @param[in] textureInfo TextureInfo that want to cache in container.
    * @return Index of newly appended texture info.
@@ -234,6 +258,7 @@ public:
 
   /**
    * @brief Remove a Texture from the TextureCacheManager.
+   * @note This API doesn't consider external & encodedimagebuffer.
    *
    * Textures are cached and therefore only the removal of the last
    * occurrence of a Texture will cause its removal internally.
@@ -251,7 +276,7 @@ public:
    */
   inline TextureCacheManager::TextureInfo& operator[](const TextureCacheManager::TextureCacheIndex& textureCacheIndex) noexcept
   {
-    return mTextureInfoContainer[textureCacheIndex];
+    return mTextureInfoContainer[textureCacheIndex.GetIndex()];
   }
 
   /**
@@ -267,6 +292,9 @@ public:
 private:
   // Private defined structs.
 
+  /**
+   * @brief This struct is used to manage the life-cycle of ExternalTexture url.
+   */
   struct ExternalTextureInfo
   {
     ExternalTextureInfo(const TextureCacheManager::TextureId& textureId,
@@ -281,23 +309,77 @@ private:
     std::int16_t                   referenceCount;
   };
 
-  struct EncodedBufferTextureInfo
+  /**
+   * @brief This struct is used to manage the life-cycle of EncodedImageBuffer url.
+   */
+  struct EncodedImageBufferInfo
   {
-    EncodedBufferTextureInfo(const TextureCacheManager::TextureId& textureId,
-                             const EncodedImageBuffer&             encodedImageBuffer)
-    : textureId(textureId),
+    EncodedImageBufferInfo(const TextureCacheManager::TextureId&   bufferId,
+                           const TextureCacheManager::TextureHash& bufferHash,
+                           const EncodedImageBuffer&               encodedImageBuffer)
+    : bufferId(bufferId),
+      bufferHash(bufferHash),
       encodedImageBuffer(encodedImageBuffer),
       referenceCount(1u)
     {
     }
-    TextureCacheManager::TextureId textureId;
-    EncodedImageBuffer             encodedImageBuffer;
-    std::int16_t                   referenceCount;
+    TextureCacheManager::TextureId   bufferId;
+    TextureCacheManager::TextureHash bufferHash;
+    EncodedImageBuffer               encodedImageBuffer;
+    std::int16_t                     referenceCount;
   };
 
-  typedef std::vector<TextureCacheManager::TextureInfo>              TextureInfoContainerType;              ///< The container type used to manage the life-cycle and caching of Textures
-  typedef std::vector<TextureCacheManager::ExternalTextureInfo>      ExternalTextureInfoContainerType;      ///< The container type used to manage the life-cycle and caching of Textures
-  typedef std::vector<TextureCacheManager::EncodedBufferTextureInfo> EncodedBufferTextureInfoContainerType; ///< The container type used to manage the life-cycle and caching of Textures
+  typedef Dali::FreeList TextureIdConverterType; ///< The converter type from TextureId to index of TextureInfoContainer.
+
+  typedef std::unordered_map<TextureCacheManager::TextureHash, std::vector<TextureCacheManager::TextureId>> TextureHashContainerType;            ///< The container type used to fast-find the TextureId by TextureHash.
+  typedef std::vector<TextureCacheManager::TextureInfo>                                                     TextureInfoContainerType;            ///< The container type used to manage the life-cycle and caching of Textures
+  typedef std::vector<TextureCacheManager::ExternalTextureInfo>                                             ExternalTextureInfoContainerType;    ///< The container type used to manage the life-cycle and caching of ExternalTexture url
+  typedef std::vector<TextureCacheManager::EncodedImageBufferInfo>                                          EncodedImageBufferInfoContainerType; ///< The container type used to manage the life-cycle and caching of EncodedImageBuffer url
+
+private:
+  // Private API: only used internally
+
+  /**
+   * @brief Used to lookup an index into the ExternalTextureInfoContainer from a textureId
+   * @param[in] textureId The TextureId to look up
+   * @return              The cache index
+   */
+  TextureCacheManager::TextureCacheIndex GetCacheIndexFromExternalTextureId(const TextureCacheManager::TextureId& textureId);
+
+  /**
+   * @brief Used to lookup an index into the EncodedImageBufferInfoContainer from a bufferId
+   * @param[in] bufferId The bufferId to look up
+   * @return             The cache index
+   */
+  TextureCacheManager::TextureCacheIndex GetCacheIndexFromEncodedImageBufferId(const TextureCacheManager::TextureId& bufferId);
+
+  /**
+   * @brief Looks up a cached encoded image buffer cached by its hash.
+   * If found, the given parameters are used to check there is no hash-collision.
+   * @param[in] hash               The hash to look up
+   * @param[in] encodedImageBuffer The image buffer to load
+   * @return                       A TextureCacheIndex of a cached Texture if found. Or INVALID_CACHE_INDEX if not found.
+   */
+  TextureCacheManager::TextureCacheIndex FindCachedEncodedImageBuffer(const TextureCacheManager::TextureHash& hash, const EncodedImageBuffer& encodedImageBuffer);
+
+  /**
+   * @brief Remove id in HashContainer.
+   * @param[in] hash The hash of the texture/buffer to be delete
+   * @param[in] id   The texture/buffer id to be deleted.
+   */
+  void RemoveHashId(const TextureCacheManager::TextureHash& hash, const TextureCacheManager::TextureId& id);
+
+  /**
+   * @brief Remove data from container by the TextureCacheIndex.
+   * It also valiate the TextureIdConverter internally.
+   * We will assume that only valid TextureCacheIndex will come.
+   *
+   * @tparam ContainerType The type of container. It will automatically defined
+   * @param[in] cacheContainer The container that will remove texture info.
+   * @param[in] removeContainerIndex The index of texture info that will remove.
+   */
+  template<class ContainerType>
+  void RemoveTextureInfoByIndex(ContainerType& cacheContainer, const TextureCacheManager::TextureCacheIndex& removeContainerIndex);
 
 private:
   /**
@@ -310,11 +392,13 @@ private:
    */
   TextureCacheManager& operator=(const TextureCacheManager& rhs) = delete;
 
-private:                                                          // Member Variables:
-  TextureInfoContainerType              mTextureInfoContainer{};  ///< Used to manage the life-cycle and caching of Textures
-  ExternalTextureInfoContainerType      mExternalTextures{};      ///< Externally provided textures
-  EncodedBufferTextureInfoContainerType mEncodedBufferTextures{}; ///< Externally encoded buffer textures
-  TextureCacheManager::TextureId        mCurrentTextureId;        ///< The current value used for the unique Texture Id generation
+private:                                            // Member Variables:
+  TextureIdConverterType   mTextureIdConverter{};   ///< Convert TextureId into various container's index.
+  TextureHashContainerType mTextureHashContainer{}; ///< Used to manage the life-cycle and caching of Textures + EncodedImageBuffer by TextureHash
+
+  TextureInfoContainerType            mTextureInfoContainer{}; ///< Used to manage the life-cycle and caching of Textures
+  ExternalTextureInfoContainerType    mExternalTextures{};     ///< Externally provided textures
+  EncodedImageBufferInfoContainerType mEncodedImageBuffers{};  ///< Externally encoded image buffer
 };
 
 } // namespace Internal
index a3cb054..390bb20 100644 (file)
@@ -123,6 +123,7 @@ TextureManager::TextureManager()
   mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]() { return TextureAsyncLoadingHelper(*this); }),
   mLifecycleObservers(),
   mLoadQueue(),
+  mRemoveQueue(),
   mQueueLoadFlag(false)
 {
   // Initialize the AddOn
@@ -211,7 +212,7 @@ Devel::PixelBuffer TextureManager::LoadPixelBuffer(
     {
       if(url.IsBufferResource())
       {
-        const EncodedImageBuffer& encodedImageBuffer = mTextureCacheManager.GetEncodedImageBuffer(url.GetUrl());
+        const EncodedImageBuffer& encodedImageBuffer = mTextureCacheManager.GetEncodedImageBuffer(url);
         if(encodedImageBuffer)
         {
           pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection);
@@ -468,10 +469,10 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
   TextureCacheIndex cacheIndex  = INVALID_CACHE_INDEX;
   if(storageType != StorageType::RETURN_PIXEL_BUFFER && useCache)
   {
-    textureHash = mTextureCacheManager.GenerateHash(url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, cropToMask);
+    textureHash = mTextureCacheManager.GenerateHash(url, desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, cropToMask);
 
     // Look up the texture by hash. Note: The extra parameters are used in case of a hash collision.
-    cacheIndex = mTextureCacheManager.FindCachedTexture(textureHash, url.GetUrl(), desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, preMultiplyOnLoad, (animatedImageLoading) ? true : false, cropToMask);
+    cacheIndex = mTextureCacheManager.FindCachedTexture(textureHash, url, desiredSize, fittingMode, samplingMode, useAtlas, maskTextureId, cropToMask, preMultiplyOnLoad, (animatedImageLoading) ? true : false);
   }
 
   TextureManager::TextureId textureId = INVALID_TEXTURE_ID;
@@ -489,41 +490,19 @@ 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\n", url.GetUrl().c_str(), observer, cacheIndex, textureId);
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture id@%d, textureId=%d premultiplied=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, mTextureCacheManager[cacheIndex].preMultiplied ? 1 : 0);
   }
 
   if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required
   {
-    if(VisualUrl::BUFFER == url.GetProtocolType())
-    {
-      std::string location = url.GetLocation();
-      if(location.size() > 0u)
-      {
-        TextureId                 targetId           = std::stoi(location);
-        const EncodedImageBuffer& encodedImageBuffer = mTextureCacheManager.GetEncodedImageBuffer(targetId);
-        if(encodedImageBuffer)
-        {
-          textureId = targetId;
-
-          // Increase EncodedImageBuffer reference during it contains mTextureInfoContainer.
-          // TODO! We should change action when reload policy is FORCE.
-          // Eunki Hong will fix it after refactoring patch merged.
-          mTextureCacheManager.UseExternalResource(url.GetUrl());
-        }
-      }
-    }
-
-    if(textureId == INVALID_TEXTURE_ID)
-    {
-      textureId = mTextureCacheManager.GenerateUniqueTextureId();
-    }
+    textureId = mTextureCacheManager.GenerateTextureId();
 
     bool preMultiply = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD);
 
     // Cache new texutre, and get cacheIndex.
     cacheIndex = mTextureCacheManager.AppendCache(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, useAtlas, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex));
 
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId);
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId);
   }
 
   // The below code path is common whether we are using the cache or not.
@@ -544,7 +523,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
      TextureManager::LoadState::MASK_APPLIED != textureInfo.loadState &&
      TextureManager::LoadState::CANCELLED != textureInfo.loadState)
   {
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::RequestLoad( url=%s observer=%p ) ForcedReload cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex, textureId);
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::RequestLoad( url=%s observer=%p ) ForcedReload cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId);
 
     textureInfo.loadState = TextureManager::LoadState::NOT_STARTED;
   }
@@ -651,19 +630,30 @@ TextureManager::TextureId TextureManager::RequestLoadInternal(
 
 void TextureManager::Remove(const TextureManager::TextureId& textureId, TextureUploadObserver* observer)
 {
-  // Remove textureId in CacheManager.
-  mTextureCacheManager.RemoveCache(textureId);
-
-  if(observer)
+  if(textureId != INVALID_TEXTURE_ID)
   {
-    // Remove element from the LoadQueue
-    for(auto&& element : mLoadQueue)
+    if(mQueueLoadFlag)
+    {
+      // Remove textureId after NotifyObserver finished
+      mRemoveQueue.PushBack(textureId);
+    }
+    else
     {
-      if(element.mObserver == observer)
+      // Remove textureId in CacheManager.
+      mTextureCacheManager.RemoveCache(textureId);
+    }
+
+    if(observer)
+    {
+      // Remove element from the LoadQueue
+      for(auto&& element : mLoadQueue)
       {
-        // Do not erase the item. We will clear it later in ProcessQueuedTextures().
-        element.mObserver = nullptr;
-        break;
+        if(element.mObserver == observer)
+        {
+          // Do not erase the item. We will clear it later in ProcessLoadQueue().
+          element.mObserver = nullptr;
+          break;
+        }
       }
     }
   }
@@ -679,7 +669,7 @@ Devel::PixelBuffer TextureManager::LoadImageSynchronously(
   Devel::PixelBuffer pixelBuffer;
   if(url.IsBufferResource())
   {
-    const EncodedImageBuffer& encodedImageBuffer = mTextureCacheManager.GetEncodedImageBuffer(url.GetUrl());
+    const EncodedImageBuffer& encodedImageBuffer = mTextureCacheManager.GetEncodedImageBuffer(url);
     if(encodedImageBuffer)
     {
       pixelBuffer = LoadImageFromBuffer(encodedImageBuffer.GetRawBuffer(), desiredSize, fittingMode, samplingMode, orientationCorrection);
@@ -789,7 +779,7 @@ void TextureManager::LoadTexture(TextureManager::TextureInfo& textureInfo, Textu
   ObserveTexture(textureInfo, observer);
 }
 
-void TextureManager::ProcessQueuedTextures()
+void TextureManager::ProcessLoadQueue()
 {
   for(auto&& element : mLoadQueue)
   {
@@ -815,6 +805,15 @@ void TextureManager::ProcessQueuedTextures()
   mLoadQueue.Clear();
 }
 
+void TextureManager::ProcessRemoveQueue()
+{
+  for(const auto& textureId : mRemoveQueue)
+  {
+    mTextureCacheManager.RemoveCache(textureId);
+  }
+  mRemoveQueue.Clear();
+}
+
 void TextureManager::ObserveTexture(TextureManager::TextureInfo& textureInfo,
                                     TextureUploadObserver*       observer)
 {
@@ -829,13 +828,13 @@ void TextureManager::ObserveTexture(TextureManager::TextureInfo& textureInfo,
 
 void TextureManager::AsyncLoadComplete(const TextureManager::TextureId& textureId, Devel::PixelBuffer pixelBuffer)
 {
-  DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( textureId:%d )\n", textureId);
   TextureCacheIndex cacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureId);
+  DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( textureId:%d CacheIndex:%d )\n", textureId, cacheIndex.GetIndex());
   if(cacheIndex != INVALID_CACHE_INDEX)
   {
     TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]);
 
-    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "  textureId:%d Url:%s CacheIndex:%d LoadState: %s\n", textureInfo.textureId, textureInfo.url.GetUrl().c_str(), cacheIndex, GET_LOAD_STATE_STRING(textureInfo.loadState));
+    DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "  textureId:%d Url:%s CacheIndex:%d LoadState: %s\n", textureInfo.textureId, textureInfo.url.GetUrl().c_str(), cacheIndex.GetIndex(), GET_LOAD_STATE_STRING(textureInfo.loadState));
 
     if(textureInfo.loadState != LoadState::CANCELLED)
     {
@@ -939,11 +938,12 @@ void TextureManager::CheckForWaitingTexture(TextureManager::TextureInfo& maskTex
 {
   // Search the cache, checking if any texture has this texture id as a
   // maskTextureId:
-  const TextureCacheIndex size = static_cast<TextureCacheIndex>(mTextureCacheManager.size());
+  const std::size_t size = mTextureCacheManager.size();
 
   const bool maskLoadSuccess = maskTextureInfo.loadState == LoadState::LOAD_FINISHED ? true : false;
 
-  for(TextureCacheIndex cacheIndex = 0; cacheIndex < size; ++cacheIndex)
+  // TODO : Refactorize here to not iterate whole cached image.
+  for(TextureCacheIndex cacheIndex = TextureCacheIndex(TextureManagerType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, 0u); cacheIndex.GetIndex() < size; ++cacheIndex.detailValue.index)
   {
     if(mTextureCacheManager[cacheIndex].maskTextureId == maskTextureInfo.textureId &&
        mTextureCacheManager[cacheIndex].loadState == LoadState::WAITING_FOR_MASK)
@@ -1052,7 +1052,7 @@ 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, textureInfo.url.GetUrl().c_str(), GET_LOAD_STATE_STRING(textureInfo.loadState));
+    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));
 
     // It is possible for the observer to be deleted.
     // Disconnect and remove the observer first.
@@ -1072,7 +1072,8 @@ void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, c
   }
 
   mQueueLoadFlag = false;
-  ProcessQueuedTextures();
+  ProcessLoadQueue();
+  ProcessRemoveQueue();
 
   if(info->storageType == StorageType::RETURN_PIXEL_BUFFER && info->observerList.Count() == 0)
   {
@@ -1082,10 +1083,10 @@ void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, c
 
 void TextureManager::ObserverDestroyed(TextureUploadObserver* observer)
 {
-  const TextureCacheIndex count = static_cast<TextureCacheIndex>(mTextureCacheManager.size());
-  for(TextureCacheIndex i = 0; i < count; ++i)
+  const std::size_t size = mTextureCacheManager.size();
+  for(TextureCacheIndex cacheIndex = TextureCacheIndex(TextureManagerType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, 0u); cacheIndex.GetIndex() < size; ++cacheIndex.detailValue.index)
   {
-    TextureInfo& textureInfo(mTextureCacheManager[i]);
+    TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]);
     for(TextureInfo::ObserverListType::Iterator j = textureInfo.observerList.Begin();
         j != textureInfo.observerList.End();)
     {
index 01f62ee..ee1f065 100644 (file)
@@ -283,11 +283,11 @@ public:
   }
 
   /**
-   * @copydoc TextureCacheManager::RemoveExternalEncodedImageBuffer
+   * @copydoc TextureCacheManager::RemoveEncodedImageBuffer
    */
-  inline EncodedImageBuffer RemoveExternalEncodedImageBuffer(const std::string& url)
+  inline EncodedImageBuffer RemoveEncodedImageBuffer(const std::string& url)
   {
-    return mTextureCacheManager.RemoveExternalEncodedImageBuffer(url);
+    return mTextureCacheManager.RemoveEncodedImageBuffer(url);
   }
 
   /**
@@ -315,11 +315,11 @@ public:
   }
 
   /**
-   * @copydoc TextureCacheManager::AddExternalEncodedImageBuffer
+   * @copydoc TextureCacheManager::AddEncodedImageBuffer
    */
-  inline std::string AddExternalEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer)
+  inline std::string AddEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer)
   {
-    return mTextureCacheManager.AddExternalEncodedImageBuffer(encodedImageBuffer);
+    return mTextureCacheManager.AddEncodedImageBuffer(encodedImageBuffer);
   }
 
 public: // Load Request API
@@ -536,7 +536,12 @@ private:
   /**
    * @brief Initiate load of textures queued whilst NotifyObservers invoking callbacks.
    */
-  void ProcessQueuedTextures();
+  void ProcessLoadQueue();
+
+  /**
+   * @brief Initiate remove of texture queued whilst NotifyObservers invoking callbacks.
+   */
+  void ProcessRemoveQueue();
 
   /**
    * Add the observer to the observer list
@@ -624,9 +629,10 @@ private:                                    // Member Variables:
   RoundRobinContainerView<TextureAsyncLoadingHelper> mAsyncLocalLoaders;  ///< The Asynchronous image loaders used to provide all local async loads
   RoundRobinContainerView<TextureAsyncLoadingHelper> mAsyncRemoteLoaders; ///< The Asynchronous image loaders used to provide all remote async loads
 
-  Dali::Vector<LifecycleObserver*> mLifecycleObservers; ///< Lifecycle observers of texture manager
-  Dali::Vector<LoadQueueElement>   mLoadQueue;          ///< Queue of textures to load after NotifyObservers
-  bool                             mQueueLoadFlag;      ///< Flag that causes Load Textures to be queued.
+  Dali::Vector<LifecycleObserver*>        mLifecycleObservers; ///< Lifecycle observers of texture manager
+  Dali::Vector<LoadQueueElement>          mLoadQueue;          ///< Queue of textures to load after NotifyObservers
+  Dali::Vector<TextureManager::TextureId> mRemoveQueue;        ///< Queue of textures to remove after NotifyObservers
+  bool                                    mQueueLoadFlag;      ///< Flag that causes Load Textures to be queued.
 };
 
 } // namespace Internal
index 514e544..366e7cb 100644 (file)
@@ -38,18 +38,123 @@ namespace Internal
  */
 namespace TextureManagerType
 {
+// Enum!
+
+enum TextureCacheIndexType
+{
+  TEXTURE_CACHE_INDEX_FREE_LIST  = 0, ///< Only for FreeList. We should not use this for TextureCacheIndex.
+  TEXTURE_CACHE_INDEX_TYPE_LOCAL = 1,
+  TEXTURE_CACHE_INDEX_TYPE_TEXTURE,
+  TEXTURE_CACHE_INDEX_TYPE_BUFFER,
+  TEXTURE_CACHE_INDEX_TYPE_MASKING, ///< Not implemented yet.
+  TEXTURE_CACHE_INDEX_TYPE_MAX = 7, ///< Maximum number of cache type we can use.
+};
+
+// Union!
+
+/**
+ * @brief standard union type of texture index.
+ * Due to the FreeList can only use for uint32_t and we need to seperate
+ * each index per container type, we can only hold maximum 2^28 textures
+ * at the same time.
+ *      0 ~   2^28 - 1 : index of FreeList. TextureCacheManager will not use it.
+ *   2^28 ~ 2*2^28 - 1 : index of mTextureInfoContainer, the main texture container.
+ * 2*2^28 ~ 3*2^28 - 1 : index of mExternalTextures.
+ * 3*2^28 ~ 4*2^28 - 1 : index of mEncodedBufferTextures.
+ */
+union TextureCacheIndexData
+{
+  TextureCacheIndexData() = default;
+  constexpr TextureCacheIndexData(const std::uint32_t& index)
+  : indexValue(index)
+  {
+  }
+  constexpr explicit TextureCacheIndexData(const std::int32_t& index)
+  : indexValue(static_cast<std::uint32_t>(index))
+  {
+  }
+  constexpr TextureCacheIndexData(const TextureCacheIndexType& type, const std::uint32_t& index)
+  : detailValue{index, type}
+  {
+  }
+  constexpr TextureCacheIndexData(const TextureCacheIndexData& indexData)
+  : indexValue(indexData.indexValue)
+  {
+  }
+  constexpr TextureCacheIndexData(TextureCacheIndexData&& indexData)
+  : indexValue(indexData.indexValue)
+  {
+  }
+
+  TextureCacheIndexData& operator=(const std::uint32_t& index)
+  {
+    indexValue = index;
+    return *this;
+  }
+  TextureCacheIndexData& operator=(const TextureCacheIndexData& rhs)
+  {
+    indexValue = rhs.indexValue;
+    return *this;
+  }
+  TextureCacheIndexData& operator=(TextureCacheIndexData&& rhs)
+  {
+    indexValue = rhs.indexValue;
+    return *this;
+  }
+
+  constexpr operator std::uint32_t() const
+  {
+    return indexValue;
+  }
+  constexpr operator std::uint32_t()
+  {
+    return indexValue;
+  }
+  constexpr explicit operator std::int32_t() const
+  {
+    return static_cast<std::int32_t>(indexValue);
+  }
+  constexpr explicit operator std::int32_t()
+  {
+    return static_cast<std::int32_t>(indexValue);
+  }
+
+  // Return detailValue.index. - the real index of datailValue.type container
+  constexpr inline std::uint32_t GetIndex() const
+  {
+    return detailValue.index;
+  }
+
+  inline constexpr bool operator==(const TextureCacheIndexData& rhs)
+  {
+    return indexValue == rhs.indexValue;
+  }
+  inline constexpr bool operator<(const TextureCacheIndexData& rhs)
+  {
+    return indexValue < rhs.indexValue;
+  }
+
+  // Data area
+  std::uint32_t indexValue;
+  struct
+  {
+    unsigned int          index : 28;
+    TextureCacheIndexType type : 4;
+  } detailValue;
+};
+
 // Typedef:
 
-typedef std::int32_t TextureId;         ///< The TextureId type. This is used as a handle to refer to a particular Texture.
-typedef std::int32_t TextureCacheIndex; ///< The TextureCacheIndex type. This is used as a handles to refer to a particular Texture in TextureCacheManager.
-                                        ///  Note : For the same Texture, TextureId will not be changed. But TextureCacheIndex can be chaged when TextureCacheManager
-                                        ///  Internal container informations changed by Append or Remove.
-typedef std::size_t TextureHash;        ///< The type used to store the hash used for Texture caching.
+typedef std::int32_t          TextureId;         ///< The TextureId type. This is used as a handle to refer to a particular Texture.
+typedef TextureCacheIndexData TextureCacheIndex; ///< The TextureCacheIndex type. This is used as a handles to refer to a particular Texture in TextureCacheManager.
+                                                 ///  Note : For the same Texture, TextureId will not be changed. But TextureCacheIndex can be chaged when TextureCacheManager
+                                                 ///  Internal container informations changed by Append or Remove.
+typedef std::size_t TextureHash;                 ///< The type used to store the hash used for Texture caching.
 
 // Constant values:
 
 static constexpr TextureId         INVALID_TEXTURE_ID  = -1; ///< Used to represent a null TextureId or error
-static constexpr TextureCacheIndex INVALID_CACHE_INDEX = -1; ///< Used to represent a null TextureCacheIndex or error
+static constexpr TextureCacheIndex INVALID_CACHE_INDEX = 0;  ///< Used to represent a null TextureCacheIndex or error
 
 // Enum classes:
 
index e971be6..a2e9096 100644 (file)
@@ -202,7 +202,7 @@ ImageVisual::~ImageVisual()
       else if(mImageUrl.IsBufferResource())
       {
         TextureManager& textureManager = mFactoryCache.GetTextureManager();
-        textureManager.RemoveExternalEncodedImageBuffer(mImageUrl.GetUrl());
+        textureManager.RemoveEncodedImageBuffer(mImageUrl.GetUrl());
       }
     }