Skip to content

Commit 2716d9b

Browse files
committed
Fix schema tracker to have the proper timestamps passed through
1 parent 377de2c commit 2716d9b

6 files changed

Lines changed: 227 additions & 88 deletions

File tree

src/core/deviceio/cpp/frame_metadata_tracker_oak.cpp

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,30 @@ class FrameMetadataTrackerOak::Impl : public ITrackerImpl
2424
{
2525
}
2626

27-
bool update(XrTime time) override
27+
bool update(XrTime /* time */) override
2828
{
29-
if (m_schema_reader.read_buffer(m_buffer))
29+
m_pending_records.clear();
30+
31+
std::vector<SchemaTracker::SampleResult> raw_samples;
32+
m_schema_reader.read_all_samples(raw_samples);
33+
34+
for (auto& sample : raw_samples)
3035
{
31-
auto fb = flatbuffers::GetRoot<FrameMetadata>(m_buffer.data());
36+
auto fb = flatbuffers::GetRoot<FrameMetadata>(sample.buffer.data());
3237
if (fb)
3338
{
34-
fb->UnPackTo(&m_data);
35-
m_last_timestamp = DeviceDataTimestamp(time, time, 0);
36-
return true;
39+
FrameMetadataT parsed;
40+
fb->UnPackTo(&parsed);
41+
m_pending_records.push_back({ std::move(parsed), sample.timestamp });
3742
}
3843
}
44+
45+
if (!m_pending_records.empty())
46+
{
47+
m_data = m_pending_records.back().data;
48+
m_last_timestamp = m_pending_records.back().timestamp;
49+
}
50+
3951
return true;
4052
}
4153

@@ -51,16 +63,38 @@ class FrameMetadataTrackerOak::Impl : public ITrackerImpl
5163
return m_last_timestamp;
5264
}
5365

66+
void serialize_all(size_t /* channel_index */, const RecordCallback& callback) const override
67+
{
68+
for (const auto& record : m_pending_records)
69+
{
70+
flatbuffers::FlatBufferBuilder builder(256);
71+
auto data_offset = FrameMetadata::Pack(builder, &record.data);
72+
73+
FrameMetadataRecordBuilder record_builder(builder);
74+
record_builder.add_data(data_offset);
75+
record_builder.add_timestamp(&record.timestamp);
76+
builder.Finish(record_builder.Finish());
77+
78+
callback(record.timestamp, builder.GetBufferPointer(), builder.GetSize());
79+
}
80+
}
81+
5482
const FrameMetadataT& get_data() const
5583
{
5684
return m_data;
5785
}
5886

5987
private:
88+
struct PendingRecord
89+
{
90+
FrameMetadataT data;
91+
DeviceDataTimestamp timestamp;
92+
};
93+
6094
SchemaTracker m_schema_reader;
61-
std::vector<uint8_t> m_buffer;
6295
FrameMetadataT m_data;
6396
DeviceDataTimestamp m_last_timestamp{};
97+
std::vector<PendingRecord> m_pending_records;
6498
};
6599

66100
// ============================================================================

src/core/deviceio/cpp/generic_3axis_pedal_tracker.cpp

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,34 @@ class Generic3AxisPedalTracker::Impl : public ITrackerImpl
2323
{
2424
}
2525

26-
bool update(XrTime time) override
26+
bool update(XrTime /* time */) override
2727
{
28-
if (m_schema_reader.read_buffer(m_buffer))
28+
m_pending_records.clear();
29+
30+
std::vector<SchemaTracker::SampleResult> raw_samples;
31+
m_schema_reader.read_all_samples(raw_samples);
32+
33+
for (auto& sample : raw_samples)
2934
{
30-
auto fb = flatbuffers::GetRoot<Generic3AxisPedalOutput>(m_buffer.data());
35+
auto fb = flatbuffers::GetRoot<Generic3AxisPedalOutput>(sample.buffer.data());
3136
if (fb)
3237
{
33-
fb->UnPackTo(&m_data);
34-
m_last_timestamp = DeviceDataTimestamp(time, time, 0);
35-
return true;
38+
Generic3AxisPedalOutputT parsed;
39+
fb->UnPackTo(&parsed);
40+
m_pending_records.push_back({ std::move(parsed), sample.timestamp });
3641
}
3742
}
38-
m_data.is_valid = false;
43+
44+
if (!m_pending_records.empty())
45+
{
46+
m_data = m_pending_records.back().data;
47+
m_last_timestamp = m_pending_records.back().timestamp;
48+
}
49+
else
50+
{
51+
m_data.is_valid = false;
52+
}
53+
3954
return true;
4055
}
4156

