2 * Copyright (c) 2023 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/update/queue/update-message-queue.h>
22 #include <dali/devel-api/threading/mutex.h>
23 #include <dali/integration-api/render-controller.h>
24 #include <dali/internal/common/message-buffer.h>
25 #include <dali/internal/common/message.h>
26 #include <dali/internal/render/common/performance-monitor.h>
27 #include <dali/public-api/common/vector-wrapper.h>
31 using Dali::Integration::RenderController;
32 using Dali::Internal::SceneGraph::SceneGraphBuffers;
38 namespace // unnamed namespace
40 // A message to set Actor::SIZE is 72 bytes on 32bit device
41 // A buffer of size 32768 would store (32768 - 4) / (72 + 4) = 431 of those messages
42 static const std::size_t INITIAL_BUFFER_SIZE = 32768;
43 static const std::size_t MAX_BUFFER_CAPACITY = 73728; // Avoid keeping buffers which exceed this
44 static const std::size_t MAX_FREE_BUFFER_COUNT = 3; // Allow this number of buffers to be recycled
46 // A queue of message buffers
47 typedef vector<MessageBuffer*> MessageBufferQueue;
48 using MessageBufferIter = MessageBufferQueue::iterator;
50 using MessageQueueMutex = Dali::Mutex;
52 } // unnamed namespace
57 * Private MessageQueue data
59 struct MessageQueue::Impl
61 Impl(RenderController& controller, const SceneGraphBuffers& buffers)
62 : renderController(controller),
63 sceneGraphBuffers(buffers),
64 processingEvents(false),
66 sceneUpdateFlag(false),
68 currentMessageBuffer(nullptr)
74 // Delete the current buffer
75 if(currentMessageBuffer)
77 DeleteBufferContents(currentMessageBuffer);
78 delete currentMessageBuffer;
81 // Delete the unprocessed buffers
82 const MessageBufferIter processQueueEndIter = processQueue.end();
83 for(MessageBufferIter iter = processQueue.begin(); iter != processQueueEndIter; ++iter)
85 MessageBuffer* unprocessedBuffer = *iter;
86 DeleteBufferContents(unprocessedBuffer);
87 delete unprocessedBuffer;
90 // Delete the recycled buffers
91 const MessageBufferIter recycleQueueEndIter = recycleQueue.end();
92 for(MessageBufferIter iter = recycleQueue.begin(); iter != recycleQueueEndIter; ++iter)
94 MessageBuffer* recycledBuffer = *iter;
95 DeleteBufferContents(recycledBuffer);
96 delete recycledBuffer;
99 const MessageBufferIter freeQueueEndIter = freeQueue.end();
100 for(MessageBufferIter iter = freeQueue.begin(); iter != freeQueueEndIter; ++iter)
102 MessageBuffer* freeBuffer = *iter;
103 DeleteBufferContents(freeBuffer);
108 void DeleteBufferContents(MessageBuffer* buffer)
110 for(MessageBuffer::Iterator iter = buffer->Begin(); iter.IsValid(); iter.Next())
112 MessageBase* message = reinterpret_cast<MessageBase*>(iter.Get());
114 // Call virtual destructor explictly; since delete will not be called after placement new
115 message->~MessageBase();
119 RenderController& renderController; ///< render controller
120 const SceneGraphBuffers& sceneGraphBuffers; ///< Used to keep track of which buffers are being written or read.
122 bool processingEvents; ///< Whether messages queued will be flushed by core
123 bool queueWasEmpty; ///< Flag whether the queue was empty during the Update()
124 bool sceneUpdateFlag; ///< true when there is a new message that requires a scene-graph node tree update
125 int sceneUpdate; ///< Non zero when there is a message in the queue requiring a scene-graph node tree update
127 MessageQueueMutex queueMutex; ///< queueMutex must be locked whilst accessing processQueue or recycleQueue
128 MessageBufferQueue processQueue; ///< to process in the next update
129 MessageBufferQueue recycleQueue; ///< to recycle MessageBuffers after the messages have been processed
131 MessageBuffer* currentMessageBuffer; ///< can be used without locking
132 MessageBufferQueue freeQueue; ///< buffers from the recycleQueue; can be used without locking
135 MessageQueue::MessageQueue(Integration::RenderController& controller, const SceneGraph::SceneGraphBuffers& buffers)
138 mImpl = new Impl(controller, buffers);
141 MessageQueue::~MessageQueue()
146 void MessageQueue::EventProcessingStarted()
148 mImpl->processingEvents = true; // called from event thread
151 void MessageQueue::EventProcessingFinished()
153 mImpl->processingEvents = false; // called from event thread
156 // Called from event thread
157 uint32_t* MessageQueue::ReserveMessageSlot(uint32_t requestedSize, bool updateScene)
159 DALI_ASSERT_DEBUG(0 != requestedSize);
163 mImpl->sceneUpdateFlag = true;
166 if(!mImpl->currentMessageBuffer)
168 const MessageBufferIter endIter = mImpl->freeQueue.end();
170 // Find the largest recycled buffer from freeQueue
171 MessageBufferIter nextBuffer = endIter;
172 for(MessageBufferIter iter = mImpl->freeQueue.begin(); iter != endIter; ++iter)
174 if(endIter == nextBuffer ||
175 (*nextBuffer)->GetCapacity() < (*iter)->GetCapacity())
181 if(endIter != nextBuffer)
183 // Reuse a recycled buffer from freeQueue
184 mImpl->currentMessageBuffer = *nextBuffer;
185 mImpl->freeQueue.erase(nextBuffer);
189 mImpl->currentMessageBuffer = new MessageBuffer(INITIAL_BUFFER_SIZE);
193 // If we are inside Core::ProcessEvents(), core will automatically flush the queue.
194 // If we are outside, then we have to request a call to Core::ProcessEvents() on idle.
195 if(false == mImpl->processingEvents)
197 mImpl->renderController.RequestProcessEventsOnIdle();
200 return mImpl->currentMessageBuffer->ReserveMessageSlot(requestedSize);
203 // Called from event thread
204 bool MessageQueue::FlushQueue()
206 const bool messagesToProcess = (nullptr != mImpl->currentMessageBuffer);
208 // If there're messages to flush
209 if(messagesToProcess)
211 // queueMutex must be locked whilst accessing processQueue or recycleQueue
212 MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
214 mImpl->processQueue.push_back(mImpl->currentMessageBuffer);
215 mImpl->currentMessageBuffer = nullptr;
217 // Grab any recycled MessageBuffers
218 while(!mImpl->recycleQueue.empty())
220 MessageBuffer* recycled = mImpl->recycleQueue.back();
221 mImpl->recycleQueue.pop_back();
223 // Guard against excessive message buffer growth
224 if(MAX_FREE_BUFFER_COUNT < mImpl->freeQueue.size() ||
225 MAX_BUFFER_CAPACITY < recycled->GetCapacity())
231 mImpl->freeQueue.push_back(recycled);
235 if(mImpl->sceneUpdateFlag)
237 mImpl->sceneUpdate |= 2;
238 mImpl->sceneUpdateFlag = false;
242 return messagesToProcess;
245 bool MessageQueue::ProcessMessages(BufferIndex updateBufferIndex)
247 PERF_MONITOR_START(PerformanceMonitor::PROCESS_MESSAGES);
249 MessageBufferQueue copiedProcessQueue;
250 bool sceneUpdated = false;
252 // queueMutex must be locked whilst accessing queue
253 MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
255 mImpl->sceneUpdate >>= 1;
256 sceneUpdated = (mImpl->sceneUpdate & 0x01); // if it was previously 2, scene graph was updated.
258 mImpl->queueWasEmpty = mImpl->processQueue.empty(); // Flag whether we processed anything
260 copiedProcessQueue = std::move(mImpl->processQueue); // Move message queue
263 for(auto&& buffer : copiedProcessQueue)
265 for(MessageBuffer::Iterator bufferIter = buffer->Begin(); bufferIter.IsValid(); bufferIter.Next())
267 MessageBase* message = reinterpret_cast<MessageBase*>(bufferIter.Get());
269 message->Process(updateBufferIndex);
271 // Call virtual destructor explictly; since delete will not be called after placement new
272 message->~MessageBase();
277 // Pass back for use in the event-thread
279 MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
280 mImpl->recycleQueue.insert(mImpl->recycleQueue.end(),
281 std::make_move_iterator(copiedProcessQueue.begin()),
282 std::make_move_iterator(copiedProcessQueue.end()));
285 PERF_MONITOR_END(PerformanceMonitor::PROCESS_MESSAGES);
290 bool MessageQueue::WasEmpty() const
292 return mImpl->queueWasEmpty;
295 bool MessageQueue::IsSceneUpdateRequired() const
297 return mImpl->sceneUpdate;
300 std::size_t MessageQueue::GetCapacity() const
302 MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
304 uint32_t capacity = 0u;
305 MessageBufferIter endIter = mImpl->freeQueue.end();
306 for(MessageBufferIter iter = mImpl->freeQueue.begin(); iter != endIter; ++iter)
308 capacity += (*iter)->GetCapacity();
310 endIter = mImpl->processQueue.end();
311 for(MessageBufferIter iter = mImpl->processQueue.begin(); iter != endIter; ++iter)
313 capacity += (*iter)->GetCapacity();
315 if(mImpl->currentMessageBuffer != nullptr)
317 capacity += mImpl->currentMessageBuffer->GetCapacity();
322 } // namespace Update
324 } // namespace Internal