Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

[core] sort symbols using symbol-sort-key before placement #16023

Merged
merged 12 commits into from Feb 11, 2020

Conversation

ansis
Copy link
Contributor

@ansis ansis commented Dec 6, 2019

fix #15964
partially port mapbox/mapbox-gl-js#9054

Currently symbol-sort-key works:

  • across tile boundaries with -allow-overlap: true
  • within tiles for collision detection

This pr makes it work for collisions across tile boundaries as well.

This splits the bucket into ranges of symbolInstances with the same sort key. All the ranges from all the tiles in a layer are inserted into a sorted list. Placement is done in the order of the list.

@pozdnyakov feel free to assign reviewing to someone else if you want

@ansis ansis changed the title sort symbols using symbol-sort-key before placement [core] sort symbols using symbol-sort-key before placement Dec 6, 2019
@pozdnyakov pozdnyakov added Core The cross-platform C++ core, aka mbgl text rendering labels Dec 9, 2019
@@ -29,7 +30,7 @@ class LayerRenderData {

class LayerPlacementData {
public:
std::reference_wrapper<Bucket> bucket;
std::reference_wrapper<SymbolBucket> bucket;
Copy link
Contributor

Choose a reason for hiding this comment

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

it's better to keep generic interface here, otherwise LTO will not be able to reduce SymbolLayer-related symbols from the binary if the symbol layer is disabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, thanks! I switched back to Bucket

friend bool operator<(const BucketPlacementParameters& lhs, const BucketPlacementParameters& rhs) {
return lhs.sortKey < rhs.sortKey;
}
SymbolBucket& bucket;
Copy link
Contributor

Choose a reason for hiding this comment

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

BucketPlacementParameters shall not know about SymbolBucket

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Would it be ok for BucketPlacementParameters to have a reference to Bucket?

Copy link
Contributor

Choose a reason for hiding this comment

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

Could we instead just pre-sort LayerPlacementData in RenderSymbolLayer::prepare() accordingly to the the symbol bucket sort key?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, changed to your suggestion. This means that there are now possibly multiple entries into LayerPlacementData per tile which we have to account for elsewhere

const RenderTile& tile;
const mat4& projMatrix;
std::string sourceId;
std::shared_ptr<FeatureIndex> featureIndex;
bool showCollisionBoxes;
float sortKey;
size_t symbolInstanceStart;
size_t symbolInstanceEnd;
Copy link
Contributor

Choose a reason for hiding this comment

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

could the newly added fields be SymbolBucket properties?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These properties come from SortKeyRange. A bucket can have many SortKeyRanges

@chloekraw chloekraw added this to the release-unicorn milestone Dec 18, 2019
@asheemmamoowala asheemmamoowala added the GL JS parity For feature parity with Mapbox GL JS label Feb 3, 2020
@ansis ansis force-pushed the ansis-symbol-sort-key-collisions branch from 2534f87 to 410a2b4 Compare February 10, 2020 15:51
@ansis
Copy link
Contributor Author

ansis commented Feb 10, 2020

@pozdnyakov sorry about the delay getting back to this! I made changes based on your review so this is ready for another look.

The android-api-breakage ci run is failing. Any ideas why?


bool firstInBucket = true;

if (bucket->sortKeyRanges.size() == 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: bucket->sortKeyRanges.empty()

for (const SortKeyRange& sortKeyRange : bucket->sortKeyRanges) {
LayerPlacementData layerData{*bucket,
renderTile,
static_cast<const GeometryTile*>(tile)->getFeatureIndex(),
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: might make sense to cache static_cast<const GeometryTile*>(tile)->getFeatureIndex() in a local variable

@@ -656,18 +661,17 @@ void Placement::placeBucket(const SymbolBucket& bucket,
}

} else {
for (const SymbolInstance& symbol : bucket.symbolInstances) {
placeSymbol(symbol);
auto endIt = bucket.symbolInstances.begin() + params.symbolInstanceEnd;
Copy link
Contributor

Choose a reason for hiding this comment

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

do we check that endIt <= bucket.symbolInstances.end()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope, would an assert be what you're looking for?

Copy link
Contributor

Choose a reason for hiding this comment

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

assert is fine (as long as there are no possible code paths that could violate it 😄 )


if (mode == MapMode::Tile || anchorInsideTile) {
if (sortFeaturesByKey) {
if (sortKeyRanges.size() && sortKeyRanges.back().sortKey == feature.sortKey) {
sortKeyRanges.back().symbolInstanceEnd = symbolInstances.size() + 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

sortKeyRanges.back().symbolInstanceEnd = symbolInstances.size(); shall be enough, no? begin() + size = end() 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It looks like I'm setting it before adding the new symbol instance. I guess moving it after and removing the + 1 would be cleaner

for (const SymbolInstance& symbol : bucket.symbolInstances) {
placeSymbol(symbol);
auto endIt = bucket.symbolInstances.begin() + params.symbolInstanceEnd;
for (auto it = bucket.symbolInstances.begin() + params.symbolInstanceStart; it != endIt; it++) {
Copy link
Contributor

Choose a reason for hiding this comment

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

styling nit: better have auto beginIt = bucket.symbolInstances.begin() + params.symbolInstanceStar

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: ++it

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Core The cross-platform C++ core, aka mbgl GL JS parity For feature parity with Mapbox GL JS text rendering
Projects
None yet
Development

Successfully merging this pull request may close these issues.

symbolSortKey does not equally sort icon and text
4 participants