2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "texture-cache-manager.h"
22 #include <dali/devel-api/common/hash.h>
23 #include <dali/integration-api/debug.h>
34 extern Debug::Filter* gTextureManagerLogFilter; ///< Define at texture-manager-impl.cpp
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" : \
52 } // Anonymous namespace
54 TextureCacheManager::TextureCacheManager()
55 : mCurrentTextureId(0)
59 TextureCacheManager::~TextureCacheManager()
63 VisualUrl TextureCacheManager::GetVisualUrl(const TextureCacheManager::TextureId& textureId)
65 VisualUrl visualUrl("");
66 TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
68 if(cacheIndex != INVALID_CACHE_INDEX)
70 DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached texture id=%d, textureId=%d\n", cacheIndex, textureId);
72 TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]);
73 visualUrl = cachedTextureInfo.url;
78 TextureCacheManager::LoadState TextureCacheManager::GetTextureState(const TextureCacheManager::TextureId& textureId)
80 LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED;
82 TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
83 if(cacheIndex != INVALID_CACHE_INDEX)
85 TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]);
86 loadState = cachedTextureInfo.loadState;
90 for(auto&& elem : mExternalTextures)
92 if(elem.textureId == textureId)
94 loadState = LoadState::UPLOADED;
102 TextureCacheManager::LoadState TextureCacheManager::GetTextureStateInternal(const TextureCacheManager::TextureId& textureId)
104 LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED;
106 TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
107 if(cacheIndex != INVALID_CACHE_INDEX)
109 TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]);
110 loadState = cachedTextureInfo.loadState;
116 TextureSet TextureCacheManager::GetTextureSet(const TextureCacheManager::TextureId& textureId)
118 TextureSet textureSet; // empty handle
120 TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId);
121 if(cacheIndex != INVALID_CACHE_INDEX)
123 TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]);
124 textureSet = cachedTextureInfo.textureSet;
128 for(auto&& elem : mExternalTextures)
130 if(elem.textureId == textureId)
132 textureSet = elem.textureSet;
140 TextureSet TextureCacheManager::GetExternalTextureSet(const TextureCacheManager::TextureId& textureId)
142 TextureSet textureSet; // empty handle
143 for(auto&& elem : mExternalTextures)
145 if(elem.textureId == textureId)
147 textureSet = elem.textureSet;
154 EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const TextureCacheManager::TextureId& textureId)
156 EncodedImageBuffer encodedImageBuffer; // empty handle
157 for(auto&& elem : mEncodedBufferTextures)
159 if(elem.textureId == textureId)
161 encodedImageBuffer = elem.encodedImageBuffer;
165 return encodedImageBuffer;
168 EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const std::string& url)
170 EncodedImageBuffer encodedImageBuffer; // empty handle
171 if(url.size() > 0 && VisualUrl::BUFFER == VisualUrl::GetProtocolType(url))
173 std::string location = VisualUrl::GetLocation(url);
174 if(location.size() > 0u)
176 TextureId targetId = std::stoi(location);
177 return GetEncodedImageBuffer(targetId);
180 return encodedImageBuffer;
183 std::string TextureCacheManager::AddExternalTexture(const TextureSet& textureSet)
185 TextureCacheManager::ExternalTextureInfo info(GenerateUniqueTextureId(), textureSet);
186 mExternalTextures.emplace_back(info);
187 return VisualUrl::CreateTextureUrl(std::to_string(info.textureId));
190 std::string TextureCacheManager::AddExternalEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer)
193 for(auto&& elem : mEncodedBufferTextures)
195 if(elem.encodedImageBuffer == encodedImageBuffer)
197 // If same buffer added, increase reference count and return.
198 elem.referenceCount++;
199 return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId));
202 TextureCacheManager::EncodedBufferTextureInfo info(GenerateUniqueTextureId(), encodedImageBuffer);
203 mEncodedBufferTextures.emplace_back(info);
204 return VisualUrl::CreateBufferUrl(std::to_string(info.textureId));
207 TextureSet TextureCacheManager::RemoveExternalTexture(const std::string& url)
211 if(VisualUrl::TEXTURE == VisualUrl::GetProtocolType(url))
213 // get the location from the Url
214 std::string location = VisualUrl::GetLocation(url);
215 if(location.size() > 0u)
217 TextureId id = std::stoi(location);
218 const auto end = mExternalTextures.end();
219 for(auto iter = mExternalTextures.begin(); iter != end; ++iter)
221 if(iter->textureId == id)
223 auto textureSet = iter->textureSet;
224 if(--(iter->referenceCount) <= 0)
226 mExternalTextures.erase(iter);
237 EncodedImageBuffer TextureCacheManager::RemoveExternalEncodedImageBuffer(const std::string& url)
241 if(VisualUrl::BUFFER == VisualUrl::GetProtocolType(url))
243 // get the location from the Url
244 std::string location = VisualUrl::GetLocation(url);
245 if(location.size() > 0u)
247 TextureId id = std::stoi(location);
248 const auto end = mEncodedBufferTextures.end();
249 for(auto iter = mEncodedBufferTextures.begin(); iter != end; ++iter)
251 if(iter->textureId == id)
253 auto encodedImageBuffer = iter->encodedImageBuffer;
254 if(--(iter->referenceCount) <= 0)
256 mEncodedBufferTextures.erase(iter);
258 return encodedImageBuffer;
264 return EncodedImageBuffer();
267 void TextureCacheManager::UseExternalResource(const VisualUrl& url)
269 if(VisualUrl::TEXTURE == url.GetProtocolType())
271 std::string location = url.GetLocation();
272 if(location.size() > 0u)
274 TextureId id = std::stoi(location);
275 for(auto&& elem : mExternalTextures)
277 if(elem.textureId == id)
279 elem.referenceCount++;
285 else if(VisualUrl::BUFFER == url.GetProtocolType())
287 std::string location = url.GetLocation();
288 if(location.size() > 0u)
290 TextureId id = std::stoi(location);
291 for(auto&& elem : mEncodedBufferTextures)
293 if(elem.textureId == id)
295 elem.referenceCount++;
303 TextureCacheManager::TextureId TextureCacheManager::GenerateUniqueTextureId()
305 return mCurrentTextureId++;
308 TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromId(const TextureCacheManager::TextureId& textureId)
310 const TextureCacheIndex size = static_cast<TextureCacheIndex>(mTextureInfoContainer.size());
312 for(TextureCacheIndex i = 0; i < size; ++i)
314 if(mTextureInfoContainer[i].textureId == textureId)
320 return INVALID_CACHE_INDEX;
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)
331 std::vector<std::uint8_t> hashTarget(url.begin(), url.end());
332 const size_t urlLength = url.length();
333 const uint16_t width = size.GetWidth();
334 const uint16_t height = size.GetWidth();
336 // If either the width or height has been specified, include the resizing options in the hash
337 if(width != 0 || height != 0)
339 // We are appending 5 bytes to the URL to form the hash input.
340 hashTarget.resize(urlLength + 5u);
341 std::uint8_t* hashTargetPtr = &(hashTarget[urlLength]);
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;
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);
355 // We are not including sizing information, but we still need an extra byte for atlasing.
356 hashTarget.resize(urlLength + 1u);
358 // Add the atlasing to the hash input.
361 case UseAtlas::NO_ATLAS:
363 hashTarget[urlLength] = 'f';
366 case UseAtlas::USE_ATLAS:
368 hashTarget[urlLength] = 't';
374 if(maskTextureId != INVALID_TEXTURE_ID)
376 auto textureIdIndex = hashTarget.size();
377 hashTarget.resize(hashTarget.size() + sizeof(TextureId));
378 std::uint8_t* hashTargetPtr = reinterpret_cast<std::uint8_t*>(&(hashTarget[textureIdIndex]));
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)
385 *hashTargetPtr++ = saltedMaskTextureId & 0xff;
386 saltedMaskTextureId >>= 8u;
390 return Dali::CalculateHash(hashTarget);
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 bool isAnimatedImage)
404 // Default to an invalid ID, in case we do not find a match.
405 TextureCacheIndex cacheIndex = INVALID_CACHE_INDEX;
407 // Iterate through our hashes to find a match.
408 const TextureCacheIndex count = static_cast<TextureCacheIndex>(mTextureInfoContainer.size());
409 for(TextureCacheIndex i = 0u; i < count; ++i)
411 if(mTextureInfoContainer[i].hash == hash)
413 // We have a match, now we check all the original parameters in case of a hash collision.
414 TextureInfo& textureInfo(mTextureInfoContainer[i]);
416 if((url == textureInfo.url.GetUrl()) &&
417 (useAtlas == textureInfo.useAtlas) &&
418 (maskTextureId == textureInfo.maskTextureId) &&
419 (size == textureInfo.desiredSize) &&
420 (isAnimatedImage == textureInfo.isAnimatedImageFormat) &&
421 ((size.GetWidth() == 0 && size.GetHeight() == 0) ||
422 (fittingMode == textureInfo.fittingMode &&
423 samplingMode == textureInfo.samplingMode)))
425 // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different.
426 // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false.
427 if((preMultiplyOnLoad == MultiplyOnLoad::MULTIPLY_ON_LOAD && textureInfo.preMultiplyOnLoad) || (preMultiplyOnLoad == MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY && !textureInfo.preMultiplied))
429 // The found Texture is a match.
440 TextureCacheManager::TextureCacheIndex TextureCacheManager::AppendCache(const TextureCacheManager::TextureInfo& textureInfo)
442 TextureCacheIndex cacheIndex = static_cast<TextureCacheIndex>(mTextureInfoContainer.size());
443 mTextureInfoContainer.emplace_back(textureInfo);
447 void TextureCacheManager::RemoveCache(const TextureCacheManager::TextureId& textureId)
449 TextureCacheIndex textureInfoIndex = GetCacheIndexFromId(textureId);
451 if(textureInfoIndex != INVALID_CACHE_INDEX)
453 TextureInfo& textureInfo(mTextureInfoContainer[textureInfoIndex]);
454 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);
456 // Decrement the reference count and check if this is the last user of this Texture.
457 if(--textureInfo.referenceCount <= 0)
459 // This is the last remove for this Texture.
460 textureInfo.referenceCount = 0;
461 bool removeTextureInfo = false;
463 // If loaded, we can remove the TextureInfo and the Atlas (if atlased).
464 if(textureInfo.loadState == LoadState::UPLOADED)
466 if(textureInfo.atlas)
468 textureInfo.atlas.Remove(textureInfo.atlasRect);
470 removeTextureInfo = true;
472 else if(textureInfo.loadState == LoadState::LOADING)
474 // We mark the textureInfo for removal.
475 // Once the load has completed, this method will be called again.
476 textureInfo.loadState = LoadState::CANCELLED;
480 // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data.
481 removeTextureInfo = true;
484 // If the state allows us to remove the TextureInfo data, we do so.
485 if(removeTextureInfo)
487 // If url location is BUFFER, decrease reference count of EncodedImageBuffer.
488 if(textureInfo.url.IsBufferResource())
490 RemoveExternalEncodedImageBuffer(textureInfo.url.GetUrl());
492 // Permanently remove the textureInfo struct.
493 mTextureInfoContainer.erase(mTextureInfoContainer.begin() + textureInfoIndex);
499 } // namespace Internal
501 } // namespace Toolkit