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/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 // Called from event thread
152 uint32_t* MessageQueue::ReserveMessageSlot(uint32_t requestedSize, bool updateScene)
154 DALI_ASSERT_DEBUG(0 != requestedSize);
158 mImpl->sceneUpdateFlag = true;
161 if(!mImpl->currentMessageBuffer)
163 const MessageBufferIter endIter = mImpl->freeQueue.end();
165 // Find the largest recycled buffer from freeQueue
166 MessageBufferIter nextBuffer = endIter;
167 for(MessageBufferIter iter = mImpl->freeQueue.begin(); iter != endIter; ++iter)
169 if(endIter == nextBuffer ||
170 (*nextBuffer)->GetCapacity() < (*iter)->GetCapacity())
176 if(endIter != nextBuffer)
178 // Reuse a recycled buffer from freeQueue
179 mImpl->currentMessageBuffer = *nextBuffer;
180 mImpl->freeQueue.erase(nextBuffer);
184 mImpl->currentMessageBuffer = new MessageBuffer(INITIAL_BUFFER_SIZE);
188 // If we are inside Core::ProcessEvents(), core will automatically flush the queue.
189 // If we are outside, then we have to request a call to Core::ProcessEvents() on idle.
190 if(false == mImpl->processingEvents)
192 mImpl->renderController.RequestProcessEventsOnIdle(false);
195 return mImpl->currentMessageBuffer->ReserveMessageSlot(requestedSize);
198 // Called from event thread
199 bool MessageQueue::FlushQueue()
201 const bool messagesToProcess = (nullptr != mImpl->currentMessageBuffer);
203 // If there're messages to flush
204 if(messagesToProcess)
206 // queueMutex must be locked whilst accessing processQueue or recycleQueue
207 MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
209 mImpl->processQueue.push_back(mImpl->currentMessageBuffer);
210 mImpl->currentMessageBuffer = nullptr;
212 // Grab any recycled MessageBuffers
213 while(!mImpl->recycleQueue.empty())
215 MessageBuffer* recycled = mImpl->recycleQueue.back();
216 mImpl->recycleQueue.pop_back();
218 // Guard against excessive message buffer growth
219 if(MAX_FREE_BUFFER_COUNT < mImpl->freeQueue.size() ||
220 MAX_BUFFER_CAPACITY < recycled->GetCapacity())
226 mImpl->freeQueue.push_back(recycled);
230 if(mImpl->sceneUpdateFlag)
232 mImpl->sceneUpdate |= 2;
233 mImpl->sceneUpdateFlag = false;
237 mImpl->processingEvents = false;
239 return messagesToProcess;
242 bool MessageQueue::ProcessMessages(BufferIndex updateBufferIndex)
244 PERF_MONITOR_START(PerformanceMonitor::PROCESS_MESSAGES);
246 MessageBufferQueue copiedProcessQueue;
247 bool sceneUpdated = false;
249 // queueMutex must be locked whilst accessing queue
250 MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
251 copiedProcessQueue = mImpl->processQueue;
253 mImpl->sceneUpdate >>= 1;
255 sceneUpdated = (mImpl->sceneUpdate & 0x01); // if it was previously 2, scene graph was updated.
257 mImpl->queueWasEmpty = mImpl->processQueue.empty(); // Flag whether we processed anything
259 mImpl->processQueue.clear();
262 for(auto&& buffer : copiedProcessQueue)
264 for(MessageBuffer::Iterator bufferIter = buffer->Begin(); bufferIter.IsValid(); bufferIter.Next())
266 MessageBase* message = reinterpret_cast<MessageBase*>(bufferIter.Get());
268 message->Process(updateBufferIndex);
270 // Call virtual destructor explictly; since delete will not be called after placement new
271 message->~MessageBase();
276 // Pass back for use in the event-thread
278 MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
279 mImpl->recycleQueue.insert(mImpl->recycleQueue.end(),
280 std::make_move_iterator(copiedProcessQueue.begin()),
281 std::make_move_iterator(copiedProcessQueue.end()));
284 PERF_MONITOR_END(PerformanceMonitor::PROCESS_MESSAGES);
289 bool MessageQueue::WasEmpty() const
291 return mImpl->queueWasEmpty;
294 bool MessageQueue::IsSceneUpdateRequired() const
296 return mImpl->sceneUpdate;
299 std::size_t MessageQueue::GetCapacity() const
301 MessageQueueMutex::ScopedLock lock(mImpl->queueMutex);
303 uint32_t capacity = 0u;
304 MessageBufferIter endIter = mImpl->freeQueue.end();
305 for(MessageBufferIter iter = mImpl->freeQueue.begin(); iter != endIter; ++iter)
307 capacity += (*iter)->GetCapacity();
309 endIter = mImpl->processQueue.end();
310 for(MessageBufferIter iter = mImpl->processQueue.begin(); iter != endIter; ++iter)
312 capacity += (*iter)->GetCapacity();
314 if(mImpl->currentMessageBuffer != nullptr)
316 capacity += mImpl->currentMessageBuffer->GetCapacity();
321 } // namespace Update
323 } // namespace Internal