From: Francisco Santos Date: Fri, 1 Sep 2017 11:00:35 +0000 (+0100) Subject: Add multiple thread support to TextureManager X-Git-Tag: dali_1.2.56~5^2 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=421e8bf2b19f6e6e53866595d29db9cc6b8d210e Add multiple thread support to TextureManager Change-Id: Ib7c620f509678ab0283e68b0b14435e2eda3bd1e --- diff --git a/dali-toolkit/internal/helpers/round-robin-container-view.h b/dali-toolkit/internal/helpers/round-robin-container-view.h new file mode 100644 index 0000000..c3619f9 --- /dev/null +++ b/dali-toolkit/internal/helpers/round-robin-container-view.h @@ -0,0 +1,124 @@ + +#ifndef DALI_TOOLKIT_INTERNAL_ROUND_ROBIN_CONTAINER_VIEW_H +#define DALI_TOOLKIT_INTERNAL_ROUND_ROBIN_CONTAINER_VIEW_H + +/* + * Copyright (c) 2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// EXTERNAL INCLUDES +#include +#include + +namespace Dali +{ + +namespace Toolkit +{ + +namespace Internal +{ + +/** + * @brief RoundRobinContainerView is a view to a container that allows iterating through the elements cyclically. + */ +template +class RoundRobinContainerView +{ +public: + using ContainerType = std::vector; + + /** + * @brief Constructs a new RoundRobinControlView with the given number elements using the provided factory. + * @param[in] numberOfElements The number of elements in the container + * @param[in] factory Factory function of functor that will be used to create instances of the elements + */ + template + RoundRobinContainerView(size_t numberOfElements, const FactoryType& factory) + : mElements(), + mNextIndex{} + { + mElements.reserve(numberOfElements); + for(unsigned i = {}; i < numberOfElements; ++i) + { + mElements.push_back(factory()); + } + } + + /** + * @brief Reset the position of the iterator returned by GetNext() to the first element. + */ + void Reset() + { + mNextIndex = 0u; + } + + /** + * @brief Returns the next element on the container. + * @return Iterator for the next element + */ + typename ContainerType::iterator GetNext() + { + SetValidNextIndex(); + + return mElements.begin() + mNextIndex++; + } + + /** + * @brief Returns the iterator to the end of the container. + * + * Can be used to compare against GetNext() to check if the container is empty. + * + * @return The container end() element + */ + typename ContainerType::const_iterator End() const + { + return mElements.cend(); + } + + // default members + ~RoundRobinContainerView() = default; + + RoundRobinContainerView(const RoundRobinContainerView&) = delete; + RoundRobinContainerView& operator=(const RoundRobinContainerView&) = delete; + RoundRobinContainerView(RoundRobinContainerView&&) = default; + RoundRobinContainerView& operator=(RoundRobinContainerView&&) = default; + +private: + /** + * @brief Check the current index and reset if necessary. + */ + void SetValidNextIndex() + { + if(mNextIndex >= mElements.size()) + { + Reset(); + } + } + +private: + ContainerType mElements; //< container of elements + size_t mNextIndex; //< index to the next element to be viewed +}; + +} // namespace Internal + +} // namespace Toolkit + +} // namespace Dali + + +#endif // DALI_TOOLKIT_INTERNAL_ROUND_ROBIN_CONTAINER_VIEW_H diff --git a/dali-toolkit/internal/visuals/texture-manager.cpp b/dali-toolkit/internal/visuals/texture-manager.cpp index 824213e..9468935 100644 --- a/dali-toolkit/internal/visuals/texture-manager.cpp +++ b/dali-toolkit/internal/visuals/texture-manager.cpp @@ -19,18 +19,45 @@ #include "texture-manager.h" // EXTERNAL HEADERS +#include +#include #include #include #include #include // INTERNAL HEADERS -#include -#include #include -#include #include +namespace +{ + +constexpr auto DEFAULT_NUMBER_OF_LOCAL_LOADER_THREADS = size_t{4u}; +constexpr auto DEFAULT_NUMBER_OF_REMOTE_LOADER_THREADS = size_t{8u}; + +constexpr auto NUMBER_OF_LOCAL_LOADER_THREADS_ENV = "DALI_TEXTURE_LOCAL_THREADS"; +constexpr auto NUMBER_OF_REMOTE_LOADER_THREADS_ENV = "DALI_TEXTURE_REMOTE_THREADS"; + +size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue) +{ + using Dali::EnvironmentVariable::GetEnvironmentVariable; + auto numberString = GetEnvironmentVariable(environmentVariable); + auto numberOfThreads = numberString ? std::strtol(numberString, nullptr, 10) : 0; + return (numberOfThreads > 0) ? 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); +} + +} // namespace namespace Dali { @@ -58,12 +85,10 @@ const int INVALID_CACHE_INDEX( -1 ); ///< Invalid Cache index TextureManager::TextureManager() -: mAsyncLocalLoader( Toolkit::AsyncImageLoader::New() ), - mAsyncRemoteLoader( Toolkit::AsyncImageLoader::New() ), - mCurrentTextureId( 0 ) +: mCurrentTextureId( 0 ), + mAsyncLocalLoaders( GetNumberOfLocalLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } ), + mAsyncRemoteLoaders( GetNumberOfRemoteLoaderThreads(), [&]() { return AsyncLoadingHelper(*this); } ) { - DevelAsyncImageLoader::PixelBufferLoadedSignal(mAsyncLocalLoader).Connect( this, &TextureManager::AsyncLocalLoadComplete ); - DevelAsyncImageLoader::PixelBufferLoadedSignal(mAsyncRemoteLoader).Connect( this, &TextureManager::AsyncRemoteLoadComplete ); } TextureManager::TextureId TextureManager::RequestLoad( @@ -294,22 +319,12 @@ bool TextureManager::LoadTexture( TextureInfo& textureInfo ) if( !textureInfo.loadSynchronously ) { - if( textureInfo.url.IsLocal() ) - { - mAsyncLocalLoadingInfoContainer.push_back( AsyncLoadingInfo( textureInfo.textureId ) ); - mAsyncLocalLoadingInfoContainer.back().loadId = - GetImplementation(mAsyncLocalLoader).Load( textureInfo.url, textureInfo.desiredSize, - textureInfo.fittingMode, - textureInfo.samplingMode, true ); - } - else - { - mAsyncRemoteLoadingInfoContainer.push_back( AsyncLoadingInfo( textureInfo.textureId ) ); - mAsyncRemoteLoadingInfoContainer.back().loadId = - GetImplementation(mAsyncRemoteLoader).Load( textureInfo.url, textureInfo.desiredSize, - textureInfo.fittingMode, - textureInfo.samplingMode, true ); - } + auto& loadersContainer = textureInfo.url.IsLocal() ? mAsyncLocalLoaders : mAsyncRemoteLoaders; + auto loadingHelperIt = loadersContainer.GetNext(); + DALI_ASSERT_ALWAYS(loadingHelperIt != loadersContainer.End()); + loadingHelperIt->Load(textureInfo.textureId, textureInfo.url, + textureInfo.desiredSize, textureInfo.fittingMode, + textureInfo.samplingMode, true); } } @@ -326,16 +341,6 @@ void TextureManager::ObserveTexture( TextureInfo& textureInfo, } } -void TextureManager::AsyncLocalLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer ) -{ - AsyncLoadComplete( mAsyncLocalLoadingInfoContainer, id, pixelBuffer ); -} - -void TextureManager::AsyncRemoteLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer ) -{ - AsyncLoadComplete( mAsyncRemoteLoadingInfoContainer, id, pixelBuffer ); -} - void TextureManager::AsyncLoadComplete( AsyncLoadingInfoContainerType& loadingContainer, uint32_t id, Devel::PixelBuffer pixelBuffer ) { DALI_LOG_INFO( gTextureManagerLogFilter, Debug::Concise, "TextureManager::AsyncLoadComplete( id:%d )\n", id ); @@ -674,13 +679,48 @@ void TextureManager::ObserverDestroyed( TextureUploadObserver* observer ) TextureManager::~TextureManager() { - mTextureInfoContainer.clear(); - mAsyncLocalLoadingInfoContainer.clear(); - mAsyncRemoteLoadingInfoContainer.clear(); } +TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(TextureManager& textureManager) +: AsyncLoadingHelper(Toolkit::AsyncImageLoader::New(), textureManager, + AsyncLoadingInfoContainerType()) +{ +} +void TextureManager::AsyncLoadingHelper::Load(TextureId textureId, + const VisualUrl& url, + ImageDimensions desiredSize, + FittingMode::Type fittingMode, + SamplingMode::Type samplingMode, + bool orientationCorrection) +{ + mLoadingInfoContainer.push_back(AsyncLoadingInfo(textureId)); + auto id = mLoader.Load(url.GetUrl(), desiredSize, fittingMode, samplingMode, orientationCorrection); + mLoadingInfoContainer.back().loadId = id; +} +TextureManager::AsyncLoadingHelper::AsyncLoadingHelper(AsyncLoadingHelper&& rhs) +: AsyncLoadingHelper(rhs.mLoader, rhs.mTextureManager, std::move(rhs.mLoadingInfoContainer)) +{ +} + +TextureManager::AsyncLoadingHelper::AsyncLoadingHelper( + Toolkit::AsyncImageLoader loader, + TextureManager& textureManager, + AsyncLoadingInfoContainerType&& loadingInfoContainer) +: mLoader(loader), + mTextureManager(textureManager), + mLoadingInfoContainer(std::move(loadingInfoContainer)) +{ + DevelAsyncImageLoader::PixelBufferLoadedSignal(mLoader).Connect( + this, &AsyncLoadingHelper::AsyncLoadComplete); +} + +void TextureManager::AsyncLoadingHelper::AsyncLoadComplete(uint32_t id, + Devel::PixelBuffer pixelBuffer) +{ + mTextureManager.AsyncLoadComplete(mLoadingInfoContainer, id, pixelBuffer); +} } // namespace Internal diff --git a/dali-toolkit/internal/visuals/texture-manager.h b/dali-toolkit/internal/visuals/texture-manager.h index 5e07735..090846e 100644 --- a/dali-toolkit/internal/visuals/texture-manager.h +++ b/dali-toolkit/internal/visuals/texture-manager.h @@ -18,19 +18,23 @@ */ // EXTERNAL INCLUDES +#include +#include #include #include #include #include #include #include -#include // INTERNAL INCLUDES +#include #include #include #include #include +#include +#include namespace Dali @@ -485,18 +489,75 @@ private: const bool useAtlas, TextureId maskTextureId ); +private: + + /** + * @brief Helper class to keep the relation between AsyncImageLoader and corresponding LoadingInfo container + */ + class AsyncLoadingHelper : public ConnectionTracker + { + public: + /** + * @brief Create an AsyncLoadingHelper. + * @param[in] textureManager Reference to the texture manager + */ + AsyncLoadingHelper(TextureManager& textureManager); + + /** + * @brief Load a new texture. + * @param[in] textureId TextureId to reference the texture that will be loaded + * @param[in] url The URL of the image to load + * @param[in] desiredSize The size the image is likely to appear at. This can be set to 0,0 for automatic + * @param[in] fittingMode The FittingMode to use + * @param[in] samplingMode The SamplingMode to use + * @param[in] orientationCorrection Whether to use image metadata to rotate or flip the image, e.g., from portrait to landscape + */ + void Load(TextureId textureId, + const VisualUrl& url, + ImageDimensions desiredSize, + FittingMode::Type fittingMode, + SamplingMode::Type samplingMode, + bool orientationCorrection); + + public: + AsyncLoadingHelper(const AsyncLoadingHelper&) = delete; + AsyncLoadingHelper& operator=(const AsyncLoadingHelper&) = delete; + + AsyncLoadingHelper(AsyncLoadingHelper&& rhs); + AsyncLoadingHelper& operator=(AsyncLoadingHelper&&rhs) = delete; + + private: + /** + * @brief Main constructor that used by all other constructors + */ + AsyncLoadingHelper(Toolkit::AsyncImageLoader loader, + TextureManager& textureManager, + AsyncLoadingInfoContainerType&& loadingInfoContainer); + + /** + * @brief Callback to be called when texture loading is complete, it passes the pixel buffer on to texture manager. + * @param[in] id Loader id + * @param[in] pixelBuffer Image data + */ + void AsyncLoadComplete(uint32_t id, Devel::PixelBuffer pixelBuffer); + + private: + Toolkit::AsyncImageLoader mLoader; + TextureManager& mTextureManager; + AsyncLoadingInfoContainerType mLoadingInfoContainer; + }; private: /** - * Undefined copy constructor. + * Deleted copy constructor. */ - TextureManager( const TextureManager& ); + TextureManager( const TextureManager& ) = delete; /** - * Undefined assignment operator. + * Deleted assignment operator. */ - TextureManager& operator=( const TextureManager& rhs ); + TextureManager& operator=( const TextureManager& rhs ) = delete; /** * This is called by the TextureManagerUploadObserver when an observer is destroyed. @@ -507,14 +568,12 @@ private: private: // Member Variables: - AsyncLoadingInfoContainerType mAsyncLocalLoadingInfoContainer; ///< Used to manage Asynchronous loads in progress - AsyncLoadingInfoContainerType mAsyncRemoteLoadingInfoContainer; ///< Used to manage Asynchronous loads in progress - AtlasInfoContainerType mAtlasContainer; ///< Used to manage Atlas creation and destruction - TextureInfoContainerType mTextureInfoContainer; ///< Used to manage the life-cycle and caching of Textures - Toolkit::AsyncImageLoader mAsyncLocalLoader; ///< The Asynchronous image loader used to provide all local async loads - Toolkit::AsyncImageLoader mAsyncRemoteLoader; ///< The Asynchronous image loader used to provide all remote async loads - TextureId mCurrentTextureId; ///< The current value used for the unique Texture Id generation + AtlasInfoContainerType mAtlasContainer; ///< Used to manage Atlas creation and destruction + TextureInfoContainerType mTextureInfoContainer; ///< Used to manage the life-cycle and caching of Textures + TextureId mCurrentTextureId; ///< The current value used for the unique Texture Id generation + RoundRobinContainerView mAsyncLocalLoaders; ///< The Asynchronous image loaders used to provide all local async loads + RoundRobinContainerView mAsyncRemoteLoaders; ///< The Asynchronous image loaders used to provide all remote async loads };