Refactoring TextureManager cache as Dali::FreeList
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / texture-manager / texture-cache-manager.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include "texture-cache-manager.h"
20
21 // EXTERNAL HEADERS
22 #include <dali/devel-api/common/hash.h>
23 #include <dali/integration-api/debug.h>
24
25 // INTERNAL HEADERS
26
27 namespace Dali
28 {
29 namespace Toolkit
30 {
31 namespace Internal
32 {
33 #ifdef DEBUG_ENABLED
34 extern Debug::Filter* gTextureManagerLogFilter; ///< Define at texture-manager-impl.cpp
35
36 // clang-format off
37 #define GET_LOAD_STATE_STRING(loadState) \
38   loadState == TextureManagerType::LoadState::NOT_STARTED      ? "NOT_STARTED"      : \
39   loadState == TextureManagerType::LoadState::LOADING          ? "LOADING"          : \
40   loadState == TextureManagerType::LoadState::LOAD_FINISHED    ? "LOAD_FINISHED"    : \
41   loadState == TextureManagerType::LoadState::WAITING_FOR_MASK ? "WAITING_FOR_MASK" : \
42   loadState == TextureManagerType::LoadState::MASK_APPLYING    ? "MASK_APPLYING"    : \
43   loadState == TextureManagerType::LoadState::MASK_APPLIED     ? "MASK_APPLIED"     : \
44   loadState == TextureManagerType::LoadState::UPLOADED         ? "UPLOADED"         : \
45   loadState == TextureManagerType::LoadState::CANCELLED        ? "CANCELLED"        : \
46   loadState == TextureManagerType::LoadState::LOAD_FAILED      ? "LOAD_FAILED"      : \
47                                                                  "Unknown"
48 // clang-format on
49 #endif
50
51 // Due to the compile issue, this specialized template code must be defined top of this code.
52 template<>
53 void TextureCacheManager::RemoveTextureInfoByIndex<TextureCacheManager::EncodedImageBufferInfoContainerType>(TextureCacheManager::EncodedImageBufferInfoContainerType& cacheContainer, const TextureCacheManager::TextureCacheIndex& removeContainerIndex)
54 {
55   // Swap last data of cacheContainer.
56   if(static_cast<std::size_t>(removeContainerIndex.GetIndex() + 1) < cacheContainer.size())
57   {
58     // First, change the cache index infomations inside of converter
59     mTextureIdConverter[cacheContainer.back().bufferId] = static_cast<std::uint32_t>(removeContainerIndex);
60
61     // After change converter, swap the value between current data and last data.
62     std::swap(cacheContainer[removeContainerIndex.GetIndex()], cacheContainer.back());
63   }
64
65   // Now we can assume that latest data should be removed. pop_back.
66   cacheContainer.pop_back();
67 }
68
69 template<class ContainerType>
70 void TextureCacheManager::RemoveTextureInfoByIndex(ContainerType& cacheContainer, const TextureCacheManager::TextureCacheIndex& removeContainerIndex)
71 {
72   // Swap last data of cacheContainer.
73   if(static_cast<std::size_t>(removeContainerIndex.GetIndex() + 1) < cacheContainer.size())
74   {
75     // First, change the cache index infomations inside of converter
76     mTextureIdConverter[cacheContainer.back().textureId] = static_cast<std::uint32_t>(removeContainerIndex);
77
78     // After change converter, swap the value between current data and last data.
79     std::swap(cacheContainer[removeContainerIndex.GetIndex()], cacheContainer.back());
80   }
81
82   // Now we can assume that latest data should be removed. pop_back.
83   cacheContainer.pop_back();
84 }
85
86 TextureCacheManager::TextureCacheManager()
87 {
88 }
89
90 TextureCacheManager::~TextureCacheManager()
91 {
92 }
93
94 VisualUrl TextureCacheManager::GetVisualUrl(const TextureCacheManager::TextureId& textureId)
95 {
96   VisualUrl         visualUrl("");
97   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(textureId)]);
98
99   switch(static_cast<TextureCacheIndexType>(cacheIndex.detailValue.type))
100   {
101     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL:
102     {
103       DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached texture index=%d, textureId=%d\n", cacheIndex.GetIndex(), textureId);
104
105       TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
106       visualUrl = cachedTextureInfo.url;
107       break;
108     }
109     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE:
110     {
111       DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached external texture index=%d, textureId=%d\n", cacheIndex.GetIndex(), textureId);
112       visualUrl = VisualUrl::CreateTextureUrl(std::to_string(textureId));
113       break;
114     }
115     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER:
116     {
117       DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached buffer index=%d, bufferId=%d\n", cacheIndex.GetIndex(), textureId);
118       visualUrl = VisualUrl::CreateBufferUrl(std::to_string(textureId));
119       break;
120     }
121     default:
122     {
123       break;
124     }
125   }
126
127   return visualUrl;
128 }
129
130 TextureCacheManager::LoadState TextureCacheManager::GetTextureState(const TextureCacheManager::TextureId& textureId)
131 {
132   LoadState         loadState  = TextureCacheManager::LoadState::NOT_STARTED;
133   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(textureId)]);
134
135   switch(static_cast<TextureCacheIndexType>(cacheIndex.detailValue.type))
136   {
137     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL:
138     {
139       TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
140       loadState = cachedTextureInfo.loadState;
141       break;
142     }
143     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE:
144     {
145       loadState = LoadState::UPLOADED;
146       break;
147     }
148     default:
149     {
150       break;
151     }
152   }
153
154   return loadState;
155 }
156
157 TextureCacheManager::LoadState TextureCacheManager::GetTextureStateInternal(const TextureCacheManager::TextureId& textureId)
158 {
159   LoadState         loadState  = TextureCacheManager::LoadState::NOT_STARTED;
160   TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
161   if(cacheIndex != INVALID_CACHE_INDEX)
162   {
163     TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
164     loadState = cachedTextureInfo.loadState;
165   }
166
167   return loadState;
168 }
169
170 TextureSet TextureCacheManager::GetTextureSet(const TextureCacheManager::TextureId& textureId)
171 {
172   TextureSet        textureSet; // empty handle
173   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(textureId)]);
174
175   switch(static_cast<TextureCacheIndexType>(cacheIndex.detailValue.type))
176   {
177     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL:
178     {
179       TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
180       textureSet = cachedTextureInfo.textureSet;
181       break;
182     }
183     case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE:
184     {
185       textureSet = mExternalTextures[cacheIndex.GetIndex()].textureSet;
186       break;
187     }
188     default:
189     {
190       break;
191     }
192   }
193
194   return textureSet;
195 }
196
197 TextureSet TextureCacheManager::GetExternalTextureSet(const TextureCacheManager::TextureId& textureId)
198 {
199   TextureSet        textureSet; // empty handle
200   TextureCacheIndex cacheIndex = GetCacheIndexFromExternalTextureId(textureId);
201   if(cacheIndex != INVALID_CACHE_INDEX)
202   {
203     textureSet = mExternalTextures[cacheIndex.GetIndex()].textureSet;
204   }
205   return textureSet;
206 }
207
208 EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const TextureCacheManager::TextureId& bufferId)
209 {
210   EncodedImageBuffer encodedImageBuffer; // empty handle
211   TextureCacheIndex  cacheIndex = GetCacheIndexFromEncodedImageBufferId(bufferId);
212   if(cacheIndex != INVALID_CACHE_INDEX)
213   {
214     encodedImageBuffer = mEncodedImageBuffers[cacheIndex.GetIndex()].encodedImageBuffer;
215   }
216   return encodedImageBuffer;
217 }
218
219 EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const VisualUrl& url)
220 {
221   EncodedImageBuffer encodedImageBuffer; // empty handle
222   if(url.IsValid() && VisualUrl::BUFFER == url.GetProtocolType())
223   {
224     std::string location = url.GetLocation();
225     if(location.size() > 0u)
226     {
227       TextureId bufferId = std::stoi(location);
228       return GetEncodedImageBuffer(bufferId);
229     }
230   }
231   return encodedImageBuffer;
232 }
233
234 std::string TextureCacheManager::AddExternalTexture(const TextureSet& textureSet)
235 {
236   TextureId textureId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE, mExternalTextures.size()));
237
238   TextureCacheManager::ExternalTextureInfo textureInfo(textureId, textureSet);
239   mExternalTextures.emplace_back(textureInfo);
240
241   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalTexture() : New texture registered. textureId:%d\n", textureInfo.textureId);
242
243   return VisualUrl::CreateTextureUrl(std::to_string(textureInfo.textureId));
244 }
245
246 std::string TextureCacheManager::AddEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer)
247 {
248   // Duplication check
249   TextureHash       bufferHash       = static_cast<TextureHash>(encodedImageBuffer.GetHash());
250   TextureCacheIndex bufferCacheIndex = FindCachedEncodedImageBuffer(bufferHash, encodedImageBuffer);
251   if(bufferCacheIndex != INVALID_CACHE_INDEX)
252   {
253     EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[bufferCacheIndex.GetIndex()]);
254
255     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));
256
257     // If same buffer added, increase reference count and return.
258     bufferInfo.referenceCount++;
259     return VisualUrl::CreateBufferUrl(std::to_string(bufferInfo.bufferId));
260   }
261
262   TextureId bufferId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER, mEncodedImageBuffers.size()));
263
264   TextureCacheManager::EncodedImageBufferInfo info(bufferId, bufferHash, encodedImageBuffer);
265   mEncodedImageBuffers.emplace_back(info);
266
267   // Insert TextureHashContainer
268   // Find exist list -or- Create new list.
269   std::vector<TextureId>& idList = mTextureHashContainer[bufferHash];
270   // We already assume that list doesn't contain id. just emplace back
271   idList.emplace_back(bufferId);
272
273   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalEncodedImageBuffer() : New buffer regested. bufferId:%d\n", info.bufferId);
274
275   return VisualUrl::CreateBufferUrl(std::to_string(info.bufferId));
276 }
277
278 TextureSet TextureCacheManager::RemoveExternalTexture(const VisualUrl& url)
279 {
280   TextureSet        textureSet;
281   bool              removeTextureInfo  = false;
282   TextureCacheIndex removeTextureIndex = INVALID_CACHE_INDEX;
283   if(url.IsValid())
284   {
285     if(VisualUrl::TEXTURE == url.GetProtocolType())
286     {
287       // get the location from the Url
288       std::string location = url.GetLocation();
289       if(location.size() > 0u)
290       {
291         TextureId textureId = std::stoi(location);
292         removeTextureIndex  = GetCacheIndexFromExternalTextureId(textureId);
293         if(removeTextureIndex != INVALID_CACHE_INDEX)
294         {
295           ExternalTextureInfo& textureInfo(mExternalTextures[removeTextureIndex.GetIndex()]);
296           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));
297           textureSet = textureInfo.textureSet;
298           if(--(textureInfo.referenceCount) <= 0)
299           {
300             removeTextureInfo = true;
301             // id life is finished. Remove it at converter
302             mTextureIdConverter.Remove(textureId);
303           }
304         }
305       }
306     }
307   }
308
309   // Post removal process to avoid mExternalTextures reference problems.
310   if(removeTextureInfo)
311   {
312     // Swap last data of mExternalTextures, and pop_back.
313     RemoveTextureInfoByIndex(mExternalTextures, removeTextureIndex);
314   }
315   return textureSet;
316 }
317
318 EncodedImageBuffer TextureCacheManager::RemoveEncodedImageBuffer(const VisualUrl& url)
319 {
320   EncodedImageBuffer encodedImageBuffer;
321   bool               removeBufferInfo  = false;
322   TextureCacheIndex  removeBufferIndex = INVALID_CACHE_INDEX;
323   if(url.IsValid())
324   {
325     if(VisualUrl::BUFFER == url.GetProtocolType())
326     {
327       // get the location from the Url
328       std::string location = url.GetLocation();
329       if(location.size() > 0u)
330       {
331         TextureId bufferId = std::stoi(location);
332         removeBufferIndex  = GetCacheIndexFromEncodedImageBufferId(bufferId);
333
334         if(removeBufferIndex != INVALID_CACHE_INDEX)
335         {
336           EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[removeBufferIndex.GetIndex()]);
337           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));
338
339           encodedImageBuffer = bufferInfo.encodedImageBuffer;
340           if(--(bufferInfo.referenceCount) <= 0)
341           {
342             removeBufferInfo = true;
343             // Step 1. remove current textureId information in mTextureHashContainer.
344             RemoveHashId(bufferInfo.bufferHash, bufferId);
345             // Step 2. id life is finished. Remove it at converter
346             mTextureIdConverter.Remove(bufferId);
347           }
348         }
349       }
350     }
351   }
352
353   // Post removal process to avoid mEncodedImageBuffers reference problems.
354   if(removeBufferInfo)
355   {
356     // Step 3. swap last data of mEncodedImageBuffers, and pop_back.
357     RemoveTextureInfoByIndex(mEncodedImageBuffers, removeBufferIndex);
358   }
359   return encodedImageBuffer;
360 }
361
362 void TextureCacheManager::UseExternalResource(const VisualUrl& url)
363 {
364   if(VisualUrl::TEXTURE == url.GetProtocolType())
365   {
366     std::string location = url.GetLocation();
367     if(location.size() > 0u)
368     {
369       TextureId         id         = std::stoi(location);
370       TextureCacheIndex cacheIndex = GetCacheIndexFromExternalTextureId(id);
371       if(cacheIndex != INVALID_CACHE_INDEX)
372       {
373         ExternalTextureInfo& textureInfo(mExternalTextures[cacheIndex.GetIndex()]);
374         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));
375
376         textureInfo.referenceCount++;
377         return;
378       }
379     }
380   }
381   else if(VisualUrl::BUFFER == url.GetProtocolType())
382   {
383     std::string location = url.GetLocation();
384     if(location.size() > 0u)
385     {
386       TextureId         id         = std::stoi(location);
387       TextureCacheIndex cacheIndex = GetCacheIndexFromEncodedImageBufferId(id);
388       if(cacheIndex != INVALID_CACHE_INDEX)
389       {
390         EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]);
391         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));
392
393         bufferInfo.referenceCount++;
394         return;
395       }
396     }
397   }
398 }
399
400 TextureCacheManager::TextureId TextureCacheManager::GenerateTextureId(const TextureCacheIndex& textureCacheIndex)
401 {
402   return mTextureIdConverter.Add(static_cast<std::uint32_t>(textureCacheIndex.indexValue));
403 }
404
405 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromId(const TextureCacheManager::TextureId& textureId)
406 {
407   if(textureId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
408
409   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(textureId)]);
410   if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL))
411   {
412     return INVALID_CACHE_INDEX;
413   }
414
415   DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mTextureInfoContainer.size());
416
417   return cacheIndex;
418 }
419
420 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromExternalTextureId(const TextureCacheManager::TextureId& textureId)
421 {
422   if(textureId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
423
424   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(textureId)]);
425   if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE))
426   {
427     return INVALID_CACHE_INDEX;
428   }
429
430   DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mExternalTextures.size());
431
432   return cacheIndex;
433 }
434
435 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromEncodedImageBufferId(const TextureCacheManager::TextureId& bufferId)
436 {
437   if(bufferId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
438
439   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(bufferId)]);
440   if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER))
441   {
442     return INVALID_CACHE_INDEX;
443   }
444
445   DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mEncodedImageBuffers.size());
446
447   return cacheIndex;
448 }
449
450 TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedEncodedImageBuffer(const TextureCacheManager::TextureHash& hash, const EncodedImageBuffer& encodedImageBuffer)
451 {
452   // Iterate through our hashes to find a match.
453   const auto& hashIterator = mTextureHashContainer.find(hash);
454   if(hashIterator != mTextureHashContainer.cend())
455   {
456     for(const auto& id : hashIterator->second)
457     {
458       // We have a match, now we check all the original parameters in case of a hash collision.
459       TextureCacheIndex cacheIndex = GetCacheIndexFromEncodedImageBufferId(id);
460       if(cacheIndex != INVALID_CACHE_INDEX)
461       {
462         EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]);
463
464         if(bufferInfo.encodedImageBuffer == encodedImageBuffer)
465         {
466           // The found encoded image buffer.
467           return cacheIndex;
468         }
469       }
470     }
471   }
472
473   // Default to an invalid ID, in case we do not find a match.
474   return INVALID_CACHE_INDEX;
475 }
476
477 TextureCacheManager::TextureHash TextureCacheManager::GenerateHash(
478   const VisualUrl&                      url,
479   const Dali::ImageDimensions&          size,
480   const Dali::FittingMode::Type&        fittingMode,
481   const Dali::SamplingMode::Type&       samplingMode,
482   const TextureCacheManager::UseAtlas&  useAtlas,
483   const TextureCacheManager::TextureId& maskTextureId,
484   const bool&                           cropToMask)
485 {
486   std::vector<std::uint8_t> hashTarget(url.GetUrl().begin(), url.GetUrl().end());
487   const size_t              urlLength = hashTarget.size();
488   const uint16_t            width     = size.GetWidth();
489   const uint16_t            height    = size.GetWidth();
490
491   // If either the width or height has been specified, include the resizing options in the hash
492   if(width != 0 || height != 0)
493   {
494     // We are appending 5 bytes to the URL to form the hash input.
495     hashTarget.resize(urlLength + 5u);
496     std::uint8_t* hashTargetPtr = &(hashTarget[urlLength]);
497
498     // Pack the width and height (4 bytes total).
499     *hashTargetPtr++ = size.GetWidth() & 0xff;
500     *hashTargetPtr++ = (size.GetWidth() >> 8u) & 0xff;
501     *hashTargetPtr++ = size.GetHeight() & 0xff;
502     *hashTargetPtr++ = (size.GetHeight() >> 8u) & 0xff;
503
504     // Bit-pack the FittingMode, SamplingMode and atlasing.
505     // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit
506     *hashTargetPtr = (fittingMode << 4u) | (samplingMode << 1) | (useAtlas == UseAtlas::USE_ATLAS ? 1 : 0);
507   }
508   else
509   {
510     // We are not including sizing information, but we still need an extra byte for atlasing.
511     hashTarget.resize(urlLength + 1u);
512
513     // Add the atlasing to the hash input.
514     switch(useAtlas)
515     {
516       case UseAtlas::NO_ATLAS:
517       {
518         hashTarget[urlLength] = 'f';
519         break;
520       }
521       case UseAtlas::USE_ATLAS:
522       {
523         hashTarget[urlLength] = 't';
524         break;
525       }
526     }
527   }
528
529   if(maskTextureId != INVALID_TEXTURE_ID)
530   {
531     auto textureIdIndex = hashTarget.size();
532     hashTarget.resize(hashTarget.size() + sizeof(TextureId) + 1u);
533     std::uint8_t* hashTargetPtr = reinterpret_cast<std::uint8_t*>(&(hashTarget[textureIdIndex]));
534
535     // Append the texture id to the end of the URL byte by byte:
536     // (to avoid SIGBUS / alignment issues)
537     TextureId saltedMaskTextureId = maskTextureId;
538     for(size_t byteIter = 0; byteIter < sizeof(TextureId); ++byteIter)
539     {
540       *hashTargetPtr++ = saltedMaskTextureId & 0xff;
541       saltedMaskTextureId >>= 8u;
542     }
543     *hashTargetPtr++ = (cropToMask ? 'C' : 'M');
544   }
545
546   return Dali::CalculateHash(hashTarget);
547 }
548
549 TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedTexture(
550   const TextureCacheManager::TextureHash&    hash,
551   const VisualUrl&                           url,
552   const Dali::ImageDimensions&               size,
553   const Dali::FittingMode::Type&             fittingMode,
554   const Dali::SamplingMode::Type&            samplingMode,
555   const TextureCacheManager::UseAtlas&       useAtlas,
556   const TextureCacheManager::TextureId&      maskTextureId,
557   const bool&                                cropToMask,
558   const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad,
559   const bool&                                isAnimatedImage)
560 {
561   // Iterate through our hashes to find a match.
562   const auto& hashIterator = mTextureHashContainer.find(hash);
563   if(hashIterator != mTextureHashContainer.cend())
564   {
565     for(const auto& textureId : hashIterator->second)
566     {
567       // We have a match, now we check all the original parameters in case of a hash collision.
568       TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
569       if(cacheIndex != INVALID_CACHE_INDEX)
570       {
571         TextureInfo& textureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
572
573         if((url.GetUrl() == textureInfo.url.GetUrl()) &&
574            (useAtlas == textureInfo.useAtlas) &&
575            (maskTextureId == textureInfo.maskTextureId) &&
576            (cropToMask == textureInfo.cropToMask) &&
577            (size == textureInfo.desiredSize) &&
578            (isAnimatedImage == textureInfo.isAnimatedImageFormat) &&
579            ((size.GetWidth() == 0 && size.GetHeight() == 0) ||
580             (fittingMode == textureInfo.fittingMode &&
581              samplingMode == textureInfo.samplingMode)))
582         {
583           // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different.
584           // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false.
585           if((preMultiplyOnLoad == MultiplyOnLoad::MULTIPLY_ON_LOAD && textureInfo.preMultiplyOnLoad) || (preMultiplyOnLoad == MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY && !textureInfo.preMultiplied))
586           {
587             // The found Texture is a match.
588             return cacheIndex;
589           }
590         }
591       }
592     }
593   }
594
595   // Default to an invalid ID, in case we do not find a match.
596   return INVALID_CACHE_INDEX;
597 }
598
599 TextureCacheManager::TextureCacheIndex TextureCacheManager::AppendCache(const TextureCacheManager::TextureInfo& textureInfo)
600 {
601   // If we use EncodedImageBuffer, increase reference during it contains mTextureInfoContainer.
602   // This reference will be decreased when we call RemoveCache
603   if(textureInfo.url.GetProtocolType() == VisualUrl::BUFFER)
604   {
605     UseExternalResource(textureInfo.url);
606   }
607
608   TextureHash hash = textureInfo.hash;
609   TextureId   id   = textureInfo.textureId;
610
611   // Insert TextureHash container first
612   // Find exist list -or- Create new list.
613   std::vector<TextureId>& idList = mTextureHashContainer[hash];
614   // We already assume that list doesn't contain id. just emplace back
615   idList.emplace_back(id);
616
617   // Insert TextureInfo back of mTextureInfoContainer.
618   TextureCacheIndex cacheIndex = TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, mTextureInfoContainer.size());
619   mTextureInfoContainer.emplace_back(textureInfo);
620
621   // Add converter id --> cacheIndex
622   // NOTE : We should assume that id already generated by GenerateTextureId function.
623   mTextureIdConverter[id] = cacheIndex;
624
625   return cacheIndex;
626 }
627
628 void TextureCacheManager::RemoveCache(const TextureCacheManager::TextureId& textureId)
629 {
630   TextureCacheIndex textureInfoIndex = GetCacheIndexFromId(textureId);
631
632   bool removeTextureInfo = false;
633
634   if(textureInfoIndex != INVALID_CACHE_INDEX)
635   {
636     TextureInfo& textureInfo(mTextureInfoContainer[textureInfoIndex.GetIndex()]);
637
638     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);
639
640     // Decrement the reference count and check if this is the last user of this Texture.
641     if(--textureInfo.referenceCount <= 0)
642     {
643       // This is the last remove for this Texture.
644       textureInfo.referenceCount = 0;
645
646       // If loaded, we can remove the TextureInfo and the Atlas (if atlased).
647       if(textureInfo.loadState == LoadState::UPLOADED)
648       {
649         if(textureInfo.atlas)
650         {
651           textureInfo.atlas.Remove(textureInfo.atlasRect);
652         }
653         removeTextureInfo = true;
654       }
655       else if(textureInfo.loadState == LoadState::LOADING || textureInfo.loadState == LoadState::MASK_APPLYING)
656       {
657         // We mark the textureInfo for removal.
658         // Once the load has completed, this method will be called again.
659         textureInfo.loadState = LoadState::CANCELLED;
660       }
661       else
662       {
663         // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data.
664         removeTextureInfo = true;
665       }
666
667       // If the state allows us to remove the TextureInfo data, we do so.
668       if(removeTextureInfo)
669       {
670         // If url location is BUFFER, decrease reference count of EncodedImageBuffer.
671         if(textureInfo.url.IsBufferResource())
672         {
673           RemoveEncodedImageBuffer(textureInfo.url.GetUrl());
674         }
675
676         // Permanently remove the textureInfo struct.
677
678         // Step 1. remove current textureId information in mTextureHashContainer.
679         RemoveHashId(textureInfo.hash, textureId);
680         // Step 2. make textureId is not using anymore. After this job, we can reuse textureId.
681         mTextureIdConverter.Remove(textureId);
682       }
683     }
684   }
685
686   // Post removal process to avoid mTextureInfoContainer reference problems.
687   if(removeTextureInfo)
688   {
689     // Step 3. swap last data of TextureInfoContainer, and pop_back.
690     RemoveTextureInfoByIndex(mTextureInfoContainer, textureInfoIndex);
691   }
692 }
693
694 void TextureCacheManager::RemoveHashId(const TextureCacheManager::TextureHash& textureHash, const TextureCacheManager::TextureId& textureId)
695 {
696   auto hashIterator = mTextureHashContainer.find(textureHash);
697   if(hashIterator != mTextureHashContainer.end())
698   {
699     auto        hashIdList     = hashIterator->second;
700     const auto& hashIdIterator = std::find(hashIdList.cbegin(), hashIdList.cend(), textureId);
701     if(hashIdIterator != hashIdList.cend())
702     {
703       hashIdList.erase(hashIdIterator);
704       if(hashIdList.size() == 0)
705       {
706         // If id list in current hash is empty, remove it self in the container.
707         mTextureHashContainer.erase(hashIterator);
708       }
709     }
710   }
711 }
712
713 } // namespace Internal
714
715 } // namespace Toolkit
716
717 } // namespace Dali