Split texture-manager-impl files
[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 namespace
51 {
52 } // Anonymous namespace
53
54 TextureCacheManager::TextureCacheManager()
55 : mCurrentTextureId(0)
56 {
57 }
58
59 TextureCacheManager::~TextureCacheManager()
60 {
61 }
62
63 VisualUrl TextureCacheManager::GetVisualUrl(const TextureCacheManager::TextureId& textureId)
64 {
65   VisualUrl         visualUrl("");
66   TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
67
68   if(cacheIndex != INVALID_CACHE_INDEX)
69   {
70     DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached texture id=%d, textureId=%d\n", cacheIndex, textureId);
71
72     TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]);
73     visualUrl = cachedTextureInfo.url;
74   }
75   return visualUrl;
76 }
77
78 TextureCacheManager::LoadState TextureCacheManager::GetTextureState(const TextureCacheManager::TextureId& textureId)
79 {
80   LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED;
81
82   TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
83   if(cacheIndex != INVALID_CACHE_INDEX)
84   {
85     TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]);
86     loadState = cachedTextureInfo.loadState;
87   }
88   else
89   {
90     for(auto&& elem : mExternalTextures)
91     {
92       if(elem.textureId == textureId)
93       {
94         loadState = LoadState::UPLOADED;
95         break;
96       }
97     }
98   }
99   return loadState;
100 }
101
102 TextureCacheManager::LoadState TextureCacheManager::GetTextureStateInternal(const TextureCacheManager::TextureId& textureId)
103 {
104   LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED;
105
106   TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
107   if(cacheIndex != INVALID_CACHE_INDEX)
108   {
109     TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]);
110     loadState = cachedTextureInfo.loadState;
111   }
112
113   return loadState;
114 }
115
116 TextureSet TextureCacheManager::GetTextureSet(const TextureCacheManager::TextureId& textureId)
117 {
118   TextureSet textureSet; // empty handle
119
120   TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
121   if(cacheIndex != INVALID_CACHE_INDEX)
122   {
123     TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]);
124     textureSet = cachedTextureInfo.textureSet;
125   }
126   else
127   {
128     for(auto&& elem : mExternalTextures)
129     {
130       if(elem.textureId == textureId)
131       {
132         textureSet = elem.textureSet;
133         break;
134       }
135     }
136   }
137   return textureSet;
138 }
139
140 TextureSet TextureCacheManager::GetExternalTextureSet(const TextureCacheManager::TextureId& textureId)
141 {
142   TextureSet textureSet; // empty handle
143   for(auto&& elem : mExternalTextures)
144   {
145     if(elem.textureId == textureId)
146     {
147       textureSet = elem.textureSet;
148       break;
149     }
150   }
151   return textureSet;
152 }
153
154 EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const TextureCacheManager::TextureId& textureId)
155 {
156   EncodedImageBuffer encodedImageBuffer; // empty handle
157   for(auto&& elem : mEncodedBufferTextures)
158   {
159     if(elem.textureId == textureId)
160     {
161       encodedImageBuffer = elem.encodedImageBuffer;
162       break;
163     }
164   }
165   return encodedImageBuffer;
166 }
167
168 EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const std::string& url)
169 {
170   EncodedImageBuffer encodedImageBuffer; // empty handle
171   if(url.size() > 0 && VisualUrl::BUFFER == VisualUrl::GetProtocolType(url))
172   {
173     std::string location = VisualUrl::GetLocation(url);
174     if(location.size() > 0u)
175     {
176       TextureId targetId = std::stoi(location);
177       return GetEncodedImageBuffer(targetId);
178     }
179   }
180   return encodedImageBuffer;
181 }
182
183 std::string TextureCacheManager::AddExternalTexture(const TextureSet& textureSet)
184 {
185   TextureCacheManager::ExternalTextureInfo info(GenerateUniqueTextureId(), textureSet);
186   mExternalTextures.emplace_back(info);
187   return VisualUrl::CreateTextureUrl(std::to_string(info.textureId));
188 }
189
190 std::string TextureCacheManager::AddExternalEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer)
191 {
192   // Duplication check
193   for(auto&& elem : mEncodedBufferTextures)
194   {
195     if(elem.encodedImageBuffer == encodedImageBuffer)
196     {
197       // If same buffer added, increase reference count and return.
198       elem.referenceCount++;
199       return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId));
200     }
201   }
202   TextureCacheManager::EncodedBufferTextureInfo info(GenerateUniqueTextureId(), encodedImageBuffer);
203   mEncodedBufferTextures.emplace_back(info);
204   return VisualUrl::CreateBufferUrl(std::to_string(info.textureId));
205 }
206
207 TextureSet TextureCacheManager::RemoveExternalTexture(const std::string& url)
208 {
209   if(url.size() > 0u)
210   {
211     if(VisualUrl::TEXTURE == VisualUrl::GetProtocolType(url))
212     {
213       // get the location from the Url
214       std::string location = VisualUrl::GetLocation(url);
215       if(location.size() > 0u)
216       {
217         TextureId  id  = std::stoi(location);
218         const auto end = mExternalTextures.end();
219         for(auto iter = mExternalTextures.begin(); iter != end; ++iter)
220         {
221           if(iter->textureId == id)
222           {
223             auto textureSet = iter->textureSet;
224             if(--(iter->referenceCount) <= 0)
225             {
226               mExternalTextures.erase(iter);
227             }
228             return textureSet;
229           }
230         }
231       }
232     }
233   }
234   return TextureSet();
235 }
236
237 EncodedImageBuffer TextureCacheManager::RemoveExternalEncodedImageBuffer(const std::string& url)
238 {
239   if(url.size() > 0u)
240   {
241     if(VisualUrl::BUFFER == VisualUrl::GetProtocolType(url))
242     {
243       // get the location from the Url
244       std::string location = VisualUrl::GetLocation(url);
245       if(location.size() > 0u)
246       {
247         TextureId  id  = std::stoi(location);
248         const auto end = mEncodedBufferTextures.end();
249         for(auto iter = mEncodedBufferTextures.begin(); iter != end; ++iter)
250         {
251           if(iter->textureId == id)
252           {
253             auto encodedImageBuffer = iter->encodedImageBuffer;
254             if(--(iter->referenceCount) <= 0)
255             {
256               mEncodedBufferTextures.erase(iter);
257             }
258             return encodedImageBuffer;
259           }
260         }
261       }
262     }
263   }
264   return EncodedImageBuffer();
265 }
266
267 void TextureCacheManager::UseExternalResource(const VisualUrl& url)
268 {
269   if(VisualUrl::TEXTURE == url.GetProtocolType())
270   {
271     std::string location = url.GetLocation();
272     if(location.size() > 0u)
273     {
274       TextureId id = std::stoi(location);
275       for(auto&& elem : mExternalTextures)
276       {
277         if(elem.textureId == id)
278         {
279           elem.referenceCount++;
280           return;
281         }
282       }
283     }
284   }
285   else if(VisualUrl::BUFFER == url.GetProtocolType())
286   {
287     std::string location = url.GetLocation();
288     if(location.size() > 0u)
289     {
290       TextureId id = std::stoi(location);
291       for(auto&& elem : mEncodedBufferTextures)
292       {
293         if(elem.textureId == id)
294         {
295           elem.referenceCount++;
296           return;
297         }
298       }
299     }
300   }
301 }
302
303 TextureCacheManager::TextureId TextureCacheManager::GenerateUniqueTextureId()
304 {
305   return mCurrentTextureId++;
306 }
307
308 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromId(const TextureCacheManager::TextureId& textureId)
309 {
310   const TextureCacheIndex size = static_cast<TextureCacheIndex>(mTextureInfoContainer.size());
311
312   for(TextureCacheIndex i = 0; i < size; ++i)
313   {
314     if(mTextureInfoContainer[i].textureId == textureId)
315     {
316       return i;
317     }
318   }
319
320   return INVALID_CACHE_INDEX;
321 }
322
323 TextureCacheManager::TextureHash TextureCacheManager::GenerateHash(
324   const std::string&                    url,
325   const Dali::ImageDimensions&          size,
326   const Dali::FittingMode::Type&        fittingMode,
327   const Dali::SamplingMode::Type&       samplingMode,
328   const TextureCacheManager::UseAtlas&  useAtlas,
329   const TextureCacheManager::TextureId& maskTextureId)
330 {
331   std::string    hashTarget(url);
332   const size_t   urlLength = hashTarget.length();
333   const uint16_t width     = size.GetWidth();
334   const uint16_t height    = size.GetWidth();
335
336   // If either the width or height has been specified, include the resizing options in the hash
337   if(width != 0 || height != 0)
338   {
339     // We are appending 5 bytes to the URL to form the hash input.
340     hashTarget.resize(urlLength + 5u);
341     char* hashTargetPtr = &(hashTarget[urlLength]);
342
343     // Pack the width and height (4 bytes total).
344     *hashTargetPtr++ = size.GetWidth() & 0xff;
345     *hashTargetPtr++ = (size.GetWidth() >> 8u) & 0xff;
346     *hashTargetPtr++ = size.GetHeight() & 0xff;
347     *hashTargetPtr++ = (size.GetHeight() >> 8u) & 0xff;
348
349     // Bit-pack the FittingMode, SamplingMode and atlasing.
350     // FittingMode=2bits, SamplingMode=3bits, useAtlas=1bit
351     *hashTargetPtr = (fittingMode << 4u) | (samplingMode << 1) | (useAtlas == UseAtlas::USE_ATLAS ? 1 : 0);
352   }
353   else
354   {
355     // We are not including sizing information, but we still need an extra byte for atlasing.
356     hashTarget.resize(urlLength + 1u);
357
358     // Add the atlasing to the hash input.
359     switch(useAtlas)
360     {
361       case UseAtlas::NO_ATLAS:
362       {
363         hashTarget[urlLength] = 'f';
364         break;
365       }
366       case UseAtlas::USE_ATLAS:
367       {
368         hashTarget[urlLength] = 't';
369         break;
370       }
371     }
372   }
373
374   if(maskTextureId != INVALID_TEXTURE_ID)
375   {
376     auto textureIdIndex = hashTarget.length();
377     hashTarget.resize(hashTarget.length() + sizeof(TextureId));
378     unsigned char* hashTargetPtr = reinterpret_cast<unsigned char*>(&(hashTarget[textureIdIndex]));
379
380     // Append the texture id to the end of the URL byte by byte:
381     // (to avoid SIGBUS / alignment issues)
382     TextureId saltedMaskTextureId = maskTextureId;
383     for(size_t byteIter = 0; byteIter < sizeof(TextureId); ++byteIter)
384     {
385       *hashTargetPtr++ = saltedMaskTextureId & 0xff;
386       saltedMaskTextureId >>= 8u;
387     }
388   }
389
390   return Dali::CalculateHash(hashTarget);
391 }
392
393 TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedTexture(
394   const TextureCacheManager::TextureHash&    hash,
395   const std::string&                         url,
396   const Dali::ImageDimensions&               size,
397   const Dali::FittingMode::Type&             fittingMode,
398   const Dali::SamplingMode::Type&            samplingMode,
399   const TextureCacheManager::UseAtlas&       useAtlas,
400   const TextureCacheManager::TextureId&      maskTextureId,
401   const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad)
402 {
403   // Default to an invalid ID, in case we do not find a match.
404   TextureCacheIndex cacheIndex = INVALID_CACHE_INDEX;
405
406   // Iterate through our hashes to find a match.
407   const TextureCacheIndex count = static_cast<TextureCacheIndex>(mTextureInfoContainer.size());
408   for(TextureCacheIndex i = 0u; i < count; ++i)
409   {
410     if(mTextureInfoContainer[i].hash == hash)
411     {
412       // We have a match, now we check all the original parameters in case of a hash collision.
413       TextureInfo& textureInfo(mTextureInfoContainer[i]);
414
415       if((url == textureInfo.url.GetUrl()) &&
416          (useAtlas == textureInfo.useAtlas) &&
417          (maskTextureId == textureInfo.maskTextureId) &&
418          (size == textureInfo.desiredSize) &&
419          ((size.GetWidth() == 0 && size.GetHeight() == 0) ||
420           (fittingMode == textureInfo.fittingMode &&
421            samplingMode == textureInfo.samplingMode)))
422       {
423         // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different.
424         // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false.
425         if((preMultiplyOnLoad == MultiplyOnLoad::MULTIPLY_ON_LOAD && textureInfo.preMultiplyOnLoad) || (preMultiplyOnLoad == MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY && !textureInfo.preMultiplied))
426         {
427           // The found Texture is a match.
428           cacheIndex = i;
429           break;
430         }
431       }
432     }
433   }
434
435   return cacheIndex;
436 }
437
438 TextureCacheManager::TextureCacheIndex TextureCacheManager::AppendCache(const TextureCacheManager::TextureInfo& textureInfo)
439 {
440   TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureInfoContainer.size());
441   mTextureInfoContainer.emplace_back(textureInfo);
442   return cacheIndex;
443 }
444
445 void TextureCacheManager::RemoveCache(const TextureCacheManager::TextureId& textureId)
446 {
447   TextureCacheIndex textureInfoIndex = GetCacheIndexFromId(textureId);
448
449   if(textureInfoIndex != INVALID_CACHE_INDEX)
450   {
451     TextureInfo& textureInfo(mTextureInfoContainer[textureInfoIndex]);
452
453     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);
454
455     // Decrement the reference count and check if this is the last user of this Texture.
456     if(--textureInfo.referenceCount <= 0)
457     {
458       // This is the last remove for this Texture.
459       textureInfo.referenceCount = 0;
460       bool removeTextureInfo     = false;
461
462       // If loaded, we can remove the TextureInfo and the Atlas (if atlased).
463       if(textureInfo.loadState == LoadState::UPLOADED)
464       {
465         if(textureInfo.atlas)
466         {
467           textureInfo.atlas.Remove(textureInfo.atlasRect);
468         }
469         removeTextureInfo = true;
470       }
471       else if(textureInfo.loadState == LoadState::LOADING)
472       {
473         // We mark the textureInfo for removal.
474         // Once the load has completed, this method will be called again.
475         textureInfo.loadState = LoadState::CANCELLED;
476       }
477       else
478       {
479         // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data.
480         removeTextureInfo = true;
481       }
482
483       // If the state allows us to remove the TextureInfo data, we do so.
484       if(removeTextureInfo)
485       {
486         // If url location is BUFFER, decrease reference count of EncodedImageBuffer.
487         if(textureInfo.url.IsBufferResource())
488         {
489           RemoveExternalEncodedImageBuffer(textureInfo.url.GetUrl());
490         }
491         // Permanently remove the textureInfo struct.
492         mTextureInfoContainer.erase(mTextureInfoContainer.begin() + textureInfoIndex);
493       }
494     }
495   }
496 }
497
498 } // namespace Internal
499
500 } // namespace Toolkit
501
502 } // namespace Dali