[core] SymbolBucket updates complete at placement stage
`RenderSymbolLayer` does not have to update dynamic vertices of its buckets, this logic is moved to placement (which is already updates opacity vertices). * fixes clustering of labels when text variable placement enabled - as assignes `usesVariablePlacement` per bucket * simplifies the code in `RenderSymbolLayer` (the `RenderSymbolLayer::upload()` is now omitted). * symbol buckets are not modified after orchestration finishes
This commit is contained in:
@@ -361,15 +361,13 @@ namespace mbgl {
|
||||
|
||||
|
||||
void reprojectLineLabels(gfx::VertexVector<gfx::Vertex<SymbolDynamicLayoutAttributes>>& dynamicVertexArray, const std::vector<PlacedSymbol>& placedSymbols,
|
||||
const mat4& posMatrix, const style::SymbolPropertyValues& values,
|
||||
const mat4& posMatrix, bool pitchWithMap, bool rotateWithMap, bool keepUpright,
|
||||
const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state) {
|
||||
|
||||
const ZoomEvaluatedSize partiallyEvaluatedSize = sizeBinder.evaluateForZoom(state.getZoom());
|
||||
|
||||
const std::array<double, 2> clippingBuffer = {{ 256.0 / state.getSize().width * 2.0 + 1.0, 256.0 / state.getSize().height * 2.0 + 1.0 }};
|
||||
|
||||
const bool pitchWithMap = values.pitchAlignment == style::AlignmentType::Map;
|
||||
const bool rotateWithMap = values.rotationAlignment == style::AlignmentType::Map;
|
||||
|
||||
const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1, state.getZoom());
|
||||
|
||||
const mat4 labelPlaneMatrix = getLabelPlaneMatrix(posMatrix, pitchWithMap,
|
||||
@@ -405,19 +403,19 @@ namespace mbgl {
|
||||
const float perspectiveRatio = 0.5 + 0.5 * (cameraToAnchorDistance / state.getCameraToCenterDistance());
|
||||
|
||||
const float fontSize = evaluateSizeForFeature(partiallyEvaluatedSize, placedSymbol);
|
||||
const float pitchScaledFontSize = values.pitchAlignment == style::AlignmentType::Map ?
|
||||
const float pitchScaledFontSize = pitchWithMap ?
|
||||
fontSize * perspectiveRatio :
|
||||
fontSize / perspectiveRatio;
|
||||
|
||||
const Point<float> anchorPoint = project(placedSymbol.anchorPoint, labelPlaneMatrix).first;
|
||||
|
||||
PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio());
|
||||
PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio());
|
||||
|
||||
useVertical = placeUnflipped == PlacementResult::UseVertical;
|
||||
|
||||
if (placeUnflipped == PlacementResult::NotEnoughRoom || useVertical ||
|
||||
(placeUnflipped == PlacementResult::NeedsFlipping &&
|
||||
placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio()) == PlacementResult::NotEnoughRoom)) {
|
||||
placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint, state.getSize().aspectRatio()) == PlacementResult::NotEnoughRoom)) {
|
||||
hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace mbgl {
|
||||
PointAndCameraDistance project(const Point<float>& point, const mat4& matrix);
|
||||
|
||||
void reprojectLineLabels(gfx::VertexVector<gfx::Vertex<SymbolDynamicLayoutAttributes>>&, const std::vector<PlacedSymbol>&,
|
||||
const mat4& posMatrix, const style::SymbolPropertyValues&,
|
||||
const mat4& posMatrix, bool pitchWithMap, bool rotateWithMap, bool keepUpright,
|
||||
const RenderTile&, const SymbolSizeBinder& sizeBinder, const TransformState&);
|
||||
|
||||
optional<std::pair<PlacedGlyph, PlacedGlyph>> placeFirstAndLastGlyph(const float fontScale,
|
||||
|
||||
@@ -19,6 +19,7 @@ class PatternDependency;
|
||||
using PatternLayerMap = std::map<std::string, PatternDependency>;
|
||||
class Placement;
|
||||
class BucketPlacementParameters;
|
||||
class RenderTile;
|
||||
|
||||
class Bucket {
|
||||
public:
|
||||
@@ -59,7 +60,7 @@ public:
|
||||
}
|
||||
// Places this bucket to the given placement. Returns bucket cross-tile id on success call; `0` otherwise.
|
||||
virtual uint32_t place(Placement&, const BucketPlacementParameters&, std::set<uint32_t>&) { return 0u; }
|
||||
virtual void updateOpacities(Placement&, std::set<uint32_t>&) {}
|
||||
virtual void updateVertices(Placement&, bool /*updateOpacities*/, const RenderTile&, std::set<uint32_t>&) {}
|
||||
|
||||
protected:
|
||||
Bucket() = default;
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace mbgl {
|
||||
|
||||
using namespace style;
|
||||
namespace {
|
||||
std::atomic<uint32_t> maxBucketInstanceId;
|
||||
std::atomic<uint32_t> maxBucketInstanceId;
|
||||
} // namespace
|
||||
|
||||
SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layout_,
|
||||
@@ -62,7 +62,11 @@ void SymbolBucket::upload(gfx::UploadPass& uploadPass) {
|
||||
}
|
||||
|
||||
if (!dynamicUploaded) {
|
||||
text.dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(text.dynamicVertices), gfx::BufferUsageType::StreamDraw);
|
||||
if (!text.dynamicVertexBuffer) {
|
||||
text.dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(text.dynamicVertices), gfx::BufferUsageType::StreamDraw);
|
||||
} else {
|
||||
uploadPass.updateVertexBuffer(*text.dynamicVertexBuffer, std::move(text.dynamicVertices));
|
||||
}
|
||||
}
|
||||
if (!placementChangesUploaded) {
|
||||
if (!text.opacityVertexBuffer) {
|
||||
@@ -84,7 +88,11 @@ void SymbolBucket::upload(gfx::UploadPass& uploadPass) {
|
||||
uploadPass.updateIndexBuffer(*icon.indexBuffer, std::move(icon.triangles));
|
||||
}
|
||||
if (!dynamicUploaded) {
|
||||
icon.dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(icon.dynamicVertices), gfx::BufferUsageType::StreamDraw);
|
||||
if (!icon.dynamicVertexBuffer) {
|
||||
icon.dynamicVertexBuffer = uploadPass.createVertexBuffer(std::move(icon.dynamicVertices), gfx::BufferUsageType::StreamDraw);
|
||||
} else {
|
||||
uploadPass.updateVertexBuffer(*icon.dynamicVertexBuffer, std::move(icon.dynamicVertices));
|
||||
}
|
||||
}
|
||||
if (!placementChangesUploaded) {
|
||||
if (!icon.opacityVertexBuffer) {
|
||||
@@ -150,11 +158,6 @@ bool SymbolBucket::hasCollisionCircleData() const {
|
||||
return !collisionCircle.segments.empty();
|
||||
}
|
||||
|
||||
void SymbolBucket::updateOpacity() {
|
||||
placementChangesUploaded = false;
|
||||
uploaded = false;
|
||||
}
|
||||
|
||||
void addPlacedSymbol(gfx::IndexVector<gfx::Triangles>& triangles, const PlacedSymbol& placedSymbol) {
|
||||
auto endIndex = placedSymbol.vertexStartIndex + placedSymbol.glyphOffsets.size() * 4;
|
||||
for (auto vertexIndex = placedSymbol.vertexStartIndex; vertexIndex < endIndex; vertexIndex += 4) {
|
||||
@@ -242,8 +245,8 @@ bool SymbolBucket::hasFormatSectionOverrides() const {
|
||||
}
|
||||
|
||||
std::pair<uint32_t, bool> SymbolBucket::registerAtCrossTileIndex(CrossTileSymbolLayerIndex& index, const OverscaledTileID& tileID, uint32_t& maxCrossTileID) {
|
||||
bool added = index.addBucket(tileID, *this, maxCrossTileID);
|
||||
return std::make_pair(bucketInstanceId, added);
|
||||
bool firstTimeAdded = index.addBucket(tileID, *this, maxCrossTileID);
|
||||
return std::make_pair(bucketInstanceId, firstTimeAdded);
|
||||
}
|
||||
|
||||
uint32_t SymbolBucket::place(Placement& placement, const BucketPlacementParameters& params, std::set<uint32_t>& seenIds) {
|
||||
@@ -251,8 +254,14 @@ uint32_t SymbolBucket::place(Placement& placement, const BucketPlacementParamete
|
||||
return bucketInstanceId;
|
||||
}
|
||||
|
||||
void SymbolBucket::updateOpacities(Placement& placement, std::set<uint32_t>& seenIds) {
|
||||
placement.updateBucketOpacities(*this, seenIds);
|
||||
void SymbolBucket::updateVertices(Placement& placement, bool updateOpacities, const RenderTile& tile, std::set<uint32_t>& seenIds) {
|
||||
if (updateOpacities) {
|
||||
placement.updateBucketOpacities(*this, seenIds);
|
||||
placementChangesUploaded = false;
|
||||
}
|
||||
placement.updateBucketDynamicVertices(*this, tile);
|
||||
dynamicUploaded = false;
|
||||
uploaded = false;
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -60,14 +60,14 @@ public:
|
||||
bool hasData() const override;
|
||||
std::pair<uint32_t, bool> registerAtCrossTileIndex(CrossTileSymbolLayerIndex&, const OverscaledTileID&, uint32_t& maxCrossTileID) override;
|
||||
uint32_t place(Placement&, const BucketPlacementParameters&, std::set<uint32_t>&) override;
|
||||
void updateOpacities(Placement&, std::set<uint32_t>&) override;
|
||||
void updateVertices(Placement&, bool updateOpacities, const RenderTile&, std::set<uint32_t>&) override;
|
||||
bool hasTextData() const;
|
||||
bool hasIconData() const;
|
||||
bool hasCollisionBoxData() const;
|
||||
bool hasCollisionCircleData() const;
|
||||
bool hasFormatSectionOverrides() const;
|
||||
|
||||
void updateOpacity();
|
||||
|
||||
void sortFeatures(const float angle);
|
||||
// The result contains references to the `symbolInstances` items, sorted by viewport Y.
|
||||
std::vector<std::reference_wrapper<SymbolInstance>> getSortedSymbols(const float angle);
|
||||
@@ -138,6 +138,7 @@ public:
|
||||
const float tilePixelRatio;
|
||||
uint32_t bucketInstanceId;
|
||||
bool justReloaded = false;
|
||||
bool hasVariablePlacement = false;
|
||||
mutable optional<bool> hasFormatSectionOverrides_;
|
||||
|
||||
std::shared_ptr<std::vector<size_t>> featureSortOrder;
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
#include <mbgl/tile/geometry_tile.hpp>
|
||||
#include <mbgl/tile/geometry_tile_data.hpp>
|
||||
#include <mbgl/style/layers/symbol_layer_impl.hpp>
|
||||
#include <mbgl/text/placement.hpp>
|
||||
#include <mbgl/gfx/cull_face_mode.hpp>
|
||||
#include <mbgl/layout/symbol_projection.hpp>
|
||||
#include <mbgl/layout/symbol_layout.hpp>
|
||||
#include <mbgl/layout/symbol_layout.hpp>
|
||||
#include <mbgl/util/math.hpp>
|
||||
@@ -29,16 +27,6 @@ namespace mbgl {
|
||||
|
||||
using namespace style;
|
||||
namespace {
|
||||
Point<float> calculateVariableRenderShift(style::SymbolAnchorType anchor, float width, float height, float radialOffset, float textBoxScale, float renderTextSize) {
|
||||
AnchorAlignment alignment = AnchorAlignment::getAnchorAlignment(anchor);
|
||||
float shiftX = -(alignment.horizontalAlign - 0.5f) * width;
|
||||
float shiftY = -(alignment.verticalAlign - 0.5f) * height;
|
||||
Point<float> offset = SymbolLayout::evaluateRadialOffset(anchor, radialOffset);
|
||||
return Point<float>(
|
||||
(shiftX / textBoxScale + offset.x) * renderTextSize,
|
||||
(shiftY / textBoxScale + offset.y) * renderTextSize
|
||||
);
|
||||
}
|
||||
|
||||
style::SymbolPropertyValues iconPropertyValues(const style::SymbolPaintProperties::PossiblyEvaluated& evaluated_,
|
||||
const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) {
|
||||
@@ -116,122 +104,6 @@ struct RenderableSegment {
|
||||
}
|
||||
};
|
||||
|
||||
void uploadIcon(gfx::UploadPass& uploadPass,
|
||||
UploadParameters& uploadParameters,
|
||||
const RenderTile& tile,
|
||||
const LayerRenderData& renderData) {
|
||||
assert(tile.tile.kind == Tile::Kind::Geometry);
|
||||
auto& bucket = static_cast<SymbolBucket&>(*renderData.bucket);
|
||||
const auto& layout = bucket.layout;
|
||||
|
||||
const bool alongLine = layout.get<SymbolPlacement>() != SymbolPlacementType::Point &&
|
||||
layout.get<IconRotationAlignment>() == AlignmentType::Map;
|
||||
|
||||
if (alongLine) {
|
||||
const auto& evaluated = getEvaluated<SymbolLayerProperties>(renderData.layerProperties);
|
||||
reprojectLineLabels(bucket.icon.dynamicVertices, bucket.icon.placedSymbols, tile.matrix,
|
||||
iconPropertyValues(evaluated, layout), tile, *bucket.iconSizeBinder,
|
||||
uploadParameters.state);
|
||||
|
||||
uploadPass.updateVertexBuffer(*bucket.icon.dynamicVertexBuffer,
|
||||
std::move(bucket.icon.dynamicVertices));
|
||||
}
|
||||
}
|
||||
|
||||
void uploadText(gfx::UploadPass& uploadPass,
|
||||
UploadParameters& uploadParameters,
|
||||
const RenderTile& tile,
|
||||
const LayerRenderData& renderData,
|
||||
bool& hasVariablePlacement) {
|
||||
assert(tile.tile.kind == Tile::Kind::Geometry);
|
||||
auto& bucket = static_cast<SymbolBucket&>(*renderData.bucket);
|
||||
const auto& layout = bucket.layout;
|
||||
|
||||
const bool alongLine = layout.get<SymbolPlacement>() != SymbolPlacementType::Point &&
|
||||
layout.get<TextRotationAlignment>() == AlignmentType::Map;
|
||||
|
||||
if (alongLine) {
|
||||
const auto& evaluated = getEvaluated<SymbolLayerProperties>(renderData.layerProperties);
|
||||
reprojectLineLabels(bucket.text.dynamicVertices,
|
||||
bucket.text.placedSymbols,
|
||||
tile.matrix,
|
||||
textPropertyValues(evaluated, layout),
|
||||
tile,
|
||||
*bucket.textSizeBinder,
|
||||
uploadParameters.state);
|
||||
|
||||
uploadPass.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices));
|
||||
} else if (!layout.get<TextVariableAnchor>().empty()) {
|
||||
bucket.text.dynamicVertices.clear();
|
||||
|
||||
hasVariablePlacement = false;
|
||||
|
||||
const auto partiallyEvaluatedSize = bucket.textSizeBinder->evaluateForZoom(uploadParameters.state.getZoom());
|
||||
const float tileScale = std::pow(2, uploadParameters.state.getZoom() - tile.tile.id.overscaledZ);
|
||||
const bool rotateWithMap = layout.get<TextRotationAlignment>() == AlignmentType::Map;
|
||||
const bool pitchWithMap = layout.get<TextPitchAlignment>() == AlignmentType::Map;
|
||||
const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, uploadParameters.state.getZoom());
|
||||
const auto labelPlaneMatrix = getLabelPlaneMatrix(tile.matrix, pitchWithMap, rotateWithMap, uploadParameters.state, pixelsToTileUnits);
|
||||
|
||||
for (const PlacedSymbol& symbol : bucket.text.placedSymbols) {
|
||||
optional<VariableOffset> variableOffset;
|
||||
if (!symbol.hidden && symbol.crossTileID != 0u) {
|
||||
auto it = uploadParameters.variableOffsets.find(symbol.crossTileID);
|
||||
if (it != uploadParameters.variableOffsets.end()) {
|
||||
hasVariablePlacement |= true;
|
||||
variableOffset = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (!variableOffset) {
|
||||
// These symbols are from a justification that is not being used, or a label that wasn't placed
|
||||
// so we don't need to do the extra math to figure out what incremental shift to apply.
|
||||
hideGlyphs(symbol.glyphOffsets.size(), bucket.text.dynamicVertices);
|
||||
} else {
|
||||
const Point<float> tileAnchor = symbol.anchorPoint;
|
||||
const auto projectedAnchor = project(tileAnchor, pitchWithMap ? tile.matrix : labelPlaneMatrix);
|
||||
const float perspectiveRatio = 0.5f + 0.5f * (uploadParameters.state.getCameraToCenterDistance() / projectedAnchor.second);
|
||||
float renderTextSize = evaluateSizeForFeature(partiallyEvaluatedSize, symbol) * perspectiveRatio / util::ONE_EM;
|
||||
if (pitchWithMap) {
|
||||
// Go from size in pixels to equivalent size in tile units
|
||||
renderTextSize *= bucket.tilePixelRatio / tileScale;
|
||||
}
|
||||
|
||||
auto shift = calculateVariableRenderShift(
|
||||
(*variableOffset).anchor,
|
||||
(*variableOffset).width,
|
||||
(*variableOffset).height,
|
||||
(*variableOffset).radialOffset,
|
||||
(*variableOffset).textBoxScale,
|
||||
renderTextSize);
|
||||
|
||||
// Usual case is that we take the projected anchor and add the pixel-based shift
|
||||
// calculated above. In the (somewhat weird) case of pitch-aligned text, we add an equivalent
|
||||
// tile-unit based shift to the anchor before projecting to the label plane.
|
||||
Point<float> shiftedAnchor;
|
||||
if (pitchWithMap) {
|
||||
shiftedAnchor = project(Point<float>(tileAnchor.x + shift.x, tileAnchor.y + shift.y),
|
||||
labelPlaneMatrix).first;
|
||||
} else {
|
||||
if (rotateWithMap) {
|
||||
auto rotated = util::rotate(shift, -uploadParameters.state.getPitch());
|
||||
shiftedAnchor = Point<float>(projectedAnchor.first.x + rotated.x,
|
||||
projectedAnchor.first.y + rotated.y);
|
||||
} else {
|
||||
shiftedAnchor = Point<float>(projectedAnchor.first.x + shift.x,
|
||||
projectedAnchor.first.y + shift.y);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < symbol.glyphOffsets.size(); i++) {
|
||||
addDynamicAttributes(shiftedAnchor, 0, bucket.text.dynamicVertices);
|
||||
}
|
||||
}
|
||||
}
|
||||
uploadPass.updateVertexBuffer(*bucket.text.dynamicVertexBuffer, std::move(bucket.text.dynamicVertices));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DrawFn>
|
||||
void drawIcon(const DrawFn& draw,
|
||||
const RenderTile& tile,
|
||||
@@ -314,8 +186,7 @@ void drawText(const DrawFn& draw,
|
||||
const LayerRenderData& renderData,
|
||||
SegmentsWrapper textSegments,
|
||||
const SymbolBucket::PaintProperties& bucketPaintProperties,
|
||||
const PaintParameters& parameters,
|
||||
bool hasVariablePlacement) {
|
||||
const PaintParameters& parameters) {
|
||||
assert(tile.tile.kind == Tile::Kind::Geometry);
|
||||
auto& geometryTile = static_cast<GeometryTile&>(tile.tile);
|
||||
auto& bucket = static_cast<SymbolBucket&>(*renderData.bucket);
|
||||
@@ -335,7 +206,7 @@ void drawText(const DrawFn& draw,
|
||||
|
||||
if (values.hasHalo) {
|
||||
draw(parameters.programs.getSymbolLayerPrograms().symbolGlyph,
|
||||
SymbolSDFTextProgram::layoutUniformValues(true, hasVariablePlacement, values, texsize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo),
|
||||
SymbolSDFTextProgram::layoutUniformValues(true, bucket.hasVariablePlacement, values, texsize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Halo),
|
||||
bucket.text,
|
||||
textSegments,
|
||||
bucket.textSizeBinder,
|
||||
@@ -350,7 +221,7 @@ void drawText(const DrawFn& draw,
|
||||
|
||||
if (values.hasFill) {
|
||||
draw(parameters.programs.getSymbolLayerPrograms().symbolGlyph,
|
||||
SymbolSDFTextProgram::layoutUniformValues(true, hasVariablePlacement, values, texsize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill),
|
||||
SymbolSDFTextProgram::layoutUniformValues(true, bucket.hasVariablePlacement, values, texsize, parameters.pixelsToGLUnits, parameters.pixelRatio, alongLine, tile, parameters.state, parameters.symbolFadeChange, SymbolSDFPart::Fill),
|
||||
bucket.text,
|
||||
textSegments,
|
||||
bucket.textSizeBinder,
|
||||
@@ -413,24 +284,6 @@ bool RenderSymbolLayer::hasCrossfade() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void RenderSymbolLayer::upload(gfx::UploadPass& uploadPass, UploadParameters& uploadParameters) {
|
||||
for (const RenderTile& tile : renderTiles) {
|
||||
const LayerRenderData* renderData = tile.tile.getLayerRenderData(*baseImpl);
|
||||
if (!renderData) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& bucket = static_cast<SymbolBucket&>(*renderData->bucket);
|
||||
if (bucket.hasIconData()) {
|
||||
uploadIcon(uploadPass, uploadParameters, tile, *renderData);
|
||||
}
|
||||
|
||||
if (bucket.hasTextData()) {
|
||||
uploadText(uploadPass, uploadParameters, tile, *renderData, hasVariablePlacement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderSymbolLayer::render(PaintParameters& parameters) {
|
||||
if (parameters.pass == RenderPass::Opaque) {
|
||||
return;
|
||||
@@ -537,7 +390,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters) {
|
||||
if (sortFeaturesByKey) {
|
||||
addRenderables(bucket.text.segments, true /*isText*/);
|
||||
} else {
|
||||
drawText(draw, tile, *renderData, std::ref(bucket.text.segments), bucketPaintProperties, parameters, hasVariablePlacement);
|
||||
drawText(draw, tile, *renderData, std::ref(bucket.text.segments), bucketPaintProperties, parameters);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -619,7 +472,7 @@ void RenderSymbolLayer::render(PaintParameters& parameters) {
|
||||
if (sortFeaturesByKey) {
|
||||
for (auto& renderable : renderableSegments) {
|
||||
if (renderable.isText) {
|
||||
drawText(draw, renderable.tile, renderable.renderData, renderable.segment, renderable.bucketPaintProperties, parameters, hasVariablePlacement);
|
||||
drawText(draw, renderable.tile, renderable.renderData, renderable.segment, renderable.bucketPaintProperties, parameters);
|
||||
} else {
|
||||
drawIcon(draw, renderable.tile, renderable.renderData, renderable.segment, renderable.bucketPaintProperties, parameters);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,6 @@ private:
|
||||
void evaluate(const PropertyEvaluationParameters&) override;
|
||||
bool hasTransition() const override;
|
||||
bool hasCrossfade() const override;
|
||||
void upload(gfx::UploadPass&, UploadParameters&) override;
|
||||
void render(PaintParameters&) override;
|
||||
void prepare(const LayerPrepareParameters&) override;
|
||||
|
||||
@@ -76,7 +75,6 @@ private:
|
||||
float textSize = 16.0f;
|
||||
|
||||
bool hasFormatSectionOverrides = false;
|
||||
bool hasVariablePlacement = false;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -345,10 +345,8 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
|
||||
placement->setStale();
|
||||
}
|
||||
|
||||
if (placementChanged || symbolBucketsChanged) {
|
||||
for (auto it = layersNeedPlacement.rbegin(); it != layersNeedPlacement.rend(); ++it) {
|
||||
placement->updateLayerOpacities(*it);
|
||||
}
|
||||
for (auto it = layersNeedPlacement.rbegin(); it != layersNeedPlacement.rend(); ++it) {
|
||||
placement->updateLayerBuckets(*it, placementChanged || symbolBucketsChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,7 +383,6 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
|
||||
|
||||
UploadParameters uploadParameters{
|
||||
updateParameters.transformState,
|
||||
placement->getVariableOffsets(),
|
||||
*imageManager,
|
||||
*lineAtlas,
|
||||
};
|
||||
|
||||
@@ -7,23 +7,18 @@ namespace mbgl {
|
||||
|
||||
class TransformState;
|
||||
class LineAtlas;
|
||||
class VariableOffset;
|
||||
using VariableOffsets = std::unordered_map<uint32_t, VariableOffset>;
|
||||
|
||||
class UploadParameters {
|
||||
public:
|
||||
UploadParameters(const TransformState& state_,
|
||||
const VariableOffsets& variableOffsets_,
|
||||
ImageManager& imageManager_,
|
||||
LineAtlas& lineAtlas_)
|
||||
: state(state_),
|
||||
variableOffsets(variableOffsets_),
|
||||
imageManager(imageManager_),
|
||||
lineAtlas(lineAtlas_) {
|
||||
}
|
||||
|
||||
const TransformState& state;
|
||||
const VariableOffsets& variableOffsets;
|
||||
ImageManager& imageManager;
|
||||
LineAtlas& lineAtlas;
|
||||
};
|
||||
|
||||
@@ -396,10 +396,110 @@ void Placement::commit(TimePoint now) {
|
||||
fadeStartTime = placementChanged ? commitTime : prevPlacement->fadeStartTime;
|
||||
}
|
||||
|
||||
void Placement::updateLayerOpacities(const RenderLayer& layer) {
|
||||
void Placement::updateLayerBuckets(const RenderLayer& layer, bool updateOpacities) {
|
||||
std::set<uint32_t> seenCrossTileIDs;
|
||||
for (const auto& item : layer.getPlacementData()) {
|
||||
item.bucket.get().updateOpacities(*this, seenCrossTileIDs);
|
||||
item.bucket.get().updateVertices(*this, updateOpacities, item.tile, seenCrossTileIDs);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
Point<float> calculateVariableRenderShift(style::SymbolAnchorType anchor, float width, float height, float radialOffset, float textBoxScale, float renderTextSize) {
|
||||
AnchorAlignment alignment = AnchorAlignment::getAnchorAlignment(anchor);
|
||||
float shiftX = -(alignment.horizontalAlign - 0.5f) * width;
|
||||
float shiftY = -(alignment.verticalAlign - 0.5f) * height;
|
||||
Point<float> offset = SymbolLayout::evaluateRadialOffset(anchor, radialOffset);
|
||||
return { (shiftX / textBoxScale + offset.x) * renderTextSize,
|
||||
(shiftY / textBoxScale + offset.y) * renderTextSize };
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Placement::updateBucketDynamicVertices(SymbolBucket& bucket, const RenderTile& tile) {
|
||||
using namespace style;
|
||||
const auto& layout = bucket.layout;
|
||||
const bool alongLine = layout.get<SymbolPlacement>() != SymbolPlacementType::Point;
|
||||
if (alongLine) {
|
||||
if (bucket.hasIconData() && layout.get<IconRotationAlignment>() == AlignmentType::Map) {
|
||||
const bool pitchWithMap = layout.get<style::IconPitchAlignment>() == style::AlignmentType::Map;
|
||||
const bool keepUpright = layout.get<style::IconKeepUpright>();
|
||||
reprojectLineLabels(bucket.icon.dynamicVertices, bucket.icon.placedSymbols,
|
||||
tile.matrix, pitchWithMap, true /*rotateWithMap*/, keepUpright,
|
||||
tile, *bucket.iconSizeBinder, state);
|
||||
}
|
||||
|
||||
if (bucket.hasTextData() && layout.get<TextRotationAlignment>() == AlignmentType::Map) {
|
||||
const bool pitchWithMap = layout.get<style::TextPitchAlignment>() == style::AlignmentType::Map;
|
||||
const bool keepUpright = layout.get<style::TextKeepUpright>();
|
||||
reprojectLineLabels(bucket.text.dynamicVertices, bucket.text.placedSymbols,
|
||||
tile.matrix, pitchWithMap, true /*rotateWithMap*/, keepUpright,
|
||||
tile, *bucket.textSizeBinder, state);
|
||||
}
|
||||
} else if (!layout.get<TextVariableAnchor>().empty() && bucket.hasTextData()) {
|
||||
bucket.text.dynamicVertices.clear();
|
||||
bucket.hasVariablePlacement = false;
|
||||
|
||||
const auto partiallyEvaluatedSize = bucket.textSizeBinder->evaluateForZoom(state.getZoom());
|
||||
const float tileScale = std::pow(2, state.getZoom() - tile.tile.id.overscaledZ);
|
||||
const bool rotateWithMap = layout.get<TextRotationAlignment>() == AlignmentType::Map;
|
||||
const bool pitchWithMap = layout.get<TextPitchAlignment>() == AlignmentType::Map;
|
||||
const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, state.getZoom());
|
||||
const auto labelPlaneMatrix = getLabelPlaneMatrix(tile.matrix, pitchWithMap, rotateWithMap, state, pixelsToTileUnits);
|
||||
|
||||
for (const PlacedSymbol& symbol : bucket.text.placedSymbols) {
|
||||
optional<VariableOffset> variableOffset;
|
||||
if (!symbol.hidden && symbol.crossTileID != 0u) {
|
||||
auto it = variableOffsets.find(symbol.crossTileID);
|
||||
if (it != variableOffsets.end()) {
|
||||
bucket.hasVariablePlacement = true;
|
||||
variableOffset = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (!variableOffset) {
|
||||
// These symbols are from a justification that is not being used, or a label that wasn't placed
|
||||
// so we don't need to do the extra math to figure out what incremental shift to apply.
|
||||
hideGlyphs(symbol.glyphOffsets.size(), bucket.text.dynamicVertices);
|
||||
} else {
|
||||
const Point<float> tileAnchor = symbol.anchorPoint;
|
||||
const auto projectedAnchor = project(tileAnchor, pitchWithMap ? tile.matrix : labelPlaneMatrix);
|
||||
const float perspectiveRatio = 0.5f + 0.5f * (state.getCameraToCenterDistance() / projectedAnchor.second);
|
||||
float renderTextSize = evaluateSizeForFeature(partiallyEvaluatedSize, symbol) * perspectiveRatio / util::ONE_EM;
|
||||
if (pitchWithMap) {
|
||||
// Go from size in pixels to equivalent size in tile units
|
||||
renderTextSize *= bucket.tilePixelRatio / tileScale;
|
||||
}
|
||||
|
||||
auto shift = calculateVariableRenderShift(
|
||||
(*variableOffset).anchor,
|
||||
(*variableOffset).width,
|
||||
(*variableOffset).height,
|
||||
(*variableOffset).radialOffset,
|
||||
(*variableOffset).textBoxScale,
|
||||
renderTextSize);
|
||||
|
||||
// Usual case is that we take the projected anchor and add the pixel-based shift
|
||||
// calculated above. In the (somewhat weird) case of pitch-aligned text, we add an equivalent
|
||||
// tile-unit based shift to the anchor before projecting to the label plane.
|
||||
Point<float> shiftedAnchor;
|
||||
if (pitchWithMap) {
|
||||
shiftedAnchor = project(Point<float>(tileAnchor.x + shift.x, tileAnchor.y + shift.y),
|
||||
labelPlaneMatrix).first;
|
||||
} else {
|
||||
if (rotateWithMap) {
|
||||
auto rotated = util::rotate(shift, -state.getPitch());
|
||||
shiftedAnchor = Point<float>(projectedAnchor.first.x + rotated.x,
|
||||
projectedAnchor.first.y + rotated.y);
|
||||
} else {
|
||||
shiftedAnchor = Point<float>(projectedAnchor.first.x + shift.x,
|
||||
projectedAnchor.first.y + shift.y);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < symbol.glyphOffsets.size(); ++i) {
|
||||
addDynamicAttributes(shiftedAnchor, 0, bucket.text.dynamicVertices);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,7 +659,6 @@ void Placement::updateBucketOpacities(SymbolBucket& bucket, std::set<uint32_t>&
|
||||
}
|
||||
}
|
||||
|
||||
bucket.updateOpacity();
|
||||
bucket.sortFeatures(state.getBearing());
|
||||
auto retainedData = retainedQueryData.find(bucket.bucketInstanceId);
|
||||
if (retainedData != retainedQueryData.end()) {
|
||||
|
||||
@@ -105,7 +105,7 @@ public:
|
||||
Placement(const TransformState&, MapMode, style::TransitionOptions, const bool crossSourceCollisions, std::unique_ptr<Placement> prevPlacementOrNull = nullptr);
|
||||
void placeLayer(const RenderLayer&, const mat4&, bool showCollisionBoxes);
|
||||
void commit(TimePoint);
|
||||
void updateLayerOpacities(const RenderLayer&);
|
||||
void updateLayerBuckets(const RenderLayer&, bool updateOpacities);
|
||||
float symbolFadeChange(TimePoint now) const;
|
||||
bool hasTransitions(TimePoint now) const;
|
||||
|
||||
@@ -116,9 +116,6 @@ public:
|
||||
void setStale();
|
||||
|
||||
const RetainedQueryData& getQueryData(uint32_t bucketInstanceId) const;
|
||||
using VariableOffsets = std::unordered_map<uint32_t, VariableOffset>;
|
||||
const VariableOffsets& getVariableOffsets() const { return variableOffsets; }
|
||||
|
||||
private:
|
||||
friend SymbolBucket;
|
||||
void placeLayerBucket(
|
||||
@@ -126,6 +123,7 @@ private:
|
||||
const BucketPlacementParameters&,
|
||||
std::set<uint32_t>& seenCrossTileIDs);
|
||||
|
||||
void updateBucketDynamicVertices(SymbolBucket& bucket, const RenderTile& tile);
|
||||
void updateBucketOpacities(SymbolBucket&, std::set<uint32_t>&);
|
||||
void markUsedJustification(SymbolBucket&, style::TextVariableAnchorType, SymbolInstance&);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user