2 * Copyright (c) 2024 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
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;
33 Graphics::UniquePtr<UniformBufferV2> UniformBufferV2::New(Dali::Graphics::Controller* controller, bool emulated, uint32_t alignment)
35 return Graphics::UniquePtr<UniformBufferV2>(new UniformBufferV2(controller, emulated, alignment));
38 UniformBufferV2::UniformBufferV2(Dali::Graphics::Controller* controller, bool emulated, uint32_t alignment)
39 : mController(controller),
40 mBlockAlignment(alignment),
41 mCurrentGraphicsBufferIndex(0),
44 mBufferList.resize(emulated ? 1 : INTERNAL_UBO_BUFFER_COUNT);
45 for(size_t i = 0; i < mBufferList.size(); ++i)
47 mBufferList[i].graphicsBuffer = nullptr;
48 mBufferList[i].graphicsMemory = nullptr;
52 void UniformBufferV2::ReSpecify(uint32_t sizeInBytes)
54 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Respec(%p) [%d] BufferType:%s newSize:%d\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU", sizeInBytes);
57 ReSpecifyCPU(sizeInBytes);
61 ReSpecifyGPU(sizeInBytes);
65 void UniformBufferV2::Write(const void* data, uint32_t size, uint32_t offset)
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);
71 WriteCPU(data, size, offset);
75 WriteGPU(data, size, offset);
79 void UniformBufferV2::Map()
81 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Map(%p) [%d] BufferType:%s\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU");
92 void UniformBufferV2::Unmap()
94 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Unmap(%p) [%d] BufferType:%s\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU");
105 void UniformBufferV2::Flush()
107 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Flush(%p) [%d] BufferType:%s\n", this, mCurrentGraphicsBufferIndex, mEmulated ? "CPU" : "GPU");
109 // Flush only for GPU buffertype by unmapping
110 if(!mEmulated && mMappedPtr)
112 auto& buffer = mBufferList[mCurrentGraphicsBufferIndex];
113 if(buffer.graphicsMemory)
119 // Swap buffers for GPU UBOs
120 auto s = mBufferList.size();
121 mCurrentGraphicsBufferIndex = ((mCurrentGraphicsBufferIndex + 1) % s);
122 DALI_ASSERT_DEBUG(mCurrentGraphicsBufferIndex < mBufferList.size());
126 void UniformBufferV2::Rollback()
128 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Rollback(%p) [%d]\n", this, mCurrentGraphicsBufferIndex);
129 if(!mBufferList.empty())
131 mBufferList[mCurrentGraphicsBufferIndex].currentOffset = 0; // reset offset
135 uint32_t UniformBufferV2::AlignSize(uint32_t size)
137 if(size % mBlockAlignment != 0)
139 size = ((size / mBlockAlignment) + 1) * mBlockAlignment;
144 uint32_t UniformBufferV2::IncrementOffsetBy(uint32_t value)
146 if(mEmulated && !mBufferList.empty())
148 mBufferList[mCurrentGraphicsBufferIndex].currentOffset += value; // reset offset
149 return mBufferList[mCurrentGraphicsBufferIndex].currentOffset;
151 else if(!mBufferList.empty())
153 mBufferList[mCurrentGraphicsBufferIndex].currentOffset += value; // reset offset
154 return mBufferList[mCurrentGraphicsBufferIndex].currentOffset;
156 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Buffer should be allocated before incrementing offset\n");
160 bool UniformBufferV2::MemoryCompare(void* data, uint32_t offset, uint32_t size)
162 return !memcmp(data, reinterpret_cast<uint8_t*>(mMappedPtr) + offset, size);
165 uint32_t UniformBufferV2::GetBlockAlignment() const
167 return mBlockAlignment;
170 uint32_t UniformBufferV2::GetCurrentOffset() const
172 if(!mBufferList.empty())
174 return mBufferList[mCurrentGraphicsBufferIndex].currentOffset;
176 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::General, "Buffer should be allocated before getting offset\n");
180 uint32_t UniformBufferV2::GetCurrentCapacity() const
182 uint32_t capacity = 0;
183 if(!mBufferList.empty())
185 DALI_ASSERT_DEBUG(mBufferList.size() > mCurrentGraphicsBufferIndex);
186 capacity = mBufferList[mCurrentGraphicsBufferIndex].capacity;
191 Dali::Graphics::Buffer* UniformBufferV2::GetGraphicsBuffer() const
193 return mBufferList[mCurrentGraphicsBufferIndex].graphicsBuffer.get();
196 void UniformBufferV2::ReSpecifyCPU(uint32_t sizeInBytes)
200 uint32_t currentCapacity = GetCurrentCapacity();
202 if(sizeInBytes > currentCapacity)
204 Graphics::UniquePtr<Graphics::Buffer> oldBuffer{nullptr};
206 // If the CPU buffer already exist use it when applying respec
207 if(!mBufferList.empty())
209 mBufferList[0].graphicsMemory = nullptr; // Discard mapped memory if exists
210 oldBuffer = std::move(mBufferList[0].graphicsBuffer); // Store old buffer for re-using
212 Graphics::BufferPropertiesFlags flags = 0u | Graphics::BufferPropertiesFlagBit::CPU_ALLOCATED;
214 auto createInfo = Graphics::BufferCreateInfo()
215 .SetSize(sizeInBytes)
216 .SetBufferPropertiesFlags(flags)
217 .SetUsage(0u | Graphics::BufferUsage::UNIFORM_BUFFER);
219 gfxBuffer.graphicsBuffer = mController->CreateBuffer(createInfo, std::move(oldBuffer));
220 gfxBuffer.capacity = sizeInBytes;
221 gfxBuffer.currentOffset = 0;
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();
229 mMappedPtr = nullptr;
233 // After respecifying the buffer, we can map it persistently as it already exists
239 void UniformBufferV2::ReSpecifyGPU(uint32_t sizeInBytes)
241 uint32_t currentCapacity = GetCurrentCapacity();
243 if(sizeInBytes > currentCapacity)
246 Graphics::UniquePtr<Graphics::Buffer> oldBuffer{nullptr};
248 // If the GPU buffer already exist use it when applying respec
249 if(!mBufferList.empty())
251 mBufferList[mCurrentGraphicsBufferIndex].graphicsMemory = nullptr; // Discard mapped memory if exists
252 oldBuffer = std::move(mBufferList[mCurrentGraphicsBufferIndex].graphicsBuffer); // Store old buffer for re-using
254 Graphics::BufferPropertiesFlags flags = 0u;
256 auto createInfo = Graphics::BufferCreateInfo()
257 .SetSize(sizeInBytes)
258 .SetBufferPropertiesFlags(flags)
259 .SetUsage(0u | Graphics::BufferUsage::UNIFORM_BUFFER);
261 gfxBuffer.graphicsBuffer = mController->CreateBuffer(createInfo, std::move(oldBuffer));
262 gfxBuffer.capacity = sizeInBytes;
263 gfxBuffer.currentOffset = 0;
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");
272 mMappedPtr = nullptr;
276 MapGPU(); // Note, this will flush the creation buffer queues in the backend and initialize buffer.
280 void UniformBufferV2::WriteCPU(const void* data, uint32_t size, uint32_t offset)
285 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Warning: CPU buffer should already be mapped!\n");
289 DALI_ASSERT_DEBUG(offset + size <= mBufferList[mCurrentGraphicsBufferIndex].capacity);
291 // just copy whatever comes here
292 memcpy(reinterpret_cast<uint8_t*>(mMappedPtr) + offset, data, size);
295 void UniformBufferV2::WriteGPU(const void* data, uint32_t size, uint32_t offset)
299 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "Warning: GPU buffer should already be mapped!\n");
303 DALI_ASSERT_DEBUG(offset + size <= mBufferList[mCurrentGraphicsBufferIndex].capacity);
305 memcpy(reinterpret_cast<uint8_t*>(mMappedPtr) + offset, data, size);
308 void UniformBufferV2::MapCPU()
310 auto& buffer = mBufferList[0]; // CPU contains always one buffer
311 if(!buffer.graphicsMemory)
313 Graphics::MapBufferInfo info{};
314 info.buffer = buffer.graphicsBuffer.get();
315 info.usage = 0 | Graphics::MemoryUsageFlagBits::WRITE;
317 info.size = buffer.capacity;
318 buffer.graphicsMemory = mController->MapBufferRange(info);
321 // obtain pointer instantly
322 if(buffer.graphicsMemory)
324 mMappedPtr = buffer.graphicsMemory->LockRegion(0, buffer.capacity);
325 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "CPU buffer is mapped to %p\n", mMappedPtr);
329 void UniformBufferV2::MapGPU()
331 auto& buffer = mBufferList[mCurrentGraphicsBufferIndex];
332 if(!buffer.graphicsMemory)
334 Graphics::MapBufferInfo info{};
335 info.buffer = buffer.graphicsBuffer.get();
336 info.usage = 0 | Graphics::MemoryUsageFlagBits::WRITE;
338 info.size = buffer.capacity;
339 buffer.graphicsMemory = mController->MapBufferRange(info);
342 // obtain pointer instantly
343 if(buffer.graphicsMemory)
345 mMappedPtr = buffer.graphicsMemory->LockRegion(0, buffer.capacity);
346 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GPU buffer is mapped to %p\n", mMappedPtr);
350 void UniformBufferV2::UnmapCPU()
352 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "CPU buffer is unmapped\n");
355 void UniformBufferV2::UnmapGPU()
357 auto& buffer = mBufferList[mCurrentGraphicsBufferIndex];
358 if(buffer.graphicsMemory)
360 mController->UnmapMemory(std::move(buffer.graphicsMemory));
361 buffer.graphicsMemory = nullptr;
363 mMappedPtr = nullptr;
364 DALI_LOG_INFO(gUniformBufferLogFilter, Debug::Verbose, "GPU buffer is unmapped\n");
367 } // namespace Dali::Internal::Render