Skip to content

SDK API

Tim Mcguinness edited this page Apr 24, 2026 · 1 revision

SDK API and Examples

A thin overview of the public API surface plus copy-pasteable recipes for common integration tasks. The authoritative reference is docs/SDK_API.md in the repo; runnable sample programs live in samples/ and are described in docs/SAMPLES.md.

For the behavioural contracts that sit alongside these calls (thread safety, ownership, error handling, memory, limits) see Runtime Contracts.

Entry points

Header Purpose
vtx/reader/core/vtx_reader_facade.h OpenReplayFile, IVtxReaderFacade, ReaderContext, ReaderChunkState
vtx/writer/core/vtx_writer_facade.h CreateFlatBuffersWriterFacade, CreateProtobuffWriterFacade, IVtxWriterFacade, WriterFacadeConfig
vtx/differ/core/vtx_differ_facade.h VtxDiff::CreateDifferFacade, PatchIndex, DiffOptions
vtx/writer/core/vtx_data_source.h IFrameDataSource for streaming producers into the writer
vtx/common/vtx_error_policy.h StrictErrorPolicy, LenientErrorPolicy, SilentErrorPolicy
vtx/common/vtx_types.h Frame, PropertyContainer, FlatArray<T>, VtxFormat

Reader factories return std::unique_ptr<IVtxReaderFacade>. Writer factories return std::unique_ptr<IVtxWriterFacade>. OpenReplayFile returns a ReaderContext by value that owns the reader internally.

Recipes

Open a VTX file and read a frame

#include "vtx/reader/core/vtx_reader_facade.h"

VTX::ReaderContext ctx = VTX::OpenReplayFile("replay.vtx");
if (!ctx) {
    std::cerr << "open failed: " << ctx.GetError() << "\n";
    return 1;
}

int32_t total = ctx->GetTotalFrames();

// Sync read. Pointer is valid until the next call that can evict the chunk.
const VTX::Frame* frame = ctx->GetFrameSync(0);
for (const auto& bucket : frame->GetBuckets()) {
    for (size_t i = 0; i < bucket.entities.size(); ++i) {
        const auto& entity = bucket.entities[i];
        const auto& uid    = bucket.unique_ids[i];
        // entity.float_properties[idx], entity.vector_properties[idx], ...
    }
}

Read a range of frames

std::vector<VTX::Frame> frames;
ctx->GetFrameRange(0, 99, frames);        // frames 0..99 (returned by value)

auto window = ctx->GetFrameContext(500, 5, 5);  // frames 495..505

The range / context forms copy frames out and are always safe to hold.

Hold a frame across reader calls (copy-out)

GetFrameSync returns a pointer into the chunk cache, which may be evicted. If you need to keep a frame past the next reader call, use the copy-out form:

VTX::Frame frame;
if (!ctx->GetFrame(42, frame)) {
    // invalid frame index, or chunk load failed
}
// `frame` is caller-owned and stays valid regardless of cache state.

Scrub randomly: tune the cache window

Default cache window favours sequential playback. For random-access UIs, widen the window so your working set fits.

ctx->SetCacheWindow(/*backward=*/ 8, /*forward=*/ 8);

// Before a known seek, prefetch the target chunk.
ctx->WarmAt(target_frame_index);
const VTX::Frame* frame = ctx->GetFrameSync(target_frame_index);

If the cache is consistently mis-sized against your workload, it can be up to 59% slower than no cache at all. See Performance, Cache-window sizing.

Resolve properties by name

Schema-driven lookup rather than hard-coded indices:

auto cache  = ctx->GetPropertyAddressCache();
auto schema = ctx->GetContextualSchema();

// Cache maps human names ("Health") to (container type, index) pairs.
// Full usage is covered in docs/SDK_API.md.

Inspect file metadata

VTX::FileHeader header = ctx->GetHeader();
// header.replay_name, header.replay_uuid, header.recorded_utc_timestamp
// header.version.format_major / format_minor / schema_version
// header.custom_json_metadata

VTX::FileFooter footer = ctx->GetFooter();
// footer.total_frames, footer.duration_seconds
// footer.chunk_index (seek table), footer.events (timeline events)

const auto& seek_table = ctx->GetSeekTable();
for (const auto& e : seek_table) {
    // e.chunk_index, e.start_frame, e.end_frame, e.file_offset, e.chunk_size_bytes
}

Observe chunk load state from another thread

ReaderChunkState is the one reader-adjacent surface that's safe to poll from a thread other than the one driving the reader. Useful for debug overlays.

VTX::ReaderChunkSnapshot snap = ctx.chunk_state->GetSnapshot();
// snap.loaded_chunks, snap.loading_chunks

Write a VTX file

