| /* | |
| * Copyright 2022 Rive | |
| */ | |
| // Shared between goldens.cpp and the env gated diagnostics in | |
| // goldens_bench.cpp. | |
| #pragma once | |
| // Don't compile the goldens tool as part of the "tests" project. | |
| #ifndef TESTING | |
| #include "goldens_arguments.hpp" | |
| #include "common/test_harness.hpp" | |
| #include "common/testing_window.hpp" | |
| #include "rive/artboard.hpp" | |
| #include "rive/renderer.hpp" | |
| #include "rive/file.hpp" | |
| #include "rive/refcnt.hpp" | |
| #include "rive/animation/state_machine_instance.hpp" | |
| #include "rive/static_scene.hpp" | |
| #ifdef WITH_RIVE_SCRIPTING | |
| #include "rive/lua/scripting_vm.hpp" | |
| #include "rive/lua/rive_lua_libs.hpp" | |
| #endif | |
| #ifdef RIVE_CANVAS | |
| // RIVE_GOLDENS_DEFER_ORE records through a DeferredOreContext and replays on | |
| // the real context in the same frame, single threaded. | |
| #include "common/testing_window_sink.hpp" | |
| #include "rive/renderer/render_context.hpp" | |
| #include "rive/renderer/cmd/deferred_session.hpp" | |
| #endif | |
| #include <cstdlib> | |
| #include <memory> | |
| #include <vector> | |
| // Builds without scripting or canvas never include the deferred headers but | |
| // the accessors below still name the type. | |
| namespace rive::cmd | |
| { | |
| class DeferredSession; | |
| } | |
| extern GoldensArguments s_args; | |
| // Consoles build without an env API, probes just come back unset there. | |
| inline const char* goldens_getenv(const char* name) | |
| { | |
| #ifdef NO_GETENV | |
| return nullptr; | |
| #else | |
| return getenv(name); | |
| #endif | |
| } | |
| void dumpPixelsAsPng(const char* rivName, | |
| int windowWidth, | |
| int windowHeight, | |
| std::vector<uint8_t> pixels); | |
| #ifdef RIVE_CANVAS | |
| // Goldens frames present on a white background. | |
| inline TestingWindowFrameSink goldensFrameSink(bool doClear = true) | |
| { | |
| return TestingWindowFrameSink({ | |
| .clearColor = 0xffffffff, | |
| .doClear = doClear, | |
| .triangulationThresholds = DeterministicTriangulationThresholds, | |
| }); | |
| } | |
| // RIVE_GOLDENS_BENCH=<iters> loads the same scene immediate and deferred and | |
| // reports per frame record cost, replay cost, and stream size. | |
| void run_benchmark(const std::vector<uint8_t>& bytes, | |
| const char* artboardName, | |
| const char* stateMachineName, | |
| int iters); | |
| #endif | |
| class RIVLoader | |
| { | |
| public: | |
| // Auto defers for --deferred, or RIVE_GOLDENS_DEFER_ORE with a single | |
| // cell. The benchmark forces one or the other to load both side by side. | |
| enum class DeferMode | |
| { | |
| Auto, | |
| Immediate, | |
| Deferred | |
| }; | |
| RIVLoader(const std::vector<uint8_t>& rivBytes, | |
| const char* artboardName, | |
| const char* stateMachineName, | |
| DeferMode mode = DeferMode::Auto) | |
| { | |
| rive::Factory* importFactory = TestingWindow::Get()->factory(); | |
| #ifdef RIVE_CANVAS | |
| // Importing through the DeferredSession makes the artboard's own 2D | |
| // resources deferred objects with ids so drawInternal can record. | |
| bool wantDeferred = | |
| mode == DeferMode::Deferred || | |
| (mode == DeferMode::Auto && | |
| (s_args.deferred() || (goldens_getenv("RIVE_GOLDENS_DEFER_ORE") && | |
| s_args.cols() * s_args.rows() == 1))); | |
| if (wantDeferred) | |
| { | |
| if (auto* rc = TestingWindow::Get()->renderContext()) | |
| { | |
| if (auto* ore = rc->getOreContext()) | |
| { | |
| m_session = std::make_unique<rive::cmd::DeferredSession>( | |
| rive::ore::ReplayCaps::from(*ore)); | |
| // Bound before import so registration scripts that reach | |
| // for the device find it, like a real host. | |
| m_session->bindRenderContext(rc); | |
| importFactory = m_session.get(); | |
| } | |
| } | |
| } | |
| #endif | |
| m_file = rive::File::import(rivBytes, importFactory); | |
| if (m_file == nullptr) | |
| { | |
| throw "Bad riv file"; | |
| } | |
| if (artboardName != nullptr && artboardName[0] != '\0') | |
| { | |
| m_artboard = m_file->artboardNamed(artboardName); | |
| } | |
| else | |
| { | |
| m_artboard = m_file->artboardDefault(); | |
| } | |
| if (m_artboard == nullptr) | |
| { | |
| throw "Can't load artboard"; | |
| } | |
| // Bind the default view model instance | |
| m_viewModelInstance = m_file->createViewModelInstance(m_artboard.get()); | |
| m_artboard->bindViewModelInstance(m_viewModelInstance); | |
| if (stateMachineName != nullptr && stateMachineName[0] != '\0') | |
| { | |
| m_scene = m_artboard->stateMachineNamed(stateMachineName); | |
| } | |
| else | |
| { | |
| m_scene = m_artboard->defaultStateMachine(); | |
| } | |
| if (m_scene == nullptr) | |
| { | |
| // This is a riv without any state machines. Just draw the artboard. | |
| m_scene = std::make_unique<rive::StaticScene>(m_artboard.get()); | |
| } | |
| if (m_scene != nullptr && m_viewModelInstance != nullptr) | |
| { | |
| m_scene->bindViewModelInstance(m_viewModelInstance); | |
| } | |
| } | |
| rive::Scene* stateMachine() const { return m_scene.get(); } | |
| rive::Artboard* artboard() const { return m_artboard.get(); } | |
| // Null when deferred mode is off. | |
| rive::cmd::DeferredSession* deferredSession() const | |
| { | |
| #ifdef RIVE_CANVAS | |
| return m_session.get(); | |
| #else | |
| return nullptr; | |
| #endif | |
| } | |
| private: | |
| // Destroyed last since deferred resources held by the file record their | |
| // destruction into the session, so it must outlive them. | |
| #ifdef RIVE_CANVAS | |
| std::unique_ptr<rive::cmd::DeferredSession> m_session; | |
| #endif | |
| rive::rcp<rive::File> m_file; | |
| std::unique_ptr<rive::ArtboardInstance> m_artboard; | |
| std::unique_ptr<rive::Scene> m_scene; | |
| rive::rcp<rive::ViewModelInstance> m_viewModelInstance; | |
| }; | |
| #endif // TESTING |