@@ -51,16 +66,38 @@ class Generic3AxisPedalTracker::Impl : public ITrackerImpl
5166
return m_last_timestamp;
5267
}
5368

69+
void serialize_all(size_t /* channel_index */, const RecordCallback& callback) const override
70+
{
71+
for (const auto& record : m_pending_records)
72+
{
73+
flatbuffers::FlatBufferBuilder builder(256);
74+
auto data_offset = Generic3AxisPedalOutput::Pack(builder, &record.data);
75+
76+
Generic3AxisPedalOutputRecordBuilder record_builder(builder);
77+
record_builder.add_data(data_offset);
78+
record_builder.add_timestamp(&record.timestamp);
79+
builder.Finish(record_builder.Finish());
80+
81+
callback(record.timestamp, builder.GetBufferPointer(), builder.GetSize());
82+
}
83+
}
84+
5485
const Generic3AxisPedalOutputT& get_data() const
5586
{
5687
return m_data;
5788
}
5889

5990
private:
91+
struct PendingRecord
92+
{
93+
Generic3AxisPedalOutputT data;
94+
DeviceDataTimestamp timestamp;
95+
};
96+
6097
SchemaTracker m_schema_reader;
61-
std::vector<uint8_t> m_buffer;
6298
Generic3AxisPedalOutputT m_data;
6399
DeviceDataTimestamp m_last_timestamp{};
100+
std::vector<PendingRecord> m_pending_records;
64101
};
65102

66103
// ============================================================================

src/core/deviceio/cpp/inc/deviceio/schema_tracker.hpp

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#pragma once
55

66
#include <oxr_utils/oxr_session_handles.hpp>
7+
#include <schema/timestamp_generated.h>
78

89
#include <XR_NVX1_tensor_data.h>
910
#include <cstddef>
@@ -87,32 +88,51 @@ struct SchemaTrackerConfig
8788
* Impl(const OpenXRSessionHandles& handles, SchemaTrackerConfig config)
8889
* : m_schema_reader(handles, std::move(config)) {}
8990
*
90-
* bool update(XrTime time) override {
91-
* if (m_schema_reader.read_buffer(buffer_)) {
92-
* auto fb = GetLocomotionCommand(buffer_.data());
91+
* bool update(XrTime) override {
92+
* m_pending.clear();
93+
* std::vector<SchemaTracker::SampleResult> raw;
94+
* m_schema_reader.read_all_samples(raw);
95+
* for (auto& s : raw) {
96+
* auto fb = flatbuffers::GetRoot<LocomotionCommand>(s.buffer.data());
9397
* if (fb) {
94-
* fb->UnPackTo(&data_);
95-
* return true;
98+
* LocomotionCommandT parsed;
99+
* fb->UnPackTo(&parsed);
100+
* m_pending.push_back({std::move(parsed), s.timestamp});
96101
* }
97102
* }
98-
* return false;
103+
* if (!m_pending.empty()) data_ = m_pending.back().data;
104+
* return true;
99105
* }
100106
*
101107
* DeviceDataTimestamp serialize(flatbuffers::FlatBufferBuilder& builder, size_t) const override {
102-
* auto data_offset = LocomotionCommand::Pack(builder, &data_);
108+
* auto offset = LocomotionCommand::Pack(builder, &data_);
109+
* auto& ts = m_pending.empty() ? DeviceDataTimestamp{} : m_pending.back().timestamp;
103110
* LocomotionCommandRecordBuilder rb(builder);
104-
* rb.add_data(data_offset);
105-
* rb.add_timestamp(&m_last_timestamp);
111+
* rb.add_data(offset);
112+
* rb.add_timestamp(&ts);
106113
* builder.Finish(rb.Finish());
107-
* return m_last_timestamp;
114+
* return ts;
115+
* }
116+
*
117+
* void serialize_all(size_t, const RecordCallback& cb) const override {
118+
* for (const auto& r : m_pending) {
119+
* flatbuffers::FlatBufferBuilder b(256);
120+
* auto offset = LocomotionCommand::Pack(b, &r.data);
121+
* LocomotionCommandRecordBuilder rb(b);
122+
* rb.add_data(offset);
123+
* rb.add_timestamp(&r.timestamp);
124+
* b.Finish(rb.Finish());
125+
* cb(r.timestamp, b.GetBufferPointer(), b.GetSize());
126+
* }
108127
* }
109128
*
110129
* const LocomotionCommandT& get_data() const { return data_; }
111130
*
112131
* private:
132+
* struct Pending { LocomotionCommandT data; DeviceDataTimestamp timestamp; };
113133
* SchemaTracker m_schema_reader;
114-
* std::vector<uint8_t> buffer_;
115134
* LocomotionCommandT data_;
135+
* std::vector<Pending> m_pending;
116136
* };
117137
* };
118138
* @endcode
@@ -145,16 +165,26 @@ class SchemaTracker
145165
*/
146166
static std::vector<std::string> get_required_extensions();
147167

