summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2019-10-25 11:57:09 +0200
committerKonstantin Käfer <mail@kkaefer.com>2019-10-25 12:31:10 +0200
commit5e7456d95fb5259f297c342c4888ba3a86667b05 (patch)
tree59c6f29d4cae7795efe66258dd673f6803da0752
parent646d4e3996513ed3e9247e958d39ce9e9b506652 (diff)
downloadqtlocation-mapboxgl-upstream/individual-line-dash-pattern-textures.tar.gz
[core] use individual textures for line dash patternsupstream/individual-line-dash-pattern-textures
This moves the LineAtlas from a shared texture that contained SDF dash patterns to use individual textures. Previously, the texture space was limited to a texture of 512 pixels height. Dash patterns were never removed (and are still never removed as of this patch), which means that this texture could fill up for styles that use a lot of different dash patterns. In particular, dash patterns for lines with a round line cap take up 15 pixels of texture height, limiting the amount of unique dash patterns to 34. While this was probably enough for rendering a single style, we quickly exhausted this number when reusing the Map object to render different styles. Instead of a global shared texture, we're now creating individual textures for every dash pattern. These textures are still cached so that we don't need to re-upload the texture on every frame.
-rw-r--r--src/mbgl/geometry/line_atlas.cpp128
-rw-r--r--src/mbgl/geometry/line_atlas.hpp50
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.cpp41
-rw-r--r--src/mbgl/renderer/render_orchestrator.cpp24
-rw-r--r--test/geometry/line_atlas.test.cpp4
5 files changed, 143 insertions, 104 deletions
diff --git a/src/mbgl/geometry/line_atlas.cpp b/src/mbgl/geometry/line_atlas.cpp
index 106a24d015..2e4de3edbe 100644
--- a/src/mbgl/geometry/line_atlas.cpp
+++ b/src/mbgl/geometry/line_atlas.cpp
@@ -1,50 +1,33 @@
#include <mbgl/geometry/line_atlas.hpp>
#include <mbgl/gfx/upload_pass.hpp>
+#include <mbgl/math/minmax.hpp>
+#include <mbgl/util/hash.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/platform.hpp>
-#include <mbgl/util/hash.hpp>
#include <cmath>
namespace mbgl {
+namespace {
-LineAtlas::LineAtlas(const Size size)
- : image(size),
- dirty(true) {
-}
-
-LineAtlas::~LineAtlas() = default;
-
-LinePatternPos LineAtlas::getDashPosition(const std::vector<float>& dasharray,
- LinePatternCap patternCap) {
- size_t key = patternCap == LinePatternCap::Round ? std::numeric_limits<size_t>::min()
- : std::numeric_limits<size_t>::max();
+size_t getDashPatternHash(const std::vector<float>& dasharray, const LinePatternCap patternCap) {
+ size_t key =
+ patternCap == LinePatternCap::Round ? std::numeric_limits<size_t>::min() : std::numeric_limits<size_t>::max();
for (const float part : dasharray) {
util::hash_combine<float>(key, part);
}
-
- // Note: We're not handling hash collisions here.
- const auto it = positions.find(key);
- if (it == positions.end()) {
- auto inserted = positions.emplace(key, addDash(dasharray, patternCap));
- assert(inserted.second);
- return inserted.first->second;
- } else {
- return it->second;
- }
+ return key;
}
-LinePatternPos LineAtlas::addDash(const std::vector<float>& dasharray, LinePatternCap patternCap) {
+LinePatternPos addDashPattern(AlphaImage& image,
+ const int32_t yOffset,
+ const std::vector<float>& dasharray,
+ const LinePatternCap patternCap) {
const uint8_t n = patternCap == LinePatternCap::Round ? 7 : 0;
- const uint8_t dashheight = 2 * n + 1;
- const uint8_t offset = 128;
+ constexpr const uint8_t offset = 128;
if (dasharray.size() < 2) {
- return LinePatternPos();
- }
-
- if (nextRow + dashheight > image.size.height) {
- Log::Warning(Event::OpenGL, "line atlas bitmap overflow");
+ Log::Warning(Event::ParseStyle, "line dasharray requires at least two elements");
return LinePatternPos();
}
@@ -60,7 +43,7 @@ LinePatternPos LineAtlas::addDash(const std::vector<float>& dasharray, LinePatte
bool oddLength = dasharray.size() % 2 == 1;
for (int y = -n; y <= n; y++) {
- int row = nextRow + n + y;
+ int row = yOffset + n + y;
int index = image.size.width * row;
float left = 0;
@@ -72,7 +55,6 @@ LinePatternPos LineAtlas::addDash(const std::vector<float>& dasharray, LinePatte
}
for (uint32_t x = 0; x < image.size.width; x++) {
-
while (right < x / stretch) {
left = right;
if (partIndex >= dasharray.size()) {
@@ -111,37 +93,79 @@ LinePatternPos LineAtlas::addDash(const std::vector<float>& dasharray, LinePatte
}
LinePatternPos position;
- position.y = (0.5 + nextRow + n) / image.size.height;
- position.height = (2.0 * n) / image.size.height;
+ position.y = (0.5 + yOffset + n) / image.size.height;
+ position.height = (2.0 * n + 1) / image.size.height;
position.width = length;
- nextRow += dashheight;
-
- dirty = true;
-
return position;
}
-Size LineAtlas::getSize() const {
- return image.size;
+} // namespace
+
+DashPatternTexture::DashPatternTexture(const std::vector<float>& from_,
+ const std::vector<float>& to_,
+ const LinePatternCap cap) {
+ const bool patternsIdentical = from_ == to_;
+ const int32_t patternHeight = cap == LinePatternCap::Round ? 15 : 1;
+
+ AlphaImage image({256, static_cast<uint32_t>((patternsIdentical ? 1 : 2) * patternHeight)});
+
+ from = addDashPattern(image, 0, from_, cap);
+ to = patternsIdentical ? from : addDashPattern(image, patternHeight, to_, cap);
+
+ texture = std::move(image);
}
-void LineAtlas::upload(gfx::UploadPass& uploadPass) {
- if (!texture) {
- texture = uploadPass.createTexture(image);
- } else if (dirty) {
- uploadPass.updateTexture(*texture, image);
+void DashPatternTexture::upload(gfx::UploadPass& uploadPass) {
+ if (texture.is<AlphaImage>()) {
+ texture = uploadPass.createTexture(texture.get<AlphaImage>());
}
+}
+
+gfx::TextureBinding DashPatternTexture::textureBinding() const {
+ // The texture needs to have been uploaded already.
+ assert(texture.is<gfx::Texture>());
+ return {texture.get<gfx::Texture>().getResource(),
+ gfx::TextureFilterType::Linear,
+ gfx::TextureMipMapType::No,
+ gfx::TextureWrapType::Repeat,
+ gfx::TextureWrapType::Clamp};
+}
- dirty = false;
+Size DashPatternTexture::getSize() const {
+ return texture.match([](const auto& obj) { return obj.size; });
}
-gfx::TextureBinding LineAtlas::textureBinding() {
- assert(texture);
- // All _changes_ to the texture should've been made and uploaded already.
- assert(!dirty);
- return { texture->getResource(), gfx::TextureFilterType::Linear, gfx::TextureMipMapType::No,
- gfx::TextureWrapType::Repeat, gfx::TextureWrapType::Clamp };
+LineAtlas::LineAtlas() = default;
+
+LineAtlas::~LineAtlas() = default;
+
+DashPatternTexture& LineAtlas::getDashPatternTexture(const std::vector<float>& from,
+ const std::vector<float>& to,
+ const LinePatternCap cap) {
+ const size_t hash = util::hash(getDashPatternHash(from, cap), getDashPatternHash(to, cap));
+
+ // Note: We're not handling hash collisions here.
+ const auto it = textures.find(hash);
+ if (it == textures.end()) {
+ auto inserted = textures.emplace(
+ std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(from, to, cap));
+ assert(inserted.second);
+ needsUpload.emplace_back(hash);
+ return inserted.first->second;
+ } else {
+ return it->second;
+ }
+}
+
+void LineAtlas::upload(gfx::UploadPass& uploadPass) {
+ for (const size_t hash : needsUpload) {
+ const auto it = textures.find(hash);
+ if (it != textures.end()) {
+ it->second.upload(uploadPass);
+ }
+ }
+ needsUpload.clear();
}
} // namespace mbgl
diff --git a/src/mbgl/geometry/line_atlas.hpp b/src/mbgl/geometry/line_atlas.hpp
index b43583c9c8..853305d138 100644
--- a/src/mbgl/geometry/line_atlas.hpp
+++ b/src/mbgl/geometry/line_atlas.hpp
@@ -3,10 +3,11 @@
#include <mbgl/gfx/texture.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/optional.hpp>
+#include <mbgl/util/variant.hpp>
-#include <vector>
-#include <unordered_map>
+#include <map>
#include <memory>
+#include <vector>
namespace mbgl {
@@ -26,29 +27,46 @@ enum class LinePatternCap : bool {
Round = true,
};
-class LineAtlas {
+class DashPatternTexture {
public:
- LineAtlas(Size);
- ~LineAtlas();
-
- // Binds the atlas texture to the GPU, and uploads data if it is out of date.
- gfx::TextureBinding textureBinding();
+ DashPatternTexture(const std::vector<float>& from, const std::vector<float>& to, LinePatternCap);
// Uploads the texture to the GPU to be available when we need it. This is a lazy operation;
- // the texture is only bound when the data is out of date (=dirty).
+ // the texture is only bound when the data is uploaded for the first time.
void upload(gfx::UploadPass&);
- LinePatternPos getDashPosition(const std::vector<float>&, LinePatternCap);
- LinePatternPos addDash(const std::vector<float>& dasharray, LinePatternCap);
+ // Binds the atlas texture to the GPU, and uploads data if it is out of date.
+ gfx::TextureBinding textureBinding() const;
+ // Returns the size of the texture image.
Size getSize() const;
+ const LinePatternPos& getFrom() const { return from; }
+ const LinePatternPos& getTo() const { return to; }
+
private:
- const AlphaImage image;
- bool dirty;
- optional<gfx::Texture> texture;
- uint32_t nextRow = 0;
- std::unordered_map<size_t, LinePatternPos> positions;
+ LinePatternPos from, to;
+ variant<AlphaImage, gfx::Texture> texture;
+};
+
+class LineAtlas {
+public:
+ LineAtlas();
+ ~LineAtlas();
+
+ // Obtains or creates a texture that has both line patterns in it
+ DashPatternTexture& getDashPatternTexture(const std::vector<float>& from,
+ const std::vector<float>& to,
+ LinePatternCap);
+
+ // Uploads the textures to the GPU to be available when we need it.
+ void upload(gfx::UploadPass&);
+
+private:
+ std::map<size_t, DashPatternTexture> textures;
+
+ // Stores a list of hashes of texture objcts that need uploading.
+ std::vector<size_t> needsUpload;
};
} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp
index 36665c1db4..e5bbe74bf9 100644
--- a/src/mbgl/renderer/layers/render_line_layer.cpp
+++ b/src/mbgl/renderer/layers/render_line_layer.cpp
@@ -78,8 +78,8 @@ void RenderLineLayer::prepare(const LayerPrepareParameters& params) {
const LinePatternCap cap = bucket.layout.get<LineCap>() == LineCapType::Round
? LinePatternCap::Round : LinePatternCap::Square;
// Ensures that the dash data gets added to the atlas.
- params.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().from, cap);
- params.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().to, cap);
+ params.lineAtlas.getDashPatternTexture(
+ evaluated.get<LineDasharray>().from, evaluated.get<LineDasharray>().to, cap);
}
}
@@ -146,27 +146,26 @@ void RenderLineLayer::render(PaintParameters& parameters) {
};
if (!evaluated.get<LineDasharray>().from.empty()) {
- const LinePatternCap cap = bucket.layout.get<LineCap>() == LineCapType::Round
- ? LinePatternCap::Round : LinePatternCap::Square;
- LinePatternPos posA = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().from, cap);
- LinePatternPos posB = parameters.lineAtlas.getDashPosition(evaluated.get<LineDasharray>().to, cap);
+ const LinePatternCap cap =
+ bucket.layout.get<LineCap>() == LineCapType::Round ? LinePatternCap::Round : LinePatternCap::Square;
+ const auto& dashPatternTexture = parameters.lineAtlas.getDashPatternTexture(
+ evaluated.get<LineDasharray>().from, evaluated.get<LineDasharray>().to, cap);
draw(parameters.programs.getLineLayerPrograms().lineSDF,
- LineSDFProgram::layoutUniformValues(
- evaluated,
- parameters.pixelRatio,
- tile,
- parameters.state,
- parameters.pixelsToGLUnits,
- posA,
- posB,
- crossfade,
- parameters.lineAtlas.getSize().width),
- {},
- {},
- LineSDFProgram::TextureBindings{
- parameters.lineAtlas.textureBinding(),
- });
+ LineSDFProgram::layoutUniformValues(evaluated,
+ parameters.pixelRatio,
+ tile,
+ parameters.state,
+ parameters.pixelsToGLUnits,
+ dashPatternTexture.getFrom(),
+ dashPatternTexture.getTo(),
+ crossfade,
+ dashPatternTexture.getSize().width),
+ {},
+ {},
+ LineSDFProgram::TextureBindings{
+ dashPatternTexture.textureBinding(),
+ });
} else if (!unevaluated.get<LinePattern>().isUndefined()) {
const auto& linePatternValue = evaluated.get<LinePattern>().constantOr(Faded<std::basic_string<char>>{ "", ""});
diff --git a/src/mbgl/renderer/render_orchestrator.cpp b/src/mbgl/renderer/render_orchestrator.cpp
index 50077784b5..e13a439ae4 100644
--- a/src/mbgl/renderer/render_orchestrator.cpp
+++ b/src/mbgl/renderer/render_orchestrator.cpp
@@ -103,19 +103,17 @@ public:
} // namespace
-RenderOrchestrator::RenderOrchestrator(
- bool backgroundLayerAsColor_,
- optional<std::string> localFontFamily_)
- : observer(&nullObserver())
- , glyphManager(std::make_unique<GlyphManager>(std::make_unique<LocalGlyphRasterizer>(std::move(localFontFamily_))))
- , imageManager(std::make_unique<ImageManager>())
- , lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 }))
- , patternAtlas(std::make_unique<PatternAtlas>())
- , imageImpls(makeMutable<std::vector<Immutable<style::Image::Impl>>>())
- , sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>())
- , layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>())
- , renderLight(makeMutable<Light::Impl>())
- , backgroundLayerAsColor(backgroundLayerAsColor_) {
+RenderOrchestrator::RenderOrchestrator(bool backgroundLayerAsColor_, optional<std::string> localFontFamily_)
+ : observer(&nullObserver()),
+ glyphManager(std::make_unique<GlyphManager>(std::make_unique<LocalGlyphRasterizer>(std::move(localFontFamily_)))),
+ imageManager(std::make_unique<ImageManager>()),
+ lineAtlas(std::make_unique<LineAtlas>()),
+ patternAtlas(std::make_unique<PatternAtlas>()),
+ imageImpls(makeMutable<std::vector<Immutable<style::Image::Impl>>>()),
+ sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>()),
+ layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>()),
+ renderLight(makeMutable<Light::Impl>()),
+ backgroundLayerAsColor(backgroundLayerAsColor_) {
glyphManager->setObserver(this);
imageManager->setObserver(this);
}
diff --git a/test/geometry/line_atlas.test.cpp b/test/geometry/line_atlas.test.cpp
index 960e4ad7ad..1d817220e1 100644
--- a/test/geometry/line_atlas.test.cpp
+++ b/test/geometry/line_atlas.test.cpp
@@ -13,7 +13,7 @@ TEST(LineAtlas, Random) {
std::normal_distribution<float> lengthDistribution(3, 5);
for (size_t it = 0; it < 100; it++) {
- LineAtlas atlas{ Size{ 128, 1024 } };
+ LineAtlas atlas;
std::vector<float> dasharray;
dasharray.reserve(8);
for (size_t j = 0; j < 100; j++) {
@@ -25,7 +25,7 @@ TEST(LineAtlas, Random) {
const LinePatternCap patternCap =
capStyleDistribution(generator) > 0 ? LinePatternCap::Round : LinePatternCap::Square;
- atlas.addDash(dasharray, patternCap);
+ atlas.getDashPatternTexture(dasharray, dasharray, patternCap);
}
}
}