Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Text rendering or collision detection performance issues #3223

Open
Buthrakaur opened this issue Feb 14, 2025 · 9 comments
Open

Text rendering or collision detection performance issues #3223

Buthrakaur opened this issue Feb 14, 2025 · 9 comments

Comments

@Buthrakaur
Copy link

We just migrated from Mapbox to Maplibre and discovered degraded performance especially on iOS (React Native app for both platforms) when zooming in/out or quickly panning the map. We have 3 symbol layers with small/medium/large variants of POI markers to let collision use the largest possible icon available for a nice map UX with hundreds of POIs in a city. I discovered the rendering performance goes massively down when I add a text in the style and especially when I set text halo (textHaloWidth, textHaloColor) so I had to turn off text halo effects to keep at least some acceptable performance. The Mapbox component seems to have some more performance optimization around text collision detection or text rendering in general. It's surprising the Android performance is still acceptable (even though worse than Mapbox) and it's sluggish on iPhones.

I'd appreciate any recommendations on how to improve the performance by changing the style - I ended up with this:

const useTextHalo = Platform.OS === 'android';

const baseStyle: SymbolLayerStyle = {
  textHaloWidth: 2,
  textHaloBlur: 0,
  textAnchor: 'top',
  textMaxWidth: 10,
  textFont: ['get', 'textFont'],
  iconAllowOverlap: false,
  textAllowOverlap: false,
  iconOptional: false,
  iconAnchor: ['get', 'iconAnchor'],
  symbolSortKey: ['get', 'priority'],
  textOffset: [0, 0.1],
};

export const pinLargeStyle: SymbolLayerStyle = {
  ...baseStyle,
  textField: ['get', 'title'],
  textSize: SIZE_L,
  textColor: ['get', 'textColor'],
  textHaloColor: ['get', 'textHaloColor'],
  iconImage: ['get', 'large'],
  iconSize: ICON_SIZE_MULTIPLIER,
};

export const pinMediumStyle: SymbolLayerStyle = {
  ...baseStyle,
  textField: ['get', 'title'],
  iconImage: ['get', 'medium'],
  textSize: ['case', ['==', ['get', 'type'], 'tour'], SIZE_L, SIZE_M],
  textColor: ['get', 'textColor'],
  iconColor: ['get', 'iconColor'],
  textHaloColor: ['get', 'textHaloColor'],
  iconSize: ICON_SIZE_MULTIPLIER,
};

export const pinSmallStyle: SymbolLayerStyle = {
  iconImage: ['get', 'small'],
  iconColor: ['get', 'iconColor'],
  symbolSortKey: ['get', 'priority'],
  iconAnchor: 'center',
  iconSize: ICON_SIZE_MULTIPLIER,
};

if (!useTextHalo) {
  [pinLargeStyle, pinMediumStyle, pinSmallStyle].forEach((style) => {
    delete style.textHaloWidth;
    delete style.textHaloBlur;
    delete style.textHaloColor;
    delete style.textColor;
  });
}


...

  return (
    <>
      <Images images={images} />
      <ShapeSource id={props.layerId} shape={featuresCollection} onPress={onFeaturePress} hitbox={useMemo(() => ({ width: 0, height: 0 }), [])}>
        <SymbolLayer
          id={`SymbolLayer${props.layerId}Small`}
          minZoomLevel={props.minZoomLevel}
          maxZoomLevel={props.maxZoomLevel}
          style={pinSmallStyle}
        />
        <SymbolLayer
          id={`SymbolLayer${props.layerId}Medium`}
          minZoomLevel={props.minZoomLevel}
          maxZoomLevel={props.maxZoomLevel}
          style={pinMediumStyle}
        />
        <SymbolLayer
          id={`SymbolLayer${props.layerId}Large`}
          minZoomLevel={props.minZoomLevel}
          maxZoomLevel={props.maxZoomLevel}
          style={pinLargeStyle}
        />
      </ShapeSource>
    </>
  );

This is the intended result which worked well with Mapbox in terms of performance but is not acceptable with MapLibre on iOS:

image

@Buthrakaur
Copy link
Author

I'm not sure if #2382 could resolve at least part of the problem but it sounds relevant.

@Buthrakaur Buthrakaur changed the title Text rendering performance issues Text rendering or collision detection performance issues Feb 14, 2025
@louwers
Copy link
Collaborator

louwers commented Feb 14, 2025

Hi @Buthrakaur thanks for sharing your performance-related issue!

