Merge "TextureUploadManager implement" into devel/master
[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 #endif
159
160     DALI_LOG_INFO(gTextureUploadManagerLogFilter, Debug::Concise, "Upload request %zu images\n", queue.size());
161     for(auto& requests : queue)
162     {
163       auto& resourceId = requests.first;
164       auto& pixelData  = requests.second;
165
166       Graphics::Texture* graphicsTexture = nullptr;
167
168       {
169         // We always need to create new one
170         auto createInfo = Graphics::TextureCreateInfo();
171         createInfo
172           .SetTextureType(Dali::Graphics::ConvertTextureType(Dali::TextureType::TEXTURE_2D))
173           .SetUsageFlags(static_cast<Graphics::TextureUsageFlags>(Graphics::TextureUsageFlagBits::SAMPLE))
174           .SetFormat(Dali::Graphics::ConvertPixelFormat(pixelData.GetPixelFormat()))
175           .SetSize({pixelData.GetWidth(), pixelData.GetHeight()})
176           .SetLayout(Graphics::TextureLayout::LINEAR)
177           .SetAllocationPolicy(Graphics::TextureAllocationPolicy::UPLOAD)
178           .SetData(nullptr)
179           .SetDataSize(0u)
180           .SetNativeImage(nullptr)
181           .SetMipMapFlag(Graphics::TextureMipMapFlag::DISABLED);
182
183         graphicsTexture = mGraphicsController->CreateTextureByResourceId(resourceId, createInfo);
184       }
185
186       if(graphicsTexture)
187       {
188         Graphics::TextureUpdateInfo info{};
189
190         info.dstTexture   = graphicsTexture;
191         info.dstOffset2D  = {0u, 0u};
192         info.layer        = 0u;
193         info.level        = 0u;
194         info.srcReference = 0;
195         info.srcExtent2D  = {pixelData.GetWidth(), pixelData.GetHeight()};
196         info.srcOffset    = 0;
197         info.srcSize      = Dali::Integration::GetPixelDataBuffer(pixelData).bufferSize;
198         info.srcStride    = pixelData.GetStride();
199         info.srcFormat    = Dali::Graphics::ConvertPixelFormat(pixelData.GetPixelFormat());
200
201         Graphics::TextureUpdateSourceInfo updateSourceInfo{};
202         updateSourceInfo.sourceType                = Graphics::TextureUpdateSourceInfo::Type::PIXEL_DATA;
203         updateSourceInfo.pixelDataSource.pixelData = pixelData;
204
205         mGraphicsController->UpdateTextures({info}, {updateSourceInfo});
206
207         uploaded = true;
208 #ifdef TRACE_ENABLED
209         ++uploadedCount;
210 #endif
211       }
212     }
213
214     if(uploaded)
215     {
216       // Flush here
217       Graphics::SubmitInfo submitInfo;
218       submitInfo.cmdBuffer.clear(); // Only flush
219       submitInfo.flags = 0 | Graphics::SubmitFlagBits::FLUSH;
220       mGraphicsController->SubmitCommandBuffers(submitInfo);
221     }
222 #ifdef TRACE_ENABLED
223     if(gTraceFilter && gTraceFilter->IsTraceEnabled())
224     {
225       std::ostringstream stream;
226       stream << "[uploaded : \'" << uploadedCount << "\']";
227       DALI_TRACE_END_WITH_MESSAGE(gTraceFilter, "DALI_WORKER_THREAD_RESOURCE_UPLOAD", stream.str().c_str());
228     }
229 #endif
230   }
231
232   return uploaded;
233 }
234
235 // Called by worker thread
236
237 void TextureUploadManager::RequestUpload(Dali::Devel::TextureUploadManager::ResourceId resourceId, Dali::PixelData pixelData)
238 {
239   DALI_ASSERT_ALWAYS(resourceId != Dali::Devel::TextureUploadManager::INVALID_RESOURCE_ID && "Invalid resource id generated!");
240   DALI_ASSERT_ALWAYS(pixelData && "Invalid pixelData!");
241
242   {
243     Dali::Mutex::ScopedLock lock(mRequestMutex); // Worker-Update thread mutex
244
245     mRequestUploadQueue.push_back(std::move(UploadRequestItem(resourceId, pixelData)));
246   }
247
248   // wake up the main thread
249   // TODO : Is there any way to request upload once without main thread dependency?
250   mRenderTrigger->Trigger();
251 }
252
253 } // namespace Adaptor
254
255 } // namespace Internal
256
257 } // namespace Dali