Added rudimentary frame caching support to LottieComponent.

This commit is contained in:
Leon Styhre 2022-01-08 15:04:10 +01:00
parent 3f629b6c1e
commit b3421823a7
3 changed files with 77 additions and 4 deletions

View file

@ -277,6 +277,8 @@ void Settings::setDefaults()
mBoolMap["DebugSkipInputLogging"] = {false, false}; mBoolMap["DebugSkipInputLogging"] = {false, false};
mStringMap["ROMDirectory"] = {"", ""}; mStringMap["ROMDirectory"] = {"", ""};
mStringMap["UIMode_passkey"] = {"uuddlrlrba", "uuddlrlrba"}; mStringMap["UIMode_passkey"] = {"uuddlrlrba", "uuddlrlrba"};
mIntMap["LottieMaxFileCache"] = {100, 100};
mIntMap["LottieMaxTotalCache"] = {1024, 1024};
// //
// Hardcoded or program-internal settings. // Hardcoded or program-internal settings.

View file

@ -17,6 +17,10 @@
LottieComponent::LottieComponent(Window* window) LottieComponent::LottieComponent(Window* window)
: GuiComponent{window} : GuiComponent{window}
, mCacheFrames{true}
, mMaxCacheSize{0}
, mCacheSize{0}
, mFrameSize{0}
, mAnimation{nullptr} , mAnimation{nullptr}
, mSurface{nullptr} , mSurface{nullptr}
, mTotalFrames{0} , mTotalFrames{0}
@ -35,6 +39,17 @@ LottieComponent::LottieComponent(Window* window)
mTexture->setFormat(Renderer::Texture::BGRA); mTexture->setFormat(Renderer::Texture::BGRA);
#endif #endif
// Keep per-file cache size within 0 to 1024 MiB.
mMaxCacheSize = static_cast<size_t>(
glm::clamp(Settings::getInstance()->getInt("LottieMaxFileCache"), 0, 1024) * 1024 * 1024);
// Keep total cache size within 0 to 4096 MiB.
int maxTotalCache =
glm::clamp(Settings::getInstance()->getInt("LottieMaxTotalCache"), 0, 4096) * 1024 * 1024;
if (mMaxTotalFrameCache != static_cast<size_t>(maxTotalCache))
mMaxTotalFrameCache = static_cast<size_t>(maxTotalCache);
// Set component defaults. // Set component defaults.
setOrigin(0.5f, 0.5f); setOrigin(0.5f, 0.5f);
setSize(Renderer::getScreenWidth() * 0.2f, Renderer::getScreenHeight() * 0.2f); setSize(Renderer::getScreenWidth() * 0.2f, Renderer::getScreenHeight() * 0.2f);
@ -43,6 +58,13 @@ LottieComponent::LottieComponent(Window* window)
setZIndex(30.0f); setZIndex(30.0f);
} }
LottieComponent::~LottieComponent()
{
if (mFuture.valid())
mFuture.get();
mTotalFrameCache -= mCacheSize;
}
void LottieComponent::setAnimation(const std::string& path) void LottieComponent::setAnimation(const std::string& path)
{ {
if (mAnimation != nullptr) { if (mAnimation != nullptr) {
@ -51,6 +73,7 @@ void LottieComponent::setAnimation(const std::string& path)
mSurface.reset(); mSurface.reset();
mAnimation.reset(); mAnimation.reset();
mPictureRGBA.clear(); mPictureRGBA.clear();
mCacheSize = 0;
} }
mPath = path; mPath = path;
@ -98,6 +121,7 @@ void LottieComponent::setAnimation(const std::string& path)
double duration = mAnimation->duration(); double duration = mAnimation->duration();
mTotalFrames = mAnimation->totalFrame(); mTotalFrames = mAnimation->totalFrame();
mFrameRate = mAnimation->frameRate(); mFrameRate = mAnimation->frameRate();
mFrameSize = width * height * 4;
mTargetPacing = static_cast<int>(1000.0 / mFrameRate); mTargetPacing = static_cast<int>(1000.0 / mFrameRate);
@ -109,7 +133,8 @@ void LottieComponent::setAnimation(const std::string& path)
void LottieComponent::onSizeChanged() void LottieComponent::onSizeChanged()
{ {
// Setting the animation again will completely reinitialize it. // Setting the animation again will completely reinitialize it.
setAnimation(mPath); if (mPath != "")
setAnimation(mPath);
} }
void LottieComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, void LottieComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
@ -156,16 +181,45 @@ void LottieComponent::render(const glm::mat4& parentTrans)
if (mFuture.valid()) { if (mFuture.valid()) {
if (mFuture.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready) { if (mFuture.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready) {
mFuture.get();
// Cache frame if caching is enabled and we're not exceeding either the per-file
// max cache size or the total cache size. Note that this is completely unrelated
// to the texture caching used for images.
if (mCacheFrames && mFrameCache.find(mFrameNum) == mFrameCache.end()) {
size_t newCacheSize = mCacheSize + mFrameSize;
if (newCacheSize < mMaxCacheSize &&
mTotalFrameCache + mFrameSize < mMaxTotalFrameCache) {
mFrameCache[mFrameNum] = mPictureRGBA;
mCacheSize += mFrameSize;
mTotalFrameCache += mFrameSize;
}
}
mTexture->initFromPixels(&mPictureRGBA.at(0), static_cast<size_t>(mSize.x), mTexture->initFromPixels(&mPictureRGBA.at(0), static_cast<size_t>(mSize.x),
static_cast<size_t>(mSize.y)); static_cast<size_t>(mSize.y));
mFuture.get();
++mFrameNum; ++mFrameNum;
renderNextFrame = true;
if (mFrameNum == mTotalFrames)
renderNextFrame = false;
else
renderNextFrame = true;
} }
} }
else { else {
renderNextFrame = true; if (mFrameCache.find(mFrameNum) != mFrameCache.end()) {
if (!mHoldFrame) {
mTexture->initFromPixels(&mFrameCache[mFrameNum][0], static_cast<size_t>(mSize.x),
static_cast<size_t>(mSize.y));
++mFrameNum;
}
}
else {
renderNextFrame = true;
}
} }
if (renderNextFrame && !mHoldFrame) if (renderNextFrame && !mHoldFrame)
mFuture = mAnimation->render(mFrameNum, *mSurface, mKeepAspectRatio); mFuture = mAnimation->render(mFrameNum, *mSurface, mKeepAspectRatio);

View file

@ -12,19 +12,27 @@
#include "GuiComponent.h" #include "GuiComponent.h"
#include "renderers/Renderer.h" #include "renderers/Renderer.h"
#include "resources/TextureResource.h" #include "resources/TextureResource.h"
#include "utils/MathUtil.h"
#include "rlottie.h" #include "rlottie.h"
#include <chrono> #include <chrono>
#include <future> #include <future>
#include <unordered_map>
class LottieComponent : public GuiComponent class LottieComponent : public GuiComponent
{ {
public: public:
LottieComponent(Window* window); LottieComponent(Window* window);
~LottieComponent();
void setAnimation(const std::string& path); void setAnimation(const std::string& path);
void setKeepAspectRatio(bool value) { mKeepAspectRatio = value; } void setKeepAspectRatio(bool value) { mKeepAspectRatio = value; }
void setFrameCaching(bool value) { mCacheFrames = value; }
void setMaxCacheSize(int value)
{
mMaxCacheSize = static_cast<size_t>(glm::clamp(value, 0, 1024) * 1024 * 1024);
}
void onSizeChanged() override; void onSizeChanged() override;
@ -39,6 +47,14 @@ private:
std::shared_ptr<TextureResource> mTexture; std::shared_ptr<TextureResource> mTexture;
std::vector<uint8_t> mPictureRGBA; std::vector<uint8_t> mPictureRGBA;
std::unordered_map<size_t, std::vector<uint8_t>> mFrameCache;
// Set a 1024 MiB total Lottie animation cache as default.
inline static size_t mMaxTotalFrameCache = 1024 * 1024 * 1024;
inline static size_t mTotalFrameCache;
bool mCacheFrames;
size_t mMaxCacheSize;
size_t mCacheSize;
size_t mFrameSize;
std::unique_ptr<rlottie::Animation> mAnimation; std::unique_ptr<rlottie::Animation> mAnimation;
std::unique_ptr<rlottie::Surface> mSurface; std::unique_ptr<rlottie::Surface> mSurface;
@ -53,6 +69,7 @@ private:
bool mKeepAspectRatio; bool mKeepAspectRatio;
// TEMPORARY.
std::chrono::time_point<std::chrono::system_clock> mAnimationStartTime; std::chrono::time_point<std::chrono::system_clock> mAnimationStartTime;
std::chrono::time_point<std::chrono::system_clock> mAnimationEndTime; std::chrono::time_point<std::chrono::system_clock> mAnimationEndTime;
}; };