| /* |
| * Copyright 2022 Rive |
| */ |
| |
| #include "bench.hpp" |
| |
| #include "common/render_context_null.hpp" |
| #include "rive/file.hpp" |
| #include "rive/scene.hpp" |
| #include "assets/paper.riv.hpp" |
| #include "rive/renderer/rive_renderer.hpp" |
| #include "rive/renderer/render_context.hpp" |
| #include "rive_render_paint.hpp" |
| #include "rive_render_path.hpp" |
| |
| using namespace rive; |
| using namespace rive::gpu; |
| |
| using PathPaint = std::tuple<rcp<RiveRenderPath>, rcp<RiveRenderPaint>>; |
| |
| // Measure the speed of RiveRenderer::drawPath(), with a null render context. |
| class DrawRiveRenderPath : public Bench |
| { |
| protected: |
| int run() const final |
| { |
| for (int i = 0; i < 10; ++i) |
| { |
| m_nullContext->beginFrame({ |
| .renderTargetWidth = m_renderTarget->width(), |
| .renderTargetHeight = m_renderTarget->height(), |
| }); |
| for (const auto& [path, paint] : m_pathPaints) |
| { |
| m_renderer.drawPath(path.get(), paint.get()); |
| } |
| m_nullContext->flush({.renderTarget = m_renderTarget.get()}); |
| } |
| return 0; |
| } |
| |
| std::vector<PathPaint> m_pathPaints; |
| std::unique_ptr<RenderContext> m_nullContext = |
| RenderContextNULL::MakeContext(); |
| mutable RiveRenderer m_renderer{m_nullContext.get()}; |
| rive::rcp<RenderTarget> m_renderTarget = |
| m_nullContext->static_impl_cast<RenderContextNULL>()->makeRenderTarget( |
| 1600, |
| 1600); |
| }; |
| |
| class SniffPathsRenderer : public Renderer |
| { |
| public: |
| SniffPathsRenderer(std::vector<PathPaint>* pathPaints, |
| bool allStrokes, |
| bool allRoundJoin, |
| float forcedFeather = 0) : |
| m_pathPaints(pathPaints), |
| m_allStrokes(allStrokes), |
| m_allRoundJoin(allRoundJoin), |
| m_forcedFeather(forcedFeather) |
| {} |
| void save() override {} |
| void restore() override {} |
| void transform(const Mat2D& matrix) override {} |
| void drawPath(RenderPath* path, RenderPaint* paint) override |
| { |
| auto renderPath = static_cast<RiveRenderPath*>(path); |
| auto renderPaint = static_cast<RiveRenderPaint*>(paint); |
| if (m_allStrokes) |
| { |
| renderPaint->style(RenderPaintStyle::stroke); |
| renderPaint->join(StrokeJoin::bevel); |
| renderPaint->thickness(2); |
| } |
| if (m_allRoundJoin) |
| { |
| renderPaint->join(StrokeJoin::round); |
| } |
| if (m_forcedFeather) |
| { |
| renderPaint->feather(m_forcedFeather); |
| renderPath->fillRule(FillRule::clockwise); |
| } |
| |
| m_pathPaints->push_back({ref_rcp(renderPath), ref_rcp(renderPaint)}); |
| } |
| void clipPath(RenderPath* path) override {} |
| void drawImage(const RenderImage*, ImageSampler, BlendMode, float) override |
| {} |
| void drawImageMesh(const RenderImage*, |
| ImageSampler, |
| rcp<RenderBuffer>, |
| rcp<RenderBuffer>, |
| rcp<RenderBuffer>, |
| uint32_t vertexCount, |
| uint32_t indexCount, |
| BlendMode, |
| float) override |
| {} |
| |
| private: |
| std::vector<PathPaint>* m_pathPaints; |
| bool m_allStrokes; |
| bool m_allRoundJoin; |
| float m_forcedFeather; |
| }; |
| |
| // Measure the speed of RiveRenderer::drawPath() from the contents of a .riv |
| // file. |
| class DrawRiveRenderPaths : public DrawRiveRenderPath |
| { |
| public: |
| DrawRiveRenderPaths(bool allStrokes = false, bool allRoundJoin = false) |
| { |
| // Sniff paths out of a .riv file. |
| SniffPathsRenderer sniffer(&m_pathPaints, allStrokes, allRoundJoin); |
| rcp<File> rivFile = |
| File::import(assets::paper_riv(), m_nullContext.get()); |
| std::unique_ptr<ArtboardInstance> artboard = rivFile->artboardDefault(); |
| std::unique_ptr<Scene> scene = artboard->defaultScene(); |
| scene->advanceAndApply(0); |
| scene->draw(&sniffer); |
| } |
| }; |
| REGISTER_BENCH(DrawRiveRenderPaths); |
| |
| // Measure the speed of RiveRenderer::drawPath() from the contents of a .riv |
| // file, all paths converted to strokes. |
| class DrawRiveRenderPathsAsStrokes : public DrawRiveRenderPaths |
| { |
| public: |
| DrawRiveRenderPathsAsStrokes() : DrawRiveRenderPaths(true) {} |
| }; |
| REGISTER_BENCH(DrawRiveRenderPathsAsStrokes); |
| |
| class DrawRiveRenderPathsAsRoundJoinStrokes : public DrawRiveRenderPaths |
| { |
| public: |
| DrawRiveRenderPathsAsRoundJoinStrokes() : DrawRiveRenderPaths(true, true) {} |
| }; |
| REGISTER_BENCH(DrawRiveRenderPathsAsRoundJoinStrokes); |
| |
| class DrawCustomPaths : public DrawRiveRenderPath |
| { |
| public: |
| void setup() final |
| { |
| for (int i = 0; i < 1000; ++i) |
| { |
| RawPath rawPath; |
| auto renderPath = static_rcp_cast<RiveRenderPath>( |
| m_nullContext->makeRenderPath(rawPath, FillRule::clockwise)); |
| setupPath(renderPath.get()); |
| auto renderPaint = static_rcp_cast<RiveRenderPaint>( |
| m_nullContext->makeRenderPaint()); |
| setupPaint(renderPaint.get()); |
| |
| m_pathPaints.emplace_back(std::move(renderPath), |
| std::move(renderPaint)); |
| } |
| } |
| |
| protected: |
| virtual void setupPath(RiveRenderPath* path) = 0; |
| virtual void setupPaint(RiveRenderPaint* paint) {} |
| }; |
| |
| class DrawCustomStrokes : public DrawCustomPaths |
| { |
| public: |
| void setupPaint(RiveRenderPaint* paint) override |
| { |
| paint->style(RenderPaintStyle::stroke); |
| paint->thickness(2); |
| } |
| }; |
| |
| class DrawZeroChopStrokes : public DrawCustomStrokes |
| { |
| protected: |
| void setupPath(RiveRenderPath* path) final |
| { |
| path->moveTo(199, 1225); |
| for (int j = 0; j < 50; ++j) |
| { |
| path->cubicTo(197, 943, 349, 607, 549, 427); |
| path->cubicTo(349, 607, 197, 943, 199, 1225); |
| } |
| } |
| }; |
| |
| REGISTER_BENCH(DrawZeroChopStrokes); |
| |
| class DrawOneChopStrokes : public DrawCustomStrokes |
| { |
| protected: |
| void setupPath(RiveRenderPath* path) final |
| { |
| for (int j = 0; j < 50; ++j) |
| { |
| path->cubicTo(100, 0, 50, 100, 100, 100); |
| path->cubicTo(0, -100, 200, 100, 0, 0); |
| } |
| } |
| }; |
| |
| REGISTER_BENCH(DrawOneChopStrokes); |
| |
| class DrawTwoChopStrokes : public DrawCustomStrokes |
| { |
| protected: |
| void setupPath(RiveRenderPath* path) final |
| { |
| path->moveTo(460, 1060); |
| for (int j = 0; j < 50; ++j) |
| { |
| path->cubicTo(403, -320, 60, 660, 1181, 634); |
| path->cubicTo(60, 660, 403, -320, 460, 1060); |
| } |
| } |
| }; |
| |
| REGISTER_BENCH(DrawTwoChopStrokes); |
| |
| class DrawOneCuspStrokes : public DrawCustomStrokes |
| { |
| protected: |
| void setupPath(RiveRenderPath* path) final |
| { |
| for (int j = 0; j < 50; ++j) |
| { |
| path->cubicTo(100, 100, 100, 0, 0, 100); |
| path->cubicTo(100, 0, 100, 100, 0, 0); |
| } |
| } |
| }; |
| |
| REGISTER_BENCH(DrawOneCuspStrokes); |
| |
| class DrawTwoCuspStrokes : public DrawCustomStrokes |
| { |
| protected: |
| void setupPath(RiveRenderPath* path) final |
| { |
| for (int j = 0; j < 50; ++j) |
| { |
| path->cubicTo(100, 0, 50, 0, 150, 0); |
| path->cubicTo(50, 0, 100, 0, 0, 0); |
| } |
| } |
| }; |
| |
| class DrawFeatheredPaths : public DrawRiveRenderPath |
| { |
| public: |
| DrawFeatheredPaths(rive::Span<uint8_t> riv) |
| { |
| // Sniff paths out of a .riv file. |
| SniffPathsRenderer sniffer(&m_pathPaints, |
| /*allStrokes=*/false, |
| /*allRoundJoin=*/false, |
| /*forcedFeather=*/100); |
| rcp<File> rivFile = File::import(riv, m_nullContext.get()); |
| std::unique_ptr<ArtboardInstance> artboard = rivFile->artboardDefault(); |
| std::unique_ptr<Scene> scene = artboard->defaultScene(); |
| scene->advanceAndApply(0); |
| scene->draw(&sniffer); |
| } |
| }; |
| |
| class DrawFeatheredPaths_paper : public DrawFeatheredPaths |
| { |
| public: |
| DrawFeatheredPaths_paper() : DrawFeatheredPaths(assets::paper_riv()) {} |
| }; |
| REGISTER_BENCH(DrawFeatheredPaths_paper); |
| |
| class DrawCustomFeathers : public DrawCustomPaths |
| { |
| public: |
| void setupPath(RiveRenderPath* path) final |
| { |
| path->fillRule(FillRule::clockwise); |
| for (int j = 0; j < 50; ++j) |
| { |
| path->cubicTo(-800, 1600, 2400, 1600, 1600, 0); |
| } |
| } |
| |
| void setupPaint(RiveRenderPaint* paint) override |
| { |
| paint->style(RenderPaintStyle::fill); |
| paint->feather(85); |
| } |
| }; |
| REGISTER_BENCH(DrawCustomFeathers); |
| |
| REGISTER_BENCH(DrawTwoCuspStrokes); |