2 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/render/renderers/uniform-buffer.h>
22 #include <dali/integration-api/debug.h>
25 Debug::Filter* gUniformBufferLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_UNIFORM_BUFFER");
28 namespace Dali::Internal::Render
32 static constexpr uint32_t INVALID_BUFFER_INDEX = std::numeric_limits<uint32_t>::max();
34 UniformBuffer::UniformBuffer(Dali::Graphics::Controller* controller,
37 Graphics::BufferUsageFlags usageFlags,
38 Graphics::BufferPropertiesFlags propertiesFlags)
39 : mController(controller),
41 mUsageFlags(usageFlags),
42 mPropertiesFlags(propertiesFlags),
43 mLockedBufferIndex(INVALID_BUFFER_INDEX),
45 mReadyToBeLocked(false)
47 mAlignment = alignment;
50 Resize(sizeInBytes, true);
54 UniformBuffer::~UniformBuffer()
56 // Unmap and flush all allocated buffers
57 for(auto i = 0u; i < mBuffers.size(); ++i)
64 void UniformBuffer::Flush(uint32_t bufferIndex)
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)
70 buffer.memory->Flush();
74 void UniformBuffer::Resize(uint32_t newSize, bool invalidate)
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))
80 newSize = (((newSize - 1) / mAlignment) + 1) * mAlignment;
83 // The buffer is already optimal
84 if(newSize == mSize && !invalidate)
88 if(invalidate && newSize == mSize && mBuffers.size() == 1)
93 if(DALI_UNLIKELY(mReadyToBeLocked))
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);
99 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize %d --> %d with %s [mBuffers : %d]\n", mSize, newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size());
102 // Throw away content
107 UnlockUniformBuffer();
108 mReadyToBeLocked = true;
110 // Flush and unmap all allocated buffers
111 for(auto i = 0u; i < mBuffers.size(); ++i)
122 auto createInfo = Graphics::BufferCreateInfo()
123 .SetSize(newSize - mSize)
124 .SetBufferPropertiesFlags(mPropertiesFlags)
125 .SetUsage(mUsageFlags);
127 auto buffer = mController->CreateBuffer(createInfo, nullptr);
129 mBuffers.emplace_back(GfxBuffer(std::move(buffer), createInfo));
134 // If invalidate during locked, begin lock again.
135 if(DALI_UNLIKELY(invalidate && mReadyToBeLocked))
137 mReadyToBeLocked = false;
138 ReadyToLockUniformBuffer();
141 if(DALI_UNLIKELY(mReadyToBeLocked))
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);
147 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Resize done as %d with %s [mBuffers : %d]\n", newSize, invalidate ? "Invalidate" : "Rendering", mBuffers.size());
151 const UniformBuffer::GfxBuffer* UniformBuffer::GetBufferByOffset(uint32_t offset, uint32_t* newOffset, uint32_t* outBufferIndex) const
153 uint32_t bufferOffset = offset;
154 uint32_t bufferIndex = 0u;
156 // Find buffer if UBO is fragmented
157 if(mBuffers.size() > 1)
159 for(const auto& buffer : mBuffers)
161 if(bufferOffset >= buffer.createInfo.size)
163 bufferOffset -= buffer.createInfo.size;
173 auto& bufferDesc = mBuffers[bufferIndex];
177 *outBufferIndex = bufferIndex;
182 *newOffset = bufferOffset;
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());
190 void UniformBuffer::Write(const void* data, uint32_t size, uint32_t dstOffset)
192 // find which buffer we want to write into
193 uint32_t bufferOffset = dstOffset;
194 uint32_t bufferIndex = 0u;
196 // Find buffer if UBO is fragmented
197 if(mBuffers.size() > 1)
199 for(const auto& buffer : mBuffers)
201 if(bufferOffset >= buffer.createInfo.size)
203 bufferOffset -= buffer.createInfo.size;
213 auto& bufferDesc = mBuffers[bufferIndex];
215 if(bufferDesc.needsUpdate)
217 mController->WaitIdle();
218 bufferDesc.needsUpdate = false;
221 DALI_ASSERT_ALWAYS(mBuffers.size() > bufferIndex);
222 DALI_ASSERT_ALWAYS(mBuffers[bufferIndex].buffer);
223 DALI_ASSERT_ALWAYS(mBuffers[bufferIndex].createInfo.size > bufferOffset + size);
225 const bool locallyMapped = (bufferDesc.mappedPtr != nullptr);
228 // Map once and keep it
232 if(bufferDesc.memory)
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))
238 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Unlock (lockedBufferIndex : %d / %d [%d], lockedPtr : %p)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr);
240 // mLockedBufferIndex == INVALID_BUFFER_INDEX only first time of current RenderScene.
241 if(DALI_LIKELY(mLockedBufferIndex != INVALID_BUFFER_INDEX))
243 // Unlock previous memory
244 if(mBuffers[mLockedBufferIndex].memory)
246 mBuffers[mLockedBufferIndex].memory->Unlock(true);
249 mLockedBufferIndex = bufferIndex;
250 mLockedPtr = nullptr;
252 // Initial mapping done previously. Just lock and roll now.
253 if(mBuffers[mLockedBufferIndex].memory)
255 mLockedPtr = reinterpret_cast<uint8_t*>(mBuffers[mLockedBufferIndex].memory->LockRegion(0, mBuffers[mLockedBufferIndex].createInfo.size));
257 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Lock (lockedBufferIndex : %d / %d [%d], lockedPtr : %p)\n", mLockedBufferIndex, mBuffers.size(), mSize, mLockedPtr);
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);
262 // We already check validation of buffer range. We can assume that bufferOffset + size <= mBuffers[mLockedBufferIndex].createInfo.size
265 memcpy(mLockedPtr + bufferOffset, data, size);
270 void UniformBuffer::Map(uint32_t bufferIndex)
272 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Map (bufferIndex : %d / %d [%d])\n", bufferIndex, mBuffers.size(), mSize);
273 auto& buffer = mBuffers[bufferIndex];
275 if(buffer.needsUpdate)
277 mController->WaitIdle();
278 buffer.needsUpdate = false;
283 Graphics::MapBufferInfo info{};
284 info.buffer = buffer.buffer.get();
285 info.usage = 0 | Graphics::MemoryUsageFlagBits::WRITE;
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);
293 void UniformBuffer::Unmap(uint32_t bufferIndex)
295 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Unmap (bufferIndex : %d / %d [%d])\n", bufferIndex, mBuffers.size(), mSize);
296 auto& buffer = mBuffers[bufferIndex];
299 mController->UnmapMemory(std::move(buffer.memory));
303 void UniformBuffer::ReadyToLockUniformBuffer()
305 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "LockUniformBuffer\n");
306 if(DALI_UNLIKELY(mReadyToBeLocked && mLockedBufferIndex != INVALID_BUFFER_INDEX))
308 // Unlock previous locked buffer first
309 DALI_LOG_ERROR("Warning! : called LockUniformBuffer() before called UnlockUniformBuffer()!\n");
310 UnlockUniformBuffer();
313 mReadyToBeLocked = true;
314 mLockedBufferIndex = INVALID_BUFFER_INDEX;
315 mLockedPtr = nullptr;
318 void UniformBuffer::UnlockUniformBuffer()
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)
323 auto& bufferDesc = mBuffers[mLockedBufferIndex];
324 if(bufferDesc.memory)
326 bufferDesc.memory->Unlock(true);
328 // Flush all allocated buffers
329 for(auto i = 0u; i < mBuffers.size(); ++i)
334 mLockedPtr = nullptr;
335 mLockedBufferIndex = INVALID_BUFFER_INDEX;
336 mReadyToBeLocked = false;
339 } // namespace Dali::Internal::Render