Skip to content

core: add Rust-backed MLT tile data#4428

Draft
louwers wants to merge 11 commits into
maplibre:mainfrom
louwers:vector-mlt-tile-data-impl
Draft

core: add Rust-backed MLT tile data#4428
louwers wants to merge 11 commits into
maplibre:mainfrom
louwers:vector-mlt-tile-data-impl

Conversation

@louwers

@louwers louwers commented Jul 23, 2026

Copy link
Copy Markdown
Member

The Rust MLT decoder has a safe FastPFor implementation.

This PR is a vibe coded (GPT 5.6) integration with mlt-core.

@github-actions github-actions Bot added build Related to build, configuration or CI/CD core Changes that affect the C++ core of MapLibre Native labels Jul 23, 2026
@louwers louwers changed the title Add Rust-backed MLT tile data core: add Rust-backed MLT tile data Jul 23, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Bloaty Results 🐋

Compared to main

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%    +592  +0.0%    +664    TOTAL

Full report: https://maplibre-native.s3.eu-central-1.amazonaws.com/bloaty-results/pr-4428-compared-to-main.txt

Compared to d387090 (legacy)

    FILE SIZE        VM SIZE    
 --------------  -------------- 
   +55% +63.3Mi  +472% +28.2Mi    TOTAL

Full report: https://maplibre-native.s3.eu-central-1.amazonaws.com/bloaty-results/pr-4428-compared-to-legacy.txt

@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results ⚡

Benchmark                                                          Time             CPU      Time Old      Time New       CPU Old       CPU New
-----------------------------------------------------------------------------------------------------------------------------------------------
OVERALL_GEOMEAN                                                 +0.4538         +0.4536             0             0             0             0

Full report: https://maplibre-native.s3.eu-central-1.amazonaws.com/benchmark-results/pr-4428-compared-to-main.txt

@CommanderStorm CommanderStorm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM, a few aspects are a bit weird though.

How is perf compared to cpp?

Comment thread rustutils/src/mlt.rs
Comment on lines +19 to +39
#[derive(Clone, Copy, PartialEq, Eq)]
enum MltValueType {
Missing = 0,
Null = 1,
Bool = 2,
I64 = 3,
U64 = 4,
F32 = 5,
F64 = 6,
String = 7,
}

#[derive(Clone, Copy)]
struct MltValue {
typ: MltValueType,
bool_value: bool,
i64_value: i64,
u64_value: u64,
f32_value: f32,
f64_value: f64,
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit sad that there is no simpler way to do this.

Comment thread rustutils/src/mlt.rs
Comment on lines +41 to +45
#[derive(Clone, Copy)]
struct MltFeatureId {
has_id: bool,
value: u64,
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting that this is not yet implement in cxx


if (!testCase.fastPFOREnabled && testCase.useFastPFOR) {
// If fast PFOR is not enabled, the fast PFOR tile should fail to parse
if (!testCase.fastPFOREnabled && testCase.useFastPFOR && layerNames.empty()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If layerNames is empty, how can size be anything but zero?
Is this still testing something?

GeometryCollection(const GeometryCollection&) = default;

std::shared_ptr<const mlt::MapLibreTile> triangleOwner;
std::shared_ptr<const void> triangleOwner;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still save?
What is const void here?
Why do we need to keep it?

Comment on lines +23 to +25
std::string toStdString(rust::String value) {
return {value.data(), value.size()};
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rust::String implements the explicit converter constructor (not sure what the name is), so you can just std::string(value).
https://cxx.rs/binding/string.html

Also applies to below rust::Str.
Also, Inline afterwards ^^

Comment on lines +311 to +328
VectorMLTTileData::VectorMLTTileData(std::shared_ptr<const std::string> data, bool)
: impl(std::make_shared<Impl>(std::move(data))) {}

VectorMLTTileData::VectorMLTTileData(const VectorMLTTileData&) = default;
VectorMLTTileData::VectorMLTTileData(VectorMLTTileData&&) noexcept = default;
VectorMLTTileData::~VectorMLTTileData() = default;

std::unique_ptr<GeometryTileData> VectorMLTTileData::clone() const {
return std::make_unique<VectorMLTTileData>(*this);
}

std::unique_ptr<GeometryTileLayer> VectorMLTTileData::getLayer(const std::string& name) const {
return impl->getLayer(name);
}

std::vector<std::string> VectorMLTTileData::layerNames() const {
return impl->layerNames();
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should they not also be in the class? Why are they below the class?

Comment thread rustutils/src/mlt.rs
.collect(),
property_values: Vec::new(),
ffi_property_values: Vec::new(),
part_offsets: vec![0],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will mean we always alloc here, can we make the first index implied 0 in the cpp code or in the decoding instead?

I assume this is reasonably hot

Comment thread rustutils/src/mlt.rs
}

fn value(typ: ffi::MltValueType) -> ffi::MltValue {
ffi::MltValue {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I'd inline these, derive Default for the last and .. Default::default(), but that is likely fine too.

Comment thread rustutils/src/mlt.rs
f64_value: *f64_value,
..value(ffi::MltValueType::F64)
},
MltPropertyValue::String(_) => value(ffi::MltValueType::String),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this what we want? Where is the string stored?

Comment thread rustutils/src/mlt.rs
Comment on lines +856 to +860
let Some(start) = layer.part_offsets.get(offset_index) else {
return 0;
};
let Some(end) = layer.part_offsets.get(offset_index + 1) else {
return 0;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 💯 sure if this compiles, but my intuition would be to do this.

Suggested change
let Some(start) = layer.part_offsets.get(offset_index) else {
return 0;
};
let Some(end) = layer.part_offsets.get(offset_index + 1) else {
return 0;
let Some([start,end]) = layer.part_offsets.get(offset_index..offset_index +1) else {
return 0;
};

@louwers

louwers commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

How is perf compared to cpp?

About the same.

Regarding your Rust comments, this was 100% vibe coded as a soft of proof of concept. I don't really know Rust.

@github-actions

Copy link
Copy Markdown
Contributor

Bloaty Results (iOS) 🐋

Compared to main

    FILE SIZE        VM SIZE    
 --------------  -------------- 
  +0.0%     +80  [ = ]       0    TOTAL

Full report: https://maplibre-native.s3.eu-central-1.amazonaws.com/bloaty-results-ios/pr-4428-compared-to-main.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build Related to build, configuration or CI/CD core Changes that affect the C++ core of MapLibre Native

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants