Added threading support and proper frame pacing to LottieComponent.

This commit is contained in:
Leon Styhre 2022-01-07 19:09:07 +01:00
parent 0c552dd8fb
commit 3ee4e4cb35
2 changed files with 68 additions and 80 deletions

View file

@ -20,16 +20,19 @@ LottieComponent::LottieComponent(Window* window)
, mAnimation{nullptr} , mAnimation{nullptr}
, mSurface{nullptr} , mSurface{nullptr}
, mTotalFrames{0} , mTotalFrames{0}
, mLastRenderedFrame{0} , mFrameNum{0}
, mLastDisplayedFrame{0}
, mFrameRate{0.0} , mFrameRate{0.0}
, mTargetPacing{0} , mTargetPacing{0}
, mSkipAccumulator{0} , mTimeAccumulator{0}
, mSkipFrame{false} , mHoldFrame{false}
{ {
// Get an empty texture for rendering the animation. // Get an empty texture for rendering the animation.
mTexture = TextureResource::get(""); mTexture = TextureResource::get("");
mBuffer.clear(); #if defined(USE_OPENGLES_10)
// This is not really supported by the OpenGL ES standard so hopefully it works
// with all drivers and on all operating systems.
mTexture->setFormat(Renderer::Texture::BGRA);
#endif
// TODO: Temporary test files. // TODO: Temporary test files.
std::string filePath{":/animations/a_mountain.json"}; std::string filePath{":/animations/a_mountain.json"};
@ -67,11 +70,11 @@ LottieComponent::LottieComponent(Window* window)
size_t width = static_cast<size_t>(mSize.x); size_t width = static_cast<size_t>(mSize.x);
size_t height = static_cast<size_t>(mSize.y); size_t height = static_cast<size_t>(mSize.y);
size_t bytesPerLine = width * sizeof(uint32_t);
mBuffer.resize(width * height); mPictureRGBA.resize(width * height * 4);
mSurface = std::make_unique<rlottie::Surface>(&mBuffer[0], width, height, bytesPerLine); mSurface = std::make_unique<rlottie::Surface>(reinterpret_cast<uint32_t*>(&mPictureRGBA[0]),
width, height, width * sizeof(uint32_t));
// Animation time in seconds. // Animation time in seconds.
double duration = mAnimation->duration(); double duration = mAnimation->duration();
@ -97,43 +100,14 @@ void LottieComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
void LottieComponent::update(int deltaTime) void LottieComponent::update(int deltaTime)
{ {
// LOG(LogDebug) << "deltatime: " << deltaTime; if (mTimeAccumulator < mTargetPacing) {
mHoldFrame = true;
if (deltaTime + mSkipAccumulator < mTargetPacing) { mTimeAccumulator += deltaTime;
mSkipFrame = true;
mSkipAccumulator += deltaTime;
} }
else { else {
mSkipFrame = false; mHoldFrame = false;
mSkipAccumulator = 0; mTimeAccumulator = mTimeAccumulator - mTargetPacing + deltaTime;
} }
if (mLastRenderedFrame == mTotalFrames)
return;
// const auto updateStartTime = std::chrono::system_clock::now();
mAnimation->renderSync(mLastRenderedFrame, *mSurface);
mPictureRGBA.clear();
// TODO: This is way too slow.
for (auto i : mBuffer) {
mPictureRGBA.emplace_back((i >> 16) & 0xff);
mPictureRGBA.emplace_back((i >> 8) & 0xff);
mPictureRGBA.emplace_back(i & 0xff);
mPictureRGBA.emplace_back(i >> 24);
}
if (mBuffer2.size() < mTotalFrames)
mBuffer2.emplace_back(mPictureRGBA);
++mLastRenderedFrame;
// LOG(LogDebug) << "Update cycle time: "
// << std::chrono::duration_cast<std::chrono::milliseconds>(
// std::chrono::system_clock::now() - updateStartTime)
// .count()
// << " ms";
} }
void LottieComponent::render(const glm::mat4& parentTrans) void LottieComponent::render(const glm::mat4& parentTrans)
@ -141,45 +115,61 @@ void LottieComponent::render(const glm::mat4& parentTrans)
if (!isVisible()) if (!isVisible())
return; return;
if (mPictureRGBA.empty()) if (mFrameNum >= mTotalFrames)
return; mFrameNum = 0;
if (!mSkipFrame && mLastDisplayedFrame == mTotalFrames - 1) if (mFrameNum == 0)
mLastDisplayedFrame = 0;
if (mLastDisplayedFrame == 0)
mAnimationStartTime = std::chrono::system_clock::now(); mAnimationStartTime = std::chrono::system_clock::now();
glm::mat4 trans{parentTrans * getTransform()}; bool renderNextFrame = false;
mTexture->initFromPixels(&mBuffer2.at(mLastDisplayedFrame).at(0), static_cast<size_t>(mSize.x), if (mFuture.valid()) {
static_cast<size_t>(mSize.y)); if (mFuture.wait_for(std::chrono::milliseconds(1)) == std::future_status::ready) {
mTexture->bind(); mTexture->initFromPixels(&mPictureRGBA.at(0), static_cast<size_t>(mSize.x),
static_cast<size_t>(mSize.y));
mFuture.get();
++mFrameNum;
renderNextFrame = true;
}
}
else {
renderNextFrame = true;
}
if (!mSkipFrame) if (renderNextFrame && !mHoldFrame)
++mLastDisplayedFrame; mFuture = mAnimation->render(mFrameNum, *mSurface);
// clang-format off if (mTexture->getSize().x != 0.0f) {
mVertices[0] = {{0.0f, 0.0f }, {0.0f, 0.0f}, 0xFFFFFFFF}; mTexture->bind();
mVertices[1] = {{0.0f, mSize.y}, {0.0f, 1.0f}, 0xFFFFFFFF};
mVertices[2] = {{mSize.x, 0.0f }, {1.0f, 0.0f}, 0xFFFFFFFF};
mVertices[3] = {{mSize.x, mSize.y}, {1.0f, 1.0f}, 0xFFFFFFFF};
// clang-format on
// Round vertices. // clang-format off
for (int i = 0; i < 4; ++i) mVertices[0] = {{0.0f, 0.0f }, {0.0f, 0.0f}, 0xFFFFFFFF};
mVertices[i].pos = glm::round(mVertices[i].pos); mVertices[1] = {{0.0f, mSize.y}, {0.0f, 1.0f}, 0xFFFFFFFF};
mVertices[2] = {{mSize.x, 0.0f }, {1.0f, 0.0f}, 0xFFFFFFFF};
mVertices[3] = {{mSize.x, mSize.y}, {1.0f, 1.0f}, 0xFFFFFFFF};
// clang-format on
// Render it. // Round vertices.
Renderer::setMatrix(trans); for (int i = 0; i < 4; ++i)
Renderer::drawTriangleStrips(&mVertices[0], 4, trans); mVertices[i].pos = glm::round(mVertices[i].pos);
if (!mSkipFrame && mLastDisplayedFrame == mTotalFrames - 1) { #if defined(USE_OPENGL_21)
// Perform color space conversion from BGRA to RGBA.
mVertices[0].shaders = Renderer::SHADER_BGRA_TO_RGBA;
#endif
// Render it.
glm::mat4 trans{parentTrans * getTransform()};
Renderer::setMatrix(trans);
Renderer::drawTriangleStrips(&mVertices[0], 4, trans);
}
if (!mHoldFrame && mFrameNum == mTotalFrames - 1) {
mAnimationEndTime = std::chrono::system_clock::now(); mAnimationEndTime = std::chrono::system_clock::now();
LOG(LogInfo) << "Actual duration time: " LOG(LogDebug) << "LottieComponent::render(): Animation duration: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(mAnimationEndTime - << std::chrono::duration_cast<std::chrono::milliseconds>(mAnimationEndTime -
mAnimationStartTime) mAnimationStartTime)
.count() .count()
<< " ms"; << " ms";
} }
} }

