[杂项] rlottie

前言

面向用户的企业级产品,UI 和交互会直接影响到用户的体验:鼠标交互可以附加 UI 的 hover, pressed 状态; 对于持续的状态 (Download, Uninstall, Update), 则可以考虑在功能入口引入 GIF 。下图是博主随意截取的 GIF (Sam Smith)

预备知识

  • Concurrency In Action: single-producer, single-consumer

    • Lock-free concurrent data structure:

      lock-free data structures rely on the use of atomic operations and the associated memory-ordering guarantees in order to ensure that data becomes visible to other threads in the correct order.

    • Guildllines:

      • use std::memory_order_seq_cst for prototyping
      • use a lock-free memory reclamation scheme
      • simplify it to the context:
        • only one thread calling push() at a time
        • only one thread calling pop() at a time
      • watch out for the ABA problem
      • identify busy-wait loops and help the other thread
  • Lottie:

    A lottie is JSON-based animation file format that allows you to ship animations on any playform as easily as shipping static assets.

    • Parse Lottie:

      • LottieAnimation: a bodymovin player for Qt
      • rlottie: a platform independent standalone c++ library for rendering vector based animations and art IN REALTIME
    • Tips on rlottie:

      • render mode: synchronize (rendersync) and asynchronzie (render)
      • cache policy: load from data with corresponding key to distinguish different cache

简介

客户端中使用动画的业务场景大致相同, 如下范式可供参考:

本博主负责的 AR 直播 (1080p59.94) 项目中使用过类似范式: 16.6ms 内使用最少的 cpu 时间处理 4 路输出, 而每路每帧的 1080p 图片 copy 耗时 3ms 左右哦.

  • desigen structure:
    • 设计 单生产单消费者 数据结构: lock_free_queue
    • 分析业务场景并简化之,以适用于 lock_free_queue
  • request render:
    • 通过异步接口 render() 请求渲染一帧图片,并得到对应的 future 对象.
    • future 对象存至对应的 lock_free_queue: render_queue
  • process request: 遍历所有 render_queue 中的所有 future 对象 (得益于良好的数据结构,业务逻辑无需锁)

准备阶段

本阶段就是要设计 (拿来主义) 对应的 无锁 数据结构啦, 一切从简, 就简单贴出标准的接口.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
template<typename T>
class lock_free_queue
{
private:
struct node
{
std::shared_ptr<T> data;
ndoe* next;
node():next(nullptr){}
};
std::atomic<ndoe*> head;
std::atomic<node*> tail;
node* pop_head(){
node* const old_head = head.load();
if(old_head == tail.load()) { // <order> 1
return nullptr;
}
head.store(old_head->next);
return old_head;
}
public:
lock_free_queue():head(new node), tail(head.load()){}
lock_free_queue(const lock_free_queue& other)=delete;
lock_free_queue& operator=(const lock_free_queue& other)=delete;
~lock_free_queue();

std::shared_ptr<T> pop(){
node* old_head=pop_head();
if(!old_head) return std::shared_ptr<T>();
std::shared_ptr<T> const res(old_head->data); // <order> 2
delete old_head;
return res;
}
void push(T new_value){
std::shared_ptr<T> new_data(std::make_shared<T>(new_value));
node* p = new node; // <order> 3
node* const old_tail = tail.load(); // <order> 4
old_tail->data.swap(new_data); // <order> 5
old_tail->next = p; // <order> 6
tail.store(p); // <order> 7
}
};

代码实现

本阶段仅仅列出 rlottie 接口相关的内容.

预备知识简介 是理论和实践经验的陈述. 其中涉及的异步处理框架, 需要根据项目的业务场景和采用的设计模式 (MVC, MVVM) 进行定制.

  • parseLottie: lottie 初始化, 包括: Animation, Surface 对象的构造, 堆内存分配
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void parseLottie(std::string lottie_file)
    {
    // load data
    auto lottieData = util::loadFromFile(lottie_file); // load lottie.json file from lottie_file
    // parse lottie and contruct `Animation`
    lottieAnimation = Animation::loadFromData(lottieData, lottie_file); // ****key: lottie_file****
    // alloc 1 frame buffer and construct `Surface`
    size_t bytesPerLine = cols * sizeof(unint32_t); // row bytes to iterate
    auto lottieBuffer = (uint32_t*)malloc(bytesPerline * rows);
    lottieSurface = Surface(lottieBuffer, cols, rows, bytesPerline);
    }
  • requestRender: 以 lottie 动画的帧率, 调用该接口完成 1 次异步渲染请求
    1
    2
    3
    4
    5
    6
    7
    void requestRender()
    {
    auto future = lottieAnimation->render(currentFrame, lottieSurface, true);
    currentFrame = (currentFrame + 1) % lottieAnimation->totalFrame();
    // lock-free queue
    lottieQueue->push(std::make_pair(key_to_index, future));
    }

插件集成 – Windows

  • Build rlottie
    • Install make on Windows: in case of command not found on make -j 2
    • Build libs for Windows: no need to flow rlottie
      • step 1: cmake .. produces Visual Studio Solution (default on Windows)
      • step 2: open rlottie.sln and build it with Visual Studio
  • Integrate rlottie
    • Header Files:

      • Visual Studio: setup Additional Include Directories
      • CMakeLists
        1. include_directories (
        2.   ${CMAKE_SOURCE_DIR}/rlottie/include
        3. )
    • Libs

      • Visual Studio: setup Additional Dependencies
      • CMakeLists
        1. target_link_libraries (${PROJECT_NAME}
        2. PRIVATE
        3. debug
        4. ${CMAKE_SOURCE_DIR}/rlottie/sdk/debug/rlottie.lib
        5. optimized
        6. ${CMAKE_SOURCE_DIR}/rlottie/sdk/release/rlottie.lib
        7. )
    • Dlls

      • Visual Studio: copy them to Output Directory
      • CMakeLists
        1. cmake_path(CONVERT  " ${CMAKE_SOURCE_DIR} /rlottie/sdk/$/*.dll TO_NATIVE_PATH_LIST rlottie_dll_path)
        2. cmake_path(CONVERT "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/" TO_NATIVE_PATH_LIST runtime_output_ path)
        3. add_custom_command(TARGET${XXX_TARGET_NAME} POST_BUILD
        4.     COMMAND xcopy "${rlottie_dll_path}" "${runtime_output_path}" /y
        5.     WORKING_DIRECTORY${CMAKE_CURRENT_SOURCE_DIR}
        6.     COMMAND "Copying thirdparty files" VERBATIM
        7.    )

[杂项] rlottie
https://jalencui.com/2023/02/18/CPP-Animation-rlottie/
Author
Jalen Cui
Posted on
February 18, 2023
Licensed under