X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dali-toolkit%2Finternal%2Ftexture-manager%2Ftexture-manager-impl.cpp;h=0dceae29a385926be6eca9daf6b9d59c7c72a211;hb=6a48a1e1e9ef88da73bee62a7e07f690f430b7d5;hp=2bb79a29642c2d48dd2b067e251977bd13ac358e;hpb=5b4a2b122d0c633084864a099de5ba5b8d919373;p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git diff --git a/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp b/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp index 2bb79a2..0dceae2 100644 --- a/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp +++ b/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * Copyright (c) 2023 Samsung Electronics Co., Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,12 +16,12 @@ */ // CLASS HEADER -#include "texture-manager-impl.h" +#include // EXTERNAL HEADERS -#include #include #include +#include #include #include @@ -33,43 +33,10 @@ namespace { -constexpr auto INITIAL_HASH_NUMBER = size_t{0u}; -constexpr auto DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS = size_t{4u}; -constexpr auto DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS = size_t{8u}; +constexpr auto INITIAL_HASH_NUMBER = size_t{0u}; -constexpr auto TEXTURE_INDEX = 0u; ///< The Index for texture -constexpr auto MASK_TEXTURE_INDEX = 1u; ///< The Index for mask texture - -constexpr auto NUMBER_OF_LOCAL_LOADER_THREADS_ENV = "DALI_TEXTURE_LOCAL_THREADS"; -constexpr auto NUMBER_OF_REMOTE_LOADER_THREADS_ENV = "DALI_TEXTURE_REMOTE_THREADS"; -constexpr auto LOAD_IMAGE_YUV_PLANES_ENV = "DALI_LOAD_IMAGE_YUV_PLANES"; - -size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue) -{ - using Dali::EnvironmentVariable::GetEnvironmentVariable; - auto numberString = GetEnvironmentVariable(environmentVariable); - auto numberOfThreads = numberString ? std::strtoul(numberString, nullptr, 10) : 0; - constexpr auto MAX_NUMBER_OF_THREADS = 100u; - DALI_ASSERT_DEBUG(numberOfThreads < MAX_NUMBER_OF_THREADS); - return (numberOfThreads > 0 && numberOfThreads < MAX_NUMBER_OF_THREADS) ? numberOfThreads : defaultValue; -} - -size_t GetNumberOfLocalLoaderThreads() -{ - return GetNumberOfThreads(NUMBER_OF_LOCAL_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS); -} - -size_t GetNumberOfRemoteLoaderThreads() -{ - return GetNumberOfThreads(NUMBER_OF_REMOTE_LOADER_THREADS_ENV, DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS); -} - -bool NeedToLoadYuvPlanes() -{ - auto loadYuvPlanesString = Dali::EnvironmentVariable::GetEnvironmentVariable(LOAD_IMAGE_YUV_PLANES_ENV); - bool loadYuvPlanes = loadYuvPlanesString ? std::atoi(loadYuvPlanesString) : false; - return loadYuvPlanes; -} +constexpr auto TEXTURE_INDEX = 0u; ///< The Index for texture +constexpr auto MASK_TEXTURE_INDEX = 1u; ///< The Index for mask texture } // namespace namespace Dali @@ -92,6 +59,7 @@ Debug::Filter* gTextureManagerLogFilter = Debug::Filter::New(Debug::NoLogging, f loadState == TextureManagerType::LoadState::MASK_APPLIED ? "MASK_APPLIED" : \ loadState == TextureManagerType::LoadState::UPLOADED ? "UPLOADED" : \ loadState == TextureManagerType::LoadState::CANCELLED ? "CANCELLED" : \ + loadState == TextureManagerType::LoadState::MASK_CANCELLED ? "MASK_CANCELLED" : \ loadState == TextureManagerType::LoadState::LOAD_FAILED ? "LOAD_FAILED" : \ "Unknown" // clang-format on @@ -129,15 +97,15 @@ TextureManager::MaskingData::MaskingData() { } -TextureManager::TextureManager() +TextureManager::TextureManager(bool loadYuvPlanes) : mTextureCacheManager(), - mAsyncLocalLoaders(GetNumberOfLocalLoaderThreads(), [&]() { return TextureAsyncLoadingHelper(*this); }), - mAsyncRemoteLoaders(GetNumberOfRemoteLoaderThreads(), [&]() { return TextureAsyncLoadingHelper(*this); }), + mAsyncLoader(std::unique_ptr(new TextureAsyncLoadingHelper(*this))), mLifecycleObservers(), mLoadQueue(), + mLoadingQueueTextureId(INVALID_TEXTURE_ID), mRemoveQueue(), - mQueueLoadFlag(false), - mLoadYuvPlanes(NeedToLoadYuvPlanes()) + mLoadYuvPlanes(loadYuvPlanes), + mRemoveProcessorRegistered(false) { // Initialize the AddOn RenderingAddOn::Get(); @@ -145,6 +113,12 @@ TextureManager::TextureManager() TextureManager::~TextureManager() { + if(mRemoveProcessorRegistered && Adaptor::IsAvailable()) + { + Adaptor::Get().UnregisterProcessor(*this, true); + mRemoveProcessorRegistered = false; + } + for(auto iter = mLifecycleObservers.Begin(), endIter = mLifecycleObservers.End(); iter != endIter; ++iter) { (*iter)->TextureManagerDestroyed(); @@ -157,6 +131,8 @@ TextureSet TextureManager::LoadAnimatedImageTexture( const uint32_t& frameIndex, TextureManager::TextureId& textureId, MaskingDataPointer& maskInfo, + const Dali::ImageDimensions& desiredSize, + const Dali::FittingMode::Type& fittingMode, const Dali::SamplingMode::Type& samplingMode, const bool& synchronousLoading, TextureUploadObserver* textureObserver, @@ -169,7 +145,7 @@ TextureSet TextureManager::LoadAnimatedImageTexture( Devel::PixelBuffer pixelBuffer; if(animatedImageLoading) { - pixelBuffer = animatedImageLoading.LoadFrame(frameIndex); + pixelBuffer = animatedImageLoading.LoadFrame(frameIndex, desiredSize, fittingMode, samplingMode); } if(!pixelBuffer) { @@ -180,7 +156,7 @@ TextureSet TextureManager::LoadAnimatedImageTexture( Texture maskTexture; if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid()) { - Devel::PixelBuffer maskPixelBuffer = LoadImageFromFile(maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true); + Devel::PixelBuffer maskPixelBuffer = LoadImageFromFile(maskInfo->mAlphaMaskUrl.GetUrl(), desiredSize, fittingMode, samplingMode, true); if(maskPixelBuffer) { if(!maskInfo->mPreappliedMasking) @@ -235,7 +211,7 @@ TextureSet TextureManager::LoadAnimatedImageTexture( } } - textureId = RequestLoadInternal(url, alphaMaskId, contentScaleFactor, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR, UseAtlas::NO_ATLAS, cropToMask, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiplyOnLoad, animatedImageLoading, frameIndex, false); + textureId = RequestLoadInternal(url, alphaMaskId, textureId, contentScaleFactor, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, cropToMask, StorageType::UPLOAD_TO_TEXTURE, textureObserver, true, TextureManager::ReloadPolicy::CACHED, preMultiplyOnLoad, animatedImageLoading, frameIndex, false); TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId); if(loadState == TextureManager::LoadState::UPLOADED) @@ -283,7 +259,7 @@ Devel::PixelBuffer TextureManager::LoadPixelBuffer( } else { - RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false); + RequestLoadInternal(url, INVALID_TEXTURE_ID, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, StorageType::RETURN_PIXEL_BUFFER, textureObserver, orientationCorrection, TextureManager::ReloadPolicy::FORCED, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, false); } return pixelBuffer; @@ -318,13 +294,40 @@ TextureSet TextureManager::LoadTexture( std::string location = url.GetLocation(); if(location.size() > 0u) { - TextureId id = std::stoi(location); - textureSet = mTextureCacheManager.GetExternalTextureSet(id); - if(textureSet) + TextureId id = std::stoi(location); + auto externalTextureInfo = mTextureCacheManager.GetExternalTextureInfo(id); + if(externalTextureInfo.textureSet) { - preMultiplyOnLoad = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - textureId = id; - return textureSet; + if(preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD) + { + // Change preMultiplyOnLoad value so make caller determine to preMultiplyAlpha or not. + // TODO : Should we seperate input and output value? + preMultiplyOnLoad = externalTextureInfo.preMultiplied ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; + } + + TextureId alphaMaskId = INVALID_TEXTURE_ID; + if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid()) + { + maskInfo->mAlphaMaskId = RequestMaskLoad(maskInfo->mAlphaMaskUrl, StorageType::KEEP_TEXTURE, synchronousLoading); + alphaMaskId = maskInfo->mAlphaMaskId; + + // Create new textureId. this textureId is not same as location + textureId = RequestLoad(url, alphaMaskId, textureId, 1.0f, desiredSize, fittingMode, samplingMode, UseAtlas::NO_ATLAS, false, textureObserver, orientationCorrection, reloadPolicy, preMultiplyOnLoad, synchronousLoading); + + TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId); + if(loadState == TextureManager::LoadState::UPLOADED) + { + textureSet = GetTextureSet(textureId); + } + } + else + { + // TextureId is same as location + textureId = id; + + textureSet = TextureSet::New(); + textureSet.SetTexture(TEXTURE_INDEX, externalTextureInfo.textureSet.GetTexture(TEXTURE_INDEX)); + } } } } @@ -420,6 +423,7 @@ TextureSet TextureManager::LoadTexture( textureId = RequestLoad( url, alphaMaskId, + textureId, contentScaleFactor, desiredSize, fittingMode, @@ -446,7 +450,7 @@ TextureSet TextureManager::LoadTexture( loadState == TextureManager::LoadState::MASK_APPLYING || loadState == TextureManager::LoadState::MASK_APPLIED || loadState == TextureManager::LoadState::NOT_STARTED || - mQueueLoadFlag); + mLoadingQueueTextureId != INVALID_TEXTURE_ID); } else { @@ -475,12 +479,13 @@ TextureManager::TextureId TextureManager::RequestLoad( TextureManager::MultiplyOnLoad& preMultiplyOnLoad, const bool& synchronousLoading) { - return RequestLoadInternal(url, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading); + return RequestLoadInternal(url, INVALID_TEXTURE_ID, INVALID_TEXTURE_ID, 1.0f, desiredSize, fittingMode, samplingMode, useAtlas, false, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading); } TextureManager::TextureId TextureManager::RequestLoad( const VisualUrl& url, const TextureManager::TextureId& maskTextureId, + const TextureManager::TextureId& previousTextureId, const float& contentScale, const Dali::ImageDimensions& desiredSize, const Dali::FittingMode::Type& fittingMode, @@ -493,7 +498,7 @@ TextureManager::TextureId TextureManager::RequestLoad( TextureManager::MultiplyOnLoad& preMultiplyOnLoad, const bool& synchronousLoading) { - return RequestLoadInternal(url, maskTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading); + return RequestLoadInternal(url, maskTextureId, previousTextureId, contentScale, desiredSize, fittingMode, samplingMode, useAtlas, cropToMask, StorageType::UPLOAD_TO_TEXTURE, observer, orientationCorrection, reloadPolicy, preMultiplyOnLoad, Dali::AnimatedImageLoading(), 0u, synchronousLoading); } TextureManager::TextureId TextureManager::RequestMaskLoad( @@ -503,12 +508,13 @@ TextureManager::TextureId TextureManager::RequestMaskLoad( { // Use the normal load procedure to get the alpha mask. auto preMultiply = TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, UseAtlas::NO_ATLAS, false, storageType, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading); + return RequestLoadInternal(maskUrl, INVALID_TEXTURE_ID, INVALID_TEXTURE_ID, 1.0f, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, UseAtlas::NO_ATLAS, false, storageType, NULL, true, TextureManager::ReloadPolicy::CACHED, preMultiply, Dali::AnimatedImageLoading(), 0u, synchronousLoading); } TextureManager::TextureId TextureManager::RequestLoadInternal( const VisualUrl& url, const TextureManager::TextureId& maskTextureId, + const TextureManager::TextureId& previousTextureId, const float& contentScale, const Dali::ImageDimensions& desiredSize, const Dali::FittingMode::Type& fittingMode, @@ -540,9 +546,10 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( // Check if the requested Texture exists in the cache. if(cacheIndex != INVALID_CACHE_INDEX) { - if(TextureManager::ReloadPolicy::CACHED == reloadPolicy) + if(TextureManager::ReloadPolicy::CACHED == reloadPolicy || TextureManager::INVALID_TEXTURE_ID == previousTextureId) { - // Mark this texture being used by another client resource. Forced reload would replace the current texture + // Mark this texture being used by another client resource, or Reload forced without request load before. + // Forced reload which have current texture before, would replace the current texture. // without the need for incrementing the reference count. ++(mTextureCacheManager[cacheIndex].referenceCount); } @@ -550,8 +557,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( // Update preMultiplyOnLoad value. It should be changed according to preMultiplied value of the cached info. preMultiplyOnLoad = mTextureCacheManager[cacheIndex].preMultiplied ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY; - - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture id@%d, textureId=%d, frameindex=%d, premultiplied=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, frameIndex, mTextureCacheManager[cacheIndex].preMultiplied ? 1 : 0); + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) Using cached texture id@%d, textureId=%d, maskTextureId=%d, prevTextureId=%d, frameindex=%d, premultiplied=%d, refCount=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, previousTextureId, frameIndex, mTextureCacheManager[cacheIndex].preMultiplied ? 1 : 0, static_cast(mTextureCacheManager[cacheIndex].referenceCount)); } if(textureId == INVALID_TEXTURE_ID) // There was no caching, or caching not required @@ -562,8 +568,7 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( // Cache new texutre, and get cacheIndex. cacheIndex = mTextureCacheManager.AppendCache(TextureInfo(textureId, maskTextureId, url, desiredSize, contentScale, fittingMode, samplingMode, false, cropToMask, useAtlas, textureHash, orientationCorrection, preMultiply, animatedImageLoading, frameIndex, loadYuvPlanes)); - - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d, frameindex=%d premultiply=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, frameIndex, preMultiply); + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestLoad( url=%s observer=%p ) New texture, cacheIndex:%d, textureId=%d, maskTextureId=%d, frameindex=%d premultiply=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, frameIndex, preMultiply); } // The below code path is common whether we are using the cache or not. @@ -574,6 +579,16 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( textureInfo.storageType = storageType; textureInfo.orientationCorrection = orientationCorrection; + // the case using external texture has already been loaded texture, so change its status to WAITING_FOR_MASK. + if(url.GetProtocolType() == VisualUrl::TEXTURE) + { + if(textureInfo.loadState != LoadState::UPLOADED) + { + textureInfo.preMultiplied = (preMultiplyOnLoad == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD); + textureInfo.loadState = TextureManager::LoadState::WAITING_FOR_MASK; + } + } + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureInfo loadState:%s\n", GET_LOAD_STATE_STRING(textureInfo.loadState)); // Force reloading of texture by setting loadState unless already loading or cancelled. @@ -582,10 +597,10 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( TextureManager::LoadState::WAITING_FOR_MASK != textureInfo.loadState && TextureManager::LoadState::MASK_APPLYING != textureInfo.loadState && TextureManager::LoadState::MASK_APPLIED != textureInfo.loadState && - TextureManager::LoadState::CANCELLED != textureInfo.loadState) + TextureManager::LoadState::CANCELLED != textureInfo.loadState && + TextureManager::LoadState::MASK_CANCELLED != textureInfo.loadState) { - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::RequestLoad( url=%s observer=%p ) ForcedReload cacheIndex:%d, textureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId); - + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::RequestLoad( url=%s observer=%p ) ForcedReload cacheIndex:%d, textureId=%d, maskTextureId=%d, prevTextureId=%d\n", url.GetUrl().c_str(), observer, cacheIndex.GetIndex(), textureId, maskTextureId, previousTextureId); textureInfo.loadState = TextureManager::LoadState::NOT_STARTED; } @@ -606,7 +621,11 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( case TextureManager::LoadState::MASK_APPLYING: case TextureManager::LoadState::MASK_APPLIED: { - ObserveTexture(textureInfo, observer); + // Do not observe even we reload forced when texture is already loading state. + if(TextureManager::ReloadPolicy::CACHED == reloadPolicy || TextureManager::INVALID_TEXTURE_ID == previousTextureId) + { + ObserveTexture(textureInfo, observer); + } break; } case TextureManager::LoadState::UPLOADED: @@ -625,6 +644,14 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( ObserveTexture(textureInfo, observer); break; } + case TextureManager::LoadState::MASK_CANCELLED: + { + // A cancelled texture hasn't finished mask applying yet. Treat as a mask applying texture + // (it's ref count has already been incremented, above) + textureInfo.loadState = TextureManager::LoadState::MASK_APPLYING; + ObserveTexture(textureInfo, observer); + break; + } case TextureManager::LoadState::LOAD_FINISHED: { // Loading has already completed. @@ -643,58 +670,66 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( if(!(textureInfo.loadState == TextureManager::LoadState::UPLOADED || textureInfo.loadState == TextureManager::LoadState::LOAD_FINISHED)) { - std::vector pixelBuffers; - LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection, loadYuvPlanes, pixelBuffers); - - if(pixelBuffers.empty()) - { - // If pixelBuffer loading is failed in synchronously, call Remove() method. - Remove(textureId, nullptr); - return INVALID_TEXTURE_ID; - } - - if(storageType == StorageType::KEEP_PIXEL_BUFFER) // For the mask image loading. + if(url.GetProtocolType() == VisualUrl::TEXTURE) { - textureInfo.pixelBuffer = pixelBuffers[0]; // Store the pixel data - textureInfo.loadState = LoadState::LOAD_FINISHED; + // Get external textureSet from cacheManager. + std::string location = textureInfo.url.GetLocation(); + if(!location.empty()) + { + TextureId id = std::stoi(location); + auto externalTextureInfo = mTextureCacheManager.GetExternalTextureInfo(id); + textureInfo.textures.push_back(externalTextureInfo.textureSet.GetTexture(0)); + textureInfo.loadState = LoadState::UPLOADED; + } } - else // For the image loading. + else { - Texture maskTexture; - if(maskTextureId != INVALID_TEXTURE_ID) + std::vector pixelBuffers; + LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection, loadYuvPlanes, pixelBuffers); + + if(pixelBuffers.empty()) + { + // If pixelBuffer loading is failed in synchronously, call RequestRemove() method. + RequestRemove(textureId, nullptr); + return INVALID_TEXTURE_ID; + } + + if(storageType == StorageType::KEEP_PIXEL_BUFFER) // For the mask image loading. + { + textureInfo.pixelBuffer = pixelBuffers[0]; // Store the pixel data + textureInfo.loadState = LoadState::LOAD_FINISHED; + } + else // For the image loading. { - TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(maskTextureId); - if(maskCacheIndex != INVALID_CACHE_INDEX) + Texture maskTexture; + if(maskTextureId != INVALID_TEXTURE_ID) { - if(mTextureCacheManager[maskCacheIndex].storageType == StorageType::KEEP_TEXTURE) + TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(maskTextureId); + if(maskCacheIndex != INVALID_CACHE_INDEX) { - if(!mTextureCacheManager[maskCacheIndex].textures.empty()) + if(mTextureCacheManager[maskCacheIndex].storageType == StorageType::KEEP_PIXEL_BUFFER) { - maskTexture = mTextureCacheManager[maskCacheIndex].textures[0]; + Devel::PixelBuffer maskPixelBuffer = mTextureCacheManager[maskCacheIndex].pixelBuffer; + if(maskPixelBuffer) + { + pixelBuffers[0].ApplyMask(maskPixelBuffer, contentScale, cropToMask); + } + else + { + DALI_LOG_ERROR("Mask image cached invalid pixel buffer!\n"); + } } } - else if(mTextureCacheManager[maskCacheIndex].storageType == StorageType::KEEP_PIXEL_BUFFER) + else { - Devel::PixelBuffer maskPixelBuffer = mTextureCacheManager[maskCacheIndex].pixelBuffer; - if(maskPixelBuffer) - { - pixelBuffers[0].ApplyMask(maskPixelBuffer, contentScale, cropToMask); - } - else - { - DALI_LOG_ERROR("Mask image cached invalid pixel buffer!\n"); - } + DALI_LOG_ERROR("Mask image is not stored in cache.\n"); } } - else - { - DALI_LOG_ERROR("Mask image is not stored in cache.\n"); - } - } - PreMultiply(pixelBuffers[0], preMultiplyOnLoad); + PreMultiply(pixelBuffers[0], preMultiplyOnLoad); - // Upload texture - UploadTextures(pixelBuffers, textureInfo); + // Upload texture + UploadTextures(pixelBuffers, textureInfo); + } } } } @@ -702,37 +737,100 @@ TextureManager::TextureId TextureManager::RequestLoadInternal( return textureId; } -void TextureManager::Remove(const TextureManager::TextureId& textureId, TextureUploadObserver* observer) +void TextureManager::RequestRemove(const TextureManager::TextureId& textureId, TextureUploadObserver* observer) { + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::RequestRemove( textureId=%d observer=%p )\n", textureId, observer); + + // Queue to remove. if(textureId != INVALID_TEXTURE_ID) { - if(mQueueLoadFlag) + TextureCacheIndex textureCacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureId); + if(textureCacheIndex != INVALID_CACHE_INDEX) { - // Remove textureId after NotifyObserver finished + if(observer) + { + // Remove observer from cached texture info + TextureInfo& textureInfo(mTextureCacheManager[textureCacheIndex]); + RemoveTextureObserver(textureInfo, observer); + } + mRemoveQueue.PushBack(textureId); + + if(!mRemoveProcessorRegistered && Adaptor::IsAvailable()) + { + mRemoveProcessorRegistered = true; + Adaptor::Get().RegisterProcessor(*this, true); + } } - else - { - // Remove textureId in CacheManager. - mTextureCacheManager.RemoveCache(textureId); - } + } +} - if(observer) +void TextureManager::Remove(const TextureManager::TextureId& textureId) +{ + if(textureId != INVALID_TEXTURE_ID) + { + TextureCacheIndex textureCacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureId); + if(textureCacheIndex != INVALID_CACHE_INDEX) { - // Remove element from the LoadQueue - for(auto&& element : mLoadQueue) + TextureManager::TextureId maskTextureId = INVALID_TEXTURE_ID; + TextureInfo& textureInfo(mTextureCacheManager[textureCacheIndex]); + // We only need to consider maskTextureId when texture's loadState is not cancelled. Because it is already deleted. + if(textureInfo.loadState != LoadState::CANCELLED && textureInfo.loadState != LoadState::MASK_CANCELLED) + { + if(textureInfo.maskTextureId != INVALID_TEXTURE_ID) + { + maskTextureId = textureInfo.maskTextureId; + } + } + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::Remove( textureId=%d ) cacheIndex:%d removal maskTextureId=%d, loadState=%s\n", textureId, textureCacheIndex.GetIndex(), maskTextureId, GET_LOAD_STATE_STRING(textureInfo.loadState)); + + // Remove textureId in CacheManager. Now, textureInfo is invalidate. + mTextureCacheManager.RemoveCache(textureInfo); + + // Remove maskTextureId in CacheManager + if(maskTextureId != INVALID_TEXTURE_ID) { - if(element.mObserver == observer) + TextureCacheIndex maskCacheIndex = mTextureCacheManager.GetCacheIndexFromId(maskTextureId); + if(maskCacheIndex != INVALID_CACHE_INDEX) { - // Do not erase the item. We will clear it later in ProcessLoadQueue(). - element.mObserver = nullptr; - break; + TextureInfo& maskTextureInfo(mTextureCacheManager[maskCacheIndex]); + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::Remove mask texture( maskTextureId=%d ) cacheIndex:%d, loadState=%s\n", maskTextureId, maskCacheIndex.GetIndex(), GET_LOAD_STATE_STRING(maskTextureInfo.loadState)); + + mTextureCacheManager.RemoveCache(maskTextureInfo); } } } } } +void TextureManager::ProcessRemoveQueue() +{ + // Note that RemoveQueue is not be changed during Remove(). + for(auto&& textureId : mRemoveQueue) + { + if(textureId != INVALID_TEXTURE_ID) + { + Remove(textureId); + } + } + mRemoveQueue.Clear(); +} + +void TextureManager::Process(bool postProcessor) +{ + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::Process()\n"); + + ProcessRemoveQueue(); + + if(Adaptor::IsAvailable()) + { + Adaptor::Get().UnregisterProcessor(*this, true); + mRemoveProcessorRegistered = false; + } +} + void TextureManager::LoadImageSynchronously( const VisualUrl& url, const Dali::ImageDimensions& desiredSize, @@ -799,7 +897,7 @@ void TextureManager::LoadOrQueueTexture(TextureManager::TextureInfo& textureInfo case LoadState::NOT_STARTED: case LoadState::LOAD_FAILED: { - if(mQueueLoadFlag) + if(mLoadingQueueTextureId != INVALID_TEXTURE_ID) { QueueLoadTexture(textureInfo, observer); } @@ -811,7 +909,7 @@ void TextureManager::LoadOrQueueTexture(TextureManager::TextureInfo& textureInfo } case LoadState::UPLOADED: { - if(mQueueLoadFlag) + if(mLoadingQueueTextureId != INVALID_TEXTURE_ID) { QueueLoadTexture(textureInfo, observer); } @@ -819,12 +917,16 @@ void TextureManager::LoadOrQueueTexture(TextureManager::TextureInfo& textureInfo { // The Texture has already loaded. The other observers have already been notified. // We need to send a "late" loaded notification for this observer. - EmitLoadComplete(observer, textureInfo, true); + if(observer) + { + EmitLoadComplete(observer, textureInfo, true); + } } break; } case LoadState::LOADING: case LoadState::CANCELLED: + case LoadState::MASK_CANCELLED: case LoadState::LOAD_FINISHED: case LoadState::WAITING_FOR_MASK: case LoadState::MASK_APPLYING: @@ -838,29 +940,29 @@ void TextureManager::LoadOrQueueTexture(TextureManager::TextureInfo& textureInfo void TextureManager::QueueLoadTexture(const TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer) { const auto& textureId = textureInfo.textureId; - mLoadQueue.PushBack(LoadQueueElement(textureId, observer)); + mLoadQueue.PushBack(QueueElement(textureId, observer)); - observer->DestructionSignal().Connect(this, &TextureManager::ObserverDestroyed); + if(observer) + { + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, " Connect DestructionSignal to observer:%p\n", observer); + observer->DestructionSignal().Connect(this, &TextureManager::ObserverDestroyed); + } } void TextureManager::LoadTexture(TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer) { DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::LoadTexture(): url:%s sync:%s\n", textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously ? "T" : "F"); - textureInfo.loadState = LoadState::LOADING; if(!textureInfo.loadSynchronously) { - auto& loadersContainer = (textureInfo.url.IsLocalResource() || textureInfo.url.IsBufferResource()) ? mAsyncLocalLoaders : mAsyncRemoteLoaders; - auto loadingHelperIt = loadersContainer.GetNext(); - auto premultiplyOnLoad = (textureInfo.preMultiplyOnLoad && textureInfo.maskTextureId == INVALID_TEXTURE_ID) ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF; - DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End()); + auto premultiplyOnLoad = (textureInfo.preMultiplyOnLoad && textureInfo.maskTextureId == INVALID_TEXTURE_ID) ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF; if(textureInfo.animatedImageLoading) { - loadingHelperIt->LoadAnimatedImage(textureInfo.textureId, textureInfo.animatedImageLoading, textureInfo.frameIndex, premultiplyOnLoad); + mAsyncLoader->LoadAnimatedImage(textureInfo.textureId, textureInfo.animatedImageLoading, textureInfo.frameIndex, textureInfo.desiredSize, textureInfo.fittingMode, textureInfo.samplingMode, premultiplyOnLoad); } else { - loadingHelperIt->Load(textureInfo.textureId, textureInfo.url, textureInfo.desiredSize, textureInfo.fittingMode, textureInfo.samplingMode, textureInfo.orientationCorrection, premultiplyOnLoad, textureInfo.loadYuvPlanes); + mAsyncLoader->Load(textureInfo.textureId, textureInfo.url, textureInfo.desiredSize, textureInfo.fittingMode, textureInfo.samplingMode, textureInfo.orientationCorrection, premultiplyOnLoad, textureInfo.loadYuvPlanes); } } ObserveTexture(textureInfo, observer); @@ -870,7 +972,7 @@ void TextureManager::ProcessLoadQueue() { for(auto&& element : mLoadQueue) { - if(!element.mObserver) + if(element.mTextureId == INVALID_TEXTURE_ID) { continue; } @@ -879,9 +981,15 @@ void TextureManager::ProcessLoadQueue() if(cacheIndex != INVALID_CACHE_INDEX) { TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]); + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::General, "TextureManager::ProcessLoadQueue() textureId=%d, observer=%p, cacheIndex=@%d, loadState:%s\n", element.mTextureId, element.mObserver, cacheIndex.GetIndex(), GET_LOAD_STATE_STRING(textureInfo.loadState)); + if((textureInfo.loadState == LoadState::UPLOADED) || (textureInfo.loadState == LoadState::LOAD_FINISHED && textureInfo.storageType == StorageType::RETURN_PIXEL_BUFFER)) { - EmitLoadComplete(element.mObserver, textureInfo, true); + if(element.mObserver) + { + EmitLoadComplete(element.mObserver, textureInfo, true); + } } else if(textureInfo.loadState == LoadState::LOADING) { @@ -898,15 +1006,6 @@ void TextureManager::ProcessLoadQueue() mLoadQueue.Clear(); } -void TextureManager::ProcessRemoveQueue() -{ - for(const auto& textureId : mRemoveQueue) - { - mTextureCacheManager.RemoveCache(textureId); - } - mRemoveQueue.Clear(); -} - void TextureManager::ObserveTexture(TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer) { @@ -915,6 +1014,8 @@ void TextureManager::ObserveTexture(TextureManager::TextureInfo& textureInfo, if(observer) { textureInfo.observerList.PushBack(observer); + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, " Connect DestructionSignal to observer:%p\n", observer); observer->DestructionSignal().Connect(this, &TextureManager::ObserverDestroyed); } } @@ -928,15 +1029,14 @@ void TextureManager::AsyncLoadComplete(const TextureManager::TextureId& textureI TextureInfo& textureInfo(mTextureCacheManager[cacheIndex]); DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, " textureId:%d Url:%s CacheIndex:%d LoadState: %s\n", textureInfo.textureId, textureInfo.url.GetUrl().c_str(), cacheIndex.GetIndex(), GET_LOAD_STATE_STRING(textureInfo.loadState)); - - if(textureInfo.loadState != LoadState::CANCELLED) + if(textureInfo.loadState != LoadState::CANCELLED && textureInfo.loadState != LoadState::MASK_CANCELLED) { // textureInfo can be invalidated after this call (as the mTextureInfoContainer may be modified) PostLoad(textureInfo, pixelBuffers); } else { - Remove(textureInfo.textureId, nullptr); + Remove(textureInfo.textureId); } } } @@ -1072,10 +1172,15 @@ void TextureManager::CheckForWaitingTexture(TextureManager::TextureInfo& maskTex UploadTextures(pixelBuffers, maskTextureInfo); } - // Search the cache, checking if any texture has this texture id as a - // maskTextureId: + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::CheckForWaitingTexture(): maskTextureId=%d, maskTextureUrl=%s\n", maskTextureInfo.textureId, maskTextureInfo.url.GetUrl().c_str()); + + // Search the cache, checking if any texture has this texture id as a maskTextureId const std::size_t size = mTextureCacheManager.size(); + // Keep notify observer required textureIds. + // Note : NotifyObservers can change mTextureCacheManager cache struct. We should check id's validation before notify. + std::vector notifyRequiredTextureIds; + // TODO : Refactorize here to not iterate whole cached image. for(TextureCacheIndex cacheIndex = TextureCacheIndex(TextureManagerType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, 0u); cacheIndex.GetIndex() < size; ++cacheIndex.detailValue.index) { @@ -1096,26 +1201,74 @@ void TextureManager::CheckForWaitingTexture(TextureManager::TextureInfo& maskTex { if(maskTextureInfo.storageType == StorageType::KEEP_TEXTURE) { - // Upload image texture. textureInfo.loadState will be UPLOADED. - std::vector pixelBuffers; - pixelBuffers.push_back(textureInfo.pixelBuffer); - UploadTextures(pixelBuffers, textureInfo); + if(textureInfo.url.GetProtocolType() == VisualUrl::TEXTURE) + { + // Get external textureSet from cacheManager. + std::string location = textureInfo.url.GetLocation(); + if(!location.empty()) + { + TextureId id = std::stoi(location); + auto externalTextureInfo = mTextureCacheManager.GetExternalTextureInfo(id); + textureInfo.textures.push_back(externalTextureInfo.textureSet.GetTexture(0)); + textureInfo.loadState = LoadState::UPLOADED; + } + } + else + { + // Upload image texture. textureInfo.loadState will be UPLOADED. + std::vector pixelBuffers; + pixelBuffers.push_back(textureInfo.pixelBuffer); + UploadTextures(pixelBuffers, textureInfo); + } + + // Increase reference counts for notify required textureId. + // Now we can assume that we don't remove & re-assign this textureId + // during NotifyObserver signal emit. + maskTextureInfo.referenceCount++; + textureInfo.referenceCount++; - // notify mask texture set. - NotifyObservers(textureInfo, true); + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::CheckForWaitingTexture(): Ready to notify textureId=%d\n", textureInfo.textureId); + + notifyRequiredTextureIds.push_back(textureInfo.textureId); } } - else + else // maskTextureInfo.loadState == LoadState::LOAD_FAILED { // Url texture load success, But alpha mask texture load failed. Run as normal image upload. DALI_LOG_ERROR("Alpha mask image loading failed! Image will not be masked\n"); std::vector pixelBuffers; pixelBuffers.push_back(textureInfo.pixelBuffer); UploadTextures(pixelBuffers, textureInfo); - NotifyObservers(textureInfo, true); + + // Increase reference counts for notify required textureId. + // Now we can assume that we don't remove & re-assign this textureId + // during NotifyObserver signal emit. + maskTextureInfo.referenceCount++; + textureInfo.referenceCount++; + + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::CheckForWaitingTexture(): Ready to notify textureId=%d\n", textureInfo.textureId); + + notifyRequiredTextureIds.push_back(textureInfo.textureId); } } } + + // Notify textures are masked + for(const auto textureId : notifyRequiredTextureIds) + { + TextureCacheIndex textureCacheIndex = mTextureCacheManager.GetCacheIndexFromId(textureId); + if(textureCacheIndex != INVALID_CACHE_INDEX) + { + TextureInfo& textureInfo(mTextureCacheManager[textureCacheIndex]); + NotifyObservers(textureInfo, true); + } + } + + // Decrease reference count + for(const auto textureId : notifyRequiredTextureIds) + { + RequestRemove(textureId, nullptr); + } } void TextureManager::ApplyMask(TextureManager::TextureInfo& textureInfo, const TextureManager::TextureId& maskTextureId) @@ -1129,12 +1282,9 @@ void TextureManager::ApplyMask(TextureManager::TextureInfo& textureInfo, const T DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::ApplyMask(): url:%s sync:%s\n", textureInfo.url.GetUrl().c_str(), textureInfo.loadSynchronously ? "T" : "F"); - textureInfo.loadState = LoadState::MASK_APPLYING; - auto& loadersContainer = (textureInfo.url.IsLocalResource() || textureInfo.url.IsBufferResource()) ? mAsyncLocalLoaders : mAsyncRemoteLoaders; - auto loadingHelperIt = loadersContainer.GetNext(); - auto premultiplyOnLoad = textureInfo.preMultiplyOnLoad ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF; - DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End()); - loadingHelperIt->ApplyMask(textureInfo.textureId, pixelBuffer, maskPixelBuffer, textureInfo.scaleFactor, textureInfo.cropToMask, premultiplyOnLoad); + textureInfo.loadState = LoadState::MASK_APPLYING; + auto premultiplyOnLoad = textureInfo.preMultiplyOnLoad ? DevelAsyncImageLoader::PreMultiplyOnLoad::ON : DevelAsyncImageLoader::PreMultiplyOnLoad::OFF; + mAsyncLoader->ApplyMask(textureInfo.textureId, pixelBuffer, maskPixelBuffer, textureInfo.scaleFactor, textureInfo.cropToMask, premultiplyOnLoad); } } @@ -1158,7 +1308,7 @@ void TextureManager::UploadTextures(std::vector& pixelBuffer for(auto&& pixelBuffer : pixelBuffers) { - Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight()); + Texture texture = Texture::New(Dali::TextureType::TEXTURE_2D, pixelBuffer.GetPixelFormat(), pixelBuffer.GetWidth(), pixelBuffer.GetHeight()); PixelData pixelData = Devel::PixelBuffer::Convert(pixelBuffer); texture.Upload(pixelData); textureInfo.textures.push_back(texture); @@ -1191,7 +1341,7 @@ void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, c info->animatedImageLoading.Reset(); } - mQueueLoadFlag = true; + mLoadingQueueTextureId = textureId; // Reverse observer list that we can pop_back the observer. std::reverse(info->observerList.Begin(), info->observerList.End()); @@ -1208,10 +1358,10 @@ void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, c // invalidating the reference to the textureInfo struct. // Texture load requests for the same URL are deferred until the end of this // method. - DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::NotifyObservers() textureId:%d url:%s loadState:%s\n", textureId, info->url.GetUrl().c_str(), GET_LOAD_STATE_STRING(info->loadState)); - + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Concise, "TextureManager::NotifyObservers() observer:%p textureId:%d url:%s loadState:%s\n", observer, textureId, info->url.GetUrl().c_str(), GET_LOAD_STATE_STRING(info->loadState)); // It is possible for the observer to be deleted. // Disconnect and remove the observer first. + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, " Disconnect DestructionSignal to observer:%p\n", observer); observer->DestructionSignal().Disconnect(this, &TextureManager::ObserverDestroyed); info->observerList.Erase(info->observerList.End() - 1u); @@ -1227,18 +1377,19 @@ void TextureManager::NotifyObservers(TextureManager::TextureInfo& textureInfo, c info = &mTextureCacheManager[textureInfoIndex]; } - mQueueLoadFlag = false; + mLoadingQueueTextureId = INVALID_TEXTURE_ID; ProcessLoadQueue(); - ProcessRemoveQueue(); if(info->storageType == StorageType::RETURN_PIXEL_BUFFER && info->observerList.Count() == 0) { - Remove(info->textureId, nullptr); + RequestRemove(info->textureId, nullptr); } } void TextureManager::ObserverDestroyed(TextureUploadObserver* observer) { + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, "TextureManager::ObserverDestroyed() observer:%p\n", observer); + const std::size_t size = mTextureCacheManager.size(); for(TextureCacheIndex cacheIndex = TextureCacheIndex(TextureManagerType::TEXTURE_CACHE_INDEX_TYPE_LOCAL, 0u); cacheIndex.GetIndex() < size; ++cacheIndex.detailValue.index) { @@ -1262,7 +1413,8 @@ void TextureManager::ObserverDestroyed(TextureUploadObserver* observer) { if(element.mObserver == observer) { - element.mObserver = nullptr; + element.mTextureId = INVALID_TEXTURE_ID; + element.mObserver = nullptr; } } } @@ -1294,7 +1446,7 @@ void TextureManager::EmitLoadComplete(TextureUploadObserver* observer, TextureMa TextureSet TextureManager::GetTextureSet(const TextureManager::TextureId& textureId) { - TextureSet textureSet; + TextureSet textureSet; TextureManager::LoadState loadState = mTextureCacheManager.GetTextureStateInternal(textureId); if(loadState == TextureManager::LoadState::UPLOADED) { @@ -1349,6 +1501,23 @@ TextureSet TextureManager::GetTextureSet(const TextureManager::TextureInfo& text return textureSet; } +void TextureManager::RemoveTextureObserver(TextureManager::TextureInfo& textureInfo, TextureUploadObserver* observer) +{ + // Remove its observer + if(observer) + { + const auto iterEnd = textureInfo.observerList.End(); + const auto iter = std::find(textureInfo.observerList.Begin(), iterEnd, observer); + if(iter != iterEnd) + { + // Disconnect and remove the observer. + DALI_LOG_INFO(gTextureManagerLogFilter, Debug::Verbose, " Disconnect DestructionSignal to observer:%p\n", observer); + observer->DestructionSignal().Disconnect(this, &TextureManager::ObserverDestroyed); + textureInfo.observerList.Erase(iter); + } + } +} + } // namespace Internal } // namespace Toolkit