Add multiple thread support to TextureManager
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / visuals / texture-manager.h
1 #ifndef DALI_TOOLKIT_TEXTURE_MANAGER_H
2 #define DALI_TOOLKIT_TEXTURE_MANAGER_H
3
4 /*
5  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 // EXTERNAL INCLUDES
21 #include <deque>
22 #include <functional>
23 #include <string>
24 #include <dali/public-api/common/dali-vector.h>
25 #include <dali/public-api/object/ref-object.h>
26 #include <dali/public-api/rendering/texture-set.h>
27 #include <dali/devel-api/common/owner-container.h>
28 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
29
30 // INTERNAL INCLUDES
31 #include <dali-toolkit/devel-api/image-loader/async-image-loader-devel.h>
32 #include <dali-toolkit/devel-api/image-loader/image-atlas.h>
33 #include <dali-toolkit/public-api/image-loader/async-image-loader.h>
34 #include <dali-toolkit/internal/visuals/texture-upload-observer.h>
35 #include <dali-toolkit/internal/visuals/visual-url.h>
36 #include <dali-toolkit/internal/helpers/round-robin-container-view.h>
37 #include <dali-toolkit/internal/image-loader/async-image-loader-impl.h>
38
39
40 namespace Dali
41 {
42
43 namespace Toolkit
44 {
45
46 namespace Internal
47 {
48
49 class MaskTextureObserver;
50
51 /**
52  * The TextureManager provides a common Image loading API for Visuals.
53  *
54  * The TextureManager is responsible for providing sync, async, atlased and non-atlased loads.
55  * Texture caching is provided and performed when possible.
56  * Broken Images are automatically provided on load failure.
57  */
58 class TextureManager : public ConnectionTracker
59 {
60 public:
61
62   typedef int32_t TextureId;       ///< The TextureId type. This is used as a handle to refer to a particular Texture.
63   static const int INVALID_TEXTURE_ID = -1; ///< Used to represent a null TextureId or error
64
65   /**
66    * Whether the texture should be atlased or uploaded into it's own GPU texture
67    */
68   enum UseAtlas
69   {
70     NO_ATLAS,
71     USE_ATLAS
72   };
73
74   /**
75    * Whether the pixel data should be kept in TextureManager, or uploaded for rendering
76    */
77   enum StorageType
78   {
79     KEEP_PIXEL_BUFFER,
80     UPLOAD_TO_TEXTURE
81   };
82
83   /**
84    * Whether the texture should be loaded synchronously or asynchronously.
85    */
86   enum LoadType
87   {
88     LOAD_ASYNCHRONOUSLY,
89     LOAD_SYNCHRONOUSLY
90   };
91
92   /**
93    * @brief The LoadState Enumeration represents the current state of a particular Texture's life-cycle.
94    */
95   enum LoadState
96   {
97     NOT_STARTED,     ///< Default
98     LOADING,         ///< Loading has been started, but not finished.
99     LOAD_FINISHED,   ///< Loading has finished. (for CPU storage only)
100     WAITING_FOR_MASK,///< Loading has finished, but waiting for mask image
101     UPLOADED,        ///< Uploaded and ready. (For GPU upload only)
102     CANCELLED,       ///< Removed before loading completed
103     LOAD_FAILED      ///< Async loading failed, e.g. connection problem
104   };
105
106 public:
107
108   /**
109    * Constructor.
110    */
111   TextureManager();
112
113   /**
114    * Destructor.
115    */
116   ~TextureManager();
117
118
119   // TextureManager Main API:
120
121   /**
122    * @brief Requests an image load of the given URL.
123    *
124    * The parameters are used to specify how the image is loaded.
125    * The observer has the UploadComplete method called when the load is ready.
126    *
127    * When the client has finished with the Texture, Remove() should be called.
128    *
129    * @param[in] url               The URL of the image to load
130    * @param[in] desiredSize       The size the image is likely to appear at. This can be set to 0,0 for automatic
131    * @param[in] fittingMode       The FittingMode to use
132    * @param[in] samplingMode      The SamplingMode to use
133    * @param[in] useAtlasing       Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
134    *                              but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
135    * @param[in] observer          The client object should inherit from this and provide the "UploadCompleted" virtual.
136    *                              This is called when an image load completes (or fails).
137    * @return                      A TextureId to use as a handle to reference this Texture
138    */
139   TextureId RequestLoad( const VisualUrl&         url,
140                          const ImageDimensions    desiredSize,
141                          FittingMode::Type        fittingMode,
142                          Dali::SamplingMode::Type samplingMode,
143                          const UseAtlas           useAtlasing,
144                          TextureUploadObserver*   observer );
145
146   /**
147    * @brief Requests an image load of the given URL, when the texture has
148    * have loaded, it will perform a blend with the image mask, and upload
149    * the blended texture.
150    *
151    * The parameters are used to specify how the image is loaded.
152    * The observer has the UploadComplete method called when the load is ready.
153    *
154    * When the client has finished with the Texture, Remove() should be called.
155    *
156    * @param[in] url               The URL of the image to load
157    * @param[in] maskTextureId     The texture id of an image to mask this with (can be INVALID if no masking required)
158    * @param[in] contentScale      The scale factor to apply to the image before masking
159    * @param[in] desiredSize       The size the image is likely to appear at. This can be set to 0,0 for automatic
160    * @param[in] fittingMode       The FittingMode to use
161    * @param[in] samplingMode      The SamplingMode to use
162    * @param[in] useAtlasing       Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
163    *                              but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
164    * @param[in] cropToMask        Only used with masking, this will crop the scaled image to the mask size. If false, then the mask will be scaled to fit the image before being applied.
165    * @param[in] observer          The client object should inherit from this and provide the "UploadCompleted" virtual.
166    *                              This is called when an image load completes (or fails).
167    * @return                      A TextureId to use as a handle to reference this Texture
168    */
169   TextureId RequestLoad( const VisualUrl&         url,
170                          TextureId                maskTextureId,
171                          float                    contentScale,
172                          const ImageDimensions    desiredSize,
173                          FittingMode::Type        fittingMode,
174                          Dali::SamplingMode::Type samplingMode,
175                          const UseAtlas           useAtlasing,
176                          bool                     cropToMask,
177                          TextureUploadObserver*   observer );
178
179   /**
180    * Requests a masking image to be loaded. This mask is not uploaded to GL,
181    * instead, it is stored in CPU memory, and can be used for CPU blending.
182    */
183   TextureId RequestMaskLoad( const VisualUrl& maskUrl );
184
185   /**
186    * @brief Remove a Texture from the TextureManager.
187    *
188    * Textures are cached and therefore only the removal of the last
189    * occurrence of a Texture will cause its removal internally.
190    *
191    * @param[in] textureId The ID of the Texture to remove.
192    */
193   void Remove( const TextureManager::TextureId textureId );
194
195   /**
196    * Get the visualUrl associated with the texture id
197    */
198   const VisualUrl& GetVisualUrl( TextureId textureId );
199
200   /**
201    * @brief Get the current state of a texture
202    * @param[in] textureId The texture id to query
203    * @return The loading state if the texture is valid, or NOT_STARTED if the textureId
204    * is not valid.
205    */
206   LoadState GetTextureState( TextureId textureId );
207
208   /**
209    * @brief Get the associated texture set if the texture id is valid
210    * @param[in] textureId The texture Id to look up
211    * @return the associated texture set, or an empty handle if textureId is not valid
212    */
213   TextureSet GetTextureSet( TextureId textureId );
214
215 private:
216
217   /**
218    * @brief Requests an image load of the given URL, when the texture has
219    * have loaded, if there is a valid maskTextureId, it will perform a
220    * CPU blend with the mask, and upload the blend texture.
221    *
222    * The parameters are used to specify how the image is loaded.
223    * The observer has the UploadComplete method called when the load is ready.
224    *
225    * When the client has finished with the Texture, Remove() should be called.
226    *
227    * @param[in] url               The URL of the image to load
228    * @param[in] maskTextureId     The texture id of an image to use as a mask. If no mask is required, then set to INVALID_TEXTURE_ID
229    * @param[in] contentScale      The scaling factor to apply to the content when masking
230    * @param[in] desiredSize       The size the image is likely to appear at. This can be set to 0,0 for automatic
231    * @param[in] fittingMode       The FittingMode to use
232    * @param[in] samplingMode      The SamplingMode to use
233    * @param[in] useAtlasing       Set to USE_ATLAS to attempt atlasing. If atlasing fails, the image will still be loaded, and marked successful,
234    *                              but "useAtlasing" will be set to false in the "UploadCompleted" callback from the TextureManagerUploadObserver.
235    * @param[in] cropToMask        Whether to crop the target after masking, or scale the mask to the image before masking.
236    * @param[in] storageType,      Whether the pixel data is stored in the cache or uploaded to the GPU
237    * @param[in] observer          The client object should inherit from this and provide the "UploadCompleted" virtual.
238    *                              This is called when an image load completes (or fails).
239    * @return                      A TextureId to use as a handle to reference this Texture
240    */
241   TextureId RequestLoadInternal(
242     const VisualUrl&         url,
243     TextureId                maskTextureId,
244     float                    contentScale,
245     const ImageDimensions    desiredSize,
246     FittingMode::Type        fittingMode,
247     Dali::SamplingMode::Type samplingMode,
248     UseAtlas                 useAtlas,
249     bool                     cropToMask,
250     StorageType              storageType,
251     TextureUploadObserver*   observer );
252
253
254   typedef size_t TextureHash; ///< The type used to store the hash used for Texture caching.
255
256   /**
257    * @brief This struct is used to manage the life-cycle of Texture loading and caching.
258    */
259   struct TextureInfo
260   {
261     TextureInfo( TextureId textureId,
262                  TextureId maskTextureId,
263                  const VisualUrl& url,
264                  ImageDimensions desiredSize,
265                  float scaleFactor,
266                  FittingMode::Type fittingMode,
267                  Dali::SamplingMode::Type samplingMode,
268                  bool loadSynchronously,
269                  bool cropToMask,
270                  UseAtlas useAtlas,
271                  TextureManager::TextureHash hash )
272     : url( url ),
273       desiredSize( desiredSize ),
274       useSize( desiredSize ),
275       atlasRect( 0.0f, 0.0f, 1.0f, 1.0f ), // Full atlas rectangle
276       textureId( textureId ),
277       maskTextureId( maskTextureId ),
278       hash( hash ),
279       scaleFactor( scaleFactor ),
280       referenceCount( 1u ),
281       loadState( NOT_STARTED ),
282       fittingMode( fittingMode ),
283       samplingMode( samplingMode ),
284       storageType( UPLOAD_TO_TEXTURE ),
285       loadSynchronously( loadSynchronously ),
286       useAtlas( useAtlas ),
287       cropToMask( cropToMask )
288     {
289     }
290
291     /**
292      * Container type used to store all observer clients of this Texture
293      */
294     typedef Dali::Vector< TextureUploadObserver* > ObserverListType;
295
296     ObserverListType observerList; ///< Container used to store all observer clients of this Texture
297     Toolkit::ImageAtlas atlas;     ///< The atlas this Texture lays within (if any)
298     Devel::PixelBuffer pixelBuffer;///< The PixelBuffer holding the image data (May be empty after upload)
299     TextureSet textureSet;         ///< The TextureSet holding the Texture
300     VisualUrl url;                 ///< The URL of the image
301     ImageDimensions desiredSize;   ///< The size requested
302     ImageDimensions useSize;       ///< The size used
303     Vector4 atlasRect;             ///< The atlas rect used if atlased
304     TextureId textureId;           ///< The TextureId associated with this Texture
305     TextureId maskTextureId;       ///< The mask TextureId to be applied on load
306     TextureManager::TextureHash hash; ///< The hash used to cache this Texture
307     float scaleFactor;             ///< The scale factor to apply to the Texture when masking
308     int16_t referenceCount;        ///< The reference count of clients using this Texture
309     LoadState loadState:3;         ///< The load state showing the load progress of the Texture
310     FittingMode::Type fittingMode:2; ///< The requested FittingMode
311     Dali::SamplingMode::Type samplingMode:3; ///< The requested SamplingMode
312     StorageType storageType:1;     ///< CPU storage / GPU upload;
313     bool loadSynchronously:1;      ///< True if synchronous loading was requested
314     UseAtlas useAtlas:1;           ///< USE_ATLAS if an atlas was requested. This is updated to false if atlas is not used
315     bool cropToMask:1;             ///< true if the image should be cropped to the mask size.
316   };
317
318   // Structs:
319
320   /**
321    * Struct to hold information about a requested Async load.
322    * This is used to look up a TextureManager::TextureId from the returned AsyncLoad Id.
323    */
324   struct AsyncLoadingInfo
325   {
326     AsyncLoadingInfo( TextureId textureId )
327     : textureId( textureId ),
328       loadId( 0 )
329     {
330     }
331
332     TextureId           textureId;   ///< The external Texture Id assigned to this load
333     unsigned short      loadId;      ///< The load Id used by the async loader to reference this load
334   };
335
336   /**
337    * @brief This struct is used within a container to manage atlas creation and destruction.
338    */
339   struct AtlasInfo
340   {
341     AtlasInfo( Toolkit::ImageAtlas atlas, TextureSet textureSet )
342     : atlas( atlas ),
343       textureSet( textureSet )
344     {
345     }
346
347     Toolkit::ImageAtlas                 atlas;                          ///< The ImageAtlas object
348     TextureSet                          textureSet;                     ///< The TextureSet is kept in the struct to allow fast lookup of TextureSet to Atlas
349   };
350
351   // Private typedefs:
352
353   typedef std::deque<AsyncLoadingInfo>  AsyncLoadingInfoContainerType;  ///< The container type used to manage Asynchronous loads in progress
354   typedef std::vector<AtlasInfo>        AtlasInfoContainerType;         ///< The container type used to manage Atlas creation and destruction
355   typedef std::vector<TextureInfo>      TextureInfoContainerType;       ///< The container type used to manage the life-cycle and caching of Textures
356
357   /**
358    * @brief Used internally to initiate a load.
359    * @param[in] textureInfo The TextureInfo struct associated with the Texture
360    * @return                True if the load was initiated
361    */
362   bool LoadTexture( TextureInfo& textureInfo );
363
364   /**
365    * Add the observer to the observer list
366    * @param[in] textureInfo The TextureInfo struct associated with the texture
367    * observer The observer wishing to observe the texture upload
368    */
369   void ObserveTexture( TextureInfo & textureInfo, TextureUploadObserver* observer );
370
371   /**
372    * @brief This signal handler is called when the async local loader finishes loading.
373    * @param[in] id        This is the async image loaders Id
374    * @param[in] pixelBuffer The loaded image data
375    */
376   void AsyncLocalLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer );
377
378   /**
379    * @brief This signal handler is called when the async local loader finishes loading.
380    * @param[in] id        This is the async image loaders Id
381    * @param[in] pixelBuffer The loaded image data
382    */
383   void AsyncRemoteLoadComplete( uint32_t id, Devel::PixelBuffer pixelBuffer );
384
385   /**
386    * Common method to handle loading completion
387    * @param[in] container The Async loading container
388    * @param[in] id        This is the async image loaders Id
389    * @param[in] pixelBuffer The loaded image data
390    */
391   void AsyncLoadComplete( AsyncLoadingInfoContainerType& container, uint32_t id, Devel::PixelBuffer pixelBuffer );
392
393   /**
394    * @brief Performs Post-Load steps including atlasing.
395    * @param[in] textureInfo The struct associated with this Texture
396    * @param[in] pixelBuffer The image pixelBuffer
397    * @return    True if successful
398    */
399   void PostLoad( TextureManager::TextureInfo& textureInfo, Devel::PixelBuffer& pixelBuffer );
400
401   /**
402    * Check if there is a texture waiting to be masked. If there
403    * is then apply this mask and upload it.
404    * @param[in] maskTextureInfo The texture info of the mask that has just loaded.
405    */
406   void CheckForWaitingTexture( TextureInfo& maskTextureInfo );
407
408   /**
409    * Apply the mask to the pixelBuffer.
410    * @param[in] pixelBuffer The pixelBuffer to apply the mask to
411    * @param[in] maskTextureId The texture id of the mask.
412    * @param[in] contentScale The factor to scale the content
413    * @param[in] cropToMask Whether to crop the content to the mask size
414    */
415   void ApplyMask( Devel::PixelBuffer& pixelBuffer, TextureId maskTextureId,
416                   float contentScale, bool cropToMask );
417
418   /**
419    * Upload the texture specified in pixelBuffer to the appropriate location
420    * @param[in] pixelBuffer The image data to upload
421    * @param[in] textureInfo The texture info containing the location to
422    * store the data to.
423    */
424   void UploadTexture( Devel::PixelBuffer& pixelBuffer, TextureInfo& textureInfo );
425
426   /**
427    * Mark the texture as complete, and inform observers
428    * @param[in] textureInfo The struct associated with this Texture
429    */
430   void UploadComplete( TextureInfo& textureInfo );
431
432   /**
433    * Notify the current observers that the texture upload is complete,
434    * then remove the observers from the list.
435    * @param[in] textureInfo The struct associated with this Texture
436    * @param[in] success If the pixel data was retrieved successfully and uploaded to GPU
437    */
438   void NotifyObservers( TextureInfo& textureInfo, bool success );
439
440   /**
441    * @brief Generates a new, unique TextureId
442    * @return A unique TextureId
443    */
444   TextureManager::TextureId GenerateUniqueTextureId();
445
446   /**
447    * @brief Used to lookup an index into the TextureInfoContainer from a TextureId
448    * @param[in] textureId The TextureId to look up
449    * @return              The cache index
450    */
451   int GetCacheIndexFromId( TextureId textureId );
452
453
454   /**
455    * @brief Generates a hash for caching based on the input parameters.
456    * Only applies size, fitting mode andsampling mode if the size is specified.
457    * Only applies maskTextureId if it isn't INVALID_TEXTURE_ID
458    * Always applies useAtlas.
459    * @param[in] url          The URL of the image to load
460    * @param[in] size         The image size
461    * @param[in] fittingMode  The FittingMode to use
462    * @param[in] samplingMode The SamplingMode to use
463    * @param[in] useAtlas     True if atlased
464    * @param[in] maskTextureId The masking texture id (or INVALID_TEXTURE_ID)
465    * @return                 A hash of the provided data for caching.
466    */
467   TextureHash GenerateHash( const std::string& url, const ImageDimensions size,
468                             const FittingMode::Type fittingMode,
469                             const Dali::SamplingMode::Type samplingMode, const UseAtlas useAtlas,
470                             TextureId maskTextureId );
471   /**
472    * @brief Looks up a cached texture by its hash.
473    * If found, the given parameters are used to check there is no hash-collision.
474    * @param[in] hash          The hash to look up
475    * @param[in] url           The URL of the image to load
476    * @param[in] size          The image size
477    * @param[in] fittingMode   The FittingMode to use
478    * @param[in] samplingMode  The SamplingMode to use
479    * @param[in] useAtlas      True if atlased
480    * @param[in] maskTextureId Optional texture ID to use to mask this image
481    * @return A TextureId of a cached Texture if found. Or INVALID_TEXTURE_ID if not found.
482    */
483   TextureManager::TextureId FindCachedTexture(
484     const TextureManager::TextureHash hash,
485     const std::string& url,
486     const ImageDimensions size,
487     const FittingMode::Type fittingMode,
488     const Dali::SamplingMode::Type samplingMode,
489     const bool useAtlas,
490     TextureId maskTextureId );
491
492 private:
493
494   /**
495    * @brief Helper class to keep the relation between AsyncImageLoader and corresponding LoadingInfo container
496    */
497   class AsyncLoadingHelper : public ConnectionTracker
498   {
499   public:
500     /**
501      * @brief Create an AsyncLoadingHelper.
502      * @param[in] textureManager Reference to the texture manager
503      */
504     AsyncLoadingHelper(TextureManager& textureManager);
505
506     /**
507      * @brief Load a new texture.
508      * @param[in] textureId             TextureId to reference the texture that will be loaded
509      * @param[in] url                   The URL of the image to load
510      * @param[in] desiredSize           The size the image is likely to appear at. This can be set to 0,0 for automatic
511      * @param[in] fittingMode           The FittingMode to use
512      * @param[in] samplingMode          The SamplingMode to use
513      * @param[in] orientationCorrection Whether to use image metadata to rotate or flip the image, e.g., from portrait to landscape
514      */
515     void Load(TextureId textureId,
516               const VisualUrl& url,
517               ImageDimensions desiredSize,
518               FittingMode::Type fittingMode,
519               SamplingMode::Type samplingMode,
520               bool orientationCorrection);
521
522   public:
523     AsyncLoadingHelper(const AsyncLoadingHelper&) = delete;
524     AsyncLoadingHelper& operator=(const AsyncLoadingHelper&) = delete;
525
526     AsyncLoadingHelper(AsyncLoadingHelper&& rhs);
527     AsyncLoadingHelper& operator=(AsyncLoadingHelper&&rhs) = delete;
528
529   private:
530     /**
531      * @brief Main constructor that used by all other constructors
532      */
533     AsyncLoadingHelper(Toolkit::AsyncImageLoader loader,
534                        TextureManager& textureManager,
535                        AsyncLoadingInfoContainerType&& loadingInfoContainer);
536
537     /**
538      * @brief Callback to be called when texture loading is complete, it passes the pixel buffer on to texture manager.
539      * @param[in] id          Loader id
540      * @param[in] pixelBuffer Image data
541      */
542     void AsyncLoadComplete(uint32_t id, Devel::PixelBuffer pixelBuffer);
543
544   private:
545     Toolkit::AsyncImageLoader     mLoader;
546     TextureManager&               mTextureManager;
547     AsyncLoadingInfoContainerType mLoadingInfoContainer;
548   };
549
550 private:
551
552   /**
553    * Deleted copy constructor.
554    */
555   TextureManager( const TextureManager& ) = delete;
556
557   /**
558    * Deleted assignment operator.
559    */
560   TextureManager& operator=( const TextureManager& rhs ) = delete;
561
562   /**
563    * This is called by the TextureManagerUploadObserver when an observer is destroyed.
564    * We use the callback to know when to remove an observer from our notify list.
565    * @param[in] observer The observer that generated the callback
566    */
567   void ObserverDestroyed( TextureUploadObserver* observer );
568
569 private:  // Member Variables:
570
571   AtlasInfoContainerType        mAtlasContainer;                  ///< Used to manage Atlas creation and destruction
572   TextureInfoContainerType      mTextureInfoContainer;            ///< Used to manage the life-cycle and caching of Textures
573   TextureId                     mCurrentTextureId;                ///< The current value used for the unique Texture Id generation
574
575   RoundRobinContainerView<AsyncLoadingHelper> mAsyncLocalLoaders;  ///< The Asynchronous image loaders used to provide all local async loads
576   RoundRobinContainerView<AsyncLoadingHelper> mAsyncRemoteLoaders; ///< The Asynchronous image loaders used to provide all remote async loads
577 };
578
579
580 } // name Internal
581
582 } // namespace Toolkit
583
584 } // namespace Dali
585
586 #endif // DALI_TOOLKIT_TEXTURE_MANAGER_H