[Tizen] TextureUploadManager implement
[platform/core/uifw/dali-adaptor.git] / dali / internal / system / common / texture-upload-manager-impl.cpp
1 /*
2  * Copyright (c) 2023 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/internal/system/common/texture-upload-manager-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/common/singleton-service.h>
23 #include <dali/graphics-api/graphics-texture-upload-helper.h> ///< for Dali::Graphics::ConvertPixelFormat
24 #include <dali/integration-api/adaptor-framework/adaptor.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/integration-api/pixel-data-integ.h>
27 #include <dali/integration-api/texture-integ.h>
28 #include <dali/integration-api/trace.h>
29
30 // INTERNAL INCLUDES
31 #include <dali/internal/adaptor/common/adaptor-impl.h>
32
33 namespace Dali
34 {
35 namespace Internal
36 {
37 namespace Adaptor
38 {
39 namespace
40 {
41 Dali::Devel::TextureUploadManager::ResourceId gUniqueResourceId = 0;
42
43 DALI_INIT_TRACE_FILTER(gTraceFilter, DALI_TRACE_PERFORMANCE_MARKER, false);
44
45 #if defined(DEBUG_ENABLED)
46 Debug::Filter* gTextureUploadManagerLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_TEXTURE_UPLOAD_MANAGER");
47 #endif
48
49 } // unnamed namespace
50
51 // Called by main thread
52
53 Dali::Devel::TextureUploadManager TextureUploadManager::Get()
54 {
55   Dali::Devel::TextureUploadManager manager;
56   SingletonService                  singletonService(SingletonService::Get());
57   if(singletonService)
58   {
59     // Check whether the texture upload manager is already created
60     Dali::BaseHandle handle = singletonService.GetSingleton(typeid(Dali::Devel::TextureUploadManager));
61     if(handle)
62     {
63       // If so, downcast the handle of singleton
64       manager = Dali::Devel::TextureUploadManager(dynamic_cast<Internal::Adaptor::TextureUploadManager*>(handle.GetObjectPtr()));
65     }
66
67     if(!manager)
68     {
69       // If not, create the texture upload manager and register it as a singleton
70       Internal::Adaptor::TextureUploadManager* internalTextureUploadManager = new Internal::Adaptor::TextureUploadManager();
71       manager                                                               = Dali::Devel::TextureUploadManager(internalTextureUploadManager);
72       singletonService.Register(typeid(manager), manager);
73     }
74   }
75   return manager;
76 }
77
78 TextureUploadManager::TextureUploadManager()
79 : mGraphicsController{nullptr},
80   mRenderTrigger(new EventThreadCallback(MakeCallback(this, &TextureUploadManager::RequestUpdateOnce)))
81 {
82 }
83
84 TextureUploadManager::~TextureUploadManager()
85 {
86 }
87
88 Dali::Texture TextureUploadManager::GenerateTexture2D()
89 {
90   ResourceId resourceId = GenerateUploadResourceId();
91
92   Dali::Texture ret = Dali::Integration::NewTextureWithResourceId(Dali::TextureType::TEXTURE_2D, resourceId);
93
94   return ret;
95 }
96
97 Dali::Devel::TextureUploadManager::ResourceId TextureUploadManager::GenerateUploadResourceId()
98 {
99   auto id = ++gUniqueResourceId;
100
101   // Jump overflow case so we can assume that resource id always valid.
102   if(DALI_UNLIKELY(gUniqueResourceId == Dali::Devel::TextureUploadManager::INVALID_RESOURCE_ID))
103   {
104     ++gUniqueResourceId;
105   }
106
107   return id;
108 }
109
110 void TextureUploadManager::RequestUpdateOnce()
111 {
112   if(Dali::Adaptor::IsAvailable())
113   {
114     DALI_LOG_INFO(gTextureUploadManagerLogFilter, Debug::Concise, "RenderOnce requested\n");
115     Dali::Adaptor::Get().RenderOnce();
116   }
117 }
118
119 // Called by update thread
120
121 bool TextureUploadManager::ResourceUpload()
122 {
123   DALI_ASSERT_DEBUG(mGraphicsController && "GraphicsController is not prepared!");
124
125   // Copy queue first.
126   RequestUploadQueue copiedRequestUploadQueue;
127
128   {
129     Dali::Mutex::ScopedLock lock(mRequestMutex);
130     copiedRequestUploadQueue = std::move(mRequestUploadQueue);
131   }
132
133   // Upload.
134   bool uploaded = ProcessUploadQueue(std::move(copiedRequestUploadQueue));
135
136   return uploaded;
137 }
138
139 void TextureUploadManager::InitalizeGraphicsController(Dali::Graphics::Controller& graphicsController)
140 {
141   mGraphicsController = &graphicsController;
142 }
143
144 bool TextureUploadManager::ProcessUploadQueue(RequestUploadQueue&& queue)
145 {
146   bool uploaded = false;
147
148   if(!queue.empty())
149   {
150 #ifdef TRACE_ENABLED
151     if(gTraceFilter && gTraceFilter->IsTraceEnabled())
152     {
153       std::ostringstream stream;
154       stream << "[upload request \'" << queue.size() << "\' images]";
155       DALI_TRACE_BEGIN_WITH_MESSAGE(gTraceFilter, "DALI_WORKER_THREAD_RESOURCE_UPLOAD", stream.str().c_str());
156     }
157     uint32_t uploadedCount = 0u;
158     uint32_t canceledCount = 0u;
159 #endif
160
161     DALI_LOG_INFO(gTextureUploadManagerLogFilter, Debug::Concise, "Upload request %zu images\n", queue.size());
162     for(auto& requests : queue)
163     {
164       auto& resourceId = requests.first;
165       auto& pixelData  = requests.second;
166
167       Graphics::Texture* graphicsTexture = nullptr;
168
169       // TODO : Could we detect TEXTURE_2D or TEXTURE_CUBE case in future?
170       {
171         // We always need to create new one
172         auto createInfo = Graphics::TextureCreateInfo();
173         createInfo
174           .SetTextureType(Dali::Graphics::ConvertTextureType(Dali::TextureType::TEXTURE_2D))
175           .SetUsageFlags(static_cast<Graphics::TextureUsageFlags>(Graphics::TextureUsageFlagBits::SAMPLE))
176           .SetFormat(Dali::Graphics::ConvertPixelFormat(pixelData.GetPixelFormat()))
177           .SetSize({pixelData.GetWidth(), pixelData.GetHeight()})
178           .SetLayout(Graphics::TextureLayout::LINEAR)
179           .SetData(nullptr)
180           .SetDataSize(0u)
181           .SetNativeImage(nullptr)
182           .SetMipMapFlag(Graphics::TextureMipMapFlag::DISABLED);
183
184         graphicsTexture = mGraphicsController->CreateTextureByResourceId(resourceId, createInfo);
185       }
186
187       if(graphicsTexture)
188       {
189         Graphics::TextureUpdateInfo info{};
190
191         info.dstTexture   = graphicsTexture;
192         info.dstOffset2D  = {0u, 0u};
193         info.layer        = 0u;
194         info.level        = 0u;
195         info.srcReference = 0;
196         info.srcExtent2D  = {pixelData.GetWidth(), pixelData.GetHeight()};
197         info.srcOffset    = 0;
198         info.srcSize      = Dali::Integration::GetPixelDataBuffer(pixelData).bufferSize;
199         info.srcStride    = pixelData.GetStride();
200         info.srcFormat    = Dali::Graphics::ConvertPixelFormat(pixelData.GetPixelFormat());
201
202         Graphics::TextureUpdateSourceInfo updateSourceInfo{};
203         updateSourceInfo.sourceType                = Graphics::TextureUpdateSourceInfo::Type::PIXEL_DATA;
204         updateSourceInfo.pixelDataSource.pixelData = pixelData;
205
206         mGraphicsController->UpdateTextures({info}, {updateSourceInfo});
207
208         uploaded = true;
209 #ifdef TRACE_ENABLED
210         ++uploadedCount;
211 #endif
212       }
213       else
214       {
215         // Invalidate resouce id! ignore.
216 #ifdef TRACE_ENABLED
217         ++canceledCount;
218 #endif
219       }
220     }
221
222     if(uploaded)
223     {
224       // Flush here
225       Graphics::SubmitInfo submitInfo;
226       submitInfo.cmdBuffer.clear(); // Only flush
227       submitInfo.flags = 0 | Graphics::SubmitFlagBits::FLUSH;
228       mGraphicsController->SubmitCommandBuffers(submitInfo);
229     }
230 #ifdef TRACE_ENABLED
231     if(gTraceFilter && gTraceFilter->IsTraceEnabled())
232     {
233       std::ostringstream stream;
234       stream << "[upload : \'" << uploadedCount << "\', cancel : \'" << canceledCount << "\']";
235       DALI_TRACE_END_WITH_MESSAGE(gTraceFilter, "DALI_WORKER_THREAD_RESOURCE_UPLOAD", stream.str().c_str());
236     }
237 #endif
238   }
239
240   return uploaded;
241 }
242
243 // Called by worker thread
244
245 void TextureUploadManager::RequestUpload(Dali::Devel::TextureUploadManager::ResourceId resourceId, Dali::PixelData pixelData)
246 {
247   DALI_ASSERT_ALWAYS(resourceId != Dali::Devel::TextureUploadManager::INVALID_RESOURCE_ID && "Invalid resource id generated!");
248   DALI_ASSERT_ALWAYS(pixelData && "Invalid pixelData!");
249
250   {
251     Dali::Mutex::ScopedLock lock(mRequestMutex); // Worker-Update thread mutex
252
253     mRequestUploadQueue.push_back(std::move(UploadRequestItem(resourceId, pixelData)));
254   }
255
256   // wake up the main thread
257   // TODO : Is there any way to request upload once without main thread dependency?
258   mRenderTrigger->Trigger();
259 }
260
261 } // namespace Adaptor
262
263 } // namespace Internal
264
265 } // namespace Dali