X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftexture-manager%2Ftexture-cache-manager.cpp;h=c0e7ac128fe5cf1b35dc6bacb19b45cd4144cd11;hb=ca51ee97baf5f41ecf741e22d865ff6c9e0bf769;hp=cc90fd8b37b11c17630f0fb6edea2a2d96eec328;hpb=38f0ea9fcdf1dc5037144fa19c8a52316c8af763;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git diff --git a/dali-toolkit/internal/texture-manager/texture-cache-manager.cpp b/dali-toolkit/internal/texture-manager/texture-cache-manager.cpp index cc90fd8..c0e7ac1 100644 --- a/dali-toolkit/internal/texture-manager/texture-cache-manager.cpp +++ b/dali-toolkit/internal/texture-manager/texture-cache-manager.cpp @@ -47,12 +47,43 @@ extern Debug::Filter* gTextureManagerLogFilter; ///< Define at texture-manager-i "Unknown" // clang-format on #endif -namespace + +// Due to the compile issue, this specialized template code must be defined top of this code. +template<> +void TextureCacheManager::RemoveTextureInfoByIndex(TextureCacheManager::EncodedImageBufferInfoContainerType& cacheContainer, const TextureCacheManager::TextureCacheIndex& removeContainerIndex) +{ + // Swap last data of cacheContainer. + if(static_cast(removeContainerIndex.GetIndex() + 1) < cacheContainer.size()) + { + // First, change the cache index infomations inside of converter + mTextureIdConverter[cacheContainer.back().bufferId] = static_cast(removeContainerIndex); + + // After change converter, swap the value between current data and last data. + std::swap(cacheContainer[removeContainerIndex.GetIndex()], cacheContainer.back()); + } + + // Now we can assume that latest data should be removed. pop_back. + cacheContainer.pop_back(); +} + +template +void TextureCacheManager::RemoveTextureInfoByIndex(ContainerType& cacheContainer, const TextureCacheManager::TextureCacheIndex& removeContainerIndex) { -} // Anonymous namespace + // Swap last data of cacheContainer. + if(static_cast(removeContainerIndex.GetIndex() + 1) < cacheContainer.size()) + { + // First, change the cache index infomations inside of converter + mTextureIdConverter[cacheContainer.back().textureId] = static_cast(removeContainerIndex); + + // After change converter, swap the value between current data and last data. + std::swap(cacheContainer[removeContainerIndex.GetIndex()], cacheContainer.back()); + } + + // Now we can assume that latest data should be removed. pop_back. + cacheContainer.pop_back(); +} TextureCacheManager::TextureCacheManager() -: mCurrentTextureId(0) { } @@ -63,118 +94,136 @@ TextureCacheManager::~TextureCacheManager() VisualUrl TextureCacheManager::GetVisualUrl(const TextureCacheManager::TextureId& textureId) { VisualUrl visualUrl(""); - TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId); + TextureCacheIndex cacheIndex = static_cast(mTextureIdConverter[static_cast(textureId)]); - if(cacheIndex != INVALID_CACHE_INDEX) + switch(static_cast(cacheIndex.detailValue.type)) { - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached texture id=%d, textureId=%d\n", cacheIndex, textureId); + case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL: + { + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached texture index=%d, textureId=%d\n", cacheIndex.GetIndex(), textureId); - TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]); - visualUrl = cachedTextureInfo.url; + TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]); + visualUrl = cachedTextureInfo.url; + break; + } + case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE: + { + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached external texture index=%d, textureId=%d\n", cacheIndex.GetIndex(), textureId); + visualUrl = VisualUrl::CreateTextureUrl(std::to_string(textureId)); + break; + } + case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER: + { + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::GetVisualUrl. Using cached buffer index=%d, bufferId=%d\n", cacheIndex.GetIndex(), textureId); + visualUrl = VisualUrl::CreateBufferUrl(std::to_string(textureId)); + break; + } + default: + { + break; + } } + return visualUrl; } TextureCacheManager::LoadState TextureCacheManager::GetTextureState(const TextureCacheManager::TextureId& textureId) { - LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED; + LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED; + TextureCacheIndex cacheIndex = static_cast(mTextureIdConverter[static_cast(textureId)]); - TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId); - if(cacheIndex != INVALID_CACHE_INDEX) - { - TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]); - loadState = cachedTextureInfo.loadState; - } - else + switch(static_cast(cacheIndex.detailValue.type)) { - for(auto&& elem : mExternalTextures) + case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL: { - if(elem.textureId == textureId) - { - loadState = LoadState::UPLOADED; - break; - } + TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]); + loadState = cachedTextureInfo.loadState; + break; + } + case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE: + { + loadState = LoadState::UPLOADED; + break; + } + default: + { + break; } } + return loadState; } TextureCacheManager::LoadState TextureCacheManager::GetTextureStateInternal(const TextureCacheManager::TextureId& textureId) { - LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED; - + LoadState loadState = TextureCacheManager::LoadState::NOT_STARTED; TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId); if(cacheIndex != INVALID_CACHE_INDEX) { - TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]); + TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]); loadState = cachedTextureInfo.loadState; } return loadState; } -TextureSet TextureCacheManager::GetTextureSet(const TextureCacheManager::TextureId& textureId) +Texture TextureCacheManager::GetTexture(const TextureCacheManager::TextureId& textureId, uint32_t textureIndex) { - TextureSet textureSet; // empty handle - + Texture texture; // empty handle TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId); - if(cacheIndex != INVALID_CACHE_INDEX) - { - TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex]); - textureSet = cachedTextureInfo.textureSet; - } - else + + switch(static_cast(cacheIndex.detailValue.type)) { - for(auto&& elem : mExternalTextures) + case TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL: { - if(elem.textureId == textureId) + TextureInfo& cachedTextureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]); + if(textureIndex < static_cast(cachedTextureInfo.textures.size())) { - textureSet = elem.textureSet; - break; + texture = cachedTextureInfo.textures[textureIndex]; } + break; + } + default: + { + break; } } - return textureSet; + + return texture; } TextureSet TextureCacheManager::GetExternalTextureSet(const TextureCacheManager::TextureId& textureId) { - TextureSet textureSet; // empty handle - for(auto&& elem : mExternalTextures) + TextureSet textureSet; // empty handle + TextureCacheIndex cacheIndex = GetCacheIndexFromExternalTextureId(textureId); + if(cacheIndex != INVALID_CACHE_INDEX) { - if(elem.textureId == textureId) - { - textureSet = elem.textureSet; - break; - } + textureSet = mExternalTextures[cacheIndex.GetIndex()].textureSet; } return textureSet; } -EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const TextureCacheManager::TextureId& textureId) +EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const TextureCacheManager::TextureId& bufferId) { EncodedImageBuffer encodedImageBuffer; // empty handle - for(auto&& elem : mEncodedBufferTextures) + TextureCacheIndex cacheIndex = GetCacheIndexFromEncodedImageBufferId(bufferId); + if(cacheIndex != INVALID_CACHE_INDEX) { - if(elem.textureId == textureId) - { - encodedImageBuffer = elem.encodedImageBuffer; - break; - } + encodedImageBuffer = mEncodedImageBuffers[cacheIndex.GetIndex()].encodedImageBuffer; } return encodedImageBuffer; } -EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const std::string& url) +EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const VisualUrl& url) { EncodedImageBuffer encodedImageBuffer; // empty handle - if(url.size() > 0 && VisualUrl::BUFFER == VisualUrl::GetProtocolType(url)) + if(url.IsValid() && VisualUrl::BUFFER == url.GetProtocolType()) { - std::string location = VisualUrl::GetLocation(url); + std::string location = url.GetLocation(); if(location.size() > 0u) { - TextureId targetId = std::stoi(location); - return GetEncodedImageBuffer(targetId); + TextureId bufferId = std::stoi(location); + return GetEncodedImageBuffer(bufferId); } } return encodedImageBuffer; @@ -182,86 +231,130 @@ EncodedImageBuffer TextureCacheManager::GetEncodedImageBuffer(const std::string& std::string TextureCacheManager::AddExternalTexture(const TextureSet& textureSet) { - TextureCacheManager::ExternalTextureInfo info(GenerateUniqueTextureId(), textureSet); - mExternalTextures.emplace_back(info); - return VisualUrl::CreateTextureUrl(std::to_string(info.textureId)); + TextureId textureId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE, mExternalTextures.size())); + + TextureCacheManager::ExternalTextureInfo textureInfo(textureId, textureSet); + mExternalTextures.emplace_back(textureInfo); + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalTexture() : New texture registered. textureId:%d\n", textureInfo.textureId); + + return VisualUrl::CreateTextureUrl(std::to_string(textureInfo.textureId)); } -std::string TextureCacheManager::AddExternalEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer) +std::string TextureCacheManager::AddEncodedImageBuffer(const EncodedImageBuffer& encodedImageBuffer) { // Duplication check - for(auto&& elem : mEncodedBufferTextures) + TextureHash bufferHash = static_cast(encodedImageBuffer.GetHash()); + TextureCacheIndex bufferCacheIndex = FindCachedEncodedImageBuffer(bufferHash, encodedImageBuffer); + if(bufferCacheIndex != INVALID_CACHE_INDEX) { - if(elem.encodedImageBuffer == encodedImageBuffer) - { - // If same buffer added, increase reference count and return. - elem.referenceCount++; - return VisualUrl::CreateBufferUrl(std::to_string(elem.textureId)); - } + EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[bufferCacheIndex.GetIndex()]); + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalEncodedImageBuffer() : Increase reference. bufferId:%d, cache index:%d, reference:%d\n", bufferInfo.bufferId, bufferCacheIndex.GetIndex(), static_cast(bufferInfo.referenceCount)); + + // If same buffer added, increase reference count and return. + bufferInfo.referenceCount++; + return VisualUrl::CreateBufferUrl(std::to_string(bufferInfo.bufferId)); } - TextureCacheManager::EncodedBufferTextureInfo info(GenerateUniqueTextureId(), encodedImageBuffer); - mEncodedBufferTextures.emplace_back(info); - return VisualUrl::CreateBufferUrl(std::to_string(info.textureId)); + + TextureId bufferId = GenerateTextureId(TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER, mEncodedImageBuffers.size())); + + TextureCacheManager::EncodedImageBufferInfo info(bufferId, bufferHash, encodedImageBuffer); + mEncodedImageBuffers.emplace_back(info); + + // Insert TextureHashContainer + // Find exist list -or- Create new list. + std::vector& idList = mTextureHashContainer[bufferHash]; + // We already assume that list doesn't contain id. just emplace back + idList.emplace_back(bufferId); + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::AddExternalEncodedImageBuffer() : New buffer regested. bufferId:%d\n", info.bufferId); + + return VisualUrl::CreateBufferUrl(std::to_string(info.bufferId)); } -TextureSet TextureCacheManager::RemoveExternalTexture(const std::string& url) +TextureSet TextureCacheManager::RemoveExternalTexture(const VisualUrl& url) { - if(url.size() > 0u) + TextureSet textureSet; + bool removeTextureInfo = false; + TextureCacheIndex removeTextureIndex = INVALID_CACHE_INDEX; + if(url.IsValid()) { - if(VisualUrl::TEXTURE == VisualUrl::GetProtocolType(url)) + if(VisualUrl::TEXTURE == url.GetProtocolType()) { // get the location from the Url - std::string location = VisualUrl::GetLocation(url); + std::string location = url.GetLocation(); if(location.size() > 0u) { - TextureId id = std::stoi(location); - const auto end = mExternalTextures.end(); - for(auto iter = mExternalTextures.begin(); iter != end; ++iter) + TextureId textureId = std::stoi(location); + removeTextureIndex = GetCacheIndexFromExternalTextureId(textureId); + if(removeTextureIndex != INVALID_CACHE_INDEX) { - if(iter->textureId == id) + ExternalTextureInfo& textureInfo(mExternalTextures[removeTextureIndex.GetIndex()]); + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::RemoveExternalTexture(url:%s) textureId:%d reference:%d\n", url.GetUrl().c_str(), textureId, static_cast(textureInfo.referenceCount)); + textureSet = textureInfo.textureSet; + if(--(textureInfo.referenceCount) <= 0) { - auto textureSet = iter->textureSet; - if(--(iter->referenceCount) <= 0) - { - mExternalTextures.erase(iter); - } - return textureSet; + removeTextureInfo = true; + // id life is finished. Remove it at converter + mTextureIdConverter.Remove(textureId); } } } } } - return TextureSet(); + + // Post removal process to avoid mExternalTextures reference problems. + if(removeTextureInfo) + { + // Swap last data of mExternalTextures, and pop_back. + RemoveTextureInfoByIndex(mExternalTextures, removeTextureIndex); + } + return textureSet; } -EncodedImageBuffer TextureCacheManager::RemoveExternalEncodedImageBuffer(const std::string& url) +EncodedImageBuffer TextureCacheManager::RemoveEncodedImageBuffer(const VisualUrl& url) { - if(url.size() > 0u) + EncodedImageBuffer encodedImageBuffer; + bool removeBufferInfo = false; + TextureCacheIndex removeBufferIndex = INVALID_CACHE_INDEX; + if(url.IsValid()) { - if(VisualUrl::BUFFER == VisualUrl::GetProtocolType(url)) + if(VisualUrl::BUFFER == url.GetProtocolType()) { // get the location from the Url - std::string location = VisualUrl::GetLocation(url); + std::string location = url.GetLocation(); if(location.size() > 0u) { - TextureId id = std::stoi(location); - const auto end = mEncodedBufferTextures.end(); - for(auto iter = mEncodedBufferTextures.begin(); iter != end; ++iter) + TextureId bufferId = std::stoi(location); + removeBufferIndex = GetCacheIndexFromEncodedImageBufferId(bufferId); + + if(removeBufferIndex != INVALID_CACHE_INDEX) { - if(iter->textureId == id) + EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[removeBufferIndex.GetIndex()]); + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::RemoveEncodedImageBuffer(url:%s) bufferId:%d reference:%d\n", url.GetUrl().c_str(), bufferId, static_cast(bufferInfo.referenceCount)); + + encodedImageBuffer = bufferInfo.encodedImageBuffer; + if(--(bufferInfo.referenceCount) <= 0) { - auto encodedImageBuffer = iter->encodedImageBuffer; - if(--(iter->referenceCount) <= 0) - { - mEncodedBufferTextures.erase(iter); - } - return encodedImageBuffer; + removeBufferInfo = true; + // Step 1. remove current textureId information in mTextureHashContainer. + RemoveHashId(bufferInfo.bufferHash, bufferId); + // Step 2. id life is finished. Remove it at converter + mTextureIdConverter.Remove(bufferId); } } } } } - return EncodedImageBuffer(); + + // Post removal process to avoid mEncodedImageBuffers reference problems. + if(removeBufferInfo) + { + // Step 3. swap last data of mEncodedImageBuffers, and pop_back. + RemoveTextureInfoByIndex(mEncodedImageBuffers, removeBufferIndex); + } + return encodedImageBuffer; } void TextureCacheManager::UseExternalResource(const VisualUrl& url) @@ -271,14 +364,15 @@ void TextureCacheManager::UseExternalResource(const VisualUrl& url) std::string location = url.GetLocation(); if(location.size() > 0u) { - TextureId id = std::stoi(location); - for(auto&& elem : mExternalTextures) + TextureId id = std::stoi(location); + TextureCacheIndex cacheIndex = GetCacheIndexFromExternalTextureId(id); + if(cacheIndex != INVALID_CACHE_INDEX) { - if(elem.textureId == id) - { - elem.referenceCount++; - return; - } + ExternalTextureInfo& textureInfo(mExternalTextures[cacheIndex.GetIndex()]); + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::UseExternalResource(url:%s) type:TEXTURE, location:%s, reference:%d\n", url.GetUrl().c_str(), url.GetLocation().c_str(), static_cast(textureInfo.referenceCount)); + + textureInfo.referenceCount++; + return; } } } @@ -287,59 +381,117 @@ void TextureCacheManager::UseExternalResource(const VisualUrl& url) std::string location = url.GetLocation(); if(location.size() > 0u) { - TextureId id = std::stoi(location); - for(auto&& elem : mEncodedBufferTextures) + TextureId id = std::stoi(location); + TextureCacheIndex cacheIndex = GetCacheIndexFromEncodedImageBufferId(id); + if(cacheIndex != INVALID_CACHE_INDEX) { - if(elem.textureId == id) - { - elem.referenceCount++; - return; - } + EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]); + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::UseExternalResource(url:%s) type:BUFFER, location:%s, reference:%d\n", url.GetUrl().c_str(), url.GetLocation().c_str(), static_cast(bufferInfo.referenceCount)); + + bufferInfo.referenceCount++; + return; } } } } -TextureCacheManager::TextureId TextureCacheManager::GenerateUniqueTextureId() +TextureCacheManager::TextureId TextureCacheManager::GenerateTextureId(const TextureCacheIndex& textureCacheIndex) { - return mCurrentTextureId++; + return mTextureIdConverter.Add(static_cast(textureCacheIndex.indexValue)); } TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromId(const TextureCacheManager::TextureId& textureId) { - const TextureCacheIndex size = static_cast(mTextureInfoContainer.size()); + if(textureId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX; - for(TextureCacheIndex i = 0; i < size; ++i) + TextureCacheIndex cacheIndex = static_cast(mTextureIdConverter[static_cast(textureId)]); + if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL)) { - if(mTextureInfoContainer[i].textureId == textureId) + return INVALID_CACHE_INDEX; + } + + DALI_ASSERT_DEBUG(static_cast(cacheIndex.GetIndex()) < mTextureInfoContainer.size()); + + return cacheIndex; +} + +TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromExternalTextureId(const TextureCacheManager::TextureId& textureId) +{ + if(textureId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX; + + TextureCacheIndex cacheIndex = static_cast(mTextureIdConverter[static_cast(textureId)]); + if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_TEXTURE)) + { + return INVALID_CACHE_INDEX; + } + + DALI_ASSERT_DEBUG(static_cast(cacheIndex.GetIndex()) < mExternalTextures.size()); + + return cacheIndex; +} + +TextureCacheManager::TextureCacheIndex TextureCacheManager::GetCacheIndexFromEncodedImageBufferId(const TextureCacheManager::TextureId& bufferId) +{ + if(bufferId == INVALID_TEXTURE_ID) return INVALID_CACHE_INDEX; + + TextureCacheIndex cacheIndex = static_cast(mTextureIdConverter[static_cast(bufferId)]); + if(DALI_UNLIKELY(cacheIndex.detailValue.type != TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_BUFFER)) + { + return INVALID_CACHE_INDEX; + } + + DALI_ASSERT_DEBUG(static_cast(cacheIndex.GetIndex()) < mEncodedImageBuffers.size()); + + return cacheIndex; +} + +TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedEncodedImageBuffer(const TextureCacheManager::TextureHash& hash, const EncodedImageBuffer& encodedImageBuffer) +{ + // Iterate through our hashes to find a match. + const auto& hashIterator = mTextureHashContainer.find(hash); + if(hashIterator != mTextureHashContainer.cend()) + { + for(const auto& id : hashIterator->second) { - return i; + // We have a match, now we check all the original parameters in case of a hash collision. + TextureCacheIndex cacheIndex = GetCacheIndexFromEncodedImageBufferId(id); + if(cacheIndex != INVALID_CACHE_INDEX) + { + EncodedImageBufferInfo& bufferInfo(mEncodedImageBuffers[cacheIndex.GetIndex()]); + + if(bufferInfo.encodedImageBuffer == encodedImageBuffer) + { + // The found encoded image buffer. + return cacheIndex; + } + } } } + // Default to an invalid ID, in case we do not find a match. return INVALID_CACHE_INDEX; } TextureCacheManager::TextureHash TextureCacheManager::GenerateHash( - const std::string& url, + const VisualUrl& url, const Dali::ImageDimensions& size, const Dali::FittingMode::Type& fittingMode, const Dali::SamplingMode::Type& samplingMode, const TextureCacheManager::UseAtlas& useAtlas, const TextureCacheManager::TextureId& maskTextureId, - const bool& cropToMask) + const bool& cropToMask, + const std::uint32_t& frameIndex) { - std::vector hashTarget(url.begin(), url.end()); - const size_t urlLength = url.length(); - const uint16_t width = size.GetWidth(); - const uint16_t height = size.GetWidth(); + std::vector hashTarget; + const uint16_t width = size.GetWidth(); + const uint16_t height = size.GetWidth(); // If either the width or height has been specified, include the resizing options in the hash if(width != 0 || height != 0) { // We are appending 5 bytes to the URL to form the hash input. - hashTarget.resize(urlLength + 5u); - std::uint8_t* hashTargetPtr = &(hashTarget[urlLength]); + hashTarget.resize(5u); + std::uint8_t* hashTargetPtr = &(hashTarget[0u]); // Pack the width and height (4 bytes total). *hashTargetPtr++ = size.GetWidth() & 0xff; @@ -354,19 +506,19 @@ TextureCacheManager::TextureHash TextureCacheManager::GenerateHash( else { // We are not including sizing information, but we still need an extra byte for atlasing. - hashTarget.resize(urlLength + 1u); + hashTarget.resize(1u); // Add the atlasing to the hash input. switch(useAtlas) { case UseAtlas::NO_ATLAS: { - hashTarget[urlLength] = 'f'; + hashTarget[0u] = 'f'; break; } case UseAtlas::USE_ATLAS: { - hashTarget[urlLength] = 't'; + hashTarget[0u] = 't'; break; } } @@ -389,112 +541,182 @@ TextureCacheManager::TextureHash TextureCacheManager::GenerateHash( *hashTargetPtr++ = (cropToMask ? 'C' : 'M'); } - return Dali::CalculateHash(hashTarget); + // Append the frameIndex. We don't do additional job when frameIndex = 0u due to the non-animated image case. + if(frameIndex > 0u) + { + auto textureIdIndex = hashTarget.size(); + hashTarget.resize(hashTarget.size() + sizeof(std::uint32_t)); + std::uint8_t* hashTargetPtr = reinterpret_cast(&(hashTarget[textureIdIndex])); + + // Append the frame index to the end of the URL byte by byte: + std::uint32_t saltedFrameIndex = frameIndex; + for(size_t byteIter = 0; byteIter < sizeof(std::uint8_t); ++byteIter) + { + *hashTargetPtr++ = saltedFrameIndex & 0xff; + saltedFrameIndex >>= 8u; + } + } + + return url.GetUrlHash() ^ Dali::CalculateHash(hashTarget); } TextureCacheManager::TextureCacheIndex TextureCacheManager::FindCachedTexture( const TextureCacheManager::TextureHash& hash, - const std::string& url, + const VisualUrl& url, const Dali::ImageDimensions& size, const Dali::FittingMode::Type& fittingMode, const Dali::SamplingMode::Type& samplingMode, const TextureCacheManager::UseAtlas& useAtlas, + const StorageType& storageType, const TextureCacheManager::TextureId& maskTextureId, + const bool& cropToMask, const TextureCacheManager::MultiplyOnLoad& preMultiplyOnLoad, - bool isAnimatedImage, - const bool& cropToMask) + const bool& isAnimatedImage, + const std::uint32_t& frameIndex) { - // Default to an invalid ID, in case we do not find a match. - TextureCacheIndex cacheIndex = INVALID_CACHE_INDEX; - // Iterate through our hashes to find a match. - const TextureCacheIndex count = static_cast(mTextureInfoContainer.size()); - for(TextureCacheIndex i = 0u; i < count; ++i) + const auto& hashIterator = mTextureHashContainer.find(hash); + if(hashIterator != mTextureHashContainer.cend()) { - if(mTextureInfoContainer[i].hash == hash) + for(const auto& textureId : hashIterator->second) { // We have a match, now we check all the original parameters in case of a hash collision. - TextureInfo& textureInfo(mTextureInfoContainer[i]); - - if((url == textureInfo.url.GetUrl()) && - (useAtlas == textureInfo.useAtlas) && - (maskTextureId == textureInfo.maskTextureId) && - (cropToMask == textureInfo.cropToMask) && - (size == textureInfo.desiredSize) && - (isAnimatedImage == textureInfo.isAnimatedImageFormat) && - ((size.GetWidth() == 0 && size.GetHeight() == 0) || - (fittingMode == textureInfo.fittingMode && - samplingMode == textureInfo.samplingMode))) + TextureCacheIndex cacheIndex = GetCacheIndexFromId(textureId); + if(cacheIndex != INVALID_CACHE_INDEX) { - // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different. - // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false. - if((preMultiplyOnLoad == MultiplyOnLoad::MULTIPLY_ON_LOAD && textureInfo.preMultiplyOnLoad) || (preMultiplyOnLoad == MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY && !textureInfo.preMultiplied)) + TextureInfo& textureInfo(mTextureInfoContainer[cacheIndex.GetIndex()]); + + if((url.GetUrl() == textureInfo.url.GetUrl()) && + (useAtlas == textureInfo.useAtlas) && + (maskTextureId == textureInfo.maskTextureId) && + (cropToMask == textureInfo.cropToMask) && + (size == textureInfo.desiredSize) && + (isAnimatedImage == textureInfo.isAnimatedImageFormat) && + (storageType == textureInfo.storageType) && + (frameIndex == textureInfo.frameIndex) && + ((size.GetWidth() == 0 && size.GetHeight() == 0) || + (fittingMode == textureInfo.fittingMode && + samplingMode == textureInfo.samplingMode))) { - // The found Texture is a match. - cacheIndex = i; - break; + // 1. If preMultiplyOnLoad is MULTIPLY_ON_LOAD, then textureInfo.preMultiplyOnLoad should be true. The premultiplication result can be different. + // 2. If preMultiplyOnLoad is LOAD_WITHOUT_MULTIPLY, then textureInfo.preMultiplied should be false. + if((preMultiplyOnLoad == MultiplyOnLoad::MULTIPLY_ON_LOAD && textureInfo.preMultiplyOnLoad) || (preMultiplyOnLoad == MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY && !textureInfo.preMultiplied)) + { + // The found Texture is a match. + return cacheIndex; + } } } } } - return cacheIndex; + // Default to an invalid ID, in case we do not find a match. + return INVALID_CACHE_INDEX; } TextureCacheManager::TextureCacheIndex TextureCacheManager::AppendCache(const TextureCacheManager::TextureInfo& textureInfo) { - TextureCacheIndex cacheIndex = static_cast(mTextureInfoContainer.size()); + // If we use EncodedImageBuffer, increase reference during it contains mTextureInfoContainer. + // This reference will be decreased when we call RemoveCache + if(textureInfo.url.GetProtocolType() == VisualUrl::BUFFER) + { + UseExternalResource(textureInfo.url); + } + + TextureHash hash = textureInfo.hash; + TextureId id = textureInfo.textureId; + + // Insert TextureHash container first + // Find exist list -or- Create new list. + std::vector& idList = mTextureHashContainer[hash]; + // We already assume that list doesn't contain id. just emplace back + idList.emplace_back(id); + + // Insert TextureInfo back of mTextureInfoContainer. + TextureCacheIndex cacheIndex = TextureCacheIndex(TextureCacheIndexType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, mTextureInfoContainer.size()); mTextureInfoContainer.emplace_back(textureInfo); + + // Add converter id --> cacheIndex + // NOTE : We should assume that id already generated by GenerateTextureId function. + mTextureIdConverter[id] = cacheIndex; + return cacheIndex; } -void TextureCacheManager::RemoveCache(const TextureCacheManager::TextureId& textureId) +void TextureCacheManager::RemoveCache(TextureCacheManager::TextureInfo& textureInfo) { - TextureCacheIndex textureInfoIndex = GetCacheIndexFromId(textureId); + TextureCacheIndex textureInfoIndex = GetCacheIndexFromId(textureInfo.textureId); + bool removeTextureInfo = false; + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::Remove(textureId:%d) url:%s\n cacheIdx:%d loadState:%s reference count = %d\n", textureInfo.textureId, textureInfo.url.GetUrl().c_str(), textureInfoIndex.GetIndex(), GET_LOAD_STATE_STRING(textureInfo.loadState), textureInfo.referenceCount); - if(textureInfoIndex != INVALID_CACHE_INDEX) + // Decrement the reference count and check if this is the last user of this Texture. + if(--textureInfo.referenceCount <= 0) { - TextureInfo& textureInfo(mTextureInfoContainer[textureInfoIndex]); - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureCacheManager::Remove(textureId:%d) url:%s\n cacheIdx:%d loadState:%s reference count = %d\n", textureId, textureInfo.url.GetUrl().c_str(), textureInfoIndex, GET_LOAD_STATE_STRING(textureInfo.loadState), textureInfo.referenceCount); + // This is the last remove for this Texture. + textureInfo.referenceCount = 0; - // Decrement the reference count and check if this is the last user of this Texture. - if(--textureInfo.referenceCount <= 0) + // If loaded, we can remove the TextureInfo and the Atlas (if atlased). + if(textureInfo.loadState == LoadState::UPLOADED) { - // This is the last remove for this Texture. - textureInfo.referenceCount = 0; - bool removeTextureInfo = false; - - // If loaded, we can remove the TextureInfo and the Atlas (if atlased). - if(textureInfo.loadState == LoadState::UPLOADED) - { - if(textureInfo.atlas) - { - textureInfo.atlas.Remove(textureInfo.atlasRect); - } - removeTextureInfo = true; - } - else if(textureInfo.loadState == LoadState::LOADING) + if(textureInfo.atlas) { - // We mark the textureInfo for removal. - // Once the load has completed, this method will be called again. - textureInfo.loadState = LoadState::CANCELLED; + textureInfo.atlas.Remove(textureInfo.atlasRect); } - else + removeTextureInfo = true; + } + else if(textureInfo.loadState == LoadState::LOADING || textureInfo.loadState == LoadState::MASK_APPLYING) + { + // We mark the textureInfo for removal. + // Once the load has completed, this method will be called again. + textureInfo.loadState = LoadState::CANCELLED; + } + else + { + // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data. + removeTextureInfo = true; + } + + // If the state allows us to remove the TextureInfo data, we do so. + if(removeTextureInfo) + { + // If url location is BUFFER, decrease reference count of EncodedImageBuffer. + if(textureInfo.url.IsBufferResource()) { - // In other states, we are not waiting for a load so we are safe to remove the TextureInfo data. - removeTextureInfo = true; + RemoveEncodedImageBuffer(textureInfo.url.GetUrl()); } - // If the state allows us to remove the TextureInfo data, we do so. - if(removeTextureInfo) + // Permanently remove the textureInfo struct. + + // Step 1. remove current textureId information in mTextureHashContainer. + RemoveHashId(textureInfo.hash, textureInfo.textureId); + // Step 2. make textureId is not using anymore. After this job, we can reuse textureId. + mTextureIdConverter.Remove(textureInfo.textureId); + } + } + + // Post removal process to avoid mTextureInfoContainer reference problems. + if(removeTextureInfo) + { + // Step 3. swap last data of TextureInfoContainer, and pop_back. + RemoveTextureInfoByIndex(mTextureInfoContainer, textureInfoIndex); + } +} + +void TextureCacheManager::RemoveHashId(const TextureCacheManager::TextureHash& textureHash, const TextureCacheManager::TextureId& textureId) +{ + auto hashIterator = mTextureHashContainer.find(textureHash); + if(hashIterator != mTextureHashContainer.end()) + { + auto hashIdList = hashIterator->second; + const auto& hashIdIterator = std::find(hashIdList.cbegin(), hashIdList.cend(), textureId); + if(hashIdIterator != hashIdList.cend()) + { + hashIdList.erase(hashIdIterator); + if(hashIdList.size() == 0) { - // If url location is BUFFER, decrease reference count of EncodedImageBuffer. - if(textureInfo.url.IsBufferResource()) - { - RemoveExternalEncodedImageBuffer(textureInfo.url.GetUrl()); - } - // Permanently remove the textureInfo struct. - mTextureInfoContainer.erase(mTextureInfoContainer.begin() + textureInfoIndex); + // If id list in current hash is empty, remove it self in the container. + mTextureHashContainer.erase(hashIterator); } } }