6db70e80b8deacd27748d9c7160cb5379b75c7fc
[platform/core/uifw/dali-adaptor.git] / platform-abstractions / tizen / resource-loader / resource-loader.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include "resource-loader.h"
20
21 // EXTERNAL HEADERS
22 #include <boost/thread.hpp>
23 #include <iostream>
24 #include <fstream>
25 #include <queue>
26
27 // INTERNAL HEADERS
28 #include <dali/integration-api/bitmap.h>
29 #include <dali/integration-api/debug.h>
30 #include <dali/integration-api/resource-cache.h>
31 #include <dali/public-api/common/dali-common.h>
32 #include <dali/devel-api/common/set-wrapper.h>
33 #include <dali/public-api/math/vector2.h>
34 #include "resource-requester-base.h"
35 #include "resource-bitmap-requester.h"
36 #include "resource-shader-requester.h"
37 #include "debug/resource-loader-debug.h"
38
39
40 /**
41  * A macro to expand an argument to a compile time constant string literal.
42  * Wrapping the stringify in an outer macro, means that any macro passed as
43  * "x" will be expanded before being turned into a string.
44  * Use this for example to turn the current line number into a string:
45  *   puts("The current line number is " DALI_TO_STRING(__LINE__) ".");
46  */
47 #define DALI_TO_STRING_INNER(x) #x
48 #define DALI_TO_STRING(x) DALI_TO_STRING_INNER(x)
49
50 using namespace Dali::Integration;
51 using boost::mutex;
52 using boost::unique_lock;
53
54 namespace Dali
55 {
56
57 namespace TizenPlatform
58 {
59
60 namespace
61 {
62
63 } // unnamed namespace
64
65
66 struct ResourceLoader::ResourceLoaderImpl
67 {
68   typedef std::pair<ResourceId, ResourceRequest>  RequestStorePair;
69   typedef std::map<ResourceId, ResourceRequest>   RequestStore;
70   typedef RequestStore::iterator                  RequestStoreIter;
71
72   typedef std::queue<LoadedResource> LoadedQueue;
73   typedef std::queue<SavedResource>  SavedQueue;
74   typedef std::queue<FailedResource> FailedQueue;
75
76   typedef std::pair<ResourceTypeId, ResourceRequesterBase*> RequestHandlerPair;
77   typedef std::map<ResourceTypeId,  ResourceRequesterBase*> RequestHandlers;
78   typedef RequestHandlers::iterator                         RequestHandlersIter;
79
80   boost::mutex mQueueMutex;             ///< used to synchronize access to mLoadedQueue, mSavedQueue and mFailedQueue
81   LoadedQueue  mPartiallyLoadedQueue;   ///< Partially complete load requests notifications are stored here until fetched by core
82   LoadedQueue  mLoadedQueue;            ///< Completed load requests notifications are stored here until fetched by core
83   SavedQueue   mSavedQueue;             ///< Completed save request notifications are stored here until fetched by core
84   FailedQueue  mFailedLoads;            ///< Failed load request notifications are stored here until fetched by core
85   FailedQueue  mFailedSaves;            ///< Failed save request notifications are stored here until fetched by core
86
87   RequestHandlers mRequestHandlers;
88   RequestStore mStoredRequests;         ///< Used to store load requests until loading is completed
89
90   ResourceLoaderImpl( ResourceLoader* loader )
91   {
92     mRequestHandlers.insert(std::make_pair(ResourceBitmap, new ResourceBitmapRequester(*loader)));
93     mRequestHandlers.insert(std::make_pair(ResourceShader, new ResourceShaderRequester(*loader)));
94   }
95
96   ~ResourceLoaderImpl()
97   {
98     // Delete resource handlers
99     for( RequestHandlersIter it = mRequestHandlers.begin(); it != mRequestHandlers.end(); ++it )
100     {
101       ResourceRequesterBase* requestBase = it->second;
102       delete requestBase;
103     }
104   }
105
106   void Pause()
107   {
108     // Pause all the request handlers:
109     for( RequestHandlersIter it = mRequestHandlers.begin(), end = mRequestHandlers.end(); it != end;  ++it )
110     {
111       ResourceRequesterBase * const requester = it->second;
112       if( requester )
113       {
114         requester->Pause();
115       }
116     }
117   }
118
119   void Resume()
120   {
121     // Wake up all the request handlers:
122     for( RequestHandlersIter it = mRequestHandlers.begin(), end = mRequestHandlers.end(); it != end;  ++it )
123     {
124       ResourceRequesterBase * const requester = it->second;
125       if( requester )
126       {
127         requester->Resume();
128       }
129     }
130   }
131
132   ResourceRequesterBase* GetRequester(ResourceTypeId typeId)
133   {
134     ResourceRequesterBase* requestHandler = NULL;
135     RequestHandlersIter iter = mRequestHandlers.find(typeId);
136     if(iter != mRequestHandlers.end())
137     {
138       requestHandler = iter->second;
139     }
140     DALI_ASSERT_DEBUG(requestHandler && "All resource types should have a requester defined for them.");
141     return requestHandler;
142   }
143
144   void LoadResource(const ResourceRequest& request)
145   {
146     // Store resource request for partial loaders. Will get cleaned up after load complete has finished
147     StoreRequest(request);
148
149     ResourceRequesterBase* requester = GetRequester(request.GetType()->id);
150     if( requester )
151     {
152       ResourceRequest* storedRequest = GetRequest(request.GetId());
153       if( storedRequest != NULL )
154       {
155         requester->LoadResource(*storedRequest); // Pass in stored request
156       }
157     }
158     else
159     {
160       DALI_LOG_ERROR( "Unknown resource type (%u) with path \"%s\" in load request.\n", request.GetType()->id, request.GetPath().c_str() );
161       DALI_ASSERT_DEBUG( 0 == "Unknown resource type in load request at " __FILE__ ", line " DALI_TO_STRING(__LINE__) ".\n" );
162     }
163   }
164
165   void SaveResource(const ResourceRequest& request)
166   {
167     ResourceRequesterBase* requester = GetRequester( request.GetType()->id );
168     if( requester )
169     {
170       requester->SaveResource( request );
171     }
172   }
173
174   void CancelLoad(ResourceId id, ResourceTypeId typeId)
175   {
176     ResourceRequesterBase* requester = GetRequester(typeId);
177     if( requester )
178     {
179       requester->CancelLoad( id, typeId );
180     }
181     ClearRequest( id );
182   }
183
184   LoadStatus LoadFurtherResources( LoadedResource partialResource )
185   {
186     LoadStatus loadStatus = RESOURCE_LOADING;
187     RequestStoreIter iter = mStoredRequests.find(partialResource.id);
188
189     if( mStoredRequests.end() != iter ) // else cancelled. Ignore response
190     {
191       ResourceRequest& request = iter->second;
192       ResourceRequesterBase* requester = GetRequester(request.GetType()->id);
193       if( requester )
194       {
195         loadStatus = requester->LoadFurtherResources( request, partialResource );
196       }
197
198       DALI_LOG_INFO(gLoaderFilter, Debug::General, "ResourceLoader::LoadFurtherResources( ID:%u complete: %s)\n",  request.GetId(), loadStatus==RESOURCE_LOADING?"Loading":loadStatus==RESOURCE_PARTIALLY_LOADED?"PARTIAL":"COMPLETE" );
199     }
200
201     if( loadStatus == RESOURCE_COMPLETELY_LOADED )
202     {
203       ClearRequest( partialResource.id );
204     }
205
206     return loadStatus;
207   }
208
209   bool IsLoading()
210   {
211     // TODO - not used - remove?
212     DALI_ASSERT_DEBUG( 0 == "IsLoading() Is not implemented so don't call it." );
213     return true;
214   }
215
216   void GetResources(ResourceCache& cache)
217   {
218     // Fill the resource cache
219
220     unique_lock<mutex> lock(mQueueMutex);
221
222     // iterate through the partially loaded resources
223     while (!mPartiallyLoadedQueue.empty())
224     {
225       LoadedResource loaded( mPartiallyLoadedQueue.front() );
226       mPartiallyLoadedQueue.pop();
227       LoadStatus loadStatus = LoadFurtherResources( loaded );
228       cache.LoadResponse( loaded.id, loaded.type, loaded.resource, loadStatus );
229     }
230
231     // iterate through the successfully loaded resources
232     while (!mLoadedQueue.empty())
233     {
234       LoadedResource loaded( mLoadedQueue.front() );
235       mLoadedQueue.pop();
236       ClearRequest( loaded.id );
237       cache.LoadResponse( loaded.id, loaded.type, loaded.resource, RESOURCE_COMPLETELY_LOADED );
238     }
239
240     // iterate through the successfully saved resources
241     while (!mSavedQueue.empty())
242     {
243       SavedResource saved(mSavedQueue.front());
244       mSavedQueue.pop();
245       cache.SaveComplete(saved.id, saved.type);
246     }
247
248     // iterate through the resources which failed to load
249     while (!mFailedLoads.empty())
250     {
251       FailedResource failed(mFailedLoads.front());
252       mFailedLoads.pop();
253       ClearRequest(failed.id);
254       cache.LoadFailed(failed.id, failed.failureType);
255     }
256
257     // iterate through the resources which failed to save
258     while (!mFailedSaves.empty())
259     {
260       FailedResource failed(mFailedSaves.front());
261       mFailedSaves.pop();
262       cache.SaveFailed(failed.id, failed.failureType);
263     }
264   }
265
266   void AddPartiallyLoadedResource( LoadedResource& resource)
267   {
268     // Lock the LoadedQueue to store the loaded resource
269     unique_lock<mutex> lock(mQueueMutex);
270
271     mPartiallyLoadedQueue.push( resource );
272   }
273
274   void AddLoadedResource(LoadedResource& resource)
275   {
276     // Lock the LoadedQueue to store the loaded resource
277     unique_lock<mutex> lock(mQueueMutex);
278
279     mLoadedQueue.push( resource );
280   }
281
282   void AddSavedResource(SavedResource& resource)
283   {
284     // Lock the SavedQueue to store the loaded resource
285     unique_lock<mutex> lock(mQueueMutex);
286
287     mSavedQueue.push(resource);
288   }
289
290   void AddFailedLoad(FailedResource& resource)
291   {
292     // Lock the FailedQueue to store the failed resource information
293     unique_lock<mutex> lock(mQueueMutex);
294
295     mFailedLoads.push(resource);
296   }
297
298   void AddFailedSave(FailedResource& resource)
299   {
300     // Lock the FailedQueue to store the failed resource information
301     unique_lock<mutex> lock(mQueueMutex);
302
303     mFailedSaves.push(resource);
304   }
305
306   void StoreRequest( const ResourceRequest& request )
307   {
308     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader: StoreRequest(id:%u)\n", request.GetId());
309     mStoredRequests.insert( RequestStorePair( request.GetId(), request ) ); // copy request as value type
310   }
311
312   ResourceRequest* GetRequest( ResourceId id )
313   {
314     ResourceRequest* found(NULL);
315     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader: GetRequest(id:%u)\n", id);
316     RequestStoreIter iter = mStoredRequests.find( id );
317     if( mStoredRequests.end() != iter )
318     {
319       found = &iter->second;
320     }
321     return found;
322   }
323
324   void ClearRequest( ResourceId resourceId )
325   {
326     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader: ClearRequest(id:%u)\n", resourceId);
327     RequestStoreIter iter = mStoredRequests.find( resourceId );
328     if( mStoredRequests.end() != iter ) // Can't assert here - cancel load may cross with load failed
329     {
330       mStoredRequests.erase( iter );
331     }
332   }
333 };
334
335 /********************************************************************************/
336 /****************************   RESOURCE LOADER METHODS  ************************/
337 /********************************************************************************/
338 ResourceLoader::ResourceLoader()
339 : mTerminateThread(0)
340 {
341   mImpl = new ResourceLoaderImpl( this );
342 }
343
344 ResourceLoader::~ResourceLoader()
345 {
346   // Flag that the ResourceLoader is exiting
347   (void)__sync_or_and_fetch( &mTerminateThread, -1 );
348
349   delete mImpl;
350 }
351
352 void ResourceLoader::Pause()
353 {
354   mImpl->Pause();
355 }
356
357 void ResourceLoader::Resume()
358 {
359   mImpl->Resume();
360 }
361
362 bool ResourceLoader::IsTerminating()
363 {
364   return __sync_fetch_and_or( &mTerminateThread, 0 );
365 }
366
367 void ResourceLoader::GetResources(ResourceCache& cache)
368 {
369   mImpl->GetResources( cache );
370 }
371
372 /********************************************************************************/
373 /**************************   CALLED FROM LOADER THREADS   **********************/
374 /********************************************************************************/
375
376 void ResourceLoader::AddPartiallyLoadedResource( LoadedResource& resource)
377 {
378   mImpl->AddPartiallyLoadedResource( resource );
379 }
380
381 void ResourceLoader::AddLoadedResource(LoadedResource& resource)
382 {
383   mImpl->AddLoadedResource( resource );
384 }
385
386 void ResourceLoader::AddSavedResource(SavedResource& resource)
387 {
388   mImpl->AddSavedResource( resource );
389 }
390
391 void ResourceLoader::AddFailedLoad(FailedResource& resource)
392 {
393   mImpl->AddFailedLoad( resource );
394 }
395
396 void ResourceLoader::AddFailedSave(FailedResource& resource)
397 {
398   mImpl->AddFailedSave( resource );
399 }
400
401 /********************************************************************************/
402 /*********************   CALLED FROM PLATFORM ABSTRACTION  **********************/
403 /********************************************************************************/
404
405 void ResourceLoader::LoadResource(const ResourceRequest& request)
406 {
407   mImpl->LoadResource(request);
408 }
409
410 void ResourceLoader::SaveResource(const ResourceRequest& request)
411 {
412   mImpl->SaveResource(request);
413 }
414
415 void ResourceLoader::CancelLoad(ResourceId id, ResourceTypeId typeId)
416 {
417   mImpl->CancelLoad(id, typeId);
418 }
419
420 bool ResourceLoader::IsLoading()
421 {
422   return mImpl->IsLoading();
423 }
424
425 void ResourceLoader::SetDpi(unsigned int dpiHor, unsigned int dpiVer)
426 {
427   // Unused
428 }
429
430 bool ResourceLoader::LoadFile( const std::string& filename, std::vector< unsigned char >& buffer ) const
431 {
432   DALI_LOG_TRACE_METHOD(gLoaderFilter);
433
434   DALI_ASSERT_DEBUG( 0 != filename.length());
435
436   bool result;
437
438   std::filebuf buf;
439   buf.open(filename.c_str(), std::ios::in | std::ios::binary);
440   if( buf.is_open() )
441   {
442     std::istream stream(&buf);
443
444     // determine data length
445     stream.seekg(0, std::ios_base::end);
446     unsigned int length = static_cast<unsigned int>( stream.tellg() );
447     stream.seekg(0, std::ios_base::beg);
448
449     // allocate a buffer
450     buffer.resize(length);
451     // read data into buffer
452     stream.read(reinterpret_cast<char*>(buffer.data()), length);
453
454     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader::LoadFile(%s) - loaded %d bytes\n", filename.c_str(), length);
455
456     result = true;
457   }
458   else
459   {
460     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader::LoadFile(%s) - failed to load\n", filename.c_str());
461     result = false;
462   }
463
464   return result;
465 }
466
467 std::string ResourceLoader::LoadFile(const std::string& filename) const
468 {
469   DALI_LOG_TRACE_METHOD(gLoaderFilter);
470
471   DALI_ASSERT_DEBUG( 0 != filename.length());
472
473   std::string contents;
474
475   std::filebuf buf;
476   buf.open(filename.c_str(), std::ios::in);
477   if( buf.is_open() )
478   {
479     std::istream stream(&buf);
480
481     // determine data length
482     stream.seekg(0, std::ios_base::end);
483     unsigned int length = static_cast<unsigned int>( stream.tellg() );
484     stream.seekg(0, std::ios_base::beg);
485
486     // allocate a buffer
487     contents.resize(length);
488     // read data into buffer
489     stream.read(&contents[0], length);
490
491     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader::LoadFile(%s) - loaded %d bytes\n", filename.c_str(), length);
492   }
493   else
494   {
495     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader::LoadFile(%s) - failed to load\n", filename.c_str());
496   }
497
498   return contents;
499 }
500
501 bool ResourceLoader::SaveFile(const std::string& filename, std::vector< unsigned char >& buffer)
502 {
503   DALI_LOG_TRACE_METHOD(gLoaderFilter);
504
505   DALI_ASSERT_DEBUG( 0 != filename.length());
506
507   bool result = false;
508
509   std::filebuf buf;
510   buf.open(filename.c_str(), std::ios::out | std::ios_base::trunc | std::ios::binary);
511   if( buf.is_open() )
512   {
513     std::ostream stream(&buf);
514
515     // determine size of buffer
516     int length = static_cast<int>(buffer.size());
517
518     // write contents of buffer to the file
519     stream.write(reinterpret_cast<char*>(buffer.data()), length);
520
521     if( !stream.bad() )
522     {
523       DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader::SaveFile(%s) - wrote %d bytes\n", filename.c_str(), length);
524       result = true;
525     }
526   }
527
528 #if defined(DEBUG_BUILD)
529   if( !result )
530   {
531     DALI_LOG_INFO(gLoaderFilter, Debug::Verbose, "ResourceLoader::SaveFile(%s) - failed to load\n", filename.c_str());
532   }
533 #endif
534
535   return result;
536 }
537
538 } // namespace TizenPlatform
539
540 } // namespace Dali