View file

@ -16,6 +16,7 @@
#include "rlottie.h" #include "rlottie.h"
#include <chrono> #include <chrono>
#include <future>
class LottieComponent : public GuiComponent class LottieComponent : public GuiComponent
{ {
@ -33,21 +34,18 @@ private:
std::shared_ptr<TextureResource> mTexture; std::shared_ptr<TextureResource> mTexture;
std::vector<uint8_t> mPictureRGBA; std::vector<uint8_t> mPictureRGBA;
std::vector<std::vector<uint8_t>> mBuffer2;
std::unique_ptr<rlottie::Animation> mAnimation;
Renderer::Vertex mVertices[4]; Renderer::Vertex mVertices[4];
std::vector<uint32_t> mBuffer; std::unique_ptr<rlottie::Animation> mAnimation;
std::unique_ptr<rlottie::Surface> mSurface; std::unique_ptr<rlottie::Surface> mSurface;
std::future<rlottie::Surface> mFuture;
size_t mTotalFrames; size_t mTotalFrames;
size_t mLastRenderedFrame; size_t mFrameNum;
size_t mLastDisplayedFrame;
double mFrameRate; double mFrameRate;
int mTargetPacing; int mTargetPacing;
int mSkipAccumulator; int mTimeAccumulator;
bool mSkipFrame; bool mHoldFrame;
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;