Looping in @sjg-wdw. Maybe the rendering team can have a look at it. Do you have the style available as a JSON? Would you be willing to (privately) share your style with MapLibre developers? We're always interested in heavy styles to help improve performance. We have some tracing integrated which can be helpful to study what in particular is slowing things down.

@sjg-wdw
Copy link
Collaborator

sjg-wdw commented Feb 14, 2025

Could this be a problem with sorting? @TimSylvester

@TimSylvester
Copy link
Collaborator

I wouldn't think so, but it doesn't seem like layout or rendering should be significant in that case.

@jakub-oone
Copy link

Hi @louwers,

we would really appreciate that your rendering team could take a look at it.

how should I get the style as JSON?? I have just this:

`pinSmallStyle:

{"iconImage":["get","small"],"iconColor":["get","iconColor"],"symbolSortKey":["get","priority"],"iconAnchor":"center","iconSize":0.5}

pinMediumStyle:

{"textHaloWidth":2,"textHaloBlur":0,"textAnchor":"top","textMaxWidth":10,"textFont":["get","textFont"],"iconAllowOverlap":false,"textAllowOverlap":false,"iconOptional":false,"iconAnchor":["get","iconAnchor"],"symbolSortKey":["get","priority"],"textOffset":[0,0.1],"textField":["get","title"],"iconImage":["get","medium"],"textSize":["case",["==",["get","type"],"tour"],14,12],"textColor":["get","textColor"],"iconColor":["get","iconColor"],"textHaloColor":["get","textHaloColor"],"iconSize":0.5}

pinLargeStyle:

{"textHaloWidth":2,"textHaloBlur":0,"textAnchor":"top","textMaxWidth":10,"textFont":["get","textFont"],"iconAllowOverlap":false,"textAllowOverlap":false,"iconOptional":false,"iconAnchor":["get","iconAnchor"],"symbolSortKey":["get","priority"],"textOffset":[0,0.1],"textField":["get","title"],"textSize":14,"textColor":["get","textColor"],"textHaloColor":["get","textHaloColor"],"iconImage":["get","large"],"iconSize":0.5}`

THX!

@louwers
Copy link
Collaborator

louwers commented Feb 17, 2025

@jakub-oone We would need the sources as well in order to debug it.

You could use https://maplibre.org/maputnik to create it.

@jakub-oone
Copy link

@louwers you mean ShapeSource with shapes? Like this? {"type":"FeatureCollection","features":[{"type":"Feature","id":"627603bd-439a-4573-82cd-459238d07f67","properties":{"id":"627603bd-439a-4573-82cd-459238d07f67","small":"standard_minimised","medium":"see_and_do","large":"map_placeholder_poi_see","title":"Prague Castle","textColor":"rgba(48, 48, 48, 1)","textHaloColor":"rgb(255, 255, 255)","textFont":["Roboto Medium"],"type":"sight","iconAnchor":"bottom","priority":8000000,"isLocked":false,"isVisited":false},"geometry":{"type":"Point","coordinates":[14.4012459,50.0908412]}},{"type":"Feature","id":"dc3e6f6c-19c5-4a92-9c0a-983f009d7469","properties":{"id":"dc3e6f6c-19c5-4a92-9c0a-983f009d7469","small":"standard_minimised","medium":"see_and_do","large":"map_placeholder_poi_see","title":"Charles Bridge","textColor":"rgba(48, 48, 48, 1)","textHaloColor":"rgb(255, 255, 255)","textFont":["Roboto Medium"],"type":"sight","iconAnchor":"bottom","priority":8000001,"isLocked":false,"isVisited":false},"geometry":{"type":"Point","coordinates":[14.411229548783695,50.08621680257775]}}]}

of course there are much more features...

@Buthrakaur
Copy link
Author

We were finally able to isolate the issue and prepared a minimum sample repo here: https://github.com/SmartGuideApp/maplibre-test - it's a React Native app so for running it on iOS following sequence needs to be done:

npm -i
cd ios
pod install
cd ..
npm run ios

The root cause of the performance problem is style having symbolSortKey: ['get', 'priority'] https://github.com/SmartGuideApp/maplibre-test/blob/main/App.tsx#L6 as suggested by @sjg-wdw . When it's present the map rendering is slowed down when moving the map and especially when zooming in and out. The rendering is back to normal when this line is commented out. The map style doesn't seem to matter - https://demotiles.maplibre.org/style.json is used in the repo. The performance seems to be impacted more significantly on iOS but some smaller performance impact is visible even on Android phones.

@sjg-wdw
Copy link
Collaborator

sjg-wdw commented Feb 21, 2025

Sort keys are very slow. Do you need them here?

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

No branches or pull requests

5 participants