[core] Use a separate atlas and store for annotation sprites
Fixes #1488
This commit is contained in:
committed by
John Firebaugh
parent
58886842bc
commit
e1091db45e
@@ -10,7 +10,11 @@ namespace mbgl {
|
||||
const std::string AnnotationManager::SourceID = "com.mapbox.annotations";
|
||||
const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points";
|
||||
|
||||
AnnotationManager::AnnotationManager() = default;
|
||||
AnnotationManager::AnnotationManager(float pixelRatio)
|
||||
: spriteStore(pixelRatio),
|
||||
spriteAtlas(512, 512, pixelRatio, spriteStore) {
|
||||
}
|
||||
|
||||
AnnotationManager::~AnnotationManager() = default;
|
||||
|
||||
AnnotationIDs
|
||||
@@ -122,6 +126,7 @@ void AnnotationManager::updateStyle(Style& style) {
|
||||
layer->sourceLayer = PointLayerID;
|
||||
layer->layout.icon.image = std::string("{sprite}");
|
||||
layer->layout.icon.allowOverlap = true;
|
||||
layer->spriteAtlas = &spriteAtlas;
|
||||
|
||||
style.addLayer(std::move(layer));
|
||||
}
|
||||
@@ -152,4 +157,14 @@ void AnnotationManager::removeTileMonitor(AnnotationTileMonitor& monitor) {
|
||||
monitors.erase(&monitor);
|
||||
}
|
||||
|
||||
void AnnotationManager::setSprite(const std::string& name, std::shared_ptr<const SpriteImage> sprite) {
|
||||
spriteStore.setSprite(name, sprite);
|
||||
spriteAtlas.updateDirty();
|
||||
}
|
||||
|
||||
double AnnotationManager::getTopOffsetPixelsForAnnotationSymbol(const std::string& name) {
|
||||
auto sprite = spriteStore.getSprite(name);
|
||||
return sprite ? -sprite->height / 2 : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <mbgl/annotation/annotation.hpp>
|
||||
#include <mbgl/annotation/point_annotation_impl.hpp>
|
||||
#include <mbgl/annotation/shape_annotation_impl.hpp>
|
||||
#include <mbgl/sprite/sprite_store.hpp>
|
||||
#include <mbgl/sprite/sprite_atlas.hpp>
|
||||
#include <mbgl/util/geo.hpp>
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
|
||||
@@ -21,7 +23,7 @@ class Style;
|
||||
|
||||
class AnnotationManager : private util::noncopyable {
|
||||
public:
|
||||
AnnotationManager();
|
||||
AnnotationManager(float pixelRatio);
|
||||
~AnnotationManager();
|
||||
|
||||
AnnotationIDs addPointAnnotations(const std::vector<PointAnnotation>&, const uint8_t maxZoom);
|
||||
@@ -31,6 +33,10 @@ public:
|
||||
AnnotationIDs getPointAnnotationsInBounds(const LatLngBounds&) const;
|
||||
LatLngBounds getBoundsForAnnotations(const AnnotationIDs&) const;
|
||||
|
||||
void setSprite(const std::string& name, std::shared_ptr<const SpriteImage>);
|
||||
double getTopOffsetPixelsForAnnotationSymbol(const std::string& name);
|
||||
SpriteAtlas& getSpriteAtlas() { return spriteAtlas; }
|
||||
|
||||
void updateStyle(Style&);
|
||||
|
||||
void addTileMonitor(AnnotationTileMonitor&);
|
||||
@@ -48,6 +54,9 @@ private:
|
||||
ShapeAnnotationImpl::Map shapeAnnotations;
|
||||
std::vector<std::string> obsoleteShapeAnnotationLayers;
|
||||
std::set<AnnotationTileMonitor*> monitors;
|
||||
|
||||
SpriteStore spriteStore;
|
||||
SpriteAtlas spriteAtlas;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ std::unique_ptr<StyleLayer> SymbolLayer::clone() const {
|
||||
result->copy(*this);
|
||||
result->layout = layout;
|
||||
result->paint = paint;
|
||||
result->spriteAtlas = spriteAtlas;
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
@@ -178,7 +179,7 @@ std::unique_ptr<Bucket> SymbolLayer::createBucket(StyleBucketParameters& paramet
|
||||
// needed by this tile.
|
||||
if (!parameters.partialParse) {
|
||||
bucket->addFeatures(parameters.tileUID,
|
||||
parameters.spriteAtlas,
|
||||
*spriteAtlas,
|
||||
parameters.glyphAtlas,
|
||||
parameters.glyphStore,
|
||||
parameters.collisionTile);
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class SpriteAtlas;
|
||||
|
||||
class SymbolLayoutProperties {
|
||||
public:
|
||||
LayoutProperty<PlacementType> placement = PlacementType::Point;
|
||||
@@ -94,6 +96,8 @@ public:
|
||||
|
||||
SymbolLayoutProperties layout;
|
||||
SymbolPaintProperties paint;
|
||||
|
||||
SpriteAtlas* spriteAtlas;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ bool MapContext::renderSync(const TransformState& state, const FrameData& frame)
|
||||
glObjectStore.performCleanup();
|
||||
|
||||
if (!painter) painter = std::make_unique<Painter>(data, transformState);
|
||||
painter->render(*style, frame);
|
||||
painter->render(*style, frame, data.getAnnotationManager()->getSpriteAtlas());
|
||||
|
||||
if (data.mode == MapMode::Still) {
|
||||
callback(nullptr, std::move(view.readStillImage()));
|
||||
@@ -269,12 +269,7 @@ bool MapContext::isLoaded() const {
|
||||
|
||||
double MapContext::getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol) {
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
auto sprite = style->spriteStore->getSprite(symbol);
|
||||
if (sprite) {
|
||||
return -sprite->height / 2;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return data.getAnnotationManager()->getTopOffsetPixelsForAnnotationSymbol(symbol);
|
||||
}
|
||||
|
||||
void MapContext::setSourceTileCacheSize(size_t size) {
|
||||
@@ -299,14 +294,8 @@ void MapContext::onLowMemory() {
|
||||
}
|
||||
|
||||
void MapContext::setSprite(const std::string& name, std::shared_ptr<const SpriteImage> sprite) {
|
||||
if (!style) {
|
||||
Log::Info(Event::Sprite, "Ignoring sprite without stylesheet");
|
||||
return;
|
||||
}
|
||||
|
||||
style->spriteStore->setSprite(name, sprite);
|
||||
|
||||
style->spriteAtlas->updateDirty();
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
data.getAnnotationManager()->setSprite(name, sprite);
|
||||
}
|
||||
|
||||
void MapContext::onTileDataChanged() {
|
||||
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
: mode(mode_)
|
||||
, contextMode(contextMode_)
|
||||
, pixelRatio(pixelRatio_)
|
||||
, annotationManager(pixelRatio)
|
||||
, animationTime(Duration::zero())
|
||||
, defaultFadeDuration(mode_ == MapMode::Continuous ? std::chrono::milliseconds(300) : Duration::zero())
|
||||
, defaultTransitionDuration(Duration::zero())
|
||||
|
||||
@@ -64,7 +64,9 @@ TileParseResult TileWorker::parsePendingLayers() {
|
||||
if (layer.type == StyleLayerType::Symbol) {
|
||||
auto symbolBucket = dynamic_cast<SymbolBucket*>(bucket.get());
|
||||
if (!symbolBucket->needsDependencies(*style.glyphStore, *style.spriteStore)) {
|
||||
symbolBucket->addFeatures(reinterpret_cast<uintptr_t>(this), *style.spriteAtlas,
|
||||
const SymbolLayer* symbolLayer = dynamic_cast<const SymbolLayer*>(&layer);
|
||||
symbolBucket->addFeatures(reinterpret_cast<uintptr_t>(this),
|
||||
*symbolLayer->spriteAtlas,
|
||||
*style.glyphAtlas, *style.glyphStore, *collisionTile);
|
||||
insertBucket(layer.bucketName(), std::move(bucket));
|
||||
pending.erase(it++);
|
||||
|
||||
@@ -128,7 +128,7 @@ void Painter::prepareTile(const Tile& tile) {
|
||||
config.stencilFunc = { GL_EQUAL, ref, mask };
|
||||
}
|
||||
|
||||
void Painter::render(const Style& style, const FrameData& frame_) {
|
||||
void Painter::render(const Style& style, const FrameData& frame_, SpriteAtlas& annotationSpriteAtlas) {
|
||||
frame = frame_;
|
||||
|
||||
glyphAtlas = style.glyphAtlas.get();
|
||||
@@ -158,6 +158,7 @@ void Painter::render(const Style& style, const FrameData& frame_) {
|
||||
spriteAtlas->upload();
|
||||
lineAtlas->upload();
|
||||
glyphAtlas->upload();
|
||||
annotationSpriteAtlas.upload();
|
||||
|
||||
for (const auto& item : order) {
|
||||
if (item.bucket && item.bucket->needsUpload()) {
|
||||
|
||||
@@ -93,7 +93,8 @@ public:
|
||||
void changeMatrix();
|
||||
|
||||
void render(const Style& style,
|
||||
const FrameData& frame);
|
||||
const FrameData& frame,
|
||||
SpriteAtlas& annotationSpriteAtlas);
|
||||
|
||||
// Renders debug information for a tile.
|
||||
void renderTileDebug(const Tile& tile);
|
||||
|
||||
@@ -192,7 +192,8 @@ void Painter::renderSymbol(SymbolBucket& bucket, const SymbolLayer& layer, const
|
||||
const float fontSize = properties.icon.size;
|
||||
const float fontScale = fontSize / 1.0f;
|
||||
|
||||
spriteAtlas->bind(state.isChanging() || layout.placement == PlacementType::Line
|
||||
SpriteAtlas* activeSpriteAtlas = layer.spriteAtlas;
|
||||
activeSpriteAtlas->bind(state.isChanging() || layout.placement == PlacementType::Line
|
||||
|| angleOffset != 0 || fontScale != 1 || sdf || state.getPitch() != 0);
|
||||
|
||||
if (sdf) {
|
||||
@@ -202,7 +203,7 @@ void Painter::renderSymbol(SymbolBucket& bucket, const SymbolLayer& layer, const
|
||||
layout.icon,
|
||||
properties.icon,
|
||||
1.0f,
|
||||
{{ float(spriteAtlas->getWidth()) / 4.0f, float(spriteAtlas->getHeight()) / 4.0f }},
|
||||
{{ float(activeSpriteAtlas->getWidth()) / 4.0f, float(activeSpriteAtlas->getHeight()) / 4.0f }},
|
||||
*sdfIconShader,
|
||||
&SymbolBucket::drawIcons);
|
||||
} else {
|
||||
|
||||
@@ -24,9 +24,7 @@ SpriteAtlas::SpriteAtlas(dimension width_, dimension height_, float pixelRatio_,
|
||||
pixelRatio(pixelRatio_),
|
||||
store(store_),
|
||||
bin(width_, height_),
|
||||
data(std::make_unique<uint32_t[]>(pixelWidth * pixelHeight)),
|
||||
dirty(true) {
|
||||
std::fill(data.get(), data.get() + pixelWidth * pixelHeight, 0);
|
||||
}
|
||||
|
||||
Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(const size_t pixel_width, const size_t pixel_height) {
|
||||
@@ -103,6 +101,11 @@ SpriteAtlasPosition SpriteAtlas::getPosition(const std::string& name, bool repea
|
||||
}
|
||||
|
||||
void SpriteAtlas::copy(const Holder& holder, const bool wrap) {
|
||||
if (!data) {
|
||||
data = std::make_unique<uint32_t[]>(pixelWidth * pixelHeight);
|
||||
std::fill(data.get(), data.get() + pixelWidth * pixelHeight, 0);
|
||||
}
|
||||
|
||||
const uint32_t *srcData = reinterpret_cast<const uint32_t *>(holder.texture->data.data());
|
||||
if (!srcData) return;
|
||||
const vec2<uint32_t> srcSize { holder.texture->pixelWidth, holder.texture->pixelHeight };
|
||||
@@ -189,6 +192,10 @@ void SpriteAtlas::updateDirty() {
|
||||
}
|
||||
|
||||
void SpriteAtlas::bind(bool linear) {
|
||||
if (!data) {
|
||||
return; // Empty atlas
|
||||
}
|
||||
|
||||
if (!texture) {
|
||||
MBGL_CHECK_ERROR(glGenTextures(1, &texture));
|
||||
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
|
||||
|
||||
@@ -66,6 +66,8 @@ public:
|
||||
inline dimension getTextureWidth() const { return pixelWidth; }
|
||||
inline dimension getTextureHeight() const { return pixelHeight; }
|
||||
inline float getPixelRatio() const { return pixelRatio; }
|
||||
|
||||
// Only for use in tests.
|
||||
inline const uint32_t* getData() const { return data.get(); }
|
||||
|
||||
private:
|
||||
@@ -90,7 +92,7 @@ private:
|
||||
BinPack<dimension> bin;
|
||||
std::map<Key, Holder> images;
|
||||
std::set<std::string> uninitialized;
|
||||
const std::unique_ptr<uint32_t[]> data;
|
||||
std::unique_ptr<uint32_t[]> data;
|
||||
std::atomic<bool> dirty;
|
||||
bool fullUploadRequired = true;
|
||||
GLuint texture = 0;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <mbgl/map/map_data.hpp>
|
||||
#include <mbgl/map/source.hpp>
|
||||
#include <mbgl/map/transform_state.hpp>
|
||||
#include <mbgl/layer/symbol_layer.hpp>
|
||||
#include <mbgl/sprite/sprite_store.hpp>
|
||||
#include <mbgl/sprite/sprite_atlas.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
@@ -88,6 +89,12 @@ StyleLayer* Style::getLayer(const std::string& id) const {
|
||||
}
|
||||
|
||||
void Style::addLayer(util::ptr<StyleLayer> layer) {
|
||||
if (SymbolLayer* symbolLayer = dynamic_cast<SymbolLayer*>(layer.get())) {
|
||||
if (!symbolLayer->spriteAtlas) {
|
||||
symbolLayer->spriteAtlas = spriteAtlas.get();
|
||||
}
|
||||
}
|
||||
|
||||
layers.emplace_back(std::move(layer));
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ TEST(Sprite, SpriteAtlas) {
|
||||
EXPECT_EQ(112, atlas.getTextureHeight());
|
||||
|
||||
// Image hasn't been created yet.
|
||||
EXPECT_TRUE(atlas.getData());
|
||||
EXPECT_FALSE(atlas.getData());
|
||||
|
||||
auto metro = atlas.getImage("metro", false);
|
||||
EXPECT_EQ(0, metro.pos.x);
|
||||
@@ -42,6 +42,8 @@ TEST(Sprite, SpriteAtlas) {
|
||||
EXPECT_EQ(18, metro.texture->pixelHeight);
|
||||
EXPECT_EQ(1.0f, metro.texture->pixelRatio);
|
||||
|
||||
EXPECT_TRUE(atlas.getData());
|
||||
|
||||
auto pos = atlas.getPosition("metro", false);
|
||||
EXPECT_DOUBLE_EQ(20, pos.size[0]);
|
||||
EXPECT_DOUBLE_EQ(20, pos.size[1]);
|
||||
|
||||
Reference in New Issue
Block a user