[dali_2.3.25] Merge branch 'devel/master'
[platform/core/uifw/dali-core.git] / dali / internal / render / renderers / uniform-buffer.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/internal/render/renderers/uniform-buffer.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/integration-api/debug.h>
23
24 #ifdef DEBUG_ENABLED
25 Debug::Filter* gUniformBufferLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_UNIFORM_BUFFER");
26 #endif
27
28 namespace Dali::Internal::Render
29 {
30 // GPU UBOs need to be double-buffered in order to avoid stalling the CPU during mapping/unmapping
31 constexpr uint32_t INTERNAL_UBO_BUFFER_COUNT = 2u;
32
33 Graphics::UniquePtr<UniformBufferV2> UniformBufferV2::New(Dali::Graphics::Controller* controller, bool emulated, uint32_t alignment)
34 {
35   return Graphics::UniquePtr<UniformBufferV2>(new UniformBufferV2(controller, emulated, alignment));
36 }
37
38 UniformBufferV2::UniformBufferV2(Dali::Graphics::Controller* controller, bool emulated, uint32_t alignment)
39 : mController(controller),
40   mBlockAlignment(alignment),
41   mCurrentGraphicsBufferIndex(0),
42   mEmulated(emulated)
43 {
44   mBufferList.resize(emulated ? 1 : INTERNAL_UBO_BUFFER_COUNT);
45   for(size_t i = 0; i < mBufferList.size(); ++i)
46   {
47     mBufferList[i].graphicsBuffer = nullptr;
48     mBufferList[i].graphicsMemory = nullptr;
49   }
50 }
51
52 void UniformBufferV2::ReSpecify(uint32_t sizeInBytes)
53 {
54   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Respec(%p) [%d] BufferType:%s  newSize:%d\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU", sizeInBytes);
55   if(mEmulated)
56   {
57     ReSpecifyCPU(sizeInBytes);
58   }
59   else
60   {
61     ReSpecifyGPU(sizeInBytes);
62   }
63 }
64
65 void UniformBufferV2::Write(const void* data, uint32_t size, uint32_t offset)
66 {
67   // Very verbose logging!
68   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::LogLevel(4), "Write(%p) [%d] BufferType:%s  offset:%d size:%d\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU", offset, size);
69   if(mEmulated)
70   {
71     WriteCPU(data, size, offset);
72   }
73   else
74   {
75     WriteGPU(data, size, offset);
76   }
77 }
78
79 void UniformBufferV2::Map()
80 {
81   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Map(%p) [%d] BufferType:%s\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU");
82   if(mEmulated)
83   {
84     MapCPU();
85   }
86   else
87   {
88     MapGPU();
89   }
90 }
91
92 void UniformBufferV2::Unmap()
93 {
94   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Unmap(%p) [%d] BufferType:%s\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU");
95   if(mEmulated)
96   {
97     UnmapCPU();
98   }
99   else
100   {
101     UnmapGPU();
102   }
103 }
104
105 void UniformBufferV2::Flush()
106 {
107   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Flush(%p) [%d] BufferType:%s\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU");
108
109   // Flush only for GPU buffertype by unmapping
110   if(!mEmulated && mMappedPtr)
111   {
112     auto& buffer = mBufferList[mCurrentGraphicsBufferIndex];
113     if(buffer.graphicsMemory)
114     {
115       UnmapGPU();
116       // flush range?
117     }
118
119     // Swap buffers for GPU UBOs
120     auto s                      = mBufferList.size();
121     mCurrentGraphicsBufferIndex = ((mCurrentGraphicsBufferIndex + 1) % s);
122     DALI_ASSERT_DEBUG(mCurrentGraphicsBufferIndex < mBufferList.size());
123   }
124 }
125
126 void UniformBufferV2::Rollback()
127 {
128   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Rollback(%p) [%d]\n", this, mCurrentGraphicsBufferIndex);
129   if(!mBufferList.empty())
130   {
131     mBufferList[mCurrentGraphicsBufferIndex].currentOffset = 0; // reset offset
132   }
133 }
134
135 uint32_t UniformBufferV2::AlignSize(uint32_t size)
136 {
137   if(size % mBlockAlignment != 0)
138   {
139     size = ((size / mBlockAlignment) + 1) * mBlockAlignment;
140   }
141   return size;
142 }
143
144 uint32_t UniformBufferV2::IncrementOffsetBy(uint32_t value)
145 {
146   if(mEmulated && !mBufferList.empty())
147   {
148     mBufferList[mCurrentGraphicsBufferIndex].currentOffset += value; // reset offset
149     return mBufferList[mCurrentGraphicsBufferIndex].currentOffset;
150   } // GPU
151   else if(!mBufferList.empty())
152   {
153     mBufferList[mCurrentGraphicsBufferIndex].currentOffset += value; // reset offset
154     return mBufferList[mCurrentGraphicsBufferIndex].currentOffset;
155   }
156   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Buffer should be allocated before incrementing offset\n");
157   return 0;
158 }
159
160 bool UniformBufferV2::MemoryCompare(void* data, uint32_t offset, uint32_t size)
161 {
162   return !memcmp(data, reinterpret_cast<uint8_t*>(mMappedPtr) + offset, size);
163 }
164
165 uint32_t UniformBufferV2::GetBlockAlignment() const
166 {
167   return mBlockAlignment;
168 }
169
170 uint32_t UniformBufferV2::GetCurrentOffset() const
171 {
172   if(!mBufferList.empty())
173   {
174     return mBufferList[mCurrentGraphicsBufferIndex].currentOffset;
175   }
176   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Buffer should be allocated before getting offset\n");
177   return 0;
178 }
179
180 uint32_t UniformBufferV2::GetCurrentCapacity() const
181 {
182   uint32_t capacity = 0;
183   if(!mBufferList.empty())
184   {
185     DALI_ASSERT_DEBUG(mBufferList.size() > mCurrentGraphicsBufferIndex);
186     capacity = mBufferList[mCurrentGraphicsBufferIndex].capacity;
187   }
188   return capacity;
189 }
190
191 Dali::Graphics::Buffer* UniformBufferV2::GetGraphicsBuffer() const
192 {
193   return mBufferList[mCurrentGraphicsBufferIndex].graphicsBuffer.get();
194 }
195
196 void UniformBufferV2::ReSpecifyCPU(uint32_t sizeInBytes)
197 {
198   GfxBuffer gfxBuffer;
199
200   uint32_t currentCapacity = GetCurrentCapacity();
201
202   if(sizeInBytes > currentCapacity)
203   {
204     Graphics::UniquePtr<Graphics::Buffer> oldBuffer{nullptr};
205
206     // If the CPU buffer already exist use it when applying respec
207     if(!mBufferList.empty())
208     {
209       mBufferList[0].graphicsMemory = nullptr;                                  // Discard mapped memory if exists
210       oldBuffer                     = std::move(mBufferList[0].graphicsBuffer); // Store old buffer for re-using
211     }
212     Graphics::BufferPropertiesFlags flags = 0u | Graphics::BufferPropertiesFlagBit::CPU_ALLOCATED;
213
214     auto createInfo = Graphics::BufferCreateInfo()
215                         .SetSize(sizeInBytes)
216                         .SetBufferPropertiesFlags(flags)
217                         .SetUsage(0u | Graphics::BufferUsage::UNIFORM_BUFFER);
218
219     gfxBuffer.graphicsBuffer = mController->CreateBuffer(createInfo, std::move(oldBuffer));
220     gfxBuffer.capacity       = sizeInBytes;
221     gfxBuffer.currentOffset  = 0;
222
223     mBufferList[0] = std::move(gfxBuffer);
224     // make sure buffer is created (move creation to run in parallel in the backed
225     // as this may be a major slowdown)
226     mController->WaitIdle();
227   }
228
229   mMappedPtr = nullptr;
230
231   if(sizeInBytes)
232   {
233     // After respecifying the buffer, we can map it persistently as it already exists
234     // in the CPU memory
235     MapCPU();
236   }
237 }
238
239 void UniformBufferV2::ReSpecifyGPU(uint32_t sizeInBytes)
240 {
241   uint32_t currentCapacity = GetCurrentCapacity();
242
243   if(sizeInBytes > currentCapacity)
244   {
245     GfxBuffer                             gfxBuffer;
246     Graphics::UniquePtr<Graphics::Buffer> oldBuffer{nullptr};
247
248     // If the GPU buffer already exist use it when applying respec
249     if(!mBufferList.empty())
250     {
251       mBufferList[mCurrentGraphicsBufferIndex].graphicsMemory = nullptr;                                                            // Discard mapped memory if exists
252       oldBuffer                                               = std::move(mBufferList[mCurrentGraphicsBufferIndex].graphicsBuffer); // Store old buffer for re-using
253     }
254     Graphics::BufferPropertiesFlags flags = 0u;
255
256     auto createInfo = Graphics::BufferCreateInfo()
257                         .SetSize(sizeInBytes)
258                         .SetBufferPropertiesFlags(flags)
259                         .SetUsage(0u | Graphics::BufferUsage::UNIFORM_BUFFER);
260
261     gfxBuffer.graphicsBuffer = mController->CreateBuffer(createInfo, std::move(oldBuffer));
262     gfxBuffer.capacity       = sizeInBytes;
263     gfxBuffer.currentOffset  = 0;
264
265     mBufferList[mCurrentGraphicsBufferIndex] = std::move(gfxBuffer);
266     // make sure buffer is created (move creation to run in parallel in the backend
267     // as this may be a major slowdown)
268     mController->WaitIdle();
269     DALI_ASSERT_ALWAYS(mBufferList[mCurrentGraphicsBufferIndex].capacity == sizeInBytes && "std::move failed");
270   }
271
272   mMappedPtr = nullptr;
273
274   if(sizeInBytes)
275   {
276     MapGPU(); // Note, this will flush the creation buffer queues in the backend and initialize buffer.
277   }
278 }
279
280 void UniformBufferV2::WriteCPU(const void* data, uint32_t size, uint32_t offset)
281 {
282   // If not mapped
283   if(!mMappedPtr)
284   {
285     DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Warning: CPU buffer should already be mapped!\n");
286     MapCPU();
287   }
288
289   DALI_ASSERT_DEBUG(offset + size <= mBufferList[mCurrentGraphicsBufferIndex].capacity);
290
291   // just copy whatever comes here
292   memcpy(reinterpret_cast<uint8_t*>(mMappedPtr) + offset, data, size);
293 }
294
295 void UniformBufferV2::WriteGPU(const void* data, uint32_t size, uint32_t offset)
296 {
297   if(!mMappedPtr)
298   {
299     DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Warning: GPU buffer should already be mapped!\n");
300     MapGPU();
301   }
302
303   DALI_ASSERT_DEBUG(offset + size <= mBufferList[mCurrentGraphicsBufferIndex].capacity);
304
305   memcpy(reinterpret_cast<uint8_t*>(mMappedPtr) + offset, data, size);
306 }
307
308 void UniformBufferV2::MapCPU()
309 {
310   auto& buffer = mBufferList[0]; // CPU contains always one buffer
311   if(!buffer.graphicsMemory)
312   {
313     Graphics::MapBufferInfo info{};
314     info.buffer           = buffer.graphicsBuffer.get();
315     info.usage            = 0 | Graphics::MemoryUsageFlagBits::WRITE;
316     info.offset           = 0;
317     info.size             = buffer.capacity;
318     buffer.graphicsMemory = mController->MapBufferRange(info);
319   }
320
321   // obtain pointer instantly
322   if(buffer.graphicsMemory)
323   {
324     mMappedPtr = buffer.graphicsMemory->LockRegion(0, buffer.capacity);
325     DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "CPU buffer is mapped to %p\n", mMappedPtr);
326   }
327 }
328
329 void UniformBufferV2::MapGPU()
330 {
331   auto& buffer = mBufferList[mCurrentGraphicsBufferIndex];
332   if(!buffer.graphicsMemory)
333   {
334     Graphics::MapBufferInfo info{};
335     info.buffer           = buffer.graphicsBuffer.get();
336     info.usage            = 0 | Graphics::MemoryUsageFlagBits::WRITE;
337     info.offset           = 0;
338     info.size             = buffer.capacity;
339     buffer.graphicsMemory = mController->MapBufferRange(info);
340   }
341
342   // obtain pointer instantly
343   if(buffer.graphicsMemory)
344   {
345     mMappedPtr = buffer.graphicsMemory->LockRegion(0, buffer.capacity);
346     DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GPU buffer is mapped to %p\n", mMappedPtr);
347   }
348 }
349
350 void UniformBufferV2::UnmapCPU()
351 {
352   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "CPU buffer is unmapped\n");
353 }
354
355 void UniformBufferV2::UnmapGPU()
356 {
357   auto& buffer = mBufferList[mCurrentGraphicsBufferIndex];
358   if(buffer.graphicsMemory)
359   {
360     mController->UnmapMemory(std::move(buffer.graphicsMemory));
361     buffer.graphicsMemory = nullptr;
362   }
363   mMappedPtr = nullptr;
364   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GPU buffer is unmapped\n");
365 }
366
367 } // namespace Dali::Internal::Render