blob: b43a5ba6393e16347ca43172582f374de43a02ed [file] [edit]
/*
* Copyright 2026 Rive
*/
// Per-frame cost of real .riv files: the state machine's advanceAndApply (which
// runs the artboard's advance and update passes) plus the artboard's draw.
//
// Run from packages/runtime/tests, e.g.
//
// out/release/bench --duration 10 ArtboardFrame_car_widgets_idle
//
// Assets load from unit_tests/assets unless RIVE_BENCH_ASSETS names another
// directory. Every run() plays kFramesPerRun frames, so the time bench prints
// divided by kFramesPerRun is the cost of one frame. Before each timed run the
// bench restarts from a fresh instance warmed up the same way, so every sample
// plays the same frames of the authored animation. Each bench also prints,
// once from setup(), the work it does per frame over the same scenario:
// render path rebuilds (each drops the renderer's cached triangulation),
// gradients, renderer calls, allocations, and how many frames asked to keep
// animating. Those counts are exact, so they show work removed even where a
// timing is inside the noise. (Allocations aren't counted in Dawn builds; see
// below.) A bench whose .riv can't be read or imported says so and skips, so
// CI can run every bench once on a device that has no assets.
//
// Two renderers:
// - a counting no-op renderer: the runtime's own advance/update/draw cost,
// with the counters above;
// - (_rive) the real RiveRenderer over a null GPU context, so the renderer's
// CPU work -- tessellation, triangulation and its caches -- is included.
#include "bench.hpp"
#include "common/render_context_null.hpp"
#include "assets/paper.riv.hpp"
#include "rive/artboard.hpp"
#include "rive/animation/state_machine_instance.hpp"
#include "rive/constraints/scrolling/scroll_constraint.hpp"
#include "rive/file.hpp"
#include "rive/generated/layout/layout_component_style_base.hpp"
#include "rive/layout_component.hpp"
#include "rive/layout/layout_enums.hpp"
#include "rive/math/math_types.hpp"
#include "rive/nested_artboard.hpp"
#include "rive/node.hpp"
#include "rive/renderer/render_context.hpp"
#include "rive/renderer/rive_renderer.hpp"
#include "rive/scene.hpp"
#include "rive/shapes/paint/shape_paint.hpp"
#include "rive/shapes/shape.hpp"
#include "rive/viewmodel/viewmodel_instance.hpp"
#include "rive/viewmodel/viewmodel_instance_list.hpp"
#include "rive/viewmodel/viewmodel_instance_list_item.hpp"
#include "utils/no_op_renderer.hpp"
#include <algorithm>
#include <atomic>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iterator>
#include <memory>
#include <new>
#include <string>
#include <vector>
using namespace rive;
using namespace rive::gpu;
// -- Allocation counting ------------------------------------------------------
//
// Replaces the global allocation functions for the whole bench binary.
// Counting is off unless a bench's counter pass turns it on, so every other
// bench only pays a relaxed load per allocation. Dawn builds leave this out:
// they link PartitionAlloc's allocator shim, which already replaces them.
#ifndef RIVE_DAWN
#define ARTBOARD_FRAME_BENCH_COUNTS_ALLOCATIONS
#endif
namespace
{
std::atomic<bool> g_countAllocations{false};
std::atomic<uint64_t> g_allocations{0};
#ifdef ARTBOARD_FRAME_BENCH_COUNTS_ALLOCATIONS
void* countedAllocation(size_t size)
{
if (g_countAllocations.load(std::memory_order_relaxed))
{
g_allocations.fetch_add(1, std::memory_order_relaxed);
}
void* ptr = malloc(size != 0 ? size : 1);
if (ptr == nullptr)
{
abort();
}
return ptr;
}
#endif
} // namespace
#ifdef ARTBOARD_FRAME_BENCH_COUNTS_ALLOCATIONS
// The array forms are replaceable in their own right, so route them here too
// rather than trusting the library's defaults to call the scalar ones.
void* operator new(size_t size) { return countedAllocation(size); }
void* operator new[](size_t size) { return countedAllocation(size); }
void operator delete(void* ptr) noexcept { free(ptr); }
void operator delete[](void* ptr) noexcept { free(ptr); }
void operator delete(void* ptr, size_t) noexcept { free(ptr); }
void operator delete[](void* ptr, size_t) noexcept { free(ptr); }
#endif
namespace
{
// -- Work counters ------------------------------------------------------------
struct WorkCounters
{
uint64_t renderPathsMade = 0;
uint64_t renderPathRewinds = 0;
uint64_t renderPathRawAdds = 0;
uint64_t gradientsMade = 0;
uint64_t renderPaintsMade = 0;
uint64_t saves = 0;
uint64_t restores = 0;
uint64_t transforms = 0;
uint64_t drawPaths = 0;
uint64_t clipPaths = 0;
uint64_t drawImages = 0;
};
// The bench process runs one bench on one thread, so a plain global is enough.
WorkCounters g_counters;
class CountingRenderPath : public RenderPath
{
public:
void rewind() override { g_counters.renderPathRewinds++; }
void fillRule(FillRule value) override {}
void addPath(CommandPath* path, const Mat2D& transform) override {}
void addRenderPath(const RenderPath* path, const Mat2D& transform) override {}
void moveTo(float x, float y) override {}
void lineTo(float x, float y) override {}
void cubicTo(float ox, float oy, float ix, float iy, float x, float y)
override
{}
void close() override {}
void addRawPath(const RawPath& path) override
{
g_counters.renderPathRawAdds++;
}
};
class CountingRenderPaint : public RenderPaint
{
public:
void color(unsigned int value) override {}
void style(RenderPaintStyle value) override {}
void thickness(float value) override {}
void join(StrokeJoin value) override {}
void cap(StrokeCap value) override {}
void blendMode(BlendMode value) override {}
void shader(rcp<RenderShader>) override {}
void shaderTransform(const Mat2D&) override {}
void invalidateStroke() override {}
void feather(float value) override {}
void modulatedImage(const RenderImage*, ImageSampler, const Mat2D&) override
{}
};
class CountingRenderShader : public RenderShader
{};
// Mirrors utils/no_op_factory.cpp (not built into this target), counting what
// the runtime asks it to make.
class CountingFactory : public Factory
{
public:
rcp<RenderBuffer> makeRenderBuffer(
RenderBufferType,
RenderBufferFlags,
size_t) override
{
return nullptr;
}
rcp<RenderShader> makeLinearGradient(
float,
float,
float,
float,
const ColorInt[],
const float[],
size_t) override
{
g_counters.gradientsMade++;
return make_rcp<CountingRenderShader>();
}
rcp<RenderShader> makeRadialGradient(
float,
float,
float,
const ColorInt[],
const float[],
size_t) override
{
g_counters.gradientsMade++;
return make_rcp<CountingRenderShader>();
}
rcp<RenderShader> makeAngularGradient(const ColorInt[], const float[], size_t)
override
{
g_counters.gradientsMade++;
return make_rcp<CountingRenderShader>();
}
rcp<RenderPath> makeRenderPath(RawPath&, FillRule) override
{
g_counters.renderPathsMade++;
return make_rcp<CountingRenderPath>();
}
rcp<RenderPath> makeEmptyRenderPath() override
{
g_counters.renderPathsMade++;
return make_rcp<CountingRenderPath>();
}
rcp<RenderPaint> makeRenderPaint() override
{
g_counters.renderPaintsMade++;
return make_rcp<CountingRenderPaint>();
}
rcp<RenderImage> decodeImage(Span<const uint8_t>) override { return nullptr; }
};
class CountingRenderer : public NoOpRenderer
{
public:
void save() override { g_counters.saves++; }
void restore() override { g_counters.restores++; }
void transform(const Mat2D&) override { g_counters.transforms++; }
void drawPath(RenderPath*, RenderPaint*) override { g_counters.drawPaths++; }
void clipPath(RenderPath*) override { g_counters.clipPaths++; }
void drawImage(const RenderImage*, ImageSampler, BlendMode, float) override
{
g_counters.drawImages++;
}
};
// -- Scenarios
// ------------------------------------------------------------------
enum class Scenario
{
// The default state machine as authored.
idle,
// Every top-level node translates together each frame: a rigid move of the
// whole scene, what scrolling or a parent animation does to its contents.
move,
// Only one leaf shape moves: a large, mostly idle artboard.
poke,
// The artboard's opacity animates: a fade over everything.
fade,
// Every scroll constraint sweeps its full range.
scroll,
// One layout's corner radius animates: a style change that only reshapes
// that layout's background.
round,
// One layout keeps tweening: its width flips every half second and
// animates to the new width over half a second.
tween,
// Every view model list grows to kLongListItems rows, each a default
// instance of its first item's view model, then the scene idles: in a
// virtualized list most rows stay unrealized, so this is what each per-row
// pass costs.
longList,
// As longList, then the lists' scroll constraints sweep a tenth of their
// content back and forth, so rows keep being realized and recycled.
longListScroll,
};
constexpr size_t kLongListItems = 1000;
enum class RendererKind
{
counting,
rive,
};
constexpr int kWarmupFrames = 120;
constexpr int kFramesPerRun = 120;
constexpr float kFrameSeconds = 1.0f / 60.0f;
// Reads unit_tests/assets/[name], or [name] under RIVE_BENCH_ASSETS. False when
// it isn't there -- CI runs every bench once from places without the assets.
bool readAsset(const char* name, std::vector<uint8_t>* bytes)
{
#ifdef NO_GETENV
// Consoles have no environment.
const char* dir = nullptr;
#else
const char* dir = getenv("RIVE_BENCH_ASSETS");
#endif
std::string path = std::string(dir != nullptr ? dir : "unit_tests/assets") +
"/" + name;
std::ifstream stream(path, std::ios::binary);
if (!stream)
{
return false;
}
bytes->assign(
std::istreambuf_iterator<char>(stream),
std::istreambuf_iterator<char>());
return true;
}
class ArtboardFrameBench : public Bench
{
public:
// A null asset name selects the embedded paper.riv.
ArtboardFrameBench(
const char* asset,
Scenario scenario,
RendererKind rendererKind)
: m_asset(asset)
, m_scenario(scenario)
, m_rendererKind(rendererKind)
{}
void setup() override
{
Factory* factory = &m_countingFactory;
if (m_rendererKind == RendererKind::rive)
{
m_nullContext = RenderContextNULL::MakeContext();
factory = m_nullContext.get();
}
if (m_asset != nullptr)
{
if (!readAsset(m_asset, &m_bytes))
{
skip("can't open it (run from packages/runtime/tests, or "
"point RIVE_BENCH_ASSETS at tests/unit_tests/assets)");
return;
}
m_file = File::import(
Span<const uint8_t>(m_bytes.data(), m_bytes.size()),
factory);
}
else
{
m_file = File::import(assets::paper_riv(), factory);
}
if (m_file == nullptr)
{
skip("it didn't import (an unfetched Git LFS pointer?)");
return;
}
instantiate();
if (m_rendererKind == RendererKind::rive)
{
m_renderer = std::make_unique<RiveRenderer>(m_nullContext.get());
auto width = std::max(1u, static_cast<uint32_t>(m_artboard->width()));
auto height = std::max(1u, static_cast<uint32_t>(m_artboard->height()));
m_renderTarget = m_nullContext->static_impl_cast<RenderContextNULL>()
->makeRenderTarget(width, height);
}
else
{
m_renderer = std::make_unique<CountingRenderer>();
}
reportScenarioTargets();
warmUp();
printCounters();
}
// Without this the authored animation keeps running from one sample to
// the next, each sample covers different frames, and bench keeps
// whichever happened to be cheapest.
void beforeRun() override
{
if (m_skipped)
{
return;
}
instantiate();
warmUp();
}
int run() const override
{
if (m_skipped)
{
return 0;
}
int keepGoing = 0;
for (int i = 0; i < kFramesPerRun; ++i)
{
keepGoing += frame() ? 1 : 0;
}
return keepGoing;
}
private:
void skip(const char* reason)
{
fprintf(
stderr,
"artboard_frame_bench: skipping %s: %s\n",
m_asset != nullptr ? m_asset : "paper.riv",
reason);
m_skipped = true;
}
// A fresh instance of the default artboard and its scene, with the
// view model and scene chosen the way the player does
// (tests/player/player.cpp).
void instantiate()
{
// The scene refers to the artboard, so it goes first.
m_scene = nullptr;
m_movers.clear();
m_scrolls.clear();
m_roundedStyle = nullptr;
m_roundedLayoutDraws = false;
m_tweenStyle = nullptr;
m_tweenLayout = nullptr;
m_tweenLayoutNested = false;
m_longLists = 0;
m_artboard = m_file->artboardDefault();
m_viewModelInstance = m_file->createDefaultViewModelInstance(
m_artboard.get());
if (m_viewModelInstance != nullptr)
{
m_artboard->bindViewModelInstance(m_viewModelInstance);
}
std::unique_ptr<StateMachineInstance>
stateMachine = m_artboard->defaultStateMachine();
if (stateMachine == nullptr && m_artboard->stateMachineCount() > 0)
{
stateMachine = m_artboard->stateMachineAt(0);
}
m_scene = std::move(stateMachine);
if (m_scene == nullptr)
{
m_scene = m_artboard->animationAt(0);
}
if (m_scene != nullptr && m_viewModelInstance != nullptr)
{
m_scene->bindViewModelInstance(m_viewModelInstance);
}
collectScenarioTargets();
m_frame = 0;
}
void warmUp()
{
for (int i = 0; i < kWarmupFrames; ++i)
{
frame();
}
}
void collectScenarioTargets()
{
switch (m_scenario)
{
case Scenario::move:
for (auto object : m_artboard->objects())
{
if (object == nullptr || object == m_artboard.get() ||
!object->is<Node>() ||
object->as<Node>()->parent() != m_artboard.get())
{
continue;
}
auto node = object->as<Node>();
// Files from before 7.3 let the layout own a layout's x/y,
// so moving one of those would measure nothing.
if (node->is<LayoutComponent>() &&
!node->as<LayoutComponent>()->composesLayoutOffset())
{
continue;
}
m_movers.push_back({node, node->x(), node->y()});
}
break;
case Scenario::poke:
for (auto shape : m_artboard->find<Shape>())
{
m_movers.push_back({shape, shape->x(), shape->y()});
break;
}
break;
case Scenario::scroll:
m_scrolls = m_artboard->find<ScrollConstraint>();
break;
case Scenario::round:
{
// Prefer a layout that draws, so its own background path
// rebuilds too; otherwise take the first one with a style.
// Styles are found through the object list:
// LayoutComponent::style() returns a type this target can't
// include.
std::vector<ContainerComponent*> painted;
for (auto object : m_artboard->objects())
{
if (object != nullptr && object->is<ShapePaint>())
{
painted.push_back(object->as<ShapePaint>()->parent());
}
}
for (auto object : m_artboard->objects())
{
if (object == nullptr || !object->is<LayoutComponentStyleBase>())
{
continue;
}
auto style = object->as<LayoutComponentStyleBase>();
auto layout = style->parent();
if (layout == m_artboard.get())
{
continue;
}
bool draws = std::find(painted.begin(), painted.end(), layout) !=
painted.end();
if (m_roundedStyle == nullptr || draws)
{
m_roundedStyle = style;
m_roundedLayoutDraws = draws;
}
if (draws)
{
break;
}
}
if (m_roundedStyle != nullptr)
{
// One radius for all four corners.
m_roundedStyle->linkCornerRadius(true);
}
break;
}
case Scenario::tween:
{
// The last styled layout in file order, likely a leaf (a card
// or a button) rather than the page it sits in. UI files tend
// to build those in nested artboards, so look in those before
// the artboard itself. Styles are found through the object
// list, as for round.
std::vector<Artboard*> artboards;
for (auto nested : m_artboard->find<NestedArtboard>())
{
if (nested->artboardInstance() != nullptr)
{
artboards.push_back(nested->artboardInstance());
}
}
artboards.push_back(m_artboard.get());
for (auto artboard : artboards)
{
for (auto object : artboard->objects())
{
if (object == nullptr || !object->is<LayoutComponentStyleBase>())
{
continue;
}
auto layout = object->as<LayoutComponentStyleBase>()->parent();
if (layout != artboard && layout->is<LayoutComponent>())
{
m_tweenStyle = object->as<LayoutComponentStyleBase>();
m_tweenLayout = layout->as<LayoutComponent>();
m_tweenLayoutNested = artboard != m_artboard.get();
}
}
if (m_tweenLayout != nullptr)
{
break;
}
}
if (m_tweenStyle != nullptr)
{
// Its own linear half-second tween, and a width fixed in
// points so the flips below resize it.
m_tweenStyle->animationStyleType(
(uint8_t)LayoutAnimationStyle::custom);
m_tweenStyle->interpolationType(
(uint8_t)LayoutStyleInterpolation::linear);
m_tweenStyle->interpolationTime(0.5f);
m_tweenStyle->layoutWidthScaleType((uint8_t)LayoutScaleType::fixed);
m_tweenStyle->widthUnitsValue(1); // YGUnitPoint
}
break;
}
case Scenario::longList:
growLists();
break;
case Scenario::longListScroll:
growLists();
m_scrolls = m_artboard->find<ScrollConstraint>();
break;
case Scenario::idle:
case Scenario::fade:
break;
}
}
void growLists()
{
if (m_viewModelInstance == nullptr)
{
return;
}
for (auto& value : m_viewModelInstance->propertyValues())
{
if (!value->is<ViewModelInstanceList>())
{
continue;
}
auto list = value->as<ViewModelInstanceList>();
const size_t count = list->listItems().size();
if (count == 0)
{
continue;
}
auto viewModel = list->listItems()[0]->viewModelInstance()->viewModel();
for (size_t i = count; i < kLongListItems; i++)
{
auto item = make_rcp<ViewModelInstanceListItem>();
item->viewModelInstance(
m_file->createDefaultViewModelInstance(viewModel));
list->addItem(item);
}
m_longLists++;
}
}
void reportScenarioTargets() const
{
if (m_scenario == Scenario::move || m_scenario == Scenario::poke)
{
fprintf(
stderr,
"artboard_frame_bench: moving %zu nodes\n",
m_movers.size());
}
if (m_scenario == Scenario::scroll && m_scrolls.empty())
{
fprintf(stderr, "artboard_frame_bench: no scroll constraint\n");
}
if (m_scenario == Scenario::tween)
{
fprintf(
stderr,
"artboard_frame_bench: %s\n",
m_tweenLayout == nullptr ? "no styled layout"
: m_tweenLayoutNested ? "tweening a layout in a nested artboard"
: "tweening a layout in the artboard");
}
if (m_scenario == Scenario::longList ||
m_scenario == Scenario::longListScroll)
{
fprintf(
stderr,
"artboard_frame_bench: %d lists grown to %zu rows\n",
m_longLists,
kLongListItems);
}
if (m_scenario == Scenario::longListScroll && m_scrolls.empty())
{
fprintf(stderr, "artboard_frame_bench: no scroll constraint\n");
}
if (m_scenario == Scenario::round)
{
fprintf(
stderr,
"artboard_frame_bench: %s\n",
m_roundedStyle == nullptr ? "no styled layout"
: m_roundedLayoutDraws ? "rounding a layout that draws"
: "rounding a layout that draws nothing");
}
}
// Returns what advanceAndApply returned: whether the scene wants another
// frame.
bool frame() const
{
// One full period of every scenario per run, so each timed run covers
// the same work.
float phase = 2.0f * math::PI * (float)(m_frame % kFramesPerRun) /
(float)kFramesPerRun;
m_frame++;
switch (m_scenario)
{
case Scenario::move:
case Scenario::poke:
for (const auto& mover : m_movers)
{
mover.node->x(mover.x + 20.0f * std::sin(phase));
mover.node->y(mover.y + 10.0f * std::cos(phase));
}
break;
case Scenario::fade:
m_artboard->opacity(0.55f + 0.45f * std::cos(phase));
break;
case Scenario::scroll:
for (auto scroll : m_scrolls)
{
float t = 0.5f - 0.5f * std::cos(phase);
scroll->scrollOffsetX(scroll->maxOffsetX() * t);
scroll->scrollOffsetY(scroll->maxOffsetY() * t);
}
break;
case Scenario::tween:
// m_frame already counts this frame. Every 30 frames, the
// length of the tween, so one is always running.
if (m_tweenLayout != nullptr && (m_frame - 1) % 30 == 0)
{
m_tweenLayout->width((m_frame - 1) % 60 == 0 ? 60.0f : 100.0f);
}
break;
case Scenario::round:
if (m_roundedStyle != nullptr)
{
m_roundedStyle->cornerRadiusTL(8.0f + 8.0f * std::sin(phase));
}
break;
case Scenario::longListScroll:
for (auto scroll : m_scrolls)
{
float t = 0.5f - 0.5f * std::cos(phase);
if (scroll->constrainsHorizontal())
{
scroll->scrollOffsetX(-0.1f * scroll->contentWidth() * t);
}
if (scroll->constrainsVertical())
{
scroll->scrollOffsetY(-0.1f * scroll->contentHeight() * t);
}
}
break;
case Scenario::idle:
case Scenario::longList:
break;
}
bool keepGoing = false;
if (m_scene != nullptr)
{
keepGoing = m_scene->advanceAndApply(kFrameSeconds);
}
else
{
keepGoing = m_artboard->advance(kFrameSeconds);
}
if (m_rendererKind == RendererKind::rive)
{
m_nullContext->beginFrame({
.renderTargetWidth = m_renderTarget->width(),
.renderTargetHeight = m_renderTarget->height(),
});
m_artboard->draw(m_renderer.get());
m_nullContext->flush({.renderTarget = m_renderTarget.get()});
}
else
{
m_artboard->draw(m_renderer.get());
}
return keepGoing;
}
void printCounters()
{
g_counters = WorkCounters();
g_allocations.store(0);
g_countAllocations.store(true);
int keepGoing = 0;
for (int i = 0; i < kFramesPerRun; ++i)
{
keepGoing += frame() ? 1 : 0;
}
g_countAllocations.store(false);
auto perFrame = [](uint64_t value) {
return (double)value / (double)kFramesPerRun;
};
#ifdef ARTBOARD_FRAME_BENCH_COUNTS_ALLOCATIONS
printf(
"[%d frames/run] per frame: allocs %.1f",
kFramesPerRun,
perFrame(g_allocations.load()));
#else
printf("[%d frames/run] per frame: allocs n/a", kFramesPerRun);
#endif
if (m_rendererKind == RendererKind::counting)
{
printf(
", path rebuilds %.1f (rewind %.1f, addRawPath %.1f), "
"paths made %.1f, gradients %.1f, paints made %.1f, "
"save %.1f, restore %.1f, transform %.1f, drawPath %.1f, "
"clipPath %.1f, drawImage %.1f",
perFrame(g_counters.renderPathRewinds),
perFrame(g_counters.renderPathRewinds),
perFrame(g_counters.renderPathRawAdds),
perFrame(g_counters.renderPathsMade),
perFrame(g_counters.gradientsMade),
perFrame(g_counters.renderPaintsMade),
perFrame(g_counters.saves),
perFrame(g_counters.restores),
perFrame(g_counters.transforms),
perFrame(g_counters.drawPaths),
perFrame(g_counters.clipPaths),
perFrame(g_counters.drawImages));
}
printf(", keep-going frames %d/%d\n", keepGoing, kFramesPerRun);
fflush(stdout);
}
struct Mover
{
Node* node;
float x;
float y;
};
const char* m_asset;
Scenario m_scenario;
RendererKind m_rendererKind;
CountingFactory m_countingFactory;
std::unique_ptr<RenderContext> m_nullContext;
rcp<RenderTarget> m_renderTarget;
std::vector<uint8_t> m_bytes;
rcp<File> m_file;
std::unique_ptr<ArtboardInstance> m_artboard;
rcp<ViewModelInstance> m_viewModelInstance;
std::unique_ptr<Scene> m_scene;
std::unique_ptr<Renderer> m_renderer;
std::vector<Mover> m_movers;
std::vector<ScrollConstraint*> m_scrolls;
LayoutComponentStyleBase* m_roundedStyle = nullptr;
bool m_roundedLayoutDraws = false;
LayoutComponentStyleBase* m_tweenStyle = nullptr;
LayoutComponent* m_tweenLayout = nullptr;
bool m_tweenLayoutNested = false;
int m_longLists = 0;
mutable uint64_t m_frame = 0;
bool m_skipped = false;
};
#define ARTBOARD_FRAME_BENCH(NAME, ASSET, SCENARIO, RENDERER) \
class ArtboardFrame_##NAME : public ArtboardFrameBench \
{ \
public: \
ArtboardFrame_##NAME() \
: ArtboardFrameBench( \
ASSET, \
Scenario::SCENARIO, \
RendererKind::RENDERER) \
{} \
}; \
REGISTER_BENCH(ArtboardFrame_##NAME);
// Layout UI.
ARTBOARD_FRAME_BENCH(
db_health_tracker_idle,
"db_health_tracker.riv",
idle,
counting)
ARTBOARD_FRAME_BENCH(
db_health_tracker_idle_rive,
"db_health_tracker.riv",
idle,
rive)
ARTBOARD_FRAME_BENCH(
db_health_tracker_round,
"db_health_tracker.riv",
round,
counting)
ARTBOARD_FRAME_BENCH(data_viz_demo_idle, "data_viz_demo.riv", idle, counting)
ARTBOARD_FRAME_BENCH(data_viz_demo_round, "data_viz_demo.riv", round, counting)
ARTBOARD_FRAME_BENCH(
db_health_tracker_tween,
"db_health_tracker.riv",
tween,
counting)
ARTBOARD_FRAME_BENCH(data_viz_demo_tween, "data_viz_demo.riv", tween, counting)
ARTBOARD_FRAME_BENCH(
layout_paint_idle,
"layout/layout_paint.riv",
idle,
counting)
ARTBOARD_FRAME_BENCH(
layout_paint_move,
"layout/layout_paint.riv",
move,
counting)
ARTBOARD_FRAME_BENCH(
layout_paint_move_rive,
"layout/layout_paint.riv",
move,
rive)
ARTBOARD_FRAME_BENCH(
layout_animation_nested_idle,
"layout/layout_animation_nested.riv",
idle,
counting)
ARTBOARD_FRAME_BENCH(
layout_animation_nested_round,
"layout/layout_animation_nested.riv",
round,
counting)
ARTBOARD_FRAME_BENCH(
layout_animation_component_list_idle,
"layout/layout_animation_component_list.riv",
idle,
counting)
ARTBOARD_FRAME_BENCH(
layout_scroll_vertical_scroll,
"layout/layout_scroll_vertical.riv",
scroll,
counting)
ARTBOARD_FRAME_BENCH(
layout_scroll_vertical_scroll_rive,
"layout/layout_scroll_vertical.riv",
scroll,
rive)
ARTBOARD_FRAME_BENCH(
stack_participant_idle,
"layout/stack_participant.riv",
idle,
counting)
ARTBOARD_FRAME_BENCH(
hug_participant_idle,
"layout/hug_participant.riv",
idle,
counting)
ARTBOARD_FRAME_BENCH(
scroll_participant_scroll,
"layout/scroll_participant.riv",
scroll,
counting)
// Vector animation.
ARTBOARD_FRAME_BENCH(car_widgets_idle, "car_widgets_v01.riv", idle, counting)
ARTBOARD_FRAME_BENCH(car_widgets_move, "car_widgets_v01.riv", move, counting)
ARTBOARD_FRAME_BENCH(car_widgets_fade, "car_widgets_v01.riv", fade, counting)
ARTBOARD_FRAME_BENCH(car_widgets_idle_rive, "car_widgets_v01.riv", idle, rive)
ARTBOARD_FRAME_BENCH(hunter_x_idle, "hunter_x_demo.riv", idle, counting)
ARTBOARD_FRAME_BENCH(hunter_x_fade, "hunter_x_demo.riv", fade, counting)
ARTBOARD_FRAME_BENCH(zombie_skins_idle, "zombie_skins.riv", idle, counting)
ARTBOARD_FRAME_BENCH(zombie_skins_fade, "zombie_skins.riv", fade, counting)
ARTBOARD_FRAME_BENCH(trim_path_idle, "trim_path.riv", idle, counting)
ARTBOARD_FRAME_BENCH(trim_path_move, "trim_path.riv", move, counting)
// Nested artboards and lists.
ARTBOARD_FRAME_BENCH(
component_list_virtualized_long,
"component_list_virtualized.riv",
longList,
counting)
ARTBOARD_FRAME_BENCH(
component_list_virtualized_long_scroll,
"component_list_virtualized.riv",
longListScroll,
counting)
ARTBOARD_FRAME_BENCH(
planets_grid_idle,
"layoutstest_8-planets-grid.riv",
idle,
counting)
ARTBOARD_FRAME_BENCH(
planets_grid_tween,
"layoutstest_8-planets-grid.riv",
tween,
counting)
ARTBOARD_FRAME_BENCH(superbowl_idle, "superbowl.riv", idle, counting)
ARTBOARD_FRAME_BENCH(echo_show_idle, "echo_show_demo.riv", idle, counting)
// Large, mostly idle.
ARTBOARD_FRAME_BENCH(paper_poke, nullptr, poke, counting)
ARTBOARD_FRAME_BENCH(paper_poke_rive, nullptr, poke, rive)
ARTBOARD_FRAME_BENCH(
bidirectional_binding_idle,
"bidirectional_binding_source.riv",
idle,
counting)
} // namespace