blob: 54b4c98a08082573b6d4db3d9188cea66e1ddf49 [file] [edit]
#ifndef _RIVE_PROFILE_HPP_
#define _RIVE_PROFILE_HPP_
#include <cstdint>
#include <functional>
#include <string>
#include <unordered_map>
#include <vector>
namespace rive
{
/// Bitmask for which profile data to log. Combine flags to enable multiple.
enum ProfileLogFlags : uint32_t
{
ProfileLogNone = 0,
ProfileLogTransitionRecords = 1u << 0,
ProfileLogListenerPerformChanges = 1u << 1,
};
/// Segment of the artboard path (root to state machine artboard).
/// type: 0 = NestedArtboard, 1 = ArtboardComponentList. index is used only when
/// type is ArtboardComponentList (logical index in list); otherwise -1.
struct ArtboardPathSegment
{
uint8_t type = 0;
uint32_t nameId = 0;
int32_t index = -1;
};
/// Record for a single state machine transition (used when profiling).
/// String fields are stored as indices into the profiler string table;
/// see getStringTable() and resolveStringId(). path is root-to-leaf host chain.
struct TransitionRecord
{
uint32_t artboardId = 0;
uint32_t smId = 0;
uint32_t layerId = 0;
uint32_t fromStateId = 0;
uint32_t toStateId = 0;
uint64_t tick = 0;
std::vector<ArtboardPathSegment> path;
};
/// Record for a single listener performChanges call (used when profiling).
/// artboardId, smId, listenerNameId index into the profiler string table.
/// listenerType and hitEvent are raw ListenerType enum values; consumer maps to
/// string.
struct ListenerPerformChangeRecord
{
uint32_t artboardId = 0;
uint32_t smId = 0;
uint32_t listenerNameId = 0;
uint32_t listenerType = 0;
uint32_t hitEvent = 0;
uint32_t pointerId = 0;
uint64_t tick = 0;
};
/// Singleton that wraps all MicroProfile calls and collects transition records
/// as they occur during advance/apply (when RIVE_MICROPROFILE is defined).
class RiveProfile
{
public:
using FlushCallback =
std::function<void(const std::vector<TransitionRecord>&)>;
using ListenerPerformChangeFlushCallback =
std::function<void(const std::vector<ListenerPerformChangeRecord>&)>;
static RiveProfile& instance();
RiveProfile(const RiveProfile&) = delete;
RiveProfile& operator=(const RiveProfile&) = delete;
// MicroProfile lifecycle (no-ops when RIVE_MICROPROFILE is not defined)
void init();
void frame();
void endFrame();
/// Profiling session (used by native binding for start/stop/dump).
void start();
void stop();
bool isActive() const;
/// Append header (once) and new frame events to \a buffer. Call after
/// flushTransitionRecords() so the binding can merge transition data.
void flushFrameDataTo(std::vector<uint8_t>& buffer);
/// Record a state machine transition. Data is collected as it runs; no
/// second pass over instances is required. No-op when RIVE_MICROPROFILE is
/// undefined, the profiling session is inactive, no transition flush
/// callback is set, or ProfileLogTransitionRecords is not set.
/// If artboardForPath is non-null, the full artboard path is built and
/// stored on the record (only when recording).
void recordTransition(std::string artboardName,
std::string smName,
std::string layerName,
std::string fromStateName,
std::string toStateName,
class Artboard* artboardForPath = nullptr);
/// Set the callback invoked when flushTransitionRecords() is called with
/// all collected records for the current frame. The binding uses this to
/// serialize records into the event buffer.
void setFlushCallback(FlushCallback callback);
/// Invoke the flush callback with accumulated transition records and clear
/// the buffer. Call after advanceAndApply. No-op when callback is not set.
void flushTransitionRecords();
/// Record a listener performChanges call. No-op when RIVE_MICROPROFILE is
/// undefined, the profiling session is inactive, no listener flush callback
/// is set, or ProfileLogListenerPerformChanges is not set. listenerType and
/// hitEvent are stored as raw uint (ListenerType enum); consumer maps to
/// string.
void recordListenerPerformChange(std::string artboardName,
std::string smName,
std::string listenerName,
uint32_t listenerType,
uint32_t hitEvent,
uint32_t pointerId);
/// Set the callback invoked when flushListenerPerformChangeRecords() is
/// called with all collected listener records for the current frame.
void setListenerPerformChangeFlushCallback(
ListenerPerformChangeFlushCallback callback);
/// Invoke the listener flush callback with accumulated records and clear
/// the buffer. Call after flushTransitionRecords() when profiling.
void flushListenerPerformChangeRecords();
/// Set which profile data to log (bitmask of ProfileLogFlags). Combine
/// flags to enable multiple log types.
void setLogFlags(uint32_t flags) { m_logFlags = flags; }
uint32_t logFlags() const { return m_logFlags; }
/// Resolve a string to a stable id in the profiler string table. Used by
/// transition records and any future record type that needs to log strings.
/// Returns existing id if the string was seen before, otherwise assigns new
/// id.
uint32_t resolveStringId(const std::string& str);
/// Return the current profiler string table (id index -> string). The
/// binding uses this to emit 0x04 string table records; other record types
/// use ids that index into this table.
const std::vector<std::string>& getStringTable() const;
private:
RiveProfile() = default;
FlushCallback m_flushCallback;
ListenerPerformChangeFlushCallback m_listenerPerformChangeFlushCallback;
std::vector<TransitionRecord> m_transitionRecords;
std::vector<ListenerPerformChangeRecord> m_listenerPerformChangeRecords;
std::unordered_map<std::string, uint32_t> m_stringTable;
std::vector<std::string> m_stringList;
bool m_profilingActive = false;
uint32_t m_logFlags = 3;
bool m_headerWritten = false;
uint64_t m_lastFlushedFrameIndex = 0;
};
} // namespace rive
#endif