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

[core] Per-bucket glyph and icon atlases #9213

Merged
merged 9 commits into from
Jun 13, 2017
Merged

[core] Per-bucket glyph and icon atlases #9213

merged 9 commits into from
Jun 13, 2017

Conversation

jfirebaugh
Copy link
Contributor

@jfirebaugh jfirebaugh commented Jun 7, 2017

Rather than having a single glyph atlas texture and a single image atlas texture, use:

  • One glyph atlas texture per tile
  • One icon atlas texture per tile
  • One atlas texture for background/fill/line patterns

This fixes a host of issues. Most prominently, it lifts limits on number and size of images and glyphs. All atlases are allowed to grow as large as they need to be (using shelf-pack-cpp's autoResize option). The effective limits are now a function of the maximum texture size (most important factor), unique glyphs/icons used per tile, and unique patterns used. Hopefully the result is high enough not to matter in practice.

Secondly, it allows us to remove the restriction on changing the dimensions of an existing image, which is a prerequisite for full "smart setStyle" support (#7893). The restriction was required because symbol buffer attributes refer to coordinates within the atlas, such that buffer updates and atlas updates must be synchronized. With per-tile atlases, we can do that.

It does increase memory consumption somewhat, as glyphs and icons used in multiple tiles will appear in multiple textures. This increase is offset to some extent by the fact that the previous implementation added all images to the atlas (#9019) and never removed them (#2202).

I started out with per-bucket atlases, which would further increase the effective size limit at the expense of further increasing memory consumption. Per-tile atlases seem more natural and a better balance between segmentation and memory consumption.

This approach diverges from gl-js, which continues to use a single image atlas, and per-font-stack glyph atlases. The latter was implemented in mapbox/mapbox-gl-js#1923 but never ported to native. The approach in this PR has the advantage that it's amenable to making text-font data-driven, while gl-js's approach is not.

TODO:

  • Add only bucket-specific glyphs/icons to atlas
  • Or maybe per-tile atlases instead?
  • Write integration tests for changing icon size
  • Write integration tests for updating pattern
  • Get upstream shelf-pack-cpp PR merged, released, and integrated
  • Quantify performance/memory effects
  • Atlas/buckets aren't fully synchronized
  • Remove unused binpack implementation

@jfirebaugh jfirebaugh force-pushed the glyph-textures branch 2 times, most recently from a8dce84 to 16acabd Compare June 8, 2017 02:32
@kkaefer kkaefer added Core The cross-platform C++ core, aka mbgl refactor labels Jun 8, 2017
@jfirebaugh
Copy link
Contributor Author

jfirebaugh commented Jun 9, 2017

Memory usage is better than expected, but rendering performance is worse.

Before:

* Measured vectorFootprint = 31113762 Bytes for 'Platform=Linux,Compiler=gcc-5,Arch=x86_64'
70.8 MB in fullscreen macosapp
2017-06-09 15:30:21.967988-0700 Bench GL[5781:1398013] | paris      | 53.4 fps |
2017-06-09 15:30:21.968083-0700 Bench GL[5781:1398013] | paris2     | 60.1 fps |
2017-06-09 15:30:21.968167-0700 Bench GL[5781:1398013] | alps       | 32.6 fps |
2017-06-09 15:30:21.968249-0700 Bench GL[5781:1398013] | us east    | 60.0 fps |
2017-06-09 15:30:21.968332-0700 Bench GL[5781:1398013] | greater la | 48.6 fps |
2017-06-09 15:30:21.968606-0700 Bench GL[5781:1398013] | sf         | 60.1 fps |
2017-06-09 15:30:21.968739-0700 Bench GL[5781:1398013] | oakland    | 58.1 fps |
2017-06-09 15:30:21.968826-0700 Bench GL[5781:1398013] | germany    | 31.2 fps |
2017-06-09 15:30:21.968955-0700 Bench GL[5781:1398013] Total FPS: 404.0
2017-06-09 15:30:21.969031-0700 Bench GL[5781:1398013] Average FPS: 50.5

After:

* Measured vectorFootprint = 14170521 Bytes for 'Platform=Linux,Compiler=gcc-5,Arch=x86_64'
62.6 MB in fullscreen macosapp
2017-06-09 15:39:03.946082-0700 Bench GL[5799:1400783] | paris      | 49.9 fps |
2017-06-09 15:39:03.946237-0700 Bench GL[5799:1400783] | paris2     | 59.4 fps |
2017-06-09 15:39:03.946327-0700 Bench GL[5799:1400783] | alps       | 30.8 fps |
2017-06-09 15:39:03.946587-0700 Bench GL[5799:1400783] | us east    | 58.7 fps |
2017-06-09 15:39:03.946728-0700 Bench GL[5799:1400783] | greater la | 41.2 fps |
2017-06-09 15:39:03.946899-0700 Bench GL[5799:1400783] | sf         | 59.9 fps |
2017-06-09 15:39:03.947110-0700 Bench GL[5799:1400783] | oakland    | 48.9 fps |
2017-06-09 15:39:03.947198-0700 Bench GL[5799:1400783] | germany    | 25.8 fps |
2017-06-09 15:39:03.947272-0700 Bench GL[5799:1400783] Total FPS: 374.6
2017-06-09 15:39:03.947344-0700 Bench GL[5799:1400783] Average FPS: 46.8

@jfirebaugh
Copy link
Contributor Author

Profiling revealed that the textures were being re-created on every frame. 🙈 After fixing that, performance is the same as before:

2017-06-09 16:27:07.437403-0700 Bench GL[5828:1410697] | paris      | 53.0 fps |
2017-06-09 16:27:07.437496-0700 Bench GL[5828:1410697] | paris2     | 60.2 fps |
2017-06-09 16:27:07.437579-0700 Bench GL[5828:1410697] | alps       | 34.5 fps |
2017-06-09 16:27:07.437662-0700 Bench GL[5828:1410697] | us east    | 60.0 fps |
2017-06-09 16:27:07.437743-0700 Bench GL[5828:1410697] | greater la | 48.0 fps |
2017-06-09 16:27:07.437877-0700 Bench GL[5828:1410697] | sf         | 60.0 fps |
2017-06-09 16:27:07.437962-0700 Bench GL[5828:1410697] | oakland    | 57.5 fps |
2017-06-09 16:27:07.438043-0700 Bench GL[5828:1410697] | germany    | 30.3 fps |
2017-06-09 16:27:07.438115-0700 Bench GL[5828:1410697] Total FPS: 403.6
2017-06-09 16:27:07.438186-0700 Bench GL[5828:1410697] Average FPS: 50.5

@@ -94,6 +93,8 @@ class SymbolLayout {
std::vector<SymbolInstance> symbolInstances;
std::vector<SymbolFeature> features;

GlyphAtlas glyphAtlas;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make the GlyphAtlas more lightweight so that we only allocate significant memory/do significant work when we actually need the glyph atlas? I.e. some buckets may not produce any output at all, and in those cases it might make sense to delay initialization.


These are disparate responsibilities and should eventually be handled by different classes. When we implement
data-driven support for `*-pattern`, we'll likely use per-bucket pattern atlases, and that would be a good time
to refactor this.
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are patterns still handled globally rather than being the responsibility of a per-bucket atlas?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The *-pattern properties are all paint properties and not currently DDS enabled. So there's only one possible pattern per layer, and changing it via runtime styling takes effect immediately, without an async layout step. If/when we enable DDS, then we probably will want per-bucket atlases (at least in the data-driven case), but for now it makes more sense to keep it global.

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