Lock uniform buffer only 1 times per each render + minor fixup of uniforms
[platform/core/uifw/dali-core.git] / dali / internal / render / renderers / uniform-buffer.cpp
1 /*
2  * Copyright (c) 2022 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 namespace
31 {
32 static constexpr uint32_t INVALID_BUFFER_INDEX = std::numeric_limits<uint32_t>::max();
33 }
34 UniformBuffer::UniformBuffer(Dali::Graphics::Controller*     controller,
35                              uint32_t                        sizeInBytes,
36                              uint32_t                        alignment,
37                              Graphics::BufferUsageFlags      usageFlags,
38                              Graphics::BufferPropertiesFlags propertiesFlags)
39 : mController(controller),
40   mSize(0u),
41   mUsageFlags(usageFlags),
42   mPropertiesFlags(propertiesFlags),
43   mLockedBufferIndex(INVALID_BUFFER_INDEX),
44   mLockedPtr(nullptr),
45   mReadyToBeLocked(false)
46 {
47   mAlignment = alignment;
48   if(sizeInBytes)
49   {
50     Resize(sizeInBytes, true);
51   }
52 }
53
54 UniformBuffer::~UniformBuffer()
55 {
56   // Unmap and flush all allocated buffers
57   for(auto i = 0u; i < mBuffers.size(); ++i)
58   {
59     Flush(i);
60     Unmap(i);
61   }
62 }
63
64 void UniformBuffer::Flush(uint32_t bufferIndex)
65 {
66   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Flush (bufferIndex : %d / %d [%d])\n", bufferIndex, mBuffers.size(), mSize);
67   const auto& buffer = mBuffers[bufferIndex];
68   if(buffer.buffer && buffer.memory)
69   {
70     buffer.memory->Flush();
71   }
72 }
73
74 void UniformBuffer::Resize(uint32_t newSize, bool invalidate)
75 {
76   // Adjust alignment, the alignment is needed for
77   // real UBOs (it should be given by the buffer requirements)
78   if(DALI_LIKELY(mAlignment && newSize > 0))
79   {
80     newSize = (((newSize - 1) / mAlignment) + 1) * mAlignment;
81   }
82
83   // The buffer is already optimal
84   if(newSize == mSize && !invalidate)
85   {
86     return;
87   }
88   if(invalidate && newSize == mSize && mBuffers.size() == 1)
89   {
90     return;
91   }
92
93   if(DALI_UNLIKELY(mReadyToBeLocked))
94   {
95     DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize %d --> %d with %s [mBuffers : %d] during lock (lockedBufferIndex : %d, lockedPtr : %p)\n", mSize, newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size(), mLockedBufferIndex, mLockedPtr);
96   }
97   else
98   {
99     DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize %d --> %d with %s [mBuffers : %d]\n", mSize, newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size());
100   }
101
102   // Throw away content
103   if(invalidate)
104   {
105     if(mReadyToBeLocked)
106     {
107       UnlockUniformBuffer();
108       mReadyToBeLocked = true;
109     }
110     // Flush and unmap all allocated buffers
111     for(auto i = 0u; i < mBuffers.size(); ++i)
112     {
113       Flush(i);
114       Unmap(i);
115     }
116     mBuffers.clear();
117     mSize = 0;
118   }
119
120   if(newSize > mSize)
121   {
122     auto createInfo = Graphics::BufferCreateInfo()
123                         .SetSize(newSize - mSize)
124                         .SetBufferPropertiesFlags(mPropertiesFlags)
125                         .SetUsage(mUsageFlags);
126
127     auto buffer = mController->CreateBuffer(createInfo, nullptr);
128
129     mBuffers.emplace_back(GfxBuffer(std::move(buffer), createInfo));
130
131     mSize = newSize;
132   }
133
134   // If invalidate during locked, begin lock again.
135   if(DALI_UNLIKELY(invalidate && mReadyToBeLocked))
136   {
137     mReadyToBeLocked = false;
138     ReadyToLockUniformBuffer();
139   }
140
141   if(DALI_UNLIKELY(mReadyToBeLocked))
142   {
143     DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize done as %d with %s [mBuffers : %d] during lock (lockedBufferIndex : %d, lockedPtr : %p)\n", newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size(), mLockedBufferIndex, mLockedPtr);
144   }
145   else
146   {
147     DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize done as %d with %s [mBuffers : %d]\n", newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size());
148   }
149 }
150
151 const UniformBuffer::GfxBuffer* UniformBuffer::GetBufferByOffset(uint32_t offset, uint32_t* newOffset, uint32_t* outBufferIndex) const
152 {
153   uint32_t bufferOffset = offset;
154   uint32_t bufferIndex  = 0u;
155
156   // Find buffer if UBO is fragmented
157   if(mBuffers.size() > 1)
158   {
159     for(const auto& buffer : mBuffers)
160     {
161       if(bufferOffset >= buffer.createInfo.size)
162       {
163         bufferOffset -= buffer.createInfo.size;
164       }
165       else
166       {
167         break;
168       }
169       bufferIndex++;
170     }
171   }
172
173   auto& bufferDesc = mBuffers[bufferIndex];
174
175   if(outBufferIndex)
176   {
177     *outBufferIndex = bufferIndex;
178   }
179
180   if(newOffset)
181   {
182     *newOffset = bufferOffset;
183   }
184
185   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GetBufferByOffset (bufferIndex : %d / %d [%d], bufferOffset : %d, Graphics::BufferPtr : %p)\n", bufferIndex, mBuffers.size(), mSize, bufferOffset, bufferDesc.buffer.get());
186
187   return &bufferDesc;
188 }
189
190 void UniformBuffer::Write(const void* data, uint32_t size, uint32_t dstOffset)
191 {
192   // find which buffer we want to write into
193   uint32_t bufferOffset = dstOffset;
194   uint32_t bufferIndex  = 0u;
195
196   // Find buffer if UBO is fragmented
197   if(mBuffers.size() > 1)
198   {
199     for(const auto& buffer : mBuffers)
200     {
201       if(bufferOffset >= buffer.createInfo.size)
202       {
203         bufferOffset -= buffer.createInfo.size;
204       }
205       else
206       {
207         break;
208       }
209       bufferIndex++;
210     }
211   }
212
213   auto& bufferDesc = mBuffers[bufferIndex];
214
215   if(bufferDesc.needsUpdate)
216   {
217     mController->WaitIdle();
218     bufferDesc.needsUpdate = false;
219   }
220
221   DALI_ASSERT_ALWAYS(mBuffers.size() > bufferIndex);
222   DALI_ASSERT_ALWAYS(mBuffers[bufferIndex].buffer);
223   DALI_ASSERT_ALWAYS(mBuffers[bufferIndex].createInfo.size > bufferOffset + size);
224
225   const bool locallyMapped = (bufferDesc.mappedPtr != nullptr);
226   if(!locallyMapped)
227   {
228     // Map once and keep it
229     Map(bufferIndex);
230   }
231
232   if(bufferDesc.memory)
233   {
234     // Rarely happened that we use over the locked memory
235     // Unlock previous buffer, and lock as current bufferIndex again
236     if(DALI_UNLIKELY(mLockedBufferIndex != bufferIndex))
237     {
238       DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Unlock (lockedBufferIndex : %d / %d [%d], lockedPtr : %p)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr);
239
240       // mLockedBufferIndex == INVALID_BUFFER_INDEX only first time of current RenderScene.
241       if(DALI_LIKELY(mLockedBufferIndex != INVALID_BUFFER_INDEX))
242       {
243         // Unlock previous memory
244         if(mBuffers[mLockedBufferIndex].memory)
245         {
246           mBuffers[mLockedBufferIndex].memory->Unlock(true);
247         }
248       }
249       mLockedBufferIndex = bufferIndex;
250       mLockedPtr         = nullptr;
251
252       // Initial mapping done previously. Just lock and roll now.
253       if(mBuffers[mLockedBufferIndex].memory)
254       {
255         mLockedPtr = reinterpret_cast<uint8_t*>(mBuffers[mLockedBufferIndex].memory->LockRegion(0, mBuffers[mLockedBufferIndex].createInfo.size));
256       }
257       DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Lock (lockedBufferIndex : %d / %d [%d], lockedPtr : %p)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr);
258     }
259
260     DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "memcpy (lockedBufferIndex : %d / %d [%d], lockedPtr : %p, offset : %d, size : %d, lockedBufferSize : %d)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr, bufferOffset, size, mBuffers[mLockedBufferIndex].createInfo.size);
261
262     // We already check validation of buffer range. We can assume that bufferOffset + size <= mBuffers[mLockedBufferIndex].createInfo.size
263     if(mLockedPtr)
264     {
265       memcpy(mLockedPtr + bufferOffset, data, size);
266     }
267   }
268 }
269
270 void UniformBuffer::Map(uint32_t bufferIndex)
271 {
272   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Map (bufferIndex : %d / %d [%d])\n", bufferIndex, mBuffers.size(), mSize);
273   auto& buffer = mBuffers[bufferIndex];
274
275   if(buffer.needsUpdate)
276   {
277     mController->WaitIdle();
278     buffer.needsUpdate = false;
279   }
280
281   if(!buffer.memory)
282   {
283     Graphics::MapBufferInfo info{};
284     info.buffer   = buffer.buffer.get();
285     info.usage    = 0 | Graphics::MemoryUsageFlagBits::WRITE;
286     info.offset   = 0;
287     info.size     = buffer.createInfo.size;
288     buffer.memory = mController->MapBufferRange(info);
289     DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GraphicsMemoryMapped (bufferIndex : %d / %d [%d], size : %d)\n", bufferIndex, mBuffers.size(), mSize, info.size);
290   }
291 }
292
293 void UniformBuffer::Unmap(uint32_t bufferIndex)
294 {
295   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Unmap (bufferIndex : %d / %d [%d])\n", bufferIndex, mBuffers.size(), mSize);
296   auto& buffer = mBuffers[bufferIndex];
297   if(buffer.memory)
298   {
299     mController->UnmapMemory(std::move(buffer.memory));
300   }
301 }
302
303 void UniformBuffer::ReadyToLockUniformBuffer()
304 {
305   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "LockUniformBuffer\n");
306   if(DALI_UNLIKELY(mReadyToBeLocked && mLockedBufferIndex != INVALID_BUFFER_INDEX))
307   {
308     // Unlock previous locked buffer first
309     DALI_LOG_ERROR("Warning! : called LockUniformBuffer() before called UnlockUniformBuffer()!\n");
310     UnlockUniformBuffer();
311   }
312
313   mReadyToBeLocked   = true;
314   mLockedBufferIndex = INVALID_BUFFER_INDEX;
315   mLockedPtr         = nullptr;
316 }
317
318 void UniformBuffer::UnlockUniformBuffer()
319 {
320   DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "UnlockUniformBuffer (lockedBufferIndex : %d / %d [%d], lockedPtr : %p)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr);
321   if(mReadyToBeLocked && mLockedBufferIndex != INVALID_BUFFER_INDEX)
322   {
323     auto& bufferDesc = mBuffers[mLockedBufferIndex];
324     if(bufferDesc.memory)
325     {
326       bufferDesc.memory->Unlock(true);
327     }
328     // Flush all allocated buffers
329     for(auto i = 0u; i < mBuffers.size(); ++i)
330     {
331       Flush(i);
332     }
333   }
334   mLockedPtr         = nullptr;
335   mLockedBufferIndex = INVALID_BUFFER_INDEX;
336   mReadyToBeLocked   = false;
337 }
338
339 } // namespace Dali::Internal::Render