Files
MapLibreParticleLab/include/mbgl/style/sources/raster_array_source.hpp
2026-09-04 14:51:42 +08:00

190 lines
6.9 KiB
C++

#pragma once
#include <mbgl/particle/field/offline_fixture_particle_adapter.hpp>
#include <mbgl/style/source.hpp>
#include <chrono>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <vector>
namespace mbgl {
class AsyncRequest;
namespace style {
// A Source-owned, preloaded validation candidate. Production runtimes can use
// updateRuntimeFieldWindow() directly with an already decoded snapshot instead.
struct RasterArrayRuntimeFieldDescriptor {
std::string fixtureID;
std::string metadataURL;
std::string fieldURL;
// Validation-only logical time in milliseconds. A production adapter owns
// its own timeline metadata; this keeps fixtures deterministic.
uint64_t timestamp = 0;
};
// A provider-neutral local feed pair. The source only transports the two
// resources; LocalParticleFieldFeed owns metadata decoding and validation.
struct RasterArrayLocalFieldDescriptor {
std::string metadataURL;
std::string fieldURL;
};
enum class RasterArrayPlaybackState : uint8_t {
Ready,
Playing,
Paused,
Waiting,
Ended
};
struct RasterArrayTimelineFrame {
std::shared_ptr<const particle::ParticleFieldSnapshot> t0;
std::shared_ptr<const particle::ParticleFieldSnapshot> t1;
float mix = 0.0f;
size_t t0Index = 0;
size_t t1Index = 0;
uint64_t windowRevision = 0;
RasterArrayPlaybackState state = RasterArrayPlaybackState::Ready;
};
// Source-owned playback state. Android only sends low-frequency commands;
// render frames obtain an immutable T0/T1 pair and scalar mix from Core.
class RasterArrayTimelineRuntime {
public:
struct Entry {
std::shared_ptr<const particle::ParticleFieldSnapshot> field;
uint64_t timestamp = 0;
};
explicit RasterArrayTimelineRuntime(std::vector<Entry>);
RasterArrayTimelineFrame tick();
bool play();
bool pause();
bool setSpeed(float);
bool seek(size_t);
bool step(int);
bool injectGap();
bool injectInvalid();
bool recover();
RasterArrayTimelineFrame frame() const;
float playbackSpeed() const;
size_t cacheCapacity() const { return cacheCapacity_; }
private:
RasterArrayTimelineFrame frameLocked() const;
bool moveToLocked(size_t);
mutable std::mutex mutex;
std::vector<Entry> entries;
size_t t0 = 0;
float mix = 0.0f;
float speed = 1.0f;
RasterArrayPlaybackState state = RasterArrayPlaybackState::Ready;
bool gap = false;
bool invalid = false;
RasterArrayPlaybackState stateBeforeWait = RasterArrayPlaybackState::Paused;
uint64_t windowRevision = 1;
std::chrono::steady_clock::time_point lastTick{};
const size_t cacheCapacity_ = 8;
};
enum class RasterArrayRuntimeFieldUpdateResult : uint8_t {
Applied,
Noop,
RejectedNotLoaded,
RejectedInvalidSnapshot,
RejectedIncompatibleSnapshot,
RejectedNonMonotonicGeneration,
RejectedCandidateNotReady,
RejectedNoCandidate,
};
class RasterArraySource final : public Source {
public:
RasterArraySource(std::string id,
std::string fixtureID,
std::string metadataURL,
std::string fieldURL,
std::string nextFixtureID = {},
std::string nextMetadataURL = {},
std::string nextFieldURL = {},
std::vector<RasterArrayRuntimeFieldDescriptor> runtimeFields = {});
RasterArraySource(std::string id, std::vector<RasterArrayLocalFieldDescriptor> localFields);
~RasterArraySource() override;
class Impl;
const Impl& impl() const;
void loadDescription(FileSource&) override;
// Advances the authoritative Source time window from T0/T1 to T1/newT1.
// The caller transfers an immutable, already decoded Field snapshot; the
// renderer observes the resulting Source change and owns GPU reuse/upload.
RasterArrayRuntimeFieldUpdateResult updateRuntimeFieldWindow(particle::ParticleFieldSnapshot newT1);
// Thin Core-owned submit point for a producer-supplied initial window.
RasterArrayRuntimeFieldUpdateResult submitInitialRuntimeFieldWindow(particle::ParticleFieldSnapshot t0,
particle::ParticleFieldSnapshot t1);
// Validation convenience: advance using the next Source-owned preloaded
// candidate. Android supplies no T0/T1 state and no GPU resource here.
RasterArrayRuntimeFieldUpdateResult advanceRuntimeFieldWindow();
// Validation convenience: resubmit the current T1 through the formal
// update API. This must return Noop without changing the Source window.
RasterArrayRuntimeFieldUpdateResult repeatRuntimeFieldWindow();
// Low-frequency JNI commands for the Source-owned validation timeline.
bool controlRuntimeTimeline(int command, float value = 0.0f);
bool supportsLayerType(const LayerTypeInfo*) const override;
mapbox::base::WeakPtr<Source> makeWeakPtr() override { return weakFactory.makeWeakPtr(); }
protected:
Mutable<Source::Impl> createMutable() const noexcept override;
private:
struct RuntimeFieldCandidate {
RasterArrayRuntimeFieldDescriptor descriptor;
std::unique_ptr<AsyncRequest> metadataRequest;
std::unique_ptr<AsyncRequest> fieldRequest;
std::optional<std::string> metadata;
std::optional<std::vector<char>> fieldBytes;
std::optional<particle::ParticleFieldSnapshot> snapshot;
};
struct LocalFieldCandidate {
RasterArrayLocalFieldDescriptor descriptor;
std::unique_ptr<AsyncRequest> metadataRequest;
std::unique_ptr<AsyncRequest> fieldRequest;
std::optional<std::string> metadata;
std::optional<std::vector<char>> fieldBytes;
std::optional<particle::ParticleFieldSnapshot> snapshot;
};
void tryLoad();
void tryLoadRuntimeField(size_t);
void tryLoadLocalField(size_t);
void tryInitializeTimeline();
RasterArrayRuntimeFieldUpdateResult rejectRuntimeFieldUpdate(RasterArrayRuntimeFieldUpdateResult,
const std::string& reason) const;
std::unique_ptr<AsyncRequest> metadataRequest;
std::unique_ptr<AsyncRequest> fieldRequest;
std::unique_ptr<AsyncRequest> nextMetadataRequest;
std::unique_ptr<AsyncRequest> nextFieldRequest;
std::optional<std::string> metadata;
std::optional<std::vector<char>> fieldBytes;
std::optional<std::string> nextMetadata;
std::optional<std::vector<char>> nextFieldBytes;
std::vector<RuntimeFieldCandidate> runtimeFields;
std::vector<LocalFieldCandidate> localFields;
size_t nextRuntimeField = 0;
uint64_t runtimeUpdateSequence = 0;
std::shared_ptr<RasterArrayTimelineRuntime> timeline;
mapbox::base::WeakPtrFactory<Source> weakFactory{this};
};
template <>
inline bool Source::is<RasterArraySource>() const {
return getType() == SourceType::RasterArray;
}
} // namespace style
} // namespace mbgl