mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-25 23:55:38 +00:00
Added threading support and proper frame pacing to LottieComponent.
This commit is contained in:
parent
0c552dd8fb
commit
3ee4e4cb35
|
@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue