Caching texture instead of textureSet in TextureManager
[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 Texture TextureCacheManager::GetTexture(const TextureCacheManager::TextureId& textureId, uint32_t textureIndex)
171 {
172   Texture        texture; // empty handle
173   TextureCacheIndex cacheIndex = GetCacheIndexFromId(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       if(textureIndex < static_cast<uint32_t>(cachedTextureInfo.textures.size()))
181       {
182         texture = cachedTextureInfo.textures[textureIndex];
183       }
184       break;
185     }
186     default:
187     {
188       break;
189     }
190   }
191
192   return texture;
193 }
194
195 TextureSet TextureCacheManager::GetExternalTextureSet(const TextureCacheManager::TextureId& textureId)
196 {
197   TextureSet        textureSet; // empty handle
198   TextureCacheIndex cacheIndex = GetCacheIndexFromExternalTextureId(textureId);
199   if(cacheIndex != INVALID_CACHE_INDEX)
200   {
201     textureSet = mExternalTextures[cacheIndex.GetIndex()].textureSet;
202   }
203   return textureSet;
204 }
205
206 EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const TextureCacheManager::TextureId& bufferId)
207 {
208   EncodedImageBuffer encodedImageBuffer; // empty handle
209   TextureCacheIndex  cacheIndex = GetCacheIndexFromEncodedImageBufferId(bufferId);
210   if(cacheIndex != INVALID_CACHE_INDEX)
211   {
212     encodedImageBuffer = mEncodedImageBuffers[cacheIndex.GetIndex()].encodedImageBuffer;
213   }
214   return encodedImageBuffer;
215 }
216
217 EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const VisualUrl& url)
218 {
219   EncodedImageBuffer encodedImageBuffer; // empty handle
220   if(url.IsValid() && VisualUrl::BUFFER == url.GetProtocolType())
221   {
222     std::string location = url.GetLocation();
223     if(location.size() > 0u)
224     {
225       TextureId bufferId = std::stoi(location);
226       return GetEncodedImageBuffer(bufferId);
227     }
228   }
229   return encodedImageBuffer;
230 }
231
232 std::string TextureCacheManager::AddExternalTexture(const TextureSet& textureSet)
233 {
234   TextureId textureId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE, mExternalTextures.size()));
235
236   TextureCacheManager::ExternalTextureInfo textureInfo(textureId, textureSet);
237   mExternalTextures.emplace_back(textureInfo);
238
239   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalTexture() : New texture registered. textureId:%d\n", textureInfo.textureId);
240
241   return VisualUrl::CreateTextureUrl(std::to_string(textureInfo.textureId));
242 }
243
244 std::string TextureCacheManager::AddEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer)
245 {
246   // Duplication check
247   TextureHash       bufferHash       = static_cast<TextureHash>(encodedImageBuffer.GetHash());
248   TextureCacheIndex bufferCacheIndex = FindCachedEncodedImageBuffer(bufferHash, encodedImageBuffer);
249   if(bufferCacheIndex != INVALID_CACHE_INDEX)
250   {
251     EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[bufferCacheIndex.GetIndex()]);
252
253     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));
254
255     // If same buffer added, increase reference count and return.
256     bufferInfo.referenceCount++;
257     return VisualUrl::CreateBufferUrl(std::to_string(bufferInfo.bufferId));
258   }
259
260   TextureId bufferId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER, mEncodedImageBuffers.size()));
261
262   TextureCacheManager::EncodedImageBufferInfo info(bufferId, bufferHash, encodedImageBuffer);
263   mEncodedImageBuffers.emplace_back(info);
264
265   // Insert TextureHashContainer
266   // Find exist list -or- Create new list.
267   std::vector<TextureId>& idList = mTextureHashContainer[bufferHash];
268   // We already assume that list doesn't contain id. just emplace back
269   idList.emplace_back(bufferId);
270
271   DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalEncodedImageBuffer() : New buffer regested. bufferId:%d\n", info.bufferId);
272
273   return VisualUrl::CreateBufferUrl(std::to_string(info.bufferId));
274 }
275
276 TextureSet TextureCacheManager::RemoveExternalTexture(const VisualUrl& url)
277 {
278   TextureSet        textureSet;
279   bool              removeTextureInfo  = false;
280   TextureCacheIndex removeTextureIndex = INVALID_CACHE_INDEX;
281   if(url.IsValid())
282   {
283     if(VisualUrl::TEXTURE == url.GetProtocolType())
284     {
285       // get the location from the Url
286       std::string location = url.GetLocation();
287       if(location.size() > 0u)
288       {
289         TextureId textureId = std::stoi(location);
290         removeTextureIndex  = GetCacheIndexFromExternalTextureId(textureId);
291         if(removeTextureIndex != INVALID_CACHE_INDEX)
292         {
293           ExternalTextureInfo& textureInfo(mExternalTextures[removeTextureIndex.GetIndex()]);
294           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));
295           textureSet = textureInfo.textureSet;
296           if(--(textureInfo.referenceCount) <= 0)
297           {
298             removeTextureInfo = true;
299             // id life is finished. Remove it at converter
300             mTextureIdConverter.Remove(textureId);
301           }
302         }
303       }
304     }
305   }
306
307   // Post removal process to avoid mExternalTextures reference problems.
308   if(removeTextureInfo)
309   {
310     // Swap last data of mExternalTextures, and pop_back.
311     RemoveTextureInfoByIndex(mExternalTextures, removeTextureIndex);
312   }
313   return textureSet;
314 }
315
316 EncodedImageBuffer TextureCacheManager::RemoveEncodedImageBuffer(const VisualUrl& url)
317 {
318   EncodedImageBuffer encodedImageBuffer;
319   bool               removeBufferInfo  = false;
320   TextureCacheIndex  removeBufferIndex = INVALID_CACHE_INDEX;
321   if(url.IsValid())
322   {
323     if(VisualUrl::BUFFER == url.GetProtocolType())
324     {
325       // get the location from the Url
326       std::string location = url.GetLocation();
327       if(location.size() > 0u)
328       {
329         TextureId bufferId = std::stoi(location);
330         removeBufferIndex  = GetCacheIndexFromEncodedImageBufferId(bufferId);
331
332         if(removeBufferIndex != INVALID_CACHE_INDEX)
333         {
334           EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[removeBufferIndex.GetIndex()]);
335           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));
336
337           encodedImageBuffer = bufferInfo.encodedImageBuffer;
338           if(--(bufferInfo.referenceCount) <= 0)
339           {
340             removeBufferInfo = true;
341             // Step 1. remove current textureId information in mTextureHashContainer.
342             RemoveHashId(bufferInfo.bufferHash, bufferId);
343             // Step 2. id life is finished. Remove it at converter
344             mTextureIdConverter.Remove(bufferId);
345           }
346         }
347       }
348     }
349   }
350
351   // Post removal process to avoid mEncodedImageBuffers reference problems.
352   if(removeBufferInfo)
353   {
354     // Step 3. swap last data of mEncodedImageBuffers, and pop_back.
355     RemoveTextureInfoByIndex(mEncodedImageBuffers, removeBufferIndex);
356   }
357   return encodedImageBuffer;
358 }
359
360 void TextureCacheManager::UseExternalResource(const VisualUrl& url)
361 {
362   if(VisualUrl::TEXTURE == url.GetProtocolType())
363   {
364     std::string location = url.GetLocation();
365     if(location.size() > 0u)
366     {
367       TextureId         id         = std::stoi(location);
368       TextureCacheIndex cacheIndex = GetCacheIndexFromExternalTextureId(id);
369       if(cacheIndex != INVALID_CACHE_INDEX)
370       {
371         ExternalTextureInfo& textureInfo(mExternalTextures[cacheIndex.GetIndex()]);
372         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));
373
374         textureInfo.referenceCount++;
375         return;
376       }
377     }
378   }
379   else if(VisualUrl::BUFFER == url.GetProtocolType())
380   {
381     std::string location = url.GetLocation();
382     if(location.size() > 0u)
383     {
384       TextureId         id         = std::stoi(location);
385       TextureCacheIndex cacheIndex = GetCacheIndexFromEncodedImageBufferId(id);
386       if(cacheIndex != INVALID_CACHE_INDEX)
387       {
388         EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]);
389         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));
390
391         bufferInfo.referenceCount++;
392         return;
393       }
394     }
395   }
396 }
397
398 TextureCacheManager::TextureId TextureCacheManager::GenerateTextureId(const TextureCacheIndex& textureCacheIndex)
399 {
400   return mTextureIdConverter.Add(static_cast<std::uint32_t>(textureCacheIndex.indexValue));
401 }
402
403 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromId(const TextureCacheManager::TextureId& textureId)
404 {
405   if(textureId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
406
407   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(textureId)]);
408   if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL))
409   {
410     return INVALID_CACHE_INDEX;
411   }
412
413   DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mTextureInfoContainer.size());
414
415   return cacheIndex;
416 }
417
418 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromExternalTextureId(const TextureCacheManager::TextureId& textureId)
419 {
420   if(textureId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
421
422   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(textureId)]);
423   if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE))
424   {
425     return INVALID_CACHE_INDEX;
426   }
427
428   DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mExternalTextures.size());
429
430   return cacheIndex;
431 }
432
433 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromEncodedImageBufferId(const TextureCacheManager::TextureId& bufferId)
434 {
435   if(bufferId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX;
436
437   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureIdConverter[static_cast<std::uint32_t>(bufferId)]);
438   if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER))
439   {
440     return INVALID_CACHE_INDEX;
441   }
442
443   DALI_ASSERT_DEBUG(static_cast<std::size_t>(cacheIndex.GetIndex()) < mEncodedImageBuffers.size());
444
445   return cacheIndex;
446 }
447
448 TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedEncodedImageBuffer(const TextureCacheManager::TextureHash& hash, const EncodedImageBuffer& encodedImageBuffer)
449 {
450   // Iterate through our hashes to find a match.
451   const auto& hashIterator = mTextureHashContainer.find(hash);
452   if(hashIterator != mTextureHashContainer.cend())
453   {
454     for(const auto& id : hashIterator->second)
455     {
456       // We have a match, now we check all the original parameters in case of a hash collision.
457       TextureCacheIndex cacheIndex = GetCacheIndexFromEncodedImageBufferId(id);
458       if(cacheIndex != INVALID_CACHE_INDEX)
459       {
460         EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]);
461
462         if(bufferInfo.encodedImageBuffer == encodedImageBuffer)
463         {
464           // The found encoded image buffer.
465           return cacheIndex;
466         }
467       }
468     }
469   }
470
471   // Default to an invalid ID, in case we do not find a match.
472   return INVALID_CACHE_INDEX;
473 }
474
475 TextureCacheManager::TextureHash TextureCacheManager::GenerateHash(
476   const VisualUrl&                      url,
477   const Dali::ImageDimensions&          size,
478   const Dali::FittingMode::Type&        fittingMode,
479   const Dali::SamplingMode::Type&       samplingMode,
480   const TextureCacheManager::UseAtlas&  useAtlas,
481   const TextureCacheManager::TextureId& maskTextureId,
482   const bool&                           cropToMask,
483   const std::uint32_t&                  frameIndex)
484 {
485   std::vector<std::uint8_t> hashTarget;
486   const uint16_t            width  = size.GetWidth();
487   const uint16_t            height = size.GetWidth();
488
489   // If either the width or height has been specified, include the resizing options in the hash
490   if(width != 0 || height != 0)
491   {
492     // We are appending 5 bytes to the URL to form the hash input.
493     hashTarget.resize(5u);
494     std::uint8_t* hashTargetPtr = &(hashTarget[0u]);
495
496     // Pack the width and height (4 bytes total).
497     *hashTargetPtr++ = size.GetWidth() & 0xff;
498     *hashTargetPtr++ = (size.GetWidth() >> 8u) & 0xff;
499     *hashTargetPtr++ = size.GetHeight() & 0xff;
500     *hashTargetPtr++ = (size.GetHeight() >> 8u) & 0xff;
501
502     // Bit-pack the FittingMode, SamplingMode and atlasing.
503     // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit
504     *hashTargetPtr = (fittingMode << 4u) | (samplingMode << 1) | (useAtlas == UseAtlas::USE_ATLAS ? 1 : 0);
505   }
506   else
507   {
508     // We are not including sizing information, but we still need an extra byte for atlasing.
509     hashTarget.resize(1u);
510
511     // Add the atlasing to the hash input.
512     switch(useAtlas)
513     {
514       case UseAtlas::NO_ATLAS:
515       {
516         hashTarget[0u] = 'f';
517         break;
518       }
519       case UseAtlas::USE_ATLAS:
520       {
521         hashTarget[0u] = 't';
522         break;
523       }
524     }
525   }
526
527   if(maskTextureId != INVALID_TEXTURE_ID)
528   {
529     auto textureIdIndex = hashTarget.size();
530     hashTarget.resize(hashTarget.size() + sizeof(TextureId) + 1u);
531     std::uint8_t* hashTargetPtr = reinterpret_cast<std::uint8_t*>(&(hashTarget[textureIdIndex]));
532
533     // Append the texture id to the end of the URL byte by byte:
534     // (to avoid SIGBUS / alignment issues)
535     TextureId saltedMaskTextureId = maskTextureId;
536     for(size_t byteIter = 0; byteIter < sizeof(TextureId); ++byteIter)
537     {
538       *hashTargetPtr++ = saltedMaskTextureId & 0xff;
539       saltedMaskTextureId >>= 8u;
540     }
541     *hashTargetPtr++ = (cropToMask ? 'C' : 'M');
542   }
543
544   // Append the frameIndex. We don't do additional job when frameIndex = 0u due to the non-animated image case.
545   if(frameIndex > 0u)
546   {
547     auto textureIdIndex = hashTarget.size();
548     hashTarget.resize(hashTarget.size() + sizeof(std::uint32_t));
549     std::uint8_t* hashTargetPtr = reinterpret_cast<std::uint8_t*>(&(hashTarget[textureIdIndex]));
550
551     // Append the frame index to the end of the URL byte by byte:
552     std::uint32_t saltedFrameIndex = frameIndex;
553     for(size_t byteIter = 0; byteIter < sizeof(std::uint8_t); ++byteIter)
554     {
555       *hashTargetPtr++ = saltedFrameIndex & 0xff;
556       saltedFrameIndex >>= 8u;
557     }
558   }
559
560   return url.GetUrlHash() ^ Dali::CalculateHash(hashTarget);
561 }
562
563 TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedTexture(
564   const TextureCacheManager::TextureHash&    hash,
565   const VisualUrl&                           url,
566   const Dali::ImageDimensions&               size,
567   const Dali::FittingMode::Type&             fittingMode,
568   const Dali::SamplingMode::Type&            samplingMode,
569   const TextureCacheManager::UseAtlas&       useAtlas,
570   const StorageType&                         storageType,
571   const TextureCacheManager::TextureId&      maskTextureId,
572   const bool&                                cropToMask,
573   const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad,
574   const bool&                                isAnimatedImage,
575   const std::uint32_t&                       frameIndex)
576 {
577   // Iterate through our hashes to find a match.
578   const auto& hashIterator = mTextureHashContainer.find(hash);
579   if(hashIterator != mTextureHashContainer.cend())
580   {
581     for(const auto& textureId : hashIterator->second)
582     {
583       // We have a match, now we check all the original parameters in case of a hash collision.
584       TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
585       if(cacheIndex != INVALID_CACHE_INDEX)
586       {
587         TextureInfo& textureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]);
588
589         if((url.GetUrl() == textureInfo.url.GetUrl()) &&
590            (useAtlas == textureInfo.useAtlas) &&
591            (maskTextureId == textureInfo.maskTextureId) &&
592            (cropToMask == textureInfo.cropToMask) &&
593            (size == textureInfo.desiredSize) &&
594            (isAnimatedImage == textureInfo.isAnimatedImageFormat) &&
595            (storageType == textureInfo.storageType) &&
596            (frameIndex == textureInfo.frameIndex) &&
597            ((size.GetWidth() == 0 && size.GetHeight() == 0) ||
598             (fittingMode == textureInfo.fittingMode &&
599              samplingMode == textureInfo.samplingMode)))
600         {
601           // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different.
602           // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false.
603           if((preMultiplyOnLoad == MultiplyOnLoad::MULTIPLY_ON_LOAD && textureInfo.preMultiplyOnLoad) || (preMultiplyOnLoad == MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY && !textureInfo.preMultiplied))
604           {
605             // The found Texture is a match.
606             return cacheIndex;
607           }
608         }
609       }
610     }
611   }
612
613   // Default to an invalid ID, in case we do not find a match.
614   return INVALID_CACHE_INDEX;
615 }
616
617 TextureCacheManager::TextureCacheIndex TextureCacheManager::AppendCache(const TextureCacheManager::TextureInfo& textureInfo)
618 {
619   // If we use EncodedImageBuffer, increase reference during it contains mTextureInfoContainer.
620   // This reference will be decreased when we call RemoveCache
621   if(textureInfo.url.GetProtocolType() == VisualUrl::BUFFER)
622   {
623     UseExternalResource(textureInfo.url);
624   }
625
626   TextureHash hash = textureInfo.hash;
627   TextureId   id   = textureInfo.textureId;
628
629   // Insert TextureHash container first
630   // Find exist list -or- Create new list.
631   std::vector<TextureId>& idList = mTextureHashContainer[hash];
632   // We already assume that list doesn't contain id. just emplace back
633   idList.emplace_back(id);
634
635   // Insert TextureInfo back of mTextureInfoContainer.
636   TextureCacheIndex cacheIndex = TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, mTextureInfoContainer.size());
637   mTextureInfoContainer.emplace_back(textureInfo);
638
639   // Add converter id --> cacheIndex
640   // NOTE : We should assume that id already generated by GenerateTextureId function.
641   mTextureIdConverter[id] = cacheIndex;
642
643   return cacheIndex;
644 }
645
646 void TextureCacheManager::RemoveCache(const TextureCacheManager::TextureId& textureId)
647 {
648   TextureCacheIndex textureInfoIndex = GetCacheIndexFromId(textureId);
649
650   bool removeTextureInfo = false;
651
652   if(textureInfoIndex != INVALID_CACHE_INDEX)
653   {
654     TextureInfo& textureInfo(mTextureInfoContainer[textureInfoIndex.GetIndex()]);
655
656     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);
657
658     // Decrement the reference count and check if this is the last user of this Texture.
659     if(--textureInfo.referenceCount <= 0)
660     {
661       // This is the last remove for this Texture.
662       textureInfo.referenceCount = 0;
663
664       // If loaded, we can remove the TextureInfo and the Atlas (if atlased).
665       if(textureInfo.loadState == LoadState::UPLOADED)
666       {
667         if(textureInfo.atlas)
668         {
669           textureInfo.atlas.Remove(textureInfo.atlasRect);
670         }
671         removeTextureInfo = true;
672       }
673       else if(textureInfo.loadState == LoadState::LOADING || textureInfo.loadState == LoadState::MASK_APPLYING)
674       {
675         // We mark the textureInfo for removal.
676         // Once the load has completed, this method will be called again.
677         textureInfo.loadState = LoadState::CANCELLED;
678       }
679       else
680       {
681         // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data.
682         removeTextureInfo = true;
683       }
684
685       // If the state allows us to remove the TextureInfo data, we do so.
686       if(removeTextureInfo)
687       {
688         // If url location is BUFFER, decrease reference count of EncodedImageBuffer.
689         if(textureInfo.url.IsBufferResource())
690         {
691           RemoveEncodedImageBuffer(textureInfo.url.GetUrl());
692         }
693
694         // Permanently remove the textureInfo struct.
695
696         // Step 1. remove current textureId information in mTextureHashContainer.
697         RemoveHashId(textureInfo.hash, textureId);
698         // Step 2. make textureId is not using anymore. After this job, we can reuse textureId.
699         mTextureIdConverter.Remove(textureId);
700       }
701     }
702   }
703
704   // Post removal process to avoid mTextureInfoContainer reference problems.
705   if(removeTextureInfo)
706   {
707     // Step 3. swap last data of TextureInfoContainer, and pop_back.
708     RemoveTextureInfoByIndex(mTextureInfoContainer, textureInfoIndex);
709   }
710 }
711
712 void TextureCacheManager::RemoveHashId(const TextureCacheManager::TextureHash& textureHash, const TextureCacheManager::TextureId& textureId)
713 {
714   auto hashIterator = mTextureHashContainer.find(textureHash);
715   if(hashIterator != mTextureHashContainer.end())
716   {
717     auto        hashIdList     = hashIterator->second;
718     const auto& hashIdIterator = std::find(hashIdList.cbegin(), hashIdList.cend(), textureId);
719     if(hashIdIterator != hashIdList.cend())
720     {
721       hashIdList.erase(hashIdIterator);
722       if(hashIdList.size() == 0)
723       {
724         // If id list in current hash is empty, remove it self in the container.
725         mTextureHashContainer.erase(hashIterator);
726       }
727     }
728   }
729 }
730
731 } // namespace Internal
732
733 } // namespace Toolkit
734
735 } // namespace Dali