168+
//! A single tensor sample with its data buffer and real timestamps.
169+
struct SampleResult
170+
{
171+
std::vector<uint8_t> buffer;
172+
DeviceDataTimestamp timestamp;
173+
};
174+
148175
/*!
149-
* @brief Read the next available raw sample buffer.
176+
* @brief Read ALL pending samples from the tensor collection.
150177
*
151-
* This method polls for tensor list updates, discovers the target collection
152-
* if not already connected, and retrieves the next available sample.
178+
* Drains every available sample since the last read, appending each to the
179+
* output vector with real timestamps from XrTensorSampleMetadataNV:
180+
* - sample_time_device_clock = rawDeviceTimestamp
181+
* - sample_time_common_clock = timestamp (OpenXR time domain)
182+
* - available_time_common_clock = arrivalTimestamp
153183
*
154-
* @param buffer Output vector that will be resized and filled with sample data.
155-
* @return true if data was read, false if no new data available.
184+
* @param samples Output vector; new samples are appended (not cleared).
185+
* @return Number of samples read in this call.
156186
*/
157-
bool read_buffer(std::vector<uint8_t>& buffer);
187+
size_t read_all_samples(std::vector<SampleResult>& samples);
158188

159189
/*!
160190
* @brief Access the configuration.
@@ -164,9 +194,10 @@ class SchemaTracker
164194
private:
165195
void initialize_tensor_data_functions();
166196
void create_tensor_list();
197+
bool ensure_collection();
167198
void poll_for_updates();
168199
std::optional<uint32_t> find_target_collection();
169-
bool read_next_sample(std::vector<uint8_t>& buffer);
200+
bool read_next_sample(SampleResult& out);
170201

171202
OpenXRSessionHandles m_handles;
172203
SchemaTrackerConfig m_config;

src/core/deviceio/cpp/inc/deviceio/tracker.hpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <openxr/openxr.h>
88
#include <schema/timestamp_generated.h>
99

10+
#include <functional>
1011
#include <memory>
1112
#include <string>
1213
#include <string_view>
@@ -41,6 +42,29 @@ class ITrackerImpl
4142
* @return DeviceDataTimestamp for MCAP log time.
4243
*/
4344
virtual DeviceDataTimestamp serialize(flatbuffers::FlatBufferBuilder& builder, size_t channel_index) const = 0;
45+
46+
/**
47+
* @brief Callback type for serialize_all: receives timestamp, raw buffer pointer, and size.
48+
*/
49+
using RecordCallback = std::function<void(const DeviceDataTimestamp&, const uint8_t*, size_t)>;
50+
51+
/**
52+
* @brief Serialize all pending records for a channel, invoking the callback for each.
53+
*
54+
* The default implementation calls serialize() once, which is correct for
55+
* direct OpenXR trackers that always have exactly one state per update.
56+
* SchemaTracker-based trackers override this to emit every queued tensor
57+
* sample so that no data is lost in MCAP recording.
58+
*
59+
* @param channel_index Which record channel to serialize.
60+
* @param callback Invoked once per record with (timestamp, data_ptr, data_size).
61+
*/
62+
virtual void serialize_all(size_t channel_index, const RecordCallback& callback) const
63+
{
64+
flatbuffers::FlatBufferBuilder builder(256);
65+
DeviceDataTimestamp ts = serialize(builder, channel_index);
66+
callback(ts, builder.GetBufferPointer(), builder.GetSize());
67+
}
4468
};
4569

4670
// Base interface for all trackers

0 commit comments

Comments
 (0)