178 lines
5.7 KiB
C++
178 lines
5.7 KiB
C++
#pragma once
|
|
|
|
#include <mbgl/particle/field/field_profile_contract.hpp>
|
|
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
namespace mbgl::particle {
|
|
|
|
enum class FieldDType : uint8_t {
|
|
Int16 = 1,
|
|
UInt16 = 2,
|
|
UInt8 = 3,
|
|
Int8 = 4,
|
|
Float32 = 5,
|
|
Bitset = 6,
|
|
};
|
|
|
|
struct FrameContentIdentity {
|
|
std::string fieldID;
|
|
uint64_t generation = 0;
|
|
uint64_t dataVersion = 0;
|
|
uint64_t validTimeMs = 0;
|
|
|
|
bool isValid() const;
|
|
bool operator==(const FrameContentIdentity&) const;
|
|
bool operator!=(const FrameContentIdentity& other) const { return !(*this == other); }
|
|
};
|
|
|
|
struct FieldPlaneKey {
|
|
uint16_t semanticID = 0;
|
|
uint16_t groupID = 0;
|
|
// Component is a plane descriptor property, not part of the QBIN V2
|
|
// plane identity. A Field may contain only one (semanticID, groupID).
|
|
FieldComponent component = FieldComponent::None;
|
|
|
|
bool operator==(const FieldPlaneKey&) const;
|
|
bool operator<(const FieldPlaneKey&) const;
|
|
};
|
|
|
|
struct DecodedFieldPlane {
|
|
FieldPlaneKey key;
|
|
FieldDType dtype = FieldDType::Float32;
|
|
FieldValueClass valueClass = FieldValueClass::Scalar;
|
|
uint16_t unitID = 0;
|
|
// REQUIRED is transport metadata retained by the provider-neutral runtime.
|
|
bool requiredFlag = false;
|
|
uint32_t count = 0;
|
|
float scale = 1.0f;
|
|
float offset = 0.0f;
|
|
std::string contentIdentity;
|
|
std::shared_ptr<const std::vector<uint8_t>> storage;
|
|
|
|
bool isStructurallyValid(uint64_t cellCount) const;
|
|
bool isInterpolationCompatibleWith(const DecodedFieldPlane&) const;
|
|
};
|
|
|
|
struct DecodedFieldGrid {
|
|
uint8_t layout = 1;
|
|
uint8_t projection = 0;
|
|
uint8_t sampleOrder = 0;
|
|
uint8_t gridRegistration = 0;
|
|
uint32_t width = 0;
|
|
uint32_t height = 0;
|
|
double west = -180.0;
|
|
double south = -85.0;
|
|
double east = 180.0;
|
|
double north = 85.0;
|
|
|
|
bool isValid() const;
|
|
bool operator==(const DecodedFieldGrid&) const;
|
|
};
|
|
|
|
struct DecodedFieldFrame {
|
|
FrameContentIdentity frameContentIdentity;
|
|
uint64_t referenceTimeMs = 0;
|
|
// Validation provenance retained so a stored frame cannot be interpolated
|
|
// across two otherwise byte-compatible but semantically different profiles.
|
|
std::string resolvedProfileID;
|
|
uint32_t resolvedProfileVersion = 0;
|
|
DecodedFieldGrid grid;
|
|
// One frame-wide validity domain. The descriptor and payload share storage;
|
|
// projections can retain the same payload without copying it.
|
|
DecodedFieldPlane validityPlane;
|
|
std::string validityContentIdentity;
|
|
std::shared_ptr<const std::vector<uint8_t>> validityBits;
|
|
std::vector<DecodedFieldPlane> planes;
|
|
|
|
bool isStructurallyValid() const;
|
|
bool isSeriesCompatibleWith(const DecodedFieldFrame&) const;
|
|
bool isInterpolationCompatibleWith(const DecodedFieldFrame&) const;
|
|
bool hasSameContentAs(const DecodedFieldFrame&) const;
|
|
// Raw decoded plane payload plus the shared validity bitset; excludes
|
|
// separately materialized CPU snapshots and renderer/GPU resources.
|
|
uint64_t decodedByteSize() const;
|
|
};
|
|
|
|
enum class FieldSeriesSubmitResult : uint8_t {
|
|
Inserted,
|
|
ReplacedRevision,
|
|
Duplicate,
|
|
RejectedInvalidFieldKey,
|
|
RejectedInvalidFrame,
|
|
RejectedIdentityConflict,
|
|
RejectedStaleRevision,
|
|
RejectedIncompatibleFrame,
|
|
RejectedCapacity,
|
|
};
|
|
|
|
struct FieldSeriesWindow {
|
|
std::shared_ptr<const DecodedFieldFrame> t0;
|
|
std::shared_ptr<const DecodedFieldFrame> t1;
|
|
float mix = 0.0f;
|
|
uint64_t requestedForecastTimeMs = 0;
|
|
};
|
|
|
|
class FieldSeries final {
|
|
public:
|
|
explicit FieldSeries(size_t capacity = 8);
|
|
|
|
FieldSeriesSubmitResult submit(std::shared_ptr<const DecodedFieldFrame>);
|
|
std::optional<FieldSeriesWindow> resolve(uint64_t requestedForecastTimeMs) const;
|
|
size_t size() const { return frames.size(); }
|
|
size_t capacity() const { return capacity_; }
|
|
uint64_t decodedByteSize() const;
|
|
const std::vector<std::shared_ptr<const DecodedFieldFrame>>& entries() const { return frames; }
|
|
|
|
private:
|
|
size_t capacity_;
|
|
std::vector<std::shared_ptr<const DecodedFieldFrame>> frames;
|
|
};
|
|
|
|
enum class ForecastPlaybackState : uint8_t { Paused, Playing };
|
|
|
|
class ForecastClock final {
|
|
public:
|
|
bool setRequestedForecastTime(uint64_t timestampMs);
|
|
bool setPlaybackSpeed(float value);
|
|
void play() { state_ = ForecastPlaybackState::Playing; }
|
|
void pause() { state_ = ForecastPlaybackState::Paused; }
|
|
bool advance(uint64_t elapsedMs);
|
|
|
|
uint64_t requestedForecastTime() const { return requestedForecastTimeMs_; }
|
|
float playbackSpeed() const { return playbackSpeed_; }
|
|
ForecastPlaybackState playbackState() const { return state_; }
|
|
|
|
private:
|
|
uint64_t requestedForecastTimeMs_ = 0;
|
|
// The supported playback rates are exact multiples of half a millisecond.
|
|
// Keep the sub-millisecond half-unit so short HALF ticks do not round up.
|
|
uint8_t fractionalHalfMillisecond_ = 0;
|
|
float playbackSpeed_ = 1.0f;
|
|
ForecastPlaybackState state_ = ForecastPlaybackState::Paused;
|
|
};
|
|
|
|
class EncodedFieldStore final {
|
|
public:
|
|
explicit EncodedFieldStore(size_t perSeriesCapacity = 8) : perSeriesCapacity_(perSeriesCapacity) {}
|
|
|
|
FieldSeriesSubmitResult submit(const std::string& fieldKey, std::shared_ptr<const DecodedFieldFrame>);
|
|
std::optional<FieldSeriesWindow> resolve(const std::string& fieldKey, uint64_t requestedForecastTimeMs) const;
|
|
const FieldSeries* find(const std::string& fieldKey) const;
|
|
bool clear(const std::string& fieldKey);
|
|
void clearAll() { series.clear(); }
|
|
size_t fieldCount() const { return series.size(); }
|
|
uint64_t decodedByteSize() const;
|
|
|
|
private:
|
|
size_t perSeriesCapacity_;
|
|
std::unordered_map<std::string, FieldSeries> series;
|
|
};
|
|
|
|
} // namespace mbgl::particle
|