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-toolkit/internal/visuals/animated-vector-image/vector-animation-thread.h>
22 #include <dali/devel-api/adaptor-framework/environment-variable.h>
23 #include <dali/devel-api/adaptor-framework/thread-settings.h>
24 #include <dali/integration-api/adaptor-framework/adaptor.h>
25 #include <dali/integration-api/debug.h>
36 constexpr auto DEFAULT_NUMBER_OF_RASTERIZE_THREADS = size_t{4u};
37 constexpr auto NUMBER_OF_RASTERIZE_THREADS_ENV = "DALI_VECTOR_RASTERIZE_THREADS";
39 size_t GetNumberOfThreads(const char* environmentVariable, size_t defaultValue)
41 using Dali::EnvironmentVariable::GetEnvironmentVariable;
42 auto numberString = GetEnvironmentVariable(environmentVariable);
43 auto numberOfThreads = numberString ? std::strtoul(numberString, nullptr, 10) : 0;
44 constexpr auto MAX_NUMBER_OF_THREADS = 100u;
45 DALI_ASSERT_DEBUG(numberOfThreads < MAX_NUMBER_OF_THREADS);
46 return (numberOfThreads > 0 && numberOfThreads < MAX_NUMBER_OF_THREADS) ? numberOfThreads : defaultValue;
49 #if defined(DEBUG_ENABLED)
50 Debug::Filter* gVectorAnimationLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_VECTOR_ANIMATION");
53 } // unnamed namespace
55 VectorAnimationThread::VectorAnimationThread()
59 mRasterizers(GetNumberOfThreads(NUMBER_OF_RASTERIZE_THREADS_ENV, DEFAULT_NUMBER_OF_RASTERIZE_THREADS), [&]() { return RasterizeHelper(*this); }),
60 mSleepThread(MakeCallback(this, &VectorAnimationThread::OnAwakeFromSleep)),
63 mDestroyThread(false),
64 mLogFactory(Dali::Adaptor::Get().GetLogFactory())
69 VectorAnimationThread::~VectorAnimationThread()
73 ConditionalWait::ScopedLock lock(mConditionalWait);
74 mDestroyThread = true;
76 mConditionalWait.Notify(lock);
79 DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::~VectorAnimationThread: Join [%p]\n", this);
84 void VectorAnimationThread::AddTask(VectorAnimationTaskPtr task)
86 ConditionalWait::ScopedLock lock(mConditionalWait);
88 // Find if the task is already in the list except loading task
89 auto iter = std::find_if(mAnimationTasks.begin(), mAnimationTasks.end(), [task](VectorAnimationTaskPtr& element) { return (element == task && !element->IsLoadRequested()); });
90 if(iter == mAnimationTasks.end())
92 auto currentTime = task->CalculateNextFrameTime(true); // Rasterize as soon as possible
94 bool inserted = false;
95 for(auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter)
97 auto nextTime = (*iter)->GetNextFrameTime();
98 if(nextTime > currentTime)
100 mAnimationTasks.insert(iter, task);
108 mAnimationTasks.push_back(task);
111 mNeedToSleep = false;
112 // wake up the animation thread
113 mConditionalWait.Notify(lock);
117 void VectorAnimationThread::OnTaskCompleted(VectorAnimationTaskPtr task, bool success, bool keepAnimation)
121 ConditionalWait::ScopedLock lock(mConditionalWait);
122 bool needRasterize = false;
124 auto workingTask = std::find(mWorkingTasks.begin(), mWorkingTasks.end(), task);
125 if(workingTask != mWorkingTasks.end())
127 mWorkingTasks.erase(workingTask);
130 // Check pending task
131 if(mAnimationTasks.end() != std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
133 needRasterize = true;
136 if(keepAnimation && success)
138 if(mCompletedTasks.end() == std::find(mCompletedTasks.begin(), mCompletedTasks.end(), task))
140 mCompletedTasks.push_back(task);
141 needRasterize = true;
147 mNeedToSleep = false;
148 // wake up the animation thread
149 mConditionalWait.Notify(lock);
154 void VectorAnimationThread::OnAwakeFromSleep()
158 mNeedToSleep = false;
159 // wake up the animation thread
160 mConditionalWait.Notify();
164 void VectorAnimationThread::Run()
166 SetThreadName("VectorAnimationThread");
167 mLogFactory.InstallLogFunction();
169 while(!mDestroyThread)
175 void VectorAnimationThread::Rasterize()
177 // Lock while popping task out from the queue
178 ConditionalWait::ScopedLock lock(mConditionalWait);
183 mConditionalWait.Wait(lock);
188 // Process completed tasks
189 for(auto&& task : mCompletedTasks)
191 if(mAnimationTasks.end() == std::find(mAnimationTasks.begin(), mAnimationTasks.end(), task))
193 // Should use the frame rate of the animation file
194 auto nextFrameTime = task->CalculateNextFrameTime(false);
196 bool inserted = false;
197 for(auto iter = mAnimationTasks.begin(); iter != mAnimationTasks.end(); ++iter)
199 auto time = (*iter)->GetNextFrameTime();
200 if(time > nextFrameTime)
202 mAnimationTasks.insert(iter, task);
210 mAnimationTasks.push_back(task);
214 mCompletedTasks.clear();
216 // pop out the next task from the queue
217 for(auto it = mAnimationTasks.begin(); it != mAnimationTasks.end();)
219 VectorAnimationTaskPtr nextTask = *it;
221 auto currentTime = std::chrono::steady_clock::now();
222 auto nextFrameTime = nextTask->GetNextFrameTime();
224 #if defined(DEBUG_ENABLED)
225 // auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(nextFrameTime - currentTime);
227 // DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::Rasterize: [next time = %lld]\n", duration.count());
230 if(nextFrameTime <= currentTime)
232 // If the task is not in the working list
233 if(std::find(mWorkingTasks.begin(), mWorkingTasks.end(), nextTask) == mWorkingTasks.end())
235 it = mAnimationTasks.erase(it);
237 // Add it to the working list
238 mWorkingTasks.push_back(nextTask);
240 auto rasterizerHelperIt = mRasterizers.GetNext();
241 DALI_ASSERT_ALWAYS(rasterizerHelperIt != mRasterizers.End());
243 rasterizerHelperIt->Rasterize(nextTask);
252 mSleepThread.SleepUntil(nextFrameTime);
258 VectorAnimationThread::RasterizeHelper::RasterizeHelper(VectorAnimationThread& animationThread)
259 : RasterizeHelper(std::unique_ptr<VectorRasterizeThread>(new VectorRasterizeThread()), animationThread)
263 VectorAnimationThread::RasterizeHelper::RasterizeHelper(RasterizeHelper&& rhs)
264 : RasterizeHelper(std::move(rhs.mRasterizer), rhs.mAnimationThread)
268 VectorAnimationThread::RasterizeHelper::RasterizeHelper(std::unique_ptr<VectorRasterizeThread> rasterizer, VectorAnimationThread& animationThread)
269 : mRasterizer(std::move(rasterizer)),
270 mAnimationThread(animationThread)
272 mRasterizer->SetCompletedCallback(MakeCallback(&mAnimationThread, &VectorAnimationThread::OnTaskCompleted));
275 void VectorAnimationThread::RasterizeHelper::Rasterize(VectorAnimationTaskPtr task)
279 mRasterizer->AddTask(task);
283 VectorAnimationThread::SleepThread::SleepThread(CallbackBase* callback)
284 : mConditionalWait(),
285 mAwakeCallback(std::unique_ptr<CallbackBase>(callback)),
287 mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
289 mDestroyThread(false)
293 VectorAnimationThread::SleepThread::~SleepThread()
297 ConditionalWait::ScopedLock lock(mConditionalWait);
298 mDestroyThread = true;
299 mConditionalWait.Notify(lock);
305 void VectorAnimationThread::SleepThread::SleepUntil(std::chrono::time_point<std::chrono::steady_clock> timeToSleepUntil)
307 ConditionalWait::ScopedLock lock(mConditionalWait);
308 mSleepTimePoint = timeToSleepUntil;
310 mConditionalWait.Notify(lock);
313 void VectorAnimationThread::SleepThread::Run()
315 SetThreadName("VectorSleepThread");
316 mLogFactory.InstallLogFunction();
318 while(!mDestroyThread)
321 std::chrono::time_point<std::chrono::steady_clock> sleepTimePoint;
324 ConditionalWait::ScopedLock lock(mConditionalWait);
326 needToSleep = mNeedToSleep;
327 sleepTimePoint = mSleepTimePoint;
329 mNeedToSleep = false;
334 #if defined(DEBUG_ENABLED)
335 // auto sleepDuration = std::chrono::duration_cast<std::chrono::milliseconds>(mSleepTimePoint - std::chrono::steady_clock::now());
337 // DALI_LOG_INFO(gVectorAnimationLogFilter, Debug::Verbose, "VectorAnimationThread::SleepThread::Run: [sleep duration = %lld]\n", sleepDuration.count());
340 std::this_thread::sleep_until(sleepTimePoint);
344 CallbackBase::Execute(*mAwakeCallback);
349 ConditionalWait::ScopedLock lock(mConditionalWait);
350 if(!mDestroyThread && !mNeedToSleep)
352 mConditionalWait.Wait(lock);
358 } // namespace Internal
360 } // namespace Toolkit