[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / image-loader / fast-track-loading-task.cpp
1 /*
2  * Copyright (c) 2024 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 <dali-toolkit/internal/image-loader/fast-track-loading-task.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/adaptor-framework/image-loading.h>
23 #include <dali/devel-api/adaptor-framework/texture-upload-manager.h>
24 #include <dali/integration-api/debug.h>
25 #include <dali/integration-api/texture-integ.h>
26 #include <dali/integration-api/trace.h>
27 #include <dali/public-api/common/vector-wrapper.h>
28
29 #ifdef TRACE_ENABLED
30 #include <chrono>
31 #include <iomanip>
32 #include <sstream>
33 #include <thread>
34 #endif
35
36 namespace Dali
37 {
38 namespace Toolkit
39 {
40 namespace Internal
41 {
42 namespace
43 {
44 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_IMAGE_PERFORMANCE_MARKER, false);
45
46 #ifdef TRACE_ENABLED
47 uint64_t GetNanoseconds()
48 {
49   // Get the time of a monotonic clock since its epoch.
50   auto epoch    = std::chrono::steady_clock::now().time_since_epoch();
51   auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(epoch);
52   return static_cast<uint64_t>(duration.count());
53 }
54 #endif
55
56 constexpr uint32_t CHROMINANCE_U_INDEX = 1u;
57 constexpr uint32_t CHROMINANCE_V_INDEX = 2u;
58
59 Dali::PixelData GetDummyChrominanceUPixelData()
60 {
61   static Dali::PixelData pixelDataU = PixelData::New(new uint8_t[2]{0x00, 0x00}, 2, 1, 2, Pixel::L8, PixelData::DELETE_ARRAY);
62   return pixelDataU;
63 }
64
65 Dali::PixelData GetDummyChrominanceVPixelData()
66 {
67   static Dali::PixelData pixelDataV = PixelData::New(new uint8_t[2]{0x00, 0x00}, 2, 2, 1, Pixel::L8, PixelData::DELETE_ARRAY);
68   return pixelDataV;
69 }
70
71 } // namespace
72
73 FastTrackLoadingTask::FastTrackLoadingTask(const VisualUrl& url, ImageDimensions dimensions, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad preMultiplyOnLoad, bool loadPlanes, CallbackBase* callback)
74 : AsyncTask(MakeCallback(this, &FastTrackLoadingTask::OnComplete), url.GetProtocolType() == VisualUrl::ProtocolType::REMOTE ? AsyncTask::PriorityType::LOW : AsyncTask::PriorityType::HIGH),
75   mUrl(url),
76   mTextures(),
77   mDimensions(dimensions),
78   mFittingMode(fittingMode),
79   mSamplingMode(samplingMode),
80   mPreMultiplyOnLoad(preMultiplyOnLoad),
81   mCallback(),
82   mTextureUploadManager(Dali::Devel::TextureUploadManager::Get()),
83   mImageInformations(),
84   mPixelData(),
85   mOrientationCorrection(orientationCorrection),
86   mLoadSuccess(false),
87   mLoadPlanesAvaliable(loadPlanes),
88   mPremultiplied(false),
89   mPlanesLoaded(false)
90 {
91   mCallback = std::unique_ptr<CallbackBase>(callback);
92   PrepareTexture();
93 }
94
95 FastTrackLoadingTask::~FastTrackLoadingTask()
96 {
97 }
98
99 void FastTrackLoadingTask::PrepareTexture()
100 {
101   const uint32_t requiredTexturesCount = mLoadPlanesAvaliable ? 3u : 1u;
102
103   mTextures.resize(requiredTexturesCount);
104   mImageInformations.resize(requiredTexturesCount);
105   for(uint32_t index = 0u; index < requiredTexturesCount; ++index)
106   {
107     mTextures[index] = mTextureUploadManager.GenerateTexture2D();
108
109     mImageInformations[index].resourceId = Integration::GetTextureResourceId(mTextures[index]);
110   }
111
112   if(mLoadPlanesAvaliable)
113   {
114     // Create static dummy chrominance pixel data now, for thread safety.
115     [[maybe_unused]] auto pixelDataU = GetDummyChrominanceUPixelData();
116     [[maybe_unused]] auto pixelDataV = GetDummyChrominanceVPixelData();
117   }
118 }
119
120 void FastTrackLoadingTask::OnComplete(AsyncTaskPtr task)
121 {
122   if(mLoadSuccess)
123   {
124     for(uint32_t index = 0u; index < mImageInformations.size(); ++index)
125     {
126       Dali::Integration::SetTextureSize(mTextures[index], Dali::ImageDimensions(mImageInformations[index].width, mImageInformations[index].height));
127       Dali::Integration::SetTexturePixelFormat(mTextures[index], mImageInformations[index].format);
128     }
129     if(mLoadPlanesAvaliable && !mPlanesLoaded)
130     {
131       // We will not use ChrominanceU and ChrominanceV texture anymore.
132       mTextures.resize(1u);
133     }
134   }
135   else
136   {
137     mTextures.clear();
138   }
139
140   if(mCallback)
141   {
142     CallbackBase::Execute(*mCallback, FastTrackLoadingTaskPtr(reinterpret_cast<FastTrackLoadingTask*>(task.Get())));
143   }
144 }
145
146 // Called by worker thread
147
148 void FastTrackLoadingTask::Process()
149 {
150   Load();
151   UploadToTexture();
152 }
153
154 bool FastTrackLoadingTask::IsReady()
155 {
156   return true;
157 }
158
159 void FastTrackLoadingTask::Load()
160 {
161 #ifdef TRACE_ENABLED
162   uint64_t mStartTimeNanoSceonds = 0;
163   uint64_t mEndTimeNanoSceonds   = 0;
164   if(gTraceFilter && gTraceFilter->IsTraceEnabled())
165   {
166     mStartTimeNanoSceonds = GetNanoseconds();
167     std::ostringstream oss;
168     oss << "[u:" << mUrl.GetEllipsedUrl() << "]";
169     // DALI_TRACE_BEGIN(gTraceFilter, "DALI_IMAGE_FAST_TRACK_UPLOADING_TASK"); ///< TODO : Open it if we can control trace log level
170     DALI_LOG_RELEASE_INFO("BEGIN: DALI_IMAGE_FAST_TRACK_UPLOADING_TASK %s", oss.str().c_str());
171   }
172 #endif
173
174   Devel::PixelBuffer              pixelBuffer;
175   std::vector<Devel::PixelBuffer> pixelBuffers;
176
177   if(mUrl.IsValid() && mUrl.IsLocalResource())
178   {
179     if(mLoadPlanesAvaliable)
180     {
181       Dali::LoadImagePlanesFromFile(mUrl.GetUrl(), pixelBuffers, mDimensions, mFittingMode, mSamplingMode, mOrientationCorrection);
182     }
183     else
184     {
185       pixelBuffer = Dali::LoadImageFromFile(mUrl.GetUrl(), mDimensions, mFittingMode, mSamplingMode, mOrientationCorrection);
186     }
187   }
188   else if(mUrl.IsValid())
189   {
190     pixelBuffer = Dali::DownloadImageSynchronously(mUrl.GetUrl(), mDimensions, mFittingMode, mSamplingMode, mOrientationCorrection);
191   }
192
193   if(pixelBuffer)
194   {
195     pixelBuffers.emplace_back(std::move(pixelBuffer));
196   }
197
198   if(pixelBuffers.empty())
199   {
200     mLoadSuccess = false;
201     DALI_LOG_ERROR("FastTrackLoadingTask::Load: Loading is failed: ResourceId : %d url : [%s]\n", mImageInformations[0u].resourceId, mUrl.GetUrl().c_str());
202   }
203   else
204   {
205     mPixelData.resize(pixelBuffers.size());
206
207     mLoadSuccess = true;
208     MultiplyAlpha(pixelBuffers[0]);
209     uint32_t index = 0u;
210     for(auto&& pixelBuffer : pixelBuffers)
211     {
212       mPixelData[index++] = Dali::Devel::PixelBuffer::Convert(pixelBuffer);
213     }
214
215     if(pixelBuffers.size() > 1u)
216     {
217       mPlanesLoaded = true;
218     }
219     else if(mLoadPlanesAvaliable && pixelBuffers.size() == 1u && mTextures.size() == 3u) ///< Case when we prepare three textures to render YUV, but loaded image is not YUV.
220     {
221       // Dummy pixel data for fake shader that we don't use actual YUV format.
222       // To fake shader, let we use indivisual sizes of texture for U and V.
223       mPixelData.resize(3u);
224       mPixelData[CHROMINANCE_U_INDEX] = GetDummyChrominanceUPixelData();
225       mPixelData[CHROMINANCE_V_INDEX] = GetDummyChrominanceVPixelData();
226     }
227
228     if(DALI_UNLIKELY(mPixelData.size() != mImageInformations.size()))
229     {
230       DALI_LOG_ERROR("FastTrackLoadingTask::Load: Undefined case. pixelBuffers.size() : %zu, image size : %zu, ResourceId : %d, url : [%s]\n", pixelBuffers.size(), mImageInformations.size(), mImageInformations[0u].resourceId, mUrl.GetUrl().c_str());
231       mLoadSuccess = false;
232     }
233   }
234
235 #ifdef TRACE_ENABLED
236   if(gTraceFilter && gTraceFilter->IsTraceEnabled())
237   {
238     mEndTimeNanoSceonds = GetNanoseconds();
239     std::ostringstream oss;
240     oss << std::fixed << std::setprecision(3);
241     oss << "[";
242     oss << "d:" << static_cast<float>(mEndTimeNanoSceonds - mStartTimeNanoSceonds) / 1000000.0f << "ms ";
243     oss << "b:" << pixelBuffers.size() << " ";
244     if(!mPixelData.empty())
245     {
246       oss << "s:" << mPixelData[0].GetWidth() << "x" << mPixelData[0].GetHeight() << " ";
247       oss << "p:" << mPremultiplied << " ";
248     }
249     oss << "u:" << mUrl.GetEllipsedUrl() << "]";
250     // DALI_TRACE_END(gTraceFilter, "DALI_IMAGE_FAST_TRACK_UPLOADING_TASK"); ///< TODO : Open it if we can control trace log level
251     DALI_LOG_RELEASE_INFO("END: DALI_IMAGE_FAST_TRACK_UPLOADING_TASK %s", oss.str().c_str());
252   }
253 #endif
254 }
255
256 void FastTrackLoadingTask::MultiplyAlpha(Dali::Devel::PixelBuffer pixelBuffer)
257 {
258   if(mPreMultiplyOnLoad == DevelAsyncImageLoader::PreMultiplyOnLoad::ON && Pixel::HasAlpha(pixelBuffer.GetPixelFormat()))
259   {
260     pixelBuffer.MultiplyColorByAlpha();
261     mPremultiplied = pixelBuffer.IsAlphaPreMultiplied();
262   }
263 }
264
265 void FastTrackLoadingTask::UploadToTexture()
266 {
267   if(mLoadSuccess)
268   {
269     DALI_ASSERT_DEBUG(mPixelData.size() == mImageInformations.size());
270
271     uint32_t index = 0u;
272     for(auto&& pixelData : mPixelData)
273     {
274       mImageInformations[index].width  = pixelData.GetWidth();
275       mImageInformations[index].height = pixelData.GetHeight();
276       mImageInformations[index].format = pixelData.GetPixelFormat();
277
278       mTextureUploadManager.RequestUpload(mImageInformations[index].resourceId, pixelData);
279
280       pixelData.Reset();
281
282       ++index;
283     }
284   }
285
286   mPixelData.clear();
287 }
288
289 } // namespace Internal
290
291 } // namespace Toolkit
292
293 } // namespace Dali