/*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
RelayoutSignalHandler::RelayoutCallback(actor);
}
};
+constexpr size_t FORCIBLE_WAIT_FLUSHED_BUFFER_COUNT_THRESHOLD = 1024;
} // anonymous namespace
END_TEST;
}
+int UtcDaliCoreProcessEventsStressTest(void)
+{
+ TestApplication application;
+ tet_infoline("Testing Dali::Integration::Core::ProcessEvents more than 1k times before render");
+
+ Vector3 size(100.0f, 100.0f, 0.0f);
+ Vector3 position(100.0f, 100.0f, 0.0f);
+
+ Actor actor = Actor::New();
+ actor.SetResizePolicy(ResizePolicy::FIXED, Dimension::ALL_DIMENSIONS);
+ actor.SetProperty(Actor::Property::SIZE, size);
+ actor.SetProperty(Actor::Property::POSITION, position);
+ application.GetScene().Add(actor);
+
+ RelayoutSignalHandler relayoutSignal(application);
+ actor.OnRelayoutSignal().Connect(&relayoutSignal, &RelayoutSignalHandler::RelayoutCallback);
+
+ application.SendNotification();
+
+ DALI_TEST_EQUALS(relayoutSignal.mSignalCalled, true, TEST_LOCATION);
+
+ DALI_TEST_EQUALS(actor.GetProperty(Actor::Property::SIZE).Get<Vector3>(), size, TEST_LOCATION);
+ DALI_TEST_EQUALS(actor.GetProperty(Actor::Property::POSITION).Get<Vector3>(), position, TEST_LOCATION);
+
+ relayoutSignal.mSignalCalled = false;
+
+ for(size_t i = 0; i < FORCIBLE_WAIT_FLUSHED_BUFFER_COUNT_THRESHOLD; ++i)
+ {
+ Vector3 newSize = size + Vector3(i + 1, i + 1, 0);
+ actor.SetProperty(Actor::Property::SIZE, newSize);
+ application.SendNotification();
+ DALI_TEST_EQUALS(relayoutSignal.mSignalCalled, true, TEST_LOCATION);
+ DALI_TEST_EQUALS(actor.GetProperty(Actor::Property::SIZE).Get<Vector3>(), newSize, TEST_LOCATION);
+
+ relayoutSignal.mSignalCalled = false;
+ }
+
+ application.Render();
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
+}
+
int UtcDaliCoreForceRelayout(void)
{
TestApplication application;
application.SendNotification();
END_TEST;
-}
\ No newline at end of file
+}
/*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
// CLASS HEADER
#include <dali/internal/update/queue/update-message-queue.h>
+// EXTERNAL INCLUDES
+#include <future> ///< for std::future and std::promise
+#include <chrono> ///< for std::chrono::milliseconds
+
// INTERNAL INCLUDES
#include <dali/devel-api/threading/mutex.h>
+#include <dali/integration-api/debug.h>
#include <dali/integration-api/render-controller.h>
#include <dali/internal/common/message-buffer.h>
#include <dali/internal/common/message.h>
static const std::size_t MAX_BUFFER_CAPACITY = 73728; // Avoid keeping buffers which exceed this
static const std::size_t MAX_FREE_BUFFER_COUNT = 3; // Allow this number of buffers to be recycled
+// Threshold of flushed buffers count to keep in the message queue.
+// If the buffer exceeded orver the max allowed count, main thread will be sleep to avoid too much message flushing.
+constexpr std::size_t MAX_MESSAGES_ALLOWED_IN_PROCESS_QUEUE = 1024;
+constexpr uint32_t TIME_TO_WAIT_FOR_MESSAGE_PROCESSING_MILLISECONDS = 10; // milliseconds
+
// A queue of message buffers
typedef vector<MessageBuffer*> MessageBufferQueue;
using MessageBufferIter = MessageBufferQueue::iterator;
MessageBufferQueue processQueue; ///< to process in the next update
MessageBufferQueue recycleQueue; ///< to recycle MessageBuffers after the messages have been processed
+ std::promise<void> messagePromise; ///< promise for message queue processing. Should be created and set value under mutex.
+ std::future<void> messageFuture; ///< future for message queue processing done. Should be created under mutex. Will be wait rarely.
+
MessageBuffer* currentMessageBuffer; ///< can be used without locking
MessageBufferQueue freeQueue; ///< buffers from the recycleQueue; can be used without locking
};
mImpl->processQueue.push_back(mImpl->currentMessageBuffer);
mImpl->currentMessageBuffer = nullptr;
+ // Reset message promise and future.
+ mImpl->messagePromise = std::promise<void>();
+ mImpl->messageFuture = mImpl->messagePromise.get_future();
+ if(DALI_UNLIKELY(mImpl->processQueue.size() >= MAX_MESSAGES_ALLOWED_IN_PROCESS_QUEUE))
+ {
+ DALI_LOG_ERROR("MessageQueue count exceeded [%zu >= %zu] Wait maximum %u ms\n", mImpl->processQueue.size(), MAX_MESSAGES_ALLOWED_IN_PROCESS_QUEUE, TIME_TO_WAIT_FOR_MESSAGE_PROCESSING_MILLISECONDS);
+ }
+ else
+ {
+ // Promise value immediatly.
+ mImpl->messagePromise.set_value();
+ }
+
// Grab any recycled MessageBuffers
while(!mImpl->recycleQueue.empty())
{
}
}
+ // Block if too much message queued without processing.
+ // It will be unlocked whenever ProcessMessages called, or time expired.
+ auto futureState = mImpl->messageFuture.wait_for(std::chrono::milliseconds(TIME_TO_WAIT_FOR_MESSAGE_PROCESSING_MILLISECONDS));
+
+ if(DALI_UNLIKELY(futureState != std::future_status::ready))
+ {
+ DALI_LOG_ERROR("MessageQueue not processed for overhead cases.\n");
+ }
+
return messagesToProcess;
}
mImpl->queueWasEmpty = mImpl->processQueue.empty(); // Flag whether we processed anything
copiedProcessQueue = std::move(mImpl->processQueue); // Move message queue
+
+ if(DALI_UNLIKELY(copiedProcessQueue.size() >= MAX_MESSAGES_ALLOWED_IN_PROCESS_QUEUE))
+ {
+ mImpl->messagePromise.set_value();
+ }
}
for(auto&& buffer : copiedProcessQueue)