#include "vtx/writer/core/vtx_writer_facade.h"

VTX::WriterFacadeConfig config;
config.output_filepath  = "output.vtx";
config.schema_json_path = "schema.json";
config.replay_name      = "My Replay";
config.chunk_max_frames = 1000;             // chunk rolls at whichever hits first
config.chunk_max_bytes  = 10 * 1024 * 1024;
config.use_compression  = true;
config.default_fps      = 60.0f;

auto writer = VTX::CreateFlatBuffersWriterFacade(config);
// or: VTX::CreateProtobuffWriterFacade(config);

while (simulation_is_running) {
    VTX::Frame frame = BuildFrameFromYourState();
    VTX::GameTime::GameTimeRegister time;
    time.game_time     = current_game_time_seconds;
    time.utc_timestamp = current_unix_timestamp;
    writer->RecordFrame(frame, time);
}

writer->Flush();   // push the in-progress chunk to disk
writer->Stop();    // finalise: write footer + embedded schema

Populate a Frame

A frame contains one or more buckets (Entity / BoneData / Events / Stats). Each bucket carries aligned unique_ids and entities (PropertyContainers) whose property slots are defined by your schema.

VTX::Frame frame;
auto& world = frame.CreateBucket("World");

world.unique_ids.push_back("player_001");
VTX::PropertyContainer entity;
entity.float_properties.push_back(100.0f);            // slot 0: Health
entity.vector_properties.push_back({1.0, 2.0, 3.0});  // slot 0: Position
entity.int32_arrays.AppendSubArray({10, 20, 30});     // slot 0: Inventory
world.entities.push_back(std::move(entity));

Slot indices are determined by the schema you authored in the Schema Creator.

Streaming producer: IFrameDataSource

When you have a pull-style data source (JSON dump, third-party demo file, live feed), implement IFrameDataSource and drive the writer from it.

#include "vtx/writer/core/vtx_data_source.h"

class MyDataSource : public VTX::IFrameDataSource {
public:
    bool   Initialize() override;
    bool   GetNextFrame(VTX::Frame& out_frame,
                        VTX::GameTime::GameTimeRegister& out_time) override;
    size_t GetExpectedTotalFrames() const override;  // 0 if streaming / unknown
};

MyDataSource src;
src.Initialize();

VTX::Frame frame;
VTX::GameTime::GameTimeRegister time;
while (src.GetNextFrame(frame, time)) {
    writer->RecordFrame(frame, time);
}
writer->Flush();
writer->Stop();

Diff two frames

#include "vtx/differ/core/vtx_differ_facade.h"

auto differ = VtxDiff::CreateDifferFacade(VTX::VtxFormat::FlatBuffers);

// Copy frame A out before asking for B -- getting B may evict A's chunk.
auto raw_a = ctx->GetRawFrameBytes(frame_a);
std::vector<std::byte> bytes_a(raw_a.begin(), raw_a.end());

auto raw_b = ctx->GetRawFrameBytes(frame_b);

VtxDiff::DiffOptions opts;
opts.compare_floats_with_epsilon = true;
opts.float_epsilon = 1e-5f;

VtxDiff::PatchIndex patch = differ->DiffRawFrames(bytes_a, raw_b, opts);
for (const auto& op : patch.operations) {
    // op.Operation (Add/Remove/Replace/ReplaceRange), op.ContainerType,
    // op.Path, op.ActorId
}

Pick an error policy for JSON / schema deserialisation

Deserialisation paths take a policy as a template parameter. Shipped in vtx/common/vtx_error_policy.h:

Policy Behaviour on missing/mismatched field
StrictErrorPolicy Throws std::runtime_error. Use for critical data.
LenientErrorPolicy Warns to stderr, leaves variable at default. Good for debug loads.
SilentErrorPolicy No-op. Highest throughput. Use only when integrity is guaranteed elsewhere.

See Runtime Contracts, Error handling for where each style surfaces.

Core types cheat-sheet

  • VTX::Frame — one or more Buckets, each holding aligned unique_ids and entities.
  • VTX::PropertyContainer — the per-entity SoA container. Scalar vectors (bool_properties, int32_properties, float_properties, ...), spatial (transform_properties, vector_properties, quat_properties), arrays (int32_arrays, float_arrays, ..., all FlatArray<T>), and nested (any_struct_properties, map_properties). Plus entity_type_id and content_hash.
  • FlatArray<T> — Structure-of-Arrays container with sub-array support. AppendSubArray({...}) / GetSubArray(i) / PushBack(subarray_idx, value).
  • VTX::VtxFormatFlatBuffers, Protobuf, or Unknown.
  • VTX::GameTime::GameTimeRegistergame_time (float seconds) + utc_timestamp.

Where to go next

Clone this wiki locally