2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/update/resources/resource-manager.h>
25 #include <dali/devel-api/common/map-wrapper.h>
26 #include <dali/devel-api/common/set-wrapper.h>
28 #include <dali/public-api/math/vector2.h>
29 #include <dali/public-api/common/dali-vector.h>
31 #include <dali/integration-api/debug.h>
33 #include <dali/internal/common/message.h>
34 #include <dali/internal/common/image-attributes.h>
36 #include <dali/internal/event/common/notification-manager.h>
37 #include <dali/internal/event/resources/resource-type-path.h>
39 #include <dali/internal/update/common/discard-queue.h>
40 #include <dali/internal/update/common/texture-cache-dispatcher.h>
41 #include <dali/internal/update/resources/texture-metadata.h>
43 #include <dali/internal/render/queue/render-queue.h>
45 using namespace Dali::Integration;
47 using Dali::Internal::SceneGraph::DiscardQueue;
48 using Dali::Internal::SceneGraph::RenderQueue;
49 using Dali::Internal::SceneGraph::TextureCacheDispatcher;
58 typedef std::set<ResourceId> LiveRequestContainer;
59 typedef LiveRequestContainer::iterator LiveRequestIter;
60 typedef LiveRequestContainer::size_type LiveRequestSize;
62 typedef std::map<ResourceId, ResourceTypeId> DeadRequestContainer;
63 typedef DeadRequestContainer::iterator DeadRequestIter;
64 typedef std::pair<ResourceId, ResourceTypeId> DeadRequestPair;
66 typedef std::vector<ResourceId> NotifyQueue;
67 typedef NotifyQueue::iterator NotifyQueueIter;
69 typedef Dali::Vector<TextureMetadata> TextureMetadataCache;
70 typedef TextureMetadataCache::Iterator TextureMetadataIter;
72 static inline bool RemoveId( LiveRequestContainer& container, ResourceId id )
74 return container.erase(id) != 0;
77 struct ResourceManager::ResourceManagerImpl
79 ResourceManagerImpl( PlatformAbstraction& platformAbstraction,
80 NotificationManager& notificationManager,
81 SceneGraph::TextureCacheDispatcher& textureCacheDispatcher,
82 LockedResourceQueue& textureUploadedQueue,
83 DiscardQueue& discardQueue,
84 RenderQueue& renderQueue )
85 : mPlatformAbstraction(platformAbstraction),
86 mNotificationManager(notificationManager),
87 mTextureCacheDispatcher(textureCacheDispatcher),
88 mTextureUploadedQueue(textureUploadedQueue),
89 mTextureUploadedProcessingQueue(),
90 mDiscardQueue(discardQueue),
91 mRenderQueue(renderQueue),
92 mNotificationCount(0),
95 mTextureMetadata.Reserve( 256 ); // reserve size for metadata to avoid early re-allocs in application startup
98 ~ResourceManagerImpl()
102 PlatformAbstraction& mPlatformAbstraction;
103 NotificationManager& mNotificationManager;
104 TextureCacheDispatcher& mTextureCacheDispatcher;
105 LockedResourceQueue& mTextureUploadedQueue;
106 TextureUploadedQueue mTextureUploadedProcessingQueue;
107 DiscardQueue& mDiscardQueue; ///< Unwanted resources are added here during UpdateCache()
108 RenderQueue& mRenderQueue;
109 unsigned int mNotificationCount;
110 bool cacheUpdated; ///< returned by UpdateCache(). Set true in NotifyTickets to indicate a change in a resource
113 * These containers are used to processs requests, and ResourceCache callbacks.
114 * The live request containers are simply sets of integer resource ids.
115 * The ID of a new request will be placed in the loading container.
116 * If the Ticket is destroyed during the load, the ID will be removed.
117 * If the load fails, the ID will be moved to the failed container.
118 * When the Ticket is notified of the failure, the ID will be removed.
119 * If the load succeeds, the ID will be moved to the new-completed container.
120 * When the Ticket is notified of the completion, the ID will be moved to the old-completed container.
121 * If a Ticket is destroyed after a successful load, the ID will be moved to the dead container.
122 * When the resources are eventually deleted, the ID will be removed from the dead container.
124 LiveRequestContainer loadingRequests;
125 LiveRequestContainer newCompleteRequests;
126 LiveRequestContainer oldCompleteRequests;
127 LiveRequestContainer newFailedRequests;
128 LiveRequestContainer oldFailedRequests;
129 DeadRequestContainer deadRequests;
132 * This is the resource cache; metadata of the textures
134 TextureMetadataCache mTextureMetadata;
137 ResourceManager::ResourceManager( PlatformAbstraction& platformAbstraction,
138 NotificationManager& notificationManager,
139 TextureCacheDispatcher& textureCacheDispatcher,
140 LockedResourceQueue& resourcePostProcessQueue,
141 DiscardQueue& discardQueue,
142 RenderQueue& renderQueue )
144 mImpl = new ResourceManagerImpl( platformAbstraction,
146 textureCacheDispatcher,
147 resourcePostProcessQueue,
152 ResourceManager::~ResourceManager()
157 /********************************************************************************
158 ************************ UpdateManager direct interface ***********************
159 ********************************************************************************/
161 bool ResourceManager::UpdateCache( BufferIndex updateBufferIndex )
163 DALI_LOG_INFO(Debug::Filter::gResource, Debug::Verbose, "ResourceManager: UpdateCache(bufferIndex:%u)\n", updateBufferIndex);
165 // 1) Move unwanted resources to the DiscardQueue
167 DiscardDeadResources( updateBufferIndex );
169 // 2) Fill the resource cache
170 mImpl->cacheUpdated = false;
172 mImpl->mPlatformAbstraction.GetResources(*this);
174 return mImpl->cacheUpdated;
177 /********************************************************************************
178 *************************** CoreImpl direct interface *************************
179 ********************************************************************************/
181 bool ResourceManager::ResourcesToProcess()
183 bool workTodo = false;
185 // need to make sure we have passed all the notifications to the event handling side
186 workTodo |= !mImpl->newCompleteRequests.empty();
187 workTodo |= !mImpl->newFailedRequests.empty();
188 // check if there's something still loading
189 workTodo |= !mImpl->loadingRequests.empty();
195 /********************************************************************************
196 ********************************* Message handlers *****************************
197 ********************************************************************************/
199 void ResourceManager::HandleLoadResourceRequest( ResourceId id, const ResourceTypePath& typePath, LoadResourcePriority priority )
201 DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceManager: HandleLoadResourceRequest(id:%u, path:%s, type.id:%d)\n", id, typePath.path.c_str(), typePath.type->id);
203 // Add ID to the loading set
204 mImpl->loadingRequests.insert(id);
206 // Make the load request last
207 mImpl->mPlatformAbstraction.LoadResource(ResourceRequest(id, *typePath.type, typePath.path, priority));
210 void ResourceManager::HandleDecodeResourceRequest(
212 const ResourceTypePath& typePath,
213 RequestBufferPtr buffer,
214 Integration::LoadResourcePriority priority )
216 DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceManager: HandleDecodeResourceRequest(id:%u, buffer.size:%u, type.id:%u)\n", id, buffer->GetVector().Size(), typePath.type->id);
218 // Add ID to the loading set
219 mImpl->loadingRequests.insert(id);
221 // Make the load request, stuffing the buffer of encoded bytes into the same field used when saving resources:
222 mImpl->mPlatformAbstraction.LoadResource(ResourceRequest(id, *typePath.type, "", buffer, priority));
225 void ResourceManager::HandleAddBitmapImageRequest( ResourceId id, BitmapPtr bitmap )
227 DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceManager: HandleAddBitmapImageRequest(id:%u)\n", id);
229 mImpl->oldCompleteRequests.insert(id);
230 mImpl->mTextureMetadata.PushBack( TextureMetadata::New( id, bitmap.Get() ) );
231 mImpl->mTextureCacheDispatcher.DispatchCreateTextureForBitmap( id, bitmap.Get() );
234 void ResourceManager::HandleAddNativeImageRequest(ResourceId id, NativeImageInterfacePtr nativeImage)
236 DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceManager: HandleAddNativeImageRequest(id:%u)\n", id);
238 mImpl->oldCompleteRequests.insert(id);
240 mImpl->mTextureMetadata.PushBack( TextureMetadata::New( id, nativeImage ) );
241 mImpl->mTextureCacheDispatcher.DispatchCreateTextureForNativeImage( id, nativeImage );
244 void ResourceManager::HandleAddFrameBufferImageRequest( ResourceId id, unsigned int width, unsigned int height, Pixel::Format pixelFormat, RenderBuffer::Format bufferFormat )
246 DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceManager: HandleAddFrameBufferImageRequest(id:%u)\n", id);
248 mImpl->oldCompleteRequests.insert(id);
250 TextureMetadata bitmapMetadata = TextureMetadata::New( id, width, height, Pixel::HasAlpha(pixelFormat) );
251 bitmapMetadata.SetIsFramebuffer(true);
252 mImpl->mTextureMetadata.PushBack( bitmapMetadata );
254 mImpl->mTextureCacheDispatcher.DispatchCreateTextureForFrameBuffer( id, width, height, pixelFormat, bufferFormat );
257 void ResourceManager::HandleAddFrameBufferImageRequest( ResourceId id, NativeImageInterfacePtr nativeImage )
259 DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceManager: HandleAddFrameBufferImageRequest(id:%u)\n", id);
261 mImpl->oldCompleteRequests.insert(id);
263 TextureMetadata bitmapMetadata = TextureMetadata::New(id, nativeImage);
264 bitmapMetadata.SetIsNativeImage(true);
265 bitmapMetadata.SetIsFramebuffer(true);
266 mImpl->mTextureMetadata.PushBack( bitmapMetadata );
268 mImpl->mTextureCacheDispatcher.DispatchCreateTextureForFrameBuffer( id, nativeImage );
271 void ResourceManager::HandleAllocateTextureRequest( ResourceId id, unsigned int width, unsigned int height, Pixel::Format pixelFormat )
273 DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceManager: HandleAllocateTextureRequest(id:%u)\n", id);
275 mImpl->oldCompleteRequests.insert(id);
276 // atlas needs metadata as well
277 TextureMetadata bitmapMetadata = TextureMetadata::New( id, width, height, Pixel::HasAlpha(pixelFormat) );
278 mImpl->mTextureMetadata.PushBack( bitmapMetadata );
280 mImpl->mTextureCacheDispatcher.DispatchCreateTexture( id, width, height, pixelFormat, true /* true = clear the texture */ );
283 void ResourceManager::HandleUpdateBitmapAreaRequest( ResourceId textureId, const RectArea& area )
287 mImpl->mTextureCacheDispatcher.DispatchUpdateTextureArea( textureId, area );
291 void ResourceManager::HandleUploadBitmapRequest( ResourceId destId, Integration::BitmapPtr bitmap, std::size_t xOffset, std::size_t yOffset )
293 if( destId && bitmap )
295 mImpl->mTextureCacheDispatcher.DispatchUpdateTexture( destId, bitmap, xOffset, yOffset );
299 void ResourceManager::HandleUploadBitmapRequest( ResourceId destId, ResourceId srcId, std::size_t xOffset, std::size_t yOffset )
301 if( destId && srcId )
303 mImpl->mTextureCacheDispatcher.DispatchUpdateTexture( destId, srcId, xOffset, yOffset );
307 void ResourceManager::HandleUploadBitmapRequest( ResourceId destId, PixelDataPtr pixelData, std::size_t xOffset, std::size_t yOffset )
309 if( destId && pixelData )
311 mImpl->mTextureCacheDispatcher.DispatchUpdateTexture( destId, pixelData, xOffset, yOffset );
315 void ResourceManager::HandleReloadResourceRequest( ResourceId id, const ResourceTypePath& typePath, LoadResourcePriority priority, bool resetFinishedStatus )
317 DALI_LOG_INFO( Debug::Filter::gResource, Debug::General, "ResourceManager: HandleReloadRequest(id:%u, path:%s)\n", id, typePath.path.c_str() );
319 if( resetFinishedStatus )
321 if( ! RemoveId( mImpl->newCompleteRequests, id ) )
323 RemoveId( mImpl->oldCompleteRequests, id );
327 // ID might be in the loading set
328 LiveRequestIter iter = mImpl->loadingRequests.find( id );
329 if ( iter == mImpl->loadingRequests.end() )
331 // Add ID to the loading set
332 mImpl->loadingRequests.insert(id);
336 void ResourceManager::HandleDiscardResourceRequest( ResourceId deadId, ResourceTypeId typeId )
338 bool wasComplete = false;
339 bool wasLoading = false;
341 DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceManager: HandleDiscardResourceRequest(id:%u)\n", deadId);
343 // Search for the ID in one of the live containers
344 // IDs are only briefly held in the new-completed or failed containers; check those last
345 // Try removing from the old-completed requests
346 bool foundLiveRequest = wasComplete = RemoveId(mImpl->oldCompleteRequests, deadId);
348 // Try removing from the loading requests
349 if (!foundLiveRequest)
351 foundLiveRequest = wasLoading = RemoveId(mImpl->loadingRequests, deadId);
354 // Try removing from the new completed requests
355 if (!foundLiveRequest)
357 foundLiveRequest = wasComplete = RemoveId(mImpl->newCompleteRequests, deadId);
360 // Try removing from the new failed requests
361 if (!foundLiveRequest)
363 foundLiveRequest = RemoveId(mImpl->newFailedRequests, deadId);
366 // Try removing from the old failed requests
367 if (!foundLiveRequest)
369 foundLiveRequest = RemoveId(mImpl->oldFailedRequests, deadId);
372 // ID should be in one of the live sets
373 if (!foundLiveRequest)
375 DALI_LOG_WARNING("HandleDiscardResourceRequest: ID should be in one of the live sets!\n");
377 DALI_ASSERT_DEBUG(foundLiveRequest);
381 if(typeId == ResourceBitmap ||
382 typeId == ResourceNativeImage ||
383 typeId == ResourceTargetImage )
385 // remove the meta data
387 TextureMetadataCache::Iterator iter = mImpl->mTextureMetadata.Begin();
388 const TextureMetadataCache::Iterator end = mImpl->mTextureMetadata.End();
389 for( ; iter != end; ++iter )
391 if( (*iter).GetId() == deadId )
398 mImpl->mTextureMetadata.Erase( iter );
401 // destroy the texture
402 mImpl->mTextureCacheDispatcher.DispatchDiscardTexture( deadId );
406 // Move ID from completed to dead set
407 mImpl->deadRequests.insert(DeadRequestPair(deadId, typeId));
413 mImpl->mPlatformAbstraction.CancelLoad(deadId, typeId);
417 void ResourceManager::HandleCreateGlTextureRequest(ResourceId id)
419 mImpl->mTextureCacheDispatcher.DispatchCreateGlTexture( id );
422 /********************************************************************************
423 ******************** Update thread object direct interface ********************
424 ********************************************************************************/
426 bool ResourceManager::IsResourceLoaded( ResourceId id ) const
432 LiveRequestIter iter = mImpl->newCompleteRequests.find(id);
433 if( iter != mImpl->newCompleteRequests.end() )
439 iter = mImpl->oldCompleteRequests.find(id);
440 if( iter != mImpl->oldCompleteRequests.end() )
450 bool ResourceManager::HasResourceLoadFailed( ResourceId id ) const
452 bool loadFailed = false;
456 LiveRequestIter iter = mImpl->newFailedRequests.find(id);
457 if( iter != mImpl->newFailedRequests.end() )
463 iter = mImpl->oldFailedRequests.find(id);
464 if( iter != mImpl->oldFailedRequests.end() )
474 void ResourceManager::SetFrameBufferBeenRenderedTo( ResourceId id, bool value )
476 TextureMetadata* metadata = NULL;
477 if( GetTextureMetadata( id, metadata ) )
479 metadata->SetFrameBufferBeenRenderedTo( value );
483 bool ResourceManager::HasFrameBufferBeenRenderedTo( ResourceId id ) const
485 bool retval( false );
486 TextureMetadata* metadata = NULL;
487 if( GetTextureMetadata( id, metadata ) )
489 retval = metadata->HasFrameBufferBeenRenderedTo();
494 bool ResourceManager::GetTextureMetadata( ResourceId id, TextureMetadata*& metadata ) const
498 const size_t count = mImpl->mTextureMetadata.Count();
499 for( size_t index = 0; index < count; ++index )
501 if( id == mImpl->mTextureMetadata[ index ].GetId() )
503 metadata = &mImpl->mTextureMetadata[ index ];
512 /********************************************************************************
513 ************************* ResourceCache Implementation ************************
514 ********************************************************************************/
516 void ResourceManager::LoadResponse( ResourceId id, ResourceTypeId type, ResourcePointer resource, LoadStatus loadStatus )
518 DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceManager: LoadResponse(id:%u, status=%s)\n", id, loadStatus==RESOURCE_LOADING?"LOADING":loadStatus==RESOURCE_PARTIALLY_LOADED?"PARTIAL":"COMPLETE");
520 // ID might be in the loading set
521 LiveRequestIter iter = mImpl->loadingRequests.find(id);
523 if ( iter != mImpl->loadingRequests.end() )
525 if( loadStatus == RESOURCE_COMPLETELY_LOADED )
527 // Remove from the loading set
528 mImpl->loadingRequests.erase(iter);
530 // Add the ID to the new-completed set, and store the resource
531 mImpl->newCompleteRequests.insert(id);
538 DALI_ASSERT_DEBUG( loadStatus == RESOURCE_COMPLETELY_LOADED && "Partial results not handled for image loading.\n" );
539 Bitmap* const bitmap = static_cast<Bitmap*>( resource.Get() );
542 DALI_LOG_ERROR( "Missing bitmap in loaded resource with id %u.\n", id );
545 unsigned int bitmapWidth = bitmap->GetImageWidth();
546 unsigned int bitmapHeight = bitmap->GetImageHeight();
548 if( Bitmap::PackedPixelsProfile * packedBitmap = bitmap->GetPackedPixelsProfile() )
550 bitmapWidth = packedBitmap->GetBufferWidth();
551 bitmapHeight = packedBitmap->GetBufferHeight();
553 ImageAttributes attrs = ImageAttributes::New( bitmapWidth, bitmapHeight ); ///!< Issue #AHC01
555 // Check for reloaded bitmap
556 TextureMetadataCache::Iterator iter = mImpl->mTextureMetadata.Begin();
557 const TextureMetadataCache::Iterator end = mImpl->mTextureMetadata.End();
558 for( ; iter != end; ++iter )
560 if( (*iter).GetId() == id )
567 iter->Update( bitmap );
568 mImpl->mTextureCacheDispatcher.DispatchUpdateTexture( id, bitmap );
572 mImpl->mTextureCacheDispatcher.DispatchCreateTextureForBitmap( id, bitmap );
573 mImpl->mTextureMetadata.PushBack( TextureMetadata::New( id, bitmap ) );
579 case ResourceNativeImage:
581 NativeImageInterfacePtr nativeImg( static_cast<NativeImageInterface*>(resource.Get()) );
583 ImageAttributes attrs = ImageAttributes::New(nativeImg->GetWidth(), nativeImg->GetHeight());
585 mImpl->mTextureMetadata.PushBack( TextureMetadata::New( id, nativeImg ) );
586 mImpl->mTextureCacheDispatcher.DispatchCreateTextureForNativeImage( id, nativeImg );
591 case ResourceTargetImage:
597 // flag that a load has completed and the cache updated
598 mImpl->cacheUpdated = true;
602 // This warning can fire if a cancelled load is forgotten here while already complete on a resource thread:
603 DALI_LOG_WARNING( "Received a notification for an untracked resource: (id:%u, status=%s)\n", id, loadStatus==RESOURCE_LOADING?"LOADING":loadStatus==RESOURCE_PARTIALLY_LOADED?"PARTIAL":"COMPLETE");
607 void ResourceManager::LoadFailed(ResourceId id, ResourceFailure failure)
609 DALI_LOG_INFO(Debug::Filter::gResource, Debug::General, "ResourceManager: LoadFailed(id:%u)\n", id);
611 // ID might be in the loading set
612 LiveRequestIter iter = mImpl->loadingRequests.find(id);
614 if (iter != mImpl->loadingRequests.end())
616 // Remove from the loading set
617 mImpl->loadingRequests.erase(iter);
619 // Add the ID to the failed set, this will trigger a notification during UpdateTickets
620 mImpl->newFailedRequests.insert(id);
622 mImpl->cacheUpdated = true;
626 /********************************************************************************
627 ********************************* Private Methods *****************************
628 ********************************************************************************/
630 void ResourceManager::DiscardDeadResources( BufferIndex updateBufferIndex )
632 for (DeadRequestIter iter = mImpl->deadRequests.begin(); iter != mImpl->deadRequests.end(); )
634 // Erase the item and increment the iterator
635 mImpl->deadRequests.erase(iter++);
639 } // namespace Internal